﻿@charset "UTF-8";
/* USER VARIABLES SECTION */
/* ============================================================================
   Theme v3 "Merged design" tokens (MERGED-PLAN-2026-07-24 + CANVAS-SPEC — R7).
   Green is RETIRED as a UI color: #00467F is THE one action color (Q1).
   Light theme lives on :root; the dark theme overrides live under
   [data-bs-theme="dark"], which the inline theme script (Resources.ascx)
   stamps on <html> before first paint (OS auto via prefers-color-scheme +
   header toggle persisted in localStorage). The design is light-first, so
   every dark value here is DERIVED (plan §5-D5) — dark stays fully shipped.
   Legacy variable names (--black, --darker-grey, ...) are ALIASED onto the new
   tokens so the existing partials inherit the new world without a mass rewrite;
   new/updated code should prefer the canonical names.
   POLISH S6a, P3-21 (2026-08-20): the alias list is not open-ended - an alias with
   ZERO consumers is deleted, not kept. --green / --green-accent / --green-text,
   --orange, --link, --panel, --blue-light, --blue-lighter, --charcoal, --caution,
   --caution-check, --shadow-card and the four non-rgb --bs-* greys all went that way
   (17 declarations); their 15 live uses were rewritten onto --cta / --ok first.
   ========================================================================== */
/* ── THE palette, authored once (R12) ─────────────────────────────────────────
   Every colour below used to exist in THREE hand-maintained places: the canonical
   --token custom properties, a "--bs-* Bootstrap bridge" block inside :root, and
   the $theme-colors map Bootstrap compiles its components and utilities from.
   Nothing kept the three in sync — which is exactly how R11 came to measure
   .bg-light at 1.06:1 in dark mode (the bridge had no dark values while the ink
   inside followed the theme) and why --bs-blue only resolved to COLAMCO blue
   because _skin.css happens to load after bootstrap.css.
   They are Sass variables now and all three consumers DERIVE from them, so the
   drift is structurally impossible rather than merely patched. to-rgb() needs
   Bootstrap's functions, hence the import here; the rest of the chain
   (variables → variables-dark → maps → utilities) is imported further down as
   before, and Bootstrap's own variables are all !default so setting $blue and
   $theme-colors above it still wins. */
/* Session K, 2026-08-11: the $blue / $c-* / $d-* declarations that used to sit here now live in
   _palette.scss, unchanged apart from the two Session K values ($c-paper-2, $c-input) and two new
   names ($c-ink-faint, $c-input / $d-input). Nothing else moved and no consumer below changed.
   The reason for the split is stated in that file: _myaccount.scss is its own compilerconfig entry,
   cannot see THIS file, and had therefore re-stated eight of these hex values by hand — two of them
   already a session out of date. A Sass-only partial can be imported by an entry that must not
   inherit the token/Bootstrap layers, and it emits nothing.
   This file still owns what the numbers MEAN: the :root token layer, the dark overrides, and the
   Bootstrap $theme-colors bridge. */
/* ============================================================================
   THE palette, as Sass variables only — no selectors, no declarations, so this
   file emits ZERO bytes wherever it is imported.

   Extracted from _variables.scss in Session K (2026-08-11) for one reason:
   _myaccount.scss is its own compilerconfig entry and cannot see
   _variables.scss, so its `.myaccount-module .message-body` document surface
   (the transactional-email sheet that must stay light in BOTH themes) had
   re-stated eight of these values as hex literals — and two of them had already
   drifted a session behind (--ink #14181d, --ink-2 #5f6670, both pre-Harbor).
   That is R11/R12's lesson for the third time: a colour restated by hand in a
   second place is a colour that will disagree with the first.

   Anything that needs a palette value at COMPILE time imports this file.
   Anything that needs it at RUNTIME uses the var(--token) custom properties
   that _variables.scss derives from it — which is almost everything.
   _variables.scss owns the token layer, the [data-bs-theme="dark"] overrides
   and the Bootstrap maps; it is still the place to read to understand a token's
   MEANING. This file is only where the numbers live.
   ========================================================================== */
:root {
  /* — canonical tokens (CANVAS-SPEC token table) — */
  --ink: #0f1b26; /* body text */
  --ink-2: #51636f; /* muted text — AA-safe (replaces #727880) */
  /* Harbor §2.3 asks for #8a99a6 here and it CANNOT ship: measured 2.92:1 on white, under the
     3:1 floor this tier exists to hold for large text (the old #8a95a0 was 3.05:1).  #8695a2 is
     the same Harbor hue one step darker — 3.07:1 — so the tier keeps its meaning and axe keeps
     its zero.  Do not "restore" the plan's value without re-measuring. */
  --ink-3: #8695a2; /* faint ink — large/struck-price text ONLY (3.07:1 on white) */
  /* Session K: #656f7a → #626c77, ONE step darker and nothing more. --paper-2 moved down to
     #e9eff5 to become a visible panel (see $c-paper-2); at that value #656f7a measured 4.41:1 on
     --paper-2, i.e. this tier would have lost the AA claim its own comment makes. #626c77 restores
     it (4.61:1 on --paper-2, 5.3:1 on --paper). Darkening is a monotone improvement — every other
     surface this tier lands on gains contrast — so there is nothing else to re-measure. */
  --ink-faint: #626c77; /* R11: THE quiet small-text tier — the faintest neutral that still
clears AA at 12px on BOTH --paper (5.3:1) and --paper-2 (4.6:1).
Use for the PDP ID row, "Share", struck price, inventory timestamp,
part numbers, pagination — anything faint that is still text.
--ink-3 is large-text-only and --rule-strong is a BORDER: neither is
AA-safe for small copy (R10 measured 2.53:1 light / 1.99:1 dark). */
  --paper: #ffffff; /* cards / primary surfaces */
  --paper-2: #e9eff5; /* soft panels / image plates */
  --ground: #f4f7fa; /* page ground / alt bands */
  /* ==================================================================================
     SURFACE SWAP, 2026-08-21 (SURFACE-SWAP-TRUST-RAIL-PLAN, S1, decision 1).

     LIGHT WAS SWAPPED. DARK DELIBERATELY WAS NOT.

     --paper itself is NOT flipped, because "white" is still meant literally in a lot of
     places: form fields, image wells, brand-logo plates, wallet buttons, overlay sheets.
     A repointable token per ROLE is what makes the swap triageable:

       --surface-card   the fill of a real card/panel that sits ON the page field.
                        light --paper-2 (#e9eff5)   dark --paper (#101d2c)
       --surface-plate  a plate/well/quiet control INSIDE such a card - it must not
                        equal the card it sits in, which --paper-2 now does in light.
                        light --paper (#ffffff)     dark --paper-2 (#16273a)
       --control-hover  the hover/focus fill of a WHITE control that sits on a card. The
                        old --paper-2 wash is now the card's own colour, i.e. invisible.
                        light --rule (#d8e2ec)      dark --paper-2 (#16273a)

     The dark values of all three are exactly what those sites already computed, so dark
     is pixel-unchanged - that is the cheapest proof the swap is token-level.

     WHY DARK CANNOT USE THE SAME MAPPING: light --paper-2 sits BELOW --paper in
     luminance (the field recedes, the cards come forward). In dark, $d-paper-2 (#16273a)
     is LIGHTER than $d-paper (#101d2c) - it had to be, see _palette.scss - so carrying
     the light mapping through would put every card IN FRONT of the field it sits on.
     Dark's field is --ground, which is the one token darker than --paper there.
     ================================================================================== */
  --surface-card: var(--paper-2);
  --surface-plate: var(--paper);
  --control-hover: var(--rule);
  --pill-border: var(--rule); /* the inset ring that makes a white pill a pill (see --pill-bg) */
  --blue: #00467f; /* COLAMCO blue — brand fields, seals (constant across themes) */
  --blue-deep: #051526; /* masthead, footer, dark hero, ASSIST band */
  --blue-tint: #e8f0f7; /* selected/active washes */
  --blue-link: #00579e; /* text links & accents (flips light in dark theme) */
  --cta: #00467f; /* THE one action color (Q1: blue CTA, green retired) */
  --cta-press: #0a5391;
  --cta-ink: #ffffff; /* text on the action blue — white (8.7:1) */
  --cta-outline: #00467f; /* outline-CTA border/text — flips lighter in dark */
  --focus: #00467f; /* focus-visible ring — flips to the on-dark accent in dark */
  --ok: #3d6785; /* positive text semantic (steel family — ticks, savings text; AA on white) */
  --steel: #5980a6; /* ornaments, icon strokes, review bars — never a CTA */
  --steel-tint: #e7edf3; /* "Buying for an organization?" panels */
  /* SURFACE SWAP S1 (2026-08-21): LIGHT went #e7edf3 -> #ffffff and gained --pill-border. The
     pill now sits on --surface-card (#e9eff5), and #e7edf3 on #e9eff5 is a hairline - the fill
     stopped reading as a pill at all (the same failure Session E fixed in dark, one surface
     over). White needs an edge to be a pill rather than a hole, hence --pill-border; it is drawn
     as an INSET RING, not a border, so no pill anywhere changes size. DARK IS UNTOUCHED. */
  --pill-bg: #ffffff; /* SAVINGS PILL fill (Session E). Was --steel-tint's value in LIGHT, and a
   separate token on purpose: in dark, --steel-tint (#14222f) is a hairline
   from dark --paper (#101d2c), so the SAVE pill's fill vanished and the pill
   read as plain text — it passes contrast (the ink is fine) so axe never
   flagged it. Retuning --steel-tint would have moved the organization panel
   and the cart with it; the pill needed its own pair. Harbor's own values. */
  --pill-ink: #3d6785; /* ink on --pill-bg */
  --warn: #b45309; /* warnings AND the amber risk stamp. 2026-08-12: "stock states render
   blue/neutral (D12)" was struck — DESIGN.md supersedes §5-D12 with
   "in-stock = blue stamp, backorder/limited = amber stamp", and the neutral
   outline D12 mandated measured 1.77:1, below the 3:1 a stamp's border owes
   as the sole carrier of a state. Now on .mc-chip-special / .mc-chip-out /
   .cs-est-tag (5.02:1 light, 7.4:1 dark). */
  --err: #b3261e;
  --rule: #d8e2ec; /* hairline card + row rules */
  --rule-2: #eceff2; /* softer inner rules */
  --rule-strong: #b9c4ce; /* limited / neutral stamp outline — DECORATION, 1.77:1 on
   --paper. Never the sole carrier of a control's boundary or
   of a state: use --input for that (see below).
   Session N raised the DARK value only (1.66:1 -> 3.49:1);
   this light value is unchanged by ruling — see the dark
   block for why the two themes diverge. */
  /* Session K, 2026-08-11 (PLAN §6 last bullet). The reported defect was that --input and
     --rule-strong had collided; measured, the ladder was INVERTED — #cdd5dd is 1.50:1 on --paper
     against --rule-strong's 1.77:1, so the token named "form-field borders" was the WEAKER of the
     two, and _components.scss had (correctly, for the values it found) put --rule-strong on
     .form-control / .form-select instead. Both were far under the 3:1 WCAG 1.4.11 asks of a
     control boundary; axe never says so, because 1.4.11 on borders is not one of its automated
     rules — which is how six redesign sessions held axe 0 over it.
     --input is now THE 3:1 interactive-boundary tier and .form-control / .form-select name it:
       light  #798896 — 3.64:1 on --paper, 3.38:1 on --ground, 3.14:1 on --paper-2 (the worst case,
                        since a darker surface costs a dark border contrast)
       dark   #5c7488 — 3.49:1 on --paper, 3.80:1 on --ground, 3.11:1 on --paper-2
     --rule-strong keeps its decorative sites. Its use as a SECONDARY BUTTON border
     (.btn-neutral, .myaccount-* outline buttons) is a genuine 1.4.11 gap at 1.77:1 and a sitewide
     restyle, so it is measured and recorded in SESSION-K §Owed rather than changed here.
     Session N, 2026-08-11 enumerated the survivors by parsing the SCSS, not by grep count: the
     real figure is 38 declarations in 10 files, not 46 — and FOUR of them were not decoration.
     .co-change (_cart.scss), .qo-upload (_cart.scss) and .newsletter-signup input[type=email]
     (_components.scss) moved to --input in that session; .theme-toggle (_chrome.scss) is dead at
     runtime (the topbar rule wins at 3.19:1) and was left alone. */
  --input: #798896; /* interactive boundaries: form fields, the qty spinner group, the warranty
radio, .view-details, suggestion chips — anything whose outline IS the
control. Held at ≥3:1 against --paper / --ground / --paper-2. */
  --stamp-instock: #00467f; /* in-stock stamps are COLAMCO blue */
  --field-ink: #ffffff; /* text on brand/navy fields — CONSTANT, never theme ink */
  --field-ink-2: #b9cddf; /* secondary text on navy */
  --field-ink-3: #9fb2c6; /* tertiary text on navy */
  --field-accent: #8cc0e8; /* kickers / accents on navy (large text + chrome only) */
  --gold: #e6a817; /* review stars (ornament only) */
  --badge-amber: #c2410c; /* cart count badge — distinct from everything, NOT green */
  /* Caution family: amber, for the states where the order can still proceed. Session K widens it
     from "ONLY the backorder acknowledgment" to the cart's stock-issue banner (.cart-alert), which
     already called itself a caution treatment while spending --warn on its fill, bar AND ink — and
     measured 4.38:1 for that ink. --caution-ink exists because --warn is a 4.5:1-on-PAPER token,
     not a 4.5:1-on-amber-fill one. Anything that draws an amber panel belongs here; --warn stays
     for warning text on paper. */
  --caution-bg: #fbf6ef;
  --caution-bd: #e7d4bd;
  --caution-ink: #7a4a12;
  --caution-mark: #9a5b16;
  /* Harbor §2.3. --band-grad is not in the plan's table but is the hero's sibling (the ASSIST
     band): it is re-stopped on the same three navies so the two bands cannot drift apart. */
  --hero-grad: radial-gradient(120% 150% at 80% -30%, #0e3d68 0%, #07223c 52%, #041526 100%);
  --band-grad: radial-gradient(130% 130% at 20% 0%, #0e3d68, #07223c 55%, #041526);
  --shadow-cta: 0 8px 22px rgba(0, 70, 127, 0.32);
  /* Redesign type system (PLAN §2.2). Three families, all self-hosted latin subsets — the
     @font-face blocks are in _global.scss.  Both text stacks still fall back through
     `Arial, Helvetica, sans-serif`, the one fallback stack the rest of the site spells out by
     hand; R79 measured an Arial/Helvetica tail at ZERO element displacement against the settled
     webfont layout where `system-ui` moved 68 (1px each), and that holds regardless of which
     webfont sits in front of it.
     The "Archivo Fallback" metric face that --ff-d used to interpose is GONE with Archivo — it
     was tuned to Archivo's metrics at font-stretch:66% (see _global.scss). */
  --ff-d: "Schibsted Grotesk", Arial, Helvetica, sans-serif; /* display: headings, hero, display prices */
  --ff-u: "IBM Plex Sans", Arial, Helvetica, sans-serif; /* UI/body */
  /* ⚠ THE NAME IS HISTORICAL — this is NOT a monospace stack any more.
     Refinement A5 (2026-08-12, Juan): IBM Plex Mono is retired sitewide. The eyebrow /
     ALL-CAPS-label grammar keeps its own token so the 53 places that spend it stay one
     decision, but the face it resolves to is now the UI face. The Mono @font-face blocks
     are deleted from _global.scss with it — four static weights that nothing requests.
     Every rule that names --ff-m and also relies on figure alignment already carries its
     own `font-variant-numeric: tabular-nums`, which IBM Plex Sans supports, so column
     alignment survives the change; do not re-add a mono fallback to "protect" it. */
  --ff-m: "IBM Plex Sans", Arial, Helvetica, sans-serif; /* eyebrows, ALL-CAPS captions, labels */
  /* Display tracking. Theme v3 drew its display grammar through Archivo's width axis
     (font-stretch:66%) with -0.01em on top; Schibsted Grotesk has no width axis, so the
     condensation is gone and the tightening is all in the tracking. One token so a later
     session can retune the whole display grammar in one place. */
  --display-tracking: -0.025em;
  /* — legacy aliases (keep old partials on-palette) — */
  --black: var(--ink);
  --darker-grey: var(--ink-2);
  /* R11: was var(--rule-strong) — a BORDER token. Every one of the nine places the legacy
     partials spend --dark-grey is faint TEXT (part numbers, struck price, stock timestamp,
     pagination, "Remove"), so it rendered at 2.53:1 light / 1.99:1 dark. Re-pointed at the
     AA-safe small-text tier; the one place that genuinely wanted a rule (the ID-row separator
     bar, _product.scss) now names --rule-strong directly. */
  --dark-grey: var(--ink-faint);
  --grey: var(--rule);
  --light-grey: var(--rule);
  --lightest-grey: var(--paper-2);
  --error: var(--err);
  --caution-text: var(--warn);
  --success: var(--ok);
  --white: #ffffff; /* constant true white (text on blue/dark fields) */
  /* Refinement A1/A2 (D1, 2026-08-12): the page FIELD is tinted; panels and cards stay
     --paper. --paper-2 (#e9eff5), not --ground (#f4f7fa) — --ground was mocked as the
     softer alternative and Juan picked the stronger one.
     HOME needs no exception (Juan, 2026-08-12): all eight of its sections are full-bleed
     bands that paint their own background and are spaced with padding, not margin, so the
     body field is never exposed there. If a future home section ships with no band of its
     own it will show the tint — that is the moment to revisit, not before. */
  /* SURFACE SWAP S1 (2026-08-21, Juan): the LIGHT field and the LIGHT card TRADE PLACES. The
     field is --paper (#ffffff) and a card is --surface-card (#e9eff5, i.e. the old field tint).
     The A1/A2 note above records why the tint was on the field; the swap keeps the same two
     values and the same 1.158:1 separation, it only decides which side of the edge carries it.
     DARK IS DELIBERATELY NOT SWAPPED - see --surface-card in the dark block for why it cannot be
     (--paper-2 is LIGHTER than --paper there), so dark keeps --bgbody: var(--ground). */
  --bgbody: var(--paper);
  --fontsize: 16px;
  --textcolor: var(--ink);
  --lineheight: 1.55;
  --userfont: var(--ff-u);
  /* POLISH S2, P2-06 (2026-08-20): the token carried a duration and an easing but NO
     property, so every `transition: var(--transition)` site expanded to
     `transition-property: all` and animated layout as well as colour. All seven live sites
     (_button.scss:178, _category.scss:165, _components.scss:875 and :887, _global.scss:371,
     _myaccount.scss:121 and :128) only ever change colour, background, border colour or
     opacity on hover/focus, so the property list is named here once rather than per
     component. `a { transition: var(--transition) }` is what made the skip link interpolate
     its padding, radius and font weight from the visually-hidden box (P2-05). */
  --transition: color 0.3s ease, background-color 0.3s ease, border-color 0.3s ease, opacity 0.3s ease;
  /* POLISH S2, P2-06/P2-08 (2026-08-20): the ONE quiet-interactive hover wash, for control
     families that carry no colour of their own to step - the .chip-stamp explainer buttons, the
     PDP gallery thumbnails and the filter rail's facet rows. An ink wash rather than a paper
     token because those three sit on different fields (--paper card, --paper-2 page, the sticky
     bar's blurred surface) and a fixed surface colour reads on only one of them; a translucent
     wash reads on all of them and flips with the theme for free. */
  --hover-wash: color-mix(in srgb, var(--ink) 8%, transparent);
  /* ------------------------------------------------------------------------------------
     POLISH S3, P2-07 (2026-08-20): THE HEADING RAMP, PUBLISHED AS TOKENS.

     Measured before this session at 1440: four different page-H1 sizes across the five
     commerce surfaces (home 60, category 40, cart 34, checkout 34, PDP 28), FIVE section-H2
     sizes on home alone (36/40/44/46 plus a 30px catalog card), and five sizes for the one
     ALL-CAPS eyebrow role (12.5 / 12.8 / 11.52 / 11.5 / 10.5).  Of all of them only 36 was on
     the scale DESIGN.md publishes.  The scale itself was never the problem - the base h1..h6
     rules in _global.scss already implement it - the problem was that every component wrote
     its own clamp() instead of naming a step, so there was nothing for a new component to
     inherit and nothing a review could check against.

     So the steps are named here and consumed by role.  A component may pick a DIFFERENT step
     from its tag (a page H1 that carries catalog data takes --fs-3, see .product-title) but it
     may not invent a size between two steps.

     The values are the clampFunction() output for the same pairs _global.scss already uses -
     written out because _variables.scss does not @import _mixin.scss.  $clampLaptop is 1440,
     so each middle term is max/1440*100vw and every step hits its max at exactly 1440.
     ------------------------------------------------------------------------------------ */
  --fs-display: clamp(38px, 4.16667vw, 60px); /* 60/38 - the home hero, one instance sitewide */
  --fs-1: clamp(34px, 3.61111vw, 52px); /* 52/34 - h1 base */
  --fs-2: clamp(27px, 2.5vw, 36px); /* 36/27 - page title, band title */
  --fs-3: clamp(21px, 1.94444vw, 28px); /* 28/21 - data page title, sub-section, empty state */
  --fs-4: clamp(18px, 1.52778vw, 22px); /* 22/18 - spec group, card title */
  --fs-5: clamp(16px, 1.25vw, 18px); /* 18/16 - panel title */
  --fs-6: clamp(14px, 1.11111vw, 16px); /* 16/14 - row title, minor title */
  --fs-label: 12.5px; /* the ALL-CAPS eyebrow / rail label / footer column head - does NOT clamp */
  /* POLISH S3, P2-18 (2026-08-20): ONE stroke weight for the authored inline-SVG icon set.
     Five weights were in the tree: 1.75 in markup (48 bare <svg> in the content estate), 1.8
     on svg.ico-use in both _content.scss and _home.scss plus the voice-search and theme-toggle
     glyphs, 1.9 on the phone deck's tabs and 2.2 on svg.ico-check.  1.8 is the dominant
     authored weight and becomes the token; the 48 markup literals were rewritten to match it
     so there is exactly one number.  _photoswipe-vendor.scss:26 keeps its own 2 - vendor.

     The SIZE ramp is deliberately narrower than the stroke rule.  An icon sitting inline with
     text takes that text's size and must keep doing so - that is why one PDP measures ten icon
     sizes, and collapsing them would divorce every glyph from its label.  These three steps are
     for a STANDALONE icon, i.e. one whose button carries no text of its own. */
  --icon-stroke: 1.8;
  --icon-sm: 16px;
  --icon-md: 20px;
  --icon-lg: 24px;
  /* Surface tokens */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 16px;
  --radius-pill: 999px;
  --radius-btn: 8px;
  --shadow-1: 0 1px 3px rgba(6, 43, 76, 0.08), 0 1px 2px rgba(6, 43, 76, 0.05);
  --shadow-2: 0 6px 20px rgba(6, 43, 76, 0.10), 0 2px 6px rgba(6, 43, 76, 0.06);
  --shadow-3: 0 18px 48px rgba(6, 43, 76, 0.18), 0 6px 16px rgba(6, 43, 76, 0.08);
  /* 2026-08-19 UI follow-up item 4 (Juan): THE FLOOR-BAR SHADOW, and it points UP.
     Every bottom-docked bar in the floor-bar family (.cart-mbar, .cart-strip, .co-mbar and
     .sticky-atc's phone floor state) used --shadow-3, whose offset is +Y - so on a decked page the
     shadow fell straight onto the navy deck 0-57px below it and drew the seam Juan reported on the
     PDP Checkout strip. A bar whose bottom edge is against another surface can only cast along its
     TOP edge. ONE token rather than four copies of the value: the five bars are one contract.
     ⚠ TOP-DOCKED BARS DO NOT USE THIS - .sticky-atc's desktop dock and its phone .atc-top state
     hang BELOW the masthead and keep --shadow-2, which points the right way for them. */
  --shadow-floor: 0 -6px 16px rgba(6, 43, 76, 0.14);
  --focus-ring: 0 0 0 3px rgba(0, 70, 127, 0.28);
  /* The navy ladder brackets --blue-deep, so Harbor's darker band drags both ends with it —
     navy-1 is the hero gradient's light stop, navy-3 one step below the band. */
  --navy-1: #0e3d68;
  --navy-2: var(--blue-deep);
  --navy-3: #030d18;
  /* R12: the hand-maintained light "--bs-* bridge" that used to sit here is GONE.
     Bootstrap's own _root.scss already emits --bs-<name> and --bs-<name>-rgb for
     every $theme-colors entry (and --bs-blue from $colors), and $theme-colors is
     now derived from the same Sass palette as the tokens above — so those 29
     declarations were re-stating, by hand, values Bootstrap generates from the
     identical source. Re-stating them is what let the light and dark halves of the
     bridge drift apart in the first place (R11 §1b).
     The dark half stays under [data-bs-theme="dark"] below, because Bootstrap emits
     no theme-colour customs there at all. */
}

/* Dark theme — true dark, not inverted gray (DESIGN.md), retuned to the Harbor dark mapping
   (PLAN §2.3, D2: dark stays and the toggle stays).  Outline / focus / link accents keep the
   LIGHT blues — see the --cta note below for why Harbor's `acc` is not one of them.
   On-navy field tokens (--field-*) are constants by design and do not flip. */
[data-bs-theme=dark] {
  --ink: #e8eef4;
  --ink-2: #9db0c0;
  --ink-3: #6b8095; /* 4.2:1 on --paper — comfortably over this tier's 3:1 large-text floor */
  --ink-faint: #859aab; /* 5.8:1 on --paper, 5.2:1 on --paper-2 — still quieter than --ink-2 */
  --ground: #0b1420;
  --paper: #101d2c;
  --paper-2: #16273a;
  /* SURFACE SWAP S1 (2026-08-21): dark's half of the three role tokens. These are NOT a swap -
     each one restates the value its sites already computed in dark, so every dark surface is
     pixel-identical after the light swap. --pill-border is transparent because dark's --pill-bg
     is a translucent accent wash that already reads on dark paper and owes nothing to an edge;
     the ring is drawn as an inset box-shadow either way, so the geometry never moves. */
  --surface-card: var(--paper);
  --surface-plate: var(--paper-2);
  --control-hover: var(--paper-2);
  --pill-border: transparent;
  /* A1 in DARK is --ground, NOT --paper-2. The light tint works because #e9eff5 sits BELOW
     --paper (#ffffff) in luminance, i.e. the field recedes and the cards come forward. Dark
     inverts that: $d-paper-2 (#16273a) is LIGHTER than $d-paper (#101d2c) — see the ⚠ note in
     _palette.scss on why it had to be — so carrying --paper-2 through would have put the field
     IN FRONT of every card on it. --ground (#0b1420) is the one token that is darker than
     --paper in this theme, so it is the dark half of the same decision. */
  --bgbody: var(--ground);
  --blue-deep: #050e18; /* Harbor dark heroBg */
  --blue-tint: #122c42;
  --blue-link: #8cc0e8;
  /* Harbor dark `acc #1b6fb8 / accH #2b82ce`.  MEASURED, and the reading matters: #1b6fb8 is
     3.5:1 on --ground and 3.3:1 on --paper, so it is NOT usable as text, a link or an outline
     on dark — it is the action FILL, where white ink holds 5.2:1.  So it lands on --cta, and
     --blue-link / --cta-outline / --focus keep the light accents that carry the current axe 0.
     --cta-press is #2477c0 and NOT the mockup's accH #2b82ce: white on accH is 4.05:1, under AA,
     and a hover state has to clear AA too.  #2477c0 is the closest step that does (4.7:1). */
  --cta: #1b6fb8;
  --cta-press: #2477c0;
  --cta-outline: #7fb3dc;
  --focus: #8cc0e8;
  --ok: #8fb3d1;
  --steel: #7fa3c6;
  --steel-tint: #14222f;
  /* Harbor dark pillBg/pillInk. The fill is a translucent wash of the on-navy accent rather than a
     near-paper solid, which is what makes the SAVE pill read as a pill on dark paper at all. */
  --pill-bg: rgba(140, 192, 232, .14);
  --pill-ink: #8cc0e8;
  --warn: #e8912d;
  --err: #e5655f;
  --rule: #1e3040;
  --rule-2: #17283a;
  /* Session N, 2026-08-11 — Juan's ruling on the chip-stamp outlines, DARK ONLY.
     Measured on /category/LaptopsNotebooks-342 with 40 chips live, both themes, 1440 + 402:
       dark  1.66:1 (#2c4356) -> 3.49:1 (#5c7488).  At 1.66:1 the outline was all but invisible
             on --paper #101d2c — the dashed SPECIAL ORDER chip barely read as a chip at all.
       light 1.77:1 was LEFT ALONE on purpose. Raising it to 3.64:1 gives the stamp the same
             visual weight as its own 6.24:1 text and narrows the hierarchy gap against the blue
             IN STOCK stamp from 5.4x to 2.6x — across 20 FREE SHIPPING chips on a 10-row listing
             that is a whole column gaining grid, for no accessibility gain (1.4.11 does not
             apply: the chip's 700-weight uppercase TEXT states the condition).
     So the two tiers stay distinct in light and coincide in dark. The tier split is a SEMANTIC
     contract — which token a rule names — not a promise that the two values differ everywhere.
     Keep naming --input for a control boundary and --rule-strong for decoration regardless.
     This also lifts the dark hairlines/separators (.home-stat, .cs-est-tag, .q-notice) for free. */
  --rule-strong: #5c7488;
  /* Session K: was #2c4356 — identical to --rule-strong, and 1.66:1 on dark --paper. Same
     3:1 boundary rule as light; see the light --input note. */
  --input: #5c7488;
  --stamp-instock: #7fb3dc;
  --gold: #eebe4d;
  /* R13: the dark override was #d95b26 — white on it measures 3.83:1, under AA for the badge's
     12px count. The light value (#c2410c) is 5.18:1 with white and still reads clearly against
     the navy masthead the badge actually sits on in BOTH themes (R8 made the masthead navy
     sitewide), so this is one value, not two drifted ones — the R12 de-duplication lesson. */
  --badge-amber: #c2410c;
  --caution-bg: #241c10;
  --caution-bd: #4a3b22;
  --caution-ink: #e3b980;
  --caution-mark: #d8a45c;
  --shadow-cta: 0 8px 22px rgba(0, 0, 0, 0.5);
  --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.4), 0 1px 2px rgba(0, 0, 0, 0.3);
  --shadow-2: 0 6px 20px rgba(0, 0, 0, 0.45), 0 2px 6px rgba(0, 0, 0, 0.3);
  --shadow-3: 0 18px 48px rgba(0, 0, 0, 0.55), 0 6px 16px rgba(0, 0, 0, 0.35);
  --shadow-floor: 0 -6px 16px rgba(0, 0, 0, 0.5);
  --focus-ring: 0 0 0 3px rgba(140, 192, 232, 0.35);
  --navy-1: #0a2033;
  --navy-3: #030910;
  /* Harbor dark heroBg is a step below the light band, so the hero gradient darkens with it.
     heroInk2 (#a6bcd0) and heroAcc (#8cc0e8) are deliberately NOT applied: --field-* are
     documented constants across themes, and both light values clear AA on this gradient
     (field-ink-2 11.9:1, field-accent 10:1 on the darkest stop). */
  --hero-grad: radial-gradient(120% 150% at 80% -30%, #0a2f52 0%, #061a2e 52%, #050e18 100%);
  /* R11: the Bootstrap bridge's SURFACE greys had no dark values, so every
     .bg-light / .bg-grey / .bg-light-grey / .bg-lightest-grey / .border-grey
     utility pinned a light panel in dark mode while the ink inside followed the
     theme — exactly the .bg-white P0, one layer down. Measured 1.06:1 on the
     MyAccount "Questions about your order?" band (worse than the 1.19:1 that
     started R11), and it also pinned the off-canvas menu + mini-cart flyout
     white (skin Default.ascx uses .bg-light on all three asides).
     Safe to flip: the matching .text-* / .border-* utilities these also feed
     are unused in markup except .border-grey (the PDP stepper), which WANTS the
     dark rule colour here. --bs-dark is deliberately NOT flipped: .bg-dark is
     meant to stay dark in both themes.
     R12: derived from the same $d-* palette as the tokens above (the hand-written
     hex pairs these replace are how the light and dark halves drifted apart), and
     --bs-primary is still deliberately absent — .text-primary (26 markup sites) and
     .bg-primary (17) share it, so flipping it would invert fills that need their
     ink inverted with them. R11's fix stands: _global.scss re-points the dark
     .text-* UTILITIES at the flipping tokens, leaving the fills alone.
     POLISH S6a, P3-21 (2026-08-20): the four NON-rgb siblings that used to head this list
     (--bs-light / --bs-lightest-grey / --bs-grey / --bs-light-grey) are DELETED. MEASURED:
     zero `var(--bs-light)`-style consumers in anything we ship - Bootstrap 5.3 spends the
     compiled hex for .btn-light and .link-light and reads only the -rgb form in .bg- and .text-
     utilities. (Spelling those as a slash-joined pair puts an asterisk-slash in this comment and
     CLOSES it - S4's documented trap, and it cost a build here.)
     ⚠ THESE FOUR -rgb ENTRIES ARE THE R11 DARK FIX ITSELF (15 consumers in bootstrap.css) -
     the plan's "nine unused --bs-* grey bridge entries" was FOUR; deleting these would put
     a light panel back in dark mode. */
  --bs-light-rgb: 16, 29, 44;
  --bs-lightest-grey-rgb: 22, 39, 58;
  --bs-grey-rgb: 30, 48, 64;
  --bs-light-grey-rgb: 30, 48, 64;
}

/* FONTS LOAD SECTION */
/* POLISH S5, P2-17 (2026-08-20): WCAG 2.5.8 target size, for a control whose BOX is its hit area
   (a flex item, an inline-flex chip) and whose ink must not grow. An inline element can simply take
   padding-block - padding on a non-replaced inline box never moves a line box - but a blockified or
   inline-flex one cannot, so this centres a transparent overlay on it instead. Pseudo-elements are
   never event targets, so the click still lands on the element itself and a delegated listener
   reading event.target is unaffected. */
/* GENERAL CSS SETTINGS */
/* Opt in to the browser's native cross-document View Transitions API so same-origin page
   navigations get the browser's built-in cross-fade with no custom animation code.  Browsers
   without support (e.g. Firefox as of now) simply navigate normally - graceful degradation. */
@view-transition {
  navigation: auto;
}
/* Respect a user's reduced-motion request: disable the cross-fade animation for anyone who
   has asked their OS/browser to minimize motion.  Navigation still works - the page just
   swaps instantly with no animation.
   POLISH S6b, P3-25 (2026-08-20): `!important` KEPT ON PURPOSE, as an accessibility guard rather
   than a cascade fight. MEASURED: nothing else in the authored estate animates these pseudos - the
   only animation is the UA's own cross-fade, which an author `animation: none` already beats - so
   today it wins nothing. It stays because a reduced-motion kill switch must not be defeatable by a
   later author rule, and the failure mode is motion for someone who asked for none. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(root),
  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation: none !important;
  }
}
/* Redesign type system (REDESIGN-2026-08-10-PLAN §2.2): Schibsted Grotesk (display) +
   IBM Plex Sans (UI / body).  Every face is SELF-HOSTED as a LATIN subset - no Google Fonts
   request at runtime.

   ⚠ IBM Plex Mono is GONE (refinement A5, 2026-08-12) - the eyebrow/caption/label tier moved
   onto IBM Plex Sans, so the site now ships TWO families, not three, and the four static mono
   weights that used to sit below are deleted with it.  --ff-m still exists as a token; see the
   note on it in _variables.scss for why the name outlived the face.

   Both remaining families are single VARIABLE files covering their whole weight axis, and both
   are preloaded from Resources.ascx.

   font-display stays `swap`, and the two alternatives are MEASURED AND REJECTED - do not
   re-litigate them from theory (R80 §1, R81):
     · `optional` locks the FALLBACK in for the page's life, so a cold visitor never sees the
       brand face at all - the opposite of standardising on it.
     · `block` bought no measurable CLS and cost a 2.5-2.9 s BLANK heading, which LCP does not
       even report (the H1 drops out of candidacy and never re-registers).

   ⚠ There are deliberately NO metric-matched fallback faces here.  R79/R86's six
   `Archivo Fallback` bands were tuned to Archivo's metrics at font-stretch:66% and are dead with
   Archivo; the redesign accepts the swap reflow instead, which is the same trade D7 makes for
   first paint.  If a later perf round wants them back, MEASURE the shipping construct against
   Schibsted Grotesk - do not derive the numbers, and do not port the Archivo ones. */
@font-face {
  font-family: "Schibsted Grotesk";
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url("/Portals/_default/Skins/COLAMCO.DNN.Skins.eCommerce/resources/fonts/schibsted-grotesk/schibsted-grotesk-var-latin.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 100 700;
  font-display: swap;
  src: url("/Portals/_default/Skins/COLAMCO.DNN.Skins.eCommerce/resources/fonts/ibm-plex-sans/ibm-plex-sans-var-latin.woff2") format("woff2");
}
/* The four IBM Plex Mono @font-face blocks that stood here are DELETED (refinement A5,
   2026-08-12): --ff-m resolves to IBM Plex Sans now, so nothing on the site requests the mono
   family and the four static weights were dead declarations. The .woff2 files are left on disk
   under resources/fonts/ibm-plex-mono/ rather than removed — they cost nothing unreferenced, and
   deleting them is the kind of change that 404s something a stale cached stylesheet still asks
   for. Do NOT re-declare these to "keep the option open"; a face nothing uses is a face that
   drifts out of the subset. */
::placeholder, .form-floating label {
  color: var(--ink-2);
}

::selection {
  background-color: var(--cta);
  color: #fff;
}

/* The ONE sitewide scrollbar mechanism (UI batch 2026-08-22, item 2 / D2).

   MEASURED in Edge on the PDP at 1920 before anything was changed:
     - a probe element with NO `scrollbar-width` declared: vertical bar 10px, horizontal bar
       15px, --cta blue thumb on a --light-grey track (the branded look, from this block);
     - `.carousel-indicators.gallery-thumbs`, which declared `scrollbar-width: thin`: a 10px
       NATIVE GREY horizontal bar, no --cta anywhere.
   So (a) the standard `scrollbar-width` property WINS over these pseudo-elements in this
   Chromium build - declaring it anywhere switches that element back to the UA scrollbar and
   throws the branding away - and (b) this block set `width` only, so every HORIZONTAL bar on
   the site took the UA's 15px height. Both halves of the reported mismatch.
   Fix: `height: 10px` here, and every per-element `scrollbar-width` / `scrollbar-color`
   deleted (the one deliberate HIDE, .myaccount-nav__tabs, stays - it wants the UA off).
   Do NOT reintroduce a standard scrollbar-* property on any element: it silently opts that
   element out of everything below. */
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
::-webkit-scrollbar-track {
  background-color: var(--light-grey);
}
::-webkit-scrollbar-thumb {
  border-radius: 5px;
  background-color: var(--cta);
}
::-webkit-scrollbar-thumb:hover {
  background-color: var(--cta);
}

body {
  font-family: var(--userfont);
  font-size: var(--fontsize);
  line-height: var(--lineheight);
  color: var(--textcolor);
  min-width: 320px;
  position: relative;
  overflow-x: hidden;
  background-color: var(--bgbody);
  /* SESSION 6 ITEM 3 (Juan, 2026-08-16): THE PAGE MUST NOT RE-FLOW WHEN A DIALOG OPENS.
     Every dialog on this site locks the scroller by setting `overflow: hidden` on it, the
     scrollbar goes away, and the whole page changes width for as long as the dialog is up.
     Measured at 1440 on a PDP before this line, all three mechanisms:
       · Bootstrap modal    body.clientWidth 1430 -> 1440, .pdp-sections x 55 -> 60
       · Bootstrap offcanvas  the same, 1430 -> 1440, x 55 -> 60
       · SweetAlert2        1430 stays, but it writes an INLINE padding-right: 10px, x 55 -> 50
     so the page jumped 5px right for two of them and 5px LEFT for the third.
     `scrollbar-gutter: stable` is the native one-property answer and it belongs HERE, on the
     element that actually scrolls: `html` is `overflow: clip visible` on this site, so BODY is
     the scroll container (measured: body.scrollHeight 6941 against html's 900) - the reason a
     fixed bar at `right: 0` covers the scrollbar (Session 2 item 4) and the reason `100vw`
     overflows by 10px (Session 5 item 1). An `overflow: hidden` box is still a scroll
     container, so the gutter survives the lock, which is the whole trick.
     Trade-off, stated: a page too short to scroll now reserves the 10px too, so its content is
     measured in 1430 rather than 1440 like every other page. That is the point - one measure
     sitewide - and it is what makes the short pages stop shifting as well. */
  scrollbar-gutter: stable;
}

/* SESSION 6 ITEM 3, second half: SweetAlert2 compensates for the scrollbar ITSELF.
   Bootstrap's compensation is already inert here - its ScrollBarHelper measures
   `window.innerWidth - document.documentElement.clientWidth`, which is 0 on this site because the
   scrollbar is on <body>, not on <html> (measured 0px of padding on body and on header.sticky-top
   with a modal open, before and after this change). SweetAlert2 measures its own way, gets the
   real 10px, and writes it inline on <body>. Against the stable gutter that is a second, opposite
   10px - so it is neutralised rather than left to fight. `!important` is not decoration: the
   declaration it has to beat is an inline style, and nothing weaker can. swal2 restores its own
   value on close, which this simply keeps at 0. */
body.swal2-shown {
  padding-right: 0 !important;
}

/* SESSION 6 ITEM 3, third half — and this one is the part that actually moved the page.
   Neutralising swal2's padding was not enough: measured with the padding rule already live and
   computing 0px, body.clientWidth STILL went 1430 -> 1420 and the content still slid 55 -> 50.
   The cause is `body.swal2-height-auto { height: auto !important }`, which swal2 applies to <html>
   AND <body> to let a tall dialog scroll the page. This site does not need that - the swal2
   container is `position: fixed; inset: 0` with its own `overflow-y: auto`, so the dialog already
   scrolls itself - and here it is actively harmful: at rest html and body are BOTH 900px (the
   viewport) with the page scrolling inside body, and `height: auto` takes them both to the
   document's 6080px, which hands <html> a scrollbar of its own ON TOP of body's reserved gutter.
   Two gutters, 20px, hence the 5px slide. Restoring the resting height puts it back.
   `html body.swal2-height-auto` is (0,2,1) so it beats the vendor's own !important at (0,2,0)
   without needing to load later than it. Measured with this rule in place, dialog open and the
   page scrolled to 1500: html.clientWidth 1440, body.clientWidth 1430, content x 55.0, content
   y 1950.2 and scrollTop 1500 - identical to at rest and identical after close. */
html.swal2-height-auto,
html body.swal2-height-auto {
  height: 100% !important;
}

.pac-container {
  z-index: 1060;
}

h1,
.h1,
h2,
.h2,
h3,
.h3,
h4,
.h4 {
  font-family: var(--ff-d);
  letter-spacing: var(--display-tracking);
  text-wrap: balance;
}

/* R100 (2026-08-21): `.cond` DELETED. It was published as "the canonical display utility for
   non-heading display type" but nothing ever reached for it: zero `cond` class tokens across every
   .cshtml / .ascx / .js / .cs in all three projects, in dbo.HtmlText / TabSettings /
   ModuleSettings on DEV, and in 24 rendered page loads (the many textual hits are the words
   "second"/"condition" inside comments and identifiers, which is why this needed a
   word-boundary grep). Components that want the display face state `font-family: var(--ff-d)`
   themselves. If a shared utility is wanted again, re-add it WITH its first consumer. */
/* POLISH S3, P2-07 (2026-08-20): these six rules ARE the published ramp and they were already
   right - what was missing was a name a component could reach for, so each step now reads from
   the --fs-* token in _variables.scss instead of repeating the clampFunction() pair. The emitted
   clamp() is byte-identical; nothing here changes size. */
h1,
.h1 {
  font-size: var(--fs-1);
  font-weight: 800;
}

h2,
.h2 {
  font-size: var(--fs-2);
  font-weight: 800;
}

h3,
.h3 {
  font-size: var(--fs-3);
  font-weight: 700;
}

h4,
.h4 {
  font-size: var(--fs-4);
  font-weight: 700;
}

h5,
.h5 {
  font-size: var(--fs-5);
  font-weight: 600;
  font-family: var(--ff-u);
}

h6,
.h6 {
  font-size: var(--fs-6);
  font-weight: 600;
  font-family: var(--ff-u);
}

p {
  font-size: var(--fs-6);
  font-family: var(--ff-u);
}

/* Tabular numerals on every price, quantity, countdown and part number
   (DESIGN.md). `.num` is the canonical utility; number inputs and the cart
   count badges get it implicitly. */
.num,
.tabular-nums,
input[type=number],
.lbl-cart-itemcount {
  font-variant-numeric: tabular-nums;
}

/* DARK-MODE HOVER FIX 2026-07-30: `.table-hover` was unreadable in dark theme everywhere it is
   used (reported on the PDP technical-specification rows). Two separate halves of the same bug:
     1. `$table-hover-bg: $c-paper-2` in _variables.scss is a Sass LITERAL, so Bootstrap compiles
        `--bs-table-hover-bg: #f4f6f8` into the `.table` rule once. It is not a var(), so it never
        flips — dark theme kept painting the hovered row near-white.
     2. Bootstrap's own `--bs-table-hover-color: var(--bs-emphasis-color)` resolves to #fff under
        [data-bs-theme="dark"].
   Together: #fff text on #f4f6f8. Re-point both at theme tokens so they flip with the theme.
   `--paper-2` IS #f4f6f8 in light theme, so light rendering is byte-identical to before.
   Declared on `.table` at the same (0,1,0) specificity as Bootstrap's rule and loaded after it,
   so it wins; .myaccount-grid__table's own --blue-tint override is (0,2,0) and still wins here. */
.table {
  --bs-table-hover-bg: var(--paper-2);
  --bs-table-hover-color: var(--ink);
}

/* R100 (2026-08-21): `.entry-title` (+ its `span`) and `.max-w-500` DELETED. Both are leftovers
   from the pre-modernization hand-maintained skin.css - `entry-title` is a WordPress-era name and
   `max-w-500` a one-off px cap outside the token scale. Zero writers across every
   .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, and 24 rendered page
   loads. Page titles come from `.page-hero__title` / `.co-sec-head` / `.home-sec-head`. */
/* Focus system (theme v3): pointer clicks stay ringless, but keyboard focus
   ALWAYS gets a visible 2px action-blue outline with 2px offset (--focus
   flips to the on-dark accent in dark theme). Composite inputs shift their
   border via :focus-within (see _components.scss).

   POLISH S6b, P3-25 (2026-08-20): both `!important`s below are LOAD-BEARING, and the reason is that
   `:not(:focus-visible)` buys no specificity - `:not()` counts as its argument, so
   `input:focus:not(:focus-visible)` is only (0,2,1) and `.btn:focus:not(:focus-visible)` (0,3,0).
   Two authored rules set a VISIBLE ring on a plain `:focus` and both outrank them:
     - `.dnnForm.dnnLogin .dnnLoginService input[type="text"]:focus { box-shadow: var(--focus-ring) }`
       (_chrome.scss, (0,3,1)) - a mouse click into the sign-in modal's field would paint the ring.
     - `.qty-stepper .qty-btn:focus { box-shadow: inset var(--focus-ring) }` (_cart.scss) - a mouse
       click on the PDP / cart quantity +/- would paint an inset ring.
   Fixing the specificity "properly" here would mean re-stating this policy at or above every such
   component rule, i.e. duplicating the policy per component - which is the thing one shared rule
   exists to avoid. So it keeps the !important, with the competition named. */
input:focus:not(:focus-visible),
button:focus:not(:focus-visible),
textarea:focus:not(:focus-visible),
.btn:focus:not(:focus-visible) {
  outline: none !important;
  box-shadow: unset !important;
}

/* POLISH S6b, P3-25 (2026-08-20): `!important` is LOAD-BEARING and it is DNN core that makes it so.
   MEASURED with CDP on the focused skip link: `default.css` declares
   `a, a:visited { outline: initial }` AND `a:hover, a:focus, a:active { outline: initial }`, and each
   selector in those lists counts on its own, so `a:focus` is (0,1,1) against this rule's (0,1,0).
   Without !important the keyboard ring is `initial` on EVERY anchor - the one indicator the whole
   focus system exists to guarantee. Cannot be fixed by specificity without re-stating the ring for
   every element type; a bare `:focus-visible` is the point. Same family as
   [[a-visited-beats-a-bare-class-and-getcomputedstyle-lies]]. */
:focus-visible {
  outline: 2px solid var(--focus) !important;
  outline-offset: 2px;
}

input:focus:required:invalid,
textarea:focus:required:invalid {
  border-color: var(--error);
}
input:required:valid,
textarea:required:valid {
  border-color: var(--ok);
}

input.form-control {
  /*		border: 1px solid var(--grey);
  border-radius: 5px;*/
}
input.form-control::placeholder {
  color: var(--ink-2);
  font-weight: 400;
}

/* =====================================================================
   UI BATCH 2026-08-27-E ITEM 1 (Juan): EVERY CHECKBOX ON THE ESTATE IS A TOGGLE SWITCH.

   "Anywhere we show a checkbox in the skin or modules (MVC or WebForms) change it to a toggle
   button instead. Make sure all toggles (even ones that already exist) are appropriately sized and
   spaced to make tapping with a finger on mobile easy and accurate."

   THIS IS DONE IN CSS, IN ONE PLACE, AND DELIBERATELY NOT IN MARKUP. There are ~45 checkbox render
   sites across the MVC views, the WebForms authentication controls and the DNN module Settings
   views, plus every facet row search.js BUILDS at runtime (_HydrateFacetOverflow) - a markup sweep
   would have to touch all of them and would still miss the ones JavaScript creates. The control
   stays a real `input[type="checkbox"]`, so every `:checked` read, every `.filter-checkbox` handler,
   every KO binding and every screen-reader announcement is unchanged; only its painting is.

   Four of the render sites ALREADY carried `role="switch"` and Bootstrap's `.form-switch`
   (ContentManager's live-preview toggle, MyAccount's "only mine" quotes filter and its approvals
   filter, and the compare sheet's "differences only"). They are not special-cased: this rule
   out-specifies `.form-switch .form-check-input` (0,2,1 against 0,2,0) so all of them land on ONE
   size instead of the 32x16 the ContentManager one used to hand-set - which is the second half of
   Juan's ask, since 16px of height is not a finger target.

   SIZE: 44x26 with a 20px thumb. 44px is the WCAG 2.5.5 (Enhanced) target width and the figure this
   estate already uses for a touch row; the height is carried past the 24px WCAG 2.2 minimum by the
   control itself rather than by a hitTarget overlay, so the switch IS its own target. Vertical
   spacing is the row's - the facet rail's `.form-check` is a 44px flex row (_filter.scss), and the
   `gap` below is what separates a switch from its label everywhere else.

   `appearance: none` is what lets the track and thumb be drawn at all, and it makes `accent-color`
   inert - so the local `accent-color` declarations that used to tint the UA control are dead and
   were removed with the size overrides they sat beside (_filter.scss, _components.scss).
   ===================================================================== */
input[type=checkbox],
input[type=checkbox].form-check-input {
  appearance: none;
  -webkit-appearance: none;
  box-sizing: border-box;
  position: relative;
  flex: 0 0 auto;
  float: none;
  width: 44px;
  height: 26px;
  min-width: 44px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--input);
  border-radius: 999px;
  background-color: var(--rule);
  background-image: none;
  vertical-align: middle;
  cursor: pointer;
  transition: background-color 0.15s ease, border-color 0.15s ease;
}

/* The thumb. A pseudo-element is never an event target, so the click still lands on the input and
   nothing that reads `event.target` changes. */
input[type=checkbox]::after,
input[type=checkbox].form-check-input::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 2px;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background-color: var(--field-ink);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  transform: translateY(-50%);
  transition: left 0.15s ease;
}

/* Selection controls carry COLAMCO blue (status/action separation: CTA green
   is reserved for purchase actions, form state is blue).

   `background-image: none` is not decoration: Bootstrap paints its tick (and, for .form-switch, its
   own thumb) as a background-image on the checked state, and both would print underneath the thumb
   drawn above. */
input[type=checkbox]:checked,
input[type=checkbox].form-check-input:checked {
  background-color: var(--cta);
  border-color: var(--cta);
  background-image: none;
}

input[type=checkbox]:checked::after,
input[type=checkbox].form-check-input:checked::after {
  left: 20px;
}

input[type=checkbox]:disabled,
input[type=checkbox].form-check-input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

@media (prefers-reduced-motion: reduce) {
  input[type=checkbox],
  input[type=checkbox].form-check-input,
  input[type=checkbox]::after,
  input[type=checkbox].form-check-input::after {
    transition: none;
  }
}
/* Bootstrap indents `.form-check` by 1.5em and pulls the control back into that gutter with a
   negative margin, which is sized for a 1em box - a 44px switch would hang outside the row. Any
   .form-check holding a checkbox is laid out as a plain flex row instead; the `:has()` arm leaves
   RADIO rows (checkout's address / card / shipping options, the star rating) on Bootstrap's own
   float layout, which they still want.

   Specificity: `:has()` takes the specificity of its most specific argument, so this is (0,2,1) and
   beats both `.form-check` (0,1,0) and `.form-check.form-switch` (0,2,0). */
.form-check:has(> input[type=checkbox]) {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding-left: 0;
}

/* SESSION 3 ITEM 4 (Juan, 2026-08-16): THE SITEWIDE RADIO ORNAMENT IS DELETED AT SOURCE.
   This block used to give EVERY input[type="radio"] an ::after that drew a 14px circle offset over
   the control (top:-2px left:-1px, blue when checked) - a hand-rolled REPLACEMENT for the platform
   radio. Nothing on the estate wants that any more: every radio either restores the platform
   control outright (`appearance: auto; -webkit-appearance: radio` on checkout's .co-opt,
   .co-shipopt and .cps-opt), draws its own dot (.warranty-plan__dot), or is visually hidden
   (.star-rating-radio). So the ornament only ever painted a second blob beside a real radio -
   Juan's "doubled up" - and two pages had already grown a local `content: none` to kill it
   (_cart.scss's .checkout-page block and _product.scss's .warranty-plan__dot). Both are deleted
   with this: the root cause was ONE inherited ornament, and a third local override would have been
   the third page paying for it.

   `accent-color` is what replaces it. It is the platform's own tinting hook, so the control stays a
   real radio - focus ring, arrow-key group navigation, checked state and forced-colors all come
   from the browser - and it simply paints COLAMCO blue instead of the UA default. Verified against
   the whole radio estate rather than the two pages that complained: checkout (shipping, payment,
   delivery options), the add-warranty sheet, the PDP plan rows, the write-a-review stars, My
   Account and the listing filters. */
input[type=radio] {
  accent-color: var(--cta);
}

/* 2026-08-09: this was "\f107" (fa-angle-down) and drew NOTHING - the header's My Account and
   Contact Us menus were the only two top-level menus with no chevron, while the mega menu's own
   .mm-caret (a CSS border triangle) rendered fine beside them. The shipped FontAwesome webfont is
   a hand-built ~73-glyph subset (Tools/FontAwesomeSubset) and U+F107 is not in its cmap - verified
   against fa-solid-900.woff2, not inferred from the class list, because all.subset.css DOES still
   define .fa-angle-left/.fa-angle-right whose glyphs are equally absent. U+F078 (fa-chevron-down)
   IS in the font, is the same affordance, and matches the .fa-chevron-* family used elsewhere in
   the skin. The reserved 12px box was always there, so nothing shifts - the glyph simply appears. */
.dropdown-toggle::after {
  content: "\f078";
  font-family: "Font Awesome 6 Free";
  font-weight: 600;
  /* .72rem lands the chevron at ~11.5px, the same visual weight as the mega menu's 9px
     .mm-caret two links away; 1rem read as a second piece of copy next to the label. */
  font-size: 0.72rem;
  vertical-align: baseline;
  border: unset;
  margin-left: 0.5rem;
}

a {
  color: var(--blue-link);
  text-decoration: none;
  transition: var(--transition);
}
a:visited {
  color: var(--blue-link);
  text-decoration: none;
}
a:hover {
  color: var(--textcolor);
}
a.btn:visited {
  color: var(--bs-btn-color);
}
a.btn:hover, a.btn:visited:hover {
  color: var(--bs-btn-hover-color);
}
a.dropdown-item:visited, a.dropdown-item:visited:hover, a.nav-item:visited, a.nav-item:visited:hover {
  color: var(--bs-dropdown-link-color);
}

/* R45 item 5: shared reset for the sitewide javascript:void(0) anchor-as-action-trigger anchors
   converted to <button type="button"> (a real link with no href and no destination fails as a
   link for keyboard/screen-reader users and breaks middle-click/open-in-new-tab; a button is the
   correct element). A <button> carries native UA chrome (border, grey background, padding) that
   <a> never had, and it never picked up the sitewide `a{color:var(--blue-link)}` rule above either
   - apply this alongside the element's own semantic class so any more specific color/size/weight
   rule that class already carries still applies on top. Deliberately does NOT set `display` -
   several of the classes this pairs with (.refresh-inventory, .cs-taxexempt) already declare their
   own display (inline-flex/block) at equal specificity in a separately-compiled stylesheet, and
   this file's load order relative to theirs isn't guaranteed - button's UA default (inline-block)
   is close enough to <a>'s (inline) for the classes that don't set their own. */
/* "Add to a list" picker (COLAMCO.Controllers.Cart.ShowAddToListPicker). SweetAlert2 renders its
   dialog at <body> level, so these rules live in the sitewide skin sheet rather than in cart.css
   or product.css - the picker opens from the cart page AND from the product detail page, which
   load different module stylesheets. Left-aligned because it is a form, not a message. */
.add-to-list {
  text-align: left;
  /* Session L: the quantity being saved. Narrow because it holds at most three digits, and a
     full-width number input reads as the primary field of the dialog when the list choice is.

     BF4 (2026-08-14, Juan): it carries the PDP buy box's - / + stepper now. The geometry and the
     tokens below are `.product__quantity`'s in _product.scss, restated rather than shared ON
     PURPOSE: product.css does not load on /cart and this dialog opens from both pages, which is
     the same reason the whole .add-to-list block lives in this sitewide sheet. Keep the two in
     step if either changes. The old `.form-control { max-width: 7rem }` is GONE with the class: the
     field is a bare .quantity-field inside a shrink-to-fit .input-group now, exactly as on the PDP,
     so Bootstrap's .form-control padding/width/border are not fighting the 56x40 box. */
  /* Session L: the first-run state. A shopper with no lists used to be shown a "Choose a list"
     select holding one option - a choice that is not a choice, and which never said in words that
     there was nothing to choose from yet. */
}
.add-to-list .form-label {
  font-size: 0.82rem;
  font-weight: 700;
  margin-bottom: 0.25rem;
}
.add-to-list__product {
  font-weight: 700;
  margin: 0 0 0.9rem;
  color: var(--ink);
}
.add-to-list__new {
  margin-top: 0.85rem;
}
.add-to-list__qty {
  margin-bottom: 0.85rem;
}
.add-to-list__qty .input-group {
  /* `width: fit-content`, not the PDP's `width: auto`. MEASURED in the live dialog at 1440:
     `auto` computed 454px - the full dialog measure - because Bootstrap's .input-group is a
     BLOCK-level flex container and `auto` on a block box means "fill the line". On the PDP
     the same declaration shrinks only because .buybox-qty-row is itself `display: flex`, so
     the group is a flex ITEM there and `auto` resolves to its content. This dialog has no
     such parent, so the width has to be asked for by name. */
  width: fit-content;
  border: 1px solid var(--input);
  border-radius: var(--radius-btn);
  overflow: hidden;
  background: var(--paper);
}
.add-to-list__qty .input-group input {
  border: 0;
  background-color: var(--paper);
  color: var(--ink);
}
.add-to-list__qty .input-group input::-webkit-outer-spin-button, .add-to-list__qty .input-group input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
.add-to-list__qty .input-group input[type=number] {
  -moz-appearance: textfield;
  width: 56px;
  height: 40px;
  margin: 0;
  font-family: var(--ff-u);
  font-size: 14.5px;
  font-weight: 600;
  text-align: center;
}
.add-to-list__qty .input-group input[type=button] {
  width: 38px;
  height: 40px;
  font-family: var(--ff-u);
  font-size: 16px;
  font-weight: 500;
  line-height: 1;
  color: var(--ink-2);
  transition: background-color 0.12s ease;
}
.add-to-list__qty .input-group input[type=button]:hover {
  /* SURFACE SWAP S2 (2026-08-21): --control-hover, matching the PDP's own
     quantity spinner (S1 deviation 2). The same widget must wash the same,
     and --paper-2 is the card fill now. */
  background-color: var(--control-hover);
}
.add-to-list__qty .input-group input[type=button]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}
.add-to-list__empty {
  margin: 0 0 0.85rem;
  color: var(--ink-2);
}

/* Session G: the sign-in modal a guest gets when they use a list control (cart.js _RequireSignIn,
   reached through MoveCartItemToList / ShowAddToListPicker). Its body is an IFRAME of the login page
   through PopupSkin.ascx - see the long note on _RequireSignIn for why the login form cannot simply
   be inlined here. Same reason as .add-to-list above for living in the sitewide sheet: SweetAlert2
   renders at <body> level and the modal opens from two pages that load different module CSS. */
.signin-modal {
  /* The frame IS the dialog body, so the default text padding would only draw a white margin
     around a white page. The dialog keeps its own outer padding for the title and Not now. */
  /* M3 section 3 (item 1, 2026-08-19). The frame is a whole DNN page render and there is always a
     beat before it paints - 0.5-1.5 s warm, longer on the first render after a deploy. Until this
     panel, that beat was an unexplained white box, and it was the reported defect: cart.js's
     watchdog then jumped the shopper to /login while they were still looking at it. Covers the
     frame rather than replacing it - the frame has to stay in the document to load at all - and
     cart.js removes it on the frame's load event. */
}
.signin-modal .swal2-html-container {
  margin: 0;
  padding: 0;
  overflow: hidden;
  /* M3 section 3: the wait panel is positioned against this box, so the dialog does not have
     to know the frame's height twice. */
  position: relative;
}
.signin-modal__wait {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  /* The spinner draws in currentColor. */
  color: var(--ink-2);
  background: var(--paper);
}
.signin-modal__wait-msg {
  margin: 0;
  font-size: 0.95rem;
}
.signin-modal__frame {
  display: block;
  width: 100%;
  /* min(), not calc() - never let a minifier near `calc(a + b)` here, it eats the spaces
     around the + and the whole declaration is dropped silently. */
  /* 37rem = 592px, MEASURED against the framed login document at 704px wide: 579px once the
     pop-up's stray footer and mini-carts are hidden above. The viewport cap is 76vh rather
     than a rounder number for the same reason - at 72vh a 1280x800 laptop landed on 576px,
     three pixels short, and the frame drew its own scrollbar down the dialog's edge. */
  height: min(76vh, 37rem);
  border: 0;
  /* The framed page paints its own field colour; this only covers the moment before it
     arrives, so the dialog does not flash a transparent hole. */
  background: var(--paper);
}

/* Any page DNN renders as a pop-up (?popUp=true), which this skin serves through PopupSkin.ascx.
   The class is stamped on <body> by PopupSkin.ascx.cs, server-side, because the cookie notice is
   created on window load and a class added from script would let it flash first.

   Session G. PopupSkin registered no stylesheets or scripts at all until this round, so no pop-up
   had ever been styled and none of this was visible. Now that it carries the site's CSS, the two
   things that belong to a PAGE rather than to a pop-up have to be taken back off. */
.popup-skin {
  /* The consent notice belongs to the page the shopper is actually on. In a pop-up it is either
     a duplicate of the one already on that page (the sign-in modal frames /login while the
     notice sits at the bottom of the page behind it) or a bar over a dialog the shopper opened
     deliberately. Dismissing either sets the same origin cookie, so nothing is lost here.
     POLISH S6b, P3-25 (2026-08-20): `!important` REMOVED - this selector never needed it.
     MEASURED: the only competing declaration is the vendor's own `.cc-window { display: flex }` in
     `Resources/Shared/components/CookieConsent/cookieconsent.min.css` at (0,1,0), and
     `.popup-skin .cc-window` is (0,2,0), so specificity settles it regardless of which sheet loads
     last. The library's own script writes `style.display` only as `"none"` (which agrees with this
     rule) or as `""` (which removes the inline declaration entirely) - it never writes a visible
     inline value for this to beat. */
  /* A pop-up IS the panel, so the page-level frame around the panel is doubled chrome: the
     dialog already draws the surface, the radius and the shadow. Kept to the outer band and the
     card's own edge - nothing about the form inside is touched. */
  /* PAGE CHROME THAT FOLLOWED THE MODULES IN. PopupSkin declares ONE pane, and DNN falls back to
     ContentPane for any module whose assigned pane the skin does not have - so the site footer
     (FooterPane) and both mini-carts (SidebarRightFull) pile in under the control the pop-up is
     actually for. MEASURED on the framed /login: the login card is 507px and the document was
     2,223, all of it these three. Mirroring all 61 panes of the full skins as hidden divs would
     fix it generically and is not worth 61 divs; naming the two module types that are page chrome
     is. A pop-up never wants either of them, whatever page it is rendered from. */
  /* `:not(.DnnModule-Admin)` is load-bearing, NOT tidiness (2026-08-27). DNN wraps the module
     settings control it renders for `ctl=Module` in the SAME `.DnnModule-<def>` class as the
     module's own front-end output, so without this exclusion the rule hid the settings form for
     every Cart and Footer module - the pop-up opened and rendered nothing at all. MEASURED on
     /cart/ctl/Module/ModuleId/760?popUp=true: the server sent all 118,641 bytes including
     `#dnnModuleSettings`, and this wrapper computed to `display: none`, collapsing
     `#dnn_ContentPane` to 0px. DNN adds `DnnModule-Admin` ONLY in that settings/ctl render -
     measured absent on all four `.DnnModule` wrappers of a normal /cart page - so it is the exact
     discriminator between "this module is page chrome that followed us in" and "this module IS
     what the pop-up is for". */
}
.popup-skin .cc-window {
  display: none;
}
.popup-skin .auth-section {
  padding: 0;
}
.popup-skin .auth-card {
  border: 0;
  box-shadow: none;
}
.popup-skin .DnnModule-COLAMCODNNModulesMVCFooter:not(.DnnModule-Admin),
.popup-skin .DnnModule-COLAMCODNNModulesMVCCart:not(.DnnModule-Admin) {
  display: none;
}

/* POLISH S3, P1-03 (2026-08-20): THE SITEWIDE INLINE TEXT BUTTON GETS A TYPE OF ITS OWN.

   `font: inherit` made this shared control take whatever its parent happened to be, so the 57
   instances in Views/ rendered at whatever size the surrounding block used. Measured on
   /checkout alone, at 1440: .meta-chip-info 11.2px/700, .cs-taxexempt 12.48/600,
   .overlay-link 12.8/500, .cs-quoteline-btn 13/600, .co-promo-link 13.12/700 and
   .co-addlink 13.76/700 - six sizes and three weights for ONE control, and the fractional
   values are em-derived from five different parents rather than chosen.

   13px/600 is the middle of that spread and the value four of the six were already closest to,
   so this is a unification rather than a resize. `font-family` has to be named too: dropping
   `font: inherit` would otherwise let the button fall back to the UA's font stack.

   TWO VARIANTS, per the ruling. The base is the text button. .btn-linklike--icon is for an
   instance whose whole content is one glyph and no label - the mini-cart's per-line trash
   buttons - which would otherwise be a 13px icon; it takes the standalone step off the P2-18
   icon ramp instead. */
.btn-linklike,
.co-addlink,
.refresh-inventory {
  border: 0;
  background: none;
  padding: 0;
  margin: 0;
  font-family: var(--ff-u);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--blue-link);
  text-decoration: none;
  cursor: pointer;
  text-align: inherit;
}

/* MEASURED: font-size alone shrank the mini-cart's trash button from 27x33 to 27x24 - the glyph
   got bigger but the line box got shorter, and losing 9px of target height on a checkout control
   is a worse outcome than the inconsistency. The box is stated so it cannot shrink again. */
.btn-linklike--icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: var(--icon-lg);
  min-height: 32px;
  font-size: var(--icon-sm);
  line-height: 1;
}

.navbar-nav .submenu .dropdown-toggle::after {
  content: "\f054";
  font-family: "Font Awesome 6 Free";
}

/* POLISH S6a, P3-21 (2026-08-20): was var(--green), the retired legacy alias; --cta is the same
   value it resolved to. ⚠ THE !important STAYS: Bootstrap emits its OWN
   `.border-secondary { border-color: var(--bs-secondary) !important }`, so an equal-specificity
   rule without !important loses no matter which sheet loads last. */
.border-secondary {
  border-color: var(--cta) !important;
}

/* R100 (2026-08-21): `.bg-light-grey` DELETED. _components.scss:1575 already records that the
   help / fine-print panel "replaces .bg-light-grey, which R11 measured at" too low a contrast -
   the replacement shipped, the utility did not go with it. Zero writers across every
   .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered page
   loads. The `--light-grey` token itself stays: other rules read it. */
/* R100 (2026-08-21): the `.info-card` block DELETED (`&__item` + its `h5`). A pre-modernization
   card whose live equivalent is `.cp-card` (_content.scss) on the content pages.
   Zero `info-card` tokens across every .cshtml / .ascx / .js / .cs in all three
   projects, dbo.HtmlText on DEV, or 24 rendered page loads - and its `h5` carried a hard 24px
   font-size and `var(--black)` ink, both outside the S3 type ramp and the theme tokens. */
.list-products .card-title {
  line-height: 1.3;
  color: var(--textcolor);
}

/* Product tile cards (_ProductSummaryPartial Cards layout, e.g. the PDP Similar
   carousel): the card keeps a constant white ground in both themes because the
   product imagery needs it (same treatment as the hero work-order card), so all
   text inside must stay pinned to constant light-theme ink instead of following
   the theme - otherwise dark mode renders white-on-white. */
.product-tilecard.card {
  --bs-card-bg: #ffffff;
  color: #14181d;
}

.product-tilecard .card-title {
  color: #14181d;
}

/* POLISH S6b, P3-25 (2026-08-20): KEEP. MEASURED with CDP - Bootstrap emits its own
   `.text-muted { color: var(--bs-secondary-color) !important }` at (0,1,0), and an !important at
   LOWER specificity beats a non-important at higher, so this (0,2,0) rule cannot win without one.
   The tile pins ink and surface to constant light values on purpose (see the note above). */
.product-tilecard .text-muted {
  color: #5f6670 !important;
}

/* Equal-height carousel tiles, done in CSS (R84 lever 3).  ProductCarousel/Index.cshtml used to
   call jquery-matchHeight on these cards; measured at cdv 652 on a PDP that plugin's own
   window-`load` handler cost ONE forced 83.8 ms restyle of 2,149 elements - the whole 3,498-node
   document - to level FOUR tiles, because _apply interleaves a read with a write on every element
   four phases deep.  Swiper's .swiper-wrapper is already `display: flex`, so every .swiper-slide is
   stretched to the tallest slide in the row for free; all that was missing was a way for the card
   INSIDE the slide to fill it.

   Making the slide a flex container (rather than putting height: 100% on the card, which is what
   the promotion branch's .h-100 does) is what keeps the card's own `m-2` margins correct: a
   stretched flex item fills its line box margin-box-first, whereas height: 100% + margins overflows
   the slide and grows the wrapper.  This works only while the slide carries no Bootstrap
   `d-*` display utility - those are `!important` and would win.  See the note in that view. */
.similar-products-swiper .swiper-slide {
  display: flex;
}

.similar-products-swiper .swiper-slide > .card.list-products {
  flex: 1 1 auto;
}

.list-star {
  list-style: none;
  padding-right: 10px;
}

.list-star li {
  height: 18px;
  width: 18px;
  margin-right: 5px;
  background-size: cover;
}

@media (max-width: 575px) {
  .list-star li {
    margin-right: 1px;
  }
}
/* R91: these two were the ONLY relative url()s left in the skin bundle, and the only reason
   bundleconfig.json still had to re-emit resources/styles/_skin.css as the project-root skin.css
   — BuildBundlerMinifier rebases a relative url() when input and output sit in different folders
   ("../../images/star-fill.svg" became "resources/images/star-fill.svg"), and WebCompiler does not
   (relativeUrls:false, so it copies the declaration verbatim). Now root-absolute, exactly like the
   @font-face urls above and the module star sprites in _product.scss, so _skin.scss can compile
   STRAIGHT to the served skin.css. Keep any new url() in this file root-absolute. */
.list-star li.star-fill {
  background-image: url("/Portals/_default/Skins/COLAMCO.DNN.Skins.eCommerce/resources/images/star-fill.svg");
}

.list-star li.star-empty {
  background-image: url("/Portals/_default/Skins/COLAMCO.DNN.Skins.eCommerce/resources/images/star-empty.svg");
}

/* R100 (2026-08-21): `.input-nospin` (both rules) and `.form-control-dropdown` DELETED. Every
   quantity control that needs the spinner gone already suppresses it on its OWN
   `input` / `input[type=number]` - this file's `&__qty .input-group input` block above,
   _minicart.scss and _product.scss - so the shared utility had no takers:
   zero `input-nospin` and zero `form-control-dropdown` tokens across every .cshtml / .ascx / .js /
   .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered page loads. */
/* "Limited Stock" state classes — theme v3 (plan §5-D12): amber has left stock
   states; limited renders NEUTRAL (muted ink + strong-rule border), matching
   the neutral stamp outline. Returned by ProductService.GetProductStockColorClass
   and used by the PDP live-refresh JS. Success / info / danger keep the
   standard Bootstrap utilities.

   POLISH S6b, P3-25 (2026-08-20): all four `!important`s below are KEPT, for two measured reasons.
   (1) These are UTILITIES standing in the same slot as `.text-success` / `.border-success` /
   `.bg-success`, and CDP confirms Bootstrap emits every one of those with !important - product.js
   swaps between them (`colorClasses.forEach(c => $label.removeClass('text-' + c))`), so a
   non-important stand-in would lose the moment the markup ships one of the real ones.
   (2) `.text-stock-limited` (0,1,0) shares its element with `.chip-stamp { color: var(--ink-2) }`,
   also (0,1,0) - and `_components.scss` is imported AFTER `_global.scss`, so `.chip-stamp` wins on
   source order. MEASURED on an injected `.chip-stamp.text-stock-limited`: without !important the
   chip's own colour owns the element, i.e. the STATE would stop carrying its own colour. The two
   values coincide today, so this would fail silently. */
.text-stock-limited {
  color: var(--ink-2, #5f6670) !important;
}

.border-stock-limited {
  border-color: var(--rule-strong, #9aa4ae) !important;
}

.bg-stock-limited {
  background-color: var(--rule-strong, #9aa4ae) !important;
  color: var(--ink, #14181d) !important;
}

/* Theme-aware replacement for Bootstrap's .bg-white (R11 P0). Bootstrap pins
   .bg-white to a literal #fff, so a panel wearing it stayed white in dark mode
   while the ink inside it followed the theme — the MyAccount order detail
   measured 1.19:1 on its own totals block, the whole Order Total row invisible.
   Any panel that just means "primary card surface" must wear .bg-paper instead.
   ~~!important matches the Bootstrap utility it replaces, so it still beats
   .card-header / .card-footer's --bs-card-cap-bg.~~ POLISH S6b, P3-25 (2026-08-20): HALF of that
   was wrong and the !important is kept for the OTHER half. MEASURED with CDP on an injected
   `.card-header.bg-paper`: `.card-header { background-color: var(--bs-card-cap-bg) }` is
   NON-important at (0,1,0), tying this rule, and skin.css loads after bootstrap.css - so source
   order already settles that fight and no !important is needed for it. What DOES need it is the
   utility it replaces: `.bg-white` is (0,1,0) `!important` (measured on an injected
   `.bg-white.bg-paper`), and every `.bg-*` fill in the system is likewise, so a non-important
   .bg-paper would be a utility that any other utility outranks.
   The ONE legitimate use of .bg-white that remains is .product-tilecard, which
   pins BOTH its surface and its ink to constant light values on purpose. */
/* SURFACE SWAP S2 (2026-08-21): this utility stays the literal --paper utility and is NOT
   re-pointed at --surface-card. Its one live markup site is _ProductRelatedPartial's
   .card-header, which is an image well; the "any panel that means primary card surface" line
   above is superseded - a CARD surface is var(--surface-card) now, and a bare Bootstrap .card
   already gets it from $card-bg (_variables.scss). Do not spend .bg-paper on a card. */
.bg-paper {
  background-color: var(--paper, #ffffff) !important;
}

/* R11 (found while re-measuring the P0): the Bootstrap bridge's SEMANTIC colours
   have no dark values either, so .text-success / .text-danger / .text-warning /
   .text-info / .text-primary render their LIGHT literal on dark paper - the PDP
   ships-today countdown measured 3.04:1 and the related-product price 1.91:1.
   Unlike the surface greys, --bs-success/-danger/-warning/-info/-primary CANNOT
   simply be flipped: .bg-success and friends are used in markup too (7 and 17
   sites), and a flipped fill would need its text colour inverted with it. So
   only the TEXT utilities are re-pointed here, at the canonical tokens that do
   flip; every .bg-* fill keeps its constant brand colour and its white ink.
   POLISH S6b, P3-25 (2026-08-20): all five `!important`s KEPT. MEASURED with CDP on a live
   `.text-success` - Bootstrap's own `.text-success { color: rgba(var(--bs-success-rgb), …) }` is
   (0,1,0) AND !important, so this (0,2,0) rule loses to it without one. Nothing about specificity
   can fix that; matching the utility's priority is the fix. */
[data-bs-theme=dark] .text-success {
  color: var(--ok) !important;
}
[data-bs-theme=dark] .text-danger {
  color: var(--err) !important;
}
[data-bs-theme=dark] .text-warning {
  color: var(--warn) !important;
}
[data-bs-theme=dark] .text-info {
  color: var(--steel) !important;
}
[data-bs-theme=dark] .text-primary {
  color: var(--blue-link) !important;
}

/* R13: the constant-white product tile's "View details" button no longer carries
   .btn-outline-success (see .view-details in _components.scss — the blue outline is
   reserved for "Backorder now"). The tile's ground is constant white in both themes,
   so its quiet button must pin constant ink and a constant border too: the themed
   tokens would flip to their on-dark values against a white card. */
/* Session K: these four values were hand-copied hex literals and #cdd5dd was the OLD --input, so
   the tile's button kept a 1.48:1 boundary in BOTH themes after --input moved to the 3:1 tier —
   measured live on the PDP. Being constant across themes is the right call and is kept; being
   constant does not mean being spelled by hand. They interpolate the LIGHT Sass palette, which is
   exactly what "constant against a constant-white card" means, so the next palette move carries
   them along. (Sass variables, not var() — a var() would flip with the theme.) */
.product-tilecard .view-details {
  color: #0f1b26;
  border-color: #798896;
  background-color: #ffffff;
}

.product-tilecard .view-details:hover,
.product-tilecard .view-details:focus-visible {
  color: #ffffff;
  background-color: #00467f;
  border-color: #00467f;
}

/* Refinement A6: the ANCHOR half of the .btn-link decision, and it needs !important.
  _chrome.scss:652 carries a sitewide `a:not(.underline) { text-decoration: unset !important }`,
  so an <a class="btn btn-link"> never had the resting underline in the first place — and no
  non-important hover rule below can give it one either. Without this pair the two shapes of the
  SAME affordance diverge: a <button class="btn btn-link"> underlines on hover and its guest <a>
  twin does not. That is the exact defect _product.scss:718-724 was written for on
  .btn-save-to-list; this generalises it so the next link-styled button does not need its own. */
a.btn-link:hover,
a.btn-link:focus-visible {
  text-decoration: underline !important;
}

a.btn, button.btn, .btn {
  font-weight: 700;
  border-radius: var(--radius-btn, 8px);
  justify-content: center;
  align-items: center;
  font-family: var(--ff-u);
  font-style: normal;
  font-weight: 700;
  font-size: var(--bs-btn-font-size);
  /* R11: this rule was the whole "unflipped link tier" P1. The literal #00467f
     overrode Bootstrap's --bs-link-color (which DOES flip) for every .btn-link
     sitewide — breadcrumbs, the warranty "learn more…" sheet trigger, MyAccount
     toolbar links — so they all measured 1.91:1 on the near-black dark page.
     --blue-link is the token that flips (#00579e light / #8cc0e8 dark); --blue is
     documented "constant across themes" and belongs to brand fields and seals,
     never to text sitting on themed paper. */
  /* Brand variants define the full Bootstrap 5.3 --bs-btn-* state API. Compiled
     Bootstrap's .btn:hover/.btn:focus-visible/.btn:active rules read these custom
     props (specificity 0,2,0 — they beat a plain background-color override), so
     without them a keyboard-focused .btn-primary flashed the $primary navy and
     .btn-blue/.btn-white went transparent. Literal color/background stay for
     legacy anchors that carry the variant class without .btn. */
  /* Purchase CTA (theme v3, Q1): action blue #00467F with WHITE text — press
     #0a5391, green retired. .btn-primary and .btn-success alias the canonical
     .btn-cta (see _components.scss) so existing markup flips sitewide. */
  /* R100 (2026-08-21): `&-lined` (.btn-lined + its ::after rule, :before arrow glyph and
     :hover::after wipe) DELETED. A pre-modernization "underlined link with a sliding rule and an
     arrow" treatment; the live equivalents are `.btn-link` above and `.co-addlink` /
     `.btn-linklike` in _components.scss. Zero `btn-lined` writers across every
     .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered page
     loads - and it needed a nested grep to find at all, because it is authored as `&-lined`
     under `.btn`, so a search for the emitted class name matches nothing in the SCSS. */
  /* Quiet surface button: follows the theme paper so it stays legible in dark
     mode (a constant-white fill goes illegible once the text tokens flip). */
}
a.btn-link, button.btn-link, .btn-link {
  text-transform: none;
  color: var(--blue-link);
  /* Refinement A6 (item 15/17, 2026-08-12): a link-STYLED BUTTON does not carry a
     resting underline. Bootstrap puts `text-decoration: underline` on .btn-link itself
     (bootstrap.css:3319) and that is the real source of it — the components that "own"
     these controls already say `text-decoration: none` and LOSE, because .btn-link and
     .btn-save-to-list are both (0,1,0) and product.css is FileOrder 5 against
     bootstrap.css's 10. Fixing it here, in skin.css (15), is the only place one
     declaration reaches all 47 markup sites — "Save to a list" and "Write a review"
     among them — instead of a per-component patch that loses the same tie again.
     The affordance moves to hover/focus, matching what .btn-save-to-list already did. */
  text-decoration: none;
}
a.btn-link:visited, button.btn-link:visited, .btn-link:visited {
  color: var(--blue-link);
}
a.btn-link:hover, a.btn-link:focus-visible, button.btn-link:hover, button.btn-link:focus-visible, .btn-link:hover, .btn-link:focus-visible {
  text-decoration: underline;
}
a.btn-primary, a.btn-success, button.btn-primary, button.btn-success, .btn-primary, .btn-success {
  --bs-btn-color: var(--cta-ink);
  --bs-btn-bg: var(--cta);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--cta-ink);
  --bs-btn-hover-bg: var(--cta-press);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-color: var(--cta-ink);
  --bs-btn-active-bg: var(--cta-press);
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-color: var(--cta-ink);
  --bs-btn-disabled-bg: var(--cta);
  --bs-btn-disabled-border-color: transparent;
  --bs-btn-focus-shadow-rgb: 0, 70, 127;
  color: var(--cta-ink);
  background-color: var(--cta);
  border: none;
}
a.btn-primary:hover, a.btn-success:hover, button.btn-primary:hover, button.btn-success:hover, .btn-primary:hover, .btn-success:hover {
  background-color: var(--cta-press);
  color: var(--cta-ink);
}
a.btn-primary:visited, a.btn-success:visited, button.btn-primary:visited, button.btn-success:visited, .btn-primary:visited, .btn-success:visited {
  color: var(--cta-ink);
}
a.btn-blue, button.btn-blue, .btn-blue {
  --bs-btn-color: var(--white);
  --bs-btn-bg: var(--cta);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--white);
  --bs-btn-hover-bg: var(--cta);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-color: var(--white);
  --bs-btn-active-bg: var(--navy-1);
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-color: var(--white);
  --bs-btn-disabled-bg: var(--cta);
  --bs-btn-disabled-border-color: transparent;
  --bs-btn-focus-shadow-rgb: 0, 70, 127;
  color: var(--white);
  background-color: var(--cta);
  border: none;
}
a.btn-blue:hover, button.btn-blue:hover, .btn-blue:hover {
  background-color: var(--cta);
  color: var(--white);
  opacity: 0.85;
}
a.btn-blue:visited, button.btn-blue:visited, .btn-blue:visited {
  color: var(--white);
}
a.btn-white, button.btn-white, .btn-white {
  --bs-btn-color: var(--textcolor);
  --bs-btn-bg: var(--paper);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--textcolor);
  --bs-btn-hover-bg: var(--paper);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-color: var(--textcolor);
  /* SURFACE SWAP S2 (2026-08-21): --control-hover. .btn-white sits ON cards, and --paper-2 is
     the card fill now - the pressed state would have been a hole in the card. */
  --bs-btn-active-bg: var(--control-hover);
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-color: var(--textcolor);
  --bs-btn-disabled-bg: var(--paper);
  --bs-btn-disabled-border-color: transparent;
  --bs-btn-focus-shadow-rgb: 95, 102, 112;
  color: var(--textcolor);
  background-color: var(--paper);
  border: none;
  transition: var(--transition);
}
a.btn-white:hover, button.btn-white:hover, .btn-white:hover {
  background-color: var(--paper);
  opacity: 0.85;
  color: var(--textcolor);
}
a.btn-white:visited, button.btn-white:visited, .btn-white:visited {
  color: var(--textcolor);
}

/* In dark mode the quiet paper button needs an edge to read as a button on
   paper-colored cards. */
[data-bs-theme=dark] .btn-white {
  border: 1px solid var(--rule);
}

.view-more-cat, .view-more {
  font-family: var(--ff-u);
  font-style: normal;
  font-weight: 700;
  font-size: var(--bs-btn-font-size);
  line-height: 19px;
  color: var(--blue-link);
  position: relative;
  padding-left: 30px;
  cursor: pointer;
}
.view-more-cat::after, .view-more::after {
  position: relative;
  content: "+";
  left: -97px;
  top: -1px;
  color: var(--blue-link);
  transition-duration: 0.4s;
}
.view-more-cat.open::after, .view-more.open::after {
  content: "-";
}

/* R100 (2026-08-21): `.button-sort` DELETED. Every sort control in the solution is a
   `<select>` now - `form-select selectsort listing-toolbar__sort` on the listings and
   `myaccount-grid__sort` in My Account - so this <button> treatment, still carrying a raw
   `color: black` outside the token system, has no writer: zero tokens across every
   .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered
   page loads. */
/* Unified dialog API (skin.js ShowConfirm/ShowDanger/ShowSuccess/ShowError).
   buttonsStyling:false renders plain .btn elements inside SweetAlert2, so provide
   the action-row spacing SweetAlert2 normally gets from its own .swal2-styled
   margins, and give the white dismiss button an edge on white dialog surfaces
   (SweetAlert2 action rows and Bootstrap modal footers). */
.swal2-actions {
  gap: 0.5rem;
}

/* Session Q (cart critique P2): the white dismiss button's edge below is 1px the filled
   buttons beside it do not carry - .btn-cta / .btn-primary / .btn-danger all set
   `border: none` - so the two buttons of one decision measured 38px and 36px side by
   side (devbeta, backorder gate at 1440). A transparent border of the same width in the
   action row only, so nothing sitewide re-measures: the dialog is the one place these
   variants sit next to a bordered button. FIRST, because .btn-white's own rule below
   ties with this one at 0,2,0 and has to win on source order. */
.swal2-actions .btn,
.modal-footer .btn {
  border: 1px solid transparent;
}

.swal2-actions .btn-white,
.modal-footer .btn-white {
  border: 1px solid var(--grey);
}

/* Session K (2026-08-13): .btn-neutral needs the same re-assertion, for the same reason and at the
   same 0,2,0. The transparent-border rule above out-specifies .btn-neutral's own flat
   `border: 1px solid var(--input)` (0,1,0), and .btn-neutral is a TRANSPARENT button - its border is
   the only thing that makes it read as a button at all (see the note on the rule in
   _components.scss). MEASURED in the write-a-review modal in dark: computed borderTopColor
   rgba(0, 0, 0, 0), 1.23:1 against the modal ground. This was already true of the five My Account
   modals whose Cancel is a .btn-neutral in a .modal-footer; K3 only exposed it by moving the PDP's
   three Cancels off Bootstrap's .btn-outline-dark onto the same control. */
.swal2-actions .btn-neutral,
.modal-footer .btn-neutral {
  border: 1px solid var(--input);
}

/* SESSION 3 ITEM 5 (Juan, 2026-08-16): THE TWO BUTTONS OF ONE DECISION ARE THE SAME HEIGHT,
   EVERYWHERE.

   Session Q already equalised the BORDER (a filled dialog button carries `border: none`, so it
   measured 2px shorter than the .btn-white beside it). What it could not fix is the case that
   remains: the two labels of a decision are never the same length - "No thanks" against "Add this
   plan", "Stay on checkout" against "Place the order anyway" - and as soon as the row is narrow
   enough for the longer one to WRAP, that button is two lines tall and its partner is one. Both
   .modal-footer and .swal2-actions are flex rows at `align-items: center`, which centres two
   different heights instead of reconciling them.

   `align-self: stretch` on the BUTTONS ONLY, not `align-items: stretch` on the row: a
   .modal-footer is allowed to carry things that are not buttons (a note, a spacer), and stretching
   those would be a second change nobody asked for. The inline-flex/centre pair is what keeps the
   short label optically centred inside the taller box instead of sitting on its top line.

   Sitewide by design - the add-warranty sheet and the backorder gate are the two Juan named, but
   every popup with a button pair is the same row with the same two selectors. */
.swal2-actions > .btn,
.modal-footer > .btn {
  align-self: stretch;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* ============================================================================
  Redesign v2 "Procurement-grade" core components (R1, 2026-07-23 — DESIGN.md).
  Buttons (green CTA system), stamped status chips, form fields, and the
  shared micro-interaction keyframes. Later sessions (R2-R5) adopt these
  classes on each surface; nothing here is page-specific.
  ========================================================================== */
/* ---- buttons: micro-interactions (DESIGN.md: press-scale .985, 100-400ms
   exponential ease-out, reduced-motion collapses everything) ---- */
.btn {
  transition: transform 0.12s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease, opacity 0.12s ease;
}
.btn:active {
  transform: scale(0.985);
}

@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: none;
  }
  .btn:active {
    transform: none;
  }
}
/* ---- CTA button: THE purchase-action style (add to cart, checkout, buy).
   Theme v3 (Q1): action blue #00467F with white text — press #0a5391. Green
   is retired. .btn-cta is the canonical hook; .btn-primary and .btn-success
   alias it so every existing purchase CTA site-wide flips with no markup
   change. ---- */
.btn-cta {
  --bs-btn-color: var(--cta-ink);
  --bs-btn-bg: var(--cta);
  --bs-btn-border-color: transparent;
  --bs-btn-hover-color: var(--cta-ink);
  --bs-btn-hover-bg: var(--cta-press);
  --bs-btn-hover-border-color: transparent;
  --bs-btn-active-color: var(--cta-ink);
  --bs-btn-active-bg: var(--cta-press);
  --bs-btn-active-border-color: transparent;
  --bs-btn-disabled-color: var(--cta-ink);
  --bs-btn-disabled-bg: var(--cta);
  --bs-btn-disabled-border-color: transparent;
  --bs-btn-focus-shadow-rgb: 0, 70, 127;
  color: var(--cta-ink);
  background-color: var(--cta);
  border: none;
  font-weight: 700;
}
.btn-cta:hover {
  background-color: var(--cta-press);
  color: var(--cta-ink);
}
.btn-cta:visited {
  color: var(--cta-ink);
}

/* ---- Outline CTA: reserved for "Backorder now" (never the solid buy color —
   CANVAS-SPEC 3b). --cta-outline flips lighter in dark so the border/text
   keep contrast on dark paper. ---- */
.btn-outline-cta {
  --bs-btn-color: var(--cta-outline);
  --bs-btn-bg: var(--paper);
  --bs-btn-border-color: var(--cta-outline);
  --bs-btn-hover-color: var(--cta-outline);
  --bs-btn-hover-bg: var(--blue-tint);
  --bs-btn-hover-border-color: var(--cta-outline);
  --bs-btn-active-color: var(--cta-outline);
  --bs-btn-active-bg: var(--blue-tint);
  --bs-btn-active-border-color: var(--cta-outline);
  --bs-btn-disabled-color: var(--cta-outline);
  --bs-btn-disabled-bg: var(--paper);
  --bs-btn-disabled-border-color: var(--cta-outline);
  --bs-btn-focus-shadow-rgb: 0, 70, 127;
  color: var(--cta-outline);
  background-color: var(--paper);
  border: 1.5px solid var(--cta-outline);
  font-weight: 700;
}
.btn-outline-cta:hover {
  background-color: var(--blue-tint);
  color: var(--cta-outline);
}
.btn-outline-cta:visited {
  color: var(--cta-outline);
}

/* ---- Neutral secondary button (Session E) — a quiet SECOND action beside a primary one, where the
   blue outline is already taken.

   The PDP buy box is exactly this case: "Request a volume quote" sits directly under Add to cart,
   and on a backorder line that primary button IS .btn-outline-cta, so a blue-outline secondary would
   be indistinguishable from the purchase control. This is a paper fill, a neutral rule and body ink —
   visibly a tier below both.

   Not .view-details, which is the neutral tier for NAVIGATION and whose hover deliberately fills
   with the CTA blue. A quote request is a real action, so its hover stays a quiet wash rather than
   promoting itself to look like the buy button.

   The full --bs-btn-* API is declared for the same reason .btn-outline-cta declares it (see
   _button.scss): compiled Bootstrap's .btn:hover / :focus-visible / :active read those custom props
   at 0,2,0 specificity and would otherwise win the tie against a plain :hover override. ---- */
/* Session K: every enabled --rule-strong here is now --input. Measured 1.77:1 light / 1.66:1 dark —
   and .btn-neutral is a transparent button, so its border is the ONLY thing that makes it a button.
   The DISABLED border stays --rule on purpose: a disabled control is exempt from 1.4.11, and the
   quieter edge is how "disabled" reads. */
.btn-neutral {
  --bs-btn-color: var(--ink);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: var(--input);
  --bs-btn-hover-color: var(--ink);
  --bs-btn-hover-bg: var(--paper-2);
  --bs-btn-hover-border-color: var(--input);
  --bs-btn-active-color: var(--ink);
  --bs-btn-active-bg: var(--paper-2);
  --bs-btn-active-border-color: var(--input);
  --bs-btn-disabled-color: var(--ink-2);
  --bs-btn-disabled-bg: transparent;
  --bs-btn-disabled-border-color: var(--rule);
  --bs-btn-focus-shadow-rgb: 0, 70, 127;
  color: var(--ink);
  background-color: transparent;
  border: 1px solid var(--input);
  font-weight: 600;
}
.btn-neutral:hover {
  background-color: var(--control-hover); /* S1: a white .btn-neutral on a --surface-card panel - the old --paper-2 wash IS the panel */
  color: var(--ink);
}
.btn-neutral:visited {
  color: var(--ink);
}

/* ---- "View details" — NAVIGATION, never a commitment (R13).
   §9 reserves the blue outline (.btn-outline-cta / --cta-outline) for "Backorder
   now". "View details" had taken the same treatment via .btn-outline-success, so on
   a backorder PDP the purchase CTA and a nav button were indistinguishable at a
   glance. This is the one quiet tertiary treatment for that label sitewide: paper
   fill, neutral rule, body ink — visibly a third tier below both the solid buy
   button and the outline backorder button.

   The full --bs-btn-* API is declared for the same reason .btn-outline-cta declares
   it (see _button.scss): compiled Bootstrap's .btn:hover / :focus-visible / :active
   read those custom props at 0,2,0 specificity and would otherwise win the tie
   against a plain :hover override on source order. ---- */
.view-details {
  --bs-btn-color: var(--ink);
  --bs-btn-bg: var(--paper);
  --bs-btn-border-color: var(--input);
  --bs-btn-hover-color: var(--cta-ink);
  --bs-btn-hover-bg: var(--cta);
  --bs-btn-hover-border-color: var(--cta);
  --bs-btn-active-color: var(--cta-ink);
  --bs-btn-active-bg: var(--cta-press);
  --bs-btn-active-border-color: var(--cta-press);
  --bs-btn-disabled-color: var(--ink-2);
  --bs-btn-disabled-bg: var(--paper);
  --bs-btn-disabled-border-color: var(--input);
  --bs-btn-focus-shadow-rgb: 0, 70, 127;
  color: var(--ink);
  background-color: var(--paper);
  border: 1px solid var(--input);
  font-weight: 600;
}
.view-details:visited {
  color: var(--ink);
}

/* ---- Buy-type buttons share ONE consistent width (feedback register §9):
   full "Add to cart" label everywhere, solid fill; outline = Backorder now.
   .btn-add-row is the listing hook, .btn-add-to-cart the PDP hook. ---- */
.btn-add-row {
  min-width: 158px;
  justify-content: center;
}

/* ---- Early-click busy state (R7, feedback register §9-G6): applied by the
   inline capture-phase queue in Resources.ascx while a clicked control waits
   for its controller bundle; pointer-events off prevents double-queueing (the
   programmatic replay click bypasses pointer-events). ---- */
.is-waiting-controllers {
  opacity: 0.65;
  pointer-events: none;
  cursor: progress;
}

/* ---- Separator ornament (R14). The sitewide replacement for a literal "&middot;" between two
   pieces of copy. R13 established the treatment for the home brand row and this generalises it:
   a middot written as text is a real text node, so it lands in the accessibility tree (some screen
   readers announce it) and every contrast scanner scores it as body text - R12 measured those dots
   at 1.75-2.26:1 and counted them as failures.

   This element is EMPTY, so there is no character anywhere: nothing to announce, nothing to score.
   It is the element itself rather than a ::before so it can sit between two runs of plain text
   inside a single parent, which is where most of the estate's middots actually live - a ::before
   can only attach to a following ELEMENT, and splitting every such sentence into two spans purely
   to hang a pseudo-element off would be far more markup churn than this.

   Usage: a span.sep-dot with no content, in place of the literal. Do NOT add aria-hidden - an
   empty element with no text content exposes nothing to begin with.

   Colour is currentColor, NOT a token. The first cut used --rule-strong (copying R13's home brand
   row) and it measured 1.99:1 in dark - #3d4954 on #10151b, an invisible separator, which is WORSE
   than the literal middot it replaced because that at least inherited the paragraph's text colour.
   A separator carries meaning (it divides two phrases), so it has to stay perceptible wherever it
   is used - navy hero, white card, dark surface. currentColor tracks the surrounding copy in every
   theme and context for free; the opacity keeps it subordinate to the words either side. ---- */
.sep-dot {
  display: inline-block;
  width: 3px;
  height: 3px;
  margin: 0 0.35rem;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.55;
  vertical-align: 0.22em;
}

/* ---- Add-to-cart "Added" morph state: the clicked CTA flips to deep navy
   confirmation for ~1.6s (app.js MorphAddButton). Deep navy because the CTA
   itself is now the action blue — the morph must remain a visible change in
   both themes (dark: --blue-deep goes near-black against the constant-blue
   CTA fill). ---- */
.btn-atc-added {
  background-color: var(--blue-deep) !important;
  border-color: var(--blue-deep) !important;
  color: var(--white) !important;
}
.btn-atc-added:hover {
  background-color: var(--blue-deep) !important;
  color: var(--white) !important;
}

/* ---- stamped status chips (theme v3 / CANVAS-SPEC stamps): 2px border,
   uppercase 12.5px/700, tabular numerals. In-stock = COLAMCO blue stamp;
   limited = neutral solid outline; backorder / special order = neutral DASHED
   outline ("ships when available"); error = red. Amber has left stock states
   entirely (plan §5-D12). R100 (2026-08-21): the optional -1deg `.chip-stamp--tilt`
   rotation is DELETED - nothing ever took it (see its note below). Stamp-in plays once per
   page load; stagger with inline `--chip-i: n`. ---- */
.chip-stamp {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  padding: 0.3em 0.65em;
  border: 2px solid currentColor;
  border-radius: 4px;
  font-family: var(--ff-u);
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.1;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--ink-2);
  background: transparent;
  white-space: nowrap;
}

.chip-stamp--ok,
.chip-stamp--instock {
  color: var(--stamp-instock);
}

/* limited: neutral solid outline, FULL --ink (never amber).
   SESSION 5 (2026-08-19), critique finding 04: this modifier and .chip-stamp--ship rendered
   byte-identically - MEASURED on the LaptopsNotebooks-342 listing, both computed
   rgb(81, 99, 111) ink on a rgb(185, 196, 206) border at the same width - so a stock WARNING and a
   shipping PERK carried exactly the same weight side by side on the same row. Amber stays retired
   (S3 §5-D12): the separation is RANK INSIDE THE NEUTRAL SYSTEM, not a colour. Limited takes the
   full ink tier and the component's base 2px rule; free shipping keeps the muted ink and drops to
   1.5px below it. */
.chip-stamp--limited,
.chip-stamp--warn {
  color: var(--ink);
  border-color: var(--rule-strong);
}

/* free shipping (Session D, listings): neutral SOLID outline per DECISIONS.md. It is a real
   <button> on listing rows because the offer has terms - it opens the same explainer the PDP and
   cart use - so the stamp needs a pointer and must not inherit a form control's own border. */
.chip-stamp--ship {
  color: var(--ink-2);
  border-color: var(--rule-strong);
  border-style: solid;
  /* Session 5: the quieter of the pair everywhere, not only inside .result-stamps. */
  border-width: 1.5px;
}

button.chip-stamp {
  background: transparent;
  cursor: pointer;
}
button.chip-stamp:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* POLISH S2, P2-08 (2026-08-20): the OPERABLE chips had no hover state at all - measured live with
   a computed-style diff across a real hover, changed = 0, on 14 of them on one PDP and 24+ on a
   listing row. They cannot step their own colour: the base rule is `border: 2px solid currentColor`
   and each state modifier owns that colour to carry meaning, so tinting the ink would repaint a
   stock state. The field behind them is what moves instead - --hover-wash, the shared quiet
   treatment (_variables.scss), which is also what the gallery thumbs and the facet rows take. No
   transition: the wash is a flat 8% step, and an instant one needs no reduced-motion arm. */
button.chip-stamp:hover,
.chip-stamp[role=button]:hover,
.mc-chip[role=button]:hover,
.meta-chip[role=button]:hover {
  background-color: var(--hover-wash);
}

/* 2026-08-17 batch, Session 1 item 6 (Juan): the stock and free-shipping chips that are SPANS
   carrying role="button" - listing rows, product summary bars, the PDP sticky bar, cart lines,
   the mini-cart - are operated by ONE delegated listener in Views/Resources/Index.cshtml, so
   they get the same pointer and focus ring the real <button> chips above already have. Keyed on
   [role="button"], not on the class, so a chip that is deliberately inert (the PDP's OVERSIZED
   stamp, data-info="none") is not given an affordance it does not honour. */
.chip-stamp[role=button],
.mc-chip[role=button],
.meta-chip[role=button] {
  cursor: pointer;
}
.chip-stamp[role=button]:focus-visible,
.mc-chip[role=button]:focus-visible,
.meta-chip[role=button]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* POLISH S5, P2-17 (2026-08-20): WCAG 2.5.8 on every OPERABLE chip. MEASURED at 402 on the PDP:
   23.25px on the buy box's stock and free-shipping stamps, 21.83 on LIMITED STOCK and 19.83 on the
   similar-product rows' stamps - so all of them were sub-24 targets. A chip is inline-flex and its
   face is a 2px stamp outline, so padding or a min-height would grow the STAMP itself, on every
   surface that draws one (listings, cart lines, the mini-cart, the PDP floor bar). hitTarget()
   centres a transparent 24px overlay on it instead: the drawn chip is pixel-identical and the hit
   area clears the minimum. Keyed on the same operable selectors as the cursor and hover rules
   above, so a deliberately inert chip (the PDP's OVERSIZED stamp, data-info="none") is untouched. */
button.chip-stamp,
.chip-stamp[role=button],
.mc-chip[role=button],
.meta-chip[role=button] {
  position: relative;
}
button.chip-stamp::after,
.chip-stamp[role=button]::after,
.mc-chip[role=button]::after,
.meta-chip[role=button]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 24px;
  transform: translateY(-50%);
}

/* backorder / special order: neutral DASHED outline, no count */
.chip-stamp--back {
  color: var(--ink-2);
  border-color: var(--rule-strong);
  border-style: dashed;
  border-width: 1.5px;
}

.chip-stamp--err {
  color: var(--err);
}

/* unavailable: the product is inactive or has no real price, so it cannot be bought at
   all — distinct from --back (dashed = "ships when available", which this never will)
   and from --err (red = a data fault, not a legitimate product state). Muted ink on a
   muted solid rule, plus strikethrough-free but clearly recessive weight: it should read
   as "this exists but is closed", not as an error the shopper caused. Added 2026-07-25
   when SearchService stopped silently deleting these rows from listings. */
.chip-stamp--unavail {
  color: var(--ink-2);
  border-color: var(--rule-strong);
  background: var(--surface-2, transparent);
}

/* NOTE: the price substitute for this state (.result-row__price--none / .price-sale--none) moved
   down into the LISTING ROW section at the foot of this file. It has to be authored AFTER
   .result-row__price, which is now in this same stylesheet at the same specificity (0,1,0) — from
   here it lost the override. */
/* R100 (2026-08-21): `.chip-stamp--tilt` DELETED, together with its stamp-in pairing
   (`.chip-stamp--tilt.chip-stamp--stamp-in`) and the now-orphaned `@keyframes chipStampInTilt`
   below. It was published above as an OPTIONAL "-1deg rotation for hero/receipt moments" and no
   surface ever took it: the variant suffix is computed by
   ProductService.GetProductStockStampSuffix, whose whole range is unavail|err|instock|limited|back,
   and `chip-stamp--tilt` appears in no .cshtml / .ascx / .js / .cs, in dbo.HtmlText on DEV, or in
   24 rendered page loads. The upright stamp-in animation is untouched. */
/* stamp-in entrance: once per load, 60ms stagger via --chip-i */
@keyframes chipStampIn {
  0% {
    opacity: 0;
    transform: scale(1.35);
  }
  100% {
    opacity: 1;
    transform: scale(1);
  }
}
.chip-stamp--stamp-in {
  animation: chipStampIn 0.28s cubic-bezier(0.16, 1, 0.3, 1) both;
  animation-delay: calc(var(--chip-i, 0) * 60ms);
}

@media (prefers-reduced-motion: reduce) {
  .chip-stamp--stamp-in {
    animation: none;
  }
}
/* ---- cart badge bump (DESIGN.md: the badge bumps and increments everywhere
   it appears). skin.js toggles .badge-bump when the count changes. ---- */
@keyframes badgeBump {
  0% {
    transform: scale(1);
  }
  40% {
    transform: scale(1.35);
  }
  100% {
    transform: scale(1);
  }
}
.badge-bump {
  animation: badgeBump 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}

@media (prefers-reduced-motion: reduce) {
  .badge-bump {
    animation: none;
  }
}
/* ---- form fields (Operate surfaces): quiet paper fields, crisp rules,
   focus shifts the border to blue (focus-within pattern for composites).
   Session K: the border was var(--rule-strong) — a decorative tier at 1.77:1, chosen back when it
   happened to be DARKER than the token actually named for this job. --input is the 3:1 boundary
   tier now (see _variables.scss), and a text field's border is the clearest case WCAG 1.4.11 has.
   Do not put --rule-strong back on a control outline. ---- */
.form-control,
.form-select {
  background-color: var(--paper);
  border: 1.5px solid var(--input);
  border-radius: var(--radius-btn);
  color: var(--ink);
  font-family: var(--ff-u);
}
.form-control:focus,
.form-select:focus {
  background-color: var(--paper);
  border-color: var(--focus);
  color: var(--ink);
  box-shadow: none;
}

/* Session K: checkboxes and radios are controls too, and Bootstrap leaves them on
   --bs-border-color — measured 1.30:1 light / 1.26:1 dark on the compare checkbox and 1.77:1 on the
   facet checkboxes. Same 3:1 tier as .form-control above. :checked keeps its own 0,2,0 blue fill, so
   this only moves the UNchecked outline — which is the state where the boundary is the only thing
   telling you the control is there. */
.form-check-input {
  border-color: var(--input);
}

/* Session Q: an .input-group-text addon draws HALF of a control's outline, so it belongs to the
   same 3:1 tier as the .form-control beside it. Bootstrap leaves it on --bs-border-color, which
   measured 1.24:1 light / 1.12:1 dark on the MyAccount search groups (Messages / Orders / Quotes /
   My Lists) while the field's other three edges sat at 3.64 / 3.49 — one control, two boundary
   tiers. _filter.scss already did this for the facet rail; this generalises it. Background and text
   are deliberately left alone — the defect is the boundary, not the fill. */
.input-group-text {
  border-color: var(--input);
}

.form-label {
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--ink);
  margin-bottom: 0.375rem;
}

.input-group:focus-within .form-control,
.input-group:focus-within .form-select {
  border-color: var(--focus);
}

/* ---- reCAPTCHA v3 privacy/terms notice (Kendo-elimination plan §1.5). Google's terms REQUIRE
   this wording wherever the floating badge is suppressed, and .grecaptcha-badge is display:none
   sitewide in _chrome.scss — so this is a compliance element, not a style flourish. Quietest
   small-text tier so it reads as fine print under the submit button without competing with it. ---- */
.recaptcha-notice {
  /* UI 2026-08-29 (Juan): the notice is a plain child of `.ma-form`, which is an
     auto-fit grid - so on any container wide enough for two tracks it landed in
     ONE of them and wrapped at half width while every field above it spanned the
     row, which read as an unbalanced form. Span the whole row wherever it is a
     grid item; the property is inert everywhere else. */
  grid-column: 1/-1;
  margin: 0.75rem 0 0;
  font-family: var(--ff-u);
  font-size: 0.75rem;
  line-height: 1.4;
  text-align: center;
  color: var(--ink-faint);
}
.recaptcha-notice a {
  color: inherit;
  text-decoration: underline;
}
.recaptcha-notice a:hover, .recaptcha-notice a:focus-visible {
  color: var(--ink);
}

/* ---- schedule-style section header (DESIGN.md "index + rule" grammar):
   heavy top rule, uppercase Public Sans label sitting on the rule line,
   Archivo heading below. One grammar sitewide. ---- */
/* R100 (2026-08-21): the bare `.sec-head` block DELETED (`.sec-head`, `.sec-head h2` /
   `.sec-head-title`, `.sec-head-all`). It was the unprefixed original of this grammar and the
   header above claimed "One grammar sitewide", but every consumer wears a PREFIXED variant -
   `.co-sec-head` (cart/checkout), `.home-sec-head` (home), `.ma-sec-head` (My Account),
   `.cp-section-head` (content) - each with its own block in this file or in _content.scss, and
   none of them inherits from `.sec-head` (they are separate class names, not modifiers). Zero
   bare `sec-head` / `sec-head-title` / `sec-head-all` tokens across every .cshtml / .ascx / .js /
   .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered page loads. Also note that this
   block's own font-size predates the S3 type ramp: it states a raw clamp() instead of --fs-*, so
   keeping it would have kept a second, drifting definition of the section-head size. */
/* ---- ASSIST awareness ad block (DESIGN.md): quiet bordered panel, label,
   one line of copy, one text link. Max 88px tall in filter rails. ---- */
.assist-ad {
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  padding: 0.75rem 0.9rem;
  background: var(--surface-card); /* S1 CARD */
  /* BF1 (2026-08-14, Juan): 13px -> 14px, not 15px. It is the same readability problem the two
     PDP shorthands had, but at weight 400 rather than 500, so it takes the middle step. The block
     is capped at 88px in a filter rail - one extra pixel of type, and the copy is one line of
     about 90 characters, so the cap still holds. */
}
.assist-ad .assist-ad-label {
  display: block;
  font: 800 11px var(--ff-d);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--blue-link);
  margin-bottom: 0.25rem;
}
.assist-ad .assist-ad-copy {
  display: block;
  font: 400 14px/1.45 var(--ff-u);
  color: var(--ink-2);
  margin-bottom: 0.3rem;
}
.assist-ad a {
  font: 600 13px var(--ff-u);
  color: var(--blue-link);
}

.newsletter-signup {
  max-width: 32rem;
  margin: 0 auto;
}
.newsletter-signup__row {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  justify-content: center;
}
.newsletter-signup__label {
  flex: 1 1 100%;
  font-family: var(--ff-u);
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--ink);
  text-align: left;
  margin-bottom: 0.1rem;
}
.newsletter-signup input[type=email] {
  box-sizing: border-box;
  flex: 1 1 16rem;
  min-width: 0;
  padding: 0.7rem 0.9rem;
  border: 1.5px solid var(--input);
  border-radius: var(--radius-btn);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--ff-u);
  font-size: 1rem;
}
.newsletter-signup input[type=email]::placeholder {
  color: var(--darker-grey);
}
.newsletter-signup input[type=email]:focus-visible {
  border-color: var(--focus);
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}
.newsletter-signup .newsletter-signup__status {
  margin: 0.9rem 0 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  min-height: 1.35em;
}
.newsletter-signup .newsletter-signup__status.is-error {
  color: var(--err);
}
@media (max-width: 30rem) {
  .newsletter-signup input[type=email],
  .newsletter-signup button {
    flex: 1 1 100%;
    width: 100%;
    justify-content: center;
  }
}

.cp-paymarks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.cp-paymark {
  background: #fff;
  border: 1px solid rgba(6, 43, 76, 0.16);
  border-radius: 5px;
  padding: 3px 5px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
  width: 74px;
}
.cp-paymark svg {
  display: block;
  width: 40px;
  height: auto;
}
.cp-paymark.cp-paymark--wide svg {
  width: 62px;
}

/* ITEM 14 (2026-08-19, Juan): THE FOUR MARKS STAY ON ONE LINE ON A PHONE.
   MEASURED at 402x874 on devbeta before this: the .cs-secure host on /cart's summary card is 318px
   wide and the set wants 4 x 74 + 3 x 8 = 320 - so Discover wrapped to a second line, by TWO pixels.
   The whole set scales together, which is the point: refinement E4's ruling is that every chip is the
   WIDE one's width so the row reads as a set of plates and nothing is singled out, and that survives
   here untouched - 52px of artwork + 4px padding a side + 1px border a side = 62px for all four.
   4 x 62 + 3 x .35rem = 264.8px, which clears 318 with room for a narrower phone.
   Artwork is SCALED, never redrawn or recoloured: these are trademarked marks on a deliberately fixed
   light plate (see .cp-paymark's own note), and R19 already measured and rejected squeezing
   Discover's eight-character wordmark into a narrow box. 52px is the wide chip's own 62 -> 52, i.e.
   ~5% smaller type than the desktop row, not R19's unreadable ~5.8px.
   The "Secure, encrypted checkout" line beside them may still take its own line - that is copy, and
   it wraps rather than clips. */
@media (max-width: 575.98px) {
  .cp-paymarks {
    gap: 0.35rem;
  }
  .cp-paymark {
    width: 62px;
    padding: 2px 4px;
  }
  .cp-paymark svg {
    width: 34px;
  }
  .cp-paymark.cp-paymark--wide svg {
    width: 52px;
  }
}
/* ============================================================================
   ACCOUNT / DOCUMENT VOCABULARY (.ma-*) — REDESIGN Session G, 2026-08-11.

   The shared grammar for every "record" surface: My Account's panes, order and
   quote detail, requisition lists, wishlists, Track Order, RMA and the User
   Impersonator.

   IT LIVES HERE, NOT IN _myaccount.scss, AND THAT IS LOAD-BEARING.
   myaccount.min.css is registered by MyAccount/Index.cshtml and by the Brand
   page's user-manuals partial — and by nothing else. /rma, /track-order and the
   impersonation page register no module stylesheet at all, so anything they
   need has to be in the skin bundle (_skin.scss -> skin.css, priority 15, every
   page). Putting these classes in _myaccount.scss would have left three pages
   rendering unstyled markup, which is exactly the failure it looks like.

   The anatomy is Sessions D–F's, re-used rather than re-invented:
     · .ma-sec-head  = checkout's .co-sec-head minus the step counter
     · .ma-card      = the cart's .cart-card (1px rule, 14px radius, no shadow)
     · .ma-line      = the cart's line CARD with its dashed footer
     · money at display weight is proportional-nums (Session E measured
       Schibsted Grotesk 800's TABULAR period at a full figure advance)
     · 12px copy is --ink-faint; --ink-3 is a LARGE-TEXT-ONLY token
   ========================================================================== */
/* ---- page head: 2px ink rule, mono eyebrow, display title ---------------- */
.ma-head {
  /* B2-8: ink top rule deleted (see .sec-head). */
  padding-top: 0;
  margin: 1.25rem 0 1.75rem;
}

.ma-head__label {
  display: block;
  font-family: var(--ff-m);
  font-size: 12.5px;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin-bottom: 0.35rem;
  font-variant-numeric: tabular-nums;
}

.ma-head__title {
  font-family: var(--ff-d);
  font-weight: 800;
  letter-spacing: var(--display-tracking);
  font-size: clamp(1.625rem, 3.2vw, 2.125rem);
  line-height: 1.06;
  color: var(--ink);
  margin: 0;
}

.ma-head__sub {
  margin: 0.7rem 0 0;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 68ch;
}

/* ---- menu list: the section rail every "pick a page / pick a pane" surface
   draws (Refinement J12, 2026-08-13). ---------------------------------------

   MEASURED FIRST, and the plan's premise was half wrong: there was NO .ma-*
   menu list here to re-use. The treatment existed once, as
   `.myaccount-module .myaccount-nav .nav-link` in _myaccount.scss — a stack of
   outlined pills, the current one a --blue-tint wash inside a --cta-outline
   border. That file compiles to myaccount.min.css, which only MyAccount and the
   Brand page's user-manuals partial register, so a content page could not have
   consumed it by reference even if it wanted to. The vocabulary belongs in the
   skin bundle for the same reason the rest of .ma-* does (see the section note
   above), so the pill is expressed here and the content-page side navs
   (.cp-sidenav, _content.scss) now emit these classes instead of maintaining a
   second, near-identical bordered-list-group implementation of the same thing.

   MyAccount's own rail is deliberately NOT re-pointed at this block in J12: it
   is a Bootstrap `.nav-pills` widget whose every declaration is !important to
   beat Bootstrap, and rewriting that is a MyAccount change with no visible
   result. ~~The two now render identically;~~ collapsing the last duplicate is a
   follow-up, not a prerequisite.

   ⚠ N3 (2026-08-13) MEASURED that "render identically" WRONG. What this block
   carries is MyAccount's phone treatment — the outlined pill strip. From 768 up,
   _myaccount.scss re-cuts the rail as a WHITE PLATE of quiet left-bar rows
   (`@media (min-width: 768px)`, keyed on `.myaccount-nav .nav-link`), and none of
   that reached .ma-menu__link. Measured side by side at 1470: plate vs no
   container, 8px radius rows vs 999px pills, a 3px --cta left bar vs a full
   --cta-outline ring, .1rem vs .4rem gaps. That is the "looks nothing like it"
   Juan reported on /business/vertical-markets. The desktop rail form is now
   .ma-rail below, and BOTH side-nav partials opt into it.

   No `a.` qualifier is needed on the state rules: _global.scss's `a:visited` /
   `a:hover` are (0,1,1) and every selector below is (0,2,0) or higher. */
.ma-menu {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  margin: 0;
  /* DNN's dnndefault default.css leaves a margin-left on a bare <ul>, which is
     what pushed the old .cp-sidenav list-group 18px right of its own title. */
  padding-left: 0;
}

.ma-menu__link {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--ink);
  font-family: var(--ff-u);
  font-size: 0.9375rem;
  font-weight: 600;
  text-decoration: none;
  background: transparent;
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  padding: 0.55rem 0.95rem;
  transition: var(--transition);
}

.ma-menu__link:visited {
  color: var(--ink);
  text-decoration: none;
}

.ma-menu__link i {
  width: 1.1rem;
  text-align: center;
  color: var(--ink-2);
  transition: var(--transition);
}

/* R11: --blue does not flip, so blue-on-dark-panel reads 1.91:1. Text and icons
   on themed paper take --blue-link. */
.ma-menu__link:hover,
.ma-menu__link:focus-visible,
.ma-menu__link:visited:hover {
  border-color: var(--blue-link);
  color: var(--blue-link);
  text-decoration: none;
}

.ma-menu__link:hover i,
.ma-menu__link:focus-visible i {
  color: var(--blue-link);
}

/* A solid blue pill is the site's purchase affordance and is not spent on
   "which page am I on" — the current row is a tint wash instead. */
.ma-menu__link.active,
.ma-menu__link[aria-current=page] {
  background: var(--blue-tint);
  border-color: var(--cta-outline);
  color: var(--ink);
  font-weight: 700;
}

.ma-menu__link.active i,
.ma-menu__link[aria-current=page] i {
  color: var(--cta-outline);
}

/* ---- rail: the same list as a DESKTOP RAIL (Refinement N3, 2026-08-13).
   -------------------------------------------------------------------------
   Every value here is lifted from _myaccount.scss's `@media (min-width: 768px)`
   rail block, not invented: --paper plate, 1px --rule, 14px radius, .5rem
   padding, .1rem row gap, rows with a 3px transparent left bar that turns --cta
   when current, 8px radius squared off on the bar side, --paper-2 on hover. It
   lives HERE because _components.scss ships in skin.css sitewide while
   _myaccount.scss compiles to a module-only myaccount.css — the exact reason the
   pill moved here in J12.

   Below 768 there is deliberately NOTHING: the side navs render a native
   <select> at phone widths (H1) and MyAccount's own strip is the pill row this
   file already defines, so a plate there would be a card around a form control.

   The hovered row keeps a TRANSPARENT left bar, matching MyAccount: the base
   `.ma-menu__link:hover` sets border-color: var(--blue-link), and the
   `border-left` shorthand below is later in the file at equal specificity, so it
   wins and only the label goes blue. Don't "fix" that by dropping the shorthand.
   ---------------------------------------------------------------------- */
@media (min-width: 768px) {
  .ma-rail {
    background: var(--surface-card); /* S1 CARD */
    border: 1px solid var(--rule);
    border-radius: 14px;
    padding: 0.5rem;
  }
  .ma-rail .ma-menu {
    gap: 0.1rem;
  }
  .ma-rail .ma-menu__link {
    width: 100%;
    border: 0;
    border-left: 3px solid transparent;
    border-radius: 8px;
    padding: 0.7rem 0.85rem;
  }
  .ma-rail .ma-menu__link:hover,
  .ma-rail .ma-menu__link:focus-visible {
    background: var(--control-hover); /* S1: .ma-rail is --surface-card now, the old wash equalled it */
  }
  .ma-rail .ma-menu__link.active,
  .ma-rail .ma-menu__link[aria-current=page] {
    border-left-color: var(--cta);
    border-radius: 0 8px 8px 0;
  }
}
/* ============================================================================
   THE PANEL CARD (refinement A3 / D3, 2026-08-12)

   Session A tints the page FIELD to --paper-2 (_variables.scss --bgbody). The moment it did,
   every block that had been relying on the field being --paper stopped reading as a surface and
   started reading as a hole in one. On the PDP that was visible as the two columns rendering in
   different MATERIALS — .product-buybox is a .product-card and the Overview / Tech specs column
   was bare — which is the whole reason D3 exists.

   Values are .product-card's (_product.scss), not a new set: same --paper, same 1px --rule,
   same 14px, same --shadow-1, same 1.5rem. The buy box is what these have to match.

   ⚠ SCOPE IS DELIBERATE, and narrower than "every section with a 2px rule":

     · .pdp-layout__main > .pdp-section — the LEFT COLUMN only (Overview, Technical
       specifications). The full-measure .pdp-sections below the grid (Accessories, Similar
       products, Shipping and returns) are NOT carded: they span both columns, so there is no
       material mismatch to fix, and two of them are grids OF cards — carding those is the
       card-inside-a-card nesting the redesign removed (_cart.scss:2011).
     · .product__ratings-and-reviews — named in D3 with the other two; it is prose, not a card
       grid, so it takes the surface without nesting. ~~Refinement D2 moved it INSIDE
       .pdp-sections (above the similar-products row).~~ **BF2 (2026-08-14) moved it back OUT**,
       into the slot the ASSIST band used to hold directly under the buy grid - the two swapped.
       It is a plain block again, so it owes its own top margin (_product.scss); nothing about the
       plate changes, and .pdp-sections now holds no carded child at all.
     · ~~.ma-sec — the My Account panels D3 names.~~ ~~**BE4 (2026-08-14, Juan's ruling on old
       open item 6): the PROFILE panels only, so the selector is `#userProfile .ma-sec`.**~~
       **BR2 (2026-08-15): NO .ma-sec is carded any more.** BE4 kept the plate on the profile
       pane by decision and left `.ma-card` records nested inside it — the other half of old open
       item 6, which Juan has now ruled on: the objection WAS the nesting, so `#userProfile
       .ma-sec` is off this selector and the profile pane is a bare section on the tint like
       order detail, quote detail, requisition-list detail, the approval history and /rma. The
       records keep their own `.ma-card` plates; the section no longer sits on one. No markup
       changed — the id selector was the whole scope mechanism, so deleting it is the whole fix.
     · ~~CHECKOUT IS NOT HERE.~~ **BD6 (2026-08-14) carded every `.co-sec`** with these exact
       values, in _cart.scss:2247. The two rulings ran in opposite directions in the same
       interview: checkout gained the plate, My Account's document panes lost it.

   The section head keeps its 2px ink rule inside the card. That is the sitewide head grammar
   (.co-sec-head, .ma-sec-head, .cp-section-head all draw it) and a card is not a reason to fork
   it. ---------------------------------------------------------------------- */
/* SESSION 8 (finding 05, 2026-08-19): `.pdp-layout__specs > .pdp-section` joins the list. The
   spec section left `.pdp-layout__main` for a full-width third grid row, and the scope note above
   argues a full-measure section needs no plate because nothing sits beside it to mismatch - true
   of the .pdp-sections group, and NOT true here: this section renders directly under a carded
   Overview, and dropping its plate would have put a bare block under a plate in the same reading
   rhythm. It is a table, not a grid of cards, so nothing nests. */
.pdp-layout__main > .pdp-section,
.pdp-layout__specs > .pdp-section,
.product__ratings-and-reviews {
  background-color: var(--surface-card); /* S1 CARD: the D3 PDP plate (left column + reviews) */
  border: 1px solid var(--rule);
  border-radius: 14px;
  box-shadow: var(--shadow-1);
  padding: 1.5rem;
}

/* ---- section: the SAME 2px-rule + mono eyebrow head checkout uses, minus the
   counter. .co-sec numbers its steps because checkout is a sequence; an account
   pane is not, so the eyebrow is the label alone. -------------------------- */
/* REFINEMENT N4 (2026-08-13): was `.ma-sec + .ma-sec`, same value. Measured on
   /rma at 1470 first: the page's `.ma-card` ends at y=837 and its `.ma-sec` starts
   at y=837 — two white plates sharing one border line — and BOTH the adjacent-
   sibling spellings that were supposed to prevent that missed, because the card
   sits inside a `.ma-split` grid (beside `.ma-aside`) and the section is a sibling
   of the SPLIT, not of the card. That is the third time a plate-rhythm rule keyed
   on `+` has been eaten by a wrapper (H's address grid, H's `.ma-cols`, this), so
   the rule is positional now: a section that is not the first thing in its
   container owes the gap, whatever the thing above it happens to be.
   `:not(:first-child)` with a simple selector is CSS3 — NUglify parses it (a
   `:has()`/`:where()` spelling would fail silently and leave a stale .min.css). */
.ma-sec:not(:first-child) {
  margin-top: 2.25rem;
}

.ma-sec-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.35rem 0.9rem;
  /* B2-8: ink top rule deleted (see .sec-head). */
  padding: 0;
}

.ma-sec-t {
  flex: 1 1 auto;
  min-width: 0;
}
.ma-sec-t > b {
  display: block;
  font-family: var(--ff-m);
  font-size: 12.5px;
  line-height: 1.3;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.ma-sec-t > span {
  display: block;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--ink-2);
  margin-top: 0.35rem;
}

/* A pane's own heading. Checkout's sections need no h-level (the eyebrow IS the
   step name), but an account pane sits under the page h1 and has to be a real
   h2 or the document outline goes h1 -> nothing. NOT written as `.ma-sec-t h2`:
   a tag-qualified rule out-ranks every class the title could carry, which is the
   specificity trap Sessions E and F both paid for. */
.ma-sec-title {
  display: block;
  font-family: var(--ff-d);
  font-weight: 800;
  letter-spacing: var(--display-tracking);
  font-size: clamp(1.375rem, 2.4vw, 1.75rem);
  line-height: 1.1;
  color: var(--ink);
  margin: 0.3rem 0 0;
}

/* The one-line explanation under a pane head. */
.ma-sec-lede {
  margin: 0.7rem 0 1.25rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 68ch;
}

/* The head's trailing action ("Add an address", "Create list"). flex:none so a
   long section label never squeezes the control onto two lines. */
.ma-sec-act {
  flex: none;
  align-self: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

.ma-sec-body {
  padding: 1rem 0 0;
}

/* ---- card: .cart-card's anatomy. NEVER write a bare `.ma-card h2` — that is
   (0,1,1) and out-ranks every class a card title can carry. ---------------- */
.ma-card {
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--surface-card); /* S1 CARD */
  box-shadow: none;
  padding: 1.25rem;
  margin: 0;
}

.ma-card + .ma-card {
  margin-top: 0.75rem;
}

/* see `.ma-sec:not(:first-child)` below for the plate-to-plate rhythm — N4 moved
   it off the adjacent-sibling spelling entirely. */
/* SESSION 4 ITEM 9 (Juan, 2026-08-16): flex-wrap WRAP -> NOWRAP, and the actions take an auto
   left margin.
   `.ma-card__title` already carries `flex: 1 1 auto; min-width: 0` and a .text-truncate span, but a
   flex line wraps on the items' HYPOTHETICAL main sizes - the title's content width - and shrinking
   is only considered afterwards, on the line it ends up on. So min-width: 0 never got its chance:
   measured at 402 on an address card with a long title, the title took the head's full 278px and
   .ma-card__act dropped to a second line at x=61, i.e. hard LEFT, while the payment-method cards
   (short "Visa ending 1111" titles) kept their button at the right edge. That is the difference Juan
   named. With nowrap the title shrinks and ellipsises instead, and the actions stay on the right at
   every title length. */
.ma-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  flex-wrap: nowrap;
  gap: 0.5rem 0.9rem;
  margin-bottom: 0.9rem;
}

.ma-card__title {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--ff-d);
  font-weight: 800;
  font-size: 1.0625rem;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0;
  /* A flex item defaults to `min-width: auto`, so .text-truncate on the span
     never truncates — it sets a min-content floor instead and pushes the action
     buttons off the card. The R46 .tab-content trap, one level down. */
}
.ma-card__title i {
  flex: none;
  color: var(--ink-2);
  font-size: 0.9em;
}
.ma-card__title > span {
  min-width: 0;
}

.ma-card__act {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  /* Session 4 item 9: holds the pair at the right edge even when the title is short enough that
     `justify-content: space-between` has nothing to push against (a one-item line). */
  margin-left: auto;
}

/* ---- fact rows: the label/value shape every detail pane repeats. Mono
   ALL-CAPS key, body-ink value, tabular figures. Replaces the old
   `col-4 text-end <strong>` rows, which right-aligned a ragged column of bold
   labels away from the value each belonged to. ----------------------------- */
.ma-facts {
  display: grid;
  grid-template-columns: minmax(6.5rem, max-content) minmax(0, 1fr);
  gap: 0.45rem 0.9rem;
  margin: 0;
  font-variant-numeric: tabular-nums;
  /* Session I: the .ma-facts__k / __v aliases that used to sit beside dt and dd are gone.
     All four call sites emit a real dl/dt/dd, so the class forms had zero markup sites -
     an escape hatch nothing ever took. */
}
.ma-facts dt {
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1.7;
  margin: 0;
}
.ma-facts dd {
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--ink);
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* A postal address reads as a block, not as a run of comma-separated fields.
   The view emits ONE <span> PER LINE — which is what replaced the four
   hand-unrolled if/else ladders the order and quote views used to print their
   addresses through.

   ⚠ It is one span per line and NOT one newline-joined string under
   `white-space: pre-line`, which is the obvious version and does not work here:
   HTMLService (COLAMCO.Libraries.DNN.Optimization) minifies the rendered HTML
   and COLLAPSES the newline before it reaches the browser. Measured live at
   cdv 730 — `white-space` computed to `pre-line` exactly as authored and the
   text node carried no line break at all, so the whole address rendered as one
   run-on line. Any future "render this preformatted" idea on this site has to
   survive that minifier. */
.ma-addr {
  font-style: normal;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink);
  margin: 0;
  /* SESSION 4 ITEM 9 (Juan, 2026-08-16): the "Also notify" line truncates instead of wrapping.
     An email address is one unbreakable token, so on a phone the label stayed on line one and the
     address dropped whole onto line two (measured at 402: the card is 320px, the block 278px, and
     "Also notify Colamco.Merchant@gmail.com" broke after "notify"), which reads as a ragged
     second entry rather than one fact. Scoped to this line by class rather than applied to every
     `> span`: a street or city line SHOULD wrap - only the email is worth cutting short, and the
     full value is on the element's title attribute (ProfilePartial.cshtml). */
}
.ma-addr > span {
  display: block;
}
.ma-addr > span.ma-addr__notify {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* ---- back link: quiet, arrow-first, RIGHT-ALIGNED -------------------------
   Refinement A8 (item 26, 2026-08-12): back navigation pulls right, away from the page's
   reading edge, with space between it and the viewport.

   `display: flex; width: fit-content; margin-left: auto` rather than a float or
   `text-align: right`: the element is its own flex container (it lays out an arrow beside a
   label), so it has to STAY flex — `inline-flex` in normal flow cannot be pushed by an auto
   margin, and a float would take it out of flow and let the .ma-head below ride up beside it.
   The `margin-right` is the "space from the viewport edge" the brief asks for; the four views
   that use this put it directly inside a .container, so on a narrow screen the container's own
   gutter is all that stands between the arrow and the glass. */
.ma-back {
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 0.4rem;
  margin: 1.25rem 0.25rem 0 auto;
  font-family: var(--ff-m);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  /* UI BATCH 2026-08-26 item 8: `display: flex` above BEATS [hidden]'s UA `display: none`,
     so an emitted-but-hidden .ma-back renders unless something says otherwise. That rule used
     to live scoped inside .cat-aisle (_category.scss) for the drill-up link; it belongs HERE,
     with the display that breaks it, so every caller that gates its back link gets it. The
     reviews page is the second such caller (referrer-gated "Back"). */
}
.ma-back[hidden] {
  display: none;
}
.ma-back:hover, .ma-back:focus-visible {
  color: var(--blue-link);
  text-decoration: underline;
}

/* ---- line CARDS (orders, quotes, list items, wishlists): a details block, a
   DASHED rule, then the figures. The cart's anatomy. ----------------------- */
.ma-lines {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
}

/* 2026-08-21 (Juan): the divider between order/quote detail lines matches the category
   results page - --rule-strong in LIGHT, --rule in dark (the .result-row pair). Here the
   separation IS the card border, so the whole border takes it. */
.ma-line {
  border: 1px solid var(--rule-strong);
  border-radius: 14px;
  background: var(--surface-card); /* S1 CARD: order/quote lines sit on a bare .ma-sec, i.e. on the page field */
  padding: 1.125rem;
  min-width: 0;
}

/* A bundled/child line is subordinate to the one above it, so it recedes rather
   than repeating the full card weight. */
.ma-line--child {
  background: var(--surface-plate); /* S1: parent .ma-line is --surface-card now, so a child line takes the plate */
  margin-left: 1.5rem;
}

[data-bs-theme=dark] .ma-line {
  border-color: var(--rule);
}

.ma-line__top {
  display: grid;
  grid-template-columns: 96px minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
  min-width: 0;
  /* A quote line has no image; the plate column collapses rather than leaving a
     96px hole down the left of every row. */
}
.ma-line__top.ma-line__top--noplate {
  grid-template-columns: minmax(0, 1fr);
}

.ma-line__plate {
  width: 96px;
  height: 96px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--ground);
  border-radius: 8px;
  overflow: hidden;
}
.ma-line__plate img {
  max-width: 84px;
  max-height: 84px;
  width: auto;
  height: auto;
  object-fit: contain;
}

.ma-line__name {
  font-size: 0.90625rem;
  line-height: 1.45;
  font-weight: 600;
  color: var(--ink);
  text-decoration: none;
  /* The full manufacturer string, clamped by LINES rather than truncated by the
     server — the rule Session E applied to the PDP title. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

a.ma-line__name:hover,
a.ma-line__name:focus-visible {
  color: var(--blue-link);
  text-decoration: underline;
}

/* --ink-faint, NOT --ink-3: --ink-3 is large-text only by its own definition and
   fails AA at 12px (SESSION-F §3e). */
.ma-line__part {
  margin-top: 0.25rem;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-faint);
  font-variant-numeric: tabular-nums;
}

.ma-line__note {
  margin-top: 0.35rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  color: var(--ink-2);
}

.ma-line__foot {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
  border-top: 1px dashed var(--rule);
  margin-top: 0.9rem;
  padding-top: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.ma-line__fig {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-size: 0.8125rem;
  color: var(--ink-2);
}
.ma-line__fig b {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--ink);
}

.ma-line__act {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

/* Serial / tracking / warranty riders under a line. They used to be
   white-on-blue .bg-primary bands — three saturated slabs stacked in one card. */
.ma-line__rider {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.5rem;
  margin-top: 0.6rem;
  padding-top: 0.6rem;
  border-top: 1px solid var(--rule-2);
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.ma-line__rider > b {
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1.85;
}
.ma-line__rider a {
  color: var(--blue-link);
}

@media (max-width: 575.98px) {
  .ma-line__top {
    grid-template-columns: 72px minmax(0, 1fr);
    gap: 0.75rem;
  }
  .ma-line__plate {
    width: 72px;
    height: 72px;
  }
  .ma-line__plate img {
    max-width: 64px;
    max-height: 64px;
  }
  .ma-line__act {
    margin-left: 0;
    width: 100%;
  }
  .ma-line--child {
    margin-left: 0;
  }
}
/* ---- summary ledger ------------------------------------------------------ */
.ma-sum {
  font-variant-numeric: tabular-nums;
}

.ma-sum__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.3rem 0;
  font-size: 0.875rem;
  color: var(--ink-2);
}
.ma-sum__row > span:last-child {
  color: var(--ink);
  white-space: nowrap;
}

.ma-sum__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  border-top: 1px solid var(--rule);
  margin-top: 0.6rem;
  padding-top: 0.8rem;
  /* Display-weight money is PROPORTIONAL on this face — Session E measured
     Schibsted Grotesk 800's tabular period at a full figure advance. */
}
.ma-sum__total > span:first-child {
  font-family: var(--ff-d);
  font-weight: 800;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  color: var(--ink);
}
.ma-sum__total > span:last-child {
  font-family: var(--ff-d);
  font-weight: 800;
  font-size: 1.375rem;
  line-height: 1;
  letter-spacing: -0.01em;
  color: var(--ink);
  white-space: nowrap;
  font-variant-numeric: proportional-nums;
}

/* The totals card never needs the full content width: the ledger is four short
   rows and a total, and stretched to 1140px the label and the figure end up at
   opposite ends of the screen. */
.ma-sumcard {
  max-width: 26rem;
  margin-left: auto;
}

/* ---- help / fine-print panel. Replaces .bg-light-grey, which R11 measured at
   1.06:1 in dark on this very block. --------------------------------------- */
.ma-help {
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--paper-2);
  padding: 1.25rem;
  margin-top: 1.5rem;
}
.ma-help p {
  margin: 0 0 0.6rem;
  font-size: 0.9375rem;
  line-height: 1.55;
  color: var(--ink-2);
}
.ma-help a {
  color: var(--blue-link);
}

.ma-help__title {
  font-family: var(--ff-d);
  font-weight: 800;
  font-size: 1.0625rem;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 0.6rem;
}

.ma-help__chain {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  margin: 0.25rem 0 1rem;
}

.ma-help__ch {
  min-width: 0;
}
.ma-help__ch > b {
  display: block;
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 0.15rem;
}

.ma-fineprint {
  margin: 0;
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-faint);
}

/* ---- empty state --------------------------------------------------------- */
.ma-empty {
  padding: 3rem 1.25rem;
  text-align: center;
  color: var(--ink-2);
}

/* R100 (2026-08-21): `.ma-empty__title` DELETED. `.ma-empty` above IS written - by
   RequisitionListDetailPartial.cshtml and myaccount.js, both of which render it as a single
   `<td colspan="6" class="ma-empty">No items on this list yet.</td>` with plain text and NO title
   element inside. So the element half of the pair shipped and the title half never did: zero
   `ma-empty__title` tokens across every .cshtml / .ascx / .js / .cs in all three projects,
   dbo.HtmlText on DEV, or 24 rendered page loads. */
/* ---- mono metadata strip (order #, date, status …) ----------------------- */
.ma-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin: 0.9rem 0 0;
  font-variant-numeric: tabular-nums;
}

.ma-meta__i {
  min-width: 0;
}
.ma-meta__i > b {
  display: block;
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
  line-height: 1.6;
}
.ma-meta__i > span {
  display: block;
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--ink);
}

/* Status reads as a chip in the redesign's savings/progress pair, not in a
   semantic colour: an order status is a state, not a warning. */
.ma-stamp {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: 999px;
  background: var(--pill-bg);
  box-shadow: inset 0 0 0 1px var(--pill-border); /* S1 surface swap: white pill needs an edge */
  color: var(--pill-ink);
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.7;
  white-space: nowrap;
}

/* ---- a two/three-up card row without reaching for Bootstrap's grid --------

   H9 (Session H, 2026-08-13): every card in the row is the same size. The WIDTH
   was already settled by the equal `1fr` tracks; the HEIGHT was not, because
   `align-items: start` sizes each card to its own content — so three saved
   addresses of two, four and five lines drew three different boxes and the row
   read as ragged rather than as a set.

   `grid-auto-rows: 1fr` is what makes the ROWS equal (a two-line card in row 2
   still matches a five-line card in row 1); `align-items: stretch` is what lets
   each card fill the row it is in. Both are needed — either one alone leaves the
   other half of the problem. Nothing measures a height in script. */
.ma-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  grid-auto-rows: 1fr;
  gap: 0.75rem;
  align-items: stretch;
  /* The grid draws its own gutter, so the stacked-cards margin has no business
     inside it — and it did real damage rather than just adding space. MEASURED
     at 1440 while shipping H9: the profile's four saved cards came out 169 /
     157 / 157 / 157 px because every card after the first carried
     `.ma-card + .ma-card`'s 12px top margin, which offsets the cell's content
     box and takes those 12px straight back out of the stretched height. The
     address grid above it was unaffected only by accident — each address card
     is followed by its own edit-modal partial, so the adjacent-sibling selector
     never matched there. */
}
.ma-cols > .ma-card + .ma-card {
  margin-top: 0;
}

/* ---- forms: Track Order, RMA, the impersonator search, "add an item" ------
   Each of those pages used to be TWO hand-maintained forks (d-block d-md-none /
   d-none d-md-block) with duplicated ids, duplicated validation and duplicated
   submit handlers. One responsive form each now, laid out here. ------------- */
.ma-form {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1rem 1.25rem;
  align-items: start;
}
.ma-form label,
.ma-form .form-label {
  display: block;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 0.3rem;
}
.ma-form .req,
.ma-form .text-danger {
  color: var(--err);
}

.ma-form__f--full {
  grid-column: 1/-1;
}

.ma-form__legend {
  margin: 0 0 1rem;
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.ma-form__act {
  grid-column: 1/-1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.25rem;
}

/* A two-column "form beside an assurance column", which Track Order and RMA both
   draw. Collapses to one column below lg. */
.ma-split {
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);
  gap: 2rem;
  align-items: start;
}

@media (max-width: 991.98px) {
  .ma-split {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
  }
}
.ma-aside {
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: var(--paper-2);
  padding: 1.5rem;
  /* The returns badge is a stamp, not an illustration — it must not run the
     full column width the Track Order artwork wants. */
}
.ma-aside img {
  max-width: 100%;
  height: auto;
  margin-bottom: 1rem;
}
.ma-aside .ma-aside__badge {
  max-width: 120px;
}
.ma-aside .ma-aside__art {
  display: block;
  width: 100%;
  aspect-ratio: 16/9;
  height: auto;
  object-fit: cover;
  border-radius: var(--radius-md);
}
.ma-aside p {
  margin: 0 0 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--ink-2);
}
.ma-aside p:last-child {
  margin-bottom: 0;
}

/* ---- FAQ: a run of Q/A pairs. RMA's was 30 loose <p> tags inside empty
   Bootstrap .row wrappers, question and answer indistinguishable. ---------- */
.ma-faq {
  display: flex;
  flex-direction: column;
}

.ma-faq__q {
  border-top: 1px solid var(--rule);
  padding: 1.1rem 0 0;
  margin: 0;
  font-family: var(--ff-d);
  font-weight: 800;
  font-size: 1rem;
  letter-spacing: -0.01em;
  color: var(--ink);
}

.ma-faq__a {
  margin: 0.5rem 0 1.1rem;
  font-size: 0.9375rem;
  line-height: 1.65;
  color: var(--ink-2);
}
.ma-faq__a p {
  margin: 0 0 0.6rem;
}
.ma-faq__a p:last-child {
  margin-bottom: 0;
}

/* ---- ruled-row table for surfaces outside the .myaccount-grid engine ------ */
.ma-table {
  width: 100%;
  margin: 0;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.ma-table thead th {
  border-bottom: 2px solid var(--ink);
  white-space: nowrap;
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--ink-2);
  padding: 0.75rem 0.6rem 0.6rem;
}
.ma-table tbody td {
  border-bottom: 1px solid var(--rule);
  padding: 0.8rem 0.6rem;
  vertical-align: middle;
  font-size: 0.9375rem;
}
.ma-table tbody tr:last-child td {
  border-bottom: 0;
}

.ma-scroll {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: 14px;
}

/* ============================================================================
   .cp-sheet — the three shared CONTENT modals  (Session H3, 2026-08-11)

   Views/Shared/Content/_{FreeShipping,TaxExempt,WarrantyInfo}Partial.cshtml:
   the shipping, tax-exemption and extended-warranty policy sheets, raised from
   the cart line, the cart warranty selector, the PDP warranty card and the
   shared product warranty selector.

   THEY LIVE HERE, NOT IN _content.scss, AND THAT IS THE WHOLE POINT.  They are
   content surfaces, but they are raised from /cart and the PDP, and content.css
   does not load on either (measured in R19 — the same reason .cp-paymark is in
   this file).  A .cp-sheet rule in _content.scss would style the modal on a
   content page and nowhere it is actually opened.

   Drawn to match .cps-sheet in _cart.scss, which is the redesign's existing
   modal vocabulary, rather than inventing a second one.
   ========================================================================== */
.cp-sheet {
  /* Replaces `.row.w-100.align-items-center` + `.col-*`.  The close button is
     a SIBLING of this row, not a third column of a two-column grid, which is
     what it used to be. */
  /* The mark beside the title.  S3 item 15 (2026-08-21, D7): ~~raster art on its own
     ground, so the cap is here and the intrinsic dimensions are in the markup~~ — both
     files are gone (callout_freeshipping.png, special-order-icon3337.png) and both marks
     are now inline stroke SVG on the same 24x24 ico-use grid the content pages draw on.
     `.cp-sheet__mark--sm` went with them: an SVG states its own box in the markup, so
     there is nothing left for a size modifier to cap.

     THE STROKE PROPS ARE STATED HERE, NOT INHERITED. `svg.ico-use` is declared in
     _content.scss and _home.scss, and neither sheet is on a content or home page —
     content.css does not load on /cart, which is the same reason .cp-sheet lives in this
     file rather than that one.  currentColor also means one drawing works in both themes;
     the old PNGs baked their ground and only read on --paper by accident. */
  /* The CPS logo beside its highlights, same shape as .cps-sheet__intro. */
  /* The warranty FAQ.  .cp-faq is declared in _content.scss and content.css
     does not load where this modal opens, so the accordion is restated here.
     Deliberately NOT an @extend or a shared partial: the two differ (this one
     hangs its questions off h4, under the sheet's own h2), and a partial
     imported into two entry files is the R46 double-touch trap. */
}
.cp-sheet .modal-content {
  border: 1px solid var(--rule);
  border-radius: 12px;
  background: var(--paper);
}
.cp-sheet .modal-header {
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  align-items: center;
  gap: 1rem;
}
.cp-sheet .cp-sheet__head {
  display: flex;
  align-items: center;
  gap: 1rem;
  min-width: 0;
}
.cp-sheet .cp-sheet__title {
  font-family: var(--ff-d);
  font-size: 1.1rem;
  font-weight: 800;
  letter-spacing: var(--display-tracking);
  line-height: 1.25;
  color: var(--ink);
  margin: 0;
}
.cp-sheet svg.cp-sheet__mark {
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke);
  stroke-linecap: round;
  stroke-linejoin: round;
}
.cp-sheet .modal-body p,
.cp-sheet .modal-body li {
  font-size: 0.9375rem;
  line-height: 1.7;
  color: var(--ink-2);
  max-width: 80ch;
}
.cp-sheet .modal-body p {
  margin: 0 0 0.8rem;
}
.cp-sheet .modal-body > :last-child {
  margin-bottom: 0;
}
.cp-sheet .modal-body strong {
  color: var(--ink);
}
.cp-sheet .cp-sheet__intro {
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  padding-bottom: 1rem;
  margin-bottom: 1rem;
  border-bottom: 1px solid var(--rule);
}
.cp-sheet .cp-sheet__logo {
  flex: none;
  width: 120px;
  height: auto;
  border-radius: var(--radius-sm);
}
.cp-sheet .cp-sheet__lead {
  font-family: var(--ff-d);
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 0 0 0.45rem;
}
.cp-sheet .cp-sheet__list {
  margin: 0;
  padding-left: 1.15rem;
}
.cp-sheet .cp-sheet__list li {
  margin-bottom: 0.25rem;
}
.cp-sheet .cp-sheet__list li:last-child {
  margin-bottom: 0;
}
.cp-sheet .cp-sheet__subhead {
  font-family: var(--ff-d);
  font-size: 1rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--ink);
  margin: 1.4rem 0 0.2rem;
}
.cp-sheet .cp-faq {
  display: flex;
  flex-direction: column;
}
.cp-sheet .cp-faq__item {
  border-top: 1px solid var(--rule);
}
.cp-sheet .cp-faq__item:last-child {
  border-bottom: 1px solid var(--rule);
}
.cp-sheet .cp-faq__item > summary {
  cursor: pointer;
  padding: 0.9rem 2.5rem 0.9rem 0;
  position: relative;
  color: var(--ink);
  list-style: none;
}
.cp-sheet .cp-faq__item > summary::-webkit-details-marker {
  display: none;
}
.cp-sheet .cp-faq__item > summary h4 {
  display: inline;
  margin: 0;
  font-family: var(--ff-d);
  font-size: 0.95rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.45;
  color: var(--ink);
}
.cp-sheet .cp-faq__item > summary::after {
  content: "";
  position: absolute;
  right: 0.35rem;
  top: 1.1rem;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--ink-2);
  border-bottom: 2px solid var(--ink-2);
  transform: rotate(45deg);
  transition: transform 0.18s ease;
}
.cp-sheet .cp-faq__item > summary:hover h4 {
  color: var(--blue-link);
}
.cp-sheet .cp-faq__item > summary:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: -2px;
}
.cp-sheet .cp-faq__item[open] > summary::after {
  transform: rotate(-135deg);
}
.cp-sheet .cp-faq__item .cp-faq__body {
  padding: 0 2.5rem 1rem 0;
}
.cp-sheet .cp-faq__item .cp-faq__body ul {
  margin: 0 0 0.8rem;
  padding-left: 1.15rem;
}
.cp-sheet .cp-faq__item .cp-faq__body > :last-child {
  margin-bottom: 0;
}
.cp-sheet .modal-footer {
  border-top: 1px solid var(--rule);
}

@media (prefers-reduced-motion: reduce) {
  .cp-sheet .cp-faq__item > summary::after {
    transition: none;
  }
}
@media (max-width: 575.98px) {
  .cp-sheet .cp-sheet__intro {
    flex-direction: column;
  }
}
/* ============================================================================
   LISTING ROW — the shared .result-row / .result-avail / .result-stamps vocabulary
   (Session J, 2026-08-11; authored by Session D in _category.scss).

   WHY IT LIVES HERE. Session D built one responsive row to replace the old
   desktop-row / phone-card split, but authored it inside `.results-schedule` in
   _category.scss - which is its own compilerconfig entry, is NOT imported by
   _skin.scss, and therefore reaches search / category / brand ONLY. The PDP
   Accessories list (Views/Shared/Product/_ProductSummaryPartial.cshtml, the
   ListLayout.Bars branch) is the same object on a page where category.css never
   loads, so it kept the pre-Session-D split for a session longer. Duplicating
   ~250 lines into product.css was the alternative; one copy in the skin bundle
   is the reason this vocabulary exists at all.

   TWO CONSEQUENCES OF THE MOVE, both deliberate:

   1. The selectors are DE-SCOPED - `.result-row__title`, not
      `.results-schedule .result-row__title`. Nesting them under a listing
      container would have meant stamping a search-results class onto a PDP
      section. The cost is that every selector here drops from 0,2,0 to 0,1,0,
      so every override elsewhere was enumerated rather than assumed:
        · `.results-grid__price .result-row__price` (_category.scss, 0,2,0) still
          wins its 21px - specificity, not sheet order.
        · `.buybox-avail .result-avail__qty` (_product.scss) and
          `.cl-avail .result-avail__qty` (_cart.scss) were both 0,2,0 and both
          restated what they inherit. DELETED in Session K, 2026-08-11 - the
          dead-CSS pass Session J §7.2 said this needed. The cart one was a
          subset, so dropping it also hands the count the tabular figures the
          base applies.
        · `.result-row__details` / `__add` now tie with `.btn-cta` /
          `.btn-outline-cta` / `.view-details` at 0,1,0 - and win, because
          _skin.scss imports _button.scss BEFORE _components.scss. Do not
          reorder that import.
        · `.result-row__price--none` had to follow `.result-row__price`; it did
          not before, when the base lived in a lower-priority sheet. See below.

   2. Precedence rises from category.css (5) to skin.css (15) on Session D's own
      surfaces. Nothing in _category.scss / _filter.scss / _compare.scss targets
      these classes at equal specificity, so nothing changed there - the two
      listing-ONLY rules that DID depend on winning (the -8px hover bleed and
      "no add-to-cart on a phone-width listing") stay in _category.scss at
      0,2,0, where specificity carries them regardless of sheet priority.
   ========================================================================== */
/* Listing stamps take the design system's label face (--ff-m — which is IBM Plex SANS as of
   refinement A5, 2026-08-12; the token name is historical, see _variables.scss). Scoped to the
   stamp ROW rather than to the two listing containers it used to name
   (.results-schedule / .results-grid): .result-stamps is emitted only by
   ResultsPartial's StampRow helper and by the product summary row, so this is
   the same set of elements plus the PDP accessory row - and it still leaves
   .chip-stamp's own face alone on the PDP buy box, the cart and receipts, which
   was the reason for the original scoping. */
.result-stamps .chip-stamp {
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: 0.3em 0.6em;
  border-width: 1.5px;
}

/* SESSION 5, finding 04: the row above puts EVERY listing stamp on 1.5px, so the limited-stock
   modifier's 2px rule would never reach a listing card - the one surface the finding was raised on.
   Same 0,2,0 specificity, later in source, so it wins; the ink tier comes from the modifier itself. */
.result-stamps .chip-stamp--limited,
.result-stamps .chip-stamp--warn {
  border-width: 2px;
}

.result-stamps {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 2px 0 0;
}

/* Availability: "1,483 available · checked 2:31 PM · Check again". */
.result-avail {
  margin: 0;
  color: var(--ink-2);
  font-family: var(--ff-u);
  font-size: 13px;
  line-height: 1.5;
}

.result-avail__qty {
  color: var(--ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* `.result-avail__recheck` deleted 2026-08-12 (REFINEMENT C3, item 37): the listing rows' "Check
   again" control is gone, so the rule had no element left to style. The PDP's `.refresh-inventory`
   (_product.scss) is the surviving control and carries the same A6 reasoning - no resting underline
   on a standalone link-styled button, colour + weight carry it, underline moves to hover. */
.result-avail--note {
  color: var(--ink-2);
}

/* Price stack. Struck was-price, display price, SAVE pill. The pill is steel,
   never green - savings and success are steel blue everywhere (DECISIONS.md). */
.result-row__prices {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
}

/* --ink-faint, NOT --ink-3. The mockup's struck price is its `ink3` tier, but this token's own
   definition in _variables.scss says "large/struck-price text ONLY (3.07:1 on white)" - and axe
   measured exactly that 3.07 light / 4.17 dark on this 12.5px run, 2 nodes at every width.
   --ink-faint is the AA-safe small-text neutral and is still quieter than --ink-2. */
.result-row__was {
  color: var(--ink-faint);
  text-decoration: line-through;
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
}

/* ⚠ proportional-nums, and it is NOT a departure from "tabular numerals on all prices" being
   applied carelessly - it is the one place that rule cannot hold.

   The display price is the only price set in --ff-d (Schibsted Grotesk 800), and in THAT face the
   tabular figure set gives the DECIMAL POINT a full figure advance. Measured 2026-08-10 on
   /category/DockingStations-262: the same "$454.43" is 89px tabular vs 83px proportional, and the
   6px all lands either side of the period, so every price on the page read "$454 . 43". The
   parent .result-row__buy carries the .num utility, which is where the tabular-nums is inherited
   from, so this has to be stated explicitly to win.

   Tabular figures stay everywhere they actually buy something and do no harm: the struck price,
   the SAVE pill, the availability count, the part numbers and the caption row are all --ff-u
   (IBM Plex Sans), whose tabular period is normal width. Column alignment is not lost here either
   - this column is right-aligned, so the prices still line up on their right edge. */
.result-row__price {
  font: 800 26px/1 var(--ff-d);
  letter-spacing: var(--display-tracking);
  color: var(--ink);
  font-variant-numeric: proportional-nums;
}

/* ⚠ ORDER IS LOAD-BEARING: this must stay AFTER .result-row__price. Both are 0,1,0 and both are
   now in skin.css, so source order is the only thing that decides - and this rule has to win.
   Until Session J it sat 300 lines up beside the .chip-stamp modifiers and won on sheet priority
   (skin 15 over category 5) instead.

   Price substitute for an unbuyable product. Never render a BAD_PRICE sentinel (999999999) as
   currency — "Price on request" is the honest read for a quote-only item. Keeps the display
   price's slot but not its weight, so the row/card rhythm does not shift. */
.result-row__price--none,
.price-sale--none {
  color: var(--ink-2);
  font-weight: 600;
  white-space: nowrap;
}

/* Session E: --pill-bg / --pill-ink, not --steel-tint / --ok. Session D shipped this pill on
   --steel-tint and recorded the consequence as owed: in dark theme --steel-tint (#14222f) sits a
   hairline from --paper (#101d2c), so the fill was invisible and the pill read as plain text. It
   passes contrast (the ink is fine) so axe stayed silent. The new pair means "savings pill" and
   carries Harbor's own light and dark values; the PDP's .discount-pill spends the same tokens. */
.result-row__save {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  background: var(--pill-bg);
  box-shadow: inset 0 0 0 1px var(--pill-border); /* S1 surface swap: white pill needs an edge */
  color: var(--pill-ink);
  font: 700 11.5px var(--ff-u);
  letter-spacing: 0.04em;
  padding: 0.2rem 0.6rem;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* --- The row itself: 120px item / description + availability / 240px unit price ---
   .results-caps is authored here rather than left behind in _category.scss for one reason: it
   carries the SAME grid-template-columns as .result-row at all three widths, and a caption row
   that drifts off its own columns is the defect. It only ever renders inside .results-schedule. */
.results-caps {
  display: grid;
  grid-template-columns: 120px 1fr 240px;
  gap: 24px;
  padding: 10px 2px;
  border-bottom: 1px solid var(--rule);
  font: 600 10.5px/1 var(--ff-m);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* --ink-faint, not the mockup's ink3: at 10.5px this is small text, and --ink-3 measured
     3.07:1 light / 4.17:1 dark here (3 axe nodes at every width from 820 up). */
  color: var(--ink-faint);
}
.results-caps span:last-child {
  text-align: right;
}

/* margin: 0, NOT the -8px bleed Session D authored. The bleed is what lets the hover tint run to
   the edges of the results column, and it is correct THERE - but this row also renders inside
   .pdp-section__body, which has no padding of its own, so -8px each side would push
   body.scrollWidth past the viewport. .results-schedule .result-row restores it at 0,2,0. */
.result-row {
  display: grid;
  grid-template-columns: 120px 1fr 240px;
  gap: 24px;
  padding: 22px 8px;
  margin: 0;
  /* SESSION 4 ITEM 3 (Juan, 2026-08-16): --rule-strong in LIGHT, --rule in dark.
     Measured at 1440 light: the splitter was #dfe6ec (--rule) drawn on a transparent row
     over a #e9eff5 page — 1.06:1, i.e. all but invisible. --rule-strong is #b9c4ce, the
     system's visible-border tier, and it is a border rather than ink, so it separates the
     rows without reading as a dark line. Dark is untouched: there --rule is #1e3040 on a
     #0b1420 page and already carries; --rule-strong (#5c7488) would be the harsh one. */
  border-bottom: 1px solid var(--rule-strong);
  align-items: start;
}
.result-row:hover {
  background: color-mix(in srgb, var(--blue-tint) 45%, transparent);
}

[data-bs-theme=dark] .result-row {
  border-bottom-color: var(--rule);
}

/* Item column: the fixed image tile with the compare toggle stacked UNDER it (2026-08-28, Juan).
   ROW LAYOUT ONLY - the grid card keeps its compare label where it was. The wrapper exists because
   the compare control cannot go inside .result-row__media (an <a>), and because a fourth direct
   child of .result-row would land in the NEXT grid row's first track rather than under the image.
   Centred, and wrapping: the label is ~55px beside a 44px switch (106px), which fits the 120px
   desktop track but not the 96px tablet / 92px phone ones, so it drops the label to its own line
   there instead of overflowing the track.
   The PDP accessory row emits no compare control and no wrapper, so it is unaffected. */
.result-row__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* 8px -> 16px (UAT feedback 2026-08-30, see .result-row__compare): 8px of clear space between a
     92x92 thumbnail LINK and a 26px switch is inside Safari's touch-target expansion radius, so the
     link won taps meant for the switch. With the label's own 7px padding this puts 23px between the
     link's bottom edge and the switch's top edge. */
  gap: 16px;
}
.result-row__item .result-row__compare {
  flex-wrap: wrap;
  justify-content: center;
  gap: 4px 7px;
  text-align: center;
}

/* Item column: fixed image tile */
.result-row__media {
  display: block;
  width: 120px;
  height: 120px;
}

.result-row__img {
  width: 120px;
  height: 120px;
  object-fit: contain;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 6px;
}

/* K1 (Refinement Session K, 2026-08-13, Juan) — the row-display thumbnail plate is a FIXED light
   plate and deliberately does not flip with the theme, exactly like .cp-paymark above and the PDP
   gallery frame in _product.scss. Almost every product thumbnail in this catalog has a white
   background baked into the image, so a --paper plate (#101d2c in dark) puts a hard white rectangle
   inside a navy frame at every one of the 10 rows a listing paints. The artwork is specified against
   white; the plate is the part that stays put, and the image is never inverted or filtered to suit
   the surface. --rule stays a token on purpose: in dark it is #1e3040, which reads as a fine dark
   edge on the white plate - which is the separation this plate needs against the dark field. */
[data-bs-theme=dark] .result-row__img {
  background: #fff;
}

/* /brand "Featured Brands" tiles (2026-08-24, Juan): the same fixed light plate, for the same
   reason as K1 above - a vendor brand logo raster carries a baked-in white background, so a tile
   that took the theme's paper put a hard white rectangle on a navy field. This is the grid-tile
   twin of .listing-context__logo-plate (_category.scss), which does the identical job for the ONE
   logo a listing header shows; it is not shared with that rule because the plate there is a
   shrink-to-fit badge that goes display:block only at xl, while these fill their column at every
   width. Lives in _components.scss, not _content.scss: the Brand module registers no module
   stylesheet, so skin.css is the only sheet /brand loads. */
.brand-logo-plate {
  /* height:100% + centering, because the logo rasters are not one size: the plate used to be the
     COLUMN, which a flex row stretches to the tallest tile, so moving it onto the <a> gave every
     tile a content-height box and a ragged grid (measured: the HP and LG plates sat 35px above
     their row neighbours' tops at 1440). The column still stretches; the plate now follows it. */
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  background: var(--paper);
  border-radius: var(--radius-sm);
  padding: 0.75rem;
}

[data-bs-theme=dark] .brand-logo-plate {
  background: #fff;
}

/* Description + availability column */
.result-row__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* POLISH S3, P2-07: --fs-6 (16/14), was a bespoke 15.5px. The `font:` shorthand is split into
   longhands because a shorthand cannot take a var() size with a line-height. */
.result-row__title {
  font-family: var(--ff-u);
  font-weight: 600;
  font-size: var(--fs-6);
  line-height: 1.4;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.result-row__title a {
  color: var(--ink);
}
.result-row__title a:hover, .result-row__title a:focus {
  color: var(--blue-link);
}

.result-row__pn {
  font: 400 12px/1.3 var(--ff-u);
  color: var(--ink-faint);
  margin: 0;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* BH2 (2026-08-14): MFG part # and part # cut off or wrap mid-word side by side at 402px.
   Stack them instead; the separator dot has nothing left to separate on its own line.

   ⚠ SESSION 8 (2026-08-20): BH2 NEVER ACTUALLY STACKED THEM, and the display:none was why.
   The three identifiers are TEXT NODES separated by two <span class="sep-dot"> elements. A
   display:none child generates no box, so the flex container sees one uninterrupted run of inline
   content and wraps it in a SINGLE anonymous flex item - which is how "MFG Part #
   HDMI2-CABLE-4K60-6FPart # 1-2598236UPC # 065030910507" reached 402px with no break at all.
   Measured on the PDP accessory row at 402 (31px, one item) before this change; the same markup on
   the new similar card put it in a 179px column at 62px.
   visibility:hidden keeps the element IN FLOW, so each text run gets its own anonymous item and the
   column actually stacks; height/margin 0 stops the invisible dot taking a row of its own. */
@media (max-width: 575.98px) {
  .result-row__pn {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .result-row__pn .sep-dot {
    visibility: hidden;
    height: 0;
    margin: 0;
  }
}
.result-row__spec {
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 64ch;
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.result-row__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 10px 18px;
  align-items: center;
  font-size: 13px;
}

/* Unit price column: right-aligned buy stack */
.result-row__buy {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  text-align: right;
}

/* D3, REVERSED FOR THE LISTING ONLY (S3, 2026-08-21, Juan): on a LISTING row "Add to cart" is now
   the filled accent action and "View details" the outline one - the fill follows the action, as it
   already does on every other buy surface. The ORDER did not move: View details is still emitted
   first. The PDP accessory row is EXEMPT and keeps BOTH of its differences - Add to cart comes
   first there, and as of 2026-08-12 it does not take the fill either: six filled accessory buttons
   out-shouted the buy box's own (correctly drained) outline Add-to-cart on an unavailable product,
   so one fill per region and no fill on a PDP that does not buy the page's own product. Which
   button wears which treatment is set in the MARKUP (_ProductSummaryPartial.cshtml,
   ResultsPartial.cshtml); these two rules are geometry only, and both take the full column width so
   the pair reads as one control stack rather than two ragged widths. */
.result-row__details,
.result-row__add {
  align-self: stretch;
  min-height: 42px;
  padding: 0 18px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  white-space: nowrap;
  font-size: 0.85rem;
}

.result-row__add {
  min-width: 0;
}

/* Compare: inline, baseline-aligned checkbox + label, shared by both listing layouts.
   .compareProductCheck stays JS-wired. Not emitted by the PDP accessory row. */
.result-row__compare {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  color: var(--ink-2);
  font-size: 13px;
  cursor: pointer;
  /* UI BATCH 2026-08-27-E item 1: the 16px box and its `accent-color` are GONE - the sitewide
     toggle switch (_global.scss) owns this control's size, and `accent-color` is inert once
     `appearance` is none. The row is already an inline-flex with its own gap, so nothing else
     here had to change. */
  /* UAT FEEDBACK 2026-08-30: on an iPhone, tapping this switch opened the product page instead of
     toggling compare. MEASURED at 390px before the change: .result-row__media is a 92x92 <a> whose
     bottom edge sat 8px above a switch only 26px tall, both inside the same 92px column. Safari's
     touch-target heuristic expands a tap outwards and prefers a LINK, so a tap on the top half of
     the switch - or in the gap - was absorbed by the thumbnail link above it. The switch is now
     given its own 44px-high target (the WCAG 2.5.5 figure the rest of this estate uses) with the
     padding INSIDE the label, so the label's own box - which is what forwards the tap to the
     implicit checkbox - is what grows, and the label/for association is untouched. The separation
     from the link is on .result-row__item's `gap` below.
     `touch-action: manipulation` drops Safari's 300ms double-tap-to-zoom wait on the control, which
     is what made a first tap read as "nothing happened" and invited the second tap that navigated. */
  min-height: 44px;
  padding: 7px 4px;
  touch-action: manipulation;
}
.result-row__compare .form-check-label {
  cursor: pointer;
}

/* Tablet (768-991): the rail is a sheet here, but the fixed 240px price column
   still crowds the description - tighten both fixed tracks. */
@media (min-width: 768px) and (max-width: 991.98px) {
  .results-caps,
  .result-row {
    grid-template-columns: 96px 1fr 190px;
    gap: 16px;
  }
  .result-row__media,
  .result-row__img {
    width: 96px;
    height: 96px;
  }
  .result-row__price {
    font-size: 22px;
  }
}
/* Phone: 92px thumbnail beside the body, then the buy block spanning both tracks - price line
   first, then the actions. The caption row has nothing to caption once the tracks collapse. */
@media (max-width: 767.98px) {
  .results-caps {
    display: none;
  }
  .result-row {
    grid-template-columns: 92px 1fr;
    gap: 12px 12px;
    padding: 16px 0;
  }
  .result-row__media,
  .result-row__img {
    width: 92px;
    height: 92px;
  }
  /* POLISH S3, P2-07: the phone `font-size: 13.5px` is GONE - --fs-6 already floors at 14px at
     this width, so the override only existed to sit half a pixel off the ramp. */
  .result-row__title {
    -webkit-line-clamp: 3;
  }
  .result-row__spec {
    display: none;
  }
  .result-row__buy {
    grid-column: 1/-1;
    display: grid;
    grid-template-columns: 1fr;
    gap: 9px;
    align-items: center;
    text-align: left;
  }
  .result-row__prices {
    grid-column: 1/-1;
    flex-direction: row;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 8px;
  }
  .result-row__price {
    font-size: 21px;
  }
  .result-row__details,
  .result-row__add {
    align-self: auto;
    padding: 0 10px;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .result-row {
    transition: background-color 0.15s ease;
  }
}
/* ============================================================================
   BF8 (Refinement Session BF, 2026-08-14, Juan) — EVERY PRODUCT-ARTWORK PLATE IS
   A FIXED LIGHT PLATE IN DARK MODE, NOT JUST THE TWO SESSION K PINNED.
   ----------------------------------------------------------------------------
   K1 pinned the PDP gallery frame and the listing row-display thumbnail and left
   six plates deliberately unpinned, each recorded as "Juan's plate call, not this
   session's". This is that call: all of them. The reasoning is K1's, and
   .cp-paymark's before it — almost every image in this catalog is a stock photo
   with a WHITE BACKGROUND BAKED INTO THE PIXELS, so the artwork is specified
   against white and the plate is the part that stays put. A tinted plate under a
   white-ground photo paints a hard white rectangle inside a dark box, which reads
   as a rendering fault rather than as a picture (MEASURED by K1 on two of four
   Similar-Products cards at 1440). The artwork is NEVER inverted, filtered or
   recoloured to suit the surface: these are manufacturer product photos, and the
   .cp-paymark rule against retouching trademarked artwork applies for the same
   reason.

   ONE rule rather than eight, and it lives HERE rather than beside each plate,
   because the eight selectors are spread across five separately-compiled
   stylesheets (_product, _category, _cart, _myaccount and this file) and a plate
   rhythm stated in five places is a plate rhythm that drifts. skin.css is
   FileOrder 15 — above every module sheet — so this wins the ties without an
   !important:
     · .similar-card__media        _product.scss   --paper-2  #16273a   (PDP)
     · .results-grid__media        _category.scss  --ground   #0b1420   (listing
       grid AND the PDP accessory row — the same plate)
     · .ma-line__plate            (this file)      --ground   #0b1420
     · .mc-thumb / .mc-thumb-sm    _cart.scss      --lightest-grey
     · .rc-item-thumb              _cart.scss      --lightest-grey
     · .reqlist-item img           _myaccount.scss  NO plate at all — the shared
       list view at /lists/view?t=. K1 read "nothing to pin"; under this ruling it
       gets a plate, because a transparent-PNG accessory on that card had no white
       behind it while every sibling thumbnail did.
   .rl-thumb (the 56px requisition-list-detail tile) is NOT in the list: it is
   already authored `background: #fff` in _myaccount.scss, so it has never flipped.

   Borders and radii stay TOKENS on purpose, exactly as K1 left --rule on the row
   thumbnail: in dark --rule is #1e3040, which reads as a fine dark edge on a white
   plate, and that edge is the separation these plates need against the dark field.

   Nothing here needs K1's four token re-declarations. That trap is about pinning a
   plate that carries CHROME on it — the gallery frame's counter and its prev/next
   chips read the same tokens the plate does. Every plate below holds one <img> and
   nothing else, so there is no ink to lose.
   ========================================================================= */
[data-bs-theme=dark] {
  /* The shared-list card's thumbnail had no plate to flip, so it is given the
     same one the other tiles have. The image is already object-fit: contain in a
     72x72 box, so the plate is exactly the box it was already occupying. */
  /* ⚠ TWO of the six plates need a stronger selector than this block can give them, and it was
     MEASURED, not predicted: at 1440 in dark on /cart, `.mc-thumb` computed rgb(11, 20, 32) with
     this block already applying. Enumerating every matching background declaration (not just the
     one that looks canonical) found the two culprits, both in _cart.scss:
       · `.cart-page .cart-items .mc-thumb:not(.mc-thumb-sm)`  (0,4,0)  --ground
       · `.co-receipt .rc-item .rc-item-thumb`                 (0,3,0)  --lightest-grey
     Against those, `[data-bs-theme="dark"] .mc-thumb` is only (0,2,0). They are restated in the
     `html[data-bs-theme="dark"]` block directly below - read its note before touching either.

     The other four plates need nothing extra: `.results-grid__media`, `.ma-line__plate` and
     `.reqlist-item img` are only ever stated at (0,1,0), and `.similar-card .similar-card__media`
     ties at (0,2,0), where skin.css (15) beats product.css (5) - verified white on 7 cards. */
}
[data-bs-theme=dark] .similar-card__media,
[data-bs-theme=dark] .results-grid__media,
[data-bs-theme=dark] .ma-line__plate,
[data-bs-theme=dark] .mc-thumb,
[data-bs-theme=dark] .rc-item-thumb {
  background: #fff;
}
[data-bs-theme=dark] .reqlist-item img {
  background: #fff;
  border-radius: var(--radius-sm);
}

/* ⚠ AND THE TIE ABOVE HAS TO BE BROKEN BY A TAG, NOT BY SHEET PRIORITY - MEASURED TWICE.
   `[data-bs-theme="dark"] .cart-page .cart-items .mc-thumb` looks stronger than
   `.cart-page .cart-items .mc-thumb:not(.mc-thumb-sm)`, and it is NOT: an ATTRIBUTE selector counts in
   the same column as a class, and `:not()` contributes its argument's weight, so both are (0,4,0). It
   shipped once at that weight and `.mc-thumb` still computed rgb(11, 20, 32) on /cart at 1440 in dark.

   The reason sheet priority did not rescue it is worth recording, because the "skin.css is FileOrder
   15, it wins the ties" rule this repo relies on is NARROWER than it looks: DNN's ClientDependency
   COMPOSES every stylesheet into ONE bundle (measured: `document.styleSheets` has 3 entries and all
   four matching rules live in entry 0), so a tie is settled by position INSIDE that bundle, and
   _cart.scss's rule sits after this file's. Sheet priority orders the files; it does not make one
   file's (0,4,0) beat another's.

   `html[data-bs-theme="dark"]` adds a TYPE selector, so these are (0,4,1) and (0,3,1) - genuinely
   above the rules they override, whatever order the bundler lands on. The theme attribute is stamped
   on <html> (documentElement), so the qualifier is real and not decorative. */
html[data-bs-theme=dark] .cart-page .cart-items .mc-thumb,
html[data-bs-theme=dark] .co-receipt .rc-item .rc-item-thumb {
  background: #fff;
}

/* ============================================================================
   ADDRESS EDITOR - "we are looking this up" over ZIP, City and State
   2026-08-20 (Juan). The zip city/state lookup used to wait for focus to leave the ZIP field;
   it now fires the moment the fifth digit lands (addressForms.js), at the same time as the
   /checkout shipping-fee refresh. That leaves fields the shopper can SEE, doing nothing, for
   the length of a Google geocode - so they say they are working.

   2026-09-19 (Juan): THE ZIP CARRIES THE INDICATOR TOO (.co-addr-lookup--zip), and on the
   first-entry path it is the only one that can. _SetLocationVisible collapses the City and State
   COLUMNS (d-none) until the lookup answers, so the original indicator spent the whole geocode
   inside a display:none subtree - a shopper typing a zip into a fresh /checkout address saw the
   form do nothing at all. The ZIP wrapper is on screen and focused at that moment.

   Lives HERE rather than in _cart.scss or _myaccount.scss because ONE partial
   (Views/Shared/Forms/AddressEditor.cshtml) renders on /checkout, /cart and /myaccount, and only
   _components.scss (via _skin.scss -> skin.css) is loaded on all three.

   TWO pseudo-elements, not one: a single ::after cannot be both the scrim and the disc. Bootstrap's
   floating-label markup uses neither pseudo on the wrapper, so both are free.

   ⚠ pointer-events: none on both. The lookup only WRITES a field it finds different, so a shopper
   who is already typing the city must keep being able to; a scrim that swallowed the caret would
   turn a 300ms nicety into a lost keystroke. And the wrapper is .form-floating, which Bootstrap
   already gives position: relative - no positioning context is added here.

   The disc is drawn from a border rather than a background-image so it inherits currentColor-ish
   tokens and costs no request. Its keyframes are declared locally: this rule must not depend on
   Bootstrap's .spinner-border being present in whatever composite the page happens to load. */
.co-addr-lookup.is-looking-up::before,
.co-addr-lookup.is-looking-up::after {
  content: "";
  position: absolute;
  pointer-events: none;
  z-index: 3;
}

.co-addr-lookup.is-looking-up::before {
  inset: 0;
  background: var(--paper);
  opacity: 0.55;
  border-radius: var(--radius-sm, 0.25rem);
}

.co-addr-lookup.is-looking-up::after {
  top: 50%;
  right: 0.85rem;
  width: 1rem;
  height: 1rem;
  margin-top: -0.5rem;
  border: 2px solid var(--rule);
  border-top-color: var(--cta);
  border-radius: 50%;
  animation: coAddrLookupSpin 0.7s linear infinite;
}

/* The State control is a .form-select, and its caret is painted at the right edge - the disc would
   sit on top of it. Clear of the caret rather than on the opposite side, so both fields spin in the
   same place relative to their own right edge.

   ⚠ A MODIFIER CLASS, NOT `:has(> .form-select)`. skin.css is minified by BuildBundlerMinifier's
   NUglify 1.5.14, which does not parse modern selectors and fails SILENTLY - it would leave a stale
   skin.min.css behind a green build and take every rule after it with the failure (R79/R91). The
   AddressEditor partial stamps the modifier. */
.co-addr-lookup--select.is-looking-up::after {
  right: 2.3rem;
}

/* THE ZIP GETS THE DISC BUT NOT THE SCRIM.
   The scrim's job on City / State is to say "this value is about to be replaced, do not trust what
   you are reading". The ZIP is the opposite case: it holds the five digits the shopper JUST typed,
   they are what the lookup is reading, and they are the thing to correct if the lookup comes back
   "Unable to validate zip code". Dimming them to .55 while the spinner ran would obscure the only
   field on screen on the first-entry path - which is precisely the path this indicator was added
   for. Disc only.

   The disc sits at the same right: .85rem as the City field's, so a form whose City and State are
   already revealed spins all three in one line rather than in three different places.

   ⚠ A MODIFIER CLASS, NOT :not() / :has() gymnastics, for the same NUglify 1.5.14 reason recorded
   on --select above: the minifier fails silently on selectors it cannot parse and takes every rule
   after it down behind a green build. */
.co-addr-lookup--zip.is-looking-up::before {
  content: none;
}

@keyframes coAddrLookupSpin {
  to {
    transform: rotate(360deg);
  }
}
@media (prefers-reduced-motion: reduce) {
  .co-addr-lookup.is-looking-up::after {
    animation-duration: 2.4s;
  }
}
/* ==========================================================================================
   TRUST RAIL, PLACEMENT T2 — the review link inside a buy panel.
   SURFACE-SWAP-TRUST-RAIL-PLAN-2026-08-21, S5.

   ONE component, three surfaces: the PDP buy box, the cart order-summary rail and the checkout
   order rail all render Views/Shared/Content/_TrustReviewsPartial.cshtml. The plan asks for one
   shared SCSS component rather than three, and this is it.

   WHY _components.scss AND NOT _product.scss / _cart.scss: the block renders on pages served by
   product.css AND by cart.css, so a rule in either entry would be missing on the other two
   surfaces. _components.scss is imported by _skin.scss, i.e. skin.css, which loads on every page
   including /cart and /checkout (the same reason .cp-paymark lives here — content.css does not
   load on /cart). skin.css is also FileOrder 15 against product.css's 5, so these rules win a
   specificity tie against the entry stylesheets rather than losing one.

   The badge plate is --surface-plate, the S1 token for "a plate/well inside a card", NOT a new
   surface token and not literal white: on light the card under it is --surface-card (#e9eff5) and
   the plate is #ffffff; on dark the card is #101d2c and the plate #16273a. Both were already the
   computed values at those sites before the swap, so this needs no per-theme override at all.

   The star is --gold, which _variables.scss already declares as the review-star ornament token and
   which is tuned per theme (#e6a817 light, #eebe4d dark). The mockup drew a --steel star on a
   --steel-tint plate plus a dark-mode-only recolour to --field-accent; --gold is the token this
   repo already uses for exactly this ornament (see .home-rev__stars) and it carries its own dark
   value, so it replaces both of the mockup's rules with none.
   ========================================================================================== */
.trust-rev {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 1.125rem;
  padding-top: 1rem;
  /* The hairline the plan calls for. It is a TOP rule only: on all three surfaces this block is
     the last thing in the card, so a bottom rule would draw a line with nothing under it. */
  border-top: 1px solid var(--rule);
  /* The sitewide a:not(.underline) { text-decoration: unset !important } rule already strips the
     anchor's own underline; the hover cue is on the CTA span below, which is where the mockup
     puts it. Stated here rather than relied on silently. */
  color: inherit;
}

.trust-rev__badge {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background: var(--surface-plate);
  color: var(--gold);
  font-size: 1.0625rem;
  line-height: 1;
}

.trust-rev__txt {
  min-width: 0;
  font-size: 0.875rem;
  line-height: 1.35;
}

.trust-rev__txt b {
  display: block;
  font-weight: 600;
  color: var(--ink);
}

.trust-rev__cta {
  color: var(--blue-link);
  font-weight: 600;
}

.trust-rev:hover .trust-rev__cta,
.trust-rev:focus-visible .trust-rev__cta {
  text-decoration: underline;
}

.gps-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.625rem calc(0.75rem - 1px);
  border: 1px solid var(--rule);
  border-radius: var(--radius-btn, 8px);
  background: var(--paper);
  color: var(--textcolor);
  font-family: var(--ff-u);
  font-style: normal;
  font-weight: 700;
  font-size: 0.75rem;
  line-height: 1.5;
  text-align: left;
  text-decoration: none;
  max-width: 100%;
  transition: var(--transition);
}
.gps-link:visited {
  color: var(--textcolor);
}
.gps-link:hover, .gps-link:focus-visible {
  color: var(--textcolor);
  background: var(--control-hover, var(--paper));
}
.gps-link:focus-visible {
  outline: 2px solid var(--focus, currentColor);
  outline-offset: 3px;
}

.gps-g {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
}

#main-footer .footer-identity .gps-link {
  align-self: flex-start;
  margin-top: 0.75rem;
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.28);
  color: var(--field-ink);
}
#main-footer .footer-identity .gps-link:visited {
  color: var(--field-ink);
}
#main-footer .footer-identity .gps-link:hover, #main-footer .footer-identity .gps-link:focus-visible {
  color: var(--field-ink);
  background: rgba(255, 255, 255, 0.16);
}

.auth-surface .ma-form__act--stretch .btn {
  flex: 1 1 100%;
}
.auth-surface .ma-form__act--stretch > a:not(.btn) {
  flex: 1 1 100%;
  text-align: center;
}
.auth-surface .auth-providers {
  margin-top: 0.25rem;
}
.auth-surface .auth-or {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1rem 0;
}
.auth-surface .auth-or hr {
  flex: 1 1 auto;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--rule);
  opacity: 1;
}
.auth-surface .auth-or span {
  font-family: var(--ff-m);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.auth-surface .auth-rail {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.5rem;
}
@media (min-width: 576px) {
  .auth-surface .auth-rail--2 {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}
@media (min-width: 1440px) {
  .auth-surface .auth-rail--3 {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
.auth-surface .auth-rail + .auth-rail {
  margin-top: 0.5rem;
}
.auth-surface .auth-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.625rem;
  min-height: 44px;
  padding: 0.5rem 0.9rem;
  border: 1px solid transparent;
  border-radius: var(--radius-btn);
  font-family: var(--ff-u);
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.2;
  text-align: center;
  transition: background-color 0.15s ease;
}
.auth-surface .auth-btn:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}
.auth-surface .auth-btn__label {
  min-width: 0;
}
.auth-surface .auth-btn__mark {
  flex: 0 0 18px;
  width: 18px;
  height: 18px;
}
.auth-surface .auth-btn--google {
  background: #fff;
  border-color: #747775;
  color: #1f1f1f;
}
.auth-surface .auth-btn--google:visited {
  color: #1f1f1f;
}
.auth-surface .auth-btn--google:hover {
  background: #f7f8f8;
  color: #1f1f1f;
}
.auth-surface .auth-btn--microsoft {
  background: #fff;
  border-color: #8c8c8c;
  color: #5e5e5e;
}
.auth-surface .auth-btn--microsoft:visited {
  color: #5e5e5e;
}
.auth-surface .auth-btn--microsoft:hover {
  background: #f4f4f4;
  color: #5e5e5e;
}
.auth-surface .auth-btn--apple {
  background: #000;
  border-color: #000;
  color: #fff;
}
.auth-surface .auth-btn--apple:visited {
  color: #fff;
}
.auth-surface .auth-btn--apple:hover {
  background: #1a1a1a;
  color: #fff;
}
.auth-surface .auth-btn--generic {
  background: var(--paper);
  border-color: var(--rule);
  color: var(--ink);
}
.auth-surface .auth-btn--generic:visited {
  color: var(--ink);
}
.auth-surface .auth-btn--generic:hover {
  background: var(--paper-2);
  color: var(--ink);
}
.auth-surface .ma-aside {
  position: relative;
  border: 0;
  padding: 2.5rem 2rem;
  background-color: var(--blue-deep);
  color: var(--field-ink);
  background-image: repeating-linear-gradient(to bottom, transparent 0, transparent 2.35rem, rgba(255, 255, 255, 0.06) 2.35rem, rgba(255, 255, 255, 0.06) calc(2.35rem + 1px));
}
.auth-surface .ma-aside p {
  color: var(--field-ink-2);
}
.auth-surface .ma-aside .ma-help__title {
  font-family: var(--ff-m);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--field-accent);
  margin-bottom: 0.9rem;
}
.auth-surface .ma-aside .auth-aside__item {
  position: relative;
  margin: 0;
  padding: 0.65rem 0 0.65rem 1.6rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.14);
  font-size: 0.9375rem;
  line-height: 1.4;
  color: var(--field-ink);
}
.auth-surface .ma-aside .auth-aside__item::before {
  content: "";
  position: absolute;
  left: 0;
  top: 1.05rem;
  width: 0.6rem;
  height: 0.32rem;
  border-left: 2px solid var(--field-accent);
  border-bottom: 2px solid var(--field-accent);
  transform: rotate(-45deg);
}
.auth-surface .ma-aside .auth-aside__item + :not(.auth-aside__item) {
  margin-top: 1.25rem;
}
.auth-surface .ma-aside a:not(.btn) {
  color: var(--field-accent);
}
.auth-surface .ma-aside a:not(.btn):visited {
  color: var(--field-accent);
}
.auth-surface .ma-aside a:not(.btn):hover, .auth-surface .ma-aside a:not(.btn):focus-visible {
  color: var(--field-ink);
}
.auth-surface .ma-aside .btn-neutral {
  margin-top: 0.25rem;
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.28);
  color: var(--field-ink);
}
.auth-surface .ma-aside .btn-neutral:visited {
  color: var(--field-ink);
}
.auth-surface .ma-aside .btn-neutral:hover, .auth-surface .ma-aside .btn-neutral:focus-visible {
  background: rgba(255, 255, 255, 0.16);
  color: var(--field-ink);
}
.auth-surface .recaptcha-notice {
  margin-top: 1.25rem;
}

[data-bs-theme=dark] .auth-surface .auth-btn--google {
  background: #131314;
  border-color: #8e918f;
  color: #e3e3e3;
}
[data-bs-theme=dark] .auth-surface .auth-btn--google:visited {
  color: #e3e3e3;
}
[data-bs-theme=dark] .auth-surface .auth-btn--google:hover {
  background: #1e1f20;
  color: #e3e3e3;
}
[data-bs-theme=dark] .auth-surface .auth-btn--microsoft {
  background: #2f2f2f;
  border-color: #2f2f2f;
  color: #fff;
}
[data-bs-theme=dark] .auth-surface .auth-btn--microsoft:visited {
  color: #fff;
}
[data-bs-theme=dark] .auth-surface .auth-btn--microsoft:hover {
  background: #3b3b3b;
  color: #fff;
}
[data-bs-theme=dark] .auth-surface .auth-btn--apple {
  background: #fff;
  border-color: #fff;
  color: #000;
}
[data-bs-theme=dark] .auth-surface .auth-btn--apple:visited {
  color: #000;
}
[data-bs-theme=dark] .auth-surface .auth-btn--apple:hover {
  background: #f2f2f2;
  color: #000;
}
[data-bs-theme=dark] .auth-surface .ma-aside {
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.popup-skin .auth-surface .ma-aside {
  display: none;
}
.popup-skin .auth-surface .ma-split {
  grid-template-columns: minmax(0, 1fr);
}
.popup-skin .auth-surface .ma-card {
  border: 0;
  box-shadow: none;
  padding: 0;
  background: transparent;
}
.popup-skin .auth-surface .ma-head {
  margin-bottom: 1rem;
}

.pagination-row {
  /* ---- R13: the pager was the mobile half of the horizontal gutter escape.
     The <nav> sits in a `.col-12.d-flex.justify-content-center`, so it is a flex
     item, and a flex item's default `min-width: auto` sizes it to MIN-CONTENT —
     here 417px (a nowrap 7-page list is ~399px on its own) inside a 326px column
     at 390. It overflowed the column, then the page, then body#Body's client box.
     (Same class of bug as R11's `.sitebar-filter`, one column over.)

     `min-width: 0` lets the flex item shrink to its column, and letting the list
     wrap gives it somewhere to go: a pager that has to choose between overflowing
     the page and using a second line should use the second line. ---- */
  /* ---- B2-9 (2026-08-13, Juan): the phone pager is prev / "Page N of M" / next, in one row.
     MEASURED at 402 before the change: the prev chevron and six 40.4px numbered buttons filled
     the 372px row, and the NEXT chevron wrapped to a second line alone. Six numbered pages is
     not a phone control; a position statement between two 44px targets is.

     The numbered items stay in the DOM and are hidden, NOT omitted: search.js reads the current
     page as `$(".current").text()` when it pages by AJAX, and `display: none` keeps that text.
     The summary <li> is server-rendered (ResultsPartial re-renders on every AJAX page), so it
     cannot drift out of step with the results. ---- */
  /* SESSION 4 (2026-08-19), critique finding 09: the number window now always carries the LAST
     page, behind this ellipsis when it is not adjacent. It is aria-hidden and unclickable - the
     jump target is the last page number beside it. */
}
.pagination-row > .col-12 {
  min-width: 0;
}
.pagination-row nav {
  min-width: 0;
  max-width: 100%;
}
.pagination-row .pagination {
  flex-wrap: wrap;
  row-gap: 0.25rem;
}
@media (max-width: 1199px) {
  .pagination-row .all-pages {
    font-size: 12px;
  }
}
.pagination-row .page-item .page-link {
  border: none;
  color: var(--dark-grey);
  font-weight: 700;
}
.pagination-row .page-item .page-link:focus, .pagination-row .page-item .page-link:hover {
  background: none;
  box-shadow: none;
  color: var(--ok);
}
.pagination-row .page-item .page-link.current {
  color: var(--ok);
}
@media (max-width: 1199px) {
  .pagination-row .per-page {
    font-size: 12px;
  }
}
.pagination-row .page-item--summary {
  display: none;
}
.pagination-row .page-item--gap {
  padding: 0 0.15rem;
  color: var(--ink-faint);
  font-weight: 700;
  align-self: center;
}
@media (max-width: 767.98px) {
  .pagination-row {
    /* The 44px target lives in _category.scss, NOT here: the listing pager's pills are stated
       at `.filterResultPartial .pagination .page-item .page-link` (0,4,0), which out-ranks
       anything this file can say at (0,2,0). MEASURED: a min-width here computed 36px on the
       page while the min-height (which had no competitor) took. */
  }
  .pagination-row .pagination {
    align-items: center;
    column-gap: 0.35rem;
    row-gap: 0;
  }
  .pagination-row .page-item--num,
  .pagination-row .page-item--gap {
    display: none;
  }
  .pagination-row .page-item--summary {
    display: block;
    font-size: 13.5px;
    font-weight: 600;
    color: var(--ink-2);
    white-space: nowrap;
  }
}

/*.fa-angle-left:before {
    content: '\f104';
    font-family: "Font Awesome 6 Free";
}
.fa-angle-right:before {
    content: '\f105';
    font-family: "Font Awesome 6 Free";
}*/
/* Formerly hand-maintained CSS (resources/styles/skin.css, desktop.css, mobile.css) migrated into
   the SCSS pipeline (UI Modernization Phase 0.1); the three empty stubs left behind by that move
   were deleted in R91 (2026-08-08) once confirmed unregistered. Import order preserves the old root
   skin.css bundle concat order [_skin.css, skin.css, desktop.css, mobile.css] - do not reorder. */
/* COLAMCO site chrome and global overrides.
  Migrated VERBATIM from the formerly hand-maintained resources/styles/skin.css (UI Modernization Phase 0.1).
  Compiled into _skin.css via scss/_skin.scss; the root skin.css bundle order is unchanged:
  [former _skin.css content] -> this file -> _desktop-overrides -> _mobile-overrides. */
/* ============================================================================
   Phone-chrome visibility (UI-Modernization D1, 2026-07-22)
   The mobile top bar + bottom bar show on PHONES ONLY — in any orientation:
   below 768px, OR a coarse-pointer device at most 500px tall (phone landscape;
   no tablet viewport is that short). Everything else follows the plain 768px
   Bootstrap md split. `pc-hide` is the inverse, for the desktop header/footer
   strips that must yield to the phone chrome on phone-landscape.
   Keep this query in sync with skin.js _Browser_IsMobile().
   POLISH S6b, P3-25 (2026-08-20): the three !importants here are RESPONSIVE DISPLAY UTILITIES and are
   kept for parity with the ones they sit beside - Bootstrap emits every `.d-none` / `.d-*-block` with
   !important, and these classes are stacked on the SAME elements as those. A non-important `.pc-hide`
   at (0,1,0) loses to a `.d-md-flex` and the phone chrome comes back on desktop.
   ========================================================================== */
.pc-only {
  display: none !important;
}

/* Classic (non-parenthesized) syntax on purpose: BuildBundlerMinifier's CSS minifier
   cannot parse the media-query-level-4 form `((a) and (b))`. */
@media (max-width: 767.98px), (pointer: coarse) and (max-height: 500px) {
  .pc-only {
    display: block !important;
  }
  .pc-hide {
    display: none !important;
  }
}
/* HACK: Fix DNN display issues with frame selection menu, need to revisit later */
/* POLISH S6b, P3-25 (2026-08-20): the three !importants below are KEPT. `.jspScrollable` / `.jspPane`
   are jScrollPane's own generated wrappers inside DNN's action menu, and jScrollPane writes `width`
   and `margin-left` as INLINE styles on `.jspPane` as it scrolls - which is precisely what these two
   have to beat, and only !important can. Admin-surface only; a shopper never sees it. */
ul > li.actionMenuMove > ul.jspScrollable {
  overflow: auto !important;
}

ul > li.actionMenuMove > ul > div > div.jspPane {
  margin-left: 0 !important;
  width: auto !important;
}

/* Content sizing / containers */
body {
  height: 100vh;
  font-size: 1rem;
  overflow-y: auto;
}

/* STICKY FOOTER (2026-08-25 item 2, Juan): "if the main content is short, the footer renders and
   then there's blank section under the footer."

   MEASURED on /feedback at 1440x2400 before the fix: body.scrollHeight 2400 (the rule above pins
   the scroller to the viewport) but #FooterContent's bottom sat at 1953 - 447px of --bgbody below
   the footer with nothing in it. Any page whose content is shorter than the viewport does this.

   The DNN page is wrapped in ONE WebForms <form runat="server">, so the form - not <body> - is the
   real column: body's other children are the deferred <script> tags, the toast host and Olark's
   frame, all zero-height. Hence `body > form` as the flex container; `min-height: 100%` resolves
   against body's definite 100vh height, so the column is at least a viewport tall and the footer
   is pushed to the bottom of it. Nothing is anchored to the viewport - a long page still scrolls
   the footer off the bottom exactly as before, which is what "pushed down" and not "fixed" means.

   ⚠ #divPageContentWrapper IS DELIBERATELY STILL FREE OF transform / filter / backdrop-filter /
   contain (see the note on it in Default.ascx - it is the cookie-notice host's parent precisely
   because it establishes no containing block for `position: fixed`). `display: flex` does NOT
   establish one, so that contract holds. Its three `.offcanvas` children are `position: fixed` and
   are therefore not flex items at all, and #divBottomBarContentWrapper's bars are `.fixed-bottom`,
   so neither takes any column height.

   The header keeps `position: sticky` - a sticky flex ITEM sticks to the nearest scrollport, which
   is still <body>. Verified on DEV: header top 0 and footer bottom 2400 after, 1953 before. */
body > form {
  display: flex;
  flex-direction: column;
  min-height: 100%;
}

#divPageContentWrapper {
  display: flex;
  flex-direction: column;
  flex: 1 0 auto;
}

#MainContent {
  flex: 1 0 auto;
}

/* R100 (2026-08-21): `.viewport-centered` and `.fill-viewport-height` DELETED. Two pre-Bootstrap-5
   layout helpers from the hand-maintained skin.css - the first still carried the -moz/-ms/-o
   transform prefixes, i.e. it predates every browser this site supports. Zero writers across every
   .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, and 24 rendered page
   loads; centring is done with flex/`position-absolute top-50 start-50 translate-middle` and full
   height with `min-vh-100`. */
/* POLISH S6b, P3-25 (2026-08-20): `.full-screen` DELETED - 2 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). The only textual hit anywhere was the phrase
   "full-screen overlay" inside a comment in ImagesGalleryPartial.cshtml. */
/* POLISH S6b, P3-25 (2026-08-20): KEPT. This is the full-bleed escape hatch, applied to elements that
   are Bootstrap row/container children carrying `.mx-*` / `.ms-*` margin utilities - all of which
   Bootstrap emits with !important. A plain (0,1,0) rule loses to any of them, and the failure is a
   band that stops being full-bleed on one page only. */
.full-width-row {
  margin-left: calc(50% - 50vw) !important;
  width: 100vw;
  max-width: none;
}

/* Session B: the page-level <footer id="FooterContent"> is a bare container — the
   navy band and all of the spacing belong to #main-footer, which bleeds out of it
   (_footer.scss). The old 10px/40px padding + --lightest-grey field showed as two
   pale strips above and below the navy band once the footer became full-bleed. */
footer {
  padding: 0;
  background-color: transparent;
}

/* Google */
/* POLISH S6b, P3-25 (2026-08-20): KEPT. Google's reCAPTCHA v3 script injects this element and sizes
   and positions it with an INLINE style, and an inline declaration beats any author selector - so
   !important is the only thing that can suppress it. Same mechanism as `#___ratingbadge_0` in
   _mobile-overrides.scss and the Olark rules below. */
.grecaptcha-badge {
  display: none !important;
}

/* Font Awesome */
/* POLISH S6b, P3-25 (2026-08-20): `.wait-for-icons` and its `.fontawesome-i2svg-active` partner DELETED - 2 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). This was the
   hide-until-FA-swaps pattern; nothing opts into it, and FA is a subset build here anyway. */
.fa, .fal, .far, .fas {
  font-family: "Font Awesome 6 Free";
  font-weight: 900;
}

.fab {
  font-family: "Font Awesome 6 Brands";
  font-weight: 900;
}

/* R100 (2026-08-21): `.fad` DELETED. Font Awesome DUOTONE is a Pro-only family; this checkout
   ships the FREE subset (Tools/FontAwesomeSubset -> all.subset.css, zero `.fad` rules) and the
   "Font Awesome 6 Duotone" face is not loaded at all, so the rule named a font that does not
   exist. Zero `fad` class tokens in any .cshtml / .ascx / .js / .cs, in dbo.HtmlText on DEV, or in
   24 rendered page loads. `.fa`/`.fas`/`.far`/`.fab` above are unaffected. */
/* Kendo overrides removed (Kendo elimination plan §2.3, 2026-07-27): the .k-rating-item and
   .k-pager rules styled Kendo's Rating and Pager widgets, which were evicted from the PDP back in
   UI-Modernization 1.1 and replaced by pure CSS/HTML stars and a vanilla-JS pager. No Kendo CSS or
   JS is loaded anywhere now, so these selectors could never match. */
/* Google Places Overrides */
.pac-container {
  z-index: 1060;
}

/* Olark overrides */
#olark-wrapper .olark-image-modal-container.olark-hidden,
.olark-launch-button-wrapper,
.olark-hidden {
  display: none !important;
}

#hbl-live-chat-wrapper #olark-wrapper .olark-launch-button {
  z-index: 999 !important;
}

/* B2-4 (2026-08-13, Juan): hide Olark's own "powered by" branding. Olark injects into the TOP
   document — not an iframe — which is why the three selectors above have always reached it, so a
   page-level !important pair is the intended mechanism for vendor DOM we do not own. The loader
   is lazy (Views/Resources/Index.cshtml fires it on the first pointerdown/keydown/scroll/touch),
   so the elements are absent until the shopper interacts: an empty querySelector for
   .olark-branding-link means "not loaded yet", not "wrong selector".

   ⚠ The class-only spelling Juan supplied does NOT work, and !important is not the reason it
   fails. MEASURED live with the chat box expanded: Olark ships a CROSS-ORIGIN stylesheet
   (static.olark.com/.../theme.css, unreadable from cssRules, so it does not show up in an
   enumeration of matching rules) that states these two properties at ID strength. A freshly
   appended `.olark-branding-link { padding-bottom: .5rem !important }` computed 0px and the link
   stayed inline-block; `#olark-wrapper .olark-branding-link` computed 8px and display:none on the
   same page, same instant. So the ID prefix is load-bearing - do not "simplify" it away because
   the !important looks sufficient. */
#olark-wrapper .olark-branding-link {
  padding-bottom: 0.5rem !important;
}

/* TWO ids, and the reason is measured, not defensive: Olark's theme.css states
   `#olark-container .olark-branding-link a { ... display:inline-block !important }` - (1,1,1),
   same as `#olark-wrapper .olark-branding-link a`, and their sheet is injected AFTER ours, so a
   one-ID spelling ties and loses on source order (verified live at cdv 816: the padding rule above
   took at 8px while the link stayed inline-block). The same sheet sets no padding-bottom on the
   wrapper, which is why the rule above needs only one ID. Fetched and grepped from
   static.olark.com/jsclient/styles/cryptic-capybara/theme.css - it is cross-origin, so its rules
   never appear in a cssRules enumeration. */
#olark-wrapper #olark-container .olark-branding-link a {
  display: none !important;
}

/* R100 (2026-08-21): the Lightbox overrides (`.lb-dataContainer`) are DELETED. Lightbox2 was
   replaced by PhotoSwipe on the PDP gallery (product.js `_InitLightbox`: "replaces the deprecated
   Lightbox2 library") and no `lb-*` markup, script or libman entry is left in the solution. */
/* Bootstrap overrides */
.toast {
  min-width: 300px;
}

/* R44: receipt-strip toast redesign - a full-width bar that slides up from the bottom edge like a
   printing receipt (DESIGN.md "World": purchase orders/packing slips), replacing the untokenized
   bg-success/warning/danger Bootstrap toast. Variant colour lives on the root .colamco-toast--ok/
   --warn/--err class (set by skin.js), not on .toast-header, so the stub, top border, and meta
   timestamp share one source of truth. */
.colamco-toast {
  display: flex;
  align-items: center;
  gap: 1rem;
  width: 100%;
  max-width: none;
  margin: 0;
  padding: 0.85rem 1.25rem;
  background: var(--paper);
  border: 0;
  border-top: 3px solid var(--cta);
  border-radius: 0;
  box-shadow: 0 -10px 28px rgba(6, 43, 76, 0.14);
  transform: translateY(100%);
  opacity: 0;
  transition: transform 0.32s cubic-bezier(0.16, 1, 0.3, 1), opacity 0.2s ease;
}

.colamco-toast.show {
  transform: translateY(0);
  opacity: 1;
}

.colamco-toast--warn {
  border-top-color: var(--warn);
}

.colamco-toast--err {
  border-top-color: var(--err);
}

.colamco-toast-stub {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: 0;
  padding: 0;
}

.colamco-stub-label {
  font-family: var(--ff-d);
  font-weight: 800;
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--cta-ink);
  background: var(--ok);
  border-radius: var(--radius-sm);
  padding: 0.3rem 0.55rem;
  line-height: 1;
}

.colamco-toast--warn .colamco-stub-label {
  background: var(--warn);
}

.colamco-toast--err .colamco-stub-label {
  background: var(--err);
}

.colamco-toast-content {
  flex: 1;
  min-width: 0;
}

.colamco-toast-heading {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.colamco-toast .toast-title {
  font-family: var(--ff-d);
  font-weight: 700;
  font-size: 13.5px;
  color: var(--ink);
}

.colamco-toast .toast-subtitle {
  font-size: 11.5px;
  color: var(--ink-2);
}

.colamco-toast .toast-body {
  padding: 0;
  font-size: 12.5px;
  color: var(--ink-2);
}

/* Session L: an action row inside the toast body. Two toasts now carry one - "saved to <list>" has
   a View list link, and "moved to your list" has an Undo. Both are the only route back from an
   action the shopper has just taken, so they sit on their own line under the sentence rather than
   inline in it, where a 12.5px link inside a 12.5px sentence is easy to miss. The button and the
   anchor are styled identically on purpose: one is a link because it navigates, the other is a
   button because it acts, and the shopper should not have to care which. */
.colamco-toast .toast-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 1rem;
  margin-top: 0.4rem;
}
.colamco-toast .toast-actions > a,
.colamco-toast .toast-actions > button {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--blue-link);
  text-underline-offset: 2px;
}

/* Session L: on phones the toast bottoms out at the viewport edge, which is where the phone chrome
   lives - the 57px tab bar. MEASURED at 402x900: tab bar y=843 h=57, and a hit-test inside the
   toast's own rect returned the tab bar's label rather than the toast. That was survivable while
   the toast body was pure prose - the sentence's first line still cleared the chrome - but the
   action row (View list, Undo) sits at the BOTTOM of the body, so the one control that reverses a
   Move to list was unreachable on a phone. Lifted clear of it. Same query as .pc-only above, which
   is what puts that chrome on screen in the first place - keep the two in sync.
   B6 (2026-08-12): was 86px = 57px tab bar + the cart orb's 29px crest. The orb is deleted, so the
   lift is the bar itself plus a 12px gap. */
@media (max-width: 767.98px), (pointer: coarse) and (max-height: 500px) {
  /* !important is not a specificity fight lost - the container carries Bootstrap's `.bottom-0`
     utility, which is itself `bottom: 0 !important`, so nothing without it can win. Measured, not
     assumed: the first version of this rule shipped green and computed to 0px. */
  #divToastContainer {
    bottom: calc(69px + env(safe-area-inset-bottom, 0px)) !important;
  }
}
/* 2026-08-19 batch, Session 2 item 2 (Juan): with the mini-cart open the toast has to stay VISIBLE
   AND CLICKABLE - the control being fixed is an unreachable Undo, so the test is a real click.
   skin.js adds .toast-dock-cart while #navSidebarRight / #navSidebarBottom is shown, and writes
   --toast-dock-top from the masthead's measured bottom edge.

   ⚠ The z-index is NOT what fixed the reported defect - see _EscapeToastContainerFromHeader in
   skin.js. The container used to live inside `header.sticky-top`, whose own z-index 1020 is a
   stacking context, so its 1090 was resolved inside 1020 and painted under the offcanvas backdrop
   (1040) and panel (1045) no matter what number it carried. It is re-parented to <body> now, where
   Bootstrap's own 1090 already clears both. */
/* ⚠ THE TWO DOCKS ARE MUTUALLY EXCLUSIVE MEDIA BLOCKS, not a base rule plus an override. MEASURED
   at 402x900 when the desktop half was written unqueried: its `left: auto !important` (itself needed
   to beat Bootstrap's `.start-0`, which is `left: 0 !important`) reached the phone and left the strip
   307px wide against the right edge instead of full width. An `!important` that has to exist cannot
   then be overridden from the other branch, so the branches must not overlap. */
@media (min-width: 768px) {
  /* Desktop: the bottom of the right-hand cart column, the same 400px track the offcanvas-end
     panel occupies (Bootstrap's --bs-offcanvas-width default, unchanged by this skin). It does not
     have to be INSIDE the panel's DOM to sit at the bottom of that section. */
  #divToastContainer.toast-dock-cart {
    left: auto !important;
    right: 0;
    width: min(400px, 100vw);
  }
}
@media (max-width: 767.98px), (pointer: coarse) and (max-height: 500px) {
  /* Phone: the mini-cart is the bottom sheet, so the bottom edge is exactly where the toast must
     not be. Top of the content area instead, immediately under the sticky masthead, whose measured
     bottom edge skin.js writes into --toast-dock-top. `!important` on `bottom` for the same reason
     the lift above needs it - `.bottom-0` is itself `bottom: 0 !important` - and on
     `flex-direction` because the container wears `.flex-column-reverse`, which is likewise
     `!important`; column (not column-reverse) puts the newest toast at the TOP edge, which is the
     edge it is docked to. This block is last on purpose: it also covers a landscape phone, where
     both queries match. */
  #divToastContainer.toast-dock-cart {
    top: var(--toast-dock-top, 0px);
    bottom: auto !important;
    left: 0;
    right: 0;
    width: auto;
    flex-direction: column !important;
  }
}
.colamco-toast-meta {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  font-size: 11.5px;
  color: var(--ink-3);
  border-left: 1px dashed var(--rule);
  padding-left: 1rem;
}

.colamco-toast-close {
  flex: 0 0 auto;
}

@media (max-width: 575.98px) {
  .colamco-toast-meta {
    display: none;
  }
  .colamco-toast {
    padding: 0.75rem 1rem;
    gap: 0.65rem;
  }
}
.notificationbar-message {
  font-size: 1.5rem;
}

.navbar-dark .nav-link {
  color: rgba(255, 255, 255, 0.85);
}

nav .nav-item > .dropdown-menu {
  display: none;
}

/* POLISH S6b, P3-25 (2026-08-20): KEPT. It has to beat the rule immediately above - this skin's own
   `nav .nav-item .dropdown-menu { display: none }` at (0,2,1) - and `.dropdown-menu.show` is only
   (0,2,0). Bootstrap's own `.dropdown-menu.show { display: block }` is (0,2,0) too and earlier in the
   composite, so without this the desktop menus would open to nothing. A specificity fix means
   restating the nav ancestry, i.e. coupling the open state to the markup tree it lives in. */
.dropdown-menu.show {
  display: block !important;
}

nav .nav-item .dropdown-menu {
  margin-top: 0;
}

/* REFINEMENT I (2026-08-13, Juan's ruling): `.btn:hover, .btn:focus` is OUT of this legacy selector
   list. It defeated Refinement A6 sitewide - `.btn-link:hover { text-decoration: underline }` in
   _button.scss is (0,2,0) against this rule's (0,2,0) !important and lost, so NO link-styled button
   had a hover underline. Measured on the PDP's "Save to a list": :hover matched, the colour changed,
   and text-decoration-line still computed `none` with three rules asking for underline.
   Solid buttons are unaffected - Bootstrap's `.btn` already sets `text-decoration: none` at rest and
   nothing re-decorates them on hover - and anchors were already exempt through
   `a.btn-link:hover { ... !important }` in _button.scss, which is why the <button> and <a> shapes of
   the same control used to disagree. The nav-link half of the list stays: the mega-menu and the
   navbar do not underline on hover by design. */
.navbar.navbar-dark .nav-link:hover, .navbar.navbar-dark .nav-link:focus, .navbar.navbar-light .nav-link:hover, .navbar.navbar-light .nav-link:focus {
  text-decoration: none !important;
}

.navbar-light .nav-link {
  color: var(--ink);
}

.form-control.custom-select {
  height: auto;
}

/* R100 (2026-08-21): `.custom-control-label::before/::after` DELETED. `.custom-control-*` is
   BOOTSTRAP 4 grammar; it was removed outright in Bootstrap 5, which this skin has shipped since
   Phase 0.1 (the equivalent is `.form-check-label`, styled in _global.scss). Nothing in any
   project, in dbo.HtmlText or in 24 rendered page loads emits the class, and no vendor sheet
   defines it any more, so the two offsets could never apply. */
/* POLISH S6b, P3-25 (2026-08-20): `.dropdown-toggle.no-icon` DELETED - 1 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). The only `no-icon` string in the tree is inside
   the PersonaBar's own bundle, which this skin does not style. */
/* R100 (2026-08-21): `.card-deck.cards-fluid` DELETED. `.card-deck` is BOOTSTRAP 4 grammar, removed
   in Bootstrap 5 in favour of the grid, and `.cards-fluid` was only ever its companion here. The
   rule needed BOTH classes on one element and neither is emitted anywhere in the solution, in
   dbo.HtmlText, or in 24 rendered page loads; the content pages use their own `.cp-grid-*`. */
.card-text {
  color: var(--ink);
}

.offcanvas-bottom {
  height: 90vh;
}

.offcanvas.show {
  visibility: visible;
}

.btn-outline-success {
  color: var(--cta-outline);
  border-color: var(--cta-outline);
}

.btn-outline-success:hover {
  color: var(--cta-ink);
  background-color: var(--cta);
  border-color: var(--cta);
}

/* B2-5 (2026-08-13, Juan): the "/" separator sat too low against the trail text. The cause was
   this rule's own 4px `margin-top`, not the type sizes. MEASURED on the PDP trail at 1440: the
   separator is 16px/24.8px type whose baseline landed at 193.2px while the 14px/21px crumb text
   baselined at 189.8px — 3.4px low, and the 4px margin was the whole of it. With the margin gone
   the two baselines land within 0.6px of each other. The separator's 16px size is Bootstrap's
   --bs-breadcrumb-divider inheriting the row; it is deliberately NOT the lever (Juan: do not fix
   this by changing the type size).

   2026-08-21 (Juan): "the / character renders like it's taller than the text, push those characters
   down a little so they look more centered vertically with the text." Which is the OTHER end of the
   same 4px, and B2-5's own framing is what made 0 look like the answer: it reasoned about BASELINES,
   and this box does not sit on one. bootstrap.css:5290 gives it `float: left`, so it is a block-level
   box whose top is pinned to the top of the line box and whose `vertical-align` is inert - which is
   exactly why `margin-top` was the only lever B2-5 found.

   MEASURED on the PDP trail at 1470, cdv 1072, with the margin at 0: the li runs 141.4-170.4 (29px),
   the crumb text's ink box (Range.getClientRects, not the inline-block's padded rect) runs
   146.4-164.4, so the TEXT centres at 155.4. The floated ::before is its own 24.8px line-height box
   starting at the li's content top, 141.4, so it centres at 153.8 - 2.1px HIGH, which against a 16px
   glyph beside 14px text reads as the separator being oversized rather than misplaced.
   2px brings the two centres to within 0.4px. It cannot grow the row: 24.8 + 2 = 26.8 inside a 29px
   li, so the float still clears. The type size is still not the lever. */
.breadcrumb-item + .breadcrumb-item::before {
  margin-top: 2px;
}

/* ---- Breadcrumb trail links (R13).
   These carried `.btn .btn-link .btn-sm`, which gave a navigation trail 4px of
   padding — a ~22px tall touch target, well under the 24px WCAG 2.2 Target Size
   (Minimum) floor and under the 44px this site uses elsewhere — and made a row of
   links render as a row of buttons. A breadcrumb is a link trail, so it is styled
   as links, with the target grown vertically by line-height rather than by button
   padding (padding would re-introduce the button look). ---- */
.breadcrumb-link {
  display: inline-block;
  padding: 0.25rem 0;
  min-height: 24px;
  color: var(--blue-link);
  font-family: var(--ff-u);
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.5;
  text-decoration: none;
}
.breadcrumb-link:hover, .breadcrumb-link:focus-visible {
  color: var(--blue-link);
  text-decoration: underline;
}
.breadcrumb-link:visited {
  color: var(--blue-link);
}

/* The trail's last crumb is quieter than the ones before it.

   Refinement A9 (item 18, 2026-08-12): `cursor: default` and `pointer-events: none` are GONE.
   They encoded "the last crumb is the current page", which is true on a category listing —
   where the markup is a bare <li> with no anchor in it anyway, so the rule never applied — and
   FALSE on a product page, where the last crumb is the product's CATEGORY and is a live <a>.
   The result was a trail whose most useful destination was the one thing on it that could not
   be clicked. Killing pointer events on a real anchor is never the right way to say "you are
   here"; `aria-current="page"` plus the muted ink says it, and a crumb that is genuinely the
   current page should not be an anchor at all. */
.breadcrumb-item.active .breadcrumb-link {
  color: var(--ink-2);
  font-weight: 600;
  text-decoration: none;
}

/* UI BATCH 2026-08-27-E item 6 (Juan): ON A PHONE THE TRAIL TRUNCATES, IT DOES NOT WRAP.
   Bootstrap's `.breadcrumb` is `display:flex; flex-wrap:wrap`, so a long category name pushed the
   trail onto a second line and cost a row of the first viewport on every listing and PDP under
   768px. `nowrap` plus a shrinkable item and an ellipsised link keeps it to one line whatever the
   name is.

   THE <li> HAS TO BECOME A FLEX ROW TOO, and that is a MEASUREMENT, not tidiness. Bootstrap draws
   the "/" separator as a `float: left` ::before on the <li>. Constrain the anchor beside it with
   `max-width: 100%` and the anchor is as wide as the li's whole content box, so there is no room
   left beside the float and the anchor DROPS BELOW IT - the trail went to two lines with a lone "/"
   on the first one, which is the very defect this rule exists to fix. Measured at 320 on
   /category/MousePointingDevices-277 with a long ancestor name: 62.6px tall before, 29px after.
   Inside a flex container the float is ignored and the ::before is simply the first flex item.

   `min-width: 0` on the LAST <li> is the other load-bearing half: a flex item's default
   `min-width: auto` floors it at its content width, so `text-overflow` would never have anything to
   clip. Only the last crumb shrinks (`flex: 0 1 auto` against `0 0 auto` on the rest) - letting
   every crumb shrink equally clipped "Home" to "…" as well, at 22.1px, which reads as damage rather
   than as truncation. */
@media (max-width: 767.98px) {
  .breadcrumb {
    flex-wrap: nowrap;
  }
  .breadcrumb-item {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
  }
  .breadcrumb-item::before {
    float: none;
    flex: none;
  }
  .breadcrumb-item:last-child {
    flex: 0 1 auto;
    min-width: 0;
  }
  .breadcrumb-item.active,
  .breadcrumb-link {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}
.invalid-feedback {
  font-weight: bold;
}

/* Sweet alert overrides */
.swal2-container div.swal2-html-container {
  text-align: left;
}

.swal2-html-container h5,
.swal2-html-container h6 {
  line-height: 1.6rem;
}

/* UI BATCH 2026-08-26 item 3: THE IN-PAGE PAGE DIALOG (COLAMCO.CurrentPage.ShowPageDialog).
   Juan: no link on /checkout may navigate away or open a new tab, so the reviews page, the terms
   and the privacy policy are all shown here instead - a same-origin <iframe> filling most of the
   viewport, because the reviews page is a live third-party embed and only exists once its own
   scripts have run.

   THREE rows, and the count is MEASURED, not assumed: the SweetAlert2 popup is already
   `display: grid`, and its own first row belongs to the close button (`grid-row: 1`, pulled back out
   of flow by a negative bottom margin, so it measures 0px). A two-row template therefore handed the
   1fr to the TITLE - measured 609px of empty white above a 150px frame - and left the html container
   in an implicit `auto` row. Rows are: close (0), title (auto), html container (the rest).
   `minmax(0, 1fr)` rather than `1fr`: a grid row's implicit min-content floor is the frame's
   intrinsic 150px, which would push the popup past its own height cap and leak the bottom off screen.
   The height is capped in vh AND in px: on a tall desktop 90vh is a 1200px-tall reading pane for a
   policy page, which is more than the content needs and reads as a page rather than a dialog. */
.swal2-popup.page-dialog {
  width: min(1180px, 92vw);
  max-width: 92vw;
  height: min(900px, 90vh);
  grid-template-rows: auto auto minmax(0, 1fr);
  padding: 0;
  overflow: hidden;
}

.page-dialog .swal2-title {
  /* Right padding clears the close button, which is positioned over this row. */
  margin: 0;
  padding: 1rem 3.25rem 0.75rem 1.25rem;
  text-align: left;
  font-size: 1.15rem;
  line-height: 1.3;
}

.page-dialog .page-dialog__html,
.page-dialog .page-dialog__body {
  margin: 0;
  padding: 0;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

.page-dialog .page-dialog__frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  /* The framed document paints its own field; this only covers the moment before it does. */
  background: var(--paper);
}

/* THE FRAMED DOCUMENT'S OWN CHROME, hidden from inside it.
   The frame loads a whole storefront page, so without this the dialog contained a second masthead,
   a second footer, the phone bottom bar and a fresh cookie notice - measured on
   /terms-and-conditions: header 125px and footer 500px of a 760px-tall dialog. The class is stamped
   on the framed document's <html> by _ShowPageDialog on every `load` of the frame, so it survives a
   navigation made INSIDE the dialog, and it is set by that one caller alone - a punchout partner
   framing the storefront (frame-ancestors carries several) never gets it, and neither does any
   cross-origin embed, because the parent could not reach into those documents at all.
   The rules live here, beside the dialog they serve, rather than as a CSS string inside skin.js.

   UI BATCH 2026-08-27 item 1 (Juan): `.ma-back` - the sitewide "< Back" grammar - goes with them.
   Inside the frame there is nowhere to go back TO: the reviews page reveals its link because
   document.referrer is same-origin (the checkout page that opened the dialog IS same-origin, and
   the script cannot tell a frame from a tab), and history.back() in a one-entry frame does nothing.
   The class covers every emitter - the reviews page, the category drill-up, order/quote/message/list
   detail and compare - so a link followed INSIDE the dialog is covered too. */
html.is-page-dialog #HeaderContent,
html.is-page-dialog #FooterContent,
html.is-page-dialog #divBottomBarContentWrapper,
html.is-page-dialog #divBottomBarCheckoutContentWrapper,
html.is-page-dialog #divCookieConsentHost,
html.is-page-dialog .ma-back {
  display: none !important;
}

/* The masthead is `position: sticky`, so removing it collapses nothing - but the page's own top air
   was budgeted around a bar that is no longer there. */
html.is-page-dialog #divPageContentWrapper {
  padding-top: 0.5rem;
}

/* Dark-mode contrast fix (memory: sweetalert-dark-mode-text-contrast-2026-08-06). SweetAlert2
   ships its own dark theme gated on a [data-swal2-theme] attribute this site never sets - our
   toggle only stamps [data-bs-theme="dark"] (Resources.ascx) - so the vendor CSS's --swal2-*
   custom properties never picked up a dark value and the popup stayed on its light defaults
   (white surface, #545454 body text) even when the rest of the page went dark. Both
   .swal2-title and .swal2-html-container set `color: inherit` from .swal2-popup's
   var(--swal2-color), so redefining these two custom properties under the skin's own dark
   selector fixes the title AND the body text (all four icon flavours - success/error/warning/
   question - share this same popup chrome) with one rule, using the theme's own foreground
   token instead of a hand-picked hex. */
[data-bs-theme=dark] .swal2-popup {
  --swal2-background: var(--paper);
  --swal2-color: var(--ink);
}

/* Sizes */
/* .h-2-5r deleted (Session J): its only writer was the QTY spinner in _ProductSummaryPartial's
   retired desktop-only Bars markup, which existed at widths a phone could never reach. Swept the
   whole Source tree - zero references outside compiled output. */
/* R100 (2026-08-21): `.h-3` DELETED, same finding as `.h-2-5r` above. Bootstrap's height scale is
   `.h-25/50/75/100/auto`, so this was a bespoke 3rem helper; zero writers across every
   .cshtml / .ascx / .js / .cs, dbo.HtmlText on DEV, or 24 rendered page loads. */
/* Backgrounds */
/* POLISH S6b, P3-25 (2026-08-20): `.bg-blue-dark`, `.bg-black` and `.bg-black hr` DELETED - 4 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). Three off-palette
   hex fills (#2C3B48 / #222 / #484848) that predate the token system; nothing wore any of them. */
/* R100 (2026-08-21): `.postback-progress-content` DELETED. A WebForms postback-progress overlay
   z-index that has no emitter: the string appears NOWHERE in the solution outside this sheet's own
   compiled output (grepped across all three projects, the whole Deployed tree and dbo.HtmlText),
   and DNN 9.13.7 core does not emit it either. */
/* Navigation - Off Canvas */
body.offcanvas-active {
  overflow: hidden;
}

/* BATCH 2026-08-20 SESSION 3 ITEM 2 - the page must not scroll behind an overlay.
   Its own rule, NOT a second selector on the one above: a selector list is all-or-nothing in CSS,
   so pairing them would put the off-canvas lock at the mercy of this one parsing.
   menukit.js darken_activate()/darken_remove() set and clear the class - the single point all
   three veil owners (this file's dropdowns, Menus/Mega/menu.js, skin.js _SuggestVeil) go through.
   The other overlays already lock the scroller themselves and are deliberately not listed here:
   MEASURED on devbeta, body computed overflow-y - at rest `auto`, SweetAlert2 `hidden`
   (body.swal2-shown), this veil `auto` before this rule. Adding them here would be a second,
   redundant owner.
   2026-08-26 (items 12 + 13): the page loading indicator USED to be on that list, via
   Bootstrap's own `body.modal-open`. It is not a modal any more, so it locks the scroller
   itself - see `body.page-loading` beside the veil below. */
body.veil-active {
  overflow: hidden;
}

/* Off-canvas panels (cart, filters, menus) follow the theme paper instead of
   the always-white .bg-light utility they carry in the skin markup.
   POLISH S6b, P3-25 (2026-08-20): KEPT - the comment above already names the fight. The element WEARS
   `.bg-light`, which Bootstrap emits as
   `background-color: rgba(var(--bs-light-rgb), var(--bs-bg-opacity)) !important`, and an !important
   utility can only be beaten by another !important at any specificity. Same shape as `.bg-paper` in
   _global.scss. */
.offcanvas.sidebar.bg-light {
  background-color: var(--paper) !important;
  color: var(--ink);
}

.offcanvas-header {
  display: none;
}

ul.submenu.dropdown-menu {
  border-radius: 0;
}

.screen-darken {
  height: 100%;
  width: 0%;
  z-index: 300;
  position: fixed;
  top: 0;
  right: 0;
  opacity: 0;
  visibility: hidden;
  background-color: rgba(34, 34, 34, 0.6);
  transition: opacity 0.2s linear, visibility 0.2s, width 2s ease-in;
}

.screen-darken.active {
  z-index: 100;
  transition: opacity 0.3s ease, width 0s;
  opacity: 1;
  width: 100%;
  visibility: visible;
}

/* Images and Icons */
.cursor-pointer {
  cursor: pointer;
}

/* POLISH S6b, P3-25 (2026-08-20): `.icon-size-xxl` DELETED - 1 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). S3 put standalone icon sizes on --icon-sm/-md/-lg. */
.icon-rating-container {
  letter-spacing: 0.1rem;
}

/* R100 (2026-08-21): `.img-objectfit-none` and `.img-gallery-badge` DELETED (the latter also loses
   its two `@media` sizing rules in _desktop-overrides.scss / _mobile-overrides.scss). Both are
   pre-PhotoSwipe PDP gallery helpers: the badge overlay was retired when the gallery was rebuilt
   (product.js `_InitLightbox` notes it "replaces the deprecated Lightbox2 library" and the current
   markup in ImagesGalleryPartial.cshtml carries no badge element). Zero writers across every
   .cshtml / .ascx / .js / .cs, dbo.HtmlText on DEV, or 24 rendered page loads.
   `.img-thumbnail-minsize` below is KEPT - it is still written. */
.img-thumbnail-minsize {
  height: 130px;
  width: auto;
}

/* POLISH S6b, P3-25 (2026-08-20): KEPT. A defensive rule against a data hole - an <img> whose src the
   server rendered empty. It has to beat whatever display the image's own component gives it (several
   are `display: block` or an inline-flex item at (0,2,0) or more), and `img[src='']` is only (0,1,1). */
img[src=""] {
  display: none !important;
}

/* Main scrollbar: the blue thumb is rounded (5px) by default (see _skin.css). When the page is
   parked at an extreme, square the leading corners so the thumb sits flush against the end of the
   track - top corners at the very top, bottom corners at the very bottom. The sb-at-top /
   sb-at-bottom state classes are toggled on <html> and <body> by the skin startup script; both are
   targeted because the viewport scrollbar can be associated with either element across browsers.
   Inner scrollbars (filter lists, etc.) are unaffected - only the html/body scrollbar is scoped. */
html.sb-at-top::-webkit-scrollbar-thumb,
body.sb-at-top::-webkit-scrollbar-thumb {
  border-top-left-radius: 0;
  border-top-right-radius: 0;
}

html.sb-at-bottom::-webkit-scrollbar-thumb,
body.sb-at-bottom::-webkit-scrollbar-thumb {
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

/* ---- Page hero masthead --------------------------------------------------------------------
   Reusable header banner that renders the page title as REAL, responsive, screen-reader-readable
   text over an on-brand dark-blue geometric background - replacing older banner graphics that had
   the title baked into the raster (which didn't scale on mobile and was invisible to assistive
   tech). Use: <section class="page-hero"><h1 class="page-hero__title">Title</h1>
   <p class="page-hero__subtitle">Optional supporting line</p></section> */
.page-hero {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 2.5rem 1.25rem;
  overflow: hidden;
  background-color: #012a4a;
  background-image: linear-gradient(115deg, #013a6b 0%, var(--blue, #00467f) 48%, #011f38 100%);
}

/* Subtle angular facets echo the brand's geometric banner art without any baked-in text. */
.page-hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(155deg, transparent 42%, rgba(255, 255, 255, 0.05) 42% 52%, transparent 52%), linear-gradient(200deg, transparent 55%, rgba(0, 0, 0, 0.18) 55% 70%, transparent 70%), linear-gradient(35deg, rgba(255, 255, 255, 0.04) 0 30%, transparent 30%);
  pointer-events: none;
}

.page-hero__title {
  position: relative;
  margin: 0;
  color: #fff;
  font-weight: 800;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: clamp(1.6rem, 4.5vw, 2.5rem);
  line-height: 1.1;
}

.page-hero__subtitle {
  position: relative;
  margin: 0.6rem auto 0;
  max-width: 62ch;
  color: rgba(255, 255, 255, 0.88);
  font-size: clamp(0.92rem, 2.2vw, 1.05rem);
  line-height: 1.4;
}

/* ---- Page hero, full-bleed image variant (M3 4.2, D5, 2026-08-19) --------------------------
   The plain .page-hero paints its own gradient plus a faceted ::before. `--img` swaps that art
   for a REAL <img> and turns ::before into the scrim, so the copy stays HTML over the raster
   and never gets baked into it - which is the exact defect the old category-banner.png had.

   Why an <img> and not a background-image: only a real element can take fetchpriority="high",
   and this hero is the LCP element on a page inside the Lighthouse scope. The <img> is
   ABSOLUTE, so the hero's height comes from padding and type alone - the art cannot move page
   layout however slowly it arrives, i.e. it contributes no CLS.

   The scrim is stated for BOTH themes at once. This hero does not re-tint per theme: it is
   navy art with white copy, the same as the masthead above it. */
.page-hero--img {
  background-image: none;
  padding-block: 3.25rem;
}

/* S3 item 14 (2026-08-21, D6): the BAND variant. /categories is a fancy page title, not a
   feature content piece, so it is shorter than the plain --img hero and much shorter than
   the home/assist heroes (min-height 600px desktop, 480 phone). The art behind it is a
   1376x400 band rather than a 16:9 plate, so a tall box would only crop it harder.

   It is a padding change and nothing else - the hero's height has always come from padding
   plus type, and the <img> is absolute, so this moves no other element and adds no CLS. */
.page-hero--img.page-hero--band {
  padding-block: 2.25rem;
}

.page-hero__bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

.page-hero--img::before {
  background: linear-gradient(180deg, rgba(1, 26, 48, 0.42) 0%, rgba(1, 26, 48, 0.66) 100%);
  z-index: 1;
}

/* .page-hero__title / __subtitle are position:relative at z-index auto, i.e. 0, and the
   scrim is a FIRST child at z-index 1 - so without this they paint UNDER it. */
.page-hero--img .page-hero__title,
.page-hero--img .page-hero__subtitle {
  z-index: 2;
}

/* R100 (2026-08-21): the whole PascalCase icon-font block DELETED - `.IconCategories`,
   `.IconDeals`, `.IconMenu`, `.TrackOrder` (the four "bottom-bar nav icons") plus the three
   "legacy standalone" helpers `.IconCart`, `.IconSearch`, `.ContactUs`. The comment they carried
   was stale: Controls/BottomBar.ascx draws every tab glyph as an INLINE SVG now
   (`.nav-bottom-glyph` > `svg`, sized by `#divBottomBarContentWrapper .nav-bottom-item svg`), so
   no ::before glyph is involved. VERIFIED at 390px, where `nav-bottom-item` IS present in the
   harvested DOM and not one of these seven classes is - across 24 page loads, plus zero tokens in
   every .cshtml / .ascx / .js / .cs, in the Mega and Sidebar menu templates (neither emits a menu
   node CssClass), and in dbo.HtmlText / TabSettings / ModuleSettings on DEV. `TrackOrder` and
   `ContactUs` are page-path fragments (/Customer-Service/ContactUs), never rendered classes.

   R100 (2026-08-21): `.table-lg tbody tr` DELETED with them. `.table-lg` is not a Bootstrap class
   in ANY version (the scale is `.table-sm`), so it could only ever have been ours, and nothing
   writes it. */
/* Typography */
/* POLISH S6b, P3-25 (2026-08-20): the five `.text-size-*` utilities DELETED - 5 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). The last consumer, the masthead
   greeting, was replaced by `.hdr-greeting` in Session W (Header.ascx:221 still says so in a
   comment). They also predate S3's --fs-* ramp, which is the sanctioned way to name a size. */
/* POLISH S6b, P3-25 (2026-08-20): BOTH THIS AND `a.underline` BELOW ARE KEPT, and this pair is the
   most load-bearing !important in the file - it is the sitewide underline policy, not a component fix.
   ⚠ SESSION 7 (2026-08-19) MEASURED what it does: `a:not(.underline)` at (0,1,1) with !important kills
   text-decoration on EVERY anchor on the site, which is why a component that wants an underline uses a
   transparent `border-bottom` instead. Removing the !important would not "fix" that - it would
   silently restore DNN core's and Bootstrap's anchor underlines everywhere, because
   `a, a:visited { … }` in default.css is also (0,1,1) and would then win on order. `a.underline` is
   (0,1,1) as well, i.e. a TIE with this rule: it wins only by being later AND equally important, so
   strip either one and the exemption inverts. */
.no-underline, .no-underline:hover, a:not(.underline) {
  text-decoration: unset !important;
}

/* The rule above only EXEMPTS a.underline from the sitewide strip - it never underlined anything,
   so an opted-in link still fell through to whichever base `a` rule won (usually none). Inline
   links in a text block need a real underline to pass axe link-in-text-block, so give the class
   the meaning its name promises. */
a.underline {
  text-decoration: underline !important;
  text-underline-offset: 2px;
  /* POLISH S5, P2-17 (2026-08-20): WCAG 2.5.8. MEASURED at 402 on the PDP: the inline support
     links are 17px and 19px tall, so every one of them was a sub-24 target. Padding on a
     non-replaced INLINE box does not move the line box, so this grows the hit area to 25/27px
     with zero layout change and zero visual change (the padding is transparent, and the
     underline sits on the text baseline either way) - verified against a before/after screenshot
     pair. Every use of this class in the repo is a plain inline link inside a paragraph; if one
     is ever given display:inline-block or made a flex item, use hitTarget() instead. */
  padding-block: 0.25rem;
}

/* ============================================================================
   LINKS ON A BRAND / NAVY FIELD — every state, one place (refinement A7, item 16)

   THE CAUSE, because it has now bitten NINE times and every previous fix was a per-surface
   patch: _global.scss:354 carries `a:visited { color: var(--blue-link) }`.  It has to be
   (0,1,1) — it exists to out-rank DNN's own `a:visited` in dnndefault/7.0.0/default.css —
   and it ships in skin.css at FileOrder 15.  Every dark-band link rule authored in a MODULE
   stylesheet is also (0,1,1) but ships at FileOrder 5, so the sitewide visited re-assert wins
   the tie on SOURCE ORDER and repaints the link --blue-link (#00579e).  On --hero-grad's
   darkest stop (#041526) that measures 1.61:1.  UNVISITED it is fine, hover is fine
   (`.assist-band__links a:hover` is (0,1,2)) — only the visited state breaks, which is why it
   keeps shipping: getComputedStyle DELIBERATELY LIES about :visited for anti-history-sniffing,
   so no DOM probe can see it.  ONLY PIXELS CATCH IT (_content.scss:337 says the same).

   The PDP COLAMCO Assist band was the surface still unpatched (Juan, 2026-08-12: "two links on
   the right, barely legible").  The eight already carrying a hand-written re-assert are
   _home.scss:135/:494/:554/:697/:724, _content.scss:530/:771/:1481 and _cart.scss:415.

   NEW dark-band links belong HERE, not in a tenth per-banner patch.  --field-accent (#8cc0e8)
   is the on-navy accent and clears 8:1 on every --hero-grad stop; on a light dark-accent
   surface the equivalent token is --cta-outline, NEVER --cta (the solid action FILL).
   ========================================================================== */
.assist-band__links a,
.assist-band__links a:visited {
  color: var(--field-accent);
}

/* Carousels */
.carousel-item.card-group .card.list-products {
  max-width: 20rem;
}

.carousel-multi-items .carousel-inner .card .card-title-container {
  min-height: 5rem;
  text-overflow: ellipsis;
}

.carousel-multi-items .carousel-inner .card .card-footer .input-group input[type=number] {
  padding-bottom: 1.3rem;
  min-width: 3rem;
}

/* Modals */
/* 2026-08-26 (items 12 + 13): the `#divModal_Loading` block that stood here - z-index 1012 plus a
   `transform: translateZ(0)` compositing hack for the iPhone blank-backdrop defect - is DELETED
   with the modal itself. The overlay is `.page-loading-veil` below, a plain fixed element whose
   spinner and message are its own CHILDREN rather than siblings of a separately-appended
   backdrop, which is what the compositing hack was working around. See Controls/Resources.ascx.

   The `:not(#divModal_Loading)` qualifier came off this cap for the same reason - the id no longer
   exists, and the veil is not a .modal, so the cap reaches exactly the real modals it always
   meant. */
.modal {
  max-height: calc(100vh - 8rem);
}

/* Page loading indicator — shown both for real page navigations (link-hijack in skin.js
   _OnLinkClick/_NavigateTo) and as a busy-blocking overlay during AJAX operations (cart,
   search, checkout, payments). Visual only — see skin.js for the show/hide state machine.
   body.page-loading is the ONE switch: skin.js adds and removes it synchronously and the veil's
   `display` follows it. No Bootstrap modal, no separately-appended backdrop, no transition to be
   mid-way through - see Controls/Resources.ascx for the three defects that bought this. */
.skip-link:focus,
.skip-link:focus-visible {
  /* POLISH S2, P2-05 (2026-08-20). P2-06 was fixed FIRST to see whether it fixed this outright.
     It fixed HALF of it, MEASURED on the first rendered frame after focus: the box is already
     186.94 x 44.80 at padding 10px 16px, radius 8px, weight 700, where the critique had measured
     0.389px padding / 0.311px radius / weight 411.5 interpolating out of the
     visually-hidden-focusable box. But `a { transition: var(--transition) }` (_global.scss) still
     names background-color and color, so the FIELD still faded: sampled rgba(27,111,184,.04) on
     frame 3, .114 on frame 4, .85 at 120ms, solid only after ~300ms - white 700 label on a
     near-transparent field for the first tenth of a second, over the COLAMCO wordmark. The first
     keyboard stop on the site does not get an entrance. */
  transition: none;
  position: fixed;
  top: 0.5rem;
  left: 0.5rem;
  z-index: 1090;
  padding: 0.625rem 1rem;
  background-color: var(--cta);
  color: var(--white);
  font-weight: 700;
  border-radius: var(--radius-btn, 8px);
  text-decoration: none;
  box-shadow: var(--shadow-2, 0 4px 12px rgba(0, 0, 0, 0.25));
}

/* THE VEIL. One element, no backdrop sibling, no dialog, no transition - `display: none` until
   <body> carries .page-loading, which skin.js toggles synchronously. z-index 1012 is the value the
   deleted #divModal_Loading carried, and `.modal-backdrop`'s own z-index 1011 below still puts a
   REAL modal over it, exactly as before.

   The dim is in the background colour's ALPHA, not in `opacity`: --navy-3 (#030d18) at .55 was on a
   separate backdrop element, and an `opacity` on this element would fade the spinner and the
   message with it. A literal rgba() because --navy-3 is a hex custom property - rgba(var(--x), a)
   is not valid CSS. Keep the two in step if the navy ladder moves. */
.page-loading-veil {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1012;
  align-items: center;
  justify-content: center;
  background-color: rgba(3, 13, 24, 0.55);
}

body.page-loading .page-loading-veil {
  display: flex;
}

/* The page must not scroll behind the veil. Bootstrap used to own this through its own
   `body.modal-open`; with the modal gone the overlay locks its own scroller, which is BODY
   here (the storefront scrolls inside body#Body, not the documentElement). No padding-right
   compensation is needed the way Bootstrap's was: `body` already carries
   `scrollbar-gutter: stable` (_global.scss), so the gutter is reserved whether or not a bar
   is drawn and nothing reflows when it goes. Its own rule, not a second selector on
   body.veil-active above, for the reason stated there - a selector list is all-or-nothing. */
body.page-loading {
  overflow: hidden;
}

/* The blur is on the veil now. On iOS this makes the veil its own composited backdrop root, and
   .page-loading-content is a CHILD of it - a child always paints above its parent, which is the
   whole reason the modal's separately-appended backdrop could hide the dialog and this cannot. */
@supports (backdrop-filter: blur(2px)) or (-webkit-backdrop-filter: blur(2px)) {
  .page-loading-veil {
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
  }
}
.page-loading-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.9rem;
}

.page-loading-spinner {
  width: 3.25rem;
  height: 3.25rem;
  border-radius: 50%;
  border: 4px solid rgba(255, 255, 255, 0.25);
  border-top-color: var(--cta);
  border-right-color: var(--cta);
  animation: page-loading-spin 0.7s linear infinite;
}

.page-loading-message {
  color: var(--white);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  opacity: 0;
  animation: page-loading-text-in 0.25s ease-out 0.35s forwards;
}

@keyframes page-loading-spin {
  to {
    transform: rotate(360deg);
  }
}
@keyframes page-loading-text-in {
  from {
    opacity: 0;
    transform: translateY(2px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
@media (prefers-reduced-motion: reduce) {
  .page-loading-spinner {
    animation-duration: 1.5s;
  }
}
.modal-backdrop {
  z-index: 1011;
}

/* Cookie consent (site-wide theme alignment).
   2026-08-18: repainted onto the SAME navy field as the redesigned masthead (.section-header) so the
   two navy bands the page can show at once are one colour, not two. The old flat #00467f read as a
   third blue against the header's deep field. The gradient's light source is moved to the BOTTOM
   edge (20% 100%) because this band hugs the bottom of the glass, not the top — same paint, mirrored.
   The header closes itself with a 1px light hairline instead of a shadow; this bar does the same on
   its top edge, and keeps a shadow only to lift it off the page it covers. */
/* POLISH S6b, P3-25 (2026-08-20): this rule's four !importants are KEPT and the reason is NOT the
   vendor stylesheet - `cookieconsent.min.css` contains ZERO !important declarations and loads ~90KB
   earlier in the composite. It is the DNN cookie-consent MODULE's own generated colour class:
   MEASURED with CDP on a live notice, `.cc-color-override--1355014326.cc-window` sets
   `background-color: rgb(0, 70, 127)` and `color: rgb(255, 255, 255)` at (0,2,0), against this
   rule's (0,1,0). Without !important the notice reverts to the flat #00467f "third blue" the note
   above exists to remove. The other two in the group (background-image, border-top) have no
   competitor of their own and are kept WITH the group on purpose: splitting one paint decision into
   an important half and a non-important half is how the next edit gets it wrong.
   The BUTTON rules below did come off - see the note there. */
.cc-window {
  font-family: var(--userfont);
  background-color: var(--blue-deep) !important;
  background-image: radial-gradient(120% 180% at 20% 100%, #0d3059 0%, #051a30 62%, #051526 100%) !important;
  color: var(--field-ink) !important;
  border-top: 0 !important;
  box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.14), var(--shadow-3);
}

.cc-window .cc-message {
  font-size: 0.9rem;
  line-height: 1.45;
}

/* POLISH S6b, P3-25 (2026-08-20): TEN !importants removed across these three rules, and the same ten
   came off their `.cc-window.cc-bottom` counterparts in _mobile-overrides.scss in the same pass -
   they had to move together, because the phone block only won on !important-versus-!important.
   MEASURED, per competitor:
     - the vendor sheet: `.cc-btn`, `.cc-theme-classic .cc-btn`, `.cc-theme-edgeless … .cc-btn`, all
       (0,1,0)/(0,2,0), NONE !important, all ~90KB earlier in the composite;
     - the module's generated `.cc-color-override--1355014326 .cc-btn` (0,2,0), which sets `color`
       and `background-color` only - and `.cc-window .cc-btn.cc-allow` is (0,3,0), so it is beaten
       on specificity without help. That override IS why the FIELD rule above keeps its four;
     - `a, a:visited { color: rgb(2,139,255) }` from DNN core at (0,1,1) - also beaten at (0,3,0).
   Nothing competes at all on border-radius or text-transform. */
.cc-window .cc-btn {
  border-radius: var(--radius-sm);
  font-weight: 700;
  text-transform: none;
}

.cc-window .cc-btn.cc-allow,
.cc-window .cc-btn.cc-dismiss {
  background-color: #ffffff;
  color: #062b4c;
  border: 0;
  min-width: 7.5rem;
  padding-left: 1.35rem;
  padding-right: 1.35rem;
}

.cc-window .cc-btn.cc-deny {
  background-color: transparent;
  color: #fff;
  border: 1px solid rgba(255, 255, 255, 0.75);
}

/* ============================================================================
   Bottom Bar — the NAVY DECK (M2, MOBILE-CHROME-M2-BUILD-PLAN-2026-08-18.md §2,
   2026-08-18). Shop / Deals / Cart / Track Order / Menu on the same navy field
   as the masthead and the cookie notice; the active tab gets a --field-accent
   top indicator. Supersedes the translucent white bar of UI-Modernization Phase
   3.3 (approved mockup 2, 2026-07-22) — the tab set, the box and the geometry
   are unchanged, only the field and the ink.
   ========================================================================== */
#divBottomBarContentWrapper {
  z-index: 1010;
}

#divBottomBarContentWrapper .fixed-bottom {
  max-height: none;
  transition: transform 0.15s ease;
}

/* visualViewport keyboard handling (UI-Modernization Phase 8): body.keyboard-open is toggled by
   skin.js whenever the visual viewport shrinks enough to indicate an on-screen keyboard (search
   box, quantity inputs, checkout fields). Slide the fixed bottom nav out of the way instead of
   letting it float over/obscure the focused input - restored the moment the keyboard closes. */
body.keyboard-open #divBottomBarContentWrapper .fixed-bottom {
  transform: translateY(100%);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  #divBottomBarContentWrapper .fixed-bottom {
    transition: none;
  }
}
/* The bar surface. M2 "Navy Deck" (MOBILE-CHROME-M2-BUILD-PLAN-2026-08-18.md §2, 2026-08-18):
   navy owns BOTH edges of the phone, so this is the SAME field as the masthead (.section-header)
   and the cookie notice (.cc-window) — not a flat #00467f, which would be a third blue on a page
   that can show all three at once (the mistake the cdv 964 notice repaint had to undo).

   Light source at 20% 100%, mirrored to the bottom edge exactly like .cc-window: this band and the
   notice both hug the bottom of the glass, and the notice docks directly on top of this one, so the
   two agree. The masthead keeps 20% 0%.

   The blur-glass over --paper it replaces (Redesign v2 R1) is gone with the light field. The 1px top
   border STAYS — a navy field closes with a light hairline instead of a --rule one, but it must stay
   a BORDER and not become a box-shadow: MEASURED 2026-08-18, dropping it to `border-top:0` took the
   row from 57px to 56px, and .cart-mbar, .sticky-atc and .cc-window all bottom out at `bottom:57px`,
   so each opened a 1px slot onto the page behind it. The drop shadow stays; this bar covers content.
   ⚠ The safe-area padding is untouched (viewport-fit=cover), and so is the BOX — see the 57px note
   under .nav-bottom-item--cart.

   M3 section 1.7 (item 15, D6, 2026-08-18): THE SURFACE IS .cart-strip's, not the masthead's.
   Juan's report is that the deck, the drawer and the strip read as three different backdrops on one
   screen. The strip is the reference (see its block below) and the deck takes its three surface
   properties verbatim — the flat #0a2f57 field in both themes, the .14 hairline and the -4px/16px
   shadow at .22 — replacing the radial gradient and the .18 shadow.
   ⚠ The strip's fourth property, the --radius-lg TOP CORNERS, is deliberately NOT taken here. The
   deck is the bottom of the dock stack and something is always landing flush on its top edge: the
   strip bottoms out at exactly `57px` and the cookie notice docks on whichever bar is up, so a 16px
   corner radius here would open two notches onto the page behind a square-bottomed bar. Section 1.8's
   own check is "the SAME navy with the same rule and shadow", which is what this is.
   ⚠ #0a2f57 was picked as ONE STEP LIGHTER THAN THIS DECK so the strip would separate from it; the
   two are now the same field, and the strip's own hairline plus its drop shadow are what separate
   them. That is D6's intent - one backdrop treatment - not an oversight. */
#divBottomBarContentWrapper .nav-bottom-row {
  background: #0a2f57;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 -4px 16px rgba(4, 26, 48, 0.22);
  color: var(--field-ink);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}

/* R90: the five nav columns must NEVER be able to take a second flex line.
   A Bootstrap `.row` is `flex-wrap: wrap`, and a flex item's `min-width` is
   `auto`, so each `.col` refuses to shrink below the min-content width of its
   label. In the fallback face "Track Order" exceeds its 1/5 of a 402px phone
   (80px), so the row laid out as THREE 134px columns and snapped back to five
   80px ones on font swap — a 0.017 layout shift at 402, 3/3 runs, with no
   stylesheet or DOM event to blame it on (it fired ~600ms after the woff2
   landed). It is timing-sensitive: it needs a fast document, so it hid behind
   an unlucky-but-realistic load and did not reproduce under any harness arm
   that rewrote the response. `nowrap` makes the geometry font-independent;
   `min-width: 0` lets the columns actually reach 1/5 each. */
#divBottomBarContentWrapper .nav-bottom-row > .col-12 > .row {
  flex-wrap: nowrap;
}

#divBottomBarContentWrapper .nav-bottom-row > .col-12 > .row > .col {
  min-width: 0;
}

/* POLISH S6b, P3-25 (2026-08-20): KEPT. A `max-*` cap is not a specificity fight - it is a separate
   PROPERTY, so nothing this rule could say about `height` would release it; the cap has to be unset,
   and the cap being released is itself !important
   ([[css-max-property-is-not-a-specificity-fight]]). */
#divBottomBarContentWrapper.checkout .fixed-bottom {
  max-height: unset !important;
}

/* Individual nav item: stroke icon stacked over a compact label, with
   app-style press-scale feedback (DESIGN.md mobile chrome). */
#divBottomBarContentWrapper .nav-bottom-item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 4px;
  padding: 9px 2px 7px;
  min-height: 56px;
  /* M2 navy deck: the tabs sit ON the field, so they wear the --field-* constants, never the
     theme's --ink ladder — the same rule the masthead's tier-1 controls follow. Ink is named on
     the CONTROLS (this item, its active state, the cart tab) and never on .nav-bottom-row, so
     nothing that hangs off the bar on a --paper surface inherits an on-navy colour. */
  color: var(--field-ink-2);
  text-decoration: none;
  transition: color 0.15s ease, transform 0.1s ease;
}

#divBottomBarContentWrapper .nav-bottom-item:active {
  transform: scale(0.92);
}

@media (prefers-reduced-motion: reduce) {
  #divBottomBarContentWrapper .nav-bottom-item:active {
    transform: none;
  }
}
#divBottomBarContentWrapper .nav-bottom-item > i {
  font-size: 1.25rem;
  line-height: 1;
}

/* B6: descendant, not `> svg`. The cart tab wraps its glyph in .nav-bottom-glyph so the
   count badge can anchor to the artwork; every tab still draws the same 23px mark. */
#divBottomBarContentWrapper .nav-bottom-item svg {
  width: 23px;
  height: 23px;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke); /* POLISH S3, P2-18: was 1.9 */
  stroke-linecap: round;
  stroke-linejoin: round;
}

#divBottomBarContentWrapper .nav-bottom-item:hover,
#divBottomBarContentWrapper .nav-bottom-item:focus,
#divBottomBarContentWrapper .nav-bottom-item:active {
  color: var(--field-ink);
}

/* Current-page indicator: a pill at the very top of the bar. On the navy field the pill is
   --field-accent and the tab's own ink goes to full white — --blue-link was a light-field colour
   and reads as ~2:1 against #051a30. */
#divBottomBarContentWrapper .nav-bottom-item.active {
  color: var(--field-ink);
  font-weight: 700;
}

#divBottomBarContentWrapper .nav-bottom-item.active::before {
  content: "";
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  /* M2 section 10.3 G1: 20px, hanging from the bar's top edge with only its BOTTOM corners rounded -
     the mockup's `.m2-tab.active::before`. 28px with all four rounded read as a floating pill
     detached from the edge it is supposed to hang off. */
  width: 20px;
  height: 3px;
  border-radius: 0 0 3px 3px;
  background: var(--field-accent);
}

/* M2 section 10.3 G1: the active tab also takes a rounded wash of the field, which is what makes the
   current tab read as a seated key rather than as brighter text. WARNING - NO BOX CHANGE: this is
   background + radius only, so the row stays 57px and .cart-strip / .sticky-atc / .cc-window /
   #divToastContainer keep bottoming out flush on that number. */
#divBottomBarContentWrapper .nav-bottom-item.active {
  background: rgba(255, 255, 255, 0.11);
  border-radius: 13px;
}

#divBottomBarContentWrapper .nav-bottom-label {
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: 0.15px;
  white-space: normal;
  text-align: center;
}

/* ---------------------------------------------------------------------------
   CART TAB — flat (Refinement B6 / D6, 2026-08-12, Juan). The 60px navy orb that
   half-embedded itself in the bar (Session B, 2026-08-10) is DELETED, with its
   specular highlight, swoosh watermark and four-layer globe shadow. Nothing
   breaks the bar's top edge any more, so the clearances that existed only to
   dodge it are gone too — see the ⚠ note under .nav-bottom-item--cart.

   Cart still reads as the primary tab, but through a tinted rounded FIELD rather
   than through elevation: the same blue the active-tab pill spends, at ~12%, in
   a pill behind the glyph and label.
   --------------------------------------------------------------------------- */
#divBottomBarContentWrapper .cart-col {
  display: flex;
  justify-content: center;
}

/* ⚠ Retired with the orb — do NOT reintroduce a bottom band on any of these
   without re-measuring first: `.cc-window.cc-bottom`'s 46px band
   (_mobile-overrides.scss), `.cart-mbar`'s 46px band (_cart.scss),
   `.sticky-atc`'s 32px band (Views/Product/Index.cshtml) and #divToastContainer's
   86px lift below. All four were 57px of tab bar + the orb's painted crest. */
#divBottomBarContentWrapper .nav-bottom-item--cart {
  /* B2-3 (2026-08-13, Juan): the tinted rounded FIELD is deleted — the 12% --blue-link wash
     and the pill radius that painted it. Cart still reads as the primary tab through its ink
     alone (the colour below, which the other four tabs do not take).
     ⚠ The box is UNCHANGED on purpose: 5 + 46 + 5 keeps the row at 57px, and .cart-mbar,
     .sticky-atc, .cc-window and #divToastContainer all bottom out against that number. Do not
     "tidy up" the margin/padding now that nothing is painted behind them.
     ~~M2 navy deck: --blue-link was the light field's primary ink; on navy the tab reads primary
     through --field-accent, one step brighter than its four --field-ink-2 siblings and distinct
     from the active tab's full white.~~ AND THAT IS REVERSED (D11, 2026-08-21, Juan: "the cart icon
     is always a different color from the other navigation buttons"). `color: var(--field-accent)` is
     DELETED here, so the tab simply inherits .nav-bottom-item's --field-ink-2 and matches its four
     siblings; the accent moves to .nav-bottom-item--cart-full below, which Knockout adds only while
     the cart has something in it. B2-3's own reasoning is what makes the reversal safe - it had
     already deleted the tinted field and the pill radius, so ink was the last thing marking this tab
     and nothing else has to be unpicked. */
  /* 5 + 46 + 5 = 56px, the sibling tabs' min-height, so the row stays 57px with its border
     and .cart-mbar / .sticky-atc / .cc-window keep bottoming out flush on it at their
     measured `bottom: 57px`. MEASURED: a 6px bottom margin made the row 58px and each of
     those three overlapped the bar's top edge by 1px. */
  margin: 5px 6px;
  padding: 4px 10px 2px;
  min-height: 46px;
}

/* D11 (2026-08-21): the accent now says "you have items", not "this tab is primary". Knockout adds
   the class from `TotalQuantity() > 0` on the .CartModel root the tab already sits inside
   (Controls/BottomBar.ascx), so there is no new subscription and no page-type test.

   It stays BELOW .nav-bottom-item.active's `color: var(--field-ink)`: that rule is
   `#divBottomBarContentWrapper .nav-bottom-item.active` at (1,2,0) against this (1,1,0), so a shopper
   standing ON /cart with a full cart still gets the active tab's full white and the seated-key wash,
   which is the distinction the active state is for. */
#divBottomBarContentWrapper .nav-bottom-item--cart-full {
  color: var(--field-accent);
}

/* The label takes whatever ink the tab is carrying rather than restating one, which is what lets the
   accent above reach it without a second selector. */
#divBottomBarContentWrapper .nav-bottom-item--cart .cart-text {
  color: inherit;
}

/* The glyph and its badge share one shrink-to-fit box, so the count is pinned to the
   ARTWORK's corner by layout instead of by hand-measured offsets (the R96 /
   2026-07-30 lesson — kept, the orb it was measured against is not). */
#divBottomBarContentWrapper .nav-bottom-glyph {
  position: relative;
  display: flex;
}

/* Count badge on the glyph's top-right corner (B7 / D9). Its colours and box are
   stated once in _cart.scss for the header and the tab bar together; only the
   anchoring lives here. */
#divBottomBarContentWrapper .nav-bottom-glyph .lbl-cart-itemcount {
  position: absolute;
  top: -6px;
  right: -10px;
  bottom: auto;
  left: auto;
  transform: none;
}

/* Cart count badges: BC1 (2026-08-14, Juan's ruling) retires the blue pill for a GREEN one —
   B2-2's --blue-link still read as part of the blue chrome it sits in, and green is unused
   anywhere else in the header, so the count separates as a positive/confirmed object.
   Overrides any .bg-* utility left in the markup: !important at equal specificity, and this
   sheet loads after Bootstrap.

   ONE value for BOTH themes, which is why the dark-theme override B2-2 needed is gone.
   MEASURED (WCAG contrast, sRGB) for #1a7f37:
     light glyph --ink #0f1b26 ....... 3.43:1   dark glyph --ink #e8eef4 ....... 4.35:1
     light masthead --paper #fff ..... 5.08:1   dark masthead --paper #101d2c .. 3.35:1
     white numerals on the pill ...... 5.08:1   mobile navy orb #051526/#050e18  3.62/3.82:1
   So it clears 3:1 against every field it lands on in either theme and 4.5:1 for its own
   numerals. Candidates rejected on measurement: #2e8b57 (white numerals 4.25:1, under AA),
   #146c43 (2.70:1 against the light glyph — the same "part of the artwork" defect B2-2 fixed).

   ⚠ Green is retired from the theme's own token set (Q1: the CTA is blue, --ok is steel), so
   this is deliberately a literal and NOT a --green/--ok/--success alias: reviving one of those
   tokens would repaint buttons and semantics sitewide. It is the badge's colour, nothing else.
   The tab-bar badge shares this rule by design (B7/D9 — one badge treatment, one place). */
.lbl-cart-itemcount {
  background-color: #1a7f37 !important;
  color: #fff !important;
}

/* POLISH S6b, P3-25 (2026-08-20): BOTH KEPT, and both are phone-chrome (M3) rules this audit was told
   not to touch without cause. `--bs-offcanvas-height` is a Bootstrap CSS VARIABLE the framework sets
   on `.offcanvas-bottom`; overriding a variable that a later framework rule consumes is exactly the
   case where source order is not enough, because `.offcanvas-bottom` is emitted in bootstrap.css and
   re-declared per breakpoint. `max-height` is a cap, per the note above. */
#navSidebarBottom.offcanvas {
  --bs-offcanvas-height:auto!important;
  max-height: 90vh !important;
}

/* M3 section 1.7 (item 15, D6, 2026-08-18): THE SCRIM BEHIND THE OFF-CANVAS.
   Bootstrap's backdrop is plain black at .5, which is the one surface in the phone dock that is not
   part of the navy family. It is re-tinted to the SHADOW colour the deck, the strip and the drawer
   all cast - rgba(4, 26, 48, ...) - so the panel and the darkened page read as one object.
   ⚠ BOOTSTRAP ALREADY OWNS THE BACKDROP ELEMENT AND THE BODY SCROLL LOCK. Only its two custom
   properties are re-pointed here; nothing re-implements the fade, the click-to-close or the lock.
   Named on .offcanvas-backdrop, not per panel: the element Bootstrap inserts carries no marker for
   which off-canvas opened it, and the drawer and the mini-cart are the same two navy panels on the
   same phone, so one tint is the consistent answer rather than a compromise. */
/* MEASURED: this Bootstrap build states the backdrop LITERALLY (skin.components.min.css:
   `.offcanvas-backdrop{...background-color:#000}` / `.offcanvas-backdrop.show{opacity:.5}`) - it is
   NOT the 5.3 custom-property form, so --bs-backdrop-bg would have re-pointed nothing. Both rules
   are matched at their own specificity and win on ORDER: Bootstrap is Priority 10 and skin.css is
   DNN's SkinCss 15 (Controls/Resources.ascx). */
.offcanvas-backdrop {
  background-color: #041a30;
}

.offcanvas-backdrop.show {
  opacity: 0.55;
}

/* ==========================================================================
   THE TOP-DOCKED BAR FAMILY - ONE PAPER FIELD FOR ALL THREE (2026-08-25, Juan)

   THE PERSISTENT CONTEXTUAL CART STRIP (.cart-strip, M2 section 10.3 G2) IS DELETED, markup and
   paint alike. It was the sitewide running total over the tab deck, and Juan's 2026-08-25 ruling is
   that a Checkout call to action belongs on /cart and nowhere else: a shopper reading a product page
   or a category shelf is not checking out, and a bar offering it on every screen turned the one
   action the cart page exists for into background furniture. /cart still has its bar - see .cart-mbar
   in _cart.scss - it is just no longer sitewide, and it is no longer on the floor.

   THE THREE SURVIVING BARS ARE TOP-DOCKED. .sticky-atc (the PDP buy bar), .cart-mbar (/cart) and
   .co-mbar (/checkout) all hang off the bottom edge of the sticky header rather than off the
   viewport floor, and each is revealed only once its own in-page control scrolls out of view.

   ⚠ THE FIELD IS PAPER, AND THE "A BAR TAKES THE FIELD OF THE HEADER IT HANGS OFF" RULE IS
   WITHDRAWN. It shipped earlier on 2026-08-25 and lasted one build. What it cost was MEASURED on
   /cart at 390: the bar's own `.cart-mbar-tot small` eyebrow computed rgb(81, 99, 111) - --ink-2, a
   dark slate - directly on the navy field, i.e. an unreadable "SUBTOTAL". The restatement that was
   supposed to prevent exactly that (`color: var(--field-ink-2)`) LOST the cascade: _cart.scss
   compiles into BOTH the skin's css/cart.css AND the MVC module's resources/styles/cart.css, and the
   module's copy is registered after skin.css, so the module's --ink-2 took the equal-specificity
   tie. A navy field therefore needs a restatement for every piece of ink on every one of these bars,
   each one a fight against a stylesheet that loads later than this file. Paper needs none: each
   bar's own paper-surface ink in _cart.scss / _product.scss is already correct, in light and in
   dark, and --paper / --rule / --ink-2 / --cta follow the theme with nothing here to keep in step.
   Juan's ruling on the pair: "switch the top bar to the same style as the bottom bar instead like it
   used to be" - .co-mbar's paint IS that bar's pre-top-dock paper, with the border and the shadow
   moved to the edge a TOP bar closes against.

   ONE BLOCK FOR ALL THREE, and it lives HERE rather than beside each bar, because two of them
   (.cart-mbar, .co-mbar) are in _cart.scss -> cart.css and one (.sticky-atc) is in _product.scss ->
   product.css. _chrome.scss is @imported by _skin.scss, so this ships in skin.css, which loads on
   EVERY page - the only file that can reach all three.

   THIS BLOCK IS NOT THEME-GATED. It replaces the `[data-bs-theme="dark"]` floor-bar family block
   (item 4a, 2026-08-19), whose whole purpose was to make a floor bar continuous with the navy deck
   BELOW it in dark mode. There is no deck under these bars any more, and --paper is theme-aware, so
   the gate has nothing left to select.

   THE CTA IS NOT RE-POINTED. The on-navy `--cta: var(--field-accent)` / #a5d1f0 trio that stood
   here went with the navy: on paper, .btn-cta's ordinary blue is the correct control, and it is the
   same button /cart's in-page rail and checkout's Place your order already spend.
   ========================================================================== */
.sticky-atc,
.cart-mbar,
.co-mbar {
  background: var(--paper);
  /* FLAT. The blur existed to let a paper panel sit over content moving behind a rounded floor
     dock; these bars are edge to edge under an opaque header and the translucency only muddies
     the join with it. */
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  /* Border and radius on the edge a TOP bar closes against. The pre-top-dock floor paint had both
     on the TOP edge - the edge that is now welded to the header. */
  border: 0;
  border-bottom: 1px solid var(--rule);
  border-radius: 0;
  /* .co-header itself states `box-shadow: none` because it is in flow and its border closes it.
     These bars are FIXED over scrolling content, so they need the lift the header does not.
     .section-header's own shadow is suppressed while a bar is docked (body.pdp-stickybar-on /
     body.topbar-on, below), so this is the ONE shadow under the header+bar pair rather than a
     second one inside it. */
  box-shadow: var(--shadow-2);
}

/* ⚠ NOTHING RESTATES THE INK HERE ANY MORE. .cart-mbar-tot and its small (_cart.scss),
   .sticky-atc-title / -price / -parts and the .sticky-atc-thumb plate's --rule border
   (_product.scss) are all paper-surface treatments already and are correct as authored. The four
   navy restatements this block used to carry are DELETED - see the cascade note above for why
   keeping them was a fight this file cannot reliably win. */
/* No header drop shadow while a bar is attached under it - the sitewide half of the rule
   .sticky-atc already carries for the PDP (see body.pdp-stickybar-on in _product.scss).
   `topbar-on` is written by skin.js's _WireTopStickyBar from the same show/hide the bars' own
   visibility uses, so the two cannot disagree.
   .co-header is included for completeness only: it already states `box-shadow: none` at every
   moment, so this arm changes nothing today and stops being a lie if that ever changes. */
body.topbar-on .section-header,
body.topbar-on .co-header {
  box-shadow: none;
}

/* ==========================================================================
   THE SAME THREE BARS, DOCKED ON THE FLOOR (2026-08-27, Juan)

   ⚠ A BAR STANDS IN FOR A CONTROL, SO IT DOCKS ON THE EDGE THAT CONTROL LEFT BY. The
   2026-08-25 build top-docked all three unconditionally, which reads backwards for the commonest
   case of the three: on a phone the buy box stacks UNDER the gallery, so a shopper landing on a
   PDP has the Add-to-cart BELOW the fold and got a bar pinned to the masthead - pointing away from
   the control it stands in for, and away from the direction they are scrolling. Trigger below the
   viewport now docks the bar on the floor; trigger scrolled past above keeps the masthead dock.
   The middle state is unchanged and is the point of the whole family: while the control is ON
   screen there is no bar at all.

   `bottom` IS `var(--deck-h)`, NOT a constant. --deck-h is the phone tab deck's own top edge,
   published onto <body> by skin.js's _MeasureDeckTop from the read that already positions the
   cookie notice and the compare bar - so the deck's height AND its `env(safe-area-inset-bottom)`
   padding are measured rather than re-stated here, and a page with no deck (checkout, a pop-up)
   resolves 0 and the bar sits on the viewport floor. The fallback covers the first frame before
   any script has run.

   THE COOKIE NOTICE NEEDS NOTHING NEW. _DockNoticeAboveBars counts any bar whose computed `bottom`
   resolves under 80px, which a deck-docked bar does (57px + the inset) and a top-docked one does
   not (`auto`) - the loop was left in place in 2026-08-25 for exactly this case. The notice lifts
   onto the bar, and the compare bar onto the notice, from that same pass.

   The reveal offset flips with the edge: a floor bar slides UP into place. `:not(.is-visible)` is
   load-bearing, not tidiness - it takes this to (0,3,0) so it cannot tie with the `.is-visible`
   rule in _cart.scss / _product.scss, which compile into stylesheets that load AFTER this one.
   ========================================================================== */
.sticky-atc.is-docked-bottom,
.cart-mbar.is-docked-bottom,
.co-mbar.is-docked-bottom {
  bottom: var(--deck-h, 0px);
  /* The closing edge follows the dock: a floor bar closes against the page ABOVE it. */
  border-bottom: 0;
  border-top: 1px solid var(--rule);
  box-shadow: 0 -4px 16px rgba(4, 26, 48, 0.22);
}

.sticky-atc.is-docked-bottom:not(.is-visible),
.cart-mbar.is-docked-bottom:not(.is-visible),
.co-mbar.is-docked-bottom:not(.is-visible) {
  transform: translateY(8px);
}

/* Mobile top bar — TWO TIERS inside the one navy field (M2 §4, 2026-08-18).
   Bootstrap's .navbar is already `display:flex; flex-wrap:wrap; align-items:center;
   justify-content:space-between`; turning the main axis vertical is the whole layout change, and
   the two tiers below are the flex lines. `align-items: stretch` is what lets tier 2 span the bar
   (the navbar's own `center` would shrink-wrap the capsule to its content).
   ⚠ NEITHER TIER MAY TAKE `position: relative` — see the note in Controls/Header.ascx: the search
   suggestions panel deliberately anchors to nav.topbar-mobile so it spans the viewport. */
.topbar-mobile {
  flex-direction: column;
  align-items: stretch;
  gap: 0.5rem;
}

/* Tier 1 — lockup left, theme toggle right. No min-height of its own: the lockup's own 34px cap
   and the toggle's 38px box set it, so nothing here has to be re-measured when either moves. */
.topbar-mobile-t1 {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

/* Tier 2 — the resident search. `.input-group` inside is already `display:flex; width:100%`, so
   this only has to be a block that fills the line. */
.topbar-mobile-t2 {
  display: block;
  width: 100%;
}

/* ============================================================================
   M2 section 10.3 G3 - TIER 1's OTHER TWO ELEMENTS: the account chip and the cart icon.
   The mockup's tier 1 is `lockup / (grow) / account chip / theme toggle / cart icon + count`.
   The 2026-08-18 build shipped the lockup and the toggle only, because section 1 of the plan recorded
   the TIERING as the delta and never diffed what the tier contains.

   WARNING - INK IS NAMED ON THESE CONTROLS, never on the tier. Same rule as the deck's tabs, the
   drawer and the search capsule's controls: the tier's field is navy in BOTH themes, so anything
   on it wears the --field-* constants. A tier-level colour would leak onto the search-suggest
   panel, which hangs INSIDE .search-header on a --paper surface (the cdv-932 lesson).
   ========================================================================== */
/* ⚠ `:visited` IS NAMED ON EVERY ANCHOR BELOW, and it is not defensive - MEASURED 2026-08-18.
   _global.scss:354 carries `a:visited { color: var(--blue-link) }` at (0,1,1), which out-ranks a
   bare class (it exists to out-rank DNN's own dnndefault a:visited). In DARK --blue-link resolves to
   the SAME #8cc0e8 these pills use as their FIELD, so the guest "Sign in" pill painted its label
   invisible the moment /login had been visited once. getComputedStyle DELIBERATELY LIES about
   :visited (anti-history-sniffing) and reported the correct #062b4c throughout, so no probe of the
   cascade could see it - only a screenshot could. Same trap as R13's `.view-details:visited` and the
   search-suggest rows (_chrome.scss:2296). */
.topbar-mobile-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: none;
}

/* Signed in: the initials disc + the first name. The name is server-rendered and already
   shortened to "Account" when it is email-shaped (Controls/Header.ascx) - a local part alone
   still overruns a 402px tier. */
/* M3 section 1.4 (item 10, D2): 999px -> --radius-md, with the avatar disc inside it untouched. */
.topbar-mobile-who,
.topbar-mobile-who:visited {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 8.5rem;
  padding: 0.25rem 0.6rem 0.25rem 0.25rem;
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.1);
  color: var(--field-ink);
  text-decoration: none;
}

/* ITEM 13: `:focus` as well as `:focus-visible` - DNN CORE's default.css declares
   `a:hover, a:focus, a:active { color: #222 }`, and each selector in that list counts on its own, so
   `a:focus` is (0,1,1) and beats this anchor's (0,1,0) colour after a POINTER tap (which sets
   `:focus` without `:focus-visible`). Measured 2026-08-19 on the cart icon that used to sit on this
   line; the icon is gone (SESSION 9 item 4) but the trap is the field's, not the icon's. */
.topbar-mobile-who:hover,
.topbar-mobile-who:focus,
.topbar-mobile-who:focus-visible {
  background: rgba(255, 255, 255, 0.18);
  color: var(--field-ink);
}

.topbar-mobile-who b {
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The accent solid with the field's own darkest stop as ink - the same pair the cookie notice's
   accept button and the drawer's node badge spend. --blue (#00467f) on this field is 1.6:1. */
.topbar-mobile-av {
  width: 27px;
  height: 27px;
  flex: none;
  border-radius: 999px;
  background: var(--field-accent);
  color: #062b4c;
  font-size: 0.72rem;
  font-weight: 800;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* Guest: the accent pill the mockup draws. Deliberately the loudest thing in the tier - it is the
   one control here that starts a task rather than continuing one. */
.topbar-mobile-signin,
.topbar-mobile-signin:visited {
  flex: none;
  padding: 0.42rem 0.8rem;
  /* M3 section 1.4 (item 10, D2): 999px -> --radius-md. */
  border-radius: var(--radius-md);
  background: var(--field-accent);
  color: #062b4c;
  font-size: 0.8rem;
  font-weight: 700;
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
}

/* ITEM 13: `:focus` as well as `:focus-visible` - DNN CORE's default.css declares
   `a:hover, a:focus, a:active { color: #222 }`, and each selector in that list counts on its own, so
   `a:focus` is (0,1,1) and beats this anchor's (0,1,0) colour after a POINTER tap (which sets
   `:focus` without `:focus-visible`). Measured 2026-08-19 on the cart icon that used to sit on this
   line; the icon is gone (SESSION 9 item 4) but the trap is the field's, not the icon's. */
.topbar-mobile-signin:hover,
.topbar-mobile-signin:focus,
.topbar-mobile-signin:focus-visible {
  background: #a5d1f0;
  color: #062b4c;
}

/* SESSION 9 item 4 (2026-08-19, Juan's decision 3): `.topbar-mobile-cart`, `.topbar-mobile-ico` and
   the badge anchor measured against its 38px box are DELETED WITH THE MASTHEAD CART ICON
   (Controls/Header.ascx). The phone's cart control is the deck's Cart tab again, whose badge anchor
   is `#divBottomBarContentWrapper .nav-bottom-glyph .lbl-cart-itemcount` further down - a different
   anchor with its own contract, which is why nothing was folded together here.
   The slot is a `.theme-toggle.theme-toggle-field` now: 38x38 from the base .theme-toggle rule, so
   the tier-1 line's geometry is unchanged and no replacement box rule is needed.
   ⚠ ITEM 13's `:focus` guard is NOT carried over and does not need to be. It existed because DNN
   CORE's default.css declares `a:hover, a:focus, a:active { color: #222 }` at (0,1,1) and beat an
   ANCHOR's (0,1,0) colour after a pointer tap. The toggle is a <button>, so that selector cannot
   match it. .topbar-mobile-who and .topbar-mobile-signin keep their own guards - they are still
   anchors on this field. */
/* ============================================================================
   M2 section 10.3 G4 - THE SCROLL-SHRINK. The mockup slims tier 1 52->44px, the lockup 26->21px,
   the capsule 46->39px and the submit 36->30px, and search never leaves the screen.
   NO NEW JAVASCRIPT: this rides `hdr-compact`, which skin.js's _InitHeaderChrome already writes on
   #HeaderContent - the DESKTOP header's compact-on-scroll state, whose four existing rules all name
   desktop-only classes (.topbar-logo, .hdr-tier1, .section-header, #navMenuMega) and none of which
   touch this bar. Reusing it inherits its HYSTERESIS (adds at 64, removes at 24, so the boundary
   cannot flicker when the class changes the header's own height), its three-way scroll read (BODY is
   the scroll container on this site, so window.pageYOffset alone stays 0), its DNN-edit-mode skip and
   its --hdr-h re-sync. The mockup's single 36px threshold is deliberately NOT copied.
   WARNING - THE BAR IS `sticky-top` AND THEREFORE IN FLOW, so its height IS page layout. Measure
   CLS on the whole Lighthouse scope (guest home, category results, PDP at 402) after ANY change
   here - section 4 measured 0/0/0 on a bar that could not change height, and that premise is gone.
   ========================================================================== */
/* SESSION 9 item 4: `.topbar-mobile .theme-toggle` is BACK in this list and in the shrink rule
   below, and `.topbar-mobile-ico` is out - the two controls traded slots (M3 section 1.5 / D12 is
   reversed). Both boxes are 38px, so the shrink target is the same 32px it always was. */
.topbar-mobile-t1,
.topbar-mobile .search-header .form-control,
.topbar-mobile #dnn_objPageHeader_dnnLOGO_SM_imgLogo,
.topbar-mobile .topbar-mobile-logo img,
.topbar-mobile .theme-toggle {
  transition: max-height 0.2s ease-out, height 0.2s ease-out, width 0.2s ease-out, padding 0.2s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .topbar-mobile-t1,
  .topbar-mobile .search-header .form-control,
  .topbar-mobile #dnn_objPageHeader_dnnLOGO_SM_imgLogo,
  .topbar-mobile .topbar-mobile-logo img,
  .topbar-mobile .theme-toggle {
    transition: none;
  }
}
.hdr-compact .topbar-mobile {
  padding-top: calc(0.25rem + env(safe-area-inset-top, 0px)) !important;
  padding-bottom: 0.25rem !important;
  gap: 0.3rem;
}

/* max-height, not height: the lockup's own cap is a max-height too, and a `height` here would
   fight it as a DIFFERENT PROPERTY rather than replace it (the 2026-08-11 lesson). */
.hdr-compact .topbar-mobile #dnn_objPageHeader_dnnLOGO_SM_imgLogo,
.hdr-compact .topbar-mobile .topbar-mobile-logo img {
  max-height: 27px;
}

.hdr-compact .topbar-mobile .search-header .form-control {
  height: 36px;
}

/* SESSION 9 item 4: the theme toggle is what shrinks on this line now, and the 38 -> 32px step is
   unchanged because both controls are 38px boxes.
   M3 section 1.3 (item 12)'s companion rule - the count badge shrinking with its 38px cart anchor -
   is DELETED with that anchor. The deck's badge is a different anchor
   (#divBottomBarContentWrapper .nav-bottom-glyph) and the deck does not shrink on scroll. */
.hdr-compact .topbar-mobile .theme-toggle {
  width: 32px;
  height: 32px;
}

/* ============================================================================
   Header chrome — Redesign v2 R1 (DESIGN.md "Procurement-grade").
   Paper top bar with the swoosh-C mark (theme-aware variant swap), a crisp
   rectangular search field with a blue Search button, and a deep-blue nav
   field carrying the mega-menu. The theme toggle is the header sun/moon.
   ========================================================================== */
/* Theme-aware logo swap: .logo-light on light paper, .logo-dark (white
   lockup) when the dark theme is active.
   HEADER REDESIGN 2026-08-17 (S1): the SITE header no longer uses this pair — its field is navy in
   both themes, so it renders the white lockup unconditionally. The rules stay because
   Controls/HeaderCheckout.ascx still carries both images. */
.logo-dark {
  display: none !important;
}

[data-bs-theme=dark] .logo-light {
  display: none !important;
}

[data-bs-theme=dark] .logo-dark {
  display: block !important;
}

/* The wordmark's RENDERED size is stated here, not in Header.ascx: the intrinsic width/height
   attributes there stay at the asset's own 144x57 because they are what gives the browser an aspect
   ratio before the SVG arrives (the Lighthouse unsized-images audit). `width: auto` preserves the
   144:57 ratio; height is the axis that matters.
   HEADER REDESIGN 2026-08-17 (S1): 44px -> 42px, and 30px in the compact state, per Concept C. The
   lockup is no longer what sets the row height either way — tier 1 states its own height below. */
.topbar-logo img {
  width: auto;
  height: 42px;
  max-height: 42px;
  transition: height 0.18s ease-out;
}

.hdr-compact .topbar-logo img {
  height: 30px;
  max-height: 30px;
}

@media (prefers-reduced-motion: reduce) {
  .topbar-logo img {
    transition: none;
  }
}
/* ---------------------------------------------------------------------------
   DESKTOP HEADER — NAVY FIELD, TWO TIERS
   (Header redesign 2026-08-17, Concept C "Merged", Session S1;
    Documentation/UI-Modernization/HEADER-REDESIGN-2026-08-17-PLAN.md §1 + §2.)

   REPLACES Session B/D1's LIGHT masthead, which itself replaced R8's navy field. Juan's §2 ruling
   settles the question the two swings were about: the header field is --blue-deep in BOTH THEMES,
   so there is ONE white lockup and no theme-driven logo swap, and everything inside wears the
   on-navy --field-* constants instead of the theme's --ink ladder. What is NOT restored from R8:
   the two tiers are inside ONE field with no hairline between them, and the field is a real
   gradient rather than a flat fill.

   The gradient is the mockup's own value, stated literally and NOT theme-swapped, which is what
   "navy in both themes" means. --blue-deep sits underneath it as the paint a browser with no
   gradient support (or a forced-colors mode) falls back to; it is also the token the mobile top bar
   and the cart orb already use, so the three navy surfaces still agree.

   Ink is TARGETED, not blanket. The rule this block replaces was
   `.section-header a { color: var(--ink) !important }`, and it needed two hand-back blocks below to
   undo itself for the mega panel and the dropdown menus — both of which are --paper surfaces
   hanging off the field, so a field-ink blanket would have made every one of their links invisible.
   Naming the three controls that actually sit ON the field is shorter and cannot leak.
   --------------------------------------------------------------------------- */
.section-header {
  background-color: var(--blue-deep);
  background-image: radial-gradient(120% 180% at 20% 0%, #0d3059 0%, #051a30 62%, #051526 100%);
  /* One hairline under the whole field (Concept C §1.1) — the light band's --rule border and the
     A4 drop shadow are both gone: a navy field is closed by a light hairline, not by a shadow. */
  border-bottom: 0;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.14);
  color: var(--field-ink);
}

/* Both tiers are transparent so the one field shows through. `.navbar-light` is no longer written
   on either of them, but the selector stays: DNN's own skin objects and any module dropped into a
   header pane can still emit one. */
/* ============================================================================
   POLISH S6b, P3-25 (2026-08-20): THE HEADER FAMILY - 33 !important declarations from here to the end
   of the phone masthead, all KEPT, with the competition named once for the whole family rather than
   38 times. HEADER-REDESIGN-2026-08-17 closed with every header rule in this file, and they fall into
   four measured fights:

   (1) ANCHOR COLOUR ON THE NAVY FIELD (.hdr-acct-link, .hdr-contact-tel, .section-header .btn-cart,
       #navMenuMega .mm-top-link, and their :hover / :focus-visible arms - 10 declarations).
       DNN core's default.css declares `a, a:visited { color: … }` AND
       `a:hover, a:focus, a:active { color: #222 }`, and EACH SELECTOR IN THOSE LISTS COUNTS ON ITS
       OWN - so `a:focus` is (0,1,1) and beats any bare class at (0,1,0). Every one of these anchors
       sits on the navy masthead where #222 is unreadable, and the failure mode is a POINTER TAP, not
       a hover, so it survives a mouse-only check. This is the same family as
       [[a-visited-beats-a-bare-class-and-getcomputedstyle-lies]] and it is why item 13 of the S1
       floor-bar session turned out to be DNN core rather than sticky hover.

   (2) BOOTSTRAP UTILITIES THE MARKUP WEARS (.hdr-tier1 / .hdr-tier2 flex-wrap, .main-nav-bar
       border-top/bottom, .section-header .navbar background-color, .section-header .btn-cart
       padding/margin - 9 declarations). Every `.flex-*`, `.border-*`, `.bg-*`, `.p-*` and `.m-*`
       utility Bootstrap emits carries !important, and these elements carry those utilities in
       Default.ascx / Header.ascx. An !important utility is only beatable by another !important.
       The .btn-cart pair has its own longer note below naming the VENDOR menukit.css rule as well.

   (3) A VALUE skin.js WRITES INLINE (.section-header .dropdown-menu min-width). skin.js sets an
       inline `min-width` on every `.dropdown-menu` (header-redesign S1 note), and an inline
       declaration beats any author selector - so only !important can hold the 14rem floor.

   (4) THE SEARCH CAPSULE (.search-header .form-control / .btn-search-submit / .btn-search-voice and
       their :hover arms, both the desktop and .topbar-mobile copies - 12 declarations). These paint
       INSIDE a Bootstrap `.input-group`, whose own `.input-group > :not(:first-child)` and
       `.form-control` rules restate border-radius and border at (0,2,0)+ per position, and the
       buttons additionally wear `.btn`, which sets background/border/color from `--bs-btn-*` at
       (0,1,0) with a `:hover` arm at (0,2,0). Both search capsules are `#fff` in BOTH themes and the
       standing rule from the header redesign is to name the CONTROLS, never the group - so the
       overrides live here, per control, and they have to out-rank the input-group grammar they sit
       inside.

   Two more in this range have their own inline notes: the `.hdr-compact .topbar-mobile` and
   `.topbar-mobile` padding pairs (safe-area insets, phone chrome - M3, not to be touched), and the
   `.topbar-mobile … imgLogo` margin.
   ============================================================================ */
.section-header .navbar,
.section-header .navbar-light,
.main-nav-bar {
  background-color: transparent !important;
}

/* TIER 1 — lockup · search · account · cart · theme toggle.
   `.navbar > .container` is ALREADY a flex row in Bootstrap (display:flex, align-items:center,
   justify-content:space-between, flex-wrap:inherit), so this only states the gap, the height and
   `nowrap`. nowrap is load-bearing: .navbar sets flex-wrap:wrap and the container inherits it, so
   at 768 the utilities would drop to a second line inside a fixed-height box and be clipped.
   The height is stated on the tier rather than left to the tallest child, because the compact state
   is a height transition and there is nothing to animate if the box is auto. */
.hdr-tier1 {
  flex-wrap: nowrap !important;
  gap: 1.25rem;
  height: 78px;
  transition: height 0.18s ease-out;
}

@media (min-width: 1200px) {
  .hdr-tier1 {
    gap: 1.875rem; /* the mockup's 30px, once there is width to spend on it */
  }
}
.hdr-compact .hdr-tier1 {
  height: 56px;
}

.topbar-logo {
  flex: none;
  display: block;
  line-height: 0; /* kills the inline-image descender gap so the lockup centres on the tier */
}

/* The utilities group. `margin-left: auto` is belt-and-braces next to the container's own
   space-between: the search field is the flex item that grows, so this only matters on a
   hypothetical page where it is absent. */
.hdr-utils {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex: none;
  margin-left: auto;
}

/* S4 (Juan, 2026-08-18): the cluster read as scrunched, and the CART BADGE is why the cart and the
   theme toggle in particular did. MEASURED signed in at 1440 with 9+ in the cart: the count badge
   hangs off .btn-cart-ico's top-right corner and ended at x 1332 against a toggle starting at 1337 —
   5px of clear air, i.e. the badge nearly touched the toggle's border while the anchors themselves
   were a nominal 8px apart. The gap goes up and the toggle takes an extra margin of its own, which
   moves the account slot and the cart LEFT into the ~250px of dead air tier 1 has between the
   720px-capped search capsule and the utilities (also measured at 1440).
   The base gap is .75rem and the toggle's own margin .25rem, which is as much as the narrow widths
   can spend: MEASURED at 768 and 820 the search field is 315px, and every pixel these gaps take
   comes out of it (315 to 303, still ~60px more than the short placeholder needs). The badge/toggle
   clearance there goes 5px to 13px. From 992px up, where tier 1 has ~250px of dead air, they open
   right up. */
.hdr-utils > .theme-toggle {
  margin-left: 0.25rem;
}

@media (min-width: 992px) {
  .hdr-utils {
    gap: 1.25rem;
  }
  .hdr-utils > .theme-toggle {
    margin-left: 0.5rem;
  }
}
/* TIER 1 SEARCH — Concept C's Counter-Ledger capsule on the navy field: the GROUP is the white
   control, with the input, the mic and the submit living inside it flush and transparent. This is
   the same construction as the phone chrome's pill (.topbar-mobile .search-header, further down),
   which is where the pattern was proved.
   ⚠ NO `overflow: hidden` HERE. The suggestions panel skin.js builds is a CHILD of .search-header
   (position:absolute, top:100% + 6px), so clipping the group would hide it outright — the reason
   the corners are rounded on the children instead of on the group's overflow. */
/* WHITE IN BOTH THEMES (Juan, 2026-08-17, after S1's dark sanity check): `var(--paper)` made this
   #101d2c in dark, and Concept C draws the capsule white on the navy field either way. The phone pill
   takes the same treatment — see the shared ink block below, which is where the consequence of a
   constant surface is handled. */
.section-header .search-header {
  flex: 1 1 auto;
  flex-wrap: nowrap;
  min-width: 0;
  max-width: 720px;
  height: 50px;
  background: #fff;
  border-radius: var(--radius-md);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22);
  transition: height 0.18s ease-out;
}

.hdr-compact .section-header .search-header {
  height: 40px;
}

.section-header .search-header:focus-within {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22), 0 0 0 2px var(--field-accent);
}

/* A CONSTANT SURFACE NEEDS CONSTANT INK — the desktop capsule and the phone pill are both #fff in
   BOTH themes, so the controls inside them cannot keep reading the theme's ink ladder: in dark --ink
   is #e8eef4 and --blue-link is #8cc0e8, i.e. white-on-white text and a ~1.9:1 hover. Re-pointing the
   four tokens the controls actually read is what keeps every rule below correct without restating one
   of them (the input's colour, the placeholder's and the mic's --ink-2, the submit's --cta, both
   hovers' --blue-link).
   ⚠ THE CHILD COMBINATOR IS LOAD-BEARING. skin.js appends `.search-suggest` INSIDE .search-header, and
   that panel is its own --paper surface: on a group-level repoint it inherited the LIGHT ink and its
   brand/category rows rendered #0f1b26 on the dark #101d2c panel in dark theme — invisible, while
   light measured perfectly. Naming the three controls keeps the panel on the theme's own tokens. */
.section-header .search-header > .form-control,
.section-header .search-header > .btn-search-voice,
.section-header .search-header > .btn-search-submit,
.topbar-mobile .search-header > .form-control,
.topbar-mobile .search-header > .btn-search-voice,
.topbar-mobile .search-header > .btn-search-submit {
  --ink: #0f1b26;
  --ink-2: #51636f;
  --cta: #00467f;
  --blue-link: #00579e;
  /* S4 (2026-08-18) — the SAME constant-surface argument, for the focus ring. The block below
     points --focus at `var(--blue-link)` for the header's --paper surfaces, and that var is
     substituted where it is DECLARED (on .search-header), so it resolved to the THEME's
     --blue-link: MEASURED in dark with the mic focused, `outline: 2px solid rgb(140, 192, 232)`
     on a #fff capsule = 1.94:1, i.e. no visible keyboard focus at all inside either capsule.
     A constant white surface needs a constant ring, so it joins the four tokens above ($c-blue-link
     on #fff measures 7.35:1). Naming it HERE and not on the group is deliberate for the same reason
     the ink is: .search-suggest hangs inside .search-header and is a THEMED --paper surface, whose
     own ring must stay #8cc0e8 in dark (8.9:1 on #101d2c). */
  --focus: #00579e;
}

/* BL1 (Session BL, 2026-08-14) still applies and the !important is not decoration: the input is not
   really :first-child (the visually-hidden <label> is), and the submit stops being :last-child the
   moment the suggestions panel is appended as a new sibling — so Bootstrap's own
   `.input-group > :not(:first-child)` rules (5 :not() clauses, higher specificity than this
   3-class selector) win and flatten both ends. */
.section-header .search-header .form-control {
  height: 100%;
  border: 0;
  border-radius: var(--radius-md) 0 0 var(--radius-md) !important;
  background: transparent;
  padding-left: 1.05rem;
  color: var(--ink);
  font-weight: 500;
}

.section-header .search-header .form-control:focus {
  background: transparent;
  box-shadow: none;
}

/* Icon-only submit, accent GLYPH and not a filled slab (Concept C §1.2): inside a white capsule the
   capsule is the affordance. This supersedes Session B/D1's solid --cta button, which existed
   because the light masthead's field had no edge of its own to read against. */
.section-header .search-header .btn-search-submit,
.section-header .search-header .btn-search-submit:visited {
  border-radius: 0 var(--radius-md) var(--radius-md) 0 !important;
  padding-left: 1rem;
  padding-right: 1.15rem;
  background: transparent !important;
  border: 0 !important;
  color: var(--cta) !important;
}

.section-header .search-header .btn-search-submit:hover,
.section-header .search-header .btn-search-submit:focus-visible {
  background: transparent !important;
  color: var(--blue-link) !important;
}

/* ACCOUNT SLOT — one treatment for the guest and the signed-in state (they share .hdr-acct-slot so
   they cannot drift apart; 2026-08-17 batch, Session 1 item 1). The min-height pair that rule used
   to carry is GONE: it existed to stop the two states setting different heights for a
   `align-items: center` grid row, and tier 1 now states its own height, so there is no row left to
   collapse. */
.hdr-acct-slot,
.hdr-acct {
  display: flex;
  align-items: center;
  flex: none;
}

.hdr-acct {
  position: relative; /* the dropdown's containing block (data-bs-reference="parent") */
}

.hdr-acct-link {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  max-width: 13rem;
  padding: 0.5rem 0.65rem;
  border-radius: var(--radius-sm);
  color: var(--field-ink) !important;
  font-family: var(--ff-u);
  font-weight: 600;
  font-size: 0.875rem;
  line-height: 1.15;
  text-decoration: none;
  white-space: nowrap;
  transition: background-color 0.15s ease;
}

.hdr-acct-link:hover,
.hdr-acct-link:focus-visible {
  background: rgba(255, 255, 255, 0.08);
  color: var(--field-ink) !important;
}

.hdr-acct-glyph {
  font-size: 1.15rem;
  flex: none;
}

.hdr-acct-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* The sub-line: "Hi, {first}" signed in, "Log in / register" as a guest. .hdr-greeting keeps its own
   W4 rule below for the long-email fallback (.text-truncate only ellipsises a BLOCK box). */
.hdr-acct-sub {
  color: var(--field-ink-3);
  font-weight: 400;
  font-size: 0.6875rem; /* 11px, the mockup's caption tier */
  line-height: 1.15;
}

.hdr-acct-title {
  color: var(--field-ink);
  line-height: 1.15;
}

/* Bootstrap's own caret on the toggle, quietened to the field's tertiary ink. */
.hdr-acct-link.dropdown-toggle::after {
  margin-left: 0.1rem;
  color: var(--field-ink-3);
}

/* W4 (2026-08-13, Juan): `display: block` is what makes the .text-truncate beside it work at all —
   overflow does not apply to a non-replaced INLINE box, so the no-first-name fallback (display name,
   then the username, which is an email address for most accounts) previously ran past its box
   instead of ellipsising. The cap is .hdr-acct-link's max-width now, not a grid column. */
.hdr-greeting {
  display: block;
  max-width: 100%;
  font-size: 0.6875rem;
}

/* The account panel is a --paper surface hanging off the navy field, so its ink is the THEME's.
   ⚠ THIS IS A STANDING OBLIGATION, not a one-off: anything new inside a header dropdown needs a
   line here, or it inherits the field ink and paints white on white in the light theme. */
.section-header .dropdown-menu,
.section-header .dropdown-menu a,
.section-header .dropdown-menu .dropdown-item {
  color: var(--ink) !important;
}

/* ⚠ THE PANEL HAS TO BE POSITIONED EXPLICITLY, and this is the one thing the flex tier broke.
   MEASURED on devbeta before the fix (1440, signed in): the open menu's box was
   y -83..166, i.e. hanging above the top of the viewport, with `top` computing to -103.9px from NO
   author rule. The toggle carries `data-bs-display="static"`, so Bootstrap does NOT run Popper and
   does NOT write a `data-bs-popper` attribute — which is exactly what its own
   `.dropdown-menu[data-bs-popper] { top: 100%; left: 0 }` is keyed on. With top/left left at `auto`
   an absolutely-positioned box falls back to its STATIC position, and its parent is now a flex line
   with `align-items: center`, so a 249px panel was centred on a 45px trigger. In the old markup the
   parent was a block `.col-12`, where the static position happened to be right under the trigger —
   the panel was never positioned by anything, it just looked like it was.
   min-width !important: skin.js's _OnLoadUIUpdates writes an inline
   `min-width: <trigger width>px` on every .dropdown-menu as a FLOOR (142.9px here), and an inline
   declaration beats any selector — so the wider floor this menu actually needs has to shout. */
.section-header .dropdown-menu {
  min-width: 14rem !important;
  margin-top: 0.25rem;
}

.hdr-acct > .dropdown-menu {
  top: 100%;
  bottom: auto;
  left: auto;
  right: 0;
}

/* CART + THEME TOGGLE on the field. The cart anchor's own box (38px) and glyph (24px) are in
   _cart.scss and unchanged by the move to tier 1; only its ink belongs to the field. */
.section-header .btn-cart,
.section-header .btn-cart:hover,
.section-header .btn-cart:focus-visible,
.section-header .lbl-cart-amount {
  color: var(--field-ink) !important;
}

.section-header .btn-cart {
  /* `position: relative` stays: the count badge is position:absolute off .btn-cart-ico INSIDE this
     anchor, and a stray positioned ancestor further up would otherwise become its containing
     block. The 2026-08-16 top/padding nudges are gone with the row they were measured in.
     ⚠ BOTH vertical paddings, and !important on both: the anchor wears `nav-link` and the VENDOR
     menukit.css carries `@media (min-width: 992px) { .navbar .nav-link { padding: 1rem .7rem
     !important } }`, which would otherwise add 32px to a 78px tier (56px compact) and put the
     glyph off-centre the moment only ONE side was zeroed. _cart.scss already fights the same
     vendor rule for the left/right pair. Specificity ties at (0,2,0) with !important on both
     sides, and skin.css loads after skin.components.css, so this wins on order. */
  position: relative;
  padding-top: 0 !important;
  padding-bottom: 0 !important;
  margin-top: 0 !important;
  top: 0;
}

/* TIER 2 — the mega-menu tabs, left; the chat-first contact line, right.
   The 2026-08-16 three-track grid (minmax(0,2fr) minmax(0,6fr) minmax(0,4fr) plus the restated
   -.75rem row margins) that centred the tabs UNDER THE SEARCH BOX is DELETED: Concept C aligns the
   tabs on the container's left edge, under the lockup, so there is nothing left to centre against.
   The container is again a plain Bootstrap navbar flex line (space-between).
   ⚠ menu.js writes .mm-anchor on `root.closest('.container')`, i.e. on this element, and the mega
   panel is positioned against it — so it must stay a .container. */
.main-nav-bar {
  border-top: 0 !important;
  border-bottom: 0 !important;
  padding-top: 0;
  padding-bottom: 0;
}

/* gap 1.5rem, not 1rem: MEASURED at 1024, where the tab strip is 563px and the contact line 354px in
   a 976px content box - the tabs' own side padding left only ~19px of clear air between "Customer
   Service" and "Chat with us", so the two read as one run-on line. 24px separates them. */
.hdr-tier2 {
  flex-wrap: nowrap !important;
  gap: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}

/* The contact line. Chat leads and wears the accent (copy law 9b); the number and the hours are the
   quiet half. It shrinks before the tab strip does — the tabs carry `flex: none` in menu.css — and
   the hours drop out below lg, where the tabs need the width more than the line does. */
.hdr-contact {
  display: flex;
  align-items: baseline;
  gap: 0.3rem;
  flex: 0 1 auto;
  min-width: 0;
  padding: 0.35rem 0;
  color: var(--field-ink-3);
  font-family: var(--ff-u);
  font-size: 0.8125rem;
  line-height: 1.25;
  white-space: nowrap;
}

.hdr-contact-chat {
  padding: 0;
  background: none;
  border: 0;
  color: var(--field-accent);
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}

.hdr-contact-chat:hover,
.hdr-contact-chat:focus-visible {
  color: var(--field-ink);
  text-decoration: underline;
}

.hdr-contact-tel {
  color: var(--field-ink-2) !important;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.hdr-contact-tel:hover,
.hdr-contact-tel:focus-visible {
  color: var(--field-ink) !important;
  text-decoration: underline;
}

@media (max-width: 991.98px) {
  .hdr-contact-rest {
    display: none;
  }
}
/* ==========================================================================================
   TRUST RAIL, PLACEMENT T1 - the review claim in a header bar.
   SURFACE-SWAP-TRUST-RAIL-PLAN-2026-08-21, S5.

   ONE component, TWO hosts: the sitewide masthead's tier-2 row (Header.ascx, .hdr-tier2) and the
   checkout bar's right cell (HeaderCheckout.ascx, .co-header-end). Both controls carry the same
   markup, so a difference between the two bars can only ever be a markup difference, never a CSS
   one.

   ⚠ xxl ONLY, AND IT IS A MEASUREMENT, NOT A TASTE. xxl HERE IS 1440px, NOT BOOTSTRAP'S 1400:
   _variables.scss:523 overrides $grid-breakpoints to (xxl: 1440px) and :525 overrides
   $container-max-widths to (xxl: 1344px). A 1399.98px gate was built first and MEASURED WRONG - at a
   1400-1439 viewport it showed T1 while the container was still the 1140/1116 xl one, and the row
   overflowed by 78px with `document.scrollWidth` reporting nothing (the row is the overflowing box,
   not the page). Any future gate on this row must be read off :523, not off Bootstrap's defaults. T1 is 232px wide (five 12px stars
   plus "Customer-rated excellent" at 13px) and neither host has that much slack below the xxl
   container:

     masthead tier 2 - flex-wrap:nowrap, both items nowrap, so the row has nothing to give:
       content 1320 (>=1440)       tabs 723 + contact 210 + T1 229 + 2 gaps 48 = 1210 -> 110 spare  FITS
       content 1116 (1200-1439)    tabs 723 + contact 210 + 2 gaps 48 = 981           -> 135 free, T1 needs 253
       content 936  (992-1199)     tabs 563 + contact 210 + 2 gaps 48 = 821           -> 115 free, T1 needs 253
       content 696  (768-991)      .hdr-contact-rest already hidden above  = 610       -> 86 free,  T1 needs 253
       below 768 this row does not render at all (.topbar-mobile is the phone chrome)

     checkout bar - F7's 1fr/auto/1fr grid, where the SIDE COLUMN is the cap, because a right cell
     wider than its column pushes the centred SECURE CHECKOUT stamp off the container's centre line
     (the exact BD7 failure, measured 2026-08-14):
       column 566 (>=1440)      seal 62 + gap 14 + chat 151 = 227, + T1 243 = 470  -> FITS in 566
       column 464 (1200-1439)                                        470           -> OVERFLOWS 464

   The 144px of opening hours came OUT of the masthead contact line to buy the xxl fit (Juan's
   ruling 2026-08-21, on these numbers) - see the note in Header.ascx. Even with them gone, the xl band
   is 118px short, which is why this is a hide and not a reflow.

   `--gold` is the repo's own review-star ornament token and is tuned per theme, so the stars need no
   dark override on either host. */
.hdr-trust {
  display: inline-flex;
  align-items: baseline;
  flex: none;
  gap: 0.5rem;
  padding: 0.35rem 0;
  /* The paper default serves the CHECKOUT bar, whose field is --paper; the navy masthead override
     is the one exception below. .co-help beside it uses --ink-2 on the same field. */
  color: var(--ink-2);
  font-family: var(--ff-u);
  font-size: 0.8125rem;
  line-height: 1.25;
  white-space: nowrap;
}

.hdr-trust__stars {
  display: inline-flex;
  gap: 0.09em;
  color: var(--gold);
  font-size: 0.75rem;
}

/* A TRANSPARENT border-bottom, not a text-decoration: _global.scss's sitewide
   `a:not(.underline) { text-decoration: unset !important }` beats any decoration this rule could
   set, hover included, so the underline has to be drawn as a border (2026-08-19, S7). */
.hdr-trust__txt {
  border-bottom: 1px solid transparent;
}

.hdr-trust:hover .hdr-trust__txt,
.hdr-trust:focus-visible .hdr-trust__txt {
  border-bottom-color: currentColor;
}

/* 2026-08-25 item 4 (Juan): "the desktop header should say 'Customer-rated excellent', only tablet
   and lower resolutions views should get the abbreviated version. This includes the /checkout page."

   ONLY THE CHECKOUT BAR CARRIES THE PAIR. The masthead rail is hidden outright below 1440 by the
   gate further down this file, so that host renders the long label unconditionally and has a single
   span (Header.ascx). This pair is HeaderCheckout.ascx's, because that bar renders the rail at every
   width down to 320.

   The swap is `display: none` rather than a visually-hidden class on purpose: the hidden label must
   leave the accessibility tree, or a screen reader announces the claim twice.

   992 is the lg boundary - laptop and desktop take the long label, tablet and phone the short one.
   The short one is what the phone bar's one-row budget is measured against (rail 170px, not 229). */
.hdr-trust__txt--short {
  display: none;
}

@media (max-width: 991.98px) {
  .hdr-trust__txt--long {
    display: none;
  }
  .hdr-trust__txt--short {
    display: inline;
  }
}
/* Navy masthead field. --field-ink-3 is what .hdr-contact beside it already uses, so the two read
   as one quiet line rather than two weights. */
.hdr-tier2 .hdr-trust {
  color: var(--field-ink-3);
}

.hdr-tier2 .hdr-trust:hover,
.hdr-tier2 .hdr-trust:focus-visible {
  color: var(--field-ink);
}

/* 2026-08-21 (Juan): THE GATE IS MASTHEAD-ONLY NOW - `.hdr-tier2 .hdr-trust`, not `.hdr-trust`.

   Everything the block above measures still holds for the masthead: tier 2 is `flex-wrap: nowrap`
   with two nowrap items, the xl band is 118px short of what T1 needs, and even with the opening
   hours gone there is no reflow that fits it - so that host keeps the hide.

   The checkout bar does not, because the rail no longer lives in the cell the measurement was taken
   in. It sits in the CENTRE cell beside the SECURE CHECKOUT stamp (see HeaderCheckout.ascx), and
   F7's `1fr auto 1fr` sizes that cell to its own content with the two 1fr columns absorbing the
   difference - so the rail costs that cell, not the third column's WIDTH, which is what the
   464-vs-470 overflow was. The phone band below 576 keeps it too, on one row with the stamp.

   ⚠ 2026-08-25, SECOND PASS - THE NUMBERS ABOVE ARE LIVE AGAIN ON THIS HOST. The morning's reword to
   "Rated excellent" took the rail ~60px narrower and this note recorded that the xl band would then
   fit; item 4 the same day put the masthead BACK to "Customer-rated excellent" (desktop-only host,
   see Header.ascx), so T1 is 229px again and every figure in the block above holds as written. The
   short label survives only on the checkout bar, below 992 - see .hdr-trust__txt--long/--short.
   THE GATE STAYS AT 1439.98 - RULED BY JUAN 2026-08-25, ON A FRESH MEASUREMENT, AND THE OLD
   xl FIGURES IN THE BLOCK ABOVE ARE SUPERSEDED FOR THE xl BAND ONLY. Measured live at cdv 1154, on
   the row itself rather than from the plan: below 1440 the container is 1140 (xl), the row spends
   tabs 723 + contact 207 with ONE 24px gap, so 186px is free. Adding the rail makes three items and
   TWO gaps, which leaves the rail 1140 - 723 - 207 - 48 = 162px:
     "Customer-rated excellent"  229px  -> over by 67   no
     "Rated excellent"           170px  -> over by  8   no, but only just
   i.e. the xl band is 8px short, NOT the 118px the block above states - those numbers were taken
   while the contact line still carried the opening hours (210px + the hours), and the line is 207px
   now. The only clean 8px is this row's own column-gap at 24 -> 20px, and that leaves ZERO slack on
   a `flex-wrap: nowrap` row whose items are all nowrap: a font fallback or one word added to the
   contact line overflows it, and `document.scrollWidth` reports NOTHING when the ROW is the
   overflowing box (that is how the first 1399.98 gate shipped 78px broken). Declined on that basis.
   Re-measure THE ROW, not the plan, before touching it. */
@media (max-width: 1439.98px) {
  .hdr-tier2 .hdr-trust {
    display: none;
  }
}
/* FOCUS RINGS ON THE FIELD. MEASURED by tabbing the header on devbeta: every control came back with
   `outline-color: rgb(0, 70, 127)` — _global.scss's sitewide
   `:focus-visible { outline: 2px solid var(--focus) !important }`, i.e. COLAMCO blue, which is
   ~1.9:1 against --blue-deep and effectively invisible on this field.
   Re-pointing the TOKEN rather than adding a competing outline rule is what avoids an !important
   fight with that sitewide rule: a custom property is inherited, so the global rule itself paints
   the accent inside the header and nothing about it has to change.
   The --paper surfaces put --focus back to the theme's own accent, because the field accent on white
   is the same failure in reverse: the account menu, the mega panel, and the white search capsule,
   whose input/mic/submit all sit on paper (the capsule's own :focus-within accent ring is what
   answers focus against the NAVY, and it is stated above). */
.section-header {
  --focus: var(--field-accent);
}

.section-header .dropdown-menu,
#navMenuMega .mm-panel,
.section-header .search-header {
  --focus: var(--blue-link);
}

@media (prefers-reduced-motion: reduce) {
  .hdr-tier1,
  .section-header .search-header,
  .hdr-acct-link {
    transition: none;
  }
}
/* MEGA MENU HAND-BACK 2026-07-30. Until the Header.ascx div fix of the same date, .main-nav-bar
   rendered OUTSIDE .section-header, so none of the ink rules above ever reached #navMenuMega.
   Now that the markup is balanced they do — and `.section-header a { color: … !important }`
   matches every link in the mega PANEL as well as the nav row's own top links.
   #navMenuMega is its own designed component (Menus/Mega/menu.css, phase 3.1) and already spends
   the correct token on every part. An id selector outscores the class-only block above, so these
   restate menu.css's own values to hand the component back to it rather than un-scoping that block
   (which #navMenuMega_Right, the phone and Contact Us links, genuinely wants).
   Keep in step with Menus/Mega/menu.css if its colours change.

   HEADER REDESIGN 2026-08-17 (S1): Session B's "the nav row is a LIGHT field now" is DEAD — tier 2
   is inside the navy field, so the TABS take the on-navy ink ladder while everything inside the
   PANEL (a --paper surface) keeps the theme's, which is what the blocks below this one state.
   The open/hover treatment is Concept C's: --field-ink plus a 3px --field-accent underline, and NO
   fill. menu.css paints `background: var(--ground)` on hover and a 2px --cta bottom border on the
   open tab for the light row it was drawn against; both would be a light slab on navy, so they are
   overridden here rather than in menu.css, which S2 rewrites. An id beats menu.css's 3-class
   selectors, so only the colour needs !important (to clear the same-file blanket history). */
#navMenuMega .mm-top-link {
  color: var(--field-ink-2) !important;
  border-bottom-width: 3px;
}

#navMenuMega .mm-top-link:hover,
#navMenuMega .mm-top-link:focus-visible,
#navMenuMega .mm-top.open > .mm-top-link {
  background: transparent;
  color: var(--field-ink) !important;
}

#navMenuMega .mm-top.open > .mm-top-link {
  border-bottom-color: var(--field-accent);
}

/* The tabs slim with tier 1 (Concept C §1.6 slims the tab padding along with the tiers). menu.css
   owns the RESTING value and it is deliberately left alone: the mockup's 13px is pre-tightening,
   and the 2026-08-16 batch (Session 1 item 7, Juan) took it to 0.62rem = 9.9px on purpose. This is
   only the compact half, scaled by the mockup's own 13:9 ratio -> 0.38rem = 6.1px. */
.hdr-compact #navMenuMega .mm-top-link {
  padding-top: 0.38rem;
  padding-bottom: 0.38rem;
}

/* THE PANEL IS A --paper SURFACE, and this is the ONE line that has to say so.

   HEADER REDESIGN 2026-08-17 (S2): the four blocks that used to sit here - a `color: var(--ink)
   !important` list of every anchor class in the panel, a `--blue-link !important` hover list beside
   it, and two on-navy blocks for `.mm-promo-link` and the `.mm-ad` cards - are DELETED, along with
   the "any new ANCHOR inside .mm-panel needs a line here" obligation they carried. They existed to
   undo a sitewide `.section-header a { color: ... !important }` blanket that S1 already replaced
   with three named on-field controls, so nothing is left to undo: menu.css states its own colours
   and no longer loses to anything.
   What DOES still reach in is the field's inherited `color: var(--field-ink)` on .section-header,
   which is white and would paint every uncoloured span inside the panel invisible. An id selector
   outranks that class, and it needs no !important because nothing competes with it any more. */
#navMenuMega .mm-panel {
  color: var(--ink);
}

/* DARK-MODE HOVER FIX 2026-07-30: bootstrap-menukit paints the hovered dropdown ROW with a
   hardcoded light `#f1f1f1` (resources/libraries/bootstrap-menukit/menukit.scss:110, bundled into
   skin.components.css). That literal never flips with the theme, so in dark mode any part of the
   row not covered by the .dropdown-item <a> — the divider rows, and every side of a padded
   `li.nav-item.p-1` like the Contact Us menu uses — turned near-white underneath light --ink text
   and became unreadable. --paper-2 is #f4f6f8 in light theme, so this is a 3-unit no-op there and
   a correct dark panel in dark. Fixed here rather than in the vendored library so menukit stays
   an unmodified drop-in; our sheet loads after skin.components and the selector matches its
   specificity exactly (0,2,1). The media query is dropped on purpose — the paint should be
   theme-correct at every width, not only >=992px. */
.dropdown-menu > li:hover {
  background-color: var(--paper-2);
}

/* ---------------------------------------------------------------------------
   VOICE SEARCH (2026-08-16 batch, Session 1 item 1, Juan). The mic button sits between the field
   and the submit inside every .search-header input group.

   ⚠ `[hidden]` IS NOT ENOUGH ON ITS OWN HERE. Header.ascx renders the button with the `hidden`
   attribute so a browser without the Web Speech API never shows a dead control, and skin.js drops
   the attribute only where SpeechRecognition / webkitSpeechRecognition exists. But `hidden` is a
   UA-stylesheet `display: none` and ANY author `display` beats it — including Bootstrap's own
   `.btn { display: inline-block }`, never mind the `inline-flex` below. The explicit
   `[hidden] { display: none !important }` is what makes the feature-detect actually hide it.

   The base treatment is the QUIET one (transparent, ink-2) because the mobile top bar's search is a
   single white capsule with transparent controls inside it. The desktop masthead's field is a sunk
   --ground rectangle between two 1px --input edges, so there the button restates that field so the
   group still reads as one control. */
.search-header .btn-search-voice {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0 0.85rem;
  background: transparent;
  border: 0;
  border-radius: 0 !important;
  color: var(--ink-2);
  transition: color 0.15s ease;
}

.search-header .btn-search-voice[hidden] {
  display: none !important;
}

.search-header .btn-search-voice svg {
  width: 18px;
  height: 18px;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke); /* POLISH S3, P2-18 */
  stroke-linecap: round;
  stroke-linejoin: round;
}

.search-header .btn-search-voice:hover,
.search-header .btn-search-voice:focus-visible {
  color: var(--blue-link);
  background: transparent;
}

/* Listening state: the accent plus a slow pulse, so the shopper can tell the mic is live without a
   second element to announce it. aria-pressed on the button carries the same fact to assistive tech. */
.search-header .btn-search-voice.is-listening {
  color: var(--cta);
  animation: voicePulse 1.1s ease-in-out infinite;
}

@keyframes voicePulse {
  50% {
    opacity: 0.4;
  }
}
@media (prefers-reduced-motion: reduce) {
  .search-header .btn-search-voice.is-listening {
    animation: none;
  }
}
/* Tier 1's capsule is WHITE and the mic sits inside it (header redesign 2026-08-17, S1), so the
   restatement of the old sunk --ground field's fill and hairlines is gone: transparent, like the
   phone pill's mic, and the group's own focus ring is what answers focus. */
.section-header .search-header .btn-search-voice {
  background: transparent;
  border: 0;
}

/* =====================================================================
   R9 — search suggestions ("typeahead-lite"). Brands + categories only,
   from lists the storefront already caches, plus an always-present
   "Search all products" row. The panel is appended to .search-header by
   COLAMCO.CurrentPage (skin.js), so it inherits the field's width and
   works for the desktop masthead, the phone chrome and the checkout
   header from one place.
   ===================================================================== */
.search-header {
  position: relative;
}

.search-suggest {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 1035; /* above the sticky masthead (1020) */
  max-height: min(60vh, 420px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--paper);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-3);
  padding: 0.35rem 0;
  text-align: left;
}

.search-suggest__group {
  padding: 0.5rem 0.85rem 0.3rem;
  color: var(--ink-2);
  font-family: var(--ff-u);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

/* `:visited` is named explicitly: a global `a:visited` rule out-ranks a bare class
   (0,2,0 over 0,1,0), so an already-seen destination rendered blue while its neighbours
   were ink - measured 2026-08-18 once the Pages group started suggesting pages a shopper
   really does revisit (/myaccount?page=orders). Suggestion rows are a list, not prose links. */
.search-suggest__item,
.search-suggest__item:visited {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-height: 42px;
  padding: 0.5rem 0.85rem;
  color: var(--ink);
  font-family: var(--ff-u);
  font-size: 0.95rem;
  line-height: 1.3;
  text-decoration: none;
}

.search-suggest__item:hover,
.search-suggest__item:visited:hover,
.search-suggest__item.is-active,
.search-suggest__item:visited.is-active {
  background: var(--blue-tint);
  color: var(--ink);
  text-decoration: none;
}

.search-suggest__name {
  font-weight: 600;
}

.search-suggest__meta {
  color: var(--ink-2);
  font-size: 0.8rem;
}

/* Named `:visited` for the same reason as `.search-suggest__item` above, and it has to be
   named HERE too: the item rule's own `:visited` is (0,2,0) and would otherwise repaint this
   row ink once the shopper had run that search before. Declared after the item rule, so the
   equal-specificity tie falls this way. */
.search-suggest__all,
.search-suggest__all:visited {
  margin-top: 0.3rem;
  border-top: 1px solid var(--rule);
  color: var(--blue-link);
  font-weight: 700;
}

/* Theme toggle (sun/moon): quiet bordered icon button. The inactive icon is
   hidden; aria-pressed=true means dark is active (shows the sun to invite
   switching back). */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  background: none;
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius-sm);
  color: var(--ink-2);
  cursor: pointer;
  transition: color 0.15s ease, border-color 0.15s ease;
}

.theme-toggle:hover {
  color: var(--ink);
  border-color: var(--ink-2);
}

.theme-toggle svg {
  width: 19px;
  height: 19px;
  fill: none;
  stroke: currentColor;
  stroke-width: var(--icon-stroke); /* POLISH S3, P2-18 */
  stroke-linecap: round;
  stroke-linejoin: round;
}

.theme-toggle .theme-icon-sun {
  display: none;
}

[data-bs-theme=dark] .theme-toggle .theme-icon-sun {
  display: block;
}

[data-bs-theme=dark] .theme-toggle .theme-icon-moon {
  display: none;
}

/* Variant riding on a NAVY field: the mobile top bar (both themes). Kept as its
   own class rather than a descendant selector so any future navy chrome can use
   it directly. */
.theme-toggle-field {
  border-color: rgba(255, 255, 255, 0.35);
  color: rgba(255, 255, 255, 0.82);
}

.theme-toggle-field:hover {
  color: #fff;
  border-color: rgba(255, 255, 255, 0.7);
}

/* PWA INSTALL PILL (2026-08-30). Same on-navy field control as the theme toggle beside it,
   widened for its label - the 38px square from the base .theme-toggle rule is the only thing
   that has to give. It ships `hidden` in Controls/Header.ascx and is revealed by app.js ONLY
   when beforeinstallprompt fires - or, since 2026-08-30, on iOS/iPadOS, where WebKit fires that
   event nowhere and the pill points at Share > Add to Home Screen instead. So this never paints
   in a browser that will not install.
   The mark is an <img> (the shipped swoosh-C), NOT an inline svg, so `.theme-toggle svg`'s
   stroke/fill rules deliberately do not reach it.
   ⚠ THE `hidden` ATTRIBUTE ALONE DOES NOT HIDE IT: `[hidden] { display: none }` is a UA rule at
   (0,1,0) and `.theme-toggle { display: inline-flex }` is an AUTHOR rule at the same specificity, so
   the author rule wins and the pill paints in every browser that never fires beforeinstallprompt -
   exactly the dead control this was built to avoid. MEASURED on dev.colamco.com at cdv 1268. */
.pwa-install[hidden] {
  display: none;
}

.pwa-install {
  width: auto;
  gap: 0.4rem;
  padding: 0 0.7rem;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
}

.pwa-install img {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
}

.topbar-mobile .pwa-install {
  font-size: 0.8125rem;
  padding: 0 0.55rem;
  gap: 0.3rem;
}

/* The mobile scroll-shrink squeezes `.topbar-mobile .theme-toggle` to a 32px SQUARE at (0,3,0),
   which would collapse the label. The pill follows it in height and keeps its own width. */
.hdr-compact .topbar-mobile .pwa-install {
  width: auto;
  padding: 0 0.45rem;
}

/* S4 (2026-08-18): the mobile navy bar had the SAME invisible focus ring S1 fixed on the desktop
   field (§6.4 trap 3) and never got the token re-point. MEASURED at 390 in LIGHT theme:
   --focus resolves to #00467f against a #051526 bar, ~1.6:1, so a keyboard focus on the wordmark or
   the theme toggle is not visible at all (in dark it happens to be fine, because the theme's own
   --focus is already the accent).
   ⚠ NAMED ON THE TWO ON-NAVY CONTROLS, NOT ON THE BAR — the cdv-932 lesson: .search-suggest hangs
   inside the pill, and a bar-level re-point would paint an accent ring on rows sitting on a white
   panel. The pill's own controls take their constant ring from the capsule block further up. */
.topbar-mobile .topbar-mobile-logo a,
.topbar-mobile .topbar-mobile-who,
.topbar-mobile .topbar-mobile-signin,
.topbar-mobile .theme-toggle {
  --focus: var(--field-accent);
}

/* ITEM 13 (2026-08-19): the wordmark is an <a> with no colour rule of its own, so DNN core's
   `a:focus { color: #222 }` owns it after a tap as well. It wraps an <img>, so nothing visible
   changes today - but the ink is stated for the same reason the other three state it, and it is the
   one that would bite the day the wordmark becomes text. */
.topbar-mobile .topbar-mobile-logo a:hover,
.topbar-mobile .topbar-mobile-logo a:focus,
.topbar-mobile .topbar-mobile-logo a:focus-visible {
  color: var(--field-ink);
}

/* ---------------------------------------------------------------------------
   MOBILE TOP BAR — NAVY, continuous from the status bar (Session B, plan §2.5;
   DECISIONS "Mobile home 2a"). Logo + full-width search pill only; no header
   cart or account (they live in the bottom tab bar). The field is --blue-deep in
   BOTH themes, exactly like the cart orb, so it carries the white wordmark
   unconditionally and its ink is the --field-* constants.

   "Continuous from the status bar" = the navy runs up into the notch area, so
   the bar absorbs env(safe-area-inset-top) instead of leaving a white strip
   above it (viewport-fit=cover is already set in Resources.ascx), and the
   theme-color meta is the same navy so the browser chrome matches.
   --------------------------------------------------------------------------- */
.topbar-mobile {
  background-color: var(--blue-deep);
  border-bottom: 0;
  padding-top: calc(0.5rem + env(safe-area-inset-top, 0px)) !important;
}

/* POLISH S7.2 (2026-08-20): a third arm, `.checkout-mobile-navbar #dnn_..._dnnLOGO_SM_imgLogo`, came
   off this selector with the rest of that dead family (see below). The two .topbar-mobile arms and
   the `margin-left: 0 !important` are live phone chrome and are untouched. */
.topbar-mobile #dnn_objPageHeader_dnnLOGO_SM_imgLogo,
.topbar-mobile .topbar-mobile-logo img {
  display: block;
  width: auto;
  max-width: 132px;
  max-height: 34px;
  height: auto;
  margin-left: 0 !important;
}

/* The pill is the GROUP, not the input: a rounded wrapper with the field and the
   submit glyph living inside it flush, so the control reads as the single white
   capsule the mockup draws rather than as an input welded to a button. */
/* ⚠ `overflow: hidden` IS GONE (2026-08-17, Juan): it CLIPPED THE SEARCH SUGGESTIONS. skin.js's
   _InitSearchSuggest appends `.search-suggest` as a CHILD of .search-header — one panel for the
   desktop capsule, the phone pill and the checkout chrome — positioned `absolute; top: 100% + 6px`,
   so a clipping group hid it outright on the phone. Nothing is lost by removing it: the pill's own
   corners come from its CHILDREN (the input's left pair and the submit's right pair, both stated
   below), not from clipping the wrapper. The desktop capsule was built without it for this reason. */
/* White in BOTH themes, matching the desktop capsule (Juan, 2026-08-17): `var(--paper)` made this
   #101d2c in dark. The constant-ink block up in the tier-1 search section covers this pill's controls
   too — one selector list for both capsules, so they cannot drift. */
/* FULL-WIDTH SUGGESTIONS ON THE PHONE (Juan, 2026-08-18, S4): the panel inherited the PILL's width
   (MEASURED 227px at 390) and most suggestion rows wrapped onto two or three lines, which is what
   made them hard to read. `position: static` on the pill is the whole fix: skin.js appends
   .search-suggest as a CHILD of .search-header, so handing the panel's containing block up to the
   nearest positioned ancestor — nav.topbar-mobile, which Bootstrap's own .navbar already makes
   `position: relative` and which spans the viewport — turns the panel's existing `left: 0; right: 0`
   into the full bar width with no JS measurement and no width literal to go stale.
   ⚠ SAFE ONLY BECAUSE NOTHING ELSE INSIDE THE PILL IS ABSOLUTELY POSITIONED (checked: the input, the
   mic and the submit are all in flow; the pill's corners come from the children, not from the
   wrapper). Anything absolute added inside it later needs this reconsidered.
   The desktop capsule KEEPS its `position: relative` — 720px is wide enough that its rows do not
   wrap, and the panel is meant to line up with the field there. */
.topbar-mobile .search-header {
  position: static;
  background: #fff;
  border-radius: var(--radius-btn);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22);
}

/* Flush under the navy bar rather than floating 6px below it, and square at the two viewport edges:
   a full-bleed panel with rounded outer corners and a border down the side of the screen reads as a
   clipped card. `top: 100%` is 100% of nav.topbar-mobile's padding box now, i.e. the bar's bottom. */
.topbar-mobile .search-suggest {
  top: 100%;
  border-left: 0;
  border-right: 0;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}

.topbar-mobile .search-header .form-control {
  height: 42px;
  border-radius: var(--radius-btn) 0 0 var(--radius-btn) !important;
  border: 0;
  background: transparent;
  color: var(--ink);
  padding-left: 1.05rem;
}

.topbar-mobile .search-header .form-control:focus {
  background: transparent;
  box-shadow: none;
}

/* Inside the white pill the submit is a quiet glyph, not a coloured slab — the
   pill itself is the affordance. Focus-within tints the whole capsule instead. */
.topbar-mobile .search-header .btn-search-submit,
.topbar-mobile .search-header .btn-search-submit:visited {
  border-radius: 0 var(--radius-btn) var(--radius-btn) 0;
  background: transparent !important;
  border: 0;
  color: var(--ink-2) !important;
}

.topbar-mobile .search-header .btn-search-submit:hover,
.topbar-mobile .search-header .btn-search-submit:focus-visible {
  background: transparent !important;
  color: var(--cta) !important;
}

/* The phone pill is only ~239px wide at 402, so the mic's side padding comes down from the .85rem
   the desktop group uses. MEASURED at 402 with a canvas text probe: the "Search 1M+ products"
   placeholder needs 134px and the .85rem mic left the field 126.3px of content box, i.e. it clipped
   the placeholder the shorter copy (item 2) was written to fit. .5rem gives the field 137.5px. */
.topbar-mobile .search-header .btn-search-voice {
  padding: 0 0.5rem;
}

.topbar-mobile .search-header:focus-within {
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22), 0 0 0 2px var(--field-accent);
}

/* Miscellaneous */
/* R100 (2026-08-21): `.bump-up` DELETED - a bespoke -.5rem nudge with zero writers across every
   .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, and 24 rendered page
   loads. app.js's cart-badge animation uses `badge-bump`, an unrelated class that survives. */
/* POLISH S6b, P3-25 (2026-08-20): `.shadow-y` DELETED - 1 !important in a rule NOTHING wears.
   MEASURED zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, the
   whole Deployed tree, and dbo.HtmlText on DEV (DNN content blocks are authored in the DB, so a
   repo grep alone would not have been enough). DESIGN.md publishes --shadow-1/2/3 plus --shadow-cta as the
   elevation scale; this was a hard-coded pair of black shadows outside it. */
/* R13: the legacy `.view-details:visited { color: white }` is gone. It dated from
   when this button was a solid fill; the treatment now lives in _components.scss
   (paper fill, body ink) and white text made the visited state invisible. */
/* R100 (2026-08-21): `hr.dark` DELETED. A pre-token grey rule from the hand-maintained skin.css;
   `<hr class="dark">` appears nowhere in the solution, in dbo.HtmlText, or in 24 rendered page
   loads, and the theme is switched by `data-bs-theme`, never by a `.dark` class. The live rules are
   the `hr` / `.dropdown-divider` pair on the token scale. */
/* POLISH S6b, P3-25 (2026-08-20): `.myaccount-nav .nav-link.active { background-color: rgb(4,78,160)
   !important }` DELETED. A legacy navy pill, stranded here between `hr.dark` and
   `.checkout-mobile-navbar` (itself since deleted, S7.2), that _myaccount.scss has had to fight in
   BOTH breakpoint blocks:
   MEASURED with CDP on a live /my-account, its (0,2,0) !important lost only because
   `.myaccount-module .myaccount-nav .nav-link.active` is (0,4,0) AND !important. So it painted
   nothing, and it was the sole external reason four of _myaccount.scss's !importants could not come
   off. Removing it here is the root-cause fix; the _myaccount block still keeps its own because of
   the ordering contract between its phone and >=lg copies (see the note there). */
/* POLISH S7.2 (2026-08-20): the whole `.checkout-mobile-navbar` family is DELETED - a legacy phone
   checkout masthead (its own logo arm, a two-column padding reset, and a `#divSecurityMetrics` seal
   block) that nothing rendered. S6b measured it dead and deliberately left it alone, because
   stripping the !important off a rule nothing matches is churn and deleting a component is a
   decision rather than an audit finding; Juan ruled the deletion. Re-measured before removal at cdv
   1036: zero consumers across every .cshtml / .ascx / .js / .cs in all three projects, zero outside
   CSS anywhere in the Deployed tree, and zero rows in dbo.HtmlText on DEV. This took the last
   dead-rule !important in the file with it. The live phone masthead is `.topbar-mobile` above;
   checkout's own chrome is in _cart.scss. */
/* ============================================================================
   DNN account-flow chrome (UI-Modernization 5.7, 2026-07-22)
   Themes the raw DNN-core surfaces of the account flows to the design system.

   1) .dnnFormMessage — DNN's skin/module message boxes (Skin.AddModuleMessage /
      Skin.AddPageMessage, rendered by admin/Skins/ModuleMessage.ascx) as
      `.dnnFormMessage.<variant>`: dnnFormSuccess (GreenSuccess), dnnFormWarning
      (YellowWarning), dnnFormValidationSummary + dnnFormError (RedError),
      dnnFormInfo (BlueInfo). These appear on: the email-verification landing
      page (/default.aspx?ctl=Login&verificationcode=...), the register and
      forgot-password flows, and DNN's sitewide "You are using an unverified
      account" page message (injected at the top of ContentPane for members of
      the 'Unverified Users' role). Overrides dnndefault/7.0.0/default.css
      (priority 8; this sheet loads later and wins on source order — keep the
      variant selectors at .dnnFormMessage.<variant> specificity or higher).

   2) .dnnForm.dnnLogin — the DNN-core login container (DesktopModules/Admin/
      Authentication/Login.ascx) hosting the core DNN username/password form
      (.dnnLoginService). Guests only reach it via the verification-email link
      above (the /login page uses the COLAMCO.Login module instead), so it is
      themed here as a compact centered card; no DNN-core files are edited.

   3) .dnnForm.dnnPasswordReset (Session AF2, 2026-08-13) — admin/Security/
      PasswordReset.ascx (DNN core), reached from the password-reset email link
      built by SignIn.ascx.cs's ResetUserPasswordRequest. Measured live: this one
      had NO chrome at all (no card, no title, top-left floating fields on an
      otherwise-empty page) — item 2 above already covered the verification
      landing page from 2026-07-22, this control was simply never touched. Its
      own markup has no .dnnLoginService/.dnnLoginActions wrapper, so it gets a
      parallel selector block below rather than joining item 2's.

   Tokens: action blue var(--cta) #00467f (theme v3), navy var(--blue) and
   var(--navy-1), caution amber #b45309, destructive var(--error),
   radius/shadow tokens from _variables.scss.
   ========================================================================== */
/* 2026-08-17 batch, Session 1 item 2 (Juan): "An error has occurred." was nearly invisible in
   dark. MEASURED on devbeta, dark theme, against the real cascade:

     - `background-color: var(--white)` is a CONSTANT true white (_variables.scss:200, "text on
       blue/dark fields"), so an unvarianted message painted a white card while `--textcolor`
       resolved to --ink #e8eef4 - white ink on a white card, about 1.06:1. --paper is the
       theme-aware card surface and is what every other card in the skin uses.
     - `.dnnModMessageHeading` at var(--navy-1) computed rgb(10,32,51) on the tinted card
       (~rgb(21,22,33)): 1.11:1.
     - a nested link at var(--blue) computed rgb(0,70,127): 1.67:1. --blue-link is the accent
       that is designed to flip light in dark (rgb(140,192,232) measured).

   `.dnnModuleMessage` joins the selector: it is the class DNN's own Admin/Skins/ModuleMessage.ascx
   puts on the panel, it is styled NOWHERE else in this repo or in the deployed site (grepped: the
   .ascx is the only file that names it, and the dnndefault/7.0.0/default.css the older notes cite
   does not exist in this deployment), and measured bare it had no card at all - transparent
   background, zero padding, no border. A message that reaches the page without one of DNN's
   variant classes now still gets a legible box. No DNN-core file is touched. */
.dnnFormMessage,
.dnnModuleMessage {
  display: block;
  max-width: 720px;
  margin: 1rem auto 1.25rem;
  padding: 1rem 1.25rem;
  /* SURFACE SWAP S2 (2026-08-21): --surface-card. This is a message PANEL sitting on the page
     field, and the field is white now - the box the note above says it finally got would have
     gone back to being invisible. */
  background-color: var(--surface-card);
  border: 1px solid var(--light-grey);
  border-left: 4px solid var(--cta);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
  color: var(--textcolor);
  font-family: var(--userfont);
  font-size: 1rem;
  line-height: var(--lineheight);
  text-align: left;
}

.dnnFormMessage a,
.dnnModuleMessage a {
  color: var(--blue-link);
  font-weight: 600;
  text-decoration: underline;
}

.dnnFormMessage .dnnModMessageHeading,
.dnnModuleMessage .dnnModMessageHeading {
  display: block;
  margin: 0 0 0.25rem;
  color: var(--ink);
  font-size: 1.0625rem;
  font-weight: 600;
}

/* Variants: soft semantic tint + solid accent edge. Same specificity as the
   default.css rules they replace, so source order decides.
   The .dnnModuleMessage half of each pair is item 2's: DNN appends the variant to the panel's
   own CssClass at runtime, and this repo cannot see which of the two class names survives that
   append, so both spellings carry the same tint. */
.dnnFormMessage.dnnFormSuccess,
.dnnModuleMessage.dnnFormSuccess {
  background-color: rgba(89, 128, 166, 0.08);
  border-color: rgba(89, 128, 166, 0.3);
  border-left-color: var(--success);
}

.dnnFormMessage.dnnFormWarning,
.dnnModuleMessage.dnnFormWarning {
  background-color: rgba(180, 83, 9, 0.06);
  border-color: rgba(180, 83, 9, 0.3);
  border-left-color: #b45309; /* caution amber design token */
}

.dnnFormMessage.dnnFormError,
.dnnFormMessage.dnnFormValidationSummary,
.dnnModuleMessage.dnnFormError,
.dnnModuleMessage.dnnFormValidationSummary {
  background-color: rgba(210, 60, 15, 0.05);
  border-color: rgba(210, 60, 15, 0.28);
  border-left-color: var(--error);
}

.dnnFormMessage.dnnFormInfo,
.dnnFormMessage.dnnFormHelpContent,
.dnnModuleMessage.dnnFormInfo,
.dnnModuleMessage.dnnFormHelpContent {
  background-color: rgba(0, 70, 127, 0.04);
  border-color: rgba(0, 70, 127, 0.22);
  border-left-color: var(--cta);
}

/* Inline auth-flow message (COLAMCO.Login SignIn control) — same card, but it
   sits inside the login form column, so let it fill the column. */
.dnnFormMessage.auth-message {
  max-width: none;
  margin: 1rem 0;
}

/* Session AE, 2026-08-13: hide DNN-core's admin-only-visibility/scheduling
   notice (Container.AddAdministratorOnlyHighlighting, DNN Platform) — the
   "Visible by Administrators only." / scheduled-start / scheduled-end box
   injected into ContentPane for admins on the live front-end page. Core file
   not touched; dnnFormInfoAdminErrMssg is unique to this box (no other
   .dnnFormMessage variant shares it). */
.dnnFormMessage.dnnFormInfoAdminErrMssg {
  display: none;
}

/* --------------------------------------------------------------------------
   DNN-core login container (email-verification landing). Scoped under
   .dnnForm.dnnLogin so admin surfaces and the COLAMCO /login page are
   untouched.
   -------------------------------------------------------------------------- */
.dnnForm.dnnLogin {
  max-width: 430px;
  margin: 2rem auto 3rem;
  padding: 1.75rem 1.75rem 1.5rem;
  /* SURFACE SWAP S2 (2026-08-21): --white -> --surface-card. THIS IS A CARD, and --white is a
     CONSTANT true white that never flips - exactly the defect the 2026-08-17 note above measured
     on .dnnFormMessage (white card + --textcolor --ink in dark, ~1.06:1). The two DNN-core
     containers here were left carrying it. Card fill is the role token now, in both themes. */
  background: var(--surface-card);
  border: 1px solid var(--light-grey);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  font-family: var(--userfont);
}

/* default.css floats .LoginPanel into a 47.5% column; stack it instead. */
.dnnForm.dnnLogin .LoginPanel {
  float: none;
  width: 100%;
  padding: 0;
  border: 0;
}

.dnnForm.dnnLogin .dnnLoginService .dnnFormItem {
  display: block;
  width: 100%;
  margin: 0 0 1rem;
}

/* Stack labels above inputs (default.css floats them into a left column). */
.dnnForm.dnnLogin .dnnLoginService .dnnLabel,
.dnnForm.dnnLogin .dnnLoginService .dnnFormLabel {
  display: block;
  float: none;
  width: auto;
  margin: 0 0 0.375rem;
  padding: 0;
  overflow: visible;
  text-align: left;
  /* SURFACE SWAP S2 (2026-08-21): --navy-1 -> --ink. The card under this ink is theme-aware now,
     and --navy-1 is #0a2033 in dark - darker than the card. Same pairing rule the 2026-08-17 note
     in this file states: a themed surface needs themed ink on it. */
  color: var(--ink);
  font-weight: 600;
}

.dnnForm.dnnLogin .dnnLoginService input[type=text],
.dnnForm.dnnLogin .dnnLoginService input[type=password] {
  display: block;
  width: 100%;
  min-width: 0;
  max-width: none;
  padding: 0.5rem 0.75rem;
  color: var(--textcolor);
  font-family: var(--userfont);
  font-size: 1rem;
  line-height: var(--lineheight);
  /* SURFACE SWAP S2 (2026-08-21): --white -> --surface-plate. A FIELD on the card above: white in
     light, $d-paper-2 in dark, so `color: var(--textcolor)` is legible in both. See the card note. */
  background: var(--surface-plate);
  border: 1px solid var(--grey);
  border-radius: var(--radius-sm);
  box-shadow: none;
}

.dnnForm.dnnLogin .dnnLoginService input[type=text]:focus,
.dnnForm.dnnLogin .dnnLoginService input[type=password]:focus {
  border-color: var(--focus);
  box-shadow: var(--focus-ring);
  outline: 0;
}

.dnnForm.dnnLogin .dnnLoginService .dnnLoginRememberMe {
  display: inline-block;
  margin: 0;
  font-weight: 400;
}

/* Primary action (Login LinkButton) -> action-blue CTA button. */
.dnnForm.dnnLogin .dnnLoginService a.dnnPrimaryAction {
  display: block;
  width: 100%;
  padding: 0.625rem 1rem;
  background: var(--cta);
  border: 1px solid var(--cta);
  border-radius: var(--radius-btn);
  color: var(--cta-ink);
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  text-shadow: none;
  box-shadow: none;
}

.dnnForm.dnnLogin .dnnLoginService a.dnnPrimaryAction:hover,
.dnnForm.dnnLogin .dnnLoginService a.dnnPrimaryAction:focus,
.dnnForm.dnnLogin .dnnLoginService a.dnnPrimaryAction:active {
  background: var(--cta-press);
  border-color: var(--cta-press);
  color: var(--cta-ink);
}

/* Secondary actions (Register / Reset password / Cancel) -> quiet navy links. */
.dnnForm.dnnLogin a.dnnSecondaryAction {
  display: inline-block;
  padding: 0.25rem 0;
  background: none;
  border: 0;
  border-radius: 0;
  color: var(--blue-link);
  font-weight: 600;
  text-decoration: underline;
  text-shadow: none;
  box-shadow: none;
}

.dnnForm.dnnLogin .dnnLoginService .dnnLoginActions .dnnActions {
  margin: 0;
  padding: 0;
  list-style: none;
}

.dnnForm.dnnLogin .dnnLoginService .dnnLoginActions .dnnActions li {
  display: inline-block;
  margin: 0 1.25rem 0 0;
}

/* --------------------------------------------------------------------------
   DNN-core password-reset container (email password-reset landing page —
   Session AF2, 2026-08-13). Reached via SignIn.ascx.cs's ResetUserPasswordRequest
   at /login?ctl=PasswordReset&resetToken=... (measured live: was completely bare,
   no card, no title, floating top-left with acres of empty page below it).
   admin/Security/PasswordReset.ascx (DNN core, not edited) wraps its own markup
   in .dnnForm.dnnPasswordReset .dnnPasswordResetContent with plain .dnnFormItem /
   ul.dnnActions — no .dnnLoginService/.dnnLoginActions wrapper like Login.ascx
   above, so this is its own selector set (same card look, same tokens) rather
   than an extension of .dnnForm.dnnLogin. Title/intro copy is CSS ::before
   content — the control ships with neither and core markup isn't touched. */
.dnnForm.dnnPasswordReset {
  max-width: 430px;
  margin: 2rem auto 3rem;
  padding: 1.75rem 1.75rem 1.5rem;
  /* SURFACE SWAP S2 (2026-08-21): --white -> --surface-card. THIS IS A CARD, and --white is a
     CONSTANT true white that never flips - exactly the defect the 2026-08-17 note above measured
     on .dnnFormMessage (white card + --textcolor --ink in dark, ~1.06:1). The two DNN-core
     containers here were left carrying it. Card fill is the role token now, in both themes. */
  background: var(--surface-card);
  border: 1px solid var(--light-grey);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  font-family: var(--userfont);
}

.dnnForm.dnnPasswordReset::before {
  content: "Reset your password";
  display: block;
  margin: 0 0 0.5rem;
  /* SURFACE SWAP S2 (2026-08-21): --navy-1 -> --ink. The card under this ink is theme-aware now,
     and --navy-1 is #0a2033 in dark - darker than the card. Same pairing rule the 2026-08-17 note
     in this file states: a themed surface needs themed ink on it. */
  color: var(--ink);
  font-size: 1.375rem;
  font-weight: 700;
}

/* DNN core hardcodes this content wrapper to a fixed 550px (admin/Security's
   own two-column assumption) - wider than this card at any viewport, and
   still wider than a 402px screen outright. Let it fill the card instead. */
.dnnForm.dnnPasswordReset .dnnPasswordResetContent {
  width: 100%;
  box-sizing: border-box;
}

.dnnForm.dnnPasswordReset .dnnPasswordResetContent::before {
  content: "Enter a new password for your account below.";
  display: block;
  margin: 0 0 1.25rem;
  color: var(--textcolor);
  font-size: 0.9375rem;
}

.dnnForm.dnnPasswordReset .dnnFormItem {
  display: block;
  width: 100%;
  margin: 0 0 1rem;
}

/* dnn.PasswordStrength.css sizes this container to 45% / max 445px and floats
   it inline-block (a two-column layout assumption) - on this card's single
   full-width column that leaves the New Password field a fraction of the
   Confirm Password field's width with a blank gap beside it. Force it back to
   the same full-width block as every other field. */
.dnnForm.dnnPasswordReset .password-strength-container {
  display: block;
  width: 100%;
  max-width: none;
}

.dnnForm.dnnPasswordReset input[type=text],
.dnnForm.dnnPasswordReset input[type=password] {
  display: block;
  width: 100%;
  min-width: 0;
  max-width: none;
  padding: 0.5rem 0.75rem;
  color: var(--textcolor);
  font-family: var(--userfont);
  font-size: 1rem;
  line-height: var(--lineheight);
  /* SURFACE SWAP S2 (2026-08-21): --white -> --surface-plate. A FIELD on the card above: white in
     light, $d-paper-2 in dark, so `color: var(--textcolor)` is legible in both. See the card note. */
  background: var(--surface-plate);
  border: 1px solid var(--grey);
  border-radius: var(--radius-sm);
  box-shadow: none;
}

/* A generic disabled-input rule elsewhere on the page drops opacity to .5,
   which (white bg over this card's pale slate page background) reads as the
   field fading into invisibility on its right-hand side rather than a plain
   disabled look. Keep it fully opaque and use background/color for the
   disabled affordance instead. */
.dnnForm.dnnPasswordReset input[disabled] {
  background: var(--paper);
  color: var(--ink-2);
  opacity: 1;
}

.dnnForm.dnnPasswordReset input[type=text]:focus,
.dnnForm.dnnPasswordReset input[type=password]:focus {
  border-color: var(--focus);
  box-shadow: var(--focus-ring);
  outline: 0;
}

.dnnForm.dnnPasswordReset .dnnActions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.dnnForm.dnnPasswordReset .dnnActions li {
  margin: 0;
}

.dnnForm.dnnPasswordReset a.dnnPrimaryAction {
  display: inline-block;
  padding: 0.625rem 1.25rem;
  background: var(--cta);
  border: 1px solid var(--cta);
  border-radius: var(--radius-btn);
  color: var(--cta-ink);
  font-size: 1rem;
  font-weight: 600;
  text-align: center;
  text-decoration: none;
  text-shadow: none;
  box-shadow: none;
}

.dnnForm.dnnPasswordReset a.dnnPrimaryAction:hover,
.dnnForm.dnnPasswordReset a.dnnPrimaryAction:focus,
.dnnForm.dnnPasswordReset a.dnnPrimaryAction:active {
  background: var(--cta-press);
  border-color: var(--cta-press);
  color: var(--cta-ink);
}

.dnnForm.dnnPasswordReset a.dnnSecondaryAction {
  display: inline-block;
  padding: 0.625rem 1rem;
  background: none;
  border: 1px solid var(--grey);
  border-radius: var(--radius-btn);
  /* SURFACE SWAP S2 (2026-08-21): --navy-1 -> --ink. The card under this ink is theme-aware now,
     and --navy-1 is #0a2033 in dark - darker than the card. Same pairing rule the 2026-08-17 note
     in this file states: a themed surface needs themed ink on it. */
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
  text-shadow: none;
  box-shadow: none;
}

/* Session AF2 gave the DNN-core checkbox on the verification-landing card
   (.dnnForm.dnnLogin's "Remember Login") a 16px box and an accent colour, because it rendered as a
   native, unstyled square — the one visibly off-brand element left on an otherwise-themed card.

   UI BATCH 2026-08-27-E item 1: that rule is DELETED. The sitewide toggle switch (_global.scss)
   already reaches this control (it is a bare input[type="checkbox"]), and a 16px override here
   would have been the ONE checkbox on the estate still painted as a box - this selector is (0,2,1)
   and would have out-ranked it. `accent-color` goes with it: it only ever tints a UA-drawn control
   and is inert once `appearance` is none. */
/* ==========================================================================
   Distraction-free checkout header (HeaderCheckout.ascx minimal variant) —
   UI-Modernization Phase 4.5: logo + "Secure checkout" + help phone only.
   ========================================================================== */
.co-header {
  /* SURFACE SWAP S2 (2026-08-21): RULED, LEFT ON --paper. Flagged out of S1 because it is a core
     commerce surface in an S2 file. It is CHROME, not a panel: at --surface-card it would paint
     the same fill as the .co-sec panels below it and read as the first panel of the page instead
     of as the header. White chrome on a white field, delimited by its --rule border-bottom (one
     step darker since S1), is the same relationship the masthead has on every other page. */
  background: var(--paper);
  border-bottom: 1px solid var(--rule);
  box-shadow: none;
  position: relative;
  z-index: 10;
}

/* Session F: the redesign's slim secure header — logo, an ALL-CAPS "SECURE CHECKOUT" stamp and the
   contact chain's primary option.

   Refinement F7 (Juan, 2026-08-12): a 3-column GRID, not flex. The stamp is centred on the
   container, which flex could only fake with `margin: 0 auto` — and that centres it in the space
   the two side groups happen to leave, so it drifts as the right-hand group's width changes
   (theme, seal, chat wording). 1fr / auto / 1fr keeps it on the container's centre line at every
   width, and the side cells simply push outward. The 2026-08-11 "three unrelated islands" note is
   superseded: the right cell now carries the SecurityMetrics seal as well, so both sides have
   weight. */
.co-header-bar {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
  padding: 0.85rem 0.25rem;
}
.co-header-bar .co-header-logo img {
  height: 34px;
  width: auto;
  display: block;
}

/* 2026-08-21 (Juan): the centre cell is a two-line STACK - the stamp, then the trust rail under it.
   ONE grid item, not two: F7's `1fr auto 1fr` is what centres the stamp on the container, and two
   bare children in that cell would become two auto tracks and move it. Centred on each other rather
   than left-aligned, because the stamp is the shorter of the two and a ragged left edge under a
   centred bar reads as a fault. */
.co-secure-stack {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1rem;
  min-width: 0;
  /* The rail's own `padding: .35rem 0` is what it needs standing alone on the masthead's tier-2
     row; stacked directly under the stamp it is 7px of dead space that grows the bar for nothing. */
}
.co-secure-stack .hdr-trust {
  padding: 0;
}

.co-secure {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.45rem;
  font-family: var(--ff-m);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.co-secure i {
  color: var(--cta-outline);
  font-size: 0.8rem;
}

/* F7: right-hand cell — seal + chat, kept together so the grid's third column is ONE item and the
   centre cell cannot be pushed off-centre by the gap between them. */
.co-header-end {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.85rem;
  min-width: 0;
}

/* SESSION 3 ITEM 7 (Juan, 2026-08-16): THE WHITE PLATE IS GONE AND THE SEAL TAKES THE SPACE IT WAS
   USING.

   ~~"The raster has a baked-in white background, so it gets a constant-white plate in BOTH themes"~~
   — F7's premise was simply WRONG, and that is why the plate read as a white backdrop rather than as
   part of the mark. Credit_Card_Safe_dark.png was sampled pixel by pixel rather than assumed: its
   border is #727173 and its field is #414042, opaque, at every corner. It is a DARK raster (the
   file name says so). The #fff plate was therefore painting a white rectangle BEHIND a dark badge
   in both themes - a backdrop the artwork never needed.

   Removing it hands back the 2px/4px of padding, which the badge itself now uses: 30px -> 34px is
   exactly the plate's old outer height, so the seal grows ~13% for ZERO added header height (the
   bar is sized by its tallest item, the 34px wordmark). Width follows the 150x82 aspect through
   `width: auto` - proportional, not stretched. */
.co-sm-seal {
  flex: none;
  display: block;
  line-height: 0;
}
.co-sm-seal img {
  display: block;
  height: 34px;
  width: auto;
}

.co-help {
  font-size: 0.84375rem;
  color: var(--ink-2);
  text-align: right;
  line-height: 1.35;
}
.co-help a {
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
  color: var(--blue-link);
}
.co-help a:hover {
  color: var(--ink);
}

/* BD7, found while verifying it: the SAME paint-over F7 fixed at 402 was still live one band up,
   576-767. MEASURED at 576, 600, 640, 680, 700 and 720: the stamp runs 209-357 and the seal starts
   at 310 - 47px of overlap at every one of them, clearing only at 768. Identical mechanism: the
   1fr/auto/1fr grid's side columns are (container - stamp) / 2 = 163px at 576, .co-header-end wants
   63 (seal) + 13.6 (gap) + 151 ("Questions? Chat with us") = 228, and `justify-content: flex-end`
   spills the excess LEFTWARD across the nowrap centre column with nothing reporting an overflow.
   The lead-in is the whole excess, exactly as F7 recorded at 402, so it drops through the tablet
   band too: 63 + 13.6 + 78 = 155 fits inside 163. Nothing else about the bar changes above 576. */
@media (max-width: 767.98px) {
  .co-help-lead {
    display: none;
  }
}
/* ITEM 21 (2026-08-19): the chat glyph exists in the markup at every width and is revealed only in
   the phone band below - above 576 the link is its own label and a glyph beside it is noise. */
.co-help-ico {
  display: none;
}

@media (max-width: 575.98px) {
  /* BD7 (2026-08-14, Juan's ruling on old open item 9): UN-CENTRED below 576, so the seal fits.
     F7's 1fr/auto/1fr keeps the stamp on the container's centre line, and that centring is what
     spent the width - F7 measured the right cell down to ~45px and hid the seal rather than
     shrink it to an unreadable smudge. Juan's call is the other way round: the seal is the thing
     that has to be legible, the stamp's exact centre line is not. Content-sized columns spread by
     `space-between` give the row back to logo / stamp / (seal + chat).
     FLEX-WRAP, not a third set of grid tracks. Everything on this bar is `white-space: nowrap`
     (the stamp below, the chat link in .co-help), and a nowrap item in a track that cannot shrink
     is exactly how F7's stamp came to PAINT OVER the chat link with nothing reporting an
     overflow - `grid-template-columns: auto minmax(0, auto) auto` reproduced it, MEASURED at 360:
     14px of stamp painted over the seal. Wrapping is the only arrangement that cannot overlap at
     ANY width and needs no measured px threshold: at 402 the row still fits on one line (logo
     16-87, stamp 98-227, seal 238-290, chat 304-376 in a 402 viewport), and narrower than that
     the seal + chat pair drops to a second line instead of painting over anything. */
  /* ITEM 21 (2026-08-19, Juan): NOWRAP, and it is only safe because the chat link is a ~40px icon
     button here instead of a ~78px label (see .co-help below). BD7's wrap was the right answer for
     the row it measured - everything on this bar is `white-space: nowrap`, and a nowrap item in a
     track that cannot shrink is how F7's stamp came to paint OVER the chat link with nothing
     reporting an overflow. The wrap was never the goal though, it was the safe fallback: MEASURED on
     devbeta, at 402 the row fits on one 55px line but at 390 AND at 360 it wrapped to 91px with the
     seal + chat pair on a second line, which is what Juan reported.
     With the label gone the row is logo 71 + stamp 129 + seal 51 + chat 40 + three .5rem gaps = 315
     inside a 358px content box at 390, so nothing has to shrink and nothing can overlap.
     ⚠ Do NOT go back to a nowrap GRID here - `grid-template-columns: auto minmax(0, auto) auto`
     reproduced BD7's paint-over at 360 (14px of stamp over the seal). Re-measure at 360, 390 and 402
     before touching any of the four numbers above. */
  .co-header-bar {
    display: flex;
    /* 2026-08-25: WRAPS now, as a SAFETY NET and not as the layout - all three items sit on one
       line (logo 71 + stack 170 + chat 40 + two .5rem gaps = 297 inside a 326px content box at
       360). The nowrap warning in the block comment above this rule was written when the centre
       item was 229px wide; the reword took it to 170 and the row fits. Wrap is what makes a
       viewport narrower than 360 drop the stack to a second line instead of painting it over
       the logo - see the .co-secure-stack note below. Nothing here carries a flex-basis now. */
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    row-gap: 0.35rem;
  }
  .co-header-bar .co-header-logo img {
    height: 28px;
  }
  /* 2026-08-25 (Juan), THIRD AND FINAL SHAPE: STACKED LIKE DESKTOP, ON LINE 1, COSTING NO LINE.
     "The mobile checkout page has 'secure checkout' and 'rated excellent' on the same line, they
     should be stacked like on desktop so all the header content fits without so much extra height."

     Both halves are satisfiable at once, and the reword is what made that true. The two earlier
     shapes each gave up one of them:
       · stamp OVER rail on a full-width line 2 -> stacked, but .co-header measured 109px at 390.
       · stamp BESIDE rail on a full-width line 2 -> 91px, but not stacked.
     The stack does not need a line of its own any more. Since the rail became "Rated excellent" it
     is 170px wide, not 229px, so the stack's OWN width is 170 and the whole bar fits one row:
     logo 71 + stack 170 + chat 40 + two .5rem gaps = 297 inside a 326px content box at 360, 356 at
     390, 368 at 402. Nothing shrinks and nothing can overlap.
     Height then comes from the TALLEST line-1 item, which is the 40px chat button and NOT the 35px
     stack, so stacking is free: 14 + 40 + 14 = 68px, below both earlier shapes.

     THE RULE THAT USED TO BE HERE IS DELETED, NOT REWRITTEN - `order: 3` + `flex: 0 0 100%` (line 2)
     and the `flex-direction: row` that replaced them are all gone. The base .co-secure-stack column
     further up this file is already the desktop treatment, so the phone inherits it and this
     breakpoint has nothing left to say about the stack.

     ⚠ `flex-wrap: wrap` on .co-header-bar STAYS, and it is the safety net rather than the layout:
     the stamp and the rail are both nowrap, so the stack cannot go under 170px, and below ~330px of
     content box (a 320px viewport) the row would otherwise reproduce F7's BD7 paint-over. With wrap
     on, the stack drops to a second line there instead - two lines at 320 is a degradation, a stamp
     painted over the logo is a fault. Do NOT convert this back to a nowrap grid: the block comment
     above records that `grid-template-columns: auto minmax(0, auto) auto` put 14px of stamp over the
     seal at 360.
     Centring is now `justify-content: space-between`'s, i.e. between the logo and the chat button
     rather than on the container - it drifts with those two widths, which the 1fr/auto/1fr grid above
     576 exists to prevent. At 71px and 40px both are effectively fixed on this surface, so the drift
     is not observable; if either ever becomes variable, the grid is the fix, not a margin. */
  /* nowrap: at 402 the stamp broke across two lines mid-phrase ("SECURE / CHECKOUT"), which reads
     as a layout fault rather than a status. The chat link beside it is the flexible one. */
  .co-secure {
    font-size: 10.5px;
    letter-spacing: 0.08em;
    white-space: nowrap;
  }
  /* 2026-08-25 (Juan): ~~D1: the phone bar stays ONE LINE, so the rail drops here~~ - REVERSED.
     The checkout header shows the rating claim under SECURE CHECKOUT on a phone exactly as it does
     on desktop; the claim is the same on both, and the surface that most needs it is the small one.

     ~~THE BAR GROWS INSTEAD OF THE ROW~~ - SUPERSEDED THE SAME DAY, and the correction is worth
     keeping because it is the reason the wording changed rather than the layout alone. The first
     build gave the STACK a line of its own (`flex: 0 0 100%` + `order: 3`) with the stamp OVER the
     rail, on D1's measurement that the rail's 232px is wider than the 129px stamp and so would paint
     over the logo or the chat icon in a nowrap row (F7's BD7 failure, inverted). That bought the
     width fight off at the price of a second text line, and MEASURED at 390 it took .co-header to
     109px - which is the "too tall" Juan reported.
     The rail's WIDTH is the real constraint, so the fix is the width: "Customer-rated excellent"
     -> "Rated excellent" (Controls/HeaderCheckout.ascx AND Controls/Header.ascx - ONE string, both
     hosts) takes the rail from 229px to 170px.
     ⚠ That width is ALSO what let the stack leave line 2 altogether, which is the shape that
     SHIPPED - see the .co-secure-stack note below. The intermediate "stamp BESIDE rail on a
     full-width line 2" build (header 91px) lasted one cdv: Juan's follow-up was that the pair
     must be STACKED as it is on desktop. At 170px the stack fits line 1 beside the logo and the
     chat button, so it is stacked AND costs no line at all - header 68px. */
  /* SESSION 6 item 5 / finding 21 (2026-08-19, Juan): ~~"the seal is BACK below 576"~~ - IT IS
     DROPPED AGAIN, and this time not for width. MEASURED on devbeta at 402 and 390, cdv 997: the
     raster renders 51x28, at which size neither the SecurityMetrics wordmark nor the "Credit Card
     Safe" line is legible - it reads as a dark smudge beside the mark, which is clutter where the
     page most needs to look trustworthy. The plan's alternative was "replace it with the
     lock-and-wordmark already in the header", and that pair is ALREADY on this bar at every width:
     the fa-lock + "SECURE CHECKOUT" stamp two cells to the left. So the seal is removed rather
     than restated. It survives untouched at 576 and up, where it is legible at 34px, and its
     certificate link is not the only route to the certificate.
     ⚠ This hands the nowrap row 51px + one .5rem gap back. Re-measure at 360, 390 and 402 before
     touching the item-21 numbers above - they were budgeted WITH the seal in the row. */
  .co-sm-seal {
    display: none;
  }
  /* ITEM 21: ICON-ONLY. The label is taken VISUALLY-HIDDEN rather than display:none, so the
     accessible name of the link is still "Questions? Chat with us" and no aria-label has to restate
     it. `.co-help-lead` is already display:none from the tablet band above; only the second half
     needs the SR treatment. The anchor becomes a 40px square target - the row's width budget is what
     this item is buying, and a 40px thumb target is the smallest one this skin ships. */
  .co-help {
    font-size: 0.78125rem;
  }
  .co-help-ico {
    display: inline-block;
    font-size: 1.05rem;
    line-height: 1;
  }
  .co-help a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 40px;
    min-height: 40px;
  }
  /* POLISH S6b, P3-25 (2026-08-20): all NINE KEPT. This is a hand-rolled copy of Bootstrap's
     `.visually-hidden`, which emits exactly these nine properties with !important - the priority IS
     the utility's contract, because a visually-hidden element has to stay hidden whatever layout the
     component around it applies. ⚠ The real cleanup is not the !important, it is the duplication:
     one markup site wears `.co-help-label`, and swapping it for `.visually-hidden` would delete all
     nine. That is a markup change on the checkout form, so it is recorded as an open item rather
     than done inside an !important audit. */
  .co-help-label {
    position: absolute !important;
    width: 1px !important;
    height: 1px !important;
    padding: 0 !important;
    margin: -1px !important;
    overflow: hidden !important;
    clip: rect(0, 0, 0, 0) !important;
    white-space: nowrap !important;
    border: 0 !important;
  }
  /* F7's `.co-help-lead { display: none }` moved UP to the tablet band (see the @include tablet
     block above .co-header-bar's phone rules) - it was the same overlap at 576-767, so the rule
     now covers both and this duplicate is gone. */
}
/* UI BATCH 2026-08-27-B, raised mid-session by Juan: THE CHECKOUT HEADER IS ONE LINE BELOW 360,
   and the chat entry point is what pays for it.
   MEASURED on devbeta, cdv 1215, signed in, sweeping 320/360/375/390/402/430/480/540/576/640/768:
   .co-header-bar is 67.2px on ONE line at every width from 360 up, and 107.9px on TWO at 320 - the
   40px chat button drops to a second row (y 54 against the logo's y 17). The wrap threshold sits
   between 320 and 360 and the failure is monotone below it, so one `max-width: 360px` block covers
   the whole range and changes nothing at the 402 floor we verify.
   Hiding .co-help returns 40px + one .5rem gap: logo 71 + stack 172 + gap 8 = 251 inside the 286px
   content box at 320, so the row fits with room rather than only just.
   ⚠ This DELETES the only chat affordance on /checkout below 360 - Juan's call, weighed against a
   two-line sticky header on the narrowest phones. The rest of the phone band keeps the icon-only
   40px button (see the ITEM 21 rules above); this is a narrower band layered on top of it, not a
   replacement for it. `.co-header-end` goes with it because .co-sm-seal is already display:none on
   a phone, so the wrapper would otherwise leave an empty flex item in the row. */
@media (max-width: 360px) {
  .co-header-end,
  .co-help {
    display: none;
  }
}
/* ==========================================================================
   Site-wide live announcement banner (admin-authored closure notices —
   holiday / weather). Accessible amber caution treatment per the approved
   checkout mockup; renders wherever the admin content block carries the
   .site-alert class, on every page including checkout.
   ========================================================================== */
.site-alert {
  background: #fbeedd;
  border-bottom: 1px solid #f3d8b5;
  color: var(--caution-text);
  font-size: 0.85rem;
  font-weight: 600;
}
.site-alert .site-alert-inner {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  max-width: 1320px;
  margin: 0 auto;
  padding: 0.6rem 1.5rem;
}
.site-alert i {
  flex: none;
  margin-top: 0.15rem;
}
.site-alert b,
.site-alert strong {
  font-weight: 800;
}
.site-alert a {
  color: inherit;
  text-decoration: underline;
}

/* ============================================================================
   CTA chevrons — 2026-08-19 UI follow-up, item 20 (Juan).

   Every &rarr; / &larr; character in Views/ (100 of them, 94 forward + 6 back) is now a
   FontAwesome chevron. Both glyphs were already in the hand-built FA subset - fa-chevron-right and
   fa-chevron-left are used by the drawer and the carousels - so nothing had to be added to it.

   ⚠ EVERY ICON IS aria-hidden. The arrow was decorative punctuation; a chevron that announced
   itself would turn "Read the case study" into "Read the case study, right chevron" on a screen
   reader. The link text alone is the accessible name, exactly as before.

   Two shapes, and the difference is where the glyph sits:
     .cta-chev      - trails the label, one small gap from it. The inline text CTA
                      (<span>Explore solutions <i …></i></span>) and every plain link.
     .cta-chev-lead - LEADS the label, for the six back links (&larr; Back to cart / to orders /
                      to quotes / to messages / to my lists, and Continue shopping).
     .btn-chev      - on a .btn whose label and chevron must sit at OPPOSITE ends: the button
                      becomes a flex row and the chevron takes margin-left:auto, so the label sits
                      left and the chevron on the trailing edge (Juan's ruling for buttons).

   `em`, not `rem`: the chevron scales with whatever type size the CTA is set in - .home-btn-ghost
   at btn-lg and a 13px .cp-tile CTA both get a proportionate mark. */
.cta-chev {
  margin-left: 0.35rem;
  font-size: 0.78em;
  line-height: 1;
}

.cta-chev-lead {
  margin-right: 0.4rem;
  font-size: 0.78em;
  line-height: 1;
}

.btn-chev {
  display: inline-flex;
  align-items: center;
}

.btn-chev > .cta-chev {
  /* ⚠ MEASURED at cdv 990: `margin-left: auto` alone resolves to 0px on all three home ghost
     buttons, because a shrink-to-fit inline-flex button has NO free space for auto to consume -
     so the chevron sat flush against the last letter. The auto stays (it is what pins the glyph
     to the trailing edge the moment a button IS wider than its content, e.g. in a stretched
     column), and PADDING supplies the minimum gap because padding is never eaten by auto. */
  margin-left: auto;
  padding-left: 0.5rem;
}

/* COLAMCO desktop (>=768px) overrides.
  Migrated VERBATIM from the formerly hand-maintained resources/styles/desktop.css (UI Modernization Phase 0.1).
  All rules must stay inside their @media (min-width) wrappers. */
/* Make sure all content is inside a media wrapper so it will only affect larger screen sizes after it's minified and bundled */
@media (min-width: 768px) {
  /* Utility / miscellaneous */
  /* R100 (2026-08-21): `.justify-content-start-md` DELETED - and it was never needed, because
     Bootstrap 5 emits `.justify-content-md-start` itself (correct infix ORDER: breakpoint before
     value). This hand-rolled reversed-name copy had zero writers across every
     .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered page
     loads. Same class of defect as the `w-sm-auto` phantom from 2026-08-20: a utility invented in
     the shape the author expected instead of the shape Bootstrap ships. */
  /* Navigation */
  /* 2026-08-16 batch, Session 1 item 6: `ul#navMenuMega > li.nav-item { min-width: 108px }` is
     DELETED. It is a leftover from the pre-mega dropdown nav, and it is the reason the tab strip is
     wider than its labels need: MEASURED at 768, Products / Brands / Services / Industries all sat
     at exactly 108px against natural widths well under that, so the strip ran 582px and - now that
     item 6 centres it under the search box - its left edge landed 13px OUTSIDE the container's
     content edge at 768/820/912. Without the floor each tab is its own width and the strip fits.
     The `li.nav-item li.nav-item` rule below is kept as-is: it is equally dead against the current
     mega template (whose rail items are .mm-rail-item and whose links are .mm-link-item), but it
     changes nothing measurable and is not this item's business. */
  ul#navMenuMega > li.nav-item li.nav-item {
    min-width: 282px;
    padding: 2px 1rem;
  }
  nav .nav-item:hover > .dropdown-menu,
  .dropdown:hover > .dropdown-menu {
    display: block;
  }
  /* Buttons */
  .btn-search-submit {
    line-height: 0.9;
  }
  /* Carousels */
  /* REFINEMENT D4 (2026-08-12): the `height: 600px` that used to sit here is DELETED, not moved.
     It predates the Session E gallery, which is a ratio box (aspect-ratio 4/3 from lg) whose own
     `.product-gallery__frame .carousel-inner, .carousel-item { height: 100% }` reserves the height.
     At (0,3,0)/(0,4,0) from skin.css it out-ranked that (0,2,0) rule in product.css and pinned the
     slide to 600px inside a 663px frame - measured at 1440, the image sat 30px above the frame's
     centre. Nothing else on the site carries .carousel-product-image-gallery, so there is no other
     consumer to preserve. */
  .carousel-inner .carousel-item.active,
  .carousel-inner .carousel-item-next,
  .carousel-inner .carousel-item-prev {
    display: flex;
  }
  /* ...and the PDP gallery opts OUT of that flex row, at (0,4,0) so it wins on specificity rather
     than on source order. Every other carousel on the site is a multi-item row that needs it; a
     gallery slide holds ONE flex-centred anchor, and as a flex ITEM that anchor shrinks to its
     image and lands hard left - measured 141px off centre in an 884px frame at 1440. Block display
     lets it fill the slide, and its own align/justify centre the picture on both axes. */
  .carousel-product-image-gallery .carousel-inner .carousel-item.active {
    display: block;
  }
  .carousel-inner .carousel-item-end.active,
  .carousel-inner .carousel-item-next {
    transform: translateX(25%);
  }
  .carousel-inner .carousel-item-start.active,
  .carousel-inner .carousel-item-prev {
    transform: translateX(-25%);
  }
  .carousel-inner .carousel-item-end,
  .carousel-inner .carousel-item-start {
    transform: translateX(0);
  }
  .carousel-indicators [data-bs-target] {
    height: auto;
    width: auto;
    text-indent: 0;
    opacity: 1;
  }
  /* Images */
  /* .img-product-thumbnail deleted (Session J): its ONLY writer was _ProductSummaryPartial's
     retired desktop/phone Bars markup. Swept across .cshtml/.ascx/.js/.cs/.txt and the whole
     Source tree - zero references outside compiled output. .result-row__img sizes the row image
     now (120/96/92px, in _components.scss). Same deletion in _mobile-overrides.scss. */
  /* R100 (2026-08-21): `.img-product-selected` and `.img-gallery-badge` DELETED here and in
     _mobile-overrides.scss, with the base `.img-gallery-badge` in _chrome.scss. Same finding as
     `.img-product-thumbnail` above: pre-PhotoSwipe PDP gallery helpers with zero writers across
     every .cshtml / .ascx / .js / .cs in all three projects, dbo.HtmlText on DEV, or 24 rendered
     page loads. The gallery sizes its own slide and thumb rail in _product.scss now. */
}
@media (min-width: 1200px) {
  /* Navigation */
  ul#navMenuMega > li.nav-item {
    min-width: 140px;
  }
  a.dropdown-item.dropdown-toggle {
    display: flex;
    justify-content: space-between;
  }
  .dropdown-menu {
    padding: 0px !important;
  }
  /* R100 (2026-08-21): `.w-lg-75` DELETED - Bootstrap 5 has NO responsive width utilities at all
     (`.w-25/50/75/100` only), so this was a hand-rolled fill for a gap, authored at min-width
     1200 rather than lg's 992. Zero writers across every .cshtml / .ascx / .js / .cs in all three
     projects, dbo.HtmlText on DEV, or 24 rendered page loads. Same finding as the `w-sm-auto`
     phantom from 2026-08-20. */
}
/* COLAMCO mobile (<=767px) overrides.
  Migrated VERBATIM from the formerly hand-maintained resources/styles/mobile.css (UI Modernization Phase 0.1).
  Mobile rules must stay inside the @media (max-width: 767px) wrapper (the trailing Google rating-badge
  rule was intentionally unwrapped in the source and remains so). */
/* Make sure all content is inside a media wrapper so it will only affect mobile screen sizes after it's minified and bundled */
@media (max-width: 767px) {
  /* POLISH S6b, P3-25 (2026-08-20): KEPT, and flagged as the one declaration in this file that
     could not be verified in a browser. This portal's notice renders
     `cc-window cc-banner cc-type-info cc-theme-block cc-bottom` (measured live at 402), so
     `cc-floating` is a vendor LAYOUT this site does not currently configure and the rule matches
     nothing to look at. Its purpose is recoverable - it ties with the vendor's own
     `.cc-window.cc-floating{max-width:24em}` / `{max-width:none}` at (0,2,0) - but a tie is settled
     by source order, and a rule that cannot be rendered is not one to go changing on a byte-offset
     argument alone. Left as found. */
  .cc-window.cc-floating {
    max-width: unset !important;
  }
  /* Mobile cookie notice: ATTACHED flush on top of the bottom tab bar, edge to edge -
     not a floating pill sitting clear of it. Same treatment as the /cart sticky bar
     (.cart-mbar, _cart.scss) and the PDP buy bar (.sticky-atc, Views/Product/Index.cshtml):
     full-bleed, top corners only, bottoming out at the tab bar's own top edge (57px,
     measured on devbeta) plus the iOS safe-area inset.
     B6 (2026-08-12): the 46px bottom band is DELETED with the cart orb it cleared - the orb
     crested 36px above the tab bar (box plus its 5px solid ring) and otherwise rode over the
     Accept button, exactly as it did on /cart (R91b). The flat tab bar breaks nothing above
     its own top edge, so the notice keeps only its own padding.
     ⚠ Session Z (2026-08-13) REVERSED the stacking, and the old claim here - "the bar sits ABOVE
     .cart-mbar / .sticky-atc (z 1031 vs 1029)" - was wrong twice over: .sticky-atc is z 1019, not
     1029, and z-index never separated them anyway because all four boxes share this `bottom`.
     Juan's order is tab bar, then sticky bar, then notice - so for the one page view before it is
     dismissed the NOTICE is offset upward onto the sticky bar's top edge, by the skin's
     _DockNoticeAboveBars (skin.js), which is also what squares the bar's top corners
     (body.cc-notice-up). The `bottom` below is still the dock the notice returns to when no
     sticky bar is up, and it is `!important`, which is why the docker writes with 'important'. */
  /* POLISH S6b, P3-25 (2026-08-20): SIX of this block's fourteen !importants are GONE and the other
     eight are named below. This whole file is a VERBATIM migration of the hand-maintained
     resources/styles/mobile.css (see the header), where !important was the house style - so the
     default assumption here is decoration until proven otherwise.
     MEASURED, and this is what decides all six: `cookieconsent.min.css` contains **ZERO**
     !important declarations, and in the live composite at cdv 1033 its `.cc-*` rules sit at bytes
     279,461-282,297 while this file's sit at ~372,179 - i.e. the vendor loses on source order at
     equal specificity and on specificity everywhere else. Per declaration:
       - `left` / `right`: vendor `.cc-window.cc-banner{left:0;right:0}` (0,2,0), SAME VALUES, and
         we are later. Redundant twice over.
       - `width` / `padding`: vendor `.cc-window.cc-banner{padding:1em 1.8em;width:100%}` and
         `.cc-theme-edgeless.cc-window{padding:0}`, both (0,2,0), both earlier.
       - `max-width`: vendor sets it only on `.cc-window.cc-floating` and `.cc-banner .cc-message`,
         neither of which is this element.
       - `border-radius`: the vendor sets it on `.cc-floating.cc-theme-classic` and
         `.cc-revoke.cc-bottom` only - this notice renders `cc-banner cc-theme-block cc-bottom`
         (measured live), so NOTHING competes. `body.cc-notice-up` squares the BAR's corners, not
         the notice's, so it is not a competitor either.
     ⚠ `bottom` and `z-index` KEEP theirs and must: they ARE the notice dock that M2 §11, M3 §2 and
     Session Z are built on. `skin.js`'s `_DockNoticeAboveBars` writes the lifted offset with
     `setProperty('bottom', …, 'important')` and says in its own comment that it has to because this
     rule is !important; stripping it here would leave that comment lying and invite a later
     "simplification" of the docker that silently loses to the stylesheet. */
  .cc-window.cc-bottom {
    left: 0;
    right: 0;
    bottom: calc(57px + env(safe-area-inset-bottom, 0px)) !important;
    width: auto;
    max-width: none;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    padding: 0.8rem 0.9rem;
    /* 2026-08-18: the masthead's navy field, mirrored to the bottom edge - see _chrome.scss.
       POLISH S6b, P3-25: these six KEEP their !important, and the reason is NOT the vendor -
       it is `_chrome.scss`'s own `.cc-window` block (~:1010), which declares the same field,
       gradient, ink and a `border-top: 0` for the DESKTOP notice, all !important at (0,1,0).
       An !important at lower specificity beats a non-important at higher, so dropping it here
       would hand the phone notice to the desktop rule - and the two DISAGREE on two of them:
       this block wants a 1px light `border-top` and a plain `--shadow-3`, chrome wants no top
       border and a hairline-plus-shadow. Same values on the other four, so it would fail
       SILENTLY on the border and the shadow only. The right fix is to de-duplicate the two
       blocks, which is a component decision and not an !important audit's business - it is
       written up as an open item in the S6b results block. */
    background-color: var(--blue-deep) !important;
    background-image: radial-gradient(120% 180% at 20% 100%, #0d3059 0%, #051a30 62%, #051526 100%) !important;
    color: #fff !important;
    box-shadow: var(--shadow-3) !important;
    border: 0 !important;
    border-top: 1px solid rgba(255, 255, 255, 0.16) !important;
    z-index: 1031 !important;
  }
  /* UI BATCH 2026-08-26 (Juan): the checkout skin has NO phone tab deck (Checkout.ascx comments
     the BottomBar out; its own BottomBarCheckout place-order bar is TOP-docked by _cart.scss),
     so the 57px above - which IS the tab deck's top edge - left the notice floating over an
     empty strip. It goes flush on the viewport floor there, safe-area inset kept.
     The class is on #divCookieConsentHost in Checkout.ascx, i.e. the notice's own container, so
     this needs no <body> hook and nothing has to detect the page from script.
     ⚠ !important for the same reason the rule above has it, and here it must ALSO out-specify
     that rule's own !important - the ID does that (1,1,2 against 0,2,0). skin.js's
     _DockNoticeAboveBars still wins over both when a floor bar is actually up: its inline write
     is `setProperty(..., 'important')`, and inline !important beats any stylesheet. */
  #divCookieConsentHost.cc-host--floor .cc-window.cc-bottom {
    bottom: env(safe-area-inset-bottom, 0px) !important;
  }
  /* M3 §7.1 (2026-08-19): the notice STEPS ASIDE while an offcanvas drawer is open.
     ROOT CAUSE it works around: `.cc-window` above is z 1031 in <body>'s own stacking context,
     while #navSidebarMenu (BottomBar.ascx's phone drawer) declares z-index 1045 INSIDE
     DIV.container at z 1030, itself inside #divBottomBarContentWrapper at z 1010 - so the drawer's
     effective stack is 1030, the notice paints over its bottom ~100px, and two boxes at the same
     `bottom` cannot be separated by stacking order anyway: the loser is unreachable to a TAP.
     Measured at 402x874 (M3 §3): the drawer's account row at y 730, h 56, took no tap at all with
     the notice up, and took it first time every time once the notice was dismissed.
     The other candidate - lifting #divBottomBarContentWrapper above 1031 - was REJECTED: M2 §11 and
     M3 §2 are both built on the notice landing ON the floor bar (body.cc-notice-up squares the
     strip's / buy bar's top corners and --sticky-bar-h is published from the same read), so raising
     the bar would undo the whole dock. This is the notice moving, exactly like _DockNoticeAboveBars.
     ⚠ opacity + pointer-events, NOT display / visibility / height. skin.js's
     _GetVisibleCookieNotice decides "a notice is up" from display, visibility and height, so any of
     those three would make the next _DockNoticeAboveBars pass read "no notice", drop
     body.cc-notice-up and un-square the bar for the rest of the page view. Opacity is the one
     property that docker is documented NOT to test - the dock keeps working underneath.
     The transition is RESTATED because cookieconsent's own is `opacity 1s ease`: a full second of
     notice still sitting over a drawer that has already finished opening.
     No aria-hidden / inert needed: Bootstrap traps focus inside an offcanvas with a backdrop, so
     nothing in the notice is keyboard-reachable while the class is on. */
  body.offcanvas-up .cc-window {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
  }
  /* 2026-08-25 (Juan, screenshot): the phone drawer rendered UNDER the masthead. The drawer's own
     .mm-header (the MENU eyebrow and its close button) and the whole first nav row were behind the
     logo bar and the search field, and the backdrop did not dim them either - so the drawer read as
     a half-drawn panel whose first row was sliced in two.
     MEASURED at 390x844 on devbeta, drawer open: `.header.sticky-top` is z 1020 and a direct child
     of FORM#Form, while #navSidebarMenu declares z 1045 inside DIV.container.fixed-bottom at z 1030.
     On the numbers the drawer should already win, and it does not: elementFromPoint(200, 60) returns
     the masthead's search INPUT with the drawer open, and raising that .fixed-bottom container to
     1046 did NOT change the hit - so this is not a z-index arithmetic problem to be out-bid from the
     drawer's side. Dropping the HEADER below the drawer's context is what actually moves it: with
     1000 here, the same probe returns the drawer's own HEADER.mm-header, the backdrop (z 1040) then
     covers the masthead as Bootstrap intends, and the close button becomes reachable.
     ⚠ Scoped to `body.offcanvas-up` so the masthead keeps its 1020 the rest of the time (it has to
     out-rank the page, the notice dock and the top-docked bars), and scoped to this phone media
     query because every drawer that can occlude the masthead is opened from the phone tab bar. The
     same mechanism as `body.offcanvas-up .cc-window` above: while a drawer is up, the chrome under
     it steps aside instead of the drawer trying to climb over it. */
  body.offcanvas-up .header.sticky-top {
    z-index: 1000;
  }
  /* 2026-08-27 (Juan): the PDP buy bar stayed ON TOP of the phone drawer. Same mechanism as the
     masthead rule above, and this is the measurement that finally names the cause the 2026-08-25
     note could only describe ("not a z-index arithmetic problem to be out-bid from the drawer's
     side").
     MEASURED at 402x874 on devbeta, a PDP with the buy bar floor-docked and the drawer open:
     `FORM#Form` is `display: flex`, so `#divBottomBarContentWrapper` is a FLEX ITEM - and a flex
     item's `z-index` applies at `position: static`. Its 1010 therefore creates a stacking context
     that PINS the whole drawer subtree at 1010, no matter what the boxes inside it declare
     (`DIV.container.fixed-bottom` 1030, `#navSidebarMenu` 1045). Probed: raising that container to
     1200 and the drawer itself to 2000 left `elementFromPoint(201, 784)` on `DIV.sticky-atc-meta`;
     raising the WRAPPER to 1050 moved it to the drawer's own `I.fa-headset` first try.
     So every fixed bar whose z sits between 1010 and the backdrop's 1040 paints over the drawer -
     .sticky-atc (1019, _product.scss), .cart-mbar (1029) and .co-mbar (1030, both _cart.scss). All
     three are named here rather than only the PDP's: it is one defect with three instances, and the
     drawer is reachable from /cart and /checkout too.
     ⚠ THE BARS STEP ASIDE, THE WRAPPER IS NOT LIFTED. Raising #divBottomBarContentWrapper past the
     backdrop (1040) is what it would take to fix it from the drawer's side, and that carries the
     phone tab deck up with it - the deck would stop being dimmed while the drawer is open. Dropping
     the bars under the wrapper's 1010 costs nothing: the backdrop still dims them, and 1000 is the
     value the masthead above already steps down to, which it cannot collide with (a top-docked bar
     sits under the header, a floor-docked one on the deck's top edge - neither overlaps it). */
  body.offcanvas-up .sticky-atc,
  body.offcanvas-up .cart-mbar,
  body.offcanvas-up .co-mbar {
    z-index: 1000;
  }
  .cc-window.cc-bottom .cc-message {
    font-size: 0.82rem;
    line-height: 1.35;
    margin-bottom: 0.55rem;
  }
  /* Full-bleed bar, so the actions get the full width too and read as one row of
     equal-weight buttons rather than a pill's worth of crowded chips. */
  .cc-window.cc-bottom .cc-compliance {
    display: flex;
    gap: 0.5rem;
    align-items: stretch;
    width: 100%;
  }
  .cc-window.cc-bottom .cc-compliance .cc-btn {
    flex: 1 1 0;
  }
  /* POLISH S6b, P3-25 (2026-08-20): the nine !importants across these three button rules are GONE,
     TOGETHER WITH _chrome.scss's ten matching ones - neither side could go alone, because the phone
     block only ever won by being !important against an !important. Once both sides are plain, this
     block still wins: it is one class more specific AND later in skin.css. The `padding` shorthand
     here beats chrome's `padding-left/right` longhands the same way, so the 402px bar keeps its
     tighter buttons. MEASURED after: see the _chrome.scss note for the full competitor list. */
  .cc-window.cc-bottom .cc-btn {
    border-radius: var(--radius-sm);
    font-weight: 700;
    padding: 0.45rem 0.9rem;
    min-width: 5.5rem;
    text-transform: none;
  }
  .cc-window.cc-bottom .cc-btn.cc-allow,
  .cc-window.cc-bottom .cc-btn.cc-dismiss {
    background-color: #ffffff;
    color: #062b4c;
    border: 0;
  }
  .cc-window.cc-bottom .cc-btn.cc-deny {
    background-color: transparent;
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.75);
  }
  /* Carousels */
  .carousel-inner .carousel-item > div {
    display: none;
  }
  .carousel-inner .carousel-item > div:first-child {
    display: block;
  }
  .carousel-inner .carousel-item.active,
  .carousel-inner .carousel-item-next,
  .carousel-inner .carousel-item-prev {
    display: flex;
  }
  /* REFINEMENT D4 (2026-08-12): the PDP gallery opts out of the flex row here too - see the longer
     note on the matching rule in _desktop-overrides.scss. A gallery slide holds one flex-centred
     anchor, and as a flex item that anchor shrinks to its image instead of filling the frame. */
  .carousel-product-image-gallery .carousel-inner .carousel-item.active {
    display: block;
  }
  .carousel-inner .carousel-item-end,
  .carousel-inner .carousel-item-start {
    transform: translateX(0);
  }
  .carousel-indicators [data-bs-target] {
    background-color: rgba(0, 70, 127, 0.35);
    max-width: 1.5rem;
    height: 0.5rem;
  }
  .carousel-indicators [data-bs-target].active {
    background-color: var(--blue, #00467F);
  }
  /* Images */
  /* .img-product-thumbnail deleted (Session J) - see the note in _desktop-overrides.scss. */
  /* R100 (2026-08-21): `.img-product-selected` and `.img-gallery-badge` DELETED - see the note in
     _desktop-overrides.scss. */
  /* Typography */
  /* POLISH S6b, P3-25 (2026-08-20): `.text-center-sm { text-align: center !important }` DELETED.
     MEASURED: zero consumers - the only occurrence of the class name in the whole repo was this
     declaration itself. A utility nothing wears is not an !important to justify, it is a rule to
     remove. */
}
/* Google */
/* POLISH S6b, P3-25 (2026-08-20): both KEPT. This element is injected by Google's own script, which
   sizes and positions it with INLINE styles - and an inline declaration beats any author rule,
   including an id selector at (1,0,0). !important is the only thing that can hide it. Deliberately
   left outside the media wrapper (see the file header): the badge is suppressed at every width. */
#___ratingbadge_0 {
  z-index: 0 !important;
  display: none !important;
}
