/*
 * global.css — Base styles + Sidebar + Login structural styles
 *
 * Loaded on every page regardless of theme.
 * All visual values come from CSS custom properties defined in
 * public/themes/<slug>/style.css which is loaded after this file.
 *
 * To restyle a component: override the relevant --sidebar-* or --brand-*
 * variables in your theme's style.css. For structural changes (different
 * HTML layout), register a custom Sidebar component via ThemeExports.Sidebar.
 */

/* ─────────────────────────────────────────────
   Base typography
   ─────────────────────────────────────────────
   --font-inter is injected by Next.js (next/font/google) onto <html>.
   --brand-font is the theme's preferred font name (default: Inter).
   System sans-serif is the final fallback.
   ───────────────────────────────────────────── */
html, body {
  /* --app-font lets a theme swap the primary UI font (Heale sets it to
     var(--font-jakarta)); other themes leave it unset and stay on Inter. */
  font-family: var(--app-font, var(--font-inter)), var(--brand-font, Inter), system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

/* --heading-font: display type for TITLES ONLY, opt-in per theme.
   For a brand that sets headings in a different face from body copy. NO
   REGISTERED THEME SETS IT TODAY — Cigna did (a system serif, standing in for
   the proprietary ValueSerif Pro) until it moved onto Heale's Charte chassis,
   which is Plus Jakarta Sans throughout per §3. The hook stays: it costs
   nothing, and it is the only sanctioned way to change a title face.
   The fallback is `inherit`,
   so a theme that never sets it keeps titles on the body font and nothing about
   its look changes. Wired into the title classes only — never body copy, never
   numeric metrics (`.dash-stat-value`): serif faces commonly ship old-style
   figures, which read as inconsistent sizes in a metric. */

/* ─────────────────────────────────────────────
   Login page
   ───────────────────────────────────────────── */

.login-root {
  display: flex;
  min-height: 100vh;
  font-family: var(--brand-font, Inter), system-ui, sans-serif;
}

/* ── Left: form panel ── */
.login-left {
  display: flex;
  flex: 3;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: var(--login-left-bg, #ffffff);
}

.login-form-wrapper {
  width: 100%;
  max-width: var(--login-form-width, 400px);
}

/* Logo */
.login-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 32px;
}

.login-logo-img {
  height: 36px;
  object-fit: contain;
}

.login-logo-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background-color: var(--brand-primary, #0a3373);
  flex-shrink: 0;
}

.login-logo-mark-letter {
  font-size: 18px;
  font-weight: 700;
  color: #ffffff;
}

.login-logo-text {
  font-size: 22px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}

/* Below 1024px the branded right panel (.login-right) is hidden, so this
   logo carries branding instead — hide it once .login-right takes over. */
@media (min-width: 1024px) {
  .login-logo {
    display: none;
  }
}

/* Header */
.login-header {
  margin-bottom: 32px;
}

.login-header-eyebrow {
  font-size: 12px;
  font-weight: 600;
  color: #9ca3af;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  margin-bottom: 10px;
  margin-top: 0;
}

.login-header-title {
  font-size: 26px;
  font-weight: 700;
  color: #111827;
  margin: 0;
  letter-spacing: -0.3px;
}

/* ── Auth status surfaces — shared building blocks so the magic-link
   "check your email" card, the MFA prompt, and the verifying / expired
   states on /auth/confirm and /reset-password all read the same. ── */

/* Soft brand-tinted circle wrapping a centered lucide status icon. */
.auth-status-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  margin: 0 auto 18px;
  color: var(--brand-secondary, #4374f3);
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, white);
}

/* `--brand-success` with the prior literal as fallback: Heale deliberately
   leaves --brand-success unmapped (see the quote-wizard note in
   heale/style.css), so it keeps rendering #32a15e; brands that map it
   (difc) get their own positive green here. Cigna does NOT — it runs Heale's
   chassis, and `--brand-success` is not part of it. */
.auth-status-icon--success {
  color: var(--brand-success, #32a15e);
  background-color: color-mix(in srgb, var(--brand-success, #32a15e) 13%, white);
}

.auth-status-icon--warning {
  color: var(--brand-accent, #ff6733);
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 14%, white);
}

/* Styled link/button that closes out an auth status card (back to login,
   request a new link) — brand-secondary with a lucide icon. */
.auth-status-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 20px;
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  background: none;
  border: none;
  cursor: pointer;
  text-decoration: none;
  transition: color 0.15s;
}

.auth-status-link:hover {
  color: color-mix(in srgb, var(--brand-secondary, #4374f3) 78%, black);
}

/* Magic-link sent / MFA prompt card */
.login-magic-sent {
  padding: 32px 24px;
  border-radius: 14px;
  background-color: color-mix(in srgb, var(--brand-primary, #0a3373) 4%, white);
  border: 1px solid color-mix(in srgb, var(--brand-primary, #0a3373) 12%, white);
  text-align: center;
}

.login-magic-sent-title {
  font-size: 20px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 8px;
  letter-spacing: -0.3px;
}

.login-magic-sent-body {
  font-size: 14px;
  color: #6b7280;
  line-height: 1.55;
  margin: 0;
}

.login-magic-sent-body strong {
  color: #374151;
  font-weight: 600;
}

.login-magic-back {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 20px;
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  background: none;
  border: none;
  cursor: pointer;
  transition: color 0.15s;
}

.login-magic-back:hover {
  color: color-mix(in srgb, var(--brand-secondary, #4374f3) 78%, black);
}

/* Form fields */
.login-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.login-label {
  font-size: 13px;
  font-weight: 500;
  color: #4b5563;
}

.login-input {
  width: 100%;
  height: 44px;
  border-radius: 8px;
  /* `transparent` fallback = the panel colour, i.e. exactly what this input
     rendered before the token existed. Only a theme that opts in gets a fill. */
  background-color: var(--login-input-bg, transparent);
  /* Both tokenised with their original values: an input's edge is a UI-component
     boundary (WCAG 1.4.11, 3:1), and #e5e7eb cannot reach that on any light
     panel — a theme needs to be able to supply its own boundary grey. */
  border: 1px solid var(--login-input-border, #e5e7eb);
  padding: 0 14px;
  font-size: 14px;
  color: var(--login-input-color, #111827);
  outline: none;
  box-sizing: border-box;
  transition: border-color 0.15s;
}

.login-input:focus {
  border-color: var(--login-input-focus-border, var(--brand-accent, #ff6733));
}

.login-input-wrapper {
  position: relative;
}

.login-input--password {
  padding-right: 44px;
  font-size: 16px;
}

.login-input-toggle {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: #9ca3af;
  font-size: 18px;
  line-height: 1;
  padding: 0;
}

.login-fields {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.login-forgot-row {
  display: flex;
  justify-content: flex-end;
  margin-top: 6px;
}

.login-forgot {
  font-size: 13px;
  font-weight: 500;
  color: var(--brand-secondary, #4374f3);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}

.login-error {
  font-size: 13px;
  /* Tokenised with its original value as fallback — a brand whose error hue
     isn't the default red (difc's rust) follows automatically. */
  color: var(--brand-error, #dc2626);
  margin-top: 12px;
  margin-bottom: 0;
}

/* Actions */
.login-actions {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 32px;
}

.login-submit {
  width: 100%;
  height: 48px;
  border-radius: 8px;
  background-color: var(--login-submit-bg, #374151);
  color: var(--login-submit-color, #ffffff);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s;
}

/* Hover is opt-in per brand: the fallback is the resting fill itself, so a
   theme that maps only --login-submit-bg (or nothing) renders exactly as
   before this token existed. */
.login-submit:hover:not(:disabled) {
  background-color: var(--login-submit-bg-hover, var(--login-submit-bg, #374151));
}

.login-submit:disabled {
  opacity: 0.7;
  cursor: not-allowed;
}

.login-switch-row {
  text-align: center;
}

.login-switch {
  font-size: 12px;
  color: #9ca3af;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0;
}

/* "OR" divider — horizontal rule with centered uppercase label */
.login-divider {
  display: flex;
  align-items: center;
  gap: 12px;
}

.login-divider::before,
.login-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background-color: #e5e7eb;
}

.login-divider-text {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: #9ca3af;
}

/* Outlined magic-link toggle — full-width button, subtle indigo tint */
.login-magic-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  height: 48px;
  border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--brand-secondary, #4374f3) 35%, white);
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 8%, white);
  color: var(--brand-secondary, #4374f3);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s;
}

.login-magic-toggle:hover {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, white);
  border-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 50%, white);
}

.login-magic-toggle-icon {
  flex-shrink: 0;
}

.login-terms {
  font-size: 12px;
  color: #9ca3af;
  text-align: center;
  margin: 0;
}

.login-terms-link {
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}

/* ── Right: branded panel ── */
.login-right {
  position: relative;
  flex: 2;
  overflow: hidden;
  display: none;
  background-color: var(--login-right-bg, color-mix(in srgb, var(--brand-secondary, #4374f3) 16%, white));
  background-image: var(--login-right-decoration-image, none);
  background-repeat: no-repeat;
  background-position: var(--login-right-decoration-position, right -80px bottom -220px);
  background-size: var(--login-decoration-size, 500px);
}

@media (min-width: 1024px) {
  .login-right {
    display: flex;
    flex-direction: column;
  }
}

/* Inner: logo + heading (positioned in upper area; circles are absolute at bottom) */
.login-right-inner {
  position: relative;
  z-index: 1;
  padding: 88px 44px 36px 76px;
  display: flex;
  flex-direction: column;
  gap: 44px;
}

/* Logo row */
/* Hiding the ROOT (not the <img>) is deliberate: this wrapper holds either the
   tenant logo or the fallback initial-mark + name, so a theme that opts out of a
   brand mark here loses both — no empty slot, and no stray flex gap left behind
   by a hidden child. `flex` = today's behaviour for every theme that sets
   nothing. */
.login-right-logo {
  display: var(--login-right-logo-display, flex);
  align-items: center;
  gap: 10px;
}

.login-right-logo-img {
  height: 40px;
  object-fit: contain;
}

.login-right-logo-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background-color: var(--brand-primary, #0a3373);
  flex-shrink: 0;
}

.login-right-logo-letter {
  font-size: 15px;
  font-weight: 700;
  color: #ffffff;
}

.login-right-logo-name {
  font-size: 19px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}

/* Main heading — max-width forces the 3-line break matching the reference design */
.login-right-heading {
  font-size: var(--login-right-heading-size, 46px);
  font-weight: var(--login-right-heading-weight, 800);
  font-family: var(--login-right-heading-font, var(--font-poppins), Poppins, system-ui, sans-serif);
  color: var(--login-right-heading-color, var(--brand-primary, #0a3373));
  text-align: left;
  margin: 0;
  line-height: var(--login-right-heading-line-height, 1.3);
  letter-spacing: -0.5px;
  max-width: 320px;
}

/* Decorative icon at bottom-right */
/* The <img> is Heale's own icon, hardcoded in <AuthBrandPanel>, so it rendered
   on EVERY brand's login. A theme hides it here and paints its own mark through
   --login-right-decoration-image instead. Defaults keep Heale exactly as it was:
   the img visible, no panel background image. */
.login-right-decoration {
  display: var(--login-right-decoration-display, block);
  position: absolute;
  width: var(--login-decoration-size, 500px);
  height: var(--login-decoration-size, 500px);
  bottom: -220px;
  right: -80px;
  z-index: 1;
  pointer-events: none;
  object-fit: contain;
}

/* Mobile: gutter padding so the form never touches the viewport edge,
   16px input font (prevents iOS Safari auto-zoom on focus), and larger
   tap targets on the secondary text-link buttons. */
@media (max-width: 768px) {
  .login-left {
    padding: 32px 20px;
    box-sizing: border-box;
  }

  .login-input {
    font-size: 16px;
  }

  .login-forgot,
  .login-switch,
  .login-magic-back,
  .auth-status-link {
    min-height: 44px;
    padding: 10px 0;
    display: inline-flex;
    align-items: center;
  }

  .login-forgot-row {
    margin-top: -4px;
  }
}

/* ─────────────────────────────────────────────
   Sidebar
   ───────────────────────────────────────────── */

/* THREE FIXED REGIONS, in this order: `.sidebar-logo` (never shrinks) /
   `.sidebar-nav` (the ONLY scrolling region) / `.sidebar-footer` (never
   shrinks — it holds `<ProfileSwitcher>`, i.e. profile switching AND sign-out).
   The aside takes its height from the flex parent (`.dash-root.flex.h-screen`,
   and `height: 100%` in the mobile drawer below) — never a magic px or a
   `calc()` over the topbar, both of which drift.

   NO `overflow` here on purpose: `.profile-switcher-popover` is absolutely
   positioned inside `.sidebar-footer` and is deliberately WIDER than the rail
   (260px at 240px expanded, 300px at 72px collapsed — see its own header), so
   clipping the aside would cut off the control this layout exists to keep
   reachable. Nothing needs clipping anyway once `.sidebar-nav` can actually
   shrink: the three regions then always sum to the aside's own height. */
.sidebar {
  display: flex;
  flex-direction: column;
  flex-shrink: 0;
  width: var(--sidebar-width, 240px);
  background-color: var(--sidebar-bg, #ffffff);
  border-right: var(--sidebar-border-right, 1px solid #e5e7eb);
  padding: var(--sidebar-padding, 24px 0);
  font-family: var(--brand-font, Inter), system-ui, sans-serif;
}

/* Hamburger button — hidden on desktop, shown only on mobile */
.sidebar-hamburger-bar {
  display: none;
}

.sidebar-hamburger {
  display: none;
}

/* Close button inside sidebar — hidden on desktop */
.sidebar-close {
  display: none;
}

/* Overlay behind open sidebar — hidden on desktop */
.sidebar-overlay {
  display: none;
}

/* ── Logo ── */
.sidebar-logo {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 20px;
  margin-bottom: 24px;
  text-decoration: none;
  /* Region 1 of 3 — pinned at the top, never compressed to make room for a
     long nav (that is `.sidebar-nav`'s job, below). */
  flex-shrink: 0;
}

/* Height/width are tokenised because the slot's 24px default assumes a WIDE
   wordmark: a stacked, near-square lockup (Cigna's is 141.7×93.5) shrinks to
   ~36px wide at that height and becomes unreadable. `object-fit: contain`
   means the aspect ratio is always preserved — a theme with a tall lockup
   raises the height rather than distorting anything. Fallbacks keep every
   existing theme pixel-identical. */
.sidebar-logo-img {
  /* `0` = flush left, exactly as before. `0 auto` lets a theme centre the
     lockup; the collapse toggle keeps its own `margin-left: auto`, so it stays
     pinned right either way. */
  margin: var(--sidebar-logo-img-margin, 0);
  height: var(--sidebar-logo-img-height, 24px);
  max-width: var(--sidebar-logo-img-max-width, 120px);
  object-fit: contain;
  filter: var(--sidebar-logo-img-filter, none);
}

.sidebar-logo-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  background-color: var(--brand-primary, #0a3373);
  flex-shrink: 0;
}

.sidebar-logo-mark-letter {
  font-size: 18px;
  font-weight: 700;
  color: #ffffff;
}

.sidebar-logo-text {
  font-size: 20px;
  font-weight: 600;
  color: var(--sidebar-logo-text-color, var(--brand-primary, #0a3373));
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Stacked platform-name + "Backoffice" lockup — shown for a platform-scoped
   `white_label_admin` so it's unambiguous which platform's backoffice is in
   view. Superadmin keeps the plain single-line `.sidebar-logo-text` above. */
.sidebar-logo-text-stack {
  display: flex;
  flex-direction: column;
  min-width: 0;
  flex: 1;
  overflow: hidden;
}

.sidebar-logo-text-stack .sidebar-logo-text--platform {
  flex: initial;
  line-height: 1.25;
}

.sidebar-logo-subtext {
  font-size: 11px;
  font-weight: 500;
  color: var(--sidebar-footer-sub-color, #9ca3af);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Desktop icon-rail collapse toggle — sits in the logo row, hidden on mobile
   (see `.sidebar-hamburger`-adjacent mobile media query below). */
.sidebar-collapse-toggle {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  margin-left: auto;
  border-radius: 6px;
  border: none;
  background: transparent;
  color: var(--sidebar-icon-color, #9ca3af);
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color 0.12s, color 0.12s;
}

.sidebar-collapse-toggle:hover {
  background-color: var(--sidebar-item-bg-hover, #f0f4ff);
  color: var(--sidebar-item-color, #374151);
}

/* ── Nav ── */
/* Region 2 of 3 — the sidebar's ONLY scrolling region.
   `min-height: 0` is the load-bearing declaration: a column flex item's
   automatic minimum size is its CONTENT height, so with `flex: 1` alone the nav
   grows but flatly REFUSES to shrink. A nav taller than the viewport therefore
   pushed `.sidebar-footer` past the bottom of the aside, where the shell's
   `overflow: hidden` (`.dash-root.flex.h-screen`) clipped it — which is why the
   profile switcher was unclickable rather than merely off-screen. This is also
   the single most common reason the fix "doesn't work" when only `overflow-y`
   is added.
   `flex: 1` (not `flex: 0 1 auto`) stays, so a SHORT nav on a tall viewport
   still fills the gap and holds the footer at the bottom exactly as before.

   Scrollbar is HIDDEN, not gutter-reserved. `.dash-body` can afford
   `scrollbar-gutter: stable` at full page width; this rail is only
   var(--sidebar-width, 240px) — and var(--sidebar-width-collapsed, 72px) in the
   icon rail — so a permanently reserved ~15px gutter would visibly shave every
   nav item and shift the collapsed rail's centred icons off-centre. Hiding it
   is the same treatment `.ins-pol-tabs` / `.mf-tabs` already use, and it costs zero layout shift whether or not the nav overflows.
   Wheel, trackpad, touch and keyboard (Tab scrolls the focused link into view)
   all still reach the overflow. */
.sidebar-nav {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-width: none;
  gap: 2px;
}

.sidebar-nav::-webkit-scrollbar {
  display: none;
}

/* Same rule (and reason) as `.dash-body > *`: items must overflow INTO the
   scroll rather than being squeezed, so expanding a `.sidebar-subitem` group on
   a short viewport lengthens the scroll instead of compressing the rows. */
.sidebar-nav > * {
  flex-shrink: 0;
}

/* Separates a temporary/informal nav item group from the main list (e.g. the
   insurer-only "Users" entry) — plain top border matching the sidebar's own
   right-edge border colour. */
.sidebar-nav-divider {
  /* Own token, decoupled from --sidebar-border-right so a theme (Heale) can drop
     the rail's vertical border while keeping this one intentional group divider.
     Falls back to --sidebar-border-right so themes that never set the new token
     (property-finder, backoffice) keep their previous divider exactly. */
  border-top: var(--sidebar-nav-divider-border, var(--sidebar-border-right, 1px solid #e5e7eb));
  margin: 8px var(--sidebar-item-margin-x, 12px);
}

.sidebar-item {
  display: flex;
  align-items: center;
  gap: var(--sidebar-item-gap, 10px);
  padding: var(--sidebar-item-padding, 10px 12px);
  margin: var(--sidebar-item-margin, 0 12px);
  border-radius: var(--sidebar-item-radius, 8px);
  font-size: var(--sidebar-item-font-size, 14px);
  font-weight: 500;
  color: var(--sidebar-item-color, #374151);
  text-decoration: none;
  transition: background-color 0.12s, color 0.12s;
  cursor: pointer;
  background: none;
  border: none;
  /* width accounts for margin on both sides */
  width: calc(100% - 2 * var(--sidebar-item-margin-x, 12px));
  text-align: left;
}

.sidebar-item:hover {
  background-color: var(--sidebar-item-bg-hover, #f0f4ff);
  color: var(--sidebar-item-color, #374151);
}

.sidebar-item--active {
  background-color: var(--sidebar-item-bg-active, #0a3373);
  color: var(--sidebar-item-color-active, #ffffff);
  font-weight: 700;
}

.sidebar-item--active:hover {
  background-color: var(--sidebar-item-bg-active, #0a3373);
  color: var(--sidebar-item-color-active, #ffffff);
}

.sidebar-item-icon {
  color: var(--sidebar-icon-color, #6b7280);
  flex-shrink: 0;
}

.sidebar-item--active .sidebar-item-icon {
  color: var(--sidebar-icon-color-active, #ffffff);
}

.sidebar-item-label {
  flex: 1;
}

/* Tenant-module-disabled nav item (e.g. Contact Center off for the tenant) —
   stays visible AND clickable (navigates to the page's own disabled empty
   state), unlike the chatbot item which is hidden outright. Only the visual
   treatment changes: muted colors, no active-route tint, a trailing lock icon. */
.sidebar-item--disabled {
  opacity: 0.55;
}

.sidebar-item--disabled:hover {
  background-color: transparent;
  color: var(--sidebar-item-color, #374151);
}

.sidebar-item-lock {
  margin-left: auto;
  color: var(--sidebar-icon-color, #9ca3af);
  flex-shrink: 0;
}

/* `<Tooltip>` wrapper around a locked nav item (permission missing or tenant
   module off). The anchor span must behave like the item it wraps inside the
   flex-column nav — otherwise the inline-flex default shrink-wraps the link
   and the row stops filling the rail. */
.sidebar-item-tooltip {
  display: block;
  width: 100%;
}

/* Unread badge */
.sidebar-badge {
  margin-left: auto;
  background-color: var(--sidebar-badge-bg, #ff6733);
  color: var(--sidebar-badge-color, #ffffff);
  font-size: 11px;
  font-weight: 600;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* ── Sub-items (dependants, contact-center children) ── */
.sidebar-subitem {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: var(--sidebar-subitem-padding, 8px 12px 8px 40px);
  /* Tokenised so a theme that widens --sidebar-item-margin-x can keep sub-items
     on the same left edge as their parents; 12px is what this always was. */
  margin: 0 var(--sidebar-subitem-margin-x, 12px);
  border-radius: var(--sidebar-item-radius, 8px);
  font-size: var(--sidebar-subitem-font-size, 13px);
  font-weight: 400;
  color: var(--sidebar-subitem-color, #6b7280);
  text-decoration: none;
  transition: background-color 0.12s, color 0.12s;
}

.sidebar-subitem:hover {
  background-color: var(--sidebar-item-bg-hover, #f0f4ff);
}

/* Deliberately SOFTER than `.sidebar-item--active`'s solid navy fill: the
   parent nav item marks "which section you're in" (strong), the sub-item
   marks "which item within it is active" (subtle) — sharing one solid dark
   fill made the two blend into a single block instead of reading as a
   parent→child hierarchy. Heale maps this to the pale blue-50 tint (the same
   token already used for status-tint surfaces/badges) with navy-700 text,
   never the same navy as the parent. */
.sidebar-subitem--active {
  background-color: var(--sidebar-subitem-bg-active, #e8effc);
  color: var(--sidebar-subitem-color-active, #0a3373);
  font-weight: 500;
}

.sidebar-subitem--active:hover {
  background-color: var(--sidebar-subitem-bg-active, #e8effc);
  color: var(--sidebar-subitem-color-active, #0a3373);
}

.sidebar-subitem-icon {
  color: var(--sidebar-subitem-color, #6b7280);
  flex-shrink: 0;
}

.sidebar-subitem--active .sidebar-subitem-icon {
  color: var(--sidebar-subitem-color-active, #0a3373);
}

/* Dynamically-resolved sub-item content (e.g. the active quote request's
   name + status dot under "Quote Requests" — see sidebar.tsx's
   `dynamicSubItem`). The dot's fill is set inline per-status from the SAME
   `quoteStatusConfig()` map the dashboard's own status pills read (quotes
   are the one feature with a large, per-status color vocabulary that isn't
   worth re-encoding as CSS modifiers here), matching the established
   convention in `quote-status.tsx`. */
.sidebar-subitem-status-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}

.sidebar-subitem-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Collapsed icon rail (desktop only — see `.sidebar-collapse-toggle` /
   mobile media query for the ≤768px cutoff) ── */
@media (min-width: 769px) {
  .sidebar--collapsed {
    width: var(--sidebar-width-collapsed, 72px);
  }

  .sidebar--collapsed .sidebar-logo {
    flex-direction: column;
    gap: 8px;
    padding: 0 12px;
  }

  .sidebar--collapsed .sidebar-logo-text {
    display: none;
  }

  .sidebar--collapsed .sidebar-logo-img {
    max-width: var(--sidebar-logo-img-max-width-collapsed, 32px);
  }

  .sidebar--collapsed .sidebar-collapse-toggle {
    margin-left: 0;
  }

  .sidebar--collapsed .sidebar-item {
    justify-content: center;
    padding: 10px 0;
    gap: 0;
    position: relative;
  }

  .sidebar--collapsed .sidebar-item-label {
    display: none;
  }

  /* Unread count collapses to a plain dot instead of hiding entirely */
  .sidebar--collapsed .sidebar-badge {
    position: absolute;
    top: 4px;
    right: 14px;
    width: 8px;
    height: 8px;
    min-width: 0;
    padding: 0;
    font-size: 0;
    border-radius: 50%;
  }

  /* No room for labelled children — collapse to the parent icon only */
  .sidebar--collapsed .sidebar-subitem {
    display: none;
  }

  /* Profile switcher: icon/avatar only, popover widens back out so its
     content stays readable (the sidebar has no overflow clipping, so the
     wider popover overlays the dashboard content instead of being cut off). */

  /* Zero the footer padding in the collapsed rail: the trigger's own
     symmetric padding defines the box. Without this, .profile-switcher's
     `margin: -16px -20px` cancels only the footer's TOP padding — the 16px
     BOTTOM padding survives (no following content to pull up) and left ~16px
     of dead space under the avatar, making the box too tall and pushing the
     avatar visually high. Border-top is preserved by re-declaring it. */
  .sidebar--collapsed .sidebar-footer {
    padding: 0;
    /* A footer inset sized for the 240px rail leaves too little room at 72px.
       Falls through to --sidebar-footer-margin and then to 0, so a theme that
       sets neither is unchanged. */
    margin: var(--sidebar-footer-margin-collapsed, var(--sidebar-footer-margin, 0));
  }

  .sidebar--collapsed .sidebar-footer .profile-switcher {
    margin: 0;
  }

  .sidebar--collapsed .profile-switcher-trigger {
    justify-content: center;
    padding: 10px 8px;
  }

  .sidebar--collapsed .profile-switcher-trigger-info,
  .sidebar--collapsed .profile-switcher-chevron {
    display: none;
  }

  .sidebar--collapsed .profile-switcher-popover {
    left: 8px;
    right: auto;
    width: 300px;
  }
}

/* ── Footer ── */
/* ── Profile Switcher ──────────────────────────────────────────────────────── */

.profile-switcher {
  position: relative;
}

/* When rendered inside .sidebar-footer, break out of the footer's padding so
   the trigger stretches edge-to-edge and flush to the bottom (no empty gap). */
.sidebar-footer .profile-switcher {
  margin: -16px -20px;
}

/* Trigger — full-width clickable row, looks like part of the footer.
   `border-radius` MATCHES the footer's own (`--sidebar-footer-radius`): the
   trigger breaks out of `.sidebar-footer`'s padding via the negative margin
   above, so on a theme where the footer is a rounded card (Heale) a square
   hover fill visibly spilled past its corners — that was the broken hover, not
   a missing state. `background-color` (not `background`) so the footer card's
   own surface shows through when idle instead of being reset. */
.profile-switcher-trigger {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 12px 20px;
  background-color: transparent;
  border: none;
  border-radius: var(--sidebar-footer-radius, 0);
  cursor: pointer;
  text-align: left;
  transition: background-color 0.12s;
}

/* CONTROL hover (this is a button, not a list row) — the sidebar's own control
   tint, never a hardcoded black wash: rgba(0,0,0,.03) over a cream/white
   sidebar card read as a grey smudge instead of a tint of the theme. */
.profile-switcher-trigger:hover:not(:disabled) {
  background-color: var(--sidebar-item-bg-hover, rgba(0, 0, 0, 0.03));
}

.profile-switcher-trigger--open {
  background-color: var(--sidebar-item-bg-hover, rgba(0, 0, 0, 0.04));
}

/* Keyboard focus: an inset OUTLINE, so nothing about the box changes — no
   border appearing, no reflow, and no clipping at the footer's rounded edge. */
.profile-switcher-trigger:focus-visible {
  outline: 2px solid var(--brand-secondary, #4374f3);
  outline-offset: -2px;
}

.profile-switcher-trigger:disabled {
  cursor: default;
}

/* Company logo — shown instead of letter avatar when tenant has a logo_url.
   Tokenised because the fill/plate has to suit whatever asset the tenant
   uploaded: a transparent PNG needs the pale plate, a mark that ships its own
   coloured tile needs a white one. Fallbacks are the original values, so every
   existing theme is unchanged. */
.profile-switcher-logo {
  width: var(--profile-switcher-logo-size, 30px);
  height: var(--profile-switcher-logo-size, 30px);
  object-fit: contain;
  border-radius: var(--profile-switcher-logo-radius, 6px);
  flex-shrink: 0;
  background: var(--profile-switcher-logo-bg, #f1f5fe);
}

/* Shared avatar used in trigger and popover items */
.profile-switcher-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  min-width: 30px;
  height: 30px;
  border-radius: 8px;
  font-size: 12px;
  font-weight: 700;
  color: #ffffff;
  flex-shrink: 0;
}

.profile-switcher-trigger-info {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.profile-switcher-trigger-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--sidebar-footer-color, #374151);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.3;
}

.profile-switcher-trigger-role {
  font-size: 11px;
  color: var(--sidebar-footer-sub-color, #9ca3af);
  line-height: 1.3;
}

.profile-switcher-chevron {
  color: var(--sidebar-footer-sub-color, #9ca3af);
  flex-shrink: 0;
  opacity: 0.7;
}

.profile-switcher-spinner {
  color: var(--sidebar-footer-sub-color, #9ca3af);
  flex-shrink: 0;
  animation: spin 0.8s linear infinite;
}

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

/* Popover — slides up from above the trigger.
   Fixed width (not left+right insets) because the sidebar itself is only
   var(--sidebar-width, 240px) wide — insets alone left ~216px, too tight for
   an avatar + name + role label + check icon. The sidebar has no overflow
   clipping (same reasoning as the collapsed-rail override below), so the
   popover is free to overlay the dashboard content slightly instead of
   being squeezed to the rail's width. */
.profile-switcher-popover {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 12px;
  width: 260px;
  background: #ffffff;
  border: 1px solid #d2d9e5;
  border-radius: 12px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06);
  overflow: hidden;
  z-index: 200;
  animation: popover-up 0.15s ease;
}

@keyframes popover-up {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}

.profile-switcher-popover-label {
  font-size: 11px;
  font-weight: 600;
  color: #9ca3af;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 10px 12px 6px;
  margin: 0;
}

.profile-switcher-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 12px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  transition: background 0.1s;
}

.profile-switcher-item:last-child {
  margin-bottom: 4px;
}

.profile-switcher-item:hover:not(:disabled) {
  background: #f1f5fe;
}

.profile-switcher-item--active {
  cursor: default;
}

.profile-switcher-item-info {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.profile-switcher-item-name {
  font-size: 13px;
  font-weight: 500;
  color: #111827;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  line-height: 1.3;
}

.profile-switcher-item-role {
  font-size: 11px;
  color: #6b7280;
  line-height: 1.3;
}

.profile-switcher-item-check {
  color: #4374f3;
  flex-shrink: 0;
}

.profile-switcher-divider {
  height: 1px;
  margin: 4px 0;
  background: #eef1f6;
  border: none;
}

.profile-switcher-action {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 500;
  color: #374151;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  text-decoration: none;
  transition: background 0.1s, color 0.1s;
}

.profile-switcher-action:last-child {
  margin-bottom: 4px;
}

.profile-switcher-action:hover {
  background: #f1f5fe;
}

.profile-switcher-action-icon {
  color: #6b7280;
  flex-shrink: 0;
}

.profile-switcher-action--danger:hover {
  background: #fef2f2;
  color: #dc2626;
}

.profile-switcher-action--danger:hover .profile-switcher-action-icon {
  color: #dc2626;
}

/* ── Sidebar Footer ──────────────────────────────────────────────────────────── */

/* Region 3 of 3 — pinned at the bottom. `flex-shrink: 0` so a nav longer than
   the viewport can never squeeze or displace it: `<ProfileSwitcher>` (profile
   switching + sign-out) has to stay clickable at every viewport height, and it
   is the region that was being clipped away entirely before `.sidebar-nav`
   became the scroll region. */
.sidebar-footer {
  padding: 16px 20px;
  flex-shrink: 0;
  border-top: var(--sidebar-footer-border, 1px solid #e5e7eb);
  /* Optional "profile card" treatment — off by default (transparent/no radius/
     no shadow/no margin), enabled per theme via these tokens. */
  background-color: var(--sidebar-footer-bg, transparent);
  border-radius: var(--sidebar-footer-radius, 0);
  box-shadow: var(--sidebar-footer-shadow, none);
  margin: var(--sidebar-footer-margin, 0);
}


.sidebar-user {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 12px;
  overflow: hidden;
}

.sidebar-avatar {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  min-width: 36px;
  height: 36px;
  border-radius: 999px;
  background-color: var(--sidebar-avatar-bg, #4374f3);
  flex-shrink: 0;
}

.sidebar-avatar-initials {
  font-size: 13px;
  font-weight: 600;
  color: var(--sidebar-avatar-color, #ffffff);
}

.sidebar-user-info {
  min-width: 0;
  flex: 1;
}

.sidebar-user-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--sidebar-footer-color, #374151);
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-user-role {
  font-size: 11px;
  color: var(--sidebar-footer-sub-color, #9ca3af);
  margin: 0;
  text-transform: capitalize;
}

.sidebar-signout {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 0;
  font-size: 13px;
  color: var(--sidebar-footer-sub-color, #9ca3af);
  background: none;
  border: none;
  cursor: pointer;
  transition: color 0.12s;
}

.sidebar-signout:hover {
  color: var(--sidebar-footer-color, #374151);
}

/* ─────────────────────────────────────────────
   Dashboard — shared components
   (dash-topbar, dash-bell, dash-body, dash-stats, dash-stat-card, dash-status)
   ───────────────────────────────────────────── */

/* ── Top bar ── */
.dash-topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--dash-topbar-padding, 12px 28px);
  background-color: var(--dash-topbar-bg, #f8fafd);
  flex-shrink: 0;
}

/* Separator below topbar — inset so it aligns with content padding */
.dash-topbar-sep {
  height: 1px;
  background-color: var(--dash-topbar-sep-color, #d2d9e5);
  flex-shrink: 0;
}

.dash-topbar-breadcrumb {
  margin-bottom: 4px;
}

/* The title block. Was an inline style on the element (flex/minWidth), which no
   theme could override — same reason every other visual value lives here. */
.dash-topbar-main {
  flex: 1;
  min-width: 0;
}

.dash-topbar-title-row {
  display: flex;
  align-items: center;
  gap: 10px;
}


.dash-topbar-title {
  font-size: var(--dash-topbar-title-size, 32px);
  font-family: var(--heading-font, inherit);
  font-weight: 800;
  color: var(--dash-topbar-title-color, #18181b);
  margin: 0;
  letter-spacing: var(--dash-topbar-title-tracking, -0.6px);
}

.dash-topbar-badge {
  font-size: var(--dash-topbar-badge-size, 10px);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dash-topbar-badge-color, var(--brand-accent, #ff6733));
  background-color: var(--dash-topbar-badge-bg, color-mix(in srgb, var(--brand-accent, #ff6733) 12%, white));
  padding: 3px 10px;
  border-radius: var(--dash-topbar-badge-radius, 100px);
}

/* ── THE INFORMATIONAL BADGE ──
   A badge that states a neutral FACT rather than a role. The default badge is
   coral, which on this system means "problem / attention" (role badge, action
   needed, action-needed row rule) — so `/hr/quotes`'s "Renewal in 136 days" wore
   it and made the page announce a problem it does not have.

   Blue-50 ground with navy text: the §1 informational pair, and deliberately
   NOT one of the §12 status tones (gold/green/coral all carry a verdict) and not
   the blue-400 action tone either, which belongs to controls. It states a fact
   and asks for nothing.

   There is NO urgency variant, on purpose: this codebase has no renewal-urgency
   notion to reuse, and inventing a threshold here would put a product decision
   in a stylesheet. Neutral at every distance until one exists. */
.dash-topbar-badge--info {
  color: var(--dash-topbar-badge-info-color, #0a3373);
  background-color: var(--dash-topbar-badge-info-bg, #e8effc);
}

.dash-topbar-title-tag {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6b7280;
  background: #f3f4f6;
  border: 1px solid #e5e7eb;
  border-radius: 6px;
  padding: 3px 8px;
  margin-left: 6px;
  align-self: center;
}

.dash-topbar-company-suffix {
  font-size: var(--dash-topbar-title-size, 32px);
  font-weight: 800;
  color: var(--dash-topbar-title-color, #0f172a);
  margin-left: 4px;
}

.dash-topbar-date {
  font-size: 13px;
  color: var(--dash-topbar-date-color, #71717a);
  margin: 4px 0 0;
}

.dash-topbar-right {
  display: flex;
  align-items: center;
  gap: 24px;
}

/* Inline action group inside a `<DashTopbar right>` slot (e.g. a back link +
   notification bell) — the 10px gap the claim detail topbars use, so the
   skeleton and real topbars stay pixel-identical without an inline style. */
.dash-topbar-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* The `/hr` "Insured lives" mini-widget that used to live in the topbar's right
   slot was REMOVED with the Charte 2026 pass on that page: §5 allows the topbar
   to carry title + subtitle + role badge + bell and nothing else, and the three
   numbers it showed are already the `.dash-stats` row's first two cards. Its
   summary now reads as the topbar SUBTITLE (`hrDashboardSummary()`), so
   `.hr-dash-insured-widget` and its siblings are gone, along with their
   `--hr-dash-widget-*` tokens — no other surface ever consumed them. */

.dash-bell-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--dash-bell-size, 46px);
  height: var(--dash-bell-size, 46px);
  border-radius: 50%;
  background-color: var(--dash-bell-bg, #ffffff);
  border: 1px solid var(--dash-bell-border, #e5e7eb);
  cursor: pointer;
  color: var(--dash-bell-color, #6b7280);
  position: relative;
  flex-shrink: 0;
  box-shadow: var(--dash-bell-shadow, 0 1px 3px rgba(0, 0, 0, 0.06));
}

.dash-bell-dot {
  position: absolute;
  top: 7px;
  right: 7px;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background-color: var(--dash-bell-dot-color, var(--brand-accent, #ff6733));
  border: 1.5px solid var(--dash-topbar-bg, #ffffff);
}

/* ── Scrollable body ── */
.dash-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Reserve the scrollbar's width even when content doesn't overflow, so the
     visible content width stays constant across steps/pages regardless of
     whether that particular one happens to need vertical scroll. */
  scrollbar-gutter: stable;
  padding: var(--dash-body-padding, 24px 28px);
  background-color: var(--dash-body-bg, #f8fafd);
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Prevent children from shrinking — they must overflow into the scroll instead */
.dash-body > * {
  flex-shrink: 0;
}

/* Kanban-board variant (quotes, claims boards): the board scrolls
   internally, so the body itself must not scroll and its card child must
   grow to fill it instead of being clipped to content height. Doubled
   class name for specificity over `.dash-body > *` above.
   (`.hr-dash-recent-card--kanban`, the card-side half of this pair, was
   dropped — no surface renders it; §11's board is still unbuilt.) */
.dash-body.dash-body--kanban {
  overflow-y: hidden;
}

/* ── HR dashboard — the coverage band + rail surface (`/hr` only) ──
   This family REPLACED the dismissible "Your team, covered" marketing hero and
   the dark quick-action stat card, both deleted here and in the Heale theme (the
   new stats row renders neither). `/hr` is the only consumer of every class below, which is what
   licenses styling them on their base rule — see the blast-radius guard in
   `src/app/(dashboard)/hr/__tests__/hr-dashboard-charte-2026.test.ts`.

   Every value is a consumed token with a literal fallback, so a white-label that
   maps nothing still renders a coherent card. */

.hr-dash-grid {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--hr-dash-grid-gap, 20px);
  align-items: start;
}

.hr-dash-col {
  display: flex;
  flex-direction: column;
  gap: var(--hr-dash-col-gap, 18px);
  min-width: 0;
}

/* ── Coverage band ──
   §8: no border of its own — the shadow carries it. The insurer panel is flush
   to the band's right edge, so the band clips its children instead of padding
   them. */
.hr-dash-band {
  display: flex;
  align-items: stretch;
  overflow: hidden;
  border: none;
  border-radius: var(--hr-dash-band-radius, 18px);
  background: var(--hr-dash-band-bg, #0a3373);
  box-shadow: var(--hr-dash-band-shadow, none);
  min-height: var(--hr-dash-band-min-height, 168px);
}

.hr-dash-band-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: var(--hr-dash-band-gap, 22px);
  padding: var(--hr-dash-band-padding, 26px 28px);
}

.hr-dash-band-copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
}

.hr-dash-band-title {
  margin: 0;
  font-family: var(--heading-font, inherit);
  font-size: var(--hr-dash-band-title-size, 21px);
  font-weight: 800;
  letter-spacing: var(--hr-dash-band-title-tracking, -0.02em);
  color: var(--hr-dash-band-title-color, #ffffff);
}

.hr-dash-band-text {
  margin: 0;
  max-width: 46ch;
  font-size: 12.5px;
  line-height: 19px;
  color: var(--hr-dash-band-text-color, rgba(255, 255, 255, 0.72));
}

/* §6 primary: blue-400 pill, white label, 44px tap target. */
.hr-dash-band-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 4px;
  min-height: 44px;
  padding: var(--hr-dash-band-cta-padding, 11px 22px);
  border: none;
  border-radius: var(--hr-dash-band-cta-radius, 999px);
  background-color: var(--hr-dash-band-cta-bg, #4374f3);
  color: var(--hr-dash-band-cta-color, #ffffff);
  font-size: 13px;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.15s;
}

.hr-dash-band-cta:hover {
  background-color: var(--hr-dash-band-cta-bg-hover, #1d4ed8);
}

/* The insurer panel. Dropped entirely (not rendered) when there is no insurer,
   so the dark band spans the full width. */
.hr-dash-band-aside {
  flex-shrink: 0;
  width: var(--hr-dash-band-aside-width, 188px);
  padding: var(--hr-dash-band-aside-padding, 22px);
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--hr-dash-band-aside-bg, #4374f3);
}

.hr-dash-band-logo {
  width: 100%;
  height: auto;
  max-height: var(--hr-dash-band-logo-max-height, 56px);
  object-fit: contain;
}

/* Name-as-text fallback for an insurer with no logo on file. */
.hr-dash-band-insurer {
  margin: 0;
  text-align: center;
  font-size: 15px;
  font-weight: 700;
  line-height: 20px;
  color: var(--hr-dash-band-insurer-color, #ffffff);
}

/* ── Coverage ring ──
   The arc is an SVG stroke so the percentage is one geometric value
   (`stroke-dashoffset`, an ATTRIBUTE, never an inline style); every colour is a
   token here. */
.hr-dash-ring {
  position: relative;
  flex-shrink: 0;
  width: var(--hr-dash-ring-size, 84px);
  height: var(--hr-dash-ring-size, 84px);
  display: flex;
  align-items: center;
  justify-content: center;
}

.hr-dash-ring-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.hr-dash-ring-track {
  fill: none;
  stroke: var(--hr-dash-ring-track-color, rgba(255, 255, 255, 0.2));
  stroke-width: var(--hr-dash-ring-width, 5);
}

.hr-dash-ring-bar {
  fill: none;
  stroke: var(--hr-dash-ring-bar-color, #ffffff);
  stroke-width: var(--hr-dash-ring-width, 5);
  stroke-linecap: round;
  transition: stroke-dashoffset 0.4s ease;
}

.hr-dash-ring-label {
  position: relative;
  font-size: var(--hr-dash-ring-label-size, 19px);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--hr-dash-ring-label-color, #ffffff);
}

/* ── Card rows (attention + waiting-for-a-policy) ── */
.hr-dash-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: var(--hr-dash-row-padding, 13px 20px);
}

.hr-dash-row:not(:last-child) {
  border-bottom: 1px solid var(--hr-dash-row-border, #eaeff6);
}

/* §12: an urgent row gets a subtle tint of the rejected tone, never a full fill. */
.hr-dash-row--urgent {
  background-color: var(--hr-dash-row-urgent-bg, rgba(220, 38, 38, 0.05));
}

.hr-dash-row-main {
  flex: 1;
  /* Required for the sub-line's truncation to bite: without it the flex child
     sizes to its longest word and pushes the row's action pill out instead. */
  min-width: 0;
}

.hr-dash-row-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--hr-dash-row-title-color, #18181b);
}

/* ONE line, ellipsised. A server-composed attention sub-line can run to a full
   paragraph ("Still outstanding: Phone number, Gender, …"); wrapping it turns a
   dense list of rows into a wall of text and pushes the CTA off the row. The full
   string stays reachable as the element's `title`. */
.hr-dash-row-sub {
  margin: 2px 0 0;
  font-size: 11.5px;
  color: var(--hr-dash-row-sub-color, #9ca3af);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  line-clamp: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}

.hr-dash-row-link {
  color: inherit;
  text-decoration: none;
}

.hr-dash-row-link:hover {
  text-decoration: underline;
}

.hr-dash-row-aside {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

.hr-dash-wait-avatar {
  flex-shrink: 0;
  width: var(--hr-dash-avatar-size, 34px);
  height: var(--hr-dash-avatar-size, 34px);
  border-radius: 999px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  font-weight: 700;
  background-color: var(--hr-dash-avatar-bg, #eef2f9);
  color: var(--hr-dash-avatar-color, #0a3373);
}

/* ── "Need a hand?" broker card ──
   Static pending broker features: the media block is a token-tinted panel, not a
   remote image, so no white-label ships a stranger's photograph. */
.hr-dash-broker-media {
  position: relative;
  height: var(--hr-dash-broker-media-height, 128px);
  background: var(--hr-dash-broker-media-bg, linear-gradient(135deg, #eef2f9 0%, #ffffff 100%));
}

.hr-dash-broker-caption {
  position: absolute;
  left: 14px;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background-color: var(--hr-dash-broker-caption-bg, #ffffff);
  color: var(--hr-dash-broker-caption-color, #0a3373);
}

.hr-dash-broker-body {
  padding: var(--hr-dash-broker-body-padding, 18px 20px 20px);
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.hr-dash-broker-title {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  color: var(--hr-dash-broker-title-color, #18181b);
}

.hr-dash-broker-text {
  margin: 0;
  font-size: 12.5px;
  line-height: 18px;
  color: var(--hr-dash-broker-text-color, #4b5563);
}

.hr-dash-broker-actions {
  display: flex;
  gap: 10px;
  margin-top: 6px;
}

/* Equal-width pair (never label-length-driven), each keeping the 44px target. */
.hr-dash-broker-actions > * {
  flex: 1;
  justify-content: center;
  min-height: 44px;
}

/* ── Loading parity ──
   The skeleton reuses the band shell so nothing shifts when the fetch resolves;
   only the fill flips to a light surface, since the shimmer bars are dark-on-light. */
.hr-dash-band--loading {
  background: var(--hr-dash-band-loading-bg, #ffffff);
}

/* ── Stats row ── */
.dash-stats {
  display: flex;
  gap: 16px;
}

/* A row that carries only one or two metrics, because the rest of them belong to
   a section this role can't see (e.g. the insurer dashboard without
   `portfolio.policies:read`, which keeps only "Pending Tasks"). `.dash-stat-card`'s
   `flex: 1` would stretch that lone card the full page width; here the cards keep
   a card's width and the row simply ends. The mobile 2-column grid below already
   ignores flex, so it needs no counterpart rule. */
.dash-stats--compact .dash-stat-card {
  flex: 0 1 var(--dash-stats-compact-basis, 260px);
}

.dash-stat-card {
  flex: 1;
  background-color: var(--dash-stat-bg, #ffffff);
  border: 1px solid var(--dash-stat-border, #d2d9e5);
  border-radius: var(--dash-stat-radius, 14px);
  padding: var(--dash-stat-padding, 22px 24px);
  /* Soft "floating card" shadow — off by default, set by themes that want it. */
  box-shadow: var(--dash-stat-shadow, none);
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.dash-stat-label {
  font-size: var(--dash-stat-label-size, 10px);
  font-weight: var(--dash-stat-label-weight, 600);
  letter-spacing: var(--dash-stat-label-tracking, 0.09em);
  text-transform: uppercase;
  color: var(--dash-stat-label-color, #9ca3af);
  margin: 0;
}

.dash-stat-value {
  font-size: var(--dash-stat-value-size, 36px);
  font-weight: var(--dash-stat-value-weight, 700);
  letter-spacing: var(--dash-stat-value-tracking, normal);
  color: var(--dash-stat-value-color, #18181b);
  line-height: 1.1;
  margin: 4px 0 2px;
}

.dash-stat-sub {
  font-size: 13px;
  color: var(--dash-stat-sub-color, #9ca3af);
  margin: 0;
}

/* ── Stat card states (<StatCard>) ──
   Previously inline styles on the component; promoted here so a white-label can
   retint them and so no call site needs a style prop. */
/* A card that acts (filter toggle via `onClick`, drill-down via `href`). The
   affordance has to read BEFORE the pointer arrives, so the tone comes from the
   control-hover token — never the row-hover one, which is a cream wash and
   invisible on an already-light card. As a `<Link>` it also has to shed the
   anchor's inherited text colour/underline. */
.dash-stat-card--clickable {
  text-align: left;
  cursor: pointer;
  font: inherit;
  color: inherit;
  text-decoration: none;
  transition: background-color .12s ease, box-shadow .12s ease, transform .12s ease;
}

.dash-stat-card--clickable:hover {
  /* Own token, falling back to the shared control hover: these cards act as
     filters, so a theme tinting them must not be forced to tint every icon
     button too. */
  background-color: var(--dash-stat-bg-hover, var(--ins-pol-filter-bg-hover, #f2ede5));
  box-shadow: var(--dash-stat-shadow-hover, var(--dash-stat-shadow, none));
}

.dash-stat-card--clickable:focus-visible {
  outline: 2px solid var(--brand-secondary, #4374f3);
  outline-offset: 2px;
}

/* Selected state. Tokenised because the border + hard 1px ring together read as
   a 2px saturated outline, which is heavy on any theme whose --brand-primary is
   a deep, saturated colour — and it fights the borderless-card convention
   (cards are shadow-lifted, not outlined). Fallbacks are the original values, so
   themes that want the outline keep it exactly. */
.dash-stat-card--active {
  border-color: var(--dash-stat-active-border, var(--brand-primary, #0a3373));
  box-shadow: var(--dash-stat-active-shadow, 0 0 0 1px var(--brand-primary, #0a3373));
}

.dash-stat-card--highlight {
  border-color: var(--dash-stat-highlight-border, #f59e0b);
  background-color: var(--dash-stat-highlight-bg, #fffbeb);
}

/* Loading placeholders: the real text stays in the flow (reserving its height)
   with `visibility: hidden`, and the shimmer overlays it. */
.dash-stat-slot {
  position: relative;
}

.dash-stat-hidden {
  visibility: hidden;
}

.dash-stat-shimmer {
  position: absolute;
  inset: 0;
  margin: auto 0;
  border-radius: 3px;
}

.dash-stat-shimmer--label { height: 10px; width: 55%; }
.dash-stat-shimmer--value { height: 60%; width: 45%; border-radius: 6px; }
.dash-stat-shimmer--sub   { height: 10px; width: 70%; }

/* ─────────────────────────────────────────────
   Dashboard — HR specific
   (hr-dash-grid/-band/-row/-broker above, hr-dash-recent-*)
   ─────────────────────────────────────────────  */

/* ── Recent Employees table card ── */
.hr-dash-recent-card {
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5);
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  /* Soft "floating card" shadow — off by default, set by themes that want it. */
  box-shadow: var(--hr-dash-recent-card-shadow, none);
  overflow: hidden;
}

/* Tinted variant — a card that's a read-only summary rather than an editable
   list: `/hr/quotes/new`'s "Using your company information" and "What happens
   next". Same shell (radius/shadow), a tinted fill instead of white.

   It has its OWN token because this surface sits directly ON THE PAGE BODY,
   while `--modal-total-bg` — the tone it used to borrow — is §8's INNER-CELL
   tone: cream-50 inside a WHITE card, which is what every one of its ~28 other
   consumers is (`.modal-summary`, `.task-detail-meta-cell`, `.ci-count-summary`,
   `.qw-tpl-block`…). On Heale that token resolves to cream-50 and `--dash-body-bg`
   is cream-50 too, so a tinted card painted with it was literally invisible —
   yet retuning the shared token would have wrongly repainted all 28 inner cells.
   Hence a dedicated token, falling back THROUGH the old one so a theme that maps
   only `--modal-total-bg` (difc) renders exactly as it does today. */
.hr-dash-recent-card--tinted {
  background-color: var(--hr-dash-recent-card-tinted-bg, var(--modal-total-bg, #eef2f9));
}

.hr-dash-recent-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: var(--hr-dash-recent-head-padding, 20px 24px 16px);
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

/* Head that carries only a right-aligned action (no title) — e.g. the insurer
   policy Invoices card's "Create Invoice". */
.hr-dash-recent-head--end {
  justify-content: flex-end;
}

.hr-dash-recent-title {
  font-size: 17px;
  font-family: var(--heading-font, inherit);
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
  margin: 0 0 3px;
}

.hr-dash-recent-subtitle {
  font-size: 13px;
  color: var(--hr-dash-recent-subtitle-color, #9ca3af);
  margin: 0;
}

.hr-dash-recent-btn-outline {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: var(--hr-dash-recent-btn-padding, 8px 16px);
  border-radius: var(--hr-dash-recent-btn-radius, 100px);
  background-color: var(--hr-dash-recent-btn-outline-bg, #ffffff);
  color: var(--hr-dash-recent-btn-outline-color, #374151);
  font-size: 13px;
  font-weight: 600;
  border: var(--hr-dash-recent-btn-outline-border-width, 1px) solid var(--hr-dash-recent-btn-outline-border, #d1d5db);
  cursor: pointer;
  transition: background-color 0.15s;
  white-space: nowrap;
}

.hr-dash-recent-btn-outline:disabled { opacity: .5; cursor: not-allowed; }

.hr-dash-recent-btn-outline:hover {
  background-color: var(--hr-dash-recent-btn-outline-bg-hover, #f9fafb);
}

.hr-dash-recent-btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  /* Radius/padding/shadow are tokenized so a theme can move from the historical
     pill (100px) to the mockup's soft-rounded rectangle without a base change. */
  padding: var(--hr-dash-recent-btn-padding, 8px 16px);
  border-radius: var(--hr-dash-recent-btn-radius, 100px);
  box-shadow: var(--hr-dash-recent-btn-primary-shadow, none);
  background-color: var(--hr-dash-recent-btn-primary-bg, var(--brand-primary, #0a3373));
  color: var(--hr-dash-recent-btn-primary-color, #ffffff);
  font-size: 13px;
  font-weight: var(--hr-dash-recent-btn-weight, 600);
  border: none;
  cursor: pointer;
  transition: opacity 0.15s, background-color 0.15s;
  white-space: nowrap;
}

.hr-dash-recent-btn-primary:hover {
  opacity: var(--hr-dash-recent-btn-primary-hover-opacity, 0.88);
  background-color: var(--hr-dash-recent-btn-primary-bg-hover, var(--hr-dash-recent-btn-primary-bg, var(--brand-primary, #0a3373)));
}

/* Charte §6: disabled is 45% opacity, never a grey fill swap. The
   `:disabled:hover` pair exists purely for specificity — a disabled button
   must not visually react to mouse-over (no darker fill, no opacity dip
   toward the hover value), and `:disabled:hover` (0,3,0) beats the plain
   `:hover` rule above (0,2,0) regardless of source order. */
.hr-dash-recent-btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

.hr-dash-recent-btn-primary:disabled:hover {
  opacity: 0.45;
  cursor: not-allowed;
  background-color: var(--hr-dash-recent-btn-primary-bg, var(--brand-primary, #0a3373));
}

/* The split primary button (a main half + a caret half) is RETIRED: its only
   caller, the census "Add member" control, is now one button opening a menu of
   equal choices (`<MenuButton>`), so there is no seam between two halves to draw.
   The disabled pair above still applies to that trigger — it IS a plain
   `.hr-dash-recent-btn-primary`, which is why the rule is kept and the split
   modifiers are not. */



/* "<spinner /> Saving…" label inside any button — layout only, no colour, so it
   inherits whichever button it sits in. Replaces the inline
   `style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}` span that
   every submitting modal footer used to repeat. */
.btn-busy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* Dropdown-trigger chevron that flips when its menu is open. Geometry only (no
   colour — it inherits the button), so it works on a primary, outline or icon
   trigger alike. Replaces an inline `style={{ transition, transform }}`. */
.btn-chevron {
  transition: transform 0.15s;
}

.btn-chevron--open {
  transform: rotate(180deg);
}

.hr-dash-recent-btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 16px;
  border-radius: 100px;
  background: transparent;
  color: var(--brand-primary, #0a3373);
  font-size: 13px;
  font-weight: 600;
  border: 1.5px solid var(--brand-primary, #0a3373);
  cursor: pointer;
  transition: opacity 0.15s;
  white-space: nowrap;
  text-decoration: none;
}

.hr-dash-recent-btn-secondary:hover {
  opacity: 0.78;
}

.hr-dash-recent-btn-accent {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 16px;
  border-radius: 100px;
  background-color: var(--hr-dash-recent-btn-accent-bg, var(--brand-accent, #ff6733));
  color: var(--hr-dash-recent-btn-accent-color, #ffffff);
  font-size: 13px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s;
  white-space: nowrap;
  text-decoration: none;
}

.hr-dash-recent-btn-accent:hover {
  opacity: 0.88;
}

.hr-dash-recent-btn-accent:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* The coral-tinted sibling of `.hr-dash-recent-btn-soft-primary` was deleted with
   the `/hr` Charte pass: nothing rendered it, and §6 rules coral out as a CTA
   colour, so a ready-made soft-coral button is a trap rather than a spare part. */
.hr-dash-recent-btn-soft-primary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 16px;
  border-radius: 100px;
  background-color: var(--hr-dash-recent-btn-soft-primary-bg, color-mix(in srgb, var(--brand-primary, #0a3373) 12%, white));
  color: var(--hr-dash-recent-btn-soft-primary-color, var(--brand-primary, #0a3373));
  font-size: 13px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: background-color 0.15s;
  white-space: nowrap;
  text-decoration: none;
}

.hr-dash-recent-btn-soft-primary:hover {
  background-color: var(--hr-dash-recent-btn-soft-primary-bg-hover, color-mix(in srgb, var(--brand-primary, #0a3373) 20%, white));
}

/* Table */
.hr-dash-recent-table {
  width: 100%;
  border-collapse: collapse;
}

.hr-dash-recent-th {
  padding: var(--hr-dash-recent-th-padding, 11px 24px);
  font-size: var(--hr-dash-recent-th-size, 11px);
  font-weight: var(--hr-dash-recent-th-weight, 600);
  letter-spacing: var(--hr-dash-recent-th-tracking, 0.07em);
  text-transform: uppercase;
  color: var(--hr-dash-recent-th-color, #9ca3af);
  background-color: var(--hr-dash-recent-th-bg, transparent);
  text-align: left;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
  white-space: nowrap;
}

.hr-dash-recent-tr:not(:last-child) .hr-dash-recent-td {
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

/* THE standard row-hover background for the whole app. `--hr-dash-recent-row-hover-bg`
   is the single token every ROW / LIST-ITEM hover resolves to (tables here, the
   Task Manager's `.tm-tr`, the member-form + Contact Center
   rail rows via `.mf-member-btn`, the quote-wizard rows, claim document rows) —
   Heale maps it to cream-50, the same warm tone as the page background.
   Off by default (transparent) so a theme opts in.

   Deliberately NOT used for BUTTON/CONTROL hovers (`.ins-pol-action-btn`,
   `.hr-dash-recent-btn-outline`, `.dropdown-item`,
   `.modal-close`, sidebar items…): those keep their own hover tokens
   (`--ins-pol-filter-bg-hover` is now exclusively the CONTROL hover token,
   `--sidebar-item-bg-hover` the nav one) because a cream wash on an
   already-light control would be invisible. Row-hover and control-hover used to
   share `--ins-pol-filter-bg-hover`, which is how an off tone leaked into rows. */
.hr-dash-recent-tr:hover .hr-dash-recent-td {
  background-color: var(--hr-dash-recent-row-hover-bg, transparent);
}

/* Census employee row expand/collapse + nested dependent rows (rendered as
   flattened <DataTable> rows, styled via rowClassName since the shared
   component only accepts a className per row, not inline style). */
/* Expanded parent / nested child rows. Tokenized so the Charte themes can use
   their warm neutrals instead of the historical cool blue (which other
   white-labels keep via these fallbacks). */
.hr-dash-recent-tr--clickable { cursor: pointer; }
.hr-dash-recent-tr--expanded { background-color: var(--hr-dash-recent-tr-expanded-bg, #f8fafd); }
.hr-dash-recent-tr--dependent { background-color: var(--hr-dash-recent-tr-dependent-bg, #eaf0fb); }

/* `.hr-dash-recent-tr--selected` (the single-select table's active-row tint) was
   deleted with the `/hr` Charte pass — the insurer quote detail page it was
   written for no longer renders it, and §12 now specifies row selection as a
   blue-400 left rule + blue-400 @0.06 fill (`.tm-tr--selected`), so reviving
   this one would contradict the contract rather than save work. */

/* Two-layer coverage status (spec: docs/specs/census/coverage-status.md):
   a binary COVERED/NOT COVERED badge with a lifecycle-state sub-label + dot. */
.cov-cell { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.cov-cell--inline { flex-direction: row; align-items: center; gap: 8px; }
.cov-badge {
  display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.03em; text-transform: uppercase;
  white-space: nowrap;
}
/* Charte 2026 §2 — a status surface is the semantic token at 12–16% for the
   fill with the SAME token at 100% for the label. The pre-Charte flattened
   pairs stay as the fallbacks, so only themes that map the tokens move. */
.cov-badge--covered {
  background: var(--cov-covered-bg, #e6f4ec);
  color: var(--cov-covered-color, #1f7a45);
}
.cov-badge--not {
  background: var(--cov-not-bg, #fbe9e9);
  color: var(--cov-not-color, #b42318);
}
.cov-state {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 11px; color: var(--cov-state-color, #64748b); white-space: nowrap;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis;
}
.cov-dot { width: 7px; height: 7px; border-radius: 999px; flex-shrink: 0; }
/* Tone dots — reused by Census coverage badges, the Task Manager Activity
   timeline (`.task-detail-timeline-dot`) and the Claims conversation
   timeline: the SAME neutral/pending/info/success/error semantic everywhere,
   so retuning to the Charte tone tokens (already mapped in the Heale theme)
   is a consistent correction, not a one-off. Old hex kept as the fallback for
   themes with no override. */
.cov-dot--grey { background: var(--dash-status-inactive-color, #94a3b8); }
.cov-dot--amber { background: var(--dash-status-pending-color, #eca04b); }
.cov-dot--blue { background: var(--brand-secondary, #4374f3); }
.cov-dot--green { background: var(--dash-status-approved-color, #32a15e); }
.cov-dot--red { background: var(--dash-status-rejected-color, #dc2626); }

/* Prominent coverage status for the member-editor card (<CoverageStatusDetail>) — same
   colours/tokens as the compact .cov-* cell, but a larger pill + bigger dot/state text so
   it reads as a proper status row inside the card rather than a tiny table badge. */
.cov-detail { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.cov-detail-badge {
  display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  white-space: nowrap;
}
.cov-detail-badge--covered {
  background: var(--cov-covered-bg, #e6f4ec);
  color: var(--cov-covered-color, #1f7a45);
}
.cov-detail-badge--not {
  background: var(--cov-not-bg, #fbe9e9);
  color: var(--cov-not-color, #b42318);
}
.cov-detail-state { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600; color: var(--cov-detail-state-color, #374151); }
.cov-detail-dot { width: 8px; height: 8px; border-radius: 999px; flex-shrink: 0; }

/* Borderless cell input used inside the census spreadsheet editor — keeps the
   bare "grid cell" look while reusing shared inputs (e.g. <DateField>).
   GEOMETRY IS LOCAL ON PURPOSE: 32px / radius 4 / 13px is the spreadsheet row
   rhythm, not the field standard — a 44px cell would break the grid. Colour and
   state come from the contract, which is what the cell was missing: its focus
   border was a bare `#4374f3` literal with no token at all. The resting border
   stays `transparent` as a structural choice (reserve the 1px so focus does not
   reflow the row), NOT as a colour. */
.census-sheet-input {
  width: 100%;
  height: 32px;
  border: 1px solid transparent;
  border-radius: 4px;
  padding: 0 6px;
  font-size: 13px;
  outline: none;
  background-color: transparent;
  color: var(--field-color, inherit);
}
.census-sheet-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}
.census-sheet-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  background-color: var(--field-bg, #ffffff);
}

.hr-dash-recent-td {
  padding: var(--hr-dash-recent-td-padding, 14px 24px);
  font-size: 14px;
  color: var(--hr-dash-recent-td-color, #374151);
  vertical-align: middle;
}

.hr-dash-recent-td--name {
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
}

.hr-dash-recent-td--id {
  text-align: right;
  color: var(--hr-dash-recent-id-color, #6b7280);
  font-size: 13px;
}

.hr-dash-recent-th--right {
  text-align: right;
}

.hr-dash-recent-th--center,
.hr-dash-recent-td--center {
  text-align: center;
}

/* Tighter left padding than the default 24px — used by loading skeletons
   whose first column (e.g. Member) mirrors a <DataTable> column that passes
   a tight headerStyle/cellStyle padding directly as a prop (page.tsx), which
   a plain <th> here can't replicate via a prop — a scoped class instead. */
.hr-dash-recent-th--tight-left {
  padding-left: 10px;
}

.hr-dash-recent-td--right {
  text-align: right;
}

.hr-dash-recent-empty {
  color: var(--hr-dash-recent-empty-color, #d1d5db);
}

/* ── HR census list (/hr/census) ──
   The table itself uses the SHARED `.hr-dash-recent-*` chrome with no
   page-scoped row-height modifier: it follows the same rhythm as the insurer
   portfolio/census tables (Charte 2026). Only the cells that don't exist
   anywhere else get a class here. */

/* Selection column — always present (there is no select-mode toggle). The
   native checkbox carries a UA margin around its 15px box; with zero
   horizontal padding it sat flush against the card's rounded corner
   (`.hr-dash-recent-card` is `overflow: hidden; border-radius: 16px`) and
   clipped on the first/last row — hence `margin: 0` on the input plus an
   explicit left buffer on the cell. */
.census-select-cell {
  width: 36px;
  padding-left: 14px;
  padding-right: 4px;
  text-align: center;
}

.census-checkbox {
  width: 15px;
  height: 15px;
  margin: 0;
  accent-color: var(--brand-primary, #0a3373);
  cursor: pointer;
  vertical-align: middle;
}

/* Member cell — built on `.ins-pol-company--inline` (the shared avatar+name
   row); these add only the expand affordance, truncation and the dependent
   indent. */
.census-expand {
  width: 16px;
  flex-shrink: 0;
  display: inline-flex;
  color: var(--ins-pol-th-sort-color, #cbd5e1);
}

.census-expand--open {
  color: var(--brand-secondary, #4374f3);
}

.census-member-body {
  min-width: 0;
  flex: 1;
}

.census-truncate {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A dependant belongs to the employee above it. That relationship is drawn as a
   TREE — a hairline descending from the parent row with an elbow into each
   child's name — and NOT as a tinted row: a fill reads as "selected/other kind
   of row", and a family is a shape, not a state. Both census tables (HR and
   insurer) use it, so the two surfaces agree.
   The connector lives INSIDE the member cell (never an extra column), so
   <DataTable>'s column alignment is untouched, and it is drawn with
   pseudo-elements so a clickable/hovered row keeps its own background. */
.census-member--dependent {
  padding-left: 0;
}

/* 30px so the child's name sits a readable step in from its parent's, while the
   trunk still hangs from the parent's own chevron column (8px). */
.census-tree {
  position: relative;
  flex: 0 0 auto;
  align-self: stretch;
  width: 30px;
  min-height: 18px;
}

.census-tree::before,
.census-tree::after {
  content: '';
  position: absolute;
  background-color: var(--census-tree-line, var(--hr-dash-recent-row-border, #d2d9e5));
}

/* The rule is CONTAINED in its own row's cell — it never paints across the row
   separator or outside the cell box (no negative offsets), so the table chrome
   stays unbroken and on top and the tree reads as part of each row rather than
   as an overlay drawn over the table. A middle dependant carries the rule from
   edge to edge (a tee); the LAST one stops it at the elbow — that stop is what
   makes the connector read as a tree rather than as decoration. Aligned to the
   employee row's expand chevron (`.census-expand`, 16px) so the trunk hangs
   from the parent's own glyph. */
.census-tree::before {
  left: 8px;
  top: 0;
  bottom: 0;
  width: 1px;
}

.census-tree--last::before {
  bottom: 50%;
}

.census-tree::after {
  left: 8px;
  top: 50%;
  width: 16px;
  height: 1px;
}

.census-relation {
  text-transform: capitalize;
}

/* Dependents count cell */
.census-dep-count {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
  white-space: nowrap;
}

.census-dep-count svg {
  color: var(--ins-pol-subtext-color, #9ca3af);
}

/* Account-connection icon column. Tone comes from the shared status tokens so
   it can't drift from the badges using the same semantics. */
.census-connected-cell {
  width: 44px;
  text-align: center;
}

.census-link-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  border: none;
  background: none;
  cursor: pointer;
}

.census-link-btn--connected { color: var(--dash-status-approved-color, #065f46); }
.census-link-btn--invited { color: var(--dash-status-pending-color, #a16207); }
.census-link-btn--not-connected { color: var(--dash-status-rejected-color, #b42318); }

/* Its dropdown: a read-only email line, or the email next to a Send/Resend action. */
.census-email-item {
  cursor: default;
}

.census-invite-menu {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 2px 4px;
}

.census-email-text {
  font-size: 12.5px;
  color: var(--ins-pol-type-color, #374151);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.census-invite-action {
  width: auto;
  flex-shrink: 0;
}

/* Status badges */
.dash-status {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 100px;
  font-size: 11px;
  /* Weight is tokenized so a theme can dial it down (mockup uses 600) without
     changing every badge in the other white-labels. */
  font-weight: var(--dash-status-weight, 700);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
}

.dash-status--approved {
  background-color: var(--dash-status-approved-bg, #d1fae5);
  color: var(--dash-status-approved-color, #059669);
}

.dash-status--pending {
  background-color: var(--dash-status-pending-bg, #fef3c7);
  color: var(--dash-status-pending-color, #d97706);
}

.dash-status--rejected {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  color: var(--dash-status-rejected-color, #dc2626);
}

.dash-status--inactive {
  background-color: var(--dash-status-inactive-bg, #eef2f6);
  color: var(--dash-status-inactive-color, #6b7280);
}

/* Draft — distinct from Inactive: a policy that was never activated yet
   (created via Add Policy, status='draft') vs one that lapsed after being
   active. Reuses the pending/amber tokens so it reads as "not live yet"
   rather than "was live, now dead" (that's --rejected/terminated). */
.dash-status--draft {
  background-color: var(--dash-status-pending-bg, #fef3c7);
  color: var(--dash-status-pending-color, #d97706);
}

/* /hr/quotes list table — scoped compact rhythm + softened nested-category
   treatment. Deliberately its OWN modifiers, not edits to the shared
   `.hr-dash-recent-th/-td` (reused across dozens of other tables) nor to the
   shared `.hr-dash-recent-tr--dependent` (reused as-is by the census page's
   employee->dependant rows).
   A quote's categories are flattened as a permanent sibling row under nearly
   EVERY quote here (unlike census dependants, which are the exception, not
   the rule), so `--dependent`'s saturated blue fill under every single row
   reads as heavy rather than tree-like — hence a lighter, page-scoped
   treatment instead of reusing or editing that shared class. */
.qd-quotes-table .hr-dash-recent-th {
  padding: 9px 24px;
}
.qd-quotes-table .hr-dash-recent-td {
  padding: 10px 24px;
}

/* Quote (own) row — no shared row-hover convention exists on
   `.hr-dash-recent-tr` itself (each page opts in individually), so this page
   gets one for a clean "scannable list" feel. */
.qd-quotes-tr--quote:hover {
  background-color: #f8fafc;
}

/* Category (child) row — a soft neutral tint + a thin left rule read as
   "nested under its parent" without the visual weight of a solid color
   block repeated down the whole table. */
.qd-quotes-tr--category {
  background-color: #fafbfd;
  box-shadow: inset 3px 0 0 #e1e7f0;
}
.qd-quotes-tr--category:hover {
  background-color: #f2f5f9;
}

/* Muted em-dash for a category row's columns that only apply at the quote
   level (category count / created / expires) — keeps every row visually
   "complete" instead of a run of blank cells that could read as a rendering
   bug rather than an intentional "not applicable here". */
.qd-quotes-td-na {
  color: #cbd5e1;
}

/* Category-row tree indicator — replaces an ASCII `└` glyph (looked dated)
   with a small filled dot. The row's own left rule
   (`.qd-quotes-tr--category`'s inset box-shadow) already reads as a
   connecting spine, so this dot only needs to mark "this is a nested leaf",
   not redraw the whole connector. */
.qd-quotes-cat-dot {
  flex-shrink: 0;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: #b6c0cf;
}

/* Per-quote expand/collapse toggle — each quote row is independently
   collapsible (categories hidden by default), unlike the census page's
   single-expanded-row pattern. A plain icon button, not a whole-row click
   target, so it never conflicts with the quote name's own navigation link
   sitting right next to it in the same cell. */
.qd-quotes-expand-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  color: #9ca3af;
  border-radius: 4px;
}

.qd-quotes-expand-btn:hover {
  background-color: #eef2f6;
  color: var(--brand-secondary, #4374f3);
}

/* Per-category contextual action — sits in the RIGHT-aligned Actions column
   (same cell position the quote row's own View button occupies, wrapped in
   the shared `.ins-pol-row-actions`), not beside the category name, so every
   row in the table keeps its actions in one consistent rightmost column
   regardless of nesting depth. Which action applies depends on the
   category's own `display_status` (Compare offers / Go to negotiation /
   View Policy). Compact + pill-shaped so it reads as a secondary nudge, not
   a competing CTA. `--primary` (solid navy, matching
   `.hr-dash-recent-btn-primary`) is used
   only for "Go to negotiation →" — the one action that means HR has
   something actively waiting on them; the default outlined style covers the
   lower-urgency Compare offers / View Policy actions. */
.qd-quotes-cat-action {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  padding: 3px 10px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid var(--brand-secondary, #4374f3);
  background: #fff;
  color: var(--brand-secondary, #4374f3);
  transition: background-color 0.12s;
}

.qd-quotes-cat-action:hover {
  background-color: #eef2ff;
}

.qd-quotes-cat-action--primary {
  border: none;
  background-color: var(--brand-primary, #0a3373);
  color: #fff;
}

.qd-quotes-cat-action--primary:hover {
  opacity: 0.88;
}

/* Quote (rollup) vs category (child) status pills must read as a clear
   size/weight HIERARCHY in this table's Status column, not as two
   identically-styled chips — a quote row's pill is the "parent" verdict,
   each nested category row's pill is a smaller, subordinate detail.
   `.qd-summary-status` (shared with the detail page's summary bar, which
   keeps its own softer default look there) is bumped up to the bigger,
   bolder chip; `.dash-status` (the category badge) is scaled down below its
   own shared default. Both overrides are scoped to this table only. */
.qd-quotes-table .qd-summary-status {
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 700;
  font-size: 11px;
  padding: 3px 10px;
}

.qd-quotes-table .qd-quotes-tr--category .dash-status {
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 2px 8px;
}

/* Category name link inside the HR quote detail categories table
   (`/hr/quotes/[id]`) — replaces the old whole-card onClick; only the name
   cell navigates now. Status badges in that same table map each status onto
   a visually distinct `.dash-status--*` modifier (inactive/pending/approved/
   quoting/action/pre-selected/won/rejected) — see `CATEGORY_STATUS_BADGE` in
   the page for the exact mapping and why each choice was made. */
.qd-cat-link {
  background: none;
  border: none;
  padding: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  cursor: pointer;
  text-align: left;
}

.qd-cat-link:hover {
  text-decoration: underline;
}

/* Trailing "Add category" row spanning the categories table — the primary
   affordance for adding a category from within the table itself. */
.qd-cat-add-row {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 4px 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}

.qd-cat-add-row:hover {
  color: var(--brand-primary, #0a3373);
}

.qd-cat-add-row:disabled {
  color: var(--ins-pol-subtext-color, #9ca3af);
  cursor: not-allowed;
}

/* Footer */
.hr-dash-recent-footer {
  display: flex;
  justify-content: flex-end;
  padding: var(--hr-dash-recent-footer-padding, 14px 24px);
  border-top: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

/* Centred variant — the rail's "Show N more" link, which reads as a continuation
   of the list rather than a corner action. */
.hr-dash-recent-footer--center {
  justify-content: center;
}

.hr-dash-recent-view-all {
  font-size: 14px;
  font-weight: 600;
  color: var(--hr-dash-recent-view-all-color, var(--brand-primary, #0a3373));
  background: none;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  text-decoration: none;
  transition: opacity 0.15s;
}

.hr-dash-recent-view-all:hover {
  opacity: 0.75;
}

/* §6 disabled treatment — for the `<button>` form of this action (a busy
   "Add a category" in a card head); a `<Link>` is never disabled. */
.hr-dash-recent-view-all:disabled {
  opacity: 0.45;
  cursor: default;
}

/* ─────────────────────────────────────────────
   Insurer Dashboard
   ───────────────────────────────────────────── */

/* Stat card: icon variant — label + icon on top row, value below */
.dash-stat-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  margin-bottom: 10px;
}

.dash-stat-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background-color: var(--dash-stat-icon-bg, color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, white));
  color: var(--dash-stat-icon-color, var(--brand-secondary, #4374f3));
  flex-shrink: 0;
}

/* Icon-square color variants — reused wherever a section needs an icon badge
   in a color other than the default secondary-blue tint (e.g. the quote
   detail page's Premium Information section). */
.dash-stat-icon--warning {
  background-color: color-mix(in srgb, var(--brand-warning, #eca04b) 16%, white);
  color: var(--brand-warning, #eca04b);
}
.dash-stat-icon--success {
  background-color: color-mix(in srgb, var(--brand-success, #32a15e) 14%, white);
  color: var(--brand-success, #32a15e);
}

/* Two-column grid: tasks (wider) + inbox (narrower) */
.ins-dash-grid {
  display: grid;
  grid-template-columns: 1fr 360px;
  gap: 20px;
}

/* Panel card (shared by tasks + inbox) */
.ins-dash-panel {
  background-color: var(--ins-dash-panel-bg, #ffffff);
  border: 1px solid var(--ins-dash-panel-border, #d2d9e5);
  border-radius: var(--ins-dash-panel-radius, 16px);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.ins-dash-panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--ins-dash-panel-row-border, #eaeff6);
}

.ins-dash-panel-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--ins-dash-panel-title-color, #18181b);
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}

/* Flags a panel still backed by placeholder data (no live feed wired yet). */
.ins-dash-mock-badge {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #6b7280;
  background-color: #f1f5f9;
  padding: 2px 8px;
  border-radius: 100px;
}

.ins-dash-panel-link {
  font-size: 13px;
  font-weight: 600;
  color: var(--ins-dash-panel-link-color, var(--brand-primary, #0a3373));
  background: none;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  text-decoration: none;
  transition: opacity 0.15s;
}

.ins-dash-panel-link:hover {
  opacity: 0.75;
}

/* Task rows */
.ins-dash-panel-empty {
  padding: 24px 20px;
  text-align: center;
  font-size: 13px;
  color: #94a3b8;
}

.ins-dash-task-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--ins-dash-panel-row-border, #eaeff6);
}

.ins-dash-task-row:last-child {
  border-bottom: none;
}

.ins-dash-task-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--ins-dash-task-name-color, #18181b);
  margin: 0 0 2px;
}

.ins-dash-task-company {
  font-size: 12px;
  color: var(--ins-dash-task-company-color, #9ca3af);
  margin: 0;
}

.ins-dash-task-badge {
  display: inline-flex;
  align-items: center;
  padding: 4px 10px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  flex-shrink: 0;
}

.ins-dash-task-badge--overdue {
  background-color: var(--ins-dash-badge-overdue-bg, #fee2e2);
  color: var(--ins-dash-badge-overdue-color, #dc2626);
}

.ins-dash-task-badge--today {
  background-color: var(--ins-dash-badge-today-bg, #fef3c7);
  color: var(--ins-dash-badge-today-color, #d97706);
}

.ins-dash-task-badge--soon {
  background-color: var(--ins-dash-badge-soon-bg, #dbeafe);
  color: var(--ins-dash-badge-soon-color, #2563eb);
}

/* Inbox message rows */
.ins-dash-msg-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 20px;
  border-bottom: 1px solid var(--ins-dash-panel-row-border, #eaeff6);
}

.ins-dash-msg-row:last-child {
  border-bottom: none;
}

.ins-dash-msg-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ins-dash-msg-title-color, #18181b);
  margin: 0 0 2px;
}

.ins-dash-msg-sender {
  font-size: 12px;
  color: var(--ins-dash-msg-sender-color, #9ca3af);
  margin: 0;
}

/* Right side of an inbox preview row — stacks the "New" pill above the
   relative-time text, both right-aligned. */
.ins-dash-msg-right {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex-shrink: 0;
}

/* Unread-thread pill on the dashboard inbox preview — a subtle brand-accent
   badge on the RIGHT of the row (replaces the former left-side dot). Sized to
   match `.ins-dash-mock-badge`; tinted with the accent colour rather than the
   loud solid fill an alert would use. */
.ins-dash-msg-new {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  border-radius: 100px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-dash-msg-new-color, var(--brand-accent, #ff6733));
  background-color: var(--ins-dash-msg-new-bg, #fff0eb);
}

.ins-dash-msg-time {
  font-size: 12px;
  color: var(--ins-dash-msg-time-color, #9ca3af);
  white-space: nowrap;
  margin-top: 2px;
}

/* ── Client portfolio: header row + card grid ── */
.ins-dash-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}

.ins-dash-section-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--ins-dash-panel-title-color, #18181b);
  margin: 0;
}

.ins-dash-portfolio-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}

.ins-dash-client-card {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 18px 20px;
  background-color: var(--ins-dash-panel-bg, #ffffff);
  border: 1px solid var(--ins-dash-panel-border, #d2d9e5);
  border-radius: var(--ins-dash-panel-radius, 16px);
  text-decoration: none;
  transition: border-color 0.12s, box-shadow 0.12s;
}

.ins-dash-client-card:hover {
  border-color: var(--ins-dash-client-card-border-hover, color-mix(in srgb, var(--brand-primary, #0a3373) 25%, white));
  box-shadow: 0 2px 10px rgba(13, 21, 38, 0.06);
}

.ins-dash-client-name {
  font-size: 15px;
  font-weight: 700;
  color: var(--ins-dash-task-name-color, #18181b);
  margin: 0;
}

.ins-dash-client-meta {
  font-size: 12px;
  color: var(--ins-dash-task-company-color, #9ca3af);
  margin: 0 0 4px;
}

.ins-dash-client-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.ins-dash-client-renewal {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 700;
  color: var(--ins-dash-task-name-color, #18181b);
}

.ins-dash-client-renewal svg {
  color: var(--dash-stat-icon-color, var(--brand-secondary, #4374f3));
  flex-shrink: 0;
}

.ins-dash-client-arrow {
  color: var(--ins-dash-task-company-color, #9ca3af);
  flex-shrink: 0;
}

/* ─────────────────────────────────────────────
   Dashboard — responsive
   ───────────────────────────────────────────── */

/* ── Tablet (≤ 1024px) ── */
@media (max-width: 1024px) {
  .dash-topbar {
    padding: var(--dash-topbar-padding, 18px 20px);
  }

  .dash-body {
    padding: var(--dash-body-padding, 20px);
  }
}

/* ── Tablet (insurer grid) ── */
@media (max-width: 1024px) {
  .ins-dash-grid {
    grid-template-columns: 1fr 1fr;
  }

  .ins-dash-portfolio-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* ── Mobile (≤ 768px) ── */
@media (max-width: 768px) {
  .ins-dash-grid {
    grid-template-columns: 1fr;
  }

  .ins-dash-portfolio-grid {
    grid-template-columns: 1fr;
  }

  /* Topbar sits below the fixed hamburger bar (~86px = 46px button + 40px padding). */
  .dash-topbar {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    padding: 86px 20px 12px;
  }


  .dash-topbar-right {
    width: 100%;
    justify-content: flex-end;
    gap: 12px;
  }

  /* Every wrapper div and every widget box in the right slot stretches full width. */
  .dash-topbar-right > div {
    width: 100%;
  }

  .mf-policybox,
  .ins-pol-statbar {
    width: 100%;
    box-sizing: border-box;
  }

  /* Stat items share equal space across the full width. */
  .ins-pol-statbar .ins-pol-stat {
    flex: 1;
    justify-content: center;
  }

  .dash-topbar-title {
    font-size: var(--dash-topbar-title-size-mobile, 22px);
  }

  .dash-topbar-title-row {
    flex-wrap: wrap;
    gap: 6px;
  }

  /* Hero */
  .dash-body {
    padding: 16px;
    gap: 14px;
  }

  /* The two-column landing surface collapses to one column, and the band's
     insurer panel stacks under the copy instead of squeezing it. */
  .hr-dash-grid {
    grid-template-columns: 1fr;
  }

  .hr-dash-band {
    flex-direction: column;
    border-radius: var(--hr-dash-band-radius-mobile, 14px);
  }

  .hr-dash-band-main {
    flex-direction: column;
    align-items: flex-start;
  }

  .hr-dash-band-aside {
    width: 100%;
  }

  .hr-dash-band-cta {
    width: 100%;
    justify-content: center;
  }

  /* Rows: the action pill drops under the label instead of overflowing. */
  .hr-dash-row {
    flex-wrap: wrap;
  }

  /* Page header: stack on mobile */
  .page-header {
    flex-wrap: wrap;
    gap: 12px;
    padding: 16px 20px;
  }

  .page-header-right {
    flex-wrap: wrap;
    gap: 8px;
    flex-shrink: 1;
  }

  /* Stats: 2-column grid on mobile */
  .dash-stats {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
  }

  /* A one-or-two-metric row (see `.dash-stats--compact` above) auto-fills instead
     of leaving a hole in the second column. */
  .dash-stats--compact {
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  }

  /* Table: horizontal scroll on mobile */
  .hr-dash-recent-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  .hr-dash-recent-head {
    flex-direction: column;
    gap: 12px;
    align-items: flex-start;
  }

  .hr-dash-recent-btn-outline,
  .hr-dash-recent-btn-primary {
    flex: 1;
    justify-content: center;
  }

  /* Policy tabs bar — scrollable on mobile */
  .ins-pol-tabs {
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
  }

  .ins-pol-tabs::-webkit-scrollbar { display: none; }

  /* Policy detail subheader */
  .pol-subheader {
    padding: 10px 16px;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
  }

  .pol-subheader-actions {
    width: 100%;
    margin-left: 0;
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
    padding-bottom: 2px;
  }

  .pol-subheader-actions::-webkit-scrollbar { display: none; }

  .pol-split-btn-menu {
    right: auto;
    left: 0;
  }

  /* Census / policies filter toolbar: stack + wrap on mobile */
  .ins-pol-toolbar {
    flex-wrap: wrap;
    gap: 10px;
  }

  .ins-pol-search {
    flex: 1 1 100%;
  }

  /* Hide Insurance ID column on small screens */
  .hr-dash-recent-th--right,
  .hr-dash-recent-td--id {
    display: none;
  }

  /* Sidebar: off-canvas drawer on mobile */
  /* The three-region column (logo / scrolling nav / pinned footer) carries over
     unchanged: `position: fixed` makes the viewport the containing block, so
     `height` here is definite exactly like the desktop flex stretch is.
     `100dvh` after the `100%` fallback because the two differ on mobile Safari
     — `100%` resolves against the LARGE viewport, which parks the footer (and
     the profile switcher in it) underneath the URL bar on the shortest
     viewports we ship to. Any engine that doesn't parse `dvh` keeps `100%`. */
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    height: 100dvh;
    z-index: 50;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    box-shadow: none;
  }

  .sidebar--open {
    transform: translateX(0);
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.12);
  }

  /* Icon-rail collapse is a desktop-only affordance — the mobile drawer
     already has its own open/close mechanism (hamburger + overlay). */
  .sidebar-collapse-toggle {
    display: none;
  }

  /* Overlay behind the open sidebar */
  .sidebar-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.3);
    z-index: 49;
  }

  .sidebar-overlay--visible {
    display: block;
  }

  /* Hamburger button — only shown on mobile */
  .sidebar-hamburger-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    padding: 20px;
    background: var(--dash-topbar-bg, #f8fafd);
    z-index: 48;
  }

  .sidebar-hamburger-bar-right {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  /* Bell already shown in hamburger bar — hide it from topbar right slot */
  .dash-topbar-right .dash-bell-btn {
    display: none;
  }

  .sidebar-hamburger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 12px;
    border: 1px solid var(--dash-bell-border, #e5e7eb);
    background: #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
    cursor: pointer;
    color: #374151;
  }

  /* Close button inside sidebar — only shown on mobile */
  .sidebar-close {
    display: flex;
    align-items: center;
    justify-content: center;
    position: absolute;
    top: 16px;
    right: 16px;
    width: 32px;
    height: 32px;
    border-radius: 6px;
    border: 1px solid #e5e7eb;
    background: transparent;
    cursor: pointer;
    color: #374151;
  }

  .sidebar-close:hover {
    background: #f3f4f6;
  }
}

/* ── Small mobile (≤ 480px) ── */
@media (max-width: 480px) {

  .dash-topbar-title {
    font-size: var(--dash-topbar-title-size-sm, 19px);
  }
}

/* ─────────────────────────────────────────────
   Page header — white panel, rounded bottom corners
   Reusable across list/detail pages (breadcrumb + title
   + subtitle on the left, widgets + bell on the right).
   ───────────────────────────────────────────── */
/* Content surface behind the page header — must match the body bg so the
   header's rounded-corner notch is the same color as the rest of the page. */
/* Dashboard shell outer wrapper (was an inline `#f1f5fe` on the layout div).
   Tokenized so a white-label can set its own app-background; base default keeps
   the historical cool tone, the Heale theme overrides it to warm cream. */
.dash-root {
  background-color: var(--dash-root-bg, #f1f5fe);
}

.dash-surface {
  background-color: var(--dash-body-bg, #f8fafd);
}

.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-shrink: 0;
  padding: var(--page-header-padding, 24px 32px);
  background-color: var(--page-header-bg, #ffffff);
  border-bottom: var(--page-header-border, var(--sidebar-border-right, 1px solid #e5e7eb));
  border-bottom-left-radius: var(--page-header-radius, 24px);
  border-bottom-right-radius: var(--page-header-radius, 24px);
}

.page-header-right {
  display: flex;
  align-items: center;
  gap: 24px;
  flex-shrink: 0;
}

/* Top-aligned variant — for taller headers (e.g. member identity) */
.page-header--top {
  align-items: flex-start;
}

/* Compact variant — smaller title/subtitle + tighter padding so list/detail
   pages fit more content above the fold. Matches the census list header. */
.page-header--compact {
  padding-top: 14px;
  padding-bottom: 12px;
}
.page-header--compact .page-header-title { font-size: 20px; }
.page-header--compact .page-header-subtitle { font-size: 13px; margin-top: 2px; }

/* ── Policy detail subheader (actions bar below DashTopbar) ── */
.pol-subheader {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 28px;
  background: var(--dash-topbar-bg, #f8fafd);
  border-bottom: 1px solid var(--dash-topbar-sep-color, #d2d9e5);
  flex-shrink: 0;
  flex-wrap: wrap;
}

.pol-subheader-identity {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.pol-subheader-num {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  font-family: monospace;
  letter-spacing: 0.3px;
}

.pol-subheader-sep {
  font-size: 13px;
  color: #d1d5db;
}

.pol-subheader-company {
  font-size: 13px;
  font-weight: 500;
  color: #6b7280;
}

.pol-subheader-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-left: auto;
}

.page-header-title {
  font-size: var(--page-header-title-size, 32px);
  font-family: var(--heading-font, inherit);
  font-weight: 800;
  color: var(--page-header-title-color, #18181b);
  letter-spacing: -0.6px;
  margin: 0;
}

.page-header-subtitle {
  font-size: 14px;
  color: var(--page-header-subtitle-color, #6b7280);
  margin: 6px 0 0;
  max-width: 640px;
}

.page-header-subtitle strong {
  font-weight: 700;
  color: var(--page-header-title-color, #18181b);
}

/* ─────────────────────────────────────────────
   Dashboard — breadcrumb + bell count (shared)
   ───────────────────────────────────────────── */
.dash-breadcrumb {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--dash-breadcrumb-color, #9ca3af);
}

.dash-breadcrumb-link {
  display: inline-flex;
  align-items: center;
  color: var(--dash-breadcrumb-color, #9ca3af);
  text-decoration: none;
  transition: color 0.12s;
}

.dash-breadcrumb-link:hover {
  color: var(--brand-primary, #0a3373);
}

.dash-breadcrumb-current {
  color: var(--dash-breadcrumb-current-color, #374151);
}

/* Numeric count badge for the topbar bell */
.dash-bell-count {
  position: absolute;
  top: -3px;
  right: -3px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
  color: #ffffff;
  background-color: var(--brand-accent, #ff6733);
  border: 1.5px solid var(--dash-topbar-bg, #ffffff);
  border-radius: 100px;
}

/* ─────────────────────────────────────────────
   Insurer Policies — topbar stat bar
   ───────────────────────────────────────────── */
.ins-pol-statbar {
  display: flex;
  align-items: stretch;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 14px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  overflow: hidden;
}

.ins-pol-stat {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 20px;
}

.ins-pol-stat:not(:first-child) {
  border-left: 1px solid var(--ins-pol-statbar-divider, #eaeff6);
}

.ins-pol-stat-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--ins-pol-stat-icon-color, var(--brand-secondary, #4374f3));
  flex-shrink: 0;
}

.ins-pol-stat-icon--lives {
  color: var(--ins-pol-stat-lives-color, #32a15e);
}

.ins-pol-stat > div {
  display: flex;
  align-items: baseline;
  gap: 5px;
}

.ins-pol-stat-value {
  font-size: 15px;
  font-weight: 700;
  color: var(--ins-pol-stat-value-color, #18181b);
  line-height: 1;
  margin: 0;
}

.ins-pol-stat-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ins-pol-stat-label-color, #9ca3af);
  margin: 0;
}

/* ── Toolbar inside the table card (chips + search + filter) ── */
.ins-pol-toolbar {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: var(--ins-pol-toolbar-padding, 18px 24px);
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ins-pol-chips {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.ins-pol-chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 14px;
  border-radius: 100px;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  background-color: var(--ins-pol-chip-bg, #ffffff);
  color: var(--ins-pol-chip-color, #6b7280);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.12s, border-color 0.12s, color 0.12s;
}

.ins-pol-chip:hover {
  background-color: var(--ins-pol-chip-bg-hover, #f9fafb);
}

.ins-pol-chip--active {
  background-color: var(--ins-pol-chip-active-bg, color-mix(in srgb, var(--brand-primary, #0a3373) 8%, white));
  border-color: var(--ins-pol-chip-active-border, color-mix(in srgb, var(--brand-primary, #0a3373) 25%, white));
  color: var(--brand-primary, #0a3373);
}

.ins-pol-chip-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 100px;
  background-color: var(--ins-pol-chip-count-bg, #eef2f9);
  color: var(--ins-pol-chip-count-color, #6b7280);
  font-size: 11px;
  font-weight: 700;
}

.ins-pol-chip--active .ins-pol-chip-count {
  background-color: var(--brand-primary, #0a3373);
  color: #ffffff;
}

/* Status filter group (e.g. All / Active / Inactive / Expiring Soon) */
.ins-pol-filters {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.ins-pol-filter {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 13px;
  border: none;
  border-radius: 100px;
  background: transparent;
  color: var(--ins-pol-filter-color, #6b7280);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}

.ins-pol-filter:hover {
  background-color: var(--ins-pol-filter-bg-hover, #f1f5fe);
}

.ins-pol-filter--active {
  background-color: var(--brand-primary, #0a3373);
  color: #ffffff;
}

.ins-pol-filter-count {
  color: var(--ins-pol-filter-count-color, #9ca3af);
  font-weight: 600;
}

.ins-pol-filter--active .ins-pol-filter-count {
  color: rgba(255, 255, 255, 0.7);
}

/* THE shared search field (~21 call sites: Policies, Companies, Census,
   Claims, Task Manager, Contact Center's rail, quotes, HR pages…). Every
   visual value is tokenized so a theme restyles all of them at once — Charte
   §9 turns this into a cream-filled pill for Heale via `--ins-pol-search-radius`
   / `-height` / `-bg` / `-border`; the base defaults below keep the historical
   10px-radius white box for every other white label. */
.ins-pol-search {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  height: var(--ins-pol-search-height, 40px);
  padding: 0 14px;
  border-radius: var(--ins-pol-search-radius, 10px);
  border: 1px solid var(--ins-pol-search-border, #e5e7eb);
  background-color: var(--ins-pol-search-bg, #ffffff);
}

.ins-pol-search-icon {
  color: var(--ins-pol-search-icon-color, #9ca3af);
  flex-shrink: 0;
}

/* The search pill is a VARIANT of the field contract, not a family of its own:
   `--ins-pol-search-*` carries only what differs (radius 999, its own fill and
   1px border), and everything else falls through to `--field-*`. */
.ins-pol-search-input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--field-font-size, 13px);
  color: var(--ins-pol-search-input-color, var(--field-color, #374151));
}

.ins-pol-search-input::placeholder {
  color: var(--ins-pol-search-placeholder-color, var(--field-placeholder-color, #9ca3af));
}

/* ── Table cell helpers ── */
/* Sortable column-header button — must read identically to the plain
   uppercase `.hr-dash-recent-th` cells so a table's headers never look like a
   mix of title-case and uppercase. Same size/weight/spacing/colour token. */
.ins-pol-th-sort {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  text-transform: uppercase;
  font-size: var(--hr-dash-recent-th-size, 11px);
  font-weight: var(--hr-dash-recent-th-weight, 600);
  letter-spacing: var(--hr-dash-recent-th-tracking, 0.07em);
  color: var(--hr-dash-recent-th-color, #9ca3af);
}

/* When rendered as a real <button> (clickable sort header), reset native button
   chrome so it matches the decorative <span> usage elsewhere. */
button.ins-pol-th-sort {
  border: none;
  background: transparent;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}

.ins-pol-th-sort svg {
  color: var(--ins-pol-th-sort-color, #cbd5e1);
}

/* Active (currently-sorted) column header — tints the sort chevron with the
   brand secondary, replacing an inline color style on the icon. */
.ins-pol-th-sort--active svg {
  color: var(--brand-secondary, #4374f3);
}

/* Section heading inside a filter dropdown panel (uppercase group label). */
/* ─────────────────────────────────────────────
   "Filters (N)" trigger pill (`<FilterPanel>`)
   ─────────────────────────────────────────────
   The single button that opens a screen's filter panel, replacing a row of
   one-dropdown-per-dimension chips. A BUTTON, not a §7 selector shape — the
   panel's own controls stay `.dropdown-item` rows / pills.

   Charte look (via `--filter-trigger-*`): fully-rounded 40px pill, 1px navy-700
   border, a very light blue-400 tint fill (NOT white, so it reads as a live
   control next to the cream page) and a navy funnel + label. Base fallbacks
   resolve to the outlined-button tokens, so a non-Charte white-label keeps its
   historical secondary-button look. */
.filter-trigger-pill {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  height: var(--filter-trigger-height, 40px);
  padding: 0 var(--filter-trigger-padding-x, 18px);
  border-radius: 999px;
  border: 1px solid var(--filter-trigger-border, var(--hr-dash-recent-btn-outline-border, #d1d5db));
  background-color: var(--filter-trigger-bg, var(--hr-dash-recent-btn-outline-bg, #ffffff));
  color: var(--filter-trigger-color, var(--hr-dash-recent-btn-outline-color, #0a3373));
  font-size: var(--filter-trigger-font-size, 13px);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.12s, border-color 0.12s, color 0.12s;
}

.filter-trigger-pill:hover {
  background-color: var(--filter-trigger-bg-hover, var(--hr-dash-recent-btn-outline-bg-hover, #f1f5fe));
}

/* At least one filter is applied. Deliberately the SAME fill/border as the
   resting pill — the count in the label is what says "active"; a second visual
   state here would compete with it. Kept as a hook so a theme can opt into one. */
.filter-trigger-pill--active {
  border-color: var(--filter-trigger-border-active, var(--filter-trigger-border, var(--hr-dash-recent-btn-outline-border, #d1d5db)));
  background-color: var(--filter-trigger-bg-active, var(--filter-trigger-bg, var(--hr-dash-recent-btn-outline-bg, #ffffff)));
}

.ins-filter-group-label {
  margin: 2px 8px 6px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--hr-dash-recent-th-color, #9ca3af);
}

.ins-pol-company {
  display: flex;
  align-items: center;
  gap: 12px;
}

.ins-pol-company-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
  margin: 0;
}

/* Clickable company name (links to the company's policies page) */
a.ins-pol-company-name {
  display: block;
  text-decoration: none;
  cursor: pointer;
  transition: color 0.12s;
}

a.ins-pol-company-name:hover {
  color: var(--brand-secondary, #4374f3);
}

.ins-pol-expiring {
  display: inline-flex;
  align-items: center;
  margin-top: 4px;
  padding: 2px 9px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 600;
  /* Same amber as the pending/onboarding badge so all amber in the app agrees. */
  background-color: var(--ins-pol-expiring-bg, var(--dash-status-pending-bg, #fef3c7));
  color: var(--ins-pol-expiring-color, var(--dash-status-pending-color, #d97706));
}

.ins-pol-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 26px;
  height: 24px;
  padding: 0 8px;
  border-radius: 100px;
  font-size: 13px;
  font-weight: 700;
  background-color: var(--ins-pol-count-bg, #e6f4ec);
  color: var(--ins-pol-count-color, #32a15e);
}

.ins-pol-members {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--ins-pol-members-color, #374151);
  font-weight: 600;
}

.ins-pol-members svg {
  color: var(--ins-pol-members-icon-color, #9ca3af);
}

.ins-pol-premium {
  font-weight: 700;
  color: var(--ins-pol-premium-color, #18181b);
}

/* ── Policies-view cells ── */
.ins-pol-num {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ins-pol-num-color, #18181b);
}

/* Two-line policy identity cell — §10's "first column Bold ink-900" plus the
   identifier below it. Introduced when migration `20260729_1` stopped
   overloading the policy NAME onto `policies.category`: the Category column
   carries the employee class again, so the name (the value the insurer types
   on the Create Policy screen) needs a home of its own — and these tables are
   already 8 columns wide, so it shares the first cell with the number instead
   of adding a ninth. Rendered only via <PolicyIdentityCell>
   (src/components/policies/policy-identity-cell.tsx), never hand-built. */
.ins-pol-policy-cell {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.ins-pol-policy-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
  text-decoration: none;
}

a.ins-pol-policy-name:hover {
  color: var(--ins-pol-num-color, #18181b);
}

/* The `.ins-pol-num` identifier treatment (mono, navy token) one step down in
   size/weight, so as the sub-line it never competes with the name above it. */
.ins-pol-num--sub {
  font-size: 11.5px;
  font-weight: 600;
}

.ins-pol-company--inline {
  display: flex;
  align-items: center;
  gap: 10px;
}

.ins-pol-type {
  color: var(--ins-pol-type-color, #6b7280);
}

.ins-pol-status-cell {
  display: flex;
  align-items: center;
  gap: 8px;
}

.ins-pol-end {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ins-pol-end-date {
  font-size: 13px;
  font-weight: 600;
  color: var(--ins-pol-end-date-color, #374151);
}

.ins-pol-end-sub {
  font-size: 11px;
  font-weight: 600;
  color: var(--ins-pol-end-sub-color, #d97706);
}

.ins-pol-end-sub--expired {
  color: var(--ins-pol-end-sub-expired-color, #dc2626);
}

.ins-pol-end--muted .ins-pol-end-date {
  color: var(--ins-pol-end-date-muted-color, #9ca3af);
  font-weight: 500;
}

/* ── Row actions menu ── */
.ins-pol-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--ins-pol-action-color, #9ca3af);
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}

.ins-pol-action-btn:hover,
.ins-pol-action-btn--open {
  background-color: var(--ins-pol-action-bg-hover, #f1f5fe);
  color: var(--brand-primary, #0a3373);
}

/* Danger variant (e.g. delete document) */
.ins-pol-action-btn--danger {
  color: var(--brand-error, #dc2626);
}

.ins-pol-action-btn--danger:hover {
  background-color: var(--ins-pol-action-danger-bg-hover, #fef2f2);
  color: var(--brand-error, #dc2626);
}

.ins-pol-action-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
  background-color: transparent;
}

/* Right-aligned cluster of icon actions inside a table cell */
.ins-pol-row-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  justify-content: flex-end;
}

/* ─────────────────────────────────────────────
   Dropdown menu — shared, app-wide component.
   Reached through `<PopoverMenu>` (src/components/ui/popover-menu.tsx), which
   PORTALS the menu to document.body and passes the measured position inline; the
   `position: absolute` + `top`/`right` below is the in-flow fallback for a menu
   rendered inside a `position: relative` anchor instead.
   Variants: .dropdown-item--danger, separator .dropdown-sep.
   ───────────────────────────────────────────── */
.dropdown-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 20;
  min-width: 168px;
  padding: 6px;
  background-color: var(--dropdown-bg, #ffffff);
  border: 1px solid var(--dropdown-border, #e5e7eb);
  border-radius: 12px;
  /* §8 floating elevation — tokenized so a theme can drop the border and let
     the shadow carry the menu instead. */
  box-shadow: var(--dropdown-shadow, 0 10px 30px rgba(13, 21, 38, 0.12));
}

/* Chevron on a dropdown trigger — flips when the menu is open. Replaces the
   inline `transform: rotate(180deg)` every trigger used to carry. */
.dropdown-chevron {
  transition: transform 0.15s;
  flex-shrink: 0;
}

.dropdown-chevron--open {
  transform: rotate(180deg);
}

.dropdown-item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 6px 12px;
  border: none;
  background: transparent;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--dropdown-item-color, #374151);
  text-align: left;
  cursor: pointer;
  transition: background-color 0.12s;
}

.dropdown-item:hover,
/* Roving arrow-key focus inside a `role="menu"` has to be as visible as hover, or
   a keyboard user cannot see where they are (`<MenuButton>`). */
.dropdown-item:focus-visible {
  background-color: var(--dropdown-item-bg-hover, #f1f5fe);
  outline: none;
}

.dropdown-item svg {
  color: var(--dropdown-item-icon-color, #9ca3af);
  flex-shrink: 0;
}

/* Own token: a theme that drops the menu's border (shadow-only, §8) still needs
   a visible hairline between item groups. */
.dropdown-sep {
  height: 1px;
  margin: 4px 4px;
  background-color: var(--dropdown-sep-color, var(--dropdown-border, #e5e7eb));
}

/* Leading check on a selectable dropdown option. It stays in the layout when
   the option isn't selected (hidden, not removed) so every label in the group
   lines up on the same left edge — replaces the inline
   `style={{ visibility }}` the filter panels used to carry. */
.dropdown-check--hidden {
  visibility: hidden;
}

/* Wider menu for a multi-group filter panel (labelled sections + long option
   labels), replacing an inline `menuStyle={{ minWidth }}` at the call site. */
.dropdown-menu--filters {
  min-width: 220px;
}

/* Muted "no options" hint inside a dropdown popover (replaces inline styles). */
.dropdown-hint {
  margin: 0;
  padding: 8px 12px;
  font-size: 12px;
  color: var(--hr-dash-recent-th-color, #9ca3af);
}

/* §6's rejection tone. The consumed token is what lets Heale reach rust-600
   here: the bare `--brand-error` fallback is the retired §1 error red, which a
   destructive MENU item would otherwise wear while its row-level siblings do
   not. Unmapped themes keep their historical red. */
.dropdown-item--danger {
  color: var(--dropdown-item-danger-color, var(--brand-error, #dc2626));
}

.dropdown-item--danger:hover {
  background-color: var(--dropdown-item-danger-bg-hover, #fef2f2);
}

.dropdown-item--danger svg {
  color: inherit;
}

/* ─────────────────────────────────────────────
   Tooltip — shared, app-wide component (`<Tooltip>`, src/components/ui/tooltip.tsx).
   The bubble is PORTALLED to document.body and positioned inline (fixed
   coordinates measured from the anchor), same as `.dropdown-menu`; everything
   below is purely visual. §8 floating elevation, §3 metadata type scale.
   ───────────────────────────────────────────── */
.tooltip-anchor {
  display: inline-flex;
  /* The wrapper must not add its own box: it only carries hover/focus
     listeners around whatever the caller rendered. */
  max-width: 100%;
}

.tooltip-bubble {
  position: fixed;
  z-index: 1100;
  max-width: 240px;
  padding: 8px 10px;
  border-radius: 10px;
  background-color: var(--tooltip-bg, #1f2937);
  color: var(--tooltip-color, #ffffff);
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.45;
  box-shadow: var(--tooltip-shadow, 0 3px 14px rgba(28, 26, 23, 0.18));
  /* Never intercept the pointer — hovering the bubble itself must not keep it
     open, nor block a click on whatever sits underneath. */
  pointer-events: none;
}

/* An action the caller isn't allowed to perform: §6 disabled = 45% opacity
   (never a grey swap). Applied by `<LockedButton>` on top of the button's own
   class, so it works on a primary, outline or icon button alike. The element
   stays focusable and `aria-disabled` — see that component for why. */
.ui-locked {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Footer pagination ── */
.ins-pol-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--ins-pol-footer-padding, 16px 24px);
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ins-pol-footer-info {
  font-size: 13px;
  color: var(--ins-pol-footer-info-color, #9ca3af);
}

.ins-pol-footer-info strong {
  color: var(--ins-pol-footer-info-strong-color, #374151);
  font-weight: 700;
}

.ins-pol-pager {
  display: flex;
  align-items: center;
  gap: 6px;
}

.ins-pol-pager-btn {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  border-radius: 8px;
  border: 1px solid var(--ins-pol-pager-border, #e5e7eb);
  background-color: var(--ins-pol-pager-bg, #ffffff);
  font-size: 13px;
  font-weight: 600;
  color: var(--ins-pol-pager-color, #6b7280);
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}

.ins-pol-pager-btn:hover:not(:disabled) {
  background-color: var(--ins-pol-pager-bg-hover, #f9fafb);
  color: var(--brand-primary, #0a3373);
}

.ins-pol-pager-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* Clickable policy number */
a.ins-pol-num {
  text-decoration: none;
  cursor: pointer;
  transition: color 0.12s;
}

a.ins-pol-num:hover {
  color: var(--brand-secondary, #4374f3);
}

/* ─────────────────────────────────────────────
   Employee claims / pre-approvals table cells
   (parity with the .ins-pol-* portfolio table)
   ───────────────────────────────────────────── */
.emp-claim-tr {
  cursor: pointer;
}

.emp-claim-ref {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 13px;
  font-weight: 600;
  color: var(--emp-claim-ref-color, #18181b);
}

.emp-claim-badge {
  display: inline-flex;
  align-items: center;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  background-color: var(--emp-claim-badge-bg, #f3f4f6);
  color: var(--emp-claim-badge-color, #6b7280);
}

.emp-claim-badge--preapproval {
  margin-left: 0;
  padding: 2px 7px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.03em;
  background-color: var(--emp-claim-badge-preapproval-bg, #ede9fe);
  color: var(--emp-claim-badge-preapproval-color, #6d28d9);
}

.emp-claim-treatment {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 13px;
  color: var(--emp-claim-treatment-color, #374151);
}

.emp-claim-cell {
  font-size: 13px;
  color: var(--emp-claim-cell-color, #374151);
}

.emp-claim-amount {
  font-size: 13px;
  color: var(--emp-claim-amount-color, #18181b);
}

.emp-claim-amount strong {
  font-weight: 700;
}

.emp-claim-amount-currency {
  margin-left: 4px;
  font-weight: 400;
  color: var(--emp-claim-amount-currency-color, #6b7280);
}

/* Secondary "Approved: X" line — shown only when approved_amount differs from
   the originally claimed amount (e.g. a partial approval). */
.emp-claim-amount-approved {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 600;
  color: var(--brand-success, #32a15e);
  white-space: nowrap;
}

.emp-claim-updated {
  font-size: 12px;
  color: var(--emp-claim-updated-color, #9ca3af);
}

.emp-claim-chevron {
  color: var(--emp-claim-chevron-color, #cbd5e1);
}

/* ─────────────────────────────────────────────
   Policy detail page
   ───────────────────────────────────────────── */

/* Vertical divider used between the "Back" link and the breadcrumb path */
.ins-pol-crumb-divider {
  width: 1px;
  height: 14px;
  background-color: var(--ins-pol-crumb-divider, #d2d9e5);
  margin: 0 2px;
}

/* ── Policy detail split button ── */
.pol-split-btn {
  position: relative;
  display: flex;
  align-items: stretch;
}

/* The five solid action buttons below read --brand-primary directly, which is
   also INK (headings, identifiers, nav) — so a theme could not recolour the
   control without recolouring its own headings. Tokenised per control, with the
   original values as fallbacks, so no other theme moves. */
.pol-split-btn-primary {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  background: var(--pol-split-btn-bg, var(--brand-primary, #0a3373));
  color: var(--pol-split-btn-fg, #ffffff);
  font-size: 13px;
  font-weight: 600;
  border: none;
  border-radius: 8px 0 0 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s;
}

.pol-split-btn-primary:hover { background: var(--pol-split-btn-bg-hover, #0c3d8a); }

.pol-split-btn-chevron {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 10px;
  background: var(--pol-split-btn-bg, var(--brand-primary, #0a3373));
  color: var(--pol-split-btn-fg, #ffffff);
  border: none;
  border-left: 1px solid rgba(255,255,255,0.2);
  border-radius: 0 8px 8px 0;
  cursor: pointer;
  transition: background 0.15s;
}

.pol-split-btn-chevron:hover { background: var(--pol-split-btn-bg-hover, #0c3d8a); }

.pol-split-btn-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  min-width: 160px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 10px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.10);
  z-index: 50;
  padding: 4px;
}

.pol-split-btn-item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 8px 12px;
  background: none;
  border: none;
  border-radius: 7px;
  font-size: 13px;
  font-weight: 500;
  color: #374151;
  cursor: pointer;
  text-align: left;
  transition: background 0.12s;
}

.pol-split-btn-item:hover { background: #f3f4f6; }

.pol-split-btn-item--danger { color: #dc2626; }
.pol-split-btn-item--danger:hover { background: #fef2f2; }

.pol-split-btn-divider {
  height: 1px;
  background: #f3f4f6;
  margin: 4px 0;
}

/* Danger variant of the outline button (Terminate) */
.hr-dash-recent-btn-outline--danger {
  color: var(--brand-error, #dc2626);
  border-color: var(--hr-dash-btn-danger-border, color-mix(in srgb, var(--brand-error, #dc2626) 35%, white));
}

.hr-dash-recent-btn-outline--danger:hover {
  background-color: var(--hr-dash-btn-danger-bg-hover, #fef2f2);
}

/* Compact size modifier for the primary/outline buttons above — used inside
   dense rows (e.g. the quote-detail offer sub-rows) where the default 13px/
   `padding: 8px 16px` shape is too tall. */
.hr-dash-recent-btn-primary--sm,
.hr-dash-recent-btn-outline--sm {
  height: 32px;
  font-size: 12px;
}

/* Icon-only square variant (e.g. the delete-draft trash icon). */
.hr-dash-recent-btn-outline--icon {
  height: 32px;
  width: 32px;
  padding: 0;
}

/* Tab bar */
.ins-pol-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px;
  background-color: var(--ins-pol-tabs-bg, #ffffff);
  border: 1px solid var(--ins-pol-tabs-border, #d2d9e5);
  border-radius: 14px;
}

.ins-pol-tab {
  padding: 9px 18px;
  border: none;
  border-radius: 10px;
  background: transparent;
  font-size: 14px;
  font-weight: 600;
  color: var(--ins-pol-tab-color, #6b7280);
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}

.ins-pol-tab:hover {
  background-color: var(--ins-pol-tab-bg-hover, #f1f5fe);
}

/* The FILL was already themeable; the LABEL was not, so a brand could tint this
   segmented tab's background and then be unable to recolour the text on it. Named
   for the block's own convention (`--ins-pol-tab-active-bg` beside it), fallback =
   the expression that was inline. */
.ins-pol-tab--active {
  background-color: var(--ins-pol-tab-active-bg, color-mix(in srgb, var(--brand-primary, #0a3373) 8%, white));
  color: var(--ins-pol-tab-active-color, var(--brand-primary, #0a3373));
}

/* Stat-card head: icon + label inline */
.ins-pol-stat-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
}

/* Generic padded card body */
.ins-pol-card-body {
  padding: var(--ins-pol-card-body-padding, 22px 24px);
}

/* Progress bar */
.ins-pol-progress {
  width: 100%;
  height: 8px;
  border-radius: 100px;
  background-color: var(--ins-pol-progress-track, #eef2f9);
  overflow: hidden;
}

.ins-pol-progress-fill {
  height: 100%;
  border-radius: 100px;
  background-color: var(--brand-primary, #0a3373);
}

.ins-pol-timeline-note {
  font-size: 13px;
  color: var(--ins-pol-timeline-note-color, #9ca3af);
}

.ins-pol-timeline-dates {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 10px;
  font-size: 13px;
  color: var(--ins-pol-timeline-date-color, #6b7280);
}

/* Recent activity list */
.ins-pol-activity {
  padding: 8px 24px 16px;
}

.ins-pol-activity-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
}

.ins-pol-activity-row:not(:last-child) {
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ins-pol-activity-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  flex-shrink: 0;
  background-color: var(--ins-pol-activity-icon-bg, #eef2f9);
  color: var(--ins-pol-activity-icon-color, var(--brand-primary, #0a3373));
}

.ins-pol-activity-text {
  font-size: 14px;
  font-weight: 500;
  color: var(--ins-pol-activity-text-color, #18181b);
  margin: 0;
}

.ins-pol-activity-time {
  font-size: 12px;
  color: var(--ins-pol-activity-time-color, #9ca3af);
  margin: 2px 0 0;
}

/* ── Policy term-progress strip (§5 + §8) ────────────────────────────────
   Was a 4-cell fact strip (status + identity head row, then premium /
   coverage start / coverage end / term progress). Every one of those facts is
   already in the page header — title `{company} — {policyNumber}`, subtitle
   `status · type · premium · start → end` — so by user decision the strip
   keeps ONLY the term progress, as one prominent full-width bar. The removed
   `.pol-summary-head/-identity/-company/-grid/-cell` classes are gone on
   purpose; don't reinstate them to put those facts back. Still a §8 card:
   white, no border (the shadow carries it). The `.pol-meta-widget` family that
   once sat beside this note is ALSO gone now: `/insurer/quotes/[id]` was its
   last consumer, and the decision-screen rebuild replaced that topbar widget
   with the §5 subtitle plus the hero's fact line. */
.pol-summary {
  display: flex;
  flex-direction: column;
  gap: 9px;
  padding: 14px 18px 16px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5);
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  box-shadow: var(--hr-dash-recent-card-shadow, none);
}

/* Section label left, remaining-term label right; wraps rather than clipping. */
.pol-summary-term {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
}

/* §3 section label — always the metadata tone, never semantically coloured. */
.pol-summary-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--pol-summary-label-color, #9ca3af);
  margin: 0;
}

/* Wraps rather than clips (§21). */
.pol-summary-value {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--pol-summary-value-color, #18181b);
  margin: 0;
  overflow-wrap: anywhere;
}

/* "Expiring soon" tone — gold, the §12 pending/waiting colour. */
.pol-summary-value--expiring {
  color: var(--dash-status-pending-color, #d97706);
}

/* Deliberately chunky (12px, was 6px in the old cell): this bar is the whole
   point of the strip now, not a detail inside one of four cells. */
.pol-summary-bar {
  display: block;
  width: 100%;
  height: 12px;
  border-radius: 999px;
  overflow: hidden;
  background-color: var(--pol-summary-bar-track, #eef2f9);
}

/* The fill's length is the one genuinely per-instance value on this page, so it
   arrives as the `--pol-summary-pct` custom property set on the element (never
   an inline `background`/`color`). */
.pol-summary-bar-fill {
  display: block;
  height: 100%;
  border-radius: 999px;
  width: var(--pol-summary-pct, 0%);
  background-color: var(--pol-summary-bar-fill, var(--brand-primary, #0a3373));
}

/* ─────────────────────────────────────────────
   Census page
   ───────────────────────────────────────────── */

/* Colored stat values */
.dash-stat-value--success { color: var(--dash-status-approved-color, #059669); }
.dash-stat-value--warning { color: var(--dash-status-pending-color, #d97706); }
.dash-stat-value--danger  { color: var(--dash-status-rejected-color, #dc2626); }

/* Top-level filter row (§6): sits directly under the page header, ABOVE the
   stat cards — status pills on the left, a primary action on the right. Kept
   generic (layout only) so any list page can reuse it. `.dash-body`'s own
   `gap` handles the spacing to the next block. */
.ins-pol-filterbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

/* The same row when it carries ONLY the page action — no filter pills on its left
   to hold the other end. `space-between` would park a lone child at the START,
   i.e. exactly where a page action must not be (§6/§21: page actions sit to the
   RIGHT of the filter row). Used by `/hr/quotes/categories`, whose filtering all
   lives inside the member card's own toolbar. */
.ins-pol-filterbar--end {
  justify-content: flex-end;
}

/* A `.ins-pol-search` sitting in the filter row's RIGHT slot (§6/§9: the row's own
   controls live there, never in the page header).
   `.ins-pol-search` is `flex: 1`, and so is `.ins-pol-segs` — so without a cap the
   two split the row in half and a two-pill filter set gets as much width as a search
   field nobody is typing in. Bounded like `.ins-claim-search`, which solved the same
   problem in the claims toolbar. */
.ins-pol-filterbar-search {
  flex: 0 1 320px;
  min-width: 200px;
  max-width: 380px;
}

/* Segmented toggle pills (All / Covered / Not covered) */
.ins-pol-segs {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  flex: 1 1 auto;
  min-width: 0;
}

/* Rendered as a <button> by `<FilterTabs variant="pill">` and every list page,
   and as a `next/link` <a> by the policy-detail tab row — hence the explicit
   `text-decoration: none`, which the anchors need (a no-op for the buttons). */
.ins-pol-seg {
  padding: 8px 16px;
  border: var(--ins-pol-seg-border, none);
  border-radius: 100px;
  background-color: var(--ins-pol-seg-bg, #eef2f9);
  color: var(--ins-pol-seg-color, #6b7280);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s, border-color 0.12s;
}

/* Scoped to the NON-selected pill. `:hover` (0,2,0) outranks
   `.ins-pol-seg--active` (0,1,0), so without the :not() the selected pill loses
   its own fill the moment the pointer touches it — it flashed pale on every
   theme, just less visibly when both fills were cool-toned. */
.ins-pol-seg:not(.ins-pol-seg--active):hover {
  background-color: var(--ins-pol-seg-bg-hover, #e2e8f3);
}

/* Selected filter pill. Tokenised so a theme can paint the selected state
   without repainting --brand-primary (which is also ink: headings, identifiers,
   nav). Fallbacks are the original values, so themes that never set these keep
   the navy pill exactly. */
.ins-pol-seg--active {
  background-color: var(--ins-pol-seg-active-bg, var(--brand-primary, #0a3373));
  /* Match the border to the fill so the inactive border doesn't show as a ring
     around the active pill. */
  border-color: var(--ins-pol-seg-active-border, var(--ins-pol-seg-active-bg, var(--brand-primary, #0a3373)));
  color: var(--ins-pol-seg-active-color, #ffffff);
}

.ins-pol-seg:disabled {
  opacity: .5;
  cursor: not-allowed;
}

/* There is deliberately NO blue-active variant of this row. One was added for
   the policy-detail tab row and reverted: the blue the user asked for belongs
   to the page action in the row's RIGHT slot (§6 primary,
   `.hr-dash-recent-btn-primary`), not to the selected segment, which stays
   navy-700 everywhere per §6. */

/* <FilterTabs> shared wrapper — row layout + count badge (used by both pill and underline variants) */
.filter-tabs-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
}

.filter-tabs-count {
  margin-left: 6px;
  font-size: 11px;
  font-weight: 700;
  opacity: .6;
}

.filter-tabs-count--active {
  opacity: .85;
}

.filter-tabs-right-slot {
  flex-shrink: 0;
  /* Keeps the §6 page action RIGHT-aligned even when `.filter-tabs-row`'s
     `flex-wrap` drops it onto its own line — `justify-content: space-between`
     alone puts a lone item on a wrapped line at flex-START, which is what made
     /hr/census's "Add member" sit flush left under long policy pills ("Al
     Nakheel Group Medical – Category A"). Byte-identical whenever the pills and
     the action share one line: `.ins-pol-segs`/`.tabs-underline` are
     `flex: 1 1 auto`, so they absorb the free space and this margin resolves to
     0. Nothing renders at all when a caller passes no `rightSlot`. */
  margin-left: auto;
  /* A row, so a slot holding MORE than one control (the policy tab row now
     carries a tab-published action beside Menu) keeps them on one line instead
     of stacking them as blocks. A single-child slot is unaffected. */
  display: flex;
  align-items: center;
  gap: 8px;
}

/* <FilterTabs variant="underline"> — the app's canonical sub-screen tab row
   (Charte §7), shared by the Task Manager list, `/hr/quotes/[id]` and
   `/insurer/quotes/[id]/categories/[categoryId]`. Every state value is
   tokenized (with the historical value as the fallback, so non-Heale themes are
   untouched) and comes ONLY from active/inactive state — never from a tab's
   DOM position. A previous Task-Manager-scoped `nth-child(1|2|3)` block gave
   each tab its own colour and its own underline colour, which is exactly the
   incoherence this replaced; do not reintroduce position-dependent selectors
   here. */
.tabs-underline {
  display: flex;
  align-items: stretch;
  gap: var(--tabs-underline-gap, 2px);
  border-bottom: 1px solid var(--tabs-underline-divider, #e5e7eb);
  flex: 1 1 auto;
  min-width: 0;
}

/* Rendered as a <button> by `<FilterTabs variant="underline">` and as a
   `next/link` <a> by the route-driven tab rows (policy detail) — hence the
   explicit `text-decoration: none`. */
.tabs-underline-seg {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: var(--tabs-underline-seg-padding, 9px 16px);
  margin-bottom: -1px;
  cursor: pointer;
  text-decoration: none;
  font-size: 13px;
  font-weight: var(--tabs-underline-seg-weight, 600);
  border: none;
  border-bottom: 2px solid transparent;
  background: transparent;
  color: var(--tabs-underline-seg-color, #64748b);
  transition: color .12s, border-color .12s;
}

.tabs-underline-seg:hover {
  color: var(--tabs-underline-seg-active-color, var(--brand-primary, #0a3373));
}

.tabs-underline-seg--active {
  color: var(--tabs-underline-seg-active-color, var(--brand-primary, #0a3373));
  font-weight: var(--tabs-underline-seg-active-weight, 600);
  border-bottom-color: var(--tabs-underline-active-color, var(--brand-secondary, #4374f3));
}

/* Counter colour is scoped to the UNDERLINE variant on purpose: the shared
   `.filter-tabs-count` above keeps its `opacity` treatment for the PILL variant,
   where the count inherits a filled pill's own (sometimes white) text colour and
   an explicit ink colour would be unreadable. */
.tabs-underline-seg .filter-tabs-count {
  font-size: var(--tabs-underline-count-size, 11px);
  opacity: var(--tabs-underline-count-opacity, .6);
  color: var(--tabs-underline-count-color, inherit);
}

.tabs-underline-seg--active .filter-tabs-count {
  opacity: var(--tabs-underline-count-opacity, .85);
  color: var(--tabs-underline-seg-active-color, inherit);
}

/* <StatusTabs> — the same §7 underline row, worn by a REAL ARIA tablist that
   swaps the panel below it, with a per-tab status dot. Only the additions live
   here; the shape itself is `.tabs-underline*` above, so there is still one
   underline-tab look in the app.

   The strip scrolls rather than wrapping: a catalogue-driven tab row (benefit
   families) has no fixed length, and a wrapped strip pushes the panel off
   screen. `<StatusTabs>` scrolls the active tab back into view whenever it
   changes, so the selected one is never the clipped one. */
.status-tabs {
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.status-tabs-seg {
  flex: 0 0 auto;
  white-space: nowrap;
}

/* Dot tones mirror the status vocabulary used everywhere else (§12). The tone
   is ALSO spelled out in text by the component, so this is never the only
   carrier of the state. */
.status-tabs-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex: 0 0 auto;
}

.status-tabs-dot--done { background-color: var(--dash-status-approved-color, #3a8a5f); }
.status-tabs-dot--todo { background-color: var(--dash-status-pending-color, #e3b341); }
.status-tabs-dot--idle { background-color: var(--dash-status-inactive-color, #8d8579); }

/* Reset link-button */
.ins-pol-reset {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  border: none;
  background: transparent;
  color: var(--ins-pol-reset-color, #6b7280);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.12s;
}

.ins-pol-reset:hover {
  color: var(--brand-primary, #0a3373);
}

/* Small muted secondary text inside a table cell (e.g. member email) */
.ins-pol-subtext {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #9ca3af);
  margin: 1px 0 0;
}

/* Blue/info status badge (e.g. Pending document) */
.dash-status--info {
  background-color: var(--dash-status-info-bg, #dbeafe);
  color: var(--dash-status-info-color, #2563eb);
}

/* "Needs action" status — distinct from --pending: something is blocked on
   THIS user, not just awaiting the other side (e.g. quote offer negotiation). */
.dash-status--action {
  background-color: var(--dash-status-action-bg, #fff7ed);
  color: var(--dash-status-action-color, #9a3412);
}

/* "Won"/finalized status — filled navy, for a deal that's fully closed
   (distinct from --approved's green, which reads as "currently active"). */
.dash-status--won {
  background-color: var(--dash-status-won-bg, var(--brand-primary, #0a3373));
  color: var(--dash-status-won-color, #ffffff);
}

/* Quote/category is submitted to insurers and HR is waiting on their
   offers ("Awaiting Offers") — warm amber/gold, same "waiting" family as
   --action's orange (Comparing) but a distinct hue so the two pre-decision
   waiting stages never look identical in the same categories table. */
.dash-status--awaiting {
  background-color: var(--dash-status-awaiting-bg, #fef9c3);
  color: var(--dash-status-awaiting-color, #854d0e);
}

/* Insurers actively preparing quotes for this category — distinct
   brand-secondary blue, kept apart from --info's darker blue and
   --approved's green so a quote's status lifecycle never has two
   same-colored steps in a row (HR quote detail categories table). */
.dash-status--quoting {
  background-color: var(--dash-status-quoting-bg, #eef2ff);
  color: var(--dash-status-quoting-color, var(--brand-secondary, #4374f3));
}

/* HR has chosen an offer for this category, pending final confirmation —
   distinct pink/magenta (HR quote detail categories table). */
.dash-status--pre-selected {
  background-color: var(--dash-status-pre-selected-bg, #fce7f3);
  color: var(--dash-status-pre-selected-color, #be185d);
}

/* Insurer is actively negotiating a preselected offer (post insurer-review,
   pre final-proposal) — distinct teal, kept apart from --quoting/--info's
   blues and --action's orange so this later negotiation stage never reads
   as the same step (HR quote detail/list categories tables). */
.dash-status--negotiating {
  background-color: var(--dash-status-negotiating-bg, #ccfbf1);
  color: var(--dash-status-negotiating-color, #0f766e);
}

/* Insurer has proposed a final offer, awaiting HR's decision — distinct
   violet, the last pre-decision negotiation stage before won/rejected
   (HR quote detail/list categories tables). */
.dash-status--final-review {
  background-color: var(--dash-status-final-review-bg, #ede9fe);
  color: var(--dash-status-final-review-color, #6d28d9);
}

/* ─────────────────────────────────────────────
   Member detail page
   ───────────────────────────────────────────── */

/* Section head with icon */
.ins-pol-section-head {
  display: flex;
  align-items: center;
  gap: 12px;
}

/* ─────────────────────────────────────────────
   THE FIELD CONTRACT — `--field-*`  (Charte §9)

   ONE token vocabulary for every form control in the app, whatever family the
   class name comes from (`.ins-pol-input`, `.modal-input`, `.modal-textarea`,
   the `<Combobox>` trigger, `<DateField>`, `<PhoneField>`, and — since Phase 2 —
   every single-surface field family too: the census sheet cell, the claim
   reference/request controls, the task review + comment composers, the Contact
   Center composer, the category board's editors and the OTP boxes). Before this
   block each family repeated the same
   literal values (height 44, radius 10, padding `0 14px`, font-size 14) and each
   theme had to rediscover them, which is how a page ended up with a plain input,
   a combobox and a date field disagreeing on fill, height and radius on the same
   row.

   WHAT IS ON THE CONTRACT, AND WHAT STAYS LOCAL
   Colours and STATE (fill, border, text, placeholder, focus, hover, locked) are
   always the contract's — those are the things a white label restyles, and a
   private copy of them is how a surface forks. GEOMETRY is the contract's only
   when the control is a standard-size form field; a control whose size is a
   LAYOUT requirement rather than a style choice keeps it local and says so at
   the rule (the 32px spreadsheet cell, the 30px inline doc-row select, the 34px
   board-rail search pill, the 20px-radius chat composer pill, the 52px OTP box).

   HOW IT WORKS
   - Every rule below reads a `--field-*` token and carries the PRE-CONTRACT
     value as its `var()` fallback. A theme that maps nothing therefore renders
     byte-identically to before (this is how `property-finder` and `backoffice`
     keep their historical look — neither maps a single `--field-*`).
   - THE BACKOFFICE IS OUT OF SCOPE, by product decision: it is not being
     redesigned, so it maps nothing and its own dark controls
     (`.boc-combo-trigger` in `backoffice/style.css`, `.claims-settings-input`
     below, and the `.dialog-card .modal-input` override) keep their literals.
     The fallback-only design is what makes that free — but it also means a
     fallback in this file is load-bearing for that brand: changing one is a
     backoffice visual change, not a no-op.
   - A brand opts in by declaring the set in its own `public/themes/<slug>/style.css`.
     Heale declares all of it with the Charte 2026 values, so the whole app's
     field standard moves from one place.
   - `global.css` stays brand-neutral: no Charte value appears here, only the
     legacy fallback + the token name.

   THE TOKENS
     --field-bg                fill of the control                    (#ffffff)
     --field-border            the control's OWN 1px border           (#e5e7eb)
     --field-hairline          1px chrome AROUND fields — dropdown
                               panel/date-roller borders, group
                               outlines, in-field separators. Separate
                               from `--field-border` on purpose: Heale
                               makes the field itself borderless (the
                               fill is the boundary) while those
                               hairlines must stay visible. Also what
                               `.fields-on-tint` (below) borrows as the
                               field's own edge.                      (#e5e7eb)
     --field-color             value text                             (#374151)
     --field-placeholder-color placeholder text                       (#9ca3af)
     --field-disabled-bg       fill when locked          (falls back to the fill)
     --field-disabled-color    text when locked          (falls back to the text)
     --field-disabled-opacity  dimming when locked — a brand that gives
                               the locked state its own FILL sets this
                               to 1 instead of dimming a copy of the
                               editable field                             (0.6)
     --field-height            control height                            (44px)
     --field-radius            corner radius                             (10px)
     --field-padding-x         horizontal inset                          (14px)
     --field-padding-y         vertical inset — only for controls whose
                               height is content-driven (textareas, and
                               inputs padded instead of fixed-height);
                               a fixed-height control pads 0 vertically
                               and centres its value       (per-site legacy)
     --field-font-size         value size                                (14px)
     --field-textarea-min-height  resting height of a FORM textarea. A chat
                               composer's min-height is layout, not this
                               token — see `.cc-composer-textarea`.   (92px)
     --field-focus-border      border colour on focus  (--brand-secondary)
     --field-focus-ring        the focus `box-shadow` ring, for the families
                               that have always drawn one. Not added where
                               none existed — this token unifies the five
                               hardcoded rings, it does not introduce a
                               sixth.                  (3px --brand-secondary/15%)
     --field-hover-bg          fill on hover, for a field-shaped control that
                               is a click-to-OPEN trigger (a select /
                               combobox trigger). A plain text input has no
                               hover affordance and reads none of this.
                               Defaults to the resting fill = no shift.
     --field-hover-border      border on hover for those same triggers. A
                               brand whose field is borderless sets this
                               `transparent` and lets the FILL carry the
                               affordance (§6: control hover is sand-200),
                               which is why hover is two tokens and not
                               one.                                  (#c8d3e8)
     --field-label-color       label tone                             (#9ca3af)
     --field-label-size        label size                                (11px)
     --field-label-gap         label → control gap        (per-site legacy gap)
     --field-grid-gap          two-column form grid gap            (18px 28px)

   A borderless field still has to show focus, so `--field-border` is expected
   to be `transparent` (not 0-width) wherever the fill is the boundary: the box
   reserves the 1px, and focus paints `--field-focus-border` into it with no
   layout shift. Hover follows the same logic one step further: on such a brand
   `--field-hover-border` is also `transparent` and `--field-hover-bg` does the
   work, because painting a hairline onto an otherwise borderless control on
   hover reads as a rendering glitch rather than an affordance.

   `.fields-on-tint` — THE ONE EXCEPTION, AND WHY IT IS A MODIFIER
   The borderless field works because the fill CONTRASTS with what is behind it:
   a cream-50 control on a white card reads as a box. Put the same control on an
   inner cream/tinted surface (`--modal-total-bg`, a cream metadata cell, a
   tinted row strip) and fill and backdrop are the same tone — the box vanishes
   and a filled value reads as static label text, with no affordance at all (§9).
   Apply `.fields-on-tint` to the CONTAINER in that situation. It re-points the
   shared `--field-border`/`--field-hover-border` at `--field-hairline`: the
   visible 1px (sand-400 on Heale) that the same field family already draws
   around its dropdown panels and date roller — no new colour. Because that is a
   custom PROPERTY swap rather than a painted `border-color`, ONE declaration on
   the container covers every control inside it and still composes with the focus
   and hover rules below: a surface never tags its inputs one by one, and no
   state colour is copied into a private override.

   THE RULE ITSELF IS NOT IN THIS FILE — IT IS PER BRAND. Which edge a field
   shows is a brand VALUE decision, and this file may only READ the contract,
   never declare it; that is the whole reason an unmapped brand renders
   unchanged. So a brand that needs the edge declares `.fields-on-tint` in its
   own `public/themes/<slug>/style.css`:
     · heale                    opts in — its field is deliberately borderless
                                (`--field-border: transparent`), so on cream it
                                has nothing left to read as a box, and the
                                sand-400 hairline goes into the reserved 1px.
     · cigna, iic               opt in for the SAME reason — both run Heale's
                                Charte chassis with only their brand hue swapped,
                                so both inherit `--field-border: transparent` and
                                the sand-400 hairline with it. (Cigna used to sit
                                in the row below, on a visible grey border of its
                                own; it stopped when its grey ramp went.)
     · difc, healthcross, r2s   nothing to do — `--field-border` is already a
                                visible grey/stone there, so the edge is present
                                on any backdrop. Re-pointing it at their
                                DECORATIVE `--field-hairline` would make it
                                fainter, not clearer.
     · property-finder,
       backoffice               map no `--field-*` at all, so every read falls
                                back to the historical `#e5e7eb` field border —
                                already visible on any surface.
   A brand with no `.fields-on-tint` rule therefore renders an opted-in container
   exactly like any other field, which is the right answer for every brand above
   that has none.

   The base token stays `transparent` on purpose — do NOT "fix" it globally.
   Field-on-white is the overwhelmingly common case and the Charte wants it
   edgeless there (the fill is the boundary); flipping the token would draw a
   hairline on every input in the app to serve the minority case. Transparent
   also has to stay transparent rather than 0-width so focus can paint
   `--field-focus-border` into the reserved 1px without shifting the box — which
   is exactly why this modifier can swap the COLOUR and change nothing else.
   This is the CONTROL layer only: opted-in surfaces stay borderless (§8).
   ───────────────────────────────────────────── */

/* Form grid */
.ins-pol-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--field-grid-gap, 18px 28px);
}

.ins-pol-field {
  display: flex;
  flex-direction: column;
  gap: var(--field-label-gap, 7px);
}

/* A cell that spans the whole grid row. Two consumers, both structural rather
   than decorative:

     - a `<SingleFileField fullWidth>` in a grid with an ODD number of cells —
       the upload row is taller than a 44px input, so it is the cell that should
       take the width rather than leave a hole beside it;
     - the trailing `.wizard-actions` row, which is never a field and always
       spans. That was an inline `style={{ gridColumn: '1 / -1' }}` at its call
       site; it is a layout rule, so it belongs here where a theme can see it.

   No-op at ≤768px, where the grid is already a single column. */
.ins-pol-field--full,
.ins-pol-form-grid > .wizard-actions {
  grid-column: 1 / -1;
}

.ins-pol-field-label {
  font-size: var(--field-label-size, 11px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--field-label-color, #9ca3af);
}

.ins-pol-input {
  width: 100%;
  height: var(--field-height, 44px);
  padding: 0 var(--field-padding-x, 14px);
  border-radius: var(--field-radius, 10px);
  border: 1px solid var(--field-border, #e5e7eb);
  background-color: var(--field-bg, #ffffff);
  font-size: var(--field-font-size, 14px);
  color: var(--field-color, #374151);
  outline: none;
  transition: border-color 0.12s;
}

.ins-pol-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.ins-pol-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

/* Locked field (§9). Base rule rather than a per-page override: every surface
   that renders a read-only identity field needs the same signal, and the three
   variants below are the three shapes the same control takes. */
.ins-pol-input:disabled,
.mf-combo-trigger.is-disabled,
.mf-phone.is-disabled {
  background-color: var(--field-disabled-bg, var(--field-bg, #ffffff));
  color: var(--field-disabled-color, var(--field-color, #374151));
  opacity: var(--field-disabled-opacity, 0.6);
}

.mf-phone.is-disabled .mf-phone-codebtn,
.mf-phone.is-disabled .mf-phone-number {
  color: var(--field-disabled-color, var(--field-color, #374151));
}

/* ── Settings surfaces ───────────────────────────────────────────────────────
   Shared by the tenant account page (`/settings`) and HR's company profile
   (`/hr/company-settings`). Its controls are on THE FIELD CONTRACT above
   (`--field-*`); everything around them — card, head, section label, buttons,
   the two-factor panel — reads `--settings-*`.

   Every read carries the value the rule already had as its fallback, so a brand
   that maps nothing renders byte-identically and only Heale picks up Charte 2026.
   NB the backoffice's `.claims-settings-*` form is a SEPARATE class family, not a
   consumer of these rules — a class token must match whole, so `.settings-input`
   never matches `class="claims-settings-input"`. It is out of scope and untouched.
   ──────────────────────────────────────────────────────────────────────────── */

.settings-grid {
  display: flex;
  flex-direction: column;
  gap: 20px;
  width: 100%;
  max-width: 760px;
}

/* §8: radius 16–18, no border — the shadow carries the card. Base keeps the
   pre-Charte bordered/flat look via the fallbacks. */
.settings-card {
  background-color: #ffffff;
  border: 1px solid var(--settings-card-border, #e5e7eb);
  border-radius: var(--settings-card-radius, 12px);
  padding: 24px;
  box-shadow: var(--settings-card-shadow, none);
}

/* Title + subtitle only. The 40px blue-tint glyph pad that used to sit left of
   the title was removed by explicit product decision after review (see
   `<SectionCard>`'s JSDoc in src/components/census/member-cards.tsx for the full
   history — the same call removed it from the member editor and
   /employee/my-information). `flex-start` + the gap existed to align the title
   against that pad, so there is nothing left to align to. */
.settings-card-head {
  margin-bottom: 20px;
}

.settings-card-title {
  margin: 0;
  font-family: var(--heading-font, inherit);
  font-size: var(--settings-card-title-size, 16px);
  font-weight: var(--settings-card-title-weight, 600);
  color: var(--settings-card-title-color, var(--brand-primary, #0a3373));
}

.settings-card-sub {
  margin: 2px 0 0;
  font-size: var(--settings-card-sub-size, 13px);
  color: var(--settings-card-sub-color, #7d93b3);
}

.settings-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.settings-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--field-grid-gap, 16px);
}

/* Stacking is the app-wide `.form-grid` rule in the 768px block below — one
   breakpoint for every two-column form row, not a private 560px one. */

.settings-field {
  display: flex;
  flex-direction: column;
  gap: var(--field-label-gap, 6px);
}

/* §9: label above the field. Weight/case/tracking are their own tokens rather
   than hardcoded uppercase (as `.ins-pol-field-label` is) because these two
   surfaces predate the Charte on every brand — an unmapped brand keeps its
   sentence-case 13px label. */
.settings-label {
  font-size: var(--field-label-size, 13px);
  font-weight: var(--settings-label-weight, 500);
  letter-spacing: var(--settings-label-tracking, normal);
  text-transform: var(--settings-label-transform, none);
  color: var(--field-label-color, #374151);
}

.settings-input {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: var(--field-height, 40px);
  padding: 0 var(--field-padding-x, 12px);
  font-size: var(--field-font-size, 14px);
  color: var(--field-color, #111827);
  background-color: var(--field-bg, #ffffff);
  border: 1px solid var(--field-border, #d2d9e5);
  border-radius: var(--field-radius, 8px);
  transition: border-color 0.12s, box-shadow 0.12s;
}

.settings-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.settings-input:focus {
  outline: none;
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  box-shadow: var(--field-focus-ring, 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent));
}

/* Not a real `:disabled` — the read-only email / MFA-secret rows are divs. The
   locked FILL and TEXT are the contract's; `--field-disabled-opacity` is not
   read here, so an unmapped brand keeps a full-opacity read-only row. */
.settings-input--readonly {
  background-color: var(--field-disabled-bg, #f8fafc);
  color: var(--field-disabled-color, #6b7280);
}

.settings-input-adorn {
  color: var(--field-placeholder-color, #9ca3af);
  flex-shrink: 0;
}

.settings-hint {
  font-size: var(--settings-hint-size, 12px);
  color: var(--settings-hint-color, #9ca3af);
}

/* Sub-section heading INSIDE a .settings-card, for a card with more than one
   logical group of fields (e.g. company profile's Address / Main contact /
   Billing contact groups) — a lighter-weight divider than a whole new card.
   §3's section label: 9.5–10px Bold UPPERCASE, tracking +6%, always ink-400. */
.settings-section-label {
  margin: 4px 0 -4px;
  font-size: var(--settings-section-label-size, 12px);
  font-weight: 600;
  color: var(--settings-section-label-color, #7d93b3);
  text-transform: uppercase;
  letter-spacing: var(--settings-section-label-tracking, 0.04em);
}

.settings-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 4px;
}

/* A lone opt-in CTA reads as part of the card body, not as a save bar, so it
   sits under the copy instead of right-aligned (§9's save-bar rule is for a
   form's Cancel/Save pair). */
.settings-actions--start {
  justify-content: flex-start;
}

/* §6 primary: solid fill, white label, pill on Heale. */
.settings-btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: var(--settings-btn-height, 40px);
  padding: 0 var(--settings-btn-padding-x, 18px);
  font-size: var(--settings-btn-size, 14px);
  font-weight: var(--settings-btn-weight, 500);
  color: var(--settings-btn-color, #ffffff);
  background-color: var(--settings-btn-bg, var(--brand-primary, #0a3373));
  border: none;
  border-radius: var(--settings-btn-radius, 8px);
  cursor: pointer;
  transition: opacity 0.12s, background-color 0.12s;
}

.settings-btn:hover:not(:disabled) {
  background-color: var(--settings-btn-bg-hover, var(--brand-secondary, #4374f3));
}

/* §6: disabled dims, never swaps to a grey fill. */
.settings-btn:disabled {
  opacity: var(--settings-btn-disabled-opacity, 0.5);
  cursor: not-allowed;
}

.settings-spin {
  animation: settings-spin 0.8s linear infinite;
}

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

/* §6 secondary: white fill, navy label + border on Heale. */
.settings-btn--outline {
  background-color: #ffffff;
  color: var(--settings-btn-outline-color, #374151);
  border: var(--settings-btn-outline-border, 1px solid #d2d9e5);
}

.settings-btn--outline:hover:not(:disabled) {
  background-color: var(--settings-btn-outline-bg-hover, #f8fafc);
}

/* §6 rejection: white fill, rust label AND border on Heale. */
.settings-btn--danger {
  background-color: #ffffff;
  color: var(--settings-btn-danger-color, #dc2626);
  border: var(--settings-btn-danger-border, 1px solid #fecaca);
}

.settings-btn--danger:hover:not(:disabled) {
  background-color: var(--settings-btn-danger-bg-hover, #fef2f2);
}

/* Two-factor authentication card */
.settings-mfa-enroll {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* §8: inner metadata cells inside a card are the page fill, radius 12, no shadow. */
.settings-mfa-qr {
  display: flex;
  justify-content: center;
  padding: 16px;
  background-color: var(--settings-panel-bg, #f8fafc);
  border: 1px solid var(--settings-panel-border, #e5e7eb);
  border-radius: var(--settings-panel-radius, 10px);
}

.settings-mfa-secret {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 0.04em;
  word-break: break-all;
}

.settings-mfa-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.settings-mfa-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding: 12px 16px;
  border: 1px solid var(--settings-panel-border, #e5e7eb);
  border-radius: var(--settings-panel-radius, 10px);
  background-color: var(--settings-panel-bg, #f8fafc);
}

.settings-mfa-row-info {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* §2 tinted status surface: the token at 12–16% for the fill, the same token at
   100% for the glyph and the "Enabled" label. */
.settings-mfa-row-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 8px;
  flex-shrink: 0;
  background-color: var(--settings-ok-bg, #e7f5ec);
  color: var(--settings-ok-color, #32a15e);
}

.settings-mfa-row-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--settings-mfa-row-title-color, #111827);
}

.settings-mfa-row-sub {
  margin: 1px 0 0;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--settings-ok-color, #32a15e);
}

/* Loading parity: the field/label/section shimmers read the SAME tokens the
   resolved controls do, so a brand that changes `--field-height` can never
   leave the skeleton at the old size (that regression has shipped twice).
   Class-only geometry, following `.dash-stat-shimmer` — <Shimmer>'s numeric
   props would win over these rules as inline styles. */
.settings-shimmer {
  display: block;
  width: 100%;
  border-radius: 4px;
}

.settings-shimmer--label {
  height: var(--field-label-size, 12px);
  width: 70px;
}

.settings-shimmer--section {
  height: var(--settings-section-label-size, 11px);
  width: 110px;
  margin: 4px 0 -4px;
}

.settings-shimmer--field {
  height: var(--field-height, 40px);
  border-radius: var(--field-radius, 8px);
}

.settings-shimmer--title {
  height: var(--settings-card-title-size, 16px);
  width: 160px;
}

.settings-shimmer--sub {
  height: var(--settings-card-sub-size, 12px);
  width: 220px;
  margin-top: 8px;
}

/* Segmented OTP/authenticator-code input — shared by the login step-up
   screen and the /settings MFA-enrollment verify step (src/components/ui/otp-input.tsx). */
.otp-input {
  display: flex;
  gap: 8px;
}

/* GEOMETRY IS LOCAL ON PURPOSE: a 44×52 box with a 20px digit is the segmented
   code shape — it is deliberately taller than a field and its width is one
   character, so `--field-height` / `--field-font-size` would both be wrong.
   Everything a brand actually reskins (fill, border, value, focus, locked) is on
   the contract. The `transparent` fill fallback is its historical look: before
   the contract these boxes drew no background at all. */
.otp-input-box {
  width: 44px;
  height: 52px;
  border-radius: var(--field-radius, 8px);
  border: 1px solid var(--field-border, #d2d9e5);
  background-color: var(--field-bg, transparent);
  text-align: center;
  font-size: 20px;
  font-weight: 600;
  color: var(--field-color, #111827);
  outline: none;
  box-sizing: border-box;
  transition: border-color 0.12s, box-shadow 0.12s;
}

.otp-input-box:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  box-shadow: var(--field-focus-ring, 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent));
}

.otp-input-box:disabled {
  background-color: var(--field-disabled-bg, #f8fafc);
  color: var(--field-disabled-color, #9ca3af);
}

@media (max-width: 400px) {
  .otp-input { gap: 6px; }
  .otp-input-box { width: 38px; height: 46px; font-size: 18px; }
}

/* Login step-up screen: the OTP row sits directly below the centered
   .login-magic-sent info card. Center it to match (scoped to .login-field,
   which only the login pages use — the /settings MFA verify step uses
   .settings-field and is left-aligned like the rest of that form, so it's
   unaffected) and add breathing room below the card. */
.login-field .otp-input {
  justify-content: center;
}

.login-magic-sent + .login-fields,
.login-magic-sent + .login-field {
  margin-top: 20px;
}

/* ─────────────────────────────────────────────
   Modal — shared, app-wide dialog component.
   Structure: .modal-overlay > .modal-card
     > .modal-head (.modal-title / .modal-subtitle / .modal-close)
     > .modal-body (.modal-section-label, .modal-grid, .modal-field …)
     > .modal-foot (.modal-btn-text / .modal-btn-primary)
   ───────────────────────────────────────────── */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  /* Scroll the backdrop (not the card) only when the modal can't fit the
     viewport. `margin: auto` on the card centres it when it fits. The scrollbar
     is visually hidden so a tall modal still looks "glued", never showing a bar. */
  overflow-y: auto;
  scrollbar-width: none;
  -ms-overflow-style: none;
  padding: 20px;
  background-color: var(--modal-overlay-bg, rgba(13, 21, 38, 0.45));
  animation: modal-fade 0.12s ease;
}

.modal-overlay::-webkit-scrollbar {
  display: none;
}

@keyframes modal-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

.modal-card {
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: var(--modal-max-width, 560px);
  /* Natural height — grows with content, never scrolls internally. Centred via
     auto margins; the overlay scrolls if it ever exceeds the viewport. */
  margin: auto;
  background-color: var(--modal-bg, #ffffff);
  border-radius: var(--modal-radius, 16px);
  box-shadow: 0 20px 60px rgba(13, 21, 38, 0.25);
  animation: modal-pop 0.14s ease;
}

@keyframes modal-pop {
  from { opacity: 0; transform: translateY(8px) scale(0.99); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

/* Wider variant for modal steps that host a denser layout (e.g.
   <CreateQuoteModal>'s cotation-type step, `<CotationTypeSelector>`'s
   two-card grid). */
.modal-card--wide {
  max-width: 720px;
}

.modal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 22px 24px 0;
  flex-shrink: 0;
}

.modal-title {
  font-size: 20px;
  font-family: var(--heading-font, inherit);
  font-weight: 800;
  letter-spacing: -0.3px;
  color: var(--modal-title-color, #18181b);
  margin: 0;
}

.modal-subtitle {
  font-size: 13px;
  color: var(--modal-subtitle-color, #9ca3af);
  margin: 4px 0 0;
}

.modal-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--modal-close-radius, 8px);
  /* Square bordered close button (Charte §12/§16 chrome) — old themes get
     `0 solid transparent` (invisible), same as the former `border: none`. */
  border: var(--modal-close-border-width, 0) solid var(--modal-close-border-color, transparent);
  background: transparent;
  color: var(--modal-close-color, #9ca3af);
  cursor: pointer;
  flex-shrink: 0;
  transition: background-color 0.12s, color 0.12s;
}

.modal-close:hover {
  background-color: var(--ins-pol-action-bg-hover, #f1f5fe);
  color: var(--brand-primary, #0a3373);
}

.modal-body {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 18px 24px;
  flex-shrink: 0;
}

.modal-section-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

.modal-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px;
}

.modal-field {
  display: flex;
  flex-direction: column;
  gap: var(--field-label-gap, 6px);
}

.modal-label {
  font-size: 13px;
  font-weight: 500;
  color: var(--modal-label-color, #374151);
}

.modal-label-req {
  color: var(--brand-error, #dc2626);
}

/* Same control as `.ins-pol-input`, inside a modal — so it reads the SAME field
   contract (§9) instead of repeating its literal metrics. Kept as its own
   selector at single-class weight, because `backoffice/style.css` reskins it
   dark via `.dialog-card .modal-input` and that override must keep winning. */
.modal-input {
  width: 100%;
  height: var(--field-height, 44px);
  padding: 0 var(--field-padding-x, 14px);
  border-radius: var(--field-radius, 10px);
  border: 1px solid var(--field-border, #e5e7eb);
  background-color: var(--field-bg, #ffffff);
  font-size: var(--field-font-size, 14px);
  color: var(--field-color, #374151);
  outline: none;
  box-sizing: border-box;
  transition: border-color 0.12s;
}

.modal-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.modal-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

.modal-input:disabled {
  background-color: var(--field-disabled-bg, var(--field-bg, #ffffff));
  color: var(--field-disabled-color, var(--field-color, #374151));
  opacity: var(--field-disabled-opacity, 0.6);
}

/* Read-only link + Copy button on one line (<ShareProfileModal>). `form-row` is not
   used here on purpose: the two controls belong together on mobile too — the row
   simply lets the input shrink instead of pushing the button off-card. */
.share-link-row {
  display: flex;
  align-items: center;
  gap: 8px;
}

.share-link-row .modal-input {
  flex: 1;
  min-width: 0;
}

.share-link-row .modal-btn-primary {
  flex-shrink: 0;
  white-space: nowrap;
}

/* Not editable, but not disabled either (the share link is meant to be copied).
   It shares the locked-field surface from the contract so a brand tunes both at
   once, while keeping its own override slot. */
.modal-input--readonly {
  background-color: var(--modal-input-readonly-bg, var(--field-disabled-bg, #eef2f9));
  color: var(--modal-input-readonly-color, var(--field-disabled-color, #6b7280));
  border-color: transparent;
}

/* Small helper note under a locked/disabled modal field (e.g. "start date can't
   be changed"). Neutral tone — this is guidance, not an error. */
.modal-field-note {
  margin: 0;
  font-size: 12px;
  color: var(--modal-field-note-color, #6b7280);
}

/* Inline validation/server-error message under a modal form (mirrors the
   ad-hoc red text already used in the create-invoice modal, promoted to a
   shared class so new modals don't re-inline the same style). */
.modal-field-error {
  margin: 0;
  font-size: 13px;
  color: var(--modal-field-error-color, var(--brand-error, #dc2626));
}

/* Grouped modal section — icon + label heading followed by its field grid,
   with a divider before every section after the first. Used by modals that
   want clearer visual rhythm between blocks (e.g. Create Invoice) without
   changing the plain `.modal-section-label` used elsewhere (Share modal). */
.modal-section {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.modal-section + .modal-section {
  padding-top: 16px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.modal-section-head {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--brand-secondary, #4374f3);
}

.modal-section-head svg {
  flex-shrink: 0;
}

/* Roomier variant of `.modal-grid` for sections with 4+ fields, so paired
   rows (e.g. Insured Members) don't read as cramped. */
.modal-grid--roomy {
  row-gap: 18px;
}

/* Prominent live-computed summary — two stat values side by side (e.g. end
   member count + total premium preview), replacing a single plain sentence. */
.modal-summary {
  display: flex;
  align-items: stretch;
  gap: 4px;
  padding: 16px 18px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #eef2f9);
}

.modal-summary-item {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.modal-summary-divider {
  width: 1px;
  align-self: stretch;
  background-color: var(--field-hairline, #d2d9e5);
  margin: 0 16px;
}

.modal-summary-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.modal-summary-value {
  font-size: 20px;
  font-weight: 800;
  color: var(--modal-total-color, #374151);
  line-height: 1.2;
}

.modal-summary-value--accent {
  color: var(--brand-primary, #0a3373);
}

@media (max-width: 420px) {
  .modal-summary {
    flex-direction: column;
    gap: 12px;
  }
  .modal-summary-divider {
    width: auto;
    height: 1px;
    margin: 0;
  }
}

.modal-foot {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 14px;
  padding: 16px 24px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  flex-shrink: 0;
}

.modal-btn-text {
  border: none;
  background: transparent;
  font-size: 14px;
  font-weight: 600;
  color: var(--modal-btn-text-color, #6b7280);
  cursor: pointer;
  padding: 8px;
  transition: color 0.12s;
}

.modal-btn-text:hover {
  color: var(--brand-primary, #0a3373);
}

/* §6 primary CTA, in a dialog footer. The fill is tokenized so a theme can put
   its real primary-action colour here — on Heale that's blue-400/blue-600, the
   same pair `.hr-dash-recent-btn-primary` uses, because "primary CTA" must be
   one colour app-wide; the base fallback keeps the historical navy for every
   other theme. */
.modal-btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 100px;
  background-color: var(--modal-btn-primary-bg, var(--brand-primary, #0a3373));
  color: var(--modal-btn-primary-fg, #ffffff);
  font-size: 14px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s, background-color 0.15s;
}

.modal-btn-primary:hover:not(:disabled) {
  opacity: var(--modal-btn-primary-hover-opacity, 0.88);
  background-color: var(--modal-btn-primary-bg-hover, var(--modal-btn-primary-bg, var(--brand-primary, #0a3373)));
}

.modal-btn-primary:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Title with a leading icon */
.modal-title--icon {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* Danger variants (e.g. Terminate Policy) */
.modal-title--danger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  color: var(--brand-error, #dc2626);
}

.modal-btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 100px;
  background-color: var(--brand-error, #dc2626);
  color: #ffffff;
  font-size: 14px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s;
}

.modal-btn-danger:hover:not(:disabled) {
  opacity: 0.88;
}

.modal-btn-danger:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ─────────────────────────────────────────────
   Dialog — <DialogProvider> / useDialog() (src/components/ui/dialog.tsx), the
   app-wide replacement for native confirm()/alert()/prompt(). It reuses the
   `.modal-*` chrome above (overlay, card, head, body, foot, text/primary
   buttons); only what a message dialog needs and a form modal doesn't lives
   here. Structure:
     .modal-overlay > .modal-card.dialog-card
       > .modal-head (.dialog-head-main > .dialog-icon + .dialog-title, .modal-close)
       > .modal-body (.dialog-text, .dialog-items > .dialog-item, .modal-field)
       > .modal-foot (.modal-btn-text + .modal-btn-primary | .dialog-btn-danger)
   ───────────────────────────────────────────── */

/* Narrower than a form modal — these hold a sentence, not a field grid. `min()`
   keeps it inside a 375px viewport alongside the overlay's 20px padding. */
.dialog-card {
  max-width: min(440px, 100%);
}

/* A title-only dialog renders no `.modal-body`, and `.modal-head` has 0 bottom
   padding — without this the title butts against the footer divider. */
.dialog-card--bare .modal-head {
  padding-bottom: 20px;
}

.dialog-head-main {
  display: flex;
  align-items: center;
  gap: 12px;
  min-width: 0;
}

.dialog-title {
  font-size: 17px;
  /* Long interpolated names wrap instead of clipping (Charte §21). */
  overflow-wrap: anywhere;
}

.dialog-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  flex-shrink: 0;
}

/* Outlined ring: transparent fill, a 1.5px border in the variant's own token
   color, and the glyph in that same color — not a filled/tinted surface. */
.dialog-icon--danger {
  background-color: transparent;
  border: 1.5px solid var(--dialog-danger-color, #dc2626);
  color: var(--dialog-danger-color, #dc2626);
}

.dialog-icon--success {
  background-color: transparent;
  border: 1.5px solid var(--dialog-success-color, #32a15e);
  color: var(--dialog-success-color, #32a15e);
}

.dialog-icon--info {
  background-color: transparent;
  border: 1.5px solid var(--dialog-info-color, #4374f3);
  color: var(--dialog-info-color, #4374f3);
}

.dialog-text {
  margin: 0;
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--dialog-text-color, #4b5563);
  overflow-wrap: anywhere;
}

/* Multi-part results (bulk-invite summary, bulk-delete failure list) — the
   structured replacement for newline-joining text into a native alert. Scrolls
   inside the panel rather than growing the card past the viewport. */
.dialog-items {
  list-style: none;
  margin: 0;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--dialog-items-bg, #eef2f9);
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.dialog-item {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--dialog-text-color, #4b5563);
  overflow-wrap: anywhere;
}

/* Destructive confirm CTA — same pill shape as `.modal-btn-primary` on the
   dialog danger token. Kept separate from `.modal-btn-danger` (which reads
   `--brand-error`) so a theme can put its real destructive colour here without
   retinting every error string in the app. */
.dialog-btn-danger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 100px;
  border: none;
  background-color: var(--dialog-danger-color, #dc2626);
  color: #ffffff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.15s, opacity 0.15s;
}

.dialog-btn-danger:hover:not(:disabled) {
  background-color: var(--dialog-danger-color-hover, var(--dialog-danger-color, #dc2626));
  opacity: 0.9;
}

.dialog-btn-danger:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

@media (max-width: 768px) {
  .dialog-card {
    max-width: 100%;
  }
  /* 44px tap targets, and the footer pair shares the width equally so label
     length never decides button size. */
  .dialog-card .modal-foot {
    gap: 10px;
  }
  .dialog-card .modal-btn-text,
  .dialog-card .modal-btn-primary,
  .dialog-card .dialog-btn-danger {
    flex: 1;
    min-height: 44px;
    justify-content: center;
    text-align: center;
  }
}

/* Callout box — summary of consequences */
.modal-callout {
  padding: 16px 18px;
  border-radius: 12px;
  background-color: var(--modal-callout-bg, #f5f6fa);
}

.modal-callout--danger {
  background-color: var(--modal-callout-danger-bg, color-mix(in srgb, var(--brand-error, #dc2626) 7%, white));
}

.modal-callout-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 10px;
}

/* Plain descriptive line under a callout title (e.g. "Delete this
   category?" → the consequence sentence) — a class-based stand-in for the
   inline `<p style={{ fontSize: 14, color: '#4b5563' }}>` some older modals
   still use ad hoc. */
.modal-callout-text {
  font-size: 14px;
  color: var(--modal-callout-item-color, #4b5563);
  margin: 0;
}

.modal-callout-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.modal-callout-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  color: var(--modal-callout-item-color, #4b5563);
}

.modal-callout-item svg {
  color: var(--modal-callout-check-color, #9ca3af);
  flex-shrink: 0;
}

/* A callout item whose text runs to several lines (a consequence spelled out,
   not a one-word fact) — the icon then belongs beside the FIRST line rather
   than floating at the vertical centre of the paragraph. */
.modal-callout-item--multiline {
  align-items: flex-start;
}

.modal-callout-item--multiline svg {
  margin-top: 2px;
}

/* Multiline text field */
.modal-textarea {
  width: 100%;
  min-height: var(--field-textarea-min-height, 92px);
  padding: var(--field-padding-y, 12px) var(--field-padding-x, 14px);
  border-radius: var(--field-radius, 10px);
  border: 1px solid var(--field-border, #e5e7eb);
  background-color: var(--field-bg, #ffffff);
  font-size: var(--field-font-size, 14px);
  font-family: inherit;
  color: var(--field-color, #374151);
  outline: none;
  resize: vertical;
  box-sizing: border-box;
  transition: border-color 0.12s;
}

.modal-textarea::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.modal-textarea:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

/* Custom select — styled dropdown (native <select> can't style its options) */
.modal-select {
  position: relative;
}

.modal-select-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  width: 100%;
  height: var(--field-height, 44px);
  padding: 0 var(--field-padding-x, 14px);
  border-radius: var(--field-radius, 10px);
  border: 1px solid var(--field-border, #e5e7eb);
  background-color: var(--field-bg, #ffffff);
  font-size: var(--field-font-size, 14px);
  color: var(--field-color, #374151);
  cursor: pointer;
  text-align: left;
  transition: border-color 0.12s;
}

/* Hover on a click-to-OPEN field trigger (§6 "control hover"). Two tokens, not
   one: this used to read `--field-hairline`, which on a brand whose field is
   deliberately borderless painted a visible edge on hover only — a rendering
   glitch, not an affordance. Such a brand now sets `--field-hover-border:
   transparent` and lets `--field-hover-bg` carry it, while a bordered brand
   keeps its historical `#c8d3e8` edge and no fill shift at all (the fill
   defaults back to the resting fill, so declaring it is a no-op there).
   A plain text input is deliberately NOT in here: it has no open-me affordance to
   signal. Neither is `.mf-combo-trigger` — see the note at its rule for why a
   base-level hover there would override a caller's own trigger skin. */
.modal-select-trigger:hover:not(:disabled) {
  background-color: var(--field-hover-bg, var(--field-bg, #ffffff));
  border-color: var(--field-hover-border, #c8d3e8);
}

.modal-select-trigger--open {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

.modal-select-placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.modal-select-chevron {
  color: var(--ins-pol-select-icon-color, var(--field-placeholder-color, #9ca3af));
  flex-shrink: 0;
  transition: transform 0.15s;
}

.modal-select-trigger--open .modal-select-chevron {
  transform: rotate(180deg);
}

.modal-select-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  z-index: 10;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 6px;
  max-height: 240px;
  overflow-y: auto;
  background-color: var(--dropdown-bg, #ffffff);
  border: 1px solid var(--dropdown-border, #e5e7eb);
  border-radius: 12px;
  box-shadow: 0 10px 30px rgba(13, 21, 38, 0.12);
  animation: modal-fade 0.12s ease;
}

/* Portaled variant — `<ModalSelect>` renders its menu into `document.body` so
   it escapes the modal body's overflow, and passes ONLY the measured
   top/left/width inline (genuine per-instance geometry). Position mode and
   stacking live here, so they can't drift per call site. Declared AFTER the
   base block on purpose: same specificity, so source order decides. */
.modal-select-menu--portal {
  position: fixed;
  right: auto;
  z-index: 1000;
}

.modal-select-option {
  width: 100%;
  padding: 11px 14px;
  border: none;
  background: transparent;
  border-radius: 8px;
  font-size: 14px;
  color: var(--modal-title-color, #18181b);
  text-align: left;
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}

.modal-select-option:hover {
  background-color: var(--ins-pol-action-bg-hover, #f1f5fe);
}

.modal-select-option--active {
  background-color: var(--modal-select-active-bg, color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, white));
  color: var(--brand-primary, #0a3373);
  font-weight: 600;
}

/* Check group — bordered list of circular checkboxes (e.g. Share modal) */
.modal-checkgroup {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 8px;
  border: 1px solid var(--field-hairline, #e5e7eb);
  border-radius: 12px;
}

.modal-check {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 9px 10px;
  border: none;
  background: transparent;
  border-radius: 8px;
  cursor: pointer;
  text-align: left;
  transition: background-color 0.12s;
}

.modal-check:hover {
  background-color: var(--ins-pol-action-bg-hover, #f1f5fe);
}

.modal-check-box {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 2px solid var(--modal-check-border, #d2d9e5);
  background: transparent;
  color: transparent;
  transition: background-color 0.12s, border-color 0.12s;
}

.modal-check--on .modal-check-box {
  background-color: var(--brand-primary, #0a3373);
  border-color: var(--brand-primary, #0a3373);
  color: #ffffff;
}

.modal-check-label {
  font-size: 14px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
}

.modal-check-sub {
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #9ca3af);
  margin-left: 6px;
}

@media (max-width: 560px) {
  .modal-grid { grid-template-columns: 1fr; }
}

/* Category multi-select list (square checkbox + name, selected rows tinted)
   — originally `<CreateQuoteModal>`'s, now the "Categories to quote" card on
   `/hr/quotes/new` (`src/app/(dashboard)/hr/quotes/new/page.tsx`). None of
   the generic `.modal-checkgroup`/`.modal-check` (circular radio-style)
   shapes fit a real checkable list, so this gets its own small class set.
   `.cqm-category-item`'s border/background tokens are pinned by
   `US-FLD.4.5` (`field-contract-brand-isolation.test.ts`) — a selectable
   tile, not a form field, so it deliberately does NOT ride the `--field-*`
   contract. */
.cqm-category-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.cqm-category-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: 8px;
  cursor: pointer;
  border: 1px solid var(--cqm-category-border, #e5e7eb);
  background-color: var(--cqm-category-bg, #ffffff);
  transition: background-color 0.12s, border-color 0.12s;
}

.cqm-category-item--selected {
  border-color: var(--brand-secondary, #4374f3);
  background-color: var(--modal-total-bg, #eef2f9);
}

/* A listed-but-ineligible category (0 members, or 1+ incomplete member) —
   still visible (never hidden, §17/§4 "disable + show reason" convention),
   just muted and non-interactive. */
.cqm-category-item--disabled {
  cursor: not-allowed;
  opacity: 0.7;
}

.cqm-category-checkbox {
  width: 16px;
  height: 16px;
  accent-color: var(--brand-secondary, #4374f3);
  flex-shrink: 0;
}

.cqm-category-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1;
  min-width: 0;
}

.cqm-category-label {
  font-size: 14px;
  font-weight: 500;
  color: var(--modal-label-color, #374151);
}

.cqm-category-item--selected .cqm-category-label {
  color: var(--brand-primary, #0a3373);
}

/* Member/"lives" count under the category name. */
.cqm-category-count {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* ─────────────────────────────────────────────
   Policy Benefits — summary cards + accordion
   ───────────────────────────────────────────── */
.bnf-summary {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}

.bnf-summary-card {
  background-color: var(--dash-stat-bg, #ffffff);
  border: 1px solid var(--dash-stat-border, #d2d9e5);
  border-radius: var(--dash-stat-radius, 14px);
  padding: 18px 22px;
}

.bnf-summary-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--dash-stat-label-color, #9ca3af);
  margin: 0 0 6px;
}

.bnf-summary-value {
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.3px;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

/* Accordion card */
.bnf-accordion {
  background-color: var(--dash-stat-bg, #ffffff);
  border: 1px solid var(--dash-stat-border, #d2d9e5);
  border-radius: 16px;
  overflow: hidden;
}

.bnf-section:not(:last-child) {
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.bnf-section-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  width: 100%;
  padding: 16px 22px;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  transition: background-color 0.12s;
}

.bnf-section-header:hover {
  background-color: var(--ins-pol-action-bg-hover, #f1f5fe);
}

.bnf-section-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

.bnf-section-chevron {
  color: var(--bnf-section-chevron-color, #9ca3af);
  flex-shrink: 0;
}

.bnf-section-body {
  padding: 0 22px 6px;
}

.bnf-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 11px 0;
}

.bnf-row:not(:last-child) {
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.bnf-row-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-field-label-color, #9ca3af);
}

.bnf-row-value {
  font-size: 14px;
  font-weight: 700;
  color: var(--bnf-row-value-color, var(--brand-primary, #0a3373));
  text-align: right;
  /* Values wrap instead of clipping (§21) — a long contact email or address
     line must stay fully readable. */
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Right-hand side of a row that carries more than one node (a coverage badge +
   its limit/optional note). */
.bnf-row-value-group {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}

/* Secondary note beside a row's value (limit, "Optional", inline amount). */
.bnf-row-meta {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--bnf-row-meta-color, #9ca3af);
  overflow-wrap: anywhere;
}

/* Benefit rows lead with the benefit NAME (body tone, sentence case), not an
   uppercase field label — the label variant above is for fixed metadata rows. */
.bnf-row-name {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--bnf-row-name-color, #374151);
  overflow-wrap: anywhere;
}

@media (max-width: 560px) {
  .bnf-summary { grid-template-columns: 1fr; }
}

/* ─────────────────────────────────────────────
   Policy Company — 2×2 info cards
   (rows reuse .bnf-row / .bnf-row-label / .bnf-row-value)
   ───────────────────────────────────────────── */
/* auto-fill + minmax rather than a fixed 2-up: the cards drop to one column on
   their own at narrow widths instead of overflowing. */
.cmp-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  gap: 16px;
}

.cmp-card {
  background-color: var(--dash-stat-bg, #ffffff);
  border: 1px solid var(--dash-stat-border, #d2d9e5);
  border-radius: var(--dash-stat-radius, 16px);
  /* §8: on a theme that drops the card border the shadow carries the card. */
  box-shadow: var(--dash-stat-shadow, none);
  padding: 16px 22px 8px;
}

.cmp-card-head {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 6px;
}

.cmp-card-icon {
  display: flex;
  align-items: center;
  color: var(--brand-primary, #0a3373);
  flex-shrink: 0;
}

.cmp-card-title {
  font-size: var(--cmp-card-title-size, 15px);
  font-weight: 700;
  color: var(--cmp-card-title-color, var(--brand-primary, #0a3373));
  margin: 0;
}

@media (max-width: 768px) {
  .cmp-grid { grid-template-columns: 1fr; }
}

/* ─────────────────────────────────────────────
   Insurer — Claims & pre-approvals Kanban board

   The Charte has no kanban section of its own (§11 is unbuilt), so the board is
   assembled from existing primitives: the column is chrome (dot + label + count
   + a tone underline), each CARD is a §8 card (white, no border on a Charte
   theme, soft shadow), each state is a §2 tinted surface, and the six tones
   resolve through `--ins-claim-tone-*` — mapped onto the palette's semantics in
   `heale/style.css`, and falling back to the historical `--brand-*` values for
   every other white-label.
   ───────────────────────────────────────────── */

/* Fullscreen board — card fills the whole viewport above everything else */
.ins-claim-card-wrap--full {
  position: fixed;
  inset: 0;
  z-index: 120;
  border-radius: 0;
  border: none;
  display: flex;
  flex-direction: column;
}

.ins-claim-card-wrap--full .ins-claim-board-wrap,
.ins-claim-card-wrap--full .hr-dash-recent-table-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
}

/* In fullscreen there's ample width, so always keep 5 equal columns and let
   each column scroll its own cards vertically. */
.ins-claim-card-wrap--full .ins-claim-board:not(.ins-claim-board--single) {
  grid-template-columns: repeat(5, minmax(0, 1fr));
  grid-auto-flow: row;
}

.ins-claim-card-wrap--full .ins-claim-board {
  height: 100%;
  align-items: stretch;
}

.ins-claim-card-wrap--full .ins-claim-col {
  min-height: 0;
}

.ins-claim-card-wrap--full .ins-claim-col-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  /* Breathing room so the card hover lift + shadow isn't clipped by the
     scroll container edge (which was hiding the first card's top border). */
  padding: 6px 4px 8px 2px;
}

/* Toolbar: search + view toggle + statuses (card header row) */
.ins-claim-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 16px 20px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ins-claim-search {
  flex: 1;
  min-width: 240px;
  max-width: 460px;
}

/* Sub-bar: filters (left) + status tabs (right), wraps on narrow widths */
.ins-claim-subbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px 24px;
  flex-wrap: wrap;
  padding: 14px 20px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ins-claim-toolbar-right {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-shrink: 0;
}

/* Filters row */
.ins-claim-filters {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Status tabs */
.ins-claim-tabs {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.ins-claim-tab-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* The six board tones. Each resolves through its own `--ins-claim-tone-*`
   consumed token so a Charte theme maps them onto the palette's semantic
   colours (gold-500 = waiting, green-500 = done, rust-600 = rejected…) without
   touching `--brand-warning`/`--brand-success`, which stay unmapped on purpose
   (they still carry §1-retired values for the screens not yet converted). */
.ins-claim-tab-dot--coral { background-color: var(--ins-claim-tone-coral, var(--brand-accent, #ff6733)); }
.ins-claim-tab-dot--blue  { background-color: var(--ins-claim-tone-blue, var(--brand-secondary, #4374f3)); }
.ins-claim-tab-dot--amber { background-color: var(--ins-claim-tone-amber, var(--brand-warning, #eca04b)); }
.ins-claim-tab-dot--navy  { background-color: var(--ins-claim-tone-navy, var(--brand-primary, #0a3373)); }
.ins-claim-tab-dot--green { background-color: var(--ins-claim-tone-green, var(--brand-success, #32a15e)); }
.ins-claim-tab-dot--red   { background-color: var(--ins-claim-tone-red, var(--dash-status-rejected-color, #dc2626)); }

/* Claims status FILTER pills (§7) reuse the shared `.ins-pol-seg` look but carry
   a leading status dot + count, so they need inline-flex alignment the base
   text-only pill doesn't set. Scoped to the claims status tab row so no other
   `.ins-pol-seg` caller (census / policies) is affected. */
.ins-claim-tabs .ins-pol-seg {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.ins-claim-tabs .ins-pol-seg .filter-tabs-count {
  margin-left: 0;
}

/* The board sits in a soft inset panel inside the white card — §8's "inner
   cell" tone, so on a Charte theme it is cream-50 against the card's white. */
.ins-claim-board-wrap {
  padding: 18px 20px;
  background-color: var(--ins-claim-board-bg, var(--dash-body-bg, #f8fafd));
  overflow-x: auto;
}

/* Board grid: 5 equal columns that shrink to fit (no overflow on wide screens) */
.ins-claim-board {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 14px;
  align-items: start;
}

/* Single-column view (when a status filter is active) */
.ins-claim-board--single {
  grid-template-columns: minmax(0, 420px);
}

.ins-claim-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* Column header */
.ins-claim-col-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 7px;
  padding: 10px 4px;
  margin-bottom: 8px;
  border-bottom: 2px solid var(--ins-claim-col-accent, #e5e7eb);
}

.ins-claim-col-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background-color: var(--ins-claim-col-accent, #9ca3af);
  flex-shrink: 0;
}

.ins-claim-col-title {
  font-size: var(--ins-claim-col-title-size, 14px);
  font-weight: 700;
  color: var(--ins-claim-col-title-color, var(--brand-primary, #0a3373));
  white-space: nowrap;
}

.ins-claim-col-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 100px;
  background-color: var(--ins-claim-col-count-bg, var(--ins-pol-chip-count-bg, #eef2f9));
  color: var(--ins-claim-col-count-color, var(--ins-pol-filter-color, #6b7280));
  font-size: 12px;
  font-weight: 700;
}

.ins-claim-col-overdue {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-left: auto;
  padding: 2px 9px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 700;
  background-color: var(--ins-claim-alert-bg, var(--dash-status-rejected-bg, #fee2e2));
  color: var(--ins-claim-alert-color, var(--dash-status-rejected-color, #dc2626));
  cursor: default;
}

/* Compact variant (collapsed board): icon + count only, full text on hover */
.ins-claim-col-overdue--compact {
  gap: 3px;
  padding: 2px 7px;
}

.ins-claim-col-head--coral { --ins-claim-col-accent: var(--ins-claim-tone-coral, var(--brand-accent, #ff6733)); }
.ins-claim-col-head--blue  { --ins-claim-col-accent: var(--ins-claim-tone-blue, var(--brand-secondary, #4374f3)); }
.ins-claim-col-head--amber { --ins-claim-col-accent: var(--ins-claim-tone-amber, var(--brand-warning, #eca04b)); }
.ins-claim-col-head--navy  { --ins-claim-col-accent: var(--ins-claim-tone-navy, var(--brand-primary, #0a3373)); }
.ins-claim-col-head--green { --ins-claim-col-accent: var(--ins-claim-tone-green, var(--brand-success, #32a15e)); }
.ins-claim-col-head--red   { --ins-claim-col-accent: var(--ins-claim-tone-red, var(--dash-status-rejected-color, #dc2626)); }

.ins-claim-col-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.ins-claim-col-empty {
  padding: 18px 12px;
  text-align: center;
  font-size: 13px;
  color: var(--ins-claim-muted-color, #9ca3af);
  border: 1px dashed var(--ins-claim-col-empty-border, var(--ins-pol-chip-border, #e5e7eb));
  border-radius: var(--brand-radius, 12px);
  margin: 0;
}

/* Card */
.ins-claim-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  background-color: var(--ins-claim-card-bg, #ffffff);
  /* §8: a Charte theme drops the hairline to transparent and lets the soft warm
     shadow carry the card; every other white-label keeps its border via the
     fallback and opts out of the shadow (default `none`). */
  border: 1px solid var(--ins-claim-card-border, var(--ins-pol-chip-border, #e5e7eb));
  border-radius: var(--ins-claim-card-radius, var(--brand-radius, 12px));
  box-shadow: var(--ins-claim-card-shadow, none);
  cursor: grab;
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.14s, border-color 0.14s, transform 0.14s;
}
.ins-claim-card:active { cursor: grabbing; }

.ins-claim-card:hover {
  border-color: var(--ins-claim-card-border-hover, color-mix(in srgb, var(--brand-secondary, #4374f3) 40%, white));
  box-shadow: var(--ins-claim-card-shadow-hover, 0 4px 14px rgba(10, 51, 115, 0.08));
  transform: translateY(-1px);
}

/* An overdue card is the board's "action needed" row. §12 spends that state as a
   3px coral left rule and NOTHING else, so a Charte theme sets the rule width +
   colour and neutralises the tint; base themes keep the tinted border/fill via
   the fallbacks. `border-left` is declared last so it wins over `border-color`. */
.ins-claim-card--overdue {
  border-color: var(--ins-claim-overdue-border, var(--dash-status-rejected-bg, #fecaca));
  background-color: var(--ins-claim-overdue-bg, #fffaf9);
  border-left: var(--ins-claim-overdue-rule-width, 1px) solid
    var(--ins-claim-overdue-rule-color,
        var(--ins-claim-overdue-border, var(--dash-status-rejected-bg, #fecaca)));
}

.ins-claim-card--dragging {
  opacity: 0.4;
  cursor: grabbing;
}

.ins-claim-col-rejected-sep {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0 4px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ins-claim-rejected-sep-color, #e05a3a);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.ins-claim-col-rejected-sep::before,
.ins-claim-col-rejected-sep::after {
  content: '';
  flex: 1;
  height: 1px;
  background: currentColor;
  opacity: 0.25;
}

.ins-claim-card--skeleton {
  pointer-events: none;
  cursor: default;
  gap: 10px;
  display: flex;
  flex-direction: column;
}

.ins-claim-col--can-drop {
  outline: 2px dashed var(--brand-primary, #0a3373);
  outline-offset: -4px;
  border-radius: 10px;
}

/* The live drop target reads as a SELECTION (§12: blue-400 marker + a blue
   @0.06 wash on a Charte theme), never as "action needed" — coral is reserved
   for the overdue rule above so the two can't be confused mid-drag. */
.ins-claim-col--drop-over {
  outline-color: var(--ins-claim-drop-over-outline, var(--brand-accent, #ff6733));
  background: var(--ins-claim-drop-over-bg, rgba(255, 103, 51, 0.04));
}

/* A board column the caller has no permission to move a claim INTO, while a
   drag is in flight. The counterpart of `--can-drop`: same dashed outline
   geometry so the two read as one vocabulary, muted instead of brand-coloured.
   Deliberately NOT `.ui-locked` on the column — that dims at 45% and would take
   the column's real claim cards with it; a read-only caller is entitled to READ
   them. Only the drop affordance dims (this outline + the "Can't move here"
   line); the permanent marker is the header's `.ins-claim-col-lock` glyph. */
.ins-claim-col--locked {
  outline: 2px dashed var(--ins-claim-col-locked-outline, #d1d5db);
  outline-offset: -4px;
  border-radius: 10px;
}

/* The always-present lock marker in a locked column's header — the reason a
   drop will be refused is discoverable here BEFORE any drag, since HTML5 drag
   suppresses the pointer events a `<Tooltip>` needs. `margin-left: auto` pushes
   it right; the overdue pill after it keeps its own auto margin, so the two sit
   together at the end of the row. Focusable so a keyboard user reaches the
   tooltip too (same reason `<LockedButton>` stays in the tab order). */
.ins-claim-col-lock {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  color: var(--ins-claim-col-lock-color, #9ca3af);
  cursor: not-allowed;
  flex-shrink: 0;
}

/* Both the lock and the overdue pill claim `margin-left: auto`. With both
   present the second one would open a second gap and strand the lock mid-row,
   so the pill yields its auto margin and the pair sits together at the end. */
.ins-claim-col-lock + .ins-claim-col-overdue {
  margin-left: 0;
}

/* Approve / generic modal */
.ins-claim-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.ins-claim-modal {
  background: #fff;
  border-radius: var(--ins-claim-modal-radius, 14px);
  padding: 28px 28px 24px;
  width: 340px;
  max-width: 90vw;
  box-shadow: var(--ins-claim-modal-shadow, 0 8px 40px rgba(0,0,0,0.18));
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Wider variant for the decision popups that host a full form (amount +
   comment + document upload) rather than a single field. Replaces a
   `style={{ width: 440 }}` repeated at seven call sites. */
.ins-claim-modal--wide {
  width: 440px;
}

.ins-claim-modal-title {
  font-size: 16px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

.ins-claim-modal-sub {
  font-size: 13px;
  color: var(--ins-claim-muted-color, #6b7280);
  margin: 0;
}

.ins-claim-modal-label {
  display: flex;
  flex-direction: column;
  gap: 6px;
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text, #111);
}

.ins-claim-modal-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
  margin-top: 4px;
}

.ins-claim-card-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

/* The card's identifier line. §10 paints identifiers navy-700, which is what a
   Charte theme maps `--ins-claim-ref-color` to; base themes keep the grey. */
.ins-claim-ref {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: var(--ins-claim-ref-color, #9ca3af);
}

.ins-claim-tags {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
  justify-content: flex-end;
}

.ins-claim-tag {
  display: inline-flex;
  align-items: center;
  height: 18px;
  padding: 0 6px;
  border-radius: 5px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
}

/* Every tag is a §2 tinted surface: the tone at ~14% for the fill, the same
   tone at 100% for the label. `--no` is the one neutral (a raw claim number). */
.ins-claim-tag--direct {
  background-color: color-mix(in srgb, var(--ins-claim-tone-green, var(--brand-success, #32a15e)) 14%, transparent);
  color: var(--ins-claim-tone-green, var(--brand-success, #32a15e));
}

.ins-claim-tag--net {
  background-color: var(--ins-claim-tag-neutral-bg, var(--ins-pol-chip-count-bg, #eef2f9));
  color: var(--ins-claim-tone-navy, var(--brand-primary, #0a3373));
}

.ins-claim-tag--pre {
  background-color: color-mix(in srgb, var(--ins-claim-tone-blue, var(--brand-secondary, #4374f3)) 14%, transparent);
  color: var(--ins-claim-tone-blue, var(--brand-secondary, #4374f3));
}

.ins-claim-tag--no {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  background-color: var(--ins-claim-tag-neutral-bg, #f3f4f6);
  color: var(--ins-claim-tag-neutral-color, #6b7280);
}

.ins-claim-flag {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  background-color: var(--ins-claim-alert-bg, var(--dash-status-rejected-bg, #fee2e2));
  color: var(--ins-claim-alert-color, var(--dash-status-rejected-color, #dc2626));
}

.ins-claim-card-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--ins-claim-title-color, var(--brand-primary, #0a3373));
  margin: 0;
  line-height: 1.35;
}

.ins-claim-member {
  display: flex;
  align-items: center;
  gap: 9px;
}

.ins-claim-member-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.ins-claim-member-name {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ins-claim-member-color, #18181b);
}

.ins-claim-relation {
  display: inline-flex;
  align-items: center;
  height: 15px;
  padding: 0 6px;
  border-radius: 100px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.04em;
  background-color: color-mix(in srgb, var(--ins-claim-tone-coral, var(--brand-accent, #ff6733)) 14%, transparent);
  color: var(--ins-claim-relation-color, var(--brand-accent, #ff6733));
}

.ins-claim-member-sub {
  font-size: 11px;
  color: var(--ins-claim-muted-color, #9ca3af);
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ins-claim-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding-top: 9px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.ins-claim-amount {
  font-size: 12px;
  color: var(--ins-claim-muted-color, #6b7280);
  font-weight: 500;
}

.ins-claim-amount strong {
  font-size: 14px;
  font-weight: 700;
  color: var(--ins-claim-amount-color, var(--brand-primary, #0a3373));
}

/* Secondary "Approved: X" line — shown only when approved_amount differs from
   the originally claimed amount (e.g. a partial approval). Shared by the
   claims board card and the flat claims list on /insurer/claims. */
.ins-claim-amount-approved {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ins-claim-tone-green, var(--brand-success, #32a15e));
  white-space: nowrap;
}

.ins-claim-foot-meta {
  display: flex;
  align-items: center;
  gap: 10px;
}

.ins-claim-date {
  font-size: 12px;
  color: var(--ins-claim-muted-color, #9ca3af);
  font-weight: 500;
}

.ins-claim-ago {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  color: var(--ins-claim-muted-color, #9ca3af);
  font-weight: 500;
}

/* List-view status badges (reuse .dash-status base) */
/* List-view status badges — one §2 tinted surface per tone (token @14% fill +
   the same token at 100% for the label). amber/green/red already resolve through
   the shared `--dash-status-*` pairs, which a Charte theme maps to
   gold-500 / green-500 / rust-600. */
.ins-claim-status--coral { background-color: color-mix(in srgb, var(--ins-claim-tone-coral, var(--brand-accent, #ff6733)) 14%, transparent); color: var(--ins-claim-tone-coral, var(--brand-accent, #ff6733)); }
.ins-claim-status--blue  { background-color: color-mix(in srgb, var(--ins-claim-tone-blue, var(--brand-secondary, #4374f3)) 14%, transparent); color: var(--ins-claim-tone-blue, var(--brand-secondary, #4374f3)); }
.ins-claim-status--amber { background-color: var(--dash-status-pending-bg, #fef3c7); color: var(--dash-status-pending-color, #d97706); }
.ins-claim-status--navy  { background-color: color-mix(in srgb, var(--ins-claim-tone-navy, var(--brand-primary, #0a3373)) 10%, transparent); color: var(--ins-claim-tone-navy, var(--brand-primary, #0a3373)); }
.ins-claim-status--green { background-color: var(--dash-status-approved-bg, #d1fae5); color: var(--dash-status-approved-color, #059669); }
.ins-claim-status--red   { background-color: var(--dash-status-rejected-bg, #fee2e2); color: var(--dash-status-rejected-color, #dc2626); }

/* Claim detail page status badge — scoped override so THIS page's status
   pill (topbar + `<InsurerClaimInfoCard>`) reads as Task Manager-consistent:
   normal-case text + leading colored dot, not the shared `.dash-status`
   base's uppercase/letter-spaced/no-dot treatment. Same technique as
   `.task-detail-status .task-status` above — deliberately a compound
   selector so it only affects `.dash-status` elements also carrying this
   class, never the shared base used elsewhere in the app. */
.dash-status.ins-claim-status-badge {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 600;
  gap: 6px;
}

.dash-status.ins-claim-status-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 999px;
  flex-shrink: 0;
  background-color: currentColor;
}

/* Responsive: below ~1180px, 5 equal columns get too cramped, so switch the
   board to a horizontal scroller with comfortable fixed-width columns. */
@media (max-width: 1180px) {
  .ins-claim-board {
    grid-auto-flow: column;
    grid-auto-columns: 264px;
    grid-template-columns: none;
  }
  .ins-claim-board--single {
    grid-auto-columns: minmax(0, 1fr);
  }
}

.ins-claim-list-link {
  text-decoration: none;
  transition: color 0.12s;
}

.ins-claim-list-link:hover {
  color: var(--brand-secondary, #4374f3);
}

/* ─────────────────────────────────────────────
   Claim / pre-approval submitted confirmation
   (`<ClaimSuccessScreen>`) — centers a fixed-width
   card inside whatever page shell hosts it (the
   dashboard `.wizard-card` or the bare standalone page).
   ───────────────────────────────────────────── */
.claim-success {
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: var(--dash-body-bg, #f8fafd);
  padding: 24px;
}

.claim-success-card {
  max-width: 480px;
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
}

/* Decision panel below the success card — only mounts once a decision exists
   (nothing right after submit), centred to the same width as the card. */
.claim-success-decision {
  max-width: 480px;
  width: 100%;
  margin: 0 auto 24px;
  padding: 0 24px;
}

.claim-success-icon {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background-color: var(--dash-status-approved-bg, #d1fae5);
  display: flex;
  align-items: center;
  justify-content: center;
}

.claim-success-heading {
  text-align: center;
}

.claim-success-title {
  font-size: 28px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 8px;
  line-height: 1.2;
}

.claim-success-subtitle {
  font-size: 15px;
  color: #6b7280;
  margin: 0;
  line-height: 1.5;
}

.claim-success-ref-card {
  background-color: #ffffff;
  border-radius: 12px;
  padding: 20px 24px;
  width: 100%;
  box-sizing: border-box;
}

.claim-success-ref-label {
  font-size: 11px;
  font-weight: 600;
  color: #9ca3af;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin-bottom: 6px;
}

.claim-success-ref-value {
  font-size: 26px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin-bottom: 12px;
  letter-spacing: -0.01em;
}

.claim-success-ref-hint {
  font-size: 13px;
  color: #6b7280;
  margin: 0;
  line-height: 1.5;
}

.claim-success-close-hint {
  font-size: 14px;
  color: #9ca3af;
  margin: 0;
  text-align: center;
}

.claim-success-actions {
  display: flex;
  gap: 12px;
  width: 100%;
}

.claim-success-btn {
  flex: 1;
  min-width: 160px;
  height: 48px;
  border-radius: 12px;
  font-size: 15px;
  font-weight: 500;
  cursor: pointer;
}

.claim-success-btn--secondary {
  border: 1px solid #d1d5db;
  background-color: #ffffff;
  color: #374151;
}

.claim-success-btn--primary {
  border: none;
  background-color: var(--brand-accent, #ff6733);
  color: #ffffff;
}

/* ─────────────────────────────────────────────
   Claim detail page
   ───────────────────────────────────────────── */

/* Breadcrumb chips (insurer ref, channel) */
.ins-claim-crumb-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 600;
  background-color: var(--ins-pol-chip-count-bg, #eef2f9);
  color: var(--brand-primary, #0a3373);
}

.ins-claim-crumb-tag strong {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-weight: 700;
}

.ins-claim-crumb-tag--muted {
  background-color: transparent;
  color: var(--dash-breadcrumb-color, #9ca3af);
  font-weight: 600;
}

/* Header meta row */
.ins-claim-detail-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-top: 10px;
}

.ins-claim-detail-member {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.ins-claim-detail-member-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}

.ins-claim-meta-sep {
  width: 1px;
  height: 18px;
  background-color: var(--ins-pol-chip-border, #e5e7eb);
}

.ins-claim-meta-item {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}

.ins-claim-meta-icon {
  color: #9ca3af;
}

.ins-claim-meta-key {
  color: #9ca3af;
  font-weight: 600;
}

.ins-claim-meta-val {
  color: #374151;
  font-weight: 600;
}

.ins-claim-meta-val--mono {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-weight: 700;
  color: #18181b;
}

.ins-claim-plan-badge {
  display: inline-flex;
  align-items: center;
  margin-top: 12px;
  padding: 4px 12px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 600;
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 10%, white);
  color: var(--brand-secondary, #4374f3);
}

.ins-claim-detail-head-right {
  align-items: flex-start;
  gap: 12px;
}

/* Employee claim detail — two-column grid (documents | conversation) */
.claim-detail-grid {
  display: grid;
  grid-template-columns: 1fr 1.6fr;
  gap: 16px;
  align-items: start;
}
@media (max-width: 700px) {
  .claim-detail-grid {
    grid-template-columns: 1fr;
  }
}

/* Employee claim / pre-approval DETAIL loading skeleton — mirrors the real
   detail page's outer scroll shell + white summary card so the route-transition
   `loading.tsx` and the page's own mount-time skeleton hand off with zero
   layout shift. Shimmer blocks come from `<Shimmer>`; these classes are only
   the containers, so no call site needs an inline style. */
.claim-detail-shell {
  display: flex;
  height: 100%;
  overflow: hidden;
}
.claim-detail-shell-scroll {
  flex: 1;
  overflow-y: auto;
}
.claim-detail-max {
  max-width: 860px;
}
.claim-detail-summary-card {
  background: #fff;
  border-radius: 16px;
  border: 1px solid #e5e7eb;
  padding: 28px;
  margin-bottom: 16px;
}
.claim-detail-skel-head {
  display: flex;
  justify-content: space-between;
  margin-bottom: 20px;
}
.claim-detail-skel-titles {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 24px;
}
.claim-detail-skel-rule {
  border: none;
  border-top: 1px solid #f1f5f9;
  margin: 0 0 24px;
}
.claim-detail-skel-stats {
  display: flex;
  gap: 40px;
}
.claim-detail-skel-stat {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Two-column layout: main + decision/activity sidebar */
/* Claim detail layout: full-height grid, sidebar flush to topbar */
.ins-claim-detail-body {
  flex: 1;
  min-height: 0;
  display: flex;
  overflow: hidden;
}

.ins-claim-detail-grid {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  align-items: start;
  overflow: hidden;
}

.ins-claim-detail-main {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
  padding: 24px 28px;
  overflow-y: auto;
  height: 100%;
  box-sizing: border-box;
  background: var(--dash-body-bg, #f8fafd);
}

.ins-claim-detail-side {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  height: 100%;
  overflow-y: auto;
  border-left: 1px solid var(--dash-topbar-sep-color, #d2d9e5);
  background: var(--dash-topbar-bg, #f8fafd);
}

@media (max-width: 980px) {
  .ins-claim-detail-body {
    flex-direction: column;
    overflow: visible;
  }
  .ins-claim-detail-grid {
    grid-template-columns: minmax(0, 1fr);
    display: flex;
    flex-direction: column;
    overflow: visible;
    height: auto;
  }
  .ins-claim-detail-main,
  .ins-claim-detail-side {
    height: auto;
    overflow: visible;
    border-left: none;
    border-top: 1px solid var(--dash-topbar-sep-color, #d2d9e5);
  }
}

/* Insurer claim / pre-approval detail — employee-style layout: a single-column
   main stack (capped at 860px, mirroring the employee claim page) beside an
   insurer-only right rail (Decision + Activity). The whole page shares one
   vertical scroll (the dash-body), NOT independent scrolling columns. The rail
   sits to the right on desktop and stacks BELOW the main column, full width, at
   ≤768px — no horizontal page scroll at 375px. */
.ins-claim-page-grid {
  display: flex;
  align-items: flex-start;
  gap: 20px;
}

.ins-claim-page-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Insurer-only activity column — Decision + the merged Conversation & activity
   feed. Sits right of the main column on desktop, stacks BELOW it (full width)
   at ≤768px so there's no horizontal scroll at 375px. Widened from 320px so the
   feed's composer, reply button and message bubbles read comfortably. */
.ins-claim-page-rail {
  width: 460px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

@media (max-width: 768px) {
  .ins-claim-page-grid {
    flex-direction: column;
  }
  .ins-claim-page-main {
    max-width: 100%;
    width: 100%;
  }
  .ins-claim-page-rail {
    width: 100%;
  }
}

/* Member info card above reference */
.ins-claim-member-card {
  padding: 14px 18px;
}

.ins-claim-member-card-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 13px;
}

/* Payment/approval reference field. A standard form field, so it takes the
   contract's geometry as well as its colours; `font-weight: 600` stays local
   because the value IS an identifier and is deliberately heavier than body text.
   It used to borrow `--ins-pol-chip-border` for its edge — a CHIP token, which
   only fitted because that token happened to hold the value it wanted (the same
   trap `.cqm-category-item` was pulled out of in Phase 1). */
.ins-claim-ref-input {
  width: 100%;
  padding: var(--field-padding-y, 9px) var(--field-padding-x, 12px);
  border: 1px solid var(--field-border, #e5e7eb);
  border-radius: var(--field-radius, 8px);
  font-size: var(--field-font-size, 14px);
  font-weight: 600;
  color: var(--field-color, #18181b);
  background-color: var(--field-bg, #ffffff);
  outline: none;
  transition: border-color 0.12s, box-shadow 0.12s;
}

.ins-claim-ref-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.ins-claim-ref-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  box-shadow: var(--field-focus-ring, 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent));
}

.ins-claim-ref-input:disabled {
  background-color: var(--field-disabled-bg, #f8fafc);
  color: var(--field-disabled-color, #9ca3af);
  opacity: var(--field-disabled-opacity, 1);
  cursor: not-allowed;
}

/* Document tabs */
.ins-claim-doc-tabs {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 12px 16px 0;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ins-claim-doc-tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border: none;
  background: transparent;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: #6b7280;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s;
}

.ins-claim-doc-tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--brand-secondary, #4374f3);
  color: #fff;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  margin-left: 4px;
}

/* Same §7 underline selector, same reason as `.mf-seg-btn.is-active`: two tokens
   (label / rule) whose fallbacks are the expressions that were inline. They are
   SPLIT even though both resolved to the same value here, because the family they
   join separates the two — `--tabs-underline-seg-active-color` vs
   `--tabs-underline-active-color` — and a brand that wants a lighter rule under a
   darker label must not have to fork the rule to get it. */
.ins-claim-doc-tab--active {
  color: var(--ins-claim-doc-tab-active-color, var(--brand-primary, #0a3373));
  border-bottom-color: var(--ins-claim-doc-tab-active-border, var(--brand-primary, #0a3373));
}

.ins-claim-doc-body {
  padding: 0;
  background-color: transparent;
}

.ins-claim-doc-preview {
  display: flex;
  justify-content: center;
}

.ins-claim-doc-sheet {
  width: 100%;
  max-width: 380px;
  background-color: #ffffff;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  border-radius: 10px;
  padding: 16px 18px;
}

.ins-claim-doc-sheet-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.ins-claim-doc-sheet-title {
  font-size: 13px;
  font-weight: 800;
  letter-spacing: 0.08em;
  color: var(--brand-primary, #0a3373);
}

.ins-claim-doc-sheet-ref {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 12px;
  font-weight: 700;
  color: #9ca3af;
}

.ins-claim-doc-sheet-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 150px;
  margin: 14px 0;
  border-radius: 8px;
  background-image: repeating-linear-gradient(
    45deg,
    #f3f6fb,
    #f3f6fb 10px,
    #eef2f9 10px,
    #eef2f9 20px
  );
  color: #9ca3af;
  font-size: 12px;
  font-weight: 500;
}

.ins-claim-doc-sheet-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  font-weight: 600;
  color: #6b7280;
}

/* Skeleton document row on the claim detail page — the shimmer twin of a
   `.mf-doc-row`, so the real list slots in without a layout jump. Its own class
   (rather than utilities) because the row divider is a theme decision and §10
   drops it on the last row. */
.ins-claim-doc-skel-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.ins-claim-doc-skel-row:last-child {
  border-bottom: none;
}

.ins-claim-doc-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 16px;
  margin-bottom: 16px;
}

.ins-claim-doc-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ins-claim-doc-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  background: var(--color-bg-subtle, #f8fafc);
}
.ins-claim-doc-row:hover { background: var(--hr-dash-recent-row-hover-bg, #f1f5fe); }

.ins-claim-doc-row-icon { color: var(--brand-secondary, #4374f3); flex-shrink: 0; }

.ins-claim-doc-row-info {
  display: flex;
  flex-direction: column;
  flex: 1;
  min-width: 0;
}

.ins-claim-doc-row-name {
  font-size: 13px;
  font-weight: 500;
  color: var(--color-text, #1a2233);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ins-claim-doc-row-type {
  font-size: 11px;
  color: var(--color-text-muted, #6b7280);
  margin-top: 1px;
}

.ins-claim-doc-row-actions { display: flex; gap: 4px; flex-shrink: 0; }

.ins-claim-doc-row-btn {
  padding: 5px 8px !important;
  min-width: unset !important;
  display: inline-flex;
  align-items: center;
}

.ins-claim-doc-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 40px 0;
  color: var(--ins-claim-muted-color, #6b7280);
  font-size: 13px;
}

/* Decision card — plain white like every other `.hr-dash-recent-card` (and
   like Task Manager's `.tm-detail-panel`/`.modal-card`). No whole-panel tint;
   the amount stat below gets its own scoped soft-tint cell instead, matching
   Task Manager's `.task-detail-meta-cell` pattern (tint the data, not the
   container).
   §8: it IS an in-flow card (it sits in the page's own rail, unlike the
   square-cornered right-docked overlay), so it keeps the shared card corner
   radius — this rule deliberately does NOT re-declare `border-radius`, letting
   the base `.hr-dash-recent-card`'s `var(--hr-dash-recent-card-radius)` stand.
   It used to force `border-radius: 0` here and read as a flush, sharp pane.
   Only the border is still dropped (the shadow carries the card). Composed as
   `.hr-dash-recent-card .ins-claim-decision-panel`, so this override wins by
   source order — anything re-added here silently beats the base again. */
.ins-claim-decision-panel {
  background: var(--hr-dash-recent-card-bg, #ffffff);
  border: none;
}

/* Everything below the head inside the Decision panel — matches the head's
   24px horizontal inset so amount/reference/actions/feed don't render flush
   against the card edge. */
.ins-claim-decision-body {
  padding: 20px 24px 24px;
}

/* Claimed-amount stat gets its own scoped soft-tint cell (Task Manager's
   `.task-detail-meta-cell` treatment) now that the whole panel is white —
   keeps the key figure visually grouped without re-tinting the full card. */
.ins-claim-decision-amount {
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #eef2f9);
  margin-bottom: 16px;
}

.ins-claim-decision-amount strong {
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.5px;
  color: var(--brand-primary, #0a3373);
}

.ins-claim-decision-amount span {
  font-size: 15px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}

.ins-claim-decision-amount small {
  font-size: 12px;
  font-weight: 500;
  color: var(--ins-claim-muted-color, #9ca3af);
}

/* Approved-amount cell — sits alongside the claimed-amount cell once a
   decision has been made (claim.approved_amount != null). Same shape as
   `.ins-claim-decision-amount`, tinted success-green so the two figures read
   as distinct (what was claimed vs what was approved) at a glance. */
.ins-claim-decision-amount--approved {
  background-color: color-mix(in srgb, var(--ins-claim-tone-green, var(--brand-success, #32a15e)) 12%, transparent);
}
.ins-claim-decision-amount--approved strong,
.ins-claim-decision-amount--approved span {
  color: var(--ins-claim-tone-green, var(--brand-success, #32a15e));
}

.ins-claim-decision-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

.ins-claim-decision-btn {
  justify-content: center;
}

.ins-claim-approve-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 11px 16px;
  border: none;
  border-radius: 100px;
  background-color: var(--ins-claim-approve-btn-bg, var(--brand-secondary, #4374f3));
  color: var(--ins-claim-approve-btn-fg, #ffffff);
  font-size: 14px;
  font-weight: 700;
  cursor: pointer;
  transition: opacity 0.15s;
}

.ins-claim-approve-btn:hover {
  opacity: 0.9;
}

.ins-claim-approve-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}
.ins-claim-approve-btn:disabled:hover {
  opacity: 0.5;
}

/* Activity feed */
.ins-claim-activity {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.ins-claim-activity-event {
  font-size: 12px;
  font-weight: 600;
  color: #9ca3af;
}

.ins-claim-activity-msg {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.ins-claim-activity-bubble {
  flex: 1;
  min-width: 0;
}

.ins-claim-activity-author {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 2px;
}

.ins-claim-activity-text {
  font-size: 13px;
  color: #374151;
  line-height: 1.45;
  margin: 0;
}

.ins-claim-activity-time {
  font-size: 11px;
  color: #9ca3af;
  margin: 4px 0 0;
}

/* Flat timeline row (new sidebar design) */
.ins-claim-activity-timeline-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 0;
  border-bottom: 1px solid rgba(10, 51, 115, 0.07);
  position: relative;
}
.ins-claim-activity-timeline-row:last-child {
  border-bottom: none;
}
.ins-claim-activity-line-text {
  font-size: 13px;
  color: #374151;
}
.ins-claim-activity-line-actor {
  font-size: 12px;
  color: #9ca3af;
}

/* Request info inline form */
.ins-claim-request-form {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ins-claim-request-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.ins-claim-request-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
}

.ins-claim-request-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: none;
  background: none;
  cursor: pointer;
  color: #9ca3af;
  border-radius: 6px;
  transition: background 0.12s;
}
.ins-claim-request-close:hover { background: rgba(0,0,0,0.06); color: #374151; }

/* A form textarea — same contract as `.modal-textarea`, including the shared
   resting height, so the reject/approve/request bodies stop each picking their
   own. Also off `--ins-pol-chip-border` (a chip token) for the same reason as
   `.ins-claim-ref-input` above. */
.ins-claim-request-textarea {
  width: 100%;
  box-sizing: border-box;
  border: 1px solid var(--field-border, #e5e7eb);
  border-radius: var(--field-radius, 10px);
  padding: var(--field-padding-y, 12px) var(--field-padding-x, 12px);
  min-height: var(--field-textarea-min-height, auto);
  font-size: var(--field-font-size, 13px);
  line-height: 1.5;
  color: var(--field-color, #111);
  background: var(--field-bg, #fff);
  resize: vertical;
  font-family: inherit;
  outline: none;
  transition: border-color 0.12s, box-shadow 0.12s;
}
.ins-claim-request-textarea::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}
.ins-claim-request-textarea:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  box-shadow: var(--field-focus-ring, 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent));
}

.ins-claim-request-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Pill chips reuse Task Manager's `.task-tag` visual language (soft neutral
   fill, no border, fully rounded) instead of the earlier outlined/white pill —
   the "+" prefix is rendered in the button's own text (see the component). */
.ins-claim-request-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 6px 12px;
  border: none;
  border-radius: 999px;
  background-color: #f1f5f9;
  font-size: 12px;
  font-weight: 600;
  color: #475569;
  cursor: pointer;
  transition: background-color 0.12s, color 0.12s;
}
.ins-claim-request-chip:hover {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, white);
  color: var(--brand-secondary, #4374f3);
}

/* Inline form error text — same red as the reject/danger tokens elsewhere. */
.ins-claim-request-error {
  font-size: 12px;
  color: var(--color-error, #dc2626);
  margin: 0;
}

.ins-claim-request-send {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 12px;
  border: none;
  border-radius: 999px;
  background: var(--ins-claim-request-send-bg, var(--brand-secondary, #4374f3));
  color: var(--ins-claim-request-send-fg, #fff);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.15s;
  /* On top of the parent's 10px gap, mirrors `.ins-claim-approve-inline-actions`'
     own margin-top: the primary CTA in every sibling inline form (Approve/
     Reject/Acknowledge/Mark-as-paid) gets 14px of total breathing room above
     it, not just the base 10px rhythm used between the fields themselves. */
  margin-top: 4px;
}
.ins-claim-request-send:hover { opacity: 0.88; }
.ins-claim-request-send:disabled { opacity: 0.5; cursor: not-allowed; }
.ins-claim-request-send:disabled:hover { opacity: 0.5; }

/* Cancel/Approve row inside the inline Approve form — equal-width pair
   (never let label length control width), mirrors `.ins-claim-modal-actions`
   but flex:1 both sides since it's inline (no fixed-width modal shell). */
.ins-claim-approve-inline-actions {
  display: flex;
  gap: 8px;
  margin-top: 4px;
}
.ins-claim-approve-inline-actions > button {
  flex: 1;
}

/* "Also mark as paid now" opt-in toggle inside the Approve form (claims
   usage only) — a plain text-link-style affordance, not a full button, since
   expanding it is optional and never blocks submit. */
.ins-claim-approve-mark-paid-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}
.ins-claim-approve-mark-paid-toggle:hover {
  opacity: 0.85;
}

/* Expanded "Also mark as paid now" section — payment details input + optional
   proof-of-payment upload, visually grouped so it reads as a distinct
   sub-section of the Approve form. */
.ins-claim-approve-mark-paid-section {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  border-radius: 10px;
  background: #f8fafc;
}

/* Claim detail header meta row */
.ins-claim-detail-meta-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
  font-size: 13px;
  color: var(--color-text-muted, #6b7280);
}

.ins-claim-detail-meta-name {
  font-weight: 600;
  color: var(--color-text, #111);
}

.ins-claim-detail-meta-item {
  color: var(--color-text-muted, #6b7280);
}

.ins-claim-detail-meta-item strong {
  font-weight: 600;
  color: var(--color-text, #111);
}

.ins-claim-detail-meta-company {
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  background: var(--brand-chip-bg, #eef2fb);
  padding: 2px 8px;
  border-radius: 6px;
}

.ins-claim-detail-plan-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 8px;
  background: var(--brand-chip-bg, #eef2fb);
  border-radius: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
}

/* InsurerClaimInfoCard (`<InsurerClaimInfoCard>`) — the top member/status card
   on the insurer claim and pre-approval detail pages. Sits inside the shared
   `.hr-dash-recent-card` look (bg/border/radius); this class just adds the
   card's own content padding, since it has no `.hr-dash-recent-head`/body
   split like list-page cards do. */
.ins-claim-info-body {
  padding: 28px;
}

.ins-claim-info-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.ins-claim-info-header-member {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* ── Charte 2026 identity row (`reference · status · ageing … via Heale`) ──
   The claim-detail card's first line. Wraps rather than truncating, so a long
   reference plus three pills degrade onto a second line instead of clipping. */
.ins-claim-info-topline {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* The Heale-issued reference — the id the MEMBER quotes. Tabular figures so a
   column of references reads evenly, and the same identifier tone §10 gives
   table identifiers. */
.ins-claim-info-ref {
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ins-claim-info-ref-color, var(--ins-claim-body-color, #374151));
}

/* How old the claim is. Deliberately the SAME `--ins-claim-alert-*` pair as the
   board's overdue pill (§12 action-needed: the tone at ~14% + the deeper text),
   so "this has been waiting" reads identically on both surfaces. */
.ins-claim-info-ageing {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 100px;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  background-color: var(--ins-claim-alert-bg, var(--dash-status-rejected-bg, #fee2e2));
  color: var(--ins-claim-alert-color, var(--dash-status-rejected-color, #dc2626));
}

/* Submission channel ("via Heale") — metadata, so §3's muted tone, pushed to
   the far end of the row. `margin-left: auto` survives wrapping: on a second
   line it simply sits at the start of the remaining space. */
.ins-claim-info-channel {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-left: auto;
  font-size: 11.5px;
  font-weight: 500;
  white-space: nowrap;
  color: var(--ins-claim-muted-color, #9ca3af);
}

/* `member · company · provider · treated <date>` under the heading. The caller
   drops nullish segments, so the separators here are only ever BETWEEN two real
   values — a redacted member name or a missing provider can't strand a "·". */
.ins-claim-info-metaline {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin: 6px 0 0;
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ins-claim-muted-color, #9ca3af);
}

.ins-claim-info-metaline-sep {
  color: var(--ins-claim-metaline-sep-color, var(--ins-pol-chip-border, #e5e7eb));
}

.ins-claim-info-member-name {
  font-size: 15px;
  font-weight: 600;
  color: var(--ins-claim-member-color, #18181b);
}

/* The claim/request type — the card's own heading. Its own colour token (a
   Charte theme paints it navy-700, the §5 identity tone) rather than the shared
   value colour. */
.ins-claim-info-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--ins-claim-info-title-color, var(--ins-claim-member-color, #18181b));
  margin: 14px 0 4px;
}

.ins-claim-info-subtitle {
  font-size: 13px;
  color: var(--ins-claim-muted-color, #6b7280);
  margin: 0;
}

.ins-claim-info-plan-badge {
  margin-top: 10px;
}

/* Policy/reference meta grid, replacing the card's former "·"-separated meta
   line — every item is its own tinted `.task-detail-meta-cell` (Task Manager's
   metadata-panel pattern), capped at 4 cells.

   ONE ROW, whatever the cell count: `grid-auto-flow: column` +
   `grid-auto-columns: minmax(0, 1fr)` gives N equal columns across the card
   instead of the inherited `.modal-grid` 2-column shape (which wrapped 3 or 4
   cells onto a second row). `minmax(0, 1fr)` — not plain `1fr` — is what lets a
   long value shrink and wrap inside its cell rather than widening the column.
   Scoped to this claims/pre-approval class ONLY: the shared
   `.task-detail-meta-grid` keeps its 2-column Task Manager panel shape. Narrow
   widths stack again — see the `@media (max-width: 560px)` rule below. */
.ins-claim-info-grid {
  margin-top: 20px;
  grid-template-columns: none;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
}

/* The row gap needs BOTH classes: `.task-detail-meta-grid` declares its own
   `gap` further down this file, so at equal specificity source order would beat
   a single-class rule here and the token would never be read. */
.ins-claim-info-grid.task-detail-meta-grid {
  gap: var(--ins-claim-info-grid-gap, 12px);
}

/* Below the modal-grid breakpoint the cells would squash, so the row becomes a
   single stacked column again. `grid-auto-flow`/`grid-auto-columns` must both be
   reset — the base `.modal-grid { grid-template-columns: 1fr }` mobile rule
   alone can't undo an implicit COLUMN flow. */
@media (max-width: 560px) {
  .ins-claim-info-grid {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: 1fr;
  }
}

/* Any `meta` items beyond the 4-cell grid cap render here as a plain inline
   line instead of growing the grid. */
.ins-claim-info-overflow-meta {
  margin-top: 12px;
}

.ins-claim-pending-state {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ins-claim-waiting-color, var(--brand-warning-text, #92400e));
  background: var(--ins-claim-waiting-bg, var(--brand-warning-bg, #fef3c7));
  border-radius: 8px;
  padding: 9px 12px;
}

.ins-claim-pending-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--ins-claim-tone-amber, var(--brand-warning, #eca04b));
  flex-shrink: 0;
  animation: pulse-dot 1.6s ease-in-out infinite;
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

@keyframes pulse-dot {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.4; }
}

/* Shared shimmer/skeleton placeholder background — used by <Shimmer> (src/components/ui/skeleton.tsx).
   Themes override --ui-shimmer-bg / --ui-shimmer-bg-dark; base default stays the original cool grey. */
.ui-shimmer {
  background: var(--ui-shimmer-bg, #f1f5f9);
  animation: pulse 1.5s ease-in-out infinite;
}

.ui-shimmer--dark {
  background: var(--ui-shimmer-bg-dark, #e2e8f0);
  animation: pulse 1.5s ease-in-out infinite;
}

/* ===========================================================================
   Insurer claim detail — Decision panel modifiers (2026-07). Small one-off
   tweaks that used to be inline `style={{...}}` on top of the existing
   `.ins-claim-*` classes, promoted here so the panel has zero inline styles
   left, matching the Task Manager redesign's convention below.
   =========================================================================== */

/* Generic "spans the full 2-col action grid" modifier for a decision button
   that has no sibling to pair with in its row — e.g. the awaiting-payment
   branch's lone "Mark as paid" button. Paired buttons (Reject + Acknowledge/
   Approve) are left as plain grid items so `.ins-claim-decision-actions`'
   2-col template sits them side by side instead. */
.ins-claim-decision-btn--full {
  grid-column: 1 / -1;
}

/* "Mark as paid" reuses the approve pill shape but in the success (green)
   tone — awaiting-payment is a distinct action from Approve/Acknowledge. */
.ins-claim-approve-btn--success {
  background-color: var(--ins-claim-tone-green, var(--brand-success, #32a15e));
}

/* Terminal "no further actions" line (paid/rejected) reads as de-emphasized
   next to the active action rows other statuses show here. */
.ins-claim-pending-state--muted {
  opacity: 0.6;
}

.ins-claim-request-inline {
  margin-top: 20px;
}

/* Divider between the decision actions and the Conversation & activity feed
   below them — only rendered when the feed has content. */
.ins-claim-decision-divider {
  border: none;
  border-top: 1px solid var(--ins-claim-divider-color, #f1f5f9);
  margin: 20px 0;
}

/* ===========================================================================
   ClaimDecisionPanel (`src/components/claims/claim-decision-panel.tsx`) —
   a minimal tinted callout surfacing the insurer's latest decision TEXT
   (rejection reason / approval comment / payment reference). No title, labels,
   icons or amount — just the text, tinted by outcome (red = rejected,
   green = approved/paid), with a muted timestamp. Distinct from the
   conversation & activity feed on purpose; renders nothing until there's
   decision text, so it can be mounted unconditionally.
   =========================================================================== */
.ins-claim-decision-outcome {
  border-radius: 12px;
  border: 1px solid transparent;
  padding: 14px 16px;
  margin-bottom: 16px;
}

.ins-claim-decision-outcome--red {
  background: var(--dash-status-rejected-bg, #fee2e2);
  border-color: color-mix(in srgb, var(--dash-status-rejected-color, #dc2626) 22%, white);
}

/* Matches the lighter green of the approved-amount cell
   (`.ins-claim-decision-amount--approved`) that sits right above it. */
.ins-claim-decision-outcome--green {
  background: color-mix(in srgb, var(--ins-claim-tone-green, var(--brand-success, #32a15e)) 12%, transparent);
  border-color: color-mix(in srgb, var(--ins-claim-tone-green, var(--brand-success, #32a15e)) 18%, transparent);
}

.ins-claim-decision-outcome-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ins-claim-body-color, #374151);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.ins-claim-decision-outcome-text + .ins-claim-decision-outcome-text {
  margin-top: 8px;
}

.ins-claim-decision-outcome-text--mono {
  font-family: monospace;
}

.ins-claim-decision-outcome-date {
  display: block;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 500;
  color: var(--ins-claim-muted-color, #6b7280);
  opacity: 0.85;
}

/* ===========================================================================
   ClaimReferenceCard (`src/components/claims/claim-reference-card.tsx`) —
   member-facing reference-id box + "temporary number" explanation, reused on
   the claim/pre-approval detail views and the post-submit success screens.
   White bordered card so it reads on both the light dashboard body and the
   light success-screen background. NEVER used on insurer views.
   =========================================================================== */
.ins-claim-reference-card {
  width: 100%;
  box-sizing: border-box;
  background: #ffffff;
  border: 1px solid var(--ins-pol-border-color, #d2d9e5);
  border-radius: 12px;
  padding: 16px 20px;
  margin-bottom: 16px;
  text-align: left;
}

.ins-claim-reference-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: #9ca3af;
  margin-bottom: 6px;
}

.ins-claim-reference-value {
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--brand-primary, #0a3373);
  margin-bottom: 10px;
}

/* "Pending reference" placeholder — the not-yet-assigned second half of the
   value, rendered as a small NEUTRAL grey status pill (reuses the app's neutral
   chip tone, same soft grey as `.ins-claim-status--navy`'s background) rather
   than plain text. */
.ins-claim-reference-pending {
  display: inline-block;
  vertical-align: middle;
  padding: 2px 10px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background-color: var(--ins-pol-chip-count-bg, #eef2f9);
  color: #6b7280;
}

.ins-claim-reference-note {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: #6b7280;
}

/* ===========================================================================
   InsurerClaimAdditionalInfo — "Conversation" + "Activity" sections.
   Reuses the Task Manager detail redesign's timeline (`.task-detail-timeline*`)
   for system activity rows and its comment bubble pattern
   (`.task-detail-comment*` + `.task-avatar--*`) for the two-party (insurer/
   member) message exchange, so these sections read as the SAME design
   language as the Task Manager Activity/Comments sections rather than a
   parallel bespoke feed. Only the pieces with no Task Manager equivalent (the
   card wrapper for non-bare mode, the section labels, the reply composer
   spacing, and the two message-bubble tone tints) get new classes here.
   =========================================================================== */

/* Non-bare wrapper (standalone card look) — bare mode (the only mode used by
   the claim/pre-approval detail pages today, embedded inside the Decision
   panel) renders with no className at all, i.e. no background/border/padding. */
.ins-claim-additional-info {
  background: #fff;
  border-radius: 12px;
  border: 1px solid #e5e7eb;
  padding: 20px;
}

.ins-claim-conv-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.07em;
  color: #9ca3af;
  text-transform: uppercase;
  margin: 0 0 14px;
}

/* Second section label ("Activity", below Conversation) — same look, with
   top spacing to separate it from the Conversation feed above it. */
.ins-claim-activity-label {
  margin-top: 20px;
}

.ins-claim-conv-feed {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* The reply/ask composer bodies (textarea → chips → error → send button)
   have no wrapping flex layout of their own in the JSX, so without an
   explicit `gap` here they stack with ~0 space between them (plain block
   flow) — this is the single source of truth for that vertical spacing,
   matching `.ins-claim-request-form`'s 10px rhythm used by the sibling
   Approve/Reject/Acknowledge/Mark-as-paid inline forms. */
.ins-claim-conv-composer {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-bottom: 2px;
}

.ins-claim-conv-error {
  font-size: 12px;
  color: var(--brand-error, #dc2626);
  margin: 8px 0 0;
}

/* "Awaiting response" note under an unanswered question bubble — same muted
   colour token Task Manager's own empty/waiting notes use
   (`.task-detail-empty-note`/`.task-detail-waiting-on`), kept italic per the
   original copy's emphasis. */
.ins-claim-conv-awaiting {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #9ca3af);
  font-style: italic;
  margin: 4px 0 0;
}

/* "Attach a document" affordance — sits alongside the composer at the top of
   InsurerClaimAdditionalInfo, always available (mirrors the always-visible
   attach affordance on the member-side `ClaimConversationCard`). Collapsed
   state is a plain outlined button; picking a file swaps it for a confirm
   panel (file name + doc-type select + Cancel/Upload). */
.ins-claim-attach-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  border-radius: 8px;
  padding: 7px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: #374151;
  cursor: pointer;
  transition: background 0.12s;
}
.ins-claim-attach-toggle:hover {
  background: #f8fafc;
}

.ins-claim-attach-panel {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  background: #f8fafc;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  border-radius: 8px;
  padding: 10px 12px;
}

.ins-claim-attach-name {
  font-size: 12.5px;
  font-weight: 600;
  color: #374151;
  max-width: 160px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ins-claim-attach-select {
  font-size: 12.5px;
  color: #374151;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  border-radius: 6px;
  padding: 5px 8px;
  background: #fff;
  cursor: pointer;
}

.ins-claim-attach-actions {
  display: flex;
  gap: 6px;
  margin-left: auto;
}

.ins-claim-attach-btn {
  background: transparent;
  border: 1px solid var(--ins-pol-chip-border, #e5e7eb);
  border-radius: 8px;
  padding: 6px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: #374151;
  cursor: pointer;
}
.ins-claim-attach-btn:disabled {
  color: #9ca3af;
  cursor: not-allowed;
}

.ins-claim-attach-btn--primary {
  background: var(--brand-accent, #ff6733);
  border: none;
  color: #fff;
}
.ins-claim-attach-btn--primary:disabled {
  background: #e5e7eb;
  color: #9ca3af;
}

.ins-claim-attach-error {
  font-size: 12px;
  color: var(--brand-error, #dc2626);
  margin: 8px 0 0;
}

.ins-claim-attach-success {
  font-size: 12px;
  color: var(--brand-success, #32a15e);
  margin: 8px 0 0;
}

/* ===========================================================================
   Task Manager (insurer) — reuses .ins-pol-seg filter pills + .hr-dash-recent-table
   =========================================================================== */

/* Status badge with leading icon (reuses .dash-status base, normal case) */
.task-status {
  gap: 5px;
  text-transform: none;
  letter-spacing: 0;
  font-size: 12px;
  font-weight: 600;
}

.task-status--new {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, white);
  color: var(--brand-secondary, #4374f3);
}

.task-status--pending {
  background-color: var(--dash-status-pending-bg, #fef3c7);
  color: var(--dash-status-pending-color, #d97706);
}

.task-status--done {
  background-color: var(--dash-status-approved-bg, #d1fae5);
  color: var(--dash-status-approved-color, #059669);
}

/* Cancelled reads as neutral/inactive (ink-400), not an error — a cancelled
   thread isn't a failure state, so it shouldn't share the red "rejected"
   tone. Same `--dash-status-inactive-*` token `.dash-status--inactive` uses. */
.task-status--cancelled {
  background-color: var(--dash-status-inactive-bg, #eef2f6);
  color: var(--dash-status-inactive-color, #6b7280);
}

/* "Action needed" badge — the viewer's own assignee row on this task is
   currently `can_act`. Reuses the brand accent (coral) token, the same
   "attention" colour already used by `.dash-topbar-badge`/`.ins-claim-status--coral`,
   rather than inventing a new colour for the same meaning. */
.task-status--action {
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 14%, white);
  color: var(--brand-accent, #ff6733);
}

/* Task Manager list rows where the viewer needs to act — a subtle accent
   tint on the row (same "background-only" convention as
   `.hr-dash-recent-tr--dependent`/`--expanded` above) plus an inset left
   accent bar, so the row is scannable at a glance without adding another
   text column. */
.hr-dash-recent-tr--action-needed {
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 5%, white);
  box-shadow: inset 3px 0 0 var(--brand-accent, #ff6733);
}
.hr-dash-recent-tr--action-needed:hover {
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 9%, white);
}

/* Compact 22px variant of `.task-avatar` (28px) for dense table rows —
   compound selector so it reliably overrides the base size regardless of
   each rule's position in this file. */
.task-avatar.task-avatar--sm {
  width: 22px;
  height: 22px;
  font-size: 10.5px;
}

/* ===========================================================================
   <TaskDetailModal> — redesign (2026-07). Reuses the app-wide `.modal-*`
   dialog chrome (`.modal-section`/`.modal-section-head`/`.modal-section-label`
   for Action needed/Activity/Comments, `.modal-grid`/`.modal-summary-label`
   for the 2x2 metadata panel) rather than the old flat `.hr-dash-recent-card`
   stack — only the pieces with no existing equivalent (tag chips, role/entity
   avatars, the activity timeline, comment bubbles) get new classes here.
   =========================================================================== */

.task-detail-head-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
}

.task-detail-title {
  margin-top: 2px;
}

/* Scoped override for `<TaskThreadStatusBadge>` where it renders inside the
   shared detail header (`<TaskDetailContent>`, both the modal and the
   insurer split-pane panel). The shared pill itself (`.task-status--<tone>`)
   already carries the correct soft-tinted background per tone — open (`new`)
   = blue, completed (`done`) = green, cancelled = red, the SAME tone colours
   `.tm-row-status--<tone>` uses for its own soft-tinted pill on the list rows
   (see that block below), so the two surfaces agree. This override does NOT
   touch that background/colour — it only swaps the leading icon glyph
   (Plus/Check/X) for a plain colored dot, since the reference design's pill
   has a dot, never an icon glyph, in this header context. */
.task-detail-status .task-status {
  gap: 6px;
}

.task-detail-status .task-status svg {
  display: none;
}

.task-detail-status .task-status::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 999px;
  flex-shrink: 0;
  background-color: currentColor;
}

.task-detail-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Section heading labels ("Action needed" / "Activity" / "Comments · N") use
   the shared `.modal-section-label` class, which defaults to navy
   (`--brand-primary`) for other modals (Create Invoice, Share modal) that
   keep it — but every micro-label in this reference design (section
   headings AND the metadata grid's "Related to"/"Created"/"Assigned to"/"Due"
   labels, `.modal-summary-label`) reads as the SAME muted ink-400 uppercase
   style, with no colour distinction between the two. Scoped to the detail
   panel only (both classes), so the shared classes stay untouched for every
   other modal — a dedicated token rather than retuning the widely-shared
   `--ins-pol-subtext-color` (many other, not-yet-retinted surfaces consume
   that one). */
.tm-detail-panel .modal-section-label,
.tm-detail-panel .modal-summary-label {
  color: var(--tm-detail-label-color, #9ca3af);
  letter-spacing: 0.06em;
}

/* Neutral module tag (`task.feature`, e.g. "Census") — also reused by the
   insurer Task Manager list page's Category/Waiting-on chips, so the pill
   language matches between the list and the detail modal. */
.task-tag {
  display: inline-flex;
  align-items: center;
  padding: 3px 12px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background-color: var(--task-tag-bg, #f1f5f9);
  color: var(--task-tag-color, #475569);
}

/* Warm category tag (`metadata.category`, e.g. "Certificate") */
.task-tag--accent {
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 14%, white);
  color: var(--brand-accent, #ff6733);
}

/* 2x2 metadata panel — Related to / Created / Assignees / Due, each its own
   soft rounded cell. Sits on top of the existing `.modal-grid` (2-col grid,
   already collapses to 1 column at `max-width: 560px`). */
.task-detail-meta-grid {
  gap: 12px;
}

/* §13's 2-equal-column metadata grid holds a VARIABLE number of cells (the
   member-identification cells render only when their fact exists), so an odd
   count would leave the final cell half-width beside a hole. It spans both
   columns instead — this became live when the Company cell was removed (company
   is a column + a filter on the list now, so repeating it here was redundant),
   which flipped the DOB-carrying panel from 6 cells to 5. */
.task-detail-meta-grid > .task-detail-meta-cell:last-child:nth-child(odd) {
  grid-column: 1 / -1;
}

.task-detail-meta-cell {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #eef2f9);
}

.task-detail-meta-value {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--modal-total-color, #374151);
}

.task-detail-meta-value--muted {
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.task-detail-related-row {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.task-detail-related-name {
  font-size: 13.5px;
  font-weight: 700;
  color: #18181b;
  overflow-wrap: anywhere;
}

.task-detail-assignee-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/* One assignee = its role chip + that participant's own `actor_status` badge
   (`<DocRequestStatusBadge>`), kept together as one wrapping unit so the badge
   never wraps away from the role it belongs to. */
.task-detail-assignee-entry {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.task-detail-assignee-chip {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 4px 10px;
  border-radius: 999px;
  background-color: #f8fafc;
  border: 1px solid #e5e7eb;
  font-size: 12px;
  font-weight: 600;
  color: #374151;
}

/* Role/entity avatar — reused for the "Related to" real-entity chip (navy)
   and the role-bucket comment/activity avatars (hr/insurer/member/system).
   Comment authors have NO per-user identity in this data model
   (`TaskActionComment.created_by_type` is a role bucket only), so these are
   never person initials — same precedent as the Contact Center's role-only
   sender labels. */
.task-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 700;
  color: #ffffff;
}

.task-avatar--related { background-color: var(--brand-primary, #0a3373); }
.task-avatar--hr { background-color: var(--brand-accent, #ff6733); }
.task-avatar--insurer { background-color: var(--brand-success, #32a15e); }
.task-avatar--member { background-color: var(--brand-secondary, #4374f3); }
.task-avatar--system { background-color: #94a3b8; }

/* Action needed / Activity / Comments section bodies */
.task-detail-action-body {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* "Action needed" is the panel's ONE call-to-action block, so it gets a soft
   accent wash + an accent-toned section label to lift it away from the neutral
   Activity/Comments sections below. Rounded like every other card-ish surface in
   the panel (the only square-cornered element in this feature is the docked
   overlay panel itself). The doubled `.modal-section.task-detail-action-section`
   selector is needed to out-specify `.modal-section + .modal-section`, whose top
   border/padding would otherwise cut across this tinted block. */
.modal-section.task-detail-action-section {
  border-radius: 12px;
  border-top: none;
  padding: 14px 16px;
  background-color: var(--tm-action-section-bg, color-mix(in srgb, var(--brand-accent, #ff6733) 7%, white));
}

.tm-detail-panel .task-detail-action-section .modal-section-label {
  color: var(--tm-action-label-color, var(--brand-accent, #ff6733));
}

/* ── Shared reviewer accept/reject block (`<TaskReviewAction>`, Charte §6) ──
   One instruction line, one subject row, two equal-width decision buttons.
   Used by every review task (identity document, insurer document, policy
   addition, policy deletion) so a styling change lands once. */
.tm-review {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.tm-review-instruction {
  margin: 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--modal-total-color, #374151);
}

/* Subject row — a white card inside the tinted section (still rounded; only the
   section itself is square). */
.tm-review-doc {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  border: 1px solid var(--tu-doc-row-border, #e5e7eb);
  border-radius: 12px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
}

.tm-review-doc-icon {
  display: flex;
  flex-shrink: 0;
  color: var(--tu-muted-color, #6b7280);
}

.tm-review-doc-info {
  flex: 1;
  min-width: 0;
}

/* Filenames are arbitrarily long ("Capture d'écran 2026-07-13 à 10.20.02.png")
   and the panel is only ~430px wide, so the name is clipped to ONE line with an
   ellipsis — never wrapped. Wrapping is what previously pushed this row to two
   lines and made the leading file icon read as vertically off-centre against
   it. The ellipsis needs `.tm-review-doc-info`'s `min-width: 0` (a flex item
   otherwise refuses to shrink below its content) — keep both. The full name
   stays reachable via the element's `title`. */
.tm-review-doc-name {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Unlike the name above, the subtitle WRAPS rather than ellipsising: it carries
   qualifying metadata (a billing period plus an amount — "01 Aug 2026 → 31 Aug
   2026 · AED 1,054,250"), which at the overlay's ~250px of info width is longer
   than one line and is worth more read in full over two lines than truncated.
   `overflow-wrap: anywhere` covers the pathological single long token so it
   breaks instead of widening the row past the panel. */
.tm-review-doc-sub {
  margin: 2px 0 0;
  font-size: 12px;
  line-height: 1.45;
  overflow-wrap: anywhere;
  color: var(--tu-muted-color, #9ca3af);
}

.tm-review-doc-view {
  flex-shrink: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  text-decoration: none;
}

.tm-review-doc-view:hover {
  text-decoration: underline;
}

/* Compact §6 pills, side by side and EQUAL width. `flex: 1 1 0` makes both
   share the row instead of each sizing to its own label (which is what made
   "Reject" visibly narrower than "✓ Accept document"), and the 14px side padding
   — down from 20px — is what lets the longest of those labels (162px at
   13px/600 Plus Jakarta Sans) fit inside its half of the row: at the overlay's
   430px width the row is 350px (430 − 2×24 panel padding − 2×16 section
   padding), so a half is 170px.

   `white-space: nowrap` keeps a label from ever wrapping INSIDE a pill (which
   previously doubled the button height). The pair that genuinely cannot fit two
   halves — "Reject cancellation" / "✓ Confirm cancellation", 171px + 182px —
   makes the ROW wrap instead, and each pill then takes a full-width line of its
   own: still equal, still unclipped. The mobile block below stacks them the
   same way unconditionally, because a half-row is too narrow for any of these
   labels at phone widths.

   The transparent 1px border on the BASE class (the reject variant only recolours
   it) is load-bearing for equality: flex hands its free space to each item's
   CONTENT box, so a pill with a border ends up that border wider than a
   borderless sibling — which is how "Reject" measured 171px against "Accept
   document"'s 169px even with both set to `flex: 1 1 0`. */
.tm-review-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.tm-review-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  flex: 1 1 0;
  min-height: 40px;
  padding: 10px 12px;
  border: 1px solid transparent;
  border-radius: var(--hr-dash-recent-btn-radius, 100px);
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
}

/* §6 Approval — green-500 fill, white label. */
.tm-review-btn--approve {
  background-color: var(--dash-status-approved-color, #32a15e);
  color: #ffffff;
}

/* §6 Rejection — white fill, rust-600 label AND border. */
.tm-review-btn--reject {
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  color: var(--dash-status-rejected-color, #dc2626);
  border-color: var(--dash-status-rejected-color, #dc2626);
}

.tm-review-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.tm-review-reveal {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* The reject-reason / extra-value controls inside <TaskReviewAction>. Phase 1
   reconciled only their BORDER token (off the §1-retired `#d2d9e5`, onto
   `--field-hairline`); Phase 2 moved fill and value colour off the CARD's tokens
   (`--hr-dash-recent-card-bg` / `-name-color`) onto `--field-bg` / `--field-color`,
   which stands.

   THE EDGE IS THE EXCEPTION, AND ON PURPOSE. Phase 2 also put the border on
   `--field-border` ("this control IS a field, so it owns its edge"), which holds
   only while a field sits on WHITE: on a brand whose field is borderless the fill
   is the boundary. These controls never sit on white — they live inside the
   tinted "Action needed" block (`.task-detail-action-section`,
   `--tm-action-section-bg` = the accent at ~8% on white), and on Heale that pale
   rose and the cream field fill are the same lightness. A borderless control there
   has NO boundary at all and renders as bare placeholder text on the panel — the
   reported "doesn't look like an input". So this family reads `--field-hairline`
   (the token for 1px chrome that must stay visible on a brand whose field edge is
   transparent) and keeps the contract's fill/text/focus/placeholder. An unmapped
   brand is unaffected: both tokens fall back to `#e5e7eb`. */
.tm-review-textarea,
.tm-review-input {
  width: 100%;
  border-radius: var(--field-radius, 10px);
  border: 1px solid var(--field-hairline, #e5e7eb);
  background-color: var(--field-bg, #ffffff);
  padding: var(--field-padding-y, 9px) var(--field-padding-x, 11px);
  font-size: var(--field-font-size, 13px);
  font-family: inherit;
  color: var(--field-color, #18181b);
}

/* One height for every control on the reveal's form, text and native date alike,
   so the three fields read as one form. Left to content height they disagree: a
   date input's UA-rendered value box is taller than a bare text line at the same
   padding. Fixed height + zero vertical padding is the contract's own shape for a
   single-line control (see `.ins-pol-input`); the 36px fallback is what this
   family measured before (9px padding + a 13px line + 2px border), so only a brand
   that maps `--field-height` moves. */
.tm-review-input {
  height: var(--field-height, 36px);
  padding: 0 var(--field-padding-x, 11px);
}

/* A native date input lays its value out itself and sits top-aligned in a
   fixed-height box on WebKit, half a line above the text input beside it; the
   flex centring is the standard fix. `appearance: none` is deliberately NOT set
   — it strips Safari's own picker glyph, and the calendar affordance is the one
   piece of UA chrome worth keeping. `min-width: 0` lets the pair shrink inside
   the ~430px panel's two-column row instead of overflowing it. The glyph is
   dimmed rather than recoloured so it follows whatever tone the brand gives the
   value. */
.tm-review-input[type='date'] {
  display: flex;
  align-items: center;
  min-width: 0;
}

.tm-review-input[type='date']::-webkit-calendar-picker-indicator {
  opacity: 0.55;
  cursor: pointer;
}

.tm-review-input[type='date']::-webkit-calendar-picker-indicator:hover {
  opacity: 1;
}

.tm-review-textarea {
  resize: vertical;
  min-height: var(--field-textarea-min-height, auto);
}

.tm-review-textarea::placeholder,
.tm-review-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.tm-review-textarea:focus,
.tm-review-input:focus {
  outline: none;
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

/* Multi-input accept reveals (the insurer's activation: insurance ID +
   confirmed start/end dates). Labelled fields inside the reveal rather than a
   dialog — the panel IS the surface (design doc 2026-07-31 A6). Labels follow
   §9/§3: 11px bold uppercase, always the label tone, never semantic. */
.tm-review-fields {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* `min-width: 0` is load-bearing: as a grid item a native date input's
   intrinsic width would otherwise push the pair past the ~430px panel. */
.tm-review-field {
  display: flex;
  flex-direction: column;
  gap: var(--field-label-gap, 7px);
  min-width: 0;
}

/* Wraps because a label can carry the "From card" provenance badge beside it
   and the reveal's date pair is only ~170px per column at the overlay's 430px:
   the badge takes its own line there rather than pushing the label out. */
.tm-review-field-label {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  font-size: var(--field-label-size, 11px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--field-label-color, #9ca3af);
}

/* Provenance: this value was READ OFF THE CARD, not typed. Card layouts vary
   and a wrong insurance id is a claim that fails at a clinic, so an OCR'd value
   is marked rather than silently indistinguishable from a confirmed one. Blue
   (the same tone as the drop CTA) because it is informational, not a status. */
.tm-review-field-src {
  padding: 2px 7px;
  border-radius: 999px;
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, transparent);
  color: var(--brand-secondary, #4374f3);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
}

.tm-review-field-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* Read-only context line (what HR asked for) — metadata tone, so a divergence
   from the dates being granted is visible without competing with them. */
.tm-review-field-note {
  margin: 0;
  font-size: 12px;
  color: var(--tu-muted-color, #6b7280);
}

.tm-review-field-note strong {
  font-weight: 700;
  color: var(--field-color, #18181b);
}

/* ── Optional billing-card drop inside the activation reveal (<ActivationCardDrop>) ──
   Charte §16's dropzone shape (cream fill, solid blue-400 edge, radius 12,
   tinted icon), but COMPACT: `.tu-dropzone`'s 22px padding + 38px icon circle
   is a card-sized control, and this one sits above three fields inside a 430px
   overlay that already scrolls. Same tokens, so a §16 retint reaches both. */
.tm-card-drop-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.tm-card-drop {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 2px;
  padding: 12px 14px;
  border: 1.4px solid var(--tu-dropzone-border, var(--hr-dash-recent-card-border, #e5e7eb));
  border-radius: 12px;
  background-color: var(--tu-dropzone-bg, transparent);
  cursor: pointer;
  transition: border-color .12s ease, background-color .12s ease;
}

.tm-card-drop:hover,
.tm-card-drop--active {
  border-color: var(--brand-secondary, #4374f3);
  background: color-mix(in srgb, var(--brand-secondary, #4374f3) 5%, white);
}

.tm-card-drop--disabled {
  cursor: not-allowed;
  opacity: .6;
}

.tm-card-drop-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: var(--tu-dropzone-icon-bg, transparent);
  color: var(--brand-secondary, #4374f3);
  margin-bottom: 2px;
}

.tm-card-drop-text { margin: 0; font-size: 12.5px; }
.tm-card-drop-cta { font-weight: 700; color: var(--brand-secondary, #4374f3); }
.tm-card-drop-muted { color: var(--tu-muted-color, #7d93b3); }
.tm-card-drop-hint { margin: 1px 0 0; font-size: 11px; color: var(--tu-muted-color, #94a3b8); }

/* The chosen file, replacing the drop target — same white-card treatment as
   `.tm-review-doc` so the reveal reads as one form. */
.tm-card-file {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 11px;
  border: 1px solid var(--tu-doc-row-border, #e5e7eb);
  border-radius: 12px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
}

.tm-card-file-icon {
  display: flex;
  flex-shrink: 0;
  color: var(--tu-muted-color, #6b7280);
}

/* One ellipsised line — real card filenames are long and the panel is ~430px;
   `min-width: 0` is what lets a flex item shrink below its content. */
.tm-card-file-name {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tm-card-file-state {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--tu-muted-color, #6b7280);
}

.tm-card-file-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 26px;
  height: 26px;
  padding: 0;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--tu-muted-color, #6b7280);
  cursor: pointer;
}

.tm-card-file-remove:hover {
  background-color: var(--field-hover-bg, #f1f5f9);
}

/* §2 tinted note surfaces. `--muted` is deliberately NOT semantic: a card the
   model could not read is a degradation to manual entry, not an alarm. */
.tm-card-note {
  display: flex;
  align-items: flex-start;
  gap: 7px;
  margin: 0;
  padding: 9px 11px;
  border-radius: 10px;
  font-size: 12px;
  line-height: 1.5;
}

.tm-card-note-icon {
  display: flex;
  flex-shrink: 0;
  margin-top: 1px;
}

.tm-card-note--muted {
  background-color: var(--modal-total-bg, #eef2f9);
  color: var(--tu-muted-color, #6b7280);
}

.tm-card-note--ok {
  background-color: var(--dash-status-approved-bg, #d1fae5);
  color: var(--dash-status-approved-color, #32a15e);
}

/* The wrong-member warning. Rust, not the gold of a "waiting" state: the card
   naming someone else is a data-integrity problem the reader must resolve
   before activating — but it still only warns, so nothing here is a button. */
.tm-card-note--warn {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  color: var(--dash-status-rejected-color, #dc2626);
}

.tm-card-note-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

.tm-card-note-title {
  margin: 0;
  font-weight: 700;
}

.tm-card-note-list {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.tm-card-note-row {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.tm-card-note-field {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  opacity: .8;
}

/* Both sides of the disagreement, each on its own line and WRAPPING — a name is
   exactly the value a reader has to compare character by character, so it may
   never be clipped (§21). */
.tm-card-note-values {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.tm-card-note-value {
  overflow-wrap: anywhere;
}

/* Defensive states (missing enrollment reference / unresolvable document) —
   replaces the former inline-styled paragraphs in each reviewer. */
.tm-review-loading {
  margin: 0;
  font-size: 13px;
  color: var(--tu-muted-color, #6b7280);
}

.tm-review-error {
  margin: 0;
  font-size: 13px;
  color: var(--dash-status-rejected-color, #dc2626);
}

/* ── Outstanding-items block (`<CompleteMemberInformationTaskAction>`) ──
   The "Pending information" enrollment thread's HR-side content: one group per
   KIND of blocker (missing details / missing documents), each a §3 section
   label over a plain list, then the deep links into the matching editor tab.
   Reuses `.tm-review`'s shell + instruction line so it sits in the panel's
   Action-needed section like every other feature control. */
.tm-outstanding-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* §3 section label — 9.5px Bold uppercase, always ink-400, never semantically
   coloured; the leading icon inherits that colour (§18). */
.tm-outstanding-label {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.tm-outstanding-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Values WRAP, never clip (§21) — an outstanding field label can be long and
   the overlay is only ~430px wide. */
.tm-outstanding-item {
  position: relative;
  padding-left: 14px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--modal-total-color, #374151);
  overflow-wrap: anywhere;
}

.tm-outstanding-item::before {
  content: '';
  position: absolute;
  left: 0;
  top: 7px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: var(--brand-accent, #ff6733);
}

/* Same equal-width rule as `.tm-review-actions`: the pair shares the row and
   wraps to full-width lines when the labels can't both fit. */
.tm-outstanding-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.tm-outstanding-link {
  flex: 1 1 0;
  justify-content: center;
  min-width: 140px;
  min-height: 44px;
  text-decoration: none;
}

.task-detail-action-item {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.task-detail-empty-note {
  margin: 0;
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

/* Generic "waiting on: <actor>" fallback line — used both by the modal's own
   no-match fallback and by `<WaitingOnLine>` (`task-action-components.tsx`),
   which some registered controls (e.g. `InsurerUploadDocumentTaskAction`)
   also render as part of their own layout. */
.task-detail-waiting-on {
  margin: 0;
  font-size: 13px;
  color: #6b7280;
}

.task-detail-waiting-on strong {
  color: #374151;
}

/* Deep-link CTA — the prominent primary "Open in …" button. For claim-related
   tasks it is ALWAYS shown as a standalone section (`.task-detail-open-section`,
   regardless of any inline control); for other features it stands in for the
   generic "waiting on" line when the viewer must act but the real controls
   live on the record page. Reuses the app's coral accent + a 44px tap target. */
.task-detail-open-section {
  display: flex;
}

.task-detail-open-cta {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  align-self: flex-start;
  padding: 11px 22px;
  min-height: 44px;
  border-radius: 8px;
  background-color: var(--brand-accent, #ff6733);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  transition: opacity 0.12s;
}

.task-detail-open-cta:hover {
  opacity: 0.92;
}

/* Activity — connected timeline (dot + line marker, reusing the coverage
   spec's `.cov-dot--*` tone colours per `COMMENT_TONE`) + bold title/muted
   subline content. */
.task-detail-timeline {
  display: flex;
  flex-direction: column;
}

.task-detail-timeline-item {
  display: flex;
  gap: 12px;
}

.task-detail-timeline-marker {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 9px;
  flex-shrink: 0;
  padding-top: 4px;
}

/* Every row in THIS timeline is an already-logged fact ("Task created",
   "Assigned to Insurer", a comment/approval/rejection…) — there is no
   not-yet-happened placeholder step rendered here, so every entry is
   "completed" per the Charte §14 dot convention and always gets the SOLID,
   fully tone-filled dot (the ring/hollow-white-center treatment is reserved
   for an in-progress step in a step-by-step progress indicator elsewhere,
   which this list is not). `border: none` is explicit, not just relying on
   `.cov-dot`/`.cov-dot--*` never declaring one, so this can't silently
   regress into a ring if a future edit adds a border to the shared class. */
.task-detail-timeline-dot {
  width: 9px;
  height: 9px;
  border: none;
}

.task-detail-timeline-line {
  flex: 1;
  width: 1px;
  min-height: 20px;
  margin-top: 4px;
  background-color: var(--hr-dash-recent-head-border, #e5e7eb);
}

.task-detail-timeline-content {
  flex: 1;
  min-width: 0;
  padding-bottom: 16px;
}

.task-detail-timeline-item:last-child .task-detail-timeline-content {
  padding-bottom: 0;
}

.task-detail-timeline-title {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: #18181b;
}

.task-detail-timeline-sub {
  margin: 2px 0 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

/* Comments — chat-style bubbles: avatar sits as a flex sibling of a content
   column; the rounded bubble itself contains BOTH the role/timestamp line
   AND the message body stacked inside it (not a header outside the bubble).
   Shared by Task Manager's own single-party comment thread
   (`task-detail-content.tsx`'s `<CommentBubble>`) AND the claims conversation
   feed (`insurer-claim-additional-info.tsx`) — both reuse the same base
   classes so the layout can't drift between the two surfaces. */
.task-detail-comments {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.task-detail-comment {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.task-detail-comment-content {
  flex: 1;
  min-width: 0;
}

.task-detail-comment-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 8px;
}

.task-detail-comment-role {
  font-size: 12.5px;
  font-weight: 700;
  color: #374151;
}

.task-detail-comment-time {
  margin-left: auto;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #9ca3af);
  white-space: nowrap;
}

.task-detail-comment-bubble {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 16px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #eef2f9);
}

/* Employee-side `ClaimConversationCard` bubble fill — mirrors
   `.task-detail-comment-bubble`'s background so both conversation surfaces
   (employee + insurer) render every message with the same single tint,
   regardless of sender. */
.claim-conv-bubble-fill {
  background-color: var(--modal-total-bg, #eef2f9);
}

.task-detail-comment-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.6;
  color: #374151;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* Loading-state placeholders */
.task-detail-loading-head {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.task-detail-loading-block {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* Not-found state */
.task-detail-notfound {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 4px;
  padding: 48px 24px;
}

.task-detail-notfound-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: 4px;
  border-radius: 16px;
  color: #94a3b8;
  background-color: #f1f5f9;
}

.task-detail-notfound-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: #374151;
}

.task-detail-notfound-text {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

/* THE success state for task actions (Charte §16) — `<TaskSuccessBanner>`,
   shared by the detail panel's completed/handed-off banner and the document
   dropzone's "all required documents received" receipt, so both green surfaces
   are one component family driven by ONE green token
   (`--dash-status-approved-color`, already green-500 in the Heale theme; the
   unmapped `--brand-success` is deliberately not used). Rounded 12px, no border
   — the tint alone carries it — with a thin `Check` tick, a bold ink-900 title
   and an ink-400 body. Replaced `.task-detail-provided-banner*`, which was the
   same thing scoped to one call site (and outlined + all-green text). */
.task-success-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 14px 16px;
  border: none;
  border-radius: 12px;
  background-color: var(--task-success-bg, color-mix(in srgb, var(--dash-status-approved-color, #32a15e) 9%, white));
}

.task-success-banner-icon {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--dash-status-approved-color, #32a15e);
}

.task-success-banner-body {
  min-width: 0;
}

.task-success-banner-title {
  margin: 0 0 2px;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #166534);
}

.task-success-banner-text {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--tu-muted-color, #15803d);
}

/* ===========================================================================
   Task Manager — full-width list + right-docked detail overlay
   Shared by the HR / insurer / employee routes (`<TaskManagerView>`). The task
   list is ONE full-width card; `<TaskDetailPanel>` docks OVER it as a
   viewport-fixed right-edge overlay (Charte §13 "Overlay variant"), so the
   list never resizes when a task is opened. This replaced the earlier 60/40
   split-pane (`.tm-detail-card` + `.dash-body--split`) — `.dash-body--split`
   itself still exists further down this file for the Contact Center's own
   rail/detail split, which is genuinely side-by-side.
   =========================================================================== */

/* `.tm-filterbar-row` (category filter pills — Enrollment/Policy Management/…)
   and `.tm-stats-row` (the 5 metrics: Overdue/New/Action needed/Done/All) are
   both plain grouping wrappers with NO rules of their own — deliberately. There
   is intentionally no `.tm-filterbar-row {}` / `.tm-stats-row {}` block below;
   the single descendant rule further down only re-aligns the metric values
   under a two-line label, and gives the wrappers themselves no layout.

   They live INSIDE `.dash-body.dash-body--tm`, above `.tm-list-card`, so the
   whole Task Manager (pills → metrics → list) scrolls as ONE region with a
   single scrollbar and only `<DashTopbar>` pinned. That is an explicit product
   decision: nothing here is meant to stay fixed while the list scrolls.

   Being direct children of `.dash-body` they already get everything they need
   from it — the page's horizontal inset (`--dash-body-padding`, 28px → 20px →
   16px across the breakpoints), the 20px/14px vertical `gap` between blocks,
   and `flex-shrink: 0` via `.dash-body > *`. They previously carried their own
   `padding: 20px 28px 0` because they sat OUTSIDE the body; keeping that here
   would double-inset them and misalign them against `.tm-list-card`'s edges,
   and a `padding-top` on top of the body's `gap` would double the rhythm.
   Their inner markup is shared as-is (`.ins-pol-filterbar`/`.ins-pol-segs`/
   `.ins-pol-seg`, `.dash-stats`) — identical to Policies/Companies/Census. */

/* "Action needed" is the longest of the five labels and wraps to two lines in
   the narrow-desktop band (~1024–1090px), which would otherwise push that one
   card's number a line below its neighbours'. The cards already stretch to a
   common height, so pinning the value to the bottom keeps all five numbers on
   one baseline whether the label takes one line or two. Scoped to this row:
   these cards carry no sublabel, so the value is genuinely the last element. */
.tm-stats-row .dash-stat-value {
  margin-top: auto;
}

/* Row layout: two panes side by side, each scrolling independently — same
   "the scrollable child owns min-height: 0" contract as `.dash-body--kanban`
   above, just a row instead of a column. NOT used by the Task Manager any more
   (its detail pane became the overlay below); the CONTACT CENTER's
   `.cc-rail`/`.cc-detail` split is the sole remaining consumer, including the
   `flex-direction: column` mobile override in the media block further down —
   don't remove either. */
.dash-body--split {
  flex-direction: row;
  gap: 20px;
  overflow: hidden;
}

/* `.dash-body--tm` intentionally declares NOTHING — it is a marker class only.
   The Task Manager must NOT contain a nested scroll container: the body keeps
   `.dash-body`'s own `overflow-y: auto`, exactly like every other dashboard
   list page, and holds the WHOLE screen below the topbar (filter pills →
   metrics → list card), so everything scrolls together under one scrollbar and
   the card grows to its natural height. An earlier version set
   `overflow-y: hidden` here (with
   `.tm-list-card`/`.tm-list-rows` filling it and scrolling internally) and was
   deliberately reverted — do not reintroduce it, and do not delete the class
   from the markup (`<TaskManagerView>` + the loading skeleton keep it as a
   documented hook, asserted by their tests). */

/* The one, always FULL-WIDTH list card. It keeps the standard
   `.hr-dash-recent-card` chrome (white fill, 16px radius, soft shadow) like
   every other list page — it is no longer half of a flush split. It is sized
   by its CONTENT, with no `flex: 1`/`min-height: 0`: the card must not stretch
   to the body's height, or its rows would need an internal scroll again.
   Deliberately no `max-width`/`flex` ratio either: the detail panel overlays
   this card instead of sitting beside it, so nothing here ever reflows when a
   task is selected. The column flex only stacks the filter/table blocks. */
.tm-list-card {
  display: flex;
  flex-direction: column;
}

/* The list card's filter row — the pill search field (`.ins-pol-search`), the
   shared `Filters (N)` trigger (`.filter-trigger-pill`, holding the Company and
   Policy `<FilterGroup>`s) and the "Clear filters" reset. The search field takes
   the remaining width via its own `flex: 1`, which needs this row's flex
   context to resolve against. Wraps on narrow viewports so the trigger never
   pushes the field out of the card. */
.tm-list-filter-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  flex-shrink: 0;
}

.tm-list-filter-row .ins-pol-search {
  flex: 1;
  min-width: 0;
}

/* The filtered-REFETCH affordance. Filtering the Task Manager is server-side,
   so changing a filter (or typing, once the 300ms debounce elapses) costs a
   round trip during which the rows on screen are stale.

   It is deliberately NOT the §17 loading state: swapping the table to skeleton
   rows on every keystroke flickers hard and throws away the context the user is
   reading. Instead the rows STAY, dimmed by `.tm-table-wrap--busy` below, and
   this small spinner in the filter row says why. §17's skeleton is still what
   the FIRST paint renders — the two states are distinct. */
.tm-filter-busy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.tm-filter-busy-icon {
  flex-shrink: 0;
  animation: spin 0.8s linear infinite;
}

/* Stale rows: legible but visibly not current, and inert so a click can't open
   a row that is about to be replaced. Opacity only — no colour swap, so nothing
   here needs a token and every theme keeps its own row chrome. */
.tm-table-wrap--busy {
  opacity: 0.45;
  pointer-events: none;
  transition: opacity 120ms ease;
}

/* The rows stack, full height, NO internal scroll — the page body
   (`.dash-body`) is the only scroll container on this screen. Adding
   `overflow-y: auto` (+ the `flex: 1; min-height: 0` that a fixed-height card
   needs) back here is what produced the second, nested scrollbar. */
.tm-list-rows {
  display: flex;
  flex-direction: column;
}

/* The two §17 empty states are the shared `<ListState>` (`.tm-detail-empty*`),
   not a one-line paragraph: once filtering moved server-side the list has to
   distinguish "nothing matched these filters" (with a way out) from "there is
   nothing here at all", which a single muted sentence cannot. The former
   `.tm-list-empty` rule was deleted with its only call site. */

/* ---------------------------------------------------------------------------
   The Task Manager TABLE (Task / Member / Company / Policy / Urgency / Status).
   It replaced the stacked `.tm-row` list, so all of the row-layout classes that
   used to live here are gone — only the presentation atoms the table still
   renders (`.tm-row-status*`, `.tm-row-waiting`, `.tm-placeholder-*`) survive
   below. There is no grouping any more either — grouping was dropped end to
   end (`20260807_7_task_list_flat_search.sql` removed `healev2.task_groups`,
   `tasks.group_id` and the per-row `group` rollup), so every task is a plain
   row and the whole `.tm-group-*` family went with `<TaskGroupRow>`.

   Chrome is the shared `.hr-dash-recent-table` family (§10): fill-less
   uppercase headers, a sand-300 hairline per row, no divider on the last row.
   Columns are CONTENT-SIZED — no `min-width` on the table and no `<colgroup>`,
   which is what would break the horizontal scroll below.
   --------------------------------------------------------------------------- */

/* Horizontal scroll lives HERE, at EVERY width — not only inside the ≤768px
   block the shared wrapper limits itself to — so a six-column table never makes
   the page scroll sideways. Safe to opt in: this table opens no row dropdown
   for a scroll container to clip. */
.tm-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.tm-table {
  table-layout: auto;
}

.tm-table .hr-dash-recent-th {
  padding: 11px 18px;
}

.tm-tr .hr-dash-recent-td {
  padding: 13px 18px;
  vertical-align: middle;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
}

/* §12 — SELECTED is the only PERSISTENT background: blue-400 @ 6% fill + a
   blue-400 left rule on the first cell (a <tr> can't carry a border in a
   border-collapse table, so the rule is an inset box-shadow). The `:hover` pair
   keeps the tint while hovering a selected row, so selection never washes back
   down. */
.tm-tr--selected .hr-dash-recent-td,
.tm-tr--selected:hover .hr-dash-recent-td {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 6%, white);
}

.tm-tr--selected .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 var(--brand-secondary, #4374f3);
}

/* §12 — action needed: a 3px coral-500 left rule and NOTHING else (no tint).
   Declared AFTER `--selected` so, at equal specificity, its rule colour wins on
   a row that is both selected and actionable ("action needed wins over
   selection"), while the background still comes from selection alone. Do not
   reorder these two blocks. */
.tm-tr--action-needed .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 var(--brand-accent, #ff6733);
}

.tm-tr--skeleton .hr-dash-recent-td {
  cursor: default;
}

.tm-tr--span .hr-dash-recent-td {
  padding: 0;
}

/* The Task cell: the ACTION (the member's name is stripped out of the derived
   title — the Member column carries it) over the category label. */
.tm-td-task-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.tm-cell-title-line {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* The row's keyboard entry point. A <tr> can't take focus, so the title is a
   real button (or, on a placeholder row, a real link) reset to read as plain
   row text — the row-wide click is the mouse affordance, this is the tab stop. */
.tm-cell-trigger {
  padding: 0;
  border: none;
  background: none;
  font: inherit;
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
}

/* `.tm-cell-trigger--group` (the group header's expander affordance) was
   deleted with grouping — there is one trigger shape now, the plain row title
   above. */

.tm-cell-sub {
  margin: 0;
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

/* Never an empty cell — an absent Company/Policy states its absence (§21). */
.tm-cell-empty {
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.tm-member-cell {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.tm-member-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.tm-member-name {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
}

.tm-td-company,
.tm-td-policy {
  font-size: 12.5px;
  color: var(--ins-pol-text-color, #374151);
}

/* Stacked inside a WRAPPER, not on the `<td>` itself: `display: flex` on a
   table cell takes it out of the table box model and the column stops aligning
   with its header. */
.tm-td-status-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}

/* Urgency — a two-line indicator inside a soft band tinted by level. The TEXT
   already says the state ("−4d" over "overdue"), so the tint is redundant
   reinforcement, never the only signal. Tones follow §12: overdue/due-today =
   rejected (rust), due-soon = pending (gold), later/none = inactive (ink-400),
   done = approved (green). Each fill is its own token at 12% (§2), never a
   flattened pre-mixed colour. */
.tm-urgency {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 5px 10px;
  border-radius: 10px;
  min-width: 56px;
}

.tm-urgency-label {
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.15;
}

.tm-urgency-sub {
  font-size: 10.5px;
  font-weight: 500;
  line-height: 1.15;
  opacity: 0.85;
}

.tm-urgency--overdue,
.tm-urgency--due_today {
  color: var(--dash-status-rejected-color, #dc2626);
  background-color: color-mix(in srgb, var(--dash-status-rejected-color, #dc2626) 12%, transparent);
}

.tm-urgency--due_soon {
  color: var(--dash-status-pending-color, #b45309);
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #b45309) 12%, transparent);
}

.tm-urgency--later,
.tm-urgency--none {
  color: var(--dash-status-inactive-color, #6b7280);
  background-color: color-mix(in srgb, var(--dash-status-inactive-color, #6b7280) 10%, transparent);
}

.tm-urgency--done {
  color: var(--dash-status-approved-color, #059669);
  background-color: color-mix(in srgb, var(--dash-status-approved-color, #059669) 12%, transparent);
}

/* Row-local status presentation — dot + text inside a soft-tinted pill,
   same visual language (and same tone colours) as the shared
   `<TaskThreadStatusBadge>` pill used by `.task-detail-status .task-status`.
   Tone colours match `.task-status--<tone>` 1:1, so a given status reads as
   the same colour family on both the list rows and the detail panel — only
   the leading glyph differs (a plain dot here vs. the icon/dot swap in the
   detail header). */
.tm-row-status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #6b7280);
}

.tm-row-status-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
  background-color: currentColor;
}

.tm-row-status--new {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, white);
  color: var(--brand-secondary, #4374f3);
}

.tm-row-status--done {
  background-color: var(--dash-status-approved-bg, #d1fae5);
  color: var(--dash-status-approved-color, #059669);
}

/* Neutral/inactive, not red — matches `.task-status--cancelled`'s own tone
   correction above (a cancelled thread isn't an error state). */
.tm-row-status--cancelled {
  background-color: var(--dash-status-inactive-bg, #eef2f6);
  color: var(--dash-status-inactive-color, #6b7280);
}

/* Lightweight "Waiting on <role>" hint on a row the viewer does NOT currently
   hold the ball on (participant, but can_act false) — gold-500 (the same
   "pending" tone `.task-status--pending`/`<TaskUploadDropzone>`'s Pending
   pill use), so a row waiting on someone else reads consistently with every
   other "pending" surface in Task Manager. */
.tm-row-waiting {
  font-size: 12px;
  font-weight: 600;
  color: var(--dash-status-pending-color, #9ca3af);
}



/* 32px tinted glyph pad — §16's document-row icon treatment, retoned to the
   §12 "action needed" coral so the row's leading mark agrees with its rule. */
.tm-placeholder-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: 10px;
  color: var(--brand-accent, #ff6733);
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 12%, transparent);
}

/* Right-hand affordance — plain blue-400 label + chevron rather than a filled
   button: the whole row is already the link, so a nested CTA pill would read as
   a second, competing target. */
.tm-placeholder-cta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12.5px;
  font-weight: 700;
  white-space: nowrap;
  color: var(--brand-secondary, #4374f3);
}

/* Keyset "Load more" pager (Phase 1) — the standard outlined pill
   (`.hr-dash-recent-btn-outline`, which supplies fill/border/radius/hover/
   disabled), centred in the full-width list. This class only widens it and
   raises it to the 44px mobile tap-target floor. */
.tm-list-more {
  padding: 14px 18px 18px;
  display: flex;
  justify-content: center;
}

.tm-list-more-btn {
  padding: 12px 28px;
  min-height: 44px;
}

/* Paging failure (§17, narrow variant) — a page-2+ request that failed is
   reported NEXT TO the pager, never as a full-table error state, so the rows
   already on screen survive. Stacks the message above the button, which
   doubles as the retry. */
.tm-list-more--error {
  flex-direction: column;
  gap: 10px;
}

.tm-list-more-error {
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
  color: var(--dash-status-rejected-color, #dc2626);
}

/* Platform "view as company" defensive fallback — brief inline message shown
   instead of the list when no company scope resolved (see the HR page's own
   note). Replaces a former inline-styled block. */
.tm-company-fallback {
  padding: 48px 20px;
  text-align: center;
  font-size: 14px;
  font-weight: 600;
  color: #374151;
}

/* Platform "view as company" context strip — shown at the top of the list
   card when a platform-role caller has a company selected (the counterpart to
   the `.tm-company-fallback` "no company" state). Lightweight, brand-tinted. */
.tm-viewing-as {
  padding: 8px 18px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 8%, white);
}

/* THE right-docked overlay panel (Charte §13 "Overlay variant") — ONE rule,
   worn by every panel that docks to the right edge over the page: the Task
   Manager's task detail, the assistant's export spec, the company-information
   editor opened from `/hr/quotes/new`. They are the same object, so they share
   one class (`.side-panel`, rendered by `<SidePanel>` in
   `src/components/ui/side-panel.tsx`) instead of a comma-list that grows a name
   per feature; each keeps its own class only for content rules.

   Viewport-fixed to the right edge, full viewport height, so it covers the page
   header/topbar too while the page runs full width underneath and never
   reflows. 430px is the top of the Charte's 400–430px range, matching the
   mockup.

   z-index 60 — the same layer as the app's other right-edge slide-overs
   (`.qd-doc-preview-slideover`, `.mf-preview`): above all page content and the
   in-flow topbar, above the ≤768px sidebar drawer (50) so the mobile
   full-screen detail view isn't half-covered by it, and below `.modal-overlay`
   (100) so a dialog opened from inside the panel still sits on top of it.

   Per §13 the overlay variant has NO border and NO corner radius — the
   leftward drop shadow above is the only thing lifting it off the page. (The
   Contact Center's `.cc-rail`/`.cc-detail` are genuine in-flow CARDS and do
   keep their rounded corners; this is not one.) */
/* ── READ-ONLY, WHICH IS NOT AN ERROR ──
   Stated at the top of a panel whose data the caller may READ but not EDIT
   (`canManage === false`). Before this, the only signal was a tooltip on the
   locked Save button — discoverable solely by hovering a control that looks
   broken, so the honest answer ("you can look, you cannot change") was the one
   thing the screen never said out loud.

   IT MUST NOT LOOK LIKE A FAILURE, and that is the whole rule: no `ListState`,
   no alert icon, no coral, no rust. Nothing failed — the read SUCCEEDED and the
   data is right there underneath. §2 informational tint (blue-50 ground, navy
   ink — the same pair as `.dash-topbar-badge--info`, and deliberately none of
   the §12 status tones, each of which carries a verdict).

   Generic on purpose: it is named for the STATE, never for the panel that first
   needed it, and a per-panel variant of it must never be added. Three panels
   share the permission split that produced it (see `panel-read-failure.ts`) and
   any of them may need to say this; what stays per-panel is the SENTENCE, which
   names the specific thing that cannot be edited. */
.panel-readonly-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border-radius: var(--brand-radius, 12px);
  background-color: var(--panel-readonly-note-bg, #e8effc);
  color: var(--panel-readonly-note-color, #0a3373);
  font-size: 12px;
  font-weight: 600;
  line-height: 1.45;
}

.panel-readonly-note svg {
  flex-shrink: 0;
  margin-top: 1px;
}

.side-panel {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  width: var(--tm-detail-overlay-width, 430px);
  max-width: 100vw;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  border-radius: 0;
  box-shadow: var(--tm-detail-overlay-shadow, -4px 0 24px rgba(15, 23, 42, 0.12));
}

.side-panel .modal-head {
  flex-shrink: 0;
  padding: 16px 24px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.side-panel .modal-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 16px 24px 24px;
}

/* The MODAL form of the shell (`<SidePanel modal>`): a real dialog, for a panel
   the user opened deliberately to edit something — the page behind must hold
   still, so it gets a backdrop that dims it, absorbs the click that closes the
   panel, and stops the wheel from scrolling the list underneath
   (`overflow: hidden` + `overscroll-behavior: contain`, since a fixed backdrop
   otherwise chains its scroll to the page region it was rendered inside).

   The Task Manager and assistant panels are deliberately NOT modal — they show
   the current selection / auto-opened chrome, and the surface behind stays
   visible and clickable — so they render no backdrop at all. */
.side-panel-backdrop {
  position: fixed;
  inset: 0;
  z-index: 60;
  overflow: hidden;
  overscroll-behavior: contain;
  background-color: var(--modal-overlay-bg, rgba(13, 21, 38, 0.45));
  animation: modal-fade 0.12s ease;
}

/* Set by `<SidePanel modal>` while it is open. A no-op inside the dashboard
   shell (which is `h-screen overflow-hidden`, so the document never scrolls and
   has no scrollbar to remove — hence no horizontal shift when the panel opens);
   it is what keeps a standalone/scrolling page behind from scrolling. */
body.side-panel-open {
  overflow: hidden;
}

/* Centred icon + title + text empty/error state inside a list or table card —
   the shared `<ListState>` (`src/components/ui/list-state.tsx`) block, also
   used inline by the insurer policies/census pages. Named `.tm-detail-*` for
   historical reasons (it started as the Task Manager's right-column resting
   state, which the overlay layout no longer has); every remaining consumer is
   an in-card list state. */
.tm-detail-empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 6px;
  padding: 40px;
}

.tm-detail-empty-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  margin-bottom: 4px;
  border-radius: 16px;
  color: #94a3b8;
  background-color: #f1f5f9;
}

/* Error variant of the shared <ListState> icon — tinted with the theme's
   rejected/error token (Heale → rust-600) over a matching soft fill. */
.tm-detail-empty-icon--error {
  color: var(--dash-status-rejected-color, #dc2626);
  background-color: var(--dash-status-rejected-bg, #fee2e2);
}

.tm-detail-empty-title {
  margin: 0;
  font-size: 14px;
  font-weight: 700;
  color: #374151;
}

.tm-detail-empty-text {
  margin: 4px 0 0;
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

@media (max-width: 768px) {
  /* Shared `.dash-body--split` row→column flip — consumed by the CONTACT
     CENTER (`.cc-rail`/`.cc-detail`), whose own mobile block below depends on
     this rule living here. Not a Task Manager rule any more. */
  .dash-body--split {
    flex-direction: column;
  }

  /* The overlay goes full-screen so it reads as a proper mobile detail VIEW
     (one pane at a time, same end result the old
     `.tm-list-card--hidden-mobile`/`.tm-detail-card--mobile-active` pair
     achieved by hiding the list — unnecessary now that the panel covers the
     whole screen). Its close ("×") button doubles as "back to list". */
  .side-panel {
    width: 100vw;
  }

  /* "Action needed" (the longest tab label) otherwise wraps onto two lines
     at narrow widths, misaligning the tab row's count badges against the
     shorter "All"/"Completed" tabs — force one line and shrink the tab a
     little so all three still fit at a 360-414px viewport. Scoped to
     `.tm-list-tabs-row` only; the generic `.tabs-underline-seg` default
     (used unscoped by `/insurer/quotes/[id]` and `/hr/quotes/[id]`) is
     untouched. */
  .tm-list-tabs-row .tabs-underline {
    gap: 18px;
  }
  .tm-list-tabs-row .tabs-underline-seg {
    white-space: nowrap;
    padding: 6px 0 10px;
    font-size: 12px;
    gap: 5px;
  }

  /* The full-screen panel's action row is ~295px at 375px, so a half-row (143px)
     is narrower than every accept label ("✓ Accept document" needs 162px). Stack
     the two decision pills instead of letting the wider one eat the row: full
     width each, and equal by construction. */
  .tm-review-actions {
    flex-direction: column;
  }

  /* Same arithmetic for the date pair in an activation reveal — two 143px
     columns cannot hold a native date control. */
  .tm-review-field-row {
    grid-template-columns: 1fr;
  }

  /* …and for the outstanding-items deep links ("Complete details" /
     "Upload documents"), which are wider still. */
  .tm-outstanding-actions {
    flex-direction: column;
  }
}

/* =====================================================================
   Member form — shared census new + edit (<MemberForm>)
   Structural classes; brand colours come from --brand-* tokens so the
   form reskins per theme. Neutral greys are local, matching the rest of
   this stylesheet.
   ===================================================================== */
.mf-root { display: flex; height: 100%; overflow: hidden; }
.mf-col { position: relative; display: flex; flex-direction: column; flex: 1; min-width: 0; overflow: hidden; }

/* Compact form styles shared by both the HR member editor (mf-root) and the employee
   self-service page (mf-form). White card boxes are preserved — no transparency override. */
.mf-root .page-header-title { font-size: 20px; }
.mf-root .page-header--top { padding-top: 14px; padding-bottom: 12px; }
.mf-root .dash-stat-icon { width: 28px; height: 28px; }
.mf-root .hr-dash-recent-title { font-size: 13px; }

.mf-form .ins-pol-card-body { padding: 14px 20px 16px; display: flex; flex-direction: column; gap: 14px; }
.mf-form .dash-body { gap: 16px; padding: 16px 20px 24px; }
/* RETIRED (Phase 2 of the field consolidation): `.mf-form` used to re-size the
   whole field family compact — `.ins-pol-input`/`select.ins-pol-input` at
   38/8/13, `.ins-pol-field-label` at 10.5px/0.04em, `.ins-pol-form-grid` at
   12px 20px, `.ins-pol-field` at 5px — at (0,2,0), i.e. EQUAL specificity to the
   base rules. That made a second field size in the app and forced `.mf-charte`
   to exist purely to out-rank it by source order. Every one of those rules is
   gone: the base rules (see THE FIELD CONTRACT above) are now the only thing
   sizing a field, on every surface and every brand. The two `.mf-form` rules
   left are page/card LAYOUT, not fields.

   Consequence, deliberate: the surfaces this used to shrink now render at the
   contract's size. On Heale that is 44/10/14 (see the four `.mf-form`-without-
   `.mf-charte` surfaces listed at the `.mf-charte` block below); on a brand that
   maps no `--field-*` it is the base fallbacks 44/10/14 as well — this is the ONE
   place Phase 2 changes an unmapped brand, and it is unavoidable: `.mf-form`-only
   compactness cannot be expressed as a brand token value without shrinking every
   OTHER field on that brand too. */


/* header */
.mf-back { background: none; border: none; cursor: pointer; }
.mf-title-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mf-title-row .page-header-title { min-width: 0; overflow-wrap: anywhere; }
.mf-rel-badge { flex-shrink: 0; font-size: 12px; font-weight: 600; padding: 3px 9px; border-radius: 999px; background: #e5edfb; color: var(--brand-primary, #0a3373); }
.mf-rel-badge--dep { background: #eef3fe; }
/* Member-form header: title block left, policy stat-box right (wraps under on
   narrow screens). Mirrors the portfolio/HR rounded widget language. */
.mf-header { gap: 16px; flex-wrap: wrap; }
.mf-header-left { min-width: 0; }
.mf-header .page-header-right { flex-shrink: 1; min-width: 0; }
.mf-policy-empty { color: #94a3b8; }
.mf-status { padding: 4px 10px; border-radius: 999px; font-weight: 600; }

/* Condensed policy box: icon + (plan name / coverage) + status chip. */
.mf-policybox {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: #ffffff;
  border: 1px solid #e5e7eb;
  border-radius: 14px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}
.mf-policybox-icon { display: flex; align-items: center; justify-content: center; color: #9aa6bb; flex-shrink: 0; }
.mf-policybox-main { min-width: 0; }
.mf-policybox-name {
  margin: 0; font-size: 12.5px; font-weight: 600; line-height: 1.25; color: #374151;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 210px;
}
.mf-policybox-sub {
  margin: 1px 0 0; font-size: 11px; line-height: 1.2; color: #94a3b8;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 210px;
}
.mf-policybox-status {
  display: inline-flex; align-items: center; gap: 5px; flex-shrink: 0;
  padding: 3px 9px; border-radius: 999px; font-size: 11px; font-weight: 600;
}
.mf-policybox-status .mf-policybox-dot { width: 6px; height: 6px; border-radius: 999px; }
/* Dot-driven status chip — colour matches the coverage spec dots (.cov-dot--*) so the
   header chip agrees with the census list / profile coverage surfaces. amber=pending,
   blue=future-start, green=active, red=rejected, grey/neutral=terminated. */
.mf-policybox-status.dot-amber { background: #fcf6e6; color: #8a6d1f; }
.mf-policybox-status.dot-amber .mf-policybox-dot { background: #eca04b; }
.mf-policybox-status.dot-blue { background: #eaf0fe; color: #2c4fb0; }
.mf-policybox-status.dot-blue .mf-policybox-dot { background: #4374f3; }
.mf-policybox-status.dot-green { background: #eef6ee; color: var(--brand-success, #2f6b43); }
.mf-policybox-status.dot-green .mf-policybox-dot { background: var(--brand-success, #32a15e); }
.mf-policybox-status.dot-red { background: #fbe9e9; color: #b42318; }
.mf-policybox-status.dot-red .mf-policybox-dot { background: #dc2626; }
.mf-policybox-status.dot-grey { background: #f1f5f9; color: #64748b; }
.mf-policybox-status.dot-grey .mf-policybox-dot { background: #94a3b8; }
.mf-status--pending { background: #fcf6e6; border: 1px solid #f0e0b6; color: #8a6d1f; }

/* Coverage summary box (Insurance tab) — informational card: status line (reuses
   .cov-badge/.cov-state/.cov-dot for list parity) + a phase explanation + read-only
   policy detail rows. Styled to match hr-dash-recent-card / mf-warn surfaces. */
.mf-coverage-box {
  /* No own margin: the parent .dash-body gap (18px) already spaces it from the
     Policy card — a margin-top here would double it. */
  margin-top: 0;
  padding: 14px 16px;
  background: var(--hr-dash-recent-card-bg, #ffffff);
  border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5);
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(16, 24, 40, 0.06), 0 1px 2px rgba(16, 24, 40, 0.04);
}
.mf-coverage-status { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mf-coverage-desc { margin: 9px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--mf-coverage-desc-color, #64748b); }
.mf-coverage-rows {
  margin-top: 12px; padding-top: 12px;
  border-top: 1px solid var(--mf-coverage-rows-border, var(--ins-pol-statbar-border, #eef1f6));
  display: flex; flex-direction: column; gap: 7px;
}
.mf-coverage-row { display: flex; align-items: baseline; gap: 12px; font-size: 12.5px; }
/* 168px, not 120px: the merged Policy card puts "Requested coverage start date" in
   this label column, and every row must keep ONE alignment line. */
.mf-coverage-label { flex-shrink: 0; width: 168px; color: var(--mf-coverage-label-color, #94a3b8); font-weight: 600; }
/* A detail row whose value is an editable control (the requested start date) —
   centre it against the label and keep the input at field width, not full row width. */
.mf-coverage-row--field { align-items: center; }
.mf-coverage-row--field .mf-datefield { max-width: 220px; }
.mf-coverage-value { color: var(--mf-coverage-value-color, #374151); font-weight: 500; overflow-wrap: anywhere; }

/* Lifecycle action (Cancel enrollment / Terminate) — sits top-right of the
   coverage box header, in line with the status badge. */
.mf-coverage-actions { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
/* NB the member editor's own action buttons are the app-standard
   `.hr-dash-recent-btn-outline` (+ `--danger`), not a `mf-*` shape: the bespoke
   `.mf-action-btn*` family died with the "Actions" dropdown it was drawn for. */

/* Assign-to-policy picker (Policy card, editable states). One flex row,
   bottom-aligned so the CTA sits level with the input controls (labels above
   don't offset it); wraps — the CTA drops below on narrow widths. */
.mf-assign-panel { display: flex; flex-direction: column; gap: 14px; }
.mf-assign-row { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 18px 28px; }
.mf-assign-field { flex: 1 1 220px; min-width: 200px; }
.mf-assign-cta { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.mf-ghost-btn { background: none; border: none; cursor: pointer; padding: 6px 8px; border-radius: var(--mf-ghost-btn-radius, 6px); font-size: 12.5px; font-weight: 600; color: var(--mf-ghost-btn-color, #64748b); transition: background-color .12s, color .12s; }
.mf-ghost-btn:hover { background: var(--mf-ghost-btn-bg-hover, #f1f5fe); color: var(--brand-primary, #0a3373); }
.mf-ghost-btn:disabled { opacity: .45; cursor: not-allowed; }
.mf-ghost-btn--danger { color: var(--mf-ghost-btn-danger-color, var(--brand-error, #dc2626)); }
.mf-ghost-btn--danger:hover { background: var(--mf-ghost-btn-danger-bg-hover, #fdf2f2); color: var(--mf-ghost-btn-danger-color, var(--brand-error, #dc2626)); }

/* Staged (pending-save) markers — chip in the coverage box header, note under the
   picker. "Pending save" is a §12 waiting state, so the chip is the gold/pending
   status surface (§2: token @16% fill + the same token at 100% for the label). */
.mf-staged-chip { display: inline-flex; align-items: center; padding: 3px 10px; border-radius: 100px; font-size: 11px; font-weight: 700; letter-spacing: .02em; background: var(--mf-staged-chip-bg, #fcf6e6); color: var(--mf-staged-chip-color, #b08a2e); border: 1px solid var(--mf-staged-chip-border, #f0e0b6); }
.mf-staged-note { margin: 0; font-size: 12.5px; line-height: 1.5; color: var(--mf-coverage-desc-color, #64748b); }

/* Previous enrollments — modal (opened from a subtle link / contextual line).
   Rows follow the .bnf-section-header accordion pattern; the lazy timeline
   renders as a simple dotted vertical list. */
/* Quiet secondary link, right-aligned as a footer of the coverage box area —
   same voice as the dashboards' "View all" panel links. */
.mf-hist-link { align-self: flex-end; margin-top: -12px; display: inline-flex; align-items: center; gap: 3px; background: none; border: none; cursor: pointer; padding: 0; font-size: 12.5px; font-weight: 600; color: var(--mf-hist-link-color, #64748b); transition: color .12s; }
.mf-hist-link:hover { color: var(--brand-primary, #0a3373); }
/* Compound selector on purpose: the base .mf-modal (width 380px) is defined
   LATER in this file — same specificity would let it win, so the variant must
   out-rank it, not out-order it. */
.mf-modal.mf-modal--wide { width: min(820px, calc(100vw - 48px)); }
.mf-hist-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.mf-hist-modal-head .mf-modal-title { margin: 0; }
.mf-hist-body--modal { max-height: 66vh; overflow-y: auto; margin: 16px -24px -24px; border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6); border-radius: 0 0 12px 12px; }
/* Roomier rows + timeline inside the modal (the inline defaults are card-sized). */
.mf-hist-body--modal .mf-hist-row-head { padding: 16px 24px; gap: 16px; }
.mf-hist-body--modal .mf-hist-policy { font-size: 13.5px; }
.mf-hist-body--modal .mf-hist-row-main { gap: 4px; }
.mf-hist-body--modal .mf-hist-timeline { padding: 4px 24px 20px 44px; gap: 12px; }
.mf-hist-body--modal .mf-hist-empty { padding: 4px 24px 20px 44px; }
.mf-hist-chevron { color: #9ca3af; flex-shrink: 0; transition: transform .15s; }
.mf-hist-chevron.is-open { transform: rotate(180deg); }
.mf-hist-body { border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6); }
.mf-hist-row:not(:last-child) { border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6); }
.mf-hist-row-head { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; width: 100%; padding: 12px 16px; background: none; border: none; cursor: pointer; text-align: left; transition: background-color .12s; }
.mf-hist-row-head:hover { background: #fbfcfe; }
.mf-hist-row-main { flex: 1; min-width: 180px; display: flex; flex-direction: column; gap: 2px; }
.mf-hist-policy { font-size: 13px; font-weight: 600; color: var(--brand-primary, #0a3373); overflow-wrap: anywhere; }
.mf-hist-sub { font-size: 12px; font-weight: 500; color: #64748b; }
.mf-hist-meta { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.mf-hist-date { font-size: 11.5px; color: #94a3b8; white-space: nowrap; }
.mf-hist-timeline { padding: 2px 16px 14px 32px; display: flex; flex-direction: column; gap: 10px; }
.mf-hist-event { position: relative; padding-left: 16px; font-size: 12.5px; color: #374151; }
.mf-hist-event::before { content: ''; position: absolute; left: 0; top: 5px; width: 7px; height: 7px; border-radius: 50%; background: #c7d2e8; }
.mf-hist-event-title { font-weight: 600; color: var(--brand-primary, #0a3373); }
.mf-hist-event-meta { color: #94a3b8; font-size: 11.5px; margin-left: 6px; }
.mf-hist-event-note { display: block; color: #64748b; margin-top: 2px; }
.mf-hist-empty { padding: 2px 16px 14px 32px; margin: 0; font-size: 12.5px; color: #94a3b8; }

/* body + rail */
.mf-body { flex: 1; min-height: 0; display: flex; }
.mf-rail { width: 216px; flex-shrink: 0; border-right: 1px solid #e5e7eb; padding: 12px; display: flex; flex-direction: column; gap: 3px; background: #fbfcfe; overflow: auto; }
.mf-rail-title { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: #9ca3af; margin: 0 0 6px 4px; }
.mf-member { position: relative; }
.mf-member-btn { display: flex; align-items: center; gap: 9px; width: 100%; padding: 6px 9px; border-radius: 8px; border: none; text-align: left; cursor: pointer; background: transparent; transition: background-color .12s; }
/* Rail ROW (census member-form rail + every Contact Center thread row, which
   reuses this exact class) — the standard row hover, not a control hover. */
.mf-member-btn:hover { background: var(--hr-dash-recent-row-hover-bg, #eef2f7); }
.mf-member-btn.is-active { background: #e5edfb; box-shadow: inset 0 0 0 1px #cbdaf9; }
.mf-avatar { width: 28px; height: 28px; border-radius: 8px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; border: 1px solid transparent; }
.mf-avatar--emp { background: #e5edfb; color: var(--brand-primary, #0a3373); }
.mf-avatar--emp.is-active { background: var(--brand-primary, #0a3373); color: #fff; }
.mf-avatar--dep { background: #e2e8f0; color: #475569; }
.mf-avatar--dep.is-active { background: #eef3fe; border-color: var(--brand-secondary, #4374f3); }
.mf-member-textwrap { min-width: 0; }
.mf-member-textwrap--dep { padding-right: 26px; }
.mf-member-name { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; font-size: 13px; font-weight: 600; line-height: 1.25; color: var(--brand-primary, #0a3373); overflow: hidden; overflow-wrap: anywhere; }
.mf-member-sub { display: block; font-size: 12px; color: #64748b; }
.mf-member-remove { position: absolute; top: 50%; right: 6px; transform: translateY(-50%); width: 24px; height: 24px; border-radius: 6px; border: none; background: transparent; color: #9aa6bb; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: background-color .12s, color .12s; }
.mf-member-remove:hover { background: #fbe9e9; color: var(--brand-error, #dc2626); }
.mf-member-remove:disabled { opacity: .4; cursor: not-allowed; }
.mf-add-dep { display: flex; align-items: center; justify-content: center; gap: 8px; margin-top: 8px; padding: 9px 10px; border-radius: 8px; border: 1px dashed #c7d2e8; background: #fff; color: var(--brand-primary, #0a3373); font-size: 13px; font-weight: 600; cursor: pointer; }
.mf-add-dep:disabled { color: #9aa6bb; cursor: not-allowed; opacity: .7; }
.mf-hint { font-size: 11px; color: #94a3b8; margin: 6px 4px 0; line-height: 1.5; }

/* content + tabs */
.mf-content { position: relative; flex: 1; min-width: 0; overflow: hidden; display: flex; flex-direction: column; }
/* Underline tabs: read clearly as a view/context switch (whole panel below changes),
   not as an in-page pill filter. The row's bottom rule spans the content width and the
   active tab "owns" it via a brand-colored underline. */
.mf-tabs { display: flex; align-items: stretch; padding: 12px 28px 0; flex-shrink: 0; border-bottom: 1px solid #e5e7eb; }
.mf-seg { display: inline-flex; align-items: stretch; gap: 2px; }
/* Tab-level chrome at the right end of the tabs row (`<SectionTabs right>`): the
   member editor's action buttons. Centred against the tab labels, never stretched.
   It WRAPS rather than overflowing — the set is now up to three visible buttons
   instead of one dropdown trigger, so on a narrow viewport the extra ones drop to
   a second line inside the row. */
.mf-tabs-actions { margin-left: auto; align-self: center; display: flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; gap: 8px; padding-bottom: 8px; }
/* No `gap`: the tab button holds a single text label since the per-tab icon was
   removed (`<SectionTabs>`), so there is nothing left to space. */
.mf-seg-btn { display: inline-flex; align-items: center; gap: 7px; padding: 9px 16px; margin-bottom: -1px; cursor: pointer; font-size: 13px; font-weight: 600; border: none; border-bottom: 2px solid transparent; background: transparent; color: #64748b; transition: color .12s, border-color .12s; }
/* The active/hovered label and the 2px rule the active tab owns. Tokenised so a
   white label can recolour this row: it is the same §7 "swap the view" selector as
   `.tabs-underline-seg--active` above and must be able to match it, and until these
   tokens existed it was reachable ONLY through `--brand-primary`/`--brand-secondary`
   — which are also ink, links, focus and identifiers, so no theme could move the tab
   without moving those. Structured to mirror that family: ONE token for the label
   (read by `:hover` and `.is-active` alike, exactly as `.tabs-underline-seg:hover`
   reads its active colour) and one for the rule. Each fallback is the expression
   that was inline here, so a brand that maps neither renders unchanged. */
.mf-seg-btn:hover { color: var(--mf-seg-active-color, var(--brand-primary, #0a3373)); }
.mf-seg-btn.is-active { color: var(--mf-seg-active-color, var(--brand-primary, #0a3373)); border-bottom-color: var(--mf-seg-active-border, var(--brand-secondary, #4374f3)); }
.mf-lead { display: flex; flex-direction: column; gap: 14px; }
/* Single-column document list, flush with the flat form gutter. The descriptive note
   now lives in the card header (.mf-doc-subtitle), so the list gets a uniform, moderate
   top breathing room instead of an oversized empty band. */
.mf-root .mf-docs { grid-template-columns: 1fr; gap: 10px; }
/* Note-as-subtitle under the card title (above the header divider). */
.mf-doc-head-text { display: flex; flex-direction: column; min-width: 0; }
.mf-doc-subtitle { font-size: 12.5px; color: var(--mf-doc-subtitle-color, #7d93b3); margin: 0; line-height: 1.45; }
/* Secondary "Additional" subgroup inside the same documents card. */
.mf-doc-subgroup { margin-top: 4px; padding-top: 14px; border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6); display: flex; flex-direction: column; gap: 10px; }
/* §3 section label — 10px Bold uppercase, tracking +6%, ALWAYS the muted ink
   tone (reuses `--mf-doc-label-color`, the same token `.mf-doc-col-title` reads). */
.mf-doc-subgroup-head { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mf-doc-label-color, #9ca3af); margin: 0; }
/* padding-bottom clears the floating save/cancel cluster so the last field stays reachable */
.mf-scroll { flex: 1; min-height: 0; overflow: auto; padding-bottom: 84px; }
/* Advisory (never blocking) notice — §12 "pending / waiting" semantics, so the
   surface is the gold token tint with a gold icon and body-tone text. The
   historical amber hexes stay as the base fallbacks for themes with no map. */
.mf-warn { display: flex; gap: 8px; padding: 10px 14px; border-radius: 12px; background: var(--mf-warn-bg, #fcf6e6); border: 1px solid var(--mf-warn-border, #f0e0b6); color: var(--mf-warn-color, #8a6d1f); font-size: 12.5px; line-height: 1.55; }
.mf-warn-icon { flex-shrink: 0; margin-top: 1px; color: var(--mf-warn-icon-color, inherit); }
.mf-warn-body { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.mf-warn-list { display: flex; flex-direction: column; gap: 4px; margin: 0; padding: 0; list-style: none; }
.mf-warn-list li { display: flex; gap: 8px; align-items: baseline; font-size: 12.5px; }
.mf-warn-cat {
  flex-shrink: 0; min-width: 76px; font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--mf-warn-cat-color, #b08a2e);
}
.mf-warn-actions { display: flex; flex-wrap: wrap; gap: 14px; }
.mf-warn-link {
  display: inline-flex; align-items: center; gap: 2px; padding: 0;
  background: none; border: none; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--brand-secondary, #4374f3);
}
.mf-warn-link:hover { text-decoration: underline; }
.mf-error { padding: 10px 12px; border-radius: var(--mf-error-radius, 8px); background-color: var(--mf-error-bg, #fde8e8); color: var(--mf-error-color, var(--brand-error, #dc2626)); font-size: 13px; }

/* "Send invitation now" footer block (create mode only) — checkbox gated on the
   employee's live first/last name + email, explanation always visible above it.
   Sits inside `.mf-form .ins-pol-card-body`, which already applies a 14px flex
   gap between children, so these carry no margin of their own. */
.mf-invite-desc { font-size: 13px; color: #64748b; margin: 0; line-height: 1.5; }
.mf-invite-check { display: flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; color: #374151; }
.mf-invite-check input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--brand-secondary, #4374f3); flex-shrink: 0; }
.mf-invite-check:has(input:disabled) { cursor: not-allowed; color: #9ca3af; }
.mf-invite-hint { font-size: 12px; color: #7d93b3; margin: 0; }

/* preview */
/* Wrapper that turns the in-flow `.mf-preview` pane into a right-docked
   slide-over, for the three hosts that have no split-pane shell for it to sit
   in as a flex sibling (HR company settings, insurer policy Company +
   Documents tabs). Same layer as `.mf-preview`/`.tm-detail-panel` (60). The
   shadow is tokenized so Heale can use §8's right-docked overlay value while
   other themes keep the historical one. Replaces an inline `style` that
   carried a hardcoded shadow colour. */
.doc-preview-overlay {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  box-shadow: var(--doc-preview-overlay-shadow, -4px 0 24px rgba(15, 23, 42, 0.12));
}

.mf-preview { width: 440px; flex-shrink: 0; height: 100%; background: #fff; border-left: 1px solid var(--mf-preview-border, #e5e7eb); display: flex; flex-direction: column; }
.mf-preview-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--mf-preview-border, #e5e7eb); }
.mf-preview-title { font-size: 14px; font-weight: 600; color: var(--mf-preview-title-color, #111827); margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mf-preview-actions { display: flex; align-items: center; gap: 8px; margin-left: 12px; }
/* §13's 32px square close/action control — radius + border tokenized so the
   Charte's 10px/sand-400 treatment doesn't touch the other white-labels. */
.mf-icon-btn { width: 32px; height: 32px; border-radius: var(--mf-icon-btn-radius, 6px); border: 1px solid var(--mf-icon-btn-border, #e5e7eb); background: #fff; color: var(--mf-icon-btn-color, inherit); cursor: pointer; display: flex; align-items: center; justify-content: center; }
.mf-icon-btn:hover { background: var(--mf-icon-btn-bg-hover, #f8fafc); }
/* `position: relative` lives here (not inline) so the loading shimmer below can
   be absolutely positioned against it. */
.mf-preview-body { position: relative; flex: 1; overflow: auto; background: var(--mf-preview-body-bg, #f1f5fe); }
/* Loading placeholder for the iframe/image — geometry only; the tone comes from
   the shared `.ui-shimmer` primitive (§17: shimmer, never a spinner). */
.mf-preview-shimmer { position: absolute; inset: 0; z-index: 1; }
/* The media fades in once it has actually loaded (was an inline opacity). */
.mf-preview-media { opacity: 0; transition: opacity .2s; }
.mf-preview-media.is-loaded { opacity: 1; }
.mf-preview-frame { width: 100%; height: 100%; border: none; }
.mf-preview-imgwrap { padding: 20px; display: flex; align-items: center; justify-content: center; height: 100%; }
.mf-preview-img { max-width: 100%; max-height: 100%; border-radius: 8px; object-fit: contain; }
.mf-preview-fallback { padding: 40px; text-align: center; }
.mf-preview-fallback-icon { color: var(--mf-preview-fallback-icon-color, #7d93b3); margin-bottom: 16px; }
.mf-preview-dl { padding: 8px 20px; border-radius: 8px; background: var(--brand-secondary, #2563eb); color: #fff; font-size: 13px; font-weight: 600; border: none; cursor: pointer; }

/* footer — floating bottom-right cluster over the content (no full-width bar) */
/* The member editor's / employee self-service page's save footer is the SAME row
   as the add-member wizard's — see the shared `.mfw-footer` rule below, which
   both `.mf-footer` and `.mfw-footer` now declare together. It used to be a
   floating cluster: absolutely positioned bottom-right, `pointer-events: none`,
   with a translucent blurred pill around the buttons and a second one around the
   count. Both pills are gone; the row is in-flow, full-bleed, meta left and
   actions right on the page surface, with a hairline above. */

/* §12 "pending / waiting" = gold-500. Was `--brand-warning`, which no theme maps,
   so every surface using it (wizard footer, editor footer, /employee) rendered the
   RETIRED #eca04b warning hex. `--dash-status-pending-color` is the mapped one. */
.mf-unsaved { color: var(--dash-status-pending-color, #a16207); font-weight: 600; }
.mf-root .hr-dash-recent-btn-primary:disabled { opacity: .6; cursor: not-allowed; }

/* leave modal */
.mf-modal-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, .45); display: flex; align-items: center; justify-content: center; z-index: 50; }
.mf-modal { background: #fff; border-radius: 12px; padding: 24px; width: 380px; box-shadow: 0 12px 40px rgba(0, 0, 0, .2); }
.mf-modal-title { font-size: 16px; font-weight: 700; color: var(--brand-primary, #0a3373); margin: 0 0 8px; }
.mf-modal-text { font-size: 13.5px; color: #64748b; margin: 0 0 18px; line-height: 1.5; }
/* A textarea inside an `.mf-modal` (e.g. the required termination reason) — only
   the gap to the action row differs from the shared `.modal-textarea`. */
.mf-modal-textarea { margin-bottom: 18px; }
/* Same job for a labelled field (`<SaveTemplateModal>`): the label+input pair
   keeps `.ins-pol-field`'s own gap and only needs clearance above the actions.
   It replaces four inline style declarations that no theme could reach. */
.mf-modal-field { margin-bottom: 20px; }
.mf-modal-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; }
.mf-modal-stay { padding: 9px 16px; border-radius: 8px; border: 1px solid #d2d9e5; background: #fff; color: #64748b; font-size: 13px; font-weight: 600; cursor: pointer; }
.mf-modal-leave { padding: 9px 16px; border-radius: 8px; border: none; background: var(--brand-error, #dc2626); color: #fff; font-size: 13px; font-weight: 600; cursor: pointer; }
.mf-modal-leave:disabled { opacity: .6; cursor: not-allowed; }

/* =====================================================================
   §4 — Add-member wizard page grid (<AddMemberWizard>)

   The wizard borrows the `.mf-*` editor shell, but the EDITOR is deliberately
   full-bleed with a tighter 20px gutter (it has a member rail down its side and
   is a dense multi-tab form). A wizard is an ordinary dashboard PAGE, so it must
   sit on the ordinary dashboard grid: the same content edges as `/hr/census`,
   `/hr/policies`, `/hr/task-manager` etc.

   That grid is NOT a max-width — there is none anywhere in this stylesheet, and
   the Charte's §4 only specifies main padding. Every dashboard page is full-bleed
   inside a gutter: `.dash-body` = 28px (20px at <= 1024px, per the responsive
   block above). So "same width as other pages" means the same gutter.

   `--mfw-gutter` is that one value for the whole wizard column, so the step rail,
   the body cards and the footer bar share EXACTLY the same content edges as each
   other and as the topbar. Scoped to `.mfw-root` so `/hr/census/[id]` (same shell,
   `mf-root mf-form`, no `mfw-root`) keeps its full-bleed editor gutter untouched.
   ===================================================================== */
.mfw-root {
  --mfw-gutter: 28px;
}
/* Same declaration as `.dash-body`'s own, re-applied to beat `.mf-form
   .dash-body`'s tighter editor padding (equal specificity, later in the file). */
.mfw-root .dash-body {
  padding: 24px var(--mfw-gutter);
  gap: 20px;
}
/* The 84px in `.mf-scroll` exists only to clear the EDITOR's floating footer
   cluster. The wizard's bar is in-flow (see `.mfw-footer`) so it never overlaps
   content and no reservation is needed — `.dash-body`'s own 24px bottom padding
   is the breathing room above the divider. */
.mfw-root .mf-scroll {
  padding-bottom: 0;
}
@media (max-width: 1024px) {
  .mfw-root {
    --mfw-gutter: 20px;
  }
}

/* =====================================================================
   §13 — Add-member wizard footer bar
   "the committing action, full-width, pinned, divider above".

   `.mf-footer` (the HR member editor + the employee self-service page) now
   DECLARES THIS SAME RULE alongside `.mfw-footer` — see the shared block below.
   It used to be a `position: absolute` / `pointer-events: none` floating pill
   CLUSTER (two rounded translucent pills with their own shadows +
   backdrop-filter); that treatment was retired so the editor and the wizard read
   as one product, and the shared selector means neither can drift from the other.

   PINNED BY CONSTRUCTION, not by `position: fixed`: `.mf-col` is a column flex
   container whose `.mf-body` is `flex: 1; min-height: 0`, so a `flex-shrink: 0`
   last child sits at the bottom permanently — the body shrinks to make room and
   its `.mf-scroll` scrolls underneath. No z-index, no overlap, and no magic
   bottom padding needed on the scroller to stop the last card hiding behind it.

   Surface is FULL-BLEED, contents inset to `--mfw-gutter`: the bar reaches both
   edges of the wizard column while its text and buttons line up with the cards
   above. Background is the topbar surface (cream-50 on Heale), so the column
   reads symmetrically — cream chrome top and bottom, sand-300 divider at each
   boundary, white cards between (§4/§8: the shared warm surface separates, the
   divider draws the line; the bar is not a card).
   ===================================================================== */
/* ONE footer row for all three surfaces that have one — the add-member wizard
   (`.mfw-footer`), the HR member editor and the employee self-service page (both
   `.mf-footer`). Declared together rather than copied so they cannot drift: an
   in-flow `flex-shrink: 0` last child of `.mf-col`, full-bleed, meta text at the
   far left, `.wizard-actions` at the far right, a hairline above and the page
   surface behind (no white container hugging the buttons). */
.mfw-footer,
.mf-footer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  padding: 14px var(--mfw-gutter, 28px);
  border-top: 1px solid var(--hr-dash-recent-head-border, #e5e7eb);
  background-color: var(--dash-topbar-bg, #ffffff);
}
.mfw-footer-info,
.mf-footer-info {
  min-width: 0;
  font-size: 12.5px;
  color: var(--dash-stat-label-color, #7d93b3);
}
/* `.wizard-actions` supplies the equal-width buttons and the mobile centring; its
   32px top margin is meant for an in-flow step page, not a bar. */
.mfw-footer .wizard-actions,
.mf-footer .wizard-actions {
  margin-top: 0;
}
.mfw-footer .wizard-actions button,
.mf-footer .wizard-actions button {
  min-height: 44px;
}

/* =====================================================================
   §6/§12 — Add-member wizard step rail
   THE rail for every wizard, rendered by `<WizardStepRail>`
   (src/components/ui/wizard-step-rail.tsx): the add-member wizard, the quote
   wizard and the census importer all sit on this family. Geometry came from the
   quote wizard's now-deleted stepper (32px dot, 2px connector, `flex: 1`
   connectors so the row spreads the full width), but every colour reads a token
   instead of `--brand-success` — that one is unmapped in the Heale theme, which
   is why the old family kept rendering the RETIRED success hex (§1).
   Active = navy-700 (`--brand-primary`), completed
   = green-500 (`--dash-status-approved-color`, §12 "completed"), inactive =
   the sand/ink divider + label tokens.

   NO BOTTOM DIVIDER. The rail used to carry a full-bleed hairline under it; the
   rail and the content below are now separated by SPACE alone (§4: 18–22px
   between blocks, and the shared surface does the separating), which is how the
   rest of these flows read. The gap is the rail's own 14px bottom padding plus
   the content wrapper's top padding — 24px on the three wizards that open with
   `.dash-body`, and 20px supplied by `.qw-content` on the quote wizard, whose
   wrapper had none of its own precisely because the rule was doing that work.
   Removing the line WITHOUT giving `.qw-content` that padding would have fused
   the rail onto its first card.
   ===================================================================== */
.mfw-steps {
  display: flex;
  align-items: center;
  gap: 0;
  flex-shrink: 0;
  padding: 16px var(--mfw-gutter, 28px) 14px;
}
/* Each dot+label pair sizes to content; only the connectors grow. */
.mfw-step-item { display: flex; align-items: center; flex: 1 1 auto; min-width: 0; }
.mfw-step-item:last-child { flex: 0 0 auto; }
.mfw-step-inner { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; }

/* `.mfw-step-dot` is a <button> (jump back to a visited step) — never an <a>:
   the member editor's dirty guard intercepts internal anchors at capture phase. */
.mfw-step-dot {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border: none;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  cursor: pointer;
  background-color: var(--mfw-step-dot-bg, #e5e7eb);
  color: var(--mfw-step-dot-color, #6b7280);
  transition: background-color .12s, color .12s;
}
.mfw-step-dot:disabled { cursor: default; }
.mfw-step-dot--active {
  background-color: var(--mfw-step-dot-active-bg, var(--brand-primary, #0a3373));
  color: var(--hr-dash-recent-btn-primary-color, #ffffff);
}
.mfw-step-dot--completed {
  background-color: var(--mfw-step-dot-completed-bg, var(--dash-status-approved-color, #059669));
  color: var(--hr-dash-recent-btn-primary-color, #ffffff);
}
.mfw-step-label {
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
  color: var(--mfw-step-label-color, #6b7280);
}
.mfw-step-label--active {
  font-weight: 700;
  color: var(--mfw-step-label-active-color, var(--brand-primary, #0a3373));
}
.mfw-step-connector {
  flex: 1;
  min-width: 12px;
  height: 2px;
  margin: 0 12px;
  background-color: var(--mfw-step-connector-bg, #e5e7eb);
}
.mfw-step-connector--completed {
  background-color: var(--mfw-step-connector-completed-bg, var(--dash-status-approved-color, #059669));
}

/* Dependants step: member rail left, the selected dependant's cards right, in the
   step's own scroll (no nested tabs). Reuses `.mf-rail` verbatim so its mobile
   horizontal-scroller behaviour comes free. */
.mfw-dep-split { display: flex; align-items: flex-start; gap: 18px; }
.mfw-dep-split .mf-rail {
  flex: 0 0 216px;
  border-right: none;
  border: 1px solid var(--hr-dash-recent-head-border, #e5e7eb);
  border-radius: 12px;
  overflow: visible;
}
.mfw-dep-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 16px; }

/* ── Employee onboarding FTUE wizard (Charte 2026) ──────────────────────────
   Focused, sidebar-less full-page flow. A step rail (one entry per member +
   review) drives a single-screen-at-a-time editor; the sticky footer carries
   global progress + Back/Next/Confirm. Every colour reads a consumed token with
   the historical cool-grey base fallback so non-Heale themes keep their look;
   the "N left" accent is the brand accent (coral on Heale, §12 "action needed")
   and "done" is the approved/green status token. Reuses .hr-dash-recent-card /
   .mf-* editor internals for the screen body — no reinvented editor chrome. */
.onb-body { flex: 1; min-height: 0; display: flex; }
.onb-rail {
  width: 252px; flex-shrink: 0; padding: 16px 12px; overflow: auto;
  display: flex; flex-direction: column; gap: 4px;
  background: var(--dash-body-bg, #fbfcfe);
}
.onb-rail-title {
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--dash-stat-label-color, #9ca3af); margin: 0 0 6px 6px;
}
.onb-step {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 9px 10px; border-radius: 10px; border: none; text-align: left;
  cursor: pointer; background: transparent; transition: background-color .12s, box-shadow .12s;
}
.onb-step:hover { background: var(--hr-dash-recent-row-hover-bg, #eef2f7); }
.onb-step.is-active {
  background: color-mix(in srgb, var(--brand-secondary, #4374f3) 9%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand-secondary, #4374f3) 28%, transparent);
}
.onb-step-num {
  width: 27px; height: 27px; border-radius: 8px; flex-shrink: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700;
  background: var(--dash-status-inactive-bg, #eef2f6); color: var(--dash-status-inactive-color, #6b7280);
}
.onb-step.is-active .onb-step-num { background: var(--brand-primary, #0a3373); color: #fff; }
.onb-step-num--done { background: var(--dash-status-approved-bg, #d1fae5); color: var(--dash-status-approved-color, #059669); }
.onb-step-text { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
.onb-step-title {
  font-size: 13px; font-weight: 700; color: var(--brand-primary, #0a3373);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; overflow-wrap: anywhere;
}
.onb-step-sub { font-size: 11.5px; }
.onb-step-sub--left { color: var(--brand-accent, #fb7a5c); font-weight: 600; }
.onb-step-sub--done { color: var(--dash-status-approved-color, #059669); font-weight: 600; }

.onb-main { position: relative; flex: 1; min-width: 0; overflow: hidden; display: flex; flex-direction: column; }

/* Read-only "from your HR record" framing note above a member screen's cards. */
/* No icon — the note text alone conveys "this came from HR" (per-flow decision
   to drop the decorative ShieldCheck glyph that was here). */
.onb-hr-note {
  display: flex; gap: 8px; align-items: flex-start;
  font-size: 12.5px; line-height: 1.5; color: var(--dash-stat-label-color, #64748b);
}

/* Review screen — one card, a row per member with a jump link. */
.onb-review-item {
  display: flex; align-items: center; gap: 12px; padding: 13px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}
.onb-review-item:last-child { border-bottom: none; }
.onb-review-main { flex: 1; min-width: 0; }
.onb-review-name { font-size: 13.5px; font-weight: 700; color: var(--brand-primary, #0a3373); }
.onb-review-rel { font-size: 11.5px; color: var(--dash-stat-label-color, #8d8579); text-transform: capitalize; }
.onb-review-status { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; }
.onb-review-status--done { color: var(--dash-status-approved-color, #059669); }
.onb-review-status--left { color: var(--brand-accent, #fb7a5c); }
.onb-review-jump {
  background: none; border: none; cursor: pointer; padding: 0;
  font-size: 12.5px; font-weight: 600; color: var(--brand-secondary, #4374f3);
}
.onb-review-jump:hover { text-decoration: underline; }

/* Empty / all-set state (unlinked account, no screens). No status icon — the
   title text ("You're all set") already carries the meaning; per-flow decision
   to drop the decorative checkmark that was here (unlike `<TaskSuccessBanner>`'s
   tick, which stays because it's a named, established component). */
.onb-allset { text-align: center; padding: 56px 24px; display: flex; flex-direction: column; align-items: center; gap: 12px; }
.onb-allset-title { font-size: 18px; font-weight: 700; color: var(--brand-primary, #0a3373); margin: 0; }
.onb-allset-text { font-size: 13.5px; color: var(--dash-stat-label-color, #64748b); margin: 0; line-height: 1.5; max-width: 380px; }

/* §13 "the committing action, full-width, pinned, divider above" — global
   progress left, navigation/CTA right. PINNED BY CONSTRUCTION (same idiom as
   `.mfw-footer`): `.onb-body` above is `flex: 1; min-height: 0`, so this
   `flex-shrink: 0` last child sits at the bottom of `.mf-col` permanently —
   no `position: fixed`, no z-index, no bottom-padding reservation needed on
   the scroll regions above it (they shrink to make room; the footer never
   overlaps them). This only holds while `.mf-root` sits inside a
   viewport-height/overflow-hidden ancestor — see the route's own layout.tsx.
   `padding-left` is widened below by exactly `.onb-corner`'s width (see that
   rule) — the two are opposite-corner siblings sharing this same bottom
   edge, so the reserved gutter is what keeps the progress text from sitting
   under the corner widget instead of the widget overlapping it. */
.onb-footer {
  flex-shrink: 0; display: flex; align-items: center; justify-content: space-between;
  gap: 16px; flex-wrap: wrap; padding: 14px 28px;
  padding-left: calc(28px + 240px); /* 240px = .onb-corner's desktop width, kept in sync below */
  border-top: 1px solid var(--hr-dash-recent-head-border, #e5e7eb);
  background: var(--dash-topbar-bg, #fff);
}
.onb-footer-progress { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--dash-stat-label-color, #64748b); }
.onb-progress-count { font-weight: 700; }
.onb-progress-count--left { color: var(--brand-accent, #fb7a5c); }
.onb-progress-count--done { color: var(--dash-status-approved-color, #059669); }
.onb-footer-actions { display: flex; align-items: center; gap: 10px; }
.onb-footer-actions button { min-width: 120px; min-height: 44px; justify-content: center; }

/* `<ProfileSwitcher>` anchored bottom-left — the route's only way out for a
   signed-in user on the wrong account, in the SAME corner it occupies
   everywhere else (`.sidebar-footer`, bottom-left of the screen), since this
   route has no sidebar shell to host it in. `.onb-corner` supplies ONLY the
   fixed placement/size; `.sidebar-footer` is reused verbatim (not reinvented)
   for the actual padding/divider/background, via `.sidebar-footer
   .profile-switcher`'s existing edge-to-edge-trigger rule — so this reads as
   "the sidebar's corner" transplanted. Positioned `absolute` against `.mf-col`
   (already `position: relative`, already viewport-height via the route's
   layout.tsx) rather than `fixed` — the idiom `.mf-footer` itself used before
   it became an in-flow bar. Because the trigger is bottom-anchored again (not
   in a topbar), `.profile-switcher-popover`'s DEFAULT upward open is correct
   unchanged — no override needed here, unlike an earlier topbar-mounted
   iteration of this control. */
.onb-corner {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 240px; /* .sidebar's own --sidebar-width default; kept in sync with .onb-footer's padding-left above */
  z-index: 20;
  background-color: var(--sidebar-bg, #ffffff);
}

@media (max-width: 768px) {
  .onb-body { flex-direction: column; }
  .onb-rail {
    width: 100%; flex-direction: row; overflow-x: auto; gap: 8px;
    border-right: none; border-bottom: 1px solid var(--hr-dash-recent-row-border, #e5e7eb);
  }
  .onb-rail-title { display: none; }
  .onb-step { width: auto; flex-shrink: 0; }
  .onb-step-text { display: none; }
  .onb-footer { justify-content: center; padding-left: calc(28px + 64px); }
  .onb-footer-progress { width: 100%; justify-content: center; }
  .onb-footer-actions { width: 100%; }
  .onb-footer-actions button { flex: 1; }

  /* The full 240px corner would swallow most of a 375px screen — collapse it
     to an avatar-only trigger, mirroring `.sidebar--collapsed`'s own compact
     treatment (same idea, scoped here since `.onb-corner` isn't `.sidebar`). */
  .onb-corner { width: 64px; }
  .onb-corner .profile-switcher-trigger { justify-content: center; padding: 10px 8px; }
  .onb-corner .profile-switcher-trigger-info,
  .onb-corner .profile-switcher-chevron { display: none; }
  .onb-corner .profile-switcher-popover { left: 8px; right: auto; width: min(300px, calc(100vw - 16px)); }
}

/* required-field marker */
/* Required-field asterisk — §9 asks for the brand accent (coral), not the
   destructive tone: nothing is wrong yet, the field is simply mandatory. */
.mf-req { color: var(--mf-req-color, var(--brand-error, #dc2626)); }

/* documents */
.mf-doc-note { font-size: 12.5px; color: var(--mf-doc-subtitle-color, #7d93b3); margin: 0; padding: 0 16px 6px; }
.mf-docs { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 18px; padding: 0; }
/* Forces one unified list (main doc rows + optional "Additional" subgroup) to stack
   vertically instead of the default auto-fit multi-column layout above — needed by every
   current .mf-docs caller (member-cards, insurer/company doc grids). Previously this class
   had no rule of its own and only worked by accident on pages wrapped in .mf-root (which
   redeclares grid-template-columns: 1fr), so callers outside that wrapper (e.g.
   /employee/documents) rendered the "Additional" subgroup as a second grid column instead
   of stacked below the main list. */
.mf-docs--single { grid-template-columns: 1fr; }
.mf-doc-col { display: flex; flex-direction: column; gap: 0; }
.mf-doc-col-title { font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--mf-doc-label-color, #9ca3af); margin: 0 0 2px 2px; padding: 0 16px; }
/* Document rows (§16). Every colour is tokenized with the historical cool-grey
   as the base fallback, so themes without an override (property-finder,
   backoffice) keep the old look while Heale gets the warm palette + the
   green "received" tint. */
.mf-doc-row { display: flex; align-items: center; gap: 10px; padding: 12px 20px; border: none; border-bottom: 1px solid var(--mf-doc-row-border, #e5e7eb); border-radius: 0; background: var(--mf-doc-row-bg, #fff); }
.mf-doc-col .mf-doc-row:last-child { border-bottom: none; }
.ins-claim-doc-body .mf-doc-col .mf-doc-row:last-child { border-bottom: 1px solid var(--mf-doc-row-border, #e5e7eb); }
.mf-doc-row--filled { background: var(--mf-doc-row-filled-bg, #f7fcf8); }
.mf-doc-icon { width: 34px; height: 34px; border-radius: 8px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; background: var(--mf-doc-icon-bg, #eef2f8); color: var(--mf-doc-icon-color, #64748b); }
.mf-doc-row--filled .mf-doc-icon { background: var(--mf-doc-icon-filled-bg, #eef6ee); color: var(--mf-doc-icon-filled-color, var(--brand-success, #2f6b43)); }
.mf-doc-info { flex: 1; min-width: 0; }
.mf-doc-name { font-size: 13px; font-weight: 600; color: var(--mf-doc-name-color, var(--brand-primary, #0a3373)); margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mf-doc-sub { font-size: 12px; color: var(--mf-doc-sub-color, #94a3b8); margin: 2px 0 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mf-doc-row--filled .mf-doc-sub { color: var(--mf-doc-icon-filled-color, var(--brand-success, #2f6b43)); }
.mf-doc-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.mf-doc-status { flex-shrink: 0; }
/* "Requested 27 Jul 2026" beside a document row's request-status badge — Charte §3
   metadata (11px ink-400). Muted and non-wrapping: it qualifies the badge, it is
   never the row's own headline. */
.mf-doc-requested-at { flex-shrink: 0; font-size: 11px; font-weight: 500; white-space: nowrap; color: var(--mf-doc-requested-at-color, var(--ins-pol-subtext-color, #6b7280)); }
.mf-doc-btn { display: inline-flex; align-items: center; gap: 5px; padding: 6px 10px; border-radius: var(--mf-doc-btn-radius, 7px); border: 1px solid var(--mf-doc-btn-border, #d2d9e5); background: var(--mf-doc-btn-bg, #fff); color: var(--mf-doc-btn-color, #475569); font-size: 12px; font-weight: 600; cursor: pointer; text-decoration: none; }
.mf-doc-btn:hover { background: var(--mf-doc-btn-bg-hover, #f8fafc); }
.mf-doc-btn:disabled { opacity: .5; cursor: not-allowed; }
/* Non-interactive stand-in for an unavailable action (e.g. a document with no
   signed URL yet) — same 45%-ish dimming as `:disabled`, applied to a <span>
   that can't carry the pseudo-class. */
.mf-doc-btn--disabled { opacity: .5; cursor: not-allowed; }
.mf-doc-btn--primary { border-color: var(--brand-secondary, #4374f3); color: var(--brand-secondary, #4374f3); }
.mf-doc-btn--primary:hover { background: color-mix(in srgb, var(--brand-secondary, #4374f3) 8%, white); }
/* Row-scale REJECTION (§6): white fill with a rust-600 label AND border — the
   same pairing `.tm-review-btn--reject` uses for the full-size decision button,
   for a per-row reject inside a document list (policy-onboarding review). */
.mf-doc-btn--danger { border-color: var(--dash-status-rejected-color, #dc2626); color: var(--dash-status-rejected-color, #dc2626); }
.mf-doc-btn--danger:hover { background: color-mix(in srgb, var(--dash-status-rejected-color, #dc2626) 8%, white); }
.mf-doc-iconbtn { width: 30px; height: 30px; padding: 0; justify-content: center; }
/* Non-interactive icon slot (a document with no signed URL yet) — §6's 45%
   disabled treatment, never a grey colour swap. Replaces an inline
   `style={{ opacity, cursor }}` in `<DocumentTypeGrid>`. */
.mf-doc-iconbtn--disabled { opacity: .45; cursor: not-allowed; }
.mf-doc-iconbtn--danger:hover {
  background: color-mix(in srgb, var(--dash-status-rejected-color, #dc2626) 10%, white);
  color: var(--dash-status-rejected-color, #dc2626);
  border-color: color-mix(in srgb, var(--dash-status-rejected-color, #dc2626) 35%, white);
}
.mf-doc-iconbtn--ocr { color: var(--brand-secondary, #4374f3); border-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 28%, white); }
.mf-doc-iconbtn--ocr:hover { background: color-mix(in srgb, var(--brand-secondary, #4374f3) 8%, white); }
.mf-doc-progress { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--brand-secondary, #4374f3); }
/* Compact inline <select> inside a doc row — variant picker (e.g. medical app form type) and
   the "Use template…" picker. Styled to match `.mf-doc-btn`'s pill look (same border/font/hover)
   so it reads as an intentional row control, not a bare browser <select>.
   DELIBERATELY on `--mf-doc-btn-*` rather than `--field-*`: "match the button next
   to me" is this control's whole spec, and the two sit side by side in the same
   row — pointing it at the field contract would give it the field's fill while its
   twin kept the button's, which is the exact mismatch the contract exists to stop.
   Its FOCUS is the contract's, because focus is a system-wide signal. */
.mf-doc-select {
  height: 30px;
  padding: 0 10px;
  border-radius: var(--mf-doc-btn-radius, 7px);
  border: 1px solid var(--mf-doc-btn-border, #d2d9e5);
  background: var(--mf-doc-btn-bg, #fff);
  color: var(--mf-doc-btn-color, #475569);
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: border-color .12s ease, background-color .12s ease;
}
.mf-doc-select:hover { background: var(--mf-doc-btn-bg-hover, #f8fafc); }
.mf-doc-select:focus-visible { outline: none; border-color: var(--field-focus-border, var(--brand-secondary, #4374f3)); }
.mf-doc-select:disabled { opacity: .5; cursor: not-allowed; }
/* Inline variant picker — sits to the right of `.mf-doc-name` on the same line (via
   `.mf-doc-name-row`) instead of stacked full-width below it. */
.mf-doc-name-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.mf-doc-select--variant { height: 26px; max-width: 220px; }

/* Drag-and-drop target highlight + upload progress bar */
.mf-doc-row--drag { border-color: var(--brand-secondary, #4374f3); border-style: dashed; background: color-mix(in srgb, var(--brand-secondary, #4374f3) 6%, white); }
.mf-doc-bar { display: block; height: 5px; margin-top: 6px; border-radius: 999px; background: var(--mf-doc-bar-track, #e5edf9); overflow: hidden; }
.mf-doc-bar-fill { display: block; height: 100%; border-radius: 999px; background: var(--brand-secondary, #4374f3); transition: width .15s ease; }
.mf-doc-sub--reading { color: var(--brand-secondary, #4374f3) !important; display: inline-flex; align-items: center; gap: 5px; }
.mf-spin-soft { animation: mf-pulse 1.1s ease-in-out infinite; }

/* §16 emphasis panel — a required identity document that carries extra context
   (currently the UAE Golden Visa work permit, driven by the strategy `work_permit`
   doc key). Tinted surface + a 3px accent LEFT rule in the brand accent, matching
   the Charte emphasis language; the row inside is a plain `.mf-doc-row` dropzone,
   so it keeps the identical upload/capture/preview/remove affordances. All colours
   tokenized off the brand accent with the historical coral as the base fallback. */
.mf-doc-highlight {
  /* No horizontal margin: this panel is a direct flex child of `.dash-body`
     (same as the `.hr-dash-recent-card` siblings above/below it), so it must
     span the full column width to line up flush with them. Vertical spacing
     comes from `.dash-body`'s own `gap`, same as every other sibling card. */
  margin: 0;
  border-radius: 12px;
  border-left: 3px solid var(--mf-doc-highlight-accent, var(--brand-accent, #ff6733));
  background: var(--mf-doc-highlight-bg, color-mix(in srgb, var(--brand-accent, #ff6733) 8%, transparent));
  overflow: hidden;
}
.mf-doc-highlight-head { display: flex; align-items: flex-start; gap: 10px; padding: 12px 16px 2px; }
.mf-doc-highlight-icon {
  flex-shrink: 0; width: 30px; height: 30px; border-radius: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--mf-doc-highlight-accent, var(--brand-accent, #ff6733));
  background: color-mix(in srgb, var(--mf-doc-highlight-accent, var(--brand-accent, #ff6733)) 16%, transparent);
}
.mf-doc-highlight-text { flex: 1; min-width: 0; }
.mf-doc-highlight-title { margin: 0; font-size: 13.5px; font-weight: 700; color: var(--brand-primary, #0a3373); }
.mf-doc-highlight-note { margin: 2px 0 0; font-size: 12px; line-height: 1.5; color: var(--mf-doc-sub-color, #64748b); }
.mf-doc-required {
  flex-shrink: 0; display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
  font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--mf-doc-highlight-accent, var(--brand-accent, #ff6733));
  background: color-mix(in srgb, var(--mf-doc-highlight-accent, var(--brand-accent, #ff6733)) 14%, transparent);
}
/* The dropzone row sits on the panel surface: drop its own fill + divider so the
   panel tint carries through (the filled/green icon still conveys upload state).
   A subtle top divider (same token `.mf-doc-row` itself uses for dividers,
   at reduced opacity) separates the row from the title block above it. */
.mf-doc-highlight .mf-doc-row,
.mf-doc-highlight .mf-doc-row--filled { background: transparent; border-bottom: none; }
.mf-doc-highlight .mf-doc-row { margin-top: 8px; border-top: 1px solid color-mix(in srgb, var(--mf-doc-row-border, #e5e7eb) 60%, transparent); }

/* Shared plain-text status lines for registered Task Manager action
   components (loading / defensive-error messages) — replaces what used to
   be ad hoc inline styles duplicated across several upload/review controls. */
.task-action-note {
  margin: 0;
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #6b7280);
}

.task-action-error {
  margin: 0;
  font-size: 13px;
  color: var(--dash-status-rejected-color, #dc2626);
}

/* <TaskUploadDropzone> (`@/components/task-manager/task-upload-dropzone`) —
   the dashed-border "Click to upload or drag and drop" zone + a required-
   document status row per doc type, used by the upload-flavoured registered
   Task Manager action components (identity/insurer document upload, proof of
   payment, onboarding documents). Deliberately separate from `.mf-doc-*`
   (`<MemberDocumentsCard>`'s own look), which stays untouched for the
   HR/Employee member-editor and employee self-service. */
.tu-card { display: flex; flex-direction: column; }

/* Resting state matches Charte §16 exactly: cream-50 fill + a solid (not
   dashed) blue-400 border, rather than the old neutral dashed-grey/white
   look — `--tu-dropzone-border`/`--tu-dropzone-bg` fall back to the old
   values for any theme with no override. */
.tu-dropzone {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 4px;
  margin: 18px 20px 4px;
  padding: 22px 20px;
  border: 1.4px solid var(--tu-dropzone-border, var(--hr-dash-recent-card-border, #d2d9e5));
  border-radius: 12px;
  background-color: var(--tu-dropzone-bg, transparent);
  cursor: pointer;
  transition: border-color .12s ease, background-color .12s ease;
}

.tu-dropzone:hover,
.tu-dropzone--active {
  border-color: var(--brand-secondary, #4374f3);
  background: color-mix(in srgb, var(--brand-secondary, #4374f3) 5%, white);
}

.tu-dropzone--disabled {
  cursor: not-allowed;
  opacity: .6;
}

/* 38px tinted circle behind the cloud icon (§16) — old themes get a
   transparent circle (same as the former plain, uncircled icon). */
.tu-dropzone-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background-color: var(--tu-dropzone-icon-bg, transparent);
  color: var(--brand-secondary, #4374f3);
  margin-bottom: 6px;
}

.tu-dropzone-text { margin: 0; font-size: 13.5px; }
.tu-dropzone-cta { font-weight: 700; color: var(--brand-secondary, #4374f3); }
.tu-dropzone-muted { color: var(--tu-muted-color, #7d93b3); }
.tu-dropzone-hint { margin: 2px 0 0; font-size: 11.5px; color: var(--tu-muted-color, #94a3b8); }

.tu-doc-list { display: flex; flex-direction: column; padding-bottom: 4px; }
/* Receipt slot — inset so the shared `<TaskSuccessBanner>` sits inside the card
   like a panel section rather than edge-to-edge against the doc rows. */
.tu-receipt { padding: 4px 20px 16px; }
.tu-doc-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 20px;
  border-bottom: 1px solid var(--tu-doc-row-border, #e5e7eb);
}
.tu-doc-list .tu-doc-row:last-child { border-bottom: none; }
.tu-doc-row--filled { background: #f7fcf8; }
.tu-doc-row--drag { border-color: var(--brand-secondary, #4374f3); background: color-mix(in srgb, var(--brand-secondary, #4374f3) 6%, white); }
.tu-doc-row[role='button'] { cursor: pointer; }
.tu-doc-row[role='button']:hover { background: var(--hr-dash-recent-row-hover-bg, #f8fafc); }
.tu-doc-row-icon { width: 34px; height: 34px; border-radius: 8px; flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center; background: #eef2f8; color: #64748b; }
/* Uploaded-row icon tint — reuses the same green-500 token the "Uploaded"
   pill (`.task-status--done`) already renders with, instead of the unmapped
   `--brand-success` token. */
.tu-doc-row--filled .tu-doc-row-icon {
  background: color-mix(in srgb, var(--dash-status-approved-color, #059669) 14%, white);
  color: var(--dash-status-approved-color, #059669);
}
.tu-doc-row-info { flex: 1; min-width: 0; }
.tu-doc-row-name { font-size: 13px; font-weight: 600; color: var(--brand-primary, #0a3373); margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tu-doc-row-sub { font-size: 12px; color: var(--tu-muted-color, #94a3b8); margin: 2px 0 0; }
.tu-doc-row-actions { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
.tu-doc-row-uploading { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--brand-secondary, #4374f3); flex-shrink: 0; }
@keyframes mf-pulse { 0%, 100% { opacity: .35; } 50% { opacity: 1; } }

/* ===========================================================================
   Policy Onboarding (`.pon-*`) — the KYC/VAT/MOA checklist screens:
   `/hr/policies/[id]/onboarding` (upload) and
   `/insurer/portfolio/policies/[policyId]/onboarding` (review).
   Only the bits the shared `.hr-dash-recent-*` / `.mf-doc-*` / `.tm-review-*`
   families don't already cover; every colour reads a consumed token.
   =========================================================================== */

/* Card head carrying a long "N of 3 uploaded · still needed: …" summary next to
   a stage pill. WRAPS by design: this is the exact shape that silently clipped
   its own data on the policy-create screen (a `flex-wrap: nowrap` head whose
   shrinkable child had `overflow: hidden`, where `text-overflow` is inert on a
   flex child, so values were destroyed mid-glyph with no ellipsis). Hence
   `flex-wrap` + `row-gap` on the head, `min-width: 0` + a flex BASIS on the text
   side so it can shrink and then wrap, and `flex-shrink: 0` on the pill so it
   never squeezes the summary instead of moving to the next line. */
.pon-head { flex-wrap: wrap; row-gap: 10px; }
.pon-head-main { flex: 1 1 260px; min-width: 0; }
.pon-head-main .hr-dash-recent-subtitle { white-space: normal; }
.pon-head-badge { flex-shrink: 0; }

/* Explanation + secondary note inside `.ins-pol-card-body`. Body text is §3 body
   size and reads the SAME token as `.tm-review-instruction` — the pre-existing
   "here is what to do" sentence in the task panel, which is exactly this role —
   so the two never drift; the note is metadata-toned. Both wrap freely: nothing
   on these screens is allowed to be a single clipped line. */
.pon-body-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--modal-total-color, #374151);
}

.pon-body-note {
  margin: 10px 0 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.pon-body-text + .task-success-banner,
.pon-body-note + .task-success-banner { margin-top: 12px; }

.pon-inline-link {
  color: var(--brand-secondary, #4374f3);
  font-weight: 600;
  text-decoration: none;
}
.pon-inline-link:hover { text-decoration: underline; }

/* Per-row reject reveal — the row itself must wrap so the textarea drops onto
   its own full-width line under the document instead of squeezing the label. */
.pon-row--rejecting { flex-wrap: wrap; }

.pon-reject-reveal {
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
}

.pon-reject-hint {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.pon-reject-actions { display: flex; justify-content: flex-end; }
/* `.tm-review-btn` is `flex: 1 1 0` for the paired decision row it was written
   for; on its own here it must size to its label, not stretch. */
.pon-reject-actions .tm-review-btn,
.pon-review-footer .tm-review-btn { flex: 0 0 auto; min-width: 190px; }

/* Footer of the insurer review list: the truthful "why can't I approve yet"
   sentence on the left, the single green §6 approval on the right. */
.pon-review-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 16px;
  padding: 16px 20px 18px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.pon-review-note {
  flex: 1 1 260px;
  min-width: 0;
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

@media (max-width: 768px) {
  /* One column at phone widths: a 190px pill plus a sentence never both fit, and
     a stretched full-width button keeps the 44px tap target. */
  .pon-review-footer { flex-direction: column; align-items: stretch; }
  .pon-review-footer .tm-review-btn,
  .pon-reject-actions .tm-review-btn { width: 100%; min-width: 0; }
  .pon-reject-actions { justify-content: stretch; }

  /* A review row carries an Uploaded pill + View + Reject (~200px of controls).
     At 375px that leaves the label under 90px, i.e. permanently ellipsised — so
     the controls drop to their own full-width line instead of squeezing the one
     thing the reader needs to identify the document. */
  .pon-doc-list .mf-doc-row { flex-wrap: wrap; row-gap: 10px; }
  .pon-doc-list .mf-doc-info { flex-basis: calc(100% - 44px); }
  .pon-doc-list .mf-doc-actions { width: 100%; justify-content: flex-end; }
}

/* Task Manager — persistent comment composer at the bottom of <TaskDetailModal> */
.task-comment-composer { display: flex; flex-direction: column; gap: 10px; }
/* A form textarea (§15's composer) — on the contract, resting height included, so
   it agrees with `.modal-textarea` instead of choosing its own 72px. */
.task-comment-composer-textarea {
  width: 100%;
  min-height: var(--field-textarea-min-height, 72px);
  resize: vertical;
  padding: var(--field-padding-y, 10px) var(--field-padding-x, 14px);
  border: 1px solid var(--field-border, #d2d9e5);
  border-radius: var(--field-radius, 10px);
  background: var(--field-bg, #fff);
  font-size: var(--field-font-size, 13.5px);
  color: var(--field-color, #374151);
  font-family: inherit;
  outline: none;
}
.task-comment-composer-textarea::placeholder { color: var(--field-placeholder-color, #9ca3af); }
.task-comment-composer-textarea:focus { border-color: var(--field-focus-border, var(--brand-secondary, #4374f3)); }
.task-comment-composer-textarea:disabled {
  background: var(--field-disabled-bg, #f8fafc);
  color: var(--field-disabled-color, var(--field-color, #374151));
  opacity: var(--field-disabled-opacity, 1);
  cursor: not-allowed;
}
.task-comment-composer-actions { display: flex; justify-content: flex-end; }
.task-comment-composer-actions .hr-dash-recent-btn-primary { min-height: 44px; }
.task-comment-composer-actions .hr-dash-recent-btn-primary:disabled {
  background-color: #d2d9e5;
  color: #9ca3af;
  cursor: not-allowed;
  opacity: 1;
}

/* OCR autofill confirm modal — document preview on the left, read fields on the right */
.mf-ocr-modal { width: 720px; max-width: calc(100vw - 32px); padding: 0; overflow: hidden; }
.mf-ocr-grid { display: grid; grid-template-columns: 280px 1fr; min-height: 0; }
.mf-ocr-preview {
  position: relative;
  background: #0f172a; display: flex; align-items: center; justify-content: center;
  padding: 14px; max-height: 74vh; overflow: hidden;
}
.mf-ocr-zoom {
  position: absolute; top: 10px; right: 10px; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 8px; cursor: pointer;
  background: rgba(255,255,255,.16); color: #fff; backdrop-filter: blur(4px);
  transition: background .12s ease;
}
.mf-ocr-zoom:hover { background: rgba(255,255,255,.3); }
/* Fullscreen enlarge of the source document */
.mf-ocr-lightbox {
  position: fixed; inset: 0; z-index: 1200; background: rgba(8, 12, 22, .9);
  display: flex; align-items: center; justify-content: center; padding: 32px;
  animation: toast-in .15s ease-out;
}
.mf-ocr-lightbox-img { max-width: 100%; max-height: 100%; object-fit: contain; border-radius: 8px; box-shadow: 0 16px 48px rgba(0,0,0,.5); }
.mf-ocr-lightbox-frame { width: min(1100px, 100%); height: 100%; border: 0; border-radius: 8px; background: #fff; }
.mf-ocr-lightbox-close {
  position: absolute; top: 18px; right: 18px; width: 40px; height: 40px;
  display: inline-flex; align-items: center; justify-content: center;
  border: 0; border-radius: 10px; cursor: pointer; color: #fff; background: rgba(255,255,255,.14);
}
.mf-ocr-lightbox-close:hover { background: rgba(255,255,255,.28); }
.mf-ocr-preview-img { max-width: 100%; max-height: calc(74vh - 28px); border-radius: 8px; object-fit: contain; box-shadow: 0 8px 28px rgba(0,0,0,.35); }
.mf-ocr-preview-frame { width: 100%; height: calc(74vh - 28px); border: 0; border-radius: 8px; background: #fff; }
.mf-ocr-preview-empty { display: flex; flex-direction: column; align-items: center; gap: 8px; color: #64748b; font-size: 12px; font-weight: 600; }
.mf-ocr-pane { display: flex; flex-direction: column; padding: 18px 20px; min-width: 0; max-height: 74vh; }
.mf-ocr-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.mf-ocr-head-text { min-width: 0; }
.mf-ocr-head .mf-modal-title { margin: 0; line-height: 1.25; }
.mf-ocr-head .mf-modal-text { margin: 1px 0 0; }
.mf-ocr-badge {
  display: inline-flex; align-items: center; gap: 4px; padding: 4px 9px; flex-shrink: 0;
  border-radius: 999px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
  color: var(--brand-secondary, #4374f3); background: color-mix(in srgb, var(--brand-secondary, #4374f3) 10%, white);
}
.mf-ocr-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.mf-ocr-count { font-size: 12px; font-weight: 600; color: #64748b; }
.mf-ocr-toggleall { font-size: 12px; font-weight: 600; color: var(--brand-secondary, #4374f3); background: none; border: 0; cursor: pointer; padding: 2px 4px; }
.mf-ocr-toggleall:hover { text-decoration: underline; }
.mf-ocr-list { display: flex; flex-direction: column; gap: 5px; flex: 1; min-height: 0; overflow: auto; margin-bottom: 14px; }
/* Compact single-line row: checkbox · muted caption · prominent value on the right */
.mf-ocr-row {
  display: flex; align-items: center; gap: 10px; padding: 6px 11px; border: 1px solid #e8ecf3;
  border-radius: 8px; cursor: pointer; background: #fbfcfe;
}
.mf-ocr-row.is-on { border-color: #cfe0fb; background: #f5f9ff; }
.mf-ocr-row input { accent-color: var(--brand-secondary, #4374f3); flex-shrink: 0; }
.mf-ocr-field { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1; }
.mf-ocr-label { font-size: 10px; font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: #9aa6b8; flex-shrink: 0; }
.mf-ocr-vals { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; margin-left: auto; min-width: 0; }
.mf-ocr-value { font-size: 14px; font-weight: 700; color: var(--brand-primary, #0a3373); word-break: break-word; text-align: right; line-height: 1.2; }
.mf-ocr-current { font-size: 11px; font-weight: 500; color: #b08a2e; text-align: right; }
@media (max-width: 560px) {
  .mf-ocr-grid { grid-template-columns: 1fr; }
  .mf-ocr-preview { max-height: 30vh; }
  .mf-ocr-preview-img { max-height: calc(30vh - 28px); }
  .mf-ocr-preview-frame { height: calc(30vh - 28px); }
}

/* =====================================================================
   Toasts (<ToastProvider> / useToast)
   ===================================================================== */
.toast-viewport {
  position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); z-index: 1000;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  width: auto; max-width: calc(100vw - 32px); pointer-events: none;
}
.toast {
  pointer-events: auto;
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px 12px 14px; border-radius: 9999px; background: #fff;
  border: 1px solid #e5e7eb;
  box-shadow: 0 12px 36px rgba(15, 23, 42, .16);
  animation: toast-in .2s ease-out;
  min-width: 260px; max-width: min(520px, calc(100vw - 32px));
}
@keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
/* Green/red accent: tinted icon chip + colored border ring per variant. */
.toast--success { border-color: rgba(50, 161, 94, .35); }
.toast--error { border-color: rgba(220, 38, 38, .35); }
.toast--info { border-color: rgba(67, 116, 243, .35); }
.toast-icon {
  flex-shrink: 0; width: 28px; height: 28px; border-radius: 9999px;
  display: inline-flex; align-items: center; justify-content: center;
}
.toast--success .toast-icon { color: var(--brand-success, #32a15e); background: rgba(50, 161, 94, .12); }
.toast--error .toast-icon { color: var(--brand-error, #dc2626); background: rgba(220, 38, 38, .12); }
.toast--info .toast-icon { color: var(--brand-secondary, #4374f3); background: rgba(67, 116, 243, .12); }
.toast-body { flex: 1; min-width: 0; }
.toast-title { margin: 0; font-size: 13.5px; font-weight: 700; color: var(--brand-primary, #0a3373); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.toast-desc { margin: 2px 0 0; font-size: 12.5px; color: #64748b; line-height: 1.45; }
.toast-close { flex-shrink: 0; width: 24px; height: 24px; border: none; background: transparent; color: #94a3b8; cursor: pointer; border-radius: 9999px; display: inline-flex; align-items: center; justify-content: center; }
.toast-close:hover { background: #f1f5f9; color: #475569; }

/* =====================================================================
   Member form — mobile (<= 768px)
   ===================================================================== */
@media (max-width: 768px) {
  /* Body stacks: the member rail becomes a horizontal scroller above content. */
  .mf-body { flex-direction: column; }
  .mf-rail {
    width: auto; flex-direction: row; align-items: center;
    overflow-x: auto; overflow-y: hidden;
    border-right: none; border-bottom: 1px solid #e5e7eb;
    padding: 10px 12px; gap: 8px;
  }
  .mf-rail-title { display: none; }
  .mf-member { flex-shrink: 0; }
  .mf-member-btn { width: auto; }
  .mf-member-name { max-width: 130px; }
  .mf-add-dep { flex-shrink: 0; margin-top: 0; white-space: nowrap; padding: 9px 12px; }
  .mf-hint { display: none; }

  /* Tabs + cards align to a tighter gutter; tabs scroll horizontally on mobile. */
  .mf-tabs { padding: 12px 16px 0; overflow-x: auto; flex-wrap: nowrap; scrollbar-width: none; }
  .mf-tabs::-webkit-scrollbar { display: none; }
  .mf-seg { flex-shrink: 0; flex-wrap: nowrap; }
  .mf-seg-btn { white-space: nowrap; }
  /* The tabs row already scrolls horizontally here, so the action buttons ride
     that scroll at full size rather than wrapping into a squashed second line. */
  .mf-tabs-actions { flex-shrink: 0; flex-wrap: nowrap; }
  .mf-root .dash-body { padding-left: 16px; padding-right: 16px; }

  /* Header compaction. */
  .mf-root .page-header--top { padding-left: 16px; padding-right: 16px; }
  .mf-root .page-header-title { font-size: 18px; }

  /* Document columns are already auto-fit; tighten the gutter. */
  .mf-docs { padding: 0; gap: 14px; }

  /* Merged Policy card: a 168px label column plus a value (or a date field) doesn't
     fit 375px — stack every detail row instead of clipping either side. */
  .mf-coverage-row { flex-direction: column; align-items: flex-start; gap: 3px; }
  .mf-coverage-label { width: 100%; }
  .mf-coverage-row--field .mf-datefield { max-width: 100%; width: 100%; }

  /* Preview becomes a full-screen overlay instead of a 440px side panel. */
  .mf-preview { position: fixed; inset: 0; width: 100%; height: 100%; z-index: 60; border-left: none; }

  /* Add-member wizard: 16px gutter (matching `.mf-root .dash-body` above), and the
     step rail scrolls horizontally rather than squeezing. `.mf-root` sets no
     `--mfw-gutter`, so the editor/employee footers take the 16px fallback — the
     same inset their body already uses. */
  .mfw-root { --mfw-gutter: 16px; }

  /* Footer bar stacks: the status line takes its own row so the actions get the
     full width and `.wizard-actions`' mobile `flex: 1` can stretch them evenly.
     Same treatment as the shipped `.onb-footer`. Two compact rows (~96px), so the
     bar still costs well under a sixth of a 667px-tall viewport. */
  .mfw-footer,
  .mf-footer { padding: 12px var(--mfw-gutter, 16px); gap: 10px; }
  .mfw-footer-info,
  .mf-footer-info { width: 100%; }
  .mfw-footer .wizard-actions,
  .mf-footer .wizard-actions { width: 100%; }
  .mfw-steps { padding: 12px var(--mfw-gutter, 16px) 10px; overflow-x: auto; scrollbar-width: none; }
  .mfw-steps::-webkit-scrollbar { display: none; }
  .mfw-step-item { flex: 0 0 auto; }
  .mfw-step-connector { flex: 0 0 24px; min-width: 24px; margin: 0 8px; }
  /* 44px minimum tap target, without growing the 32px visual dot. */
  .mfw-step-dot { position: relative; }
  .mfw-step-dot::after { content: ''; position: absolute; inset: -6px; }

  /* Dependants step stacks: the rail becomes the same horizontal scroller as `.mf-rail`. */
  .mfw-dep-split { flex-direction: column; }
  .mfw-dep-split .mf-rail { flex: none; align-self: stretch; }

  /* Toasts stay centered above the bottom on phones. */
  .toast-viewport { left: 50%; transform: translateX(-50%); bottom: 16px; width: auto; max-width: calc(100vw - 24px); }

  /* Phone splits to its own row on phones. */
  .mf-phone { flex-wrap: wrap; }
  .mf-phone-code { width: 100%; }

  /* ── Global: form input rows always stack vertically on mobile ── */

  /* The primary form grid used across HR, employee, and insurer pages */
  .ins-pol-form-grid {
    grid-template-columns: 1fr !important;
    gap: 14px !important;
  }

  /* Generic helper classes for any new form layouts.
     The two backoffice settings rows are named here rather than renamed to
     `.form-row`, because their own class family is what carries the ops-console
     look; only the mobile stacking is shared. */
  .form-row,
  .input-row,
  .field-row,
  .claims-settings-row,
  .export-settings-row {
    flex-direction: column !important;
    align-items: stretch !important;
  }

  .form-row > *,
  .input-row > *,
  .field-row > *,
  .claims-settings-row > *,
  .export-settings-row > * {
    width: 100% !important;
    min-width: 0 !important;
    max-width: 100% !important;
  }

  /* AddTenantUserModal's right-hand roles column: vertical rule → top border
     once `.form-row` stacks it under the email field. */
  .tu-modal-roles-col {
    padding-left: 0 !important;
    border-left: none !important;
    padding-top: 16px !important;
    border-top: 1px solid var(--tu-modal-roles-col-border, #e5e7eb);
    margin-top: 4px;
  }

  .form-grid,
  .input-grid,
  .field-grid,
  /* The settings/company-profile two-column row is the same shape and used to
     stack only at 560px, leaving a 560–768px band with side-by-side fields. */
  .settings-row {
    grid-template-columns: 1fr !important;
  }

  /* Member-form label+input pairs stack */
  .mf-field-row {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }

  .mf-field-row label,
  .mf-field-row .mf-label {
    width: 100%;
    min-width: 0;
  }

  .mf-field-row input,
  .mf-field-row select,
  .mf-field-row textarea,
  .mf-field-row .mf-combo,
  .mf-field-row .mf-date {
    width: 100%;
    min-width: 0;
  }
}

/* ── Custom inputs: combobox / date field / phone (member form) ──────────── */
.mf-combo { position: relative; width: 100%; }

/* Wrapper for a <Combobox> that sits INLINE in a flex row beside labels and
   buttons (the request-overview copy pair, a missing-information fix, a benefit
   dropdown). `.mf-combo` is `width: 100%`, which as a flex item resolves
   against the whole row — dropped straight in it either fills the row or
   collapses to min-content depending on what else shares the line. This gives
   the control its own non-shrinking track for `.mf-combo` to fill; the width
   itself comes from the local class alongside it (e.g. `.qmi-select`). */
.combo-inline { flex: 0 0 auto; }

.mf-combo-trigger {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  cursor: pointer; text-align: left;
}
.mf-combo-trigger.is-disabled { cursor: not-allowed; }
/* NO hover rule here, on purpose — see `.modal-select-trigger:hover`. A
   `.mf-combo-trigger:hover` rule would need `:not(.is-disabled)` to clear the
   shared locked rule, which puts it at (0,3,0) — heavier than the single-class
   `className` a caller passes to <Combobox> to reskin the trigger
   (`.boc-combo-trigger` in `backoffice/style.css` REPLACES the default
   `ins-pol-input`). It would therefore repaint that trigger's fill on hover from
   the base stylesheet, over the head of the brand that skinned it. Giving the
   combobox a hover affordance means giving each such skin a matching hover value
   first; until then the trigger has none. */
.mf-combo-trigger:focus-visible { border-color: var(--field-focus-border, var(--brand-secondary, #4374f3)); }
.mf-combo-value { display: flex; align-items: center; gap: 8px; min-width: 0; overflow: hidden; white-space: nowrap; }
.mf-combo-value-text { overflow: hidden; text-overflow: ellipsis; }
.mf-combo-placeholder { color: var(--field-placeholder-color, #9ca3af); }
.mf-combo-caret { color: var(--field-placeholder-color, #9ca3af); flex-shrink: 0; }

/* Positioned via inline style (position: fixed; top/bottom/left/width) computed in JS
   from the trigger's getBoundingClientRect() — see combobox.tsx. It's portaled to
   document.body so it escapes any `.mf-scroll`-style ancestor overflow clipping;
   z-index only needs to clear in-page chrome (modals sit at 50, toasts/fullscreen
   overlays at 1000+). */
.mf-combo-panel {
  z-index: 1000;
  background: #fff; border: 1px solid var(--field-hairline, #e5e7eb);
  border-radius: 10px; box-shadow: 0 12px 28px rgba(16, 24, 40, 0.14);
  padding: 6px; display: flex; flex-direction: column; gap: 4px;
}
.mf-combo-search { position: relative; display: flex; align-items: center; padding: 2px 2px 6px; border-bottom: 1px solid #f1f5fe; }
.mf-combo-search-icon { position: absolute; left: 10px; color: #9ca3af; }
.mf-combo-search-input {
  width: 100%; height: 34px; border: 1px solid var(--field-hairline, #e5e7eb);
  border-radius: 8px; padding: 0 10px 0 30px; font-size: 13px; outline: none;
  color: var(--field-color, #374151);
}
.mf-combo-search-input:focus { border-color: var(--field-focus-border, var(--brand-secondary, #4374f3)); }
.mf-combo-list { max-height: 260px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.mf-combo-opt {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 8px 10px; border-radius: 7px; font-size: 13px; color: #374151;
  text-align: left; cursor: pointer; background: transparent; border: none;
}
.mf-combo-opt-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mf-combo-opt.is-active { background: #f1f5fe; }
.mf-combo-opt.is-selected { color: var(--brand-secondary, #4374f3); font-weight: 600; }
.mf-combo-opt-check { color: var(--brand-secondary, #4374f3); flex-shrink: 0; }
/* Greyed-out, unselectable option (e.g. a jurisdiction-incompatible category) — never
   hidden, per the "grey out, don't hide" rule. Overrides the hover/active highlight so a
   disabled row never LOOKS pickable; the reason surfaces via its native `title` tooltip. */
.mf-combo-opt--disabled { color: #9ca3af; cursor: not-allowed; }
.mf-combo-opt--disabled.is-active, .mf-combo-opt--disabled:hover { background: transparent; }
.mf-combo-empty { padding: 12px; text-align: center; color: #9ca3af; font-size: 13px; }
.mf-combo-group {
  padding: 8px 10px 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: #9ca3af;
}

.mf-datefield { position: relative; width: 100%; }
.mf-datefield-text { padding-right: 38px; }
.mf-datefield-btn {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 6px; border: none; background: transparent;
  color: #6b7280; cursor: pointer;
}
.mf-datefield-btn:hover { background: #f1f5fe; color: var(--brand-secondary, #4374f3); }
.mf-datefield-btn:disabled { opacity: 0.5; cursor: not-allowed; }
.mf-datefield-native { position: absolute; right: 6px; bottom: 0; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* Phone: ONE control — dial picker embedded inside the input box. It is its own
   box rather than an `.ins-pol-input`, so it spells the contract out; the 38/8
   fallbacks are its pre-contract metrics. */
.mf-phone {
  display: flex; align-items: stretch; width: 100%; height: var(--field-height, 38px);
  border: 1px solid var(--field-border, #e5e7eb); border-radius: var(--field-radius, 8px);
  background: var(--field-bg, #fff);
}
.mf-phone:focus-within { border-color: var(--field-focus-border, var(--brand-secondary, #4374f3)); }
.mf-phone .mf-combo { width: auto; flex-shrink: 0; }
.mf-phone-codebtn {
  height: 100%; width: auto; border: none; background: transparent;
  border-radius: 8px 0 0 8px; padding: 0 6px 0 10px; gap: 6px;
  font-size: var(--field-font-size, 13px); color: var(--field-color, #374151);
}
.mf-phone-sep { width: 1px; align-self: center; height: 20px; background: var(--field-hairline, #e5e7eb); flex-shrink: 0; }
.mf-phone-number {
  flex: 1; min-width: 0; height: 100%; border: none; background: transparent;
  padding: 0 12px; font-size: var(--field-font-size, 13px); color: var(--field-color, #374151); outline: none;
}
.mf-phone-number::placeholder { color: var(--field-placeholder-color, #9ca3af); }
/* PhoneField's dial-code trigger is narrow (just the code button); the panel needs to
   be wider to fit country names. Since the panel is portaled (no longer a descendant
   of .mf-phone), width comes from the `panelMinWidth` prop, not an ancestor selector. */
.mf-phone-flag { font-size: 16px; line-height: 1; }

/* Clear (×) action inside a clearable combobox trigger. */
.mf-combo-clear {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 5px; flex-shrink: 0;
  color: #9ca3af; cursor: pointer;
}
.mf-combo-clear:hover { background: #f1f5fe; color: var(--brand-secondary, #4374f3); }

/* Date roller popover — Year / Month / Day scroll columns (good for birth dates).
   Portaled to document.body (see date-field.tsx) so an ancestor's overflow:hidden
   (onboarding/member cards) can't clip it; position:fixed + left/top|bottom are set
   inline from the trigger's rect. z-index sits above cards, panels and the modal
   layer (matches .mf-combo-panel). */
.mf-roll {
  z-index: 1000;
  width: 248px; background: #fff; border: 1px solid var(--field-hairline, #e5e7eb);
  border-radius: 12px; box-shadow: 0 12px 28px rgba(16, 24, 40, 0.16); padding: 10px;
}
.mf-roll-cols { display: grid; grid-template-columns: 1fr 1fr 0.8fr; gap: 6px; }
.mf-roll-colwrap { display: flex; flex-direction: column; min-width: 0; }
.mf-roll-label { text-align: center; font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: #9ca3af; padding-bottom: 4px; }
.mf-roll-col {
  height: 104px; overflow-y: auto; scroll-snap-type: y proximity;
  display: flex; flex-direction: column; gap: 2px;
  border: 1px solid #f1f5fe; border-radius: 8px; padding: 4px;
}
.mf-roll-col::-webkit-scrollbar { width: 6px; }
.mf-roll-col::-webkit-scrollbar-thumb { background: #e5e7eb; border-radius: 3px; }
.mf-roll-item {
  padding: 7px 6px; text-align: center; border: none; background: transparent; cursor: pointer;
  border-radius: 7px; font-size: 13px; color: #6b7280; scroll-snap-align: center; flex-shrink: 0;
}
.mf-roll-item:hover:not(.is-sel) { background: #f1f5fe; }
.mf-roll-item.is-sel { background: var(--brand-secondary, #4374f3); color: #fff; font-weight: 600; }
.mf-roll-done {
  margin-top: 10px; width: 100%; height: 34px; border: none; border-radius: 8px;
  background: var(--brand-primary, #0a3373); color: #fff; font-size: 13px; font-weight: 600; cursor: pointer;
}
.mf-roll-done:hover { filter: brightness(1.08); }

/* Member-type dropdown lives full-width above the name row. */
.mf-type-field { margin-bottom: 14px; max-width: 220px; }
/* Small leading icon inside dropdown option rows. */
.mf-opt-icon { color: #6b7280; flex-shrink: 0; }
.mf-combo-opt.is-selected .mf-opt-icon { color: var(--brand-secondary, #4374f3); }

/* Wizard page shell — DashTopbar + a centered white card on a tinted body.
   Shared outer chrome for every claims wizard step layout (new claim,
   pre-approval, submitted confirmation; HR/Employee) via
   `<WizardPageLayout>` (src/components/ui/wizard-page-layout.tsx). */
.wizard-page {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}
.wizard-card-wrap {
  flex: 1;
  min-height: 0;
  overflow: auto;
  background: var(--dash-body-bg, #f8fafd);
  padding: 24px 28px;
}
.wizard-card {
  min-height: 0;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  max-width: 760px;
  align-self: flex-start;
  width: 100%;
}

/* Wizard action rows — right-aligned on desktop, centered on mobile */
.wizard-actions {
  display: flex;
  flex-direction: row;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 32px;
}
.wizard-actions button { min-width: 140px; justify-content: center; }
@media (max-width: 768px) {
  .wizard-actions { justify-content: center; }
  .wizard-actions button { flex: 1; max-width: 180px; }

  .wizard-card {
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    max-width: 100% !important;
  }
  .wizard-card-wrap {
    padding: 0 !important;
  }
}

/* Wizard step loading skeleton — renders INSIDE the wizard `layout.tsx`'s
   `.wizard-card` (Next nests `loading.tsx` inside the same-segment layout), so
   it mirrors only a step's INNER shape: a scrollable 640px column with the
   progress header + title + field-tile grids. No topbar/card here — the layout
   already provides them, so adding them would double the chrome. */
.wizard-step-skel {
  height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.wizard-step-skel-scroll {
  flex: 1;
  overflow-y: auto;
  padding-bottom: 100px;
}
.wizard-step-skel-inner {
  max-width: 640px;
  margin: 0 auto;
  padding: 24px 20px 0;
}
.wizard-step-skel-progress {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 32px;
}
.wizard-step-skel-bars {
  display: flex;
  gap: 6px;
  flex: 1;
}
.wizard-step-skel-bar {
  flex: 1;
  height: 4px;
  border-radius: 9999px;
  background: #e5e7eb;
}
.wizard-step-skel-bar--active {
  background: var(--brand-accent, #ff6733);
}
.wizard-step-skel-section {
  margin-bottom: 32px;
}
.wizard-step-skel-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  gap: 10px;
}
.wizard-step-skel-tile {
  border-radius: 10px;
  border: 1.5px solid #e5e7eb;
  padding: 14px 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Compact CTA — just the two buttons, right-aligned. It is a bare LAYOUT row: no
   background, border, shadow or padding of its own, because it now renders in the
   scope filter row's `rightSlot`, straight on the cream page body (§6: page
   actions sit to the right of the filter row). It used to be a standalone
   full-width white bar in the middle of the page. The same class is reused inside
   per-tab / per-segment empty states, where a surface would equally be wrong —
   the card already provides one. */
.emp-cta-bar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Employee claims/pre-approvals WELCOME state (member has no records at all) —
   the two-CTA description cards. The per-tab / per-segment inner empty states are
   the shared `<ListState>` (§17), not a local block. */
.emp-empty {
  padding: 48px 24px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 24px;
  text-align: center;
}
.emp-empty-title {
  font-size: 20px;
  font-weight: 700;
  color: var(--emp-empty-title-color, var(--brand-primary, #0a3373));
  margin: 0;
}
.emp-empty-desc {
  font-size: 14px;
  color: var(--emp-empty-desc-color, #6b7280);
  margin: 0;
  max-width: 400px;
  line-height: 1.6;
}
.emp-empty-options {
  display: flex;
  gap: 16px;
  width: 100%;
  max-width: 720px;
  align-items: stretch;
  justify-content: center;
}
.emp-empty-option {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: left;
  /* Inner cell inside a card — cream surface, no border (§8), like every other
     metadata cell. The old `1px solid #d2d9e5` was a retired hex AND a border. */
  border: 1px solid var(--emp-empty-option-border, #d2d9e5);
  border-radius: 12px;
  padding: 20px;
  background-color: var(--emp-empty-option-bg, #ffffff);
}
.emp-empty-option-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--emp-empty-option-title-color, #18181b);
  margin: 0;
}
.emp-empty-option-desc {
  font-size: 13px;
  color: var(--emp-empty-option-desc-color, #6b7280);
  line-height: 1.6;
  margin: 0;
}
.emp-empty-option-btn {
  margin-top: auto;
  align-self: flex-start;
}
@media (max-width: 768px) {
  .emp-empty-options {
    flex-direction: column;
  }
}

/* Amount + currency pair on the wizard's Step 1 — the amount input grows to
   fill the row while the currency <select> keeps a fixed width beside it. */
.wizard-amount-row {
  display: flex;
  gap: 10px;
  align-items: stretch;
}
/* Amount stays the dominant field (~60%); the currency combobox takes the
   rest (~40%) — wide enough for a "CODE — Name" label. */
.wizard-amount-row > input {
  flex: 3 1 0;
  min-width: 0;
}
.wizard-currency-field {
  flex: 2 1 0;
  min-width: 0;
}
/* Stretch the combobox (root + trigger) to the amount input's height so both
   align on one row. */
.wizard-currency-field .mf-combo,
.wizard-currency-field .mf-combo-trigger {
  height: 100%;
}

/* Hover target so a disabled wizard Continue button can still show its reason
   via `title` (disabled buttons don't fire hover in most browsers). Keeps the
   button as the effective flex child so `.wizard-actions` layout is unchanged. */
.wizard-btn-wrap {
  display: inline-flex;
}
@media (max-width: 768px) {
  .wizard-actions .wizard-btn-wrap {
    flex: 1;
    max-width: 180px;
  }
  .wizard-actions .wizard-btn-wrap > button {
    width: 100%;
    max-width: none;
  }
}

/* Backoffice insurer claims-settings form (filing window + multi-currency
   default selector). Dark ops-console palette, kept in the class system on
   purpose so this form has no inline styles.

   `.export-settings-*` (the exporter's daily-limit + row-cap form on the same
   "Modules" tab) is ALIASED onto these rules rather than given its own copy:
   the two forms are the same ops-console shape, and one rule set is what stops
   them drifting apart. Each keeps its own semantic name so a grep for either
   form lands on its own markup. The currency-list selectors below are
   claims-only and deliberately not aliased. */
.claims-settings-loading,
.export-settings-loading {
  font-size: 13px;
  color: #64748b;
  margin: 0;
}
.claims-settings-row,
.export-settings-row {
  display: flex;
  gap: 16px;
  margin-bottom: 14px;
}
.claims-settings-col,
.export-settings-col {
  flex: 1;
}
.claims-settings-label,
.export-settings-label {
  display: block;
  font-size: 11px;
  font-weight: 600;
  color: #64748b;
  letter-spacing: 0.06em;
  margin-bottom: 6px;
}
/* Backoffice-only, and deliberately NOT on the field contract: the backoffice is
   out of scope for the input consolidation (see the note in THE FIELD CONTRACT),
   so this keeps its dark literals. Phase 3's `settings-input` work owns the
   light-on-dark bug this file's sibling controls have. */
.claims-settings-input,
.export-settings-input {
  width: 100%;
  font-size: 14px;
  color: #e2e8f0;
  background-color: #0f1117;
  border: 1px solid #2d3748;
  border-radius: 6px;
  padding: 8px 10px;
  outline: none;
}
.claims-settings-error,
.export-settings-error {
  font-size: 12px;
  color: #f87171;
  margin: 6px 0 0;
}
.claims-settings-currency-list {
  margin-top: 8px;
  max-height: 200px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 1px solid #2d3748;
  border-radius: 6px;
  padding: 4px;
  background-color: #0f1117;
}
.claims-settings-empty {
  font-size: 13px;
  color: #64748b;
  margin: 0;
  padding: 8px;
}
.claims-settings-currency-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 8px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
  color: #64748b;
}
.claims-settings-currency-row--selected {
  color: #e2e8f0;
}
.claims-settings-help,
.export-settings-help {
  font-size: 12px;
  color: #64748b;
  margin: 0 0 16px;
  line-height: 1.5;
}
.claims-settings-actions,
.export-settings-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
.claims-settings-save,
.export-settings-save {
  font-size: 13px;
  font-weight: 600;
  color: #ffffff;
  background-color: #3b5bdb;
  border: none;
  border-radius: 6px;
  padding: 8px 16px;
  cursor: pointer;
}
.claims-settings-save:disabled,
.export-settings-save:disabled {
  cursor: default;
  opacity: 0.5;
}
.claims-settings-msg,
.export-settings-msg {
  font-size: 13px;
  color: #f87171;
}
.claims-settings-msg--ok,
.export-settings-msg--ok {
  color: #10b981;
}

/* The "Modules" tab's own chrome — the column of setting cards the module
   forms above sit inside. It belongs to the PAGE rather than to any one form,
   so it gets one page-level name used by every card instead of an aliased pair
   per module: the card shell is byte-identical for Claims, Exports and
   Notification channels, and one rule body is what stops them drifting. Same
   dark ops-console literals as the forms (see the note there — the backoffice
   is out of scope for the token work).

   The Notification channels card's body is the `.bo-nch-*` grid (in
   `public/themes/backoffice/style.css`), which adapts to this card's 480px cap
   from its own side — as a descendant of `.module-settings-card` — so nothing
   in this block bends for it. */
.module-settings-list {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.module-settings-card {
  max-width: 480px;
  background-color: #1e2433;
  border: 1px solid #2d3748;
  border-radius: 8px;
  padding: 24px;
}
.module-settings-card-title {
  margin: 0 0 16px;
  font-size: 14px;
  font-weight: 600;
  color: #e2e8f0;
}

/* =====================================================================
   Contact Center (thread list + detail) — WhatsApp-style two-pane LAYOUT
   and INTERACTION pattern for the rail CHROME + detail pane (fixed-width
   chat list, always-on search, aligned bubbles, pill composer, mobile
   one-screen-at-a-time nav) rendered entirely in this app's OWN design
   tokens. No WhatsApp colors (green bubbles, grey/beige fills) are used —
   WhatsApp is the layout/interaction reference only, not the palette.
   Still reuses `.mf-modal`/`.hr-dash-recent-btn-*`/`.ins-pol-*` field
   primitives for the New Thread modal's chrome; the rail shell/bubbles/
   composer are their own `.cc-*` rule set.

   CHARTE 2026 (design-system consolidation, phase 6): every visual value in
   this block reads a `--cc-*` (or an app-wide) token whose FALLBACK is the
   pre-Charte literal it replaced, so `property-finder`/`backoffice` — which
   map none of them — render as before. That promise binds the `.cc-rail`-SCOPED
   overrides below too: for an unmapped brand each of their declarations must
   resolve to whatever the BASE `.mf-*` rule already gave that row, which is why
   the selected row's ring and the row title's weight are tokens rather than
   bare Charte values (four of them were not, and shipping that was the gap the
   QA gate caught — `US-CC26.2.5`). The four values that DO reach every brand are
   listed in the spec, `US-CC26.2.6`/`5.4`; nothing else may join them silently.
   Heale opts in from `public/themes/heale/style.css`. Deltas on Heale: warm cream canvas,
   borderless panes carried by the §8 elevation, navy-700 selected thread row,
   circle avatars, ink-900 titles, blue-400@12% "mine" bubbles against white
   "theirs" cards, sand-200 idle send button, coral@14% unread pill.

   THREAD ROWS REUSE THE BASE `.mf-*` CLASSES ON PURPOSE
   (`.mf-member-btn`/`.mf-avatar`/`.mf-member-name`/`.mf-member-sub`), so a
   "pick one of these" row is one implementation across every rail in the app.
   That is why the Charte restyle of those rows lives in a `.cc-rail`-SCOPED
   block further down — symmetric with the census rail's `.mf-rail`-scoped
   block — and NOT in the base `.mf-*` rules: an unscoped edit there silently
   reskins the other feature. Pinned from both sides by
   `src/components/census/__tests__/member-rail-charte-2026.test.ts` and
   `src/modules/contact-center/components/threads/__tests__/contact-center-charte-2026.test.ts`.

   Outer SHELL is the shared `.dash-body.dash-body--split` wrapper (cream
   padded body, row layout, 20px gap) — the Contact Center is now its only
   consumer; see the `.cc-rail`/`.cc-detail` comment below.
   ===================================================================== */
.cc-root { display: flex; flex-direction: column; height: 100%; overflow: hidden; }
/* The rail/detail split itself is NOT a bespoke `.cc-body` rule anymore — both
   contact-center layouts render the shared `.dash-body.dash-body--split`
   wrapper (cream padded body, row layout, 20px gap between panes) directly, so
   `.cc-rail`/`.cc-detail` sit on the cream background as two separate cards
   instead of a full-bleed white block with an internal divider. Both panes keep
   the standard `.hr-dash-recent-card` rounded-card look used everywhere else
   (Policies/Companies/Census) — same `--hr-dash-recent-card-border`/
   `-radius`/`-shadow` tokens, just applied directly on `.cc-rail`/
   `.cc-detail` instead of composing the class itself, since neither pane
   wants that class's own padding/table-specific children. `.cc-rail` keeps its
   fixed 380px width — this is a TRUE side-by-side split, unlike the Task
   Manager, whose detail pane became a right-docked overlay. */
.cc-root .hr-dash-recent-btn-primary:disabled,
.cc-root .hr-dash-recent-btn-outline:disabled { opacity: var(--cc-disabled-opacity, .6); cursor: not-allowed; }

/* A small square icon button, reused for every icon-only action in this
   feature: Archive, the mobile back chevron, the modal close and the
   composer's attachment button. §13's close-button shape — 32px square, 1px
   sand-400 border, radius 10 on Heale (radius 6 stays the unmapped-brand
   fallback) — plus a CONTROL-hover tint (`--cc-hover-bg`, deliberately a
   different token from the rail's ROW hover: a cream wash on an
   already-light control is invisible). */
.cc-icon-btn { width: var(--cc-icon-btn-size, 32px); height: var(--cc-icon-btn-size, 32px); border-radius: var(--cc-icon-btn-radius, 6px); border: 1px solid var(--cc-icon-border-color, #e5e7eb); background: var(--cc-icon-btn-bg, #fff); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--cc-muted-color, #64748b); cursor: pointer; text-decoration: none; }
.cc-icon-btn:hover { background: var(--cc-hover-bg, #f8fafc); color: var(--brand-primary, #0a3373); }
.cc-icon-btn:disabled { opacity: var(--cc-disabled-opacity, .5); cursor: not-allowed; background: var(--cc-icon-btn-bg, #fff); }

/* Left rail — fixed-width chat list, WhatsApp Web/Desktop proportions.
   No border-right: the panes are now separated by the 20px cream gap
   `.dash-body--split` provides, so an internal divider would be redundant.
   Border/radius/shadow mirror `.hr-dash-recent-card`'s exact Heale values. */
.cc-rail { width: 380px; flex-shrink: 0; padding: 10px 0 0; display: flex; flex-direction: column; background-color: var(--hr-dash-recent-card-bg, #ffffff); border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5); border-radius: var(--hr-dash-recent-card-radius, 16px); box-shadow: var(--hr-dash-recent-card-shadow, none); overflow: hidden; }
.cc-rail-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 0 12px 10px 16px; }
/* §3 card title — 16px Bold, ink-900 on Heale (navy stays the unmapped fallback). */
.cc-rail-title { font-size: 16px; font-weight: 700; color: var(--cc-title-color, var(--brand-primary, #0a3373)); margin: 0; }
/* "New thread" composes the shared `.mf-add-dep` action row — `.cc-newchat-btn`
   only adds the horizontal inset the other rail sections use. It sits directly
   above the thread rows (after search + Archived), so — unlike `.mf-add-dep`'s
   default `margin-top` (meant to trail a list) — it keeps a small top gap from
   the Archived link above it and a bottom gap before the first thread row.
   Its Charte look (borderless tinted ghost row instead of the base dashed box)
   is in the `.cc-rail`-scoped block below, not here. */
.cc-newchat-btn { margin: 4px 12px 10px; flex-shrink: 0; }

/* Always-visible search (never collapses) — `.ins-pol-search`'s existing
   white/bordered field verbatim; `.cc-search` only adds rail-specific
   margins and overrides its `flex: 1` (meant for a horizontal toolbar) back
   to a fixed-height item inside this vertical rail. */
.cc-search { flex: 0 0 auto; margin: 0 12px 8px; }
.cc-search-clear { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; border: none; background: transparent; padding: 0; color: var(--cc-muted-color, #9ca3af); cursor: pointer; }
.cc-search-clear:hover { color: var(--cc-text-color, #64748b); }

/* De-emphasized "Archived" row/link instead of a prominent filter — WhatsApp
   treats Archived as a single tucked-away row, not a peer tab. */
.cc-archived-link { display: flex; align-items: center; gap: 8px; margin: 0 12px 6px; padding: 8px 12px; border-radius: 8px; border: none; background: transparent; color: var(--cc-muted-color, #64748b); font-size: 12.5px; font-weight: 600; cursor: pointer; flex-shrink: 0; }
.cc-archived-link:hover { background: var(--cc-hover-bg, #eef2f7); color: var(--brand-primary, #0a3373); }

.cc-rail-list { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: var(--cc-rail-list-gap, 3px); padding: 0 12px 12px; }
.cc-rail-empty { padding: 24px 16px; text-align: center; font-size: 12.5px; color: var(--cc-muted-color, #94a3b8); }

/* Thread row — reuses the census member-form rail's `.mf-member-btn` (avatar +
   two-line text block) verbatim instead of a WhatsApp-spacious row;
   `.cc-thread-btn` only strips the underline a `<Link>` would otherwise get and
   keeps the row full-width inside `.cc-rail-list`'s own horizontal padding
   (mirroring how `.mf-member-btn` sits inside `.mf-rail`'s padding on the census
   page). Row METRICS and TONE are in the `.cc-rail`-scoped block below. */
.cc-thread-btn { width: 100%; text-decoration: none; }

/* "New" pill on an unread thread row — pushed to the right edge of the row.
   §2/§12's tinted surface: coral-500 at 14% for the fill, the SAME token at
   100% for the label. Coral is the brand accent here (unread / needs
   attention), never a CTA. */
.cc-thread-new { margin-left: auto; flex-shrink: 0; display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 100px; font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--cc-thread-new-color, var(--brand-accent, #ff6733)); background-color: var(--cc-thread-new-bg, #fff0eb); }

/* ─────────────────────────────────────────────
   Thread rail rows — Charte 2026, SCOPED to `.cc-rail`
   Mirror image of the census rail's `.mf-rail`-scoped block: the two rails
   share the base `.mf-member-btn`/`.mf-avatar`/`.mf-member-name`/
   `.mf-member-sub` definitions, so each feature restyles its own rows under
   its own shell class and neither can reskin the other. The base rules stay
   exactly as they were.

   Charte deltas vs the base: taller rows with a generous radius, NAVY-700
   selected state (§6 — navy is the "selected control" tone; blue-400 is
   reserved for actions and is already the "mine" bubble here, so reusing it
   for selection would make the two read as the same thing), circle avatars on
   the shared `--avatar-circle-*` tones, ink-900 row title, ink-400 sub-line,
   and a borderless tinted ghost row for "New thread" instead of the base
   dashed box.
   ───────────────────────────────────────────── */
.cc-rail .mf-member-btn {
  min-height: var(--cc-row-min-height, auto);
  padding: var(--cc-row-padding, 6px 9px);
  gap: var(--cc-row-gap, 9px);
  border-radius: var(--cc-row-radius, 8px);
}

/* ROW hover — its own token, never the control-hover one (see `.cc-icon-btn`). */
.cc-rail .mf-member-btn:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #eef2f7);
}

/* Selected thread: soft navy tint on Heale, where the outline is also dropped
   (§6 — the fill carries the state). An unmapped brand keeps the base rule's
   inset ring, which is why the ring is a TOKEN and not a bare `none`. */
.cc-rail .mf-member-btn.is-active {
  background-color: var(--cc-row-active-bg, #e5edfb);
  box-shadow: var(--cc-row-active-shadow, inset 0 0 0 1px #cbdaf9);
}

.cc-rail .mf-member-name {
  font-size: var(--cc-row-name-size, 13px);
  font-weight: var(--cc-row-name-weight, 600);
  color: var(--cc-row-name-color, var(--brand-primary, #0a3373));
}

.cc-rail .mf-member-sub {
  font-size: var(--cc-row-sub-size, 12px);
  color: var(--cc-muted-color, #64748b);
  margin-top: var(--cc-row-sub-gap, 0);
}

/* Avatars: borderless circle on Heale (radius 8 stays the unmapped fallback),
   tinted from the app-wide `.avatar-circle` tones so every avatar in the app
   agrees. Also applies to the detail header's counterparty avatar. */
.cc-rail .mf-avatar,
.cc-detail-head .mf-avatar {
  width: var(--cc-avatar-size, 28px);
  height: var(--cc-avatar-size, 28px);
  border-radius: var(--cc-avatar-radius, 8px);
  border: none;
}

.cc-rail .mf-avatar--emp,
.cc-detail-head .mf-avatar--emp {
  background-color: var(--avatar-circle-brand-bg, #e5edfb);
  color: var(--avatar-circle-brand-color, var(--brand-primary, #0a3373));
}

/* Selected row keeps the solid-brand flip — still a borderless filled circle,
   just the strongest tone in the set. Declared after the resting tone above
   (same weight) so it wins. */
.cc-rail .mf-avatar--emp.is-active {
  background-color: var(--brand-primary, #0a3373);
  color: #fff;
}

/* "New thread": a borderless tinted ghost action row, left-aligned like the
   thread rows above it — not a dashed placeholder box. */
.cc-rail .mf-add-dep {
  justify-content: var(--cc-add-justify, center);
  min-height: var(--cc-add-min-height, auto);
  border: var(--cc-add-border, 1px dashed #c7d2e8);
  border-radius: var(--cc-row-radius, 8px);
  background-color: var(--cc-add-bg, #fff);
  color: var(--cc-add-color, var(--brand-primary, #0a3373));
  font-size: 13px;
  font-weight: 600;
}

.cc-rail .mf-add-dep:hover:not(:disabled) {
  background-color: var(--cc-add-bg-hover, #fff);
}

/* Right pane — same rounded-card treatment as `.cc-rail` above (mirrors
   `.hr-dash-recent-card`'s border/radius/shadow tokens exactly). */
.cc-detail { flex: 1; display: flex; flex-direction: column; min-width: 0; background-color: var(--hr-dash-recent-card-bg, #ffffff); border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5); border-radius: var(--hr-dash-recent-card-radius, 16px); box-shadow: var(--hr-dash-recent-card-shadow, none); overflow: hidden; }
/* Compact single-row header (~59px), WhatsApp-style — avatar + name/subtitle
   + an icon-only Archive action; the thread name/counterparty are already
   visible on the selected rail row, so this stays a light context strip. */
.cc-detail-head { display: flex; align-items: center; gap: 10px; padding: 8px 16px; border-bottom: 1px solid var(--cc-border-color, #e5e7eb); min-height: 59px; }
/* Hidden on desktop — shown only in the mobile media query below, where the
   rail and detail panes are never both on screen at once. */
.cc-back-btn { display: none; }
.cc-detail-heading { flex: 1; min-width: 0; display: flex; flex-direction: column; }
/* §3 card title / metadata sub-line. Sizes are tokenized so the head skeleton
   (`.cc-shimmer--head-title`/`--head-sub`) can read the very same tokens and
   cannot freeze at a stale geometry. */
.cc-detail-title { margin: 0; font-size: var(--cc-detail-title-size, 14px); font-weight: 700; color: var(--cc-title-color, var(--brand-primary, #0a3373)); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cc-detail-sub { margin: 0; font-size: var(--cc-detail-sub-size, 12px); font-weight: 400; color: var(--cc-muted-color, #64748b); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Conversation canvas — the app's page surface (cream-50 on Heale), so the
   white "theirs" bubble reads as a card ON that surface (§8) and needs no
   border of its own. */
.cc-messages { flex: 1; min-height: 0; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 10px; background: var(--cc-surface-tint-bg, #fbfcfe); }
.cc-load-more { align-self: center; }
.cc-empty-state { margin: auto; text-align: center; font-size: 13px; color: var(--cc-muted-color, #94a3b8); }

/* Message bubbles — WhatsApp's left/right alignment + in-bubble timestamp
   pattern, uniform corner rounding (no speech-bubble tail).

   THE TWO-PARTY DISTINCTION (Charte 2026): the two bubbles differ in HUE
   only — same radius, same elevation, same ink-900 body text — so legibility
   is identical in both directions and neither side reads as "the important
   one".
     · theirs = the pane/card white on the cream canvas, borderless, carried
       by the §8 standard elevation. An incoming message is a card on the page
       surface — exactly the relationship every other card in the app has.
     · mine   = blue-400 at 12% (§2's tint band). Blue-400 is the app's
       self/primary identity; the tint is the same family as
       `.tm-tr--selected`'s blue wash, so "this one is me" is a tone the user
       already knows.
   Rejected on purpose: a coral bubble (coral is action-needed, an alert), a
   green/gold bubble (approval/pending semantics), and a SOLID navy bubble —
   that would need white body text on one side only, breaking the single
   body-text tone, and navy-700 is the selected-CONTROL tone, already spent on
   the selected thread row in the rail beside it. */
.cc-msg { display: flex; flex-direction: column; max-width: 65%; gap: 2px; }
.cc-msg--mine { align-self: flex-end; align-items: flex-end; }
.cc-msg--theirs { align-self: flex-start; align-items: flex-start; }
.cc-msg-meta { font-size: var(--cc-msg-time-size, 11px); color: var(--cc-muted-color, #94a3b8); }
.cc-msg-bubble { display: flex; flex-direction: column; padding: 10px 14px; border-radius: var(--cc-msg-radius, 16px); font-size: var(--cc-msg-font-size, 14px); line-height: 1.5; box-shadow: var(--cc-msg-shadow, none); }
/* Author label — an identifier, so navy-700 on Heale (§1), not the blue-400
   reserved for actions. Role bucket only, never a person's name (see the
   Contact Center privacy rule in CLAUDE.md). */
.cc-msg-sender { font-size: 11.5px; font-weight: 700; color: var(--cc-msg-sender-color, var(--brand-secondary, #4374f3)); margin-bottom: 2px; }
.cc-msg-text { white-space: pre-wrap; overflow-wrap: anywhere; color: var(--cc-text-color, #1f2937); }
/* ONE timestamp tone for both sides — it is metadata (§3: 11px ink-400),
   not a per-side signal; two tones was a pre-Charte artefact. */
.cc-msg-time { font-size: var(--cc-msg-time-size, 10.5px); align-self: flex-end; margin-top: 3px; color: var(--cc-muted-color, #94a3b8); }
.cc-msg--mine .cc-msg-bubble { background: var(--cc-msg-mine-bg, var(--cc-blue-tint-bg, #e5edfb)); }
.cc-msg--theirs .cc-msg-bubble { background: var(--cc-msg-theirs-bg, #fff); border: 1px solid var(--cc-msg-theirs-border, var(--cc-border-color, #e5e7eb)); }
.cc-msg-attachments { display: flex; gap: 6px; flex-wrap: wrap; }

/* Small bordered pill for a file — shared by the composer's pending-upload
   chip and a message's downloadable attachment chip. */
.cc-attachment-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 8px; border: 1px solid var(--cc-input-border-color, #d2d9e5); background: var(--cc-chip-bg, #fff); color: var(--brand-primary, #0a3373); font-size: 12px; font-weight: 600; cursor: pointer; }
.cc-attachment-chip:disabled { opacity: var(--cc-disabled-opacity, .6); cursor: not-allowed; }
.cc-attachment-chip--pending { background: var(--cc-blue-tint-bg, #f1f5fe); border-color: var(--cc-attachment-pending-border, #d7e3fb); cursor: default; }

.cc-composer { padding: 10px 16px; border-top: 1px solid var(--cc-border-color, #e5e7eb); background: var(--cc-composer-bg, #fff); }
.cc-composer-archived { text-align: center; font-size: 13px; color: var(--cc-muted-color, #94a3b8); padding: 8px 0; }
.cc-composer-files { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.cc-composer-row { display: flex; gap: 6px; align-items: center; }
/* Rounded pill input (WhatsApp's LAYOUT) in the app's own FIELD style — the
   shared `.cc-icon-btn` attach button and a circular send button (grey until
   there's something to send, brand navy once active) flank it.
   GEOMETRY IS LOCAL ON PURPOSE: the 20px radius, the 40→160px growth range and
   the wider 16px inset that radius needs are the pill's layout, not the field
   standard (`--field-textarea-min-height` would pin a chat line at a form
   textarea's resting height). Colour and state are the contract's — it used to
   read `--cc-input-border-color`, which `.cc-attachment-chip` also consumes, so a
   field and a chip shared one edge token. That token stays for the chip. */
.cc-composer-textarea { flex: 1; resize: none; min-height: 40px; max-height: 160px; padding: 9px 16px; border: 1px solid var(--field-border, #d2d9e5); border-radius: 20px; background: var(--field-bg, #fff); font-size: var(--field-font-size, 13.5px); color: var(--field-color, #374151); font-family: inherit; outline: none; }
.cc-composer-textarea::placeholder { color: var(--field-placeholder-color, #9ca3af); }
.cc-composer-textarea:focus { border-color: var(--field-focus-border, var(--brand-secondary, #4374f3)); }
.cc-send-btn { display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 40px; height: 40px; border-radius: 9999px; border: none; background: var(--cc-send-idle-bg, #e5e7eb); color: var(--cc-muted-color, #94a3b8); cursor: pointer; }
/* Sending is the composer's PRIMARY action (Charte §6: primary = blue-400),
   not a "selection" state (which would be navy-700) — intentionally a
   different token than the rest of this file's navy accents. */
.cc-send-btn--active { background: var(--cc-send-active-bg, var(--brand-secondary, #4374f3)); color: var(--cc-send-active-fg, #fff); }
.cc-send-btn:disabled { cursor: not-allowed; }

/* New-thread modal — .mf-modal with a wider, flex-column body (its own gap
   replaces each child's default bottom margin), a slightly larger corner
   radius for a more WhatsApp-"New chat"-picker feel, + a Back/Cancel/Next
   action row split left/right. */
.cc-modal { display: flex; flex-direction: column; gap: 16px; width: min(480px, calc(100vw - 48px)); border-radius: 16px; }
.cc-modal .mf-modal-text { margin: 0; }
.cc-modal-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.cc-modal-head .mf-modal-title { margin: 0; }
/* §13's close button — 28px here (it sits in a modal head, not a pane head),
   sharing `.cc-icon-btn`'s radius token so the two never drift. */
.cc-modal-close { display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: var(--cc-icon-btn-radius, 6px); border: none; background: transparent; color: var(--cc-muted-color, #64748b); cursor: pointer; }
.cc-modal-close:hover { background: var(--cc-hover-bg, #f1f5fe); color: var(--brand-primary, #0a3373); }
/* NB: this modal deliberately holds no label-gap override — its fields are
   plain `.ins-pol-field`s and take the field contract's `--field-label-gap`
   like every other form row in the app. */
.cc-modal-actions { justify-content: space-between; }
.cc-modal-actions-right { display: flex; align-items: center; gap: 8px; }

/* Module-gate pane wrapper — the `<ModuleDisabledState>` slot inside the
   `.dash-body.dash-body--split` wrapper on both the insurer and HR
   contact-center layouts (module disabled / no active policy). Reuses the
   existing `--dash-body-bg` token (already Heale-mapped to cream) rather than
   introducing a new one. */
.cc-gate-pane { flex: 1; min-height: 0; display: flex; align-items: flex-start; justify-content: flex-start; padding: 24px; background: var(--dash-body-bg, #f8fafd); }

/* ─────────────────────────────────────────────
   Loading parity (§17) — every shimmer in `contact-center-loading-skeleton.tsx`
   takes its geometry from HERE, reading the SAME tokens the resolved element
   reads, so a brand that re-points `--cc-avatar-radius` or
   `--cc-detail-title-size` can never leave the skeleton at the old size.
   Class-only, following `.settings-shimmer--*` / `.dash-stat-shimmer`:
   `<Shimmer height=… width=…>`'s numeric props become INLINE styles, which
   would win over these rules — that regression has shipped three times in this
   repo, so the skeleton renders plain `<span className="ui-shimmer cc-shimmer …">`
   instead of `<Shimmer>`.
   ───────────────────────────────────────────── */
.cc-shimmer { display: inline-block; border-radius: 4px; flex-shrink: 0; }

/* Rail row: avatar + the two text lines of `.mf-member-name`/`.mf-member-sub`. */
.cc-shimmer--avatar {
  width: var(--cc-avatar-size, 28px);
  height: var(--cc-avatar-size, 28px);
  border-radius: var(--cc-avatar-radius, 8px);
}
.cc-shimmer--row-title { height: var(--cc-row-name-size, 13px); width: 70%; margin-bottom: 6px; }
.cc-shimmer--row-sub { height: var(--cc-row-sub-size, 12px); width: 45%; }

/* Rail chrome: the search pill's icon + value, the Archived toggle, "New thread". */
.cc-shimmer--search-icon { width: 14px; height: 14px; }
.cc-shimmer--search-value { height: 13px; width: 70%; }
.cc-shimmer--archived-icon { width: 13px; height: 13px; }
.cc-shimmer--archived-label { height: 12px; width: 70px; }
.cc-shimmer--newchat-icon { width: 15px; height: 15px; }
.cc-shimmer--newchat-label { height: 13px; width: 90px; }

/* Detail head: `.cc-detail-title` / `.cc-detail-sub` / the Archive `.cc-icon-btn`. */
.cc-shimmer--head-title { height: var(--cc-detail-title-size, 14px); width: 180px; margin-bottom: 6px; }
.cc-shimmer--head-sub { height: var(--cc-detail-sub-size, 12px); width: 120px; }
.cc-shimmer--head-action {
  width: var(--cc-icon-btn-size, 32px);
  height: var(--cc-icon-btn-size, 32px);
  border-radius: var(--cc-icon-btn-radius, 6px);
}

/* Bubbles: three alternating shapes, on `.cc-msg-bubble`'s own radius token. */
.cc-shimmer--bubble { border-radius: var(--cc-msg-radius, 16px); }
.cc-shimmer--bubble-a { height: 40px; width: 220px; }
.cc-shimmer--bubble-b { height: 30px; width: 160px; }
.cc-shimmer--bubble-c { height: 54px; width: 260px; }

/* New-thread modal: the in-flight `<select>` placeholder. Reads the FIELD
   contract, because the control it stands in for is a plain `.ins-pol-input`. */
.cc-shimmer--field { display: block; width: 100%; height: var(--field-height, 44px); border-radius: var(--field-radius, 10px); }

/* Composer pill. `.cc-composer-textarea`'s 40px/20px are LITERAL by contract
   (US-FLD.9.5 — the chat pill's growth range is layout, not the field
   standard), so these two literals are its pair and the guard test asserts the
   two rules agree rather than trusting a comment. */
.cc-shimmer--composer { display: block; height: 40px; width: 100%; border-radius: 20px; }

@media (max-width: 768px) {
  /* WhatsApp mobile shows exactly ONE pane at a time (full screen), not a
     side-by-side split: the list route shows only the rail, the thread
     route shows only the conversation. `.cc-rail--has-active` is set by
     <ThreadList> whenever `activeId` is passed (i.e. we're on
     `/[side]/contact-center/[id]`), so it's the rail that hides itself
     there; `.cc-detail-placeholder` (the list route's empty-state right
     pane) hides the same way so the list gets the whole screen instead.
     `.cc-back-btn` (hidden on desktop by default) becomes visible here as
     the only way back from a full-screen conversation to the list.
     The row→column flip itself needs no rule here — `.dash-body--split`'s
     own mobile override (further up this file) already switches the shared
     wrapper to `flex-direction: column`, so `.cc-rail`/`.cc-detail` stack
     instead of both trying to render side by side; this block only handles
     the contact-center-specific one-pane-visible-at-a-time behaviour on top
     of that. */
  .cc-rail { width: 100%; }
  .cc-rail.cc-rail--has-active { display: none; }
  .cc-detail-placeholder { display: none; }
  .cc-detail { width: 100%; min-height: 0; }
  .cc-back-btn { display: inline-flex; }
  .cc-msg { max-width: 80%; }
  .cc-messages { padding: 14px; }
  .cc-composer { padding: 8px 10px; }
}

/* =====================================================================
   <ModuleDisabledState> / <ModuleDisabledBadge> — "this feature isn't
   enabled for your organization" card shown in place of a page's real
   content when a tenant-level module gate is off. Shared by the Contact
   Center gate (`.cc-gate-pane` above), Claims and Chatbot gate layouts.
   ===================================================================== */
.module-disabled-card { width: 100%; padding: 40px; text-align: center; }
.module-disabled-icon { color: var(--module-disabled-muted-color, #7d93b3); margin: 0 auto 16px; }
.module-disabled-title { font-size: 18px; font-weight: 600; color: var(--module-disabled-title-color, #111827); margin: 0 0 8px; }
.module-disabled-text { font-size: 14px; color: var(--module-disabled-muted-color, #7d93b3); line-height: 1.5; margin: 0 auto; max-width: 340px; }
.module-disabled-badge { display: inline-flex; align-items: center; gap: 4px; }

/* =====================================================================
   <LockedSection> — a gated section's "locked window into the page": the
   section's own loading SKELETON behind a floating state card
   (`<NoAccessState>` / `<ModuleDisabledState>`), blurred, dimmed and FROZEN.
   Keeps the page's shape legible so the gate reads as "this section exists
   and you can't open it" (the same message as the sidebar lock) instead of
   "this section is broken". Every tone derives from the theme's own page
   background (`--dash-root-bg`) via `color-mix`, so a dark-surfaced theme
   scrims dark without a second rule set; a theme can still override the two
   `--locked-section-*` tokens directly.
   ===================================================================== */
.locked-section {
  position: relative;
  /* The overlay is absolutely positioned, so the wrapper's height comes from
     the backdrop alone — a short skeleton must still leave room for the card. */
  min-height: 420px;
}

.locked-section-backdrop {
  filter: blur(4px) saturate(0.6);
  opacity: 0.55;
  pointer-events: none;
  user-select: none;
  /* A blur bleeds past the element box; clip it so nothing smears into the
     topbar or the page gutters. */
  overflow: hidden;
}

/* A pulsing shimmer under a blur reads as "still loading" — the opposite of
   the intended message. Freeze every animation in the backdrop so it reads as
   a single static frame of the page. */
.locked-section-backdrop *,
.locked-section-backdrop {
  animation: none !important;
  transition: none !important;
}

.locked-section-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  justify-content: center;
  /* Anchored near the top third rather than dead-centre: on a tall skeleton a
     vertically centred card lands below the fold. */
  align-items: flex-start;
  padding: 84px 16px 24px;
  /* Soft scrim over the blurred skeleton, fading downwards so the page shape
     stays readable at the bottom edge. */
  background: linear-gradient(
    to bottom,
    var(--locked-section-scrim, color-mix(in srgb, var(--dash-root-bg, #faf6f0) 78%, transparent)) 0%,
    var(--locked-section-scrim-fade, color-mix(in srgb, var(--dash-root-bg, #faf6f0) 42%, transparent)) 100%
  );
}

.locked-section-overlay > * {
  max-width: 520px;
}

/* Split-body variant — the two Contact Center gates. Their section body is a
   ROW split (`.dash-body--split`) whose panes must sit side by side and fill
   the row, where every other gated section stacks blocks in a column body. So
   the wrapper becomes the row's flex item (replacing `.cc-gate-pane`'s job)
   and the backdrop re-establishes the row, since `.locked-section-backdrop` is
   a plain block. `min-height` drops to 0: the row already has a definite
   height from `.cc-root`, and the 420px floor would force a scroll on short
   viewports. */
.locked-section--split {
  flex: 1;
  min-width: 0;
  min-height: 0;
}

.locked-section--split .locked-section-backdrop {
  display: flex;
  flex-direction: row;
  gap: 20px;
  height: 100%;
}

@media (max-width: 768px) {
  .locked-section { min-height: 340px; }
  .locked-section-overlay { padding: 40px 12px 20px; }
}

/* =====================================================================
   Insurer Policy Wizard — <PolicyBucketBoard>, ONE persistent screen (no
   step navigation, no separate "manage policies" list): a LEFT backlog
   panel (draggable member cards, grouped into labelled sections — a flat
   "Eligible members" list for the insurer-manual-entry flow today, ready for
   quote-category grouping later) next to a wider RIGHT panel of policy
   sections — ONE per policy being defined. Each policy section reuses THIS
   APP'S standard card container (`.hr-dash-recent-card`'s look: white bg,
   hairline border, 16px radius — no colored accent bar, no bespoke kanban
   skin) with a compact READ-ONLY summary header (name/dates/premium as
   plain text) + an Edit icon button that opens the SAME `.modal-overlay`/
   `.modal-card` dialog chrome as `AddPolicyModal`/`PolicyActions`'s "Change
   Policy Dates" modal — never inline-editable inputs in the header itself.
   Native HTML5 drag-and-drop (no DnD library, see AGENTS/CLAUDE.md
   conventions): each section body/the backlog is its own drop zone
   (`.pw-column--over`/`.pw-sprint--over` toggle on dragenter/dragleave for
   the highlight). Cards reuse the shared
   <CoverageStatus> badge for the phase chip, so a member card looks like a
   first-class citizen of the existing insurer surfaces, not a bespoke
   kanban skin.

   D18 — the Charte 2026 pass. Every colour in this block reads a `--pw-*`
   CONSUMED token whose base fallback is the pre-Charte value, so the themes
   outside the rollout (property-finder, backoffice) render byte-identically while
   Heale maps the same tokens onto the warm Charte palette in
   `public/themes/heale/style.css`. No hex and no palette primitive is named here.
   The roles the tokens encode, so a future edit picks the right one:
   `--pw-panel-*` the two panels' surface/edge · `--pw-divider` internal hairlines ·
   `--pw-title-color` names · `--pw-value-color` values · `--pw-muted-color`
   labels/metadata · `--pw-placeholder-color` genuinely absent values ONLY, never a
   normal label (that is what read washed-out before) · `--pw-control-*` the 32px
   icon buttons · `--pw-card-*` a backlog member card · `--pw-badge-*` the numbered
   slot circle + member count · `--pw-over-bg` the active drop target ·
   `--pw-warning-color`/`--pw-danger-*` the amber slot warning and the destructive
   hover. What the pass deliberately did NOT do: restructure the board, rename
   `.pw-*` into `.ins-pol-*`, or touch the drag-and-drop geometry — rewriting the
   layout of a DnD surface with no browser verification is exactly how the
   header-clipping regression happened.
   ===================================================================== */
.pw-layout {
  display: grid;
  grid-template-columns: minmax(240px, 300px) 1fr;
  gap: 10px;
  align-items: start;
  height: 100%;
}
.pw-backlog {
  background: var(--pw-panel-bg, #fff);
  border: 1px solid var(--pw-panel-border, #d2d9e5);
  border-radius: 12px;
  display: flex;
  flex-direction: column;
  min-height: 200px;
  align-self: stretch;
}
.pw-backlog-head {
  padding: 8px 10px;
  border-bottom: 1px solid var(--pw-divider, #eaeff6);
}
/* Optional `backlogHeaderExtra` (e.g. the company picker) renders above this
   row, inside the same head block — zero extra vertical chrome, the control
   just lives with the panel it drives instead of a separate top bar. */
.pw-backlog-head-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.pw-backlog-head > * + .pw-backlog-head-row,
.pw-backlog-head > * + .pw-backlog-notice {
  margin-top: 6px;
}
.pw-backlog-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}
.pw-backlog-sub {
  font-size: 11px;
  color: var(--pw-muted-color, #6b7280);
  white-space: nowrap;
}
/* One-line explanation for why the backlog holds fewer members than the offer's
   category does: the excluded ones already hold a live enrollment, and
   pre-assigning them made `createPolicyWizardBatch` reject and roll back the
   WHOLE commit. Informational, not a warning — hence the muted tone rather than
   `.pw-sprint-warning`'s amber. Wraps (no `nowrap`, unlike `.pw-backlog-sub`)
   because it is a sentence in a 240–300px rail. Consumes a token with a base
   fallback matching this block's own muted tone, so the D18 Charte pass can
   retone it without touching this rule. */
.pw-backlog-notice {
  display: flex;
  align-items: flex-start;
  gap: 5px;
  margin: 0;
  font-size: 11px;
  line-height: 15px;
  color: var(--pw-notice-color, var(--pw-muted-color, #6b7280));
}
.pw-backlog-notice svg {
  flex-shrink: 0;
  margin-top: 1.5px;
}
/* Search box, shown once the backlog has enough members (>8) to be worth
   filtering — a company's census can run into the hundreds. Sizing/border
   matches `.mf-combo-search-input`, this codebase's own compact search
   input already used for the census picker. */
.pw-backlog-search {
  position: relative;
  padding: 8px 10px 0;
  flex-shrink: 0;
}
/* Consumes the SAME tokens as the shared `.ins-pol-search` (fill/border/pill
   radius/icon/text/placeholder) so this standalone search field matches every
   other one in the app, but keeps its own compact 34px height + absolutely
   positioned icon — it sits inside a narrow wizard backlog rail, where the
   shared field's 44px height would crowd the list below it. */
.pw-backlog-search-icon {
  position: absolute;
  left: 22px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--ins-pol-search-icon-color, #6b7280);
  pointer-events: none;
}
/* Same VARIANT pattern as `.ins-pol-search-input`: the search pill's own
   `--ins-pol-search-*` tokens first, the field contract second, so restyling the
   app's search boxes still moves this one. Its 34px height is local — this pill
   lives in a board rail, not a page toolbar. */
.pw-backlog-search-input {
  width: 100%;
  box-sizing: border-box;
  height: 34px;
  border: 1px solid var(--ins-pol-search-border, #e5e7eb);
  border-radius: var(--ins-pol-search-radius, 8px);
  background: var(--ins-pol-search-bg, #fff);
  font-size: var(--field-font-size, 13px);
  color: var(--ins-pol-search-input-color, var(--field-color, #374151));
  padding: 0 10px 0 30px;
}
.pw-backlog-search-input:focus {
  outline: none;
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}
.pw-backlog-search-input::placeholder {
  color: var(--ins-pol-search-placeholder-color, var(--field-placeholder-color, #9ca3af));
}
.pw-backlog-body {
  flex: 1;
  padding: 6px;
  overflow-y: auto;
}
.pw-backlog-section + .pw-backlog-section {
  margin-top: 8px;
}
.pw-backlog-section-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--pw-muted-color, #6b7280);
  margin-bottom: 6px;
  padding: 0 4px;
}
.pw-backlog-section-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.pw-right-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

/* Compact company-picker trigger, relocated into the backlog panel's own
   header (previously a full-width top info-strip) — 32px tall to match the
   dense, utilitarian read the whole board went for, not the 44px form-input
   height used elsewhere in modals. */
.pw-company-picker {
  width: 100%;
  height: 32px;
  padding: 0 10px;
  border-radius: 7px;
  font-size: 12px;
}
.pw-company-locked {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Bare back-arrow icon button, top-left of the page — replaces the old
   labeled "Back to Policies" nav row. Same 32px bordered shape as
   `.mf-icon-btn`/`.pw-sprint-icon-btn` so it reads as part of this app's
   established icon-button family, not a bespoke control. */
.pw-back-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: 1px solid var(--pw-control-border, #e5e7eb);
  background: var(--pw-control-bg, #fff);
  color: var(--pw-control-color, #374151);
  cursor: pointer;
  flex-shrink: 0;
}
.pw-back-btn:hover {
  background: var(--pw-control-bg-hover, #f8fafc);
  color: var(--brand-primary, #0a3373);
}

/* ONE policy section — the app's standard card container
   (`.hr-dash-recent-card` look), no colored accent border. `.pw-sprint--over`
   (active drop target) is signalled the same way `.ins-pol-search`/inputs
   elsewhere show focus: a brand-blue border + soft ring, not a permanent
   decorative bar. */
.pw-sprint {
  background: var(--pw-panel-bg, #fff);
  border: 1px solid var(--pw-panel-border, #d2d9e5);
  border-radius: 12px;
  overflow: hidden;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.pw-sprint--over {
  border-color: var(--brand-secondary, #4374f3);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent);
}
/* Name, dates, premium, member count, Edit, Delete on one baseline, with
   `.pw-sprint-actions` pinned to the row's end via `margin-left: auto`.
   WRAPS when the content genuinely doesn't fit — it used to be `nowrap`, and
   that silently DESTROYED data: `.pw-sprint-warning` carries `flex-basis: 100%`
   (written for the mobile wrap case), which on a nowrap line demands the whole
   container as its base size and over-constrains the line, so every shrinkable
   sibling collapses. `.pw-sprint-meta` (`flex: 1 1 auto`) was squeezed to its
   `min-width` while its own children are `flex-shrink: 0`, so they overflowed
   its `overflow: hidden` box and were CLIPPED — a real slot rendered
   "Start 2026-07-" with the End and AED items cut off entirely, mid-glyph and
   without even an ellipsis (`text-overflow` does nothing to flex children).
   Wrapping costs nothing in the common case: a complete slot still fits on one
   line, so the extra header height only appears exactly when the alternative
   was hiding values. Never re-introduce `nowrap` here. */
.pw-sprint-header {
  padding: 6px 12px;
  border-bottom: 1px solid var(--pw-divider, #eaeff6);
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  row-gap: 4px;
  min-height: 40px;
  box-sizing: border-box;
}
.pw-sprint-body {
  padding: 4px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  transition: background-color 0.15s;
}
.pw-sprint--over .pw-sprint-body {
  background: var(--pw-over-bg, color-mix(in srgb, var(--brand-secondary, #4374f3) 5%, white));
}

/* Read-only summary row — name/dates/premium as plain text (never an inline
   input); the Edit icon button opens the modal that actually changes them.
   `.pw-sprint-badge` is a small numbered circle so multiple sections stay
   visually distinct without needing "Policy N" spelled out in words. */
.pw-sprint-badge {
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--pw-badge-bg, #eef2f9);
  color: var(--pw-badge-color, var(--brand-secondary, #4374f3));
  font-size: 11px;
  font-weight: 700;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.pw-sprint-name {
  flex-shrink: 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--pw-title-color, #18181b);
  max-width: 220px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pw-sprint-name--placeholder {
  color: var(--pw-placeholder-color, #9ca3af);
  font-weight: 600;
}
/* Wraps its items instead of clipping them. `overflow: hidden` +
   `min-width: 40px` + `flex-shrink: 0` children is what cut a date in half (see
   `.pw-sprint-header`); the `text-overflow: ellipsis` that was meant to soften
   it never applied, because these are flex children, not inline text.
   `flex: 0 1 auto` so the box asks for its content width rather than greedily
   growing — the right-hand cluster is anchored by `.pw-sprint-count`'s
   `margin-left: auto` regardless. Each ITEM keeps its own `nowrap`, so a value
   never breaks mid-date; only the gaps between items are break points. */
.pw-sprint-meta {
  flex: 0 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 14px;
  font-size: 13px;
  color: var(--pw-value-color, #374151);
}
.pw-sprint-meta-item {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 5px;
  white-space: nowrap;
}
.pw-sprint-meta-label {
  color: var(--pw-muted-color, #6b7280);
  font-weight: 600;
}
.pw-sprint-count {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 100px;
  background: var(--pw-badge-bg, #eef2f9);
  color: var(--brand-primary, #0a3373);
  white-space: nowrap;
  /* Anchors the count + actions cluster to the row's end regardless of how
     much space `.pw-sprint-name`/`.pw-sprint-meta` end up taking — this is
     what keeps Edit/Delete from wrapping onto their own line. */
  margin-left: auto;
}
.pw-sprint-actions {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Edit/Remove icon buttons — same shape as `.mf-icon-btn`, this codebase's
   established generic icon-button (32px, bordered white box). */
.pw-sprint-icon-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 6px;
  border: 1px solid var(--pw-control-border, #e5e7eb);
  background: var(--pw-control-bg, #fff);
  color: var(--pw-control-color, #374151);
  cursor: pointer;
}
.pw-sprint-icon-btn:hover {
  background: var(--pw-control-bg-hover, #f8fafc);
  color: var(--brand-primary, #0a3373);
}
/* Destructive hover. `--pw-danger-color` is rust-600 on Heale; the base fallback
   keeps the legacy `#dc2626` for the themes outside the Charte rollout. */
.pw-sprint-icon-btn--danger:hover {
  background: var(--pw-danger-bg, color-mix(in srgb, var(--pw-danger-color, #dc2626) 8%, white));
  color: var(--pw-danger-color, #dc2626);
  border-color: color-mix(in srgb, var(--pw-danger-color, #dc2626) 35%, white);
}
/* `flex-basis: 100%` takes a line of its own now that `.pw-sprint-header` wraps
   — which is what it was always written for. On the old `nowrap` header it was
   instead the thing starving `.pw-sprint-meta` into clipping its values. */
.pw-sprint-warning {
  flex-basis: 100%;
  flex-shrink: 0;
  font-size: 12px;
  color: var(--pw-warning-color, #d97706);
  font-weight: 600;
}

.pw-add-slot-btn {
  align-self: flex-start;
}
.pw-add-slot-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

/* Empty drop-zone placeholder — a SLIM single line, not a large reserved
   box; height is driven by content (member cards, once any are dropped),
   never reserved upfront. This directly fixes the "huge empty box for 0
   members" complaint. */
.pw-column-empty {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--pw-muted-color, #6b7280);
  padding: 8px 10px;
  border: 1px dashed var(--pw-panel-border, #d2d9e5);
  border-radius: 8px;
}
/* Fixed-height, 3-line row — never a variable-height padding-heavy card.
   With a company census running into the hundreds, backlog cards must stay
   cheap and uniform (fixed height helps scroll performance/consistency too).
   Line 1 = bold name, line 2 = relationship + age meta (bucketing context),
   line 3 = ONE compact status indicator (coverage badge+dot for eligible
   members, the ineligible reason in its place for disabled ones) — never
   more than these three. */
.pw-card {
  background: var(--pw-card-bg, #fff);
  border: 1px solid var(--pw-card-border, #e5e7eb);
  border-radius: 8px;
  padding: 6px 8px;
  height: 56px;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  gap: 8px;
  cursor: grab;
  transition: box-shadow 0.15s, border-color 0.15s, opacity 0.15s;
}
.pw-card:hover {
  border-color: var(--brand-secondary, #4374f3);
  box-shadow: 0 2px 8px rgba(15, 23, 42, 0.08);
}
.pw-card--dragging {
  opacity: 0.4;
}
.pw-card--disabled {
  cursor: not-allowed;
  background: var(--pw-card-bg-disabled, #f9fafb);
}
.pw-card--disabled:hover {
  border-color: var(--pw-card-border, #e5e7eb);
  box-shadow: none;
}
.pw-card-body {
  min-width: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 1px;
  justify-content: center;
}
.pw-card-name {
  font-size: 12px;
  font-weight: 700;
  color: var(--pw-title-color, #18181b);
  line-height: 1.3;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Relationship + age meta line — the extra bucketing-decision context
   (e.g. "Employee" vs "Spouse · 34y") between the name and the status line. */
.pw-card-meta-line {
  display: flex;
  align-items: center;
  gap: 6px;
  line-height: 1.2;
  overflow: hidden;
}
.pw-card-relation {
  font-size: 10px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  white-space: nowrap;
}
.pw-card-age {
  font-size: 10px;
  color: var(--pw-muted-color, #6b7280);
  white-space: nowrap;
}
.pw-card-age::before {
  content: '·';
  margin-right: 6px;
  color: var(--pw-panel-border, #d2d9e5);
}
.pw-card-status-line {
  line-height: 1.2;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pw-card-status-line .cov-cell--inline {
  gap: 5px;
}
.pw-card-status-line .cov-badge {
  font-size: 9px;
  padding: 1px 6px;
}
.pw-card-status-line .cov-state {
  font-size: 10px;
  color: var(--pw-muted-color, #6b7280);
}
.pw-card-reason {
  font-size: 10px;
  color: var(--pw-warning-color, #b45309);
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ── Page shell (D18: these three were inline `style` props on page.tsx, i.e.
   unthemable and unoverridable — the exact thing the styling rules forbid) ── */

/* Page shell. Column layout inside the `overflow: hidden` dashboard frame — never
   `100vh`. A class rather than an inline `style` so the ≤768px block can reach it. */
.pw-root {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

/* The wizard's scroll column. The dashboard shell is `overflow: hidden`, so the
   page owns its own `flex: 1 / overflow-y: auto` pair (never `100vh`). */
.pw-body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 10px 16px;
  gap: 8px;
}

/* <OfferPoliciesTable> above the board. `flex-shrink: 0` so the board's
   `flex: 1` never squeezes this card into clipping its own rows — the body
   scrolls instead. */
.pw-offer-policies {
  flex-shrink: 0;
}

/* The board takes the remaining height. `min-height: 0` lets it shrink below its
   content so `.pw-body` scrolls instead of the board overflowing; `.pw-backlog`'s own
   `min-height: 200px` is the floor that stops it collapsing to nothing. */
.pw-board-slot {
  flex: 1;
  min-height: 0;
}

.pw-error {
  margin: 0;
  font-size: 13px;
  color: var(--pw-danger-color, #dc2626);
}

/* Sticky commit bar. Charte §6: the button inside is the shared blue-400
   `.hr-dash-recent-btn-primary`; disabled is 45% opacity, never a grey fill swap.
   `.hr-dash-recent-btn-primary:disabled` is now an app-wide rule (see its
   definition above) with the same 0.45 value — this scoped copy is a
   harmless duplicate kept for the historical comment context. */
.pw-footer-bar {
  border-top: 1px solid var(--pw-divider, #e5e7eb);
  background: var(--pw-panel-bg, #fff);
  padding: 10px 20px;
  flex-shrink: 0;
}

.pw-footer-bar .hr-dash-recent-btn-primary:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

@media (max-width: 768px) {
  .pw-layout { grid-template-columns: 1fr; }
  .pw-backlog { max-height: 320px; }
  /* The base rule already wraps; mobile just gives the rows more air and puts
     the whole meta group on its own full-width line below the name, rather than
     letting it wrap item-by-item in a ~340px column. */
  .pw-sprint-header { row-gap: 6px; }
  .pw-sprint-meta { flex-basis: 100%; order: 3; }
}

/* =====================================================================
   <OfferPoliciesTable> — the policies one quote offer has produced, on the
   offer's policy-validation page and inside the Policy Wizard entered with
   `?offerId=`. Everything else it needs (card, head, table, footer, status
   badge, identity cell, §17 states) is the shared insurer chrome; these two
   rules are only what that chrome doesn't already have.
   ===================================================================== */

/* `.hr-dash-recent-head` is a flex row; without `min-width: 0` the title +
   summary block can't shrink, and a long summary would push the head wider than
   the card instead of wrapping. */
.offer-pol-head-text {
  min-width: 0;
}

/* PRE-EXISTING GAP, scoped here rather than fixed app-wide: the shared
   `.hr-dash-recent-table-wrap` only gets `overflow-x: auto` inside the ≤768px
   block, so above 768px it is an unstyled div and a wide table overflows its
   card instead of scrolling. Promoting that declaration to the base rule is the
   real fix, but it would turn every table wrapper in the app into a scroll
   container, which CLIPS the absolutely-positioned `.dropdown-menu` that several
   of those tables open from a row-actions cell — not a change to make without a
   browser pass. This table has no row menu, so it can opt in safely. */
.offer-pol-table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* The "Next step" cell when there is no action — the SENTENCE saying why, never
   a disabled control. It WRAPS: this is prose in a table cell, and a clipped
   explanation is worse than no explanation (the failure `.pw-sprint-meta`
   already made once — `text-overflow` is inert on flex children, so a squeezed
   flex box cuts glyphs with no ellipsis to warn anyone). Hence `inline-flex` +
   `flex-wrap` on the box and no `nowrap`/`overflow: hidden` anywhere. */
.offer-pol-next-note {
  display: inline-flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 5px;
  font-size: 12.5px;
  line-height: 17px;
  color: var(--offer-pol-note-color, #9ca3af);
}

.offer-pol-next-note svg {
  flex-shrink: 0;
  margin-top: 2px;
}

/* Locked = the read-only rule is the reason, as opposed to "nothing to do yet".
   Slightly stronger tone so the rule reads as a statement of fact rather than
   de-emphasized metadata. */
.offer-pol-next-note--locked {
  color: var(--offer-pol-note-locked-color, #6b7280);
}

/* =====================================================================
   HR Quote Requests — <CategoriesBoard> (`/hr/quotes/categories`).
   Post-kanban-removal rework: the drag-and-drop board + the per-category
   "List" sections are GONE — the whole screen is now one flat,
   alphabetically-sorted member table (`.hr-dash-recent-table*`, matching
   `/hr/census`'s own list/sort convention) plus a compact category
   MANAGEMENT TABLE above it (`.cat-mgmt-card`/`.cat-mgmt-table` — its own
   `.hr-dash-recent-table*` shell, one row per category, not a stack of
   independent cards) that keeps the rename/settings/delete affordances the
   old per-category section heads carried — assignment itself now happens
   per-row via a searchable `<Combobox>` (`.cat-quick-assign-select`), never
   a column/section a member has to be dragged or grouped into.
   `.cat-col-head`/`-dot`/`-title`/`-count`/`-actions` survive verbatim as
   table-cell content — same pieces as the old column/section head, just
   laid out as row cells instead of topping a 280px column, a full-width
   section card, or (the previous pass) a standalone chip.
   The `.cat-card-relation*` badge family is GONE: its last consumer was
   `step-informations.tsx`, whose member rows now state the relation as plain
   text in the meta line (`Employee · 12/03/1988`) rather than as a chip.
   ===================================================================== */

/* THE HERO PANEL IS GONE (2026-08-15) — `.cat-hero`, `-info`, `-icon`, `-title`,
   `-text`, `-stats`, `-stat`, `-stat-value`, `-stat-label`, `-divider`,
   `-progress`, `-progress-track`, `-progress-fill`, `-progress-label` and their
   mobile block are DELETED, not orphaned.

   It was a blue gradient banner (raw hex, its own border, its own 30px circular
   icon pad) carrying three lines explaining what a group IS — ~120px of
   documentation rendered as the most prominent element on the screen, directly
   above a first-run block that explained the same thing again. The definition
   now lives once, in that block's own line; the reuse promise it also carried
   ("create once and every future quote request reuses the member list") moved
   there with it, because that sentence was said nowhere else.

   The icon pad is the second half of the story: `member-cards.tsx` records the
   user removing these circular pads across three surfaces, one being restored
   from a Charte maquette, and the user removing it again — "the user's review is
   the tiebreaker, not the maquette." This was the fourth.

   THE ONE FACT THE STATS CARRIED SURVIVES: "N of M members grouped" is now the
   member card's own head subtitle (`.hr-dash-recent-subtitle`), where a count
   line belongs on every other card in this app. The decorative progress track
   and the category tally did not survive — the management table above IS the
   category tally, and a 5px bar restated a sentence sitting next to it. */

/* ── Category management TABLE: ONE `.hr-dash-recent-table*` (the same shell
   the flat member table below uses), one row per category — never a stack of
   independent card elements (a direct user correction: categories must read
   as one table, matching the member list's own look, not as separate boxed
   chips). `.cat-mgmt-card` is just `.hr-dash-recent-card` with a bottom
   margin so it stacks above the member-list card; `.cat-mgmt-table` reuses
   `.hr-dash-recent-table/-th/-td/-tr` verbatim — column widths are
   content-sized (`tableLayout: 'auto'`, no `%`/`minWidth`, per Charte §10).
   `.cat-col-head`/`-dot`/`-title`/`-count`/`-actions` survive from the
   pre-table chip design — same pieces, now laid out as table cells instead
   of a standalone card head. A category's name + city/salary_range are
   edited together in the `.modal-*`-shell `CategoryEditModal` (opened by
   the row's gear icon) — a direct user correction replaced the earlier
   inline-expanding "settings" sub-row, which read as a big, ugly standalone
   panel next to these compact rows. Creating a group is the table's HEAD
   action now (a §6 primary in the view-tabs row's right slot) — it used to
   be a dashed trailing row, which was a third button shape and put the one
   action of an empty screen at its bottom. ── */
/* The GROUPS section's card. `.dash-body`'s own 20px gap already separates it from the EMPLOYEES
   card below, so the extra bottom margin this used to carry (from when the two were unlabelled
   siblings and needed the distance to read as separate things) is gone — the two now carry
   headings, which is what tells them apart. */
.cat-mgmt-card {
  flex-shrink: 0;
}
.cat-mgmt-td-name {
  width: 260px;
}
.cat-mgmt-td-count,
.cat-mgmt-td-actions {
  white-space: nowrap;
}
.cat-col-head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 28px;
}
.cat-col-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background-color: var(--cat-accent, #9ca3af);
  flex-shrink: 0;
}
.cat-col-title {
  min-width: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cat-col-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 20px;
  padding: 0 7px;
  border-radius: 100px;
  background-color: #eef2f9;
  color: #6b7280;
  font-size: 11.5px;
  font-weight: 700;
  flex-shrink: 0;
}
.cat-col-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 2px;
  flex-shrink: 0;
}
@media (max-width: 768px) {
  .cat-mgmt-td-name {
    width: auto;
  }
}
/* Read-only chip shown when the settings panel is collapsed and a city is already set —
   the LEGACY (manually-tagged, no jurisdiction_key) fallback only. */
.cat-col-tags-summary {
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  font-size: 11px;
  font-weight: 600;
  color: #475569;
  background-color: #eef2f9;
  border-radius: 100px;
  padding: 3px 10px;
  margin-top: -4px;
}
/* Auto-categorization's constraint display: a category's resolved `jurisdiction_key`
   (label + regulatory note), the PRIMARY constraint summary once a category carries one —
   see `CategoryChip`. Unlike `.cat-col-tags-summary` (a short one-line pill) this can hold a
   full sentence, so it wraps as two lines of block text instead of a pill that would
   overflow the card. */
.cat-col-jurisdiction-summary {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-top: -2px;
}
.cat-col-jurisdiction-label {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}
.cat-col-jurisdiction-note {
  font-size: 10.5px;
  line-height: 1.35;
  color: var(--hr-dash-recent-subtitle-color, #6b7280);
}
/* Read-only derived-tag transparency line under `<JurisdictionField>`'s picker — the legacy
   city/salary_range Basic-plan-eligibility tag the server derives from whichever
   `jurisdiction_key` was just picked (`deriveLegacyCategoryTags`), never recomputed
   client-side. */
.cat-col-jurisdiction-derived {
  margin: 0;
  font-size: 11px;
  color: var(--hr-dash-recent-subtitle-color, #6b7280);
}

/* ── Multi-select bulk-move bar: appears once 1+ rows are checked, mirrors
   the census list's floating bulk-action bar shape/positioning. Its
   destination picker is the same `<Combobox>`-based `.cat-quick-assign-select`
   the per-row picker uses (Charte-aligned; primary blue, never the brand
   accent — the accent is reserved for badges/"action needed", not a CTA). ── */
.cat-selection-bar {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 16px;
  border-radius: 14px;
  background-color: #ffffff;
  border: 1px solid #e5e7eb;
  box-shadow: 0 12px 40px rgba(10, 51, 115, 0.18);
}
/* The SAME bar, pinned in `<GroupMembersModal>`'s footer instead of floating. That modal is
   already a height-bounded card whose footer never scrolls away, so it needs no fixed
   positioning — and a second `position: fixed` element over an open modal is how two overlays
   end up fighting for the same corner of the viewport. Border/shadow/radius are released with
   the float: inside the card the footer's own top rule is the edge. */
.cat-selection-bar--inline {
  position: static;
  transform: none;
  z-index: auto;
  width: 100%;
  border: none;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  border-radius: 0;
  box-shadow: none;
  padding: 12px 24px;
  flex-wrap: wrap;
}

.cat-selection-bar-count {
  font-size: 13px;
  font-weight: 600;
  color: #18181b;
  white-space: nowrap;
}
.cat-selection-bar-clear {
  background: none;
  border: none;
  padding: 0;
  font-size: 13px;
  color: #6b7280;
  text-decoration: underline;
  cursor: pointer;
}
.cat-selection-bar-divider {
  width: 1px;
  height: 22px;
  background: #e5e7eb;
  flex-shrink: 0;
}
.cat-selection-bar-move {
  display: flex;
  align-items: center;
  gap: 6px;
  height: 36px;
  padding: 0 16px;
  border-radius: 8px;
  border: none;
  background-color: var(--brand-secondary, #4374f3);
  color: #fff;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.cat-selection-bar-move:disabled {
  background-color: #d2d9e5;
  color: #9ca3af;
  cursor: not-allowed;
}
@media (max-width: 768px) {
  .cat-selection-bar {
    left: 12px;
    right: 12px;
    bottom: 12px;
    transform: none;
    flex-wrap: wrap;
    justify-content: center;
  }
}

/* Warning icon on a stale-data orphan row (a dependant whose employee sits in
   a different category) — reused as-is from the old board/list cards. */
.cat-card-orphan-icon {
  color: var(--brand-warning, #eca04b);
  flex-shrink: 0;
}
/* "+N dependants" collapsible toggle on an employee row — reused as-is from
   the old board/list cards. */
.cat-card-deps-toggle {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  padding: 2px 6px;
  border: none;
  border-radius: 100px;
  background-color: #eef2f9;
  color: #6b7280;
  font-size: 10.5px;
  font-weight: 700;
  cursor: pointer;
}
.cat-card-deps-toggle:hover {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, #eef2f9);
  color: var(--brand-secondary, #4374f3);
}

/* Inline rename, in place of a chip's own title. GEOMETRY IS LOCAL: it has to
   be the height of the title row it replaces, or renaming reflows the chip. */
.cat-rename-input {
  flex: 1;
  min-width: 0;
  height: 32px;
  border: 1px solid var(--field-border, #d2d9e5);
  border-radius: var(--field-radius, 8px);
  background-color: var(--field-bg, transparent);
  padding: 0 9px;
  font-size: var(--field-font-size, 13px);
  color: var(--field-color, #111827);
  outline: none;
  box-sizing: border-box;
}
.cat-rename-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  box-shadow: var(--field-focus-ring, 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent));
}

/* Per-category settings panel (name field's sibling): now hosts `<JurisdictionField>`'s
   Jurisdiction picker (was City/Salary Range selects — see `inline-create-category-form.tsx`'s
   Round 11 note). Jurisdiction still ultimately backs the "Basic" quotation-type eligibility
   gate (`cotation-type-selector.tsx`) via the server-derived legacy city/salary_range tags. */
/* `<JurisdictionField>`'s stack. IT IS NO LONGER A BOX (2026-08-15): the white
   fill, the 1px border and the shadow are gone, and with them the worst detail on
   the create form.

   The two fields of one form rendered under two different contracts. Heale runs
   the field contract BORDERLESS on purpose (`--field-border: transparent`, see
   heale/style.css) because the cream-50 fill IS the boundary on a white card — so
   the name input is a cream pill with no edge, exactly as designed. This wrapper
   then drew a bordered white PANEL around the field below it, so one form showed a
   borderless field and a boxed one, a line apart.

   The box is a leftover from when this was a settings SUB-ROW that expanded inside
   the management table and needed to look like a panel; it is now two fields inside
   a form, and a form does not put a card around half of itself. Removing it also
   un-boxes the same field inside `CategoryEditModal`, where a bordered white panel
   on a white modal body was the same mistake with less contrast.

   (Fields on a CREAM surface still need an edge — that is what `.fields-on-tint`
   is for, and it is a container decision, not a per-field one.) */
.cat-col-settings {
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.cat-col-settings-field {
  display: flex;
  flex-direction: column;
  gap: var(--field-label-gap, 4px);
}
/* Label size/tone from the contract; the weight stays 500 rather than the §9
   uppercase-bold treatment, because turning this into a section label is a design
   change, not a tokenization. */
.cat-col-settings-label {
  font-size: var(--field-label-size, 11px);
  font-weight: 500;
  color: var(--field-label-color, #374151);
}
/* Label + inline help-icon row for the city/salary_range fields — a first-time
   user has no way to know these gate Basic-plan eligibility without this hint
   (native `title` tooltip on the icon, no bespoke Tooltip component needed). */
.cat-col-settings-label-row {
  display: flex;
  align-items: center;
  gap: 4px;
}
.cat-col-settings-help {
  display: inline-flex;
  color: #9ca3af;
  cursor: help;
  flex-shrink: 0;
}
/* Trigger for the Jurisdiction/Country `<Combobox>` in `<JurisdictionField>` — was a native
   `<select>` until the app-wide "no bare browser controls" pass. Its geometry matched the create
   form's own input until that form moved to the shared `.ins-pol-input`; it still matches
   `.cat-rename-input` in this same panel, which is why it is unchanged here — restyling it would
   reach the category EDIT modal, a different surface. `.mf-combo-trigger` contributes the
   flex/cursor/text-align a button needs that a `<select>` got for free. */
.cat-col-select {
  width: 100%;
  height: var(--field-height, 36px);
  border-radius: var(--field-radius, 8px);
  border: 1px solid var(--field-border, #d2d9e5);
  padding: 0 var(--field-padding-x, 10px);
  font-size: var(--field-font-size, 13px);
  color: var(--field-color, #111827);
  outline: none;
  box-sizing: border-box;
  background: var(--field-bg, #fff);
}
.cat-col-select:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}
.cat-col-select:disabled {
  background: var(--field-disabled-bg, #f9fafb);
  color: var(--field-disabled-color, #9ca3af);
  opacity: var(--field-disabled-opacity, 1);
  cursor: not-allowed;
}

/* Locked-state reason note inside `CategoryEditModal`, shown next to the disabled
   City/Salary Range controls once a category has 1+ assigned members (a direct user
   correction: those members' jurisdiction wouldn't match a changed City/Salary Range
   any more). Same "greyed control + adjacent explanatory note" convention as the
   insurer-documents "Assign a policy first" note — never a silently-disabled control. */
.cat-modal-lock-note {
  margin: 0;
  font-size: 12px;
  font-weight: 500;
  color: var(--dash-status-pending-color, #9ca3af);
}

/* ── The dashed "add another list" trigger is DELETED, not orphaned (2026-08-15) ──
   Two rules and a mobile floor lived here for a Trello-style dashed "+ Add group" button
   rendered as the groups table's trailing row. A direct user correction moved that action into
   the table's HEAD ("put Add group in the upper part of the table, so it's consistent"), where
   it is the shared §6 primary button — so the dashed shape, its two raw hex values and its own
   44px tap floor all went with it. Nothing reads those class names any more; their behaviour
   lives in `.hr-dash-recent-btn-primary` + `.filter-tabs-right-slot`. ── */
/* ── The create-category form (`<InlineCreateCategoryForm variant="card">`) ──
   `.cat-add-form`, `-input`, `-tags-toggle`, `-actions`, `-submit` and `-cancel` were HERE and
   are DELETED, not orphaned: six rules re-deriving the field contract and two button shapes from
   raw hex, on a form reached from `/hr/quotes`, where every field is `.ins-pol-*` and every
   action is one of two button weights. The form now composes those directly — a labelled
   `.ins-pol-field` + `.ins-pol-input`, the shared `<Combobox>`, and `.wizard-actions` (Cancel
   outline first, primary last).

   `.cat-add-tags-toggle` went with the "+ Set jurisdiction" disclosure it opened: a 12px link
   occupying the same vertical space as the single optional field it hid, in a form with exactly
   one other field. See the component header.

   What is left here is the stacking, which no shared class owns: this form is a column of fields
   with an action row under it. */
.cat-create-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
}

/* The two fields SIDE BY SIDE. Stacked in a 340px column they made a four-element
   first-run state ~700px tall; the block now fits in a card head. `form-row` (the
   shared mobile rule) collapses this to a column under 768px, so no width query
   lives here. */
.cat-create-fields {
  display: flex;
  align-items: flex-start;
  gap: 14px;
}

.cat-create-fields > * {
  flex: 1;
  min-width: 0;
}

/* The action row hugs the LEFT edge, under the fields it commits, and the button
   keeps its normal size. It used to span the form's full width with a stretched
   filled button — the only filled control on the whole screen, sized like a page
   CTA for a two-field inline form. It stays the PRIMARY weight (creating the first
   group is the one thing this state exists for; §6 "full = fix/primary"), just not
   the size of the card it sits in. `.wizard-actions`'s own 32px top margin is
   released — the form's `gap` already spaces it. */
.cat-create-form .wizard-actions {
  justify-content: flex-start;
  margin-top: 0;
}

/* The Jurisdiction field is `<JurisdictionField>`, shared with the category EDIT modal — an
   out-of-scope surface — so its label keeps the `.cat-col-settings-*` shape it has there. Inside
   THIS form that left two label styles a line apart: the name's uppercase-bold `.ins-pol-field-label`
   and the jurisdiction's sentence-case medium. Rather than restyle the shared component (which
   would silently change the modal), the seam is closed where it shows, reading the same two
   contract tokens the base label does. If the modal is ever brought into this language, delete
   this block and let the component carry it. */
.cat-create-form .cat-col-settings-label {
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--field-label-color, #9ca3af);
}

/* "Optional" beside a field label — the one thing that has to be said now the Jurisdiction field
   is shown rather than hidden behind a disclosure that implied it. §3 metadata: it qualifies the
   label, it is not a status. */
.cat-col-settings-optional {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* ── There is no zero-groups first-run STATE any more (2026-08-15) ──
   It was, in order: a centred container-less column between two cards; a region inside the member
   card's head; and finally its own card, titled "No groups yet", holding a create form. A direct
   user correction removed the state itself — "if there is no group at all, just display the 'Not
   in a group' group with the member count next to it" — so zero groups is now the ordinary groups
   table with one row, and the explanatory line moved to the page's `<DashTopbar>` subtitle.

   What survives here is the INSET, because the groups table's loading slot (two shimmer bars)
   still wants the card's own horizontal padding. It keeps the name it had rather than gaining a
   new one, so a `git log -S` on either still finds this history.

   The centring and the 56px icon pad that preceded both are gone for good: `member-cards.tsx`'s
   header records the user removing these pads across three surfaces, one returning from a Charte
   maquette, and the user removing it again — "the user's review is the tiebreaker, not the
   maquette. Do not restore the pads from a maquette again; ask." */
.cat-firstrun {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 24px 22px;
}

/* The by-group / by-employee switch (§7 underline tabs — the selection SWAPS the view). It sits
   at the top of the card, so it needs the card's own horizontal inset, and its rule reads as the
   card's first internal divider.

   THE RULE MOVED FROM THE TABS TO THIS WRAPPER, and that is the whole reason this block grew:
   the row now also carries the table's "Add group" action in `<FilterTabs>`'s right slot, and
   `.tabs-underline`'s own `border-bottom` spans only the tab strip — so the divider would have
   stopped short and left a notch under the button. Drawing it on the wrapper keeps it full-width
   (§7) whatever the slot holds. The wrapper has no bottom padding, so the segs' `margin-bottom:
   -1px` still overlaps the active tab's 2px underline onto it exactly as before.

   ═ THE ROW HAS TO HOLD A §6 ACTION, NOT JUST A §7 TAB STRIP ═

   The action arrived in this row without the row's geometry changing, and the two do not fit:
   a tab strip's clearance IS its seg padding (`8px 0 12px`), which the button does not have, and
   at the default size the button is as tall as the whole strip. Rendered, it sat ON the divider
   with 4px of card above it — the whole head was button. Neither reference has this problem
   because neither row is a tab strip: /hr/census's "Add member" sits in an 18px-padded
   `.ins-pol-toolbar`, /hr/quotes/archive's pair floats in the page body's 18–22px gaps.

   Two facts decide the fix, and both are arithmetic rather than taste:

     1. THE STRIP'S TOP PADDING MUST BE ITS BOTTOM PADDING PLUS THE 2px UNDERLINE. Only then is
        the tab LABEL's centre the ROW's centre, and only then does a vertically-centred action
        share the labels' centre line. 14 = 12 + 2, and it holds at any line-height. The §7
        bottom padding is untouched, so the underline still hugs the label.
     2. WITH THE CENTRES ALIGNED, THE ACTION'S CLEARANCE IS (label height − 4) / 2 — fixed by
        the label's anchor, not by how tall this head is made. So the only way to give a
        centred action room above the divider is a SHORTER action: the button carries
        `--sm` (32px), the modifier that exists for exactly this ("dense rows where the default
        shape is too tall"), and keeps the §6 primary weight it is meant to have.

   Growing the wrapper instead was tried on paper and fails: every extra pixel goes BELOW the
   labels (they are anchored to the rule) while the button stays centred, so the two drift apart
   and the strip's underline detaches from its own label. */
.cat-view-tabs {
  padding: 4px 24px 0;
  border-bottom: 1px solid var(--tabs-underline-divider, #e5e7eb);
  --tabs-underline-seg-padding: 14px 0 12px;
}

.cat-view-tabs .tabs-underline {
  border-bottom: none;
}

/* The tab strip must reach the rule whichever side of the row is taller — `.filter-tabs-row`'s
   own `align-items: center` would float the active tab's underline above it the moment the
   action button out-grew the tabs. The button is unaffected: `.filter-tabs-right-slot` centres
   its own children. */
.cat-view-tabs .filter-tabs-row {
  align-items: stretch;
}

@media (max-width: 768px) {
  /* Same 44px floor `.qd-block-action` takes, for the same reason and with the same release:
     `--sm` sets `height`, so `min-height` alone would lose to it. At this width the row wraps
     and the action gets a line of its own — which is directly on the rule unless the slot
     carries the clearance the wrapper's zero bottom padding cannot. */
  .cat-view-tabs .hr-dash-recent-btn-primary--sm {
    height: auto;
    min-height: 44px;
  }

  .cat-view-tabs .filter-tabs-right-slot {
    padding-bottom: 10px;
  }
}

/* The by-employee toolbar's count line, pushed to the far end of the row it shares with the
   search pill and the group filter. Same `.qd-fix-count` treatment the modal heads use. */
.cat-view-count {
  margin: 0 0 0 auto;
  white-space: nowrap;
}

/* ── "Not in a group" — the work-queue row of the groups table ──
   A row, not a derived fact: it is the same kind of thing as the group rows above it (a bucket of
   members you open), and HR must not have to subtract head counts from a total to find it.

   Coral ONLY while the count is non-zero (§12 "action needed" — the quotes dashboard already
   spends coral on exactly this fact, because an ungrouped employee silently breaks a quote
   request). At zero it stays a plain quiet row: a coral row that can never be cleared teaches the
   reader to ignore the colour. */
.cat-mgmt-tr--unassigned .cat-col-title {
  color: var(--ins-pol-subtext-color, #8d8579);
}

.cat-mgmt-tr--unassigned-open .cat-col-title,
.cat-mgmt-tr--unassigned-open .cat-col-count {
  color: var(--brand-accent, #fb7a5c);
}

.cat-mgmt-tr--unassigned-open .cat-col-count {
  background-color: color-mix(in srgb, var(--brand-accent, #fb7a5c) 14%, transparent);
}

/* ── Per-row category-assignment control (the flat member list's ONLY way to
   (re)assign a member, alongside the checkbox + floating bulk-move bar) — a
   searchable `<Combobox>` (`combobox.tsx`), not a native `<select>`: picking
   "+ Create new category…" swaps it for an inline name input reusing
   `.cat-rename-input`'s geometry, matching this screen's own inline-rename
   pattern instead of `window.prompt`. The SAME classes back the floating
   bulk-move bar's destination picker (`allowCreate` off there). Categories a
   member's jurisdiction is incompatible with render inside the combobox's
   own panel as grey, unselectable options (`.mf-combo-opt--disabled` in the
   combobox's own CSS) — never hidden. */
.cat-quick-assign-select {
  height: 30px;
  min-width: 150px;
  max-width: 220px;
  border-radius: var(--field-radius, 8px);
  border: 1px solid var(--field-border, #d2d9e5);
  padding: 0 8px;
  font-size: 12.5px;
  color: var(--field-color, #111827);
  background: var(--field-bg, #fff);
  outline: none;
  box-sizing: border-box;
  cursor: pointer;
}
.cat-quick-assign-select:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}
.cat-quick-assign.cat-quick-assign-create {
  display: flex;
  align-items: center;
  gap: 4px;
}
.cat-quick-assign-create .cat-rename-input {
  height: 30px;
  min-width: 150px;
  max-width: 190px;
}

/* Missing-required-information note (§5 of the manage-route rework) — replaces the
   assignment combobox entirely for an incomplete member, in the same "fix in place"
   tone as `<MissingInfoModal>` (`.qd-missing-info-tag`/`.qd-missing-info-link`, reused
   verbatim below rather than re-skinned). */
.cat-missing-info-cell {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  max-width: 240px;
}

/* The member's CURRENT category name, when they already have one — plain read-only text (never
   a combobox/link) shown alongside the note above so an incomplete-but-assigned member doesn't
   silently disappear from their category's view in the flat list. */
.cat-missing-info-category {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* Toolbar category filter — a plain native `<select>` (not the searchable `<Combobox>` used for
   assignment) narrowing the flat member table to one category / Unassigned / All. Same field
   geometry as `.cat-col-select` but its own class since the two are semantically distinct
   (category-tag editor vs. a list filter) even though they share a look. */
.cat-filter-select {
  height: 40px;
  min-width: 160px;
  border-radius: var(--field-radius, 8px);
  border: 1px solid var(--field-border, #d2d9e5);
  padding: 0 10px;
  font-size: 13px;
  color: var(--field-color, #111827);
  background: var(--field-bg, #fff);
  outline: none;
  box-sizing: border-box;
  cursor: pointer;
}
.cat-filter-select:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}
@media (max-width: 768px) {
  .cat-quick-assign-select {
    max-width: none;
    width: 100%;
  }
  .cat-quick-assign.cat-quick-assign-create .cat-rename-input {
    max-width: none;
    flex: 1;
  }
  .cat-filter-select {
    width: 100%;
    min-width: 0;
  }
}

/* =====================================================================
   Insurer Quoting board — `/insurer/quotes/quoting` (`.qk-*`).

   THE PIPELINE HALF of the `/insurer/quotes` split: the requests this insurer
   IS bidding on, as FOUR columns (Quoting · Comparing · Pre-selected · Won)
   with declined/expired/lost stacked BELOW the last column
   (`.qk-col-terminal`), never as a fifth. The triage half — requests not yet
   answered — is a LIST, `.iqn-*`, further down; a narrow column cannot give a
   company name the width it needs, which is what this board's `nowrap` +
   `ellipsis` title used to prove by rendering `Gulf M…`.

   Not drag-and-drop, and not dynamic columns like `.cat-*`/`CategoriesBoard`
   above — hence its own namespace rather than reusing `.cat-col`. Every
   transition on this board is the COMPANY's to make, so there are no
   `--can-drop`/`--drop-over` states to mirror from `.ins-claim-col`.

   Column chrome mirrors `/insurer/claims` (`.ins-claim-*`): the board sits
   inside an outer `.hr-dash-recent-card`, this inset `.qk-board-wrap` is the
   tinted (`--dash-body-bg`) panel behind the grid, individual `.qk-col`s are
   borderless (only the cards are boxed), and each header is a small coloured
   dot + plain navy title + count pill with a 2px accent underline. Per-status
   colour lives ONLY on the dot + underline, via `--qk-col-accent`.

   §1: every colour here is a token. The retired `#dc2626`/`#eca04b` timer pair
   and the off-palette `#7e22ce` purple / `#9a3412` / `#d97706` / `#059669`
   column accents are GONE — mapped onto blue-400 / gold-500 / green-500 /
   ink-400. Do not reintroduce a hue that is not one of the 23. */
.qk-board-wrap {
  /* `flex: 1; min-height: 0;` so the wrap fills its flex parent (the
     `.hr-dash-recent-card`/kanban shell) and scrolls internally — every
     consumer (HR quotes, insurer quotes, the loading skeleton) needs this,
     so it lives on the base class instead of being repeated at each call site. */
  flex: 1;
  min-height: 0;
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 18px 20px;
  background-color: var(--dash-body-bg, #f8fafd);
}

/* Full-height page shell for the Quote Requests loading skeleton — same
   shape as the real page's own bare wrapper div. */
.qk-loading-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}
/* Board grid: FOUR equal columns that shrink to fit (no overflow on wide
   screens) — same mechanism (and gap) as `.ins-claim-board` on
   `/insurer/claims`. Below ~1180px the columns get too cramped, so the
   responsive block further down switches this to a horizontal scroller with
   fixed-width columns, mirroring the claims board's own breakpoint.

   WENT 6 → 4. The old board carried `received` (now its own triage LIST) and a
   `declined_expired` column (now stacked under Won). Four columns at the same
   gap means each one is ~50% wider than before, which is what buys the card
   title enough room to WRAP instead of being clipped. */
.qk-board {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
  height: 100%;
  min-height: 420px;
}
.qk-col {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.qk-col-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 7px;
  padding: 10px 4px;
  margin-bottom: 8px;
  border-bottom: 2px solid var(--qk-col-accent, #e5e7eb);
}
.qk-col-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background-color: var(--qk-col-accent, #9ca3af);
  flex-shrink: 0;
}
.qk-col-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  white-space: nowrap;
}
.qk-col-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 100px;
  background-color: var(--ins-pol-chip-count-bg, #eef2f9);
  color: var(--ins-pol-filter-color, #6b7280);
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
}
.qk-col-body {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  min-height: 0;
}
/* An empty column is a statement about the pipeline's shape, not a dropzone —
   so it carries no dashed outline. §8 has no dashed-border surface at all, and
   the old one read as "drag something here" on a board that has never been
   draggable. Just the sentence, in §3 metadata tone. */
.qk-col-empty {
  padding: 18px 12px;
  text-align: center;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* Per-status colour lives ONLY on the header dot + underline (via
   `--qk-col-accent`, the same custom-property pattern as
   `.ins-claim-col-head--*`), and every value is now a §12 semantic token:

     Quoting      → blue-400  (open, in progress)
     Comparing    → gold-500  (waiting on the company)
     Pre-selected → gold-500  (still waiting, one step further along)
     Won          → green-500 (completed)

   Comparing and Pre-selected deliberately SHARE gold: they are the same answer
   to "whose move is it" (the company's), and the column header already tells
   them apart by name. The purple that used to separate them (`#7e22ce`) is not
   in the 23-token palette — it was borrowed from the Basic tag, which is a
   product marker, not a stage. */
.qk-col--quoting .qk-col-head { --qk-col-accent: var(--dash-status-info-color, #2563eb); }
.qk-col--comparing .qk-col-head { --qk-col-accent: var(--dash-status-pending-color, #d97706); }
.qk-col--pre_selected .qk-col-head { --qk-col-accent: var(--dash-status-pending-color, #d97706); }
.qk-col--won .qk-col-head { --qk-col-accent: var(--dash-status-approved-color, #059669); }

/* ── THE STACKED TERMINAL GROUP ──
   Declined · Expired · Lost, inside the Won column rather than beside it. The
   separator is a hairline with the label riding on it, exactly
   `.ins-claim-col-rejected-sep`'s shape on `/insurer/claims` (Rejected under
   Paid) — two boards in one product must not answer "where do terminal states
   go" two different ways.

   Tone is INK-400, not rust: these are three ordinary endings, and only one of
   them (declined) is even a refusal. Rust would make every closed request read
   as an error. */
.qk-col-terminal {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.qk-col-terminal-sep {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 14px 0 0;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qk-col-terminal-sep::before,
.qk-col-terminal-sep::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--hr-dash-recent-row-border, #eee8df);
}

/* A closed card is present, not prominent: full contrast on the company name
   (it still has to be identifiable) and a quieter card surface. */
.qk-col-terminal .qk-card {
  background-color: var(--modal-total-bg, #faf6f0);
}

/* §8: NO BORDER — the shadow carries the card. The `1px solid #e5e7eb` this
   replaces was both a raw hex and a contradiction of the elevation contract, and
   at four cards deep in a column the stacked hairlines read as a table.

   It is an `<a>` (the card used to be a `<div onClick>`, which is focusable by
   nobody and cmd+clickable by nobody), so it resets link colour/decoration and
   carries a real `:focus-visible` ring — the shadow-only hover state is invisible
   to a keyboard. */
.qk-card {
  display: block;
  background-color: #ffffff;
  border-radius: var(--brand-radius, 12px);
  overflow: hidden;
  color: inherit;
  text-decoration: none;
  box-shadow: var(--dash-stat-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
  transition: box-shadow 0.14s, transform 0.14s;
}
.qk-card:hover {
  box-shadow: 0 4px 14px rgba(10, 51, 115, 0.08);
  transform: translateY(-1px);
}
.qk-card:focus-visible {
  outline: 2px solid var(--brand-secondary, #4374f3);
  outline-offset: 2px;
}

/* §17 loading row — the card's own shape, shimmered. */
.qk-card--skeleton {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
  pointer-events: none;
}

/* One-time highlight for a `<tr>` in the `/hr/quotes` table — border/box-shadow
   don't reliably render on table rows across browsers, so this variant only
   animates background-color. */
.hr-dash-recent-tr--just-submitted {
  animation: hr-dash-recent-tr-just-submitted-fade 2.6s ease forwards;
}
@keyframes hr-dash-recent-tr-just-submitted-fade {
  0% { background-color: #eafbf1; }
  60% { background-color: #eafbf1; }
  100% { background-color: transparent; }
}
.qk-card-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px;
}
/* `align-items: flex-start` + `flex-wrap`: the title now wraps to two or three
   lines, so a tag vertically centred against it would float mid-name. Tags sit at
   the top, beside the first line. */
.qk-card-title-group {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}
/* §21, THE WORST VIOLATION ON THE OLD SCREEN: this was `white-space: nowrap` +
   `text-overflow: ellipsis` in a 1/6-width column, so a real company name rendered
   `Gulf M…` / `Al Nak…` — two different companies, indistinguishable, in the field
   that IS the card's identity. It wraps now, at any width, and
   `overflow-wrap: anywhere` covers the one-long-token case so a wrap is always
   possible. Colour comes from the title token, not `#111827`. */
.qk-card-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
  min-width: 0;
  line-height: 1.35;
  overflow-wrap: anywhere;
}
.qk-card-tag {
  flex-shrink: 0;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 2px 8px;
  border-radius: 100px;
}
/* KEPT — the Basic marker is real and useful. Only its colour changed: it wore a
   `#7e22ce` purple on an `rgba(168,85,247,…)` fill, a hue that is not one of the
   23. "Basic" is a PRODUCT marker (which quotation form the company used), not a
   status, so §1's tag surface — sand-200 with body ink — is the right home for it;
   giving it a semantic hue would make it compete with the state pill beside it.
   (`.qd-banner--basic` on the detail page still carries the old purple; it was not
   in this pass's scope.)
   The tag surface is the quote family's OWN `--quote-tag-neutral-*` pair, not
   `--hr-dash-recent-card-tinted-bg` + `--hr-dash-recent-td-color`: both resolve to
   sand-200/ink-700 here, so borrowing them looked right AND rendered right, but
   their signed-off meanings are "read-only summary CARD" and "table BODY CELL" —
   see the token's own comment in `heale/style.css`, and `.qd-blocker-text` for the
   same call caught the same way. */
.qk-card-tag--basic {
  background-color: var(--quote-tag-neutral-bg, #f2ede5);
  color: var(--quote-tag-neutral-color, #4a4640);
}
.qk-card-tag--auto {
  background-color: var(--dash-status-info-bg, #dbeafe);
  color: var(--dash-status-info-color, #2563eb);
  font-size: 10px;
  letter-spacing: 0.05em;
}
/* A request the company withdrew. Same `--dash-status-rejected-*` pair as the
   detail banner (`.qd-banner--cancelled`), so the board card and the page it
   opens state the same thing in the same tone. */
.qk-card-tag--cancelled {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  color: var(--dash-status-rejected-color, #dc2626);
}

/* A cancelled request's "when and why" line on a CARD — the insurer board's
   cards and HR's own active-request card, which is why the class is not in
   either page's namespace. The board is where an insurer first meets the
   cancellation, so the reason belongs on the card rather than one click away;
   clamped to two lines so one long reason cannot push a card past its
   neighbours. A compact list ROW, with a single meta string to work with, uses
   `cancellationMetaSegment()` instead. */
.quote-cancelled-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--dash-status-rejected-color, #dc2626);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  overflow-wrap: anywhere;
}

.quote-cancelled-note-date {
  font-weight: 700;
}
/* ── WHOSE MOVE IT IS ──
   THE RULE THIS REPLACED A COUNTDOWN WITH: this board states whose move a request
   is, and shows NO deadline at all. A deadline belongs to the TRIAGE list, because a
   bid already placed is past it — so nothing here may reintroduce a bare day count,
   and in particular not in `--brand-error` (`#dc2626`) or `--brand-warning`
   (`#eca04b`), which is how the retired predecessor coloured one. §1 lists both as
   superseded.

   Each tone is a §2 tinted surface: the token at 14% for the fill, the SAME token
   at 100% for the text and the dot. Same three-answers-three-tones vocabulary as
   `/hr/quotes`'s `.qd-request-state`, so one request cannot read "your move" on one
   portal and "waiting" on the other. The dot is `currentColor`, so a tone can never
   be changed in one place and not the other. */
.qk-card-move {
  display: inline-flex;
  align-items: flex-start;
  gap: 6px;
  align-self: flex-start;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.4;
  min-width: 0;
  overflow-wrap: anywhere;
}
.qk-card-move-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  margin-top: 5px;
  border-radius: 999px;
  background-color: currentColor;
}
/* §12 open — your move. */
.qk-card-move--you {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, transparent);
  color: var(--brand-secondary, #4374f3);
}
/* §12 pending — out with the company, nothing on you. */
.qk-card-move--company {
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 14%, transparent);
  color: var(--dash-status-pending-color, #d97706);
}
/* §12 completed. The ONLY green on this board — see `quotingCardView`'s header for
   why an under-budget delta deliberately does NOT get it. */
.qk-card-move--won {
  background-color: var(--dash-status-approved-bg, #dcfce7);
  color: var(--dash-status-approved-color, #059669);
}
/* §12 cancelled — ink-400. Three ordinary endings, only one of which is a refusal;
   rust would make every closed request read as an error. */
.qk-card-move--closed {
  background-color: var(--dash-status-inactive-bg, #f3f4f6);
  color: var(--dash-status-inactive-color, #8d8579);
}

/* ── THE FOUR FACTS ──
   TWO RULES, EACH LEARNED FROM A RETIRED PREDECESSOR. One was three icon rows, a
   `DollarSign` beside an AED figure among them, every icon carrying a hardcoded
   `#7d93b3` inline against §18 — so no icon on this card may name its own colour.
   The other was a footer BAR filled with the primary-button blue-400, carrying the
   two least decision-relevant facts in the loudest colour on the page, identically
   on a won card and a declined one — so `--brand-secondary` is never spent on a
   card's least important facts. Both are named in `quoting-card.tsx`'s own "what is
   gone" header, which is where the search for either belongs.

   Now: four §3-labelled values in cream-50 inner cells (§8: radius 12, no shadow).
   `auto-fit`/`minmax` rather than `repeat(2, 1fr)`, so the grid folds to one column
   on the narrow board instead of overflowing (§21). No icons at all — a label says
   what a glyph only hints at, in less space. */
.qk-card-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 6px;
  margin: 0;
}
.qk-card-fact {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 7px 9px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
}
/* §3 section label: 9.5px Bold uppercase, tracking +6%, ALWAYS ink-400 — never
   semantically coloured, however urgent the value under it is. */
.qk-card-fact-label {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}
.qk-card-fact-value {
  font-size: 12px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
  overflow-wrap: anywhere;
}
/* The one fact that carries a tone: a premium ABOVE the company's budget, in the
   §12 caution tone. One-sided on purpose — at or under budget stays neutral,
   because green is this board's won/approval tone (§6) and spending it on "your
   price is fine" would make a lost request with a keen price read as good news. */
.qk-card-fact-value--over {
  color: var(--dash-status-pending-color, #d97706);
}

/* A cancelled request's reason line sits inside the card body's flex column, so it
   needs no margin of its own. */
.qk-card-body > .quote-cancelled-note {
  margin: 0;
}

/* Responsive: below ~1180px, four equal columns get too cramped, so switch the
   board to a horizontal scroller with comfortable fixed-width columns — same
   breakpoint/mechanism as `.ins-claim-board` on `/insurer/claims`. */
@media (max-width: 1180px) {
  .qk-board {
    grid-auto-flow: column;
    grid-auto-columns: 264px;
    grid-template-columns: none;
  }
}
@media (max-width: 768px) {
  .qk-board {
    grid-auto-columns: 220px;
  }
}

/* =====================================================================
   Insurer NEW REQUESTS list — `/insurer/quotes` (`.iqn-*`).

   THE TRIAGE HALF of the split. A LIST, not a board, and that is the whole
   point: deciding whether to bid is a per-row comparison of four facts, and the
   company name — the row's identity — needs width. On the six-column board this
   replaced, `.qk-card-title` was `nowrap` + `ellipsis` inside a 1/6-width
   column, so `Gulf Meridian Logistics` and `Al Nakheel Trading` both rendered as
   six characters and an ellipsis (§21: no text is clipped anywhere).

   THE `.qk-*` FAMILY IS SINGLE-PORTAL NOW: every rule in it belongs to the insurer
   Quoting board. `/hr/quotes` used to share it, and when that page became a
   requests-first dashboard its four board-chrome rules — a toolbar pair and two
   column modifiers — kept their definitions with no renderer anywhere for a whole
   release. This split swept them rather than leave them to rot. They are named, and
   pinned deleted, by `US-IQT.10.37`; do not re-add an HR selector here.

   Rows are `.hr-dash-recent-card` children separated by a sand-300 hairline —
   §10's row treatment, not a card each: a card per row inside a card is a
   double frame, and the shadow that §8 says carries a card cannot carry five
   of them stacked. */
.iqn-list {
  display: flex;
  flex-direction: column;
}

/* §10: 1px sand-300 divider between rows, NONE on the last one. */
.iqn-row {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}
.iqn-row:last-child {
  border-bottom: none;
}
.iqn-row--skeleton {
  pointer-events: none;
}

.iqn-row-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.iqn-row-title-group {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  min-width: 0;
}

/* §3 card title, and it WRAPS. `overflow-wrap: anywhere` covers the
   one-long-token case so a wrap is always possible. */
.iqn-row-title {
  font-size: 16px;
  font-family: var(--heading-font, inherit);
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #1c1a17);
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Tags: KEPT (Basic and Auto both earn their place), just moved out of the
   company name's way — the head is a wrapping flex row, so a long name pushes
   them to the next line instead of squeezing itself. Same tones as their
   `.qk-card-tag--*` twins, so one marker looks the same on both screens. */
.iqn-tag {
  flex-shrink: 0;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 3px 9px;
  border-radius: 999px;
}
.iqn-tag--basic {
  background-color: var(--quote-tag-neutral-bg, #f2ede5);
  color: var(--quote-tag-neutral-color, #4a4640);
}
.iqn-tag--auto {
  background-color: var(--dash-status-info-bg, #dbeafe);
  color: var(--dash-status-info-color, #2563eb);
}
.iqn-tag--cancelled {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  color: var(--dash-status-rejected-color, #dc2626);
}

/* ── THE DEADLINE, THE ONE THING ON THE ROW WITH A TONE ──
   A tone is a claim on attention; on a triage queue exactly one thing is worth
   claiming it, and it is not the budget. Each is a §2 tinted surface (token at
   14% for the fill, the SAME token at 100% for text and dot), from §12's
   vocabulary only — the retired `#dc2626`/`#eca04b` pair the old timer used
   appears nowhere. */
.iqn-deadline {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 4px 11px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
}
.iqn-deadline-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background-color: currentColor;
}
/* The window has closed. Rust, the §1 destructive tone — this row is here to be
   read, not answered. */
.iqn-deadline--overdue {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  color: var(--dash-status-rejected-color, #dc2626);
}
/* §12 action-needed: coral, and the only coral on the screen. Answer today. */
.iqn-deadline--urgent {
  background-color: color-mix(in srgb, var(--brand-accent, #fb7a5c) 14%, transparent);
  color: var(--brand-accent, #fb7a5c);
}
/* §12 pending — worth planning around, not yet urgent. */
.iqn-deadline--soon {
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 14%, transparent);
  color: var(--dash-status-pending-color, #d97706);
}
/* A date, not a deadline yet. */
.iqn-deadline--muted {
  background-color: var(--dash-status-inactive-bg, #f3f4f6);
  color: var(--dash-status-inactive-color, #8d8579);
}

/* ── THE FOUR FACTS ──
   Scope · Their budget · Cover starts · Respond by, each a §3 micro-label over a
   value in a cream-50 radius-12 inner cell (§8: no shadow on an inner cell).
   REPLACES three icon rows and a blue footer bar: an unlabelled `44d` beside an
   unlabelled `01/11/2026`, with the two calendar glyphs the wrong way round and
   a `DollarSign` on an AED figure.
   `auto-fit`/`minmax` rather than `repeat(4, 1fr)`: the grid folds to two
   columns and then one on its own, where a fixed count would overflow (§21). */
.iqn-row-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px;
  margin: 0;
}
.iqn-fact {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 9px 11px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
}
/* §3: 9.5px Bold uppercase, tracking +6%, ALWAYS ink-400 — a section label is
   never semantically coloured, however urgent its value. */
.iqn-fact-label {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  line-height: 1.3;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}
.iqn-fact-value {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
  overflow-wrap: anywhere;
}
/* The quieter second line — a detail ABOUT the value above it, never a second
   value, which is why it lives inside the same `<dd>`. */
.iqn-fact-note {
  font-size: 11px;
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The row's action, right-aligned. Wraps to its own line on narrow screens
   because the facts grid above it has already folded by then. */
.iqn-row-actions {
  display: flex;
  justify-content: flex-end;
}

@media (max-width: 768px) {
  .iqn-row {
    padding: 14px;
  }
  /* The action has the row to itself at this width, so it spans it and takes the
     44px tap-target floor explicitly — the button's own `11px 20px` padding and
     13px label come to ~38px, which is UNDER the floor. Same fix, same reason, as
     `.qd-request-row .hr-dash-recent-btn-primary` on `/hr/quotes`. */
  .iqn-row-actions > .hr-dash-recent-btn-primary {
    width: 100%;
    justify-content: center;
    min-height: 44px;
  }

  /* The search field in the filter row stops competing for width and takes the row,
     since `.ins-pol-filterbar` has already wrapped the pills above it. */
  .ins-pol-filterbar-search {
    flex: 1 1 100%;
    max-width: none;
  }
}

/* =====================================================================
   Insurer quote screens — `/insurer/quotes/[id]` and its
   `/categories/[categoryId]` child.
   Category/view tabs reuse `.ins-pol-tabs`/`.ins-pol-tab` (same component as
   `PolicyTabs`); section cards reuse `.hr-dash-recent-card` +
   `.ins-pol-card-body`. The classes below cover the page-specific shapes
   that don't have an existing generic equivalent: the Basic-plan banner,
   collapsible field sections, benefit/field value chips, the category
   comparator table and the offers list. The DECISION screen's own family is
   `.iqd-*`, at the end of this file.

   The header meta chip this note used to point at (`.pol-meta-widget` and its
   grid/row/item/label/val/sep parts) is RETIRED: the decision-screen rebuild
   replaced it with the §5 subtitle plus the hero's fact line, and it had no
   other consumer.
   ===================================================================== */

/* Full-height page shell (topbar + scrollable `.dash-body`) — the loading/
   not-found/loaded states of this page all render the same outer frame. */
.qd-page-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-height: 0;
}

/* Small brand-tinted icon/text helpers reused across this page (spinners,
   collapsible chevrons, tab headings) so a CSS variable color never has to
   be set inline. */
.qd-icon-secondary { color: var(--brand-secondary, #4374f3); }
.qd-text-secondary { color: var(--brand-secondary, #4374f3); }
.qd-icon-primary { color: var(--brand-primary, #0a3373); }
.qd-icon-warning { color: var(--brand-warning, #eca04b); }
.qd-text-error { color: var(--brand-error, #dc2626); }

/* Fixed slide-over shell for `<DocPreviewPanel>` — no split-pane layout on
   this page, so the preview renders as a right-edge overlay. */
.qd-doc-preview-slideover {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 60;
  box-shadow: -4px 0 24px rgba(15, 23, 42, 0.12);
}

/* Backdrop for <DocPreviewDrawer> — turns the fixed right-edge
   `.qd-doc-preview-slideover` shell into a proper slide-over: dim the page and
   close on click-outside (Escape is handled in the component). */
.doc-drawer-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(15, 23, 42, 0.45);
  animation: doc-drawer-fade 0.15s ease;
}

@keyframes doc-drawer-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Keep the 440px preview panel within the viewport on narrow screens. */
.doc-drawer-overlay .mf-preview {
  width: min(440px, 92vw);
}

/* "Basic" plan informational banner (Dubai EBP vs. generic basic-plan request) */
.qd-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  border-radius: 10px;
  padding: 14px 20px;
  margin-bottom: 20px;
}
.qd-banner-title {
  font-size: 14px;
  font-weight: 700;
  margin: 0 0 2px;
}
.qd-banner-text {
  font-size: 13px;
  margin: 0;
}
.qd-banner--basic {
  background-color: #faf5ff;
  border: 1px solid #a855f7;
}
.qd-banner--basic .qd-banner-title { color: #7e22ce; }
.qd-banner--basic .qd-banner-text { color: #6b21a8; }

/* Purple-tone icon inside a `.qd-banner--basic` (Dubai EBP) banner — sibling
   of `.qd-banner-icon--warning`/`--success` below. */
.qd-banner-icon--basic {
  color: #7e22ce;
  flex-shrink: 0;
}
.qd-banner--basic-generic {
  background-color: #fffbeb;
  border: 1px solid var(--brand-warning, #eca04b);
}
.qd-banner--basic-generic .qd-banner-title { color: #92400e; }
.qd-banner--basic-generic .qd-banner-text { color: #78350f; }

/* Warning-tone icon inside a `.qd-banner--basic-generic` (amber) banner —
   e.g. the HR quote detail "quotation details needed" readiness banner. */
.qd-banner-icon--warning {
  color: var(--brand-warning, #eca04b);
  flex-shrink: 0;
}

/* Success (green) tone — the positive-case sibling of `.qd-banner--basic-generic`,
   shown once every category on a quote request is ready and it's the single
   Submit CTA left on the HR quote detail page. Same structural shape, just
   green instead of amber. */
.qd-banner--success {
  background-color: #f0fdf4;
  border: 1px solid var(--brand-success, #32a15e);
}
.qd-banner--success .qd-banner-title { color: #166534; }
.qd-banner--success .qd-banner-text { color: #15803d; }
.qd-banner-icon--success {
  color: var(--brand-success, #32a15e);
  flex-shrink: 0;
}

/* Cancelled quote request — the marker's own banner, on both the HR and the
   insurer detail page. §2 tinted surface: it reads the SAME
   `--dash-status-rejected-*` pair every rejected/terminal badge in the app
   already uses (rust-600 @14% fill + rust-600 text on Heale), so cancellation
   never becomes a second red of its own. §8: no border, the tint carries it.
   Aligned to the top rather than centred — this banner has three lines (what,
   when, why), unlike the single-line `--basic` sibling above. */
.qd-banner--cancelled {
  align-items: flex-start;
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  border: 1px solid transparent;
}
.qd-banner--cancelled .qd-banner-title { color: var(--dash-status-rejected-color, #dc2626); }
.qd-banner--cancelled .qd-banner-text { color: var(--modal-callout-item-color, #4b5563); }
.qd-banner-icon--cancelled {
  color: var(--dash-status-rejected-color, #dc2626);
  flex-shrink: 0;
  /* Optically centres the glyph on the title line now the row is top-aligned. */
  margin-top: 1px;
}

.qd-cancelled-body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  /* The reason is free text of any length: it must wrap, never clip (§21). */
  min-width: 0;
}

/* The reason HR typed, quoted back. Set on the card's own surface so it reads
   as a citation rather than more banner copy. */
.qd-cancelled-reason {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--modal-callout-item-color, #4b5563);
  background-color: var(--modal-total-bg, #ffffff);
  border-radius: 10px;
  padding: 8px 12px;
  overflow-wrap: anywhere;
}

.qd-cancelled-reason-label {
  display: block;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dash-stat-label-color, #6b7280);
  margin-bottom: 3px;
}

.qd-cancelled-reason--none {
  font-style: italic;
  color: var(--dash-stat-label-color, #6b7280);
}

/* Prominent Submit CTA inside the `.qd-banner--success` "ready to submit"
   banner. It is the single unambiguous submit action once every category is
   ready — deliberately larger than an ordinary inline button. */
.qd-banner-submit-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  gap: 8px;
  padding: 11px 22px;
  border-radius: 8px;
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  border: none;
  background-color: var(--brand-success, #32a15e);
  color: #fff;
  flex-shrink: 0;
}
.qd-banner-submit-btn:disabled {
  cursor: not-allowed;
  opacity: 0.6;
}

/* Collapsible field section (Plan Benefits / Premium Information / Known
   medical conditions / Additional Information) */
.qd-collapsible {
  border-radius: 10px;
  border: 1px solid #e5e7eb;
  overflow: hidden;
}
.qd-collapsible + .qd-collapsible {
  margin-top: 12px;
}
.qd-collapsible-head {
  width: 100%;
  padding: 16px 20px;
  border: none;
  background-color: #f8fafc;
  cursor: pointer;
}
.qd-collapsible-head--plain {
  background-color: #ffffff;
}
/* `.qd-collapsible-head--plain` sits directly inside a `.hr-dash-recent-card`
   (no wrapping `.qd-collapsible`), so it needs to match the card's own
   corner radius — squared at the bottom while its body is open. */
.qd-collapsible-head--plain-radius-top {
  border-radius: 16px 16px 0 0;
}
.qd-collapsible-head--plain-radius-all {
  border-radius: 16px;
}
.qd-collapsible-title {
  font-size: 16px;
  font-weight: 700;
  color: #111827;
}
/* Larger, brand-primary variant — the Benefits tab's per-category card
   heading (bigger + tinted, vs. the plain grey collapsible title above). */
.qd-collapsible-title--lg {
  font-size: 18px;
  color: var(--brand-primary, #0a3373);
}
.qd-collapsible-body {
  padding: 0 20px 12px;
}
.qd-collapsible-body--plain {
  padding: 0 24px 16px;
}

/* Medical disclosure on the insurer category screen — the headline answer plus
   the per-condition COUNTS the company declared. Counts only: nothing here
   renders a name, a diagnosis or free text (§ the disclosure is a six-item
   checklist, not a record). The count reads as an identifier, so it takes the
   same navy as `.ins-pol-num`. */
.qd-medical-headline {
  font-size: 13px;
  color: var(--hr-dash-recent-td-color, #374151);
  margin: 0;
}
.qd-medical-list {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.qd-medical-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  color: var(--hr-dash-recent-td-color, #374151);
}
.qd-medical-count {
  font-weight: 700;
  color: var(--ins-pol-num-color, #18181b);
}

/* Unstyled toggle button wrapping a Benefits-tab card heading — a plain
   reset (no border/background) since the card itself supplies the chrome. */
.qd-tab-toggle-btn {
  width: 100%;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  margin-bottom: 0;
}
.qd-tab-toggle-btn--open {
  margin-bottom: 16px;
}

/* Label/value field row inside a collapsible section */
.qd-field-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0;
  border-bottom: 1px solid #f3f4f6;
}
.qd-field-row:last-child {
  border-bottom: none;
}
.qd-field-label {
  font-size: 14px;
  color: #374151;
}
.qd-field-value {
  font-size: 14px;
  font-weight: 600;
  color: #111827;
  padding: 4px 12px;
  border-radius: 6px;
  background-color: #f3f4f6;
}

/* Benefit value chip (Covered / Optional / value / Not covered) — used by
   both the Benefits tab and the category comparator table. */
.qd-benefit-value {
  display: inline-block;
  font-size: 13px;
  font-weight: 500;
  padding: 2px 10px;
  border-radius: 6px;
  background-color: #f3f4f6;
  color: #9ca3af;
}
.qd-benefit-value--value {
  font-weight: 600;
  color: #111827;
}
.qd-benefit-value--optional {
  font-weight: 600;
  color: var(--dash-status-action-color, #9a3412);
  background-color: var(--dash-status-action-bg, #fff7ed);
}
.qd-benefit-value--covered {
  font-weight: 600;
  color: var(--dash-status-approved-color, #059669);
  background-color: var(--dash-status-approved-bg, #d1fae5);
}

/* Insured Lives stat row (Total Employees / Total Dependents / …) */
.qd-lives-stats {
  display: flex;
  gap: 12px;
  margin-bottom: 16px;
}
.qd-lives-stat {
  flex: 1;
  text-align: center;
}
.qd-lives-stat-value {
  font-size: 24px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 4px;
}
.qd-lives-stat-label {
  font-size: 11px;
  color: #6b7280;
  margin: 0;
  white-space: pre-line;
}

/* Small info tile (Nationality mix / Location breakdown) */
.qd-info-tile {
  flex: 1;
  padding: 16px;
  border-radius: 10px;
  border: 1px solid #e5e7eb;
  text-align: center;
}
.qd-info-tile-title {
  font-size: 13px;
  font-weight: 700;
  color: #111827;
  margin: 0 0 4px;
}
.qd-info-tile-value {
  font-size: 13px;
  color: #374151;
  margin: 0;
}

/* Borderless variant — Client information's Industry/City/Country trio uses
   the same tile shape without the card border/padding (it already sits
   inside a `.ins-pol-card-body`). */
.qd-info-tile--plain {
  border: none;
  padding: 0;
}
.qd-info-tile-label {
  font-size: 12px;
  color: #7d93b3;
  margin: 0 0 4px;
}
.qd-info-tile-strong {
  font-size: 15px;
  font-weight: 600;
  color: #111827;
  margin: 0;
}

/* Document / Q&A list rows */
.qd-doc-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
  border-radius: 8px;
  border: 1px solid #e5e7eb;
}
.qd-doc-title {
  font-size: 13px;
  font-weight: 600;
  color: #111827;
  margin: 0;
}
.qd-doc-sub {
  font-size: 11px;
  color: #7d93b3;
  margin: 2px 0 0;
}
.qd-qa-item {
  border-radius: 8px;
  border: 1px solid #e5e7eb;
  overflow: hidden;
}
.qd-qa-head {
  width: 100%;
  padding: 12px 16px;
  border: none;
  background-color: #ffffff;
  cursor: pointer;
  text-align: left;
}
.qd-qa-question {
  font-size: 13px;
  font-weight: 600;
  color: #111827;
  margin: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.qd-qa-meta {
  font-size: 11px;
  color: #7d93b3;
  margin: 2px 0 0;
}
.qd-qa-body {
  padding: 0 16px 14px;
  border-top: 1px solid #f3f4f6;
}
.qd-qa-label {
  font-size: 11px;
  color: #7d93b3;
  margin: 0 0 4px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.qd-qa-answer {
  font-size: 13px;
  color: #374151;
  margin: 0;
  white-space: pre-wrap;
}
.qd-qa-answer--pending {
  color: #9ca3af;
  font-style: italic;
}

/* Category comparator table */
.qd-compare-table-wrap {
  overflow-x: auto;
}
.qd-compare-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.qd-compare-table th {
  text-align: left;
  padding: 10px 12px;
  color: var(--brand-primary, #0a3373);
  font-weight: 700;
  border-bottom: 1px solid #e5e7eb;
  background-color: #f8fafc;
}
.qd-compare-table th:first-child,
.qd-compare-table td:first-child {
  position: sticky;
  left: 0;
  min-width: 220px;
}
.qd-compare-table th:not(:first-child),
.qd-compare-table td:not(:first-child) {
  min-width: 160px;
}
.qd-compare-table td {
  padding: 10px 12px;
  border-bottom: 1px solid #f3f4f6;
  background-color: #ffffff;
}
.qd-compare-table td:first-child {
  color: #374151;
}
.qd-compare-section-row td {
  background-color: #f1f5fe;
  color: var(--brand-secondary, #4374f3);
  font-weight: 700;
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

/* Offers tab — one card per category, one row per offer */
.qd-offer-cat-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.qd-offer-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border-radius: 8px;
  background-color: #f9fafb;
  border: 1px solid #f3f4f6;
}

/* ─────────────────────────────────────────────
   Reset password page (src/app/reset-password)
   Reuses the full .login-root/.login-left/.login-right two-panel layout
   (see login-form.tsx) — only introduces new .reset-password-* classes for
   elements that don't already have a shared equivalent (checklist,
   inline error, expired-link state).
   ───────────────────────────────────────────── */
.reset-password-checklist {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 4px 0;
}

.reset-password-checklist-title {
  font-size: 13px;
  font-weight: 500;
  color: #4b5563;
  margin: 0 0 4px;
}

.reset-password-checklist-item {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: #9ca3af;
}

.reset-password-checklist-item--satisfied {
  color: #32a15e;
}

.reset-password-checklist-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  line-height: 1;
  background-color: color-mix(in srgb, #9ca3af 16%, white);
  color: #9ca3af;
  flex-shrink: 0;
}

.reset-password-checklist-icon--satisfied {
  background-color: color-mix(in srgb, #32a15e 14%, white);
  color: #32a15e;
}

.reset-password-error {
  font-size: 13px;
  color: #dc2626;
  margin-top: 12px;
  margin-bottom: 0;
}

.reset-password-footer {
  font-size: 13px;
  color: #6b7280;
  text-align: center;
  margin: 20px 0 0;
}

.reset-password-footer-link {
  color: var(--brand-secondary, #4374f3);
  text-decoration: underline;
}

/* Expired / invalid link card — mirrors .login-magic-sent, tinted with the
   brand accent to read as a "needs attention" state. */
.reset-password-expired {
  padding: 32px 24px;
  border-radius: 14px;
  background-color: color-mix(in srgb, var(--brand-accent, #ff6733) 5%, white);
  border: 1px solid color-mix(in srgb, var(--brand-accent, #ff6733) 16%, white);
  text-align: center;
}

.reset-password-expired-title {
  font-size: 20px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 8px;
  letter-spacing: -0.3px;
}

.reset-password-expired-body {
  font-size: 14px;
  color: #6b7280;
  line-height: 1.55;
  margin: 0;
}

/* =====================================================================
   HR Quote Detail — `/hr/quotes/[id]` (`QuoteDetailPage`). Classes below
   replace inline `style={{}}` objects that were purely static (no runtime-
   computed value). A few spots stay inline on purpose because the value is
   genuinely dynamic per row/status (e.g. a status color pulled from the
   page's own `statusConfig`/`CATEGORY_STATUS_BADGE` map, or a hover border
   swap that's now a CSS `:hover` rule instead of a JS handler) — those are
   called out in the page's own comments at each call site.
   ===================================================================== */

/* Generic single-line ellipsis clip — reused by several compare-table cell
   renderers (`InfoOrDocCell`) that used to repeat the same 3 properties. */
.qd-ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Compare-table info/document cell text (`InfoOrDocCell`) */
.qd-cell-text {
  font-size: 12px;
  max-width: 100%;
  color: #374151;
}

.qd-cell-text--empty {
  color: #9ca3af;
  font-style: italic;
}

.qd-cell-link {
  font-size: 13px;
  color: var(--brand-secondary, #4374f3);
  text-decoration: none;
  max-width: 100%;
}

.qd-cell-description {
  width: 100%;
  min-height: 56px;
  border-radius: 8px;
  background-color: #f8fafc;
  border: 1px solid #e5e7eb;
  padding: 6px 10px;
  font-size: 12px;
  color: #374151;
  text-align: left;
  white-space: pre-wrap;
  word-break: break-word;
  overflow: auto;
}

.qd-cell-description--empty {
  color: #9ca3af;
  font-style: italic;
}

.qd-cell-doc-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
  color: var(--brand-primary, #0a3373);
  font-size: 12px;
  font-weight: 500;
  max-width: 100%;
}

/* Benefit-cell status dot (comparison table) — 28px circular wrap around a
   check/cross icon; `backgroundColor` stays inline since it's one of three
   states passed to the shared `wrap()` helper. */
.qd-benefit-dot-wrap {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
}

.qd-benefit-dot--muted {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: #9ca3af;
}

/* Per-category comparison block: section label + "Compare offers" heading */
.qd-compare-category-label {
  font-size: 13px;
  font-weight: 700;
  color: #6b7280;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 12px;
}

.qd-compare-title {
  font-size: 22px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

.qd-compare-subtitle {
  font-size: 13px;
  color: #6b7280;
  margin-top: 4px;
}

/* Compare-offers filter toolbar: "Filters" trigger + popover (active/inactive
   toggle pills reused by both the main trigger and the similarity options). */
.qd-filter-wrap {
  position: relative;
}

.qd-toggle-pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: 100px;
  border: 1px solid #d2d9e5;
  background-color: #fff;
  color: #374151;
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
}

.qd-toggle-pill--active {
  border-color: var(--brand-primary, #0a3373);
  background-color: var(--brand-primary, #0a3373);
  color: #fff;
}

.qd-toggle-pill--lg {
  padding: 6px 12px;
  white-space: nowrap;
}

.qd-filter-badge {
  font-size: 10px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 100px;
  background-color: #fff;
  color: var(--brand-primary, #0a3373);
}

.qd-filter-popover {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 280px;
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(10, 51, 115, 0.15);
  border: 1px solid #e5e7eb;
  padding: 16px;
  z-index: 20;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.qd-filter-check-label {
  gap: 8px;
  font-size: 13px;
  color: #374151;
  cursor: pointer;
}

.qd-filter-section-label {
  font-size: 11px;
  font-weight: 700;
  color: #6b7280;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.qd-filter-section-label--spaced {
  margin-bottom: 8px;
}

.qd-filter-show-all-btn {
  font-size: 11px;
  color: var(--brand-secondary, #4374f3);
  background: transparent;
  border: none;
  padding: 0;
  cursor: pointer;
}

.qd-filter-sections-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 200px;
  overflow-y: auto;
}

.qd-compare-empty {
  font-size: 13px;
  color: #9ca3af;
  text-align: center;
  padding: 32px 0;
  background-color: #fff;
  border-radius: 12px;
  border: 1px solid #e5e7eb;
}

/* Compare-offers section-header grid row — `grid-template-columns` stays
   inline (computed per-category). */
.qd-compare-section-grid {
  display: grid;
  column-gap: 12px;
  align-items: stretch;
}

/* AI "Apple Intelligence" column-border overlay grid — `grid-template-columns`
   stays inline (computed per-category), everything else is static. */
.qd-ai-overlay-grid {
  position: absolute;
  inset: 0;
  display: grid;
  column-gap: 12px;
  pointer-events: none;
  z-index: 4;
}

/* Compare-offers "spine" row (grid of YOUR REQUEST + insurer cards).
   `grid-template-columns` stays inline — it's computed per-category from the
   offer count (`gridTemplate` in the page). */
.qd-compare-spine {
  display: grid;
  column-gap: 12px;
  align-items: stretch;
  margin-bottom: 0;
  min-width: fit-content;
}

/* "YOUR REQUEST" card — HR's own request rendered like an insurer card */
.qd-hr-card {
  background-color: #4476f5;
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
  padding: 20px;
  color: #fff;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  text-align: center;
}

.qd-hr-card-badge {
  font-size: 10px;
  font-weight: 700;
  padding: 3px 10px;
  border-radius: 100px;
  background-color: rgba(255, 255, 255, 0.2);
  color: #fff;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.qd-hr-card-title {
  font-size: 18px;
  font-weight: 700;
  line-height: 1.2;
  color: #fff;
}

.qd-hr-card-subtitle {
  font-size: 13px;
  color: rgba(255, 255, 255, 0.8);
}

.qd-hr-card-divider {
  width: 100%;
  height: 1px;
  background-color: rgba(255, 255, 255, 0.3);
}

.qd-hr-card-stat-label {
  font-size: 10px;
  font-weight: 700;
  color: rgba(255, 255, 255, 0.7);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.qd-hr-card-stat-value {
  font-size: 16px;
  font-weight: 700;
  color: #fff;
  margin-top: 2px;
}

.qd-hr-card-stat-value--sm {
  font-size: 14px;
}

/* AI compare sidebar shell */
.qd-ai-sidebar {
  width: 420px;
  flex-shrink: 0;
  border-left: 1px solid #e5e7eb;
  background-color: #fff;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  font-family: Inter, sans-serif;
}

.qd-ai-sidebar-header {
  background-color: #fff;
  border-bottom: 1px solid #e5e7eb;
  padding: 16px 20px;
  flex-shrink: 0;
}

.qd-ai-sidebar-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}

.qd-ai-sidebar-close-btn {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background-color: transparent;
  border: none;
  cursor: pointer;
  color: #6b7280;
}

.qd-ai-sidebar-close-btn:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.qd-ai-pill {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 100px;
  background-color: #eef2ff;
  color: var(--brand-primary, #0a3373);
}

.qd-ai-messages-area {
  flex: 1;
  overflow-y: auto;
  padding: 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-height: 0;
}

.qd-ai-suggestion-btn {
  padding: 8px 12px;
  border-radius: 100px;
  background-color: #fff;
  color: var(--brand-primary, #0a3373);
  font-size: 12px;
  font-weight: 500;
  border: 1px solid #d2d9e5;
  cursor: pointer;
  text-align: left;
}

.qd-ai-suggestion-btn:disabled {
  cursor: not-allowed;
}

.qd-ai-bubble-user {
  max-width: 85%;
  background-color: var(--brand-primary, #0a3373);
  color: #fff;
  font-size: 13px;
  padding: 8px 12px;
  border-radius: 12px;
  border-bottom-right-radius: 4px;
  white-space: pre-wrap;
  line-height: 1.5;
}

.qd-ai-bubble-assistant {
  max-width: 92%;
  background-color: #f3f4f6;
  color: #111827;
  font-size: 13px;
  padding: 10px 12px;
  border-radius: 12px;
  border-bottom-left-radius: 4px;
  line-height: 1.5;
}

.qd-ai-bubble-assistant--error {
  background-color: #fef2f2;
  color: #b91c1c;
}

.qd-ai-bubble-loading {
  max-width: 92%;
  background-color: #f3f4f6;
  border-radius: 12px;
  border-bottom-left-radius: 4px;
  padding: 12px 14px;
}

.qd-ai-bubble-loading-title {
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  margin-bottom: 6px;
}

/* Pulsing "typing" dot — `animation-delay` stays inline (staggered per dot
   index) since it's computed from a loop index. */
.qd-ai-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: var(--brand-secondary, #4374f3);
  display: inline-block;
  animation: ai-dot-pulse 1.4s ease-in-out infinite;
}

.qd-ai-input-bar {
  background-color: #fff;
  border-top: 1px solid #e5e7eb;
  padding: 12px 16px;
  flex-shrink: 0;
}

.qd-ai-input-shell {
  gap: 8px;
  border: 1px solid #d2d9e5;
  border-radius: 12px;
  padding: 8px 8px 8px 12px;
  background-color: #fff;
}

.qd-ai-textarea {
  flex: 1;
  border: none;
  outline: none;
  resize: none;
  font-size: 13px;
  font-family: Inter, sans-serif;
  color: #111827;
  background-color: transparent;
  max-height: 120px;
  line-height: 1.5;
}

.qd-ai-send-btn {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background-color: var(--qd-ai-send-btn-bg, var(--brand-primary, #0a3373));
  border: none;
  color: var(--qd-ai-send-btn-fg, #fff);
  cursor: pointer;
}

.qd-ai-send-btn:disabled {
  cursor: not-allowed;
  opacity: 0.4;
}

.qd-ai-footer-note {
  font-size: 11px;
  color: #9ca3af;
  margin-top: 6px;
  text-align: center;
}

/* Floating "compare offers" action bar */
.qd-float-bar {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  background-color: #fff;
  border-radius: 12px;
  padding: 12px 20px;
  box-shadow: 0 8px 32px rgba(10, 51, 115, 0.16);
  border: 1px solid #e5e7eb;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 16px;
}

.qd-float-bar-label {
  color: var(--brand-primary, #0a3373);
  font-weight: 600;
  font-size: 14px;
}

.qd-float-btn-clear {
  height: 36px;
  padding: 0 14px;
  border-radius: 8px;
  background-color: #fff;
  color: var(--brand-primary, #0a3373);
  font-size: 13px;
  font-weight: 600;
  border: 1px solid var(--brand-primary, #0a3373);
  cursor: pointer;
}

.qd-float-btn-ai {
  height: 36px;
  gap: 8px;
  padding: 0 16px;
  border-radius: 8px;
  background-color: var(--qd-float-btn-ai-bg, var(--brand-primary, #0a3373));
  color: var(--qd-float-btn-ai-fg, #fff);
  font-size: 13px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

/* Whole-quote delete confirmation modal — kept as its own distinct
   (non-`.modal-*`) class family: it predates the shared `.modal-*` system
   and has different sizing/overlay tone (420px card, 0.3-alpha black
   overlay vs `.modal-card`'s 560px / `.modal-overlay`'s navy-tinted
   backdrop) — reusing `.modal-*` here would visibly change its appearance. */
.qd-delete-quote-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(0, 0, 0, 0.3);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 50;
}

.qd-delete-quote-card {
  background-color: #ffffff;
  border-radius: 12px;
  padding: 32px;
  max-width: 420px;
  width: 90%;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.12);
}

.qd-delete-quote-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 8px;
}

.qd-delete-quote-text {
  font-size: 14px;
  color: #374151;
  margin: 0 0 4px;
}

.qd-delete-quote-warning {
  font-size: 13px;
  color: var(--brand-error, #dc2626);
  margin: 0 0 20px;
}

.qd-delete-quote-btn-cancel {
  height: 36px;
  padding: 0 16px;
  border-radius: 8px;
  background-color: #ffffff;
  color: #374151;
  font-size: 13px;
  font-weight: 500;
  border: 1px solid #e5e7eb;
  cursor: pointer;
}

.qd-delete-quote-btn-confirm {
  height: 36px;
  gap: 6px;
  padding: 0 16px;
  border-radius: 8px;
  background-color: var(--brand-error, #dc2626);
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

.qd-delete-quote-btn-confirm:disabled {
  cursor: not-allowed;
}

/* "Choose this policy" pre-selection confirm modal — its own class family
   for the same reason as the delete-quote modal above (different overlay
   tint/z-index/card width than `.modal-*`). */
.qd-select-offer-overlay {
  position: fixed;
  inset: 0;
  background-color: rgba(10, 51, 115, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 80;
}

.qd-select-offer-card {
  background-color: #fff;
  border-radius: 12px;
  padding: 28px;
  max-width: 520px;
  width: 90%;
  box-shadow: 0 20px 60px rgba(10, 51, 115, 0.18);
  font-family: Inter, sans-serif;
}

.qd-select-offer-title {
  font-size: 20px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 8px;
}

.qd-select-offer-text {
  font-size: 14px;
  color: #374151;
  margin: 0 0 24px;
  line-height: 1.5;
}

.qd-select-offer-error {
  margin-bottom: 16px;
  padding: 8px 12px;
  border-radius: 8px;
  background-color: #fff1f2;
  border: 1px solid #fecaca;
  color: var(--brand-error, #dc2626);
  font-size: 13px;
}

.qd-select-offer-btn-cancel {
  height: 40px;
  padding: 0 22px;
  border-radius: 8px;
  background-color: #fff1f2;
  color: var(--brand-accent, #ff6733);
  font-size: 14px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

.qd-select-offer-btn-cancel:disabled {
  cursor: not-allowed;
}

.qd-select-offer-btn-confirm {
  height: 40px;
  gap: 6px;
  padding: 0 24px;
  border-radius: 8px;
  background-color: var(--brand-secondary, #4374f3);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

.qd-select-offer-btn-confirm:disabled {
  cursor: not-allowed;
}

/* Insurer offer card (compare-offers spine row). `--recommended` overrides
   the border + top padding — the only two properties that used to be
   ternaries on `isRecommended` — so the wrapper itself needs zero inline
   style now. The logo bubble's `backgroundColor` and the delta's sign-based
   color modifier stay separate (below) since they carry genuinely
   per-offer/per-value data. */
.qd-offer-card {
  position: relative;
  background-color: #fff;
  border-top: 1px solid #e5e7eb;
  border-left: 1px solid #e5e7eb;
  border-right: 1px solid #e5e7eb;
  border-top-left-radius: 12px;
  border-top-right-radius: 12px;
  padding: 20px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  text-align: center;
}

.qd-offer-card--recommended {
  border-top: 2px solid var(--brand-primary, #0a3373);
  border-left: 2px solid var(--brand-primary, #0a3373);
  border-right: 2px solid var(--brand-primary, #0a3373);
  padding-top: 32px;
}

.qd-offer-card-checkbox {
  position: absolute;
  top: 10px;
  left: 10px;
  width: 16px;
  height: 16px;
  accent-color: var(--brand-primary, #0a3373);
  cursor: pointer;
}

.qd-offer-card-badge {
  position: absolute;
  top: 8px;
  right: 10px;
  font-size: 9px;
  font-weight: 700;
  padding: 3px 8px;
  border-radius: 100px;
  background-color: var(--brand-success, #32a15e);
  color: #fff;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.qd-offer-card-logo {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  font-weight: 700;
}

.qd-offer-card-name {
  font-size: 16px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  line-height: 1.2;
}

.qd-offer-card-subtitle {
  font-size: 12px;
  color: #6b7280;
}

.qd-offer-card-price {
  font-size: 22px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  line-height: 1.1;
}

.qd-offer-card-delta {
  font-size: 12px;
  font-weight: 600;
}

.qd-offer-card-delta--up {
  color: var(--brand-success, #32a15e);
}

.qd-offer-card-delta--down {
  color: var(--brand-error, #dc2626);
}

.qd-offer-card-cta {
  width: 100%;
  height: 36px;
  border-radius: 8px;
  background-color: var(--qd-offer-card-cta-bg, var(--brand-primary, #0a3373));
  color: var(--qd-offer-card-cta-fg, #fff);
  border: none;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  margin-top: 4px;
}

/* AI compare sidebar — inline markdown renderer output */
.qd-ai-list {
  margin: 4px 0 4px 18px;
  padding: 0;
  list-style: disc;
}

.qd-ai-list-item {
  margin-bottom: 2px;
}

.qd-ai-spacer {
  height: 6px;
}

.qd-ai-line {
  margin: 0 0 6px;
  line-height: 1.5;
}

/* Page shell: split layout (main scroll area + optional AI sidebar) */
.qd-split-layout {
  flex: 1;
  display: flex;
  overflow: hidden;
  min-height: 0;
}

/* Same surface token as `.dash-body`, so this page's content area matches its
   own header (and whatever a white label maps) instead of being pinned to one
   brand's blue. */
.qd-scroll-area {
  padding: 24px;
  flex: 1;
  overflow: auto;
  background-color: var(--dash-body-bg, #f8fafd);
  min-width: 0;
}

/* "Quote not found" empty state */
.qd-notfound-card {
  background-color: #ffffff;
  border-radius: 12px;
  padding: 60px;
  text-align: center;
  border: 1px solid #e5e7eb;
}

.qd-notfound-title {
  font-size: 16px;
  font-weight: 500;
  color: #374151;
  margin: 0 0 4px;
}

.qd-notfound-subtitle {
  font-size: 13px;
  color: #7d93b3;
  margin: 0 0 16px;
}

.qd-notfound-btn {
  height: 36px;
  padding: 0 16px;
  border-radius: 8px;
  background-color: var(--brand-secondary, #4374f3);
  color: #ffffff;
  font-size: 13px;
  font-weight: 600;
  border: none;
  cursor: pointer;
}

/* The horizontal summary bar that used to head the quote-detail page is gone:
   the post-submit view now carries its status, metadata and category pills in
   the full-width status band (`<RequestStatusBand>`, `.qrb-*` below). The pill
   classes here SURVIVED that removal — they are still worn by the band and by
   HR's request lists — which is why they are not dead code. */

/* Quote-level status pill — deliberately its own class, not `.dash-status`:
   the latter is uppercase/bold/letter-spaced (a different, bigger look) and
   swapping to it here would visibly change this pill's appearance. */
.qd-summary-status {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: 100px;
  white-space: nowrap;
}

/* The cancelled variant of that pill, for HR's own request lists. It REPLACES
   the live status pill rather than joining it: "Awaiting Offers · 2 of 3
   categories ready" describes work that has stopped. Colours come from a class
   because every other state of this pill is tinted by an inline
   `quoteStatusConfig()` pair — this one does not add to that legacy. */
.qd-status-cancelled {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
  color: var(--dash-status-rejected-color, #dc2626);
}

.qd-summary-pill {
  gap: 5px;
  padding: 4px 10px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 500;
  border: 1px solid #e5e7eb;
  background-color: #fff;
  cursor: pointer;
  color: #374151;
  transition: border-color 0.12s;
}

.qd-summary-pill:hover {
  border-color: var(--brand-secondary, #4374f3);
}

.qd-summary-pill-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  flex-shrink: 0;
}

.qd-summary-pill-count {
  font-size: 10px;
  color: #9ca3af;
}


/* "Cancel request" in the quote-detail TOPBAR, alongside the pre-submit Edit
   and Delete icons it takes over from after submission. Structurally the §6
   secondary pill (`.hr-dash-recent-btn-outline`), retoned to the §6 REJECTION
   pair — white fill, rust label and border — because this closes a request
   insurers are working on and HR cannot undo it. It is not a neutral secondary,
   and it is not a primary either: nothing here should invite a click.

   Deliberately NOT `.hr-dash-recent-btn-outline--danger`, which reads the
   unmapped `--brand-error` and so falls back to the §1-retired red on Heale.
   These two tokens are the mapped rust-600 pair every other terminal state in
   the app already wears. */
.qd-topbar-cancel-btn {
  color: var(--dash-status-rejected-color, #dc2626);
  border-color: var(--dash-status-rejected-color, #dc2626);
}

.qd-topbar-cancel-btn:hover {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
}

/* =====================================================================
   Quote HR Dashboard — `/hr/quotes` (Charte 2026). The 3 landing states
   (Empty state / Dashboard first-visit / Dashboard configured — see
   `src/components/quotes/dashboard/`) share this `.qd-dash-*` namespace.
   Card shells reuse `.hr-dash-recent-card`/`.hr-dash-recent-head`/
   `.hr-dash-recent-title` (§8, no border, shadow only) — the rules below
   only cover the page-specific shapes with no existing generic equivalent:
   the prerequisite checklist, the two-step setup card's radio options, the
   unassigned-members warning row and the renewal history rows.
   ===================================================================== */

.qd-dash-lede {
  font-size: 14px;
  color: var(--ins-pol-subtext-color, #4a4640);
  margin: 0;
}

/* Prerequisite checklist (Empty state, §5) */
.qd-dash-prereq-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.qd-dash-prereq-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 4px;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qd-dash-prereq-row:last-child {
  border-bottom: none;
}

.qd-dash-prereq-main {
  min-width: 0;
}

/* The status glyph sits in a §2 tinted square (the token at 14%, the glyph in
   the same token at 100%) rather than floating bare against the row — the same
   treatment `.qd-missing-info-tag` already gives this page's "still needs
   fixing" pills, and what makes a 4-row checklist read as a designed list
   rather than four sentences with an icon in front. `transparent` is the
   second colour, not `white`: these rows sit on the card, and mixing toward a
   hard white would leave a pale square wherever a theme's card is not #fff. */
.qd-dash-prereq-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 14%, transparent);
  color: var(--dash-status-pending-color, #d97706);
}

.qd-dash-prereq-icon--done {
  background-color: color-mix(in srgb, var(--dash-status-approved-color, #059669) 14%, transparent);
  color: var(--dash-status-approved-color, #059669);
}

.qd-dash-prereq-text {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
}

/* §3 metadata: the reason to act, under the instruction rather than welded to
   it by a dash. */
.qd-dash-prereq-subtext {
  margin: 3px 0 0;
  font-size: 12px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* ═ THE AFFORDANCE RULE CHANGED — READ THIS BEFORE COPYING EITHER CLASS ═

   OLD RULE (what these two classes encode, and why they still look like this):
   SHAPE carried both meanings — the pill acts on this page, the quiet text link
   navigates, and the "→" reinforced the second.

   NEW RULE (`.qd-block-action`, below): AN ACTION LOOKS LIKE AN ACTION, AND
   NOTHING MORE. Every actionable thing in the quote-request blocks wears the
   same secondary button; neither shape NOR arrow encodes where the click lands —
   the LABEL does. What was lost is the two-signal distinction; what was gained
   is that nothing actionable reads as body text, which is what a real reader
   reported after seeing it rendered.

   THE OLD RULE IS STILL LIVE for the surfaces below that have not moved to the
   new one — the Empty view's prerequisite checklist and `<BlockingReasonNotice>`
   — so both classes keep their consumers and their meaning. Do NOT restyle them
   into buttons as a "consistency" pass: that is a decision about those screens,
   which are out of the block rule's scope, and it would be made here by
   accident. */

/* A row that NAVIGATES keeps the quiet text link — that is what a "→" means. */
.qd-dash-prereq-link {
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  text-decoration: none;
  /* The reset below is a no-op on an anchor, and kept because a caller may
     still render this shape as a <button>. */
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.qd-dash-prereq-link:hover {
  text-decoration: underline;
}

/* A row that FIXES ITS PREREQUISITE HERE gets a real §6 primary pill instead:
   it opens a panel over this screen, and an arrow would promise a screen it
   never goes to. Sized down from the page CTA (this is a row action, not the
   primary action of the screen) — the 44px mobile floor is applied in the
   768px block below rather than here, where it would swallow the row. */
.qd-dash-prereq-action {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin-top: 9px;
  padding: 8px 15px;
  border: none;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  background-color: var(--hr-dash-recent-btn-primary-bg, var(--brand-primary, #0a3373));
  color: var(--hr-dash-recent-btn-primary-color, #ffffff);
}

/* The same pill inside the "Can't be checked · N people" row, which is a CENTRED
   flex row rather than a stacked checklist item — so it drops the standalone top
   offset and never shrinks below its label. Identical treatment to
   `.qd-fix-row-btn` inside the fix-in-place list, for the identical reason: the
   row already aligns it. */
.qd-dash-unassigned-row .qd-dash-prereq-action {
  margin-top: 0;
  flex-shrink: 0;
}

.qd-dash-prereq-action:hover {
  background-color: var(--hr-dash-recent-btn-primary-bg-hover, var(--hr-dash-recent-btn-primary-bg, var(--brand-primary, #0a3373)));
}

/* `.qd-dash-prereq-action--outstanding` WAS HERE and is DELETED, not orphaned:
   the same pill in the §12 pending tone (gold @14%, dashed) for a fix attached to
   a missing input. Its only consumers were the blocker fixes, which now wear the
   app's own secondary button (`.hr-dash-recent-btn-outline--sm`) because the
   toned chip read as a TAG rather than as the control it is. The reasoning it
   carried survives one level up: the fix must not out-shout the CTA, which the
   outline button satisfies by leaving the SOLID fill to the primary. The dashed
   pending tone remains for `.qd-chip-gap` — an empty group, which states a gap
   and does not act as a button. */

/* "New Quote Request" gated CTA — the disabled reason sits directly underneath
   (§5) in `<BlockingReasonNotice>`, which is NOT a shape of its own: it is the
   shared warning-row box `.qd-dash-unassigned-row` (declared further down, in
   the configured-dashboard section) in the `--pending` tone, so the box, the
   12px gap, the centring and the trailing action all come from the family and
   cannot drift from the other messages on this page. Only the two deltas below
   are the notice's own — everything else deliberately ISN'T repeated here,
   because a second copy of the geometry is exactly how the two would drift.

   Order note: the family class is declared AFTER this one, so anything
   redeclared here would silently lose to it. Nothing here conflicts — that is
   a property to preserve, not a coincidence. */
.qd-dash-cta-notice {
  /* The family's own rows never wrap (their labels are a few words). This one
     carries a full sentence, so at narrow widths the action drops under it
     rather than squeezing it. */
  flex-wrap: wrap;
}

/* Deliberately NOT `.qd-dash-unassigned-text` (13px Bold): this is a sentence
   explaining a disabled button, and it stays lighter than the standing warnings
   the family's other consumers carry — one of the two levers keeping the CTA
   above it the loudest thing in the block (the other being the row's low-tint
   fill). §2: the same `--dash-status-pending-*` token the box's fill reads. */
.qd-dash-cta-reason {
  font-size: 12.5px;
  color: var(--dash-status-pending-color, #d97706);
  margin: 0;
}

/* The `.qd-dash-unassigned-row .qd-dash-prereq-action` reset further down
   already positions the trailing pill for this row too — it is the same box. */
.qd-dash-cta-notice .qd-dash-prereq-link {
  flex-shrink: 0;
}

/* <MissingInfoModal> — reuses the `.mf-modal-overlay`/`.mf-modal`/
   `.mf-hist-modal-head` shell (same family as the Company Information
   history modal), including its title+subtitle header stack (compare
   `.ci-history-head`/`.ci-history-subtitle` below) and the `.qd-dash-lede`
   count-line convention (`configured-view.tsx`'s Categories card); only the
   body-row content below is new. */

/* Header: title + subtitle stack on the left, close (X) on the right — same
   re-alignment as `.ci-history-head` (the base `.mf-hist-modal-head` centers
   a single-line title, this modal adds a subtitle line under it). */
.mf-hist-modal-head.qd-missing-info-head {
  align-items: flex-start;
}

.qd-missing-info-subtitle {
  margin: 2px 0 0;
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qd-missing-info-count {
  margin-top: 12px;
}

.qd-missing-info-body {
  max-height: 66vh;
  overflow-y: auto;
  margin: 16px -24px -24px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.qd-missing-info-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  padding: 13px 24px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.qd-missing-info-row:last-child {
  border-bottom: none;
}

.qd-missing-info-row-main {
  min-width: 0;
}

.qd-missing-info-name {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* One pill per missing field (§2 tinted status surface — pending/gold-500 —
   the same fill+text token pair `.qd-dash-prereq-icon`/`.qd-dash-cta-reason`
   already use for "still needs fixing" states), same shape as the shared
   `.task-tag` pill so it reads as this app's one established small-tag
   convention rather than a bespoke chip. */
.qd-missing-info-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.qd-missing-info-tag {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  background-color: var(--dash-status-pending-bg, #fef3c7);
  color: var(--dash-status-pending-color, #d97706);
}

.qd-missing-info-link {
  flex-shrink: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  text-decoration: none;
}

.qd-missing-info-link:hover {
  text-decoration: underline;
}

/* ── The shared "fix in place" modal FAMILY (`<FixInPlaceModal>` /
   `<FixInPlaceRow>`) — `<MissingInfoModal>` and `<BlockedFamiliesModal>` ──

   Both list employees whose record is blocking something, both are reached from
   `/hr/quotes`, and both can run to a hundred rows. So they share one shell (the
   `.mf-modal-overlay`/`.mf-modal`/`.mf-hist-modal-head` family above, unchanged)
   and one row geometry, stated once here. */

/* Wider than `.mf-modal--wide` (820px): a row carries a name column, a
   missing-field column that regularly holds 5-9 pills, and an action — at 820px
   the pills wrap into a block taller than the row's own name. Still bounded by
   the viewport with the same 48px gutter the other sizes use. */
.mf-modal.mf-modal--xwide {
  width: min(1040px, calc(100vw - 48px));
}

/* The card is height-bounded so the BODY is the only scroller: head, subtitle
   and count line stay put while the list moves under them, and the page behind
   never gets involved. */
.qd-fix-modal {
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 64px);
}

.qd-fix-body {
  flex: 1;
  min-height: 0;
  /* Beats `.qd-missing-info-body`'s 66vh cap: inside a flex column the row
     above already bounds this, and the cap would strand the last rows on a
     short viewport. */
  max-height: none;
  overscroll-behavior: contain;
  /* The body bleeds to the card's edges (negative margins, inherited from
     `.qd-missing-info-body`), so it has to carry the card's own bottom radius —
     the last row would otherwise square off the corners. Deliberately NOT
     `overflow: hidden` on the card: the fix panel is a `position: fixed`
     descendant and must stay unclipped. */
  border-radius: 0 0 12px 12px;
}

/* …EXCEPT when a footer follows, where both of those are wrong: the FOOTER is
   what reaches the card's bottom edge, so it owns the radius, and the body must
   not still be pulling the next element up over itself.

   THE BUG THIS FIXES, in numbers, because they are the whole story. The bleed is
   -24px (it cancels `.mf-modal`'s 24px padding) while this body's last child
   `.qd-group-wizard-rows` ends in 16px of padding — so the footer, and with it
   the `border-top` that DRAWS the divider, was pulled 8px ABOVE the last row's
   bottom edge. On the group wizard's step 1 that last row is the "Add a group"
   button, which had no background of its own, so the divider ran straight
   through it and the button read as sitting ON the line rather than above it.
   Both wizard steps carried the overlap; only step 1 showed it, because step 2's
   body ends in a table whose own last-row divider absorbed the collision.

   Cancelled at the source rather than compensated for with a `margin-top` on the
   footer: the bleed is a statement about which element touches the card's bottom,
   and with a footer present that statement is simply false. */
.qd-fix-body:has(+ .qd-wizard-foot) {
  margin-bottom: 0;
  border-radius: 0;
}

/* Search + live count, between the head and the scrolling list. Both stay put
   while the body scrolls (they are siblings of it, not content in it), which is
   the whole reason the card is a bounded flex column.

   The search is the SHARED `.ins-pol-search` pill (Charte §9: "search is the same
   field as a pill"), so restyling `--ins-pol-search-*` restyles this one too — it
   is not a bespoke box. `.ins-pol-search` already carries `flex: 1`, so the count
   simply sits at the far end. */
.qd-fix-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 16px;
  margin-top: 14px;
}

/* Overrides `.qd-missing-info-count`'s standalone-line offset: inside the toolbar
   the row owns the spacing, and the count must not wrap mid-phrase. */
.qd-fix-count {
  margin: 0;
  white-space: nowrap;
}

/* Step 1 once the question is SETTLED — real groups exist, so the three options
   are gone and this states the outcome instead. A tinted, quietly "done" block
   rather than a card: it sits INSIDE step 1's slot, where the radios were, and a
   second card there would read as new work rather than as finished work. */
.qd-setup-settled {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
  margin-top: 12px;
  padding: 14px 16px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #eef2f9);
}

.qd-setup-settled-text {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* `.qd-setup-settled-groups` is GONE: the named-group chips are the shared
   `.qd-chip-row` now (`<SettledGroupChips>`), because the configured screen's
   inputs block states the same settled outcome and the two must not drift. The
   box below is a flex column with its own gap, which is why that strip carries
   no margin of its own. */

/* Navigates to `/manage`, so it keeps the arrow — the same rule that took the
   arrow OFF `.qd-dash-prereq-action` (which opens something over the page). */
.qd-setup-settled-link {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  text-decoration: none;
}

.qd-setup-settled-link:hover {
  text-decoration: underline;
}

/* ── The group wizard (`<GroupWizard>`), inside the shared `<WideModalShell>` ──

   Both steps live in ONE modal, so everything below sits between the shell's head
   and its pinned footer and scrolls with the body — except the rail and the
   footer, which the shell keeps put. */

/* "Step 1 of 2" — a rail, not tabs: the steps are ORDERED and step 2 is not
   reachable until step 1 commits, so it must not offer itself as something to
   click. Rendered as `headExtra`, so it stays visible while a hundred member rows
   scroll under it. */
.qd-wizard-rail {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 22px;
  margin-top: 14px;
  padding-bottom: 2px;
}

.qd-wizard-rail-step {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qd-wizard-rail-step--active {
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* The step NUMBER carries the state, so the label never has to shout: muted ring
   for a step not reached, brand fill for the current one, and the same fill for a
   completed one (this rail only ever moves forward). */
.qd-wizard-rail-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  background-color: var(--ins-pol-filter-bg, #f2ede5);
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qd-wizard-rail-step--active .qd-wizard-rail-num,
.qd-wizard-rail-step--done .qd-wizard-rail-num {
  background-color: var(--hr-dash-recent-btn-primary-bg, var(--brand-primary, #0a3373));
  color: var(--hr-dash-recent-btn-primary-color, #ffffff);
}

/* Step 1 — one row per group. The name input takes the width, the lives chip and
   the remove control sit at the end; the regulatory note (prefilled rows only)
   wraps under the input, which is what makes a recommended row visibly different
   from a hand-added one. */
/* The gap here is the SEPARATOR between groups, and it has to lose decisively to
   the 4px coupling inside `.qd-group-row-main` — otherwise a row's regulatory
   note is equidistant between its own field and the next one, and reads as the
   caption for the group BELOW it. That is a wrong-by-one-row misreading of a
   mandatory minimum cover ("Standard DHA floor"), so it is a correctness
   requirement, not spacing taste. It was 10px against a 7px coupling. */
.qd-group-wizard-rows {
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 16px 24px;
}

.qd-group-row {
  display: flex;
  align-items: flex-start;
  gap: 12px;
}

.qd-group-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

/* The row's regulatory note, attached to the FIELD ABOVE IT — the second half of
   the separation above. It keeps `.qd-review-note`'s §3 metadata tone (ink-400,
   12px) unchanged and overrides only the attachment, the same way `.qd-fix-count`
   and `.qd-wizard-foot .qd-review-note` override that shared note elsewhere.

   Two signals, because proximity alone was what failed:
     - the top margin goes, leaving the parent's 4px coupling against an 18px
       gap to the next row;
     - it is INDENTED to the input's own text inset (`--field-padding-x`), so the
       sentence begins exactly where the group's name begins. Alignment, unlike
       spacing, survives being glanced at.
   A hand-added group simply has no note, which reads as "no regulatory floor
   stated" rather than as a broken row: the note was never a fixed slot in the
   row, and the 18px rhythm is identical either way. */
.qd-group-row-note {
  margin: 0 0 0 var(--field-padding-x, 14px);
}

/* "Add a group" — the app's own secondary button. It was a bespoke DASHED ghost,
   which made it the third answer to "what does an add-a-thing control look like"
   in one feature; `.cat-add-trigger` was deleted from the categories board on
   this same branch for exactly that, and the tone chip below it went the same way
   (see `.qd-block-action`'s note above). Its stated reason for not being a §6
   pill — the footer's commit is this step's primary, and a second filled button
   would compete — is fully satisfied by the outline secondary, which leaves the
   solid fill to the primary. So only the geometry is local now; fill, border,
   radius, type and states all come from `.hr-dash-recent-btn-outline--sm`. */
.qd-group-add-row {
  align-self: flex-start;
}

/* The wizard's pinned footer: the sentence explaining what the button WRITES on
   the left, the button on the right. The sentence is not optional chrome — it is
   the only place the flow admits that crossing this button creates real groups. */
.qd-wizard-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 18px;
  flex-shrink: 0;
  padding: 14px 24px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.qd-wizard-foot .qd-review-note {
  margin: 0;
  flex: 1;
  min-width: 200px;
}

/* Same pinned bar, but for a footer that is ONLY buttons — the review-proposal
   modal's Adjust manually + Apply pair. The base `space-between` exists to hold a
   sentence apart from its button; with nothing between them it would fling the
   two buttons to opposite ends of a 1040px card. Charte §9 puts a save bar at the
   right, secondary first. */
.qd-wizard-foot--end {
  justify-content: flex-end;
}

/* The hidden-selection note in step 2's bulk bar. Muted against the count it
   qualifies — it is a caveat on that number, not a second number. */
.cat-selection-bar-hidden {
  font-weight: 500;
  opacity: 0.75;
}

/* ── The fix-in-place list is a REAL TABLE (`<DataTable>` → `.hr-dash-recent-*`) ──
   `.qd-fix-row`/`-who`/`-lives`/`-fields`/`-action` and the `.qd-fix-row-secondary`
   block WERE HERE and are deleted, not orphaned. The row was a bespoke 3-column
   grid, which held alignment until a row needed to say something about a SECOND
   person and grew a full-width band spanning all three columns — the exact
   misalignment the grid existed to prevent. This is a work list a hundred families
   long, so it now wears the app's own table vocabulary (header row, one fact per
   column, aligned the whole way down) and a dependant is a SUB-ROW on those same
   columns rather than a band underneath. `.qd-fix-row-btn` survives: it is the
   action pill's cell-level reset, and the categories board's own blocker cell
   (`.cat-missing-info-cell`, via `<MemberIncompleteStatus>`) wears it too.

   Compact rhythm via the shared padding TOKENS rather than an override of
   `.hr-dash-recent-th/-td` (reused by dozens of other tables) — same scoping
   discipline as `.qd-quotes-table`, and the same numbers. */
.qd-fix-table {
  --hr-dash-recent-th-padding: 9px 24px;
  --hr-dash-recent-td-padding: 10px 24px;
}

.qd-fix-table .hr-dash-recent-tr:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #fbfcfe);
}

/* A family and its sub-rows are ONE block: the divider falls after the last of
   them, never between a member and their dependants. Specificity has to beat
   `.hr-dash-recent-tr:not(:last-child) .hr-dash-recent-td`, hence the doubled
   row class. */
.qd-fix-table .hr-dash-recent-tr.qd-fix-tr--grouped .hr-dash-recent-td {
  border-bottom-color: transparent;
}

/* Names wrap rather than truncate — a clipped name is the one thing HR needs to
   recognise the person by (Charte §21). */
.qd-fix-member-name,
.qd-fix-subrow-name {
  overflow-wrap: anywhere;
}

/* A FLOOR under the Member column, so an ordinary first+last name gets a whole
   line. `table-layout: auto` sizes by content demand, and the pills column always
   demands more, so this column was settling narrow enough to wrap the one string
   HR recognises the person by. The floor is on the CELL, never a `<colgroup>` or
   a table `min-width` — those are what break the mobile horizontal scroll.

   Sized for the widest ordinary row: 14px icon + a ~17-character name + the lives
   chip, plus the 24px cell padding either side. Genuinely long names still WRAP
   (`overflow-wrap: anywhere` above) rather than truncate — an ellipsis on a name
   defeats the column's whole purpose, and this list is read to find a person.
   The sum of every column's floor stays well under the 1040px card, so nothing
   here introduces a horizontal scroll on desktop, where `.hr-dash-recent-table-wrap`
   has no `overflow-x` to catch one. */
.qd-fix-cell-member {
  font-size: 13.5px;
  min-width: 280px;
}

/* …and when even that is not enough, the LIVES CHIP is what yields — it drops to
   a second line and the name keeps the first. The name would otherwise shrink to
   near-nothing before the chip ever moved: `overflow-wrap: anywhere` gives it a
   one-character min-content, so as a flex item it always loses. Hence a floor on
   the name too, which is what makes the line wrap instead. */
.qd-fix-cell-member .ins-pol-company--inline {
  flex-wrap: wrap;
}

.qd-fix-member-name {
  min-width: 130px;
}

.qd-fix-lives {
  flex-shrink: 0;
}

/* One step down from the main row's name: a sub-row is a person the row is
   ABOUT, not the person it is titled by. Note this DIVERGES from the two sibling
   lists that render the same dependant with the same tree glyph — the census list
   and `<MemberAssignmentTable>` both give the child's name the full
   `.ins-pol-company-name` treatment and let the glyph alone say "dependant".
   Here the size/weight step already says it, so the tone follows.

   A dedicated token rather than `--hr-dash-recent-td-color`, which it first read:
   this span sits inside `.hr-dash-recent-td--name`, a cell that deliberately
   overrides the body-cell tone to the NAME tone, so the rule is not inheriting
   that token's signed-off "table BODY CELL" meaning — it is authoring a new one
   that merely equals ink-700 on Heale today. Same call, same reason, as
   `--tl-preview-label-color` and `--qd-block-text-color`. */
.qd-fix-subrow-name {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--qd-fix-subrow-name-color, #4a4640);
}

/* The pills are a cell's whole content here, not a sub-note under a name, so
   they lose the 4px offset they carry inside `.qd-missing-info-row-main`. */
.qd-fix-cell-missing .qd-missing-info-tags {
  margin-top: 0;
}

.qd-fix-cell-reason .qd-review-note {
  margin-top: 0;
}

/* A cell with nothing to say prints an em-dash — a run of blank cells reads as a
   rendering fault rather than as "not applicable here" (same reasoning, same
   §3 metadata tone, as `.qd-quotes-td-na`). */
.qd-fix-na {
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* A sub-row's WHY cell: it says this member is NOT blocking anything, so it is
   deliberately the §3 metadata tone and never a status colour — a coloured
   "not blocking" would say the opposite of its own words. */
.qd-fix-subrow-note {
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qd-fix-cell-action {
  text-align: right;
  white-space: nowrap;
}

/* The §6 primary pill `.qd-dash-prereq-action` already established for "this
   action opens a panel over the screen" — only its checklist-row top offset is
   wrong inside a table cell that is already aligned. */
.qd-fix-row-btn {
  margin-top: 0;
}

/* The row action, ONE weight for every row in the table. `.qd-fix-secondary-btn`
   WAS HERE and is renamed, not orphaned: it meant "one weight below the main
   row's filled pill", and there is no other weight left for it to be below. Both
   rows now wear the app's secondary button — see the Action column in
   `fix-in-place-modal.tsx` for why a work list with no CTA of its own has no use
   for a second weight. No arrow: it opens a panel over this modal and goes
   nowhere. */
.qd-fix-action-btn {
  flex-shrink: 0;
}

/* =====================================================================
   Categories — "Review proposed categories" screen (auto-categorization,
   spec §8). Reuses `.hr-dash-recent-card`/`-head`/`-title`/`-subtitle`,
   `.qd-missing-info-row`/`-row-main`/`-name`/`-link`, `.task-tag` and
   `.wizard-actions` — only the shapes with no existing equivalent below.
   ===================================================================== */
.qd-review-row-icon {
  color: var(--brand-secondary, #4374f3);
  vertical-align: -2px;
  margin-right: 4px;
}

.qd-review-note {
  margin: 3px 0 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qd-review-note--error {
  color: var(--dash-status-rejected-color, #dc2626);
  font-weight: 600;
}

/* Sizes the `.combo-inline` wrapper around a manual-choice `<Combobox>` — one
   fixed track per row so several ambiguous families line their pickers up
   instead of each sizing to its own candidate names. */
.qd-review-choice {
  width: 230px;
  max-width: 100%;
}

/* Condensed "Cannot be placed" summary (Fix, replacing the old itemized-per-
   family list): one `.qd-dash-unassigned-row` — same warning-row convention
   the Categories dashboard card already uses for its own unassigned-members
   line — with a "Review →" link opening `<BlockedFamiliesModal>` for the
   per-person detail. `qd-review-blocked` supplies the 24px edge padding
   `.qd-missing-info-row` uses elsewhere in this screen, since the row itself
   only carries its own compact internal padding. */
.qd-review-blocked {
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  padding: 16px 24px;
}

/* No sibling row above it here (unlike the dashboard card, where a second
   warning row can follow the unassigned one) — drop the shared class's own
   top margin so it doesn't stack with this container's own padding. */
.qd-review-blocked .qd-dash-unassigned-row {
  margin-top: 0;
}

.qd-review-blocked-links {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex-shrink: 0;
}

/* THE CURRENT-POLICY CARD'S FOUR RULES WERE HERE — its stat grid, its two stat
   text rules and its countdown — and are DELETED, not orphaned. The block is a
   LINE now, not a card: `<CurrentPolicySection>`, wearing the `.qd-inputs`
   strip's own section classes plus `.qd-inputs-countdown` below. Two of the
   grid's four stat cells were `categories` figures rather than policy facts and
   went with it; the remaining three facts do not need a grid, a stat scale or a
   card head. Stale CSS for a moved element is exactly the debris this sheet's
   tombstones exist to prevent.

   The four names are deliberately NOT spelled out. A comment naming a selector
   that has no rule sends the next reader hunting for one — the very rot a
   tombstone is meant to prevent — and `US-FLD.12.2` fails the build on it.
   Anyone arriving from an old commit or spec story searching the retired names
   lands on the component above, which is where the behaviour went. */

/* Dashboard — first visit: two-step setup card */
.qd-setup-step {
  padding: 20px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qd-setup-step:last-child {
  border-bottom: none;
}

.qd-setup-step--locked {
  opacity: 0.5;
  pointer-events: none;
}

.qd-setup-step-head {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0 0 14px;
}

.qd-setup-step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  border-radius: 999px;
  background-color: var(--dash-status-inactive-bg, #eef2f6);
  color: var(--dash-status-inactive-color, #6b7280);
  font-size: 12px;
  font-weight: 700;
  flex-shrink: 0;
}

.qd-setup-step-num--done {
  background-color: var(--dash-status-approved-bg, #d1fae5);
  color: var(--dash-status-approved-color, #059669);
}

.qd-setup-step-title {
  font-size: 15px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
}

.qd-setup-options {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.qd-setup-option {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 16px;
  border-radius: 12px;
  border: var(--ins-pol-seg-border, 1.2px solid #e9e4dc);
  background-color: #ffffff;
  cursor: pointer;
  text-align: left;
  width: 100%;
}

.qd-setup-option:hover {
  border-color: var(--brand-secondary, #4374f3);
}

.qd-setup-option--active {
  border-color: var(--brand-primary, #0a3373);
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 6%, transparent);
}

.qd-setup-option-radio {
  flex-shrink: 0;
  margin-top: 2px;
  width: 16px;
  height: 16px;
}

.qd-setup-option-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0 0 2px;
}

.qd-setup-option-sub {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* =====================================================================
   Dashboard — first visit, step 1 "Yes — everyone gets the same plan":
   the refusal panel (`<SinglePlanRefusalPanel>`). ONE inner cell — Charte
   §8's inner-metadata tone (cream on Heale via `--modal-total-bg`, radius
   12, no border, no shadow) — holding WHY the answer could not be
   recorded. The accepted answer renders nothing at all: it is committed by
   the step's own Save & Continue and the screen moves on. Everything
   inside the cell reuses the classes this screen already has
   (`.qd-missing-info-name`, `.qd-review-note`, `.task-tag`,
   `.qd-dash-unassigned-row`); only the shapes with no existing equivalent
   are below.
   ===================================================================== */
.qd-single-plan {
  margin-top: 14px;
  padding: 16px 18px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #eef2f9);
}

/* The one verdict that STOPS the step: "Yes" on a census spanning several
   jurisdictions. §12's ERROR tone, deliberately not the pending gold the rest of
   this screen wears for an outstanding prerequisite — gold says "not done yet",
   and this is "cannot be true". §2's tinted-surface shape: the rejected token at
   14% for the fill, the same token at 100% for the heading. The reasoning lines
   keep the body tone; a whole block in rust reads as a system error rather than
   as an answer. */
.qd-single-plan--blocked {
  background-color: var(--dash-status-rejected-bg, #fee2e2);
}

.qd-single-plan--blocked .qd-missing-info-name {
  color: var(--dash-status-rejected-color, #dc2626);
}

/* The named jurisdictions behind a `multiple_jurisdictions` refusal. They
   are listed as items rather than folded into the sentence: the refusal
   has to survive being read literally, and "you have staff in two
   jurisdictions" without saying WHICH is the version that sends HR
   looking for a bug. */
.qd-single-plan-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 12px;
}

.qd-single-plan-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-radius: 10px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
}

.qd-single-plan-actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 14px;
}

/* What `Confirm and continue` WRITES, under the button rather than over it: it
   describes the click, so it is read once the button it belongs to has been
   noticed. Keeps `.qd-review-note`'s §3 metadata tone and overrides only the
   offset — this is a footnote to the actions row, not a third paragraph of the
   verdict. */
.qd-single-plan-commit-note {
  margin-top: 10px;
}

/* A refusal the census did NOT explain (`already_categorized`, a 500, a
   network failure): the server's own sentence — never re-worded
   client-side — above whatever way out its `code` maps to. Stacked rather
   than a row, since the sentence is a full line of prose, and flush to the
   cell: it is the only thing in it when it renders. */
.qd-single-plan-refusal {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}

/* The single-plan box's way out (`<SinglePlanActionLink>`): a real SECONDARY
   control on the shared `.hr-dash-recent-btn-outline`, never a fourth button
   style. It sits beside a §6 PRIMARY pill (the answer-selecting action), which is
   why it must not be solid too — two filled pills side by side compete for the
   same click.

   It KEEPS its trailing `→`: it navigates to `/hr/quotes/categories`, and the
   arrow is what distinguishes it from the arrow-less `.qd-dash-prereq-action`
   pill, which opens something over the page (see both classes above). A button
   shape does not exempt an action from that rule.

   Only the anchor reset is needed on top of the shared class — `<a>` brings its
   own underline, which the outline button does not want. This replaced
   `.qd-setup-goto-categories`, a quiet text link whose last consumers these were;
   the class was deleted rather than left behind. */
.qd-single-plan-action {
  text-decoration: none;
}

/* Dashboard — configured: warning row inside the Categories card — coral,
   not a plain neutral row. Shared by TWO warnings that render independently
   (each gated on its own count, no all-good state): unassigned-members
   (§6.3, "the most consequential silent failure in the flow") and, directly
   below it, employees missing required information (folded in from what
   used to be a standalone "Insured Members" card). */
.qd-dash-unassigned-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 14px;
  border-radius: 10px;
  margin-top: 10px;
  background-color: color-mix(in srgb, var(--brand-accent, #fb7a5c) 12%, transparent);
}

/* The same box in the PENDING tone, for a message that is merely still
   outstanding rather than a standing risk. Charte §12 gives "action needed"
   coral (what the two rows above are) and "pending / waiting" gold-500 (what a
   blocked prerequisite is), so the tone is swapped WHOLESALE — fill and text
   both move to the `--dash-status-pending-*` pair, per §2. Retoning only the
   fill would leave a coral sentence on a gold wash, which is the one thing §2
   forbids; that is why the text rule below exists even though this modifier's
   first consumer (`<BlockingReasonNotice>`, via `.qd-dash-cta-notice`) brings
   its own lighter sentence class. */
.qd-dash-unassigned-row--pending {
  background-color: var(--dash-status-pending-bg, #fef3c7);
}

.qd-dash-unassigned-text {
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-accent, #fb7a5c);
  margin: 0;
}

.qd-dash-unassigned-row--pending .qd-dash-unassigned-text {
  color: var(--dash-status-pending-color, #d97706);
}

/* Geometry only, no forced colour — an icon nested in `.qd-dash-unassigned-text`
   (e.g. the review screen's blocked-families summary) inherits the coral text
   colour via `currentColor`, same as every other lucide icon in this app. */
.qd-dash-unassigned-icon {
  vertical-align: -2px;
  margin-right: 2px;
}

/* ── A strip of chips ──
   The one chip rhythm the quotes dashboard states a settled fact in: the named
   groups of an answered step 1 (`<SettledGroupChips>`, shared by the setup
   screen's locked step 1 and the configured screen's inputs block) and the
   company-information digest's conditions/licence/attachment tags. It carries no
   spacing of its own — the container that holds it owns that, because its two
   homes are a flex column with its own gap and a wrapping line that sits it
   beside a sentence. */
.qd-chip-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

/* A chip that is NOT a value — an input that is still missing, stated where the
   input is listed and carrying its own way out (an empty group, or no groups at
   all: both link to where members are assigned).

   IT MUST NOT LOOK LIKE `.task-tag`, WHICH IS THE WHOLE POINT. An absence
   rendered in the shape of a value ("Makekeke · 0 lives" beside "Dubai · 1
   life") reads as data, so nobody acts on it. Three signals separate them, and
   all three are deliberate:

     - §12 PENDING tone, applied §2-style — the fill is the status token at 14%
       and the text is the SAME token at 100%, never a different hue for each;
     - a DASHED border — the one shape in this sheet that says "empty slot", and
       what distinguishes it from a filled status pill that states a real status;
     - it is a link or a button, never a `<span>`, and it keeps its "→" when it
       navigates.

   Coral is deliberately NOT used here even though an empty group is a mistake
   worth fixing: inside this strip coral belongs to `.qd-dash-unassigned-row`
   (a full-width statement about the member list) and gold to the inline
   outstanding item. Two families, two tones — one tone doing both jobs is how
   they stop being legible. */
.qd-chip-gap {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  border: 1px dashed color-mix(in srgb, var(--dash-status-pending-color, #d97706) 45%, transparent);
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 14%, transparent);
  color: var(--dash-status-pending-color, #d97706);
  font-size: 11.5px;
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
}

.qd-chip-gap:hover {
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 22%, transparent);
}

/* ── Dashboard — configured: the "quote request inputs" strip ──
   Everything a quote request is built from — the groups and the company
   information — in ONE quiet surface (`quote-request-inputs.tsx` — see its
   header). The button that builds it is NOT here any more: it lives in the
   Requests card, with every reason it is off beside it (`.qd-start`).

   IT IS NOT A CARD, DELIBERATELY, AND THAT IS WHAT THIS RULE BUYS. It shipped
   as two `.hr-dash-recent-card`s, then as one, and both read as a PEER of the
   Current policy / Active request / Past renewals cards below it. It is not
   their peer — it is the caption of a button — so it takes a DIFFERENT KIND of
   surface from every white card on the page:

     - the §8 tinted "read-only summary sitting directly on the page body" fill,
       the same token (and same fallback chain) `.hr-dash-recent-card--tinted`
       reads — a warm neutral that carries no status meaning, which is the point:
       the coral and `--pending` tones on this screen mean something, and this
       surface must not be mistaken for either. That token is the only one in the
       sheet already GUARANTEED to differ from every theme's page body
       (`hr/quotes/new/__tests__/page.test.tsx` US-NQ.61/.62), which is what
       stops a "quieter" surface from disappearing into the background instead;
     - no card head, no card title, no border and no shadow — a card's chrome
       measured ~104px here, more than the content it framed;
     - one 20px line per section instead of two, so the whole settled case is
       shorter than the card head it replaced.

   The white cards keep the elevation; this one keeps the tint. That inversion IS
   the hierarchy. */
/* ONE RIGHT-HAND AXIS ACROSS BOTH BLOCKS. The horizontal padding is the CARD
   BODY's (`.ins-pol-card-body`, 24px), not a value of its own — the strip's
   section actions and the Requests card's fixes are the same family of control
   and were landing on two axes 10px apart, which reads as a mistake rather than
   as two containers. The vertical padding stays this strip's own: 12px is the
   compactness that makes it a caption rather than a card, and it costs no
   alignment because nothing lines up across blocks vertically. */
.qd-inputs {
  border-radius: var(--brand-radius, 12px);
  /* Even top and bottom, and not tighter than the 19px BETWEEN sections — an
     outer padding smaller than the internal rhythm makes the first and last rows
     look pinned to the edges of the tint. */
  padding: 18px 24px;
  background-color: var(--hr-dash-recent-card-tinted-bg, var(--modal-total-bg, #eef2f9));
}

/* Rows are multi-line now (Company information runs to four), so the 10px this
   replaces let three thematic areas blur into one block.

   SPACE ONLY — NO HAIRLINES between sections, deliberately. Rules would give the
   band the weight of a table and put it in competition with the Requests card
   above it, which is the one thing this block is arranged not to do: it is a
   quiet preamble, and the tint is already doing the containing. */
.qd-inputs-section + .qd-inputs-section {
  margin-top: 19px;
}

/* ── THE BAND HEADER: WHAT THIS IS, AND WHETHER IT IS COMPLETE ──
   The band sits BELOW the requests now (see `quote-request-inputs.tsx`), so a
   reader reaches it having already decided the page's main question. One line
   tells them whether anything down here wants attention: the §3 label, a muted
   gloss, and the completeness marker pinned right.

   It wraps rather than clipping — the incomplete marker is a whole sentence
   ("2 things missing before you can request a quote"), and at narrow widths it
   takes its own line under the label. */
.qd-inputs-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding-bottom: 10px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qd-inputs-head-note {
  font-size: 11.5px;
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* §2 tinted surface — the token at 14% for the fill and the SAME token at 100%
   for the text, never a flattened pair. Two states only: green-500 says the
   inputs are complete, §12 pending says how many are not. Never coral: an
   incomplete input is an outstanding item, not an alert, and the coral on this
   screen belongs to a request that cannot be sent. */
.qd-inputs-state {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-left: auto;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
}

.qd-inputs-state--complete {
  background-color: color-mix(in srgb, var(--dash-status-approved-color, #059669) 14%, transparent);
  color: var(--dash-status-approved-color, #059669);
}

.qd-inputs-state--incomplete {
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 14%, transparent);
  color: var(--dash-status-pending-color, #d97706);
}

/* THIS BAND HAS NO COLUMN LAYOUT, and the classes for one were deleted rather
   than parked: two rounds tried a side-by-side arrangement — three equal
   columns, then a Groups row above a pair of columns — and both were withdrawn.
   It is three full-width `.qd-inputs-section` rows, which is the shape the
   pre-redesign strip already had.

   WHY A SIDE-BY-SIDE LAYOUT CANNOT WORK HERE, so it is not attempted a third
   time: Groups is a variable-length LIST of chips named from tenant data, and a
   third-width measure (~360px) cannot hold one ("Category A – Management" is
   ~220px a chip). The chips stacked one per line and the one list on the band
   drove its whole height. At full width they sit inline. */

/* The row's headline — the one fact it exists to state ("3 groups · 216 lives",
   "HealthCROSS · Expires 31 Dec 2026 · 216 lives"). A step up from
   `.qd-inputs-digest`, which follows it on the same line as the muted rest, so
   the two read as value and detail rather than as two equal sentences. §21: it
   wraps, it never clips. */
.qd-inputs-value {
  min-width: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
  overflow-wrap: anywhere;
}

/* Nothing on file yet, said in the row's own value slot rather than as a chip or
   a toned pill — this band states only what EXISTS. That an absence BLOCKS a
   request is stated once, above, where every outstanding item can be counted
   together. */
.qd-inputs-value--muted {
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* ── THE ONE ACCENT LINE IN THIS BAND ──
   The declared insurer differs from the live policy's. It is the PAYOFF for
   keeping Company information and Current policy as two adjacent ROWS instead of
   reconciling them into one sentence, and it is an OBSERVATION, not a gap: it
   never enters the problem list, never moves the completeness marker, never
   gates the CTA (see `insurerDiffersFromPolicy()`).

   Coral text, and NO FILL — which is what separates it from everything that IS a
   verdict. A tinted pill here would read as a status the reader has to clear;
   this is a difference they have to know about. Same treatment, same reason, as
   `.qd-request-fact-note--alert` on a request row.

   `flex-basis: 100%` claims its own line inside the row's wrapping content
   column. It is the ONE child that does: the value, its detail and the chips all
   flow inline and wrap only when they run out of room, but a warning inlined
   between a premium and a chip strip is a warning nobody reads. */
.qd-inputs-alert {
  flex-basis: 100%;
  min-width: 0;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--brand-accent, #fb7a5c);
  margin: 0;
  overflow-wrap: anywhere;
}

/* NOTHING RELEASES `.qd-block-action`'s `margin-left: auto` any more. The
   withdrawn column layout above needed a rule that did — three buttons chasing
   one right-hand axis across three grid tracks read as three misalignments — and
   it went with the columns. In a full-width row that auto-margin is exactly
   right: it is what gives the two actions their single shared axis. */

/* ── ONE ROW PER SECTION: A FLOWING COLUMN, THEN THE ACTION COLUMN ──
   §3 label + the section's facts + its chips on the left; its single way in
   pinned to the box's right edge.

   TWO COLUMNS, NOT ONE WRAPPING ROW — do not restore `flex-wrap` here from an
   old comment. The line used to be a single wrapping row with `margin-left:auto`
   on the action, which put the action at the right edge of whatever line it
   happened to land on: a long chip strip pushed it onto a row of its own, and
   the two sections' actions sat at different heights and different left edges
   depending on how much content each had. The outer row therefore does NOT
   wrap; `.qd-inputs-line-main` wraps INSIDE its own column, so the content can
   grow to any height without ever moving the action.

   `align-items: flex-start`, NOT `center` — and this is the fix for a real
   rendered fault, not a preference. Company information is two or three lines
   tall (value + detail, the insurer-discrepancy line, a chip strip), and with
   centring its label and its action floated to the vertical middle of all that:
   the label sat ~25px below the row's own first line, and the two actions no
   longer shared a horizontal axis, so they stopped reading as a column. Both now
   pin to the row's FIRST line, which is the line they are about. (The value this
   replaces was itself a fix — it was `baseline`, which a button has none worth
   sharing with a 9.5px label. Neither centring nor baselines; top.) */
.qd-inputs-line {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

/* The flowing half. `min-width: 0` is what lets it shrink instead of shoving the
   action out of the box — §21: content wraps, it never clips and never pushes. */
.qd-inputs-line-main {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 7px 10px;
}

/* ── THE ACTION COLUMN ──
   Every section action, and the warning rows' actions with them, share a floor
   width so the block reads as ONE right-hand column rather than as controls
   landing wherever their row's content leaves them.

   A FLOOR, NOT A FIXED WIDTH — and this is the deliberate part. The labels are
   deliberately verb + object and differ a lot in length ("Manage groups" vs
   "Update company information"), so equal widths would mean sizing every action
   to the longest one: a 200px "Assign" button, mostly empty. What the eye
   actually follows down the block is the RIGHT EDGE, which is shared exactly;
   the floor only stops a short label from shrinking to a stub that reads as a
   chip. Same 140px floor, and the same reasoning, as `.wizard-actions`. */
/* The warning rows whose actions this selector list used to align are GONE from
   the strip — every problem, blocking or not, is stated in the block beside the
   CTA now. So the second axis that could not be reconciled (a control inside a
   coral box, inset by that box's own padding) no longer exists: what is left is
   the strip's two section actions and the block's fixes, and those DO share one
   axis, since the strip takes the card body's horizontal padding. */
.qd-inputs-line > .qd-block-action {
  min-width: 140px;
  justify-content: center;
}

/* ── TIER TWO: THE QUIET ROW ACTION ──
   The inputs band's two ways in ("Manage groups", "Update company
   information"). Same shape, same size, same column as a fix above — but no
   border and no fill until hover.

   WHY TWO TIERS, and this IS the resolution of the round that made every action
   identical — do not flip it back to either extreme. A text link read as body
   text, which is what made these buttons in the first place; a full outline on
   every one of them put five equal buttons on the screen with the only filled
   one disabled, so nothing read as the way forward. The split is by JOB:

     - a FIX (in the problem list) is the path to the goal — full outline, tier
       one;
     - a ROW ACTION is MAINTENANCE: it changes a fact that is already correct,
       and nothing is wrong if it is never pressed — ghost, tier two.

   IT WORKS BECAUSE THE ACTION IS PINNED BESIDE ITS CONTENT. Borderless reads as
   a quiet PEER of the text next to it, and in a full-width row there is text
   next to it. A round that promoted both to the full outline belonged to a
   withdrawn three-column layout, where the action sat at the BOTTOM of a column
   with nothing beside it and so read as body text at rest. The geometry is what
   decides this: if these ever leave a row, re-derive the tier from the new
   geometry rather than assuming either answer.

   Still obviously clickable: it keeps the button's geometry and gets a real
   hover fill, so it is a control that is quiet, not a label that happens to
   respond. */
.qd-block-action--ghost {
  border-color: transparent;
  background-color: transparent;
}

.qd-block-action--ghost:hover {
  background-color: var(--hr-dash-recent-btn-outline-bg-hover, #f9fafb);
}

/* THE WARNING ROWS' ACTIONS JOIN THAT COLUMN, but from INSIDE their own box —
   they share the floor width and the right alignment, and their right edge lands
   the row's own 14px padding short of the section actions'. That inset is
   deliberate: a control inside a tinted surface aligns to that surface, and
   pulling it flush with the strip would need the row's right padding to go to
   zero, leaving the button touching the coral edge and the box looking broken.
   Sharing the width is what makes the four read as one column; the 14px is read
   as the box's inset, not as a misalignment. */

/* The right-edge rule that used to live here (`.qd-inputs-line > .qd-dash-prereq-*`)
   is now `.qd-block-action`, which every action in these blocks carries — one
   rule instead of one per container, and a name the arrow guard can scan for. */

/* §3 section label — 9.5px Bold uppercase, ALWAYS ink-400, never semantically
   coloured. It is also the only naming this block gets now that the card title
   is gone.

   ── IT IS A FIXED COLUMN, WHICH IS WHAT MAKES THE BAND READ AS A TABLE ──
   `min-width` so every row's value starts on ONE x axis. With `flex-shrink: 0`
   alone each label was only as wide as its own text, so "GROUPS", "CURRENT
   POLICY" and "COMPANY INFORMATION" pushed their values to three different left
   edges and the band read as three unrelated lines rather than three rows of one
   thing. 152px is sized to clear the longest of the three; it is a floor, so a
   longer label in some future row simply widens its own row rather than
   truncating.

   THE COLUMN BELONGS TO A DIRECT CHILD OF `.qd-inputs-line`, and that selector
   IS the fix. It was `.qd-inputs-line-main > .qd-inputs-label` — the label INSIDE
   the wrapping content column — which gave a fixed width to nothing: a wrapping
   flex container returns every item that lands on a second line to the
   CONTAINER's left edge, and that edge is under the label. So the label column
   existed only on each row's FIRST line, and `0 unassigned`, `Differs from the
   policy insurer` and the whole company chip row escaped back to the band's
   padding edge instead of hanging off the value axis.

   As a SIBLING the label owns a real track, so anything that wraps inside the
   content column aligns to the value. Nothing but a label ever returns to the
   band's left edge. `flex: 0 0` rather than `min-width`: it must neither grow
   into the value's measure nor shrink under a long one — a column, not a floor.

   THE SCOPE IS STILL LOAD-BEARING, and the child combinator is what keeps it
   narrow. `.qd-inputs-label` is the §3 micro-label everywhere, including the
   band's own header; the `<CurrentPolicySection>` that `<EmptyStateView>`
   renders (whose label sits inside `-main` — correct there: a lone section with
   no sibling rows to align to); and the one that would BREAK, the four `<dt>`s
   of an open request row's fact grid, whose cells are `minmax(130px, 1fr)` and
   would overflow under a 152px track. The combinator reaches none of them.

   `line-height` comes with it for the same reason it exists: the label is
   top-aligned against a 12px value now (see `.qd-inputs-line`), and a 9.5px
   label's default leading would sit its cap-height a couple of pixels high. */
.qd-inputs-label {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
  flex-shrink: 0;
}

.qd-inputs-line > .qd-inputs-label {
  flex: 0 0 152px;
  line-height: 18px;
}

/* The section's whole answer in one sentence — the settled-groups summary, or
   the company digest's insurer · years · premium. A step down from a card's
   12.5px body: this is a caption. §21: it wraps, never clips. */
.qd-inputs-digest {
  min-width: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
  overflow-wrap: anywhere;
}

/* HOW A SECTION WITH NOTHING TO SHOW YET SAYS SO: one short phrase in metadata
   tone ("No groups yet.", "No current-insurance information on file yet."),
   never a chip per absence — this strip states only what EXISTS, and an absent
   input is not data the request will be built from. That it BLOCKS a request is
   stated once, beside the CTA it blocks, where every outstanding item can be
   counted together (`.qd-blockers`). The section's own way in stays the way to
   act on it here.

   The one absence that is still an outstanding item in this strip is an EMPTY
   GROUP (`.qd-chip-gap`) — a group HR really created, which exists, prices
   nothing, and is silently dropped from the request. */
.qd-inputs-digest--muted {
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The renewal countdown, riding on the end of the CURRENT POLICY section's line
   (`<CurrentPolicySection>`). It keeps the BRAND SECONDARY it wore on the card
   this section replaces — deliberately not a §12 status tone: "Renewal in 120
   days" is a fact about a date, and gold or coral would make the strip announce
   a problem the screen does not have. Bold rather than sized up, because it has
   to hold its own beside the digest without becoming a second heading, and it
   does not shrink: it is the shortest thing on the line and the reason the
   section is on the page at all. */
.qd-inputs-countdown {
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  margin: 0;
  flex-shrink: 0;
}

/* `.qd-inputs-meta` WAS HERE and is DELETED, not orphaned: a secondary fact
   riding on the end of the digest sentence ("· 24 total lives"). Its one
   consumer was the groups line's settled sentence, which is gone — that sentence
   is wizard guidance rather than an input, and it could contradict the chips
   beside it (see `quote-request-inputs.tsx`). The total is now the section's own
   `.qd-inputs-digest` value, with nothing for it to ride on. */

/* Chips are WHITE inside the tint, not sand — `.task-tag`'s own fill IS this
   strip's fill on Heale, so an unscoped chip would vanish into it. Flipping them
   to the card surface also states the hierarchy the right way round: on this one
   block the facts are lifted off the surface and the surface is the quiet thing.
   Sized down with the rest of the strip.

   The two chip families arrive with two different inks — `.task-tag` reads
   slate-700 (the controls tone) and `.qd-dash-file-chip` the inactive-status
   grey — and side by side in one row that reads as an accident. They are unified
   on this block's own body token, NOT on `--hr-dash-recent-td-color`: see
   `.qd-blocker-text` above for why a table-cell token is the wrong place to
   borrow from. */
.qd-inputs .task-tag,
.qd-inputs .qd-dash-file-chip {
  padding: 3px 10px;
  font-size: 11.5px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  color: var(--qd-block-text-color, #4a4640);
}

/* Tighter between chips than the family's own 6px: here the strip sits them
   BESIDE the sentence on one line rather than under it, so the row's 10px column
   gap is already doing the separating. */
.qd-inputs .qd-chip-row {
  gap: 5px;
}

/* ── Dashboard — configured: WHERE THE PAGE ACTION IS NOT ──
   `.qd-start` — the action zone at the top of the Requests card, holding the CTA
   with every reason it was off beside it — is DELETED, along with
   `.qd-start:not(:last-child)`'s divider and its primary-button sizing. Recorded
   rather than silently dropped, because this is the fourth home that button has
   had and each move was argued for at length:

     - it was `.qd-inputs-cta`, inside the inputs strip;
     - then `.qd-dash-cta-block`, centred in a column of its own;
     - then `.qd-start`, in the Requests card, welded to `.qd-blockers`;
     - it is now the PAGE TOPBAR's (`<NewQuoteRequestButton>`), and its reasons
       are `.qd-blockers` inside the inputs band.

   The reversal that produced the last move: the open requests were promoted to
   the FIRST block on the page (they are what HR opens the screen for; the inputs
   are settled by definition on a configured screen and were spending the fold to
   say "fine"). A CTA plus a blocker list at the top of that card would have
   pushed the requests back down by exactly as much as the strip used to.

   No divider rule survives either — the Requests card's body now holds exactly
   one thing, the list or the empty state, so there is nothing to separate. */

/* Every reason a request is blocked, enumerated — now a block in the inputs
   band, under its completeness marker and above its descriptive columns. It is
   no longer a flex CHILD of anything (that was `.qd-start`'s row), so it carries
   no `flex`/`min-width` of its own: it takes the band's full width and its items
   wrap inside it. */
.qd-blockers {
  min-width: 0;
}

/* §3 section label — 9.5px Bold uppercase, ALWAYS ink-400, never semantically
   coloured, even though everything under it is a §12 pending item. Two of these
   can render at once: "Before you can request a quote", then the advisory
   heading below. */
.qd-blockers-label {
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0 0 6px;
}

/* The SECOND heading in this block: problems that are real but are NOT in the
   way ("Not stopping you, but worth fixing"). It follows a list, so it needs the
   air a first heading doesn't — and it is separated by a hairline, because the
   one thing it must never be mistaken for is more of the list above it. Same §3
   ink-400 as its sibling: the difference between the two groups is what they SAY,
   not a tone, and colouring one of them would make "not stopping you" look like a
   status. */
.qd-blockers-label--advisory {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qd-blockers-list {
  display: flex;
  flex-direction: column;
  /* Sized against the fix beside each item: these rows carry a 32px secondary
     button, not a chip, so they need more air between them than a text list. */
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* ONE ITEM PER BLOCKER, so they can be counted: a dot, what is outstanding, and
   the way to close it. Never a paragraph and never one fused sentence — that is
   the shape this replaced. */
.qd-blocker {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 4px 10px;
}

/* The second group's items are NOT pending gate items — they are real problems
   that stop nothing — so their marker drops to §3 metadata. The heading says it
   in words; this stops the row from LOOKING like a blocker while it says so. */
.qd-blockers-list--advisory .qd-blocker-dot {
  background-color: var(--ins-pol-subtext-color, #8d8579);
}

/* §12 pending, the same token the fix beside it reads — the only marker the item
   needs, and cheaper than an icon per row. */
.qd-blocker-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background-color: var(--dash-status-pending-color, #d97706);
}

/* §21: it wraps, never clips. Body ink rather than the status colour — the tone
   that means "still outstanding" belongs on the dot and on the fix; a whole
   coloured sentence per row would out-shout the button these explain.

   THE INK IS THIS FAMILY'S OWN TOKEN, NOT `--hr-dash-recent-td-color`. Both this
   and the strip's chips below briefly read that one, which is ink-700 on Heale
   and therefore looked right — and `hr-dashboard-charte-2026.test.ts`
   (US-HRD.1.6) caught it, correctly. That token's signed-off meaning is "table
   BODY CELL": borrowing it for a list item and a chip would put both inside the
   blast radius of every future retint of every `<td>` in the app, for a
   resemblance rather than a shared meaning. Same call, and the same reason, as
   `--tl-preview-label-color` and the dozen other per-family body tones in
   `heale/style.css`. */
.qd-blocker-text {
  min-width: 0;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--qd-block-text-color, #4a4640);
  overflow-wrap: anywhere;
}

/* ── `.qd-block-action` — EVERY action in the two quote-request blocks ──
   The inputs strip's section ways-in ("Manage groups →", "Edit"), its warning
   rows' actions ("Assign →", "Complete"), and each blocker's fix ("Manage
   Categories →", "Upload trade licence"). Not the CTA, which is the one primary.

   ═ THE RULE, IN ONE SENTENCE ═

   EVERYTHING ACTIONABLE IN THESE BLOCKS IS THE SAME SECONDARY BUTTON, AND
   NOTHING ABOUT ITS APPEARANCE ENCODES WHERE THE CLICK LANDS.

   That is the end state of four deliberate reversals on this branch, each of
   which lost to the next. Kept in full, because the reasoning that lost is what
   stops it being reinvented:

     1. SHAPE said where the click lands — pill = acts on this page, quiet text
        link = navigates. It lost because, rendered, a text link does not read as
        pressable at all: beside a real button it is body text, so the way OUT of
        a blocked state was the one thing that didn't look clickable.
     2. A fix attached to a missing input wore the §12 pending tone at chip
        proportions, so it could not out-shout a CTA disabled at 45%. It lost
        because it read as a TAG rather than a control — same fault as (1), one
        step quieter. The constraint it existed for is met a better way: these
        carry no FILL, so the primary is still the only filled control anywhere
        near them.
     3. The ARROW then carried "this one leaves the page" alone, which is all the
        arrow had ever meant. It lost because it is a 6px glyph asked to be the
        sole difference between two otherwise identical controls — a distinction
        that fine is not one a reader makes, and a copy-paste drops it silently.
     4. What is left is uniformity, and it is now the rule rather than a
        casualty: one shape, one weight, no ornament. Where a click lands is
        stated by the LABEL ("Manage groups" leaves; "Update company information"
        opens the panel) — the one channel a reader actually reads.

   Consequence to accept rather than work around: the destination is no longer
   inferable from the control. If a click's destination ever genuinely needs
   announcing, say it in words; do not reintroduce a glyph and call it a rule.

   THE ARROW RULE STILL GOVERNS `.qd-dash-prereq-*`, which these blocks no longer
   use but the Empty view's checklist and `<BlockingReasonNotice>` still do — see
   above. Do not "align" those two here by accident.

   The class exists (rather than the geometry being repeated per container) so
   the rule stays STRUCTURALLY CHECKABLE: `action-affordance-shape.test.ts` scans
   for it and asserts every one is a real outline button and none carries an
   arrow. Scanning the outline class itself would be useless — the quotes area
   has arrow-less outline `<Link>`s outside this rule's scope.

   HIERARCHY: none of these carries a FILL. That is what keeps the CTA the
   primary even while disabled at 45% — it is the only filled control in the
   Requests card, and the inputs strip now has no filled control at all (it used
   to hold two blue-filled pills, which was the fault this fixes in passing).

   Geometry only: it holds the right edge on one line or after wrapping, so every
   way out is found in the same column, whichever container it sits in. */
.qd-block-action {
  margin-left: auto;
  flex-shrink: 0;
}

/* ── THE OPEN REQUESTS, INSIDE THE REQUESTS CARD ──
   They used to be one `.hr-dash-recent-card` EACH, siblings of the card named
   "Requests" — which left that card the only thing on the screen containing no
   requests, and gave a four-fact row three nested levels of chrome. They are
   rows in that card's body now, in the list rhythm the rest of the app uses.
   NEVER a card inside a card: that is the shape these rules replace. */

/* NO SUB-HEAD SITS OVER THESE ROWS, and the rule for the one that used to —
   the pluralized "Active request(s)" heading — was deleted rather than left
   orphaned. The open COUNT rides in the card's own title now ("Requests · 2
   open"), so a heading under it named the same rows a second time. */

/* The empty state of the card, which is now the FIRST block on the page rather
   than the last — so it says plainly that nothing is in flight and then, in the
   caller's own quieter line, whether that is expected and whether a request
   could be started. Not a `<ListState>`: there is no icon and no action, because
   the action is in the topbar and an empty requests list is a normal state, not
   an error or a dead end. */
.qd-requests-empty {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 6px 0 2px;
}

.qd-requests-empty-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
}

.qd-requests-empty-note {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
  max-width: 62ch;
}

.qd-request-list {
  display: flex;
  flex-direction: column;
}

/* One request. The action holds the right edge; everything else stacks in the
   column it is given (§21 — content wraps, it never clips and never pushes), and
   the whole row folds when there is no measure left for two columns.

   `align-items: flex-start` — the row is three stacked bands tall now (head,
   progress, fact grid), so a vertically-centred action would float in the middle
   of it instead of aligning with the name it acts on. */
.qd-request-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px 16px;
  padding: 16px 0;
}

/* A hairline BETWEEN rows only, never above the first and never below the last
   (§10: no divider on the last row). */
.qd-request-row + .qd-request-row {
  border-top: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qd-request-row:first-child {
  padding-top: 4px;
}

.qd-request-row:last-child {
  padding-bottom: 2px;
}

.qd-request-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}

/* The name and the whose-move pill, on one line — the pill is a caption on the
   name, not a separate statement, so it must never wrap onto its own row while
   there is measure for it. */
.qd-request-head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 10px;
  min-width: 0;
}

.qd-request-name {
  font-size: 17px;
  font-family: var(--heading-font, inherit);
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* ── WHOSE MOVE IT IS ──
   Three tones, because there are three answers — see `<OpenRequestRow>`'s header
   for why this replaces the ten-hue `quoteStatusConfig()` pill on THIS surface
   only. Each is a §2 tinted surface: the token at 14% for the fill and the SAME
   token at 100% for the text and the dot, never a different hue for each.

   The dot is `currentColor`, so a tone can never be changed in one place and not
   the other. */
.qd-request-state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  min-width: 0;
}

.qd-request-state-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 999px;
  background-color: currentColor;
}

/* §12 pending — out with insurers, nothing on you. */
.qd-request-state--waiting {
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #d97706) 14%, transparent);
  color: var(--dash-status-pending-color, #d97706);
}

/* §12 open — your move. The action tone, which is also the current segment of
   the progress bar beside it: one colour for "this is where you are". */
.qd-request-state--action {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, transparent);
  color: var(--brand-secondary, #4374f3);
}

/* §12 action-needed — coral, and the ONLY coral on this screen. It marks a draft
   that cannot be sent, which is the one state where pressing the row's action
   will not move the request forward. Coral is never a CTA fill here (§6). */
.qd-request-state--blocked {
  background-color: color-mix(in srgb, var(--brand-accent, #fb7a5c) 14%, transparent);
  color: var(--brand-accent, #fb7a5c);
}

/* ── THE FOUR-STAGE PROGRESS BAR ──
   Prepare → In market → Decide → Bound, as four discrete pills rather than one
   filled track: a continuous bar implies a percentage and there is none — a
   request does not sit "40% through In market". Segments also give the row a
   shape that can be scanned down a list.

   Done is the brand navy (settled), current is the action blue (the same tone
   the "your move" pill wears), and a future stage is the band's own hairline —
   present, so the reader can see how many stages are left, but carrying no
   claim. */
.qd-progress {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  max-width: 260px;
}

.qd-progress-seg {
  flex: 1;
  height: 4px;
  border-radius: 100px;
}

.qd-progress-seg--done {
  background-color: var(--brand-primary, #0a3373);
}

.qd-progress-seg--current {
  background-color: var(--brand-secondary, #4374f3);
}

.qd-progress-seg--todo {
  background-color: var(--hr-dash-recent-row-border, #eee8df);
}

/* ── THE FOUR FACTS ──
   Scope · Cover starts · Sent · Offers, each a §3 micro-label over its value.
   `auto-fit` + `minmax` rather than `repeat(4, 1fr)`: at narrow widths the grid
   folds to two columns and then to one on its own, and a fixed count would make
   the row overflow instead (§21). */
.qd-request-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 8px 18px;
  width: 100%;
  margin: 0;
}

.qd-request-fact {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.qd-request-fact-value {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
  overflow-wrap: anywhere;
}

/* The quieter second line of a fact — §3 metadata tone. It is a detail ABOUT the
   value above it, never a second value, which is why it is inside the same
   `<dd>` rather than a cell of its own. */
.qd-request-fact-note {
  font-size: 11px;
  font-weight: 500;
  line-height: 1.4;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The one note that is a WARNING rather than a detail: cover starting before the
   current policy ends means two policies running at once. Coral, the accent this
   screen reserves for "look at this", and never a fill — a tinted pill inside a
   fact cell would out-shout the value it qualifies. */
.qd-request-fact-note--alert {
  font-weight: 700;
  color: var(--brand-accent, #fb7a5c);
}

/* The row's own action never shrinks below its label and never stretches to the
   row's full height — it aligns with the name, which is what it acts on. */
.qd-request-action {
  flex-shrink: 0;
  align-self: flex-start;
}

@media (max-width: 768px) {
  /* The row folds to a stack, so the action stops being a column and becomes the
     thing at the bottom of the card — full width, because a 90px pill floating
     under a full-width fact grid reads as an afterthought and misses the 44px
     tap floor on the horizontal axis. */
  .qd-request-action {
    width: 100%;
    align-self: stretch;
    justify-content: center;
  }

  /* The bar is a proportion of the row, not a fixed measure, once the row is
     narrower than the desktop cap. */
  .qd-progress {
    max-width: none;
  }

  /* The band's completeness marker is a whole sentence when something is
     missing; on its own line it reads, pinned right it collides with the label. */
  .qd-inputs-state {
    margin-left: 0;
  }

  /* The label column is a table device and there is no table at this width — the
     row stacks, so a 152px floor would just indent every value off a narrow
     screen. Released, and the action goes full width with it (§21's 44px floor
     on the horizontal axis, same call as `.qd-request-action`). */
  .qd-inputs-line {
    flex-wrap: wrap;
  }

  .qd-inputs-line > .qd-inputs-label {
    flex: 0 0 100%;
  }

  .qd-inputs-line > .qd-block-action {
    width: 100%;
    margin-left: 0;
  }
}

/* ── `/hr/quotes/new` — the "Quotation details" form card ──
   FOUR fields do not need a card sized like a list. Scoped to this card only —
   the generic `.hr-dash-recent-card` rhythm is right for the tables and lists
   that wear it everywhere else — and it compresses the space BETWEEN things
   (head, body, field gaps), never the controls: the fields keep the field
   contract's 44px height. */
.nqr-details .hr-dash-recent-head {
  padding: 16px 20px 12px;
}

.nqr-details .ins-pol-card-body {
  padding: 16px 20px 18px;
}

.nqr-details-fields {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.nqr-details .ins-pol-form-grid {
  gap: 14px 16px;
}

/* A field error that belongs to a LIST rather than to one input: it follows the
   list instead of sitting inside an `.ins-pol-field`'s label/control stack, so
   it needs the gap that stack would have given it. */
.nqr-list-error {
  margin-top: 10px;
}

/* ── `/hr/quotes/new` — "What happens next" ──
   The quiet explainer at the foot of the request's right rail: a §8 tinted
   card carrying a §3 section label and three numbered steps. Every number in
   it is derived by the page (selected categories, `QUOTE_REQUEST_VALIDITY_DAYS`),
   so nothing here encodes a count. */
.nqr-next {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.nqr-next-steps {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nqr-next-step {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

/* §2 tinted surface: the action token at 14% for the fill, the same token at
   100% for the numeral. */
.nqr-next-num {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 999px;
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, transparent);
  color: var(--brand-secondary, #4374f3);
  font-size: 11.5px;
  font-weight: 700;
}

/* Company information summary chips (Configured state) */
.qd-dash-file-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 10px;
  border-radius: 999px;
  background-color: var(--dash-status-inactive-bg, #eef2f6);
  color: var(--dash-status-inactive-color, #6b7280);
  font-size: 12px;
  font-weight: 600;
  text-decoration: none;
}

/* THE CHIP ABOVE HAS NO LINKED VARIANT, and the modifier for one was deleted
   rather than left orphaned. It briefly made the Table-of-benefits /
   Claims-history chips real links on the quotes-dashboard band, since the signed
   URL was already on the summary and the chips named a file the screen offered
   no route to. Both chips were then removed from that band — they are artefacts
   of the DECLARED INSURANCE (the incumbent insurer's benefits table, the claims
   history under the current cover), and the row is about the COMPANY, so the
   modifier went with its only consumer.

   The fix itself is not lost: `<CompanyInformationForm>`, inside the panel that
   row's button opens, renders both documents with download links from the same
   signed URLs. `.qd-dash-file-chip` (the base, unlinked chip) stays — it is
   still used by `/hr/quotes/new`. */

/* `.qd-dash-audit-line` / `.qd-dash-history-link` lived here — the inputs
   block's "Last updated … by Jane Doe · View history" line. Deleted rather than
   left behind when that line was dropped from the strip (see
   `quote-request-inputs.tsx`): provenance is an audit question, not an input to
   a quote request, and the same affordance still ships on the Company
   Information tab, which styles its own. */

/* Past renewals (§6.5) */
.qd-dash-renewal-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 0;
  font-size: 13px;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.qd-dash-renewal-year {
  font-weight: 700;
}

/* =====================================================================
   Company Information form — `company-information-form.tsx` (Dashboard
   first-visit Step 2 + the later dedicated edit screen). `.ci-*` covers the
   medical-conditions checklist and its conditional "N declared — how many
   people?" count summary: a plain checklist first, then a SEPARATE block
   below listing only the checked conditions with a count stepper — an
   unchecked condition never renders a count control anywhere.
   ===================================================================== */

.ci-check-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.ci-check-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 2px;
  cursor: pointer;
}

.ci-check-row input[type='checkbox'] {
  width: 16px;
  height: 16px;
  accent-color: var(--brand-secondary, #4374f3);
  flex-shrink: 0;
}

.ci-check-label {
  font-size: 13px;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.ci-count-summary {
  border-radius: 12px;
  padding: 14px 16px;
  background-color: var(--modal-total-bg, #faf6f0);
}

.ci-count-summary-title {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0 0 10px;
}

.ci-count-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.ci-count-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.ci-count-row-label {
  font-size: 13px;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  flex: 1;
  min-width: 0;
}

.ci-stepper {
  display: inline-flex;
  align-items: center;
  gap: 0;
  border-radius: 999px;
  border: 1px solid var(--field-border, #e5e7eb);
  background-color: var(--field-bg, #ffffff);
  flex-shrink: 0;
}

.ci-stepper-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border: none;
  background: transparent;
  color: var(--brand-primary, #0a3373);
  cursor: pointer;
  border-radius: 999px;
}

.ci-stepper-btn:hover {
  background-color: var(--ins-pol-filter-bg-hover, #f2ede5);
}

.ci-stepper-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  background: transparent;
}

.ci-stepper-input {
  width: 34px;
  height: 28px;
  border: none;
  background: transparent;
  text-align: center;
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  outline: none;
  /* Hide native number spinners so the custom +/- buttons are the only affordance. */
  -moz-appearance: textfield;
}

.ci-stepper-input::-webkit-outer-spin-button,
.ci-stepper-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.ci-count-unit {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
  flex-shrink: 0;
  min-width: 46px;
}

/* ── The SAME editor, hosted in the right-docked `<SidePanel>` opened from
   `/hr/quotes/new` (`company-information-panel.tsx`) ──

   `<CompanyInformationTab>` is unchanged and so is the settings PAGE it also
   renders: there each block is a genuine CARD, floating on a wide tinted
   surface it needs chrome to separate itself from. A 430px panel is already a
   bounded container with its own padding, so that same chrome becomes a card
   inside a card — two sets of horizontal padding eating a rail that starts
   narrow, plus borders separating nothing and radii doing nothing. Every rule
   below is therefore scoped to `.company-info-panel` and re-reads the blocks as
   SECTIONS OF ONE PANEL. Nothing here may appear unscoped.

   The section divider is a `+` rule (a rule ABOVE each block after the first),
   never a bottom border on each block: a trailing rule under the last section
   is then structurally impossible rather than something a `:last-child` has to
   remember — and `:last-child` would in any case miss, since the tab's last
   child becomes the history modal while it is open. */

.company-info-panel .modal-head {
  padding: 14px 20px;
}

/* 20px gutters rather than the shell's 24px: at 430px that is 8px of content
   width back, and the content inside no longer has padding of its own. */
.company-info-panel .modal-body {
  padding: 14px 20px 20px;
}

.company-info-panel .ci-tab {
  gap: 0;
}

.company-info-panel .hr-dash-recent-card {
  background: transparent;
  border: none;
  border-radius: 0;
  box-shadow: none;
  overflow: visible;
}

.company-info-panel .hr-dash-recent-card + .hr-dash-recent-card {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

/* The card head becomes a plain §3 section label with its own action beside it
   — no fill, no rule under it, no gutters. */
.company-info-panel .hr-dash-recent-head {
  align-items: center;
  padding: 0 0 10px;
  border-bottom: none;
}

.company-info-panel .hr-dash-recent-title {
  margin: 0;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.company-info-panel .hr-dash-recent-view-all {
  font-size: 12px;
}

.company-info-panel .ins-pol-card-body {
  padding: 0;
}

.company-info-panel .wizard-actions {
  margin-top: 20px;
}

/* `.ins-pol-form-grid` is two columns, which at the panel's width leaves ~180px
   per field — enough to squeeze a premium input, not enough to read its label.
   The mobile single-column rule can't help here (the viewport is wide; the
   container is not), so the panel states it for its own content. */
.company-info-panel .ins-pol-form-grid {
  grid-template-columns: 1fr;
}

/* ── `<CompanySettingsForm>` hosted in the SAME right-docked shell
   (`company-profile-panel.tsx`, opened from `/hr/quotes`) ──

   Only two rules, and NOT by the same route `.company-info-panel` above takes.
   That panel flattens its editor's cards with scoped compound selectors; the
   settings family cannot be treated that way, because `settings-charte-2026`
   (US-FLD.15.3) forbids ANY `.settings-*` rule from being a compound — a
   heavier base compound in `global.css` silently out-specifies a brand file's
   single-class skin. So the card keeps its chrome here (which reads fine: it
   carries the company name and the view-only line), and the one thing that
   genuinely breaks at this width is fixed by the mechanism that actually
   describes the problem.

   `.settings-row` is two columns of a WIDE page. In a 430px panel that is
   ~150px per field — enough to squeeze a "Building" input, not enough to read
   its label. The app-wide single-column rule is a 768px VIEWPORT rule and
   cannot help a narrow container inside a wide viewport, so the panel body
   becomes a query CONTAINER and the row answers to its own width. Single
   class, so the specificity contract holds; and it is inert on
   `/hr/company-settings`, where nothing declares a container. */

.company-profile-panel .modal-head {
  padding: 14px 20px;
}

.company-profile-panel .modal-body {
  padding: 14px 20px 20px;
  container-type: inline-size;
}

@container (max-width: 520px) {
  .settings-row {
    grid-template-columns: 1fr;
  }
}

/* ── `<MemberMissingFieldsPanel>` in the SAME right-docked shell — the census
   member's still-missing fields, opened from the `/hr/quotes` "fix in place"
   modals ──

   Three zones per Charte §13: a head carrying the member's name plus a live
   "N fields still missing" line (which is why it builds its own head instead of
   passing `title` to `<SidePanel>`), a scrolling body of fields, and the
   committing action PINNED at the bottom — a save that scrolls away with the
   form is exactly what §13 forbids.

   Same 20px gutters and same single-column form grid as `.company-info-panel`
   above, and for the same reason: `.ins-pol-form-grid`'s two columns leave
   ~180px per field at this width, enough to squeeze an input but not to read
   its label. */

.member-fix-panel .modal-head {
  align-items: flex-start;
  padding: 14px 20px;
}

.member-fix-panel .modal-body {
  padding: 14px 20px 20px;
}

.member-fix-panel .modal-foot {
  padding: 14px 20px;
}

.member-fix-head-main {
  min-width: 0;
}

.member-fix-head-main .modal-title {
  overflow-wrap: anywhere;
}

.member-fix-subtitle {
  margin: 3px 0 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.member-fix-panel .ins-pol-form-grid {
  grid-template-columns: 1fr;
}

.member-fix-reason {
  margin: 0 0 6px;
}

.member-fix-hint {
  margin: 0 0 16px;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* An ANY-OF requirement inside the fix panel: one labelled cell holding the two inputs that can
   each close it (today the UAE visa-pending route — Emirates ID application number OR entry permit
   number). It is one requirement, so it wears one label; it has two ways to satisfy it, so it
   shows both. The hint is not decoration — without it the cell reads as two fields that are both
   required, which is the opposite of what `requiredAnyOf` means. */
.member-fix-anyof {
  display: flex;
  gap: 10px;
}

.member-fix-anyof > * {
  flex: 1;
  min-width: 0;
}

/* Sits between the label and the inputs, so it is read before either — §3 metadata tone. */
.member-fix-anyof-hint {
  margin: -2px 0 6px;
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

@media (max-width: 768px) {
  /* Two text inputs side by side have no measure left at 375px. */
  .member-fix-anyof {
    flex-direction: column;
  }
}

/* The escape hatch to the full record — quiet on purpose: everything this panel
   does NOT cover (documents, coverage, dependants) lives there, but it is the
   detour, not the task. */
.member-fix-fullrecord {
  display: inline-block;
  margin-top: 20px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  text-decoration: none;
}

.member-fix-fullrecord:hover {
  text-decoration: underline;
}

/* §13: the committing action is full-width in the footer. */
.member-fix-save {
  flex: 1;
  justify-content: center;
  min-height: 44px;
}

/* Company Information version-history modal (`company-information-history-
   modal.tsx`) — reuses the `.mf-modal-overlay`/`.mf-modal`/`.mf-hist-modal-head`
   shell (see `enrollment-history.tsx`, the closest visual precedent). Layout
   is header → bordered card of version rows (current row tinted) → info box
   → footer button row — see the component's header comment for the full
   wireframe mapping. */

/* Header: title + subtitle stack on the left, close (X) on the right. The
   base `.mf-hist-modal-head` vertically centers a single-line title against
   the close button; this modal adds a subtitle line, so the compound
   selector re-aligns to the top instead. */
.mf-hist-modal-head.ci-history-head {
  align-items: flex-start;
}

.ci-history-subtitle {
  margin: 2px 0 0;
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Compare-selection hint row, shown between the header and the card while
   `compareMode` is active and no result is showing yet — a way to back out
   of selection mode without using the top-right close (which would dismiss
   the whole modal). */
.ci-history-hint-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 12px;
}

.ci-history-select-hint {
  flex: 1;
  margin: 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The bordered card wrapping the version list — §8: no border on cards is
   the Charte default, but a version-history list is deliberately an
   inset, bordered card (per the reference wireframe) rather than a
   shadow-only surface, since it sits inside an already-elevated modal. */
.ci-history-card {
  margin-top: 16px;
  border: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  border-radius: 12px;
  overflow: hidden;
}

.ci-history-body {
  max-height: 48vh;
  overflow-y: auto;
}

.ci-history-row {
  padding: 14px 16px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ci-history-row:last-child {
  border-bottom: none;
}

/* Current (latest) version row — the only row with a tinted surface, per
   §2's "token @ 0.12–0.16 fill, same token at 100% for text" pattern. */
.ci-history-row--current {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 7%, white);
}

.ci-history-row--current .ci-history-version,
.ci-history-row--current .ci-history-summary {
  color: color-mix(in srgb, var(--brand-secondary, #4374f3) 82%, black);
}

.ci-history-row-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.ci-history-version {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.ci-history-meta {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
  white-space: nowrap;
}

.ci-history-dot {
  color: var(--ins-pol-subtext-color, #8d8579);
}

.ci-history-summary {
  font-size: 12.5px;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 4px 0 0;
  line-height: 1.45;
}

/* "Sent to insurers with <request name(s)>" tag (§2 tinted surface) — shown
   under a version's summary only once `sentWithRequests` names at least one
   quote request that carried this version out to insurers. */
.ci-history-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, white);
  color: color-mix(in srgb, var(--brand-secondary, #4374f3) 78%, black);
}

/* Per-row compare checkbox, shown only while `compareMode` is active. Max 2
   selections — enforced in component state, not here; the disabled visual
   is the standard opacity dim. */
.ci-history-checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 8px;
  cursor: pointer;
}

.ci-history-checkbox-row input[type='checkbox'] {
  width: 15px;
  height: 15px;
  accent-color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}

.ci-history-checkbox-row input[type='checkbox']:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.ci-history-checkbox-label {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Info box below the card — explains that quote requests keep the version
   they were sent with, independent of later edits. Inner-cell tone per §8
   (`--modal-total-bg`), no shadow. */
.ci-history-info-box {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin-top: 12px;
  padding: 12px 14px;
  border-radius: 10px;
  background-color: var(--modal-total-bg, #f8fafc);
}

.ci-history-info-box p {
  margin: 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.ci-history-info-icon {
  flex-shrink: 0;
  margin-top: 1px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Footer button row — "Compare two versions" (left) + "Close" (right),
   roughly equal width per the reference wireframe. */
.ci-history-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 16px;
}

.ci-history-footer .hr-dash-recent-btn-outline {
  flex: 1;
  justify-content: center;
  padding: 12px 18px;
}

/* Comparison result head — replaces the hint row once two versions are
   picked: the "Version X → Version Y" title + the only way back to the
   plain list (closing the modal is NOT required to get back). */
.ci-compare-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  margin-top: 12px;
}

.ci-compare-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.ci-compare-field-label {
  margin: 0;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ins-pol-subtext-color, #8d8579);
  align-self: start;
  padding-top: 2px;
}

/* Side-by-side diff table — a narrow field-label column + one column per
   compared version, so the two values sit genuinely next to each other
   (not inline with an arrow between them). Wraps to a single stacked
   column on mobile, see the `@media` block below. */
.ci-compare-table {
  display: flex;
  flex-direction: column;
}

.ci-compare-table-head {
  display: grid;
  grid-template-columns: minmax(100px, 150px) 1fr 1fr;
  gap: 12px;
  padding: 10px 16px;
  background-color: var(--modal-total-bg, #f8fafc);
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ci-compare-table-head-version {
  display: block;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* Old-version header column reads muted — same "old = dim, new = emphasized"
   convention carried through to the value cells below. */
.ci-compare-table-head-col--old .ci-compare-table-head-version {
  color: var(--ins-pol-subtext-color, #8d8579);
  font-weight: 600;
}

.ci-compare-table-head-date {
  display: block;
  margin-top: 2px;
  font-size: 11px;
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.ci-compare-table-row {
  display: grid;
  grid-template-columns: minmax(100px, 150px) 1fr 1fr;
  gap: 12px;
  align-items: start;
  padding: 13px 16px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.ci-compare-table-row:last-child {
  border-bottom: none;
}

.ci-compare-value {
  font-size: 12.5px;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.ci-compare-value--old {
  color: var(--ins-pol-subtext-color, #8d8579);
}

.ci-compare-value--new {
  color: var(--hr-dash-recent-name-color, #1c1a17);
  font-weight: 700;
}

/* File-type diff cell (`current_tob_file_path`/`current_claims_file_path`) —
   a compact filename + Preview/Download chip in place of the plain-text
   value, sized down from the wider `.mf-doc-*` document row shell to fit
   this narrow 3-column grid cell. Filename truncates with an ellipsis
   rather than wrapping/overflowing the column. */
.ci-compare-file-chip {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.ci-compare-file-icon {
  flex-shrink: 0;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.ci-compare-file-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ci-compare-file-actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
}

.ci-compare-file-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border-radius: 6px;
  border: 1px solid var(--mf-doc-btn-border, #d2d9e5);
  background: var(--mf-doc-btn-bg, #fff);
  color: var(--mf-doc-btn-color, #475569);
  cursor: pointer;
}

.ci-compare-file-btn:hover {
  background: var(--mf-doc-btn-bg-hover, #f8fafc);
}

@media (max-width: 768px) {
  /* Three side-by-side columns don't fit a phone width — collapse the
     diff table to one column per row (label, then old value, then new
     value stacked) rather than truncating/squeezing the version columns. */
  .ci-compare-table-head,
  .ci-compare-table-row {
    grid-template-columns: 1fr;
    gap: 4px;
  }

  .ci-compare-table-head-col {
    padding-top: 4px;
  }

  .ci-compare-value--old::before {
    content: 'Old: ';
    font-weight: 600;
  }

  .ci-compare-value--new::before {
    content: 'New: ';
    font-weight: 600;
  }
}

/* =====================================================================
   Quote Wizard shell — `/hr/quotes/[id]/[categoryId]` (`<QuoteWizard>`).
   Covers the 4-step progress stepper and the read-only "validated"
   banner that wrap the step components (`StepInformations`/
   `StepPlanNetwork`/`StepRequestedBenefits`/`StepValidate`, styled
   independently).
   ===================================================================== */

.qw-shell {
  /* `.qw-shell` is a direct flex item of `.dash-body` (flex-direction: column).
     Per spec, `margin: auto` on the cross axis makes the item ABSORB available
     space rather than stretch to fill it, so without an explicit `width: 100%`
     this shrink-wraps to its content's natural width — causing the shell to
     visibly narrow/widen depending on content (loading spinner vs full grid,
     collapsed vs expanded cards) even though `max-width` never changes.
     `width: 100%` makes sizing deterministic; `max-width` still caps it and
     `margin: auto` still centers it once capped.

     This does NOT rigidly pin its own height to the viewport — `.dash-body`
     scrolls normally (its default `overflow-y: auto`) and the shell just
     flows inside it like every other dashboard page. `position: relative`
     is kept here for general stacking-context hygiene. The progress sidebar
     (`.qw-side`) sticks to `.dash-body`'s scroll frame, not to this box. */
  position: relative;
  width: 100%;
  max-width: 1440px;
  margin: 0 auto;
}

/* The read-only banner / active step content flow normally one after another
   — no internal scroll region of its own. `display: flex; flex-direction:
   column; gap` just spaces them; scrolling for the whole wizard is handled by
   `.dash-body` like any other dashboard page. No `padding-bottom` reserve any
   more: the floating `.qw-footer` pill it protected is retired, replaced by
   the persistent `.qw-side` progress panel (see `.qw-layout`). */
/* `padding-top` matches the wrapper's own 20px rhythm and replaces the step
   rail's retired bottom hairline — this is the one wizard whose content wrapper
   had no top spacing of its own, because the rule was providing it. */
.qw-content {
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding-top: 20px;
  padding-bottom: 8px;
  background-color: var(--dash-body-bg, #f8fafd);
}

/* The floating Previous/Continue footer (`.qw-footer*`) is RETIRED. Step
   navigation, completion and the autosave indicator all live in the
   persistent progress sidebar now (`.qw-layout` / `.qw-side*` at the end of
   this file); backward navigation is the shared step rail. Nothing renders
   these classes any more, so they were deleted rather than left to rot. */

.qw-loading {
  padding: 80px;
}

.qw-loading-icon {
  color: var(--brand-secondary, #4374f3);
}

.qw-empty {
  padding: 40px;
  text-align: center;
  color: #6b7280;
}

/* The quote wizard's step rail is the shared `<WizardStepRail>` (`.mfw-step-*`)
   as of the bulk-import work; its own local stepper family was deleted with it.
   That family hung its completed dot off `--brand-success`, which no Heale token
   maps, so it kept rendering the retired success hex (§1). */

/* Read-only "validated" banner — info tone, reuses the `.qd-banner` shape. */
.qd-banner--info {
  background-color: var(--dash-body-bg, #f8fafd);
  border: 1px solid #dce7fd;
}
.qd-banner-icon--info {
  color: var(--brand-secondary, #4374f3);
  flex-shrink: 0;
}
.qd-banner-text--info {
  font-size: 14px;
  font-weight: 500;
  color: var(--brand-primary, #0a3373);
}

/* ─────────────────────────────────────────────
   Configuration Method screen ("8. Configuration method")
   `<ConfigurationMethodScreen>` — the per-category landing screen offering
   the 4 build routes (Basic / Upload / Guided / Manual), all converging on
   the same benefit table. Tiles are modelled on `.qd-setup-option*`
   (`dashboard/setup-view.tsx`) but arranged as a 2x2 grid with an icon,
   since these are 4 parallel routes rather than a linear radio choice.
   ───────────────────────────────────────────── */
.qcm-header {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin-bottom: 18px;
}

/* §3 panel title (20-21px). */
.qcm-title {
  font-size: 21px;
  font-weight: 800;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0 0 4px;
}

.qcm-tile-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}

@media (max-width: 640px) {
  .qcm-tile-grid {
    grid-template-columns: 1fr;
  }
}

.qcm-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 18px 20px;
  border-radius: 14px;
  border: var(--ins-pol-seg-border, 1.2px solid #e9e4dc);
  background-color: #ffffff;
  cursor: pointer;
  text-align: left;
  width: 100%;
  box-shadow: 0 2px 10px rgba(28, 26, 23, 0.05);
}

.qcm-tile:hover {
  border-color: var(--brand-secondary, #4374f3);
}

/* The default-selected/highlighted "Answer 10 questions" tile — same active
   treatment as `.qd-setup-option--active` (brand-primary border + a faint
   brand-secondary tint), applied unconditionally rather than on a selected
   state, since this tile is the recommended default, not a picked answer. */
.qcm-tile--recommended {
  border-color: var(--brand-primary, #0a3373);
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 6%, transparent);
}

.qcm-tile-badge {
  display: inline-flex;
  align-items: center;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  background-color: color-mix(in srgb, var(--brand-primary, #0a3373) 14%, transparent);
  color: var(--brand-primary, #0a3373);
  margin-bottom: 2px;
}

.qcm-tile-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  background-color: var(--dash-stat-icon-bg, color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, white));
  color: var(--dash-stat-icon-color, var(--brand-secondary, #4374f3));
  flex-shrink: 0;
  margin-bottom: 8px;
}

.qcm-tile-title {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  margin: 0;
}

.qcm-tile-sub {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #4a4640);
  margin: 0;
  line-height: 1.4;
}

.qcm-tile-meta {
  font-size: 11px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 6px 0 0;
}

/* A listed-but-unavailable tile (Basic, when no legal floor is published for
   this category's jurisdiction) — disabled + the reason, never hidden: same
   "shown LOCKED, never hidden" convention as every other gated affordance. */
.qcm-tile--disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

.qcm-tile--disabled:hover {
  border-color: var(--ins-pol-seg-border, #e9e4dc);
}

/* The reason is the SERVER's sentence — it names the jurisdiction and what to do
   instead — so this variant of `.qd-missing-info-tag` wraps across the tile
   width as a rounded block instead of staying a one-line pill. Tone/colour are
   untouched: it is still the same tag. */
.qcm-tile-reason {
  margin-top: 8px;
  align-self: stretch;
  max-width: 100%;
  padding: 7px 11px;
  border-radius: 10px;
  text-align: left;
  line-height: 1.5;
  white-space: normal;
}

/* "Back to the wizard" — only rendered when the tiles were reopened from the
   wizard header on purpose (change mode), so it is an escape from an escape
   hatch: same subordinate text-link treatment, never a button. */
.qcm-back {
  align-self: flex-start;
  margin-top: 6px;
}

/* "REMAINING CATEGORIES" list heading — §3 section label, always the
   metadata tone, never semantically coloured. */
.qcm-remaining-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0 0 10px;
}

/* ─────────────────────────────────────────────
   Specific requirements screen (`<SpecificRequirementsScreen>`, sketch 9)
   — the free-text notes HR sends to every insurer, at request level and
   per category. Only the pieces with no existing equivalent live here: the
   hint/counter row under a textarea and the per-category note rows. The
   cards, textareas, banner and action row are the shared `.hr-dash-recent-*`
   / `.modal-textarea` / `.qd-banner--info` / `.wizard-actions` families.
   ───────────────────────────────────────────── */
.qsr-meta-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 8px;
}

.qsr-hint {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

.qsr-counter {
  font-size: 12px;
  font-variant-numeric: tabular-nums;
  color: var(--ins-pol-subtext-color, #8d8579);
  flex-shrink: 0;
}

/* At the cap the counter is the only signal that further typing is dropped,
   so it takes the destructive tone rather than staying metadata-grey. */
.qsr-counter--full {
  color: var(--dash-status-rejected-color, #c2542e);
  font-weight: 700;
}

.qsr-note-row {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.qsr-note-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.qsr-note-row:first-child {
  padding-top: 0;
}

.qsr-note-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
  flex: 1;
}

.qsr-note-name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
}

/* The stored note, shown in full — never truncated: HR must be able to read
   back exactly what every insurer will receive. */
.qsr-note-preview {
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #4a4640);
  overflow-wrap: anywhere;
}

.qsr-note-preview--empty {
  color: var(--ins-pol-subtext-color, #8d8579);
  font-style: italic;
}

.qsr-note-btn {
  padding: 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
  flex-shrink: 0;
}

.qsr-note-btn:hover {
  text-decoration: underline;
}

/* Drops the revealed editor onto its own full-width line under the row. */
.qsr-note-editor {
  flex-basis: 100%;
}

@media (max-width: 768px) {
  /* The hint sentence and the counter can't share 375px without the hint
     collapsing to one word per line — stack them instead. */
  .qsr-meta-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
  }
}

/* ─────────────────────────────────────────────
   Category gap list — the rendering of `computeCategoryGaps`' output.
   Owned by the wizard's validate step today; the sketch-12 missing-
   information modal reuses the same family, which is why it is named for
   the data rather than for either screen.
   ───────────────────────────────────────────── */
.qgap-list {
  display: flex;
  flex-direction: column;
  align-self: stretch;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
  padding: 4px 0;
}

.qgap-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 10px 16px;
  text-align: left;
}

.qgap-item + .qgap-item {
  border-top: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.qgap-item-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
}

.qgap-item-help {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Wizard validate step — the pre-modal loading row and the modal's own
   centred body / split footer (Cancel left, draft + Confirm right). */
.qw-validate-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 80px;
}

.qw-validate-body {
  align-items: center;
  text-align: center;
  padding-top: 26px;
}

.qw-validate-foot {
  justify-content: space-between;
}

/* ─────────────────────────────────────────────
   HR quote wizard — "Plan, Premium & Network" step.
   The step's cards and field rows are the `.qw-pn-*` family at the end of this
   file (the bespoke card/field-row classes this block used to own were retired
   with the redesign — see the note further down), styled to match the wizard's
   own "Informations" step (`.qw-info-card`: white bg, 12px radius, 24px
   padding, no border) so both steps read as the same design system. What stays
   here is only what the step still renders on its own: the step title, the
   read-only dim, the per-card spinner, the network checkbox and the
   Covered/Optional switch. Deliberately does NOT reuse the insurer quote detail
   page's `.qd-collapsible`/`.qd-field-row`/`.dash-stat-icon` classes
   (see `src/app/(dashboard)/insurer/quotes/[id]/page.tsx`) — that page
   is a different, read-only surface and those classes stay untouched
   for it.
   ───────────────────────────────────────────── */
.qw-plan-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  margin: 0 0 20px;
}

/* Disables interaction + dims the whole step body while read-only
   (e.g. viewing a submitted category). */
.qw-plan-locked {
  pointer-events: none;
  opacity: 0.75;
}

/* Colours the per-card loading spinner via currentColor while a section's
   data is still loading (title/grid/cards render immediately; only the
   content inside a card body swaps for this spinner). */
.qw-plan-spinner {
  color: var(--brand-secondary, #4374f3);
}

/* Step 2's card grid, its collapsed-card chrome and its bespoke select/input
   controls were retired with the redesign: the step is now three `.qw-pn-card`
   cards of shared `<Combobox>`/`.ins-pol-input` fields (see `.qw-pn-*` at the
   end of this file). Only the pieces still rendered survive below. */

/* Provider-network checkbox — still rendered inside `.qw-pn-net-row`. */
.qw-plan-checkbox {
  width: 18px;
  height: 18px;
  accent-color: var(--brand-secondary, #4374f3);
}

/* Covered/Optional two-state switch (out-of-network reimbursement,
   pre-existing conditions). */
.qw-plan-switch {
  border-radius: 8px;
  border: 1px solid #e5e7eb;
  overflow: hidden;
}

.qw-plan-switch-btn {
  padding: 9px 16px;
  font-size: 13px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  background-color: #ffffff;
  color: #6b7280;
}

.qw-plan-switch-btn + .qw-plan-switch-btn {
  border-left: 1px solid #e5e7eb;
}

.qw-plan-switch-btn--covered.is-active {
  background-color: var(--brand-primary, #0a3373);
  color: #ffffff;
}

.qw-plan-switch-btn--optional.is-active {
  background-color: var(--brand-accent, #ff6733);
  color: #ffffff;
}

/* =====================================================================
   HR Quote Wizard — "Informations" step (`step-informations.tsx`): ONE card,
   the census member selection (`.qw-mem-*`). Step navigation lives in
   `<QuoteWizard>`'s shared step rail and progress sidebar (`.qw-side*`), not
   in this step.

   The step used to open with a quote-information card carrying the REQUEST's
   name, policy start date and currency. Those moved to `/hr/quotes/[id]`
   (`<RequestDetailsModal>`, `.modal-*`) — they are the request's own details,
   not the category's — and the card went with them, taking its
   `.qw-info-static-value` read-only value style along.

   Deleted at the same time, and named so they are not reinvented: the whole
   member-TABLE family this step once rendered (`.qw-info-table*`,
   `.qw-info-checkbox*`, `.qw-info-row*`, `.qw-info-chevron--collapsed`,
   `.qw-info-expand-toggle*`, `.qw-info-dep-*`), retired earlier when selection
   became the full-bleed `.qw-mem-*` list, and `.qw-info-save-state*`, the
   autosave indicator that sat beside that card's title — the status now shows
   in the wizard's progress sidebar. All of them outlived their only callers.
   ===================================================================== */

/* Was a `2fr 3fr` side-by-side split (`.qw-info-grid`), which left the quote
   card too narrow to put two fields on one row and read as unfinished (direct
   user correction). Stacking retired the grid's own `min-width: 0` track guard
   along with the 768px single-column override the split needed. It is a
   one-card column now, and stays a flex stack so the step can gain a second
   card without reintroducing that split. */
.qw-info-stack {
  display: flex;
  flex-direction: column;
  gap: 22px;
}

/* `--qw-info-card-pad` is read back by the member list, which escapes this
   padding to run its rows full-bleed and then re-applies it per row — so the
   two can never drift, including under the 768px override at the foot of the
   `.qw-mem-*` block. */
.qw-info-card {
  --qw-info-card-pad: 24px;
  background-color: #ffffff;
  border-radius: 12px;
  padding: var(--qw-info-card-pad);
}

.qw-info-card-title {
  font-size: 16px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

/* "Select all" / "Deselect all" text-link actions */
.qw-info-link {
  background: none;
  border: none;
  padding: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
  text-decoration: underline;
}

.qw-info-link--muted {
  color: #6b7280;
}

/* Head-count tile (Employees / Dependants / Lives) — distinct from
   `.dash-stat-card` (value-first layout, much smaller scale). An inner
   metadata cell per §8: cream fill, radius 12, NO border and no shadow —
   it sits inside `.qw-info-card`, so a border would read as a second card. */
.qw-info-stat {
  flex: 1 1 auto;
  min-width: 88px;
  padding: 10px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
  text-align: center;
}

.qw-info-stat-value {
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--hr-dash-recent-title-color, #1c1a17);
  margin: 0 0 2px;
}

/* §3 section label — 9.5px bold uppercase, always the metadata tone. */
.qw-info-stat-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* =====================================================================
   HR Quote Wizard — "Requested Benefits" step (`step-requested-benefits.tsx`,
   `<StepRequestedBenefits>`). Step navigation and the autosave indicator live
   in `<QuoteWizard>`'s shared step rail / progress sidebar (`.qw-side*`), not
   in this step. Reuses the
   `.qw-plan-locked`-style read-only dimming pattern (`.qw-benefits-locked`,
   same shape, own name since this component's CSS is being added
   independently of the plan-network step).

   The step is ONE card (`.qw-benefits-card`) holding a header row, the shared
   `.status-tabs` family strip and the ACTIVE family's rows only. The former
   one-long-page layout — a section per family, each with a sticky illustrated
   info panel, navigated by a scroll-spy tab bar — is gone, and so are its
   classes.

   Most inline styles here were purely static; a handful stay inline on purpose
   because the value is genuinely per-tab/per-row data (`tab.color`, a
   percentage slider fill) — each is called out in the component's own comments
   at the call site.
   ===================================================================== */

.qw-benefits-loading {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 80px;
}

.qw-benefits-spinner {
  color: var(--brand-secondary, #4374f3);
}

/* Covered/Optional two-state switch for main benefits. */
.qw-benefits-switch {
  display: flex;
  border-radius: 8px;
  border: 1px solid var(--qw-benefits-control-border, #e5e7eb);
  overflow: hidden;
}
.qw-benefits-switch-btn {
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  background-color: #ffffff;
  color: #6b7280;
}
.qw-benefits-switch-btn + .qw-benefits-switch-btn {
  border-left: 1px solid var(--qw-benefits-control-border, #e5e7eb);
}
/* When neither Covered nor Optional is selected, the inactive buttons drop
   to the more muted grey (vs. the darker grey used when the *other*
   button in the pair is active). */
.qw-benefits-switch--off .qw-benefits-switch-btn:not(.is-active) {
  color: #9ca3af;
}
/* §6 "selected control": the chosen segment is navy in BOTH positions — which
   one is chosen is carried by its label and position, not by a second hue.
   (Optional used to be filled with the brand accent, which read as a CTA.) */
.qw-benefits-switch-btn.is-active {
  background-color: var(--brand-primary, #0a3373);
  color: #ffffff;
}

/* Percentage slider (dropdown benefits with % options) — the fill width,
   fill/track colour and thumb `currentColor` are genuinely per-instance
   (bound to the owning tab's `color`), so those stay inline; everything
   else (box model) is static and lives here. */
.qw-benefits-slider-wrap {
  display: flex;
  align-items: center;
  gap: 16px;
  min-width: 240px;
}
.qw-benefits-slider-track-area {
  flex: 1;
}
.qw-benefits-slider-rail {
  position: relative;
  height: 18px;
}
.qw-benefits-slider-rail-bg {
  position: absolute;
  left: 0;
  right: 0;
  top: 6px;
  height: 6px;
  border-radius: 3px;
  background-color: var(--qw-benefits-rail-bg, #e5e7eb);
}
.qw-benefits-slider-rail-fill {
  position: absolute;
  left: 0;
  top: 6px;
  height: 6px;
  border-radius: 3px;
  transition: width 0.15s ease;
}
/* Nil (or no value): the fill has no per-tab colour to take, so it falls back
   to the neutral track tone instead of an inline literal. */
.qw-benefits-slider-rail-fill--empty {
  background-color: var(--qw-benefits-rail-bg, #e5e7eb);
}
.qw-benefits-slider-input {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 18px;
  appearance: none;
  -webkit-appearance: none;
  background: transparent;
  cursor: pointer;
  margin: 0;
  outline: none;
  z-index: 1;
}
.qw-benefits-slider-ticks {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 2px;
}
.qw-benefits-slider-tick {
  font-size: 10px;
  color: var(--qw-benefits-tick-color, #9ca3af);
}
/* The selected tick's COLOUR is the per-tab one (inline), except at Nil where
   there is none — hence the neutral emphasis tone here. */
.qw-benefits-slider-tick--active {
  font-weight: 600;
  color: var(--qw-benefits-label-color, #6b7280);
}
.qw-benefits-slider-chips {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.qw-benefits-slider-chip {
  padding: 4px 10px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 500;
  cursor: pointer;
  white-space: nowrap;
  background-color: #ffffff;
  border: 1px solid var(--qw-benefits-control-border, #e5e7eb);
  color: var(--qw-benefits-meta-color, #6b7280);
}
/* Selected: border/tint/label are the owning family's colour, applied inline
   because that colour is catalogue data. */
.qw-benefits-slider-chip--selected {
  font-weight: 600;
}

/* Custom range-input thumb styling — was an inline `<style>` tag; moved
   here since the rules are static (only the `color` prop driving
   `currentColor` is per-instance, and that stays inline on the input). */
input[type='range'].qw-benefits-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #ffffff;
  border: 3px solid currentColor;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
  cursor: pointer;
  margin-top: 0px;
}
input[type='range'].qw-benefits-range::-webkit-slider-runnable-track {
  height: 18px;
  background: transparent;
}
input[type='range'].qw-benefits-range::-moz-range-thumb {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: #ffffff;
  border: 3px solid currentColor;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
  cursor: pointer;
}
input[type='range'].qw-benefits-range::-moz-range-track {
  height: 18px;
  background: transparent;
  border: none;
}

/* The benefits body is a <fieldset> so `disabled` can lock every control
   inside it natively (a dimmed div left them keyboard-reachable). This strips
   the element's default chrome so the layout is identical to the div it
   replaced — `min-inline-size: 0` matters: a fieldset defaults to
   `min-content`, which would stop the grid inside it from shrinking. */
.qw-benefits-fieldset {
  border: 0;
  margin: 0;
  padding: 0;
  min-inline-size: 0;
}

/* Read-only/locked step body — same dim shape used by other wizard steps'
   `.qw-plan-locked`. The `disabled` fieldset above does the enforcing; this is
   the visual half only. */
.qw-benefits-locked {
  pointer-events: none;
  opacity: 0.75;
}

/* "Basic plan — admin-managed benefits" lock banner (info tone). */
.qw-benefits-lock-banner {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 12px 16px;
  margin-bottom: 16px;
  border-radius: 8px;
  background-color: #f1f5fe;
  border: 1px solid #93b4f8;
}
.qw-benefits-lock-icon {
  color: var(--brand-primary, #0a3373);
  margin-top: 2px;
  flex-shrink: 0;
}
.qw-benefits-lock-text {
  font-size: 13px;
  color: var(--brand-primary, #0a3373);
  line-height: 1.5;
}

/* The whole step is ONE card now (§8: no border, the shadow carries it):
   header row, then the family tab strip, then the active family's rows. The
   per-family info panel — icon, illustration, description — is gone. */
.qw-benefits-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 20px 24px;
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  background-color: #ffffff;
  border: 1px solid var(--hr-dash-recent-card-border, transparent);
  box-shadow: var(--hr-dash-recent-card-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
}

.qw-benefits-header {
  margin: 0;
  gap: 12px;
  flex-wrap: wrap;
}
.qw-benefits-title {
  font-size: 18px;
  font-weight: 600;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}
.qw-benefits-actions {
  gap: 8px;
  flex-wrap: wrap;
}

/* Pill shape for the header's two remaining utility actions (Apply template /
   Save as template). "Help me to configure" and "Import PDF" were removed —
   both duplicated a build route on the configuration-method screen — so only
   the `--muted` (navy outline) modifier survives. */
.qw-benefits-pill {
  gap: 6px;
  height: 36px;
  padding: 0 16px;
  border-radius: 100px;
  background-color: #ffffff;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid #e5e7eb;
  cursor: pointer;
}
.qw-benefits-pill--muted {
  color: var(--brand-primary, #0a3373);
  border-color: #cfd9ee;
}

/* The active family's rows. The tab strip that selects it is the shared
   `.status-tabs` / `.tabs-underline` row — the former scroll-spy tab bar, its
   section per family and their sticky illustrated info cards are gone, along
   with the one-long-page layout they navigated. */
.qw-benefits-panel {
  outline: none;
}

.qw-benefits-main-list {
  margin-bottom: 0;
}
.qw-benefits-main-list--with-additional {
  margin-bottom: 20px;
}

/* Main-benefit row — label left, control right, hairline between
   (`:last-child` replaces a per-index border-bottom check). */
.qw-benefits-row {
  padding: 13px 0;
  gap: 16px;
  border-bottom: 1px solid var(--qw-benefits-divider, #f3f4f6);
}
.qw-benefits-row:last-child {
  border-bottom: none;
}
.qw-benefits-label {
  font-size: 13px;
  color: var(--qw-benefits-label-color, #374151);
  flex: 1;
  min-width: 0;
}
.qw-benefits-row-controls {
  gap: 12px;
  flex-shrink: 0;
}
@media (max-width: 768px) {
  /* Below the fold width a label + a 240px slider cannot share a line without
     one of them being clipped, so the row stacks instead. */
  .qw-benefits-row {
    flex-wrap: wrap;
  }
  .qw-benefits-row-controls {
    margin-left: auto;
  }
}
/* Sizes the `.combo-inline` wrapper around a benefit-value `<Combobox>`. The
   trigger itself now takes the shared `.ins-pol-input` field geometry, so this
   no longer re-declares a bespoke 36px/6px control. */
.qw-benefits-select {
  width: 200px;
  max-width: 100%;
}

.qw-benefits-section-head {
  margin-bottom: 12px;
}
.qw-benefits-section-title {
  font-size: 14px;
  font-weight: 600;
  color: #6b7280;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.qw-benefits-section-actions {
  gap: 8px;
}
.qw-benefits-btn-request-all {
  padding: 5px 12px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  background-color: var(--brand-accent, #ff6733);
  color: #ffffff;
}
.qw-benefits-btn-delete-all {
  padding: 5px 12px;
  border-radius: 6px;
  font-size: 11px;
  font-weight: 600;
  border: 1px solid #e5e7eb;
  cursor: pointer;
  background-color: #ffffff;
  color: #374151;
}

.qw-benefits-additional-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 8px;
}
/* Resting state lives here; the SELECTED tint (border + background) is the
   owning family's catalogue colour and stays inline. */
.qw-benefits-chip {
  gap: 10px;
  padding: 10px 14px;
  border-radius: 8px;
  cursor: pointer;
  background-color: #ffffff;
  border: 1px solid var(--qw-benefits-control-border, #e5e7eb);
}
.qw-benefits-chip-checkbox {
  width: 16px;
  height: 16px;
}
.qw-benefits-chip-label {
  font-size: 13px;
  color: #6b7280;
  font-weight: 400;
}
.qw-benefits-chip-label--covered {
  color: #111827;
  font-weight: 500;
}

.qw-benefits-custom-wrap {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}
.qw-benefits-custom-wrap--divided {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px dashed #fde68a;
}
.qw-benefits-custom-head {
  gap: 8px;
  margin-bottom: 10px;
}
.qw-benefits-custom-title {
  font-size: 14px;
  font-weight: 600;
  color: #a16207;
  margin: 0;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.qw-benefits-custom-count {
  font-size: 10px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 4px;
  background-color: var(--brand-warning, #f59e0b);
  color: #ffffff;
  text-transform: uppercase;
  letter-spacing: 0.3px;
}
.qw-benefits-custom-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Custom (OCR-imported, non-catalog) benefit row — hover reveals the full
   label via `.qw-benefits-custom-tip` (was `.hr-custom-row`/`.hr-custom-tip`). */
.qw-benefits-custom-row {
  position: relative;
  gap: 10px;
  padding: 10px 12px;
  border-radius: 8px;
  background-color: #fffbeb;
  border: 1px solid #fde68a;
}
.qw-benefits-custom-tip {
  position: absolute;
  left: 12px;
  bottom: calc(100% + 6px);
  padding: 8px 12px;
  max-width: min(420px, 90vw);
  background: var(--brand-primary, #0a3373);
  color: #fff;
  font-size: 12px;
  line-height: 1.4;
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(10, 51, 115, 0.25);
  white-space: normal;
  word-break: break-word;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity 0.12s ease;
  z-index: 20;
}
.qw-benefits-custom-tip::after {
  content: '';
  position: absolute;
  top: 100%;
  left: 16px;
  border: 5px solid transparent;
  border-top-color: var(--brand-primary, #0a3373);
}
.qw-benefits-custom-row:hover .qw-benefits-custom-tip {
  opacity: 1;
  visibility: visible;
}
.qw-benefits-custom-info {
  gap: 8px;
  flex: 1;
  min-width: 0;
  overflow: hidden;
}
.qw-benefits-custom-tag {
  font-size: 9px;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 4px;
  background-color: var(--brand-warning, #f59e0b);
  color: #ffffff;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  flex-shrink: 0;
}
.qw-benefits-custom-label {
  font-size: 13px;
  color: #374151;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
  flex: 1;
}
.qw-benefits-custom-actions {
  gap: 8px;
  flex-shrink: 0;
}
.qw-benefits-custom-toggle {
  display: flex;
  border-radius: 6px;
  overflow: hidden;
  border: 1px solid #e5e7eb;
}
.qw-benefits-custom-toggle-btn {
  padding: 4px 10px;
  font-size: 11px;
  font-weight: 600;
  border: none;
  cursor: pointer;
  background-color: #ffffff;
  color: #6b7280;
}
.qw-benefits-custom-toggle-btn + .qw-benefits-custom-toggle-btn {
  border-left: 1px solid #e5e7eb;
}
.qw-benefits-custom-toggle-btn--danger.is-active {
  background-color: var(--brand-error, #dc2626);
  color: #ffffff;
}
.qw-benefits-custom-toggle-btn--success.is-active {
  background-color: var(--brand-success, #32a15e);
  color: #ffffff;
}
.qw-benefits-custom-remove-btn {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid #e5e7eb;
  background-color: #ffffff;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #6b7280;
  font-size: 16px;
  line-height: 1;
}

/* Note used by `<UploadPlanModal>`; the apply-template list this once sat
   beside is gone (see the `.tl-*` template library below). */
.qw-benefits-modal-empty {
  font-size: 13px;
  color: var(--tu-muted-color, #6b7280);
  text-align: center;
  padding: 24px;
}

@media (max-width: 768px) {
  .qw-benefits-card {
    padding: 16px;
  }
  /* Real tap targets for the header pills and the family tabs. */
  .qw-benefits-pill {
    height: 44px;
  }
  .status-tabs-seg {
    min-height: 44px;
  }
  /* The slider owns the row's second line here (see `.qw-benefits-row`'s
     wrap above), so its desktop 240px floor would only force a sideways
     scroll. */
  .qw-benefits-slider-wrap {
    min-width: 0;
    width: 100%;
  }
  .qw-benefits-select {
    width: 100%;
  }
}

/* =====================================================================
   Onboarding loading skeleton (`OnboardingLoadingSkeleton`, /hr/onboarding)
   — dedicated classes since the real page (page.tsx) is a legacy
   inline-styled mock with no shared classes to reuse.
   ===================================================================== */
.onboarding-skeleton {
  max-width: 900px;
  margin: 0 auto;
}

.onboarding-skeleton-stepper {
  display: flex;
  align-items: center;
  gap: 0;
  margin-bottom: 32px;
}
.onboarding-skeleton-stepper-step {
  display: flex;
  align-items: center;
  flex: 1;
}
.onboarding-skeleton-stepper-step-inner {
  display: flex;
  align-items: center;
  gap: 10px;
}
.onboarding-skeleton-stepper-num {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background-color: #e5e7eb;
  color: #6b7280;
  font-size: 13px;
  font-weight: 600;
}
.onboarding-skeleton-stepper-num--active {
  background-color: #0a3373;
  color: #ffffff;
}
.onboarding-skeleton-stepper-label {
  font-size: 13px;
  font-weight: 400;
  color: #6b7280;
}
.onboarding-skeleton-stepper-label--active {
  font-weight: 600;
  color: #0a3373;
}
.onboarding-skeleton-stepper-connector {
  flex: 1;
  height: 2px;
  margin: 0 12px;
  background-color: #e5e7eb;
}

.onboarding-skeleton-body {
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.onboarding-skeleton-intro {
  margin-bottom: 4px;
}
.onboarding-skeleton-intro-title {
  font-size: 20px;
  font-weight: 700;
  color: #0a3373;
  margin: 0 0 6px;
}
.onboarding-skeleton-intro-sub {
  font-size: 14px;
  color: #6b7280;
  margin: 0;
}

.onboarding-skeleton-card {
  background-color: #ffffff;
  border-radius: 12px;
  padding: 24px;
  border: 1px solid #e5e7eb;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.onboarding-skeleton-card-header {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 20px;
}
.onboarding-skeleton-card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: 8px;
}
/* Fixed 4-way icon palette mirroring the real onboarding page's own inline
   hex values (Building2/Shield/AlertCircle/FileText) — kept as-is rather
   than introducing new brand tokens for a mock page. */
.onboarding-skeleton-card-icon--building { background-color: #dce7fd; color: #4374f3; }
.onboarding-skeleton-card-icon--shield { background-color: #e0f8e9; color: #32a15e; }
.onboarding-skeleton-card-icon--alert { background-color: #fde8e8; color: #dc2626; }
.onboarding-skeleton-card-icon--doc { background-color: #fcf0d8; color: #eca04b; }
.onboarding-skeleton-card-title {
  font-size: 16px;
  font-weight: 600;
  color: #111827;
  margin: 0;
}

.onboarding-skeleton-label {
  font-size: 13px;
  font-weight: 500;
  color: #374151;
  display: block;
  margin-bottom: 6px;
}

/* Paired with the generic `.form-grid` helper (global.css mobile block)
   so these collapse to a single column on narrow screens. */
.onboarding-skeleton-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
}
.onboarding-skeleton-grid-3 {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 16px;
  margin-top: 16px;
}

/* =====================================================================
   /hr/documents loading skeleton (`DocumentsLoadingSkeleton`) — dedicated
   classes since the real page (page.tsx) is a legacy inline-styled mock
   with no shared classes to reuse.
   ===================================================================== */
.hr-docs-skeleton-header {
  width: 100%;
  height: 64px;
  padding: 0 24px;
  background-color: #ffffff;
  border-bottom: 1px solid #e5e7eb;
}
.hr-docs-skeleton-body {
  padding: 24px;
  flex: 1;
  overflow: auto;
  background-color: #f1f5fe;
}
.hr-docs-skeleton-card {
  background-color: #ffffff;
  border-radius: 12px;
  overflow: hidden;
}
.hr-docs-skeleton-table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}
.hr-docs-skeleton-thead-row {
  background-color: #f8fafc;
}
.hr-docs-skeleton-th {
  font-size: 13px;
  font-weight: 600;
  color: #0a3373;
  text-align: left;
  padding: 14px 16px;
  border-bottom: 1px solid #e5e7eb;
}
.hr-docs-skeleton-td {
  padding: 14px 16px;
  border-bottom: 1px solid #f3f4f6;
}
.hr-docs-skeleton-footer {
  padding: 12px 20px;
  border-top: 1px solid #f3f4f6;
}

/* =====================================================================
   /hr/templates loading skeleton (`TemplatesTableSkeleton`,
   `TemplatesPageSkeleton`) — dedicated classes; the real page is a
   legacy inline-styled mock with no shared classes to reuse.
   ===================================================================== */
.tpl-skeleton-page {
  padding: 24px;
  flex: 1;
  overflow: auto;
  background-color: #f1f5fe;
}
.tpl-skeleton-card {
  background-color: #ffffff;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid #e5e7eb;
}
.tpl-skeleton-table {
  width: 100%;
  border-collapse: collapse;
}
.tpl-skeleton-thead-row {
  background-color: #f8fafc;
}
.tpl-skeleton-th {
  font-size: 13px;
  font-weight: 600;
  color: #0a3373;
  text-align: left;
  padding: 14px 20px;
  border-bottom: 1px solid #e5e7eb;
}
.tpl-skeleton-th--center { text-align: center; }
.tpl-skeleton-th--right { text-align: right; }
.tpl-skeleton-td {
  padding: 14px 20px;
  border-bottom: 1px solid #f3f4f6;
  text-align: left;
}
.tpl-skeleton-td--center { text-align: center; }
.tpl-skeleton-td--right { text-align: right; }

/* =====================================================================
   Platform-role "view as company" flow — `<ViewingAsBanner>` +
   `<CompanyScopePicker>` (src/components/ui/). First used on /hr/census;
   any future page a superadmin/white_label_admin can scope to one tenant
   (policies, claims, …) is expected to reuse both.
   ===================================================================== */

/* Rendered as a normal flex child ABOVE <DashTopbar> inside `.dash-surface`
   (a fixed-height, overflow-hidden flex column) — no `position: fixed`
   needed to stay pinned above the topbar/body; see the component's own
   comment for the full reasoning. Solid amber fill (not the usual diluted
   `color-mix` tint) + a WCAG-AA-for-bold-text-contrasting dark amber text
   is deliberate: this banner must read as "you are not looking at your own
   data" at a glance, stronger than any other warning treatment in the app. */
.viewing-as-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-shrink: 0;
  padding: 10px 28px;
  background-color: var(--brand-warning, #eca04b);
  color: var(--brand-warning-text, #92400e);
}

.viewing-as-banner-text {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 700;
}

.viewing-as-banner-exit {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  min-height: 32px;
  padding: 6px 14px;
  border-radius: 100px;
  border: 1px solid var(--brand-warning-text, #92400e);
  background-color: transparent;
  color: var(--brand-warning-text, #92400e);
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
  white-space: nowrap;
}

.viewing-as-banner-exit:hover {
  background-color: rgba(255, 255, 255, 0.35);
}

/* Full-viewport centering shell for the portal-wide gate in
   `(dashboard)/hr/layout.tsx` — the picker/spinner it wraps is the ONLY
   thing rendered in place of `.dash-surface`'s content area while a
   platform caller has no company selected (the sidebar hides itself
   independently, see Sidebar's own platform + no-scope self-suppression). */
.platform-view-scope-shell {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 100%;
  padding: 24px;
}

/* Framing block above the company picker card: tenant logo + a heading
   naming the platform caller's role, so the bare picker doesn't leave the
   caller guessing who they're logged in as or why they're seeing it. */
.platform-view-scope-frame {
  text-align: center;
  margin-bottom: 24px;
}

.platform-view-scope-heading {
  font-size: 20px;
  font-weight: 700;
  color: #111827;
  margin: 16px 0 4px;
}

.platform-view-scope-subtext {
  font-size: 14px;
  color: #7d93b3;
  margin: 0;
}

/* Company picker — dropped where the census table/empty-state normally
   sits, inside the standard `.hr-dash-recent-card` treatment (same card
   language as <ModuleDisabledState>) so it doesn't look like a one-off. */
.company-scope-picker {
  width: 100%;
  max-width: 480px;
  margin: 40px auto;
  padding: 40px;
  text-align: center;
}

.company-scope-picker-title {
  font-size: 18px;
  font-weight: 600;
  color: #111827;
  margin: 0 0 8px;
}

.company-scope-picker-desc {
  font-size: 14px;
  color: #7d93b3;
  line-height: 1.5;
  margin: 0 auto 24px;
  max-width: 360px;
}

.company-scope-picker-controls {
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-align: left;
}

.company-scope-picker-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 11px 28px;
  border-radius: 10px;
  border: none;
  background-color: var(--brand-accent, #ff6733);
  color: #ffffff;
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  transition: opacity 0.15s;
}

.company-scope-picker-btn:hover:not(:disabled) { opacity: 0.9; }
.company-scope-picker-btn:disabled {
  background-color: #d2d9e5;
  color: #9ca3af;
  cursor: not-allowed;
}

@media (max-width: 768px) {
  .viewing-as-banner {
    flex-wrap: wrap;
    padding: 10px 16px;
  }
  .company-scope-picker {
    max-width: 100%;
    margin: 16px auto;
    padding: 28px 20px;
  }
}

/* ─────────────────────────────────────────────
   Employee dashboard (`/employee`) — Charte 2026 §4/§8/§12
   Two-column shell (main + advice rail), the gradient insurance member card,
   the family coverage list, the needs-attention list, the recent-claims list
   and the "Need a hand?" rail card. Every colour reads a consumed token with a
   pre-Charte base fallback, so a white-label that sets no --emp-dash-* token
   still renders a coherent (cool-grey) surface.

   SIZING IS PART OF THE CONTRACT HERE, not a default: the mockup's top row is a
   SHORT 212px band (a credit-card-shaped member card + an equally short family
   card), so the row height and the member card's 2:1 width cap are declared
   rather than left to content. Anything that could outgrow the band scrolls
   inside it (`.emp-dash-scroll`) instead of stretching it.
   ───────────────────────────────────────────── */

.emp-dash-grid {
  display: grid;
  /* ~75 / 25 split with a 24px gutter, matching the mockup's 925px main +
     305px rail. The rail is capped rather than fractional so it stays a rail. */
  grid-template-columns: minmax(0, 1fr) minmax(240px, 25%);
  gap: 24px;
  align-items: start;
}

.emp-dash-col {
  display: flex;
  flex-direction: column;
  /* Vertical rhythm between the major blocks (card row → stats → lists). */
  gap: 18px;
  min-width: 0;
}

/* ── Top card row: member card + family, equal height, 212px band ──
   The member card is width-capped (424px = 2 × 212) so it keeps its
   credit-card proportion; the family card takes whatever is left. */
.emp-dash-duo {
  display: flex;
  gap: 11px;
  align-items: stretch;
  height: var(--emp-dash-duo-height, 212px);
}

.emp-dash-duo > .emp-dash-member-card {
  flex: 0 1 auto;
  width: var(--emp-dash-member-width, 424px);
  max-width: 100%;
}

/* The family card in the duo: fills the band, never grows it. */
.emp-dash-duo-card {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Scrolls internally when a family has more rows than the band fits. */
.emp-dash-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* ── Insurance member card (§8 radius/elevation, gradient fill) ── */
.emp-dash-member-card {
  border-radius: var(--emp-dash-member-radius, 18px);
  padding: var(--emp-dash-member-padding, 24px);
  background-image: var(
    --emp-dash-member-gradient,
    linear-gradient(135deg, var(--brand-secondary, #4374f3) 0%, var(--brand-primary, #0a3373) 100%)
  );
  color: var(--emp-dash-member-color, #ffffff);
  box-shadow: var(--emp-dash-member-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.emp-dash-member-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
}

.emp-dash-member-wordmark {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
}

.emp-dash-member-insurer {
  font-size: 13px;
  font-weight: 400;
  text-align: right;
  opacity: 0.85;
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* The card's identifier (the member's insurance id), on its own line above the
   holder name. Deliberately QUIET — muted tint, regular weight, small: the name
   is the loud element and this sits under it in the visual hierarchy.
   Full card width, so a long id stays on one line; `break-word` (not `anywhere`)
   so a 20+ char id only ever breaks as a last resort, never opportunistically
   mid-token while the line still has room. */
.emp-dash-member-ident {
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  opacity: 0.72;
  margin: 0 0 2px;
  min-width: 0;
  overflow-wrap: break-word;
}

/* No enrollment at all — the card's only line below the name. */
.emp-dash-member-empty {
  font-size: 13px;
  font-weight: 400;
  opacity: 0.72;
  margin: 0 0 2px;
  overflow-wrap: anywhere;
}

.emp-dash-member-name {
  font-size: 20px;
  font-weight: 700;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin: 0;
  overflow-wrap: anywhere;
}

/* `margin-top: auto` pins the label row flush to the bottom — the space above
   it is leftover flex space, never a fixed tall gap. */
.emp-dash-member-fields {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
  gap: 4px 16px;
  margin-top: auto;
}

.emp-dash-member-field-label {
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0.7;
  margin: 0 0 2px;
}

.emp-dash-member-field-value {
  font-size: 13px;
  font-weight: 700;
  margin: 0;
  overflow-wrap: anywhere;
}

/* ── Compact card header (the duo's family card) ──
   Narrower than `.hr-dash-recent-head` and divider-less: the band is only
   212px tall, so the header has to give its height to the rows. */
.emp-dash-card-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 20px 8px;
}

.emp-dash-card-title {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--emp-dash-title-color, #18181b);
  margin: 0;
}

.emp-dash-card-sub {
  font-size: 13px;
  color: var(--emp-dash-muted-color, #9ca3af);
  margin: 3px 0 0;
}

/* ── Shared row shell for the family / attention / claims lists ── */
.emp-dash-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: var(--emp-dash-row-padding, 12px 20px);
  min-width: 0;
}

.emp-dash-row:not(:last-child) {
  border-bottom: 1px solid var(--emp-dash-row-border, #eaeff6);
}

.emp-dash-row-main {
  flex: 1;
  min-width: 0;
}

.emp-dash-row-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--emp-dash-title-color, #18181b);
  margin: 0;
  overflow-wrap: anywhere;
}

.emp-dash-row-sub {
  font-size: 12px;
  color: var(--emp-dash-muted-color, #9ca3af);
  margin: 2px 0 0;
  overflow-wrap: anywhere;
}

.emp-dash-row-amount {
  font-size: 13px;
  font-weight: 700;
  color: var(--emp-dash-title-color, #18181b);
  white-space: nowrap;
}

.emp-dash-row-aside {
  display: flex;
  align-items: center;
  gap: 12px;
  /* min-width:0 (not flex-shrink:0): the aside's children already carry
     `white-space: nowrap` + ellipsis, so the ROW MAIN must be the part that
     gives way at 375px — pinning the aside would push the row wider instead. */
  min-width: 0;
}

/* The family list's initials avatar is the shared `.avatar-circle` primitive
   (defined below, next to the members-rail block that also consumes it). */

/* Attention rows: a status dot sized like the timeline dot, top-aligned so it
   sits on the title line when the detail wraps. */
.emp-dash-attn-row {
  align-items: flex-start;
}

.emp-dash-attn-dot {
  width: 9px;
  height: 9px;
  border-radius: 999px;
  flex-shrink: 0;
  margin-top: 6px;
}

/* Compact §17 empty state — the shared `<ListState>` block is sized for a
   full-height table card; inside the 212px band it has to lose its tall
   padding and oversized icon square, otherwise the placeholder alone is
   taller than the whole row. */
.emp-dash-duo-card .tm-detail-empty {
  padding: 10px 20px 18px;
  gap: 8px;
}

.emp-dash-duo-card .tm-detail-empty-icon {
  width: 34px;
  height: 34px;
}

.emp-dash-duo-card .tm-detail-empty-title {
  font-size: 13px;
}

/* ── Stat tiles: 3 equal columns, 117px band, smaller type than the default
   dashboard tile (which is sized for a 36px metric). ── */
.emp-dash-col .dash-stat-card {
  flex: 1 1 0;
  min-width: 0;
  padding: var(--emp-dash-stat-padding, 18px 20px);
  min-height: var(--emp-dash-stat-height, 117px);
  justify-content: center;
}

.emp-dash-col .dash-stat-label { font-size: 9px; }
.emp-dash-col .dash-stat-value { font-size: 30px; margin: 6px 0 3px; }
.emp-dash-col .dash-stat-sub   { font-size: 13px; }

/* ── Rail card body (the WhatsApp assistant card) ── */
.emp-dash-help-body {
  padding: 4px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 14px;
  align-items: flex-start;
}

.emp-dash-help-text {
  font-size: 12.5px;
  color: var(--emp-dash-body-color, #4b5563);
  margin: 0;
}

/* Coloured stat sub-labels (<StatCard subTone>) — the metric's own qualifier
   ("2 in review", "Uploaded"), not a status badge. */
.dash-stat-sub--info    { color: var(--brand-secondary, #4374f3); }
.dash-stat-sub--warning { color: var(--dash-status-pending-color, #d97706); }
.dash-stat-sub--success { color: var(--dash-status-approved-color, #059669); }
.dash-stat-sub--danger  { color: var(--dash-status-rejected-color, #dc2626); }

/* ── Tablet: the rail drops under the main column ── */
@media (max-width: 1024px) {
  .emp-dash-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 18px;
  }
}

/* ── Mobile: the top row stacks. The member card keeps its 2:1 proportion via
   aspect-ratio (its width is now fluid), and the family card is released to
   its natural height so nothing scrolls inside a short band on a small screen. ── */
@media (max-width: 768px) {
  .emp-dash-duo {
    flex-direction: column;
    height: auto;
    gap: 14px;
  }

  .emp-dash-duo > .emp-dash-member-card {
    width: 100%;
    max-width: var(--emp-dash-member-width, 424px);
    aspect-ratio: 2 / 1;
  }

  .emp-dash-duo-card {
    height: auto;
    overflow: visible;
  }

  .emp-dash-scroll {
    overflow-y: visible;
  }

  /* A 0 flex-basis would keep all three tiles on one 375px line (~110px each,
     enough to clip a two-word label). Basis 140px lets `.dash-stats`'
     `flex-wrap` actually engage, so they reflow to 2 + 1. */
  .emp-dash-col .dash-stat-card {
    flex: 1 1 140px;
  }
}

/* ─────────────────────────────────────────────
   Shared avatar primitive — `.avatar-circle`
   A borderless filled circle with centred initials (Charte §8/§15: no outline,
   the tint carries it). Extracted from the employee dashboard's family rows so
   the census members rail renders the SAME avatar instead of a second copy —
   any surface listing people should use this, never a bespoke square.
   Geometry only; the tone comes from a modifier, so a caller can keep its own
   contextual tint (e.g. the rail's selected-employee flip to solid navy).
   ───────────────────────────────────────────── */
.avatar-circle {
  width: var(--avatar-circle-size, 32px);
  height: var(--avatar-circle-size, 32px);
  border-radius: 999px;
  border: none;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1;
}

.avatar-circle--brand {
  background-color: var(--avatar-circle-brand-bg, #eaf0fb);
  color: var(--avatar-circle-brand-color, var(--brand-primary, #0a3373));
}

.avatar-circle--muted {
  background-color: var(--avatar-circle-muted-bg, #eef2f7);
  color: var(--avatar-circle-muted-color, #4b5563);
}

/* ─────────────────────────────────────────────
   Census MEMBERS rail (`.mf-rail`) — Charte 2026 restyle
   Applies to every surface that renders the shared <MemberRail>: the
   Add-member wizard's dependants step, the HR member editor, and the insurer
   member-detail page — one rule set, so they cannot drift.

   WHY THIS IS A SCOPED OVERRIDE AND NOT AN EDIT OF THE `.mf-*` BASE RULES:
   the Contact Center's thread rows deliberately reuse `.mf-member-btn` /
   `.mf-avatar` / `.mf-member-name` / `.mf-member-sub` verbatim (see the
   Contact Center block above), and its rail is a 380px chat list whose row
   density is its own decision. Scoping to `.mf-rail` restyles the members rail
   on all three of its surfaces while leaving that feature untouched.

   Charte deltas vs the original: no panel border (a plain surface separates it
   from the white content), no outline on the selected row (soft blue tint
   only), no dashed box on the add action (borderless tinted ghost row), a
   borderless 32px circle avatar, and the dashboard's row metrics (60px rows,
   14px/600 name, 12px muted sub-line).
   ───────────────────────────────────────────── */
.mf-rail {
  padding: 14px 10px;
  gap: 2px;
  /* No border on any edge — the surface tint is the separation (§4). */
  border: none;
  background-color: var(--mf-rail-bg, #fbfcfe);
  border-radius: var(--mf-rail-radius, 14px);
}

/* Editor context: the rail is a flush full-height column, so it needs its own
   inset for the rounded surface to read as a panel against the white body. */
.mf-body > .mf-rail {
  margin: 12px 0 12px 12px;
}

/* Wizard context: kill the 1px card border the dependants step used to draw. */
.mfw-dep-split .mf-rail {
  border: none;
}

/* Section label — the app's standard small uppercase muted label
   (same treatment as `.dash-stat-label` / `.onb-rail-title`, §3). */
.mf-rail-title {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--dash-stat-label-color, #9ca3af);
  margin: 0 0 8px 10px;
}

/* Row: dashboard-family metrics — ~60px tall, 10px gutter, generous radius. */
.mf-rail .mf-member-btn {
  min-height: 60px;
  padding: 10px 12px;
  gap: 10px;
  border-radius: 12px;
}

.mf-rail .mf-member-btn:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #eef2f7);
}

/* Selected row: soft tint, NO outline (the old inset ring is gone). */
.mf-rail .mf-member-btn.is-active {
  background-color: var(--mf-rail-active-bg, #e5edfb);
  box-shadow: none;
}

.mf-rail .mf-member-name {
  font-size: 14px;
  font-weight: 600;
  color: var(--mf-rail-name-color, var(--brand-primary, #0a3373));
}

.mf-rail .mf-member-sub {
  font-size: 12px;
  color: var(--mf-rail-sub-color, #64748b);
  margin-top: 2px;
}

/* Avatar tones inside the rail (geometry comes from `.avatar-circle`).
   The selected employee keeps its solid-brand flip — still a borderless
   filled circle, just the strongest tone in the set. */
.mf-rail .mf-avatar--dep {
  background-color: var(--avatar-circle-muted-bg, #eef2f7);
  color: var(--avatar-circle-muted-color, #4b5563);
}

.mf-rail .mf-avatar--dep.is-active {
  background-color: var(--mf-rail-active-bg, #e5edfb);
  color: var(--avatar-circle-brand-color, var(--brand-primary, #0a3373));
}

/* Delete icon: muted and out of the way until the row is hovered or the button
   itself is focused. Hidden only where hover actually exists — on touch there
   is no hover, so it stays visible rather than becoming unreachable. */
@media (hover: hover) {
  .mf-rail .mf-member-remove {
    opacity: 0;
    transition: opacity .12s, background-color .12s, color .12s;
  }

  .mf-rail .mf-member:hover .mf-member-remove,
  .mf-rail .mf-member-remove:focus-visible {
    opacity: 1;
  }
}

.mf-rail .mf-member-remove {
  right: 8px;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  color: var(--mf-rail-sub-color, #9aa6bb);
}

/* "+ Add dependant": a borderless tinted ghost action row, left-aligned like
   the member rows above it — not a dashed placeholder box. */
.mf-rail .mf-add-dep {
  justify-content: flex-start;
  min-height: 44px;
  margin-top: 6px;
  padding: 10px 12px;
  border: none;
  border-radius: 12px;
  background-color: var(--mf-rail-add-bg, #f1f5fe);
  color: var(--mf-rail-add-color, var(--brand-secondary, #4374f3));
  font-size: 13px;
  font-weight: 600;
}

.mf-rail .mf-add-dep:hover:not(:disabled) {
  background-color: var(--mf-rail-add-bg-hover, #e5edfb);
}

.mf-rail .mf-add-dep:disabled {
  opacity: .45;
  color: var(--mf-rail-add-color, var(--brand-secondary, #4374f3));
}

.mf-rail .mf-hint {
  margin: 8px 10px 0;
}

@media (max-width: 768px) {
  .mf-body > .mf-rail {
    margin: 10px 12px;
  }

  .mf-rail {
    border-bottom: none;
    padding: 10px;
  }

  .mf-rail .mf-add-dep {
    margin-top: 0;
  }
}

/* ─────────────────────────────────────────────
   Census member editor — TAB CONTENT (`.mf-charte`) — Charte 2026 restyle
   The page chrome (topbar, members rail, tabs row) was retinted earlier; this
   block is the content inside the Details / Insurance / Documents tabs.

   NOTHING ABOUT THE FIELD IS IN THIS SCOPE ANY MORE (it used to hold the whole
   field restyle, and then — through Phase 1 — the field METRICS). Colours, focus,
   locked state, height, radius, value size, label size and the form-grid gaps all
   live on the base rules that read the field contract (`--field-*`, see "THE
   FIELD CONTRACT" far above), because "every input follows the same standard" is
   the system rule rather than a per-page correction.

   Phase 2 removed the last reason this scope had to touch a field at all: the
   `.mf-form` compact shim (38/8/13 at equal specificity) is gone, so there is no
   longer anything to out-rank. The four surfaces that rendered `.mf-form` WITHOUT
   `.mf-charte` — the insurer census member detail
   (`insurer/census/[memberId]/member-detail-view.tsx` + its loading skeleton
   `components/census/member-detail-loading-skeleton.tsx`), the employee chatbot
   setup (`employee/chatbot/page.tsx` + `components/chatbot/chatbot-loading-skeleton.tsx`)
   and the add-member wizard (`components/census/wizard/add-member-wizard.tsx`) —
   therefore now render the standard field, which is the point.

   What remains scoped is page-specific chrome only: card-body padding, the
   maquette card head (icon pad + title), and the policy card's fact rows.

   The scope is still set on BOTH surfaces the product compares field-for-field —
   the HR member editor (`member-form.tsx`) and the employee's own
   `/employee/my-information` — because they render the SAME
   `<MemberDetailsCards>`. Anything less and the two drift by CSS alone, which is
   exactly the delta that was reported.
   ───────────────────────────────────────────── */
.mf-charte {
  /* Card body padding, so the divider-to-content gap matches the head's 16px
     bottom inset instead of stacking 16 + 22. */
  --ins-pol-card-body-padding: var(--mf-card-body-padding, 22px 24px);
}

/* Card head: a text-only title. The maquette's 40px blue-tint pad left of it was
   removed by explicit product decision after review, and with it the scoped
   `.dash-stat-icon` resize and the `--mf-card-icon-*` / `--mf-card-head-gap`
   tokens (no mf-charte surface renders a pad in a card head any more). The base
   `.dash-stat-icon` rule is untouched — it is the app's generic 32px badge with
   ~20 callers elsewhere, none of them a form-card head.
   `.hr-dash-recent-title` still needs the scope: it is EVERY card title in the
   app and may not be resized globally for these two surfaces. */
.mf-charte .hr-dash-recent-head .hr-dash-recent-title {
  font-size: var(--mf-card-title-size, 17px);
  margin: 0;
}

/* Policy-card fact rows — one alignment line down the whole card.
   The label column was 168px, which is narrower than "Requested coverage start
   date" at this size: that one label wrapped to two lines, breaking both its own
   baseline and the card's vertical rhythm. It is wide enough for the longest
   label AND pinned to a single line, so every row is the same height. */
.mf-charte .mf-coverage-rows {
  gap: var(--mf-coverage-row-gap, 7px);
}

.mf-charte .mf-coverage-label {
  width: var(--mf-coverage-label-width, 168px);
  white-space: nowrap;
}

/* Values must WRAP, never clip (§21). */
@media (max-width: 768px) {
  .mf-charte .mf-coverage-row {
    flex-direction: column;
    gap: 2px;
  }

  .mf-charte .mf-coverage-label {
    width: auto;
    white-space: normal;
  }
}

/* ─────────────────────────────────────────────
   EMPLOYEE COVERAGE CARD (/employee/my-information/insurance)
   ─────────────────────────────────────────────
   The member's own coverage summary. It was built entirely from inline styles
   (raw hex, hardcoded sizes), so it could neither be retinted per white label
   nor corrected from CSS — converted here, class-for-value.

   `.emp-cov-id` is the insurer's own member id (census_policy.insurance_id) —
   THE number printed on the insurance card, so it reads as an identifier
   (§10: navy-700 via `--ins-pol-num-color`, tracked out), not as metadata. It
   renders only when the insurer has issued one, which is why the label lives
   inside the conditional row rather than as a permanent empty slot.

   Metadata tones reuse `--mf-coverage-label-color` (ink-400 on Heale) rather
   than minting a near-duplicate: this card sits inside `.mf-charte`, next to
   the fact rows that already use it. */
.emp-cov-summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.emp-cov-main {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.emp-cov-plan {
  margin: 0;
  font-size: 15px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

.emp-cov-id {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0;
}

.emp-cov-id-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--mf-coverage-label-color, #94a3b8);
}

.emp-cov-id-value {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--ins-pol-num-color, #0a3373);
  overflow-wrap: anywhere;
}

.emp-cov-dates,
.emp-cov-empty {
  margin: 0;
  font-size: 13px;
  color: var(--mf-coverage-label-color, #94a3b8);
  overflow-wrap: anywhere;
}

/* ── Insurer Pending Members workspace (`<PolicyMembersQueue>`, the policy
   Members tab). The table itself is the shared `.hr-dash-recent-*` chrome and
   the selection column reuses `.census-select-cell` / `.census-checkbox` — the
   HR census list's leading checkbox, which is structural, not census-specific.
   Only the toolbar in the filter row's right slot is new here. ── */
.pmq-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* The queue card's head: underline tabs leading on the LEFT, search beneath —
   the Task Manager list's shape. There is no title block; the tab labels name
   what the table holds and their counts say how many, so a heading above them
   only said it twice. */
.pmq-head {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0;
}

/* Horizontal inset matches `.hr-dash-recent-head`'s 24px, so the tabs, the
   search and the table below all sit on the same left edge. Without it the row
   ran flush to the card's border, which the removed title block had been
   providing padding for. */
.pmq-tabs-row {
  padding: 4px 24px 0;
}

/* Sits in the head's own padding rather than the table's, so the input lines up
   with the tab labels above it instead of the first column below. */
.pmq-search {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 24px;
  /* Separates the head from the table, the job the title block's own head
     border used to do. */
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  flex-wrap: wrap;
}

/* Rejection is destructive and permanent (census_policy is append-only, so a
   rejected enrollment is frozen and a retry is a new row) — rust text, never a
   filled button competing with Activate. */
/* Per-member reject sits in the row's top-right, opposite the name: the
   decision belongs to that member, not to the sheet. Rust text rather than a
   filled button — it must not compete with Confirm. */
.bam-row-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.bam-row-ident {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

/* How this member was matched, when it is worth a second look. METADATA ink,
   never a warning tone: since the matcher prefills every unambiguous match,
   this rides on rows that are about to be activated — a name-only agreement or
   a value read from prose is provenance, not a complaint. */
.bam-row-note {
  margin: 0;
  font-size: 11px;
  color: var(--ins-pol-subtext-color, #8d8579);
  overflow-wrap: anywhere;
}

/* A rejection SUCCEEDED, so it is not a failure — but it is not a green tick
   either. Its own tone keeps the three outcomes (activated / rejected / needs
   attention) legible at a glance in one result list. */
.bam-result--rejected .bam-result-icon {
  color: var(--dash-status-rejected-fg, #b45309);
}

/* A rejected row keeps its place in the list but stops looking like an entry
   waiting to be filled in. */
.bam-row--rejected {
  background-color: var(--dash-status-rejected-bg, rgba(180, 83, 9, 0.06));
}

.bam-reject {
  background: none;
  border: none;
  padding: 0 8px;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
  color: var(--dash-status-rejected-fg, #b45309);
}

.bam-reject:disabled {
  opacity: 0.5;
  cursor: default;
}

/* Sits INSIDE `.bam-row`, which already provides the padding — it briefly
   carried modal-level insets from when it lived above the footer, which pushed
   the reason field right of the activation inputs it replaces. It must occupy
   the same box as those inputs, so swapping a row between the two decisions
   moves nothing sideways. */
.bam-reject-reveal {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* The floating bulk bar — pinned above the viewport's bottom edge so it stays
   reachable however far down a 54-row list the user has scrolled, which is the
   whole reason it floats rather than sitting in the card. Centred over the
   content column, not the window, so the sidebar does not push it off-centre. */
.pmq-bulkbar {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  border-radius: 14px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  box-shadow: 0 8px 28px rgba(15, 23, 42, 0.16);
}

.pmq-bulkbar-count {
  font-size: 13px;
  font-weight: 600;
  color: var(--hr-dash-recent-title-color, #18181b);
}

.pmq-bulkbar-sep {
  width: 1px;
  height: 22px;
  background-color: var(--hr-dash-recent-head-border, #e5e7eb);
}

.pmq-bulkbar-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  height: 34px;
  padding: 0 14px;
  border-radius: 8px;
  border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5);
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  color: var(--ins-pol-num-color, #0a3373);
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
}

.pmq-bulkbar-btn--primary {
  border-color: transparent;
  background-color: var(--modal-btn-primary-bg, #0a3373);
  color: var(--modal-btn-primary-fg, #ffffff);
  font-weight: 600;
}

@media (max-width: 768px) {
  .pmq-bulkbar {
    left: 12px;
    right: 12px;
    transform: none;
    flex-wrap: wrap;
    justify-content: center;
  }
}

/* A row whose click toggles selection reads as clickable; a row that cannot be
   activated deliberately does not. */
.pmq-row--clickable {
  cursor: pointer;
}

/* Quiet by design — see the note at its call site. */
.pmq-aside {
  margin: 0;
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.pmq-selected-count {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ins-pol-num-color, #0a3373);
}

.pmq-clear {
  background: none;
  border: none;
  padding: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #9ca3af);
  cursor: pointer;
}

.pmq-clear:hover {
  color: var(--brand-primary, #0a3373);
}

/* Member cell: bold name over its Employee/Dependant caption. */
.pmq-member {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

@media (max-width: 768px) {
  .pmq-toolbar {
    width: 100%;
  }

  .pmq-toolbar .hr-dash-recent-btn-primary {
    flex: 1;
    min-height: 44px;
  }
}

/* The "Pending Activation" stat card's sub-line becomes a deep link into the
   queue when there is work. It inherits `.dash-stat-sub`'s size on purpose —
   `.hr-dash-recent-view-all` is a 14px card-header affordance and reads as an
   oversized second value here. */
.pmq-stat-link {
  color: var(--brand-secondary, #4374f3);
  font-weight: 600;
  text-decoration: none;
}

.pmq-stat-link:hover {
  text-decoration: underline;
}

/* ── Bulk activation modal (`<ActivateMembersModal>`) ───────────────────────
   The chrome is the shared `.modal-*` family; the per-member form row is the
   shared `<ActivationFields>` (`.tm-review-field*`, which are field classes,
   not task-panel ones). Only the list, the progress bar and the result rows
   are new. Charte §8/§9/§12 tokens throughout — no colour is named here. ── */

/* `.modal-card` is natural-height and lets the OVERLAY scroll, which is right
   for a 4-field dialog and wrong for one listing 50 members: the card would run
   several viewports tall and the footer's confirm would sit off-screen. This
   variant caps the card at the viewport so `.modal-body--scroll` scrolls
   INSIDE it and head + foot stay pinned. */
.modal-card--tall {
  max-height: calc(100vh - 40px);
  overflow: hidden;
}

/* `.modal-body` is `flex-shrink: 0` (it must never squash a short form).
   Inside a capped card that has to be reversed, and `min-height: 0` is
   load-bearing — a flex item's default `min-height: auto` refuses to shrink
   below its content and the scroll never engages. */
.modal-body--scroll {
  flex: 1;
  min-height: 0;
  flex-shrink: 1;
  overflow-y: auto;
}

.bam-note {
  margin: 0;
  font-size: 12.5px;
  color: var(--tu-muted-color, #6b7280);
}

.bam-rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* One member = one inner cell (§8: cream, radius 12, no shadow), so 50 of them
   read as a list rather than a wall of inputs. */
.bam-row {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafc);
}

.bam-row-name {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

/* Insurance ID takes the wider column — it is the value being typed; the two
   dates arrive prefilled and are usually only read. Collapses to one column on
   mobile, where three native date/text controls cannot share a line. */
.act-fields-row {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: 10px;
}

@media (max-width: 768px) {
  .act-fields-row {
    grid-template-columns: 1fr;
  }
}

.bam-progress {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.bam-progress-track {
  height: 6px;
  border-radius: 999px;
  background-color: var(--ui-shimmer-bg, #eef2f7);
  overflow: hidden;
}

.bam-progress-fill {
  height: 100%;
  border-radius: 999px;
  background-color: var(--brand-secondary, #4374f3);
  transition: width 0.2s ease;
}

.bam-progress-label {
  margin: 0;
  font-size: 11.5px;
  color: var(--tu-muted-color, #6b7280);
}

.bam-foot-note {
  margin: 0 auto 0 0;
  font-size: 12px;
  color: var(--tu-muted-color, #6b7280);
}

/* Batch-level rejection: nothing in that batch was written. §2 tinted surface
   on the destructive token. */
.bam-request-error {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--dash-status-rejected-bg, rgba(220, 38, 38, 0.12));
  color: var(--dash-status-rejected-color, #b42318);
  font-size: 12.5px;
  font-weight: 600;
}

.bam-results {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Success is the quiet state and failure the loud one — the reader is scanning
   for what still needs doing, not counting what worked. */
.bam-result {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafc);
}

.bam-result-icon {
  display: flex;
  flex-shrink: 0;
  padding-top: 1px;
  color: var(--dash-status-approved-color, #3a8a5f);
}

.bam-result--failed {
  background-color: color-mix(in srgb, var(--dash-status-rejected-color, #b42318) 8%, transparent);
}

.bam-result--failed .bam-result-icon {
  color: var(--dash-status-rejected-color, #b42318);
}

.bam-result-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.bam-result-name {
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

/* The server's own copy, rendered verbatim — never clipped. */
.bam-result-message {
  margin: 0;
  font-size: 12px;
  color: var(--tu-muted-color, #6b7280);
  overflow-wrap: anywhere;
}

@media (max-width: 768px) {
  /* Full-bleed card, 44px tap targets, and the footer pair shares the width so
     "Activate 120 members" never dictates the button size (same treatment as
     `.dialog-card`'s footer). The gate note wraps onto its own line above them
     rather than squeezing them out. */
  .modal-card--tall {
    max-width: 100%;
    max-height: calc(100vh - 24px);
  }

  .modal-card--tall .modal-foot {
    flex-wrap: wrap;
    gap: 10px;
  }

  .bam-foot-note {
    width: 100%;
    margin: 0;
  }

  .modal-card--tall .modal-btn-text,
  .modal-card--tall .modal-btn-primary {
    flex: 1;
    min-height: 44px;
    justify-content: center;
    text-align: center;
  }
}

/* ── Activation ingest — the COMPOSER above the insurer's pending-activation
   queue, plus the reconciliation block that rides inside
   `<ActivateMembersModal>`.

   One field, chat-composer shaped: type or paste into it, attach or drop files
   onto it, submit once. It replaced a drop zone + a separate textarea, which
   were two inputs for one intent and made the user choose up front which kind
   of artefact they had. ── */
.aig-card {
  gap: 0;
}

/* ONE surface, and it is a CARD (§8): white fill, card radius, the standard soft
   shadow, no border of its own. It used to take the §9 field tokens instead —
   cream on a cream page — so the only thing separating the composer from the
   background was a hairline, and it read as page furniture rather than as
   something to type into. The field tone now belongs to the textarea inside
   (`.aig-composer-input`), which is what the §9 rule is actually about, and the
   card carries the surface. `.aig-card` below still gives up its own chrome, so
   there is still exactly one edge. */
.aig-composer {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5);
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  box-shadow: var(--hr-dash-recent-card-shadow, none);
  transition: border-color .12s ease, box-shadow .12s ease;
}

/* The wrapper keeps its layout role and gives up its surface, so the composer
   inside it is not framed twice. */
.aig-card {
  background-color: transparent;
  border: none;
  box-shadow: none;
  border-radius: 0;
}

/* The second route, named rather than implied. */
.aig-alt {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin: 18px 0 14px;
}

.aig-or {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
}

.aig-or-line {
  flex: 1;
  height: 1px;
  background-color: var(--hr-dash-recent-head-border, #eaeff6);
}

.aig-or-word {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.aig-alt-text {
  margin: 0;
  font-size: 13px;
  text-align: center;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

/* Dragging a file anywhere over the composer lights the whole box, not just a
   sub-target — the drop area IS the field.
   Border + fill only, deliberately NO box-shadow: `--field-focus-ring` belongs
   to the focus state, and US-FLD.10.5 keeps the hand-drawn-ring allow-list
   empty. Borrowing the ring here would both re-open that list and make a drag
   look identical to a focus. */
   The card warms to the field tone while a file is over it, so the whole
   surface — not just its edge — answers the drag. */
.aig-composer--drag {
  border-color: var(--field-focus-border, #4374f3);
  background-color: var(--field-bg, #faf6f0);
}

/* §9: label above the field, uppercase ink-400. */
.aig-composer-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--field-label-color, #8d8579);
}

/* The §9 field lives HERE, inside the card: cream fill, sand hairline, field
   radius — so the typing area is the thing that reads as an input, and the card
   around it is the panel. */
.aig-composer-input {
  width: 100%;
  min-height: 64px;
  resize: vertical;
  border: 1px solid var(--field-hairline, #e9e4dc);
  border-radius: var(--field-radius, 10px);
  background-color: var(--field-bg, #faf6f0);
  padding: var(--field-padding-y, 12px) var(--field-padding-x, 14px);
  font: inherit;
  font-size: var(--field-font-size, 14px);
  color: var(--field-color, #1c1a17);
  transition: border-color .12s ease;
}

.aig-composer-input::placeholder {
  color: var(--field-placeholder-color, #8d8579);
}

.aig-composer-input:focus {
  outline: none;
  border-color: var(--field-focus-border, #4374f3);
}

.aig-composer-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.aig-composer-hint {
  flex: 1;
  min-width: 0;
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
  overflow-wrap: anywhere;
}

/* The attach control — a real button, so the hidden file input never has to be
   reached by keyboard. */
.aig-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 10px;
  border: 1px solid var(--field-hairline, #e9e4dc);
  background: none;
  cursor: pointer;
  color: var(--brand-primary, #0a3373);
}

.aig-icon-btn:hover:not(:disabled) {
  background-color: var(--ins-pol-filter-bg-hover, #f2ede5);
}

.aig-icon-btn:disabled {
  opacity: .45;
  cursor: default;
}

.aig-file-input {
  display: none;
}

/* Attachments are chips under the field — visible, individually removable, and
   never a silent list the user cannot correct before submitting. */
.aig-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.aig-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 100%;
  padding: 4px 6px 4px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  /* The same sand-200 tag surface the Task Manager's tags use (§1). */
  background-color: var(--task-tag-bg, #f2ede5);
  color: var(--hr-dash-recent-td-color, #4a4640);
}

.aig-chip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 220px;
}

.aig-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border: none;
  border-radius: 999px;
  background: none;
  cursor: pointer;
  color: inherit;
}

.aig-chip-remove:disabled {
  opacity: .45;
  cursor: default;
}

/* 60 cards is 60 model calls, so the wait is real and must be visible. The bar
   advances per CHUNK — the only boundary the client can observe. */
.aig-progress {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 20px 16px;
}

.aig-progress-track {
  height: 6px;
  border-radius: 999px;
  overflow: hidden;
  background-color: var(--ui-shimmer-bg, #eee8df);
}

.aig-progress-fill {
  height: 100%;
  border-radius: 999px;
  background-color: var(--brand-secondary, #4374f3);
  transition: width .2s ease;
}

.aig-progress-label {
  margin: 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The reconciliation block, INSIDE the modal above the member rows. Its own
   bottom rule separates "what this upload did" from "what you are confirming". */
.aig-recon {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eee8df);
}

/* Compact inside the modal: every pixel above the rows pushes Confirm down, and
   the rows are what the insurer came to act on. */
.aig-summary--compact .aig-counters {
  grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
  gap: 6px;
}

.aig-summary--compact .aig-counter {
  padding: 7px 10px;
}

.aig-summary--compact .aig-counter-value {
  font-size: 18px;
}

/* CLOSED by default: opened, the three lanes can run to dozens of entries and
   would bury the sheet. The summary still carries the count, so a collapsed
   section can never hide the fact that entries went nowhere. */
.aig-disclosure {
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
}

.aig-disclosure-summary {
  padding: 9px 12px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  color: var(--brand-primary, #0a3373);
}

.aig-disclosure-body {
  padding: 4px 12px 12px;
}

.aig-summary {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* auto-fill + minmax so six tiles collapse to two on a phone rather than
   overflowing (mobile rule: never a fixed column count). */
.aig-counters {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 10px;
}

.aig-counter {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
}

.aig-counter-value {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.aig-counter-label {
  font-size: 11px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* §2 tinted surfaces: the token at 100% for the figure, nothing else recoloured
   — the label stays metadata ink so the tile reads as one number, not a badge. */
.aig-counter--good .aig-counter-value { color: var(--dash-status-approved-color, #3a8a5f); }
.aig-counter--warn .aig-counter-value { color: var(--dash-status-pending-color, #e3b341); }
.aig-counter--bad  .aig-counter-value { color: var(--dash-status-rejected-color, #c2542e); }

.aig-summary-note {
  margin: 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* A file the endpoint would not read is the worst outcome of a drop — it is
   never a quiet aside. */
.aig-notes {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0;
  padding: 10px 12px;
  list-style: none;
  border-radius: 12px;
  background-color: var(--dash-status-pending-bg, rgba(227, 179, 65, 0.16));
}

.aig-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 12px;
  overflow-wrap: anywhere;
}

.aig-note--warn {
  color: var(--dash-status-rejected-color, #c2542e);
}

.aig-lanes {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

.aig-lane {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.aig-lane-head {
  display: flex;
  align-items: center;
  gap: 8px;
}

.aig-lane-icon {
  display: inline-flex;
  align-items: center;
}

.aig-lane-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.aig-lane-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  background-color: var(--modal-total-bg, #faf6f0);
  color: var(--ins-pol-subtext-color, #8d8579);
}

.aig-lane-text {
  margin: 0 0 4px;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.aig-lane--good .aig-lane-icon { color: var(--dash-status-approved-color, #3a8a5f); }
.aig-lane--warn .aig-lane-icon { color: var(--dash-status-pending-color, #e3b341); }
.aig-lane--bad  .aig-lane-icon { color: var(--dash-status-rejected-color, #c2542e); }

.aig-lane-rows {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* The lane's tone is carried by a left rule, the same device `.tm-tr` uses for
   "this row needs something from you" — so the four lanes stay distinguishable
   when the sections scroll apart. */
.aig-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
  flex-wrap: wrap;
}

/* The lane's colour rule is an INSET SHADOW, not a border. Charte's no-border
   rule (US-MEC.5.4) is checked on the `border` shorthand, and a 3px left border
   is exactly the kind of hairline it exists to keep off these surfaces — an
   inset shadow paints the same 3px stripe without claiming to be an edge. */

.aig-row--review      { box-shadow: inset 3px 0 0 var(--dash-status-pending-color, #e3b341); }
.aig-row--discrepancy { box-shadow: inset 3px 0 0 var(--brand-accent, #fb7a5c); }
.aig-row--unmatched   { box-shadow: inset 3px 0 0 var(--dash-status-rejected-color, #c2542e); }

.aig-row-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.aig-row-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-td-color, #1c1a17);
  overflow-wrap: anywhere;
}

.aig-row-sub {
  margin: 0;
  font-size: 12.5px;
  color: var(--hr-dash-recent-td-color, #4a4640);
  overflow-wrap: anywhere;
}

/* Why this row is not simply prefilled — the sentence the reader acts on. */
.aig-row-flag {
  margin: 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--dash-status-rejected-color, #c2542e);
  overflow-wrap: anywhere;
}

.aig-row-meta {
  margin: 0;
  font-size: 11px;
  color: var(--ins-pol-subtext-color, #8d8579);
  overflow-wrap: anywhere;
}

@media (max-width: 768px) {
  /* Nothing side by side at 375px: the action drops under the text rather than
     squeezing the member's name into two characters. */
  .aig-row {
    flex-direction: column;
    align-items: stretch;
  }

  /* The composer's send button owns the row at 375px rather than being
     squeezed next to the hint. */
  .aig-composer-bar .hr-dash-recent-btn-primary {
    width: 100%;
    min-height: 44px;
    justify-content: center;
    order: 3;
  }

  .aig-composer-hint {
    order: 2;
    flex-basis: 100%;
  }

  .aig-composer {
    margin-left: 14px;
    margin-right: 14px;
  }

  .aig-chip-name {
    max-width: 160px;
  }
}

/* =====================================================================
   §5/§8/§9/§17 — CENSUS BULK IMPORT (`<ImportWizard>`, `/hr/census/import`)
   The chrome is borrowed wholesale: the shell is the add-member wizard's
   (`.mf-root mfw-root`, `.mfw-steps`, `.mfw-footer`, `.wizard-actions`),
   the cards are `.hr-dash-recent-card`, the per-member cell is `.bam-row`,
   every field is `.tm-review-field*`/`.tm-review-input`, the result list is
   `.bam-results`, the filters are `.ins-pol-seg` pills and the counters are
   `.dash-stat-*`. Only what genuinely did not exist is declared here: the
   step stack, the mapping rows, the family grouping/indent, the row chips
   and the indeterminate progress fill. No colour is named — consumed tokens
   with the historical base fallback throughout.
   ===================================================================== */

/* Every step is one vertical stack of cards at the page gutter (§4's 18–22px). */
.cim-step {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* CAPPED BATCH — rendered on every step after Source for the life of the batch
   (see `truncationNotice`). §2 tinted surface on the pending/waiting token: it is
   not an error (the import is valid, and proceeding is the right move) but it must
   not be missable either, because the people it names will not exist afterwards. */
.cim-notice {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #b45309) 14%, transparent);
  color: var(--dash-status-pending-color, #b45309);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.45;
}

/* The icon keeps its line while the sentence wraps beside it (§18: icons inherit
   the context colour). */
.cim-notice svg {
  flex-shrink: 0;
  margin-top: 1px;
}

.cim-read-note {
  margin: 0;
  font-size: 12.5px;
  color: var(--tu-muted-color, #6b7280);
}

/* ── Step 2: one row per detected header ───────────────────────────────────── */

.cim-map-rows {
  display: flex;
  flex-direction: column;
}

/* Divider BETWEEN rows only (§10's last-row rule) — the card's own edge closes
   the list. */
.cim-map-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  align-items: center;
  gap: 14px;
  padding: 13px 24px;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #f1f5f9);
}

.cim-map-row:last-child {
  border-bottom: none;
}

/* An unanswered column is the ONE thing blocking Continue (D2), so it carries
   §12's 3px left rule in the destructive tone.
   AS AN INSET SHADOW, NOT A BORDER: §8's "no border" rule is absolute inside the
   member-editor guard's scope (`member-tabs-charte-2026.test.ts` US-MEC.5.4), and
   a shadow also costs no layout — a border on one state of a row shifts its
   content by 3px the moment the state changes. Same technique as
   `.cim-trow--unlinked`, which carries the identical semantic in the table. */
.cim-map-row--unmapped {
  box-shadow: inset 3px 0 0 0 var(--dash-status-rejected-color, #b42318);
}

.cim-map-header {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

/* The file's own words, wrapped rather than clipped — a header can be a sentence. */
.cim-map-name {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

.cim-map-control {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
}

.cim-map-dateorder {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.cim-map-dateorder-label {
  font-size: var(--field-label-size, 11px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--field-label-color, #9ca3af);
}

/* Spans both columns so the explanation sits under the pair it explains. */
.cim-map-issue {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--dash-status-rejected-color, #b42318);
}

/* The target-side question (`ACKNOWLEDGEABLE_TARGETS`): a field the file does not
   carry whose absence has to be accepted on purpose. An inner cell (§8 — cream,
   radius 12, no shadow) below the column list, so it reads as part of the same
   decision rather than as a warning about it. */
.cim-ack {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0 24px 18px;
  padding: 12px 14px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafd);
}

/* The consequence of ticking it, in metadata tone — indented to the label's text
   so it reads as that sentence's second line, not as a new statement. */
.cim-ack-note {
  margin: 4px 0 0 24px;
  font-size: 12px;
  color: var(--tu-muted-color, #6b7280);
  line-height: 1.45;
}

@media (max-width: 768px) {
  .cim-ack {
    margin: 0 14px 14px;
  }
}

/* ── Step 3: the review TABLE ──────────────────────────────────────────────
   Chrome is `<DataTable>`'s `.hr-dash-recent-table*` (fill-less header per the
   Deviations note — the old importer's sticky thead used the retired
   table-header hex). Only DENSITY, the in-cell editor, the dependant indent and
   the expanded band are new: at 12 columns × N members the shared 13–15px row
   padding wastes the horizontal budget the screen exists to buy. */
.cim-table .hr-dash-recent-td,
.cim-table .hr-dash-recent-th {
  padding: 7px 10px;
  white-space: nowrap;
}

/* THE ROW NEVER RESIZES WHEN AN EDITOR OPENS.
   Every body cell is exactly one control tall — `--cim-cell-h`, the height of the
   TALLEST thing a cell can hold (the input / the combobox trigger), not the height
   of its text. A cell whose resting state is a line of text and whose editing state
   is an input would grow by the difference the moment it is clicked, and the table
   would jump under the cursor mid-edit. The row height is derived from the same
   token plus this table's own vertical padding, so the two cannot drift apart.

   `height` on a `<td>` behaves as a MINIMUM in table layout, which is exactly
   right: the geometry is pinned for every control, and the one cell that wraps
   (the Action column's issue sentence) can still grow rather than clip text — and
   it has no editing state, so growing there is never triggered by a click. */
.cim-table {
  --cim-cell-h: 30px;
}

.cim-table tbody .hr-dash-recent-td {
  height: calc(var(--cim-cell-h) + 14px);
  vertical-align: middle;
}

/* The table is wider than the page on purpose (12 columns); horizontal scroll
   belongs to `.hr-dash-recent-table-wrap`, which already owns `overflow-x` — no
   `min-width` on the table, which is what breaks mobile scroll. */

/* A value cell is a BUTTON until clicked (see the component header): it must read
   as text, not as a control, or twelve columns of buttons read as a toolbar. */
/* IDENTICAL BOX GEOMETRY, three states of one cell: the resting button, the open
   input, and the live combobox trigger. Same height, same padding, same 1px
   border box — the button's border is TRANSPARENT rather than absent, so the
   input's visible border appears in place instead of adding a pixel on each
   edge. `box-sizing` is explicit here because this geometry is the contract, not
   an inherited convenience. */
.cim-cell-btn,
.cim-cell-input,
.cim-cell-select {
  box-sizing: border-box;
  height: var(--cim-cell-h, 30px);
  border-width: 1px;
  border-style: solid;
}

.cim-cell-btn {
  display: flex;
  align-items: center;
  width: 100%;
  max-width: 260px;
  padding: 0 5px;
  border-color: transparent;
  border-radius: 6px;
  background: none;
  cursor: text;
  text-align: left;
  font: inherit;
  color: var(--hr-dash-recent-td-color, #374151);
  overflow: hidden;
}

/* The value itself does the ellipsing — the flex parent owns the geometry. */
.cim-cell-btn > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cim-cell-btn:hover:not(:disabled) {
  border-color: var(--field-hairline, #e5e7eb);
  background-color: var(--hr-dash-recent-row-hover-bg, #f9fafb);
}

.cim-cell-btn:disabled {
  cursor: default;
}

/* The field the server objected to — §2's destructive token, and the only red on
   the row (no whole-row tint: the row is a form to fix, not a failure). */
.cim-cell-btn--error {
  color: var(--dash-status-rejected-color, #b42318);
  font-weight: 700;
  border-color: color-mix(in srgb, var(--dash-status-rejected-color, #b42318) 35%, transparent);
  background-color: color-mix(in srgb, var(--dash-status-rejected-color, #b42318) 8%, transparent);
}

.cim-cell-empty {
  color: var(--field-placeholder-color, #9ca3af);
}

/* Same box as the button it replaces (see the shared geometry rule above), so
   opening and closing an editor changes nothing about layout. */
.cim-cell-input {
  width: 100%;
  max-width: 260px;
  padding: 0 5px;
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  border-radius: 6px;
  background-color: var(--dash-topbar-bg, #ffffff);
  font: inherit;
  color: var(--field-color, #18181b);
  outline: none;
}

/* Compact combobox trigger for a table cell — the field contract's control at
   table density, on the SAME height token as the text cells so the row height is
   one value across every column. */
.cim-cell-select {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  min-width: 118px;
  max-width: 200px;
  padding: 0 8px;
  border-color: var(--field-hairline, #e5e7eb);
  border-radius: 8px;
  background-color: var(--dash-topbar-bg, #ffffff);
  font-size: 12.5px;
  color: var(--field-color, #18181b);
  cursor: pointer;
}

.cim-cell-select:disabled {
  background-color: var(--field-disabled-bg, #f3f4f6);
  color: var(--field-placeholder-color, #9ca3af);
  cursor: not-allowed;
}

.cim-tick {
  width: 15px;
  height: 15px;
  accent-color: var(--brand-secondary, #4374f3);
}

.cim-td-tight {
  width: 1%;
}

.cim-td-num {
  width: 1%;
  color: var(--dash-stat-label-color, #9ca3af);
  font-variant-numeric: tabular-nums;
}

/* A dependant belongs to the employee above it, and the indent is the only thing
   that says so in a flat table. The rule is the divider tone: nesting is
   structure, not a status. */
/* Indent only: horizontal padding and a left rule, never vertical padding — a
   dependant row must measure exactly what an employee row measures. The rule is an
   inset shadow for the same two reasons as `.cim-map-row--unmapped` above (§8, and
   zero layout cost). */
.cim-nested {
  display: flex;
  align-items: center;
  gap: 8px;
  height: var(--cim-cell-h, 30px);
  padding: 0 0 0 14px;
  box-shadow: inset 2px 0 0 0 var(--hr-dash-recent-head-border, #e5e7eb);
}

/* Excluded from the batch — dimmed, never hidden: a row that vanishes when
   unticked leaves the reader unable to tick it back. */
.cim-trow--skipped .hr-dash-recent-td {
  opacity: 0.5;
}

/* Blocks the confirm (D4) — a coral left rule on the row, §12's action-needed
   treatment, and no tint. */
.cim-trow--unlinked .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 0 var(--brand-accent, #fb7a5c);
}

.cim-cell-issue {
  font-size: 11.5px;
  color: var(--tu-muted-color, #6b7280);
  white-space: normal;
  overflow-wrap: anywhere;
}

.cim-cell-issue--attention {
  color: var(--dash-status-rejected-color, #b42318);
  font-weight: 600;
}

.cim-action-segs .ins-pol-seg {
  height: var(--cim-cell-h, 30px);
  padding: 0 10px;
  font-size: 11.5px;
}

.cim-row-tools {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* Chevron / remove: icon affordances at row density, tinted by context (§18 —
   icons inherit their colour). */
.cim-expand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border: none;
  border-radius: 8px;
  background: none;
  cursor: pointer;
  color: var(--dash-stat-label-color, #9ca3af);
}

/* The hover outline is an inset shadow rather than a border: it paints inside the
   26px box, so the icon does not shift by a pixel on hover, and §8's no-border
   rule holds without a transparent placeholder border. */
.cim-expand:hover:not(:disabled) {
  box-shadow: inset 0 0 0 1px var(--field-hairline, #e5e7eb);
  background-color: var(--ins-pol-filter-bg-hover, #f3f4f6);
  color: var(--brand-primary, #0a3373);
}

.cim-expand:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The expanded band under one row (`<DataTable renderDetailRow>`): an inner
   metadata surface (§8 — cream, radius 12, no shadow), not another card. */
/* EXEMPT from the fixed row height: the expanded band is a separate `<tr>` and
   may be as tall as its fields need. */
.cim-table tbody .cim-detail-cell {
  height: auto;
  padding: 0 10px 12px;
  background-color: var(--modal-total-bg, #f8fafd);
}

.cim-detail-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
  padding: 12px 14px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--dash-topbar-bg, #ffffff);
  white-space: normal;
}

/* ── Chips (§2 tinted surfaces: token at 14%, same token at 100%) ─────────── */

.cim-chip {
  flex-shrink: 0;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* Neutral tag surface (§1 sand-200 on Heale) — same token the task tags read. */
  background-color: var(--task-tag-bg, #f1f5f9);
  color: var(--dash-stat-label-color, #6b7280);
}

.cim-chip--ready {
  background-color: color-mix(in srgb, var(--dash-status-approved-color, #3a8a5f) 14%, transparent);
  color: var(--dash-status-approved-color, #3a8a5f);
}

.cim-chip--attention {
  background-color: color-mix(in srgb, var(--dash-status-rejected-color, #b42318) 14%, transparent);
  color: var(--dash-status-rejected-color, #b42318);
}

.cim-chip--duplicate,
.cim-chip--guessed {
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 14%, transparent);
  color: var(--brand-secondary, #4374f3);
}

/* The confirm's progress bar is `.bam-progress*` unmodified: the batch is sent as
   N capped chunks, so the fill is DETERMINATE and its width comes from the count
   of committed chunks. The indeterminate sweep this block used to hold went with
   the single-request confirm that could not observe its own progress. */

@media (max-width: 768px) {
  /* The header and its picker stack: a 375px row cannot hold a column name and a
     combobox side by side without clipping one of them. */
  .cim-map-row {
    grid-template-columns: 1fr;
    gap: 10px;
  }

  /* The expanded band's fields stack rather than sharing a 375px line. */
  .cim-detail-grid {
    grid-template-columns: 1fr;
  }

  /* The nesting rule survives, the indent shrinks — the table already scrolls
     horizontally here and every extra pixel of indent is a pixel of name lost. */
  .cim-nested {
    padding-left: 8px;
  }

  /* The meta line owns the full width so the invite tick is not squeezed against
     the action pair. */
  .cim-footer-info {
    width: 100%;
    gap: 8px;
  }

  /* 44px tap targets. The table's own controls stay at row density on purpose —
     it is a horizontally scrolling grid, not a stack of tap targets — but the
     mapping step's toggles are page-level controls and must be thumb-sized. */
  .cim-map-dateorder .ins-pol-seg {
    min-height: 44px;
  }
}

/* ─────────────────────────────────────────────────────────────────────────
   Insurer Assistant (`/insurer/assistant`) — `.asst-*`

   A chat surface built from the existing page chrome: `<DashTopbar>` above,
   one `.hr-dash-recent-card` filling the body, transcript scrolling inside it
   and the composer pinned at its foot. Same "the scrollable child owns
   min-height: 0" contract as `.dash-body--kanban` (NOT `.dash-body--tm` — the
   Task Manager list deliberately scrolls with the page instead).

   Charte notes: the card has no border (§8 — the shadow carries it), the
   action card inside it is a cream-50 inner cell with no shadow of its own
   (§8), Confirm is a blue-400 primary and Dismiss the navy outline (§6 —
   green stays reserved for approvals, and a suggested comment is not one).
   ───────────────────────────────────────────────────────────────────────── */

.dash-body.dash-body--assistant {
  overflow-y: hidden;
}

.asst-shell {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.asst-transcript {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  scrollbar-gutter: stable;
  display: flex;
  flex-direction: column;
  gap: 18px;
  padding: 24px 26px;
}

/* ── Messages ── */

.asst-msg {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 78%;
}

.asst-msg--user {
  align-self: flex-end;
  align-items: flex-end;
  max-width: 68%;
  padding: 11px 16px;
  border-radius: var(--asst-user-bubble-radius, 16px);
  background-color: var(--asst-user-bubble-bg, #e8effc);
}

.asst-msg--assistant {
  align-self: flex-start;
}

.asst-msg-text {
  margin: 0;
  font-size: var(--asst-text-size, 13px);
  line-height: 1.65;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--asst-text-color, #1f2937);
}

.asst-msg--user .asst-msg-text {
  color: var(--asst-user-bubble-color, var(--brand-primary, #0a3373));
}

/* Rehydrated `[[handle]]` → inline reference, for ALL FOUR namespaces: `[[T1]]`
   opens a task panel, `[[X1]]` opens a saved export's review card, `[[M1]]` opens
   the member panel, `[[C1]]` navigates to a claim. Identifiers are navy (§10), and
   one treatment covers all four — the difference is what it does, not what kind of
   thing it is. The first three are <button>s (they act in place), the claim is an
   <a> (it has no in-place surface), so the rules below reset BOTH the UA button
   chrome and the anchor underline back to inline prose: the reference must sit in
   the sentence exactly as the old inline link did, wrapping with it. */
.asst-ref {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  border-bottom: 1px solid transparent;
  background: none;
  font: inherit;
  text-align: left;
  color: var(--asst-ref-color, var(--brand-primary, #0a3373));
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
}

.asst-ref:hover {
  border-bottom-color: var(--asst-ref-color, var(--brand-primary, #0a3373));
}

/* An `[[X1]]` reference whose saved export is still loading. Opening a task
   panel is instant; loading a saved spec is a real round trip, so the reference
   has to say it is working. 45% opacity — the Charte's ONE disabled treatment
   (§6), never a grey swap — plus a progress cursor, and no hover underline so it
   does not read as still clickable. */
.asst-ref:disabled {
  opacity: 0.45;
  cursor: progress;
}

.asst-ref:disabled:hover {
  border-bottom-color: transparent;
}

/* An INERT `[[M1]]` name: the member panel is HR's alone, so on any other surface
   the reference is still an identifier in the prose but not a control. Same navy
   identifier tone as `.asst-ref` (§10 — navy marks identity, not clickability),
   minus every acting signal: no pointer, no hover underline. A disabled-looking
   control would be worse — an affordance an entire role can never use is noise.
   It also replaced the shape a member reference used to have: a navigating name
   with a small acting pill beside it on one baseline-aligned line. One name, one
   behaviour now, so both halves of that pair went. */
.asst-ref-static {
  font-weight: 700;
  color: var(--asst-ref-color, var(--brand-primary, #0a3373));
}

/* An event line: a turn the UI sent on the user's behalf after they acted in (or
   closed) the inline task panel. On the wire it is a `role: 'user'` message, but
   it is not something the user typed — so it reads as a centred cream-50 system
   note (metadata tone, §3) rather than the blue-50 user bubble. */
.asst-msg--event {
  align-self: center;
  align-items: center;
  max-width: 86%;
  padding: 8px 16px;
  border-radius: 999px;
  background-color: var(--asst-event-bg, var(--asst-card-bg, #faf6f0));
}

.asst-msg--event .asst-msg-text {
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 600;
  text-align: center;
  color: var(--asst-event-color, var(--asst-muted-color, #8d8579));
}

/* Skeleton rows reuse the message shells so `loading.tsx` matches the real shape. */
.asst-msg--skeleton {
  gap: 8px;
  min-width: 240px;
}

/* ── Working indicator (`tool_start`, and the pre-first-token gap) ── */

.asst-working {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: var(--asst-meta-size, 11.5px);
  color: var(--asst-muted-color, #8d8579);
}

.asst-working-dots {
  display: inline-flex;
  gap: 3px;
}

.asst-working-dots span {
  width: 5px;
  height: 5px;
  border-radius: 999px;
  background-color: var(--asst-muted-color, #8d8579);
  animation: asst-working-pulse 1.1s ease-in-out infinite;
}

.asst-working-dots span:nth-child(2) { animation-delay: 0.15s; }
.asst-working-dots span:nth-child(3) { animation-delay: 0.3s; }

@keyframes asst-working-pulse {
  0%, 100% { opacity: 0.28; }
  50%      { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .asst-working-dots span { animation: none; opacity: 0.6; }
}

/* ── Turn-level error — inline, so the transcript above it survives ── */

.asst-error {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 10px 14px;
  border-radius: 12px;
  font-size: var(--asst-meta-size, 12px);
  font-weight: 600;
  background-color: var(--asst-error-bg, rgba(194, 84, 46, 0.12));
  color: var(--asst-error-color, #c2542e);
}

/* ── Confirmation card (a PROPOSED write — the UI commits it, not the model) ── */

.asst-action-card {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 14px 16px;
  border-radius: 12px;
  background-color: var(--asst-card-bg, #faf6f0);
}

.asst-action-card-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.asst-action-card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 9px;
  background-color: var(--asst-card-icon-bg, rgba(67, 116, 243, 0.12));
  color: var(--asst-card-icon-color, var(--brand-secondary, #4374f3));
}

.asst-action-card-heading {
  min-width: 0;
}

.asst-action-card-kicker {
  margin: 0;
  font-size: var(--asst-label-size, 9.5px);
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--asst-muted-color, #8d8579);
}

.asst-action-card-task {
  margin: 2px 0 0;
  font-size: 13.5px;
  font-weight: 700;
  overflow-wrap: anywhere;
  color: var(--asst-title-color, #1c1a17);
}

.asst-action-card-text {
  margin: 0;
  font-size: var(--asst-text-size, 12.5px);
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  color: var(--asst-text-color, #4a4640);
}

.asst-action-card-outcome {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 600;
  color: var(--asst-muted-color, #8d8579);
}

.asst-action-card-outcome--ok {
  color: var(--asst-ok-color, var(--dash-status-approved-color, #3a8a5f));
}

.asst-action-card-outcome--error {
  color: var(--asst-error-color, #c2542e);
}

/* Paired buttons share a floor width so the label never sizes the control. */
.asst-action-card-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.asst-action-card-actions > button {
  min-width: 120px;
  min-height: 44px;
  justify-content: center;
}

/* ── Exporter: the export confirmation card ──

   The assistant is a QUERY BUILDER — it produced this spec and has never seen a
   row of the data it describes. So the card's job is to show exactly what will
   leave the platform (columns, in order, plus the row count and the file
   format) before a human commits to it.

   Shares `.asst-action-card`'s cream-50 inner-cell shell (§8), so only what is
   specific to an export lives here: the column list and the template-name
   reveal. Run is the blue-400 primary and "Save as template" the navy outline
   (§6) — an export is not an approval, so green stays out of it. */

/* The body of `.asst-export-panel`, never an inline transcript card — so it
   carries no shell of its own; the panel is the container. */
.asst-export-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* Two controls (Save as template · Run export) — there is no Dismiss: declining
   an export writes nothing, so nothing is retired by it and the panel's header
   close button is the way out. They still STACK rather than share a line: in a
   430px panel two full-width 44px targets read better than a squeezed pair, and
   the row is sticky, so a stacked pair keeps the primary at a constant place
   whatever the spec's length. `column-reverse` puts that primary on top,
   matching how the same row already behaves at mobile widths.

   PINNED (§13: the committing action never sits in the body flow). A 17-column
   spec scrolls, and Run export must not scroll away below the fold with it —
   `sticky` pins it to the foot of the panel's scroll area, opaque, with the
   divider above that a real footer would have. Sticky rather than a separate
   footer element because the actions belong to the card's own status machine;
   lifting them out would split one component across two containers. */
.asst-export-card .asst-action-card-actions {
  position: sticky;
  bottom: 0;
  flex-direction: column-reverse;
  padding-top: 12px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
}

.asst-export-card .asst-action-card-actions > button {
  width: 100%;
  min-height: 44px;
  justify-content: center;
}

.asst-export-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  margin: 0;
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 600;
  color: var(--asst-muted-color, #8d8579);
}

.asst-export-meta-item {
  white-space: nowrap;
}

/* The file format reads as a tag, not a status — sand-200 fill, §1. */
.asst-export-format {
  padding: 3px 9px;
  border-radius: 999px;
  background-color: var(--asst-export-tag-bg, #f2ede5);
  color: var(--asst-title-color, #1c1a17);
  font-size: var(--asst-label-size, 10px);
  font-weight: 700;
  letter-spacing: 0.06em;
}

/* The over-cap notice: the estimate is above the tenant's rows-per-file limit,
   so the server would refuse this run on that same number and the card offers no
   Run at all.

   GOLD-500, not rust-600 (§12: "pending / waiting"). Nothing has failed and
   nothing was attempted — this is a constraint the user has to satisfy before
   the export can go, which is the same tone as any other "waiting on you"
   state. Rust would read as a failure that already happened, and coral is the
   brand accent, never a status fill (§6).

   §2 tinted surface: the token at 16% for the fill, the same token at 100% for
   the icon + title. Chained through `--dash-status-pending-*` (which every theme
   already maps) so no theme file has to learn an `.asst-*` warning token, and a
   later `--asst-warn-*` still wins. Cream-50-sibling radius 12, no border — the
   tint carries it, §8. */
.asst-export-cap {
  display: flex;
  flex-direction: column;
  gap: 5px;
  padding: 11px 13px;
  border-radius: 12px;
  background-color: var(--asst-warn-bg, var(--dash-status-pending-bg, #fef3c7));
}

.asst-export-cap-title {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0;
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 700;
  color: var(--asst-warn-color, var(--dash-status-pending-color, #d97706));
}

/* Ink-700 body, not the gold: a three-line explanation set in a status colour is
   hard to read, and §3 keeps body copy on the text ramp. Wraps — never clipped
   (§21) — since both numbers are formatted and the sentence is long. */
.asst-export-cap-text {
  margin: 0;
  font-size: var(--asst-meta-size, 11.5px);
  line-height: 1.55;
  color: var(--asst-text-color, #4a4640);
  overflow-wrap: anywhere;
}

/* An <ol> because the ORDER is part of what is being approved: it is the order
   the file's header row will carry.

   A VERTICAL list, not a wrapped chip cloud: 17 chips laid out for a full-width
   card wrapped into three ragged rows, and in a 430px panel it was worse. One
   column per line reads as the file's header row, which is what it is — and the
   position number can now be shown, since it no longer has to crowd a chip.
   The number comes from a CSS counter rather than markup so each `<li>`'s text
   stays exactly the header the file will carry. Cream-50 inner cell, §8. */
.asst-export-cols {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin: 0;
  padding: 6px;
  border-radius: 12px;
  background-color: var(--asst-card-bg, #faf6f0);
  list-style: none;
  counter-reset: asst-export-col;
}

.asst-export-col {
  counter-increment: asst-export-col;
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 7px 10px;
  border-radius: 8px;
  background-color: var(--asst-chip-bg, #ffffff);
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 600;
  overflow-wrap: anywhere;
  color: var(--asst-text-color, #4a4640);
}

.asst-export-col::before {
  content: counter(asst-export-col);
  flex-shrink: 0;
  min-width: 14px;
  font-size: var(--asst-label-size, 10px);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--asst-muted-color, #8d8579);
}

/* ── The record an export leaves in the transcript ──
   The spec itself is reviewed in the side panel, so the conversation keeps this
   one-line button instead: dataset, column count, format and outcome, reopening
   the panel on click. Same affordance as a rehydrated `[[T1]]` task reference —
   a control in the transcript that mounts a docked panel. */
.asst-export-ref {
  align-self: flex-start;
  max-width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 14px;
  border: 1px solid var(--asst-chip-border, #e9e4dc);
  border-radius: 12px;
  background-color: var(--asst-chip-bg, #ffffff);
  font-family: inherit;
  text-align: left;
  cursor: pointer;
}

.asst-export-ref:hover {
  background-color: var(--asst-chip-bg-hover, var(--ins-pol-filter-bg-hover, #f2ede5));
}

.asst-export-ref-icon {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  background-color: var(--asst-card-icon-bg, rgba(67, 116, 243, 0.12));
  color: var(--asst-card-icon-color, var(--brand-secondary, #4374f3));
}

.asst-export-ref-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.asst-export-ref-title {
  font-size: 12.5px;
  font-weight: 700;
  overflow-wrap: anywhere;
  color: var(--asst-title-color, #1c1a17);
}

.asst-export-ref-meta {
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 500;
  overflow-wrap: anywhere;
  color: var(--asst-muted-color, #8d8579);
}

/* Naming a template is captured by this inline reveal — never a window.prompt
   (same rule as `<TaskReviewAction>`'s rejection reason). */
.asst-export-name {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.asst-export-name-label {
  font-size: var(--asst-label-size, 9.5px);
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--asst-muted-color, #8d8579);
}

.asst-export-name-input {
  width: 100%;
  padding: 11px 13px;
  border: 1px solid var(--field-hairline, #e9e4dc);
  border-radius: 10px;
  outline: none;
  background-color: var(--field-bg, #ffffff);
  font-family: inherit;
  font-size: var(--field-font-size, 12.5px);
  font-weight: 500;
  color: var(--field-color, #1c1a17);
}

.asst-export-name-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

.asst-export-name-actions {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}

.asst-export-name-actions > button {
  min-width: 120px;
  min-height: 44px;
  justify-content: center;
}

/* ── Exporter: the export spec, docked over the chat ──
   The overlay SHELL is the shared `.side-panel` rule (worn by every right-docked
   panel); this is only its content — its head carries a kicker above the title,
   so it builds its own rather than taking `<SidePanel>`'s. Header / body split
   follows §13. */

.asst-export-panel-head {
  flex-shrink: 0;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 24px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.asst-export-panel-heading {
  min-width: 0;
}

.asst-export-panel-title {
  margin: 2px 0 0;
  font-size: 20px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--asst-title-color, #1c1a17);
}

.asst-export-panel-body {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  padding: 16px 24px 24px;
}

/* ── Assistant: the member policy-assign panel, docked over the chat ──
   Unlike the export panel, this one takes `<SidePanel title>`'s STANDARD §13
   head (`.modal-head` + `.modal-close`) and scrolling `.modal-body` — its head
   is a title and a close button and nothing more, so there is nothing to build.
   Only the body's own blocks live here. */

.asst-member-panel-block {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.asst-member-panel-block:last-of-type {
  padding-bottom: 0;
  margin-bottom: 14px;
  border-bottom: 0;
}

/* Explanatory prose under a block: the lock sentence (which names the enrolled
   policy and what blocks a change — this is what carries a locked member now that
   the coverage badge is gone) or the "no policy available" fallback. Prose only:
   no badge, no dot, no coloured pill belongs in this panel. Wraps — never clips
   (§21). */
.asst-member-panel-note {
  margin: 0;
  font-size: var(--asst-text-size, 12.5px);
  line-height: 1.55;
  overflow-wrap: anywhere;
  color: var(--asst-text-color, #4a4640);
}

/* The way OUT of the panel and onto the member's own page — and, since the
   `[[M1]]` name itself stopped being a link, the ONLY route there from the chat.
   Kept at the foot so the acting surface comes first. */
.asst-member-panel-link {
  align-self: flex-start;
  font-size: var(--asst-meta-size, 12px);
  font-weight: 700;
  color: var(--asst-ref-color, var(--brand-primary, #0a3373));
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.asst-member-panel-link:hover {
  border-bottom-color: var(--asst-ref-color, var(--brand-primary, #0a3373));
}

.asst-export-tpl-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.asst-export-tpl {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 16px;
  border-radius: 12px;
  background-color: var(--asst-card-bg, #faf6f0);
}

.asst-export-tpl-name {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  overflow-wrap: anywhere;
  color: var(--asst-title-color, #1c1a17);
}

.asst-export-tpl-meta {
  margin: 0;
  font-size: var(--asst-meta-size, 11.5px);
  font-weight: 500;
  overflow-wrap: anywhere;
  color: var(--asst-muted-color, #8d8579);
}

/* Both actions stretch equally, so the longer label never sizes the pair. */
.asst-export-tpl-actions {
  display: flex;
  gap: 10px;
}

.asst-export-tpl-actions > button {
  flex: 1;
  min-height: 44px;
  justify-content: center;
  text-align: center;
}

/* ── Empty state: intro + the starter chips (up to four groups of three) ──

   EVERY NUMBER IN THIS SECTION IS A HEIGHT BUDGET, AND THE BUDGET IS DESKTOP-ONLY.
   On a pointer viewport the block must fit without `.asst-transcript` scrolling —
   a capability a mouse user has to scroll to find is one they will not find —
   whereas on a phone the same block scrolls by design (see the mobile block at
   the foot of this section, which relaxes these gaps again). It grew a fourth
   group (claims) plus a fourth intro sentence, worth ~52px of group and ~20px of
   wrapped text. Rather than drop a chip or a group, the vertical rhythm was
   tightened by the same ~85px:
   this padding 32→14, the gap 8→6, the icon 52→46, the intro's `max-width`
   540→600 (one line fewer), and the two group gaps in `.asst-starters-groups` /
   `.asst-starters-group`. Four groups now occupy marginally less than three did
   before. Anything added here has to find its own space the same way. */

.asst-starters {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 6px;
  padding: 14px 24px;
}

.asst-starters-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  margin-bottom: 2px;
  border-radius: 14px;
  background-color: var(--asst-card-icon-bg, rgba(67, 116, 243, 0.12));
  color: var(--asst-card-icon-color, var(--brand-secondary, #4374f3));
}

.asst-starters-title {
  margin: 0;
  font-size: 17px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--asst-title-color, #1c1a17);
}

/* Wider than the 440px it started at, and wider again at 600px: the intro carries
   one sentence per enabled capability (tasks, portfolio, claims, exports), and
   every 60px of measure is worth roughly one wrapped line back to the chip groups
   below — which is where the fourth group's height came from. */
.asst-starters-text {
  margin: 0;
  max-width: 600px;
  font-size: var(--asst-text-size, 12.5px);
  line-height: 1.55;
  color: var(--asst-muted-color, #8d8579);
}

.asst-starters-chips {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 12px;
}

/* Labelled starter groups — rendered as soon as there is more than one group, so
   a caller with none of the portfolio, claims or export capabilities keeps the
   original unlabelled single row (a label naming the only thing on screen is
   noise). Up to four groups of three read as structure; twelve undifferentiated
   pills read as a wall, and the labels are what announce a capability the user did
   not know existed. Labels follow Charte §3's section label (9.5px Bold uppercase,
   +6% tracking, always ink-400).

   THEY STAY IN ONE COLUMN. Two-up on wide viewports would have paid for the
   fourth group in one move, but it costs more than it saves: several chips are
   30–55 characters, and in a half-width column each group's row wraps to two or
   three lines — taller than the single-column stack it replaced. The height came
   out of the gaps instead (18/8 → 12/6 → 9/5), for the same reason the chips are
   smaller than a filter pill: on a POINTER viewport the whole block must fit
   without `.asst-transcript` scrolling, and a group a mouse user has to scroll to
   find defeats the point of showing it. On a phone it scrolls instead and these
   gaps are relaxed again — see the mobile block. */
.asst-starters-groups {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 9px;
  margin-top: 10px;
}

.asst-starters-group {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
}

.asst-starters-group-label {
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--asst-muted-color, #8d8579);
}

.asst-starters-group .asst-starters-chips {
  margin-top: 0;
}

/* Lighter than a filter pill on purpose. These are up to twelve suggestions, not
   twelve controls competing with the composer: 34px tall, 11.5px label, 1px
   hairline. The 44px tap-target floor still applies on touch and is restored in
   the mobile block below, where the chips also go full-width. Weight, radius and
   the sand-400 hairline stay Charte §6's unselected-control treatment — only the
   scale comes down. */
.asst-chip {
  min-height: 34px;
  padding: 7px 13px;
  border: 1px solid var(--asst-chip-border, #e9e4dc);
  border-radius: 999px;
  background-color: var(--asst-chip-bg, #ffffff);
  color: var(--asst-chip-color, var(--brand-primary, #0a3373));
  font-family: inherit;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.2;
  cursor: pointer;
}

.asst-chip:hover:not(:disabled) {
  background-color: var(--asst-chip-bg-hover, var(--ins-pol-filter-bg-hover, #f2ede5));
}

.asst-chip:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── Composer, pinned at the foot of the card ── */

.asst-composer-row {
  flex-shrink: 0;
  padding: 14px 26px 18px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.asst-composer {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  padding: 8px 8px 8px 16px;
  border: 1px solid var(--asst-composer-border, var(--field-hairline, #e9e4dc));
  border-radius: 22px;
  background-color: var(--asst-composer-bg, var(--field-bg, #ffffff));
}

.asst-composer:focus-within {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

/* `max-height` is the AUTO-GROW CAP, and the grow itself lives in
   `<AssistantComposer>` (a layout effect writing `style.height` from
   `scrollHeight`). Past 160px the field scrolls instead of pushing the transcript
   away — the composer may not eat the conversation. The JS deliberately does not
   clamp, so this line is the single owner of the limit.

   `field-sizing: content` would replace the effect with one declaration, but it
   ships in Chromium only — no Safari, no Firefox — so it cannot be the mechanism.
   It is not layered on top either: two sizing authorities on one element is a
   worse failure mode than one that works everywhere. Revisit when it is baseline;
   the effect can then be deleted, and this rule stays either way. */
.asst-composer-input {
  flex: 1;
  min-width: 0;
  max-height: 160px;
  padding: 10px 0;
  border: none;
  outline: none;
  resize: none;
  background: transparent;
  font-family: inherit;
  font-size: var(--field-font-size, 13px);
  line-height: 1.5;
  color: var(--field-color, #1c1a17);
}

.asst-composer-input::placeholder {
  color: var(--field-placeholder-color, #8d8579);
}

.asst-composer-send {
  flex-shrink: 0;
  min-height: 44px;
  justify-content: center;
}

/* The placeholder wears the real composer's TWO-PART shape — a message bar plus
   a Send-sized block — so nothing jumps when the live composer mounts, at either
   width. The send placeholder is a full-width `<Shimmer>` capped here rather than
   sized inline, which is what lets the mobile block release the cap and stack it
   full width exactly like the real button. */
.asst-composer--skeleton {
  align-items: center;
  min-height: 60px;
}

.asst-composer--skeleton > .ui-shimmer:last-child {
  flex-shrink: 0;
  max-width: 96px;
}

@media (max-width: 768px) {
  /* Bubbles get the width back — 375px cannot spare 22% of gutter. */
  .asst-msg,
  .asst-msg--user {
    max-width: 92%;
  }

  .asst-transcript {
    padding: 18px 16px;
    gap: 14px;
  }

  .asst-composer-row {
    padding: 12px 16px 16px;
  }

  /* THE COMPOSER STACKS — Send goes UNDER the textarea, full width.
     At 375px the horizontal pill left the input a ~170px column, and the shared
     `.hr-dash-recent-btn-primary { flex: 1 }` mobile rule made it worse by
     letting Send GROW to claim half the row.
     The shell is re-cut for two rows rather than merely flipped: the 22px radius
     was a single-line pill's (it reads as a lozenge on a ~100px-tall block) and
     the asymmetric `8px 8px 8px 16px` padding existed to inset the text from the
     pill's curve while hugging the button — neither holds once the button is
     below. So: even 10px padding, 16px radius, and the input carries its own
     small text inset instead of the container's 16px left one.
     `:focus-within` is untouched — it still outlines the whole block. */
  .asst-composer {
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    padding: 10px;
    border-radius: 16px;
  }

  /* TWO ROWS AT REST, so the field reads as a text area and not as a slot above a
     button. Send is a 44px full-width block here, so a one-row (≈32px) field left
     the button as the dominant element of the composer — the exact complaint the
     screenshot showed. `min-height` rather than `rows`: it is a FLOOR the auto-grow
     sits on, so the field still grows to the 160px cap and still shrinks — back to
     two rows, never to one. 2 × 19.5px line + 12px padding ≈ 51 → 52.
     Padding goes 4/6 → 6/8: at 4px the text sat almost on the border now that the
     input carries its own inset instead of the pill's 16px one. */
  .asst-composer-input {
    min-height: 52px;
    padding: 6px 8px;
  }

  /* Full width, and explicitly NOT `flex: 1`: the shared primary-button mobile
     rule would otherwise let Send grow along the new main axis — vertically.
     The 44px tap-target floor comes from the base rule and stays. */
  .asst-composer-send {
    flex: 0 0 auto;
    width: 100%;
  }

  /* Same two-row shape for the placeholder, AT THE REAL THING'S RESTING HEIGHT:
     10 + 52 (two-row field) + 8 + 44 (Send) + 10 = 124px. `space-between` puts the
     text bar at the top of the field's zone and the pill where Send actually sits,
     so nothing shifts when the live composer mounts. The desktop width cap is
     released below so that pill runs full width like the real button. */
  .asst-composer--skeleton {
    align-items: stretch;
    justify-content: space-between;
    min-height: 124px;
  }

  .asst-composer--skeleton > .ui-shimmer:last-child {
    max-width: none;
  }

  /* A turn error is 2–3 lines at this width, so the icon aligns to the FIRST
     line instead of the middle of the paragraph, and never shrinks. */
  .asst-error {
    align-items: flex-start;
  }

  .asst-error > svg {
    flex-shrink: 0;
    margin-top: 2px;
  }

  /* Confirm/Dismiss stack and stretch equally rather than sharing one line. */
  .asst-action-card-actions {
    flex-direction: column-reverse;
  }

  .asst-action-card-actions > button,
  .asst-export-name-actions > button {
    width: 100%;
  }

  /* Same treatment for the export card's name reveal and the picker's per-row
     pair: 375px cannot hold two 120px buttons plus their gap. */
  .asst-export-name-actions,
  .asst-export-tpl-actions {
    flex-direction: column-reverse;
  }

  /* Chips run full width so a long prompt never overflows the card, and get the
     44px tap-target floor back — the desktop scale is a pointer affordance. */
  .asst-starters-chips {
    flex-direction: column;
    align-self: stretch;
  }

  .asst-chip {
    min-height: 44px;
    padding: 10px 14px;
  }

  /* THE EMPTY STATE SCROLLS HERE, AND THAT IS CORRECT. Twelve stacked 44px chips
     under four labels is ~620px — taller than any phone — but the no-scroll
     budget the desktop rules are cut to is a POINTER constraint: a mouse user
     does not discover a capability below the fold, whereas scrolling is the
     native way to read a phone screen. So nothing is dropped or shrunk here.
     Two consequences to get right instead:

     `justify-content: flex-start` — with content taller than the pane, centring
     has nothing to centre, and a centred overflowing block is the classic way to
     put its own top out of scroll reach. Starting at the top also puts the intro
     where a phone user starts reading. The block scrolls inside
     `.asst-transcript` (this is a plain flex item with `min-height: auto`, so it
     cannot be squashed) and the composer stays pinned below it.

     And the desktop spacing is DELIBERATELY relaxed again: those numbers exist
     only to buy vertical space that a scrolling column does not need, and at
     phone scale they read cramped. */
  .asst-starters {
    justify-content: flex-start;
    gap: 8px;
    padding: 22px 16px 10px;
  }

  .asst-starters-groups {
    gap: 16px;
    margin-top: 14px;
  }

  .asst-starters-group {
    gap: 7px;
  }
}

/* ─────────────────────────────────────────────
   Quote submission journey — sketches 10, 11 and 12.
   `.qro-*` request overview (`<RequestOverview>`), `.qrs-*` review and send
   (`<ReviewAndSend>`), `.qmi-*` missing information (`<MissingInformationModal>`).
   Cards, banners, buttons, pills-in-a-modal and the gap list itself are the
   shared `.hr-dash-recent-*` / `.qd-banner*` / `.modal-*` / `.qgap-*`
   families — only what those don't already cover lives here.
   ───────────────────────────────────────────── */
.qro-shell {
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* ── The submission journey's shared shape ──
   A full-width verdict banner over two columns: what leaves the company on the
   left, who receives it (and the one button forward) on the right. Both the
   request overview and the review page wear it, so the two read as one journey
   rather than two products. */
.qro-layout {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr);
  align-items: start;
  gap: 18px;
}

.qro-main,
.qro-side {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

/* The rail follows a tall left column rather than stranding the CTA at the
   bottom of the page. Unstuck once the columns stack — see the media query. */
.qro-side {
  position: sticky;
  top: 0;
}

/* Opt-out for a rail that can be TALLER than the viewport — `/hr/quotes/new`'s
   carries the whole category list plus a "what happens next" panel plus the
   CTA, and `/insurer/quotes/[id]`'s carries the decision panel plus the insured
   population card beneath it. A sticky box taller than its scroll container pins
   its top and puts its own bottom permanently out of reach, which on those two
   rails would be the CTA and the population's export respectively. */
.qro-side--static {
  position: static;
}

/* ── `<RequestStatusBand>` (post-submit status band) ──
   A full-width band in the position the readiness hero occupies pre-submit, NOT
   a column: the request's status and its list of categories are two different
   things, and standing them side by side made them compete rather than
   sequence.

   TWO STACKED ROWS, and that is structural. The status badge and the facts
   share the first; the category pills always own the second. A request with a
   dozen categories would otherwise wrap its pills up into the fact row and push
   the status off the top of the band — the one thing on it that must never be
   hard to find. */
.qrb-band {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 16px 20px;
}

.qrb-main {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px 24px;
}

/* The status never shrinks or wraps away from the front of the band. */
.qrb-main > .qd-summary-status {
  flex-shrink: 0;
}

.qrb-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 24px;
  margin: 0;
}

/* Label above value, so the row wraps into aligned columns rather than a ragged
   run of text — the inline one-line version had no room for labels at all. */
.qrb-fact {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.qrb-fact-value {
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  /* Base fallback is the tone the old horizontal summary bar's values carried,
     so a non-Heale theme keeps the look it had before the band existed. */
  color: var(--qrb-fact-value-color, #111827);
  overflow-wrap: anywhere;
}

/* The second row, separated by the same hairline the card heads use. */
.qrb-cats {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 8px 14px;
  padding-top: 12px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eef2f7);
}

.qrb-cats-label {
  margin: 0;
  flex-shrink: 0;
}

.qrb-pills {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}

/* ── Readiness banner (`<ReadinessHero>`) ──
   §2 tinted surface: the state token at ~14% for the fill, the same token at
   100% inside the badge. The headline stays navy in both tones — it is the
   verdict, not the warning. */
.qro-hero {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
  padding: 15px 26px;
  border-radius: 16px;
}

.qro-hero--ready {
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
}

.qro-hero--blocked {
  background-color: var(--dash-status-pending-bg, rgba(227, 179, 65, 0.16));
}

.qro-hero-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 100px;
}

.qro-hero--ready .qro-hero-badge {
  background-color: var(--dash-status-approved-color, #3a8a5f);
  color: var(--qro-hero-badge-color, #fff);
}

.qro-hero--blocked .qro-hero-badge {
  background-color: var(--dash-status-pending-color, #e3b341);
  color: var(--qro-hero-badge-color, #fff);
}

.qro-hero-body {
  display: flex;
  flex-direction: column;
  gap: 4px;
  flex: 1;
  min-width: 200px;
}

/* Line-height is explicit so the first line's box (~25px) stays within a few
   pixels of the 32px badge beside it — the two read as optically centred with
   no negative offsets. */
.qro-hero-title {
  font-size: 18.5px;
  line-height: 1.35;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--brand-primary, #0a3373);
  margin: 0;
}

.qro-hero-meta {
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

.qro-hero-sub {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

.qro-hero-action {
  flex-shrink: 0;
  align-self: center;
}

/* The row list's own container. It exists so `.qro-row:last-child` below keeps
   meaning "the end of the LIST" rather than "the end of the card body" — the
   request overview renders `.qro-card-foot` after the rows, which silently made
   that reset stop matching: the last row kept its `border-bottom` AND the foot
   drew its own `border-top` 12px lower, i.e. two parallel rules. The separator
   between the list and the foot is the foot's border-top, and it is the only
   one. Holds for all four bodies the card renders — rows, rows + foot, an
   empty/error `<ListState>` + foot, and the loading shimmer + foot — because
   none of the other three carries a bottom rule of its own. */
.qro-rows {
  display: flow-root;
}

.qro-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 14px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.qro-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.qro-row:first-child {
  padding-top: 0;
}

/* An incomplete category is the one thing HR has to act on, so the row is
   tinted and rules its own left edge — the same warm/amber tone the
   readiness banner uses, never a red "error" treatment: nothing is broken,
   something is simply unfinished. */
.qro-row--incomplete {
  margin: 0 -12px;
  padding-left: 12px;
  padding-right: 12px;
  border-left: 3px solid var(--qro-row-incomplete-rule, #eca04b);
  border-radius: 8px;
  background-color: var(--qro-row-incomplete-bg, #fffbeb);
}

/* Complete but not yet validated: a quieter, informational rule. Nothing is
   missing here — one confirmation is — so it must not wear the same "something
   is unfinished" tint as the row above. */
.qro-row--unvalidated {
  margin: 0 -12px;
  padding-left: 12px;
  padding-right: 12px;
  border-left: 3px solid var(--qro-row-unvalidated-rule, #4374f3);
  border-radius: 8px;
  background-color: var(--qro-row-unvalidated-bg, #f4f8ff);
}

/* The leading state marker. A sendable category needs no words — a check is
   the whole message — so only the two states that must be explained keep a
   labelled pill in the row's right cluster. */
.qro-row-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 100px;
}

.qro-row-mark--ready {
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
  color: var(--dash-status-approved-color, #3a8a5f);
}

/* An empty ring: it reads as "not ticked yet" and holds the row's left edge in
   line with its ready siblings, so a mixed list doesn't zig-zag. */
.qro-row-mark--pending {
  border: 1.5px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qro-row-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1;
  min-width: 0;
}

/* Budget, pill, action and delete — the row's right cluster. */
.qro-row-right {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 14px;
  flex-shrink: 0;
}

.qro-row-budget {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
  white-space: nowrap;
}

/* "3 categories" — the count in the card head, subordinate to its title. */
.qro-head-count {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The categories card head carries a title + an explanatory line on the left
   and, on the right, the count next to the one link out to category
   administration ("Manage categories"). The rows describe what THIS request
   asks for; renaming a category or moving people between categories is a
   different job, and it gets a visible door rather than being implied by a row
   action. `min-width: 0` so the subtitle wraps instead of pushing that door off
   the card. */
.qro-head-main {
  min-width: 0;
}

.qro-head-side {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-shrink: 0;
}

/* The one line a needs-a-final-check row carries instead of a gap list. */
.qro-row-hint {
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qro-row-name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
}

.qro-row-meta {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The missing fields themselves, each a link to the wizard step that owns it.
   Wraps rather than truncates — a hidden missing field is the bug this screen
   exists to fix. */
.qro-gap-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px 10px;
  margin-top: 4px;
}

.qro-gap-link {
  padding: 0;
  border: none;
  background: none;
  font-size: 12px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
  text-align: left;
}

.qro-gap-link:hover {
  text-decoration: underline;
}

.qro-gap-link + .qro-gap-link::before {
  content: '·';
  margin-right: 10px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The blocked-send list: one line per §10.5 check standing in the way, each a
   full sentence. Stacked rather than sharing `.qro-gap-links`' inline
   dot-separated row, which is built for short field names. A check with no fix
   page renders as `.qro-block-note` — same line, no affordance, because a
   button that navigates nowhere is worse than plain text. */
.qro-block-list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-top: 6px;
}

.qro-block-link {
  padding: 2px 0;
  min-height: 26px;
  border: none;
  background: none;
  font-size: 12px;
  font-weight: 700;
  line-height: 1.4;
  text-align: left;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}

.qro-block-link:hover {
  text-decoration: underline;
}

.qro-block-link:focus-visible {
  outline: 2px solid var(--brand-secondary, #4374f3);
  outline-offset: 2px;
  border-radius: 6px;
}

.qro-block-note {
  font-size: 12px;
  line-height: 1.4;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qro-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex-shrink: 0;
  padding: 3px 10px;
  border-radius: 100px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}

.qro-pill--ready {
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
  color: var(--dash-status-approved-color, #3a8a5f);
}

.qro-pill--incomplete {
  background-color: var(--dash-status-pending-bg, rgba(227, 179, 65, 0.16));
  color: var(--dash-status-pending-color, #e3b341);
}

/* Awaiting its final check — informational (§12 "open" blue), not a warning. */
.qro-pill--unvalidated {
  background-color: var(--qro-pill-unvalidated-bg, rgba(67, 116, 243, 0.14));
  color: var(--qro-pill-unvalidated-color, #4374f3);
}

.qro-row-action {
  flex-shrink: 0;
  padding: 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
  min-height: 44px;
}

.qro-row-action:hover {
  text-decoration: underline;
}

/* The row's secondary actions (the note, "Reset plan", "Remove from request")
   moved into the row's `<RowActionsMenu>` overflow when the row reached four
   competing actions, so they wear `.dropdown-item` now. Only the PRIMARY
   Finish/Validate/Review action (`.qro-row-action` above) stays on the row. */

/* Both list-level actions ("Add a category" and the copy-forward disclosure)
   are docked at the foot of the categories card body — they act on the rows
   above them, so they live inside that card rather than as loose furniture
   under it. Copy-forward is COLLAPSED by default: an occasional tool must not
   spend the card's height on two idle pickers, so the resting state is one
   quiet line and the controls expand beneath it (inline, never a modal — the
   rows being chosen between have to stay visible). */
.qro-card-foot {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.qro-card-foot-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px;
}

/* "Add a category" — a quiet blue text action, not a second CTA competing with
   the rail's Review-and-send button. */
.qro-foot-action {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}

.qro-foot-action:hover {
  text-decoration: underline;
}

.qro-foot-action:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  text-decoration: none;
}

.qro-copy-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
  cursor: pointer;
}

.qro-copy-trigger:hover {
  color: var(--brand-secondary, #4374f3);
}

/* Expanded controls: a cream inner surface (§8), subordinate to the rows above
   — no border and no card of its own. */
.qro-copy-panel {
  margin-top: 6px;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
}

/* Dismiss without copying. Quiet by design — it undoes an expansion, it does
   not compete with Copy. */
.qro-copy-cancel {
  padding: 0 4px;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
  cursor: pointer;
  min-height: 44px;
}

.qro-copy-cancel:hover {
  text-decoration: underline;
}

.qro-copy-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
}

.qro-copy-label {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #4a4640);
}

/* Sizes the `.combo-inline` wrapper around each copy-forward `<Combobox>` —
   fixed rather than content-sized so the two pickers stay the same width
   whatever the category names are. */
.qro-copy-select {
  width: 190px;
  max-width: 100%;
}

/* ── "What insurers will see" (`<InsurerVisibilityCard>`) ──
   Three statements about what a request discloses and one about what it never
   does. The exclusion is the only red thing on the screen — that is the point
   of the card, so it is the one row that changes colour. */
.qro-see-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.qro-see-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.qro-see-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border-radius: 100px;
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
  color: var(--dash-status-approved-color, #3a8a5f);
}

/* Body copy, not a heading and not a value — four sentences about what a
   request discloses. It therefore carries the §3 BODY tone on its own token
   rather than borrowing `--hr-dash-recent-title-color`: reading the title token
   would have retinted this paragraph every time a card heading was restyled,
   which is a tone it does not belong to. */
.qro-see-text {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--qro-see-text-color, #374151);
}

.qro-see-row--excluded .qro-see-icon {
  background-color: var(--dash-status-rejected-bg, rgba(194, 84, 46, 0.12));
  color: var(--dash-status-rejected-color, #c2542e);
}

.qro-see-row--excluded .qro-see-text {
  font-weight: 600;
  color: var(--dash-status-rejected-color, #c2542e);
}

/* ── Broadcast rail (`<InsurerBroadcastCard>`) ── */
.qro-side-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.qro-side-note {
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* §3 section label — always muted, never semantically coloured. */
.qro-side-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

.qro-insurer-rows {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.qro-insurer-row {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.qro-insurer-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border-radius: 100px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  background-color: var(--qro-avatar-bg, #0a3373);
  color: var(--qro-avatar-color, #fff);
}

/* Wraps rather than truncates — an insurer's own name is never abbreviated on
   the screen that promises who receives this request. */
.qro-insurer-name {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
  overflow-wrap: anywhere;
}

/* The response window: an inner cell on the card's own surface (§8), tinted
   with the informational blue rather than a status tone — it states a fact,
   it is not a verdict. */
.qro-window {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px;
  border-radius: 12px;
  background-color: var(--qro-window-bg, rgba(67, 116, 243, 0.08));
}

.qro-window-value {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--brand-primary, #0a3373);
}

.qro-window-note {
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The one button forward. Full width because the rail has room for exactly one
   decision, and nothing should look like an alternative to it. */
.qro-side-cta {
  width: 100%;
  justify-content: center;
}

.qro-side-cta-note {
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* Leaving without sending — the rail's fallback, and a full button rather than
   the text link it used to be: at the foot of a column of actions a link read
   as furniture instead of as the alternative it is.

   It is its OWN control, not a repainted primary. Composing
   `.hr-dash-recent-btn-primary` looked cheaper but made this a variant that had
   to CANCEL its base — and the one property it forgot to cancel,
   `box-shadow: var(--hr-dash-recent-btn-primary-shadow)`, put a halo around a
   button that is not primary. So the geometry is taken from the same tokens the
   primary reads (identical width, height, radius and type by construction, not
   by imitation) and the paint is stated once, positively. */
.qro-side-draft {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  padding: var(--hr-dash-recent-btn-padding, 8px 16px);
  border-radius: var(--hr-dash-recent-btn-radius, 100px);
  border: none;
  background-color: var(--qro-draft-bg, #e5e7eb);
  color: var(--qro-draft-color, #374151);
  font-size: 13px;
  font-weight: var(--hr-dash-recent-btn-weight, 600);
  cursor: pointer;
  transition: background-color 0.15s;
  white-space: nowrap;
}

.qro-side-draft:hover:not(:disabled) {
  background-color: var(--qro-draft-bg-hover, #d1d5db);
}

/* §6: 45% opacity, never a grey swap — this button is already grey. */
.qro-side-draft:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The roster of recipients, which now follows the action block rather than
   preceding it. The hairline is what keeps that reading as "and here is who
   gets it" instead of as stray content dangling under the button. */
.qro-side-recipients {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding-top: 14px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

/* The submit route's "not validated yet" rejection, rendered beside the button
   that produced it. Stacked and flush inside the rail, unlike the full-width
   `.qd-banner` it borrows its tone from. */
.qro-side-banner {
  flex-direction: column;
  align-items: flex-start;
  margin-bottom: 0;
}

/* Body of the two request-level action cards — the notes prompt and the send
   block: copy left, one button right. Cards rather than banners, because both
   are action surfaces that stay put whether or not the request is ready. */
.qro-cta-body {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  padding: 20px 24px;
}

/* ── Document links (`.doclink-*`) ──
   The compact read-only counterpart to the `.mf-doc-row` document card: a
   stacked list of documents you can OPEN, sized to live inside a summary cell
   where a full row would not fit (`<CompanyLicenceLinks>`). Three states, kept
   visually distinct on purpose — an openable document, a record with no file
   behind it, and a read that failed — because collapsing any two of them into
   one line is how a summary ends up claiming something the data never said. */
.doclink-list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 3px;
}

.doclink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 2px 0;
  min-height: 26px;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.35;
  text-align: left;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
  overflow-wrap: anywhere;
}

.doclink:hover {
  text-decoration: underline;
}

.doclink:focus-visible {
  outline: 2px solid var(--brand-secondary, #4374f3);
  outline-offset: 2px;
  border-radius: 6px;
}

/* A licence with no scan uploaded: the fact is real, the document is not — so
   it reads as a value with a note, never as a link that opens nothing. */
.doclink-static {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  /* Its own token rather than the shared `--hr-dash-recent-title-color`: that
     one carries a signed-off cross-surface blast radius (see the /hr Charte
     guard), and a document list has no business widening it. */
  color: var(--doclink-static-color, #18181b);
  overflow-wrap: anywhere;
}

.doclink-static-note {
  font-size: 11px;
  font-weight: 500;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The read failed. Deliberately NOT a dash: "—" in this cell would tell the
   company it holds no licence, which is a different — and wrong — statement. */
.doclink-note {
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.35;
  color: var(--dash-status-rejected-color, #c2542e);
}

/* ── Review and send (sketch 11) ── */
.qrs-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 14px;
}

.qrs-cell {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
  min-width: 0;
}

.qrs-cell-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Values wrap — a licence list or an insurer name is never truncated here,
   because this card is the promise of "what insurers will receive". */
.qrs-cell-value {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-title-color, #18181b);
  overflow-wrap: anywhere;
}

.qrs-cat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.qrs-cat-row:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.qrs-cat-row:first-child {
  padding-top: 0;
}

.qrs-cat-main {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.qrs-cat-name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
}

.qrs-cat-meta {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Wraps because the timeline row now carries TWO actions — the start-date fix
   and "Continue anyway" — beside a full-sentence detail. Both buttons are
   `flex-shrink: 0`, so without this the sentence would be squeezed into a
   ribbon between 1025px and ~1200px, where the rail still takes its column.
   No effect at any width with room for both. */
.qrs-check {
  display: flex;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.qrs-check:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

.qrs-check:first-child {
  padding-top: 0;
}

.qrs-check-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 22px;
  height: 22px;
  border-radius: 100px;
}

.qrs-check--pass .qrs-check-icon {
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
  color: var(--dash-status-approved-color, #3a8a5f);
}

.qrs-check--warn .qrs-check-icon {
  background-color: var(--dash-status-pending-bg, rgba(227, 179, 65, 0.16));
  color: var(--dash-status-pending-color, #e3b341);
}

.qrs-check--fail .qrs-check-icon {
  background-color: var(--dash-status-rejected-bg, rgba(194, 84, 46, 0.12));
  color: var(--dash-status-rejected-color, #c2542e);
}

.qrs-check-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.qrs-check-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
}

.qrs-check-detail {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Composed ON TOP of `.hr-dash-recent-btn-outline(--sm)`, which carries the
   whole §6 secondary shape — this class only positions it and paints the focus
   ring. It used to redeclare `border: none; background: none`, i.e. a tinted
   text link, and the acknowledge variant below then muted that text to ink-400:
   the one control that lets HR proceed past a failing check read as a caption.
   Declared after the button classes, so nothing here may re-null the shape. */
.qrs-check-action {
  flex-shrink: 0;
  align-self: center;
}

.qrs-check-action:focus-visible {
  outline: 2px solid var(--brand-secondary, #4374f3);
  outline-offset: 2px;
}

.qrs-check-action:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* "Continue anyway" is the acknowledge path on an acknowledgeable check — a
   real button, but the lighter of the two: a sand hairline and a slate label
   against the fix action's navy outline, so the concession never outranks the
   remedy beside it (nor the send CTA in the rail). ONE tint for both places it
   appears: the check row, and the hero, which offers the same pair of ways out
   rather than only the fix. */
.qrs-check-action--continue,
.qro-hero-action--continue {
  border-color: var(--ack-btn-border, #d1d5db);
  color: var(--ack-btn-color, #334155);
}

.qrs-check-action--continue:hover,
.qro-hero-action--continue:hover {
  background-color: var(--ack-btn-bg-hover, #f2ede5);
}

.qro-hero-action--continue:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The review page's own chip list of recipients was replaced by the broadcast
   rail's `.qro-insurer-rows` / `.qro-insurer-name`, which both screens now
   share; its two rules are deleted rather than left behind, because the same
   insurer name styled in two places is exactly how the two screens drifted
   apart before. Only the empty-state line below is still rendered — by the
   shared rail. */

.qrs-insurer-empty {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* ── Missing information modal (sketch 12) ──
   The groups are direct children of `.modal-body`, which already spaces them
   (`gap: 16px`) — no margin of their own, or the two would compound. */
.qmi-group-head {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0 0 8px;
}

.qmi-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eaeff6);
}

.qmi-row:last-child {
  border-bottom: none;
}

.qmi-row-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 160px;
}

.qmi-row-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #18181b);
}

.qmi-row-help {
  font-size: 12px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* A fixed row turns green in place and keeps its slot, so HR can see the list
   shrinking without it jumping around. */
.qmi-row--fixed {
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
  border-radius: 10px;
  padding-left: 12px;
  padding-right: 12px;
  border-bottom-color: transparent;
}

.qmi-row-fixed-note {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 700;
  color: var(--dash-status-approved-color, #3a8a5f);
}

/* Sizes the `.combo-inline` wrapper around an inline gap-fix `<Combobox>`. */
.qmi-select {
  width: 220px;
  max-width: 100%;
}

.qmi-radio-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
}

.qmi-radio {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-title-color, #18181b);
  cursor: pointer;
}

.qmi-open-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
  padding: 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
  min-height: 44px;
}

.qmi-open-btn:hover {
  text-decoration: underline;
}

.qmi-row-spinner {
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The rail needs its own width to be worth a column. Below this the two stack,
   and the rail MUST lose its stickiness — a sticky block in a single-column
   flow pins itself over the content it was meant to sit beside. */
@media (max-width: 1024px) {
  .qro-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .qro-side {
    position: static;
  }
}

@media (max-width: 768px) {
  /* Every one of these rows is label-left / control-right on desktop; at
     375px the control needs the full width instead of a 40px sliver. */
  .qro-row,
  .qrs-cat-row,
  .qmi-row,
  .qrs-check {
    flex-direction: column;
    align-items: stretch;
  }

  /* Stacked, the right cluster spreads across the row it now owns instead of
     hugging one edge. */
  .qro-row-right {
    justify-content: space-between;
  }

  .qro-copy-select,
  .qmi-select {
    width: 100%;
    min-width: 0;
  }

  .qro-cta-body {
    padding: 18px 16px;
  }

  .qro-cta-body > button {
    width: 100%;
  }

  .qro-hero {
    padding: 14px 16px;
  }

  .qro-hero-action {
    width: 100%;
  }

  /* The rail's two full-width buttons keep the 44px touch target — and keep it
     TOGETHER, so the pair never reads as two different sizes. */
  .qro-side-cta,
  .qro-side-draft {
    min-height: 44px;
  }

  /* "Add a category" is a text action beside the copy disclosure on desktop,
     where a 44px box would be absurd; on touch both need a real tap target. */
  .qro-foot-action,
  .qro-copy-trigger,
  .qro-block-link,
  .doclink {
    min-height: 44px;
  }

  /* The check row stacks above, so its actions own the width they are given
     rather than hugging the left edge — and `--sm`'s 32px height gives way to
     the 44px tap target. */
  .qrs-check-action {
    align-self: stretch;
    justify-content: center;
    height: auto;
    min-height: 44px;
  }
}

/* ---------------------------------------------------------------------------
   Grouped list (`.glist-*`) — the card-grouped list treatment: ONE card per
   group, whose header row carries a tone dot + the group name + a lighter
   inline hint + a right-aligned count, followed by one row per item (bold
   title, muted meta line, and a right cluster holding a status pill and an
   optional action).

   Charte §8 card shell (supplied by `.hr-dash-recent-card`, which the group
   wrapper composes with), §10 row rhythm + sand-300 divider between rows and
   none on the last, §12 status pill. Deliberately role-agnostic — it is the
   generic shape `<GroupedListCard>` / `<GroupedListRow>`
   (`src/components/ui/grouped-list.tsx`) render, first used by
   `/hr/quotes/archive`. Distinct from the `.tm-*` list family, which is a
   whole-row `<button>` (one click target per row) with Task-Manager
   selection/action-needed rules; a `.glist-row` is a plain container whose
   only interactive child is its own action, so the two can't share classes.
   --------------------------------------------------------------------------- */
.glist-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: var(--hr-dash-recent-head-padding, 20px 24px 16px);
  padding-bottom: 14px;
}

.glist-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  flex-shrink: 0;
}

/* Group tones follow §12's own state colours: coral-500 = action needed,
   gold-500 = pending/waiting, ink-400 = closed/inactive. Consumed tokens only,
   and the same three tones any other grouped list would need. */
.glist-dot--action {
  background-color: var(--brand-accent, #ff6733);
}

.glist-dot--waiting {
  background-color: var(--dash-status-pending-color, #d97706);
}

.glist-dot--muted {
  background-color: var(--ins-pol-subtext-color, #9ca3af);
}

.glist-title {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
}

/* The lighter inline explanation, sat right of the title and given the row's
   free space so a long hint wraps instead of squashing the count. */
.glist-hint {
  margin: 0;
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.glist-count {
  flex-shrink: 0;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.glist-rows {
  display: flex;
  flex-direction: column;
}

.glist-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 13px 24px;
  border-top: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

/* §Deviations — rows use the app-wide ROW-hover token (cream-50 on Heale),
   never the control-hover one. */
.glist-row:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #f9fafb);
}

.glist-row-main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.glist-row-title {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
}

/* Meta line under the title — wraps, never clips (§21). */
.glist-row-meta {
  margin: 0;
  font-size: 12px;
  color: var(--ins-pol-subtext-color, #9ca3af);
}

.glist-row-right {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Row action — the §6 secondary pill (`.hr-dash-recent-btn-outline` supplies
   fill/border/radius/hover), raised here to the 44px mobile tap-target floor.
   Also carried by the page actions in the filter row, for the same floor. */
.glist-row-action {
  min-height: 44px;
}

/* The §6 page-action cluster in `<FilterTabs rightSlot>` — wraps on its own
   rather than pushing the pills off-screen. Local to this page family, so the
   shared `.filter-tabs-right-slot` (used by other tab rows) is untouched. */
.glist-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

@media (max-width: 768px) {
  /* At 375px a row stacks rather than squashing the title against the pill. */
  .glist-row {
    flex-direction: column;
    align-items: stretch;
    padding: 13px 16px;
  }

  .glist-row-right {
    flex-wrap: wrap;
    justify-content: flex-start;
  }

  .glist-row-action {
    flex: 1 1 auto;
    justify-content: center;
  }

  /* Both page actions take the full width, side by side at equal size. */
  .glist-actions {
    width: 100%;
  }

  .glist-head {
    padding: 16px 16px 12px;
    flex-wrap: wrap;
  }
}

/* =====================================================================
   Generic completion donut — `<ProgressDonut>` (`src/components/ui/
   progress-donut.tsx`). Role-agnostic: a percentage ring with the figure in
   the middle. The percentage itself is the ONE per-instance value and is
   passed as the `--ui-donut-pct` custom property by the component (geometry,
   not a colour), so nothing about the look is inline.
   ===================================================================== */
.ui-donut {
  --ui-donut-pct: 0;
  position: relative;
  flex: 0 0 auto;
  width: var(--ui-donut-size, 76px);
  height: var(--ui-donut-size, 76px);
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: conic-gradient(
    var(--ui-donut-fill, var(--brand-secondary, #4374f3)) calc(var(--ui-donut-pct) * 1%),
    var(--ui-donut-track, var(--pol-summary-bar-track, #eee8df)) 0
  );
}

.ui-donut-hole {
  position: absolute;
  inset: var(--ui-donut-thickness, 8px);
  border-radius: 50%;
  background-color: var(--ui-donut-hole-bg, #ffffff);
}

.ui-donut-value {
  position: relative;
  font-size: var(--ui-donut-value-size, 17px);
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--ui-donut-value-color, var(--dash-stat-value-color, #1c1a17));
}

/* =====================================================================
   Generic checkbox — THE shared checkbox treatment. Role-agnostic: put it on a
   real `<input type="checkbox">` and nothing about the element changes.
   `appearance: none` only drops the platform PAINT — it stays a native
   checkbox, so keyboard operation, `:checked`, `:disabled`, `<label>`
   association and the screen-reader role are all untouched. That is why this is
   not a div with `role="checkbox"`, and why `:focus-visible` below has to paint
   its own ring: removing the UA outline is only safe because one replaces it.

   The tick is a `::before` clip-path polygon rather than an SVG data URI or a
   background image, so its colour is a TOKEN like everything else here (a data
   URI would have to inline a hex) and it scales with `--ui-check-size`.

   Intended to replace the app's remaining native + `accent-color` boxes
   (`.mf-invite-check`, `.mf-ocr-row input`, the backoffice permission grids, …)
   INCREMENTALLY — each of those is a reviewed visual change on its own screen,
   so adopt one call site at a time rather than sweeping them all at once. First
   and, for now, only consumer: the quote wizard's member list
   (`.qw-mem-check`).
   ===================================================================== */
.ui-check {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  display: grid;
  place-content: center;
  width: var(--ui-check-size, 16px);
  height: var(--ui-check-size, 16px);
  margin: 0;
  padding: 0;
  border: 1px solid var(--ui-check-border, #e9e4dc);
  border-radius: var(--ui-check-radius, 4px);
  background-color: var(--ui-check-bg, #ffffff);
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease;
}

/* Always present, scaled to nothing when unchecked: ticking is then a pure
   transform — no layout shift, and one box model instead of two to keep in sync. */
.ui-check::before {
  content: '';
  width: calc(var(--ui-check-size, 16px) * 0.62);
  height: calc(var(--ui-check-size, 16px) * 0.62);
  background-color: var(--ui-check-glyph, #ffffff);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
  transform: scale(0);
  transition: transform 0.12s ease;
}

/* Hover is a border shift, not a fill: the unchecked box IS white, so tinting it
   would read as a half-applied tick. */
.ui-check:hover:not(:disabled) {
  border-color: var(--ui-check-border-hover, var(--brand-secondary, #4374f3));
}

.ui-check:checked {
  background-color: var(--ui-check-bg-checked, var(--brand-secondary, #4374f3));
  border-color: var(--ui-check-border-checked, var(--ui-check-bg-checked, var(--brand-secondary, #4374f3)));
}

.ui-check:checked::before {
  transform: scale(1);
}

/* The same ring every other focusable control in the app paints
   (`--field-focus-*`), so tabbing through a form does not change treatment when
   it reaches a checkbox. */
.ui-check:focus-visible {
  outline: none;
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
  box-shadow: var(--field-focus-ring, 0 0 0 3px color-mix(in srgb, var(--brand-secondary, #4374f3) 15%, transparent));
}

/* §6 disabled: a sand fill, never an `opacity` fade — a fade washes out border
   and tick together and reads as half-rendered rather than locked. */
.ui-check:disabled {
  cursor: not-allowed;
  background-color: var(--ui-check-bg-disabled, var(--field-disabled-bg, #f2ede5));
  border-color: var(--ui-check-border-disabled, #e9e4dc);
}

/* Ticked but locked. A §2 tinted surface — a pale tint of the brand blue for the
   fill, the FULL brand blue for the tick — rather than the solid blue + white
   tick of the live state: a white tick on a pale blue fill is barely visible
   (≈1.5:1), which is the trap a naive "just lighten the fill" lands in. */
.ui-check:checked:disabled {
  background-color: var(--ui-check-bg-checked-disabled, color-mix(in srgb, var(--brand-secondary, #4374f3) 28%, #ffffff));
  border-color: var(--ui-check-border-checked-disabled, color-mix(in srgb, var(--brand-secondary, #4374f3) 45%, #ffffff));
}

.ui-check:checked:disabled::before {
  background-color: var(--ui-check-glyph-checked-disabled, var(--brand-secondary, #4374f3));
}

/* DERIVED — ticked by something else (a parent row, an inherited setting) and
   shown for information only. Same lighter-blue construction as the locked state
   above, but its OWN tokens: the two states are only coincidentally similar
   ("someone else ticked this" vs "you may not touch this"), so a theme must be
   able to pull them apart. Cursor is `default`, not `not-allowed`, because
   nothing is being refused here — there was never an action to take.
   All three selectors carry `:checked:disabled` to outrank the block above,
   which the bare modifier would lose to at equal specificity. */
.ui-check--derived:disabled {
  cursor: default;
}

.ui-check--derived:checked:disabled {
  background-color: var(--ui-check-bg-derived, color-mix(in srgb, var(--brand-secondary, #4374f3) 28%, #ffffff));
  border-color: var(--ui-check-border-derived, color-mix(in srgb, var(--brand-secondary, #4374f3) 45%, #ffffff));
}

.ui-check--derived:checked:disabled::before {
  background-color: var(--ui-check-glyph-derived, var(--brand-secondary, #4374f3));
}

/* =====================================================================
   Quote Wizard layout — main column + persistent progress sidebar
   (`<QuoteWizard>` + `<CategoryProgressPanel>`). Replaces the old floating
   `.qw-footer` pill: completion, per-step state and the primary CTA all live
   in the sidebar now, so the footer family is retired.
   ===================================================================== */
.qw-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 316px;
  gap: 22px;
  align-items: start;
}

.qw-main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* Wizard head — category name + "Built with X" + the "Change build method"
   escape hatch, sitting above the step rail. Metadata scale on purpose (§3 row
   title + metadata, NOT a page title): the rail is the primary navigation on
   this screen and the sidebar carries the primary CTA, so this line must read
   as context, never as a third competing control. The link itself borrows
   `.qw-info-link` rather than any button treatment for the same reason. */
.qw-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.qw-head-main {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}

.qw-head-name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.qw-head-meta {
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.qw-head-change {
  flex-shrink: 0;
}

/* Sticks to the top of `.dash-body`'s scroll frame (the real scrolling
   ancestor — see `.qw-shell`), so the CTA stays reachable on a tall step. */
.qw-side {
  position: sticky;
  top: 0;
  align-self: start;
  min-width: 0;
}

.qw-side-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px;
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  background-color: #ffffff;
  border: 1px solid var(--hr-dash-recent-card-border, transparent);
  box-shadow: var(--hr-dash-recent-card-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
}

.qw-side-progress {
  display: flex;
  align-items: center;
  gap: 14px;
}

.qw-side-progress-text {
  min-width: 0;
}

.qw-side-headline {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #1c1a17);
}

.qw-side-hint {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

.qw-side-sep {
  height: 1px;
  background-color: var(--hr-dash-recent-row-border, #eee8df);
}

.qw-side-steps {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.qw-side-step {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 9px 8px;
  border: none;
  border-radius: 10px;
  background: none;
  text-align: left;
  cursor: pointer;
}

.qw-side-step:disabled {
  cursor: default;
}

.qw-side-step:hover:not(:disabled) {
  background-color: var(--ins-pol-filter-bg-hover, var(--hr-dash-recent-row-hover-bg, #faf6f0));
}

.qw-side-step--current {
  background-color: var(--hr-dash-recent-row-hover-bg, #faf6f0);
}

.qw-side-step-icon {
  flex: 0 0 auto;
  margin-top: 1px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
}

.qw-side-step-icon--done {
  background-color: var(--dash-status-approved-bg, rgba(58, 138, 95, 0.14));
  color: var(--dash-status-approved-color, #3a8a5f);
}

.qw-side-step-icon--todo {
  background-color: var(--dash-status-pending-bg, rgba(227, 179, 65, 0.16));
  color: var(--dash-status-pending-color, #e3b341);
}

.qw-side-step-icon--idle {
  background-color: var(--dash-status-inactive-bg, rgba(141, 133, 121, 0.14));
  color: var(--dash-status-inactive-color, #8d8579);
}

.qw-side-step-body {
  min-width: 0;
}

.qw-side-step-name {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.qw-side-step--idle .qw-side-step-name {
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

.qw-side-step-hint {
  margin: 2px 0 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

.qw-side-actions {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* Both sidebar buttons are full width, so a longer label can never make the
   pair different sizes. */
.qw-side-cta,
.qw-side-later {
  width: 100%;
  justify-content: center;
  min-height: 44px;
}

.qw-side-later {
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
  cursor: pointer;
  border-radius: 999px;
}

.qw-side-later:hover {
  background-color: var(--ins-pol-filter-bg-hover, #f2ede5);
}

/* Discreet autosave state — the sidebar's answer to the retired footer pill. */
.qw-side-save {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 16px;
  font-size: 11px;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

.qw-side-save--saved { color: var(--dash-status-approved-color, #3a8a5f); }
.qw-side-save--error { color: var(--dash-status-rejected-color, #c2542e); }

@media (max-width: 1100px) {
  .qw-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Progress + CTA move ABOVE the step body rather than to the bottom of a
     very tall page, where they would be unreachable without scrolling. */
  .qw-side {
    order: -1;
    position: static;
  }
}

/* =====================================================================
   Quote Wizard step 1 — member selection (`step-informations.tsx`).
   Membership is now CHOSEN here (search / All / Select all / per-employee
   checkbox) and validated server-side against the category's jurisdiction;
   a refusal is rendered per row (`.qw-mem-error`), never as a bare toast.
   Dependants are NOT independently selectable — they follow their employee.

   Every row is THREE zones, employee and dependant alike, which is what makes
   the list line up: checkbox (`.ui-check` + `.qw-mem-check`, and
   `.ui-check--derived` + disabled on a dependant) · name over `Relation · DOB`
   (`.qw-mem-main`) · a right-aligned
   trailing value (`.qw-mem-trail` — the dependant count, "follows {employee}",
   or `—`).

   Card order, per the reference: heading + subtitle with the head-count tiles
   right-aligned beside them (`.qw-mem-head`) · one toolbar row · the list,
   whose rows run FULL-BLEED to both card edges · a quiet right-aligned foot
   (`.qw-mem-foot`) for navigation off the card.
   ===================================================================== */
.qw-mem-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px 20px;
  flex-wrap: wrap;
  margin-bottom: 18px;
}

.qw-mem-head-main {
  flex: 1 1 260px;
  min-width: 0;
}

/* `.mf-hint` carries a 4px side margin for its usual in-form context; here it
   must start on the heading's own left edge. Capped so the subtitle wraps into
   a readable measure instead of running the full card width. */
.qw-mem-head-main .mf-hint {
  margin-inline: 0;
  max-width: 62ch;
}

/* Secondary navigation off this card, docked at the FOOT of its body — the same
   idiom as `.qro-card-foot` on the request overview's categories card (a hairline,
   then one quiet line under the rows it relates to), so "secondary action lives at
   the card's foot" is consistent across the two quote screens instead of a one-off.
   It used to sit under the subtitle, which is the whole reason the heading row read
   busier than the reference: that row is heading + subtitle + head-count tiles and
   nothing else. Right-aligned because it navigates AWAY — the left edge belongs to
   the list's own content. NOT rendered in the empty-census state, where the very
   same link is the `<ListState>` action and has to be front and centre. */
.qw-mem-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

/* Tiles hug their content and hold the heading row's right edge — they are a
   summary of the list, not a full-width band above it. */
.qw-mem-stats {
  display: flex;
  flex: 0 0 auto;
  gap: 10px;
}

.qw-mem-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

/* Search is sized, not greedy: the filter pills sit immediately beside it, so
   a growing search box would shove them into the middle of the row. */
.qw-mem-toolbar .ins-pol-search {
  flex: 0 1 260px;
  min-width: 0;
}

.qw-mem-toolbar .filter-tabs-row {
  flex: 0 0 auto;
}

/* The one action that applies to the whole list, on the row's right edge. */
.qw-mem-toolbar-action {
  margin-left: auto;
}

/* FULL-BLEED: the list escapes the card's horizontal padding so a row's hover,
   selected and refused washes reach both card edges (each row re-applies that
   padding, so its content still lines up with the card heading). Deliberately
   no border, no radius and no `overflow: hidden` — the reference has no inset
   box here, only hairlines between families. The card's own padding still
   insets the loading / empty states above it. */
.qw-mem-list {
  display: flex;
  flex-direction: column;
  margin-inline: calc(-1 * var(--qw-info-card-pad, 24px));
}

.qw-mem-group + .qw-mem-group {
  border-top: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

/* 11px, not the table's 14px: a family is 2–5 rows the user scans as one block
   and ticks through, so it wants the reference's tighter rhythm rather than the
   airier spacing of a page-level table where each row is its own record. */
.qw-mem-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px var(--qw-info-card-pad, 24px);
}

.qw-mem-row--employee {
  cursor: pointer;
}

.qw-mem-row--employee:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #faf6f0);
}

/* Declared after `:hover` so the tick keeps its tint while the pointer is on the
   row. Blue rather than the cream `--hr-dash-recent-tr-expanded-bg` this used to
   read: cream IS the hover tone, so a cream selected row read as nothing (see
   `--qw-mem-row-selected-bg` for the full reasoning). */
.qw-mem-row--selected,
.qw-mem-row--selected:hover {
  background-color: var(--qw-mem-row-selected-bg, #e8effc);
}

/* A dependant row keeps the employee row's THREE zones — its checkbox sits in
   the same column (derived and disabled), and the `↳` indents the name instead,
   so nothing in the row shifts out of alignment. It carries no divider of its
   own: the hairline now belongs to `.qw-mem-group`, so a family reads as one
   block.

   The box is the shared `.ui-check` treatment (see its block above) — this list
   is its first consumer, which is why the class carries the whole look and this
   one only positions it. It replaces the native + `accent-color` box that used
   to sit here: the reference's square is lighter than a platform checkbox at any
   size, so the fix was the paint, not shaving a pixel off the geometry.
   `margin-top` optically centres it on the row's first line. */
.qw-mem-check {
  margin-top: 2px;
}

.qw-mem-main {
  min-width: 0;
  flex: 1 1 auto;
}

/* Third zone: the dependant count on an employee row, "follows {employee}" on a
   dependant's. Pushed to the row's right edge and never wrapped, so every row —
   "1 dependant", "follows Kamil", "—" — shares one right edge whatever the
   text length. */
.qw-mem-trail {
  flex: 0 0 auto;
  margin-left: auto;
  padding-top: 1px;
  padding-left: 10px;
  text-align: right;
  white-space: nowrap;
  font-size: 11.5px;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

.qw-mem-name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.qw-mem-row--dep .qw-mem-name {
  font-size: 12.5px;
  font-weight: 500;
  color: var(--hr-dash-recent-td-color, #4a4640);
}

.qw-mem-arrow {
  margin-right: 6px;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

/* Same size and tone as `.qw-mem-trail` — both are §3 metadata on the same row, so
   a 0.5px difference between them only read as an accident. */
.qw-mem-meta {
  margin: 2px 0 0;
  font-size: 11.5px;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

/* Server refusal (422) for this member — the resolved jurisdictions come from
   the response body, so the copy names them instead of saying "invalid". */
.qw-mem-error {
  margin: 4px 0 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--dash-status-rejected-color, #c2542e);
}

/* Last of the three row washes on purpose: a refused row is rust whether or not
   it is also selected or hovered, because the refusal is what the user has to act
   on. The `:hover` selector is carried on every wash so all three sit at equal
   specificity and pure declaration order decides — hover < selected < refused. */
.qw-mem-row--refused,
.qw-mem-row--refused:hover {
  background-color: var(--dash-status-rejected-bg, rgba(194, 84, 46, 0.14));
}

@media (max-width: 768px) {
  /* Lowering the card padding retunes the full-bleed maths on its own — the
     list's negative margin and each row's padding both read this value. */
  .qw-info-card {
    --qw-info-card-pad: 16px;
  }

  /* At 375px the three toolbar members each want the whole width rather than a
     third of it, and the tiles read better as a full-width trio under the
     heading than as three slivers beside it. */
  .qw-mem-stats {
    flex: 1 1 100%;
  }

  .qw-mem-toolbar .ins-pol-search {
    flex-basis: 100%;
  }

  .qw-mem-toolbar-action {
    margin-left: 0;
    width: 100%;
    min-height: 44px;
  }

  /* A 12px text link is a fine desktop target and an unusable touch one — same
     treatment `.qro-copy-trigger` gets at this width. */
  .qw-mem-foot .qw-info-link {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* =====================================================================
   Quote Wizard step 2 — Budget / Coverage / Provider network
   (`step-plan-network.tsx`). Three labelled cards with an advisory hint under
   each label; the only computed hint is the per-covered-life figure, which
   renders nothing at all when premium or lives is unknown.
   ===================================================================== */
.qw-pn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 18px;
  align-items: start;
}

.qw-pn-card {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 20px;
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  background-color: #ffffff;
  border: 1px solid var(--hr-dash-recent-card-border, transparent);
  box-shadow: var(--hr-dash-recent-card-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
}

.qw-pn-card--wide {
  grid-column: 1 / -1;
}

.qw-pn-card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

.qw-pn-card-title {
  margin: 0;
  font-size: 16px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #1c1a17);
}

.qw-pn-field {
  min-width: 0;
}

.qw-pn-label {
  display: block;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-field-label-color, #8d8579);
}

.qw-pn-hint {
  margin: 3px 0 8px;
  font-size: 11px;
  line-height: 1.45;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

.qw-pn-amount {
  display: flex;
  align-items: center;
  gap: 8px;
}

.qw-pn-currency {
  font-size: 12px;
  font-weight: 700;
  color: var(--ins-pol-field-label-color, #8d8579);
}

/* Computed "≈ AED X per covered life" — absent, not zeroed, when either
   input is missing. */
.qw-pn-perlife {
  margin: 6px 0 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--brand-secondary, #4374f3);
}

.qw-pn-net-badge {
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  background-color: var(--dash-status-inactive-bg, rgba(141, 133, 121, 0.14));
  color: var(--dash-status-inactive-color, #8d8579);
}

.qw-pn-net-badge--some {
  background-color: var(--dialog-info-bg, rgba(67, 116, 243, 0.14));
  color: var(--brand-secondary, #4374f3);
}

.qw-pn-net-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 8px;
}

.qw-pn-net-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  min-height: 44px;
  padding: 10px 12px;
  border-radius: 10px;
  border: 1px solid var(--hr-dash-recent-row-border, #eee8df);
  background: none;
  font-size: 12.5px;
  color: var(--hr-dash-recent-td-color, #4a4640);
  cursor: pointer;
  text-align: left;
}

.qw-pn-net-row:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #faf6f0);
}

.qw-pn-net-row--on {
  border-color: var(--brand-secondary, #4374f3);
  background-color: var(--dialog-info-bg, rgba(67, 116, 243, 0.1));
  color: var(--hr-dash-recent-name-color, #1c1a17);
  font-weight: 600;
}

/* Library templates strip above the three cards. */
.qw-tpl-block {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px 20px;
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  background-color: var(--modal-total-bg, #faf6f0);
}

.qw-tpl-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

.qw-tpl-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #1c1a17);
}

.qw-tpl-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 12px;
}

.qw-tpl-card {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px;
  border-radius: 12px;
  background-color: #ffffff;
  box-shadow: var(--hr-dash-recent-card-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
}

.qw-tpl-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.qw-tpl-meta {
  font-size: 11px;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

/* 44px floor like every other tap target. */
.qw-tpl-apply {
  margin-top: auto;
  min-height: 44px;
  justify-content: center;
}

.qw-tpl-empty {
  font-size: 12px;
  color: var(--hr-dash-recent-subtitle-color, #8d8579);
}

/* =====================================================================
   Quote Wizard step 3 additions — the amber "to fill in" treatment on a
   required-but-empty control (`step-requested-benefits.tsx`; the rest of that
   step's CSS is above). The per-family completion dot moved to the shared
   `.status-tabs-dot*` family with the tab strip itself.
   ===================================================================== */
.qw-benefits-todo-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 10.5px;
  font-weight: 700;
  background-color: var(--dash-status-pending-bg, rgba(227, 179, 65, 0.16));
  color: var(--dash-status-pending-color, #e3b341);
}

/* One amber signal, two carriers: the chip beside the label and the control
   itself. On a select the border goes on the TRIGGER, not on the sizing
   wrapper around it — a border on the wrapper drew a second rectangle around
   the field's own. */
.qw-benefits-todo .qw-benefits-select-trigger,
.qw-benefits-todo .qw-benefits-switch {
  border-color: var(--dash-status-pending-color, #e3b341);
}

/* =====================================================================
   Quote Wizard step 4 — the readiness summary, in flow (`step-validate.tsx`).
   It was a modal overlay; the committing action now sits in the sidebar with
   every other step's CTA, so this card only reports state.
   ===================================================================== */
.qw-validate-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 24px;
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  background-color: #ffffff;
  border: 1px solid var(--hr-dash-recent-card-border, transparent);
  box-shadow: var(--hr-dash-recent-card-shadow, 0 2px 10px rgba(28, 26, 23, 0.05));
}

.qw-validate-icon--ready { color: var(--dash-status-approved-color, #3a8a5f); }
.qw-validate-icon--blocked { color: var(--dash-status-pending-color, #e3b341); }

.qw-validate-title {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: var(--hr-dash-recent-title-color, #1c1a17);
}

.qw-validate-text {
  margin: 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--hr-dash-recent-td-color, #4a4640);
}

/* The "to fill in" chip sits inline after the benefit label — scoped to the
   amber case so the default row layout is untouched. */
.qw-benefits-todo .qw-benefits-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

@media (max-width: 768px) {
  /* Step 1 needs no override — `.qw-info-stack` is a single column at every
     width, and since the request-details card moved out it has no field rows
     left to stack. Only the currency/limit prefix rows would still overflow at
     375px. */
  .qw-pn-amount {
    flex-wrap: wrap;
  }

  /* The head stacks rather than squeezing the link against the category name,
     and both text links get a real tap target (they are 12px links on desktop,
     where a 44px box would be absurd). */
  .qw-head {
    align-items: flex-start;
  }

  .qw-head-change,
  .qcm-back {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   CENSUS REMOVAL — bulk bar + "Remove from coverage" modal + termination form
   (Charte §2 tinted surfaces, §6 buttons, §8 elevation, §9 forms, §17 states)

   Every value reads a CONSUMED token with a base fallback, so the pre-Charte
   themes keep their historical look and a Heale palette change reskins all of
   it at once. Added because the census list's bulk bar and its bulk modal were
   the last inline-styled region on that page.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Shared: an <input type="file"> that is reachable to assistive tech but not
   painted — the visible affordance is the button that clicks it. */
.cx-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Floating bulk-action bar ─────────────────────────────────────────────── */
/* §8 floating elevation, no border — the shadow carries the bar. */
.census-bulkbar {
  position: fixed;
  bottom: 28px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  display: flex;
  align-items: center;
  /* `position: fixed` + `left: 50%` + `right: auto` makes the box shrink-to-fit
     against only the REMAINING half of the viewport, so its available width is
     50vw however wide the window is — the translate re-centres it visually but
     gives none of that space back. `width: max-content` opts out of the
     available-width constraint so the bar sizes to its own content, and
     `nowrap` keeps all three actions on one row; the mobile block below opts
     back into wrapping on purpose. */
  width: max-content;
  max-width: calc(100vw - 32px);
  flex-wrap: nowrap;
  gap: 12px;
  padding: 10px 16px;
  border-radius: var(--dash-stat-radius, 14px);
  background-color: var(--hr-dash-recent-card-bg, #fff);
  border: 1px solid var(--dash-stat-border, transparent);
  box-shadow: 0 3px 14px rgba(28, 26, 23, .12), 0 12px 40px rgba(28, 26, 23, .08);
}

.census-bulkbar-count {
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-td-color, #18181b);
}

.census-bulkbar-clear {
  background: none;
  border: none;
  padding: 0;
  font-size: 13px;
  color: var(--ins-pol-subtext-color, #6b7280);
  text-decoration: underline;
  cursor: pointer;
}

.census-bulkbar-sep {
  width: 1px;
  height: 22px;
  background: var(--hr-dash-recent-divider, #e5e7eb);
}

/* §6 secondary button, pill — identical shape to `.hr-dash-recent-btn-outline`,
   restated at bar height so the row keeps one rhythm. */
.census-bulkbar-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 34px;
  padding: 0 14px;
  border-radius: 999px;
  border: 1.2px solid var(--ins-pol-seg-border, #d2d9e5);
  background: var(--hr-dash-recent-card-bg, #fff);
  color: var(--brand-primary, #0a3373);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}

.census-bulkbar-btn:hover:not(:disabled) {
  background-color: var(--ins-pol-filter-bg-hover, #f1f5fe);
}

.census-bulkbar-btn:disabled {
  opacity: .45;
  cursor: not-allowed;
}

/* Destructive: rust label AND border on a white fill (§6 Rejection), never a
   coral or a filled-red button. */
.census-bulkbar-btn--danger {
  color: var(--dash-status-rejected-color, #dc2626);
  border-color: var(--dash-status-rejected-color, #dc2626);
}

.census-bulkbar-btn--danger:hover:not(:disabled) {
  background-color: color-mix(in srgb, var(--dash-status-rejected-color, #dc2626) 8%, transparent);
}

@media (max-width: 768px) {
  /* The bar must not exceed the viewport at 375px — it wraps into a stack and
     each button keeps a 44px tap target. Pinning both edges removes the
     shrink-to-fit problem the desktop rule works around, so the width clamp and
     the single-row rule are both handed back here. */
  .census-bulkbar {
    left: 12px;
    right: 12px;
    transform: none;
    width: auto;
    max-width: none;
    flex-wrap: wrap;
    justify-content: center;
  }

  .census-bulkbar-btn {
    min-height: 44px;
    flex: 1 1 auto;
    justify-content: center;
  }
}

/* ── Termination form fields (single-member modal + bulk rows) ─────────────── */

/* Both surfaces render their controls ON an inner cream surface (`.cx-brm-rows` /
   `.cx-brm-applyall`, `--modal-total-bg`), so both opt into `.fields-on-tint` in
   the markup — see that modifier under "THE FIELD CONTRACT" for the why. */
.cx-term-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 18px;
}

.cx-term-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* §9: label ABOVE the field, 10px bold uppercase, always ink-400. */
.cx-term-field-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Honest fallback line under a field whose server-derived requirement could not be
   resolved (§17 error, inline rather than a blocking state — HR can still attach). */
.cx-term-field-note {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.cx-term-field-retry {
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  text-decoration: underline;
  cursor: pointer;
}

.cx-term-error {
  margin: 0 0 14px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--dash-status-rejected-color, #dc2626);
}

/* ── Supporting-document upload slot ──────────────────────────────────────── */
.cx-term-upload {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.cx-term-upload-label {
  margin: 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.cx-term-upload--compact .cx-term-upload-label {
  font-size: 11px;
}

/* §16: cream fill, blue-400 border, radius 12 — the dropzone's compact sibling. */
.cx-term-upload-btn {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 38px;
  padding: 0 14px;
  border-radius: 12px;
  border: 1.4px solid var(--brand-secondary, #4374f3);
  background: var(--ins-pol-search-bg, #faf6f0);
  color: var(--brand-secondary, #4374f3);
  font-size: 12.5px;
  font-weight: 700;
  cursor: pointer;
}

.cx-term-upload-btn:disabled {
  opacity: .45;
  cursor: not-allowed;
}

/* §16 document row: white card, sand border, radius 12. */
.cx-term-upload-file {
  display: flex;
  align-items: center;
  gap: 8px;
  min-height: 38px;
  padding: 0 8px 0 12px;
  border-radius: 12px;
  border: 1px solid var(--ins-pol-seg-border, #e9e4dc);
  background: var(--hr-dash-recent-card-bg, #fff);
  color: var(--dash-status-approved-color, #3a8a5f);
}

.cx-term-upload-filename {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.cx-term-upload-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 8px;
  border: none;
  background: none;
  color: var(--ins-pol-subtext-color, #8d8579);
  cursor: pointer;
}

.cx-term-upload-remove:hover:not(:disabled) {
  background-color: var(--ins-pol-filter-bg-hover, #f2ede5);
}

.cx-term-upload-error {
  margin: 0;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--dash-status-rejected-color, #dc2626);
}

/* ── Bulk removal modal ───────────────────────────────────────────────────── */
/* Three zones like a side panel (§13): head / scrolling body / pinned actions. */
.cx-brm {
  display: flex;
  flex-direction: column;
  max-height: min(86vh, 860px);
}

.cx-brm-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--hr-dash-recent-divider, #eee8df);
}

.cx-brm-head .mf-modal-text {
  margin: 4px 0 0;
}

.cx-brm-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 32px;
  height: 32px;
  border-radius: 10px;
  border: 1px solid var(--ins-pol-seg-border, #e9e4dc);
  background: var(--hr-dash-recent-card-bg, #fff);
  color: var(--ins-pol-subtext-color, #8d8579);
  cursor: pointer;
}

.cx-brm-close:disabled {
  opacity: .45;
  cursor: not-allowed;
}

.cx-brm-body {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 18px 0;
}

.cx-brm-section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.cx-brm-section-title {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 13px;
  font-weight: 700;
  color: var(--brand-primary, #0a3373);
}

.cx-brm-section-title--danger {
  color: var(--dash-status-rejected-color, #dc2626);
}

.cx-brm-section-title--muted {
  color: var(--ins-pol-subtext-color, #8d8579);
}

.cx-brm-section-copy {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #6b7280);
}

/* Inner metadata surface (§8): cream, radius 12, no shadow. */
.cx-brm-rows {
  display: flex;
  flex-direction: column;
  border-radius: 12px;
  background: var(--modal-total-bg, #faf6f0);
  overflow: hidden;
}

.cx-brm-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
  padding: 12px 14px;
}

.cx-brm-row + .cx-brm-row {
  border-top: 1px solid var(--hr-dash-recent-divider, #eee8df);
}

.cx-brm-row-main {
  flex: 1 1 200px;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.cx-brm-row-name {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.cx-brm-row-meta {
  margin: 0;
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The outstanding-input / server-error line under a row — wraps, never clips. */
.cx-brm-row-note {
  margin: 0;
  font-size: 11.5px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--dash-status-rejected-color, #dc2626);
}

/* Grows so a terminate row's three controls (reason, date, upload) have room and
   wrap rather than squeeze; cancel/delete rows render no slot at all. */
.cx-brm-row-slot {
  flex: 1 1 320px;
  min-width: 0;
}

.cx-brm-term-inputs {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  flex-wrap: wrap;
}

.cx-brm-applyall {
  display: flex;
  align-items: flex-end;
  gap: 10px;
  flex-wrap: wrap;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--modal-total-bg, #faf6f0);
}

.cx-brm-applyall .cx-term-field-label {
  align-self: center;
}

/* The per-row / apply-to-all termination reason. Same field contract as
   `.modal-textarea` (which it is composed with), just short enough to sit on a row
   beside the date and the upload slot instead of dominating it. */
.cx-brm-reason {
  flex: 1 1 200px;
  min-width: 160px;
  min-height: 62px;
  resize: vertical;
}

/* Running document counter — gold while outstanding (§12 pending), green once
   complete, both as the token's own colour on a tinted surface (§2). */
.cx-brm-progress {
  align-self: flex-start;
  margin: 0;
  padding: 4px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  color: var(--dash-status-pending-color, #e3b341);
  background-color: color-mix(in srgb, var(--dash-status-pending-color, #e3b341) 14%, transparent);
}

.cx-brm-progress--done {
  color: var(--dash-status-approved-color, #3a8a5f);
  background-color: color-mix(in srgb, var(--dash-status-approved-color, #3a8a5f) 14%, transparent);
}

@media (max-width: 768px) {
  /* Per-member date + upload stack rather than squeezing side by side, and the
     row's slot takes the full width under the name. */
  .cx-brm-term-inputs,
  .cx-brm-applyall {
    flex-direction: column;
    align-items: stretch;
  }

  .cx-brm-row-slot {
    flex: 1 1 100%;
    width: 100%;
  }
}

/* Pinned footer (§13): the committing action never scrolls with the body. */
.cx-brm .mf-modal-actions {
  flex-shrink: 0;
  padding-top: 14px;
  border-top: 1px solid var(--hr-dash-recent-divider, #eee8df);
}

/* ── Affected family list (<AffectedFamilyList>) ───────────────────────────── */
/* Named inside a removal/deletion confirmation, so it sits on WHITE (radius 12,
   1px hairline) even when nested inside a cream `.cx-brm-rows` row — cream on
   cream would bury it exactly where HR must not miss it. */
.cx-aff {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--hr-dash-recent-card-bg, #ffffff);
  border: 1px solid var(--hr-dash-recent-divider, #eee8df);
}

/* Standalone in a modal body, the list must not butt against whatever follows it. */
.cx-aff + .cx-term-form,
.cx-aff + .mf-modal-actions {
  margin-top: 16px;
}

/* Unconfirmed family: coral (§12 action-needed), never the destructive rust —
   nothing has gone wrong yet, HR simply needs to know the list is unknown. */
.cx-aff--warn {
  border-color: color-mix(in srgb, var(--brand-accent, #fb7a5c) 40%, transparent);
  background-color: color-mix(in srgb, var(--brand-accent, #fb7a5c) 8%, transparent);
}

.cx-aff-title {
  display: flex;
  align-items: center;
  gap: 7px;
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.cx-aff-copy {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Inner metadata surface (§8): cream, radius 12, no shadow. */
.cx-aff-rows {
  display: flex;
  flex-direction: column;
  margin-top: 2px;
  border-radius: 12px;
  background: var(--modal-total-bg, #faf6f0);
  overflow: hidden;
}

.cx-aff-row {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 9px 12px;
  min-width: 0;
}

.cx-aff-row + .cx-aff-row {
  border-top: 1px solid var(--hr-dash-recent-divider, #eee8df);
}

.cx-aff-row-name {
  margin: 0;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.cx-aff-row-meta {
  margin: 0;
  font-size: 11px;
  text-transform: capitalize;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Wraps, never clips — a blocked reason is a full sentence. */
.cx-aff-row-status {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--hr-dash-recent-td-color, #4a4640);
}

/* Retained-and-unchanged (§12 cancelled tone): stated, never dropped. */
.cx-aff-row-status--none {
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* ── Termination review: HR's submission + the third review option ─────────── */
/* The requested end date and the supporting document, shown above the decision
   buttons so the insurer decides on evidence they can actually see. Inner
   metadata surface (§8): cream, radius 12, no shadow. */
.tm-review-submission {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 12px;
  padding: 10px 12px;
  border-radius: 12px;
  background: var(--modal-total-bg, #faf6f0);
}

.tm-review-submission-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin: 0;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.tm-review-submission-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Values WRAP — a long document filename must never be clipped (§21). */
.tm-review-submission-value {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

.tm-review-submission-amend {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

/* HR's reason is a SENTENCE, not a field value, so it stacks under its own label
   instead of sharing a line with it. `pre-wrap` keeps the line breaks HR typed and
   `anywhere` guarantees a long unbroken run (a pasted reference, a URL) wraps
   rather than widening the 430px panel — values never clip (§21). */
.tm-review-submission-block {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin: 0;
}

.tm-review-submission-text {
  margin: 0;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  font-size: 12.5px;
  font-weight: 500;
  line-height: 18px;
  color: var(--hr-dash-recent-td-color, #1c1a17);
}

/* The shared §16 document row (`.tu-doc-list`/`.tu-doc-row*`) borrowed onto this
   cream submission block, so HR's evidence reads exactly like every other document
   in the app. The base family is tuned to sit EDGE-TO-EDGE inside a
   `.hr-dash-recent-card` — 20px side padding, and a bottom divider instead of its
   own outline — so on an already-padded inner surface it only needs re-insetting
   and the card edge §16 gives it. Nothing else about the row is touched: that is
   the point of reusing it rather than inventing a third document style. */
.tm-review-submission-docs {
  padding-bottom: 0;
}

.tm-review-submission-docs .tu-doc-row {
  padding: 10px 12px;
  border: 1px solid var(--tu-doc-row-border, #e5e7eb);
  border-radius: 12px;
  background: var(--hr-dash-recent-card-bg, #ffffff);
}

/* Received tint, expressed off the same approved token the row's own icon pad and
   its "Provided" pill already use, rather than the base family's flat literal. */
.tm-review-submission-docs .tu-doc-row--filled {
  background: color-mix(in srgb, var(--dash-status-approved-color, #059669) 6%, #ffffff);
}

/* =====================================================================
   Template library (`<TemplateLibraryModal>`) — the modal behind "Apply
   template" on the HR quote wizard's Requested Benefits step.

   Chrome is the shared `.modal-*` family (overlay / card / head / body /
   foot / btn / field / section-label), the search pill is the shared
   `.ins-pol-search`, the row overflow is `.ins-pol-action-btn` via
   `<RowActionsMenu>`, and the four list states are `<ListState>`'s
   `.tm-detail-empty*`. Only the three-pane frame and what sits inside the
   panes is new.

   Three panes, left to right: FOLDERS · TEMPLATES · PREVIEW. The preview
   pane carries the replace warning (§12 pending/gold tone — the action is
   consequential, not an error), because applying REPLACES the category's
   benefit table rather than merging into it.
   ===================================================================== */

/* Wider than `.modal-card--wide`'s 720px: three panes at 720 leave the
   preview too narrow to list benefits without wrapping every row. */
.modal-card--library {
  max-width: 1060px;
}

.tl-toolbar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 24px 0;
  flex-shrink: 0;
}

.tl-search {
  flex: 1;
  min-width: 0;
}

.tl-new-folder {
  flex-shrink: 0;
  gap: 8px;
}

/* §2 tinted surface on the destructive token: an action's refusal, rendered in
   the server's own words. */
.tl-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 12px 24px 0;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--dash-status-rejected-bg, rgba(220, 38, 38, 0.12));
  color: var(--dash-status-rejected-color, #b42318);
  font-size: 12.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.tl-banner svg {
  flex-shrink: 0;
}

/* The body owns no padding here — each pane pads itself, so the dividers
   between them run the full height. */
.tl-body {
  padding: 16px 0 0;
  gap: 0;
}

/* Once the panes are up the body stops being the scroller, so inside a capped
   card the three panes scroll INDEPENDENTLY and reading a long preview never
   scrolls the folder rail out of reach. The loading and error states have no
   panes and keep `.modal-body--scroll`'s own overflow — clipping a retry
   button on a short viewport would strand the reader. */
.tl-body--panes {
  overflow: hidden;
}

.tl-panes {
  display: grid;
  grid-template-columns: 216px 292px minmax(0, 1fr);
  flex: 1;
  min-height: 340px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eef2f7);
}

.tl-pane {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  padding: 14px 16px 18px;
  overflow-y: auto;
}

.tl-pane + .tl-pane {
  border-left: 1px solid var(--hr-dash-recent-row-border, #eef2f7);
}

/* §3: section label is always ink-400, never semantically coloured — the
   shared `.modal-section-label` is brand-tinted, so it is neutralised here. */
.tl-pane-label {
  color: var(--tu-muted-color, #6b7280);
  font-size: 9.5px;
  letter-spacing: 0.06em;
  flex-shrink: 0;
}

.tl-folder-list,
.tl-tpl-list {
  display: flex;
  flex-direction: column;
  gap: 2px;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* SELECTION SHAPE = `.sidebar-subitem`'s, not a new one.
   Both rails are vertical lists of scopes — "which folder am I in", "which
   template am I previewing" — which is the sidebar sub-item's job, so they
   consume that component's OWN tokens (radius, resting/active colour, active
   tint, hover) rather than picking values that looked right here. A theme
   retinting its sub-items retints this modal in the same breath.

   What that replaces, deliberately: a 3px accent rule on the left edge and an
   ad-hoc 10px radius — i.e. a fourth selector shape invented for one modal. The
   soft tint carries the selection on its own, exactly as it does in the nav.
   Hover is `--sidebar-item-bg-hover` for the same reason, NOT the row-hover
   token: these behave as nav, and the two hover tokens stay separate on purpose
   (a cream wash under a blue-50 active state is invisible). */
.tl-folder-row,
.tl-tpl-row {
  display: flex;
  align-items: center;
  gap: 4px;
  border-radius: var(--sidebar-item-radius, 8px);
  padding-right: 4px;
}

.tl-folder-row:hover,
.tl-tpl-row:hover {
  background-color: var(--sidebar-item-bg-hover, #f0f4ff);
}

.tl-folder-row--active,
.tl-tpl-row--active,
.tl-folder-row--active:hover,
.tl-tpl-row--active:hover {
  background-color: var(--sidebar-subitem-bg-active, #e8effc);
}

.tl-folder-row--active .tl-folder-btn,
.tl-folder-row--active .tl-folder-btn svg,
.tl-tpl-row--active .tl-tpl-name {
  color: var(--sidebar-subitem-color-active, #0a3373);
}

.tl-folder-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 9px 8px;
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
}

.tl-folder-btn svg {
  flex-shrink: 0;
  color: var(--tu-muted-color, #6b7280);
}

.tl-folder-name {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.tl-folder-count {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  color: var(--tu-muted-color, #6b7280);
}

/* ONE inline form for all three naming acts — create a folder, rename a folder,
   rename a template. Renaming is the same act to the user whichever kind of
   thing it lands on, so it is the same control in the same place, and a refusal
   always lands under the field that caused it instead of in the banner. */
.tl-name-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafc);
}

.tl-name-input {
  font-size: 12.5px;
}

.tl-name-form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

.tl-name-form-btn {
  padding: 8px 14px;
  font-size: 12.5px;
  min-height: 36px;
}

.tl-tpl-btn {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
  padding: 10px 8px;
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
}

.tl-tpl-name {
  font-size: 13px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

.tl-tpl-meta {
  font-size: 11px;
  color: var(--tu-muted-color, #6b7280);
  overflow-wrap: anywhere;
}

/* Authorship on a COMPANY-wide shelf — quieter than the counts beside it,
   because it disambiguates rather than describes. Absent entirely when the
   author or their name is null; never a placeholder identity. */
.tl-tpl-author {
  font-size: 10.5px;
  font-style: italic;
  color: var(--tu-muted-color, #6b7280);
  overflow-wrap: anywhere;
}

.tl-preview-title {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -0.2px;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

.tl-preview-meta {
  margin: -6px 0 0;
  font-size: 11.5px;
  color: var(--tu-muted-color, #6b7280);
}

/* The replace warning. Gold (§12 "pending/consequential"), not the rejected
   token: the action is legitimate, it is its REACH the reader must see before
   committing. */
.tl-replace-note {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--dash-status-pending-bg, rgba(234, 179, 8, 0.12));
  color: var(--dash-status-pending-color, #a16207);
  font-size: 12px;
  line-height: 1.5;
  font-weight: 600;
}

.tl-replace-note svg {
  flex-shrink: 0;
  margin-top: 1px;
}

.tl-preview-blank {
  margin: 0;
  font-size: 12.5px;
  color: var(--tu-muted-color, #6b7280);
}

.tl-preview-groups {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.tl-preview-group-label {
  margin: 0 0 6px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tu-muted-color, #6b7280);
}

.tl-preview-rows {
  display: flex;
  flex-direction: column;
  gap: 2px;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* §8 inner metadata cell: cream, radius 12, no shadow — the preview is a
   read-only listing inside a card, not a second card. */
.tl-preview-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 10px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafc);
}

/* The row IS a §8 inner metadata cell (`--modal-total-bg` above), so its text
   comes from that surface's own tones, NOT from the table family:
     - the VALUE reads `--modal-total-color`, the token whose declared meaning is
       exactly "text on the inner cell" (`.task-detail-meta-value` is the same
       key/value-on-cream shape and reads it too), so surface and text retint
       together;
     - the LABEL is the quiet half and had no existing token, so it gets its own
       (`--tl-preview-label-color`) rather than borrowing
       `--hr-dash-recent-td-color`, whose signed-off meaning is a table BODY
       CELL. Reuse where a meaning exists, add where it does not. */
.tl-preview-row-label {
  min-width: 0;
  font-size: 12px;
  color: var(--tl-preview-label-color, #4a4640);
  overflow-wrap: anywhere;
}

.tl-preview-row-value {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 700;
  color: var(--modal-total-color, #374151);
  text-align: right;
  overflow-wrap: anywhere;
}

.tl-loading {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 0 24px 18px;
}

.tl-loading-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 14px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafc);
}

@media (max-width: 768px) {
  .modal-card--library {
    max-width: 100%;
  }

  /* On a phone the topbar stacks and its right slot becomes a full-width row of
     its own, so "Cancel request" sits beside the bell with room to spare — but
     at its desktop ~33px it would not be a real tap target there. */
  .qd-topbar-cancel-btn {
    min-height: 44px;
  }

  /* One column: three 216px+ panes cannot share a 375px viewport. Each pane
     keeps its own divider, now above it rather than beside it, and the BODY
     becomes the single scroller again — three independent scroll areas stacked
     on a phone is three scroll traps. */
  .tl-body--panes {
    overflow-y: auto;
  }

  .tl-panes {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  .tl-pane {
    overflow-y: visible;
  }

  .tl-pane + .tl-pane {
    border-left: none;
    border-top: 1px solid var(--hr-dash-recent-row-border, #eef2f7);
  }

  .tl-toolbar {
    flex-wrap: wrap;
  }

  .tl-search {
    flex-basis: 100%;
  }

  .tl-new-folder,
  .tl-folder-btn,
  .tl-tpl-btn {
    min-height: 44px;
  }

  /* The prerequisite row's fix-in-place pill is deliberately small on desktop
     (a row action inside a 4-row checklist); on a phone it still has to be a
     real tap target. */
  .qd-dash-prereq-action {
    min-height: 44px;
    justify-content: center;
  }

  /* The strip's section line is label + sentence + chips + the section's one
     affordance, already wrapping at any width (`.qd-inputs-line`), and the
     affordance already holds the right edge wherever it lands. All a phone needs
     is the tighter gutter — the shell's 14px inset costs real measure at 375px,
     where the sentence is the widest thing on the screen. */
  .qd-inputs {
    padding: 12px;
  }

  /* An outstanding item is a link/button HR is meant to press, so it takes the
     44px floor like every other control — its desktop chip proportions are a
     density choice, not an exemption. (Its pill-shaped sibling gets the same
     floor from `.qd-dash-prereq-action` above.) */
  .qd-chip-gap {
    min-height: 44px;
    justify-content: center;
  }

  /* The blockers already drop under the CTA at this width (`.qd-blockers` holds a
     260px floor), so the button has the row to itself — span it, as the page's
     one primary action, and take the tap-target floor. Each item's own fix keeps
     its right edge; the pill gets its 44px from `.qd-dash-prereq-action` above. */
  .qd-start .hr-dash-recent-btn-primary {
    width: 100%;
    justify-content: center;
    min-height: 44px;
  }

  /* An open request's own action already wraps under its facts at this width
     (`.qd-request-row` wraps), so it has the row to itself — span it and take the
     tap-target floor, exactly as the page CTA above does. */
  .qd-request-row .hr-dash-recent-btn-primary {
    width: 100%;
    justify-content: center;
    min-height: 44px;
  }

  /* Every action in these blocks is a real control HR is meant to press, so it
     takes the 44px floor like the rest. `height` (not `min-height`) is what
     `--sm` sets, so it has to be released first or the floor loses to it. */
  .qd-block-action {
    height: auto;
    min-height: 44px;
    justify-content: center;
  }

  /* A RIGHT-HAND ACTION COLUMN STOPS MAKING SENSE AT 375px. A 140px action beside
     a wrapping label + digest + chip strip leaves the content ~150px, so the two
     columns become two ROWS: the content, then its action spanning the width
     under it. The desktop floor is released with them — at full width there is
     nothing left for it to do, and it would only fight the stretch. */
  .qd-inputs-line {
    flex-direction: column;
    align-items: stretch;
  }

  .qd-inputs .qd-block-action {
    min-width: 0;
  }

  /* The wizard footer's explanation and its button can't share a 375px line —
     stack them, and let the commit button span the row as a real tap target. */
  .qd-wizard-foot {
    flex-direction: column;
    align-items: stretch;
  }

  .qd-wizard-foot .hr-dash-recent-btn-primary {
    justify-content: center;
    min-height: 44px;
  }

  /* The rail's two steps stack rather than truncate — the label is the only thing
     saying what the next step IS. */
  .qd-wizard-rail {
    gap: 8px;
  }

  /* The search pill and the count can't share a 375px line — the count drops
     under the full-width search rather than squeezing it to nothing. */
  .qd-fix-toolbar .ins-pol-search {
    flex-basis: 100%;
  }

  /* The fix-in-place list is a table now, so 375px is `.hr-dash-recent-table-wrap`'s
     horizontal scroll — NOT a per-row stack. Stacking is what a card list does; a
     work list's whole value is that the columns stay in line, and the four here are
     each too content-dense to survive being folded into one. Only the padding
     tightens, so more of the row fits before the scroll starts. */
  .qd-fix-table {
    --hr-dash-recent-th-padding: 9px 14px;
    --hr-dash-recent-td-padding: 10px 14px;
  }

  /* `--sm` sets `height`, so the 44px tap floor has to release it first or it
     loses — same fix, same reason, as `.qd-block-action`. */
  .qd-fix-action-btn {
    height: auto;
    min-height: 44px;
  }

  .qd-fix-row-btn {
    min-height: 44px;
  }
}

/* ─────────────────────────────────────────────
   `/insurer/quotes/[id]` — the insurer's DECISION screen (`.iqd-*`).

   Everything the shared families already cover is theirs: the two columns are
   `.qro-layout` / `.qro-main` / `.qro-side` (the same shape the HR submission
   journey wears, so the two sides of one quote read as one product), the cards
   are `.hr-dash-recent-card`, the offers table is `<DataTable>`'s
   `.hr-dash-recent-table*`, the status pills are `.dash-status--*` and the
   population tiles are `.qd-lives-stat*`. Only the hero band, the selectable
   group row, the rail's fact list and one width concession for the population
   card now that it lives in the rail (`.iqd-pop-card`) are new.

   Column contents, since the classes alone no longer say it: `.qro-main` holds
   the hero, the ask, your offers and the comparator; `.qro-side` holds the
   decision panel and the insured population beneath it. The band is INSIDE the
   left column so the rail starts on the grid's first row — see `.iqd-hero`.
   ───────────────────────────────────────────── */

/* ── The hero band ──
   The one dark surface on the screen: it states the ask, and it carries no
   action (an offer is per group — see the page header for why the mockup's
   single "Build the offer" is gone). Navy fill, so every token inside it is a
   dedicated on-navy one rather than a repaint of the page's ink scale.

   IT NOW SITS INSIDE `.qro-main`, not above the two columns, and its geometry is
   deliberately SHORT. The decision rail is the thing an insurer came here to
   act on, so it has to start at the very top of the page; a full-width band
   above the grid pushed it down by the band's whole height. The band therefore
   has to earn the vertical space it takes from the left column: chip and
   headline share one row, the padding is a compact 14/20 and the facts sit
   tight beneath. */
.iqd-hero {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 9px;
  padding: 14px 20px;
  border-radius: 16px;
  background-color: var(--iqd-hero-bg, #0a3373);
}

/* Countdown and headline on ONE row — two stacked rows cost a whole line of
   height for a chip that is shorter than the text beside it. It wraps, because
   at rail-narrow widths the headline is a full sentence and must not be
   compressed to a sliver to keep the chip company. */
.iqd-hero-lead {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 14px;
  min-width: 0;
}

/* The response countdown chip is `.respond-chip` (see the shared block near the
   end of this file) — the hero and the offer wizard's header render the same
   `<RespondByChip>`, so the same deadline cannot wear two chips. `.iqd-hero-chip`
   was that chip while the hero was its only consumer; it is retired, not moved. */

.iqd-hero-title {
  margin: 0;
  font-size: 17px;
  line-height: 1.3;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--iqd-hero-title-color, #ffffff);
}

/* Stacked label/value pairs rather than one long sentence, so a narrow column
   wraps them into aligned facts instead of a ragged run of text. */
.iqd-hero-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 22px;
  margin: 0;
  padding: 0;
}

.iqd-hero-fact {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

/* §3 section label — muted, never semantically coloured. On navy the muted
   tone is blue-200, not the page's ink-400. */
.iqd-hero-fact-label {
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--iqd-hero-label-color, #b9c9ea);
}

.iqd-hero-fact-value {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--iqd-hero-value-color, #ffffff);
}

/* ── The group list (`<InsurerCategoryList>`) ──
   Entity selection, not a Charte §7 selector: §7's two shapes answer "swap the
   view" and "filter one list", and this is neither. It is the codebase's
   established "a list selects, a panel reflects" row — the §12 selected
   treatment (blue-400 left rule + a 6% wash), same reading as `.tm-row--selected`. */
.iqd-cat-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.iqd-cat-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  width: 100%;
  padding: 13px 16px;
  border: none;
  border-left: 3px solid transparent;
  border-radius: 12px;
  background-color: transparent;
  text-align: left;
  cursor: pointer;
}

.iqd-cat-row:hover {
  background-color: var(--hr-dash-recent-row-hover-bg, #fafafa);
}

.iqd-cat-row--selected,
.iqd-cat-row--selected:hover {
  border-left-color: var(--iqd-cat-row-selected-rule, #4374f3);
  background-color: var(--iqd-cat-row-selected-bg, rgba(67, 116, 243, 0.06));
}

/* The loading placeholder wears the row's geometry without its affordance. */
.iqd-cat-row--skeleton {
  cursor: default;
}

.iqd-cat-row-main {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.iqd-cat-row-name {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.iqd-cat-row-meta {
  font-size: 11.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* The selected group's detail sits UNDER its row, inside the selection, so the
   plan summary can never look like it belongs to the row below it. */
.iqd-cat-detail {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 2px 0 6px 3px;
  padding: 14px 16px;
  border-radius: 12px;
  background-color: var(--modal-total-bg, #faf6f0);
}

.iqd-cat-plan {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 12px 20px;
  margin: 0;
}

.iqd-cat-plan-item {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.iqd-cat-plan-label {
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Wraps, never clips (§21) — an area of cover can be a long list of regions. */
.iqd-cat-plan-value {
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* A navigation, so it keeps its arrow — the quotes-area arrow rule bans the
   glyph on panel/modal openers, which this is not. */
.iqd-cat-brief-link {
  align-self: flex-start;
  padding: 0;
  border: none;
  background: none;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--brand-secondary, #4374f3);
  cursor: pointer;
}

.iqd-cat-brief-link:hover {
  text-decoration: underline;
}

/* ── Population tiles ──
   `.qd-lives-stat*` supplies the tile; only the container is new, because the
   existing one is a five-across flex with no wrap and this card has five
   tiles. `auto-fit` is what lets the same five tiles read as five across the
   left column and as two or three across the rail. */
.iqd-pop-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
  gap: 12px;
}

/* The population card lives in the ~300px RAIL, under the decision panel: it
   describes the selected group, and so does everything else in that column.
   The shared `.hr-dash-recent-head` is title-left / action-right, which at that
   width collides "Download anonymised list" into the title — so THIS card
   stacks its head and gives the button the full row. Scoped to `.iqd-pop-card`
   rather than fixed in the shared rule, which every wide card on the app
   depends on. */
.iqd-pop-card .hr-dash-recent-head {
  flex-direction: column;
  align-items: stretch;
  gap: 10px;
}

.iqd-pop-card .hr-dash-recent-btn-outline {
  width: 100%;
  justify-content: center;
}

/* ── The decision rail (`<InsurerDecisionPanel>`) ── */
.iqd-panel {
  display: flex;
  flex-direction: column;
}

.iqd-panel-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
  padding: 18px 20px 14px;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

.iqd-panel-title {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.iqd-facts {
  display: flex;
  flex-direction: column;
  gap: 12px;
  margin: 0;
  padding: 16px 20px;
}

.iqd-fact {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

.iqd-fact-label {
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.iqd-fact-value {
  margin: 0;
  font-size: 12.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* The committing action is pinned to the bottom of the rail, above a divider —
   §13's footer rule, applied to a rail rather than an overlay. */
.iqd-panel-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: auto;
  padding: 16px 20px;
  border-top: 1px solid var(--hr-dash-recent-row-border, #eee8df);
}

/* Both buttons share one width, so neither label decides how big it is. */
.iqd-panel-btn {
  width: 100%;
  justify-content: center;
}

.iqd-panel-note {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* ── The offers table's own cells ── */
.iqd-offer-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.iqd-offer-premium {
  font-size: 13px;
  font-weight: 700;
  white-space: nowrap;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.iqd-offer-updated {
  font-size: 12.5px;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.iqd-offer-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

@media (max-width: 768px) {
  /* Same compact geometry as the desktop band, one step tighter — at 375px the
     lead row has already wrapped the chip above the headline, so the band is
     two rows tall whatever the padding says. */
  .iqd-hero {
    padding: 12px 16px;
  }

  .iqd-hero-title {
    font-size: 16px;
  }

  /* The row's status pill drops under the name rather than squeezing it to a
     sliver — the group's NAME is what the reader is choosing between. */
  .iqd-cat-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    min-height: 44px;
  }

  .iqd-cat-brief-link {
    min-height: 44px;
  }

  .iqd-panel-btn {
    min-height: 44px;
  }

  /* The population export owns a full row of its own now, so it has to meet the
     tap floor like every other button on a phone. `--sm` sets `height`, so that
     has to be released first or it wins — same fix, same reason, as
     `.qd-fix-action-btn`. */
  .iqd-pop-card .hr-dash-recent-btn-outline {
    height: auto;
    min-height: 44px;
  }

  /* Stacked, the offer's two controls spread across the row they now own. */
  .iqd-offer-actions {
    justify-content: flex-end;
    flex-wrap: wrap;
  }

  /* `--sm`/`--icon` set a hard 32px `height`, so the 44px tap floor has to
     release it first or it loses — same fix, same reason, as
     `.qd-fix-action-btn`. */
  .iqd-offer-actions .hr-dash-recent-btn-primary--sm,
  .iqd-offer-actions .hr-dash-recent-btn-outline--sm,
  .iqd-offer-actions .hr-dash-recent-btn-outline--icon {
    height: auto;
    min-height: 44px;
  }

  .iqd-offer-actions .hr-dash-recent-btn-outline--icon {
    min-width: 44px;
  }
}

/* ─────────────────────────────────────────────
   `<RespondByChip>` — the response-deadline countdown, on both its surfaces.

   Two screens state the same deadline: the insurer's decision hero (navy band)
   and the offer wizard's header (cream). One component, one class family, so
   "12 days to respond" cannot come to mean two arithmetics — the wording and the
   urgency threshold are `respondByView`'s, this only paints them. Urgent is
   coral/rust, §1's brand accent for "action needed": a STATE, never a control.
   ───────────────────────────────────────────── */
.respond-chip {
  display: inline-flex;
  align-items: center;
  padding: 5px 12px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  background-color: var(--respond-chip-bg, #f1f5fe);
  color: var(--respond-chip-color, #0a3373);
}

.respond-chip--urgent {
  background-color: var(--respond-chip-urgent-bg, #fff7ed);
  color: var(--respond-chip-urgent-color, #9a3412);
}

.respond-chip--on-navy {
  background-color: var(--respond-chip-navy-bg, rgba(255, 255, 255, 0.14));
  color: var(--respond-chip-navy-color, #ffffff);
}

.respond-chip--on-navy.respond-chip--urgent {
  background-color: var(--respond-chip-navy-urgent-bg, rgba(251, 122, 92, 0.22));
  color: var(--respond-chip-navy-urgent-color, #ffa586);
}

/* ─────────────────────────────────────────────
   `/insurer/quotes/[id]/offer/new` — the four-step offer wizard (`.obw-*`).

   The shell is NOT here: `.mf-root.mfw-root` + `<DashTopbar>` +
   `<WizardStepRail>` (`.mfw-steps`) is the app's shared wizard chrome, the same
   one the census importer and the add-member wizard wear. This wizard is the
   one WITHOUT the pinned `.mfw-footer` — that bar spent ~73px of every viewport
   on two buttons and a status line, on a screen whose comparator had about a
   tenth of the room, so all four steps are two-column and the navigation lives
   at the foot of each step's right-hand panel (`.obw-nav`). The other two
   wizards keep their footer; `.mfw-footer` is untouched.

   What follows is what those four steps need and nothing else has: the two
   columns and the panel skeleton, the cover choice, the required marker, and
   the numeric answer field with its rendered unit.
   ───────────────────────────────────────────── */

/* The Build step supplies its own `.dash-body` rhythm per section, so the
   scroller keeps the wizard gutter rather than a second inner padding. */
.obw-step-pending {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 200px;
}

/* A tab-strip label: its status dot and its name. The per-tab benefit icon that
   used to sit here is gone — a dot and a glyph competing in one 15px slot made
   neither readable, and the dot is the one carrying news. */
.obw-tab-head {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  /* "In-Patient" and "Out-Patient" broke across two lines on their own hyphen,
     which made a one-word tab look like two. The strip scrolls; the label does
     not fold. */
  white-space: nowrap;
}

/* Answered / outstanding / nothing-asked-for, per section. */
.obw-tab-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  border-radius: 999px;
}

.obw-tab-dot--gap {
  background-color: var(--obw-tab-dot-gap, #fb7a5c);
}

.obw-tab-dot--done {
  background-color: var(--obw-tab-dot-done, #3a8a5f);
}

.obw-tab-dot--none {
  background-color: var(--obw-tab-dot-none, #e9e4dc);
}

/* ── The section list ──
   CHROME. Bare wrapping chips on the page surface — no card, no heading, no
   legend row, no padding of its own. It briefly had all four and they cost
   ~105px on the screen whose entire complaint is that the comparator has no
   room: the heading ("The N sections of the request") said what N section names
   sitting in a row already say, and the legend decoded two dots that now carry
   a `title` each.

   A bare pill row on the page surface is not a new treatment — it is exactly
   what `.ins-pol-filterbar` does on every list screen.

   `.obw-tabs-row` and its `.tabs-underline` scroller went earlier, with the
   single-row strip they styled: eleven sections behind a horizontal scroller
   put most of them off screen, which is what the status dots exist to prevent.
   Wrapping keeps all of them visible; metadata scale keeps that cheap. */
.obw-sections {
  min-width: 0;
}

/* The one thing the shared pill row does not do on its own: wrap. Everywhere
   else a pill row is three or four filters on one line. */
.obw-sections .ins-pol-segs {
  flex-wrap: wrap;
  gap: 6px;
}

/* Chip scale — a navigator, not content. The shared pill's 9/16 at 12.5px is a
   page filter's size; eleven of those wrap into a block taller than most of the
   table. Tight leading and small gaps, nothing decorative. */
.obw-sections .ins-pol-seg {
  padding: 4px 10px;
  font-size: 11.5px;
  border-radius: 999px;
}

.obw-sections .obw-tab-head {
  gap: 6px;
}

.obw-sections .filter-tabs-count {
  margin-left: 5px;
}

.obw-tab-card {
  min-width: 0;
}

.obw-section-label {
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
  margin: 0;
}

/* A benefit row's cells are wildly different heights — a one-line label against
   a cover toggle that reveals two fields — so they hang from the top rather
   than centring against the tallest. */
/* A benefit row's cells are wildly different heights — a one-line label against
   a cover toggle that reveals two more fields — so they hang from the TOP and
   share a baseline there. `vertical-align: top` alone was not enough: the shared
   `.hr-dash-recent-td`'s 14px/24px is a list-row rhythm, and at forty benefits
   whose tallest cell is already ~100px it turned every row into ~200px with
   three lines of empty space beside the name. 9px/14px here; the answer box
   supplies the row's height and nothing else pads it out.

   `padding-top` is 11px rather than 9 so the first line of TEXT in the name and
   ask columns sits on the same optical line as the first control in the answer
   box, which carries a 4px inset of its own. */
.obw-table .hr-dash-recent-td {
  vertical-align: top;
  padding: 11px 14px 9px;
  line-height: 1.35;
}

/* The group separators inside that one table (§10's row divider still applies
   between them, so they need no rule of their own). */
.obw-group-cell {
  padding: 14px 14px 4px;
}

/* ── The column model ──
   Three fixed columns and ONE that absorbs the slack, so a row reads the same
   way at every width: the ask, the answer and the verdict never move, and the
   benefit name gets whatever is left. It used to be the other way round — every
   column content-sized, which handed the widest cell (a 240px answer control)
   the room and left "Surgical Appliances and/or Medical Appliances" wrapping to
   four lines in a column narrower than the toggle beside it.

   `width: 100%` on the name column is the absorbing idiom under
   `table-layout: auto`, not a fractional split: the other three are px, so it
   simply takes the remainder. §10's "content-sized, never fractional grow" is
   about columns that RESIZE with their content, which is exactly what this
   stops. */
.obw-cell-name {
  /* §10: first column Bold ink-900. */
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  width: 100%;
  min-width: 180px;
}

.obw-cell-asked {
  width: 176px;
}

.obw-benefit-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  min-width: 0;
}

/* The "Required" pill that used to hang off the benefit label is gone: the
   table now carries a PRIORITY column reading the company's own must-have /
   nice-to-have flag, which says the same thing and one thing more. Its two
   tokens outlived it — `.obw-recap-doc--missing` reads them. */

/* §12 "action needed": a 3px rule and nothing else. An outstanding row is one
   the client asked for and this offer has not answered yet. */
.obw-row--outstanding > .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 0 var(--obw-outstanding-rule, #fb7a5c);
}

.obw-ask-cell {
  display: flex;
  flex-direction: column;
  gap: 3px;
  min-width: 0;
}

/* The ask and its priority read as ONE statement — the priority is a property
   of the ask (`quote_benefits.optional`), and it had a whole column of its own
   to say one word two columns away from the thing it qualified. */
.obw-ask-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  min-width: 0;
}

.obw-ask {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--obw-ask-color, #0a3373);
  word-break: break-word;
}

.obw-ask--absent {
  font-weight: 400;
  font-style: italic;
  color: var(--obw-ask-absent-color, #8d8579);
}

/* What KIND of answer the ask wants, under the ask itself (§3 metadata). */
.obw-ask-detail {
  font-size: 11px;
  font-weight: 400;
  color: var(--obw-ask-absent-color, #8d8579);
  word-break: break-word;
}

.obw-cell-offer {
  width: 248px;
  min-width: 248px;
}

/* ── One answer BOX, whatever the control ──
   A closed list, a cover toggle, a number with a unit and a free-text box are
   four shapes; they used to be four WIDTHS too, so the column's left edge was
   the only thing the eye could follow down the table. Every one of them now
   fills one bordered box of the same width and the same resting height, and the
   BOX carries the border — the controls inside it are borderless, so the column
   reads as one column of equal cells rather than as a form dropped into a table.

   The box grows only where the answer genuinely has more parts (a covered
   benefit revealing its value and its limit); it never changes width, and an
   unanswered row is exactly as tall as an answered one.

   Its edge states the VERDICT — see `answerSlotClass`. Same tones as the row
   rules, one fact at two scales, decoded once by `<OfferBenefitLegend>`.

   Replaces `.obw-offer-stack` and `.obw-offer-detail`, which were the yes/no
   arm's private layout — the value and select arms had none, which is precisely
   how the three drifted apart. */
.obw-answer {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  width: 100%;
  min-width: 0;
  /* No `min-height`: every control inside is 28px, so a one-control box is
     28 + 8 padding + 3 border = 39px by construction and they are uniform
     without a magic number that could drift from the controls. */
  padding: 4px 5px;
  border-radius: 10px;
  border: 1.5px solid var(--obw-answer-border, #e5e7eb);
  /* §8's inner metadata cell — CREAM, and deliberately not `--field-bg`.
     `--field-bg` IS cream-50 on Heale, so a cream-filled control inside a
     `--field-bg` box resolved to one surface at one value: the fields did not
     look faint, they were the same colour as the thing containing them and
     simply disappeared. The relationship is inverted now — cream box, WHITE
     controls — which is the only arrangement that also gives the cover toggle a
     boundary, since it was invisible for the same reason. */
  background-color: var(--obw-fact-bg, #f9fafb);
}

/* ── One part of an answer: its caption and its control ──
   A caption appears ONLY where a cell holds more than one value — which is
   exactly the covered-with-a-limit case, and exactly where the failure was: two
   filled number fields with nothing saying which was which, because both had
   been relying on a placeholder that disappears the moment you type. The
   caption does not disappear. A single-control cell gets none: there is nothing
   to disambiguate, and a caption on every row would cost width on all forty. */
.obw-answer-part {
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

/* A clear STEP BELOW the column header, not another of it. `YOUR ANSWER`,
   `VALUE` and `LIMIT` were three uppercase micro-labels at the same weight
   stacked vertically, so the cell's captions competed with the column they sit
   under. Sentence case at 10px/600 leaves the uppercase +6% device to the
   header alone. */
.obw-answer-cap {
  flex: 0 0 auto;
  width: 34px;
  font-size: 10px;
  font-weight: 600;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.obw-answer-part > .obw-value,
.obw-answer-part > .obw-value-num,
.obw-answer-part > .obw-cell-input {
  flex: 1 1 auto;
  min-width: 0;
}

/* The limit qualifies the answer above it rather than being one. Stated in the
   type scale, which costs no height — a divider or an indent would. */
.obw-answer-part--sub .obw-cell-input {
  font-size: 11.5px;
  font-weight: 400;
}

/* The five verdicts that carry news. `meets`, `pending`, `gap` and `not_asked`
   keep the neutral edge on purpose — the expected case is not news, and a wall
   of coloured boxes over forty rows signals nothing.

   There is deliberately NO "below request" tone: ranking two differing terms
   needs a direction of generosity per benefit and nothing carries one, so a
   differing term reads `differs` and gets the same amber edge either way. */
.obw-answer--differs,
.obw-answer--unstated,
.obw-answer--uncomparable {
  border-color: var(--obw-verdict-differs-color, #a16207);
}

.obw-answer--blocking_gap {
  border-color: var(--obw-verdict-blocking-color, #c2542e);
}

.obw-answer--bonus {
  border-color: var(--obw-verdict-bonus-color, #4374f3);
}

/* ── A field sized to the TABLE CELL it sits in ──
   §9's standard field is 44px, which is right for a form and wrong here: these
   controls sit inside a 13.5px benefit row, stacked with a 34px
   `.obw-cover-btn` in the very same slot, so a 44px box made one answer slot
   read at two scales and the row read as a form dropped into a table.

   Geometry is LOCAL for exactly the reason `.census-sheet-input` (32px cell)
   and `.mf-doc-select` (30px inline row control) keep theirs — the box dictates
   the size. 34px is not a fresh guess: it is the cover toggle's own height, so
   the two line up when they stack. Everything that is NOT geometry — fill,
   border, text, placeholder, focus, disabled — still comes from the field
   contract, so a brand restyles this control by restyling every other one. */
.obw-cell-input {
  width: 100%;
  height: 28px;
  padding: 0 8px;
  /* Concentric with the box: its 10px less the 1.5px border and 4px padding
     leaves ~4.5px of inner curve, so anything rounder than this bulges against
     a corner it cannot fill. */
  border-radius: 6px;
  /* FILLED, not outlined — a second border inside the box would be two edges
     saying one thing, and the box's edge carries the verdict. The affordance is
     the fill: WHITE on the box's cream. It was cream on cream for one round,
     which is no affordance at all. Focus still lands on the control. */
  border: 1px solid transparent;
  background-color: var(--obw-control-bg, #ffffff);
  font-size: 12.5px;
  color: var(--field-color, #374151);
  outline: none;
  transition: border-color 0.12s;
}

.obw-cell-input::placeholder {
  color: var(--field-placeholder-color, #9ca3af);
}

.obw-cell-input:focus {
  border-color: var(--field-focus-border, var(--brand-secondary, #4374f3));
}

.obw-cell-input:disabled {
  background-color: var(--field-disabled-bg, var(--field-bg, #ffffff));
  color: var(--field-disabled-color, var(--field-color, #374151));
  opacity: var(--field-disabled-opacity, 0.6);
}

/* A one-line note under an answer control — today the optional start date's
   "leave empty and the client's own date stands". Metadata scale, so it reads
   as guidance rather than as a second value in the cell. */
.obw-answer-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.4;
  color: var(--obw-ask-absent-color, #8d8579);
}

.obw-answer-secondary {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

/* Cover / decline. Segmented, sharing one hairline, with the SELECTED half
   carrying the tone — green for covered, neutral ink for declined. Both halves
   are equal and fill the slot: a pair of buttons whose widths are set by their
   own labels is the same "every control a different size" problem one level
   down. NEITHER half is filled until the insurer answers — see `<CoverChoice>`. */
.obw-cover {
  display: flex;
  width: 100%;
  /* 6px, not the pill's 999px and not the 7px that replaced it. The box's 10px
     less its 1.5px border and 4px padding leaves ~4.5px of inner curve; 7px was
     still rounder than the corner it sat in, which is why the selected half
     kept reading as pasted on rather than seated. Same value as the fields
     beside it, so every shape in the cell is concentric with the box. */
  border-radius: 6px;
  overflow: hidden;
  /* Same fill language as the fields beside it: white on the box's cream. While
     it was cream-on-cream the container was invisible, so the selected half read
     as a loose shape with two rounded corners and two square ones — which is
     what "the toggle doesn't sit in its box" actually was. */
  border: 1px solid transparent;
  background-color: var(--obw-control-bg, #ffffff);
}

.obw-cover-btn {
  flex: 1 1 0;
  min-width: 0;
  padding: 0 10px;
  min-height: 28px;
  border: none;
  background-color: transparent;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
  cursor: pointer;
  color: var(--obw-cover-color, #6b7280);
}

.obw-cover-btn:disabled {
  cursor: default;
  opacity: 0.45;
}

.obw-cover-btn--covered {
  background-color: var(--obw-cover-covered-bg, #3a8a5f);
  color: var(--obw-cover-covered-color, #ffffff);
}

.obw-cover-btn--declined {
  background-color: var(--obw-cover-declined-bg, #8d8579);
  color: var(--obw-cover-declined-color, #ffffff);
}

/* The numeric answer. The insurer types a NUMBER; the unit is ours to render,
   which is the whole reason the stored value can be canonical. */
.obw-value {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.obw-value-num {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.obw-value-input {
  flex: 1 1 auto;
  min-width: 0;
}

.obw-value-unit {
  flex-shrink: 0;
  font-size: 12.5px;
  font-weight: 700;
  color: var(--obw-unit-color, #8d8579);
}

.obw-value-sentinels {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

/* A sentinel is a legitimate ANSWER (Not applicable, Other, Precise), so it sits
   beside the field as a choice rather than being forced through a number — but
   BELOW the primary answer in weight. Selected, it used to read as a solid navy
   §7 pill, which made "Not applicable" the heaviest object on a page whose
   primary action is a blue-400 button: a sentinel outshouting the CTA. It is a
   quiet outline chip now, and selection tints it rather than inverting it. */
.obw-value-sentinel {
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--obw-sentinel-border, #e5e7eb);
  background-color: transparent;
  font-size: 11px;
  font-weight: 600;
  color: var(--obw-sentinel-color, #8d8579);
  cursor: pointer;
  white-space: nowrap;
}

.obw-value-sentinel:disabled {
  cursor: default;
  opacity: 0.45;
}

.obw-value-sentinel--selected {
  background-color: var(--obw-sentinel-selected-bg, #f3f4f6);
  border-color: var(--obw-sentinel-selected-border, #d1d5db);
  color: var(--obw-sentinel-selected-color, #1c1a17);
}

/* A stored value that is neither a quantity nor one of the catalogue's own
   choices — see `<OfferBenefitValueField>`. Dashed, because it is a value to
   clear rather than a choice to make. */
.obw-value-sentinel--legacy {
  border-style: dashed;
  font-style: italic;
}

/* The autosave line, at the foot of the step's panel (`.obw-nav`) since the
   wizard footer was retired — it kept its size and its tones, and only moved.
   The guarantee must stay visible wherever it lives: the layout may never imply
   that leaving could lose work. Only the two states that are NEWS carry a tone;
   the standing promise is meta text like any other. It supplies its own type
   scale now that no `.mfw-footer-info` sits under it. */
.obw-save {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 16px;
  font-size: 11.5px;
  color: var(--dash-stat-label-color, #7d93b3);
}

.obw-save--ok {
  color: var(--obw-save-ok-color, #3a8a5f);
}

.obw-save--error {
  color: var(--obw-save-error-color, #c2542e);
}

/* ── Every step's two columns ──
   Work on the left, the step's own panel on the right. It started as the Build
   step's score column and became the wizard's layout when the pinned
   `.mfw-footer` was retired: navigation and the autosave line live at the foot
   of this panel now, on all four steps.

   The right column is a fixed 340px rather than a fraction: a panel that grows
   with the viewport just stretches its own whitespace, and the benefit table is
   the half that actually wants the room. */
.obw-split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 340px;
  align-items: start;
  gap: 18px;
}

.obw-main {
  display: flex;
  flex-direction: column;
  gap: 18px;
  min-width: 0;
}

/* NOT STICKY, and deliberately so — the panel is ONE FLAT BLOCK in normal flow
   that ends with Continue, and nothing inside it ever scrolls.

   It was briefly sticky with a `max-height` and an inner `.obw-aside-body`
   scroller, which is the only way a pinned panel can also guarantee its own
   footer stays reachable. That bought the score following you down a long
   section, and it cost a scrollbar inside a card plus a rule and a button that
   read as detached from the clipped content above them.

   The two honest ways out were "stop sticking" and "stick only as long as it
   fits", and the second is not expressible: `position: sticky` has no
   height-aware form, so a panel taller than the scrollport pins its top and
   puts its own bottom permanently below the fold — Continue unreachable, which
   is the one unacceptable outcome. `.qro-side--static` is the same call already
   taken for `/hr/quotes/new`'s rail, for the same reason.

   So: on a short viewport the panel simply scrolls away with the page like any
   other block, and Continue is reached by scrolling to the end of it. The cost
   is that the score no longer follows the benefit table down. */
.obw-side {
  align-self: start;
  min-width: 0;
}

/* Head · body · nav, stacked. A column flex so the nav is last and the divider
   above it lands under the body's real end — no height is imposed on any of the
   three, and no box here is a scroll container. */
.obw-aside {
  display: flex;
  flex-direction: column;
}

.obw-aside-head {
  padding: 16px 18px 0;
}

.obw-aside-body {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 12px 18px 16px;
}

.obw-aside-lead {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--obw-ask-absent-color, #8d8579);
}

.obw-aside-count {
  display: block;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* ── The readiness checklist (Describe / Document / Validate) ──
   Same three-zone row as the quote wizard's `.qw-side-step`: state glyph, label
   over an optional terse hint. Not a button here — these rows describe fields
   on the screen beside them, so there is nowhere to jump to. */
.obw-check-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.obw-check {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
}

.obw-check-icon {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  margin-top: 1px;
  border-radius: 999px;
}

.obw-check--done .obw-check-icon {
  background-color: var(--obw-verdict-meets-bg, #ecfdf5);
  color: var(--obw-verdict-meets-color, #3a8a5f);
}

.obw-check--todo .obw-check-icon {
  background-color: var(--obw-verdict-differs-bg, #fefce8);
  color: var(--obw-verdict-differs-color, #a16207);
}

.obw-check-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.obw-check-label {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  word-break: break-word;
}

.obw-check-hint {
  font-size: 11px;
  line-height: 1.4;
  color: var(--obw-ask-absent-color, #8d8579);
  word-break: break-word;
}

/* ── The step's navigation, at the foot of its panel ──
   What the retired `.mfw-footer` carried, in a column: the primary first
   (Continue, or Validate's submit), the way back under it, the autosave line
   last. Both buttons are full width, so a longer label can never make the pair
   different sizes — the same rule `.qw-side-cta` / `.qw-side-later` follow. */
.obw-nav {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 14px 18px;
  border-top: 1px solid var(--hr-dash-recent-divider, #eee8df);
}

/* Side by side, not stacked. Two 44px buttons in a column cost 96px of a panel
   whose whole problem was Continue sitting below the fold; at 340px they fit on
   one row with room to spare. `row-reverse` puts the primary on the right with
   the way back to its left (§9's Cancel-then-primary), and `flex: 1 1 0` keeps
   the pair equal so a longer label can never resize one of them. */
.obw-nav-actions {
  display: flex;
  flex-direction: row-reverse;
  gap: 8px;
}

.obw-nav-btn {
  flex: 1 1 0;
  min-width: 0;
  justify-content: center;
  min-height: 44px;
}

/* ── "What they asked for" ──
   The SAME card as the section list and the benefit table — white, §8's radius
   and shadow, no border — so the column reads as three of one thing rather than
   a tinted strip between two cards. What it does not have is a head: five
   facts, two counts and a link used to cost ~190px above the comparator, and
   the leading phrase does the head's job in a fraction of it. */
.obw-brief {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px 14px;
}

.obw-brief-line {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px 12px;
  min-width: 0;
}

/* The whole of row one: who asked, and for how much. */
.obw-brief-who {
  margin: 0;
  min-width: 0;
  font-size: 11.5px;
  line-height: 1.4;
  color: var(--obw-ask-absent-color, #8d8579);
}

/* Row two, and one row of EQUALS. The facts are a set of the same kind of
   thing, so they share the row evenly instead of each sizing to its own text.
   `minmax` rather than `flex: 1 1 auto` so a short value cannot collapse its
   cell to the label's width, and 104px so all five still fit on one row in the
   left column rather than folding onto a second. */
.obw-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(104px, 1fr));
  gap: 5px;
  margin: 0;
  min-width: 0;
}

/* STACKED: label above, value below. Side by side at 11.5px the eye had to hunt
   for the boundary between a 9.5px uppercase label and the value abutting it;
   stacked, the label reads as a caption and the value as the fact.

   It costs ~13px a cell, which this block does NOT have to give — it has been
   through two rounds about its height. Paid for by tightening everything around
   it rather than by growing: the card's own padding (10→8), the row gap (6),
   the cell padding (3→2), the label's leading (1.15) and, in the row above,
   swapping the 34px template select for a 30px button. Net effect on the block
   is about −1px. §8's inner metadata cell throughout: cream, radius, no shadow
   of its own. */
.obw-fact {
  display: flex;
  flex-direction: column;
  gap: 0;
  min-width: 0;
  padding: 2px 8px 3px;
  border-radius: 8px;
  background-color: var(--obw-fact-bg, #f9fafb);
}

.obw-fact-label {
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.obw-fact-value {
  margin: 0;
  font-size: 11.5px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  word-break: break-word;
}

.obw-brief-strong {
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

/* The strip's ways IN — the template picker and the link out to the request —
   travel together at the far end of the line. */
.obw-brief-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-left: auto;
  flex-shrink: 0;
}

.obw-brief-link {
  flex-shrink: 0;
}

/* The picker sizes to the strip, not to a cell: content-width with a ceiling,
   so a long template name cannot push the link off the line. Only its width
   differs from the base — the height, radius and type scale are shared, which
   is the point of borrowing the class rather than minting a second one. */
/* The two template actions on the brief line: take one from the library, save
   this offer as one. BOTH are secondary buttons.

   The library one was a `<select>` with a glyph in it for one round. A select
   reads as "set a field" — reversible, no consequence — and applying a template
   overwrites every answer and the premium, so the shape was making the wrong
   promise. It opens `<ApplyOfferTemplateModal>` instead, which states that
   consequence and carries the commit. `.obw-brief-lib`, `.obw-brief-lib-icon`
   and `.obw-brief-tpl` went with it.

   30px, not the 34px the select was: these are chrome beside an 11.5px
   sentence, and the 4px is part of what pays for the stacked fact cells below
   them. Mobile takes them back to a 44px target. */
/* ── `<ApplyOfferTemplateModal>`'s list ──
   One row per offer template inside the shared `.mf-modal`. Capped and
   scrolling, because the library is expected to grow and a modal that runs past
   the viewport puts its own Cancel out of reach. */
.obw-tpl-pick-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 0 18px;
  padding: 0;
  list-style: none;
  max-height: 320px;
  overflow-y: auto;
}

.obw-tpl-pick-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  border-radius: 10px;
  background-color: var(--obw-fact-bg, #f9fafb);
}

.obw-tpl-pick-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1 1 auto;
  min-width: 0;
}

.obw-tpl-pick-name {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  word-break: break-word;
}

.obw-tpl-pick-meta {
  font-size: 11px;
  color: var(--obw-ask-absent-color, #8d8579);
}

.obw-tpl-pick-btn {
  flex-shrink: 0;
  min-height: 32px;
  padding: 0 14px;
  font-size: 12px;
}

/* Same modal, nothing to apply — the shared `<ListState>` (§17), sized down.
   The block is built for a full-height table card, so its 40px padding and
   56px icon square would make the empty state taller than the list it stands
   in for inside a 380px `.mf-modal`. Same reduction, same reason, as
   `.emp-dash-duo-card`'s. */
.obw-tpl-pick-empty .tm-detail-empty {
  padding: 6px 0 20px;
  gap: 8px;
}

.obw-tpl-pick-empty .tm-detail-empty-icon {
  width: 38px;
  height: 38px;
  border-radius: 12px;
}

.obw-tpl-pick-empty .tm-detail-empty-title {
  font-size: 13.5px;
}

.obw-tpl-pick-empty .hr-dash-recent-btn-primary {
  margin-top: 12px;
}

.obw-brief-tpl-btn,
.obw-brief-tpl-save {
  min-height: 30px;
  padding: 0 12px;
  gap: 6px;
  font-size: 11.5px;
  white-space: nowrap;
}

/* HR's own words, quoted. Scoped to the request or to this group — there is no
   per-benefit note in the schema, so there is none rendered per row. Full text,
   never clamped: trading a height problem for a §21 clipping one is not a
   trade, and these are usually absent anyway. */
.obw-brief-note {
  margin: 0;
  padding: 8px 12px;
  border-left: 3px solid var(--obw-note-rule, #e9e4dc);
  border-radius: 0 10px 10px 0;
  background-color: var(--obw-fact-bg, #f9fafb);
}

.obw-brief-note-scope {
  margin: 0 0 3px;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.obw-brief-note-text {
  margin: 0;
  font-size: 12px;
  font-style: italic;
  line-height: 1.5;
  color: var(--modal-callout-item-color, #4b5563);
}

/* ── Priority: the company's own must-have / nice-to-have flag ── */
.obw-priority {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
}

.obw-priority--must {
  background-color: var(--obw-priority-must-bg, #eef2f9);
  color: var(--obw-priority-must-color, #0a3373);
}

.obw-priority--nice {
  background-color: var(--obw-priority-nice-bg, #f3f4f6);
  color: var(--obw-priority-nice-color, #4a4640);
}

/* ── The verdict chip ──
   §2 tinted surfaces throughout: the fill is the tone at ~14%, the label is the
   same tone at full strength. Note the vocabulary — there is no "below", in
   either direction. Ranking two differing terms needs a direction of generosity
   per benefit and nothing in the schema carries one, so a difference reads as a
   difference. */
.obw-cell-verdict {
  white-space: nowrap;
  width: 152px;
}

.obw-verdict {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
}

.obw-verdict--meets {
  background-color: var(--obw-verdict-meets-bg, #ecfdf5);
  color: var(--obw-verdict-meets-color, #3a8a5f);
}

.obw-verdict--differs,
.obw-verdict--unstated {
  background-color: var(--obw-verdict-differs-bg, #fefce8);
  color: var(--obw-verdict-differs-color, #a16207);
}

.obw-verdict--blocking_gap {
  background-color: var(--obw-verdict-blocking-bg, #fef2f2);
  color: var(--obw-verdict-blocking-color, #c2542e);
}

.obw-verdict--bonus {
  background-color: var(--obw-verdict-bonus-bg, #eff6ff);
  color: var(--obw-verdict-bonus-color, #4374f3);
}

.obw-verdict--gap,
.obw-verdict--uncomparable,
.obw-verdict--pending {
  background-color: var(--obw-verdict-neutral-bg, #f3f4f6);
  color: var(--obw-verdict-neutral-color, #8d8579);
}

/* Nobody asked, and nobody answered — nothing is outstanding, so the chip
   carries no surface at all. It used to read "not answered yet" in the same
   grey pill as a benefit the company IS waiting on, which invented an
   obligation out of an empty catalogue row. */
.obw-verdict--not_asked {
  background-color: transparent;
  font-weight: 400;
  color: var(--obw-verdict-neutral-color, #8d8579);
}

/* ── What the left rules mean ──
   A rule says "look here" and cannot say why; four tones were carrying four
   sentences with nothing on the page to decode them. Sits once per section,
   above the tables it explains. */
.obw-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 16px;
  margin: 0;
  /* The table's own gutter, so the legend's left edge is the benefit column's. */
  padding: 10px 14px 2px;
  list-style: none;
}

.obw-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  color: var(--obw-ask-absent-color, #8d8579);
}

.obw-legend-rule {
  flex-shrink: 0;
  width: 3px;
  height: 12px;
  border-radius: 2px;
}

.obw-legend-rule--outstanding {
  background-color: var(--obw-outstanding-rule, #fb7a5c);
}

.obw-legend-rule--blocking_gap {
  background-color: var(--obw-verdict-blocking-color, #c2542e);
}

.obw-legend-rule--differs {
  background-color: var(--obw-verdict-differs-color, #a16207);
}

.obw-legend-rule--bonus {
  background-color: var(--obw-verdict-bonus-color, #4374f3);
}

/* ── Row tone, for the verdicts that carry NEWS only ──
   `meets` and `pending` are deliberately untinted: a met requirement is the
   expected case, and forty tinted rows out of forty signal nothing. Same
   argument that turned the old "wash every requested row" into a marker. */
.obw-row--blocking_gap > .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 0 var(--obw-verdict-blocking-color, #c2542e);
}

.obw-row--differs > .hr-dash-recent-td:first-child,
.obw-row--unstated > .hr-dash-recent-td:first-child,
.obw-row--uncomparable > .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 0 var(--obw-verdict-differs-color, #a16207);
}

.obw-row--bonus > .hr-dash-recent-td:first-child {
  box-shadow: inset 3px 0 0 0 var(--obw-verdict-bonus-color, #4374f3);
}

.obw-row--blocking_gap > .hr-dash-recent-td {
  background-color: var(--obw-row-blocking-bg, rgba(194, 84, 46, 0.06));
}

.obw-row--differs > .hr-dash-recent-td,
.obw-row--unstated > .hr-dash-recent-td,
.obw-row--uncomparable > .hr-dash-recent-td {
  background-color: var(--obw-row-differs-bg, rgba(227, 179, 65, 0.08));
}

/* ── The score panel ──
   Its head/body/nav skeleton is the shared `.obw-aside*` above — the score panel
   was where that shape started, and the other three steps' `<OfferReadinessPanel>`
   wears the identical one. `.obw-score-head` / `.obw-score-body` are gone with
   the duplication; what follows is only what the score itself needs. */
.obw-donut-row {
  display: flex;
  align-items: center;
  gap: 14px;
  min-width: 0;
}

.obw-donut {
  position: relative;
  flex-shrink: 0;
  width: 80px;
  height: 80px;
}

/* The ring starts at 12 o'clock and runs clockwise. */
.obw-donut-svg {
  width: 80px;
  height: 80px;
  transform: rotate(-90deg);
}

.obw-donut-track,
.obw-donut-arc {
  fill: none;
  stroke-width: 8;
}

.obw-donut-track {
  stroke: var(--obw-donut-track, #eee8df);
}

.obw-donut-arc {
  stroke-linecap: round;
}

.obw-donut-arc--good {
  stroke: var(--obw-verdict-meets-color, #3a8a5f);
}

.obw-donut-arc--warn {
  stroke: var(--obw-verdict-differs-color, #a16207);
}

.obw-donut-arc--bad {
  stroke: var(--obw-verdict-blocking-color, #c2542e);
}

.obw-donut-value {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.obw-score-read {
  min-width: 0;
}

.obw-score-word {
  margin: 0 0 3px;
  font-size: 16px;
  font-weight: 800;
  letter-spacing: -0.02em;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.obw-score-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.45;
  color: var(--obw-ask-absent-color, #8d8579);
}

.obw-driver-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 0;
  padding: 0;
  list-style: none;
}

.obw-driver {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
}

.obw-driver-dot {
  flex-shrink: 0;
  width: 6px;
  height: 6px;
  margin-top: 5px;
  border-radius: 999px;
  background-color: var(--obw-verdict-neutral-color, #8d8579);
}

.obw-driver--good .obw-driver-dot {
  background-color: var(--obw-verdict-meets-color, #3a8a5f);
}

.obw-driver--warn .obw-driver-dot {
  background-color: var(--obw-verdict-differs-color, #a16207);
}

.obw-driver--bad .obw-driver-dot {
  background-color: var(--obw-verdict-blocking-color, #c2542e);
}

.obw-driver-main {
  display: flex;
  flex-direction: column;
  gap: 1px;
  flex: 1 1 auto;
  min-width: 0;
}

.obw-driver-label {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #1c1a17);
}

.obw-driver-detail {
  font-size: 11px;
  line-height: 1.4;
  color: var(--obw-ask-absent-color, #8d8579);
  word-break: break-word;
}

/* What the component is WORTH, and nothing else.
   `.obw-driver-points` / `.obw-driver-max` are gone with the earned-points
   fraction they rendered: "60/60" sitting beside "1 of 7 must have" was one
   fact in two units, and the two read as a contradiction. The line states its
   progress in items on the left and its weight in points here — one unit per
   figure, and no arithmetic for the reader to reconcile. */
.obw-driver-weight {
  flex-shrink: 0;
  margin-top: 1px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--obw-ask-absent-color, #8d8579);
}

/* The one change worth making next, named. */
.obw-score-lead {
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 0;
  padding: 12px 14px;
  border-radius: 12px;
  background-color: var(--obw-fact-bg, #f9fafb);
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--modal-callout-item-color, #4b5563);
}

.obw-score-gain {
  align-self: flex-start;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  background-color: var(--obw-verdict-bonus-bg, #eff6ff);
  color: var(--obw-verdict-bonus-color, #4374f3);
}

@media (max-width: 1180px) {
  /* The panel stops being a column and becomes the block above the work — the
     order the reader wants once it can no longer sit beside it, and the only
     one that keeps Continue reachable without scrolling a whole step first. */
  .obw-split {
    grid-template-columns: minmax(0, 1fr);
  }

  .obw-side {
    order: -1;
  }

  /* Already a row; once the column is full width the pair must stop stretching
     to half the page each and fall back to §6's 140px floor, right-aligned. */
  .obw-nav-actions {
    justify-content: flex-end;
  }

  .obw-nav-btn {
    flex: 0 0 auto;
    min-width: 140px;
  }
}

@media (max-width: 768px) {
  /* The table's own wrap scrolls horizontally; the fixed columns just give back
     what they can first, so the scroll starts later. The name column keeps a
     floor rather than a width — it is still the one absorbing the slack. */
  .obw-cell-offer {
    width: 200px;
    min-width: 200px;
  }

  .obw-cell-asked {
    width: 140px;
  }

  .obw-cell-verdict {
    width: 132px;
  }

  .obw-cell-name {
    min-width: 150px;
  }

  .obw-brief-actions {
    margin-left: 0;
  }

  .obw-brief-tpl-btn,
  .obw-brief-tpl-save {
    min-height: 44px;
  }

  .obw-nav-btn {
    flex: 1 1 auto;
    min-width: 0;
  }

  .obw-cover-btn {
    min-height: 44px;
  }

  .obw-value-sentinel {
    min-height: 44px;
  }
}

/* ── Step 2's card: "Present your offer" ──
   Three fields down the full width of the card, each label → helper → control.
   NOT `.ins-pol-form-grid`: a two-column grid put a 5-line textarea beside a
   one-line input and left the guidance nowhere to go, so every field's only
   hint was a placeholder restating its label. Generous vertical rhythm is the
   point — three questions with reasons, not five boxes.

   These are real FORM FIELDS: `.ins-pol-input` / `.modal-textarea` at their
   full contract size, never the 34px `.obw-cell-input` the benefit table uses.
   Nothing here sits in a table cell. */
.obw-present {
  display: flex;
  flex-direction: column;
  gap: 26px;
  padding: 22px 24px 26px;
}

.obw-present-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
}

/* Between the label and the control, never beside them: the sentence is read
   BEFORE the field is answered, so it cannot sit under the box. */
.obw-present-help {
  margin: 0 0 2px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Administrator name + site, one label above them. `form-row` supplies the
   §-mobile stacking; this only shares the width out on desktop. */
.obw-present-pair {
  display: flex;
  gap: 10px;
  min-width: 0;
}

/* `> *`, not `> .ins-pol-input` — the same idiom `.cat-create-fields` and
   `.member-fix-anyof` use, and for the same reason. Sharing a row's width is a
   property of THIS container's children, not of the field family that happens
   to fill it: naming `.ins-pol-input` here writes a second place a shared
   field's metrics are decided, which is exactly what `US-MEC.5.1` guards
   against for every rule below the census restyle block (its scope is
   open-ended to end-of-file on purpose). */
.obw-present-pair > * {
  flex: 1 1 0;
  min-width: 0;
}

/* Roughly five lines. `.modal-textarea`'s own min-height is dialog-sized. */
.obw-present-textarea {
  min-height: 128px;
  resize: vertical;
}

/* ── The offer wizard's non-Build steps (Describe / Document / Validate) ──
   `.obw-step-col` — a single capped column of cards — is retired: all three
   steps wear `.obw-split` now, and the 340px panel bounds the left column
   without a `max-width` of its own. */

/* A required field left blank, marked after the first blur. Coral, §9's
   required tone — not the error red, because nothing has failed yet: the
   Validate step is where completeness actually gates the submit. */
.obw-field--missing {
  border-color: var(--mf-req-color, var(--brand-accent, #fb7a5c));
}

.obw-field-help {
  margin: 0;
  font-size: 11.5px;
  line-height: 1.45;
  color: var(--ins-pol-subtext-color, #8d8579);
}

/* Recap rows — label left, value right, one hairline between (§10). */
.obw-recap-row {
  display: grid;
  grid-template-columns: 200px minmax(0, 1fr);
  gap: 16px;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--hr-dash-recent-row-border, #f3f4f6);
}

.obw-recap-row:last-child {
  border-bottom: none;
}

.obw-recap-row--multiline {
  align-items: start;
}

.obw-recap-row--multiline .obw-recap-value {
  white-space: pre-wrap;
}

.obw-recap-label {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.obw-recap-value {
  font-size: 12.5px;
  color: var(--hr-dash-recent-name-color, #1c1a17);
  word-break: break-word;
}

.obw-recap-value--unset {
  font-style: italic;
  color: var(--ins-pol-subtext-color, #8d8579);
}

.obw-recap-docs {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

/* A satisfied document reads as settled (green tick), a missing one as
   outstanding (coral alert) — the §2 tinted surfaces, not two new colours. */
.obw-recap-doc {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--dash-status-approved-bg, #ecfdf5);
  color: var(--dash-status-approved-color, #3a8a5f);
}

.obw-recap-doc--missing {
  background-color: var(--obw-req-bg, #fff7ed);
  color: var(--obw-req-color, #9a3412);
}

.obw-recap-doc svg {
  flex-shrink: 0;
}

.obw-recap-doc-main {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.obw-recap-doc-name {
  font-size: 12.5px;
  font-weight: 700;
}

.obw-recap-doc-meta {
  font-size: 11px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Recap banners — the §2 tinted surface of the status they report. */
.obw-banner {
  margin: 0 0 12px;
  padding: 10px 14px;
  border-radius: var(--modal-total-radius, 12px);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.45;
}

.obw-banner--warning {
  background-color: var(--dash-status-pending-bg, #fff7ed);
  color: var(--dash-status-pending-color, #b45309);
}

.obw-banner--error {
  background-color: var(--dash-status-rejected-bg, #fff1f2);
  color: var(--dash-status-rejected-color, #c2542e);
}

@media (max-width: 768px) {
  .obw-recap-row {
    grid-template-columns: 1fr;
    gap: 4px;
  }
}

/* ── Standalone draft-resume gate ──────────────────────────────────────────
   The "you already started a claim" choice a bot/WhatsApp link lands on
   (`<StandaloneDraftResume>`). It owns only the outer centring, the heading
   pair and the action stack — the card, the metadata cells and both buttons
   are the shared families. Phone-first: the actions stack full-width at a
   44px tap target and only spread onto one row on a wider screen. */
.sd-draft-gate {
  /* dvh so the card still centres under a mobile browser's collapsing chrome;
     the vh line above it is the fallback for anything that lacks dvh. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 20px;
}

.sd-draft-card {
  width: 100%;
  max-width: 520px;
}

.sd-draft-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  font-family: var(--heading-font, inherit);
  font-size: 19px;
  font-weight: 700;
  line-height: 1.3;
  color: var(--brand-primary, #0a3373);
}

.sd-draft-text {
  margin: 8px 0 0;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ins-pol-subtext-color, #6b7280);
}

.sd-draft-meta {
  margin-top: 18px;
}

.sd-draft-actions {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 20px;
}

.sd-draft-actions > button {
  width: 100%;
  min-height: 44px;
  justify-content: center;
  font-size: 14px;
}

.sd-draft-error {
  margin: 14px 0 0;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.45;
  background-color: var(--dash-status-rejected-bg, #fff1f2);
  color: var(--dash-status-rejected-color, #c2542e);
}

@media (min-width: 560px) {
  .sd-draft-actions {
    flex-direction: row-reverse;
    justify-content: flex-start;
  }

  .sd-draft-actions > button {
    width: auto;
    min-width: 170px;
  }
}

/* =====================================================================
   INSURER LIBRARY (`/insurer/templates`) — `.lib-*`
   Two axes on one screen: the KIND (underline tabs, §7 — the selection
   swaps the view) and the FOLDER (a left rail, §7's "scope switching is a
   rail, never a third selector shape"). Cards are §8: white, NO border,
   the shadow carries them.
   ===================================================================== */

.lib-layout {
  display: grid;
  grid-template-columns: 232px minmax(0, 1fr);
  gap: 20px;
  align-items: start;
}

/* §2 tinted surface on the destructive token — a refusal, in the server's own
   words. Shared by the screen banner and the upload modal's inline error. */
.lib-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0;
  padding: 10px 12px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--dash-status-rejected-bg, rgba(220, 38, 38, 0.12));
  color: var(--dash-status-rejected-color, #b42318);
  font-size: 12.5px;
  font-weight: 600;
  overflow-wrap: anywhere;
}

.lib-banner svg {
  flex-shrink: 0;
}

/* ── The folder rail ───────────────────────────────────────────────── */

.lib-rail {
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
  padding: 16px 12px 18px;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  border: 1px solid var(--hr-dash-recent-card-border, #d2d9e5);
  border-radius: var(--hr-dash-recent-card-radius, 16px);
  box-shadow: var(--hr-dash-recent-card-shadow, none);
}

.lib-rail-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 0 6px;
}

/* §3: a section label is ALWAYS ink-400, never semantically coloured. */
.lib-rail-label {
  margin: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tu-muted-color, #6b7280);
}

.lib-rail-new {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 9px;
  border: none;
  border-radius: var(--sidebar-item-radius, 8px);
  background: transparent;
  color: var(--brand-secondary, #4374f3);
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
}

.lib-rail-new:hover:not(:disabled) {
  background-color: var(--ins-pol-filter-bg-hover, #f2ede5);
}

.lib-rail-new:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

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

/* SELECTION SHAPE = `.sidebar-subitem`'s, consumed through that component's own
   tokens rather than values picked here — a theme retinting its nav sub-items
   retints this rail in the same breath. Hover is the NAV hover token, not the
   row-hover one: the two stay separate on purpose (a cream wash under a
   blue-tinted active state is invisible). */
.lib-rail-row {
  display: flex;
  align-items: center;
  gap: 2px;
  border-radius: var(--sidebar-item-radius, 8px);
  padding-right: 4px;
}

.lib-rail-row:hover {
  background-color: var(--sidebar-item-bg-hover, #f0f4ff);
}

.lib-rail-row--active,
.lib-rail-row--active:hover {
  background-color: var(--sidebar-subitem-bg-active, #e8effc);
}

.lib-rail-row--active .lib-rail-btn,
.lib-rail-row--active .lib-rail-btn svg,
.lib-rail-row--active .lib-rail-count {
  color: var(--sidebar-subitem-color-active, #0a3373);
}

.lib-rail-btn {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 9px 8px;
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--hr-dash-recent-name-color, #18181b);
}

.lib-rail-btn svg {
  flex-shrink: 0;
  color: var(--tu-muted-color, #6b7280);
}

/* Wraps rather than truncating — a folder name the reader chose must stay
   readable (§21: no text clipped anywhere). */
.lib-rail-name {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.lib-rail-count {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 700;
  color: var(--tu-muted-color, #6b7280);
}

.lib-rail-loading {
  display: flex;
  flex-direction: column;
  gap: 14px;
  padding: 8px;
}

.lib-rail-hint {
  margin: 4px 6px 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--tu-muted-color, #6b7280);
}

/* The inline create/rename form — one control for both acts, so a name refusal
   always lands under the field that caused it instead of in the banner. */
.lib-name-form {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px;
  border-radius: var(--modal-total-radius, 12px);
  background-color: var(--modal-total-bg, #f8fafc);
}

.lib-name-input {
  font-size: 12.5px;
}

.lib-name-form-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
}

/* Charte §6: a PAIR of buttons is one size, so neither label can set the width. */
.lib-name-form-btn {
  flex: 1;
  justify-content: center;
  padding: 8px 12px;
  font-size: 12.5px;
  min-height: 36px;
}

/* ── The item pane ─────────────────────────────────────────────────── */

.lib-main {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

/* Search + page actions, the right half of the filter row (§6). */
.lib-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-left: auto;
}

.lib-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 16px;
}

/* §8: no border — `.hr-dash-recent-card` already reads the border token, which
   the Charte themes set to transparent, so only geometry is added here. */
.lib-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 18px;
}

.lib-card-top {
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.lib-card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  flex-shrink: 0;
  border-radius: 10px;
  background-color: color-mix(in srgb, var(--brand-secondary, #4374f3) 12%, transparent);
  color: var(--brand-secondary, #4374f3);
}

.lib-card-icon--template {
  background-color: color-mix(in srgb, var(--brand-primary, #0a3373) 12%, transparent);
  color: var(--brand-primary, #0a3373);
}

.lib-card-headings {
  min-width: 0;
  flex: 1;
}

/* Wraps, never ellipsised: the filename IS the item's identity here, and two
   documents can differ only in their tail. */
.lib-card-title {
  margin: 0;
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.35;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

/* The kind, straight from the row's own CHECK-constrained columns. */
.lib-card-kind {
  margin: 3px 0 0;
  font-size: 11.5px;
  color: var(--tu-muted-color, #6b7280);
}

/* The filename, kept beside the kind once a LABEL is hiding it — quieter than
   the kind it follows, because it disambiguates rather than describes. */
.lib-card-filename {
  color: var(--tu-muted-color, #6b7280);
  overflow-wrap: anywhere;
}

.lib-card-tags {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.lib-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  margin-top: auto;
  flex-wrap: wrap;
}

.lib-card-meta {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--tu-muted-color, #6b7280);
}

.lib-card-actions {
  display: flex;
  align-items: center;
  gap: 4px;
}

/* ── Upload modal dropzone ─────────────────────────────────────────── */

/* §16's dropzone, minus `.tu-dropzone`'s own card-inset margin — inside a modal
   field it must sit flush under its label. Geometry only; every colour still
   comes from the shared rule. */
.lib-dropzone {
  margin: 0;
}

/* ── Preview overlay (§13 overlay variant) ─────────────────────────── */

/* Right-docked, full viewport height, SQUARE and border-less: the leftward
   shadow alone lifts it. No backdrop — the grid stays visible and clickable, so
   picking another document swaps the panel instead of stacking a second one. */
.lib-preview {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 460px;
  max-width: 100vw;
  z-index: 60;
  display: flex;
  flex-direction: column;
  background-color: var(--hr-dash-recent-card-bg, #ffffff);
  box-shadow: var(--tm-detail-overlay-shadow, -18px 0 44px -8px rgba(28, 26, 23, 0.16));
}

.lib-preview-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--hr-dash-recent-head-border, #eaeff6);
}

.lib-preview-title {
  margin: 0;
  min-width: 0;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--hr-dash-recent-name-color, #18181b);
  overflow-wrap: anywhere;
}

.lib-preview-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.lib-preview-body {
  position: relative;
  flex: 1;
  overflow: auto;
  background-color: var(--dash-body-bg, #f8fafd);
}

.lib-preview-shimmer {
  position: absolute;
  inset: 0;
  display: block;
}

.lib-preview-frame {
  width: 100%;
  height: 100%;
  border: none;
}

.lib-preview-imgwrap {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  padding: 20px;
}

.lib-preview-img {
  max-width: 100%;
  max-height: 100%;
  border-radius: 8px;
  object-fit: contain;
}

.lib-preview-fallback {
  padding: 40px;
  text-align: center;
}

.lib-preview-fallback-icon {
  color: var(--tu-muted-color, #7d93b3);
  margin-bottom: 14px;
}

.lib-preview-fallback-text {
  margin: 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--tu-muted-color, #6b7280);
}

@media (max-width: 768px) {
  /* The rail becomes a band above the items — one column, no horizontal
     overflow at 375px. */
  .lib-layout {
    grid-template-columns: minmax(0, 1fr);
  }

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

  .lib-toolbar > button {
    flex: 1;
    justify-content: center;
    min-height: 44px;
  }

  /* 44px tap targets, scoped to this screen — the shared control classes keep
     their desktop geometry everywhere else. */
  .lib-rail-new,
  .lib-card-actions .ins-pol-action-btn,
  .lib-rail-row .ins-pol-action-btn {
    min-height: 44px;
    min-width: 44px;
  }

  .lib-grid {
    grid-template-columns: minmax(0, 1fr);
  }

  /* A docked panel would leave no room for the list it is docked beside. */
  .lib-preview {
    width: 100%;
  }
}
