/* ============================================================================
   MERCURY THEME
   A portable design layer for Mercury Commerce web applications.

   HOW TO USE THIS FILE IN ANOTHER APP
   -----------------------------------
   1. Copy this file to wwwroot/css/mercury-theme.css. It is self contained:
      no imports, no fonts, no images, no application specific rules.
   2. Load it AFTER Bootstrap 5 and after the Kendo "bootstrap" theme, and
      BEFORE your own site.css:

        bootstrap.min.css
        kendo/styles/bootstrap-main.css     (only if the app uses Kendo)
        mercury-theme.css                   <-- this file
        site.css                            (app specific rules, wins on conflict)

   3. Nothing else. This file layers on Bootstrap rather than replacing it, so
      stock Bootstrap components pick the palette up on their own.

   WHAT IS IN HERE
   ---------------
   SECTION 1  Design tokens (--mc-*). The single source of truth.
   SECTION 2  Bootstrap variable mapping. Bootstrap's own --bs-* variables are
              pointed at the tokens, so buttons, alerts, badges, forms, tables,
              cards and links follow the palette without per component rules.
   SECTION 3  Kendo variable mapping. Kendo derives every hover/active/subtle
              shade from its base colours, so setting the bases keeps Kendo
              widgets and Bootstrap components from drifting apart.
   SECTION 4  Base elements, focus ring, accessibility.
   SECTION 5  Page shell: .page-head, .stat-band, .page-toolbar, .filter-pills.
   SECTION 6  Content patterns: .chip, .field-label, .empty-state,
              .mail-layout, .table-cards.
   SECTION 7  Responsive and touch. Desktop is the primary target: no rule in
              this file adds padding or loses density above 992px.
   SECTION 8  THEMES. One [data-theme="..."] block per theme, each REDECLARING
              tokens from SECTION 1 and nothing else (bar a handful of dark
              mode repairs to browser-drawn chrome). Plus the theme picker.

   THEMES
   ------
   The host writes data-theme on <html>, SERVER SIDE, so the first paint is
   already correct and there is no flash to correct. Five ship with the file:

     professional   The default. Off-white page, white cards, brand blue.
     stodgy-bank    Deep navy on cream. Conservative, quiet, unplayful.
     colorful       Violet accent, tinted chips and tiles. Still measured.
     high-contrast  7:1 or better body text, hard black borders, no shadows.
     dark           Layered dark greys, brightened accents, flipped status ink.

   A theme block sets ONLY tokens. That is the contract, and it is what keeps
   the count of rules constant as themes are added: no theme owns a component
   rule, so a new component is themed by every theme the day it is written.
   Anything a theme cannot express as a token is a missing token, not a
   licence to add [data-theme="x"] .some-component { }.

   Every theme's body, muted and subtle text clears WCAG AA (4.5:1) against
   the surface it is actually set on, and high-contrast clears AAA (7:1).
   The numbers are in each block.

   TOKEN REFERENCE
   ---------------
   Colour
     --mc-blue-50 .. --mc-blue-900   Brand ramp derived from the Mercury blue
                                     #01539b (hsl 208, 99%, 31%), which sits at
                                     the 700 step. Same hue throughout, so every
                                     tint reads as the same blue.
     --mc-primary                    = --mc-blue-700, the brand colour.
     --mc-primary-hover/-active      Darker steps for interaction.
     --mc-primary-subtle             Tinted fill for selected/open states.
     --mc-brand-blue/-orange/-grey   The three colours of the product mark.
                                     DECORATIVE ONLY: logo and brand artwork.
                                     Never a button, link, badge, alert or status.
     --mc-grey-50 .. --mc-grey-900   Neutral ramp (slate). --mc-grey-900 is the
                                     body text colour; 600 is muted text, 500 is
                                     subtle text; 200 is the standard border.
                                     The 500 step is DARKER than the stock slate
                                     500: see the note on the ramp itself.
   Semantic status
     --mc-success / -subtle / -on-subtle    Delivered, active, healthy.
     --mc-warning / -subtle / -on-subtle    Needs a look, not yet broken.
     --mc-danger  / -subtle / -on-subtle    Exception, failure, denied.
     --mc-info    / -subtle / -on-subtle    Informational, in progress.
     Every solid status colour clears 4.5:1 on white and carries white text at
     4.5:1 or better, so a badge is legible either way round.
   Surface
     --mc-surface           Page background. A soft off-white, NOT #ffffff.
     --mc-surface-raised    Cards, tiles, table rows. This one IS white, so a
                            card lifts off the page without needing a shadow.
     --mc-surface-sunken    Card headers, toolbars, table headers.
     --mc-border            Default hairline.
     --mc-border-strong     Emphasised hairline (open tiles, focused panels).
     --mc-on-status         Ink that sits ON a solid status or accent fill
                            (badge, tile header, filled pill). White on light
                            themes; a dark ink on dark themes, where the status
                            solids are light. Never hardcode #ffffff for this.
   Typography
     --mc-font-sans, --mc-font-mono
     --mc-text-xs .. --mc-text-3xl   0.75 / 0.8125 / 0.9 / 1 / 1.125 / 1.35 /
                                     1.75 / 2.15 rem
     --mc-leading-tight/-normal
     --mc-weight-normal/-medium/-semibold/-bold
   Spacing   --mc-space-0 .. --mc-space-8  (0, 2, 4, 8, 12, 16, 24, 32, 48 px)
   Width
     --mc-width-form      60rem   Entry and edit screens.
     --mc-width-reading   75rem   Detail pages, dashboards. THE DEFAULT.
     --mc-width-list      110rem  Dense tables and grids.
     --mc-width-shell     Navbar and footer inner content cap.
     Applied through .app-content and the .content-width-* classes; see the
     CONTENT WIDTH block at the top of SECTION 5.
   Radius    --mc-radius-sm/-md/-lg/-pill
   Elevation --mc-shadow-1 (resting) / -2 (hover) / -3 (overlay)
   Focus     --mc-focus-ring, --mc-focus-ring-color

   No em dashes appear in this file by house rule.
   ============================================================================ */

/* ============================================================================
   SECTION 1  DESIGN TOKENS
   ============================================================================ */

:root {
  /* --- Brand ramp. One hue (208), lightness stepped. ---------------------- */
  --mc-blue-50: #ecf5fe;
  --mc-blue-100: #d4e9fc;
  --mc-blue-200: #aad4f8;
  --mc-blue-300: #6cb2ef;
  --mc-blue-400: #2a90ea;
  --mc-blue-500: #056fcc;
  --mc-blue-600: #025fb1;
  --mc-blue-700: #01539b; /* Mercury blue, the brand colour */
  --mc-blue-800: #013f75;
  --mc-blue-900: #02294b;

  /* --- Brand mark palette. LOGO AND BRAND ILLUSTRATION ONLY. ---------------
     These three are the colours of the Shipping Center mark. They are DECORATIVE
     tokens with no semantic role, and nothing else in the theme maps to them.

     Do not use --mc-brand-orange or --mc-brand-grey for buttons, links, badges,
     alerts, chips, focus states or any status. Two concrete reasons:
       * #ef4623 sits close to the danger red (--mc-danger, #c0392b). On a control
         it reads as a warning, so an ordinary Save button would look like a
         destructive one.
       * #929497 is a warm neutral and collides with the cool slate text ramp
         (--mc-grey-400 / -500), so text set in it looks like a rendering fault
         rather than a deliberate shade.
     Reach for --mc-primary, the status tokens and the grey ramp instead. */
  --mc-brand-blue: var(--mc-blue-700); /* logo only, same value as --mc-primary */
  --mc-brand-orange: #ef4623; /* logo only, never a control or a status */
  --mc-brand-grey: #929497; /* logo only, never body text or a border */

  --mc-primary: var(--mc-blue-700);
  --mc-primary-hover: var(--mc-blue-800);
  --mc-primary-active: var(--mc-blue-900);
  --mc-primary-subtle: var(--mc-blue-50);
  --mc-primary-subtle-strong: var(--mc-blue-100);
  --mc-on-primary: #ffffff;
  /* Three derived primaries the Bootstrap and Kendo mappings need. Named rather than reached
     for as ramp steps, so a theme block redefines FOUR primary tokens and is done instead of
     having to restate a ten step ramp it does not otherwise use. */
  --mc-primary-on-subtle: var(--mc-blue-900); /* ink on --mc-primary-subtle */
  --mc-primary-border-subtle: var(--mc-blue-200); /* hairline around a subtle primary fill */
  --mc-primary-emphasis: var(--mc-blue-300); /* Kendo's light accent, used on dark chrome */

  /* --- Neutral ramp (slate). ---------------------------------------------- */
  --mc-grey-50: #f8fafc;
  --mc-grey-100: #f1f5f9;
  --mc-grey-200: #e2e8f0;
  --mc-grey-300: #cbd5e1;
  --mc-grey-400: #94a3b8;
  /* The 500 step is hsl(215 17% 43%), one nudge darker than the stock slate 500
     (#64748b, hsl 215 16% 47%). Same hue, deliberately moved: #64748b measures
     4.40:1 against the off-white page surface below, which FAILS WCAG AA for the
     11 to 13px type the theme actually sets in it. #5b6b80 measures 5.03:1 on the
     page and 5.44:1 on a card. Everything that reads as "quiet slate" (subtle
     text, --mc-neutral, the neutral stat tile, --bs-secondary) moves with it. */
  --mc-grey-500: #5b6b80;
  --mc-grey-600: #475569;
  --mc-grey-700: #334155;
  --mc-grey-800: #1e293b;
  --mc-grey-900: #0f172a;

  /* THE READABILITY RANK. Three steps, each measured against the page surface
     below rather than against #ffffff, because the page is no longer white:
       --mc-text        #0f172a  16.5:1 on the page, 17.9:1 on a card.
       --mc-text-muted  #475569   7.0:1 on the page,  7.6:1 on a card. AAA.
       --mc-text-subtle #5b6b80   5.0:1 on the page,  5.4:1 on a card. AA.
     Muted and subtle were the slate 500 and 400 steps and measured 4.76:1 and
     2.56:1 on white: the 400 step FAILED outright and the 500 passed only on
     paper, at 11 to 13px, which is where Larry lost the small type. Each rank
     moved down one step and the ramp's own 500 moved with it. */
  --mc-text: var(--mc-grey-900);
  --mc-text-muted: var(--mc-grey-600);
  --mc-text-subtle: var(--mc-grey-500);

  /* --- Surfaces and borders. -----------------------------------------------
     The page is a soft slate-tinted off-white and the CARD is white, so the
     card lifts by being lighter than its page rather than by carrying a shadow.
     A full white page under full white cards is what made the app read harsh:
     nothing separated the content from its background except hairlines, and the
     large field of #ffffff drove the perceived contrast of every quiet label
     down with it. Sunken drops a further step so a card header still reads as
     a band against a white card body. */
  --mc-surface: #f4f6f9;
  --mc-surface-raised: #ffffff;
  --mc-surface-sunken: #e9edf3;
  --mc-border: var(--mc-grey-200);
  --mc-border-strong: var(--mc-grey-300);
  --mc-on-status: #ffffff;

  /* --- Semantic status. Each solid clears 4.5:1 against white. ------------- */
  --mc-success: #1e7a34;
  --mc-success-subtle: #e3f2e6;
  --mc-success-on-subtle: #145724;
  --mc-warning: #a16207;
  --mc-warning-subtle: #fdf3dc;
  --mc-warning-on-subtle: #7a4a05;
  --mc-danger: #c0392b;
  --mc-danger-subtle: #f9e6e3;
  --mc-danger-on-subtle: #8e2a20;
  --mc-info: #0b6e99;
  --mc-info-subtle: #e2f2f9;
  --mc-info-on-subtle: #08516f;
  --mc-neutral: var(--mc-grey-500);
  --mc-neutral-subtle: var(--mc-grey-100);
  --mc-neutral-on-subtle: var(--mc-grey-600);

  /* --- Typography. --------------------------------------------------------- */
  --mc-font-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mc-font-mono: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;

  /* The bottom three steps each moved up one nudge. Colour was only half of why
     the quiet type got lost: at a 16px root the old steps rendered at 11.5, 12.8
     and 14.0px, and the ranks that use them (field labels, card meta, stat tile
     headers, table sub-lines) are also the ranks set in muted and subtle. Small
     AND quiet is the combination that disappears, so both halves moved. The new
     steps render at 12.0, 13.0 and 14.4px. Nothing above base moved: the
     headings were never the complaint, and moving them would reflow every page. */
  --mc-text-xs: 0.75rem;
  --mc-text-sm: 0.8125rem;
  --mc-text-base-sm: 0.9rem;
  --mc-text-base: 1rem;
  --mc-text-lg: 1.125rem;
  --mc-text-xl: 1.35rem;
  --mc-text-2xl: 1.75rem;
  --mc-text-3xl: 2.15rem;

  --mc-leading-tight: 1.2;
  --mc-leading-normal: 1.5;

  --mc-weight-normal: 400;
  --mc-weight-medium: 500;
  --mc-weight-semibold: 600;
  --mc-weight-bold: 700;

  /* --- Spacing. ------------------------------------------------------------ */
  --mc-space-0: 0;
  --mc-space-1: 0.125rem;
  --mc-space-2: 0.25rem;
  --mc-space-3: 0.5rem;
  --mc-space-4: 0.75rem;
  --mc-space-5: 1rem;
  --mc-space-6: 1.5rem;
  --mc-space-7: 2rem;
  --mc-space-8: 3rem;

  /* --- Content width caps. -------------------------------------------------
     Practical ceilings for page content on large monitors: an ultrawide gets
     margins, not wider inputs. Why each number:
       --mc-width-form     60rem (960px at the 16px root). Entry and edit
                           screens. A form is read top to bottom with the
                           mouse; past this width the label-to-field travel
                           grows, single column inputs pass 40 characters of
                           useful width, and the Save button drifts away from
                           the last field.
       --mc-width-reading  75rem (1200px). Detail pages and dashboards: mixed
                           cards and prose. Also THE DEFAULT cap, so a page
                           that never states a width is still never full bleed.
       --mc-width-list     110rem (1760px). Dense tables and grids, which
                           genuinely use width. Fills a 1920 screen minus its
                           gutters, and on a 2560 or 3440 monitor the list
                           centers with margins instead of bleeding edge to
                           edge.
       --mc-width-shell    The widest content cap plus the shell's 1.5rem
                           gutters. The navbar and footer BARS stay full
                           bleed; this caps their inner content so the brand
                           and footer line align with list width content
                           rather than floating to the corner of an ultrawide.
  */
  --mc-width-form: 60rem;
  --mc-width-reading: 75rem;
  --mc-width-list: 110rem;
  --mc-width-shell: calc(var(--mc-width-list) + 3rem);
  /* The settings shape is a rail beside a form, so its cap is the ENTRY measure
     plus the rail plus the gap: the panel column keeps exactly the width it
     would have had as a plain form. Reusing the reading cap here would narrow
     every settings form by the width of the rail. See the settings shell block
     at the end of SECTION 5. */
  --mc-settings-rail: 16.25rem;
  --mc-width-settings: 78rem;

  /* --- Radii. -------------------------------------------------------------- */
  --mc-radius-sm: 0.25rem;
  --mc-radius-md: 0.375rem;
  --mc-radius-lg: 0.5rem;
  --mc-radius-pill: 2rem;

  /* --- Elevation. Cool tinted, so shadows sit in the same palette. --------- */
  --mc-shadow-1: 0 1px 2px rgba(15, 23, 42, 0.08);
  --mc-shadow-2: 0 2px 10px rgba(15, 23, 42, 0.12);
  --mc-shadow-3: 0 8px 28px rgba(15, 23, 42, 0.18);
  --mc-shadow-primary: 0 2px 10px rgba(1, 83, 155, 0.22);

  /* --- Focus. A surface coloured spacer then a brand halo, so the ring is
         visible on both light surfaces and coloured buttons. The spacer takes
         the SURFACE token rather than a literal white: on a dark theme a white
         spacer is the brightest thing on the page and reads as the ring. ----- */
  --mc-focus-ring-color: rgba(1, 83, 155, 0.45);
  --mc-focus-ring-spacer: var(--mc-surface-raised);
  --mc-focus-ring: 0 0 0 0.1rem var(--mc-focus-ring-spacer), 0 0 0 0.25rem var(--mc-focus-ring-color);

  /* --- Chart series. -------------------------------------------------------
     An ORDERED set: a chart with n series takes the first n. Series 1 is the
     brand primary in every theme, because a single series chart sits under stat
     tiles whose default accent IS the primary, and a chart that disagrees with
     the tile above it is claiming they are different numbers.

     These are also stated as literals in ThemeCatalog.cs, because a Kendo chart
     renders to SVG from a server built options object and cannot read a custom
     property. Two renderers, one set of values: change one, change both. */
  --mc-chart-1: var(--mc-primary);
  --mc-chart-2: var(--mc-info);
  --mc-chart-3: var(--mc-success);
  --mc-chart-4: var(--mc-warning);
  --mc-chart-5: var(--mc-danger);
  --mc-chart-6: var(--mc-neutral);
  --mc-chart-axis: var(--mc-border-strong);
  --mc-chart-grid: var(--mc-border);

  /* --- Motion. ------------------------------------------------------------- */
  --mc-transition: 0.12s ease;

  /* --- Compatibility aliases -----------------------------------------------
     Older Mercury markup used --mercury-*. Kept as aliases so an app can adopt
     this file without a find and replace pass. Prefer --mc-* in new work. */
  --mercury-primary: var(--mc-primary);
  --mercury-primary-dark: var(--mc-primary-hover);
  --mercury-dark: var(--mc-text);
  --mercury-border: var(--mc-border);
  --mercury-surface: var(--mc-surface-sunken);
  --mercury-muted: var(--mc-text-muted);
  --mercury-alert: var(--mc-danger);
}

/* ============================================================================
   SECTION 2  BOOTSTRAP MAPPING
   Point Bootstrap's own variables at the tokens. This is the cheap path: stock
   components inherit the theme with no extra selectors.
   ============================================================================ */

:root {
  --bs-font-sans-serif: var(--mc-font-sans);
  --bs-font-monospace: var(--mc-font-mono);
  --bs-body-color: var(--mc-text);
  --bs-body-color-rgb: 15, 23, 42;
  --bs-body-bg: var(--mc-surface);
  --bs-emphasis-color: var(--mc-text);
  --bs-secondary-color: var(--mc-text-muted);
  --bs-tertiary-color: var(--mc-text-subtle);
  --bs-tertiary-bg: var(--mc-surface-sunken);
  --bs-border-color: var(--mc-border);
  --bs-border-color-translucent: var(--mc-border);

  --bs-primary: var(--mc-primary);
  --bs-primary-rgb: 1, 83, 155;
  --bs-primary-text-emphasis: var(--mc-primary-on-subtle);
  --bs-primary-bg-subtle: var(--mc-primary-subtle);
  --bs-primary-border-subtle: var(--mc-primary-border-subtle);

  --bs-secondary: var(--mc-neutral);
  --bs-secondary-rgb: 91, 107, 128;
  --bs-success: var(--mc-success);
  --bs-success-rgb: 30, 122, 52;
  --bs-success-text-emphasis: var(--mc-success-on-subtle);
  --bs-success-bg-subtle: var(--mc-success-subtle);
  
  --bs-danger: var(--mc-danger);
  --bs-danger-rgb: 192, 57, 43;
  --bs-danger-text-emphasis: var(--mc-danger-on-subtle);
  --bs-danger-bg-subtle: var(--mc-danger-subtle);
  
  --bs-warning: var(--mc-warning);
  --bs-warning-rgb: 161, 98, 7;
  --bs-warning-text-emphasis: var(--mc-warning-on-subtle);
  --bs-warning-bg-subtle: var(--mc-warning-subtle);
  
  --bs-info: var(--mc-info);
  --bs-info-rgb: 11, 110, 153;
  --bs-info-text-emphasis: var(--mc-info-on-subtle);
  --bs-info-bg-subtle: var(--mc-info-subtle);
  

  /* Bootstrap's "light", "dark" and "secondary" families are the ones nothing thinks about
     until a theme moves: .alert-light is a WHITE slab with dark ink, compiled at build time,
     and on the dark theme it was the one bright rectangle on the Quick Ship page. Light means
     the sunken surface and dark means the text colour, on every theme, which is what those
     words were always shorthand for. */
  --bs-light: var(--mc-surface-sunken);
  --bs-light-bg-subtle: var(--mc-surface-sunken);
  --bs-light-border-subtle: var(--mc-border);
  --bs-light-text-emphasis: var(--mc-text-muted);
  --bs-dark: var(--mc-text);
  --bs-dark-bg-subtle: var(--mc-surface-sunken);
  --bs-dark-border-subtle: var(--mc-border-strong);
  --bs-dark-text-emphasis: var(--mc-text);
  --bs-secondary-bg: var(--mc-surface-sunken);
  --bs-secondary-bg-subtle: var(--mc-neutral-subtle);
  --bs-secondary-border-subtle: var(--mc-border-strong);
  --bs-secondary-text-emphasis: var(--mc-neutral-on-subtle);

  --bs-link-color: var(--mc-primary);
  --bs-link-color-rgb: 1, 83, 155;
  --bs-link-hover-color: var(--mc-primary-hover);
  --bs-link-hover-color-rgb: 1, 63, 117;

  --bs-border-radius: var(--mc-radius-md);
  --bs-border-radius-sm: var(--mc-radius-sm);
  --bs-border-radius-lg: var(--mc-radius-lg);
  --bs-border-radius-pill: var(--mc-radius-pill);

  --bs-box-shadow-sm: var(--mc-shadow-1);
  --bs-box-shadow: var(--mc-shadow-2);
  --bs-box-shadow-lg: var(--mc-shadow-3);

  --bs-focus-ring-color: var(--mc-focus-ring-color);
  --bs-focus-ring-width: 0.25rem;
}

/* Bootstrap 5.3 buttons read from --bs-btn-*, which Sass sets per variant at
   build time, so the :root mapping above cannot reach them. These four rules
   are the whole cost of retinting every button in the app. */
/* --bs-btn-color, not only the fill. On the dark theme the primary is a LIGHT blue and the
   label stayed Bootstrap's compiled white: 2.51:1, on the Save button of every form in the
   app. The fill and its ink are one decision and have to move together. */
.btn-primary {
  --bs-btn-bg: var(--mc-primary);
  --bs-btn-border-color: var(--mc-primary);
  --bs-btn-color: var(--mc-on-primary);
  --bs-btn-hover-color: var(--mc-on-primary);
  --bs-btn-active-color: var(--mc-on-primary);
  --bs-btn-disabled-color: var(--mc-on-primary);
  --bs-btn-hover-bg: var(--mc-primary-hover);
  --bs-btn-hover-border-color: var(--mc-primary-hover);
  --bs-btn-active-bg: var(--mc-primary-active);
  --bs-btn-active-border-color: var(--mc-primary-active);
  --bs-btn-disabled-bg: var(--mc-primary);
  --bs-btn-disabled-border-color: var(--mc-primary);
}

/* 69 views reach for .btn-outline-secondary as "the quiet button". Left alone it is a compiled
   #6c757d that belongs to no theme: too faint on cream, nearly invisible on dark. */
.btn-outline-secondary {
  --bs-btn-color: var(--mc-text-muted);
  --bs-btn-border-color: var(--mc-border-strong);
  --bs-btn-hover-bg: var(--mc-surface-sunken);
  --bs-btn-hover-color: var(--mc-text);
  --bs-btn-hover-border-color: var(--mc-text-muted);
  --bs-btn-active-bg: var(--mc-surface-sunken);
  --bs-btn-active-color: var(--mc-text);
  --bs-btn-active-border-color: var(--mc-text-muted);
  --bs-btn-disabled-color: var(--mc-text-subtle);
  --bs-btn-disabled-border-color: var(--mc-border);
}

.btn-secondary {
  --bs-btn-bg: var(--mc-neutral);
  --bs-btn-border-color: var(--mc-neutral);
  --bs-btn-color: var(--mc-on-status);
  --bs-btn-hover-bg: var(--mc-neutral-on-subtle);
  --bs-btn-hover-border-color: var(--mc-neutral-on-subtle);
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-bg: var(--mc-neutral-on-subtle);
  --bs-btn-active-border-color: var(--mc-neutral-on-subtle);
}

.btn-outline-primary {
  --bs-btn-color: var(--mc-primary);
  --bs-btn-border-color: var(--mc-primary);
  --bs-btn-hover-color: var(--mc-on-primary);
  --bs-btn-active-color: var(--mc-on-primary);
  --bs-btn-hover-bg: var(--mc-primary);
  --bs-btn-hover-border-color: var(--mc-primary);
  --bs-btn-active-bg: var(--mc-primary-hover);
  --bs-btn-active-border-color: var(--mc-primary-hover);
  --bs-btn-disabled-color: var(--mc-primary);
  --bs-btn-disabled-border-color: var(--mc-primary);
}

.btn-success {
  --bs-btn-bg: var(--mc-success);
  --bs-btn-border-color: var(--mc-success);
  --bs-btn-hover-bg: var(--mc-success-on-subtle);
  --bs-btn-hover-border-color: var(--mc-success-on-subtle);
  --bs-btn-active-bg: var(--mc-success-on-subtle);
  --bs-btn-active-border-color: var(--mc-success-on-subtle);
}

.btn-outline-success {
  --bs-btn-color: var(--mc-success);
  --bs-btn-border-color: var(--mc-success);
  --bs-btn-hover-bg: var(--mc-success);
  --bs-btn-hover-border-color: var(--mc-success);
  --bs-btn-active-bg: var(--mc-success);
  --bs-btn-active-border-color: var(--mc-success);
}

.btn-danger {
  --bs-btn-bg: var(--mc-danger);
  --bs-btn-border-color: var(--mc-danger);
  --bs-btn-hover-bg: var(--mc-danger-on-subtle);
  --bs-btn-hover-border-color: var(--mc-danger-on-subtle);
  --bs-btn-active-bg: var(--mc-danger-on-subtle);
  --bs-btn-active-border-color: var(--mc-danger-on-subtle);
}

.btn-outline-danger {
  --bs-btn-color: var(--mc-danger);
  --bs-btn-border-color: var(--mc-danger);
  --bs-btn-hover-bg: var(--mc-danger);
  --bs-btn-hover-border-color: var(--mc-danger);
  --bs-btn-active-bg: var(--mc-danger);
  --bs-btn-active-border-color: var(--mc-danger);
}

/* The warning and info buttons were the pair nobody had reached for yet, so they still carried
   Bootstrap's compiled #ffc107 and #0dcaf0. Measured on the page: an outline-warning button's
   label ("Run Movement Check", on History) is 1.51:1 against the surface, which is not a
   readable button on ANY theme, including the one that shipped before this work. */
.btn-warning {
  --bs-btn-bg: var(--mc-warning);
  --bs-btn-border-color: var(--mc-warning);
  --bs-btn-color: var(--mc-on-status);
  --bs-btn-hover-bg: var(--mc-warning-on-subtle);
  --bs-btn-hover-border-color: var(--mc-warning-on-subtle);
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-bg: var(--mc-warning-on-subtle);
  --bs-btn-active-border-color: var(--mc-warning-on-subtle);
  --bs-btn-active-color: var(--mc-on-status);
}

.btn-outline-warning {
  --bs-btn-color: var(--mc-warning);
  --bs-btn-border-color: var(--mc-warning);
  --bs-btn-hover-bg: var(--mc-warning);
  --bs-btn-hover-border-color: var(--mc-warning);
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-bg: var(--mc-warning);
  --bs-btn-active-border-color: var(--mc-warning);
  --bs-btn-active-color: var(--mc-on-status);
  --bs-btn-disabled-color: var(--mc-warning);
  --bs-btn-disabled-border-color: var(--mc-warning);
}

.btn-info {
  --bs-btn-bg: var(--mc-info);
  --bs-btn-border-color: var(--mc-info);
  --bs-btn-color: var(--mc-on-status);
  --bs-btn-hover-bg: var(--mc-info-on-subtle);
  --bs-btn-hover-border-color: var(--mc-info-on-subtle);
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-bg: var(--mc-info-on-subtle);
  --bs-btn-active-border-color: var(--mc-info-on-subtle);
  --bs-btn-active-color: var(--mc-on-status);
}

.btn-outline-info {
  --bs-btn-color: var(--mc-info);
  --bs-btn-border-color: var(--mc-info);
  --bs-btn-hover-bg: var(--mc-info);
  --bs-btn-hover-border-color: var(--mc-info);
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-bg: var(--mc-info);
  --bs-btn-active-border-color: var(--mc-info);
  --bs-btn-active-color: var(--mc-on-status);
  --bs-btn-disabled-color: var(--mc-info);
  --bs-btn-disabled-border-color: var(--mc-info);
}

/* The solid success and danger buttons carry the on-status ink for the same reason as the
   badges: on the dark theme those fills are light. */
.btn-success,
.btn-danger {
  --bs-btn-color: var(--mc-on-status);
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-color: var(--mc-on-status);
}

.btn-outline-success,
.btn-outline-danger {
  --bs-btn-hover-color: var(--mc-on-status);
  --bs-btn-active-color: var(--mc-on-status);
}

/* Bootstrap's warning badge assumes a light yellow with dark text. The Mercury
   warning is a dark amber, so it carries the on-status ink like the other
   solids. That ink is a token, not a literal white: on the dark theme the
   status solids are LIGHT and the ink flips to a near-black. */
.text-bg-warning {
  background-color: var(--mc-warning) !important;
  color: var(--mc-on-status) !important;
}

.text-bg-primary {
  background-color: var(--mc-primary) !important;
  color: var(--mc-on-primary) !important;
}

.text-bg-success {
  background-color: var(--mc-success) !important;
  color: var(--mc-on-status) !important;
}

.text-bg-danger {
  background-color: var(--mc-danger) !important;
  color: var(--mc-on-status) !important;
}

.text-bg-info {
  background-color: var(--mc-info) !important;
  color: var(--mc-on-status) !important;
}

.text-bg-secondary {
  background-color: var(--mc-neutral) !important;
  color: var(--mc-on-status) !important;
}

.text-bg-light {
  background-color: var(--mc-surface-sunken) !important;
  color: var(--mc-text) !important;
}

/* THE SURFACE UTILITIES. Bootstrap's .bg-white, .bg-light, .bg-body and
   .text-dark are literals compiled at build time, so a page that reaches for one
   is a page that ignores the theme: on a dark theme it stays a white slab with
   invisible text. Pointed at the tokens here, once, rather than hunted through
   views. A page asking for "white" is asking for the RAISED surface: it wants a
   card that stands off its page, which is exactly what the token means. */
/* The COLOUR utilities are the same problem one layer down: Bootstrap builds them from
   rgba(var(--bs-primary-rgb), ...), so following the theme would mean every theme block
   restating five rgb triples. Pointed straight at the tokens instead, the triples stay a
   :root detail and a theme block declares colours, not colour arithmetic. Opacity modifiers
   (.text-primary.text-opacity-75) are the cost, and nothing in this app uses them. */
.text-primary { color: var(--mc-primary) !important; }
.text-success { color: var(--mc-success) !important; }
.text-danger { color: var(--mc-danger) !important; }
.text-warning { color: var(--mc-warning) !important; }
.text-info { color: var(--mc-info) !important; }
.text-secondary { color: var(--mc-text-muted) !important; }
.bg-primary { background-color: var(--mc-primary) !important; }
.bg-success { background-color: var(--mc-success) !important; }
.bg-danger { background-color: var(--mc-danger) !important; }
.bg-warning { background-color: var(--mc-warning) !important; }
.bg-info { background-color: var(--mc-info) !important; }
.bg-secondary { background-color: var(--mc-neutral) !important; }
.border-primary { border-color: var(--mc-primary) !important; }
.border-success { border-color: var(--mc-success) !important; }
.border-danger { border-color: var(--mc-danger) !important; }
.border-warning { border-color: var(--mc-warning) !important; }
.border-info { border-color: var(--mc-info) !important; }

.bg-white { background-color: var(--mc-surface-raised) !important; }
.bg-body,
.bg-body-tertiary { background-color: var(--mc-surface) !important; }
.bg-light { background-color: var(--mc-surface-sunken) !important; }
.text-dark,
.text-body { color: var(--mc-text) !important; }
.text-muted,
.text-body-secondary { color: var(--mc-text-muted) !important; }
.border { border-color: var(--mc-border) !important; }

.form-check-input:checked {
  background-color: var(--mc-primary);
  border-color: var(--mc-primary);
}

.nav-pills {
  --bs-nav-pills-link-active-bg: var(--mc-primary);
}

.card {
  --bs-card-bg: var(--mc-surface-raised);
  --bs-card-color: var(--mc-text);
  --bs-card-border-color: var(--mc-border);
  --bs-card-cap-bg: var(--mc-surface-sunken);
  --bs-card-cap-color: var(--mc-text);
}

/* The hover tint is derived from the TEXT colour rather than named as a grey, so
   it darkens a light row and lightens a dark one with no per theme override. */
.table {
  --bs-table-color: var(--mc-text);
  --bs-table-hover-color: var(--mc-text);
  --bs-table-hover-bg: color-mix(in srgb, var(--mc-text) 6%, transparent);
  --bs-table-striped-color: var(--mc-text);
  --bs-table-striped-bg: color-mix(in srgb, var(--mc-text) 4%, transparent);
  --bs-table-border-color: var(--mc-border);
}

.dropdown-menu {
  --bs-dropdown-bg: var(--mc-surface-raised);
  --bs-dropdown-color: var(--mc-text);
  --bs-dropdown-link-color: var(--mc-text);
  --bs-dropdown-link-hover-color: var(--mc-text);
  --bs-dropdown-link-hover-bg: var(--mc-surface-sunken);
  --bs-dropdown-link-active-bg: var(--mc-primary);
  --bs-dropdown-link-active-color: var(--mc-on-primary);
  --bs-dropdown-header-color: var(--mc-text-muted);
  --bs-dropdown-border-color: var(--mc-border);
  --bs-dropdown-divider-bg: var(--mc-border);
  --bs-dropdown-box-shadow: var(--mc-shadow-2);
}

.modal {
  --bs-modal-bg: var(--mc-surface-raised);
  --bs-modal-color: var(--mc-text);
  --bs-modal-border-color: var(--mc-border);
  --bs-modal-header-border-color: var(--mc-border);
  --bs-modal-footer-border-color: var(--mc-border);
}

.list-group {
  --bs-list-group-bg: var(--mc-surface-raised);
  --bs-list-group-color: var(--mc-text);
  --bs-list-group-border-color: var(--mc-border);
  --bs-list-group-action-hover-bg: var(--mc-surface-sunken);
  --bs-list-group-action-hover-color: var(--mc-text);
  --bs-list-group-active-bg: var(--mc-primary);
  --bs-list-group-active-border-color: var(--mc-primary);
  --bs-list-group-active-color: var(--mc-on-primary);
}

.nav-tabs {
  --bs-nav-tabs-border-color: var(--mc-border);
  --bs-nav-tabs-link-active-bg: var(--mc-surface-raised);
  --bs-nav-tabs-link-active-color: var(--mc-primary);
  --bs-nav-tabs-link-active-border-color: var(--mc-border) var(--mc-border) var(--mc-surface-raised);
  --bs-nav-tabs-link-hover-border-color: var(--mc-border);
}

.accordion {
  --bs-accordion-bg: var(--mc-surface-raised);
  --bs-accordion-color: var(--mc-text);
  --bs-accordion-border-color: var(--mc-border);
  --bs-accordion-btn-bg: var(--mc-surface-sunken);
  --bs-accordion-btn-color: var(--mc-text);
  --bs-accordion-active-bg: var(--mc-primary-subtle);
  --bs-accordion-active-color: var(--mc-primary);
}

/* Inputs sit ON a card, so they take the raised surface, not the page. Disabled
   drops to sunken, which is the same relationship on every theme. */
.form-control,
.form-select {
  background-color: var(--mc-surface-raised);
  color: var(--mc-text);
  border-color: var(--mc-border-strong);
}

.form-control:disabled,
.form-select:disabled,
.form-control[readonly] {
  background-color: var(--mc-surface-sunken);
  color: var(--mc-text-muted);
}

.form-control::placeholder {
  color: var(--mc-text-subtle);
}

.input-group-text {
  background-color: var(--mc-surface-sunken);
  color: var(--mc-text-muted);
  border-color: var(--mc-border-strong);
}

/* ============================================================================
   SECTION 3  KENDO MAPPING
   Kendo's bootstrap theme derives hover, active and subtle shades from these
   bases with relative colour functions, so setting the bases is enough for
   buttons, pagers, grids, selection and focus rings to follow the palette.
   Declared here so Kendo widgets and Bootstrap components cannot drift.
   ============================================================================ */

:root {
  --kendo-color-primary: var(--mc-primary);
  --kendo-color-on-primary: var(--mc-on-primary);
  --kendo-color-primary-subtle: var(--mc-primary-subtle);
  --kendo-color-primary-on-subtle: var(--mc-primary-on-subtle);
  --kendo-color-primary-emphasis: var(--mc-primary-emphasis);
  --kendo-color-primary-hover: var(--mc-primary-hover);
  --kendo-color-primary-active: var(--mc-primary-active);

  --kendo-color-secondary: var(--mc-neutral);
  --kendo-color-on-secondary: var(--mc-on-status);
  --kendo-color-tertiary: var(--mc-info);

  --kendo-color-success: var(--mc-success);
  --kendo-color-success-subtle: var(--mc-success-subtle);
  --kendo-color-success-on-subtle: var(--mc-success-on-subtle);
  --kendo-color-warning: var(--mc-warning);
  --kendo-color-warning-subtle: var(--mc-warning-subtle);
  --kendo-color-warning-on-subtle: var(--mc-warning-on-subtle);
  --kendo-color-error: var(--mc-danger);
  --kendo-color-error-subtle: var(--mc-danger-subtle);
  --kendo-color-error-on-subtle: var(--mc-danger-on-subtle);
  --kendo-color-info: var(--mc-info);
  --kendo-color-info-subtle: var(--mc-info-subtle);
  --kendo-color-info-on-subtle: var(--mc-info-on-subtle);

  --kendo-color-app-surface: var(--mc-surface);
  --kendo-color-on-app-surface: var(--mc-text);
  --kendo-color-surface: var(--mc-surface-sunken);
  --kendo-color-surface-alt: var(--mc-surface-raised);
  --kendo-color-border: var(--mc-border);
  --kendo-color-border-alt: var(--mc-border-strong);
  --kendo-color-subtle: var(--mc-text-muted);

  --kendo-color-base: var(--mc-surface-sunken);
  --kendo-color-base-subtle: var(--mc-border);
  --kendo-color-base-on-surface: var(--mc-text);
  --kendo-color-on-base: var(--mc-text);

  /* Typography and geometry, so a Kendo card and a Bootstrap card are the same
     object seen twice rather than two products in one page. */
  --kendo-font-family: var(--mc-font-sans);
  --kendo-font-size: inherit;
  --kendo-font-size-md: inherit;
  --kendo-line-height: var(--mc-leading-normal);
  --kendo-border-radius-md: var(--mc-radius-md);
  --kendo-border-radius-sm: var(--mc-radius-sm);
  --kendo-border-radius-lg: var(--mc-radius-lg);
}

/* Kendo widgets sit inside Bootstrap pages: keep them on the page type scale
   rather than the theme's own 1rem default. */
.k-widget,
.k-listview,
.k-grid,
.k-pager {
  font-size: inherit;
  font-family: var(--mc-font-sans);
}

/* Kendo draws its own focus outline; align it with the Bootstrap one so keyboard
   users get one ring shape across the whole app. */
.k-focus,
.k-list-item.k-focus,
.k-button:focus-visible {
  box-shadow: var(--mc-focus-ring) !important;
  outline: none;
}

/* ============================================================================
   SECTION 4  BASE ELEMENTS, FOCUS, ACCESSIBILITY
   ============================================================================ */

html {
  font-size: 14px;
  height: 100%;
}

@media (min-width: 768px) {
  html {
    font-size: 16px;
  }
}

body {
  font-family: var(--mc-font-sans);
  color: var(--mc-text);
  background: var(--mc-surface);
}

h1, h2, h3 {
  color: var(--mc-primary);
  line-height: var(--mc-leading-tight);
}

a {
  color: var(--mc-primary);
}

a:hover {
  color: var(--mc-primary-hover);
}

/* One focus ring, everywhere, always visible. Keyboard users are the whole
   point of this block; :focus-visible keeps it off mouse clicks. */
.btn:focus-visible,
.btn-link.nav-link:focus-visible,
.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.page-link:focus-visible,
.dropdown-item:focus-visible,
a:focus-visible,
[tabindex]:focus-visible,
button:focus-visible {
  box-shadow: var(--mc-focus-ring);
  outline: none;
}

.form-control:focus,
.form-select:focus {
  border-color: var(--mc-primary);
}

/* --- Busy buttons ----------------------------------------------------------
   A submit that takes seconds says so on its own face: the button disables and
   grows a spinner, driven by data-busy in site.js. Larry: "these are longer
   running operations without feedback."

   currentColor, always. A button in flight keeps whatever colour it already had,
   so one rule serves .btn-primary (white on the fill), every outline variant
   (the accent on the surface) and all five themes including dark, with no
   per-variant overrides and nothing to keep in sync when a theme is added.

   Bootstrap fades a disabled button to 65% opacity, which on a button that is
   deliberately still the subject of attention reads as "gone" rather than
   "working". A busy button stays at full strength; the spinner is the signal. */
.mc-spinner {
  display: inline-block;
  width: 0.95em;
  height: 0.95em;
  vertical-align: -0.14em;
  border: 0.14em solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: mc-spin 0.7s linear infinite;
}

.btn.mc-busy,
.btn.mc-busy:disabled {
  opacity: 1;
  cursor: progress;
}

@keyframes mc-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
  }

  /* The spinner still has to READ as working, so it keeps turning where a
     transition would simply be dropped: a full stop would leave a busy button
     indistinguishable from a broken one. It turns slowly instead. */
  .mc-spinner {
    animation-duration: 2s;
  }
}

/* ============================================================================
   SECTION 5  PAGE SHELL
   Every page is: header, stat band, toolbar, content. In that order.
   ============================================================================ */

/* --- Content width ---------------------------------------------------------
   THE WIDTH SYSTEM. The layout renders <main> inside a wrapper carrying
   .app-content plus one of the .content-width-* classes; these rules cap the
   main column and center it, so on a wide monitor the margins absorb the extra
   space and no page bleeds edge to edge. The default is the reading cap: a
   page OPTS IN to being wider, and .content-width-full is the deliberate
   exception for a page that truly wants the whole screen.

   The margins and the page share one surface (--mc-surface) on purpose: the
   boundary is seamless rather than a framed panel, so nothing looks cropped or
   broken at 3440px. If a future app wants a contained look instead, restyle
   here once; pages do not know which it is. */
.app-content {
  --mc-content-max: var(--mc-width-reading);
  /* The wrapper itself is capped at the shell width, the same box the navbar
     and footer content is centered in, so a list page's edges land EXACTLY on
     the brand and footer line. Without this the main column centers in the
     viewport while the bars center in the shell, and on an ultrawide the two
     drift a few pixels apart. */
  max-width: var(--mc-width-shell);
  margin-inline: auto;
}

.app-content > main {
  width: 100%;
  max-width: var(--mc-content-max);
  margin-inline: auto;
}

.content-width-form { --mc-content-max: var(--mc-width-form); }
.content-width-reading { --mc-content-max: var(--mc-width-reading); }
.content-width-list { --mc-content-max: var(--mc-width-list); }
.content-width-full { --mc-content-max: 100%; }

/* The navbar and footer BARS run full bleed (background and border edge to
   edge), while their inner containers cap here so the brand, the menu and the
   footer line align with the widest content cap instead of hugging the far
   corners of an ultrawide. Goes on the container INSIDE the bar. */
.shell-max {
  max-width: var(--mc-width-shell);
  margin-inline: auto;
}

/* --- Page header --------------------------------------------------------- */
.page-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--mc-space-4);
  margin-bottom: var(--mc-space-4);
}

.page-head-text {
  min-width: 0;
}

.page-head-title {
  margin: 0;
  font-size: var(--mc-text-xl);
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-primary);
  line-height: var(--mc-leading-tight);
}

.page-head-sub {
  margin: var(--mc-space-1) 0 0;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-base-sm);
}

.page-head-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mc-space-3);
  margin-left: auto;
}

/* --- Stat band ------------------------------------------------------------
   Summary tiles directly under the page header. Every tile is a real link, so
   it is reachable by keyboard and opens in a new tab like any other link. */
.stat-band {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mc-space-3);
  margin-bottom: var(--mc-space-4);
}

/* 7.5rem, not 8.5: a nine tile band has to fit one row at 1280 or the band costs the
   desktop a whole extra row of vertical space before the data starts. */
.stat-tile {
  display: block;
  min-width: 7.5rem;
  flex: 0 1 auto;
  border: 1px solid var(--mc-tile-accent, var(--mc-primary));
  border-radius: var(--mc-radius-md);
  background: var(--mc-surface-raised);
  text-align: center;
  text-decoration: none;
  overflow: hidden;
  transition: box-shadow var(--mc-transition), transform var(--mc-transition);
}

/* The label wraps to a second line rather than widening the tile, and every header reserves
   two lines so a band of mixed length labels still lines its numbers up. */
.stat-tile .stat-header {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 2.6em;
  background: var(--mc-tile-accent, var(--mc-primary));
  color: var(--mc-on-status);
  font-weight: var(--mc-weight-semibold);
  font-size: var(--mc-text-sm);
  line-height: var(--mc-leading-tight);
  padding: 0.3rem 0.5rem;
  text-align: center;
}

.stat-tile .stat-value {
  display: block;
  color: var(--mc-tile-accent, var(--mc-primary));
  font-size: var(--mc-text-2xl);
  font-weight: var(--mc-weight-bold);
  line-height: var(--mc-leading-tight);
  padding: 0.35rem 0.5rem;
}

a.stat-tile:hover,
button.stat-tile:hover {
  box-shadow: var(--mc-shadow-2);
  transform: translateY(-1px);
}

a.stat-tile:focus-visible {
  box-shadow: var(--mc-focus-ring);
  outline: none;
}

/* Tones. A tile's accent says what kind of number it is, not how big it is. */
.stat-tile.stat-alert { --mc-tile-accent: var(--mc-danger); }
.stat-tile.stat-warn { --mc-tile-accent: var(--mc-warning); }
.stat-tile.stat-good { --mc-tile-accent: var(--mc-success); }
.stat-tile.stat-info { --mc-tile-accent: var(--mc-info); }
/* --mc-neutral, NOT the ramp's 500 step. The ramp is a light theme artefact and no theme
   block redeclares it; a neutral tile reaching straight into it stayed slate on the dark
   theme, where its header measured 3.48:1 and its number 2.83:1 against a dark card. Every
   token a component reads has to be one the themes actually own. */
.stat-tile.stat-neutral { --mc-tile-accent: var(--mc-neutral); }

/* The tile whose list is currently open. The number and the rows below have to
   read as one thing, so the open tile is filled rather than outlined. */
.stat-tile.stat-open {
  box-shadow: var(--mc-shadow-2);
}

.stat-tile.stat-open .stat-value {
  background: color-mix(in srgb, var(--mc-tile-accent, var(--mc-primary)) 12%, var(--mc-surface-raised));
}

.stat-tile.stat-open::after {
  content: "";
  display: block;
  height: 3px;
  background: var(--mc-tile-accent, var(--mc-primary));
}

/* The way back to unfiltered, always in the same place. */
.stat-band-clear {
  align-self: center;
  font-size: var(--mc-text-base-sm);
  white-space: nowrap;
}

.stat-band-note {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  margin: calc(-1 * var(--mc-space-2)) 0 var(--mc-space-4);
}

/* --- Toolbar and filter row ---------------------------------------------- */
.page-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mc-space-3);
  margin-bottom: var(--mc-space-4);
}

.page-toolbar-search {
  position: relative;
  flex: 1 1 18rem;
  max-width: 28rem;
  min-width: 12rem;
}

.page-toolbar-search .form-control {
  padding-left: 2.25rem;
}

.page-toolbar-search .toolbar-search-icon {
  position: absolute;
  left: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--mc-text-muted);
  pointer-events: none;
}

.page-toolbar-end {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mc-space-3);
}

.page-toolbar .form-select,
.page-toolbar .form-control {
  width: auto;
  min-width: 9rem;
}

/* --- Filter pills with counts -------------------------------------------- */
.filter-pills {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mc-space-2);
}

.filter-row-label {
  font-size: var(--mc-text-sm);
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text-muted);
  margin-right: var(--mc-space-1);
}

.filter-pills .filter-pill {
  display: inline-flex;
  align-items: center;
  gap: var(--mc-space-2);
  border: 1px solid var(--mc-border);
  border-radius: var(--mc-radius-pill);
  padding: 0.3rem 0.85rem;
  font-size: var(--mc-text-base-sm);
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text);
  background: var(--mc-surface-raised);
  text-decoration: none;
}

.filter-pills .filter-pill:hover {
  border-color: var(--mc-primary);
  color: var(--mc-primary);
}

.filter-pills .filter-pill .filter-count {
  background: var(--mc-surface-sunken);
  color: var(--mc-text-muted);
  border-radius: var(--mc-radius-pill);
  padding: 0 0.45rem;
  font-size: var(--mc-text-xs);
  font-weight: var(--mc-weight-bold);
}

.filter-pills .filter-pill.active {
  background: var(--mc-primary);
  border-color: var(--mc-primary);
  color: var(--mc-on-primary);
}

.filter-pills .filter-pill.active .filter-count {
  background: color-mix(in srgb, var(--mc-on-primary) 25%, transparent);
  color: var(--mc-on-primary);
}

/* --- Quick-filter bar ------------------------------------------------------
   Piece 3b of the shell (_QuickFilterBar): pill rows for secondary dimensions,
   the date quick selector, the active-filter chips. Pills reuse .filter-pill;
   only the bar chrome, the custom-date popover and the active row are new. */
.qf-bar {
  display: flex;
  flex-direction: column;
  gap: var(--mc-space-2);
  margin: calc(-1 * var(--mc-space-2)) 0 var(--mc-space-4);
}

/* The Custom... popover: a native <details>, no script. The summary IS a pill. */
.qf-custom {
  position: relative;
  display: inline-block;
}

.qf-custom > summary {
  list-style: none;
  cursor: pointer;
}

.qf-custom > summary::-webkit-details-marker {
  display: none;
}

.qf-popover {
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.35rem);
  left: 0;
  display: flex;
  align-items: flex-end;
  gap: var(--mc-space-3);
  padding: var(--mc-space-3);
  background: var(--mc-surface-raised);
  border: 1px solid var(--mc-border);
  border-radius: var(--mc-radius-md);
  box-shadow: var(--mc-shadow-3);
}

/* On a phone the two date fields stack instead of overflowing the viewport. */
@media (max-width: 575.98px) {
  .qf-popover {
    flex-wrap: wrap;
    max-width: calc(100vw - 2rem);
  }
}

.qf-popover-field {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  font-size: var(--mc-text-xs);
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text-muted);
  margin-bottom: 0;
}

/* Active-filter summary: what is narrowing the list right now, each removable. */
.qf-active {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mc-space-2);
}

.qf-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  padding-right: 0.35rem;
}

.qf-chip-x {
  color: inherit;
  text-decoration: none;
  font-weight: var(--mc-weight-bold);
  line-height: 1;
  padding: 0 0.15rem;
  border-radius: var(--mc-radius-sm);
}

.qf-chip-x:hover {
  background: color-mix(in srgb, currentColor 18%, transparent);
  color: inherit;
}

.qf-clear {
  font-size: var(--mc-text-sm);
  margin-left: var(--mc-space-1);
}

/* --- Grouped table sections ------------------------------------------------
   A group header row (Users grouped by role): the group name and its count on
   a quiet band, spanning the table. */
.table-group-head th {
  background: var(--mc-surface-sunken);
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  font-weight: var(--mc-weight-bold);
  text-transform: none;
  border-top: 2px solid var(--mc-border);
}

.table-group-head .filter-count {
  background: var(--mc-surface-raised);
  border-radius: var(--mc-radius-pill);
  padding: 0 0.45rem;
  font-size: var(--mc-text-xs);
}

/* --- Server-side pager ----------------------------------------------------- */
.list-pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mc-space-3);
  margin: var(--mc-space-3) 0 var(--mc-space-4);
}

.list-pager-summary {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
}

.list-pager-links {
  display: flex;
  align-items: center;
  gap: var(--mc-space-1);
}

.list-pager-link {
  display: inline-block;
  min-width: 2rem;
  text-align: center;
  padding: 0.25rem 0.55rem;
  border: 1px solid var(--mc-border);
  border-radius: var(--mc-radius-sm);
  background: var(--mc-surface-raised);
  color: var(--mc-text);
  font-size: var(--mc-text-sm);
  font-weight: var(--mc-weight-semibold);
  text-decoration: none;
}

.list-pager-link:hover {
  border-color: var(--mc-primary);
  color: var(--mc-primary);
}

.list-pager-link.current {
  background: var(--mc-primary);
  border-color: var(--mc-primary);
  color: var(--mc-on-primary);
}

.list-pager-gap {
  color: var(--mc-text-muted);
  padding: 0 0.2rem;
}

/* --- Settings shell --------------------------------------------------------
   THE SECOND PAGE SHAPE. The list shape is header, band, toolbar, rows. The
   SETTINGS shape is a left group rail and ONE panel at a time on the right,
   with a single save that covers every section.

   Structure, and it is only three levels:

     .settings-shell                     the two column grid
       nav.settings-nav                  the rail:  one .settings-nav-item per group
       .settings-main                    the panels: one .settings-panel per group,
                                         exactly one carrying .is-active
       aside.settings-aside              the "coming soon" card, under the rail

   WHY THE ASIDE IS A GRID ITEM AND NOT A CHILD OF THE NAV
   On a phone the rail becomes a horizontal chips row above the panel. If the
   coming-soon card lived inside it, it would sit between the chips and the
   fields and push the actual work off the first screen. As its own grid item it
   is placed under the rail on desktop and after the panel on a phone, from one
   DOM order, with no duplicate markup.

   WHY ONLY ONE PANEL SHOWS AND IT IS A CLASS, NOT A REQUEST
   Every group is in the document and in the same form, so switching groups
   cannot lose typing and one submit really does save every section. Hiding with
   display:none is deliberate: hidden inputs inside a display:none subtree are
   still submitted, which is exactly the behaviour the single save depends on.

   THE WIDTH
   .content-width-settings is its own cap rather than a reuse of the reading
   one, because the panel column has to keep the ENTRY measure it would have had
   as a plain form (--mc-width-form, 60rem) and the rail is added beside it:
   60rem panel + 16.25rem rail + the gap. Capping at the reading width instead
   would silently narrow every settings form by the width of the new rail. The
   two tokens are declared with the other widths in SECTION 1. */
.content-width-settings { --mc-content-max: var(--mc-width-settings); }

.settings-shell {
  display: grid;
  grid-template-columns: var(--mc-settings-rail) minmax(0, 1fr);
  grid-template-rows: auto 1fr;
  gap: var(--mc-space-6);
  align-items: start;
}

.settings-nav {
  grid-column: 1;
  grid-row: 1;
  /* Sticky so a long panel scrolls past a rail that stays reachable. */
  position: sticky;
  top: var(--mc-space-5);
}

.settings-main {
  grid-column: 2;
  grid-row: 1 / span 2;
  min-width: 0;
}

.settings-aside {
  grid-column: 1;
  grid-row: 2;
}

.settings-nav-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.settings-nav-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--mc-space-3);
  padding: 0.5rem 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--mc-radius-md);
  color: var(--mc-text);
  font-size: var(--mc-text-base-sm);
  font-weight: var(--mc-weight-medium);
  text-decoration: none;
  transition: background-color var(--mc-transition), color var(--mc-transition);
}

.settings-nav-item:hover {
  background: var(--mc-surface-sunken);
  color: var(--mc-text);
  text-decoration: none;
}

.settings-nav-item.is-active {
  background: var(--mc-surface-raised);
  border-color: var(--mc-border);
  box-shadow: var(--mc-shadow-1);
  color: var(--mc-primary);
  font-weight: var(--mc-weight-semibold);
}

/* The accent bar, not a fill: the rail sits on the page surface and a filled row
   would compete with the panel card beside it for the eye. */
.settings-nav-item.is-active::before {
  content: "";
  position: absolute;
  left: -1px;
  top: 0.35rem;
  bottom: 0.35rem;
  width: 3px;
  border-radius: 3px;
  background: var(--mc-primary);
}

.settings-nav-icon {
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
  color: var(--mc-text-muted);
}

.settings-nav-item.is-active .settings-nav-icon {
  color: var(--mc-primary);
}

.settings-nav-icon svg {
  display: block;
  width: 100%;
  height: 100%;
}

.settings-nav-label {
  min-width: 0;
}

.settings-nav-badge {
  margin-left: auto;
  flex: 0 0 auto;
  padding: 0 0.4rem;
  border-radius: var(--mc-radius-pill);
  background: var(--mc-surface-sunken);
  color: var(--mc-text-muted);
  font-size: var(--mc-text-xs);
  font-weight: var(--mc-weight-medium);
}

.settings-nav-item.is-active .settings-nav-badge {
  background: var(--mc-surface-sunken);
  color: var(--mc-primary);
}

/* --- Settings panels ------------------------------------------------------ */
.settings-panel {
  display: none;
}

.settings-panel.is-active {
  display: block;
}

.settings-card {
  background: var(--mc-surface-raised);
  border: 1px solid var(--mc-border);
  border-radius: var(--mc-radius-lg);
  box-shadow: var(--mc-shadow-1);
}

.settings-card + .settings-card {
  margin-top: var(--mc-space-5);
}

.settings-card-head {
  padding: var(--mc-space-5) var(--mc-space-6) 0;
}

.settings-card-title {
  margin: 0;
  font-size: var(--mc-text-lg);
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text);
  line-height: var(--mc-leading-tight);
}

.settings-card-sub {
  margin: var(--mc-space-2) 0 0;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-base-sm);
}

.settings-card-body {
  padding: var(--mc-space-5) var(--mc-space-6) var(--mc-space-6);
}

/* --- Settings fields -------------------------------------------------------
   THE CAPTION IS A FIRST CLASS ELEMENT, not an afterthought bolted under an
   input. Every field in this shape gets room for one sentence explaining what
   the setting does, which is why .settings-field carries generous bottom space:
   the caption is expected, and a field without one still reads as deliberate.
   .settings-caption is also what a Bootstrap .form-text would have been, so a
   page may use either; this one is on the Mercury type scale. */
.settings-field {
  margin-bottom: var(--mc-space-6);
}

.settings-field:last-child {
  margin-bottom: 0;
}

.settings-label {
  display: block;
  margin-bottom: var(--mc-space-2);
  font-size: var(--mc-text-base-sm);
  font-weight: var(--mc-weight-medium);
  color: var(--mc-text);
}

.settings-caption {
  display: block;
  margin-top: 0.3rem;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  line-height: 1.45;
}

/* A divider INSIDE a panel, for a group that has natural sub-sections but not
   enough of them to earn their own rail entries (the org settings list, whose
   sub-sections come from the setting codes themselves and change as the product
   grows). Quiet on purpose: the rail is the navigation, this is only a label. */
.settings-subhead {
  margin: var(--mc-space-5) 0 var(--mc-space-3);
  font-size: var(--mc-text-sm);
  font-weight: var(--mc-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--mc-primary);
}

.settings-subhead:first-child {
  margin-top: 0;
}

/* Two or three fields on a line, collapsing to one when the panel is narrow. */
.settings-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--mc-space-5);
}

/* A group of switches or checkboxes reads as a list, not as a paragraph. */
.settings-checklist {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
  gap: var(--mc-space-2) var(--mc-space-5);
}

/* --- How this section saves ------------------------------------------------
   ONE VISUAL LANGUAGE FOR BOTH MODES, so it is learned once and then read
   everywhere. The strip is a label on the section, not an alert: it sits inside
   the card body above the fields, is present before the first click, and does
   not move or disappear. Colour separates the three modes and the WORDS repeat
   the same thing, because the difference between "saves as you go" and "saves
   together" is far too important to be carried by a hue alone.

   Quiet on purpose. This is chrome that has to survive being read on every
   settings screen forever, so it borrows the subtle status tints rather than
   the solid ones, and it is one line high. */
.settings-mode {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--mc-space-2) var(--mc-space-3);
  margin: 0 0 var(--mc-space-5);
  padding: 0.45rem 0.7rem;
  border: 1px solid transparent;
  border-radius: var(--mc-radius-md);
  font-size: var(--mc-text-sm);
  line-height: 1.45;
}

.settings-mode-badge {
  flex: 0 0 auto;
  padding: 0 0.45rem;
  border-radius: var(--mc-radius-pill);
  background: var(--mc-surface-raised);
  font-size: var(--mc-text-xs);
  font-weight: var(--mc-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  white-space: nowrap;
}

.settings-mode-text {
  min-width: 0;
}

.settings-mode.is-auto {
  background: var(--mc-success-subtle);
  border-color: var(--mc-success-subtle);
  color: var(--mc-success-on-subtle);
}

.settings-mode.is-grouped {
  background: var(--mc-surface-sunken);
  border-color: var(--mc-border);
  color: var(--mc-text-muted);
}

.settings-mode.is-immediate {
  background: var(--mc-warning-subtle);
  border-color: var(--mc-warning-subtle);
  color: var(--mc-warning-on-subtle);
}

/* --- The autosave receipt --------------------------------------------------
   Written by mercury-autosave.js into the field's own [data-autosave-status],
   which carries role="status" in the markup so the answer is ANNOUNCED and not
   only drawn. Deliberately attached to the item and never to the page: a
   page-level "Saved" banner cannot say WHICH of six switches it means, and on a
   screen where every switch saves itself that is the only question worth
   answering.

   Empty by default and empty again after a success, so a settings card does not
   silt up with a column of stale receipts. An error does NOT clear itself. */
.autosave-status {
  display: inline-block;
  min-height: 1.2em;
  margin-left: var(--mc-space-3);
  font-size: var(--mc-text-sm);
  font-weight: var(--mc-weight-medium);
  line-height: 1.2;
  color: var(--mc-text-muted);
}

.autosave-status.is-saving { color: var(--mc-text-subtle); }
.autosave-status.is-saved { color: var(--mc-success-on-subtle); }

/* The one state that has to interrupt: the control has just been put back to
   what the database holds, and the person needs to know their change did not
   take. Given a border so it cannot be mistaken for the quiet receipt. */
.autosave-status.is-error {
  padding: 0.15rem 0.45rem;
  border: 1px solid var(--mc-danger-subtle);
  border-radius: var(--mc-radius-sm);
  background: var(--mc-danger-subtle);
  color: var(--mc-danger-on-subtle);
  font-weight: var(--mc-weight-semibold);
}

/* --- The one save --------------------------------------------------------- */
.settings-save {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--mc-space-4);
  margin-top: var(--mc-space-6);
  padding-top: var(--mc-space-5);
  border-top: 1px solid var(--mc-border);
}

.settings-save-note {
  flex: 1 1 16rem;
  margin: 0;
  text-align: right;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  line-height: 1.45;
}

.settings-save-btn {
  padding-inline: var(--mc-space-6);
}

/* --- The dirty gate --------------------------------------------------------
   A gated footer has two sentences and shows one. Both are rendered so the swap
   costs no reflow and no jump under the reader's eye; CSS picks. With no
   JavaScript neither .is-dirty nor .is-gated behaviour matters, because the
   script is what adds the disabled state - the footer arrives fully working. */
.settings-save.is-gated .settings-save-dirty { display: none; }
.settings-save.is-gated.is-dirty .settings-save-clean { display: none; }
.settings-save.is-gated.is-dirty .settings-save-dirty { display: block; }

/* Awake: the border above the footer takes the accent, so a person scrolling a
   long panel can see there is something outstanding without reading a word. */
.settings-save.is-gated.is-dirty {
  border-top-color: var(--mc-primary);
}

.settings-save.is-gated .settings-save-clean {
  color: var(--mc-text-subtle);
  font-style: italic;
}

.settings-discard-btn {
  flex: 0 0 auto;
}

/* --- The coming soon card --------------------------------------------------
   Part of the pattern, and honest: this is admin chrome naming groups that will
   migrate into this screen, never a promise made to a customer. */
.settings-soon {
  border: 1px dashed var(--mc-border);
  border-radius: var(--mc-radius-lg);
  background: var(--mc-surface-sunken);
  padding: var(--mc-space-5);
}

.settings-soon-title {
  font-size: var(--mc-text-sm);
  font-weight: var(--mc-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--mc-text-muted);
}

.settings-soon-list {
  list-style: none;
  margin: var(--mc-space-3) 0 0;
  padding: 0;
}

.settings-soon-list li {
  display: flex;
  align-items: baseline;
  gap: var(--mc-space-3);
  padding: 0.15rem 0;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
}

.settings-soon-dot {
  flex: 0 0 auto;
  width: 0.4rem;
  height: 0.4rem;
  border-radius: 50%;
  background: var(--mc-text-subtle);
}

.settings-soon-note {
  margin: var(--mc-space-3) 0 0;
  color: var(--mc-text-subtle);
  font-size: var(--mc-text-xs);
  line-height: 1.45;
}

/* ============================================================================
   SECTION 6  CONTENT PATTERNS
   ============================================================================ */

/* --- Status chips --------------------------------------------------------- */
.chip {
  display: inline-block;
  font-size: var(--mc-text-xs);
  font-weight: var(--mc-weight-semibold);
  padding: 0.1rem 0.55rem;
  border-radius: var(--mc-radius-sm);
  background: var(--mc-primary-subtle);
  color: var(--mc-primary);
}

.chip-alert {
  background: var(--mc-danger-subtle);
  color: var(--mc-danger-on-subtle);
}

.chip-ok {
  background: var(--mc-success-subtle);
  color: var(--mc-success-on-subtle);
}

.chip-warn {
  background: var(--mc-warning-subtle);
  color: var(--mc-warning-on-subtle);
}

/* --- Picked chips ----------------------------------------------------------
   What a person has CHOSEN on a form, each one removable. Deliberately larger
   than .chip: a status chip is a read-only marker on a dense list row, where
   small is the point, but these are somebody's own answers to a question and
   every one of them carries a control. Larry, on the contents picker: "the
   pills are so small they get lost and cramped in." Readable at a glance, and
   the remove target is a real target rather than a five pixel cross.

   Colour comes from the same primary-subtle pair .chip uses, so the five themes
   carry these for free and dark is not a special case. */
.picked-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mc-space-2);
}

.picked-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--mc-space-2);
  max-width: 100%;
  min-height: 2rem;
  padding: 0.25rem 0.3rem 0.25rem 0.7rem;
  border: 1px solid var(--mc-primary);
  border-radius: var(--mc-radius-pill);
  background: var(--mc-primary-subtle);
  color: var(--mc-primary);
  font-size: var(--mc-text-base-sm);
  font-weight: var(--mc-weight-medium);
  line-height: var(--mc-leading-tight);
}

/* A long item name wraps inside its chip rather than widening it past the form. */
.picked-chip-label {
  min-width: 0;
  overflow-wrap: anywhere;
}

.picked-chip-remove {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: inherit;
  font-size: 1.05rem;
  line-height: 1;
  cursor: pointer;
}

.picked-chip-remove:hover,
.picked-chip-remove:focus-visible {
  background: var(--mc-primary);
  color: var(--mc-on-status);
}

.picked-chip-remove:focus-visible {
  outline: 2px solid var(--mc-primary);
  outline-offset: 1px;
}

/* --- Short field caps ------------------------------------------------------
   Fields whose content has a known small size never earn a full column: a ZIP
   is five to ten characters however wide the screen is. These are the SHARED
   caps; put the class on the input rather than inline widths in views, so the
   numbers live in one place. A grid column may already make a field narrower
   than its cap; the cap only stops the silly case where a five character
   field is handed 300px of box. Native date and time pickers have intrinsic
   content and are capped by type. */
.input-state { max-width: 6rem; }
.input-postal { max-width: 9rem; }
.input-time { max-width: 8rem; }
.input-phone { max-width: 12rem; }

input.form-control[type="date"] { max-width: 11rem; }
input.form-control[type="time"] { max-width: 8rem; }

/* --- Detail pane field labels -------------------------------------------- */
.field-label {
  text-transform: uppercase;
  font-size: var(--mc-text-xs);
  letter-spacing: 0.03em;
  color: var(--mc-text-muted);
  font-weight: var(--mc-weight-semibold);
  margin-bottom: var(--mc-space-1);
}

/* --- Empty state ---------------------------------------------------------- */
.empty-state {
  border: 1px dashed var(--mc-border);
  border-radius: var(--mc-radius-lg);
  background: var(--mc-surface-raised);
  padding: var(--mc-space-8) var(--mc-space-6);
  text-align: center;
  color: var(--mc-text-muted);
}

.empty-state .empty-icon {
  font-size: var(--mc-text-3xl);
  line-height: 1;
  color: var(--mc-primary);
  opacity: 0.5;
}

.empty-state .empty-title {
  color: var(--mc-text);
  font-weight: var(--mc-weight-semibold);
  margin: var(--mc-space-4) 0 var(--mc-space-2);
}

/* --- Search hero: one prominent input on search led pages ----------------- */
.search-hero {
  position: relative;
}

.search-hero .search-hero-input {
  padding-left: 2.5rem;
  height: 3rem;
  font-size: var(--mc-text-lg);
  border-color: var(--mc-border);
}

.search-hero .search-hero-icon {
  position: absolute;
  left: 0.9rem;
  top: 50%;
  transform: translateY(-50%);
  color: var(--mc-text-muted);
  pointer-events: none;
}

/* --- Mail view master detail ---------------------------------------------
   A list of things on one side, the selected thing in full on the other. Used by
   the approval queue, which is the case it was designed against: somebody with
   twelve items to decide should read and decide all twelve without navigating
   twelve times.

   Expects the page wrapper to carry .app-content, as the Mercury layout does.

   The two panes each scroll INDEPENDENTLY and the page itself does not, so the
   list stays put while a long detail scrolls. That is only possible if the layout
   owns a fixed height, which is what the body rules below arrange. They are scoped
   to >= 768px on purpose: on a phone the panes are not side by side at all (see
   the phone section at the end of this block), and a locked 100vh body there just
   breaks normal scrolling and fights the browser's collapsing address bar. */
@media (min-width: 768px) {
  body:has(.mail-layout) {
    height: 100vh;
  }
}

body:has(.mail-layout) .app-content {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

body:has(.mail-layout) .app-content > main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.mail-layout {
  display: flex;
  gap: var(--mc-space-5);
  align-items: stretch;
  flex: 1 1 auto;
  min-height: 0;
}

.mail-list {
  width: 24rem;
  min-width: 18rem;
  border-right: 1px solid var(--mc-border);
  padding-right: var(--mc-space-3);
  overflow-y: auto;
}

.mail-list-header {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-base-sm);
  padding: 0.4rem 0.5rem;
  border-bottom: 1px solid var(--mc-border);
}

.mail-item {
  display: block;
  padding: 0.6rem 0.7rem;
  border-bottom: 1px solid var(--mc-surface-sunken);
  color: var(--mc-text);
  text-decoration: none;
}

.mail-item:hover {
  background: var(--mc-surface-sunken);
  color: var(--mc-text);
}

.mail-item.selected {
  background: var(--mc-primary-subtle);
}

.mail-item .mail-item-title {
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text);
}

.mail-item .mail-item-date {
  float: right;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
}

.mail-item .mail-item-sub {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-base-sm);
}

.mail-detail {
  flex: 1;
  min-width: 0;
  overflow-y: auto;
}

/* The item currently being read. Selection is marked with a left rule and a tint, NOT
   with a box-shadow: the theme's one focus ring is a box-shadow, and an item that is
   both selected and focused would otherwise have the two fighting for the same
   property and the focus ring would lose. Focus is handled by the global
   [tabindex]:focus-visible rule above, which these items pick up for free because
   the list gives every item a tabindex. */
.mail-item[aria-selected="true"],
.mail-item.selected {
  background: var(--mc-primary-subtle);
  border-left: 3px solid var(--mc-primary);
  padding-left: calc(0.7rem - 3px);
}

/* Shown only when the panes are stacked one-at-a-time, which is phone width. */
.mail-back {
  display: none;
}

.mail-list-hint {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  padding: 0.35rem 0.5rem 0;
}

/* --- Table scroll guard ---------------------------------------------------
   Wrap any wide table in this. If a table still exceeds its region after the
   column rules below, the TABLE scrolls sideways inside the wrapper and the
   PAGE never does. A page that scrolls horizontally feels broken; a table that
   does is a table. */
.table-scroll {
  overflow-x: auto;
}

/* --- Card grid: the responsive container for card style lists ------------- */
.card-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: var(--mc-space-4);
}

/* A Kendo ListView acting as the mail list. The widget supplies the items and the
   selection plumbing; the theme supplies the look, so a Kendo mail list and a
   hand-written one are the same list. Strip the chrome and let .mail-item be the row. */
.mail-list .k-listview {
  border: 0;
  background: transparent;
  padding: 0;
}

.mail-list .k-listview-content {
  display: block;
}

.mail-list .k-listview-item {
  border: 0;
  padding: 0;
  background: transparent;
}

/* A Kendo ListView asked to behave like that grid. Strips the widget chrome and
   lays the items out on the same track sizes, so a Kendo card list and a plain
   card list are indistinguishable. Put this class on the ListView. */
.listview-cards.k-listview {
  border: 0;
  background: transparent;
  padding: 0;
}

.listview-cards .k-listview-content {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: var(--mc-space-4);
}

.listview-cards .k-listview-item {
  border: 0;
  padding: 0;
  background: transparent;
}

.listview-cards + .k-pager,
.listview-cards .k-pager {
  margin-top: var(--mc-space-4);
  border-radius: var(--mc-radius-md);
}

/* Inside a card GRID the cards sit tighter than a lone record card: Larry set this
   at 10px by eye. The combined selector outranks .mc-card without !important. */
.listview-cards .mc-card {
  padding: 10px;
}

@media (max-width: 767.98px) {
  .listview-cards .k-listview-content {
    grid-template-columns: 1fr;
  }
}

/* The card itself: the standard Mercury record card. */
.mc-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--mc-border);
  border-radius: var(--mc-radius-md);
  background: var(--mc-surface-raised);
  /* Roomy enough that the hover border never crowds the content: Larry flagged the
     address lines and action buttons sitting too close to the card edges. The first
     fix (+4px) was imperceptible; this one is meant to be seen. */
  padding: 1rem 1.5rem 1.25rem;
  transition: box-shadow var(--mc-transition), border-color var(--mc-transition);
}

.mc-card:hover,
.mc-card:focus-within {
  border-color: var(--mc-primary);
  box-shadow: var(--mc-shadow-primary);
}

.mc-card-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--mc-space-3);
  /* Small insets per Larry: the inner regions carry a nudge of their own on top of
     the card's outer padding, so titles and body copy do not sit optically flush
     against the icon column and the card edge. */
  padding-left: 2px;
}

.mc-card-title {
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text);
  line-height: 1.25;
  min-width: 0;
}

.mc-card-sub {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-base-sm);
}

.mc-card-body {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-base-sm);
  margin-top: var(--mc-space-2);
  padding-left: 4px;
  padding-bottom: 2px;
}

/* Status chips in the top right of a card head, opposite the title. */
.mc-card-chips {
  flex: 0 0 auto;
  text-align: right;
  white-space: nowrap;
}

/* A quieter second rank inside the body: contact details, timestamps, the things
   read only once the card has already been found. */
.mc-card-meta {
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  margin-top: var(--mc-space-2);
}

.mc-card-actions {
  display: flex;
  gap: var(--mc-space-2);
  margin-top: auto;
  padding-top: var(--mc-space-4);
  padding-left: 2px;
  padding-bottom: 2px;
  flex-wrap: wrap;
}

/* ============================================================================
   SECTION 7  RESPONSIVE AND TOUCH
   Desktop first in the literal sense: nothing above 992px is changed by this
   section. Everything here is a max-width or a coarse pointer rule.
   ============================================================================ */

/* --- Tablet, roughly 768 to 1024 -----------------------------------------
   Tiles wrap into a grid so a nine tile band does not become a ragged two rows
   with one orphan. Toolbar stays on one line as long as it fits. */
@media (max-width: 991.98px) {
  /* auto-FILL, not auto-fit: with only two or three tiles auto-fit collapses the empty tracks
     and stretches each tile across half the tablet, which reads as a banner rather than a
     count. auto-fill keeps the track size so a short band stays a band. */
  .stat-band {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr));
  }

  .stat-band-clear {
    grid-column: 1 / -1;
    justify-self: start;
  }

  /* The widest value the band ever carries is money ("$4,828.81" on Shipping Analysis),
     and an 8rem track has nothing to spare. The tile clips, so an oversized value does
     not overflow - it loses its last digits, silently, which is worse. Smaller type and
     tighter side padding keep the whole number inside the tile at tablet width. */
  .stat-tile .stat-value {
    font-size: var(--mc-text-xl);
    padding-inline: 0.35rem;
  }

  .page-head-actions {
    margin-left: 0;
  }

  /* THE TABLET COLUMN RULE
     A ten column table is 850px wide and a portrait tablet is 750. Rather than scroll it
     sideways or card it up (a tablet has room for a real table), drop the columns marked
     .cell-secondary: the ones a person scanning the list is not scanning FOR. The class goes
     on the th and the td together so the header drops with its column. */
  .table-cards .cell-secondary {
    display: none;
  }

  .mail-layout {
    flex-direction: column;
  }

  .mail-list {
    width: auto;
    max-height: 40vh;
    border-right: 0;
    border-bottom: 1px solid var(--mc-border);
    padding-right: 0;
  }

  /* THE SETTINGS RAIL BECOMES A CHIPS ROW.
     A 260px rail beside a panel needs about 1000px to be worth having; below
     that it either eats the panel or wraps into a stack of full width bars that
     costs half a screen before the first field. A horizontally scrolling row of
     chips keeps every group one tap away in a fixed amount of vertical space,
     which is the same trade the quick-filter bar already makes on this breakpoint.
     A select was the other candidate and was rejected: it hides the group names
     until opened, and on a screen this size the names ARE the navigation.

     The grid columns collapse to one and the three items take explicit rows, so
     the coming-soon card lands AFTER the panel rather than between the chips and
     the fields. */
  .settings-shell {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--mc-space-5);
  }

  .settings-nav,
  .settings-main,
  .settings-aside {
    grid-column: 1;
  }

  .settings-nav {
    position: static;
    grid-row: 1;
  }

  .settings-main {
    grid-row: 2;
  }

  .settings-aside {
    grid-row: 3;
  }

  .settings-nav-list {
    flex-direction: row;
    gap: var(--mc-space-3);
    overflow-x: auto;
    padding-bottom: var(--mc-space-3);
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
  }

  .settings-nav-item {
    white-space: nowrap;
    border-color: var(--mc-border);
    border-radius: var(--mc-radius-pill);
    background: var(--mc-surface-raised);
  }

  /* A chip is small enough that the accent bar reads as damage; the active chip
     takes the fill instead. */
  .settings-nav-item.is-active::before {
    display: none;
  }

  .settings-nav-item.is-active {
    background: var(--mc-primary);
    border-color: var(--mc-primary);
    color: var(--mc-on-primary);
    box-shadow: none;
  }

  .settings-nav-item.is-active .settings-nav-icon {
    color: var(--mc-on-primary);
  }

  .settings-nav-badge,
  .settings-nav-item.is-active .settings-nav-badge {
    margin-left: 0;
    background: transparent;
    color: inherit;
    opacity: 0.75;
    padding-inline: 0;
  }

  .settings-card-head {
    padding-inline: var(--mc-space-5);
  }

  .settings-card-body {
    padding-inline: var(--mc-space-5);
  }

  .settings-save-note {
    text-align: left;
  }
}

/* --- Phone, roughly 375 to 430 --------------------------------------------
   THE WIDE TABLE RULE
   A data table with more than about five meaningful columns is unreadable on a
   phone whichever way you cut it: horizontal scrolling hides the columns that
   matter and shrinking the type makes none of them legible. So below 768px a
   table marked .table-cards stops being a table and becomes a stacked card
   list, one card per row, each cell labelled from its own data-label attribute.
   No duplicate markup, no JavaScript, no second view to keep in step.

   To convert a table: add .table-cards to the <table>, put data-label="..." on
   every <td>, mark the identifying cell .cell-primary, and mark cells that are
   not worth a phone line .cell-secondary (they stay, quieter) or .d-none
   .d-md-table-cell (they go). */
@media (max-width: 767.98px) {
  .table-cards thead {
    display: none;
  }

  .table-cards,
  .table-cards tbody,
  .table-cards tr,
  .table-cards td {
    display: block;
    width: 100%;
  }

  .table-cards tr {
    border: 1px solid var(--mc-border);
    border-radius: var(--mc-radius-md);
    background: var(--mc-surface-raised);
    box-shadow: var(--mc-shadow-1);
    margin-bottom: var(--mc-space-3);
    padding: var(--mc-space-2) 0;
  }

  .table-cards td {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--mc-space-5);
    border: 0;
    padding: 0.25rem 0.75rem;
    text-align: right;
  }

  .table-cards td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    font-size: var(--mc-text-xs);
    font-weight: var(--mc-weight-semibold);
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--mc-text-muted);
    text-align: left;
  }

  /* Cells with nothing to announce (actions, the row title) span the card. */
  .table-cards td:not([data-label])::before,
  .table-cards td[data-label=""]::before {
    content: none;
  }

  .table-cards td.cell-primary {
    display: block;
    text-align: left;
    font-size: var(--mc-text-base);
    font-weight: var(--mc-weight-semibold);
    padding-bottom: var(--mc-space-2);
  }

  .table-cards td.cell-actions {
    display: flex;
    justify-content: flex-start;
    flex-wrap: wrap;
    gap: var(--mc-space-3);
    text-align: left;
    padding-top: var(--mc-space-3);
    border-top: 1px solid var(--mc-surface-sunken);
    margin-top: var(--mc-space-2);
  }

  .table-cards td.cell-actions::before {
    content: none;
  }

  /* .cell-secondary is already hidden by the tablet rule above, which also covers this width.
     A phone card shows the fields somebody scans for and nothing else. */

  /* THE EXCEPTION: a secondary column whose value still earns a phone line. Cost is the case
     that wanted it: on a tablet the table drops it to keep its fit, but a manager reading a
     shipment CARD is exactly the reader who wants the money, and a card has the room. Mark the
     td (and its th) .cell-secondary .cell-phone-keep together. An EMPTY cell stays hidden:
     a never-rated shipment's card shows no Cost line at all, rather than a label pointing at
     nothing (and never $0.00). The td must therefore be authored without whitespace filler. */
  .table-cards td.cell-secondary.cell-phone-keep:not(:empty) {
    display: flex;
  }

  /* THE MAIL VIEW ON A PHONE: LIST, THEN DETAIL. NOT TWO SQUEEZED PANES.
     A 390px screen cannot hold a list and a detail at once and be useful at either.
     So below 768px the layout shows exactly ONE of them, the way a phone mail client
     does: the queue, and then the item, with a way back.

     Which one is showing is a class on the layout, .mail-showing-detail, set by the
     server on first render (only when an item was explicitly asked for, so arriving
     at the page lands on the QUEUE, not on somebody else's approval) and toggled by
     the page script afterwards. No duplicate markup and no second view.

     Both panes drop their own scrolling here: the page scrolls, because the layout
     no longer owns a viewport height at this width (see the min-width: 768px guard
     on the body rules). Two nested scroll regions on a phone is how you get a pane
     that will not scroll because your thumb started 3px outside it. */
  .mail-layout {
    flex-direction: column;
    display: block;
  }

  .mail-list,
  .mail-detail {
    width: auto;
    max-height: none;
    overflow-y: visible;
    border-right: 0;
    border-bottom: 0;
    padding-right: 0;
  }

  .mail-layout.mail-showing-detail .mail-list {
    display: none;
  }

  .mail-layout:not(.mail-showing-detail) .mail-detail {
    display: none;
  }

  /* The way back to the queue. Full width and at the top of the detail, because it is
     the control somebody reaches for most on this width. */
  .mail-back {
    display: inline-flex;
    align-items: center;
    gap: var(--mc-space-2);
    margin-bottom: var(--mc-space-3);
  }

  /* Arrow keys are not a phone affordance and the hint is a line of type that is not. */
  .mail-list-hint {
    display: none;
  }

  /* Stat tiles go two up: still readable, still one thumb wide. */
  .stat-band {
    grid-template-columns: repeat(auto-fill, minmax(7.5rem, 1fr));
    gap: var(--mc-space-3);
  }

  .stat-tile {
    min-width: 0;
  }

  .stat-tile .stat-header {
    font-size: var(--mc-text-xs);
  }

  .stat-tile .stat-value {
    font-size: var(--mc-text-xl);
  }

  .page-head {
    gap: var(--mc-space-3);
  }

  .page-head-actions {
    width: 100%;
  }

  .page-head-actions > * {
    flex: 1 1 auto;
  }

  .page-toolbar-search {
    flex-basis: 100%;
    max-width: none;
  }

  .page-toolbar-end {
    margin-left: 0;
    width: 100%;
  }

  .card-grid {
    grid-template-columns: 1fr;
  }

  /* One save, full width, under its caption: at 390px a right aligned button
     with a sentence beside it becomes a sentence with a button orphaned under
     its last word. */
  .settings-save {
    justify-content: stretch;
  }

  .settings-save-note {
    flex: 1 1 100%;
    text-align: left;
  }

  .settings-save-btn {
    flex: 1 1 100%;
  }

  /* Discard sits above Save on a phone rather than beside it. Two full width
     buttons in a row is how a thumb reaches the wrong one. */
  .settings-discard-btn {
    flex: 1 1 100%;
    order: -1;
  }

  /* The receipt drops under its control instead of trailing off the right edge
     of a switch label that has already wrapped twice. */
  .autosave-status {
    display: block;
    margin-left: 0;
    margin-top: var(--mc-space-2);
  }
}

/* --- Touch targets --------------------------------------------------------
   Applies by input device, not by width, so a touch laptop gets it and a small
   desktop window does not. Desktop mice keep the compact Bootstrap sizes. */
@media (pointer: coarse) {
  .btn,
  .form-control,
  .form-select,
  .page-link {
    min-height: 2.75rem;
  }

  .btn-sm {
    min-height: 2.5rem;
    padding-inline: 0.75rem;
  }

  .navbar-nav .nav-link,
  .dropdown-item {
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
  }

  .filter-pills .filter-pill {
    padding: 0.5rem 1rem;
  }

  .stat-tile {
    min-height: 4.5rem;
  }
}

/* --- Print ----------------------------------------------------------------
   Branch staff print shipment lists. Drop the chrome, keep the table. */
@media print {
  .page-head-actions,
  .page-toolbar,
  .stat-band,
  .qf-bar,
  .list-pager-links,
  .navbar,
  footer {
    display: none !important;
  }

  .table-cards {
    font-size: 0.75rem;
  }
}

/* ============================================================================
   SECTION 8  THEMES

   One block per theme, each redeclaring tokens from SECTION 1. The selector
   pairs the document attribute with a SCOPED one:

     [data-theme="x"]      on <html>, set by the server. The whole page.
     [data-mc-theme="x"]   on any element. That element and its descendants.

   The scoped form is what lets the theme picker paint a live swatch of every
   theme at once while standing in one of them: custom properties inherit, so a
   swatch carrying data-mc-theme="dark" is genuinely rendered by the dark
   block. A swatch therefore cannot drift from the theme it advertises, because
   it is not a picture of the theme, it IS the theme, three elements wide.

   Specificity: both selectors are (0,1,0), the same as :root, so these blocks
   win by ORDER. They must stay after SECTION 1. Do not "tidy" them upward.
   ============================================================================ */

/* --- PROFESSIONAL ----------------------------------------------------------
   The default, and therefore the values already declared in SECTION 1. The
   block exists so the attribute is never a no-op: a page can carry
   data-theme="professional" or data-mc-theme="professional" (the picker's
   swatch does) and get the same answer as a page carrying nothing at all.
   Measured, against the surface each rank is actually set on:
     text    #0f172a  16.5:1 page / 17.9:1 card
     muted   #475569   7.0:1 page /  7.6:1 card
     subtle  #5b6b80   5.0:1 page /  5.4:1 card
     primary #01539b   7.2:1 page /  7.7:1 card                            */
[data-theme="professional"],
[data-mc-theme="professional"] {
  --mc-primary: #01539b;
  --mc-primary-hover: #013f75;
  --mc-primary-active: #02294b;
  --mc-primary-subtle: #ecf5fe;
  --mc-primary-subtle-strong: #d4e9fc;
  --mc-primary-on-subtle: #02294b;
  --mc-primary-border-subtle: #aad4f8;
  --mc-primary-emphasis: #6cb2ef;
  --mc-on-primary: #ffffff;
  --mc-on-status: #ffffff;

  --mc-text: #0f172a;
  --mc-text-muted: #475569;
  --mc-text-subtle: #5b6b80;

  --mc-surface: #f4f6f9;
  --mc-surface-raised: #ffffff;
  --mc-surface-sunken: #e9edf3;
  --mc-border: #e2e8f0;
  --mc-border-strong: #cbd5e1;

  --mc-success: #1e7a34;
  --mc-success-subtle: #e3f2e6;
  --mc-success-on-subtle: #145724;
  --mc-warning: #a16207;
  --mc-warning-subtle: #fdf3dc;
  --mc-warning-on-subtle: #7a4a05;
  --mc-danger: #c0392b;
  --mc-danger-subtle: #f9e6e3;
  --mc-danger-on-subtle: #8e2a20;
  --mc-info: #0b6e99;
  --mc-info-subtle: #e2f2f9;
  --mc-info-on-subtle: #08516f;
  --mc-neutral: #5b6b80;
  --mc-neutral-subtle: #f1f5f9;
  --mc-neutral-on-subtle: #475569;

  --mc-focus-ring-color: rgba(1, 83, 155, 0.45);
}

/* --- STODGY BANK -----------------------------------------------------------
   The look Larry named: a bank that has been a bank for a hundred years. Deep
   navy, cream paper rather than white, warm greys, and every status pulled a
   step toward its dullest legible form. Nothing is bright and nothing is fun,
   which is the entire brief.

   The surface is warm (#f4f1ea) and so is the text ramp (#1f1b16 rather than
   the slate near-black): a cool slate label on cream reads as a printing
   fault, so the neutrals move with the paper.
     text    #1f1b16  15.2:1 page / 16.4:1 card
     muted   #5a5348   6.7:1 page /  7.5:1 card
     subtle  #655d4f   5.8:1 page /  6.4:1 card / 5.0:1 on the sunken band
     primary #123a63  10.3:1 page                                          */
[data-theme="stodgy-bank"],
[data-mc-theme="stodgy-bank"] {
  --mc-primary: #123a63;
  --mc-primary-hover: #0d2b49;
  --mc-primary-active: #081c30;
  --mc-primary-subtle: #e4e9f0;
  --mc-primary-subtle-strong: #ccd6e3;
  --mc-primary-on-subtle: #081c30;
  --mc-primary-border-subtle: #b6c4d6;
  --mc-primary-emphasis: #6d8bad;
  --mc-on-primary: #ffffff;
  --mc-on-status: #ffffff;

  --mc-text: #1f1b16;
  --mc-text-muted: #5a5348;
  --mc-text-subtle: #655d4f;

  --mc-surface: #f4f1ea;
  --mc-surface-raised: #fffdf7;
  --mc-surface-sunken: #e8e2d6;
  --mc-border: #ddd5c6;
  --mc-border-strong: #bdb3a0;

  --mc-success: #245c33;
  --mc-success-subtle: #e3ecdf;
  --mc-success-on-subtle: #17401f;
  --mc-warning: #8a5a09;
  --mc-warning-subtle: #f6ecd6;
  --mc-warning-on-subtle: #6a4406;
  --mc-danger: #9c2f24;
  --mc-danger-subtle: #f2e2df;
  --mc-danger-on-subtle: #762219;
  --mc-info: #1b5470;
  --mc-info-subtle: #e2ecf2;
  --mc-info-on-subtle: #113c52;
  --mc-neutral: #655d4f;
  --mc-neutral-subtle: #ebe6db;
  --mc-neutral-on-subtle: #4a4438;

  /* Warm shadows: a cool slate shadow on cream paper reads blue. */
  --mc-shadow-1: 0 1px 2px rgba(60, 50, 30, 0.10);
  --mc-shadow-2: 0 2px 8px rgba(60, 50, 30, 0.14);
  --mc-shadow-3: 0 8px 24px rgba(60, 50, 30, 0.20);
  --mc-shadow-primary: 0 2px 8px rgba(18, 58, 99, 0.24);

  --mc-focus-ring-color: rgba(18, 58, 99, 0.5);

  --mc-chart-1: #123a63;
  --mc-chart-2: #1b5470;
  --mc-chart-3: #245c33;
  --mc-chart-4: #8a5a09;
  --mc-chart-5: #9c2f24;
  --mc-chart-6: #655d4f;
  --mc-chart-axis: #b8ad98;
  --mc-chart-grid: #d8d0c0;
}

/* --- COLORFUL --------------------------------------------------------------
   The fun one. A violet accent instead of the corporate blue, a lilac tinted
   page, chips and tiles that tint rather than whisper, and a status set picked
   for saturation rather than restraint (the danger is a magenta pink, not the
   usual brick).

   Fun is not a licence to fail: every rank below is measured on the surface it
   is set on, and the subtle rank was darkened twice to clear AA on the tinted
   sunken band, which is the surface a picker chip actually sits on.
     text    #241436  16.2:1 page / 17.5:1 card
     muted   #5c4a72   7.4:1 page /  7.8:1 card
     subtle  #6d5a84   5.8:1 page /  6.1:1 card / 5.3:1 on the sunken band
     primary #6d28d9   6.7:1 page /  7.1:1 card                            */
[data-theme="colorful"],
[data-mc-theme="colorful"] {
  --mc-primary: #6d28d9;
  --mc-primary-hover: #5b21b6;
  --mc-primary-active: #4c1d95;
  --mc-primary-subtle: #f0e7fe;
  --mc-primary-subtle-strong: #ddd0fb;
  --mc-primary-on-subtle: #3c1273;
  --mc-primary-border-subtle: #c4a7fb;
  --mc-primary-emphasis: #a78bfa;
  --mc-on-primary: #ffffff;
  --mc-on-status: #ffffff;

  --mc-text: #241436;
  --mc-text-muted: #5c4a72;
  --mc-text-subtle: #6d5a84;

  --mc-surface: #fbf7ff;
  --mc-surface-raised: #ffffff;
  --mc-surface-sunken: #f3ebfd;
  --mc-border: #e6d9f7;
  --mc-border-strong: #c9b0ea;

  --mc-success: #0f7a52;
  --mc-success-subtle: #dcf7ec;
  --mc-success-on-subtle: #0a5639;
  --mc-warning: #b45309;
  --mc-warning-subtle: #fef0dc;
  --mc-warning-on-subtle: #8a3f06;
  --mc-danger: #d81b60;
  --mc-danger-subtle: #fde5ee;
  --mc-danger-on-subtle: #a01243;
  --mc-info: #0369a1;
  --mc-info-subtle: #e0f2fe;
  --mc-info-on-subtle: #075985;
  --mc-neutral: #6d5a84;
  --mc-neutral-subtle: #f0eaf7;
  --mc-neutral-on-subtle: #4b3b60;

  --mc-shadow-1: 0 1px 2px rgba(60, 20, 100, 0.10);
  --mc-shadow-2: 0 2px 10px rgba(60, 20, 100, 0.14);
  --mc-shadow-3: 0 8px 28px rgba(60, 20, 100, 0.20);
  --mc-shadow-primary: 0 2px 10px rgba(109, 40, 217, 0.28);

  --mc-focus-ring-color: rgba(109, 40, 217, 0.45);

  --mc-chart-1: #6d28d9;
  --mc-chart-2: #0369a1;
  --mc-chart-3: #0f7a52;
  --mc-chart-4: #b45309;
  --mc-chart-5: #d81b60;
  --mc-chart-6: #6d5a84;
  --mc-chart-axis: #c9b0ea;
  --mc-chart-grid: #e3d5f5;
}

/* --- HIGH CONTRAST ---------------------------------------------------------
   Measured to AAA for body text, and everything else that CAN clear 7:1 does.
     text    #000000  21.0:1
     muted   #1c1c1c  17.0:1 page / 14.3:1 on the sunken band
     subtle  #333333  12.6:1 page / 10.6:1 on the sunken band
     primary #00397a  11.3:1
     every status solid carries white at 7.9:1 or better, and every -on-subtle
     ink clears 8.5:1 on its own tint.

   Three deliberate departures from the other themes:
     * Borders are BLACK, both ranks. No subtle greys: a hairline somebody
       cannot see is not a boundary, and this theme's whole promise is that
       every region is bounded.
     * Shadows are none. Elevation is not an affordance here, because a shadow
       is a low contrast cue by definition. --mc-shadow-primary becomes a solid
       2px ring so the card hover state survives as something visible.
     * The focus ring is thicker and OPAQUE (0.4rem, no alpha), because a 45%
       halo is exactly the sort of soft cue this theme exists to replace.
   The page and the card are both white on purpose: a card lifts by its black
   border, not by a tint that would eat into the 21:1 the text is promised.   */
[data-theme="high-contrast"],
[data-mc-theme="high-contrast"] {
  --mc-primary: #00397a;
  --mc-primary-hover: #002a5c;
  --mc-primary-active: #001d40;
  --mc-primary-subtle: #dce9f8;
  --mc-primary-subtle-strong: #b9d2f0;
  --mc-primary-on-subtle: #001d40;
  --mc-primary-border-subtle: #00397a;
  --mc-primary-emphasis: #00397a;
  --mc-on-primary: #ffffff;
  --mc-on-status: #ffffff;

  --mc-text: #000000;
  --mc-text-muted: #1c1c1c;
  --mc-text-subtle: #333333;

  --mc-surface: #ffffff;
  --mc-surface-raised: #ffffff;
  --mc-surface-sunken: #ebebeb;
  --mc-border: #000000;
  --mc-border-strong: #000000;

  --mc-success: #0b5c22;
  --mc-success-subtle: #d7f0dd;
  --mc-success-on-subtle: #08441a;
  --mc-warning: #6b4400;
  --mc-warning-subtle: #f7e9c8;
  --mc-warning-on-subtle: #543500;
  --mc-danger: #a3131b;
  --mc-danger-subtle: #fbdedf;
  --mc-danger-on-subtle: #7d0f15;
  --mc-info: #0a4f6b;
  --mc-info-subtle: #d8eef7;
  --mc-info-on-subtle: #073b50;
  --mc-neutral: #333333;
  --mc-neutral-subtle: #ebebeb;
  --mc-neutral-on-subtle: #1c1c1c;

  --mc-shadow-1: none;
  --mc-shadow-2: none;
  --mc-shadow-3: none;
  --mc-shadow-primary: 0 0 0 2px var(--mc-primary);

  --mc-focus-ring-color: #00397a;
  --mc-focus-ring: 0 0 0 0.15rem var(--mc-focus-ring-spacer), 0 0 0 0.4rem var(--mc-focus-ring-color);

  --mc-chart-1: #00397a;
  --mc-chart-2: #0a4f6b;
  --mc-chart-3: #0b5c22;
  --mc-chart-4: #6b4400;
  --mc-chart-5: #a3131b;
  --mc-chart-6: #333333;
  --mc-chart-axis: #000000;
  --mc-chart-grid: #767676;
}

/* --- DARK ------------------------------------------------------------------
   The one that is real work, because it is not the light theme with the
   lightness inverted. Four things had to be re-derived rather than flipped:

   1. SURFACES ARE LAYERED, NOT BLACK. #161b22 page, #1e2530 card, #11161d
      sunken. Pure black plus light text is the highest contrast and the worst
      to read for an hour: the halation around every glyph is what makes people
      say a dark theme hurts. The card is LIGHTER than its page here, which is
      the same relationship as the light themes: closer to the reader is lighter.
   2. THE STATUS SOLIDS ARE LIGHT, SO THEIR INK FLIPS. --mc-on-status becomes a
      near-black. A #1e7a34 badge on a dark card measures 2.1:1 and white text
      on it would be the only legible part; #4ec97a with #0d1117 ink measures
      9.0:1 and the badge reads as a badge. This is why nothing in this file
      hardcodes #ffffff on a fill.
   3. THE SUBTLE TINTS ARE TINTS OF DARK. Every -subtle token is a deep,
      desaturated version of its status rather than the pale one, and every
      -on-subtle is the light ink that sits on it, at 7.9:1 or better.
   4. ELEVATION IS A BORDER, NOT A SHADOW. A shadow on a dark surface is
      invisible; the shadows become deep dark halos that read as separation,
      and the hover cue becomes a coloured ring.

   Measured:
     text    #e6ecf4  14.6:1 page / 13.0:1 card / 15.3:1 sunken
     muted   #a9b6c7   8.4:1 page /  7.5:1 card
     subtle  #8494a8   5.6:1 page /  5.0:1 card
     primary #5aa9f0   6.9:1 page /  6.1:1 card, and carries #06121e at 7.5:1  */
[data-theme="dark"],
[data-mc-theme="dark"] {
  color-scheme: dark;

  --mc-primary: #5aa9f0;
  --mc-primary-hover: #7dbdf5;
  --mc-primary-active: #9dcefa;
  --mc-primary-subtle: #14314d;
  --mc-primary-subtle-strong: #1b4368;
  --mc-primary-on-subtle: #bcdcfb;
  --mc-primary-border-subtle: #2a5b87;
  --mc-primary-emphasis: #9dcefa;
  --mc-on-primary: #06121e;
  --mc-on-status: #0d1117;

  --mc-text: #e6ecf4;
  --mc-text-muted: #a9b6c7;
  --mc-text-subtle: #8494a8;

  --mc-surface: #161b22;
  --mc-surface-raised: #1e2530;
  --mc-surface-sunken: #11161d;
  --mc-border: #303a49;
  --mc-border-strong: #4a5769;

  --mc-success: #4ec97a;
  --mc-success-subtle: #10301c;
  --mc-success-on-subtle: #86e2a4;
  --mc-warning: #e3a13a;
  --mc-warning-subtle: #33260c;
  --mc-warning-on-subtle: #f0c479;
  --mc-danger: #f06a5b;
  --mc-danger-subtle: #3a1a17;
  --mc-danger-on-subtle: #f7a297;
  --mc-info: #4dbbe0;
  --mc-info-subtle: #0f2c36;
  --mc-info-on-subtle: #92d8ee;
  --mc-neutral: #8494a8;
  --mc-neutral-subtle: #232c39;
  --mc-neutral-on-subtle: #c2ccd9;

  --mc-shadow-1: 0 1px 2px rgba(0, 0, 0, 0.55);
  --mc-shadow-2: 0 2px 12px rgba(0, 0, 0, 0.65);
  --mc-shadow-3: 0 10px 32px rgba(0, 0, 0, 0.75);
  --mc-shadow-primary: 0 0 0 1px var(--mc-primary), 0 2px 14px rgba(90, 169, 240, 0.22);

  --mc-focus-ring-color: rgba(122, 186, 245, 0.6);

  --mc-chart-1: #5aa9f0;
  --mc-chart-2: #4dbbe0;
  --mc-chart-3: #4ec97a;
  --mc-chart-4: #e3a13a;
  --mc-chart-5: #f06a5b;
  --mc-chart-6: #8494a8;
  --mc-chart-axis: #4a5769;
  --mc-chart-grid: #303a49;
}

/* THE THINGS THE BROWSER DRAWS THAT A TOKEN CANNOT REACH.
   Everything else in this file is a token; these are not, because Bootstrap
   ships them as baked assets rather than as colours: the select caret is an
   inline SVG data URI with a dark stroke, invisible on a dark input, and
   .btn-close is a black glyph, likewise. Scoped to dark, and this is the only
   place in SECTION 8 that is not a token declaration. */
[data-theme="dark"] .form-select,
[data-mc-theme="dark"] .form-select {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23a9b6c7' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");
}

[data-theme="dark"] .btn-close,
[data-mc-theme="dark"] .btn-close {
  filter: invert(1) grayscale(100%) brightness(200%);
}

[data-theme="dark"] .form-check-input:not(:checked),
[data-mc-theme="dark"] .form-check-input:not(:checked) {
  background-color: var(--mc-surface-sunken);
  border-color: var(--mc-border-strong);
}

/* --- THE THEME PICKER ------------------------------------------------------
   A radio group where each option carries a live swatch of the theme it names.
   The swatch is three elements painted by the token block the data-mc-theme
   attribute selects, so it is not a picture of the theme, it is the theme in
   miniature and cannot go stale.

   Kept in this file rather than in the host application's stylesheet because
   the picker is part of the theme system: an app that copies this file gets a
   working picker with it and only has to render the markup. */
.theme-choices {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: var(--mc-space-3);
}

.theme-choice {
  display: flex;
  align-items: center;
  gap: var(--mc-space-4);
  border: 1px solid var(--mc-border);
  border-radius: var(--mc-radius-md);
  background: var(--mc-surface-raised);
  padding: var(--mc-space-4);
  cursor: pointer;
  transition: border-color var(--mc-transition), box-shadow var(--mc-transition);
}

.theme-choice:hover {
  border-color: var(--mc-primary);
}

.theme-choice:has(input:checked) {
  border-color: var(--mc-primary);
  box-shadow: var(--mc-shadow-primary);
}

.theme-choice:has(input:focus-visible) {
  box-shadow: var(--mc-focus-ring);
}

.theme-choice > input {
  flex: 0 0 auto;
  margin: 0;
}

.theme-choice-text {
  min-width: 0;
}

.theme-choice-name {
  display: block;
  font-weight: var(--mc-weight-semibold);
  color: var(--mc-text);
}

.theme-choice-desc {
  display: block;
  color: var(--mc-text-muted);
  font-size: var(--mc-text-sm);
  line-height: var(--mc-leading-normal);
}

/* The swatch: a title bar in the primary over a page surface carrying a card
   and a status chip. Four tokens, which is exactly the four a person is
   actually choosing between. */
.theme-swatch {
  flex: 0 0 auto;
  display: block;
  width: 3.25rem;
  height: 2.5rem;
  border: 1px solid var(--mc-border-strong);
  border-radius: var(--mc-radius-sm);
  background: var(--mc-surface);
  overflow: hidden;
}

.theme-swatch-bar {
  display: block;
  height: 0.7rem;
  background: var(--mc-primary);
}

.theme-swatch-body {
  display: flex;
  align-items: center;
  gap: 0.2rem;
  padding: 0.35rem 0.3rem;
}

.theme-swatch-body i {
  display: block;
  height: 0.85rem;
  border-radius: 1px;
}

.theme-swatch-body i:first-child {
  flex: 1 1 auto;
  background: var(--mc-surface-raised);
  border: 1px solid var(--mc-border);
}

.theme-swatch-body i:last-child {
  flex: 0 0 0.85rem;
  background: var(--mc-success);
}

@media (max-width: 575.98px) {
  .theme-choices {
    grid-template-columns: 1fr;
  }
}
