/*
 * Staff app stylesheet entry. Staff pages link this file and nothing else
 * from /_shared/stylesheets; page-specific files are imported by the page
 * with <style>@import url("...") layer(...)</style>.
 *
 * Layer order (later wins, regardless of selector specificity):
 *   tokens      design tokens
 *   base        element defaults, below legacy so any legacy rule still
 *               beats them (decision 2026-09-29, phase 3c)
 *   legacy      vendor CSS (Font Awesome, Handsontable) and the sheets that
 *               predate the consolidation (pages/login.css, pages/profile.css,
 *               patient-alerts.css, requestDetails.css); stylesheetv3.css,
 *               its old occupant, was deleted in CSS phase 5
 *   layout      app shell
 *   components  one file per component family (see README.md in this folder)
 *   utilities   small reviewed utility set
 *   pages       rules unique to one page
 *
 * URLs stay bare: hosting:prepare stamps ?v=<release> at deploy time.
 * Spec: docs/specs/2026-09-28-css-consolidation-design.md
 */
@layer tokens, base, legacy, layout, components, utilities, pages;

@layer legacy {
:root {
  /* ── Accent ramp (Layer 1). Themeable: overridden per [data-accent]. ───────
     Default = ChartMate brand teal. --cm-accent-ring is a space-separated RGB
     triple so --cm-focus-ring can vary its alpha per mode.
     NOTE: 800 and 900 are intentionally NOT redefined in dark mode: some
     surfaces put white text on them (layout.css, base.css). */
  --cm-accent-50: #ecfdf5;
  --cm-accent-100: #ccfbf1;
  --cm-accent-300: #5eead4;
  --cm-accent-500: #14b8a6;
  --cm-accent-600: #0d9488;
  --cm-accent-700: #0f766e;
  --cm-accent-800: #115e59;
  --cm-accent-900: #134e4a;
  --cm-accent-ring: 20 184 166;

  /* Back-compat: 58 call sites still say var(--cm-teal-N). Do not remove
     without migrating them. */
  --cm-teal-50: var(--cm-accent-50);
  --cm-teal-100: var(--cm-accent-100);
  --cm-teal-300: var(--cm-accent-300);
  --cm-teal-500: var(--cm-accent-500);
  --cm-teal-600: var(--cm-accent-600);
  --cm-teal-700: var(--cm-accent-700);
  --cm-teal-800: var(--cm-accent-800);
  --cm-teal-900: var(--cm-accent-900);

  /* ---------- Color: neutrals ---------- */
  --cm-gray-0: #ffffff;
  --cm-gray-50: #f8fafc;
  --cm-gray-100: #f1f5f9;
  --cm-gray-200: #e2e8f0;
  --cm-gray-300: #cbd5e1;
  --cm-gray-400: #94a3b8;
  --cm-gray-500: #64748b;
  --cm-gray-700: #334155;
  --cm-gray-900: #0f172a;
  --cm-scrollbar-track: #595959;

  /* ---------- Color: accent + semantic ---------- */
  --cm-blue-100: #dbeafe;
  --cm-blue-500: #3b82f6;
  --cm-blue-600: #2563eb;
  --cm-navy: #1e3a8a;
  --cm-success-500: #10b981;
  --cm-warning-500: #f59e0b;
  --cm-warning-700: #b45309; /* WCAG: dark amber for warning surfaces (white-text bg) */
  --cm-danger-500: #ef4444;
  --cm-danger-700: #b91c1c; /* WCAG: dark red for error surfaces (white-text bg) */

  /* ---------- Typography ---------- */
  --cm-font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --cm-font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, monospace;

  --cm-text-xs: 0.75rem; /* 12px */
  --cm-text-sm: 0.875rem; /* 14px */
  --cm-text-base: 1rem; /* 16px */
  --cm-text-lg: 1.125rem; /* 18px */
  --cm-text-xl: 1.25rem; /* 20px */
  --cm-text-2xl: 1.5rem; /* 24px */
  --cm-text-3xl: 1.875rem; /* 30px */

  --cm-lh-tight: 1.2;
  --cm-lh-snug: 1.35;
  --cm-lh-normal: 1.5;

  /* Fixed root font size (base.css html). rem sizing across the app assumes
     16px; a relative value would follow the viewer's browser setting. */
  --cm-font-size-root: 16px;
  --cm-font-size-avatar-xs: 10px; /* initials in an 18px disc (the assignee chip) */
  --cm-font-size-help-glyph: 10px; /* the "?" in the 14px help icon */

  --cm-fw-regular: 400;
  --cm-fw-medium: 500;
  --cm-fw-semibold: 600;
  --cm-fw-bold: 700;

  /* ---------- Radius / shadow / focus ring ---------- */
  --cm-radius-sm: 4px;
  --cm-radius-md: 6px;
  --cm-radius-lg: 8px;
  --cm-radius-xl: 12px;
  --cm-radius-pill: 999px;

  /* Z-index scale, reworked in CSS consolidation phase 3d to the product
     owner's approved stacking order: "The sidenav stays usable over
     popups: content < sticky headers < in-page menus < popup backdrop <
     popup < popup opened from a popup < sidenav < context menus < toasts."
     Loader, What's New and the session-timeout modal stay below the sidenav.
     Every tier keeps the value it had before this rework except the
     sidenav (was 10000000, now sits below context menus and toasts), the
     context menu (new tier, now above the sidenav; the right-click menu
     previously shared the modal tier) and the session modal (was 1000000,
     now between the priority modal and the sidenav). */
  --cm-z-raised: 1; /* sticky headers, focus rings, in-flow lifts */
  --cm-z-raised-2: 2; /* close buttons over raised content */
  --cm-z-hover: 10; /* hover-raised panels, draggable popups */
  --cm-z-float: 20; /* in-page menus and popovers */
  --cm-z-panel: 30; /* AI insights panel */
  --cm-z-overlay: 100; /* popup container, loader messages, action menus */
  --cm-z-overlay-menu: 101; /* dropdowns and badges over overlays */
  --cm-z-popup-stacked: 110; /* a popup opened over another popup (override/confirm popups) */
  --cm-z-popup-stacked-2: 120; /* CNR popup and the duplicate-check list popup */
  --cm-z-popup-stacked-3: 125; /* duplicate-check compare pane, opened over popup-stacked-2 */
  --cm-z-modal: 1000; /* loader, magnifier glass, what's new; .cm-menu--modal (QA hotkeys popover, below the session-timeout box) */
  --cm-z-modal-progress: 1001;
  --cm-z-modal-priority: 1100; /* patient-alert gate overlay and PatientAlertPopup (common/patient-alerts.js) */
  --cm-z-session: 1500; /* session timeout modal; above every popup and modal, below the sidenav */
  --cm-z-sidenav: 2000; /* was 10000000; now below context menus and toasts */
  --cm-z-context-menu: 3000; /* .cm-menu--fixed: ContextMenu (common/dom-helpers.js); above the sidenav */
  --cm-z-toast: 9999;
  --cm-z-page-drawer: 200; /* Preload Tool's side drawer */
  /* Customer pages only (portal and request flow). */
  --cm-z-challenge: 99999; /* the reCAPTCHA challenge overlay (recaptcha.js): above toasts */
  --cm-z-customer-loader: 99990; /* the portal's wait screen (pages/portal.css): above toasts, below the challenge overlay, so a reCAPTCHA challenge raised during a lookup can be solved */

  /* Breakpoints. Custom properties cannot be used in @media conditions, so
     these are documented constants, enforced by
     tests/unit/frontend/stylesheets/tokens-usage.test.mjs:
       600px  mobile
       768px  tablet
       960px  narrow desktop
       1280px wide
     Off-scale widths still in use are listed in BREAKPOINT_EXCEPTIONS there. */

  --cm-shadow-xs: 0 1px 2px rgba(15, 23, 42, 0.04);
  --cm-shadow-sm: 0 1px 3px rgba(15, 23, 42, 0.08), 0 1px 2px rgba(15, 23, 42, 0.04);
  --cm-shadow-md: 0 4px 12px rgba(15, 23, 42, 0.08), 0 2px 4px rgba(15, 23, 42, 0.04);
  --cm-shadow-lg: 0 12px 24px rgba(15, 23, 42, 0.1), 0 4px 8px rgba(15, 23, 42, 0.06);
  --cm-shadow-popup:
    0 0 0 4px rgb(var(--cm-accent-ring) / 0.28), var(--cm-shadow-lg); /* popup box: accent glow (owner, 2026-09-30) */
  --cm-shadow-tooltip:
    0 0 0 1px rgb(var(--cm-accent-ring) / 0.5), 0 3px 10px rgb(var(--cm-accent-ring) / 0.22); /* tooltip and help icon bubbles: accent ring */
  --cm-bg-backdrop: rgba(15, 23, 42, 0.5); /* popup overlay (was --popup-background-color) */

  --cm-focus-ring: 0 0 0 3px rgb(var(--cm-accent-ring) / 0.35);
  /* A selected card's ring (components/card.css .cm-card--selected; was the
     System Health strip's expanded ring). Follows the accent in both themes
     through --cm-accent-ring, so no :root.dark copy is needed. */
  --cm-shadow-selected: 0 0 0 3px rgb(var(--cm-accent-ring) / 0.15);

  /* Toggle chip "on" state (components/chip-toggle.css, CSS consolidation
     phase 4 forms PR 2): one look in both themes, no :root.dark override
     needed - each re-resolves its nested var(--cm-accent-ring) per theme
     the same way the semantic grays below do. */
  --cm-chip-selected-border: rgb(var(--cm-accent-ring) / 0.8);
  --cm-chip-selected-bg: rgb(var(--cm-accent-ring) / 0.12);
  /* Drop zone drag tint; follows the accent theme. */
  --cm-drop-zone-active-bg: rgb(var(--cm-accent-ring) / 0.08);

  /* ---------- Spacing ---------- */
  --cm-space-1: 4px;
  --cm-space-2: 8px;
  --cm-space-3: 12px;
  --cm-space-4: 16px;
  --cm-space-5: 20px;
  --cm-space-6: 24px;
  --cm-space-8: 32px;
  --cm-space-10: 40px;

  /* ---------- Layout primitives (kept from legacy v3) ---------- */
  --scrollbar-thickness: 12px;
  --sidebar-width: 70px;

  /* ---------- Semantic tokens (light mode) ---------- */
  --cm-bg-body: var(
    --cm-gray-100
  ); /* Was gray-50 (white cards sat at 1.05:1 on it); gray-100 lifts surfaces 1.10:1, matching dark mode. */
  --cm-bg-surface: var(--cm-gray-0);
  --cm-bg-raised: var(--cm-gray-0);
  --cm-bg-muted: var(
    --cm-gray-200
  ); /* Was gray-100, which --cm-bg-body now uses; gray-200 keeps muted areas (table headers, chips, disabled controls) one step past the page like dark mode: surface 1.23:1, body 1.13:1. */
  --cm-bg-sidebar: var(--cm-gray-900);
  --cm-bg-readonly: var(
    --cm-gray-100
  ); /* Tint behind locked/read-only form fields (#f1f5f9 light, #1e293b dark via the gray scale). */

  /* Form field height (phase 3c field design); buttons adopt it in phase 4. */
  --cm-control-height: 24px;
  /* Taller fields where a form is the whole page (the sign-in card, owner
     2026-10-13): the pre-redesign 36px, level with --cm-button-height. */
  --cm-control-height-lg: var(--cm-button-height);
  /* One height for every status pill (phase 4 pills). */
  --cm-pill-height: 20px;

  /* One button height for every button (owner decision 2026-10-07, after
     reviewing 32px with a 24px small size: keep the original 36px). */
  --cm-button-height: 36px;
  /* Small icon buttons only (owner decision 2026-10-07): a square at the
     field height, for icon buttons that sit beside a field or in a header
     (Invoice's field icons, the comments table +, the report builder's
     move and remove icons). */
  --cm-button-height-sm: var(--cm-control-height);

  --cm-text-body: var(--cm-gray-700);
  --cm-text-heading: var(--cm-gray-900);
  --cm-text-muted: var(--cm-gray-500);
  --cm-text-on-dark: var(--cm-gray-0);
  --cm-text-on-interactive: #ffffff; /* Foreground on .cm-button bg; flips dark in :root.dark for AA. */

  --cm-border: var(
    --cm-gray-400
  ); /* Was gray-200 (1.23:1 on white). slate-400 is 2.56:1, so tables, cards, chips and popups read as shapes; dark mode keeps its own value below. */
  --cm-border-strong: var(
    --cm-gray-500
  ); /* Was gray-300; needed slate-500 for input-border 4.76:1 vs surface (UI 3:1 AA). */
  /* Menu frame and row hairline (components/menu.css, CSS phase 5.5 restyle).
     The frame mixes --cm-border toward --cm-border-strong, about 3:1 on the
     surface in both themes (--cm-border alone is 1.9:1 in dark). Each
     re-resolves its nested tokens per theme, so no :root.dark copy. */
  --cm-menu-border: color-mix(in srgb, var(--cm-border) 70%, var(--cm-border-strong));
  --cm-menu-separator: color-mix(in srgb, var(--cm-border) 55%, transparent);
  /* A hovered, focused or active menu row's left edge. */
  --cm-menu-edge: inset 3px 0 0 var(--cm-interactive);
  --cm-menu-edge-danger: inset 3px 0 0 var(--cm-danger-500);

  /* Interactive accent — teal-700 keeps white-on-teal AA (5.47:1) and link
     text on white AA (5.47:1). hover/active step darker for visible feedback. */
  --cm-interactive: var(--cm-teal-700);
  --cm-interactive-hover: var(--cm-teal-800);
  --cm-interactive-active: var(--cm-teal-900);
  --cm-control-accent: var(--cm-accent-800); /* checkbox and radio fill; follows the accent theme */

  --cm-bg-warning: var(
    --cm-warning-700
  ); /* Surface for .warning banners — white-on-amber-700 = 5.02:1 AA. */
  --cm-bg-danger: var(
    --cm-danger-700
  ); /* Surface for .error/.fail banners — white-on-red-700 = 5.94:1 AA. */

  /* Foreground success/warning/danger text (dot + label) on a neutral chip
     background (--cm-bg-muted) and on the status pill tints below
     (components/pill.css), not the white-on-color surface tokens above.
     Not tied to the tenant-configurable accent scale - success should read
     as green regardless of which accent a tenant picked. */
  --cm-success-text: #166534; /* green-800: 6.49:1 on its tint, 5.78:1 on --cm-bg-muted (was #15803d, 4.07:1 on muted, Automate-774m) */
  --cm-warning-text: #92400e; /* amber-800: 6.37:1 on its tint, 5.75:1 on --cm-bg-muted (was #b45309) */
  --cm-danger-text: #b91c1c;
  --cm-info-text: var(--cm-navy);
  --cm-accent-text: var(--cm-accent-700); /* follows the accent theme */

  /* Status pill tints (components/pill.css): opaque, so a pill's contrast
     does not depend on what it sits on. Dark values mix the hue over the
     surface (:root.dark). */
  --cm-bg-info-soft: var(--cm-blue-100);
  --cm-bg-accent-soft: var(--cm-accent-100);
  --cm-bg-success-soft: #dcfce7;
  --cm-bg-warning-soft: #fef3c7;

  /* Initials discs (components/avatar.css). */
  --cm-avatar-bg: var(--cm-gray-200);
  --cm-avatar-fg: var(--cm-gray-700);
  --cm-avatar-chip-disc-bg: var(--cm-accent-300);
  --cm-avatar-chip-disc-fg: var(--cm-accent-900);
  --cm-avatar-you-disc-bg: var(--cm-accent-800);

  /* --cm-bg-danger-soft: status pill danger tint (Delivery Failed,
     release restriction counts) and the System Health hot-chip surface. */
  --cm-bg-danger-soft: #fee2e2;

  /* App shell values with no match on the scales above (layout.css, CSS
     consolidation phase 3d). Kept at the values the shell rendered with in
     stylesheetv3.css; phase 4 decides whether they snap to a scale. No
     dark-mode overrides: the shell rendered them the same in both modes. */
  --cm-bg-nav-hover: rgba(255, 255, 255, 0.1); /* sidenav link hover */
  --cm-bg-nav-active: rgba(20, 184, 166, 0.12); /* active nav item (fixed teal, not the accent) */
  --cm-bg-on-dark-hover: rgba(255, 255, 255, 0.06); /* hover tint on the dark shell chrome */
  --cm-bg-on-dark-soft: rgba(255, 255, 255, 0.16); /* a tag on the dark profile banner */
  --cm-bg-on-dark-soft-hover: rgba(255, 255, 255, 0.24);
  --cm-attention-ring: 0 0 0 3px var(--cm-accent-100); /* a dot that wants attention (What's New unseen) */
  --cm-border-on-dark: rgba(255, 255, 255, 0.12); /* dividers on the pinned dark chrome */
  --cm-border-on-dark-strong: rgba(255, 255, 255, 0.25); /* the profile disc's ring */
  --cm-border-translucent: rgba(
    127,
    127,
    127,
    0.25
  ); /* a divider that reads on either theme (the esMD details header) */
  /* The PDF viewer (components/pdf-viewer.css): the page status rings on
     thumbnails and on QA's current page, the magnifier, fill and sign's
     floating box, the AI insights panel. Fixed in both themes. */
  --cm-page-ring-selected: 0 0 0 5px var(--cm-accent-800);
  --cm-page-ring-ok: 0 0 0 5px #009900;
  --cm-page-ring-new: 0 0 0 5px #64d6d7;
  --cm-page-ring-rejected: 0 0 0 5px #e7b90c;
  --cm-page-ring-misfiled: 0 0 0 5px #7d0d5c;
  --cm-page-ring-deleted: 0 0 0 5px #cd3915;
  --cm-page-ring-image-flag: 0 0 0 5px #e7620c;
  --cm-magnifier-border: #000000;
  --cm-fill-sign-action: #ffffff;
  --cm-fill-sign-text: #000000;
  --cm-shadow-fill-sign: 0 0 5px 2px #303030 inset;
  --cm-fill-sign-size: var(--cm-text-xl); /* the + and - actions set it on each text line */
  --cm-radius-fill-sign: 10px;
  --cm-shadow-insights: 0 6px 24px rgba(0, 0, 0, 0.35);
  --cm-shadow-switch-knob: 0 1px 2px rgba(15, 23, 42, 0.2); /* the task switch's knob */
  --cm-shadow-invalid: 0 0 2px 2px red; /* a field that failed a page's check (aria-invalid) */
  --cm-task-person-selected-bg: rgb(
    var(--cm-accent-ring) / 0.18
  ); /* the task popup's picked person, dark mode */
  --cm-task-person-selected-border: rgb(var(--cm-accent-ring) / 0.4);
  /* The page manager's drop marker: an accent bar on the side a dragged page lands. */
  --cm-shadow-pdfpm-drop-before: 5px 0 0 0 var(--cm-accent-800) inset;
  --cm-shadow-pdfpm-drop-after: -5px 0 0 0 var(--cm-accent-800) inset;
  --cm-shadow-nav-hover: inset 3px 0 0 var(--cm-accent-500); /* a hovered nav link's left edge */
  --cm-shadow-drawer: -4px 0 16px rgba(0, 0, 0, 0.25); /* right sidebar panel */
  --cm-shadow-drawer-tab: -4px 0 12px rgba(0, 0, 0, 0.2); /* right sidebar tab */
  --cm-radius-accent-bar: 2px; /* right-sidebar action row accent bar */
  --cm-radius-progress-bar: 3px; /* loader progress bar */
  --cm-radius-session-countdown: 5px; /* session-timeout countdown chip */

  /* Tables (components/table.css). */
  --cm-table-cell-max: 180px; /* the one width a --truncate cell stops at */
  --cm-table-marker-selected: inset var(--cm-space-1) 0 0 var(--cm-interactive);
  --cm-table-marker-urgent: inset var(--cm-space-1) 0 0 var(--cm-danger-text);
  --cm-table-marker-locked: inset var(--cm-space-1) 0 0 var(--cm-warning-text);
  --cm-table-marker-deleted: inset var(--cm-space-1) 0 0 var(--cm-text-muted);

  /* Progress bars (components/progress.css). */
  --cm-progress-height: 5px;

  /* Customer pages (portal and request flow, CSS phase 6). */
  --cm-loader-wash-edge: #d9d9d9; /* the portal wait screen's wash, its outer stop (pages/portal.css) */
  /* The reCAPTCHA challenge overlay (components/challenge.css). */
  --cm-bg-backdrop-challenge: rgba(0, 0, 0, 0.55);
  --cm-challenge-text: #1f2937;
  --cm-challenge-cancel: #6b7280; /* the Cancel link */
  --cm-shadow-challenge: 0 10px 30px rgba(0, 0, 0, 0.3);
  /* Sizing a customer site can change on its :root (the portal's
     pages/portal.css, portal refresh): the parts of the field, button, card,
     banner and pill looks the components read through these. The defaults
     are the staff look, so a site that sets none of them is unchanged. */
  --cm-control-radius: var(--cm-radius-sm); /* text fields and selects (base.css) */
  --cm-field-gap: var(--cm-space-1); /* label to control (components/field.css) */
  --cm-field-label-size: var(--cm-text-xs);
  --cm-field-label-color: var(--cm-text-muted);
  --cm-field-label-case: uppercase;
  --cm-field-label-tracking: 0.04em;
  --cm-button-radius: var(--cm-radius-md); /* components/button.css */
  --cm-button-font-size: var(--cm-text-sm);
  --cm-button-font-weight: var(--cm-fw-medium);
  --cm-button-padding-x: var(--cm-space-4);
  --cm-card-radius: var(--cm-radius-xl); /* components/card.css */
  --cm-card-border: var(--cm-border);
  --cm-card-shadow: var(--cm-shadow-md);
  --cm-banner-radius: var(--cm-radius-lg); /* components/banner.css */
  --cm-pill-font-size: var(--cm-text-xs); /* components/pill.css */
  --cm-pill-padding-x: var(--cm-space-2);
  /* A rejected field on a customer page: a red edge and a soft red halo
     (the portal sets --cm-shadow-invalid to it). */
  --cm-shadow-invalid-customer: 0 0 0 1px var(--cm-danger-700), 0 0 0 4px rgba(185, 28, 28, 0.12);
  --cm-text-caption: 0.8125rem; /* 13px: the portal's eyebrows, pills and light footer */
  --cm-text-total: 2.5rem; /* 40px: the amount in the portal's invoice total */
  --cm-text-total-phone: 2.125rem; /* 34px */
  /* The selected option of the portal's two-way switch, raised off its
     track: a lift and a slate-500 edge (4.3:1 on the track, so the choice
     shows without the color change, WCAG 1.4.11). */
  --cm-shadow-switch-selected:
    0 1px 3px rgba(15, 23, 42, 0.12), inset 0 0 0 1px var(--cm-border-strong);
  /* A portal card's lift, in place of its border. */
  --cm-shadow-card-customer: 0 1px 2px rgba(15, 23, 42, 0.05), 0 10px 32px rgba(15, 23, 42, 0.1);
  /* The Midnight hero band (components/hero.css): navy with a teal glow in
     its top corner, the same in both themes (customer pages are light only).
     On #0f172a: white title 17.9:1, #cbd5e1 text 12.0:1, teal-300 eyebrow
     and focus ring 12.1:1, teal-200 links 14.2:1 (customer.contrast.test.mjs
     checks them, on the glow too). */
  --cm-hero-bg: #0f172a;
  --cm-hero-glow: radial-gradient(
    60% 150% at 100% 0%,
    rgba(20, 184, 166, 0.26) 0%,
    rgba(20, 184, 166, 0) 60%
  );
  --cm-hero-title: #ffffff;
  --cm-hero-text: #cbd5e1;
  --cm-hero-eyebrow: #5eead4;
  --cm-hero-link: #99f6e4;
  --cm-hero-focus: #5eead4;
  --cm-hero-title-size: 2.375rem; /* 38px */
  --cm-hero-title-size-short: 2rem; /* 32px, results and the list */
  --cm-hero-title-size-phone: 1.6875rem; /* 27px */
  /* The steps on the band (components/stepper.css, --on-dark): a pending
     step's ring and the connector before it, slate-500 (3.75:1 on the
     navy), and its label, slate-400 (7.0:1). Done and current take the
     eyebrow's teal-300; the labels the link and title colors. */
  --cm-hero-step-ring: #64748b;
  --cm-hero-step-muted: #94a3b8;
  /* The request flow's compact progress bar on the band
     (components/progress.css --on-dark): a slate-700 track under the
     teal-300 fill (7.0:1). */
  --cm-hero-track: #334155;
  /* Loading placeholders (components/skeleton.css): a soft bar with a
     lighter band sweeping across it, on the page and on the hero band. */
  --cm-skeleton: var(--cm-gray-100);
  --cm-skeleton-shine: var(--cm-gray-200);
  --cm-skeleton-on-dark: #1e293b;
  --cm-skeleton-on-dark-shine: #334155;
  /* A secondary button on the band (components/button.css, --on-dark with
     --secondary): a translucent fill and ring, white text. */
  --cm-button-on-dark-bg: rgba(255, 255, 255, 0.08);
  --cm-button-on-dark-bg-hover: rgba(255, 255, 255, 0.16);
  --cm-button-on-dark-border: rgba(255, 255, 255, 0.35);
  /* Pinned dark chrome for the PDF editor and right-sidebar surfaces: one
     step lighter than --cm-bg-sidebar (#0f172a), the value dark mode's
     --cm-bg-surface resolves to, so chrome surfaces do not blend into the
     navbar and sidebar. Same in light and dark mode. */
  --cm-bg-chrome: #131c2e;
}

:root.dark {
  /* Raw gray scale flipped — 0 is now deepest, 900 is the lightest visible.
     Surface steps spaced wider than the original palette so cards / rows /
     headers read as distinct elevation tiers, and borders bumped lighter
     so they're visible against the (still dark) surface they sit on. */
  --cm-gray-0: #0b1220;
  --cm-gray-50: #131c2e;
  --cm-gray-100: #1e293b;
  --cm-gray-200: #3b4a63;
  --cm-gray-300: #4b5c76;
  --cm-gray-400: #5f7290;
  --cm-gray-500: #b6c2d2;
  --cm-gray-700: #e8eef5;
  --cm-gray-900: #ffffff;

  /* Accent — lighter steps for AA on dark surfaces. 800/900 deliberately absent. */
  --cm-accent-500: #2dd4bf;
  --cm-accent-600: #5eead4;
  --cm-accent-700: #99f6e4;
  --cm-accent-ring: 45 212 191;

  /* QA chip: dark keeps the lighter blue (light mode's --cm-blue-500) so it
     still reads on the dark surface. */
  --cm-blue-600: #3b82f6;

  /* Background semantic tokens — REMAPPED so layered surfaces get
     progressively lighter (visual-depth direction is inverted vs light) */
  --cm-bg-body: var(--cm-gray-0);
  --cm-bg-surface: var(--cm-gray-50);
  --cm-bg-raised: var(--cm-gray-100);
  --cm-bg-muted: var(--cm-gray-100);
  /* --cm-bg-sidebar would inherit --cm-gray-900 from :root, which after this
     scale redefinition resolves to #F8FAFC — that would invert the sidebar
     to LIGHT in dark mode, which is wrong. Pin it explicitly: */
  --cm-bg-sidebar: #0f172a;

  /* Text-on-dark: in dark mode, the gray scale flipped, so --cm-gray-0 = #0B1020
     (deepest). We need text-on-dark to STILL mean "light text", so re-anchor it
     to the lightest end of the redefined scale. */
  --cm-text-on-dark: var(--cm-gray-900);

  /* Text-on-interactive: dark-mode .cm-button bg resolves to teal-500 dark =
     #2DD4BF (bright accent). White text on it is 1.86:1 (FAIL). Pin foreground
     to deep slate (#0F172A) — 9.59:1 AAA. This is the inverse of light mode,
     where --cm-interactive is teal-700 and white text reads. */
  --cm-text-on-interactive: #0f172a;

  /* Interactive remap for dark-mode .cm-button: keep the bright dark-mode teals
     (already redefined above) so the button reads as a vivid accent against
     the near-black surface. Hover lightens, active lightens further — opposite
     direction from light mode but consistent with the inverted scale. */
  --cm-interactive: var(--cm-teal-500); /* dark = #2DD4BF */
  --cm-interactive-hover: var(--cm-teal-600); /* dark = #5EEAD4 */
  --cm-interactive-active: var(--cm-teal-700); /* dark = #99F6E4 */
  --cm-control-accent: var(--cm-interactive); /* dark: --cm-accent-800 is 2.25:1 on the surface */

  /* Border-strong: light-mode value (gray-500) re-resolves through the dark
     scale to #94A3B8 — that hits 6.92:1 vs bg-surface, which is correct.
     The base :root rule already maps via raw scale, so no override needed
     here — listed for clarity. */
  /* --cm-border-strong: var(--cm-gray-500);   inherited */

  /* Border: light mode moved --cm-border to gray-400 for contrast. Dark keeps
     gray-200 (#3b4a63, 1.90:1 on the surface), which is what it always was. */
  --cm-border: var(--cm-gray-200);

  /* --cm-text-heading currently re-resolves to gray-900 = #F8FAFC (light) which
     is correct for headings. Same for --cm-text-body (gray-700 = #E2E8F0).
     --cm-text-muted (gray-500 = #94A3B8) — fine. No additional overrides needed. */

  /* Text semantic tokens are NOT redefined here: they re-resolve through the
     redefined raw scale: --cm-text-body still points at --cm-gray-700, which
     in this mode = #E2E8F0 (light text). Same for headings (--cm-gray-900 →
     #F8FAFC). --cm-border is the exception, pinned above, because light mode
     points it at gray-400. */

  /* Shadows — slate alpha doesn't read on dark, use black */
  --cm-shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.4);
  --cm-shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
  --cm-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.55), 0 2px 4px rgba(0, 0, 0, 0.4);
  --cm-shadow-lg: 0 12px 24px rgba(0, 0, 0, 0.6), 0 4px 8px rgba(0, 0, 0, 0.45);
  --cm-focus-ring: 0 0 0 3px rgb(var(--cm-accent-ring) / 0.45);

  /* Re-lightened from the light-mode values for contrast against the
     dark chip background. --cm-warning-text is also the warning pill's
     dark text (components/pill.css). */
  --cm-success-text: #4ade80;
  --cm-warning-text: #fcd34d;
  --cm-danger-text: #f87171;
  --cm-info-text: #93c5fd;
  --cm-accent-text: var(--cm-accent-300);
  /* Status pill tints (components/pill.css): the hue mixed over the dark
     surface, so a pill stays opaque instead of relying on a translucent
     fill composited over whatever it sits on. */
  --cm-bg-info-soft: color-mix(in srgb, var(--cm-blue-500) 20%, var(--cm-bg-surface));
  --cm-bg-accent-soft: color-mix(in srgb, rgb(var(--cm-accent-ring)) 20%, var(--cm-bg-surface));
  --cm-bg-success-soft: color-mix(in srgb, var(--cm-success-500) 22%, var(--cm-bg-surface));
  --cm-bg-warning-soft: color-mix(in srgb, var(--cm-warning-500) 22%, var(--cm-bg-surface));
  --cm-bg-danger-soft: color-mix(in srgb, var(--cm-danger-500) 22%, var(--cm-bg-surface));

  --cm-avatar-bg: var(--cm-gray-700); /* a light disc in dark mode, as today */
  --cm-avatar-fg: var(--cm-gray-100);
  --cm-avatar-chip-disc-bg: var(--cm-gray-200);
  --cm-avatar-chip-disc-fg: var(--cm-accent-300);
  --cm-avatar-you-disc-bg: var(--cm-accent-600);
}

/*
 * ChartMate design tokens (light mode).
 *
 * Two layers:
 *   1. Raw scales (--cm-teal-*, --cm-gray-*, --cm-blue-*, semantic colors,
 *      typography, spacing, radius, shadow). Numerically consistent: low =
 *      light, high = dark.
 *   2. Semantic tokens (--cm-bg-*, --cm-text-*, --cm-border, --cm-interactive)
 *      that map raw scale steps to roles. Consumed by the staff stylesheets.
 *
 * Dark-mode overrides redefine the raw gray scale and re-map background
 * semantic tokens (text/border tokens re-resolve through the redefined
 * scale). See the :root.dark block above.
 */

:root[data-accent='orange'] {
  --cm-accent-50: #fff7ed;
  --cm-accent-100: #ffedd5;
  --cm-accent-300: #fdba74;
  --cm-accent-500: #f97316;
  --cm-accent-600: #ea580c;
  --cm-accent-700: #c2410c;
  --cm-accent-800: #9a3412;
  --cm-accent-900: #7c2d12;
  --cm-accent-ring: 249 115 22;
}
:root.dark[data-accent='orange'] {
  --cm-accent-500: #fb923c;
  --cm-accent-600: #fdba74;
  --cm-accent-700: #fed7aa;
  --cm-accent-ring: 251 146 60;
}

:root[data-accent='green'] {
  --cm-accent-50: #f0fdf4;
  --cm-accent-100: #dcfce7;
  --cm-accent-300: #86efac;
  --cm-accent-500: #22c55e;
  --cm-accent-600: #16a34a;
  --cm-accent-700: #15803d;
  --cm-accent-800: #166534;
  --cm-accent-900: #14532d;
  --cm-accent-ring: 34 197 94;
}
:root.dark[data-accent='green'] {
  --cm-accent-500: #4ade80;
  --cm-accent-600: #86efac;
  --cm-accent-700: #bbf7d0;
  --cm-accent-ring: 74 222 128;
}

:root[data-accent='violet'] {
  --cm-accent-50: #f5f3ff;
  --cm-accent-100: #ede9fe;
  --cm-accent-300: #c4b5fd;
  --cm-accent-500: #8b5cf6;
  --cm-accent-600: #7c3aed;
  --cm-accent-700: #6d28d9;
  --cm-accent-800: #5b21b6;
  --cm-accent-900: #4c1d95;
  --cm-accent-ring: 139 92 246;
}
:root.dark[data-accent='violet'] {
  --cm-accent-500: #a78bfa;
  --cm-accent-600: #c4b5fd;
  --cm-accent-700: #ddd6fe;
  --cm-accent-ring: 167 139 250;
}

:root[data-accent='fuchsia'] {
  --cm-accent-50: #fdf4ff;
  --cm-accent-100: #fae8ff;
  --cm-accent-300: #f0abfc;
  --cm-accent-500: #d946ef;
  --cm-accent-600: #c026d3;
  --cm-accent-700: #a21caf;
  --cm-accent-800: #86198f;
  --cm-accent-900: #701a75;
  --cm-accent-ring: 217 70 239;
}
:root.dark[data-accent='fuchsia'] {
  --cm-accent-500: #e879f9;
  --cm-accent-600: #f0abfc;
  --cm-accent-700: #f5d0fe;
  --cm-accent-ring: 232 121 249;
}

:root[data-accent='rose'] {
  --cm-accent-50: #fff1f2;
  --cm-accent-100: #ffe4e6;
  --cm-accent-300: #fda4af;
  --cm-accent-500: #f43f5e;
  --cm-accent-600: #e11d48;
  --cm-accent-700: #be123c;
  --cm-accent-800: #9f1239;
  --cm-accent-900: #881337;
  --cm-accent-ring: 244 63 94;
}
:root.dark[data-accent='rose'] {
  --cm-accent-500: #fb7185;
  --cm-accent-600: #fda4af;
  --cm-accent-700: #fecdd3;
  --cm-accent-ring: 251 113 133;
}

/* The neutral slot. Warm-cast on purpose: the dark ground is navy (220°), so a
   cool blue-grey neutral — the old Slate — was the background's own hue with the
   chroma drained out, and disappeared. Chroma here is low enough not to collide
   with Orange, but the warmth gives it somewhere to stand. */
:root[data-accent='stone'] {
  --cm-accent-50: #fafaf9;
  --cm-accent-100: #f5f5f4;
  --cm-accent-300: #d6d3d1;
  --cm-accent-500: #78716c;
  --cm-accent-600: #57534e;
  --cm-accent-700: #44403c;
  --cm-accent-800: #292524;
  --cm-accent-900: #1c1917;
  --cm-accent-ring: 120 113 108;
}
:root.dark[data-accent='stone'] {
  --cm-accent-500: #a8a29e;
  --cm-accent-600: #d6d3d1;
  --cm-accent-700: #e7e5e4;
  /* Unlike every other hue, the neutral MUST re-darken 800/900 for dark mode.
     Its light 800/900 (#292524/#1c1917) are near-black — on the navy ground they
     measure 1.12:1 and the button simply vanishes, while white-on-them scores 15:1
     so no contrast test complains. These values restore parity with the chromatic
     hues (2.23:1 vs bg-surface) while keeping white text readable at 7.63:1. */
  --cm-accent-800: #57534e;
  --cm-accent-900: #44403c;
  --cm-accent-ring: 168 162 158;
}

/*
 * ── Accent hues ──────────────────────────────────────────────────────────────
 * Selected via <html data-accent="…">, set pre-paint by theme-bootstrap.js.
 * Teal needs no block: :root / :root.dark already carry its values.
 *
 * INVARIANT: each :root.dark[data-accent] block MUST restate every literal
 * :root.dark sets (--cm-accent-500/600/700, --cm-accent-ring), because
 * :root.dark and :root[data-accent] have equal specificity (0,2,0).
 * Do NOT add --cm-focus-ring here (it indirects through --cm-accent-ring).
 * Do NOT add 800/900 for a chromatic hue — they must stay dark in both modes.
 * EXCEPTION: stone (the neutral slot) re-darkens 800/900 in dark mode — see
 * the block above. Its light 800/900 are near-black and read as invisible on
 * the navy dark ground even though white-on-them still scores AA; every other
 * hue's light 800/900 stays dark enough to keep working unmodified.
 * Enforced by tests/unit/frontend/theme/tokens.contrast.test.mjs.
 */

}
@layer base {
/*
 * Element defaults and typography (CSS consolidation phase 3c). staff.css,
 * portal.css and request.css import this file into layer(base), which sits
 * BELOW layer(legacy): a legacy rule that targets the same element wins over
 * these defaults whatever its specificity. Only element, pseudo-element, html
 * and body selectors belong here (tests/unit/frontend/stylesheets); component
 * styling goes in components/.
 * Spec: docs/specs/2026-09-29-css-phase-3-base-layout-design.md
 */

/* Inter, self-hosted, Latin subset */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/_shared/assets/fonts/Inter-Regular.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('/_shared/assets/fonts/Inter-Medium.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/_shared/assets/fonts/Inter-SemiBold.woff2') format('woff2');
}

@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/_shared/assets/fonts/Inter-Bold.woff2') format('woff2');
}

html {
  font-family: var(--cm-font-sans);
  font-size: var(--cm-font-size-root);
}

body {
  margin: 0;
  padding: 0;
  background: var(--cm-bg-body);
  color: var(--cm-text-body);
  font-family: var(--cm-font-sans);
  line-height: var(--cm-lh-normal);
  font-feature-settings: 'cv11', 'ss01';
  /* Safety net: tooltip pseudo-elements positioned with absolute x-offsets
     can momentarily overflow the viewport before the JS positioner runs.
     overflow-x: clip stops them from widening the page; clip (not hidden)
     does not create a containing block, so position: fixed children (toasts,
     modals) keep working. */
  overflow-x: clip;
}

/* Text color inherits from body. Only the elements the browser colors on
   its own (form controls, dialog, mark) are set back to body text here, so
   an icon or inline SVG inside a colored container takes that container's
   color through currentColor. */
button,
input,
select,
textarea,
dialog,
mark {
  color: var(--cm-text-body);
}

/* Links default to the interactive color (the dark rail sets its own);
   a link in text also takes .cm-link for its underline (components/link.css). */
a {
  color: var(--cm-interactive);
}

h1,
h2,
h3,
h4,
h5,
h6 {
  margin: 0;
  padding: 0;
  color: var(--cm-text-heading);
  line-height: var(--cm-lh-tight);
}

h1 {
  font-size: var(--cm-text-2xl);
  font-weight: var(--cm-fw-semibold);
}

h2 {
  font-size: var(--cm-text-xl);
  font-weight: var(--cm-fw-semibold);
}

h3 {
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
}

h4 {
  font-size: var(--cm-text-base);
  font-weight: var(--cm-fw-semibold);
}

p {
  margin: 2px;
  padding: 2px;
}

hr {
  width: 100%;
  margin: 0;
  padding: 0;
  border-color: var(--cm-text-body);
}

table,
td,
th {
  font-feature-settings: 'tnum';
}

input,
button,
select,
textarea {
  font-family: var(--cm-font-sans);
  font-size: inherit;
}

/* Form fields: outlined, 24px (owner decision 2026-09-29, border kept after
   reviewing the underline-only version). The focus and disabled states come
   after the field rules below, so they win at equal specificity. */
input:not([type]),
input[type='text'],
input[type='email'],
input[type='password'],
input[type='number'],
input[type='search'],
input[type='date'],
input[type='tel'],
input[type='url'],
select,
textarea {
  box-sizing: border-box;
  height: var(--cm-control-height);
  margin: 0;
  padding: 0 var(--cm-space-3);
  border: 1px solid var(--cm-border-strong);
  border-radius: var(--cm-control-radius);
  background: var(--cm-bg-surface);
  box-shadow: none;
  color: var(--cm-text-body);
  font-size: var(--cm-text-sm);
  transition:
    border-color 120ms ease,
    box-shadow 120ms ease,
    background-color 120ms ease;
}

select {
  appearance: none;
  cursor: pointer;
  padding-right: var(--cm-space-5);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--cm-text-muted) 50%),
    linear-gradient(135deg, var(--cm-text-muted) 50%, transparent 50%);
  background-position:
    calc(100% - 10px) 50%,
    calc(100% - 6px) 50%;
  background-size: 4px 4px;
  background-repeat: no-repeat;
}

select[multiple] {
  height: auto;
  padding-right: var(--cm-space-3);
  background-image: none;
}

textarea {
  height: auto;
  min-height: calc(var(--cm-control-height) * 2.5);
  padding: var(--cm-space-1) var(--cm-space-3);
  line-height: var(--cm-lh-normal);
}

:is(input, select, textarea):is([readonly], [aria-readonly='true']) {
  background-color: var(--cm-bg-readonly);
  cursor: default;
}

/* Focus and disabled states for every field (moved from stylesheetv3.css in
   CSS phase 5). After the field and read-only rules, so they win. */
input:focus,
input:focus-visible,
select:focus,
select:focus-visible,
textarea:focus,
textarea:focus-visible {
  border-color: var(--cm-interactive);
  box-shadow: var(--cm-focus-ring);
  outline: none;
}

input:disabled,
select:disabled:not([aria-readonly='true']),
textarea:disabled {
  background: var(--cm-bg-muted);
  color: var(--cm-text-muted);
  cursor: not-allowed;
}

/* The keyboard focus ring for links and anything focusable by tabindex. */
a:focus-visible,
[role='button']:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--cm-focus-ring);
  border-radius: var(--cm-radius-sm);
}

input::placeholder {
  opacity: 0.7;
}

input::placeholder,
textarea::placeholder {
  color: var(--cm-text-muted);
}

input[type='checkbox'],
input[type='radio'] {
  height: 18px;
  width: 18px;
  accent-color: var(--cm-control-accent);
}

input[type='file']::file-selector-button {
  margin-right: 20px;
  border: none;
  padding: 10px;
  border-radius: var(--cm-radius-lg);
  background: var(--cm-accent-800);
  background-image: linear-gradient(92.88deg, var(--cm-accent-800) 0%, var(--cm-accent-900) 80%);
  cursor: pointer;
  transition: background 0.2s ease-in-out;
  color: var(--cm-text-on-dark);
  line-height: 18px;
}

::-webkit-scrollbar {
  width: var(--scrollbar-thickness);
  height: var(--scrollbar-thickness);
}

::-webkit-scrollbar-track {
  background: var(--cm-scrollbar-track);
}

::-webkit-scrollbar-thumb {
  background: var(--cm-accent-900);
  border-radius: var(--cm-radius-md);
}

::-webkit-scrollbar-thumb:hover {
  background: var(--cm-accent-800);
}

}
@layer layout {
/*
 * Staff app shell (CSS consolidation phase 3d): the sidenav and its flyouts,
 * the right sidebar (Upload Records' additional-actions panel), the
 * session-timeout box's countdown chip, the body gutter that keeps page
 * content clear of the rail, and the logged-out .logo-div header. Moved out
 * of stylesheetv3.css in their original order. The session-timeout box
 * itself is a popup since phase 4 (components/popup.css, cm-popup--session).
 * The wait screen (spinner, messages, progress panel) left this file in CSS
 * consolidation phase 4 feedback for components/loader.css.
 *
 * staff.css imports this file into layer(layout), which sits ABOVE
 * layer(legacy): a rule here beats every legacy rule on the same element and
 * property, whatever that rule's specificity. Legacy rules that used to win
 * over a shell rule were moved or folded in here (see the focus ring note
 * below). No !important in this file (tests/unit/frontend/stylesheets): the
 * !important the moved rules carried in stylesheetv3.css is unnecessary here
 * since layer(layout) already outranks layer(legacy), and no remaining
 * legacy !important shares a property with a shell element. The Preload
 * Tool, which does not load staff.css, links it after base.css.
 * Spec: docs/specs/2026-09-29-css-phase-3-base-layout-design.md
 */

/* Staff pages start hidden so they never flash before their script runs;
   a page shows itself once it has: initializePage.js adds .navbar-margin on
   pages with the navbar, .page-shown on those without (login, reset, set
   password). Here, not in base.css, so the customer pages, which share
   base.css but not this script, are never hidden. */
body {
  display: none;
}

.page-shown {
  display: block;
}

/* Navbar and sidenav */
.navbar-margin {
  display: block;
  padding-left: 70px;
}

.sidenav-page-wrapper {
  display: flex;
  flex-direction: row;
  width: 100%;
  height: 100%;
}

#navbar {
  height: 100%;
  padding: 0px;
  margin: 0px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  overflow: hidden;
}

#navbar i {
  color: var(--cm-text-on-dark);
}

.nav-item-div {
  height: 100%;
  display: flex;
  flex-direction: column;
}

.logo-div {
  position: absolute;
  top: 0px;
  left: 0px;
  display: flex;
  align-items: center;
  padding: 10px;
  background-color: var(--cm-bg-surface);
  width: 100%;
  box-shadow: var(--cm-shadow-sm);
  box-sizing: border-box;
  padding-left: 1px;
}

/* The logo's slot in the logged-out header (the Reset page). */
.logo-div__brand {
  width: 30%;
  min-width: 30%;
}

/* The ChartMate logo at the top of the sidenav. */
.sidenav .logo {
  padding-top: 10px;
  padding-left: 10px;
}

/* The side navigation menu */
.sidenav {
  position: fixed;
  display: flex;
  flex-direction: column;
  height: 100vh;
  width: 70px;
  top: 0;
  left: 0;
  background-color: var(--cm-bg-sidebar);
  overflow-x: hidden;
  transition: 0.25s;
  z-index: var(--cm-z-sidenav);
}

.sidenav-hider {
  height: 100%;
  width: 20px;
  position: fixed;
  z-index: var(--cm-z-overlay);
  top: 0;
  left: 50px;
  background-color: var(--cm-bg-sidebar);
  overflow-x: hidden;
  transition: 0.25s;
}

.sidenav:hover .sidenav-hider {
  left: 230px;
  width: 0px;
}

.sidenav:hover {
  position: fixed;
  width: 250px;
}

.sidenav:hover .nav-logo {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 230px;
}

/* The navigation menu links */
.sidenav a {
  /* Symmetric horizontal padding so the icon-div ends up centered. The
     legacy 8px 8px 8px 10px combined with the .sidenav .nav-item
     horizontal margin pushed every icon ~4px right of the rail's center. */
  padding: 8px;
  text-decoration: none;
  font-size: var(--cm-text-xl);
  display: flex;
}

/* When you mouse over the navigation links, change their color */
.sidenav a:hover {
  color: var(--cm-text-on-dark);
}

/* On smaller screens, where height is less than 450px, change the style of the sidenav (less padding and a smaller font size) */
@media screen and (max-height: 450px) {
  .sidenav {
    padding-top: 15px;
  }

  .sidenav a {
    font-size: var(--cm-text-lg);
  }
}

.nav-item {
  display: block;
  padding: 10px;
  width: 100%;
}

.nav-logo {
  display: block;
  /* Original 10px padding shifted 2px right so the AutoMate wedge looks
     centered in the 70px rail when collapsed. */
  padding: 10px 8px 10px 12px;
  width: 100%;
  transition: 0.25;
}

.nav-text {
  padding-left: 30px;
  color: var(--cm-text-on-dark);
  /* Keep nav labels on a single line. When the rail is collapsed and the
     parent's overflow clips the label, white-space: normal would wrap
     the text inside the toggle's box (e.g. "Dark mode" → "Dark\nmode"),
     which inflates the toggle's height and shifts the icon vertically
     when the rail expands and the label fits on one line again. */
  white-space: nowrap;
}

div.dropdown {
  display: inline-block;
}

.dropdown-content a {
  color: var(--cm-text-on-dark);
  padding: var(--cm-space-2) var(--cm-space-4);
  text-decoration: none;
  display: block;
  text-align: left;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
  transition:
    background-color 120ms ease,
    color 120ms ease;
}

.dropdown-content a:hover {
  background-color: var(--cm-bg-on-dark-hover);
  color: var(--cm-text-on-dark);
}

.dropdown:hover .dropdown-content {
  display: block;
}

.dropdown-content {
  display: none;
  position: fixed;
  background-color: var(--cm-bg-sidebar);
  min-width: 160px;
  box-shadow: var(--cm-shadow-md);
  z-index: var(--cm-z-overlay-menu);
  /* Lifts the flyout from under its link to the item's top edge, where it
     sat before the link took the item's padding (was -41px). */
  margin-top: -51px;
  left: 250px;
}

/* A group ends at its side margin (246px) while its flyout starts at the
   rail's edge (250px). This strip fills the gap, so the pointer stays in
   the group on its way to the links (Automate-wwl6). */
.dropdown-content::before {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  right: 100%;
  width: var(--cm-space-1);
}

.icon-div {
  display: flex;
  justify-content: center;
  align-items: center;
  min-width: 30px;
  margin: 0px;
  padding: 0px;
}

.dark-mode-toggle {
  /* Mirror the .nav-link layout (flex row, icon-div + nav-text) so the
     icon centers in the rail collapsed and the label slides in on
     hover-expand. */
  display: flex;
  flex-direction: row;
  align-items: center;
  font-size: var(--cm-text-xl);
}

.dark-mode-toggle i {
  width: min-content;
  cursor: pointer;
  color: var(--cm-text-on-dark);
}

.rotate-in {
  animation: rotateIn 0.3s forwards;
}

.rotate-out {
  animation: rotateOut 0.3s forwards;
}

@keyframes rotateIn {
  from {
    transform: rotate(-180deg);
    opacity: 0;
  }
  to {
    transform: rotate(0deg);
    opacity: 1;
  }
}

@keyframes rotateOut {
  from {
    transform: rotate(0deg);
    opacity: 1;
  }
  to {
    transform: rotate(180deg);
    opacity: 0;
  }
}

/* The session-timeout box (loader.html #timeoutModal) is a popup
   (components/popup.css, cm-popup--session) whose body centers its
   children: its paragraphs and countdown row stretch across it instead, so
   the text stays left-aligned. */
#timeoutModal p,
#timeoutModal .timeout-modal-countdown-div {
  align-self: stretch;
}

.timeout-modal-countdown-div {
  display: flex;
  flex-direction: row;
  align-items: center;
  margin-bottom: 20px;
}

.timeout-modal-countdown {
  display: flex;
  color: var(--cm-text-on-dark);
  border-radius: var(--cm-radius-session-countdown);
  background-color: var(--cm-accent-800);
  padding: 10px;
  justify-content: center;
  align-items: center;
  margin-left: 5px;
  margin-right: 5px;
}

/* Right sidebar (Upload Records additional-actions panel) */
.right-sidebar {
  display: flex;
  flex-direction: column;
  position: fixed;
  top: 0;
  /* Hidden state: parked just off-screen with only the icon-div tab
     visible. The icon-div is positioned absolute on the right edge
     of the sidebar so users still see + can click it while the body
     of the sidebar is hidden. box-sizing: border-box is critical:
     without it, padding-left would extend the box past the declared
     width, and `right: -320px` would leave the padding sticking out
     past the viewport's right edge. */
  right: -320px;
  width: 320px;
  box-sizing: border-box;
  /* Use --cm-bg-chrome (pinned #131C2E) instead of --cm-bg-sidebar
     so the right-sidebar reads as a slightly elevated surface above
     the navbar's darker shade rather than blending into it. */
  background-color: var(--cm-bg-chrome);
  margin-top: 10px;
  padding-left: var(--cm-space-3);
  /* No top-left rounding: the icon tab sits at the panel's top-left
     corner with its own rounded corners, so the panel itself wants a
     straight top-left edge to butt up against the tab's straight
     right edge. Bottom-left stays rounded as a soft visual finish. */
  border-bottom-left-radius: var(--cm-radius-lg);
  box-shadow: var(--cm-shadow-drawer);
  z-index: var(--cm-z-raised);
  transition: right 220ms cubic-bezier(0.21, 1.02, 0.73, 1);
}

.right-sidebar-content {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-1);
  padding: var(--cm-space-4) var(--cm-space-4) var(--cm-space-5);
}

.right-sidebar-content * {
  color: var(--cm-text-on-dark);
}

.right-sidebar:hover,
.right-sidebar:focus-within {
  right: 0;
  z-index: var(--cm-z-hover);
}

.right-sidebar p {
  margin: 0;
  padding: 0;
}

/* Section heading at the top of an action sidebar's expanded panel.
   Uppercase small caps so it reads as a section divider rather than
   a competing page heading. Scoped to `.right-sidebar-title` so the
   hotkeys sidebar (which uses many <h3> sub-headings) keeps its
   default heading look. */
.right-sidebar-title {
  margin: 0 0 var(--cm-space-2);
  padding: 0;
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-semibold);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cm-text-on-dark);
  opacity: 0.65;
}
/* Default headings inside the sidebar: tighten the default browser
   margins but keep them readable as headings (used by the hotkeys
   sidebar and any future panel that lists multiple sub-sections). */
.right-sidebar-content h2 {
  margin: 0 0 var(--cm-space-2);
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-on-dark);
}
.right-sidebar-content h3 {
  margin: var(--cm-space-3) 0 var(--cm-space-1);
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-on-dark);
  opacity: 0.85;
}
.right-sidebar-content h3:first-child {
  margin-top: 0;
}

/* Collapsed state: an action tab sticking out of the right edge of the
   panel. Aligned to the panel's top edge so the tab + panel read as
   one continuous shape; the tab's right edge butts against the
   panel's left edge with no offset and no corner curve between them.
   Tab carries the rounded corners on the left so the joined shape has
   a tab on its top-left and a flat right side. */
.right-sidebar-icon-div {
  position: absolute;
  right: 100%;
  top: 0;
  height: 56px;
  background-color: var(--cm-bg-chrome);
  cursor: pointer;
  /* Drop-shadow only on the LEFT side of the tab so it doesn't bleed
     across the seam where the tab meets the panel. */
  box-shadow: var(--cm-shadow-drawer-tab);
}
.right-sidebar-icon-div > i {
  font-size: var(--cm-text-lg);
  color: var(--cm-text-on-dark);
}

.right-sidebar i {
  color: var(--cm-text-on-dark);
}

/* Action rows: Upload Records' .right-sidebar-action (QA's retired
   .request-action shared this look). The previous .request-action used
   --table-row-hover-color (a light-mode token) for hover, which
   washed out almost invisibly on the dark sidebar. New hover/focus
   uses a teal-tinted overlay matching the design system's interactive
   color, with a left accent that animates in from 0 → full width to
   give the row a tactile feel. */
.right-sidebar-action {
  position: relative;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--cm-space-3);
  padding: var(--cm-space-2) var(--cm-space-3);
  border-radius: var(--cm-radius-md);
  cursor: pointer;
  user-select: none;
  color: var(--cm-text-on-dark);
  transition:
    background 140ms ease,
    transform 140ms ease;
}
.right-sidebar-action:hover,
.right-sidebar-action:focus-visible {
  background: color-mix(in srgb, var(--cm-teal-500) 20%, transparent);
  outline: none;
  transform: translateX(2px);
}
.right-sidebar-action::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  bottom: 8px;
  width: 2px;
  background: var(--cm-teal-500);
  border-radius: var(--cm-radius-accent-bar);
  opacity: 0;
  transition: opacity 140ms ease;
}
.right-sidebar-action:hover::before,
.right-sidebar-action:focus-visible::before {
  opacity: 1;
}
.right-sidebar-action > i {
  width: 18px;
  font-size: var(--cm-text-base);
  text-align: center;
  flex-shrink: 0;
  color: var(--cm-text-on-dark);
  opacity: 0.85;
}
.right-sidebar-action > p {
  margin: 0;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
}

/* ===== Sidebar polish (light reskin) ===== */
.sidenav {
  background: var(--cm-bg-sidebar);
}

.sidenav a {
  color: var(--cm-text-on-dark);
  transition:
    background-color 120ms ease,
    color 120ms ease;
}

.sidenav .nav-item {
  margin: 2px var(--cm-space-1);
  /* auto, not the global rule's 100%, so the item fits inside its margins
     and the icon holds still when the rail expands. */
  width: auto;
  padding: 0;
}

/* One hover, on the link (CSS phase 5.5 restyle, docs/specs/
   2026-10-12-css-phase-5-5-restyle-design.md). The link fills its item:
   the item's 10px 8px padding moved onto it on top of its own 8px, so the
   icon lands where it did, at the rail's center. It stays lit while its
   flyout (a descendant of the item) is open. Flyout links keep their own
   tint (.dropdown-content a:hover). */
.sidenav .nav-item > a {
  padding: 18px 16px;
  border-radius: var(--cm-radius-md);
}

.sidenav .nav-item > a:hover,
.sidenav .nav-item:hover > a {
  background-color: var(--cm-bg-nav-hover);
  box-shadow: var(--cm-shadow-nav-hover);
}

.sidenav .nav-item > a:focus-visible {
  background-color: var(--cm-bg-nav-hover);
  box-shadow: var(--cm-shadow-nav-hover), var(--cm-focus-ring);
}

.sidenav .nav-item.active,
.sidenav .nav-item[aria-current='page'],
.sidenav .nav-link.active {
  background: var(--cm-bg-nav-active);
  color: var(--cm-interactive);
}

.sidenav .nav-link.active i,
.sidenav .nav-item.active i {
  color: var(--cm-interactive);
}

.sidenav .dark-mode-toggle {
  /* Mirror the COMBINED padding of .nav-item (8px) + .nav-link (8px) so
     the icon-div lands at the rail's centerX. The toggle is a single
     element (no nested nav-link wrapper), so its padding has to absorb
     both layers of indent on its own. */
  margin: 2px var(--cm-space-1);
  padding: 10px 16px;
  border-radius: var(--cm-radius-md);
  box-sizing: border-box;
  cursor: pointer;
  color: var(--cm-text-on-dark);
  transition: background-color 120ms ease;
}

.sidenav .dark-mode-toggle:hover {
  background: var(--cm-bg-nav-hover);
  box-shadow: var(--cm-shadow-nav-hover);
}

.sidenav .dark-mode-toggle:focus-visible {
  background: var(--cm-bg-nav-hover);
  box-shadow: var(--cm-shadow-nav-hover), var(--cm-focus-ring);
  outline: none;
}

/* Tooltip-CSS is intentionally absent: the existing `.sidenav:hover { width: … }`
   rule already expands the rail on hover and reveals .nav-text. */

/* ===== Right-side slide-out panels (#changelog) ===== */
.right-sidebar {
  background: var(--cm-bg-chrome);
  color: var(--cm-text-on-dark);
}

.right-sidebar * {
  color: var(--cm-text-on-dark);
}

/* Hover-tint on the icon tab (size + radius live in the primary
   .right-sidebar-icon-div rule earlier in the file). */
.right-sidebar-icon-div:hover {
  background: var(--cm-bg-on-dark-hover);
}

.right-sidebar .fa-circle-exclamation {
  color: var(--cm-warning-500);
}

/* The universal focus ring (stylesheetv3.css: a, [role='button'] and
   [tabindex] :focus-visible) out-ranked these shell rules while both sat in
   legacy. layout sits above legacy, so the ring is restated here for the
   shell elements it used to win on. */
.right-sidebar-icon-div:focus-visible {
  box-shadow: var(--cm-focus-ring);
}

.right-sidebar-action:focus-visible {
  border-radius: var(--cm-radius-sm);
}

/* Reserve a fixed-width gutter for the navbar so page content can never
   slide behind the sidenav when the viewport is narrow. The sidenav
   itself is position: fixed (out of flow); without a body padding-left,
   .page-container { min-width: 100% } expands across the entire
   viewport including the 70px navbar strip; content scrolls *under*
   the rail. Padding the body's content area pushes everything to the
   right of the navbar.

   Use :has() so the gutter only applies on pages that actually mount a
   navbar. The login page (/api/index.html) doesn't include navbar.html
   and shouldn't be offset. The pre-include placeholder div carries the
   w3-include-html attribute; once common.js's includeHTML swap runs
   the sidenav class lands in the DOM. Match either state so the
   padding doesn't pop in/out during the include. */
body:has(.sidenav),
body:has([w3-include-html*='navbar']) {
  padding-left: var(--sidebar-width);
  box-sizing: border-box;
}

/* The page column on browse pages: the page header, search bar and results
   table share one width, 95% of the page, at every window size. A table
   wider than the column scrolls sideways inside its own box
   (.page-column-table) instead of running past the header and bar, so the
   page itself never scrolls sideways. Spec:
   docs/specs/2026-10-11-css-phase-4-batch3b-design.md. */
.page-column {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: var(--cm-space-4);
  width: 95%;
  margin-top: var(--cm-space-5);
}

/* Page column items shown or hidden together (Downloads' access message,
   its card and history): the column's width and gap. */
.page-column-group {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: var(--cm-space-4);
}

/* The box clips and scrolls only while its table is wider than it
   (common.js sets data-overflowing): an unclipped box keeps the table's
   sticky header row sticking to the page as it scrolls. Row Actions menus
   inside a scrolling box float above it (common/menu.js). */
.page-column-table[data-overflowing] {
  overflow-x: auto;
}

/* ===== Right-side dock pills polish (Automate-7qdy) ===== */
.right-sidebar-icon-div {
  width: 44px;
  min-height: 44px;
  border-top-left-radius: var(--cm-radius-pill);
  border-bottom-left-radius: var(--cm-radius-pill);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--cm-space-2) var(--cm-space-3);
}
.right-sidebar-icon-div > i:not(.fa-circle-exclamation) {
  font-size: var(--cm-text-xl);
}
.right-sidebar-icon-div .fa-circle-exclamation {
  font-size: var(--cm-text-xs);
  top: 6px;
  right: 8px;
}

/* ===== Sidebar nav main label sizing (Automate-exzt) ===== */
.sidenav .nav-text {
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
  line-height: var(--cm-lh-snug);
  letter-spacing: 0;
}

/* ===== Home nav badge (tasks context, PR3) ============================ */
/* The badge's look is the count pill (components/pill.css, .cm-pill--accent
   --solid --count on the span in navbar.html); only its place is here. */
#homeNavDropdown .icon-div {
  position: relative;
}
.nav-badge {
  position: absolute;
  top: -6px;
  right: -10px;
  pointer-events: none;
  /* The overhang (right: -10px) lands the badge's right edge past x=50 in
     the collapsed 70px rail, which is where .sidenav-hider (same
     background, z-index: 100) sits (left: 50px) to mask the rail's own
     edge during the hover transition. Without this, the hider paints over
     that slice of the badge at rest, chopping its right edge off. 101
     clears the hider in both the collapsed and hovered (hider width: 0)
     states; it happens to match .dropdown-content's z-index, but the two
     never occupy the same screen area. */
  z-index: var(--cm-z-overlay-menu);
}

/* The page shell: #main pushes over when the sidenav opens; .page-container
   is the page's column (`safe center` falls back to the start when a child
   is wider, so a wide table scrolls right instead of spilling under the
   fixed navbar); .container is a centered column with a 20px gutter. The
   login page uses its own .login-shell (pages/login.css) instead. */
#main {
  transition: margin-left 0.5s;
  padding: 20px;
}

.page-container {
  display: flex;
  flex-direction: column;
  align-items: safe center;
  justify-content: safe center;
  min-height: 100vh;
  min-width: 100%;
}

.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  margin: 20px;
}

/* Layout primitives: a flex row, column or cell that centers its content.
   In layer layout, below components, so a component's own rule for a row
   inside it (.cm-popup__box .row) still wins. */
.column {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: center;
}

.cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

}
@layer components {
/* Buttons (phase 4, docs/specs/2026-10-07-css-phase-4-buttons-design.md).
 *   .cm-button                  main action: teal fill
 *   --secondary / --danger      gray outline / red outline
 *   --text                      teal text
 *   --icon (+ --on-dark)        square icon button (aria-label required)
 *   --secondary --on-dark       a secondary button on a dark band (the portal's hero)
 *   --block                     full width (one height for every button: --cm-button-height)
 *   --icon --sm                 a small icon square at the field height (--cm-button-height-sm)
 *   --busy                      waiting on its action: a spinner in place of the label
 * Radius, text size and weight and side padding come from tokens
 * (--cm-button-*, tokens.css), which the portal resizes on its :root.
 * An icon button hovers in a teal border, the selected tint and a teal icon,
 * the same hover as the popup, drawer and toast closes (CSS phase 5.5
 * restyle); --on-dark keeps its own.
 * Disabled comes last so it wins for every look.
 * [aria-disabled="true"] is a look and assistive-tech state only: it does not
 * stop focus or clicks, the caller must block activation itself.
 */

.cm-button {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--cm-space-2);
  flex-shrink: 0;
  height: var(--cm-button-height);
  margin: 0;
  padding: 0 var(--cm-button-padding-x);
  border: 1px solid transparent;
  border-radius: var(--cm-button-radius);
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
  font-family: inherit;
  font-size: var(--cm-button-font-size);
  font-weight: var(--cm-button-font-weight);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition:
    background-color 120ms ease,
    border-color 120ms ease;
}

.cm-button * {
  color: inherit;
}

.cm-button:hover {
  background: var(--cm-interactive-hover);
}

.cm-button:active {
  background: var(--cm-interactive-active);
}

.cm-button:focus-visible {
  outline: none;
  border-radius: var(--cm-button-radius);
  box-shadow: var(--cm-focus-ring);
}

.cm-button--secondary,
.cm-button--danger,
.cm-button--text {
  background: transparent;
}

.cm-button--secondary {
  border-color: var(--cm-border-strong);
  color: var(--cm-text-body);
}

.cm-button--danger {
  border-color: var(--cm-danger-text);
  color: var(--cm-danger-text);
}

/* :is(:hover, :active) so holding Space on a focused button (:active with no
 * :hover) gets the same look as a mouse hover, instead of falling through to
 * the base .cm-button:active rule's primary accent fill. */
.cm-button--secondary:is(:hover, :active),
.cm-button--danger:is(:hover, :active) {
  background: var(--cm-bg-muted);
}

.cm-button--icon:is(:hover, :active) {
  border-color: var(--cm-interactive);
  background: var(--cm-chip-selected-bg);
  color: var(--cm-interactive);
}

.cm-button--text {
  padding: 0 var(--cm-space-1);
  color: var(--cm-interactive);
}

.cm-button--text:is(:hover, :active) {
  background: transparent;
}

.cm-button--text:hover {
  text-decoration: underline;
}

.cm-button--icon {
  width: var(--cm-button-height);
  padding: 0;
  border-color: var(--cm-border-strong);
  background: var(--cm-bg-surface);
  color: var(--cm-text-body);
}

.cm-button--on-dark {
  background: transparent;
  border-color: transparent;
  color: var(--cm-text-on-dark);
}

.cm-button--on-dark:is(:hover, :active) {
  border-color: transparent;
  background: var(--cm-bg-on-dark-hover);
  color: var(--cm-text-on-dark);
}

/* --on-dark with --secondary: a secondary button on a dark band (the
   portal's hero band, components/hero.css): a translucent fill and ring,
   white text. */
.cm-button--secondary.cm-button--on-dark {
  border-color: var(--cm-button-on-dark-border);
  background: var(--cm-button-on-dark-bg);
  color: var(--cm-text-on-dark);
}

.cm-button--secondary.cm-button--on-dark:is(:hover, :active) {
  border-color: var(--cm-button-on-dark-border);
  background: var(--cm-button-on-dark-bg-hover);
}

.cm-button--icon.cm-button--sm {
  width: var(--cm-button-height-sm);
  height: var(--cm-button-height-sm);
}

.cm-button--block {
  width: 100%;
}

.cm-button:is(:disabled, [aria-disabled='true']) {
  background: var(--cm-bg-muted);
  border-color: transparent;
  color: var(--cm-text-muted);
  cursor: not-allowed;
}

.cm-button:is(.cm-button--text, .cm-button--on-dark):is(:disabled, [aria-disabled='true']) {
  background: transparent;
  text-decoration: none;
}

/* --busy: a submit waiting on its action (the portal's sign-in steps). The
   caller empties the label, disables the button and sets aria-busy; a
   spinner in the text color takes the label's place. After the disabled
   rules so its cursor wins. */
.cm-button.cm-button--busy {
  cursor: progress;
}

.cm-button--busy::before {
  content: '';
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: cm-button-spin 0.7s linear infinite;
}

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

@media (prefers-reduced-motion: reduce) {
  .cm-button--busy::before {
    animation-duration: 2s;
  }
}

}
@layer components {
/*
 * Popup (CSS consolidation phase 4). Spec:
 * docs/specs/2026-09-30-css-phase-4-popups-design.md
 * Markup: common/popup-shell.js buildPopupShell(), or by hand in page HTML:
 *   div.cm-popup[--size][--stack] > div.cm-popup__box >
 *     header.cm-popup__header > h2.cm-popup__title, p.cm-popup__subtitle?,
 *       span.cm-popup__help?, div.cm-popup__actions?, button.cm-popup__close?
 *     (__help is a .cm-help-icon beside the title, components/help-icon.css)
 *     div.cm-popup__body, div.cm-popup__footer?
 * A closed popup carries the hidden attribute ([hidden] in utilities.css);
 * openPopup() and closePopup() in common/popup-shell.js open and close it.
 */

.cm-popup {
  display: flex;
  position: fixed;
  inset: 0 0 0 var(--sidebar-width);
  justify-content: center;
  align-items: center;
  background-color: var(--cm-bg-backdrop);
  z-index: var(--cm-z-overlay);
}

/* A floating popup (draggable panels): no backdrop, the page stays usable. */
.cm-popup--floating {
  position: absolute;
  background-color: transparent;
  pointer-events: none;
  z-index: var(--cm-z-float);
}

.cm-popup--floating > .cm-popup__box {
  pointer-events: all;
}

.cm-popup--floating > .cm-popup__box > .cm-popup__header {
  cursor: move;
}

/* Stacking: a popup opened over a popup, confirm prompts, patient alerts.
   After --floating so a floating panel can also be stacked. */
.cm-popup--stacked {
  z-index: var(--cm-z-popup-stacked);
}

.cm-popup--stacked-2 {
  z-index: var(--cm-z-popup-stacked-2);
}

.cm-popup--stacked-3 {
  z-index: var(--cm-z-popup-stacked-3);
}

.cm-popup--modal {
  z-index: var(--cm-z-modal);
}

.cm-popup--priority {
  z-index: var(--cm-z-modal-priority);
}

/* The session-timeout box: above every popup and modal, below the sidenav. */
.cm-popup--session {
  z-index: var(--cm-z-session);
}

/* The default, sm, md and wide sizes name the content width, as the legacy
   content-box .popup did: each adds the body's side padding and the box's
   1px borders. Full sizes the box to the window and tall sets its height,
   so neither adds padding. Modifiers reach only their own box (child
   combinator), so a popup opened from inside another (nested in its overlay
   to stack above it) keeps its own size. */
.cm-popup__box {
  display: flex;
  flex-direction: column;
  position: relative;
  box-sizing: border-box;
  min-width: min(calc(480px + 2 * var(--cm-space-6) + 2px), 98vw);
  max-width: min(calc(720px + 2 * var(--cm-space-6) + 2px), 98vw);
  max-height: 95vh;
  overflow: hidden;
  background-color: var(--cm-bg-surface);
  color: var(--cm-text-body);
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-xl);
  box-shadow: var(--cm-shadow-popup);
}

.cm-popup--sm > .cm-popup__box {
  max-width: min(calc(480px + 2 * var(--cm-space-6) + 2px), 98vw);
}

/* Md: a fixed width for two-column popups (Task, the add/edit patient alert
   form, patient detail) so the box does not resize while its lists load,
   capped to the overlay like wide. */
.cm-popup--md > .cm-popup__box {
  width: min(
    calc(880px + 2 * var(--cm-space-6) + 2px),
    calc(100vw - var(--sidebar-width) - 2 * var(--cm-space-4))
  );
  max-width: none;
}

/* Wide also caps to the overlay (right of the sidenav rail) with a small
   margin on each side, as full does, for windows narrower than the box. */
.cm-popup--wide > .cm-popup__box {
  max-width: min(
    calc(1180px + 2 * var(--cm-space-6) + 2px),
    calc(100vw - var(--sidebar-width) - 2 * var(--cm-space-4))
  );
}

/* A body of four field columns (the Customer popup, CSS phase 5.5 restyle)
   takes a fixed width inside the wide cap, so each column fits its check
   chips on one line. */
.cm-popup--wide
  > .cm-popup__box:has(> .cm-popup__body > .cm-popup__columns > .cm-popup__column:nth-child(4)) {
  width: min(
    calc(1040px + 2 * var(--cm-space-6) + 2px),
    calc(100vw - var(--sidebar-width) - 2 * var(--cm-space-4))
  );
}

/* Full: 95% of the window, capped to the overlay (which starts right of the
   sidenav rail) with a small margin on each side. */
.cm-popup--full > .cm-popup__box {
  width: min(95vw, calc(100vw - var(--sidebar-width) - 2 * var(--cm-space-4)));
  max-width: min(98vw, 1800px);
}

.cm-popup--tall > .cm-popup__box {
  height: 95vh;
}

.cm-popup__header {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: start;
  column-gap: var(--cm-space-3);
  padding: var(--cm-space-4) var(--cm-space-4) var(--cm-space-3) var(--cm-space-6);
  border-bottom: 1px solid var(--cm-border);
}

.cm-popup__title {
  grid-column: 1;
  margin: 0;
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  line-height: 1.3;
  color: var(--cm-text-heading);
}

.cm-popup__subtitle {
  grid-column: 1;
  margin: var(--cm-space-1) 0 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-text-muted);
}

.cm-popup__actions {
  grid-column: 2;
  grid-row: 1 / span 2;
  display: flex;
  align-items: center;
  gap: var(--cm-space-2);
}

/* A help icon beside the title (CSS phase 5.5 restyle): the popup's
   instructions, on hover or focus. A header with one grows a column for it
   between the title and the actions, so the icon stays out of the title
   and so out of the dialog's name. */
.cm-popup__header:has(> .cm-popup__help) {
  grid-template-columns: auto minmax(0, 1fr) auto auto;
}

.cm-popup__header:has(> .cm-popup__help) > .cm-popup__subtitle {
  grid-column: 1 / 3;
}

.cm-popup__header:has(> .cm-popup__help) > .cm-popup__actions {
  grid-column: 3;
}

.cm-popup__header:has(> .cm-popup__help) > .cm-popup__close {
  grid-column: 4;
}

/* Two classes so the 8px from the title (the column gap less the icon's own
   margin) beats help-icon.css's margin, and the icon takes the 18px
   title's height. */
.cm-popup__header > .cm-popup__help {
  grid-column: 2;
  grid-row: 1;
  align-self: center;
  justify-self: start;
  width: 16px;
  height: 16px;
  margin-left: calc(var(--cm-space-2) - var(--cm-space-3));
  font-size: var(--cm-text-xs);
}

/* A bordered square with the icon buttons' hover (components/button.css
   .cm-button--icon, CSS phase 5.5 restyle). */
.cm-popup__close {
  grid-column: 3;
  grid-row: 1 / span 2;
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: transparent;
  border: 1px solid var(--cm-border-strong);
  border-radius: var(--cm-radius-md);
  color: var(--cm-text-body);
  font-size: var(--cm-text-lg);
  line-height: 1;
  cursor: pointer;
  transition:
    background-color 120ms ease,
    border-color 120ms ease,
    color 120ms ease;
}

.cm-popup__close:hover,
.cm-popup__close:focus-visible {
  border-color: var(--cm-interactive);
  background-color: var(--cm-chip-selected-bg);
  color: var(--cm-interactive);
}

.cm-popup__close:focus-visible {
  outline: none;
  box-shadow: var(--cm-focus-ring);
}

.cm-popup__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--cm-space-3);
  flex: 1 1 auto;
  min-height: 0;
  padding: var(--cm-space-4) var(--cm-space-6) var(--cm-space-6);
  overflow-x: hidden;
  overflow-y: auto;
}

.cm-popup__footer {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: var(--cm-space-2);
  padding: var(--cm-space-3) var(--cm-space-6);
  border-top: 1px solid var(--cm-border);
}

/* A footer item that sits at the left edge, apart from the right-aligned
   actions (e.g. What's New "Full changelog", a text button). */
.cm-popup__footer-start {
  margin-right: auto;
}

/* Content inside popups: ported from the legacy ".popup ..." rules
   (stylesheetv3.css "Modal / popup polish"). Builders now emit the field
   component (components/field.css) for every label; what's left here is
   layout only (row/cell spacing, control width) for the
   static popups built straight in page HTML. Forms PR 1 (Task 7) deleted
   the label rule this comment used to describe: the last markup that
   needed it (Post Payments' allocation-summary captions) now carries
   cm-field__label itself. The paragraph rule below stays: it styles
   plain paragraphs in popup cells, e.g. the Hotkeys popup's (invoice.html)
   nine-row shortcut list, the only remaining case. A classed or
   inline-styled <p> (every other popup's) already has its own look, so
   the plain :not([class]) guard is enough; no field-class coupling
   needed. */
.cm-popup__box .row {
  gap: var(--cm-space-4);
  width: 100%;
  align-items: center;
  margin-bottom: var(--cm-space-2);
  justify-content: stretch;
}

/* Field columns in a popup body (the Customer and SaaS Owner popups): a row
   of __column s sharing its width evenly, each stacking its fields at full
   width with their labels on the left edge. Two classes deep, after the
   .row rule above, so the row's own alignment and gap win. */
.cm-popup__box .cm-popup__columns {
  align-items: flex-start;
  gap: 10px;
}

.cm-popup__column {
  justify-content: flex-start;
  align-items: stretch;
  gap: var(--cm-space-3);
}

.cm-popup__columns > .cm-popup__column {
  flex: 1 1 0;
}

/* A single 340px field column the body centers (the Tax Code override):
   labels left-aligned, every control one width, 14px between fields. */
.cm-popup__box .cm-popup__form {
  width: 340px;
  max-width: 100%;
}

.cm-popup__form-column {
  align-items: stretch;
  width: 340px;
  max-width: 100%;
}

.cm-popup__form-column > * {
  margin-bottom: 14px;
}

.cm-popup__form-column input:disabled {
  opacity: 0.6;
}

/* A two-column field grid spanning the body (Mark Problem): fields in
   reading order, labels on the left edge, 12px between rows and 16px between
   columns; __grid-span takes a whole row. One column on a narrow window. */
.cm-popup__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--cm-space-3) var(--cm-space-4);
  align-self: stretch;
}

.cm-popup__grid-span {
  grid-column: 1 / -1;
}

@media (max-width: 600px) {
  .cm-popup__grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* A prompt's message (CustomPrompt) wraps at 400px. */
.cm-popup__prompt-text {
  max-width: 400px;
}

/* A block that spans the body's width instead of centering on its own
   (ConfirmDialog's renderDetail slot, the portal's fee total). */
.cm-popup__block {
  align-self: stretch;
}

/* A full-width divider between sections of a popup body. */
.cm-popup__rule {
  width: 100%;
  margin: 6px 0;
}

.cm-popup__box .cell {
  flex: 1 1 0;
  align-items: stretch;
  gap: var(--cm-space-1);
}

.cm-popup__box .cell p:not([class]) {
  margin: 0;
  font-size: var(--cm-text-base);
  font-weight: var(--cm-fw-medium);
  color: var(--cm-text-body);
  text-align: left;
}

/* Excludes checkbox/radio inside :where() so the exclusion costs no
   specificity (same pattern as field.css): a check row's input keeps
   base.css's 18px square instead of stretching to the cell's width. */
.cm-popup__box .cell :where(input:not([type='checkbox'], [type='radio'])),
.cm-popup__box .cell select,
.cm-popup__box .cell textarea,
.cm-popup__box td :where(input:not([type='checkbox'], [type='radio'])),
.cm-popup__box td select,
.cm-popup__box td textarea {
  width: 100%;
  box-sizing: border-box;
}

/* Unclassed body headings only: a classed heading (What's New release
   titles, a .cm-section-title) keeps its own look, which this rule would
   beat (a legacy rule by layer, a component class by specificity). */
.cm-popup__body h1:not([class]) {
  font-size: var(--cm-text-2xl);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  margin: 0;
}

.cm-popup__body h2:not([class]) {
  font-size: var(--cm-text-xl);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  margin: 0 0 var(--cm-space-3);
}

.cm-popup__body h3:not([class]) {
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  margin: 0 0 var(--cm-space-2);
}

/* Read-only label and value rows (esMD Submission Detail, Additional Fax
   Info), built by buildPopupDetails() in common/popup-shell.js: bold row
   headers on one line, values beside them. The label column shrinks to
   its widest label (width: 1% with nowrap), so short values start right
   after it instead of the spare width splitting between the columns. The
   row header inherits the value's alignment rather than the user agent's
   centered th. */
.cm-popup__details {
  width: 100%;
  border-spacing: 0 var(--cm-space-1);
}

.cm-popup__details-label {
  width: 1%;
  padding-right: var(--cm-space-3);
  font-weight: var(--cm-fw-bold);
  text-align: inherit;
  white-space: nowrap;
}

/* Request Details (common/popups.js) keeps a width floor so switching its
   tabs does not resize the popup, capped to the overlay (right of the
   sidenav rail) with a small margin on each side, as wide and full are. A
   feature rule kept here: a legacy-layer rule cannot beat .cm-popup__box's
   min-width. */
.cm-popup__box.request-details-popup {
  min-width: min(
    calc(960px + 2 * var(--cm-space-6) + 2px),
    calc(100vw - var(--sidebar-width) - 2 * var(--cm-space-4))
  );
}

/* Edit Facility (features/facilities/facilities.html) gains the same
   floor: switching tabs stops resizing and re-centering the popup.
   Measured at 1440 with an e2e run after the tabs-component rebuild (each
   tab's own content width, floor disabled): Details 1230px (the widest),
   Additional Features 997px, Users 976px, Locations 1056px. Details'
   1230px is exactly .cm-popup--wide's own max-width cap below (its box
   already carries cm-popup--wide), not a coincidence of its content: this
   floor is written as that same expression so the two pin the box at the
   --wide cap together and cannot drift apart if --cm-space-6 changes. */
.cm-popup__box.facility-edit-popup {
  min-width: min(
    calc(1180px + 2 * var(--cm-space-6) + 2px),
    calc(100vw - var(--sidebar-width) - 2 * var(--cm-space-4))
  );
}

/* What's New (common/whats-new.js) puts each release section straight in
   the body; it spans it, as it did before the popup moved onto this
   component, rather than shrinking to its content and centering. Kept here
   beside the body rule it adjusts; the release's own rules are
   components/whats-new.css. */
.cm-popup__body > .whats-new__release {
  align-self: stretch;
}

}
@layer components {
/*
 * Drawer (CSS consolidation phase 4): a fixed panel at the edge of the page
 * that leaves the rest of the page usable (no backdrop). Spec:
 * docs/specs/2026-09-30-css-phase-4-popups-design.md
 * Markup:
 *   div.cm-drawer.cm-drawer--bottom|--right[.cm-drawer--chrome] >
 *     header.cm-drawer__header > h2.cm-drawer__title, button.cm-drawer__close
 *     div.cm-drawer__body
 * Hidden until the page shows it (style.display = "flex").
 */

.cm-drawer {
  display: none;
  flex-direction: column;
  position: fixed;
  z-index: var(--cm-z-page-drawer);
  box-sizing: border-box;
  background-color: var(--cm-bg-surface);
  color: var(--cm-text-body);
  box-shadow: var(--cm-shadow-lg);
}

/* Bottom sheet across the page, right of the sidenav rail. */
.cm-drawer--bottom {
  inset: auto 0 0 var(--sidebar-width);
  min-height: 500px;
  max-height: 60vh;
  border-top: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-xl) var(--cm-radius-xl) 0 0;
}

/* Side panel against the right edge, sized by its content. */
.cm-drawer--right {
  top: var(--cm-space-5);
  right: 0;
  max-height: calc(100vh - 2 * var(--cm-space-5));
  border-radius: var(--cm-radius-xl) 0 0 var(--cm-radius-xl);
}

/* Pinned dark chrome, like the PDF viewers (owner decision 2026-09-30). */
.cm-drawer--chrome {
  background-color: var(--cm-bg-chrome);
  color: var(--cm-text-on-dark);
}

.cm-drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cm-space-3);
  padding: var(--cm-space-4) var(--cm-space-4) var(--cm-space-3) var(--cm-space-6);
  border-bottom: 1px solid var(--cm-border);
}

.cm-drawer--chrome > .cm-drawer__header {
  border-bottom-color: var(--cm-border-on-dark);
}

.cm-drawer__title {
  margin: 0;
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  line-height: 1.3;
  color: var(--cm-text-heading);
}

.cm-drawer--chrome .cm-drawer__title {
  color: var(--cm-text-on-dark);
}

/* Same 32px bordered square as the popup's close. */
.cm-drawer__close {
  flex-shrink: 0;
  box-sizing: border-box;
  width: 32px;
  height: 32px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: transparent;
  border: 1px solid var(--cm-border-strong);
  border-radius: var(--cm-radius-md);
  color: var(--cm-text-body);
  font-size: var(--cm-text-lg);
  line-height: 1;
  cursor: pointer;
  transition:
    background-color 120ms ease,
    border-color 120ms ease,
    color 120ms ease;
}

.cm-drawer__close:hover,
.cm-drawer__close:focus-visible {
  border-color: var(--cm-interactive);
  background-color: var(--cm-chip-selected-bg);
  color: var(--cm-interactive);
}

.cm-drawer__close:focus-visible {
  outline: none;
  box-shadow: var(--cm-focus-ring);
}

.cm-drawer--chrome .cm-drawer__close {
  border-color: transparent;
  color: var(--cm-text-on-dark);
}

.cm-drawer--chrome .cm-drawer__close:hover,
.cm-drawer--chrome .cm-drawer__close:focus-visible {
  border-color: transparent;
  background-color: var(--cm-bg-on-dark-hover);
  color: var(--cm-text-on-dark);
}

.cm-drawer__body {
  flex: 1 1 auto;
  min-height: 0;
  padding: var(--cm-space-4) var(--cm-space-6) var(--cm-space-6);
  overflow-y: auto;
}

}
@layer components {
/*
 * Menu (CSS consolidation phase 4, PR 3). Spec:
 * docs/specs/2026-09-30-css-phase-4-popups-design.md
 * Markup:
 *   div.cm-menu[--hover|--list|--fixed|--modal|--wrap][role=menu|listbox] >
 *     button.cm-menu__item[--danger|--active], div.cm-menu__divider
 * Rows are separated (CSS phase 5.5 restyle, docs/specs/
 * 2026-10-12-css-phase-5-5-restyle-design.md): the card has no inner
 * padding, a hairline runs between adjacent rows, and a hovered, focused or
 * --active row takes the selected tint with a 3px edge on its left.
 * Row and toolbar Actions menus come from common/menu.js buildActionsMenu().
 * A menu shows unless it has the hidden attribute, except --hover menus,
 * which show only while their .actions-dropdown wrapper is hovered or
 * holds focus, and not after Escape dismissed it (wireHoverMenu sets
 * data-menu-dismissed on the wrapper until the pointer or focus comes back
 * in). An autocomplete list (--list) is a div or a ul whose rows
 * (div or li, role=option) keep their own element.
 */

.cm-menu {
  position: absolute;
  top: calc(100% + var(--cm-space-2));
  right: 0;
  z-index: var(--cm-z-overlay-menu);
  display: flex;
  flex-direction: column;
  min-width: 180px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  /* Clips the square rows' tints to the card's corners. --list and
     --popover scroll on y. */
  overflow: hidden;
  list-style: none;
  background-color: var(--cm-bg-surface);
  color: var(--cm-text-body);
  border: 1px solid var(--cm-menu-border);
  border-radius: var(--cm-radius-lg);
  box-shadow: var(--cm-shadow-lg);
  text-align: left;
}

/* Revealed by its .actions-dropdown wrapper (the trigger's wrapper,
   position: relative), and flush against it so the pointer can travel from
   trigger to menu. Sits at --cm-z-overlay (100, as the legacy Actions menu
   did), not --cm-z-overlay-menu, so a popup opened from one of its items
   (also z 100, appended later) covers it. */
.cm-menu--hover {
  display: none;
  top: 100%;
  z-index: var(--cm-z-overlay);
}

/* :not([hidden]) keeps a menu that buildActionsMenu set hidden (no
   surviving items) hidden even while its wrapper is hovered or focused.
   Qualified with .actions-dropdown (the only wrapper class that carries a
   --hover menu) rather than a bare :hover/:focus-within, which would be
   enough on every staff page that declares a doctype, but not on one that
   doesn't: a doctype-less page (requests.html today; see tests/component/
   menu.spec.mjs's no-doctype test) renders in quirks mode, where the
   legacy HTML ":hover quirk" makes a compound selector made only of
   :hover (an implicit *:hover, which is what a bare ancestor-combinator
   :hover is) never match a non-link, non-classed element. A selector
   qualified with a class, like .actions-dropdown:hover, is exempt from
   that quirk and matches correctly either way, so qualifying is the
   robust choice regardless of which pages currently lack a doctype.
   :not([data-menu-dismissed]): Escape closed it (wireHoverMenu in
   _shared/components/menu.js), and it stays closed while the pointer and
   focus that were there stay (WCAG 1.4.13). */
.actions-dropdown:not([data-menu-dismissed]):hover > .cm-menu--hover:not([hidden]),
.actions-dropdown:not([data-menu-dismissed]):focus-within > .cm-menu--hover:not([hidden]) {
  display: flex;
}

/* An autocomplete list under its input's wrapper (position: relative).
   common/menu.js placeListUnder() overrides left, top and min-width inline
   when the wrapper is not directly under the input. */
.cm-menu--list {
  top: calc(100% + var(--cm-space-1));
  left: 0;
  right: auto;
  min-width: 100%;
  max-height: 220px;
  overflow-y: auto;
}

/* Rows wrap instead of overflowing past the menu's width when a row's
   text is too long for one line (the Patient Alert facility search, where
   a long facility name must stay readable rather than push the row's
   trailing code past the card's edge). white-space is inherited by the
   row's child spans. */
.cm-menu--wrap .cm-menu__item {
  white-space: normal;
}

/* Placed at a point by JS (style.left/top): the right-click menu and the QA
   hotkeys popover. Above the sidenav. */
.cm-menu--fixed {
  position: fixed;
  top: auto;
  right: auto;
  z-index: var(--cm-z-context-menu);
}

/* A popover card of text rather than items (the hotkeys list opened from a
   page header's keyboard icon, common/header-tools.js): a --fixed --modal
   menu with a padded __body that scrolls when the list is long. */
.cm-menu--popover {
  width: 320px;
  max-height: 70vh;
  overflow-y: auto;
}

.cm-menu__body {
  padding: var(--cm-space-4);
}

.cm-menu__body p {
  margin: 2px 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
}

.cm-menu__body kbd {
  padding: 1px 4px;
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-sm);
  background: var(--cm-bg-surface);
  color: var(--cm-text-muted);
  font-size: var(--cm-text-xs);
}

/* A --fixed menu that must stay below the session-timeout box
   (--cm-z-session, 1500), e.g. the QA hotkeys popover. */
.cm-menu--modal {
  z-index: var(--cm-z-modal);
}

.cm-menu__item {
  display: flex;
  align-items: center;
  gap: var(--cm-space-2);
  width: 100%;
  min-height: 36px;
  box-sizing: border-box;
  margin: 0;
  padding: 0 var(--cm-space-3);
  border: 0;
  border-radius: 0;
  background-color: transparent;
  color: var(--cm-text-body);
  font-size: var(--cm-text-sm);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

/* An item's leading icon (addMenuItems' `icon`): a fixed-width slot so the
   labels line up, in the item's own color. color: inherit is load-bearing
   in the PDF viewer, where pdf_viewer.css's .actionBar i { color: white }
   (injected into the legacy layer by the editor) would otherwise paint the
   PdfActions icons white on the white card. */
.cm-menu__item > i {
  flex: none;
  width: 1.25em;
  color: inherit;
  text-align: center;
}

/* A hairline on a row that follows a visible row: a list that filters by
   hiding rows (CustomDropdown) must not draw one under the frame's top
   edge. None right under a divider. */
.cm-menu__item:not([hidden]) ~ .cm-menu__item {
  border-top: 1px solid var(--cm-menu-separator);
}

.cm-menu__divider + .cm-menu__item:not([hidden]) {
  border-top: 0;
}

.cm-menu__item:hover,
.cm-menu__item:focus-visible,
.cm-menu__item--active {
  background-color: var(--cm-chip-selected-bg);
  color: var(--cm-text-heading);
  box-shadow: var(--cm-menu-edge);
}

.cm-menu__item:focus-visible {
  outline: 2px solid var(--cm-interactive);
  outline-offset: -2px;
}

.cm-menu__item--danger {
  color: var(--cm-danger-text);
}

.cm-menu__item--danger:hover,
.cm-menu__item--danger:focus-visible,
.cm-menu__item--danger.cm-menu__item--active {
  background-color: var(--cm-bg-danger-soft);
  color: var(--cm-danger-text);
  box-shadow: var(--cm-menu-edge-danger);
}

.cm-menu__divider {
  flex: none;
  height: 1px;
  margin: 0;
  background-color: var(--cm-border-strong);
}

/* Menu anchors (moved from stylesheetv3.css, CSS phase 5): the wrapper a
   --hover menu hangs from, and the wrapper an input's --list sits in. */
.actions-dropdown {
  position: relative;
  display: inline-block;
}

.input-dropdown {
  display: flex;
  margin: 0;
  padding: 0;
  position: relative;
}

}
@layer components {
/*
 * Field (CSS consolidation phase 4, forms PR 1). Spec:
 * docs/specs/2026-10-05-css-phase-4-forms-design.md
 * Markup:
 *   div.cm-field > label.cm-field__label (+ span.cm-field__required),
 *     control, p.cm-field__hint?, p.cm-field__error?
 *   label.cm-field > span.cm-field__label (+ span.cm-field__required) +
 *     control (wrapping shape; patient alerts)
 *   label.cm-field.cm-field--check[.cm-field--plain] >
 *     input[type=checkbox|radio] + text
 *   span.cm-field__help-row > (label.cm-field__label | label.cm-field--check)
 *     + span.cm-help-icon (addFieldHelp(); in a div.cm-field the row takes
 *     the label's place, before the control)
 * A checkbox row is a chip (CSS phase 5.5 restyle, docs/specs/
 * 2026-10-12-css-phase-5-5-restyle-design.md); with a help icon the
 * help row is the chip. Radio rows and --plain rows (long checklists, the
 * report builder's) stay plain.
 *   div.cm-field > span.cm-field__label#id + div.cm-field__choices[--stacked]
 *     [role=group][aria-labelledby=id] > one label.cm-chip-toggle--pill per
 *     choice (buildPillChip(); e.g. the Customer popup's Customer Type
 *     picker, stacked in its own column)
 * The control's own look (outlined, --cm-control-height) comes from
 * base.css; nothing here restates it.
 */

.cm-field {
  display: flex;
  flex-direction: column;
  gap: var(--cm-field-gap);
  min-width: 0;
}

/* Excludes checkbox/radio: they keep base.css's 18px square (components
   always outranks base by layer, so even "width: auto" here would beat it;
   see .cm-field--check > input below, which only resets their margin). The
   exclusion lives inside :where() so it costs no specificity (a bare :not()
   here would out-rank the other rules in this file for no reason). */
.cm-field > :where(input:not([type='checkbox'], [type='radio']), select, textarea) {
  width: 100%;
}

/* A tight line box (CSS phase 5.5 restyle) so the label sits about 4px
   above its control rather than 7px. Size, color, case and tracking come
   from tokens: small caps here, sentence case on the portal (pages/portal.css). */
.cm-field__label {
  font-size: var(--cm-field-label-size);
  line-height: var(--cm-lh-tight);
  font-weight: var(--cm-fw-medium);
  color: var(--cm-field-label-color);
  text-transform: var(--cm-field-label-case);
  letter-spacing: var(--cm-field-label-tracking);
  text-align: left;
}

.cm-field__required {
  margin-left: 2px;
  color: var(--cm-danger-text);
}

.cm-field__hint {
  margin: 0;
  padding: 0;
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}

.cm-field__error {
  margin: 0;
  padding: 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-danger-text);
}

/* A checkbox or radio with its option text: the text is a choice, not a
   field label, so it stays sentence case body text. Declares its own case,
   tracking and weight rather than inheriting them, so a --check label
   dropped inside a legacy uppercase label context (e.g.
   a search bar field) is not left shouting. */
.cm-field--check {
  flex-direction: row;
  align-items: center;
  gap: var(--cm-space-2);
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
  text-transform: none;
  letter-spacing: normal;
  font-weight: var(--cm-fw-regular);
  cursor: pointer;
  user-select: none;
}

.cm-field--check > input {
  margin: 0;
  /* Option text can wrap to two or more lines (narrow containers, long
     option copy); without this the checkbox is a flex item with the
     default flex-shrink: 1 and shrinks along with the row, collapsing
     well below its 18px square (base.css) instead of staying put beside
     the wrapped text. */
  flex-shrink: 0;
}

/* The check chip: a pill at the field height around a 14px box and the
   option text in the field label's small caps. A row with a help icon
   (.cm-field__help-row) is the chip instead, its label plain inside it and
   the icon beside the label. width: fit-content keeps a chip at its text in
   a stretching column, so blank space beside it is not a click target.
   The :where() keeps these at one class of specificity. */
.cm-field--check:where(
  :not(.cm-field--plain, .cm-field__help-row > *):has(> input[type='checkbox'])
),
.cm-field__help-row:where(:has(> .cm-field--check:not(.cm-field--plain) > input[type='checkbox'])) {
  box-sizing: border-box;
  width: fit-content;
  max-width: 100%;
  min-height: var(--cm-control-height);
  padding: 2px 10px;
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-pill);
  background: var(--cm-bg-surface);
  transition:
    background-color 120ms ease,
    border-color 120ms ease;
}

.cm-field--check:where(:not(.cm-field--plain):has(> input[type='checkbox'])) {
  gap: 6px;
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-medium);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.cm-field--check:where(:not(.cm-field--plain)) > input[type='checkbox'] {
  width: 14px;
  height: 14px;
}

.cm-field__help-row:where(:has(> .cm-field--check:not(.cm-field--plain) > input[type='checkbox'])) {
  gap: 0;
  padding: 0 var(--cm-space-1) 0 0;
}

/* ...and its label takes the chip's padding and full height, so the whole
   pill but the icon toggles the box, as a plain chip does. */
.cm-field__help-row > .cm-field--check:where(:not(.cm-field--plain):has(> input[type='checkbox'])) {
  align-self: stretch;
  padding: 0 6px 0 10px;
}

/* The help icon (components/help-icon.css, its [data-tooltip] chip) is
   spaced by the label's own padding, not a margin. */
.cm-field__help-row:has(> .cm-field--check) > [data-tooltip] {
  margin-left: 0;
}

/* Hover sits inside :where() too, so a checked chip keeps its border. */
.cm-field--check:where(
  :not(.cm-field--plain, .cm-field__help-row > *):has(> input[type='checkbox']):hover
),
.cm-field__help-row:where(
  :has(> .cm-field--check:not(.cm-field--plain) > input[type='checkbox']):hover
) {
  border-color: var(--cm-border-strong);
}

.cm-field--check:where(
  :not(.cm-field--plain, .cm-field__help-row > *):has(> input[type='checkbox']:checked)
),
.cm-field__help-row:where(
  :has(> .cm-field--check:not(.cm-field--plain) > input[type='checkbox']:checked)
) {
  border-color: var(--cm-chip-selected-border);
  background: var(--cm-chip-selected-bg);
  color: var(--cm-text-heading);
}

.cm-field--check:where(:not(.cm-field--plain):has(> input[type='checkbox']:checked)) {
  color: var(--cm-text-heading);
}

.cm-field--check:where(
  :not(.cm-field--plain, .cm-field__help-row > *):has(> input[type='checkbox']:disabled)
),
.cm-field__help-row:where(
  :has(> .cm-field--check:not(.cm-field--plain) > input[type='checkbox']:disabled)
) {
  opacity: 0.55;
  cursor: not-allowed;
}

.cm-field--check:where(:has(> input:disabled)) {
  cursor: not-allowed;
}

/* Pairs a field's label, or a whole check row, with an inline
   .cm-help-icon (components/help-icon.css) on one line. Built by
   addFieldHelp() in dom-helpers.js, which keeps the icon a sibling of the
   label/check row rather than a descendant of it (see that function's doc
   for why). */
.cm-field__help-row {
  display: inline-flex;
  align-items: center;
  gap: var(--cm-space-1);
}

/* A field's control replaced by a row of choice chips (role=group; e.g.
   the Customer popup's Customer Type picker, buildPillChip() per choice):
   wraps instead of overflowing, rather than restating chip-toggle.css's
   own layout. */
.cm-field__choices {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cm-space-2);
}

/* One choice per line, each at its own width (the Customer popup's
   Customer Type column, CSS phase 5.5 restyle). */
.cm-field__choices--stacked {
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: 6px;
}

/* A control a page's own check rejected (Invoice's SSA Site Code): the page
   sets aria-invalid="true" on it. */
.cm-field [aria-invalid='true'] {
  box-shadow: var(--cm-shadow-invalid);
}

}
@layer components {
/*
 * Switch (CSS phase 5.5 restyle, docs/specs/
 * 2026-10-12-css-phase-5-5-restyle-design.md; was the task popup's
 * .task-switch). Markup:
 *   button.cm-switch[type=button][role=switch][aria-checked] >
 *     span.cm-switch__knob[aria-hidden=true]
 *   label.cm-switch__label[for=<the switch's id>] (optional, beside it: it
 *     names the switch and a click on it toggles it)
 * aria-checked is the state; common/switch.js reads, sets and wires it
 * (isSwitchOn, setSwitch, wireSwitch). Without a label the switch needs an
 * aria-label.
 */

.cm-switch {
  position: relative;
  flex-shrink: 0;
  width: 42px;
  height: 24px;
  padding: 0;
  border: 0;
  border-radius: var(--cm-radius-pill);
  /* gray-500/border-strong (not gray-300): the off track needs 3:1 against
     the surface (WCAG non-text contrast); gray-300 measured ~1.2:1 in light
     mode. */
  background: var(--cm-border-strong);
  cursor: pointer;
  transition: background 120ms ease;
}

/* In dark, --cm-border-strong is too close to the knob; the off track
   takes the light gray-700 instead (the knob turns dark there). */
:root.dark .cm-switch:not([aria-checked='true']) {
  background: var(--cm-gray-700);
}

.cm-switch[aria-checked='true'] {
  background: var(--cm-interactive);
}

.cm-switch__knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 18px;
  height: 18px;
  border-radius: var(--cm-radius-pill);
  /* --cm-text-on-interactive flips light/dark for AA against the on-state
     track (already true for buttons); reusing it here also fixes the
     off-state track, which in dark mode is a near-white gray-700 that a
     white knob could not distinguish itself from (~1.2:1). */
  background: var(--cm-text-on-interactive);
  box-shadow: var(--cm-shadow-switch-knob);
  transition: left 120ms ease;
}

.cm-switch[aria-checked='true'] .cm-switch__knob {
  left: 21px;
}

.cm-switch:focus-visible {
  outline: 2px solid var(--cm-interactive);
  outline-offset: 2px;
}

.cm-switch:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.cm-switch__label {
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
  color: var(--cm-text-heading);
  cursor: pointer;
  user-select: none;
}

.cm-switch:disabled + .cm-switch__label {
  color: var(--cm-text-muted);
  cursor: not-allowed;
}

}
@layer components {
/*
 * Date filter (CSS consolidation phase 4, forms PR 1). Moved verbatim from
 * stylesheetv3.css (the comment below is original to that rule block).
 * Spec: docs/specs/2026-10-05-css-phase-4-forms-design.md
 */

/* Compact date-range filter used inside a .cm-search-bar__field--wide. The
   layout is: operator dropdown (fixed width) + date input (fills
   remaining space) for the on/before/after modes, or two date inputs
   with a separator for the between mode. JS toggles which inner div
   is visible based on the operator. */
.cm-date-filter {
  display: flex;
  gap: var(--cm-space-2);
  align-items: stretch;
  width: 100%;
}
.cm-date-filter__op {
  flex: 0 0 auto;
  width: 110px;
}
.cm-date-filter__single {
  flex: 1 1 auto;
  min-width: 140px;
}
.cm-date-filter__single > input[type='date'] {
  width: 100%;
  box-sizing: border-box;
}
.cm-date-filter__between {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  gap: var(--cm-space-2);
  align-items: center;
}
.cm-date-filter__between > input[type='date'] {
  flex: 1 1 0;
  /* The date input's intrinsic content width is the rendered MM/DD/YYYY
     label + calendar icon (~130px on Chromium). min-width: 0 lets flex
     squeeze below that and the input collapses to just the icon, so
     pin a floor that fits the rendered date text. */
  min-width: 130px;
  box-sizing: border-box;
}
.cm-date-filter__sep {
  color: var(--cm-text-muted);
  flex: 0 0 auto;
  padding: 0 var(--cm-space-1);
}

}
@layer components {
/*
 * Help icon (CSS consolidation phase 4, forms PR 1). Spec:
 * docs/specs/2026-10-05-css-phase-4-forms-design.md
 * Markup:
 *   span.cm-help-icon.cm-tooltip.cm-tooltip--fixed[tabindex=0][role=button]
 *     [aria-label][data-tooltip] "?"
 * A small "?" chip whose data-tooltip text shows on hover or keyboard focus
 * in the shared bubble (components/tooltip.css, --fixed, placed by the
 * positioner in common/common.js). This file owns only the chip. Beside a
 * field label or check row it sits in a .cm-field__help-row (addFieldHelp()
 * in common/dom-helpers.js).
 */

.cm-help-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* As tall as the text beside it (CSS phase 5.5 restyle review): a field
     label's 14.4px line box, a check chip's small caps, a 14px card or
     esMD title. A popup title restates it larger (popup.css). */
  box-sizing: border-box;
  flex-shrink: 0;
  width: 14px;
  height: 14px;
  margin-left: var(--cm-space-1);
  border-radius: var(--cm-radius-pill);
  /* A muted-but-visible chip so the affordance reads as "click for help"
     instead of disappearing into the card background. */
  background: var(--cm-bg-muted);
  color: var(--cm-text-body);
  border: 1px solid var(--cm-border);
  font-size: var(--cm-font-size-help-glyph);
  font-weight: var(--cm-fw-semibold);
  line-height: 1;
  cursor: help;
  position: relative;
  user-select: none;
}

/* A touch target (WCAG 2.2 AA 2.5.8, 24px): the customer pages, which
   patients use on phones. Same chip, sized to the minimum target. */
.cm-help-icon--touch {
  width: 24px;
  height: 24px;
}

.cm-help-icon:hover,
.cm-help-icon:focus-visible {
  background: var(--cm-bg-surface);
  color: var(--cm-interactive);
  border-color: var(--cm-interactive);
  outline: none;
}

/* Inside an element that shows its own data-tooltip bubble (a locked
   control explaining why it is locked), the icon stands down so the two
   bubbles never stack. */
[data-tooltip] .cm-help-icon {
  pointer-events: none;
}

}
@layer components {
/*
 * Toggle chip (CSS consolidation phase 4, forms PR 2). Spec:
 * docs/specs/2026-10-05-css-phase-4-forms-design.md
 * Markup:
 *   label.cm-chip-toggle > input[type=checkbox] + span (+ i.fa-lock)
 *   button.cm-chip-toggle (a chip with no checkbox, e.g. the esMD status)
 *   label.cm-chip-toggle.cm-chip-toggle--pill > input[type=checkbox]
 *     + span.cm-chip-toggle__check[aria-hidden] + span   (buildPillChip())
 * One on state for every chip in both themes: the accent tint and border
 * (--cm-chip-selected-bg/--cm-chip-selected-border, tokens.css). Those
 * tokens re-resolve under :root.dark through --cm-accent-ring, so no
 * dark override lives here.
 * --locked: forced by another control (e.g. the delivery method); the
 * reason shows in the .cm-tooltip bubble the chip already carries.
 * --in-field: the chip sits inside a field's right edge (the fax Override
 * limit chip in .address-input-wrap, which is the positioned wrapper).
 */

.cm-chip-toggle {
  display: flex;
  align-items: center;
  gap: var(--cm-space-1);
  padding: 1px 6px;
  border-radius: var(--cm-radius-sm);
  background: var(--cm-bg-muted);
  border: 1px solid var(--cm-border);
  color: var(--cm-text-body);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-regular);
  text-transform: none;
  letter-spacing: normal;
  /* 18px tall in all: fits inside a 24px field with 3px to spare. */
  line-height: 14px;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}

button.cm-chip-toggle {
  font-family: inherit;
  appearance: none;
  margin: 0;
}

.cm-chip-toggle input[type='checkbox'] {
  width: 11px;
  height: 11px;
  margin: 0;
}

.cm-chip-toggle:has(input:checked),
.cm-chip-toggle:has(input:indeterminate) {
  border-color: var(--cm-chip-selected-border);
  background: var(--cm-chip-selected-bg);
}

.cm-chip-toggle .fa-lock {
  display: none;
  /* Exactly 10px (0.625rem), matching the legacy lock icon size. Written
     as calc() against --cm-text-base rather than a literal 0.625rem so
     the raw-value guardrail (tests/unit/frontend/stylesheets/rules.mjs)
     still sees a --cm-* token, not a bare font-size literal. */
  font-size: calc(var(--cm-text-base) * 0.625);
}

/* Centered by its insets, not a transform: a transform would make the chip
   the containing block of its --fixed tooltip bubble, which then lands
   off the chip and widens the scrolling box around it (the Invoice rail). */
.cm-chip-toggle--in-field {
  position: absolute;
  top: 0;
  bottom: 0;
  right: var(--cm-space-1);
  height: fit-content;
  margin-block: auto;
}

.cm-chip-toggle--locked {
  opacity: 0.55;
  cursor: not-allowed;
}

.cm-chip-toggle--locked .fa-lock {
  display: inline;
}

/* Pill: a rounded chip at the field height. The real checkbox is hidden
   but not display: none, so Tab reaches it and Space toggles it. */
.cm-chip-toggle--pill {
  position: relative;
  display: inline-flex;
  /* The base .cm-chip-toggle rule's 1px border is content-box by default
     on a label; without border-box, height: var(--cm-control-height)
     renders 2px taller than the field height it's meant to match. */
  box-sizing: border-box;
  gap: var(--cm-space-2);
  height: var(--cm-control-height);
  padding: 0 var(--cm-space-3);
  border-radius: var(--cm-radius-pill);
  background: var(--cm-bg-surface);
  font-size: var(--cm-text-sm);
  /* No line-height override here: the base .cm-chip-toggle rule's 14px
     already fits inside the pill's 22px content box (24px border-box
     minus the 1px top/bottom border); the pill's own height (set above)
     drives its 24px, not line-height, and align-items: center (also from
     the base rule) keeps the text and check mark centered regardless. */
}

.cm-chip-toggle--pill input[type='checkbox'] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.cm-chip-toggle--pill:hover {
  border-color: var(--cm-border-strong);
}

.cm-chip-toggle--pill:has(input:focus-visible) {
  box-shadow: var(--cm-focus-ring);
}

.cm-chip-toggle--pill:has(input:checked),
.cm-chip-toggle--pill:has(input:indeterminate) {
  color: var(--cm-text-heading);
  font-weight: var(--cm-fw-medium);
}

.cm-chip-toggle__check {
  display: none;
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E")
    center / 14px 14px no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 8.5l3 3 6-7'/%3E%3C/svg%3E")
    center / 14px 14px no-repeat;
}

.cm-chip-toggle--pill:has(input:checked) .cm-chip-toggle__check,
.cm-chip-toggle--pill:has(input:indeterminate) .cm-chip-toggle__check {
  display: block;
}

.cm-chip-toggle--pill:has(input:indeterminate) .cm-chip-toggle__check {
  -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M3.5 8h9'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M3.5 8h9'/%3E%3C/svg%3E");
}

/* High Contrast Mode / forced colors: the check mark is a masked
   background (currentColor), which a forced-colors palette can otherwise
   wash out to the same color as the chip face. Pin it to the system
   CanvasText color and opt the chip out of further forced-colors repaint,
   and give a checked/indeterminate pill a system Highlight border so it
   reads as selected without relying on the (also forced-colors-flattened)
   accent tint. System color keywords, not literals: the tokens guardrail
   only forbids the standard CSS named colors. */
@media (forced-colors: active) {
  .cm-chip-toggle__check {
    forced-color-adjust: none;
    background: CanvasText;
  }

  .cm-chip-toggle--pill:has(input:checked),
  .cm-chip-toggle--pill:has(input:indeterminate) {
    border-color: Highlight;
  }
}

/* The positioned field wrap a --in-field chip sits in (moved from
   stylesheetv3.css, CSS phase 5). Fax-limit override: in-input adornment chip.
   DeliveryPanel's overrideFaxRow/overrideFaxCheckbox render as a small
   checkbox chip inside the right edge of the delivery-address input on
   Upload Records, Manual Upload, Payer Audits and the Resend popup (the
   Invoice page keeps its chip beside the Deliver To label instead). The
   panel only toggles the chip's hidden attribute on method change; the
   wrapper stays in the layout permanently. */
.address-input-wrap {
  position: relative;
  display: block;
  width: 100%;
}
.address-input-wrap input {
  width: 100%;
  margin: 0;
  box-sizing: border-box;
}
/* Room for the chip only while one shows in the wrap (fax): reserved
   always, it cut a long address off with empty space beside it on every
   other method and on the Invoice page, where no chip sits in the field
   (Automate-oyth). rem (7rem = 112px at the default root size) so a user
   font-size preference scales the room along with the chip text.
   This is the documented exception to "no component or page restates a
   field's border, background, radius, padding or height" (global
   constraints, forms PR 1): a partial padding override to make room for
   the fax-override chip, not a restatement of the field's look. */
.address-input-wrap:has(> .cm-chip-toggle--in-field:not([hidden])) input {
  padding-right: 7rem;
}

}
@layer components {
/*
 * Locked control (CSS consolidation phase 4, forms PR 2): a wrapper for
 * a temporarily unavailable control that is not a chip (Upload Records'
 * Deliver Now row). Add .cm-tooltip and data-tooltip with it so the shared
 * bubble explains why. A help icon inside stands down while it is locked
 * (components/help-icon.css). Chips use .cm-chip-toggle--locked instead.
 */

.cm-locked-control {
  position: relative;
  opacity: 0.55;
  cursor: not-allowed;
}

}
@layer components {
/*
 * Segmented control (CSS consolidation phase 4, forms PR 2 and tabs). Spec:
 * docs/specs/2026-10-05-css-phase-4-forms-design.md,
 * docs/specs/2026-10-09-css-phase-4-tabs-design.md
 * Markup: div.cm-segmented[role=radiogroup][aria-label] >
 *   button.cm-segmented__option[role=radio][aria-checked] (buildRadioGroup()
 *   in public/_shared/radio-group.js: roving tabindex, arrow keys), or
 *   div.cm-segmented[role=tablist][aria-label] >
 *   button.cm-segmented__option[role=tab][aria-selected] (QA's stage tabs,
 *   a roving tabIndex and arrow keys of its own, no shared helper).
 * --danger: the picked fill is the danger color (a release restriction).
 */

.cm-segmented {
  display: inline-flex;
  width: max-content;
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-md);
  overflow: hidden;
}

.cm-segmented__option {
  /* The container's own 1px top/bottom border (2px total) adds to this, so
     the option is sized 2px short of --cm-control-height to keep the whole
     control (container + border) at --cm-control-height, not 2px taller. */
  height: calc(var(--cm-control-height) - 2px);
  margin: 0;
  padding: 0 var(--cm-space-4);
  border: 0;
  border-left: 1px solid var(--cm-border);
  /* Fix round 1, Minor 6: an explicit 0 here (the components layer) beats
     the legacy [tabindex]:focus-visible rule's border-radius: var(--cm-
     radius-sm), which would otherwise round a keyboard-focused option's
     corners (a roving-tabIndex control, such as QA's stage switch, sets
     the tabindex attribute, so that legacy selector matches). Without a
     border-radius declaration of its own, this rule has nothing to win
     with for that property, so the legacy layer's conflicting value shows
     through regardless of layer order. */
  border-radius: 0;
  background: var(--cm-bg-surface);
  color: var(--cm-text-body);
  font: inherit;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
  cursor: pointer;
}

.cm-segmented__option:first-child {
  border-left: 0;
}

.cm-segmented__option:hover:not([aria-checked='true'], [aria-selected='true']) {
  background: var(--cm-bg-muted);
}

.cm-segmented__option:focus-visible {
  position: relative;
  z-index: var(--cm-z-raised);
  outline: none;
  box-shadow: var(--cm-focus-ring);
}

.cm-segmented__option:is([aria-checked='true'], [aria-selected='true']) {
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
}

.cm-segmented__option--danger[aria-checked='true'] {
  background: var(--cm-danger-700);
  color: var(--cm-text-on-dark);
}

/* A full-width switch (QA's stage tabs): equal options at the button
   height. The container's 1px top and bottom border add to the option, as
   in the base rule. Focus is drawn inside the option, since the container
   clips (overflow: hidden), and in the label color on the picked option. */
.cm-segmented--block {
  display: flex;
  width: 100%;
  /* content-box (the base rule's inline-flex default) would add the
     container's own 1px left/right border on top of the 100% width,
     running the switch 2px past its card. */
  box-sizing: border-box;
}

.cm-segmented--block .cm-segmented__option {
  flex: 1 1 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--cm-space-2);
  height: calc(var(--cm-button-height) - 2px);
}

.cm-segmented--block .cm-segmented__option:focus-visible {
  box-shadow: none;
  outline: 2px solid var(--cm-interactive);
  outline-offset: -4px;
}

.cm-segmented--block
  .cm-segmented__option:is([aria-checked='true'], [aria-selected='true']):focus-visible {
  outline-color: var(--cm-text-on-interactive);
}

/* A count inside an option ("2 left"): warning text at rest, the label
   color on the picked option (warning on teal would be 1.30:1). */
.cm-segmented__count {
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-warning-text);
}

.cm-segmented__option:is([aria-checked='true'], [aria-selected='true']) .cm-segmented__count {
  color: inherit;
}

}
@layer components {
/*
 * File drop zone (CSS consolidation phase 4, forms PR 3). Spec:
 * docs/specs/2026-10-05-css-phase-4-forms-design.md
 * Markup (buildDropZone() in common/drop-zone.js, or static HTML):
 *   div.cm-drop-zone__group >
 *     label.cm-drop-zone > input.cm-drop-zone__input[type=file],
 *       i.cm-drop-zone__icon, span.cm-drop-zone__text > span.cm-drop-zone__browse,
 *       span.cm-drop-zone__hint
 *     p.cm-drop-zone__count[role=status]
 *     ul.cm-drop-zone__files > li.cm-drop-zone__file.cm-tag.cm-tag--removable[data-state] >
 *       span.cm-drop-zone__file-name, span.cm-drop-zone__file-status,
 *       button.cm-drop-zone__file-remove.cm-tag__remove
 *   or, with options inside the frame (buildDropZone's footer):
 *     div.cm-drop-zone.cm-drop-zone--options >
 *       label.cm-drop-zone__target (the input, icon, text and hint above),
 *       div.cm-drop-zone__footer > label.cm-field.cm-field--check.cm-drop-zone__option
 * buildDropZone() appends the label, count and list into a parent that
 * carries .cm-drop-zone__group (the caller's element). Static markup must
 * match buildDropZone()'s structure, ids and ARIA: the input's
 * aria-labelledby names the __text span, its aria-describedby lists the
 * __hint and the __count (manual_upload.html, import_csv.html).
 * wireDropZone() without onFiles writes a drop into input.files and
 * dispatches change, so a page's existing change handler runs for drops
 * too; with onFiles the input is emptied after each choice.
 * --active is set by wireDropZone() while a drag is over the zone.
 * A file chip is a removable tag (components/tag.css, imported after this
 * file in the same layer): its look and its remove button are the tag's.
 * The rules below that must beat a tag rule (widths, upload state borders,
 * status colors) carry a second class; the state borders carry the list's
 * class too, to beat the removable tag's hover.
 */

.cm-drop-zone__group {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--cm-space-2);
  width: 100%;
}

.cm-drop-zone {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cm-space-1);
  box-sizing: border-box;
  width: 100%;
  min-height: 100px;
  padding: var(--cm-space-4);
  border: 1px dashed var(--cm-border-strong);
  border-radius: var(--cm-radius-lg);
  background: var(--cm-bg-surface);
  text-align: center;
  cursor: pointer;
  transition:
    border-color 120ms ease,
    background-color 120ms ease;
}

/* --wide: opt in to today's ~370px target (23rem) inside a shrink-wrapped
   card. buildDropZone()'s wide option adds this modifier; UploadContainer
   (common/request-widgets.js) always asks for it, so its mounts (Batch
   Upload, Upload Requests, Preload Tool, Upload Records, the Review Batches
   and PDF editor append/replace popups) keep today's width. A shrink-to-fit
   parent measures the preferred 23rem, since a percentage min or max size
   counts as 0 or none while it measures; at layout the 100% min and max
   make the zone fill its parent, wider or narrower (Upload Records' 300px
   mounts, the Payer Audits form column). Below 600px the 23rem would become
   the card's minimum, so small screens keep width: 100% alone.
   Without this modifier a plain zone (Manual Upload, Import CSV static
   markup) never asks for more than its own column gives it: it just fills
   whatever width: 100% resolves to, so it cannot force a shrink-to-fit
   ancestor (and the legacy row/column it sits in) wider than that column's
   own content. */
@media (min-width: 600px) {
  .cm-drop-zone--wide {
    inline-size: 23rem;
    min-inline-size: 100%;
    max-inline-size: 100%;
  }
}

.cm-drop-zone__input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.cm-drop-zone:hover {
  border-color: var(--cm-interactive);
}

/* The ring is for keyboard focus: a label click focuses the input too, so
   :focus-within would keep the ring after choosing files with the mouse. */
.cm-drop-zone:has(.cm-drop-zone__input:focus-visible) {
  border-color: var(--cm-interactive);
  box-shadow: var(--cm-focus-ring);
}

.cm-drop-zone--active {
  border-color: var(--cm-interactive);
  background: var(--cm-drop-zone-active-bg);
}

/* --lg: a customer page's zone (the request flow, docs/specs/
   2026-10-13-request-flow-refresh-design.md section 5): a 2px dashed edge
   on a slate-50 fill, 14px corners, more room, the icon in a 48px accent
   tile and 15px text with a bold browse link. Staff zones keep the look
   above. */
.cm-drop-zone--lg {
  gap: 6px;
  padding: 26px var(--cm-space-5);
  border: 2px dashed var(--cm-gray-300);
  border-radius: var(--cm-radius-xl);
  background: var(--cm-gray-50);
}

.cm-drop-zone--lg .cm-drop-zone__icon {
  display: inline-grid;
  place-items: center;
  width: 48px;
  height: 48px;
  margin-bottom: var(--cm-space-1);
  border-radius: var(--cm-radius-xl);
  background: var(--cm-accent-50);
  color: var(--cm-accent-700);
  font-size: var(--cm-text-xl);
}

.cm-drop-zone--lg .cm-drop-zone__text {
  font-size: var(--cm-text-base);
}

.cm-drop-zone--lg .cm-drop-zone__browse {
  color: var(--cm-accent-700);
  font-weight: var(--cm-fw-semibold);
}

.cm-drop-zone--lg .cm-drop-zone__hint {
  font-size: var(--cm-text-sm);
}

/* A zone whose input a page's check rejected (aria-invalid: the request
   flow's "Please select a file"): the danger edge, until a file comes. */
.cm-drop-zone:has(> .cm-drop-zone__input[aria-invalid='true']) {
  border-color: var(--cm-danger-500);
}

.cm-drop-zone__icon {
  font-size: var(--cm-text-xl);
  color: var(--cm-text-muted);
}

.cm-drop-zone--active .cm-drop-zone__icon {
  color: var(--cm-interactive);
}

.cm-drop-zone__text {
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
}

.cm-drop-zone__browse {
  color: var(--cm-interactive);
  font-weight: var(--cm-fw-medium);
  text-decoration: underline;
}

.cm-drop-zone__hint {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}

.cm-drop-zone__count {
  margin: 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-text-muted);
  text-align: center;
}

/* The chip list (moved from stylesheetv3.css .file-chips, same look). Large
   batches scroll inside a fixed area instead of pushing the buttons below
   the fold (Automate-uzxo). */
.cm-drop-zone__files {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cm-space-1);
  max-height: 220px;
  margin: 0;
  padding: 0 var(--cm-space-1) 0 0;
  overflow-y: auto;
  list-style: none;
}

/* Not :empty, so whitespace left in static markup cannot keep an empty
   list in the group's flow. */
.cm-drop-zone__files:not(:has(> li)) {
  display: none;
}

/* Long names stop at a fixed width (wider once a status shows). */
.cm-drop-zone__files .cm-drop-zone__file {
  max-width: 120px;
}

.cm-drop-zone__files .cm-drop-zone__file[data-state] {
  max-width: 170px;
}

.cm-drop-zone__file-name {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cm-drop-zone__file .cm-drop-zone__file-status {
  color: var(--cm-text-muted);
  white-space: nowrap;
}

.cm-drop-zone__file-status:empty {
  display: none;
}

.cm-drop-zone__files--busy .cm-drop-zone__file-remove {
  display: none;
}

/* The remove button is gone, so give the chip matching right padding
   (two classes to outrank .cm-tag--removable's own padding). */
.cm-drop-zone__files--busy .cm-drop-zone__file {
  padding-right: var(--cm-space-2);
}

.cm-drop-zone__files .cm-drop-zone__file[data-state='uploading'] {
  border-color: var(--cm-interactive);
}

.cm-drop-zone__file[data-state='uploading'] .cm-drop-zone__file-status {
  color: var(--cm-interactive);
}

.cm-drop-zone__file[data-state='done'] .cm-drop-zone__file-status {
  color: var(--cm-success-text);
}

.cm-drop-zone__file[data-state='done'] .cm-drop-zone__file-status::before {
  content: '\2713 ';
}

/* Chosen and ready to send (the request flow's files, upload-tiles.js):
   the check and the word a little apart. */
.cm-drop-zone__file[data-state='ready'] .cm-drop-zone__file-status {
  display: inline-flex;
  gap: var(--cm-space-1);
  color: var(--cm-success-text);
  font-weight: var(--cm-fw-semibold);
}

.cm-drop-zone__file[data-state='ready'] .cm-drop-zone__file-status::before {
  content: '\2713 ';
}

.cm-drop-zone__files .cm-drop-zone__file[data-state='failed'] {
  border-color: var(--cm-danger-500);
}

.cm-drop-zone__file[data-state='failed'] .cm-drop-zone__file-status {
  color: var(--cm-danger-text);
}

/* __files--rows: each chosen file a full-width row (the request flow): its
   whole name, its state at the end, a 32px remove button. Two classes beat
   the removable tag's own sizes. */
.cm-drop-zone__files--rows {
  flex-direction: column;
  flex-wrap: nowrap;
  gap: var(--cm-space-2);
  padding: 0;
}

.cm-drop-zone__files--rows .cm-drop-zone__file[data-state] {
  max-width: none;
}

.cm-drop-zone__files--rows .cm-drop-zone__file {
  gap: var(--cm-space-3);
  max-width: none;
  min-height: 48px;
  padding: 6px 6px 6px 14px;
  border-color: var(--cm-gray-200);
  border-radius: var(--cm-radius-xl);
  background: var(--cm-bg-surface);
  color: var(--cm-text-heading);
  font-size: var(--cm-text-sm);
}

.cm-drop-zone__files--rows .cm-drop-zone__file-name {
  flex: 1 1 auto;
  font-weight: var(--cm-fw-medium);
}

.cm-drop-zone__files--rows .cm-drop-zone__file-remove {
  width: 32px;
  height: 32px;
  border-color: transparent;
  font-size: var(--cm-text-lg);
}

/* --options (CSS phase 5.5 redesign review): a zone with options inside
   its frame (Compress PDFs). The frame is a div and the drop target; the
   click-to-browse label (__target) fills its top, and the options sit in
   __footer, outside that label (a label cannot hold another control's
   label), so a click on them never opens the file dialog. */
.cm-drop-zone--options {
  justify-content: flex-start;
  padding: 0;
  cursor: default;
}

.cm-drop-zone__target {
  display: flex;
  flex: 1 1 auto;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--cm-space-1);
  box-sizing: border-box;
  width: 100%;
  padding: var(--cm-space-4) var(--cm-space-4) var(--cm-space-3);
  cursor: pointer;
}

.cm-drop-zone__footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--cm-space-2);
  box-sizing: border-box;
  width: 100%;
  padding: var(--cm-space-2) var(--cm-space-3);
  border-top: 1px dashed var(--cm-border);
}

/* An option (UploadContainer's Compress PDFs check chip) sits centered like
   the zone's text, and its label shrinks to its text so a click beside it
   does not toggle the box. */
.cm-drop-zone__option {
  align-self: center;
}

/* UploadContainer's Clear and Submit under its drop zone. Not a .row: inside
   a popup the popup's .row rule would add a gap and a bottom margin. */
.cm-drop-zone__actions {
  display: flex;
  flex-direction: row;
  justify-content: space-around;
  width: 100%;
}

}
@layer components {
/* Status pills and count bubbles (phase 4, docs/specs/2026-10-07-css-phase-4-pills-design.md).
 *   .cm-pill                        neutral status: gray tint, body text
 *   --info / --accent / --success
 *   / --warning / --danger          a tone: its opaque soft tint and --cm-*-text
 *   --solid (with --danger)         a release restriction: solid red
 *   --solid (with --accent)         a count bubble: the interactive fill
 *   --count                         a number: at least as wide as it is tall
 * One height for every pill: --cm-pill-height (its text size and side
 * padding: --cm-pill-font-size, --cm-pill-padding-x; the portal resizes all
 * three). Theme and accent changes live in the tokens, so this file has no
 * theme rules.
 */

.cm-pill {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--cm-space-1);
  height: var(--cm-pill-height);
  padding: 0 var(--cm-pill-padding-x);
  border-radius: var(--cm-radius-pill);
  background: var(--cm-bg-muted);
  color: var(--cm-text-body);
  font-size: var(--cm-pill-font-size);
  font-weight: var(--cm-fw-medium);
  line-height: var(--cm-lh-tight);
  white-space: nowrap;
}

.cm-pill--info {
  background: var(--cm-bg-info-soft);
  color: var(--cm-info-text);
}

.cm-pill--accent {
  background: var(--cm-bg-accent-soft);
  color: var(--cm-accent-text);
}

.cm-pill--success {
  background: var(--cm-bg-success-soft);
  color: var(--cm-success-text);
}

.cm-pill--warning {
  background: var(--cm-bg-warning-soft);
  color: var(--cm-warning-text);
}

.cm-pill--danger {
  background: var(--cm-bg-danger-soft);
  color: var(--cm-danger-text);
}

.cm-pill--danger.cm-pill--solid {
  background: var(--cm-bg-danger);
  color: var(--cm-text-on-dark);
}

.cm-pill--accent.cm-pill--solid {
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
}

.cm-pill--count {
  min-width: var(--cm-pill-height);
  padding: 0 var(--cm-space-1);
  font-weight: var(--cm-fw-semibold);
  font-variant-numeric: tabular-nums;
}

}
@layer components {
/* Tags and chips (phase 4, docs/specs/2026-10-07-css-phase-4-pills-design.md).
 *   .cm-tag                a gray bordered label (report mode, column type, host)
 *   --muted                quieter: surface fill, muted text (column type badges)
 *   --accent-border        an accent border (the scheduled count)
 *   --danger               danger tint and text (a failing host)
 *   --link                 clickable (<a> or <button>): surface fill, interactive text, 24px
 *   --removable            holds a .cm-tag__remove button, 24px; its __text may wrap
 *   --on-dark              translucent, on the dark profile banner
 *   --mono                 monospace text (an id)
 *   __text, __remove, __dot (--attention)
 * A container recolors its tags by setting --cm-tag-bg, --cm-tag-fg and
 * --cm-tag-border (the Reports sidebar's selected row); never with a
 * .cm-tag selector outside this file.
 */

.cm-tag {
  box-sizing: border-box;
  display: inline-flex;
  align-items: center;
  gap: var(--cm-space-1);
  height: var(--cm-pill-height);
  padding: 0 var(--cm-space-2);
  border: 1px solid var(--cm-tag-border, var(--cm-border));
  border-radius: var(--cm-radius-pill);
  background: var(--cm-tag-bg, var(--cm-bg-muted));
  color: var(--cm-tag-fg, var(--cm-text-body));
  font-family: inherit;
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-regular);
  line-height: var(--cm-lh-tight);
  white-space: nowrap;
  text-decoration: none;
}

.cm-tag--muted {
  background: var(--cm-tag-bg, var(--cm-bg-surface));
  color: var(--cm-tag-fg, var(--cm-text-muted));
}

.cm-tag--accent-border {
  border-color: var(--cm-tag-border, var(--cm-accent-500));
}

.cm-tag--danger {
  background: var(--cm-tag-bg, var(--cm-bg-danger-soft));
  color: var(--cm-tag-fg, var(--cm-danger-text));
}

.cm-tag--link {
  height: var(--cm-control-height);
  padding: 0 var(--cm-space-3);
  background: var(--cm-bg-surface);
  color: var(--cm-interactive);
  font-weight: var(--cm-fw-medium);
  cursor: pointer;
}

.cm-tag--link:hover {
  background: var(--cm-bg-muted);
}

.cm-tag--link:focus-visible,
.cm-tag--on-dark:focus-visible {
  outline: none;
  box-shadow: var(--cm-focus-ring);
}

.cm-tag--removable {
  height: auto;
  min-height: var(--cm-control-height);
  max-width: 100%;
  padding: 2px var(--cm-space-1) 2px var(--cm-space-2);
}

/* A removable chip answers the pointer (CSS phase 5.5 restyle): a firmer
   border and the surface fill. A drop zone chip's upload state border wins
   (components/drop-zone.css). */
.cm-tag--removable:hover {
  border-color: var(--cm-border-strong);
  background: var(--cm-bg-surface);
}

.cm-tag__text {
  min-width: 0;
  white-space: normal;
  overflow-wrap: anywhere;
}

/* A bordered circle (CSS phase 5.5 restyle: the other icon buttons are
   squares, the chip's x stays round). */
.cm-tag__remove {
  flex: none;
  display: inline-grid;
  place-items: center;
  box-sizing: border-box;
  width: 16px;
  height: 16px;
  padding: 0;
  border: 1px solid var(--cm-border-strong);
  border-radius: 50%;
  background: transparent;
  color: var(--cm-text-muted);
  font: inherit;
  line-height: 1;
  cursor: pointer;
}

.cm-tag__remove:hover {
  border-color: var(--cm-danger-text);
  background: var(--cm-bg-danger);
  color: var(--cm-text-on-dark);
}

.cm-tag__remove:focus-visible {
  outline: none;
  box-shadow: var(--cm-focus-ring);
}

.cm-tag--on-dark {
  border-color: transparent;
  background: var(--cm-bg-on-dark-soft);
  color: var(--cm-text-on-dark);
}

button.cm-tag--on-dark {
  cursor: pointer;
}

button.cm-tag--on-dark:hover {
  background: var(--cm-bg-on-dark-soft-hover);
}

.cm-tag--mono {
  font-family: var(--cm-font-mono);
}

.cm-tag__dot {
  flex: none;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--cm-border-strong);
}

.cm-tag__dot--attention {
  background: var(--cm-accent-500);
  box-shadow: var(--cm-attention-ring);
}

}
@layer components {
/* Initials discs and the queue assignee chip (phase 4, docs/specs/2026-10-07-css-phase-4-pills-design.md).
 *   .cm-avatar               a 30px gray initials disc (decorative, aria-hidden)
 *   --xs / --lg / --xl       18px (inside a chip), 36px (Open Task banner), 56px (profile)
 *   --accent                 the interactive fill (your own task, "You")
 *   --on-dark                translucent with a light ring, on the dark profile banner
 *   --chip (+ --you)         the assignee chip: an accent-tinted pill with an --xs
 *                            disc and a __name; --you makes it solid
 * Colors are tokens (tokens.css), so this file has no theme or accent rules.
 */

.cm-avatar {
  box-sizing: border-box;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--cm-avatar-bg);
  color: var(--cm-avatar-fg);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-medium);
  line-height: 1;
}

.cm-avatar * {
  color: inherit;
}

.cm-avatar--xs {
  width: 18px;
  height: 18px;
  font-size: var(--cm-font-size-avatar-xs);
}

.cm-avatar--lg {
  width: 36px;
  height: 36px;
}

.cm-avatar--xl {
  width: 56px;
  height: 56px;
  font-size: var(--cm-text-xl);
  font-weight: var(--cm-fw-bold);
}

.cm-avatar--accent {
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
}

.cm-avatar--on-dark {
  border: 1px solid var(--cm-border-on-dark-strong);
  background: var(--cm-bg-on-dark-soft);
  color: var(--cm-text-on-dark);
}

.cm-avatar--chip {
  width: auto;
  height: auto;
  gap: var(--cm-space-2);
  padding: 2px var(--cm-space-2) 2px 3px;
  border-radius: var(--cm-radius-pill);
  background: var(--cm-bg-accent-soft);
  color: var(--cm-accent-text);
  line-height: var(--cm-lh-tight);
  white-space: nowrap;
}

.cm-avatar--chip > .cm-avatar {
  background: var(--cm-avatar-chip-disc-bg);
  color: var(--cm-avatar-chip-disc-fg);
}

.cm-avatar--chip.cm-avatar--you {
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
}

.cm-avatar--chip.cm-avatar--you > .cm-avatar {
  background: var(--cm-avatar-you-disc-bg);
  color: var(--cm-text-on-interactive);
}

}
@layer components {
/* Tables (phase 4, docs/specs/2026-10-07-css-phase-4-tables-design.md).
 *   .cm-table                       a data table: caps header band, 14px cells,
 *                                   gray row lines, hover wash, centered
 *   __row--selected/--urgent/
 *          --locked/--deleted       a 4px marker bar (urgent wins); selected keeps the wash
 *                                   (a --highlight cell inside it keeps its own tint instead)
 *   __row--danger / --dim           red semibold text / muted text; element children
 *                                   in their cells take the row's color
 *   __row--clickable                pointer
 *   __cell--num / --start           right (money) / left (long free text)
 *   __cell--truncate                marks a cell that holds a .cm-table__truncate span; carries
 *                                   no styling of its own (a td's max-width is not honored by
 *                                   table auto-layout except during a column squeeze, so the
 *                                   actual ellipsis cap lives on the inner span, set by
 *                                   setCellText/common/table.js)
 *   __truncate                      the inline element that stops at --cm-table-cell-max with
 *                                   an ellipsis; the cell's title carries the full value
 *   __cell--fill                    a column that takes the spare width, its truncated lines
 *                                   stopping at its edge (a name with its address under it)
 *   __sub                           a muted second line under a cell's value (Browse Requests'
 *                                   reference and DOB); with __truncate it stays one line
 *   __cell--fit                     as narrow as its content, on one line (a comments
 *                                   table's User and Date)
 *   __cell--muted / --highlight     muted text / accent tint (Current Due)
 *   __cell--danger                  red semibold text (a failure note, a pending count)
 *   __cell--actions                 lets a row menu escape the cell
 *   __cell--flag                    a narrow leading column holding only a flag (Urgent)
 *   __sort                          the sort button filling a sortable th (aria-sort on the th);
 *                                   in print only the sorted column keeps its arrow
 *   __header-action                 an icon button pinned to the end of a header cell
 *                                   (the comments table's add button)
 *   __totals                        a totals row; __caption a caption with a heading and a button
 *   --dense                         tighter cells (the claims grid)
 *   --nowrap-head                   header labels stay on one line (Browse Requests)
 *   --flush                         square header corners, for a table that runs
 *                                   edge to edge in its box (the patient alert list)
 *   .cm-table-scroll                a framed scroll box for a table in a fixed-height box
 * Cell and header rules use :where(.cm-table) so modifiers and .cm-empty-state win
 * without id selectors.
 */

:where(.cm-table) {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-variant-numeric: tabular-nums;
}

:where(.cm-table) > thead > tr > th {
  position: sticky;
  top: 0;
  z-index: var(--cm-z-raised);
  padding: var(--cm-space-3);
  border-bottom: 1px solid var(--cm-border);
  background: var(--cm-bg-muted);
  color: var(--cm-text-body);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-semibold);
  letter-spacing: 0.04em;
  line-height: var(--cm-lh-tight);
  text-align: center;
  text-transform: uppercase;
  vertical-align: bottom;
  white-space: normal;
}

:where(.cm-table) > thead > tr > th * {
  color: inherit;
}

:where(.cm-table) > thead > tr > th:first-child {
  border-top-left-radius: var(--cm-radius-xl);
}

:where(.cm-table) > thead > tr > th:last-child {
  border-top-right-radius: var(--cm-radius-xl);
}

:where(.cm-table) > tbody > tr > td,
:where(.cm-table) > tfoot > tr > td {
  padding: var(--cm-space-2) var(--cm-space-3);
  border-bottom: 1px solid var(--cm-border);
  background: var(--cm-bg-surface);
  color: var(--cm-text-body);
  font-size: var(--cm-text-sm);
  text-align: center;
  white-space: nowrap;
}

/* :where(:hover) keeps this at the base cell rule's specificity (a type
   selector chain, no classes), so a rule with any class in its selector -
   __cell--highlight, td.cm-empty-state (components/empty-state.css) - wins
   over the hover wash regardless of source order. */
:where(.cm-table) > tbody > tr:where(:hover) > td {
  background: var(--cm-bg-muted);
}

/* :not() excludes a --highlight cell from this selector entirely, so the
   row's wash never competes with it: __cell--highlight's own background
   applies unopposed instead of losing to this rule's class-plus-type
   specificity (a plain `> td` previously did lose a --highlight cell's tint
   this way). */
.cm-table__row--selected > td:not(.cm-table__cell--highlight) {
  background: var(--cm-bg-muted);
}

.cm-table__row--selected > td:first-child {
  box-shadow: var(--cm-table-marker-selected);
}

.cm-table__row--locked > td:first-child {
  box-shadow: var(--cm-table-marker-locked);
}

.cm-table__row--deleted > td:first-child {
  box-shadow: var(--cm-table-marker-deleted);
}

/* Last of the markers so a selected urgent row shows red. */
.cm-table__row--urgent > td:first-child {
  box-shadow: var(--cm-table-marker-urgent);
}

.cm-table__row--danger > td {
  color: var(--cm-danger-text);
  font-weight: var(--cm-fw-semibold);
}

.cm-table__row--dim > td {
  color: var(--cm-text-muted);
}

.cm-table__row--clickable {
  cursor: pointer;
}

.cm-table__cell--num {
  text-align: right;
}

.cm-table__cell--start {
  text-align: left;
  white-space: normal;
}

/* Marker only: a table auto-layout td's max-width is not honored except
   during a column squeeze (verified: an isolated truncate td in a roomy
   2-column table rendered past 1000px). The ellipsis cap lives on the inner
   .cm-table__truncate span below, which setCellText() inserts. */
.cm-table__cell--truncate {
}

.cm-table__truncate {
  display: inline-block;
  max-width: var(--cm-table-cell-max);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

/* A muted second line under a cell's main value (CSS phase 5.5 redesigns:
   Browse Requests' customer reference and DOB, Customer Details' Cust Ref).
   After .cm-table__truncate, so a truncated sub line is still a block in
   the muted color. */
.cm-table__sub {
  display: block;
  margin-top: 2px;
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
  white-space: nowrap;
}

/* A column that takes the table's spare width (Browse Facilities' and
   Browse Customers' name with its address under it): 40% of the table, and
   max-width: 0 so its text never widens it, so a truncated line in it stops
   at the column's edge instead of at --cm-table-cell-max beside empty space.
   The name wraps; the address line ends in an ellipsis. */
.cm-table__cell--fill {
  width: 40%;
  max-width: 0;
}

.cm-table__cell--fill .cm-table__truncate {
  display: block;
  max-width: 100%;
}

/* A column as narrow as its content (CSS phase 5.5 redesign review: the
   full-width comments table's User and Date, so Comment takes the rest). */
.cm-table__cell--fit {
  width: 1%;
  white-space: nowrap;
}

.cm-table__cell--muted {
  color: var(--cm-text-muted);
}

.cm-table__cell--danger {
  color: var(--cm-danger-text);
  font-weight: var(--cm-fw-semibold);
}

.cm-table__cell--highlight {
  background: var(--cm-bg-accent-soft);
  color: var(--cm-accent-text);
  font-weight: var(--cm-fw-semibold);
}

.cm-table__cell--actions {
  overflow: visible;
}

/* A narrow leading column that holds only a flag (the Urgent pill): as
   wide as its content, with 8px sides; its cells keep no top or bottom
   padding. */
.cm-table__cell--flag {
  width: 1%;
  padding-left: var(--cm-space-2);
  padding-right: var(--cm-space-2);
  white-space: nowrap;
}

td.cm-table__cell--flag {
  padding-top: 0;
  padding-bottom: 0;
}

/* The button fills its header so the whole label column is a click target;
   its label and arrow follow the header's alignment (centered by default,
   left on a --start header, right on a --num header). text-align: inherit
   undoes the button's UA centering, so a label that wraps to two lines
   lines up with its column too, not only the label block. */
.cm-table__sort {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: center;
  gap: var(--cm-space-1);
  padding: 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-align: inherit;
  text-transform: inherit;
  cursor: pointer;
}

.cm-table__cell--start > .cm-table__sort {
  justify-content: flex-start;
}

.cm-table__cell--num > .cm-table__sort {
  justify-content: flex-end;
}

/* The arrow is decoration (aria-sort on the th carries the state), so the
   alt-text form ( / '') keeps it out of the button's accessible name. */
.cm-table__sort::after {
  content: '\2195' / '';
  color: var(--cm-text-muted);
}

/* Hover brightens the idle arrow; the aria-sort rules below outrank this
   one, so an active arrow keeps --cm-interactive. */
.cm-table__sort:hover::after {
  color: var(--cm-text-body);
}

/* A printout cannot be re-sorted, so the idle arrow goes; the aria-sort
   rules below outrank this one (an attribute and a type selector more), so
   the sorted column keeps its arrow on paper. */
@media print {
  .cm-table__sort::after {
    content: none;
  }
}

th[aria-sort='ascending'] > .cm-table__sort::after {
  content: '\2191' / '';
  color: var(--cm-interactive);
}

th[aria-sort='descending'] > .cm-table__sort::after {
  content: '\2193' / '';
  color: var(--cm-interactive);
}

.cm-table__sort:focus-visible {
  outline: none;
  border-radius: var(--cm-radius-sm);
  box-shadow: var(--cm-focus-ring);
}

/* An icon button pinned to the end of a header cell (the comments table's
   add button). Header cells are sticky, so the th is its containing block;
   the th keeps room on its right so the label never runs under it. */
.cm-table__header-action {
  position: absolute;
  top: 50%;
  right: var(--cm-space-2);
  transform: translateY(-50%);
  z-index: var(--cm-z-raised-2);
}

:where(.cm-table) > thead > tr > th:has(> .cm-table__header-action) {
  padding-right: calc(var(--cm-space-3) + var(--cm-button-height-sm));
}

.cm-table__totals > td {
  border-top: 1px solid var(--cm-border);
  background: var(--cm-bg-muted);
  font-weight: var(--cm-fw-semibold);
}

/* Inset like a cell, so a caption inside a .cm-table-scroll frame keeps its
   heading and button off the frame's edges and lines the heading up with
   the first column's text. */
.cm-table__caption {
  caption-side: top;
  padding: var(--cm-space-2) var(--cm-space-3);
  text-align: left;
}

.cm-table--dense > tbody > tr > td {
  padding: var(--cm-space-1) var(--cm-space-2);
}

/* Header labels on one line: a table whose columns carry short labels and
   second lines (Browse Requests, CSS phase 5.5 redesigns). */
.cm-table--nowrap-head > thead > tr > th {
  white-space: nowrap;
}

/* After the base corner rules: equal specificity, so source order wins. */
.cm-table--flush > thead > tr > th {
  border-radius: 0;
}

.cm-table-scroll {
  overflow: auto;
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-xl);
}

.cm-table-scroll:focus-visible {
  outline: none;
  box-shadow: var(--cm-focus-ring);
}

}
@layer components {
/* Empty states (phase 4, docs/specs/2026-10-07-css-phase-4-tables-design.md):
 * a muted italic centered message, as a table row's cell (td.cm-empty-state,
 * which wins over the :where() cell rules) or as a block (p.cm-empty-state). */

/* margin: 0 so the block form owns its box (base.css gives p a margin). */
.cm-empty-state {
  margin: 0;
  padding: var(--cm-space-8) var(--cm-space-4);
  color: var(--cm-text-muted);
  font-size: var(--cm-text-sm);
  font-style: italic;
  text-align: center;
  white-space: normal;
}

/* A row form needs its own background: without one, hovering its parent
   .cm-table row let the hover wash (components/table.css) show through,
   since .cm-empty-state alone never declared a background to compete with
   it. One class plus a type selector outranks the hover rule's type-only
   chain, so this wins regardless of source order. */
td.cm-empty-state {
  background: var(--cm-bg-surface);
}

}
@layer components {
/* Card (components/card.css): the one card look (the dashboard card),
   its parts, and the inset panel used inside cards and popup bodies.
   Decisions: docs/specs/2026-10-08-css-phase-4-cards-design.md. */

/* Radius, border and shadow come from tokens (--cm-card-*, tokens.css):
   the portal drops the border for a softer lift (pages/portal.css). */
.cm-card {
  background: var(--cm-bg-surface);
  border: 1px solid var(--cm-card-border);
  border-radius: var(--cm-card-radius);
  box-shadow: var(--cm-card-shadow);
  color: var(--cm-text-body);
  min-width: 0;
  box-sizing: border-box;
}

.cm-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--cm-space-3);
  padding: var(--cm-space-3) var(--cm-space-4);
  border-bottom: 1px solid var(--cm-border);
}

.cm-card__title {
  display: flex;
  align-items: center;
  gap: var(--cm-space-2);
  margin: 0;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
  line-height: var(--cm-lh-tight);
  color: var(--cm-text-heading);
}

/* --lg: the title of a card that is the page's main content (a request
   flow step, CSS phase 6). */
.cm-card__title--lg {
  font-size: var(--cm-text-xl);
}

/* A help icon beside the card's title (a .cm-help-icon,
   components/help-icon.css; CSS phase 5.5 restyle): it hugs the title
   instead of the header's far end. Two classes beat help-icon.css's
   margin. */
.cm-card__header > .cm-card__help {
  margin-left: calc(var(--cm-space-2) - var(--cm-space-3));
  margin-right: auto;
}

.cm-card__subtitle,
.cm-card__hint {
  margin: 0;
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}

.cm-card__end {
  display: flex;
  align-items: center;
  gap: var(--cm-space-2);
  margin-left: auto;
  font-size: var(--cm-text-xs);
}

.cm-card__body {
  padding: var(--cm-space-4);
}

.cm-card__footer {
  padding: var(--cm-space-2) var(--cm-space-4);
  border-top: 1px solid var(--cm-border);
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}

/* Figures stay side by side; in a narrow card their labels wrap instead
   (.cm-card__figure has min-width: 0). */
.cm-card__figures {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--cm-space-5);
}

.cm-card__figure {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-1);
  min-width: 0;
}

.cm-card__value {
  font-size: var(--cm-text-2xl);
  font-weight: var(--cm-fw-semibold);
  line-height: var(--cm-lh-tight);
  color: var(--cm-text-heading);
  font-variant-numeric: tabular-nums;
}

.cm-card__value--lg {
  font-size: var(--cm-text-3xl);
}

/* Figures beside buttons in a page header (Post Payments' queue, CSS phase
   5.5 redesigns): a smaller value, so the header keeps its height. */
.cm-card__figures--compact .cm-card__value {
  font-size: var(--cm-text-lg);
}

.cm-card__label {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}

.cm-card__label--good {
  color: var(--cm-success-text);
}

.cm-card__label--bad {
  color: var(--cm-warning-text);
}

.cm-card--padded {
  padding: var(--cm-space-4);
}

.cm-card--page {
  padding: var(--cm-space-8);
}

.cm-card--link {
  display: block;
  color: inherit;
  text-decoration: none;
  transition:
    box-shadow 120ms ease,
    transform 120ms ease;
}

.cm-card--link:hover,
.cm-card--link:focus-visible {
  box-shadow: var(--cm-shadow-lg);
  transform: translateY(-1px);
}

.cm-card--link:focus-visible {
  outline: 2px solid var(--cm-interactive);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .cm-card--link,
  .cm-card--link:hover,
  .cm-card--link:focus-visible {
    transition: none;
    transform: none;
  }
}

/* The compound selector lets a selected inset panel beat .cm-card-panel's
   own border, which comes later in this file. */
.cm-card--selected,
.cm-card-panel.cm-card--selected {
  border-color: var(--cm-interactive);
  box-shadow: var(--cm-shadow-selected);
}

.cm-card--tile {
  padding: var(--cm-space-3);
  box-shadow: none;
}

.cm-card--tile.cm-card--accent {
  border: 2px solid var(--cm-interactive);
}

.cm-card--tile.cm-card--danger {
  border: 2px solid var(--cm-danger-text);
}

/* Inset panel: a group inside a card or a popup body. Same surface as the
   card, so it never flips depth between themes. */
.cm-card-panel {
  background: var(--cm-bg-surface);
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-lg);
  padding: var(--cm-space-3);
  color: var(--cm-text-body);
  min-width: 0;
  box-sizing: border-box;
}

}
@layer components {
/* Page header (components/page-header.css): the one page title look, the
   QA-style header card. Composed on the card:
     header.cm-card.cm-card--padded.cm-page-header
       > div.cm-page-header__titles
           > h1.cm-page-header__title + p.cm-page-header__subtitle
             (+ div.cm-page-header__meta for pills and links)
       + div.cm-page-header__end (the page's actions, main action last)
   --plain drops the card for a header that already sits inside one (QA,
   Invoice): no outer width or margins, no wrap, an ellipsis subtitle.
   Decisions: docs/specs/2026-10-11-css-phase-4-batch3b-design.md. */

.cm-page-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cm-space-3) var(--cm-space-4);
  /* The page column's width, like the search bar and the results tables
     under it (the page container centers its children). */
  width: 95%;
  margin: var(--cm-space-5) 0 var(--cm-space-4);
  box-sizing: border-box;
}

/* In a page column (layout.css) the column sets the width and spacing. */
.page-column > .cm-page-header {
  width: auto;
  margin: 0;
}

.cm-page-header__titles {
  flex: 1 1 auto;
  min-width: 0;
}

.cm-page-header__title {
  margin: 0;
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  line-height: var(--cm-lh-snug);
  color: var(--cm-text-heading);
  text-align: left;
}

.cm-page-header__subtitle {
  margin: 2px 0 0;
  padding: 0;
  font-size: var(--cm-text-sm);
  line-height: var(--cm-lh-snug);
  color: var(--cm-text-muted);
}

.cm-page-header__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cm-space-2);
  margin-top: var(--cm-space-1);
}

/* Out of the way while everything in it is hidden (QA's and Invoice's
   queue pills show only for a carried queue filter). */
.cm-page-header__meta:not(:has(> :not([hidden]))) {
  display: none;
}

/* margin-left: auto keeps the group on the right, also after it wraps
   under the titles. */
.cm-page-header__end {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: var(--cm-space-2);
  margin-left: auto;
}

/* align-self: stretch fills a flex column that centers its children
   (Invoice's form column). */
.cm-page-header--plain {
  flex-wrap: nowrap;
  column-gap: var(--cm-space-2);
  align-self: stretch;
  width: auto;
  margin: 0;
}

.cm-page-header--plain .cm-page-header__subtitle {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cm-page-header--plain .cm-page-header__end {
  flex: 0 0 auto;
  flex-wrap: nowrap;
}

}
@layer components {
/* Search bar (components/search-bar.css): the one filter bar, composed on
   the card:
     div.cm-card.cm-card--padded.cm-search-bar
       > div.cm-search-bar__fields > div.cm-field (label + control) ...
       + div.cm-search-bar__filters? (checks and chips under a divider)
       + div.cm-search-bar__actions (Search and Clear, or Run)
   Fields left in an auto-fit grid, actions in their own right column.
   Page actions (Add, New, batch menus) belong in the page header, not here.
   Decisions: docs/specs/2026-10-11-css-phase-4-batch3b-design.md. */

.cm-search-bar {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: stretch;
  gap: var(--cm-space-3);
  /* The page column's width, like the page header and the results tables. */
  width: 95%;
  box-sizing: border-box;
}

/* In a page column (layout.css) the column sets the width. */
.page-column > .cm-search-bar {
  width: auto;
}

/* align-self: center keeps a single short row of fields level with the
   middle of a taller action column instead of sinking to its bottom. */
.cm-search-bar__fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: var(--cm-space-3);
  align-items: end;
  align-self: center;
  min-width: 0;
}

/* A select gets two columns so its longest option ("All statuses",
   "Open + Suspected") is not cut off at 1fr. */
.cm-search-bar__fields > :has(select) {
  grid-column: span 2;
}

/* Fields with several inline controls (the Complete Date filter: operator
   plus one or two dates). min-width: 0 lets the track shrink below the
   controls' own minimum instead of pushing into the action column. Two
   classes outrank the :has(select) rule above. */
.cm-search-bar__fields > .cm-search-bar__field--wide {
  grid-column: span 3;
  min-width: 0;
}

/* Tight label lines so a row of fields lines up on its controls. */
.cm-search-bar__fields > * > label {
  line-height: 1;
  padding-left: 2px;
}

.cm-search-bar__filters {
  grid-column: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cm-space-3) var(--cm-space-5);
  padding-top: var(--cm-space-3);
  border-top: 1px solid var(--cm-border);
}

.cm-search-bar__actions {
  grid-column: 2;
  grid-row: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: stretch;
  gap: var(--cm-space-2);
  min-width: 110px;
  padding-left: var(--cm-space-3);
  border-left: 1px solid var(--cm-border);
}

/* With a filter row showing, the column spans it too, so it stays full
   height beside the fields and the filters. */
.cm-search-bar:has(> .cm-search-bar__filters:not([hidden])) > .cm-search-bar__actions {
  grid-row: 1 / span 2;
}

}
@layer components {
/* Progress bar (components/progress.css): the dashboard mini rows, the
   Review Batches bars and the loader's upload bar (components/loader.css
   .cm-loader__progress); --on-dark, the request flow's steps on the hero
   band. The width is set inline by the builder. */

.cm-progress {
  height: var(--cm-progress-height);
  border-radius: var(--cm-radius-pill);
  background: var(--cm-bg-muted);
  overflow: hidden;
  min-width: 0;
}

.cm-progress__fill {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: var(--cm-accent-500);
}

.cm-progress__fill--warning {
  background: var(--cm-warning-500);
}

.cm-progress__fill--danger {
  background: var(--cm-danger-500);
}

.cm-progress__fill--muted {
  background: var(--cm-text-muted);
}

/* The interactive fill (the loader's upload bar), easing between updates. */
.cm-progress__fill--interactive {
  background: var(--cm-interactive);
  transition: width 200ms ease;
}

/* --on-dark: on the customer pages' dark hero band (the records request
   flow's compact steps): a 6px slate track, the fill teal-300 whatever its
   tone. */
.cm-progress--on-dark {
  height: 6px;
  background: var(--cm-hero-track);
}

.cm-progress--on-dark > .cm-progress__fill {
  background: var(--cm-hero-eyebrow);
}

/* Indeterminate: a 35% fill slides across the track; a static full fill
   under reduced motion. The builder clears the inline width. */
.cm-progress--indeterminate {
  position: relative;
}

.cm-progress--indeterminate > .cm-progress__fill {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 35%;
  animation: cm-progress-slide 1.2s ease-in-out infinite;
}

@keyframes cm-progress-slide {
  from {
    left: -35%;
  }
  to {
    left: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cm-progress--indeterminate > .cm-progress__fill {
    left: 0;
    width: 100%;
    animation: none;
  }
}

.cm-progress-list {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.cm-progress-list__row {
  display: flex;
  align-items: center;
  gap: var(--cm-space-2);
  font-size: var(--cm-text-xs);
  color: var(--cm-text-body);
}

.cm-progress-list__row > .cm-progress {
  flex: 1 1 auto;
}

.cm-progress-list__label {
  flex: 0 0 64px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cm-progress-list__count {
  flex: 0 0 22px;
  text-align: right;
  font-weight: var(--cm-fw-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--cm-text-heading);
}

}
@layer components {
/*
 * Tabs (CSS consolidation phase 4, tabs). Spec:
 * docs/specs/2026-10-09-css-phase-4-tabs-design.md
 * Markup: div.cm-tabs[role=tablist][aria-label] >
 *   button.cm-tabs__tab[role=tab][aria-selected][aria-controls]; the element
 *   that is already the panel (a .cm-card-panel in popups, the results
 *   .cm-card on Reports) carries .cm-tabs__panel, so the tabs stand on its
 *   top border and its top-start corner is square. Selection keys on
 *   aria-selected, never a class. Imported after card.css, so the panel's
 *   corner rule wins its equal-specificity tie with the card radius.
 * --pill: view pills over a list (My Tasks).
 * --vertical: a selection list of button rows (the Reports side list), the
 *   selected row marked aria-current="true"; not role="tab". Rows may be
 *   links to pages instead (the Help guide list), the open one marked
 *   aria-current="page".
 * __heading: a group heading in a --vertical list, before its rows (the
 *   Reports side list's categories and My reports, the Help guide list's
 *   categories).
 * __tab--stacked: a tab whose label has a second, smaller line under it (a
 *   Reports run: report and grouping over facility and dates).
 * __count: a count beside a tab's label (span.cm-tabs__count).
 */

.cm-tabs {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  min-width: 0;
}

.cm-tabs__tab {
  display: inline-flex;
  align-items: center;
  gap: var(--cm-space-2);
  box-sizing: border-box;
  min-height: var(--cm-button-height);
  margin: 0;
  padding: 0 var(--cm-space-4);
  border: 1px solid var(--cm-border);
  border-bottom: 0;
  border-radius: var(--cm-radius-lg) var(--cm-radius-lg) 0 0;
  background: var(--cm-bg-surface);
  color: var(--cm-text-body);
  font: inherit;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
  white-space: nowrap;
  cursor: pointer;
}

.cm-tabs__tab:hover:not([aria-selected='true']) {
  background: var(--cm-bg-muted);
  color: var(--cm-text-heading);
}

.cm-tabs__tab[aria-selected='true'] {
  background: var(--cm-interactive);
  border-color: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
}

.cm-tabs__tab:focus-visible {
  outline: 2px solid var(--cm-interactive);
  outline-offset: 2px;
}

.cm-tabs__panel {
  border-start-start-radius: 0;
}

/* A count beside a tab's label (Request Details' Delivery Log attempts,
   CSS phase 5.5 redesigns). After .cm-tabs__tab *, so it keeps its color. */
.cm-tabs__count {
  padding: 0 6px;
  border-radius: var(--cm-radius-pill);
  background: var(--cm-bg-muted);
  color: var(--cm-text-muted);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-semibold);
  font-variant-numeric: tabular-nums;
}

.cm-tabs__tab[aria-selected='true'] .cm-tabs__count {
  background: var(--cm-bg-surface);
  color: var(--cm-interactive);
}

/* Pill tabs over a list (My Tasks): the toggle chip's selected tokens. */
.cm-tabs--pill {
  align-items: center;
  gap: var(--cm-space-1);
}

.cm-tabs--pill .cm-tabs__tab {
  min-height: var(--cm-control-height);
  padding: 0 var(--cm-space-3);
  border: 1px solid transparent;
  border-radius: var(--cm-radius-pill);
  background: transparent;
}

.cm-tabs--pill .cm-tabs__tab[aria-selected='true'] {
  background: var(--cm-chip-selected-bg);
  border-color: var(--cm-chip-selected-border);
  color: var(--cm-accent-text);
  font-weight: var(--cm-fw-semibold);
}

/* A vertical selection list (Reports side list): button rows, the
   selected row aria-current="true", or links to pages (Help), the open one
   aria-current="page". Its hover rule wins the base hover's specificity tie
   on its own (four selector components vs. three), so a current row keeps
   its fill under the pointer regardless of file order. */
.cm-tabs--vertical {
  flex-direction: column;
  align-items: stretch;
}

.cm-tabs--vertical .cm-tabs__tab {
  justify-content: flex-start;
  width: 100%;
  min-height: 0;
  padding: var(--cm-space-2);
  border: 0;
  border-radius: var(--cm-radius-md);
  background: transparent;
  text-align: left;
  text-decoration: none;
  white-space: normal;
}

.cm-tabs--vertical .cm-tabs__tab:is([aria-current='true'], [aria-current='page']),
.cm-tabs--vertical .cm-tabs__tab:is([aria-current='true'], [aria-current='page']):hover {
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
  font-weight: var(--cm-fw-semibold);
}

/* A group heading in a vertical list: small muted capitals over the group's
   rows. Any element: a div on Reports, an h3 under the Help list's card
   title, so the line height is restated to match a div's. */
.cm-tabs__heading {
  margin: 10px 0 4px;
  color: var(--cm-text-muted);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-bold);
  line-height: var(--cm-lh-normal);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.cm-tabs__heading:first-child {
  margin-top: 0;
}

.cm-tabs__dot {
  flex: none;
  width: 7px;
  height: 7px;
  border-radius: var(--cm-radius-pill);
  background: transparent;
}

.cm-tabs__dot--on {
  background: var(--cm-interactive);
}

.cm-tabs__tab[aria-current='true'] .cm-tabs__dot--on {
  background: currentColor;
}

/* The 36px min-height alone leaves a two-line label nearly touching the top
   border (the two lines need about 33px, under the floor, so the tab never
   grows). Block padding lets a stacked tab grow past the floor (about 41px)
   while a one-line label still sits at 36px. */
.cm-tabs__tab--stacked {
  padding-block: var(--cm-space-1);
}

}
@layer components {
/*
 * Toasts (CSS consolidation phase 4, feedback). Spec:
 * docs/specs/2026-10-10-css-phase-4-feedback-design.md
 * Built by showToast() in common/common.js into #cm-toast-host
 * (loader.html): div.cm-toast.cm-toast--<tone> > i.cm-toast-icon,
 * p.cm-toast-message, button.cm-toast-close and, when the toast has a
 * timeout, div.cm-toast-progress > div.cm-toast-progress-bar. The part
 * names are hyphenated, not BEM, because the customer _shared/cm-toast.js
 * writes the same names (portal.css and request.css import this file).
 * Info is the default look; --info stays as a hook. The edge, tint and
 * countdown use the tone's -500; the icon uses the tone's --cm-*-text so
 * it reads on the tint. The countdown pauses on hover and on keyboard
 * focus, matching the JS timer in showToast().
 */

.cm-toast-host {
  position: fixed;
  top: var(--cm-space-4);
  right: var(--cm-space-4);
  z-index: var(--cm-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-2);
  max-width: calc(100vw - var(--cm-space-8));
  /* The stack stays inside the window: showToast() drops the oldest toast
     that has a timeout while the stack is taller than this. */
  max-height: calc(100vh - 2 * var(--cm-space-4));
  pointer-events: none; /* Toasts opt back in, so clicks pass through the gaps. */
}

.cm-toast {
  pointer-events: auto;
  position: relative;
  /* Never shrink to fit the capped host: overflow: hidden would let a flex
     item squash below its content. showToast() drops toasts instead. */
  flex: none;
  box-sizing: border-box;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--cm-space-3);
  width: min(380px, calc(100vw - var(--cm-space-8)));
  padding: var(--cm-space-3) var(--cm-space-4);
  border: 1px solid var(--cm-border);
  border-left: 4px solid var(--cm-blue-500);
  border-radius: var(--cm-radius-lg);
  background: color-mix(in srgb, var(--cm-blue-500) 10%, var(--cm-bg-surface));
  color: var(--cm-text-body);
  box-shadow: var(--cm-shadow-md);
  overflow: hidden;
  animation: cm-toast-enter 220ms cubic-bezier(0.21, 1.02, 0.73, 1) both;
}

.cm-toast--leaving {
  animation: cm-toast-leave 180ms ease-in both;
}

.cm-toast-icon {
  font-size: var(--cm-text-lg);
  line-height: 1;
  color: var(--cm-info-text);
}

.cm-toast-message {
  margin: 0;
  font-size: var(--cm-text-sm);
  line-height: 1.4;
  word-break: break-word;
}

.cm-toast-close {
  appearance: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 24px;
  height: 24px;
  margin: 0;
  padding: 0;
  border: 1px solid var(--cm-border-strong);
  border-radius: var(--cm-radius-md);
  background: transparent;
  color: var(--cm-text-body);
  font: inherit;
  font-size: var(--cm-text-lg);
  line-height: 1;
  cursor: pointer;
  transition:
    background 120ms ease,
    color 120ms ease;
}

/* The icon buttons' hover (components/button.css .cm-button--icon). */
.cm-toast-close:hover {
  border-color: var(--cm-interactive);
  background: var(--cm-chip-selected-bg);
  color: var(--cm-interactive);
}

.cm-toast-close:focus-visible {
  outline: 2px solid var(--cm-interactive);
  outline-offset: 2px;
}

.cm-toast-progress {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 3px;
  overflow: hidden;
  background: transparent;
}

.cm-toast-progress-bar {
  width: 100%;
  height: 100%;
  transform-origin: left center;
  background: var(--cm-blue-500);
  animation-name: cm-toast-progress;
  animation-timing-function: linear;
  animation-fill-mode: forwards;
}

.cm-toast:is(:hover, :focus-within) .cm-toast-progress-bar {
  animation-play-state: paused;
}

/* Tones. Listed after the info default so each wins its equal-specificity
   tie. */
.cm-toast--success {
  border-left-color: var(--cm-success-500);
  background: color-mix(in srgb, var(--cm-success-500) 10%, var(--cm-bg-surface));
}

.cm-toast--success .cm-toast-icon {
  color: var(--cm-success-text);
}

.cm-toast--success .cm-toast-progress-bar {
  background: var(--cm-success-500);
}

.cm-toast--warning {
  border-left-color: var(--cm-warning-500);
  background: color-mix(in srgb, var(--cm-warning-500) 10%, var(--cm-bg-surface));
}

.cm-toast--warning .cm-toast-icon {
  color: var(--cm-warning-text);
}

.cm-toast--warning .cm-toast-progress-bar {
  background: var(--cm-warning-500);
}

.cm-toast--error {
  border-left-color: var(--cm-danger-500);
  background: color-mix(in srgb, var(--cm-danger-500) 10%, var(--cm-bg-surface));
}

.cm-toast--error .cm-toast-icon {
  color: var(--cm-danger-text);
}

.cm-toast--error .cm-toast-progress-bar {
  background: var(--cm-danger-500);
}

@keyframes cm-toast-enter {
  from {
    transform: translateX(16px);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes cm-toast-leave {
  from {
    transform: translateX(0);
    opacity: 1;
  }
  to {
    transform: translateX(20px);
    opacity: 0;
  }
}

@keyframes cm-toast-progress {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

/* Last in the file, so it wins its equal-specificity ties without
   !important. showToast()'s JS timer still dismisses each toast. */
@media (prefers-reduced-motion: reduce) {
  .cm-toast,
  .cm-toast--leaving,
  .cm-toast-progress-bar {
    animation: none;
  }
}

}
@layer components {
/*
 * Loader (CSS consolidation phase 4, feedback). Spec:
 * docs/specs/2026-10-10-css-phase-4-feedback-design.md
 * The blocking wait screen in loader.html: #loaderBody.cm-loader (a solid
 * wash, framed on the content area) > .cm-loader__content >
 * #loader.cm-loader__spinner (the AutoMate mark, an inline SVG of two
 * chevrons, drawing itself inside a spinning orbit), .cm-loader__title,
 * #loaderMessageDiv .cm-loader__message and, for multi-step uploads,
 * #loaderProgressDiv .cm-loader__progress (built by updateLoaderProgress()
 * in common/common.js around a .cm-progress bar). The content takes the
 * card look (surface, border, popup shadow) only while it holds a shown
 * progress block; a plain wait sits directly on the wash. The overlay
 * fades in over 150ms; the content fades in 300ms later, so a quick wait
 * never flashes the mark. displayLoader() and hideLoader() toggle
 * #loaderBody and #loader with inline display (block/none), which tests
 * and two callers read, so nothing here sets the overlay's display: the
 * content centers itself with absolute positioning and auto margins
 * instead.
 */

.cm-loader {
  position: fixed;
  inset: 0 0 0 var(--sidebar-width);
  z-index: var(--cm-z-modal);
  /* A solid wash, lighter at the center, with the page faintly visible
     through it. Worst case over any page color: heading 11.97 / 10.75,
     message 6.94 / 9.21, arc 3.67 / 5.78 (light / dark). */
  background: radial-gradient(
    circle farthest-corner at center,
    color-mix(in srgb, var(--cm-bg-surface) 85%, transparent) 0%,
    color-mix(in srgb, var(--cm-bg-body) 85%, transparent) 100%
  );
  /* The only opacity on this element: a 150ms entrance so the overlay
     never just snaps on. */
  animation: cm-loader-fade 150ms ease-out both;
}

/* No rail (login, reset): the whole window. The same test as layout.css's
   body gutter, so the frame always matches the content area. */
body:not(:has(.sidenav, [w3-include-html*='navbar'])) .cm-loader {
  inset: 0;
}

.cm-loader__content {
  position: absolute;
  inset: 0;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--cm-space-3);
  width: min(440px, 100% - 2 * var(--cm-space-4));
  height: fit-content;
  margin: auto;
  padding: var(--cm-space-6);
  border: 1px solid transparent;
  border-radius: var(--cm-radius-xl);
  text-align: center;
  /* Opacity only, 300ms behind the overlay: the content's geometry never
     moves, and a quick wait that resolves inside that delay never flashes
     the mark. */
  animation: cm-loader-fade 200ms ease-out 300ms both;
}

/* Uploads: while the progress block shows, the content sits on the popup
   box look so the steps, the bar and the detail read as one panel. */
.cm-loader__content:has(> .cm-loader__progress:not([hidden])) {
  border-color: var(--cm-border);
  background: var(--cm-bg-surface);
  box-shadow: var(--cm-shadow-popup);
}

/* The busy mark: the AutoMate chevron mark (an inline SVG of two paths in
   loader.html) inside a tapered orbit. #loader keeps the inline
   display: block contract, so its children are placed absolutely, not by a
   grid or flex parent. aria-hidden; the title says it. */
.cm-loader__spinner {
  position: relative;
  flex: none;
  width: 68px;
  height: 68px;
}

.cm-loader__orbit {
  position: absolute;
  inset: 0;
  border-radius: var(--cm-radius-pill);
  animation: cm-loader-spin 1.1s linear infinite;
}

/* The arc fades from transparent into the accent; the mask keeps a 3px
   ring (a mask color only matters for its alpha). */
.cm-loader__orbit::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: var(--cm-radius-pill);
  background: conic-gradient(from 0deg, transparent, var(--cm-interactive));
  -webkit-mask: radial-gradient(
    farthest-side,
    transparent calc(100% - 3px),
    var(--cm-interactive) calc(100% - 2.5px)
  );
  mask: radial-gradient(
    farthest-side,
    transparent calc(100% - 3px),
    var(--cm-interactive) calc(100% - 2.5px)
  );
}

/* The arc's rounded head. */
.cm-loader__orbit::after {
  content: '';
  position: absolute;
  top: 0;
  left: 50%;
  width: 3px;
  height: 3px;
  translate: -50% 0;
  border-radius: var(--cm-radius-pill);
  background: var(--cm-interactive);
}

.cm-loader__logo {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 28px;
  height: auto;
  translate: -50% -50%;
  overflow: visible;
}

/* The outline traces itself, fills, holds and clears; the inner chevron a
   beat behind. At rest (no animation: reduced motion, or a still capture)
   the mark is filled. */
.cm-loader__chevron {
  fill: var(--cm-interactive);
  stroke: var(--cm-interactive);
  stroke-width: 7;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 0;
  animation: cm-loader-draw 2.2s ease-in-out infinite;
}

.cm-loader__chevron--inner {
  animation-delay: 0.25s;
}

/* Uploads: the same mark, smaller, above the steps. */
.cm-loader__content:has(> .cm-loader__progress:not([hidden])) .cm-loader__spinner {
  width: 52px;
  height: 52px;
}

.cm-loader__content:has(> .cm-loader__progress:not([hidden])) .cm-loader__logo {
  width: 22px;
}

/* Message first. With no message, "Please wait" is the main line. */
.cm-loader__title {
  margin: 0;
  padding: 0;
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
}

/* With a message, the message is the main line and "Please wait" a small
   label over it, in body text (muted gray falls to 3.19:1 on the wash over
   dark page text; body text holds 6.94 / 9.21). */
.cm-loader__content:has(> .cm-loader__message:not(:empty)) .cm-loader__title {
  margin-bottom: calc(var(--cm-space-1) - var(--cm-space-3));
  font-size: var(--cm-text-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--cm-text-body);
}

.cm-loader__message {
  margin: 0;
  padding: 0;
  font-size: var(--cm-text-lg);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  overflow-wrap: anywhere;
}

.cm-loader__message:empty {
  display: none;
}

/* Upload progress: the steps, the app's .cm-progress bar and a detail
   line, across the card under the message. */
.cm-loader__progress {
  display: flex;
  flex-direction: column;
  align-self: stretch;
  gap: var(--cm-space-2);
}

.cm-loader__steps {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--cm-space-1) var(--cm-space-4);
}

.cm-loader__steps:empty {
  display: none;
}

/* base.css colors every element, so each step sets its own color. */
.cm-loader__step {
  font-size: var(--cm-text-sm);
  color: var(--cm-text-muted);
}

.cm-loader__step--active {
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-interactive);
}

.cm-loader__step--done {
  color: var(--cm-text-body);
}

.cm-loader__step--done::before {
  content: '\2713\20';
}

.cm-loader__detail {
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
}

.cm-loader__detail:empty {
  display: none;
}

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

@keyframes cm-loader-draw {
  0% {
    stroke-dashoffset: 1;
    fill-opacity: 0;
    opacity: 1;
  }
  45% {
    stroke-dashoffset: 0;
    fill-opacity: 0;
  }
  60%,
  85% {
    stroke-dashoffset: 0;
    fill-opacity: 1;
    opacity: 1;
  }
  100% {
    stroke-dashoffset: 0;
    fill-opacity: 1;
    opacity: 0;
  }
}

@keyframes cm-loader-fade {
  from {
    opacity: 0;
  }
}

/* Reduced motion: no entrance and no drawing (the mark rests filled); the
   orbit turns slower, not stopped, since a still mark would read as a hang. */
@media (prefers-reduced-motion: reduce) {
  .cm-loader,
  .cm-loader__content,
  .cm-loader__chevron {
    animation: none;
  }

  .cm-loader__orbit {
    animation-duration: 2.4s;
  }
}

}
@layer components {
/*
 * In-page banners (CSS consolidation phase 4, batch 3a). Spec:
 * docs/specs/2026-10-10-css-phase-4-batch3a-design.md
 * A message that stays on the page (a toast floats and leaves): a surface
 * box with a gray border and a 4px left edge in the tone color, an icon in
 * the tone's text color, the words, and any actions at the end.
 * Markup (buildBanner() in _shared/components/banner.js builds it):
 *   div.cm-banner.cm-banner--<tone>[role] >
 *     i.cm-banner__icon.fa-solid.<icon>[aria-hidden],
 *     div.cm-banner__body > span.cm-banner__title, text,
 *     div.cm-banner__actions > buttons.
 * Tones: the gray default, --info, --success, --warning, --danger. The
 * edges use the tone's -500, as toasts do; the meaning rides the icon and
 * the words, which use the tone's text color and heading text.
 * --accent: an open task that is yours (the teal tint). --stack: children
 * in a column (the task banner's row and its Done form). --compact: the
 * tighter padding of a narrow column.
 */

.cm-banner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cm-space-2) var(--cm-space-3);
  box-sizing: border-box;
  width: 100%;
  padding: var(--cm-space-3) var(--cm-space-4);
  border: 1px solid var(--cm-border);
  border-left: 4px solid var(--cm-border);
  border-radius: var(--cm-banner-radius);
  background: var(--cm-bg-surface);
  color: var(--cm-text-body);
  font-size: var(--cm-text-sm);
  line-height: var(--cm-lh-normal);
}

.cm-banner__icon {
  flex: none;
  font-size: var(--cm-text-base);
  line-height: 1;
  color: var(--cm-text-muted);
}

.cm-banner__body {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-1);
  flex: 1 1 16rem;
  min-width: 0;
}

.cm-banner__title {
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
}

.cm-banner__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cm-space-2);
  margin-left: auto;
}

.cm-banner__actions:empty {
  display: none;
}

.cm-banner--info {
  border-left-color: var(--cm-blue-500);
}

.cm-banner--info .cm-banner__icon {
  color: var(--cm-info-text);
}

.cm-banner--success {
  border-left-color: var(--cm-success-500);
}

.cm-banner--success .cm-banner__icon {
  color: var(--cm-success-text);
}

.cm-banner--warning {
  border-left-color: var(--cm-warning-500);
}

.cm-banner--warning .cm-banner__icon {
  color: var(--cm-warning-text);
}

.cm-banner--danger {
  border-left-color: var(--cm-danger-500);
}

.cm-banner--danger .cm-banner__icon {
  color: var(--cm-danger-text);
}

.cm-banner--accent {
  border-color: var(--cm-chip-selected-border);
  border-left-color: var(--cm-interactive);
  background: var(--cm-bg-accent-soft);
}

.cm-banner--stack {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
}

.cm-banner--compact {
  padding: var(--cm-space-2) var(--cm-space-3);
}

}
@layer components {
/*
 * Text links (CSS consolidation phase 4, batch 3a). Spec:
 * docs/specs/2026-10-10-css-phase-4-batch3a-design.md
 * .cm-link: a link inside text or a table cell, in the interactive color
 * and always underlined (teal is 1.9:1 against body text, so color alone
 * does not mark it, WCAG 1.4.1).
 * --quiet: a standalone link in a card header or footer ("All batches",
 * "View all"): no underline until hover or keyboard focus.
 * --danger: an overdue or failing item, in the danger text color; combine
 * with --quiet for a standalone one.
 * base.css gives every bare <a> the interactive color too, so a link with
 * no class is never the near-white the dark rail uses.
 */

.cm-link {
  color: var(--cm-interactive);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.cm-link:hover {
  text-decoration-thickness: 2px;
}

.cm-link:focus-visible {
  outline: 2px solid var(--cm-interactive);
  outline-offset: 2px;
  border-radius: var(--cm-radius-sm);
}

.cm-link--quiet {
  text-decoration: none;
}

.cm-link--quiet:hover,
.cm-link--quiet:focus-visible {
  text-decoration: underline;
}

.cm-link--danger {
  color: var(--cm-danger-text);
}

.cm-link--danger:focus-visible {
  outline-color: var(--cm-danger-text);
}

/* --line: a link on its own line in small text under a table cell's value
   (the Payer Audit link under a request number). */
.cm-link--line {
  display: block;
  font-size: var(--cm-text-xs);
}

}
@layer components {
/*
 * Section titles (CSS consolidation phase 4, batch 3a). Spec:
 * docs/specs/2026-10-10-css-phase-4-batch3a-design.md
 * A heading that labels a section inside a page card, popup body or panel,
 * one step under a card title: 12px semibold small caps in muted text, with
 * a divider under it so a section never reads as a field label (field
 * labels share the small caps). Any element: h2 to h4, a legend, a div.
 * A legend sits on its fieldset's top border, which is its divider, so it
 * takes only its text's width and draws no rule of its own.
 * --spaced: space above, for a title that follows content in a plain block
 * flow with no gap (What's New groups, the QA hotkeys popover).
 * --plain: the caption type alone, no divider and no box of its own, on
 * its host's line height: a label inline before its value (the Upload
 * Records header, the Invoice total), over a tile figure (Import CSV) or
 * beside a control in a row that draws the divider (the report builder).
 */

.cm-section-title {
  display: block;
  box-sizing: border-box;
  width: 100%;
  margin: 0 0 var(--cm-space-2);
  padding: 0 0 var(--cm-space-1);
  border-bottom: 1px solid var(--cm-border);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-semibold);
  line-height: var(--cm-lh-snug);
  letter-spacing: 0.06em;
  text-align: left;
  text-transform: uppercase;
  color: var(--cm-text-muted);
}

legend.cm-section-title {
  width: auto;
  padding-bottom: 0;
  border-bottom: 0;
}

.cm-section-title--spaced {
  margin-top: var(--cm-space-3);
}

.cm-section-title--plain {
  display: inline;
  width: auto;
  margin: 0;
  padding: 0;
  border-bottom: 0;
  line-height: inherit;
}

}
@layer components {
/*
 * Tooltips (CSS consolidation phase 4, batch 3a). Spec:
 * docs/specs/2026-10-10-css-phase-4-batch3a-design.md
 * One bubble: navy (--cm-bg-sidebar, the same in both themes) with white
 * 12px text, left-aligned, the accent ring (--cm-shadow-tooltip) for its
 * edge on a dark page. Line breaks in the text are kept.
 * .cm-tooltip[data-tooltip]: the bubble is the element's ::after, above
 *   it, on hover and keyboard focus.
 * --fixed: the bubble is position: fixed and placed by the positioner in
 *   common/common.js (--tt-x, --tt-y), so a scrolling table or a popup body
 *   cannot clip it. Help icons and table cell hints use it.
 * --left: the bubble opens to the left (the PDF action rails).
 * .cm-tooltip__bubble: the same bubble as a real element (role="tooltip",
 *   tied by aria-describedby), shown under its field while a --focus host
 *   holds keyboard focus.
 * The ::after text is drawn but kept out of the accessibility tree (the
 * empty alt text after the slash), so a hint never joins its element's
 * accessible name; an element that needs its hint read gets an
 * aria-label or aria-description (addTooltip() sets one on focusable
 * elements).
 */

/* The anchor has no specificity, so a component that positions the
   element itself (an in-field chip) keeps its position. */
:where(.cm-tooltip) {
  position: relative;
}

.cm-tooltip[data-tooltip]::after,
.cm-tooltip__bubble {
  width: max-content;
  min-width: 220px;
  max-width: min(460px, calc(100vw - var(--cm-space-8)));
  padding: var(--cm-space-2) var(--cm-space-3);
  border-radius: var(--cm-radius-md);
  background: var(--cm-bg-sidebar);
  box-shadow: var(--cm-shadow-tooltip);
  color: var(--cm-text-on-dark);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-regular);
  line-height: var(--cm-lh-snug);
  text-align: left;
  text-transform: none;
  letter-spacing: normal;
  white-space: pre-line;
  pointer-events: none;
  z-index: var(--cm-z-hover);
}

.cm-tooltip[data-tooltip]::after {
  content: attr(data-tooltip) / '';
  position: absolute;
  bottom: 130%;
  left: 50%;
  transform: translateX(-50%);
  opacity: 0;
  transition: opacity 120ms ease;
}

.cm-tooltip[data-tooltip]:hover::after,
.cm-tooltip[data-tooltip]:focus-visible::after {
  opacity: 1;
}

.cm-tooltip--fixed[data-tooltip]::after {
  position: fixed;
  top: var(--tt-y);
  bottom: auto;
  left: var(--tt-x);
  transform: none;
}

.cm-tooltip--left[data-tooltip]::after {
  top: 50%;
  right: calc(100% + 10px);
  bottom: auto;
  left: auto;
  transform: translateY(-50%);
}

.cm-tooltip--focus {
  position: relative;
}

.cm-tooltip__bubble {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: var(--cm-space-1);
}

.cm-tooltip--focus:focus-within .cm-tooltip__bubble {
  display: block;
}

}
@layer components {
/*
 * Inline row (CSS consolidation phase 5): controls side by side with a
 * small gap inside a column cell, flush left. Not .row (layout.css), which
 * centers its content. Markup: div.cm-inline-row > controls.
 */

.cm-inline-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--cm-space-2);
  width: 100%;
}

/* --fill: the row's field takes what its icon buttons or chip leave, on
   one line (Invoice's Customer Code and Bill Type). */
.cm-inline-row--fill {
  flex-wrap: nowrap;
}

.cm-inline-row--fill > input,
.cm-inline-row--fill > select {
  flex: 1 1 auto;
  min-width: 0;
  width: auto;
}

}
@layer components {
/*
 * Status dot and count (CSS consolidation phase 5): a 6px dot beside a
 * label (the esMD status chip, the submission rows) and the warning count
 * that only shows while something is incomplete.
 * Markup: span.cm-status-dot.cm-status-dot--success|--warning,
 *   span.cm-status-count.
 */

.cm-status-dot {
  display: inline-block;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}

.cm-status-dot--success {
  background: var(--cm-success-text);
}

.cm-status-dot--warning {
  background: var(--cm-warning-text);
}

.cm-status-count {
  color: var(--cm-warning-text);
}

}
@layer components {
/*
 * Pagination strip (CSS consolidation phase 5): the light pill pinned to
 * the bottom of a browse page, built by PageNavigation in
 * common/pagination.js into the page's #navigationDiv mount: previous and
 * next carets, the page select and the result count. The legacy rules moved
 * here from stylesheetv3.css in their old order, so the #navigationDiv rules
 * still win over the .navigation-div ones; PageNavigation's inline styles
 * became the __side, __caret, __results and __export parts.
 */

/* The mount PageNavigation builds into (the page's #navigationDiv). */
.pagination-mount {
  display: flex;
  justify-content: center;
}

.navigation-div {
  display: flex;
  height: 50px;
  position: fixed;
  z-index: var(--cm-z-raised);
  bottom: 0px;
  left: 70px;
  right: 0px;
  justify-content: center;
  align-items: center;
  width: fit-content;
  margin-inline: auto;
}

/* The previous and next halves, each holding its caret. */
.navigation-div__side {
  display: flex;
  justify-content: center;
  width: 50%;
}

.navigation-div__caret {
  font-size: var(--cm-text-3xl);
}

/* "Display [n] Results" and Export. */
.navigation-div__results {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
}

.navigation-div__export {
  display: flex;
  justify-content: center;
  width: 150px;
}

.navigation-div p {
  width: 100px;
  text-align: center;
}

.navigation-div i {
  color: var(--cm-text-on-dark);
}

.navigation-div select {
  cursor: pointer;
  width: auto;
}

.display-results-div {
  display: flex;
  justify-content: center;
  align-items: center;
  padding-left: 10px;
  padding-right: 10px;
}

.display-results-div p {
  padding-left: 10px;
  padding-right: 10px;
  text-align: right;
}

/* Body text for everything in the strip but its buttons (Export): in this
   layer the id would beat the button component's own color, which the
   legacy layer never did. Its id also outranks the caret rules below, so
   the carets stay body text too, as they always have. */
#navigationDiv :not(button, button *) {
  color: var(--cm-text-body);
}

#navigationDiv select,
#navigationDiv input {
  height: var(--cm-control-height);
}

/* The light pill (Automate-274y). */
.navigation-div {
  background-color: var(--cm-bg-surface);
  border: 1px solid var(--cm-border);
  border-bottom: none;
  border-top-left-radius: var(--cm-radius-xl);
  border-top-right-radius: var(--cm-radius-xl);
  box-shadow: var(--cm-shadow-md);
  padding: 0 var(--cm-space-8);
}

.navigation-div p {
  color: var(--cm-text-body);
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-medium);
}

.navigation-div select {
  color: var(--cm-text-body);
  height: var(--cm-control-height);
}

/* Caret arrows (prev / next): their color loses to the #navigationDiv
   rule above; .nav-arrow-disabled (pagination.js) hides one with no page
   to go to. */
.navigation-div .fa-caret-left,
.navigation-div .fa-caret-right {
  color: var(--cm-interactive);
  cursor: pointer;
  transition:
    color 120ms ease,
    opacity 120ms ease;
}

.navigation-div .fa-caret-left:hover,
.navigation-div .fa-caret-right:hover {
  color: var(--cm-interactive-hover);
}

.navigation-div .fa-caret-left.nav-arrow-disabled,
.navigation-div .fa-caret-right.nav-arrow-disabled {
  /* Hide the prev/next arrow when there's nowhere to navigate. Use
     visibility instead of display:none so the surrounding
     50%/center/50% layout stays balanced. */
  visibility: hidden;
  pointer-events: none;
}

/* After the .navigation-div p and .display-results-div p rules above. */
.navigation-div .navigation-div__display {
  text-align: right;
}

.navigation-div .navigation-div__count {
  text-align: left;
}

}
@layer components {
/*
 * esMD Details form (CSS phase 5, moved from stylesheetv3.css): built by
 * renderEsmdSection() in _shared/components/esmd-form.js.
 */

/* The esMD Details sub-form (Automate-itqz) in the Resend popup, Deliver Now
   and Invoice's esMD popup.
   Compact 2-column grid that fits inside narrow form columns (360px in the
   resend popup, ~420px in the Deliver Now panel). Two-column-spanning fields
   (long dropdowns + ACN) sit full width; paired short fields sit side by
   side. Designed to sit flush with surrounding form fields: no card
   background or border that would make it look like an alien block.
   The section shows and hides through its hidden attribute. */
.resend-esmd {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-2);
  margin-top: var(--cm-space-1);
}
.resend-esmd__header {
  /* Single line: bold title + its help icon. */
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--cm-space-2);
  flex-wrap: wrap;
  padding-top: var(--cm-space-2);
  border-top: 1px solid var(--cm-border-translucent);
}
.resend-esmd__title {
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  letter-spacing: 0.01em;
}
.resend-esmd__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: var(--cm-space-3);
  row-gap: var(--cm-space-2);
}
/* .resend-esmd__field stays as the layout hook esmd-form.js's closest()
   lookups rely on; its own display/gap are redundant with the cm-field
   it's always paired with (components/field.css), so only the grid-span
   modifier is left to declare here. The label, required-mark and help-text
   rules this block used to carry are gone too: esmd-form.js builds the
   star as cm-field__required and the help as a cm-help-icon beside the
   label (CSS phase 5.5 restyle). */
.resend-esmd__field--span-2 {
  grid-column: 1 / -1;
}
.resend-esmd__field-hint {
  color: var(--cm-text-muted);
  font-weight: var(--cm-fw-regular);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--cm-text-xs);
}
.resend-esmd__field input[type='text'],
.resend-esmd__field input[type='date'] {
  font-variant-numeric: tabular-nums;
}

/* The esMD submission popup's failure lines (EsmdSubmissionPopup,
   common/popups.js): label and value rows, 4px apart. Two classes deep, so
   the row gap wins over the popup's .row rule (this file follows popup.css). */
.esmd-failure {
  align-items: flex-start;
  gap: 4px;
}

.esmd-failure .esmd-failure__row {
  gap: 6px;
}

.esmd-failure__label {
  font-weight: var(--cm-fw-bold);
}

}
@layer components {
/*
 * Delivery widgets (CSS phase 5, moved from stylesheetv3.css): the Resend
 * popup's body (RequestResendPopup in common/request-widgets.js) and the
 * mail address override form (MailAddressOverride in common/popups.js),
 * which DeliveryPanel shows while the delivery method is mail.
 */

/* Resend Request popup.
   Body of RequestResendPopup (common/request-widgets.js; the popup itself
   is components/popup.css, wide): form column on the left (360px), PDF on
   the right once View Request opens it. Replaces the legacy markup that
   built every label/input pair as a `.row` (which inside a popup is
   justify-content: stretch, so labels and inputs spread inconsistently).
*/
.resend-popup__content {
  display: flex;
  flex-direction: row;
  align-items: stretch;
  gap: var(--cm-space-4);
}
.resend-popup__form {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-3);
  flex: 0 0 360px;
  min-width: 0;
}
.resend-popup__pdf {
  /* Hidden until View Request opens it (its hidden attribute). */
  display: block;
  flex: 1 1 auto;
  min-width: 0;
  min-height: 70vh;
  border-radius: var(--cm-radius-md);
  overflow: hidden;
}

/* Deliver Now's confirmation (DeliverNowInterrupt): the request and record
   PDFs side by side, each half less the gap. */
.deliver-now-interrupt__pane {
  width: calc(50% - 10px);
}

/* The mail address override's mount and its address table. */
.mail-address-override {
  display: flex;
}

.flex-table {
  display: flex;
  flex-direction: column;
  width: 100%;
  box-sizing: border-box;
}

.flex-table-header {
  display: flex;
  flex-direction: row;
  box-sizing: border-box;
  /* Opaque so rows scrolling under the sticky header stay hidden; the
     title inside is a section title (components/section-title.css). */
  background-color: var(--cm-bg-surface);
  z-index: var(--cm-z-raised);
  position: sticky;
  top: 0;
}

/* Its one user is the address title, a .cm-section-title, which brings
   its own padding and alignment. */
.flex-table-cell {
  flex: 1;
  box-sizing: border-box;
}

}
@layer components {
/*
 * Option cards (CSS phase 5, from the legacy CNR and Add Affidavit cards):
 * a column of radio choices drawn as cards. The label wraps the radio and
 * its text, so the whole card toggles it; a hovered card and the checked
 * one take the teal edge and tint. --with-input puts a text field under the
 * card's label (CNR's Other).
 * Markup: p.cm-option-card-prompt, div.cm-option-card-list >
 *   label.cm-option-card[--with-input] > input[type=radio], span
 *   (+ input[type=text] for --with-input).
 * Used by the CNR reason picker (Upload Records and Manual Upload shells,
 * CnrPopup in common/request-widgets.js) and Add Affidavit
 * (common/pdf/pdf-actions.js).
 */

.cm-option-card-prompt {
  margin: 0 0 var(--cm-space-4);
  color: var(--cm-text-muted);
  font-size: var(--cm-text-sm);
}

.cm-option-card-list {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-2);
  width: 100%;
}

.cm-option-card {
  display: flex;
  align-items: center;
  gap: var(--cm-space-3);
  padding: var(--cm-space-2) var(--cm-space-3);
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-md);
  cursor: pointer;
  user-select: none;
  transition:
    background 120ms ease,
    border-color 120ms ease;
}

.cm-option-card:hover {
  border-color: var(--cm-teal-500);
  background: color-mix(in srgb, var(--cm-teal-500) 6%, var(--cm-bg-surface));
}

.cm-option-card:has(input[type='radio']:checked) {
  border-color: var(--cm-teal-500);
  background: color-mix(in srgb, var(--cm-teal-500) 10%, var(--cm-bg-surface));
}

.cm-option-card > input[type='radio'] {
  flex: 0 0 auto;
  margin: 0;
  cursor: pointer;
}

.cm-option-card > span {
  flex: 1;
  font-weight: var(--cm-fw-medium);
}

.cm-option-card--with-input {
  flex-wrap: wrap;
}

.cm-option-card--with-input > input[type='text'] {
  flex: 1 1 100%;
  margin: var(--cm-space-2) 0 0;
}

}
@layer components {
/*
 * Comment entries (CSS phase 5, moved from stylesheetv3.css), built by
 * RequestCommentsTable in common/request-widgets.js (Automate-zx5t,
 * Automate-szbe). Markup: div.comment-entry > div.comment-meta >
 * span.comment-author, span.comment-date; div.comment-body.
 */

/* One comment in RequestCommentsTable's compact cell (a
   .cm-table__cell--start td, components/table.css): author and date over
   the comment text, left-aligned. The flex layout for .comment-meta is
   unaffected by text-align. */
.comment-entry {
  margin-bottom: var(--cm-space-3);
  padding-bottom: var(--cm-space-2);
  border-bottom: 1px solid var(--cm-border);
  text-align: left;
  width: 100%;
}
.comment-entry:last-child {
  border-bottom: none;
  margin-bottom: 0;
  padding-bottom: 0;
}
.comment-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--cm-space-2);
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
  margin-bottom: var(--cm-space-1);
  align-items: baseline;
}
.comment-author {
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
}
.comment-date {
  color: var(--cm-text-muted);
}
/* The Add comment popup's text box. */
.comment-input {
  width: 100%;
  height: 100px;
  resize: none;
}
.comment-body {
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
  line-height: var(--cm-lh-snug);
  white-space: pre-wrap;
  word-break: break-word;
}

/* The comments table's mount on the request pages (moved from stylesheetv3.css in CSS phase 5). */
#commentsDiv {
  margin-top: var(--cm-space-3);
}

}
@layer components {
/*
 * PDF viewer (CSS phase 5, moved from stylesheetv3.css and the editor's
 * inline styles): ChartmatePDFEditor (common/pdf/chartmate-pdf-editor.js)
 * and QA's QualityAssurancePDFEditor build it.
 * Markup:
 *   div.chartmate-pdf-viewer >
 *     div.actionBar.row > i.pdf-viewer__tool (__fit), div.row
 *       groups (page input, "/ N" count, zoom select), div.pdf-viewer__slot
 *     div.img-magnifier-glass[hidden]
 *     div.pdf-viewer__content.row > div.sidebar (div.pdf-thumbnail-div >
 *       canvas.pdf-thumbnail, span), div.pdf-viewer__doc > div.pdf-page >
 *       div.textHighlightLayer, div.textLayer, canvas
 * The values are the ones that won before the move (the legacy !important
 * rules over the editor's inline styles), now without !important.
 * pdf.js's own text layer CSS stays in pdf_viewer.css.
 */

/* Inside the viewer, dark surfaces use the chrome shade (--cm-bg-chrome,
   one step lighter than the navbar's --cm-bg-sidebar) so the toolbar and
   thumbnail sidebar do not blend into the navbar in the QA module
   (Automate-ehdw); the re-bind covers shared rules that read
   --cm-bg-sidebar inside the editor (a .cm-tooltip bubble on a toolbar
   control). The viewer takes its box's width: a narrow box wraps the
   toolbar (below) instead of the old 640px floor (Automate-yjr6), which
   ran the viewer past its box over the controls beside it (Automate-sl5f).
   position: relative anchors .ai-insights-panel. */
.chartmate-pdf-viewer {
  --cm-bg-sidebar: var(--cm-bg-chrome);
  position: relative;
  border-radius: var(--cm-radius-lg);
  outline: none;
  background-color: var(--cm-bg-chrome);
  color: var(--cm-text-body);
}

/* The sidebar and page row fills the height under the toolbar, however
   tall the toolbar grows. */
.chartmate-pdf-viewer > .pdf-viewer__content {
  flex: 1 1 auto;
  width: 100%;
  min-height: 0;
  background-color: var(--cm-bg-chrome);
}

/* The page's box fills the row beside the sidebar on both axes, so the fit
   modes read the space the viewer has, not the last render (Automate-yjr6;
   QA saw +1% per page change when the box hugged the canvas). A page
   narrower than the box is centered; `safe` falls back to the start when
   it overflows, so its left edge stays scrollable. */
.chartmate-pdf-viewer .pdf-viewer__doc {
  position: relative;
  flex: 1 1 0;
  align-self: stretch;
  box-sizing: border-box;
  width: auto;
  height: 100%;
  min-width: 0;
  min-height: 0;
  justify-content: flex-start;
  align-items: safe center;
  resize: both;
  scrollbar-gutter: stable;
}

/* The thumbnail rail shares the toolbar's dark surface, so the chrome reads
   as one connected unit above the lighter page. */
.chartmate-pdf-viewer .sidebar {
  flex: 0 0 120px;
  height: -webkit-fill-available;
  border-right: 1px solid var(--cm-bg-chrome);
  background-color: var(--cm-bg-chrome);
  color: var(--cm-text-on-dark);
  transition: width 0.2s;
}

/* The toolbar's sidebar button folds the rail away. */
.chartmate-pdf-viewer .sidebar.sidebar--collapsed {
  flex: 0 0 0px;
}

.chartmate-pdf-viewer .sidebar span {
  color: var(--cm-text-on-dark);
}

/* The toolbar: one slim line, controls at --cm-control-height. */
/* The toolbar wraps onto a second row when the viewer is narrower than its
   tools (about 690px; a half-window viewer at 1440 still fits them on one
   line), instead of running past the viewer's edge over whatever sits
   beside it (Automate-sl5f). */
.chartmate-pdf-viewer .actionBar {
  box-sizing: border-box;
  flex-wrap: wrap;
  gap: var(--cm-space-1);
  min-height: 36px;
  padding: var(--cm-space-1) var(--cm-space-3);
  border-bottom: 1px solid var(--cm-bg-chrome);
  border-top-left-radius: var(--cm-radius-lg);
  border-top-right-radius: var(--cm-radius-lg);
  background-color: var(--cm-bg-chrome);
  color: var(--cm-text-on-dark);
}

/* Each toolbar group (page nav, zoom, the end tools), as wide as its tools
   (in a popup, .cm-popup__box .row would stretch each to a row of its own
   once the toolbar wraps). */
.chartmate-pdf-viewer .actionBar > .row {
  width: auto;
  gap: var(--cm-space-1);
}

/* The editor's own rows take no margin, inside a popup too (where
   .cm-popup__box .row would add one; this file comes after popup.css). */
.chartmate-pdf-viewer .row {
  margin-bottom: 0;
}

.pdf-viewer__tool {
  margin: 0 5px;
  color: var(--cm-text-on-dark);
  cursor: pointer;
}

/* Fit to Width: a fixed box, so its icon swap does not shift the toolbar. */
.pdf-viewer__fit {
  width: 25px;
  text-align: center;
}

/* The PDF actions menu's slot at the toolbar's end. */
.pdf-viewer__slot {
  margin: 0 5px;
}

/* The page counter: counter-shaped, with the "/ N" count beside it. */
.chartmate-pdf-viewer .actionBar input[type='number'] {
  width: 60px;
  height: var(--cm-control-height);
  min-width: 0;
  margin: 0 5px;
  text-align: center;
}

.chartmate-pdf-viewer .actionBar input[type='number'] + span {
  margin-left: var(--cm-space-1);
  color: var(--cm-text-on-dark);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.chartmate-pdf-viewer .actionBar select {
  height: var(--cm-control-height);
  margin: 0 5px;
}

.chartmate-pdf-viewer .actionBar select option {
  color: var(--cm-text-body);
}

/* Surface fill keeps the toolbar fields legible on the dark bar. */
.chartmate-pdf-viewer input,
.chartmate-pdf-viewer select {
  background-color: var(--cm-bg-surface);
  color: var(--cm-text-body);
}

/* The page: the canvas and its overlay layers as one positioned unit, so
   the layers always sit on the canvas however the box centers or scrolls
   it. display: block leaves no descender gap under the canvas. */
.pdf-page {
  position: relative;
  flex: 0 0 auto;
}

.pdf-page > canvas {
  display: block;
}

/* QA's current page carries its marker's ring (data-page-marker). */
.pdf-page > canvas[data-page-marker='ok'] {
  box-shadow: var(--cm-page-ring-ok);
}

.pdf-page > canvas[data-page-marker='new'] {
  box-shadow: var(--cm-page-ring-new);
}

.pdf-page > canvas[data-page-marker='rejected'] {
  box-shadow: var(--cm-page-ring-rejected);
}

.pdf-page > canvas[data-page-marker='misfiled'] {
  box-shadow: var(--cm-page-ring-misfiled);
}

.pdf-page > canvas[data-page-marker='deleted'] {
  box-shadow: var(--cm-page-ring-deleted);
}

.pdf-page > canvas[data-page-marker='image-flag'] {
  box-shadow: var(--cm-page-ring-image-flag);
}

/* The magnifier: fixed, so the viewport-relative left and top the editor
   sets land in the same place on every host page. Shown and hidden through
   its hidden attribute; its size, place and image are set while it moves. */
.img-magnifier-glass {
  position: fixed;
  z-index: var(--cm-z-modal);
  width: 400px;
  height: 400px;
  border: 3px solid var(--cm-magnifier-border);
  border-radius: 50%;
  background-repeat: no-repeat;
  cursor: none;
  pointer-events: none;
}

/* Thumbnails, each ringed by its page status; the selected ring wins. */
.pdf-thumbnail-div {
  flex: 0 0 135px;
}

.pdf-thumbnail-div > span {
  color: var(--cm-text-on-dark);
  font-size: var(--cm-text-xs);
}

.pdf-thumbnail {
  cursor: pointer;
}

.pdf-thumbnail-div.ok canvas {
  box-shadow: var(--cm-page-ring-ok);
}

.pdf-thumbnail-div.new canvas {
  box-shadow: var(--cm-page-ring-new);
}

.pdf-thumbnail-div.rejected canvas {
  box-shadow: var(--cm-page-ring-rejected);
}

.pdf-thumbnail-div.misfiled canvas {
  box-shadow: var(--cm-page-ring-misfiled);
}

.pdf-thumbnail-div.deleted canvas {
  box-shadow: var(--cm-page-ring-deleted);
}

.pdf-thumbnail-div.image-flag canvas {
  box-shadow: var(--cm-page-ring-image-flag);
}

.pdf-thumbnail-div.selected canvas {
  box-shadow: var(--cm-page-ring-selected);
}

/* Fill and Sign (common/pdf/fill-and-sign.js): a box that follows the
   mouse over the page, a header of actions and the text lines. */
.pdf-fill-sign-div {
  position: absolute;
  top: 0;
  left: 0;
  z-index: var(--cm-z-overlay);
  display: flex;
  flex-direction: column;
  background-color: transparent;
  pointer-events: none;
}

/* Planted on the page: the box takes clicks and typing. */
.pdf-fill-sign-div--placed {
  pointer-events: auto;
}

.pdf-fill-sign-header {
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  padding: 5px;
  background-color: var(--cm-bg-sidebar);
  cursor: move;
}

.pdf-fill-sign-action {
  color: var(--cm-fill-sign-action);
  cursor: pointer;
}

.pdf-fill-sign-text-input-container {
  display: flex;
  flex-direction: column;
  padding: 10px;
  border-bottom-left-radius: var(--cm-radius-fill-sign);
  border-bottom-right-radius: var(--cm-radius-fill-sign);
  background-color: transparent;
  box-shadow: var(--cm-shadow-fill-sign);
}

/* A borderless text line over the page. The size is --cm-fill-sign-size,
   which the + and - actions set on each line; bold and italic are
   modifiers. The focused line keeps the shared field focus ring. */
.pdf-fill-sign-input {
  width: 100%;
  min-width: 300px;
  height: 20px;
  margin: 0;
  padding-left: 5px;
  border: 0;
  background-color: transparent;
  color: var(--cm-fill-sign-text);
  font-family: Arial, Helvetica, sans-serif;
  font-size: var(--cm-fill-sign-size);
}

.pdf-fill-sign-input--bold {
  font-weight: var(--cm-fw-bold);
}

.pdf-fill-sign-input--italic {
  font-style: italic;
}

/* A direct copy of pdf.js's web viewer hiddenCanvasElement class; pdf.js
   adds the class at runtime. */
.hiddenCanvasElement {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  display: none;
}

/* The vertical icon-button rail beside a viewer (Upload Records' Check
   Duplicates, Problem, CNR and Add Payment). Labels live in tooltips and
   aria-labels; the rail spends horizontal slack so the height-bound
   portrait page keeps every vertical pixel. */
.pdf-action-rail {
  display: flex;
  flex: 0 0 auto;
  flex-direction: column;
  align-items: center;
  gap: 10px;
}

/* AI Insights (Automate-yrq5): the toolbar icon, its count pill and the
   findings panel. Hidden until findings exist. */
.ai-insights-icon {
  position: relative;
}

.ai-insights-icon.ai-insights-warning {
  color: var(--cm-warning-500);
}

/* The count pill's tokens (components/pill.css --accent --solid --count),
   a pseudo-element until the toolbar icons become buttons. */
.ai-insights-icon::after {
  content: attr(data-count);
  position: absolute;
  top: -14px;
  right: -14px;
  box-sizing: border-box;
  height: var(--cm-pill-height);
  min-width: var(--cm-pill-height);
  padding: 0 var(--cm-space-1);
  border-radius: var(--cm-radius-pill);
  background: var(--cm-interactive);
  color: var(--cm-text-on-interactive);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-semibold);
  line-height: var(--cm-pill-height);
  text-align: center;
}

.ai-insights-icon.ai-insights-warning::after {
  background: var(--cm-bg-warning);
  color: var(--cm-text-on-dark);
}

.ai-insights-panel {
  position: absolute;
  top: 44px;
  right: 8px;
  z-index: var(--cm-z-panel);
  width: 340px;
  max-height: 60%;
  overflow-y: auto;
  padding: 12px 14px;
  border: 1px solid var(--cm-accent-600);
  border-radius: var(--cm-radius-lg);
  background: var(--cm-bg-surface);
  box-shadow: var(--cm-shadow-insights);
  color: var(--cm-text-body);
  text-align: left;
}

.ai-insights-panel h4 {
  margin: 10px 0 4px;
  color: var(--cm-text-muted);
  font-size: var(--cm-text-xs);
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* 13px before CSS phase 5, snapped to the type scale. */
.ai-insights-panel-title {
  margin-bottom: 2px;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
}

.ai-insight {
  margin: 6px 0;
  padding-left: 8px;
  border-left: 3px solid var(--cm-accent-500);
}

.ai-insight-warning {
  border-left-color: var(--cm-warning-500);
}

.ai-insight-title {
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
}

.ai-insight-detail {
  color: var(--cm-text-muted);
  font-size: var(--cm-text-xs);
  white-space: pre-wrap;
}

/* The page manager (common/pdf/pdf-page-manager.js), a popup of page tiles
   to reorder, rotate and delete:
     div.cm-popup__box.pdfpm > div.cm-popup__body.pdfpm__body >
       div.row.pdfpm__toolbar, div.cm-banner (the unsaved-changes note),
       div.pdfpm-list > div.pdfpm-tile (--drop-before/--drop-after) >
         canvas.pdfpm-canvas, div.cm-tag.pdfpm-badge, div.pdfpm-sel (--on)
     div.pdfpm-deletion-container[hidden] (deleted tiles, kept for Undo)
   The list's column minimum, --pdfpm-tile-min, follows the thumbnail width. */
.pdfpm {
  outline: none;
  user-select: none;
}

.pdfpm__body {
  justify-content: flex-start;
  align-items: stretch;
  gap: var(--cm-space-2);
}

/* Centered: popup.css's row rule (two classes deep) would spread it. */
.pdfpm .pdfpm__toolbar {
  gap: var(--cm-space-2);
  justify-content: center;
}

.pdfpm-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--pdfpm-tile-min), 1fr));
  gap: var(--cm-space-3);
  align-items: start;
  max-height: 75vh;
  overflow-x: hidden;
  overflow-y: auto;
}

.pdfpm-tile {
  position: relative;
  height: 232px;
  padding: 6px;
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-lg);
  background: var(--cm-bg-surface);
  cursor: grab;
}

.pdfpm-tile--drop-before {
  box-shadow: var(--cm-shadow-pdfpm-drop-before);
}

.pdfpm-tile--drop-after {
  box-shadow: var(--cm-shadow-pdfpm-drop-after);
}

.pdfpm-canvas {
  display: block;
  margin: auto;
}

.pdfpm-badge {
  position: absolute;
  top: 6px;
  left: 6px;
}

/* The selection ring, drawn over the tile so it never shifts the canvas. */
.pdfpm-sel {
  position: absolute;
  inset: 0;
  border: 2px solid transparent;
  border-radius: var(--cm-radius-lg);
  pointer-events: none;
}

.pdfpm-sel--on {
  border-color: var(--cm-accent-800);
}

/* The PDF Preferences popup (common/pdf/pdf-preferences.js): fields fill
   the column; check rows are chips at their text width
   (components/field.css). */
.pdf-preferences {
  align-items: stretch;
  gap: var(--cm-space-3);
}

/* Split PDF's page list (common/pdf/pdf-actions.js). */
.pdf-split__pages {
  width: 300px;
  height: 150px;
}

}
@layer components {
/* Timeline (moved from stylesheetv3.css in CSS phase 5): a vertical list of stages or events, an icon
 * column with a connector line and a label and meta line beside it.
 *   .processor-timeline > .timeline-entry (--complete/--pending/--last as
 *   timeline-entry-complete, -pending, -last) > .timeline-marker >
 *   i.timeline-icon, .timeline-body > .timeline-label, .timeline-meta
 *   (.timeline-author, .timeline-meta-sep)
 * Request Details' Processor Log and History tabs (common/popups.js); the
 * History tab's event glyphs take history-icon-<category> colors.
 */

/* ===== Processor Log timeline (Automate-20qr) =====
   Pipeline stages render as a vertical timeline: icon column on the
   left (with a connecting line drawn between markers), label + meta
   on the right. Completed stages use a teal check; pending stages
   use a muted open circle and italic "Pending" meta. */
.processor-timeline {
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: var(--cm-space-2) 0;
}
.timeline-entry {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: var(--cm-space-3);
  padding-bottom: var(--cm-space-4);
  position: relative;
}
.timeline-marker {
  width: 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  position: relative;
  padding-top: 2px;
}
/* Connector line stretching from the bottom of the icon to the next
   stage. Uses ::after on the marker so it doesn't push out the layout. */
.timeline-entry:not(.timeline-entry-last) .timeline-marker::after {
  content: '';
  position: absolute;
  top: 22px;
  bottom: -16px;
  width: 2px;
  background: var(--cm-border);
}
.timeline-entry-complete:not(.timeline-entry-last) .timeline-marker::after {
  background: var(--cm-interactive);
}
.timeline-icon {
  font-size: var(--cm-text-lg);
  line-height: 1;
}
.timeline-entry-complete .timeline-icon {
  color: var(--cm-interactive);
}
.timeline-entry-pending .timeline-icon {
  color: var(--cm-text-muted);
}
.timeline-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.timeline-label {
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  line-height: var(--cm-lh-snug);
}
.timeline-entry-pending .timeline-label {
  color: var(--cm-text-muted);
  font-weight: var(--cm-fw-medium);
}
.timeline-meta {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}
.timeline-entry-pending .timeline-meta {
  font-style: italic;
}
.timeline-author {
  font-weight: var(--cm-fw-medium);
  color: var(--cm-text-body);
}
.timeline-meta-sep {
  margin: 0 var(--cm-space-1);
  color: var(--cm-text-muted);
}

/* History event glyphs (history-icon-<category>). */
.timeline-icon.history-icon-access {
  color: var(--cm-interactive);
}
.timeline-icon.history-icon-mutation,
.timeline-icon.history-icon-auth {
  color: var(--cm-text-muted);
}

}
@layer components {
/* Tasks (moved from stylesheetv3.css in CSS phase 5): the task popup (assignee picker, rework
 * row, note; the switch itself is components/switch.css), the open-task banner (--mine, --compact, the previous task line),
 * the queue's Assigned to cell, the Dashboard's My Tasks rows and the shared
 * Done-with-a-note form. Built by common/task-widgets.js and
 * common/my-tasks.js.
 */

/* ===== Request tasking (tasks context, PR2) ============================
   Task popup, rework switch, open-task banner, queue assignee cell and the
   Assigned to me chip. Tokens only; dark variants at the end. */
.task-popup__body {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  gap: var(--cm-space-4) var(--cm-space-6);
  width: 100%;
}
/* Below 720px the two columns fall back to one, in the same DOM (and so
   visual) order: assignee, then rework, category, note, notify, error. */
@media (max-width: 720px) {
  .task-popup__body {
    grid-template-columns: minmax(0, 1fr);
  }
}
.task-popup__col {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-4);
  min-width: 0;
}
/* Lets the assignee field's list grow to fill the column (a grid item
   stretches to the row's height by default, i.e. to whichever column is
   taller), instead of leaving blank space below a short list. */
.task-popup__col--assignee {
  min-height: 0;
}
/* .task-popup__field is now only a layout hook (every element that
   carries it also carries cm-field, components/field.css, which already
   supplies the display/gap/label recipe; [hidden] wins via utilities.css) -
   this grows the assignee field to fill its column instead of leaving
   blank space below a short list. */
.task-popup__col--assignee .task-popup__field {
  flex: 1;
  min-height: 0;
}
.task-popup__list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-height: 0;
  max-height: 340px;
  overflow-y: auto;
  padding: var(--cm-space-2);
  border: 1px solid var(--cm-border);
  border-radius: var(--cm-radius-lg);
  background: var(--cm-bg-surface);
}
.task-popup__group {
  padding: var(--cm-space-2) var(--cm-space-2) var(--cm-space-1);
  font-size: var(--cm-text-xs);
  font-weight: var(--cm-fw-medium);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--cm-text-muted);
}
.task-popup__person {
  display: flex;
  align-items: center;
  gap: var(--cm-space-3);
  width: 100%;
  padding: var(--cm-space-2) var(--cm-space-3);
  border: 1px solid transparent;
  border-radius: var(--cm-radius-md);
  background: transparent;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.task-popup__person:hover,
.task-popup__person.is-highlighted {
  background: var(--cm-bg-muted);
}
.task-popup__person[aria-selected='true'] {
  background: var(--cm-accent-100);
  border-color: var(--cm-accent-300);
}
.task-popup__who {
  display: flex;
  flex-direction: column;
  flex-grow: 1;
  min-width: 0;
}
.task-popup__name {
  font-weight: var(--cm-fw-medium);
  color: var(--cm-text-heading);
}
.task-popup__meta {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}
.task-popup__check {
  color: var(--cm-teal-700);
}
/* The rework row is an inset panel (components/card.css .cm-card-panel;
   [hidden] in utilities.css wins over this display); this lays out its text and
   switch. */
.task-popup__rework {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--cm-space-4);
}
.task-popup__rework-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.task-popup__rework-title {
  font-weight: var(--cm-fw-medium);
  color: var(--cm-text-heading);
}
.task-popup__rework-hint {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}
.task-popup__body textarea,
.task-popup__body select,
.task-popup__body input[type='text'] {
  width: 100%;
  box-sizing: border-box;
}
.task-popup__hint {
  margin-right: auto;
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}
.task-popup__buttons {
  display: flex;
  gap: var(--cm-space-2);
}
:root.dark .task-popup__check {
  color: var(--cm-teal-300);
}
:root.dark .task-popup__person[aria-selected='true'] {
  background: var(--cm-task-person-selected-bg);
  border-color: var(--cm-task-person-selected-border);
}

/* Open-task banner */
.task-banner {
  margin-bottom: var(--cm-space-3);
}
/* Avatar, body and actions in a row (the actions wrap under the body in a
   narrow rail); the Done-with-a-note form sits under this row. */
.task-banner__row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--cm-space-4);
  width: 100%;
}
.task-banner__body {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-1);
  flex: 1 1 16rem;
  min-width: 0;
}
.task-banner__line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--cm-space-2) var(--cm-space-3);
}
.task-banner__title {
  font-weight: var(--cm-fw-medium);
  color: var(--cm-text-heading);
}
.task-banner__by {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}
.task-banner__note {
  margin: 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.task-banner--mine .task-banner__note {
  white-space: normal;
}
/* text-muted (--cm-gray-500) only clears 4.22:1 against the mine variant's
   teal tint (--cm-accent-100), under the 4.5:1 floor; text-body is already
   dark enough to clear both themes comfortably
   (tests/unit/frontend/theme/task-banner.contrast.test.mjs). */
.task-banner--mine .task-banner__by,
.task-banner--mine .task-banner__previous {
  color: var(--cm-text-body);
}
.task-banner__actions {
  display: flex;
  align-items: center;
  gap: var(--cm-space-2);
  flex-shrink: 0;
  margin-left: auto;
}
/* The "more" menu's mount (Reply and Re-task, common/header-tools.js):
   the positioning context its menu drops from, right-aligned. */
.task-banner__more {
  position: relative;
  display: inline-flex;
}

/* Queue "Assigned to" cell: the chip itself is the avatar component
   (components/avatar.css); the cell keeps it on one line. */
.assignee-cell {
  white-space: nowrap;
}
.assignee-unassigned {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
}

/* ===== Dashboard My Tasks (tasks context, PR3) ======================== */
.my-tasks__view-all {
  font-weight: var(--cm-fw-medium);
}
.my-tasks__tools {
  display: flex;
  align-items: center;
  gap: var(--cm-space-4);
  font-size: var(--cm-text-sm);
  color: var(--cm-text-muted);
}
.my-tasks__from select {
  height: var(--cm-control-height);
  margin-left: var(--cm-space-1);
}
.my-tasks__list {
  max-height: 340px;
  overflow-y: auto;
}
.my-tasks__row {
  display: flex;
  flex-direction: column;
  padding: var(--cm-space-3) var(--cm-space-4);
  border-bottom: 1px solid var(--cm-border);
}
.my-tasks__row:last-child {
  border-bottom: 0;
}
/* Avatar, body and actions in a row; the Done-with-a-note form (below,
   .my-tasks__done-form-slot) sits under this row instead of beside it. */
.my-tasks__row-main {
  display: flex;
  align-items: flex-start;
  gap: var(--cm-space-3);
}
.my-tasks__body {
  flex-grow: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.my-tasks__line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--cm-space-2) var(--cm-space-3);
  font-size: var(--cm-text-sm);
}
.my-tasks__req {
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
  font-feature-settings: 'tnum';
}
.my-tasks__who {
  color: var(--cm-text-body);
}
.my-tasks__by {
  color: var(--cm-text-muted);
  font-size: var(--cm-text-xs);
}
.my-tasks__note {
  margin: 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-text-body);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.my-tasks__actions {
  display: flex;
  gap: var(--cm-space-2);
  flex-shrink: 0;
}

/* ===== Previous task line (banner, task-replies brief part C) ========= */
.task-banner__previous {
  margin: 0;
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ===== Compact banner (Upload Records right column, task-replies follow-up D) ===== */
.task-banner--compact .task-banner__row {
  gap: var(--cm-space-2);
}
.task-banner--compact .task-banner__note {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  white-space: normal;
}
/* flex-basis: 100% puts the actions (the more menu and Done) on their own
   row under the body (the only other row item once the avatar is hidden). */
.task-banner--compact .task-banner__actions {
  flex-basis: 100%;
}

/* ===== Done with a note (banner + My Tasks row, task-replies brief B) = */
.task-done-form {
  display: flex;
  flex-direction: column;
  gap: var(--cm-space-3);
  width: 100%;
  margin-top: var(--cm-space-3);
  padding-top: var(--cm-space-3);
  border-top: 1px solid var(--cm-border);
}
/* The field wrapper and its label are the shared field component
   (components/field.css). The textarea keeps base.css's own color (muted
   while the form submits and it is disabled) and min-height. */
.task-done-form__note {
  width: 100%;
  box-sizing: border-box;
  margin: 0;
  resize: vertical;
}
.task-done-form__buttons {
  display: flex;
  justify-content: flex-end;
  gap: var(--cm-space-2);
}

}
@layer components {
/* What's New (moved from stylesheetv3.css in CSS phase 5): the release notes popup's releases, built
 * by common/whats-new.js over /CHANGELOG.md. The 13px note and item text
 * snapped to 14px, the code chips to 12px.
 *   .whats-new__release-head (__release-title, __date), __note, __list >
 *   __item (strong, code)
 */

.whats-new__release-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--cm-space-3);
}
.whats-new__release-title {
  margin: 0;
  font-size: var(--cm-text-sm);
  font-weight: var(--cm-fw-semibold);
  color: var(--cm-text-heading);
}
.whats-new__date {
  font-size: var(--cm-text-xs);
  color: var(--cm-text-muted);
  white-space: nowrap;
}
.whats-new__note {
  margin: 6px 0 0;
  font-size: var(--cm-text-sm);
  line-height: 1.45;
  color: var(--cm-text-body);
}
.whats-new__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.whats-new__item {
  position: relative;
  padding-left: 14px;
  font-size: var(--cm-text-sm);
  line-height: 1.45;
  color: var(--cm-text-body);
}
.whats-new__item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 8px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--cm-teal-700);
}
.whats-new__item strong {
  font-weight: 600;
  color: var(--cm-text-heading);
}
.whats-new__item code {
  font-family: var(--cm-font-mono);
  font-size: var(--cm-text-xs);
  padding: 1px 4px;
  border-radius: var(--cm-radius-sm);
  background: var(--cm-bg-muted);
}

}
@layer components {
/*
 * The Request Details popup's layout (RequestDetailsPopup, common/popups.js),
 * the parts that used to be inline styles (CSS phase 5). Its older rules stay
 * in requestDetails.css, which the popup imports into the legacy layer; these
 * sit in the components layer because they compete with component rules
 * (the table's full width, the popup's row rule).
 *   .request-details__edit (the pen and check icon buttons, top right of
 *   the Request Information tab)
 *   .request-details__grid > label/span.cm-field__label + __field (an
 *   input's cell) or __value (Request Information and Billing Information:
 *   label and value pairs in two aligned columns, buildDetailGrid; CSS
 *   phase 5.5 redesigns)
 *   .request-details__files-view / __files-action (the Files table columns),
 *   __none (a muted dash for a file with no actions)
 *   .request-details__log (the Delivery Log tab's scroll box, a
 *   .cm-table-scroll)
 */

.request-details__edit {
  position: absolute;
  right: 20px;
  display: flex;
  align-items: center;
}

.request-details__files-view {
  width: 65%;
}

.request-details__files-action {
  width: 35%;
}

.request-details__none {
  color: var(--cm-text-muted);
}

/* Request Information and Billing Information: label and value pairs in
   two aligned columns (buildDetailGrid in common/popups.js). The right
   padding keeps the values clear of the edit button. */
.request-details__grid {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) max-content minmax(0, 1fr);
  align-items: center;
  gap: var(--cm-space-2) var(--cm-space-4);
  box-sizing: border-box;
  width: 100%;
  padding-right: calc(var(--cm-button-height-sm) + var(--cm-space-3));
}

.request-details__value {
  min-width: 0;
  font-size: var(--cm-text-sm);
  color: var(--cm-text-heading);
  overflow-wrap: anywhere;
}

.request-details__field {
  position: relative;
  min-width: 0;
}

.request-details__field > input {
  width: 100%;
  max-width: 260px;
}

/* A locked field reads as text until Edit unlocks it, then takes the base
   input look. */
.request-details__field > input[readonly] {
  padding-left: 0;
  border-color: transparent;
  background: transparent;
  color: var(--cm-text-heading);
}

.request-details__field > input[readonly]::-webkit-calendar-picker-indicator {
  display: none;
}

/* The Delivery Log tab: about 12 attempts before the box scrolls under its
   sticky header. */
.request-details__log {
  max-height: 420px;
}

}
@layer components {
/*
 * The bill code form (common/bill-code-form.js BillCodeForm): Bill Codes'
 * Create and Edit popups and Invoice's Add Bill Type mount the same form.
 * Its field rows share one grid, so the Fees row's two fields line up under
 * State and Request Type; with the Active switch (Bill Codes) the grid takes
 * a fourth, content-sized column for it.
 * Spec: docs/specs/2026-10-12-css-loose-ends-design.md (wyhb).
 */

.bill-code-form {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--cm-space-3) var(--cm-space-4);
  width: 100%;
}

.bill-code-form--active {
  grid-template-columns: repeat(3, minmax(0, 1fr)) auto;
}

/* Section titles, the range table and its button span the form; the field
   rows take its columns (subgrid), so every row lines up. */
.bill-code-form > * {
  grid-column: 1 / -1;
}

.bill-code-form__row {
  display: grid;
  grid-template-columns: subgrid;
  align-items: end;
}

/* The switch and its label, level with the fields' controls. */
.bill-code-form__active {
  display: inline-flex;
  align-items: center;
  gap: var(--cm-space-2);
  height: var(--cm-control-height);
}

/* The range table's frame scrolls past about five rows. */
.bill-code-form__pages {
  max-height: 300px;
  overflow-x: hidden;
}

.bill-code-form__add {
  display: flex;
  justify-content: center;
}

}
@layer utilities {
/*
 * Utilities (CSS consolidation phase 5): the reviewed set of single-purpose
 * classes, in layer utilities (above components, below pages), so none
 * needs !important to win over a component. Spec:
 * docs/specs/2026-10-11-css-phase-5-design.md
 */

/* Show and hide go through the hidden attribute (el.hidden). Without
   !important a component's own display (a flex popup, a grid card) would
   beat the user agent's [hidden] rule and the element would still show. */
[hidden] {
  display: none !important;
}

/* Flex alignment. */
.jc-space-evenly {
  justify-content: space-evenly;
}
.jc-space-between {
  justify-content: space-between;
}
.jc-space-around {
  justify-content: space-around;
}
.jc-center {
  justify-content: center;
}
.jc-start {
  justify-content: flex-start;
}
.jc-end {
  justify-content: flex-end;
}
.align-center {
  align-items: center;
}
.align-start {
  align-items: flex-start;
}
.align-end {
  align-items: flex-end;
}
.align-self-center {
  align-self: center;
}
.cursor-pointer {
  cursor: pointer;
}
.align-self-start {
  align-self: flex-start;
}
.align-stretch {
  align-items: stretch;
}
/* Takes the free space of a flex column and may shrink under its content
   (a PDF viewer pane filling a tall popup body). */
.flex-fill {
  flex: 1 1 0;
  min-height: 0;
}
.flex-wrap {
  flex-wrap: wrap;
}

/* Sizes. */
.width-100 {
  width: 100%;
}
.width-50 {
  width: 50%;
}
.width-auto {
  width: auto;
}
.max-width-100 {
  max-width: 100%;
}
.max-width-95 {
  max-width: 95%;
}
.height-100 {
  height: 100%;
}
.max-height-100 {
  max-height: 100%;
}

/* Spacing. */
.pd-medium {
  padding: 5px;
}
.pd-large {
  padding: 10px;
}
.pd-top-xlarge {
  padding-top: 20px;
}
.pd-bottom-large {
  padding-bottom: 10px;
}
.pd-bottom-xlarge {
  padding-bottom: 20px;
}
.mg-none {
  margin: 0;
}
.mg-medium {
  margin: 5px;
}
.mg-large {
  margin: 10px;
}
.mg-xlarge {
  margin: 20px;
}
.mg-top-large {
  margin-top: 10px;
}
.mg-bottom-medium {
  margin-bottom: 5px;
}
.mg-bottom-large {
  margin-bottom: 10px;
}
.gap-medium {
  gap: 5px;
}
.gap-large {
  gap: 10px;
}
.gap-xlarge {
  gap: 20px;
}

/* Overflow. */
.overflow-auto {
  overflow: auto;
}
.overflow-y-auto {
  overflow-y: auto;
}
.overflow-x-auto {
  overflow-x: auto;
}

/* Read by screen readers, not drawn. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Muted secondary/meta copy (timestamps, captions) (moved from stylesheetv3.css in CSS phase 5). --cm-text-muted already
   re-resolves through the redefined :root.dark gray scale, so no separate
   dark-mode override is needed here, see tokens.css. */
.secondary-text {
  color: var(--cm-text-muted);
  font-size: var(--cm-text-sm);
}

}
