/* Bridge — token system:
   ink (text), paper (page bg), panel (cards), line (hairlines),
   teal (BC / primary / connective accent), rust (Bullhorn / risk accent),
   pl / pt (entity accents). No external fonts — system stacks only,
   carrying the distinction through scale/weight instead. */
:root {
  --ink: #211f1a;
  --paper: #f6f3ec;
  --panel: #ffffff;
  --line: #e2dccd;
  --muted: #756d5c;
  --teal: #0d6e6e;
  --teal-soft: #e2f2f1;
  --rust: #b3541e;
  --rust-soft: #fbe9dd;
  --pl: #1f4e9c;
  --pl-soft: #e4ecfb;
  --pt: #3c7a1e;
  --pt-soft: #e8f3df;
  --amber: #9c6b0a;
  --amber-soft: #faf0d9;
  --violet: #5b3aa6;
  --violet-soft: #ece3fb;

  --font-display: Iowan Old Style, Palatino Linotype, Palatino, Georgia, serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  /* UI modernization (2026) — design system tokens. All colors above are
     UNCHANGED from the original palette; everything below is new structure
     layered on top of it: an 8px-based spacing scale, an 8-step type scale
     (serif reserved for module/section titles + KPI hero, per the display
     font's own existing role), a 3-step radius scale, a 3-tier elevation
     system (the app previously had exactly 3 shadow rules total, all ad
     hoc), and motion tokens (previously zero transitions existed anywhere). */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px; --sp-8: 64px;

  --fs-xs: 11px; --fs-sm: 12.5px; --fs-base: 14px; --fs-md: 16px;
  --fs-lg: 19px; --fs-xl: 24px; --fs-2xl: 32px; --fs-3xl: 44px;

  --r-sm: 8px; --r-md: 14px; --r-lg: 20px; --r-pill: 999px;

  /* Employee Card section rhythm — ONE token owning both a section's own
     padding AND the gap to the next section, so the two can never disagree
     and a new section inherits the right spacing by construction. Every
     card section (drawer and full page) is the same .employee-card-section
     wrapper reading this; nothing hand-picks a value. The drawer steps it
     down one notch below (620px of width, not 1320). Guarded by
     frontend/employeeCardSpacing.test.js — see CLAUDE.md's UI convention. */
  --card-section-space: var(--sp-5);

  /* Height of every interactive form control — card inputs/selects, the
     Entity select, the segmented toggles. One definition, so "the header
     matches the cards" holds by construction. */
  --control-h: 34px;

  /* Rail: 68px = a 44px nav item (WCAG 2.5.5 minimum target) plus the
     sidebar's existing 12px padding each side, so the padding needn't
     animate alongside the width. */
  --sidebar-w: 268px;
  --sidebar-rail-w: 68px;
  /* Chrome above the nav, pinned so the first nav item sits at the same y
     in both states: collapsing hides the MODULES heading, so the collapsed
     head grows by exactly what it vacates (calc() below). */
  --sidebar-head-h: 60px;
  --sidebar-navlabel-h: 24px;
  /* ~200ms as specified; --ease is already an ease-out curve. */
  --dur-panel: 200ms;

  --shadow-1: 0 1px 2px rgba(33, 31, 26, 0.05), 0 1px 3px rgba(33, 31, 26, 0.06);
  --shadow-2: 0 2px 6px rgba(33, 31, 26, 0.07), 0 8px 24px rgba(33, 31, 26, 0.06);
  --shadow-3: 0 12px 32px rgba(33, 31, 26, 0.16), 0 4px 12px rgba(33, 31, 26, 0.08);

  --ease: cubic-bezier(0.2, 0.7, 0.3, 1);
  --dur-fast: 160ms;
  --dur-slow: 400ms;
}

@keyframes bridge-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes bridge-pop-in { from { opacity: 0; transform: translateY(4px) scale(0.98); } to { opacity: 1; transform: none; } }
@keyframes bridge-slide-in { from { transform: translateX(24px); opacity: 0; } to { transform: none; opacity: 1; } }

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 14px;
  line-height: 1.45;
}

button, input, select {
  font-family: inherit;
  font-size: inherit;
  color: inherit;
}

/* App shell — left module-nav sidebar + a content area for whichever
   module is active (see AppShell.jsx). Config-driven: adding a module is a
   MODULES entry in App.jsx, never a change here. */
.shell {
  min-height: 100vh;
  display: flex;
}

.shell-sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  /* Width only: the sidebar is a flex sibling of .shell-main, so the
     content pane reflows into the freed space — no overlay. Padding/font/gap
     stay out of it; they'd force layout every frame. */
  transition: width var(--dur-panel) var(--ease);
  /* Pinned to the viewport (not stretched to the active module's content
     height, which can run tens of thousands of px on a long table) — this
     is what keeps the global Refresh control's footer actually visible
     without scrolling, from any module. */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--line);
  padding: var(--sp-5) var(--sp-3);
}
/* Head row: brand left, collapse toggle far right. min-height is
   load-bearing — identical in both states, so the nav below doesn't shift
   when the row goes from [mark + wordmark + button] to [mark over button]. */
.shell-sidebar-head {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: var(--sidebar-head-h);
  padding: 0 var(--sp-2) var(--sp-6);
}
.brand { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.sidebar-toggle {
  flex-shrink: 0;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.sidebar-toggle svg { width: 18px; height: 18px; }
.sidebar-toggle:hover { background: var(--paper); color: var(--ink); }
.sidebar-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }

/* Collapsed head: a 68px rail can't hold the mark and the button side by
   side, so they stack. Row min-height is unchanged, so nothing below moves. */
.shell-sidebar.is-collapsed .shell-sidebar-head {
  flex-direction: column;
  gap: var(--sp-1);
  padding-left: 0;
  padding-right: 0;
  /* Absorbs the hidden MODULES heading exactly. */
  min-height: calc(var(--sidebar-head-h) + var(--sidebar-navlabel-h));
}
.shell-sidebar.is-collapsed .sidebar-toggle { margin-left: 0; }
/* Logo reduced to its mark. */
.shell-sidebar.is-collapsed .brand-name { display: none; }
.brand-name {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: 0.01em;
}

.nav-section-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 var(--sp-2);
  /* Pinned, not line-height-derived, so --sidebar-navlabel-h stays true if
     the font changes. 16 + 8 margin = 24. */
  height: 16px;
  margin-bottom: var(--sp-2);
}

.module-nav { display: flex; flex-direction: column; gap: 2px; padding: 0 var(--sp-2); }
.module-nav-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Pinned so the item measures the same with a visible label (line-box
     driven) and a clipped one (icon driven) — sub-pixel drift compounds
     down a list of seven. */
  min-height: 34px;
  position: relative;
  gap: var(--sp-2);
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  font-size: var(--fs-base);
  font-weight: 600;
  color: var(--muted);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.module-nav-main { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.module-nav-icon { display: flex; flex-shrink: 0; width: 18px; height: 18px; opacity: 0.75; }
.module-nav-icon svg { width: 100%; height: 100%; }
.module-nav-label { line-height: 1.25; }
.module-nav-item:hover:not(.disabled) { background: var(--paper); color: var(--ink); }
.module-nav-item.active { background: var(--teal-soft); color: var(--teal); }
.module-nav-item.active .module-nav-icon { opacity: 1; }
.module-nav-item.disabled { cursor: default; opacity: 0.55; }
.module-nav-item:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* --- Collapsed rail ------------------------------------------------- */
.shell-sidebar.is-collapsed { width: var(--sidebar-rail-w); }
.shell-sidebar.is-collapsed .nav-section-label { display: none; }
/* Zeroed so the item can be a full 44px inside the rail; the sidebar's own
   12px padding is what stays constant. */
.shell-sidebar.is-collapsed .module-nav { padding-left: 0; padding-right: 0; }
.shell-sidebar.is-collapsed .module-nav-item {
  justify-content: center;
  padding-left: 0;
  padding-right: 0;
}
.shell-sidebar.is-collapsed .module-nav-main { justify-content: center; gap: 0; }
/* Clipped, not display: none — see AppShell.jsx. Absolute so it takes no
   space in the centred row. */
.shell-sidebar.is-collapsed .module-nav-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.shell-sidebar.is-collapsed .soon-tag { display: none; }

/* Tooltip for the clipped label. Fixed, not absolute: the sidebar's
   overflow-y: auto clips horizontally too, so an absolute tip would be cut
   off at the rail's edge. With `top` left auto it still takes its static
   vertical position; `left` is explicit since the rail is flush to x=0. */
.module-nav-tip {
  display: none;
  position: fixed;
  left: calc(var(--sidebar-rail-w) + var(--sp-2));
  z-index: 40;
  padding: 5px 10px;
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--panel);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
  box-shadow: var(--shadow-2);
}
.shell-sidebar.is-collapsed .module-nav-item:hover .module-nav-tip,
.shell-sidebar.is-collapsed .module-nav-item:focus-visible .module-nav-tip {
  display: block;
}

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

.run-meta { color: var(--muted); font-size: 12.5px; }

/* Global "Refresh data" control (RefreshControl.jsx) — pinned to the
   bottom of the sidebar so it's visible from every module, next to the
   Last-run timestamp it keeps current. */
.shell-sidebar-footer {
  margin-top: auto;
  padding: var(--sp-4) var(--sp-2) 0;
  border-top: 1px solid var(--line);
}
.refresh-control { display: flex; flex-direction: column; gap: var(--sp-2); }
.refresh-btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); width: 100%; }
.refresh-btn-danger { color: var(--rust); border-color: var(--rust); }
/* Icon is for the collapsed rail only; the expanded sidebar keeps the text
   button exactly as it was. */
.refresh-btn-icon { display: none; }

/* Collapsed footer: Refresh must stay visible from every module
   (BRIDGE.md §4), so it survives as an icon-only button. The "Last run"
   timestamp can't — at 44px it wrapped to three lines. */
.shell-sidebar.is-collapsed .shell-sidebar-footer { padding-left: 0; padding-right: 0; }
.shell-sidebar.is-collapsed .run-meta { display: none; }
.shell-sidebar.is-collapsed .refresh-btn {
  width: 44px;
  height: var(--control-h);
  margin: 0 auto;
  padding: 0;
}
.shell-sidebar.is-collapsed .refresh-btn-icon { display: flex; width: 16px; height: 16px; }
.shell-sidebar.is-collapsed .refresh-btn-icon svg { width: 100%; height: 100%; }
/* Clipped, not removed — keeps the button's accessible name. */
.shell-sidebar.is-collapsed .refresh-btn-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
.refresh-btn-danger:hover:not(:disabled) { background: var(--rust-soft); }

@keyframes bridge-spin { to { transform: rotate(360deg); } }
.spinner {
  display: inline-block;
  width: 12px;
  height: 12px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: var(--r-pill);
  opacity: 0.7;
  animation: bridge-spin 700ms linear infinite;
}

.risk-banner {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  cursor: pointer;
  background: var(--rust-soft);
  color: var(--rust);
  padding: var(--sp-3) var(--sp-4);
  font-size: 13.5px;
  font-weight: 600;
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--dur-fast) var(--ease), filter var(--dur-fast) var(--ease);
}
.risk-banner:hover { filter: brightness(0.97); box-shadow: var(--shadow-2); }
.risk-banner:focus-visible { outline: 2px solid var(--rust); outline-offset: 2px; }

/* Unified segmented-pill control (2026) — replaces the app's former FOUR
   parallel tab/toggle patterns (.sub-view-tabs underline, .sub-tab pill,
   .paw-toggle-group pill, and PAW's own .paw-tabs underline) with ONE
   component used everywhere a person picks one of a small set of options:
   module sub-views, bucket sub-tabs, filter toggles, period presets. A
   track (var(--paper), pill-radius) holding buttons that lift onto a white
   "chip" with a soft shadow when active — never color alone, always a real
   shape change too. `role="tablist"`/`role="radiogroup"` + `role="tab"`/
   `role="radio"` on the buttons (per actual semantics) still apply; this
   only replaces the visual language, not the interaction model. */
.segmented {
  display: inline-flex;
  padding: 3px;
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  gap: 2px;
  flex-wrap: wrap;
}
.segmented button {
  border: none;
  background: transparent;
  font: inherit;
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--muted);
  padding: 7px 16px;
  border-radius: var(--r-pill);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.segmented button.active {
  background: var(--panel);
  color: var(--teal);
  box-shadow: var(--shadow-1);
  font-weight: 700;
}
.segmented button.active .count-pill { background: var(--teal); color: #fff; }
.segmented button:not(.active):hover { color: var(--ink); }
.segmented button:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

.soon-tag {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  background: var(--line);
  color: var(--muted);
  padding: 2px 6px;
  border-radius: 4px;
}

/* The frame every module sits in: the gutter to the shell, the breathing room
   above and below, and the 1400px measure the whole app is read at. A module
   that is not on this list has NO frame at all — it runs edge to edge while
   the framed screens are inset, which is what Data quality did from the day it
   shipped (2026-09-17) until it was added here (2026-09-18). Add a new module
   to this selector and to the narrow-screen gutter near the bottom of the
   file; do not give it a frame of its own.
   `.settings-module` is the one screen still off this list — it has the
   narrow-screen gutter and no frame, exactly where Data quality was. It was
   never in the control-room rollout, so that is left as it is rather than
   changed sideways here; it is a known gap, not the precedent to copy. */
.people-module, .module-placeholder, .paw-module, .chargeability-module, .risk-module, .extension-module, .timesheets-module, .mileage-module, .payroll-module, .data-quality-module, .access-module {
  padding: var(--sp-6) var(--sp-7) 60px;
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
}

.module-header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
  flex-wrap: wrap;
}
.module-eyebrow {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--teal);
  font-weight: 700;
  margin-bottom: 2px;
}
.module-title {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 700;
  letter-spacing: -0.2px;
  margin: 0;
}

.module-placeholder-text { color: var(--muted); max-width: 480px; }

.loading-state, .empty-state {
  padding: var(--sp-7);
  text-align: center;
  color: var(--muted);
}

.error-banner {
  background: var(--rust-soft);
  color: var(--rust);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-sm);
  margin-bottom: var(--sp-4);
  font-weight: 600;
}

.shared-controls {
  display: flex;
  gap: var(--sp-3);
  margin-bottom: var(--sp-4);
  flex-wrap: wrap;
}
.shared-controls input[type="search"] {
  padding: 9px var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  min-width: 260px;
  background: var(--panel);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.shared-controls select {
  padding: 9px var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel);
  transition: border-color var(--dur-fast) var(--ease);
}
.shared-controls input[type="search"]:focus-visible,
.shared-controls select:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
  border-color: var(--teal);
}

.tab-row { margin-bottom: var(--sp-5); }

/* PAW's page-level scope bar — the employee-type toggle and the Entity
   select. Both are global: each picks who the whole page is about, and
   neither parameterises one report. Per-section filters live in cards
   further down, so a closing hairline marks the tier boundary: global above
   the line, section-scoped below. Not full-width — the children size to
   their content, and the bar is block-level so its hairline spans the
   content column. */
.paw-scope-row {
  /* space-between, not a 2-column grid: the empty span between the two
     controls is the separator, so no divider element is needed. --sp-4 is
     the floor for widths with no free space left to distribute. */
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-4);
  /* Both controls are content-sized, so flex-wrap alone breaks the row at
     exactly the width where they'd touch — no breakpoint constant to keep
     in sync. */
  flex-wrap: wrap;
  padding-bottom: var(--sp-4);
  margin-bottom: var(--sp-5);
  border-bottom: 1px solid var(--line);
}
.paw-scope-row > .tab-row { margin-bottom: 0; }

/* The toggle hugs its content — that's what lets flex-wrap above find the
   break point on its own. inline-grid with grid-auto-columns: 1fr rather
   than flex: 1 children: in an auto-sized grid a 1fr track resolves to the
   widest item's max-content, giving content-sized AND equal segments at
   once (flex children give one or the other). */
.paw-scope-row .segmented {
  display: inline-grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  flex: 0 0 auto;
  height: var(--control-h);
  padding: 3px;
}
/* Horizontal padding sizes the grid track (inert inside a stretched flex
   cell before). nowrap so a segment label never breaks onto a second line —
   the row wraps instead. */
.paw-scope-row .segmented button {
  justify-content: center;
  white-space: nowrap;
  height: 100%;
  padding: 0 var(--sp-4);
}

/* Entity — sized to its content, not to a column. 220px is twice the
   no-truncation minimum (~110px for "All entities" plus padding and chevron
   clearance), so a longer entity name still fits. As the last
   space-between child its right border lands flush on the content column's
   edge, level with the Placement card below. */
.paw-scope-entity {
  position: relative;
  /* No grow, no shrink — the row wraps instead. max-width guards a
     viewport narrower than the control itself. */
  flex: 0 0 220px;
  max-width: 100%;
}
.paw-scope-entity select {
  appearance: none;
  -webkit-appearance: none;
  width: 100%;
  height: var(--control-h);
  /* Chevron inset (12) + chevron (14) + 4px, so a long value can't slide
     under it. */
  padding: 0 30px 0 var(--sp-4);
  border: 1px solid var(--teal);
  border-radius: var(--r-pill);
  background: var(--panel);
  color: var(--ink);
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  text-align: left;
  cursor: pointer;
  transition: box-shadow var(--dur-fast) var(--ease);
}
.paw-scope-entity select:hover { box-shadow: var(--shadow-1); }
.paw-scope-entity select:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
/* --sp-3, not --sp-4: on a 220px control a 16px inset reads as a stranded
   gap. */
.paw-scope-entity-chevron {
  position: absolute;
  top: 50%;
  right: var(--sp-3);
  width: 14px;
  height: 14px;
  transform: translateY(-50%);
  color: var(--teal);
  pointer-events: none;
}
.paw-scope-entity-chevron svg { width: 100%; height: 100%; display: block; }

.count-pill {
  background: var(--line);
  color: var(--ink);
  border-radius: var(--r-pill);
  padding: 1px 8px;
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}

.section-title { font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 700; margin: 0 0 4px; }
.section-subtitle { color: var(--muted); font-size: 13px; margin: 0 0 14px; }
.coming-soon-section { margin-top: 40px; }

/* Collapsible section — toggle pinned to its own top-right corner, title
   and subtitle still visible while collapsed. Placed before the
   module-specific section rules below (.paw-bench-section et al) so those
   keep overriding the default margin. The chevron is the only chrome, so
   four of these stacked down a page read as furniture. */
.collapsible-section { margin-top: var(--sp-5); }
.collapsible-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-3);
}
.collapsible-heading { min-width: 0; }
/* The gap down to the body is the body's own, so a subtitle-less section
   (Filters) and a subtitled one (Bench) sit the same distance above it. */
.collapsible-heading .section-subtitle { margin-bottom: 0; }
.collapse-toggle {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.collapse-toggle svg {
  width: 16px;
  height: 16px;
  transition: transform var(--dur-fast) var(--ease);
}
.collapse-toggle:hover { background: var(--paper); color: var(--ink); }
.collapse-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
/* Points up when open (click = fold up), down when collapsed. */
.collapsible-section:not(.is-collapsed) .collapse-toggle svg { transform: rotate(180deg); }
/* Header actions — an InfoTip, a "back to today" button — sit left of the
   chevron and stay visible while the section is collapsed, so a folded
   section is still explicable and still clearable. */
.collapsible-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-shrink: 0;
}

.collapsible-body { margin-top: var(--sp-3); }
.collapsible-body[hidden] { display: none; }

/* --- On-demand explanation (2026-09-03) -----------------------------------
   The PAW chart used to carry ~200 words of standing prose under the bars.
   Prose you cannot dismiss stops being read and starts being scenery, so it
   was both permanently in the way and not doing its job. These two patterns
   put the same words one hover away.

   `.has-hint` / `[data-hint]` — a CSS-only tooltip on something that already
   names itself (a legend swatch, a tab, a chip). No JS, no portal, no state.
   Shown on hover AND on keyboard focus; the same text is also rendered in a
   .visually-hidden span at the call site, because a ::after is invisible to
   a screen reader. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.has-hint, [data-hint] { position: relative; }
.has-hint[data-hint] { cursor: help; }
.has-hint[data-hint]:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: var(--r-sm); }

.has-hint[data-hint]::after, [data-hint]:not(.has-hint)::after {
  content: attr(data-hint);
  position: absolute;
  left: 0;
  top: calc(100% + 6px);
  z-index: 40;
  width: max-content;
  max-width: 300px;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--ink);
  color: #f4f1e9;
  font-family: var(--font-body, inherit);
  font-size: var(--fs-sm);
  font-weight: 400;
  line-height: 1.45;
  text-align: left;
  white-space: normal;
  letter-spacing: 0;
  box-shadow: 0 6px 20px rgba(0,0,0,0.18);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-2px);
  transition: opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), visibility var(--dur-fast);
  pointer-events: none;
}
.has-hint[data-hint]:hover::after,
.has-hint[data-hint]:focus-visible::after,
[data-hint]:not(.has-hint):hover::after,
[data-hint]:not(.has-hint):focus-visible::after {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}
/* A tooltip on a tab near the right edge would run off the panel. Flipping
   the last tab in each group to right-aligned is cheaper and more reliable
   than measuring at runtime. */
.paw-week-tabs button:last-child[data-hint]::after { left: auto; right: 0; }

/* InfoTip — the section-level version: a real ⓘ button carrying a definition
   list rather than one line. Hover opens it, click PINS it (so a long one can
   be read without keeping the mouse still), Escape or a click outside closes
   it. See ui.jsx. */
.infotip { position: relative; display: inline-flex; }
.infotip-trigger {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.infotip-trigger svg { width: 16px; height: 16px; }
.infotip-trigger:hover, .infotip-trigger.is-open { background: var(--paper); color: var(--ink); }
.infotip-trigger:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }

.infotip-popover {
  position: absolute;
  top: calc(100% + 6px);
  z-index: 50;
  width: 340px;
  max-width: min(340px, calc(100vw - 48px));
  /* Long enough to be worth opening, short enough not to become the page.
     If a section's explanation outgrows this it scrolls rather than running
     off the viewport on a laptop. */
  max-height: min(60vh, 460px);
  overflow-y: auto;
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  background: var(--ink);
  color: #e7e4dc;
  font-size: var(--fs-sm);
  line-height: 1.5;
  text-align: left;
  box-shadow: 0 10px 30px rgba(0,0,0,0.22);
}
.infotip-right { right: 0; }
.infotip-left { left: 0; }
.infotip-popover[hidden] { display: none; }

/* Term/meaning, not a paragraph: the popover is a lookup table you scan for
   the one band you were unsure about, not something to read end to end. */
/* Term ABOVE meaning, not beside it. Two columns looked tidier in the
   abstract but "On project — missing extension" is a five-word term, and a
   term column wide enough for it left the definitions in a 100px gutter. */
.infotip-defs { margin: 0; }
.infotip-defs dt {
  font-weight: 700;
  color: #fff;
  font-size: var(--fs-xs);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.infotip-defs dd { margin: 2px 0 var(--sp-3); color: #cfcabb; }
.infotip-defs dd:last-child { margin-bottom: 0; }

/* A view-state flag: something the CURRENT filter has done to the chart.
   A chip, not a sentence, because it is true only while that filter is on —
   the reasoning behind it is on hover. */
.paw-flags { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-3); }
.paw-flag {
  display: inline-flex;
  align-items: center;
  padding: 3px var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--paper);
  color: var(--muted);
  font-size: var(--fs-xs);
}
/* .paw-filters carries its own vertical margin for standalone use
   (Chargeability). Inside a collapsible body the header owns the space
   above, so only the gap below is kept. */
.collapsible-body > .paw-filters { margin: 0 0 var(--sp-4); }
/* The segment legend sits inside .paw-chart-wrap, so the panel's padding
   supplies the surrounding space and only the gap down to the SVG is needed.
   Centred on the panel, not the plot area — the plot's centre is 6px left of
   it (CHART_MARGIN in chartAxis.js), not worth a nudge that would drift.
   Scoped so legends outside a chart panel keep their own alignment. */
.paw-chart-wrap .paw-legend {
  justify-content: center;
  margin-bottom: var(--sp-3);
}

.coming-soon {
  padding: 24px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  color: var(--muted);
  text-align: center;
}

.exit-risk-section { margin-top: var(--sp-7); }
.exit-risk-filters { display: flex; gap: 6px; margin-bottom: var(--sp-3); }

/* Low-tier disclosure (2026) — a real card with a rotating chevron instead
   of a bare <summary> line, so progressive disclosure reads as an
   intentional pattern rather than an afterthought. Still a native
   <details>/<summary> underneath (same keyboard/AT behavior as before) —
   only the visual treatment changes. */
.exit-risk-subsection {
  margin-top: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.exit-risk-subsection summary {
  cursor: pointer;
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--muted);
  padding: var(--sp-3) var(--sp-4);
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.exit-risk-subsection summary::-webkit-details-marker { display: none; }
.exit-risk-subsection summary::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 5px solid var(--muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--dur-fast) var(--ease);
  flex-shrink: 0;
}
.exit-risk-subsection[open] summary::before { transform: rotate(90deg); }
.exit-risk-subsection summary:hover { color: var(--ink); background: var(--paper); }
.exit-risk-subsection[open] summary { border-bottom: 1px solid var(--line); }
.exit-risk-subsection .data-table-scroll { padding: 0; }
.exit-risk-subsection .data-table { border: none; border-radius: 0; box-shadow: none; }

.data-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
  font-size: 13px;
  box-shadow: var(--shadow-1);
}
.data-table thead th {
  text-align: left;
  padding: 10px 12px;
  background: #f1ede2;
  border-bottom: 1px solid var(--line);
  font-weight: 600;
  white-space: nowrap;
}
.data-table tbody td { padding: 9px 12px; border-bottom: 1px solid #efe9db; vertical-align: middle; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr { transition: background var(--dur-fast) var(--ease); }
.data-table tbody tr:hover { background: #faf8f2; cursor: pointer; }

.sortable-th { cursor: pointer; user-select: none; }
.sortable-th:hover { color: var(--teal); }
.sort-arrow { font-size: 9px; color: var(--teal); margin-left: 2px; }

.sort-bar { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 12px; }
.sort-bar-button {
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: 999px;
  padding: 5px 12px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
}
.sort-bar-button:hover { color: var(--ink); border-color: var(--muted); }
.sort-bar-button.active { border-color: var(--teal); background: var(--teal-soft); color: var(--teal); font-weight: 700; }

.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}
.badge-PL { background: var(--pl-soft); color: var(--pl); }
.badge-PT { background: var(--pt-soft); color: var(--pt); }
.badge-unknown { background: var(--line); color: var(--muted); }
/* Tenure (2026-09-25, core/tenure.js). A neutral OUTLINE, never a fill:
   tenure is not a state to act on, and every colour in Bridge means "look
   here". The 1px border is inset with box-shadow so the badge keeps the
   same box as its filled neighbours and a row of badges stays on one
   baseline. A leaver's reads muted — how long they stayed, not a status. */
.badge-tenure { background: transparent; color: var(--ink); box-shadow: inset 0 0 0 1px var(--line); }
.badge-tenure-left { color: var(--muted); font-weight: 500; }
.badge-consultant { background: var(--violet-soft); color: var(--violet); }
.badge-inferred-tag { font-weight: 400; opacity: 0.75; font-style: italic; }
.badge-backoffice { background: #dcecf7; color: #1e5c85; }
.badge-matchedby { background: var(--teal-soft); color: var(--teal); }
.badge-injected { background: var(--pt-soft); color: var(--pt); }
.badge-queued { background: var(--rust-soft); color: var(--rust); }
.badge-bucket { background: var(--line); color: var(--ink); }
.badge-bucket-risk { background: var(--rust-soft); color: var(--rust); }
.badge-terminated { background: var(--rust-soft); color: var(--rust); }
.badge-active { background: var(--pt-soft); color: var(--pt); }
.badge-pending { background: var(--teal-soft); color: var(--teal); }

/* A Bullhorn account that exists but is switched off (2026-09-07). Amber,
   not rust: it is an exception worth an HR look, not a failure — and it is
   always rendered beside the word, never as colour alone. */
.badge-account-disabled { background: var(--amber-soft); color: var(--amber); }
.badge-tier-high { background: var(--rust-soft); color: var(--rust); }
.badge-tier-medium { background: var(--amber-soft); color: var(--amber); }
.badge-tier-low { background: var(--line); color: var(--muted); }
/* An account nobody on the payroll holds. Rust, and always beside the words
   — it sits in the same column as the employment badges on All accounts, so
   the four populations read as one vocabulary (2026-09-24). */
.badge-no-ppid { background: var(--rust-soft); color: var(--rust); }
.badge-contract-PAY { background: var(--rust-soft); color: var(--rust); }
.badge-contract-B2B { background: var(--teal-soft); color: var(--teal); }

.id-code {
  font-family: var(--font-mono);
  background: #f1ede2;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 12px;
}
/* Bullhorn deep-links (PlacementLink/CandidateLink) — same chip, styled as
   a real link (not color alone: underline on hover too) so it reads as
   clickable without losing the id-code identity everywhere else uses. */
.id-code-link {
  color: inherit;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.id-code-link:hover { background: var(--teal-soft); color: var(--teal); text-decoration: underline; }
.id-code-link:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.muted { color: var(--muted); }

/* Urgency counters (2026) — a soft chip instead of bare bold text, so
   urgency reads as a shape+color change, not just a font-weight bump
   (matches the badge/pill vocabulary used everywhere else in the app).
   Cool state uses the paper surface (present, not yet urgent); hot state
   switches to the rust pairing already reserved for "needs attention"
   everywhere else (exit-risk tier, contract PAY, error banners). */
.days-open, .days-overdue, .days-remaining {
  display: inline-flex;
  align-items: center;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: var(--r-pill);
  background: var(--paper);
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.days-open-hot, .days-overdue-hot, .days-remaining-hot { background: var(--rust-soft); color: var(--rust); }

/* Extension Module (2026) — the mirror-image counter to days-overdue/
   days-since-ended: LOWER is worse here (fewer days left to act), so the
   "hot" threshold is applied by the caller at <30 days, matching the
   High-tier days-based cutoff (see extensionRisk.js), not a fixed style
   rule here. */

.action-ongoing-toggle {
  border-radius: var(--r-pill);
  padding: 5px 12px;
  font-size: 12px;
  font-weight: 600;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.action-ongoing-toggle:hover:not(:disabled) { border-color: var(--teal); color: var(--teal); }
.action-ongoing-toggle:disabled { cursor: wait; opacity: 0.7; }
.action-ongoing-toggle-active { background: var(--teal-soft); border-color: var(--teal); color: var(--teal); }

.flag-missing {
  color: var(--rust);
  font-size: 11.5px;
  font-weight: 600;
  background: var(--rust-soft);
  padding: 2px 7px;
  border-radius: 999px;
}

.disabled-action-wrap { position: relative; display: inline-block; }
.disabled-action-wrap .tooltip {
  visibility: hidden;
  position: absolute;
  bottom: 130%;
  left: 50%;
  transform: translateX(-50%);
  background: var(--ink);
  color: #fff;
  font-size: 11px;
  padding: 4px 8px;
  border-radius: 5px;
  white-space: nowrap;
  z-index: 5;
}
.disabled-action-wrap:hover .tooltip { visibility: visible; }
/* Button hierarchy (2026) — real primary/secondary/tertiary/danger variants
   distinguished by shape+weight+elevation, not hue alone (the old system
   had one flat `.btn` shape with only background-color varying per
   modifier — `.btn-inject`/`.btn-confirm`/`.btn-manual`/`.btn-quiet`/
   `.btn-unlink`, all removed in favor of these four). */
.btn {
  border-radius: var(--r-sm);
  padding: 8px 16px;
  font-size: var(--fs-sm);
  font-weight: 600;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
              color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.btn:disabled { cursor: wait; opacity: 0.6; }
.btn-primary { background: var(--teal); color: #fff; border-color: var(--teal); box-shadow: var(--shadow-1); }
.btn-primary:hover:not(:disabled) { filter: brightness(1.08); box-shadow: var(--shadow-2); }
.btn-secondary { background: var(--panel); color: var(--ink); border-color: var(--line); }
.btn-secondary:hover:not(:disabled) { border-color: var(--teal); color: var(--teal); }
.btn-tertiary { background: transparent; color: var(--muted); border-color: transparent; }
.btn-tertiary:hover:not(:disabled) { color: var(--ink); background: var(--paper); }
.btn-danger { background: var(--panel); color: var(--rust); border-color: var(--rust); }
.btn-danger:hover:not(:disabled) { background: var(--rust-soft); }
.btn-disabled { color: var(--muted); cursor: not-allowed; opacity: 0.65; background: var(--panel); border-color: var(--line); }

.inject-action-wrap { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.inject-message {
  color: var(--rust);
  font-size: 11.5px;
  font-weight: 600;
  max-width: 220px;
  white-space: normal;
}
.unmatched-actions-buttons { display: flex; gap: 6px; flex-wrap: wrap; }

.picker-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(33, 31, 26, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 30;
  animation: bridge-fade-in var(--dur-fast) var(--ease);
}
.picker-panel {
  width: 420px;
  max-width: calc(100% - 32px);
  max-height: 70vh;
  background: var(--panel);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: bridge-pop-in var(--dur-slow) var(--ease);
}
.picker-header { display: flex; align-items: center; gap: 8px; padding: 12px 14px; border-bottom: 1px solid var(--line); }
.picker-header input { flex: 1; }
.picker-close { border: none; background: none; font-size: 20px; cursor: pointer; color: var(--muted); line-height: 1; }
.picker-status-filter { margin: 10px 14px 0; }
.picker-list { overflow-y: auto; padding: 6px; }
.picker-empty { color: var(--muted); padding: 16px; text-align: center; font-size: 13px; }
.picker-row {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 3px;
  width: 100%;
  padding: 8px 10px;
  border: none;
  border-left: 3px solid transparent;
  background: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 13px;
  text-align: left;
}
.picker-row:hover { background: var(--teal-soft); }
.picker-row-main { display: flex; align-items: center; gap: 10px; }
.picker-row-name { flex: 1; font-weight: 600; }
.picker-row-top { border-left-color: var(--pt); background: var(--pt-soft); }
.picker-row-top:hover { background: var(--pt-soft); filter: brightness(0.97); }
.picker-row-reason { color: var(--muted); font-size: 11.5px; padding-left: 2px; }
.picker-row-reason-top { color: var(--pt); font-weight: 600; }
.picker-conflict-note {
  color: var(--rust);
  background: var(--rust-soft);
  font-size: 11.5px;
  font-weight: 600;
  padding: 8px 14px;
  border-bottom: 1px solid var(--line);
}

.card-list { display: flex; flex-direction: column; gap: var(--sp-3); }
.case-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-4) var(--sp-5);
  cursor: pointer;
  box-shadow: var(--shadow-1);
  transition: box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.case-card:hover { border-color: var(--teal); box-shadow: var(--shadow-2); }
/* Onboarding-risk cards in Unmatched — same rust risk language as the Risk
   module's own onboarding-risk view (days-overdue-hot, risk-banner), so the
   two surfaces of the same data read as one signal, not two colors. */
.case-card-risk { border-left: 3px solid var(--rust); }
.case-card-risk:hover { border-color: var(--rust); }
.case-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.case-card-title { font-weight: 700; font-size: 14.5px; }
.case-card-meta { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; font-size: 12.5px; margin-bottom: 8px; }
.case-card-dates { display: flex; gap: 14px; flex-wrap: wrap; font-size: 12px; margin-bottom: 8px; }
.date-range { font-variant-numeric: tabular-nums; }
.case-card-reason { color: var(--muted); font-size: 12.5px; margin-bottom: 10px; font-style: italic; }

.drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(33, 31, 26, 0.35);
  display: flex;
  justify-content: flex-end;
  z-index: 20;
  animation: bridge-fade-in var(--dur-fast) var(--ease);
}
.drawer {
  width: 560px;
  max-width: 100%;
  height: 100%;
  background: var(--panel);
  padding: var(--sp-6);
  overflow-y: auto;
  box-shadow: var(--shadow-3);
  animation: bridge-slide-in var(--dur-slow) var(--ease);
}
.drawer-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: var(--sp-5); }
.drawer-header h3 { margin: 0; font-family: var(--font-display); font-size: var(--fs-md); font-weight: 700; }
.drawer-close {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  border-radius: var(--r-sm);
  background: none;
  font-size: 22px;
  cursor: pointer;
  color: var(--muted);
  line-height: 1;
  transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.drawer-close:hover { background: var(--paper); color: var(--ink); }
.drawer-close:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* --- AI PAW report panel (2026-09-03) --------------------------------------
   Wider than the Employee Card drawer and laid out as a column, because its
   body is one monospaced block that must not wrap mid-line: the panel shows
   exactly the bytes the Copy button puts on the clipboard, so a soft wrap on
   screen would misrepresent what you are about to paste. The actions sit in a
   footer pinned below the scrolling body — Copy and Regenerate stay reachable
   however long the report runs. */
/* PAW module header: the Untyped stat and the Generate action sit together at
   the right edge. As two separate children of a space-between header the
   button floated in the middle on the Back-office tab, reading as unplaced. */
.module-header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  flex-shrink: 0;
}

.paw-report-drawer {
  width: 720px;
  display: flex;
  flex-direction: column;
  padding-bottom: 0;
}
.paw-report-body { flex: 1; min-height: 0; overflow-y: auto; }
.paw-report-meta { margin: 0; }

/* The report preview. Renders the SAME HTML the Copy button puts on the
   clipboard, so what you see is what lands in the email — bold titles, bullet
   lists and all.
   Body font, not monospace: this is prose destined for an inbox, not a code
   block. The one deliberate exception is the numbers, which get tabular
   figures so a column of them lines up rather than jittering per digit-width
   — the same treatment every chart axis in this app already uses. */
.paw-report-rich {
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--paper);
  color: var(--ink);
  font-size: 13.5px;
  line-height: 1.6;
  font-variant-numeric: tabular-nums;
}
.paw-report-rich p { margin: 0 0 var(--sp-3); }
.paw-report-rich p:last-child { margin-bottom: 0; }
.paw-report-rich strong { color: var(--ink); font-weight: 700; }
.paw-report-rich em { color: var(--muted); }
/* Tight lists: a section's eight fields are a block to scan, not eight
   separate paragraphs — generous line spacing here would push the four
   sections apart until they stopped reading as one report. */
.paw-report-rich ul {
  margin: 0 0 var(--sp-4);
  padding-left: var(--sp-5);
  list-style: disc;
}
.paw-report-rich li { margin: 0 0 2px; }
.paw-report-rich ul:last-child { margin-bottom: 0; }

/* A quiet note, not an error: the report below it is complete and correct —
   this only explains a missing AI summary or an unavailable vacancy count. */
.paw-report-note {
  margin-bottom: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-left: 3px solid var(--line);
  background: var(--paper);
  color: var(--muted);
  font-size: var(--fs-sm);
  line-height: 1.5;
}

.drawer-footer {
  flex-shrink: 0;
  border-top: 1px solid var(--line);
  padding: var(--sp-4) 0;
  background: var(--panel);
}
.paw-report-actions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.paw-report-hint { color: var(--muted); font-size: var(--fs-xs); flex: 1; min-width: 200px; }

.drawer-columns { display: flex; align-items: stretch; gap: 4px; }
.drawer-col { flex: 1; min-width: 0; }
.drawer-col-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-weight: 700;
  margin-bottom: var(--sp-2);
}
.drawer-field { margin-bottom: var(--sp-2); }
.drawer-field-label { display: block; font-size: 11.5px; color: var(--muted); }
.drawer-field-value { display: block; font-size: 13.5px; word-break: break-word; }
.connector { flex-shrink: 0; align-self: center; }

.drawer-note {
  margin-top: var(--sp-4);
  padding: var(--sp-2) var(--sp-3);
  background: var(--rust-soft);
  color: var(--rust);
  border-radius: var(--r-sm);
  font-size: var(--fs-sm);
}
.drawer-unlink { margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--line); }
.unlink-success-message { margin-top: var(--sp-2); font-size: 12px; color: var(--pt); font-weight: 600; }

.drawer-history {
  margin-top: var(--sp-5);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

/* Unmatched-case drawer redesign (2026-08-28) — the top framing banner
   (replaces a blank "Business Central: —" panel with a plain-language
   status) and the "not yet linked" callout that stands in for the old
   fake mirror-column when there's no tentative BC candidate to compare
   against. Non-interactive (cursor: default) — this is status, not a
   button, unlike .risk-banner elsewhere in the app. */
.case-framing-banner {
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--r-md);
  margin-bottom: var(--sp-4);
  box-shadow: var(--shadow-1);
  cursor: default;
}
.case-framing-title { font-weight: 700; font-size: var(--fs-sm); }
.case-framing-detail { margin-top: 4px; font-size: var(--fs-sm); opacity: 0.9; }
.case-framing-banner-amber { background: var(--amber-soft); color: var(--amber); }
.case-framing-banner-rust { background: var(--rust-soft); color: var(--rust); }
.case-framing-banner-teal { background: var(--teal-soft); color: var(--teal); }

.drawer-not-linked {
  display: flex;
  gap: var(--sp-3);
  align-items: flex-start;
  margin-top: var(--sp-4);
  padding: var(--sp-3) var(--sp-4);
  border: 1px dashed var(--line);
  border-radius: var(--r-md);
  color: var(--muted);
}
.drawer-not-linked-icon { font-size: 18px; line-height: 1; flex-shrink: 0; }
.drawer-not-linked-title { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); margin-bottom: 2px; }
.drawer-not-linked-body { font-size: var(--fs-sm); }

.confirm-modal-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(33, 31, 26, 0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 40;
  padding: 16px;
  animation: bridge-fade-in var(--dur-fast) var(--ease);
}
.confirm-modal {
  width: 440px;
  max-width: 100%;
  max-height: 90vh;
  overflow-y: auto;
  background: var(--panel);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-3);
  padding: var(--sp-5);
  animation: bridge-pop-in var(--dur-slow) var(--ease);
}
.confirm-modal-title {
  margin: 0 0 18px;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
}
.confirm-modal-sides { display: flex; flex-direction: column; align-items: stretch; gap: 2px; }
.confirm-side {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 14px;
}
.confirm-side:first-child { border-left: 3px solid var(--rust); }
.confirm-side:last-child { border-left: 3px solid var(--teal); }
.confirm-side-label {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-weight: 700;
  margin-bottom: 6px;
}
.confirm-side-name { font-size: 15px; font-weight: 600; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.confirm-side-meta { margin-top: 4px; font-size: 12px; color: var(--muted); }
.confirm-match-tag {
  font-size: 11px;
  font-weight: 600;
  color: var(--pt);
  background: var(--pt-soft);
  padding: 2px 8px;
  border-radius: 999px;
}
.confirm-connector {
  text-align: center;
  color: var(--muted);
  font-size: 16px;
  line-height: 1;
  margin: 2px 0;
}
.confirm-modal-effect {
  margin: 16px 0 0;
  font-size: 13px;
  color: var(--ink);
  line-height: 1.5;
}
.confirm-modal-message {
  margin-top: 12px;
  padding: 8px 12px;
  background: var(--rust-soft);
  color: var(--rust);
  border-radius: 6px;
  font-size: 12.5px;
  font-weight: 600;
}
.confirm-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 20px; }

/* PAW module — header stats, tabs, two filter groups, legend, chart. */
.paw-stat-row { display: flex; gap: 22px; }
.paw-stat { display: flex; flex-direction: column; align-items: flex-end; }
.paw-stat-value { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.1; }
.paw-stat-value.muted { color: var(--muted); }
.paw-stat-label { font-size: 11px; color: var(--muted); }

.paw-untyped { margin-top: 16px; }
.paw-untyped-panel {
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--panel);
  overflow: hidden;
}
.paw-untyped-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  background: #f1ede2;
  border-bottom: 1px solid var(--line);
  font-size: 12.5px;
  font-weight: 700;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.paw-untyped-list {
  list-style: none;
  margin: 0;
  padding: 4px;
  max-height: 220px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.paw-untyped-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  font-size: 13px;
}
.paw-untyped-list li:hover { background: var(--paper); border-radius: 6px; }
.paw-untyped-empty { padding: 18px; text-align: center; color: var(--muted); font-size: 13px; }

.paw-filters {
  display: flex;
  gap: var(--sp-4);
  flex-wrap: wrap;
  margin: var(--sp-5) 0;
}
.paw-filter-group {
  flex: 1;
  /* <fieldset>'s UA margin-inline: 2px inset every card from the content
     column and turned the declared 16px gutter into a real 20px. */
  margin: 0;
  /* Not a flex container: the header below relies on a floated <legend>,
     and float is ignored on a flex item. */
  min-width: 280px;
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4) var(--sp-4);
  background: var(--panel);
  box-shadow: var(--shadow-1);
}
/* Card header. A notched <legend> sits in the border, not the content flow,
   so row 1's start depended on how many lines it took — and "PLACEMENT
   narrows which placements count" wraps before "TIMELINE sets the X axis",
   pushing the two cards out of step. `float: left; width: 100%` makes it an
   ordinary in-flow block: unbroken border, fixed-height header band. Still
   <fieldset>/<legend>, so the group name is still announced. */
.paw-filter-group legend {
  float: left;
  width: 100%;
  padding: 0 0 var(--sp-2);
  margin-bottom: var(--sp-3);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-xs);
  font-weight: 700;
  color: var(--ink);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
/* A floated legend reserves no space for what follows: clear it, and let
   the header's margin-bottom be the only header/body gap. */
.paw-filter-group legend + * { clear: both; margin-top: 0; }
.paw-filter-hint {
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  color: var(--muted);
}
.paw-filter-row { display: flex; gap: var(--sp-3); flex-wrap: wrap; align-items: end; margin-top: var(--sp-2); }

/* Two-column filter grid — used by PAW's Timeline/Placement cards instead
   of the .paw-filter-row flex line above. Under flex-wrap, "Company on its
   own row" was an accident of intrinsic widths summing past the card, so a
   shorter company name would have pulled it back up beside Owner. Grid
   states the intent; minmax(0, 1fr) rather than 1fr, since plain 1fr floors
   at min-content and the min-width:120px below could push a column past
   half. */
.paw-filter-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-3);
  align-items: end;
}
.paw-field-full { grid-column: 1 / -1; }

/* One height for every control in these cards, filling the column — the
   shared rule's min-width:120px with no width left From at 120px next to To
   at 130px. */
.paw-filter-grid .paw-field input,
.paw-filter-grid .paw-field select {
  width: 100%;
  min-width: 0;
  height: var(--control-h);
}
/* The Weekly/Monthly toggle: select height, full column width, buttons
   splitting it evenly, so it restates the grid above instead of floating at
   its intrinsic 165px. Scoped, so other .segmented keep their own size. */
.paw-filter-grid .segmented {
  display: flex;
  width: 100%;
  height: var(--control-h);
  padding: 3px;
}
.paw-filter-grid .segmented button {
  flex: 1;
  justify-content: center;
  padding: 0 var(--sp-2);
  height: 100%;
}
/* The preset control's always-visible resolved dates — so picking "This
   quarter" still tells you exactly which two calendar dates that means,
   rather than hiding the resolved window behind a label. */
.paw-filter-resolved-range { font-size: 12px; color: var(--muted); margin-top: var(--sp-2); font-variant-numeric: tabular-nums; }
.paw-field { display: flex; flex-direction: column; gap: 4px; font-size: 11.5px; color: var(--muted); }
.paw-field input, .paw-field select {
  padding: 7px 9px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--panel);
  font-size: 13px;
  min-width: 120px;
  transition: border-color var(--dur-fast) var(--ease);
}
.paw-field input:focus-visible, .paw-field select:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 1px;
}

/* Chart legend — a plain swatch + label, no chip/border (2026: dropped the
   bordered-pill treatment the interactive PawChart legend still uses for
   its OWN toggle buttons below — this one is identity-only, never a
   toggle, so it reads as a caption, not a control). */
.paw-legend { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.paw-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  border: 1px solid var(--line);
  background: var(--panel);
  border-radius: 999px;
  padding: 5px 12px 5px 8px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.paw-legend-item.off { color: var(--muted); opacity: 0.5; }
/* Static variant — the stacked-bar chart's legend is identity-only, never
   a toggle (hiding a segment would break "segments sum to the true
   total"), so it drops the button chrome (border/background/cursor). */
.paw-legend-item-static { border: none; background: none; padding: 0; cursor: default; color: var(--muted); font-weight: 500; font-size: var(--fs-sm); gap: 8px; }
.paw-legend-item:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.paw-legend-swatch { width: 14px; height: 3px; border-radius: 2px; display: inline-block; }

.paw-chart-wrap {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  padding: var(--sp-4);
}
.paw-chart-svg { width: 100%; height: auto; display: block; touch-action: none; }
/* Gridlines/axis (2026) — recessive by construction: a hairline at reduced
   opacity so the DATA (bars/lines) stays the loudest thing on the chart,
   axis labels in tabular-nums so a column of numbers lines up vertically
   instead of jittering per digit-width. */
.paw-chart-gridline { stroke: var(--line); stroke-width: 1; opacity: 0.8; }
.paw-chart-axis-label { fill: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.paw-chart-scale-label { fill: var(--muted); font-size: 10px; font-style: italic; }
.paw-chart-crosshair { stroke: var(--line); stroke-width: 1.5; }
.paw-chart-end-label { fill: var(--ink); font-size: 12px; font-weight: 700; font-variant-numeric: tabular-nums; }
.paw-chart-empty { padding: 60px; text-align: center; color: var(--muted); }
.paw-chart-refreshing { opacity: 0.55; transition: opacity var(--dur-slow) var(--ease); }

/* Hover column (2026) — a soft vertical band behind the hovered/focused
   bar, drawn UNDER the bars so it never competes with them, making the
   tooltip's target obvious at a glance instead of relying on the tooltip
   position alone to imply which bar it belongs to. Same idea for both bar
   charts (PAW's stacked bars, Chargeability's monthly bars). */
.paw-chart-hover-col { fill: var(--paper); }

.paw-chart-tooltip {
  position: absolute;
  top: var(--sp-2);
  background: var(--ink);
  color: #fff;
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-sm);
  pointer-events: none;
  white-space: nowrap;
  box-shadow: var(--shadow-3);
  animation: bridge-fade-in var(--dur-fast) var(--ease);
  /* Above the NEXT card (2026-09-15): the tooltip is taller than its chart
     card and runs into the panel below, whose own positioned chart wrapper
     comes later in the DOM and painted over the Total row. One layer above
     the sticky table headers (2-3) and the command bar (5). */
  z-index: 6;
}
.paw-chart-tooltip-label { font-weight: 700; margin-bottom: 6px; font-size: 13px; }
.paw-chart-tooltip-row { display: flex; align-items: center; gap: 8px; padding: 2px 0; }
.paw-chart-tooltip-key { width: 10px; height: 3px; border-radius: 2px; display: inline-block; }
.paw-chart-tooltip-name { flex: 1; color: #e7e4dc; }
.paw-chart-tooltip-value { font-weight: 700; font-variant-numeric: tabular-nums; margin-left: 14px; }
.paw-chart-tooltip-total { margin-top: 6px; padding-top: 6px; border-top: 1px solid rgba(255,255,255,0.2); }

/* Shared small color swatch — the stacked bar's static legend and the
   chart's own tooltip both key a segment's identity with this square,
   never by coloring text (see the dataviz skill's "text never wears the
   series color" rule). */
.paw-bar-swatch { width: 11px; height: 11px; border-radius: 3px; display: inline-block; flex-shrink: 0; }

.paw-bench-section { margin-top: var(--sp-6); }

/* Chargeability & Holidays module — reuses paw-stat-row/paw-stat for its
   KPI rows and paw-filters/paw-filter-group/paw-field for its master
   filters (see PawModule.jsx); only the band coloring and the time-off
   category/codes detail below are new. Band semantics are the INVERSE of
   the exit-risk tier badges above (there, high=bad=rust; here, a high
   chargeability % is good) — new classes rather than reusing badge-tier-*,
   same three tokens (teal/amber/rust) so the palette stays the app's own. */
.badge-charge-high { background: var(--teal-soft); color: var(--teal); }
.badge-charge-mid { background: var(--amber-soft); color: var(--amber); }
.badge-charge-low { background: var(--rust-soft); color: var(--rust); }

/* Provisional (current/future month, not yet final) — same dashed,
   uncolored "unconfirmed" treatment as kpi-unspecified-note and
   timeoff-category-chip-unspecified elsewhere in this module, so the app's
   existing "dashed = not final" grammar carries over rather than inventing
   a new one. */
.badge-provisional { background: transparent; border: 1px dashed var(--muted); color: var(--muted); font-style: italic; }

/* Monthly chargeability chart — the 70/90% band-threshold gridlines double
   as the SAME boundaries the band badges/KPI meter use elsewhere, dashed so
   they read as reference lines rather than data. Provisional bars render
   through a neutral hatch (see ChargeabilityMonthlyChart.jsx's SVG
   <pattern>) with a dashed outline, deliberately withholding the real
   band color until a month is complete — a partial month's naturally lower
   % must never be mistaken for a confirmed dip. */
.chargeability-chart-threshold { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.55; }
.chargeability-bar-provisional { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 2 2; }
.chargeability-chart-legend { margin-top: var(--sp-4); padding-top: var(--sp-3); border-top: 1px solid var(--line); }
.chargeability-provisional-swatch {
  width: 14px; height: 10px; border-radius: 3px; display: inline-block;
  background-color: var(--line);
  background-image: repeating-linear-gradient(45deg, var(--muted) 0, var(--muted) 1.5px, transparent 1.5px, transparent 5px);
  border: 1px dashed var(--muted);
}
.chargeability-provisional-tag { color: #e7e4dc; font-style: italic; font-weight: 400; }
/* The current month, measured before it ends because every timesheet is in
   (2026-09-25): its real band colour, lighter, with a solid outline — it is
   a measurement, so no hatch, but not yet a closed one. */
.chargeability-bar-measured-early { fill-opacity: 0.6; stroke: var(--ink); stroke-width: 1; }
.chargeability-measured-early-swatch {
  width: 14px; height: 10px; border-radius: 3px; display: inline-block;
  background: var(--teal); opacity: 0.6; border: 1px solid var(--ink);
}

/* KPI panel — ONE card per tab, its sections separated by a hairline
   rather than floating as separate blocks (hero -> supporting stats ->
   band/category breakdown). The headline figure (avg chargeability % /
   total days off) carries the panel's visual weight; everything else is
   secondary. */
.kpi-panel {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-5);
  margin: var(--sp-4) 0 var(--sp-5);
  box-shadow: var(--shadow-2);
}
.kpi-panel-section { padding-top: var(--sp-4); margin-top: var(--sp-4); border-top: 1px solid var(--line); }
.kpi-panel-section:first-child { padding-top: 0; margin-top: 0; border-top: none; }
.kpi-panel-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); font-weight: 700; margin-bottom: 10px; }

.kpi-hero-value-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.kpi-hero-value {
  font-family: var(--font-display);
  font-size: 44px;
  font-weight: 600;
  line-height: 1;
  font-variant-numeric: proportional-nums;
}
.kpi-hero-value-high { color: var(--teal); }
.kpi-hero-value-mid { color: var(--amber); }
.kpi-hero-value-low { color: var(--rust); }
.kpi-hero-label { font-size: 13.5px; color: var(--muted); font-weight: 600; }
.kpi-meter { margin-top: 14px; height: 8px; border-radius: 999px; background: var(--line); overflow: hidden; }
.kpi-meter-fill { height: 100%; border-radius: 999px; }
.kpi-meter-fill-high { background: var(--teal); }
.kpi-meter-fill-mid { background: var(--amber); }
.kpi-meter-fill-low { background: var(--rust); }

/* Part 1 (data honesty): Unspecified sits right beside the hero, in its
   own dashed note — visually "not a confirmed figure," distinct from the
   solid-bordered panel it lives in, never silently merged into the
   headline number above it. */
.kpi-unspecified-note {
  margin-top: 14px;
  padding: 10px 14px;
  border: 1px dashed var(--line);
  border-radius: 8px;
  color: var(--muted);
  font-size: 12.5px;
  line-height: 1.5;
  max-width: 560px;
}
.kpi-unspecified-note strong { color: var(--ink); font-variant-numeric: tabular-nums; font-size: 13.5px; }

/* Band distribution — a status-scale (good/warning/critical) segmented
   bar, never a categorical one: swapping High/Mid/Low's order would change
   the meaning, so this is ordinal, one fixed color per band, always
   paired with a text legend (never color alone). 2px gaps between
   segments do the separating, no border drawn around any segment. */
.band-bar { display: flex; height: 14px; border-radius: 999px; overflow: hidden; gap: 2px; background: var(--paper); }
.band-segment { height: 100%; }
.band-segment-high { background: var(--teal); }
.band-segment-mid { background: var(--amber); }
.band-segment-low { background: var(--rust); }
.band-segment-empty { background: var(--line); }
.band-legend { display: flex; gap: var(--sp-4); margin-top: var(--sp-3); flex-wrap: wrap; }
.band-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink); }
.band-legend-item strong { font-variant-numeric: tabular-nums; }
.band-legend-swatch { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.band-legend-swatch-high { background: var(--teal); }
.band-legend-swatch-mid { background: var(--amber); }
.band-legend-swatch-low { background: var(--rust); }

/* Time-off category breakdown — plain, uncolored stat tiles (deliberately
   NOT color-coded per category): teal/rust are already spoken for by the
   contract badge (B2B/PAY) and amber/violet by other badges in the SAME
   table rows below this KPI panel — reusing them here for an unrelated
   meaning would let one hue mean two different things on one screen.
   Category identity comes from the label text instead. Unspecified never
   appears in this row — see kpi-unspecified-note above. */
.category-breakdown-row { display: flex; gap: 26px; flex-wrap: wrap; }
.category-tile { display: flex; flex-direction: column; }
.category-tile-value { font-size: 21px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1.2; }
.category-tile-label { font-size: 11px; color: var(--muted); }

/* Table section wrapper — a section-title (+ count) above each table, same
   convention RiskTab/PawModule already use, so the table reads as a named
   section rather than trailing directly off the KPI panel. */
.data-table-scroll { overflow-x: auto; }
.data-table th.th-num, .data-table td.num { text-align: right; font-variant-numeric: tabular-nums; }

.timeoff-category-summary { display: flex; gap: 6px; flex-wrap: wrap; }
.timeoff-category-chip {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 1px 8px;
  font-size: 11.5px;
  white-space: nowrap;
}
/* Same visible "not confirmed" treatment as kpi-unspecified-note above —
   dashed, muted, never reading as an ordinary leave category. */
.timeoff-category-chip-unspecified { border-style: dashed; color: var(--muted); font-style: italic; }
.timeoff-codes-detail summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
  font-weight: 600;
}
.timeoff-codes-detail summary:hover { color: var(--ink); }
.timeoff-codes-list { list-style: none; margin: 6px 0 0; padding: 0; font-size: 12px; color: var(--muted); }
.timeoff-codes-list li { padding: 1px 0; }

.chargeability-needs-recompute { padding: 32px; text-align: center; }
.chargeability-needs-recompute-detail { color: var(--muted); font-size: 12px; margin-top: 6px; }

/* Holiday Balance Tracker (Balances tab) — the four-tag status badge, new
   classes rather than reusing badge-tier-*/badge-charge-* (same reasoning
   as those two: a distinct facet, even where a color happens to overlap).
   Healthy reuses pt (green, already "good" app-wide via badge-active);
   Watch reuses amber; Over reuses rust; Burnout is the one genuinely new
   pairing — violet, otherwise only spoken for by badge-consultant (a
   TYPE badge that never appears in this table), so no new hex is needed. */
.badge-holiday-healthy { background: var(--pt-soft); color: var(--pt); }
.badge-holiday-watch { background: var(--amber-soft); color: var(--amber); }
.badge-holiday-over { background: var(--rust-soft); color: var(--rust); }
.badge-holiday-burnout { background: var(--violet-soft); color: var(--violet); }
/* Days taken before the Portuguese six-month rule lifted (2026-09-17). Amber,
   not rust: HR approved each one, so it is a thing to know rather than a
   thing that is wrong. It reads with the Watch badge because it asks for the
   same kind of attention. */
.badge-holiday-qualifying { background: var(--amber-soft); color: var(--amber); }

/* Anomaly summary — the tab's "flying blind" fix: reuses .risk-banner
   (PeopleModule's own onboarding-risk banner) so an urgent, clickable count
   reads as the SAME signal app-wide, not a new visual language. A violet
   variant for Burnout (banked leave — not urgent the same way Over is) and
   a calm/teal variant for the all-clear state, which is itself a signal
   the check ran rather than absence of one. */
.holiday-anomaly-band { display: flex; flex-direction: column; gap: var(--sp-2); }
.risk-banner-violet { background: var(--violet-soft); color: var(--violet); }
.risk-banner-calm { background: var(--teal-soft); color: var(--teal); cursor: default; }
.risk-banner-active { outline: 2px solid currentColor; outline-offset: -2px; }

.holiday-clear-filter { margin-left: var(--sp-3); font-weight: 500; font-size: 11.5px; padding: 2px 10px; }

/* A row someone is over-balance on gets a left rust rule too, on top of the
   status badge — the brief's "visually prominent" ask reads as a shape
   change (not color alone), same discipline as the rest of the app's risk
   language (see days-overdue-hot, risk-banner). */
.holiday-row-over { box-shadow: inset 3px 0 0 var(--rust); }
.holiday-balance-negative { color: var(--rust); font-weight: 700; }
.holiday-override-flag { margin-left: 4px; color: var(--muted); cursor: help; }

/* First-year 6-month lockout note — always visible (never hover-only) on
   an affected row, right under the entitlement figure it explains, so HR
   never wonders why a usable balance reads 0 despite real accrued days. */
.holiday-lockout-note { color: var(--rust); font-size: 11.5px; font-weight: 600; margin-top: 2px; max-width: 220px; }

.holiday-verify-caption { color: var(--muted); font-size: 12px; margin-top: var(--sp-4); font-style: italic; }

/* Inline HR data-entry (HolidayFieldsEditor) — an expanded row beneath the
   person it's editing, not a modal: this only ever touches Bridge's own
   holiday_balance_settings row, trivially re-editable, so a dialog detour
   would be more ceremony than the write deserves (see the component's own
   file-header comment for the fuller reasoning). */
.holiday-edit-row td { background: var(--paper); padding: var(--sp-4); }
.holiday-fields-editor { display: flex; gap: var(--sp-4); align-items: end; flex-wrap: wrap; }
.holiday-fields-field input { min-width: 140px; }
.holiday-fields-actions { display: flex; gap: var(--sp-2); }

/* Timesheet Tracking — the 5-state status badge (see ui.jsx's
   TimesheetStatusBadge), reusing the app's existing color language rather
   than inventing new hexes: posted=teal (done, good), awaiting_finance=
   amber, awaiting_hr=violet (two distinct "waiting on someone else"
   states), rejected/not_submitted=rust (both need the SAME next action —
   the employee — same "color always paired with a label" discipline as
   the chargeability bands). */
.badge-ts-posted { background: var(--teal-soft); color: var(--teal); }
.badge-ts-awaiting-finance { background: var(--amber-soft); color: var(--amber); }
.badge-ts-awaiting-hr { background: var(--violet-soft); color: var(--violet); }
.badge-ts-rejected { background: var(--rust-soft); color: var(--rust); }
.badge-ts-not-submitted { background: var(--rust-soft); color: var(--rust); }

/* Monthly progress cell — "X/N posted" headline + a status-mix bar (same
   segmented-bar shape as .band-bar, new class since its segments are keyed
   by timesheet status, not a chargeability band) + a toggle into the
   per-week detail row. */
.ts-progress-cell { display: flex; flex-direction: column; gap: 4px; min-width: 140px; }
.ts-progress-headline { font-size: 13px; }
.ts-mix-bar { display: flex; height: 8px; border-radius: 999px; overflow: hidden; gap: 1px; background: var(--line); }
.ts-mix-segment-posted { background: var(--teal); }
.ts-mix-segment-awaiting_finance { background: var(--amber); }
.ts-mix-segment-awaiting_hr { background: var(--violet); }
.ts-mix-segment-rejected { background: var(--rust); }
.ts-mix-segment-not_submitted { background: var(--muted); opacity: 0.35; }
.ts-progress-toggle { font-size: 11px; padding: 2px 0; align-self: flex-start; }

/* Per-week detail — an expanded row beneath the person it belongs to, same
   pattern as HolidayFieldsEditor's expand-row (holiday-edit-row above):
   this is a read-only detail view, not a form, so a plain list is enough. */
.ts-week-detail-row td { background: var(--paper); padding: var(--sp-4); }
.ts-week-list { display: flex; flex-direction: column; gap: 6px; }
.ts-week-item { display: flex; align-items: center; gap: 12px; font-size: 12.5px; }
.ts-week-item-dates { color: var(--muted); font-variant-numeric: tabular-nums; min-width: 130px; }
.ts-week-item-hours { font-variant-numeric: tabular-nums; min-width: 50px; text-align: right; }

.ts-issue-summary { font-size: 12.5px; max-width: 320px; }
.ts-issue-summary-clean { color: var(--muted); }

.ts-hours-cell { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ts-hours-under { color: var(--rust); font-weight: 700; }
.ts-hours-over { color: var(--amber); font-weight: 700; }

.ts-summary-row { flex-wrap: wrap; gap: 26px; }

/* The summary band's KPI tiles are real <button>s here (clicking one
   filters + sorts the table — see TrackingTab.jsx), so they need a plain-
   button reset on top of the existing .paw-stat look, plus hover/selected
   states no other .paw-stat consumer (PAW/Chargeability, still plain divs)
   needs. Selected state pairs a colored border with a tinted background —
   never color alone, same discipline as the rest of this app's status
   language. */
.paw-stat-clickable {
  border: 1px solid transparent;
  background: none;
  border-radius: var(--r-sm);
  padding: 6px 10px;
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.paw-stat-clickable:hover { background: var(--paper); }
.paw-stat-clickable:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.paw-stat-active { background: var(--teal-soft); border-color: var(--teal); }
.paw-stat-active .paw-stat-value { color: var(--teal); }

.ts-missing-count { color: var(--muted); font-size: 13px; margin: 0 0 14px; }

/* Settings module (Timesheets tab) — HR-editable deadline overrides.
   Custom uses the same solid-teal badge language as badge-active/
   badge-injected elsewhere ("this is Bridge's current live value");
   Default reuses badge-provisional (dashed/muted/italic) — the same visual
   already used app-wide for "this is a placeholder/inherited value, not a
   deliberate choice someone made." */
.settings-tab-intro { color: var(--muted); font-size: 13px; max-width: 640px; margin: 0 0 var(--sp-4); }
.badge-custom { background: var(--teal-soft); color: var(--teal); }
.deadline-row { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-4); cursor: default; }
.deadline-row-top { display: flex; flex-direction: column; gap: 4px; min-width: 220px; }
.deadline-row-label { display: flex; align-items: center; gap: var(--sp-2); font-weight: 600; font-size: var(--fs-md); }
.deadline-row-hint { color: var(--muted); font-size: 12.5px; }
.deadline-row-actions { display: flex; align-items: end; gap: var(--sp-3); flex-wrap: wrap; }
.deadline-row-field input { width: 80px; min-width: 0; }

/* Employee Card (Phase 2) — the app-level drawer, the app-wide clickable
   PersonName link, and the top-bar global find bar. Reuses the existing
   drawer/badge/risk-banner language throughout; only genuinely new shapes
   (the avatar circle, the placement mini-cards, the discrepancy comparison
   rows, the find bar itself) get new classes. */

/* shell-main wraps the top bar + content pane (see AppShell.jsx) so the
   find bar stays fixed-height and visible across every module switch,
   same "global, not per-module" contract shell-sidebar-footer already
   established for Refresh data. */
.shell-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.shell-topbar {
  flex-shrink: 0;
  display: flex;
  justify-content: flex-end;
  padding: var(--sp-3) var(--sp-6);
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.shell-content { flex: 1; }

/* PersonName — a subtle inline link, never button chrome: color/underline
   shift on hover only, same affordance language as .id-code-link's own
   hover treatment, so a name reads as "quietly clickable," not a loud CTA
   competing with the row it sits in. */
.person-name-link {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  font-weight: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  border-bottom: 1px dotted transparent;
  transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.person-name-link:hover { color: var(--teal); border-bottom-color: var(--teal); }
.person-name-link:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* Employee Card drawer stacks ABOVE the reconciliation case Drawer
   (z-index 20) — a PersonName inside that drawer's own body can open this
   one on top of it, so this needs to win regardless of source order
   (compound selector, not just append-order-wins). */
.drawer-backdrop.employee-card-backdrop { z-index: 25; }
.employee-card-drawer { width: 620px; --card-section-space: var(--sp-4); }
.employee-card-header-actions { display: flex; align-items: center; gap: 2px; }
/* The discrepancy alert stays a .risk-banner — that's a language shared with
   People, Balances and Timesheets, and an alert is not a section. It still
   sits in the card's stack, so it reads the same rhythm token. */
.employee-card-drawer .risk-banner { margin-bottom: var(--card-section-space); }

.employee-card-top { display: flex; gap: var(--sp-4); align-items: flex-start; margin-bottom: var(--sp-4); }
.employee-card-avatar {
  flex-shrink: 0;
  width: 56px;
  height: 56px;
  border-radius: var(--r-pill);
  background: var(--teal-soft);
  color: var(--teal);
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}
.employee-card-identity { min-width: 0; }
.employee-card-name { font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 700; line-height: 1.2; }
.employee-card-subline { margin-top: 4px; font-size: var(--fs-sm); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.employee-card-contact { margin-top: 4px; font-size: var(--fs-sm); }

.employee-card-facets { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: var(--card-section-space); }

/* Employee Card Phase 4 part 2 — the AI explanation block. Deliberately
   OFF the severity palette (no teal/rust/amber/violet): this is generated
   narration, not a data field or a status signal, so it's set apart by
   TYPOGRAPHY + container instead of a hue that would otherwise imply a
   tier/urgency this text doesn't carry. Cream (--paper) against the
   white drawer/panel background reads as a distinct "aside," the same
   way the app shell's own cream page background already sits behind
   white panels everywhere else. */
.ai-summary-label {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--muted);
}
.ai-summary-icon { font-size: 13px; }
.ai-summary-context { text-transform: none; letter-spacing: normal; font-weight: 600; }
.ai-summary-text {
  margin: 6px 0 0;
  font-size: var(--fs-sm);
  font-style: italic;
  color: var(--ink);
  line-height: 1.5;
}

/* Part B (response-aware chasing) — the Employee Card's TextUs
   Conversation section. The tag badge inside it reuses the SAME
   badge-reply-* classes the Chase tab uses, so a "needs_help" reads
   identically in both places.

   Conversation renders in the shared .employee-card-section like everything
   else (2026-09-04) — white ground, card padding, card rhythm, no box of its
   own. Only the transcript itself keeps a rule here: bounded height with its
   own scroll, in BOTH the drawer and the full page, because a conversation
   can run to hundreds of messages and an unbounded section is exactly what
   collapsing it was meant to solve. */
/* The transcript is a bounded, SCROLLABLE region and has to look like one.
   It reads as an unpadded section otherwise: the 320px window slices the
   last message mid-sentence, flush against the card's own edge, so the
   section looks like content overflowing a box rather than a scroller with
   more inside it. The section's padding was never missing — measured 24px on
   all four sides — but at 1,174px wide with nothing bounding the messages
   there was no visual edge for it to read against.

   A conversation is also a COLUMN, not a full-page band: at the old width a
   bubble ran to 667px and the chat shape disappeared entirely. One rule,
   scoped under the shared section wrapper, on the --sp-* scale (CLAUDE.md's
   UI convention). */
.employee-card-section .conversation-messages {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  max-height: 320px;
  overflow-y: auto;
  max-width: 640px;
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper);
}
.conversation-bubble {
  max-width: 85%;
  padding: 8px 12px;
  border-radius: var(--r-md);
  font-size: 13px;
}
.conversation-bubble-out { align-self: flex-end; background: var(--teal-soft); color: var(--ink); }
.conversation-bubble-in { align-self: flex-start; background: var(--panel); border: 1px solid var(--line); color: var(--ink); }
.conversation-bubble-text { margin: 0; white-space: pre-wrap; }
.conversation-bubble-time { display: block; margin-top: 4px; font-size: 10.5px; color: var(--muted); }

/* Mini chargeability trend (Jan..current month) — plain divs, same "no
   SVG at small scale" convention the placement timeline already
   established. Bars share the exact band coloring/thresholds as the full
   Chargeability module's own monthly chart; the in-progress current
   month reuses that chart's neutral hatched "provisional" fill so a
   partial month never reads as a real dip, at any scale. */
.chargeability-trend { display: flex; align-items: flex-end; gap: 3px; height: 58px; margin-top: 6px; }
.chargeability-trend-col { flex: 1; height: 100%; display: flex; flex-direction: column; min-width: 4px; }
.chargeability-trend-plot { flex: 1; display: flex; align-items: flex-end; }
.chargeability-trend-label {
  font-size: 9.5px; color: var(--muted); text-align: center; margin-top: var(--sp-1);
  overflow: hidden; white-space: nowrap;
}
.chargeability-trend-bar { width: 100%; border-radius: 2px 2px 0 0; min-height: 3px; }
.chargeability-trend-bar-high { background: var(--teal); }
.chargeability-trend-bar-mid { background: var(--amber); }
.chargeability-trend-bar-low { background: var(--rust); }
.chargeability-trend-bar-none { background: var(--line); }
.chargeability-trend-bar-provisional {
  background-image: repeating-linear-gradient(45deg, var(--line), var(--line) 3px, var(--panel) 3px, var(--panel) 6px);
}

/* Discrepancy alert — an amber severity alongside the existing violet/calm
   risk-banner variants (Holiday Balance's anomaly band); amber reads as
   "worth a look," not urgent-red, matching the amber tier badge/chargeability-
   mid band vocabulary already established elsewhere in this app. */
.risk-banner-amber { background: var(--amber-soft); color: var(--amber); }
.discrepancy-list {
  margin-top: var(--sp-2);
  margin-bottom: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  overflow: hidden;
}
.discrepancy-row {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-bottom: 1px solid var(--line);
  font-size: var(--fs-sm);
}
.discrepancy-row:last-child { border-bottom: none; }
.discrepancy-field { flex: 0 0 90px; font-weight: 700; color: var(--muted); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.04em; }
.discrepancy-value { flex: 1; min-width: 0; word-break: break-word; }
.discrepancy-value-label { color: var(--muted); font-size: var(--fs-xs); font-weight: 700; margin-right: 4px; }

/* RiskTab's low-tier disclosure. Was a shared selector with the Employee
   Card's own .drawer-section until 2026-09-04, when the card's sections all
   moved to .employee-card-section below — this is now the only user, so it
   keeps the rules it always had rather than inheriting a rhythm meant for a
   different surface. */
.exit-risk-subsection {
  margin-top: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--panel);
  box-shadow: var(--shadow-1);
  overflow: hidden;
}
.exit-risk-subsection summary {
  cursor: pointer;
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--muted);
  padding: var(--sp-3) var(--sp-4);
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  transition: color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.exit-risk-subsection summary::-webkit-details-marker { display: none; }
.exit-risk-subsection summary::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 5px solid var(--muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--dur-fast) var(--ease);
  flex-shrink: 0;
}
.exit-risk-subsection[open] summary::before { transform: rotate(90deg); }
.exit-risk-subsection summary:hover { color: var(--ink); background: var(--paper); }
.exit-risk-subsection[open] summary { border-bottom: 1px solid var(--line); }

/* ===== THE Employee Card section (2026-09-04) =================================
   ONE wrapper, one rule, every section in both the drawer and the full page.
   It owns the two properties that kept drifting — the section's own padding
   and the gap to the next section — and takes BOTH from --card-section-space,
   so they are the same number by construction and a new section cannot
   arrive with its own.

   Before this, the full page rendered three different section paddings
   (12/16, 16/24, 24) with gaps measured at 0/16/24/32px. There is now
   exactly one of each per context.

   Deliberately no `gap` on the parent columns: the margin below owns the
   rhythm on its own. Two mechanisms for one gap is how you get 48px where
   you meant 24. */
/* A section is a discrete CARD on the cream ground (2026-09-04). The wrapper
   owns the gap to the next card; its head and body own the padding — all
   three from --card-section-space, so they can never disagree. That single
   source is the whole point of the token, and employeeCardSpacing.test.js
   fails if any of the three is hardcoded or comes from anywhere else.
   No shadow: the house rule is hairlines (CLAUDE.md), and a uniform grey
   shadow under every one of nine cards is what makes a page look templated.
   Elevation is spent once, on the identity header. */
.employee-card-section {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  margin: 0 0 var(--card-section-space);
  overflow: hidden;
}
.employee-card-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--card-section-space);
  border-bottom: 1px solid var(--line);
}
.employee-card-section-body { padding: var(--card-section-space); }
/* Collapsed: the head is the whole card, so it carries no divider. */
.employee-card-section-collapsible:not([open]) .employee-card-section-head { border-bottom: 0; }
.employee-card-section-collapsible > summary { cursor: pointer; list-style: none; }
.employee-card-section-collapsible > summary::-webkit-details-marker { display: none; }
/* The caret belongs to the TITLE, not to the head. On the head it is a flex
   item of its own, so `justify-content: space-between` pushes the title to
   the opposite edge and a section with no status chip reads right-aligned. */
.employee-card-section-collapsible > summary .employee-card-section-title::before {
  content: '\25b8'; color: var(--muted); font-size: 9px; margin-right: var(--sp-2);
}
.employee-card-section-collapsible[open] > summary .employee-card-section-title::before { content: '\25be'; }
.employee-card-section-collapsible > summary:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.employee-card-section-count {
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted);
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 2px 8px; margin-left: var(--sp-2); font-variant-numeric: tabular-nums;
}
.employee-card-section-right { display: inline-flex; align-items: center; gap: var(--sp-2); }
/* Severity on the card's own edge, matching the metric deck — never a filled
   panel, which is something a reader stops seeing. */
/* The Bullhorn-user head carries two chips (state, then which key proved
   the match). ONE rule, scoped under the section and on the --sp-* scale,
   per CLAUDE.md's spacing convention — no value hand-picked at the call
   site. */
.employee-card-section .employee-card-bh-user-state {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}
.employee-card-section-accent-rust { border-left: 3px solid var(--rust); }
.employee-card-section-accent-amber { border-left: 3px solid var(--amber); }
.employee-card-section:last-child { margin-bottom: 0; }

/* Type scales with the surface the same way the spacing does — one rule, one
   contextual step — so the drawer doesn't stack four 19px serif headings in
   620px. Serif at --fs-md matches the drawer's own header (.drawer-header h3). */
.employee-card-section-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  color: var(--ink);
}
.employee-fullpage .employee-card-section-title { font-size: var(--fs-lg); }

/* Adjacent-sibling, so a section with no title (the AI summary) gets no
   phantom leading space. One value, defined once. */
.employee-card-section-title + .employee-card-section-body { margin-top: var(--sp-3); }

.employee-card-section-collapsible > .employee-card-section-title {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  transition: color var(--dur-fast) var(--ease);
}
.employee-card-section-collapsible > .employee-card-section-title::-webkit-details-marker { display: none; }
.employee-card-section-collapsible > .employee-card-section-title::before {
  content: '';
  width: 0;
  height: 0;
  border-left: 6px solid var(--muted);
  border-top: 5px solid transparent;
  border-bottom: 5px solid transparent;
  transition: transform var(--dur-fast) var(--ease);
  flex-shrink: 0;
}
.employee-card-section-collapsible[open] > .employee-card-section-title::before { transform: rotate(90deg); }
.employee-card-section-collapsible > .employee-card-section-title:hover { color: var(--teal); }
.employee-card-section-collapsible > .employee-card-section-title:focus-visible {
  outline: 2px solid var(--teal);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* Tone modifiers change CHARACTER only, never spacing — that stays the
   wrapper's job. 'ai' keeps the AI summary's deliberate off-the-severity-
   palette treatment (cream ground, no elevation: narration, not a data
   field or a status signal). */
.employee-card-section-ai { background: var(--paper); box-shadow: none; }

/* The three inline `style={{ marginTop }}` nudges the full page used to
   carry were all the same thing: separate this from the meter bar above it.
   One rule instead of three hand-picked values at three call sites. */
.employee-card-section .kpi-meter { margin-top: var(--sp-2); }
/* A long fact keeps its own line breaks (an address over two lines). */
.employee-card-section .employee-card-fact-long { white-space: pre-line; }
/* The Accounts section's table (2026-10-01, Access M6): grouped by address,
   one row per account. Internal spacing only, from the --sp-* scale; the
   section's own padding stays the card token's. */
.employee-card-section .ec-acc-wrap { overflow-x: auto; }
.employee-card-section .ec-acc { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.employee-card-section .ec-acc th { text-align: left; font-size: var(--fs-xs); font-weight: 600; color: var(--muted); padding: var(--sp-1) var(--sp-2); border-bottom: 1px solid var(--line); white-space: nowrap; }
.employee-card-section .ec-acc td { padding: var(--sp-1) var(--sp-2); border-bottom: 1px solid var(--line); vertical-align: top; }
.employee-card-section .ec-acc-group td { background: var(--paper); font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
.employee-card-section .ec-acc-sys { font-weight: 600; white-space: nowrap; }
.employee-card-section .ec-acc-mono { font-family: var(--font-mono); font-size: var(--fs-xs); white-space: nowrap; }
.employee-card-section .ec-acc-row-amber td:first-child { box-shadow: inset 3px 0 0 var(--amber); }
.employee-card-section .ec-acc-row-rust td:first-child { box-shadow: inset 3px 0 0 var(--rust); }
.employee-card-section .ec-acc-tag { font-weight: 700; white-space: nowrap; }
.employee-card-section .ec-acc-ok { color: var(--teal); }
.employee-card-section .ec-acc-amber { color: var(--amber); }
.employee-card-section .ec-acc-rust { color: var(--rust); }
.employee-card-section .ec-acc-quiet { color: var(--muted); }
.employee-card-section .ec-acc-todo { display: block; font-size: var(--fs-xs); color: var(--muted); }
.employee-card-section .ec-acc-note { font-size: var(--fs-xs); margin: var(--sp-2) 0; }
.employee-card-section .kpi-meter + .drawer-field,
.employee-card-section .kpi-meter + .drawer-field-label { margin-top: var(--sp-3); }

.employee-card-placement {
  padding: var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  margin-bottom: var(--sp-3);
}
.employee-card-placement:last-child { margin-bottom: 0; }
.employee-card-placement-top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; gap: var(--sp-2); }
.employee-card-placement-meta { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; font-size: var(--fs-sm); color: var(--muted); }

/* Employee Card full page (Phase 3) — a standalone route (no AppShell
   sidebar), the SAME buildEmployeeCard() data as the drawer at a roomier,
   two-column scale. */
.employee-fullpage-topbar {
  display: flex;
  align-items: center;
  padding: var(--sp-3) var(--sp-6);
  border-bottom: 1px solid var(--line);
  background: var(--panel);
}
.employee-fullpage-back { color: var(--muted); font-weight: 600; font-size: var(--fs-sm); text-decoration: none; }
.employee-fullpage-back:hover { color: var(--teal); text-decoration: underline; }

.employee-fullpage { padding: var(--sp-6) var(--sp-7) 60px; max-width: 1320px; width: 100%; margin: 0 auto; }

/* Larger than the drawer's own .employee-card-top/-avatar/-name — the
   full page is the "roomier" scale, same shapes, bigger. */
.employee-fullpage-header { margin-bottom: var(--sp-4); }
.employee-fullpage-header .employee-card-avatar { width: 88px; height: 88px; font-size: var(--fs-2xl); }
.employee-fullpage-header .employee-card-name { font-size: var(--fs-2xl); }
.employee-fullpage-header .employee-card-subline { font-size: var(--fs-base); margin-top: 6px; }
.employee-fullpage-header .employee-card-contact { font-size: var(--fs-base); }
.employee-fullpage-facets { margin-bottom: var(--card-section-space); }

.employee-fullpage-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-6);
  align-items: start;
}
/* No `gap` on purpose — .employee-card-section's own margin-bottom is the
   ONE mechanism for the gap between sections. Having both is how you end up
   with 48px where you meant 24. */
.employee-fullpage-col { display: flex; flex-direction: column; min-width: 0; }

/* The headline pair: a measured figure and a scenario, side by side and
   separately labelled, because blending them makes a number that is
   neither. The projected value is set slightly back (lighter weight, no
   meter of its own) so the measured one still leads. */
.kpi-hero-pair { display: flex; flex-wrap: wrap; gap: var(--sp-6); align-items: flex-start; }
.kpi-hero-secondary .kpi-hero-value { font-size: var(--fs-2xl); }
.kpi-hero-value-projected { opacity: 0.75; }
.badge-forecast {
  background: var(--paper);
  color: var(--muted);
  border: 1px dashed var(--muted);
}

/* ===== Chargeability forecast (2026-09-04) ================================
   The forecast is a SCENARIO, and every visual choice here exists to keep
   that legible: projected bars keep their band colour (a full month's
   projection IS a real value worth reading) but carry a hatch, a lighter
   ground and a dashed outline, and a labelled boundary rule separates them
   from what actually happened. The neutral grey hatch stays reserved for a
   PROVISIONAL month — partial actual data — which is a different claim. */
.chargeability-bar-forecast {
  stroke-width: 1.5;
  stroke-dasharray: 3 2;
  opacity: 0.9;
}
.chargeability-forecast-boundary line {
  stroke: var(--muted);
  stroke-width: 1;
  stroke-dasharray: 4 3;
}
.chargeability-forecast-boundary-label {
  fill: var(--muted);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.chargeability-forecast-tag { color: var(--muted); font-weight: 600; }
.chargeability-forecast-swatch {
  display: inline-block;
  width: 12px;
  height: 12px;
  border-radius: 3px;
  border: 1.5px dashed var(--teal);
  background: repeating-linear-gradient(45deg, var(--teal) 0 3px, var(--paper) 3px 6px);
}
/* The scenario caveat under the chart. Not an error, not a hint — a
   condition of the number above it, so it reads as part of the chart. */
.chargeability-scenario-note {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-sm);
  color: var(--muted);
  font-style: italic;
}

/* --- Employee Card: Expected chargeability ------------------------------ */
.forecast-basis { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); color: var(--ink); }
.forecast-basis-bench { color: var(--rust); }
.forecast-months { display: flex; flex-direction: column; gap: var(--sp-2); }
.forecast-month {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas: 'label value' 'detail value';
  align-items: center;
  column-gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-top: 1px solid var(--line);
}
.forecast-month:first-child { border-top: none; padding-top: 0; }
.forecast-month-label { grid-area: label; font-weight: 600; font-size: var(--fs-sm); }
.forecast-month-detail { grid-area: detail; font-size: var(--fs-xs); color: var(--muted); }
.forecast-month-value {
  grid-area: value;
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.forecast-month-value-high { color: var(--teal); }
.forecast-month-value-mid { color: var(--amber); }
.forecast-month-value-low { color: var(--rust); }
.forecast-month-value-none { color: var(--muted); }
.forecast-lowpoint { margin: var(--sp-3) 0 0; font-size: var(--fs-sm); color: var(--ink); }
.forecast-scenario-note {
  margin: var(--sp-3) 0 0;
  font-size: var(--fs-xs);
  color: var(--muted);
  font-style: italic;
}

/* --- Settings: the seasonal curve --------------------------------------- */
.curve-status { margin-bottom: var(--sp-2); font-size: var(--fs-sm); }
.curve-note { margin: 0 0 var(--sp-4); font-size: var(--fs-sm); color: var(--ink); max-width: 68ch; }
.curve-preview { display: flex; align-items: flex-end; gap: var(--sp-2); height: 110px; margin-bottom: var(--sp-4); }
.curve-preview-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; height: 100%; }
.curve-preview-track { flex: 1; width: 100%; display: flex; align-items: flex-end; }
.curve-preview-bar { width: 100%; background: var(--teal); border-radius: 3px 3px 0 0; min-height: 2px; }
.curve-preview-label { font-size: var(--fs-xs); color: var(--muted); }
.curve-inputs { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-4); }
.curve-input { display: flex; flex-direction: column; gap: 4px; }
.curve-input-label { font-size: var(--fs-xs); color: var(--muted); font-weight: 700; }
.curve-input input {
  height: var(--control-h);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-2);
  font-size: var(--fs-sm);
  background: var(--panel);
  color: var(--ink);
  width: 100%;
}
.curve-input input:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; border-color: var(--teal); }
.curve-actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.curve-message { margin: var(--sp-3) 0 0; font-size: var(--fs-sm); color: var(--teal); font-weight: 600; }
.curve-scenario-note { margin: var(--sp-3) 0 0; font-size: var(--fs-xs); color: var(--muted); font-style: italic; }

@media (max-width: 760px) {
  .curve-inputs { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* Progressive disclosure on the Holiday balance section — the summary
   numbers stay inline, this opens their day-by-day composition in the
   shared TimeOffDetailPanel. Secondary weight on purpose: it's an optional
   deeper look, not the section's point. */
.employee-card-timeoff-btn { margin-top: var(--sp-3); }

/* Placement timeline — employment window + every placement's span on ONE
   shared time axis, plain divs (not SVG — this is a handful of rows, not
   a full chart with gridlines/tooltips), so a gap or overlap is something
   you see, not something you compute from two date columns. */
.timeline { margin-bottom: var(--sp-4); }
.timeline-row { display: flex; align-items: center; gap: var(--sp-3); margin-bottom: var(--sp-2); }
.timeline-row-label { flex: 0 0 160px; font-size: var(--fs-sm); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.timeline-track { position: relative; flex: 1; height: 18px; background: var(--paper); border-radius: var(--r-sm); }
.timeline-bar { position: absolute; top: 0; height: 100%; border-radius: var(--r-sm); min-width: 3px; }
.timeline-bar-employment { background: var(--teal); opacity: 0.85; }
.timeline-bar-placement { background: var(--violet); }
.timeline-bar-ongoing { background-image: repeating-linear-gradient(45deg, rgba(255,255,255,0.35) 0, rgba(255,255,255,0.35) 4px, transparent 4px, transparent 8px); }
.timeline-today { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--rust); }
.timeline-axis { display: flex; justify-content: space-between; margin-top: 4px; padding-left: 172px; font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }

/* Discrepancies, expanded — every check shown, not just mismatches (the
   drawer's collapsed count is the quick version; this is the point of the
   full page). One indicator column instead of a status/tag column pair. */
.discrepancy-fulltable { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.discrepancy-fulltable-header, .discrepancy-fulltable-row {
  display: grid;
  grid-template-columns: 110px 1fr 1fr 120px;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  align-items: center;
}
.discrepancy-fulltable-header {
  background: #f1ede2;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  font-weight: 700;
}
.discrepancy-fulltable-row { border-top: 1px solid var(--line); font-size: var(--fs-sm); }
.discrepancy-fulltable-row .discrepancy-value { word-break: break-word; }
.discrepancy-indicator { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; font-size: var(--fs-xs); white-space: nowrap; }
.discrepancy-indicator-match { color: var(--teal); }
.discrepancy-indicator-mismatch { color: var(--amber); }
.discrepancy-indicator-nodata { color: var(--muted); font-weight: 500; }

/* Global find bar — a compact combobox in the top bar, available from
   every module. Deliberately plain (a search input + a dropdown), not a
   full directory UI. */
.find-bar { position: relative; width: 320px; }
.find-bar-input {
  width: 100%;
  padding: 9px var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-pill);
  background: var(--paper);
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
}
.find-bar-input:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; border-color: var(--teal); }
.find-bar-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  right: 0;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-3);
  max-height: 320px;
  overflow-y: auto;
  z-index: 15;
  animation: bridge-fade-in var(--dur-fast) var(--ease);
}
.find-bar-empty { padding: var(--sp-3) var(--sp-4); color: var(--muted); font-size: var(--fs-sm); }
.find-bar-result {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  padding: var(--sp-2) var(--sp-4);
  border: none;
  background: none;
  text-align: left;
  cursor: pointer;
  font-size: var(--fs-sm);
}
.find-bar-result:hover, .find-bar-result.active { background: var(--teal-soft); }
.find-bar-result-name { font-weight: 600; }
.find-bar-result-meta { color: var(--muted); font-size: var(--fs-xs); }

/* Tablet range (2026 addition) — previously nothing existed between the
   760px mobile breakpoint and full-width desktop; the two side-by-side PAW
   filter cards (min-width: 280px each) could overflow a 1280-1366px laptop
   viewport once the 248px rail + content padding are subtracted. */
@media (max-width: 1024px) {
  .paw-filters { gap: var(--sp-3); }
}

@media (max-width: 760px) {
  .shell { flex-direction: column; }
  .shell-sidebar {
    width: 100%;
    height: auto;
    flex-direction: row;
    align-items: center;
    border-right: none;
    border-bottom: 1px solid var(--line);
    padding: 12px 16px;
    gap: 16px;
  }
  .nav-section-label { display: none; }
  .shell-sidebar-head { padding: 0; min-height: 0; flex-shrink: 0; }
  .brand { flex-shrink: 0; }
  /* Below this width the sidebar is already a horizontal top bar costing no
     horizontal space, so the collapsed state is fully neutralised and the
     toggle hidden rather than offering a control that does nothing. */
  .sidebar-toggle { display: none; }
  .shell-sidebar.is-collapsed { width: 100%; }
  .shell-sidebar.is-collapsed .shell-sidebar-head { flex-direction: row; gap: var(--sp-2); }
  .shell-sidebar.is-collapsed .brand-name { display: revert; }
  .shell-sidebar.is-collapsed .module-nav-item { justify-content: space-between; padding: var(--sp-2) var(--sp-3); }
  .shell-sidebar.is-collapsed .module-nav-main { gap: var(--sp-3); }
  .shell-sidebar.is-collapsed .module-nav-label {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }
  .module-nav-tip { display: none !important; }
  .shell-sidebar.is-collapsed .run-meta { display: revert; }
  .shell-sidebar.is-collapsed .refresh-btn { width: auto; height: auto; margin: 0; padding: revert; }
  .shell-sidebar.is-collapsed .refresh-btn-icon { display: none; }
  .shell-sidebar.is-collapsed .refresh-btn-label {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip-path: none;
  }
  .module-nav { flex-direction: row; padding: 0; overflow-x: auto; }
  .module-nav-item { white-space: nowrap; }
  .shell-sidebar-footer { margin-top: 0; margin-left: auto; padding: 0 0 0 16px; border-top: none; border-left: 1px solid var(--line); flex-shrink: 0; }
  .refresh-control { flex-direction: row; align-items: center; gap: var(--sp-3); }
  .refresh-btn { width: auto; white-space: nowrap; }
  .people-module, .module-placeholder, .paw-module, .chargeability-module, .risk-module, .extension-module, .timesheets-module, .settings-module, .mileage-module, .payroll-module, .data-quality-module, .access-module { padding-left: 16px; padding-right: 16px; }
  .paw-stat-row { gap: 14px; }
  .segmented { overflow-x: auto; }
  .paw-filter-group { min-width: 100%; }
  .drawer-columns { flex-direction: column; }
  .connector { display: none; }
  .data-table { font-size: 12px; }
  .confirm-modal { width: 100%; padding: 18px; }
  .kpi-hero-value { font-size: 32px; }
  .category-breakdown-row { gap: 16px; }
  .shell-topbar { padding: 12px 16px; }
  .find-bar { width: 100%; }
  .employee-card-drawer { width: 100%; }
  .paw-report-drawer { width: 100%; }
  .employee-card-top { flex-direction: column; align-items: flex-start; }
  .employee-fullpage { padding-left: 16px; padding-right: 16px; }
  .employee-fullpage-grid { grid-template-columns: 1fr; }
  .timeline-row-label { flex-basis: 100px; }
  .timeline-axis { padding-left: 112px; }
  .discrepancy-fulltable-header, .discrepancy-fulltable-row { grid-template-columns: 80px 1fr 1fr 90px; font-size: 12px; }
}

@media (max-width: 1024px) {
  .employee-fullpage-grid { grid-template-columns: 1fr; }
}

/* Chase tab (Phase 3, TextUs SMS send-flow) — the one screen in this app
   where a click can reach a real phone. Everything here is deliberately
   MORE emphatic than the app's usual quiet register: the test-mode banner
   is the single most important thing on the page, so it gets the most
   craft — a small breathing dot (paused for prefers-reduced-motion) and
   copy that says exactly what will and won't happen, not just a label. */
.chase-tab { display: flex; flex-direction: column; gap: var(--sp-4); }

@keyframes bridge-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

.chase-testmode-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 18px;
  border-radius: var(--r-md);
  font-size: 13px;
  box-shadow: var(--shadow-1);
}
.chase-testmode-dot {
  width: 9px; height: 9px; border-radius: 50%;
  animation: bridge-pulse 1.8s ease-in-out infinite;
  flex-shrink: 0;
}
@media (prefers-reduced-motion: reduce) {
  .chase-testmode-dot { animation: none; }
  /* Snap instead of slide; the width is applied synchronously either way,
     so the transitionend listener isn't load-bearing. */
  .shell-sidebar { transition: none; }
}
.chase-testmode-label { font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; font-size: 12px; }
.chase-testmode-detail { color: inherit; opacity: 0.85; }
/* Inverted from this app's usual amber=caution/teal=healthy reading, on
   purpose: here "test mode" IS the safe, expected state (amber = "an
   ongoing deliberate safeguard, not an alarm"), and "live" is the state
   that deserves a calm, confident, unmistakably-different treatment
   (solid teal, no pulse) — never the same visual as an error state. */
.chase-testmode-test { background: var(--amber-soft); color: #6b4a06; }
.chase-testmode-test .chase-testmode-dot { background: var(--amber); }
.chase-testmode-live { background: var(--teal-soft); color: var(--teal); }
.chase-testmode-live .chase-testmode-dot { background: var(--teal); animation: none; }

.chase-kpi-band { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
.chase-kpi-band-secondary { opacity: 0.88; }
.chase-kpi-tile {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  padding: 10px 16px;
  border-radius: var(--r-md);
  border: 1px solid var(--line);
  background: var(--panel);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease);
  min-width: 108px;
}
.chase-kpi-tile:hover { box-shadow: var(--shadow-1); }
.chase-kpi-tile:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.chase-kpi-tile-active { border-color: var(--ink); box-shadow: var(--shadow-2); }
.chase-kpi-count { font-family: var(--font-display); font-size: var(--fs-2xl); line-height: 1; color: var(--ink); }

.chase-filter-bar { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--sp-3); margin-top: -4px; }
.chase-filter-row { margin: 0; }
.chase-cpowner-field { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); }
.chase-cpowner-field select { font-size: 13px; }

/* The action bar sits ABOVE the table (deliberately, per the load-time/UX
   fix — HR shouldn't have to scroll a long list to find Send) and stays
   stuck to the top of the viewport while scrolling through the table below
   it, so select-all/the running count/Send are always reachable. */
.chase-action-bar {
  position: sticky;
  top: 0;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: 10px 14px;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-1);
}
.chase-action-bar-select { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.badge-chase-ready { background: var(--teal-soft); color: var(--teal); }
.badge-chase-foreign { background: var(--amber-soft); color: var(--amber); }
.badge-chase-pick { background: var(--violet-soft); color: var(--violet); }
.badge-chase-cant { background: var(--line); color: var(--muted); }

.chase-select-cell { width: 32px; text-align: center; }
.chase-classification-cell { min-width: 220px; }
.chase-phone-detail { margin-top: 6px; display: flex; flex-direction: column; gap: 2px; font-size: 12px; }
.chase-phone-number { font-variant-numeric: tabular-nums; font-weight: 600; }
.chase-phone-account { color: var(--muted); }
.chase-flag-note { margin: 4px 0 0; color: var(--amber); font-size: 12px; max-width: 260px; }

.chase-pick-fieldset { margin-top: 8px; border: 1px dashed var(--violet); border-radius: var(--r-sm); padding: 8px 10px; }
.chase-pick-fieldset legend { font-size: 11px; color: var(--violet); padding: 0 4px; }
.chase-pick-option { display: flex; align-items: center; gap: 6px; font-size: 12.5px; font-variant-numeric: tabular-nums; padding: 3px 0; }
.chase-pick-option em { color: var(--muted); font-style: italic; }
.chase-pick-disabled { opacity: 0.5; }

.chase-unusable-note { margin: 6px 0 0; color: var(--muted); font-size: 12px; max-width: 260px; }

.chase-message-cell { min-width: 280px; max-width: 360px; }
.chase-message-text { margin: 0; font-size: 13px; white-space: pre-wrap; }
.chase-message-pending { margin: 0; font-size: 13px; color: var(--muted); font-style: italic; }
.chase-message-error { margin: 0; font-size: 13px; color: var(--rust); }
.chase-message-textarea {
  width: 100%;
  font-family: inherit;
  font-size: 13px;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  padding: 8px;
  resize: vertical;
}
.chase-message-textarea:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.chase-message-actions { display: flex; gap: 6px; margin-top: 6px; }

.btn-sm { padding: 4px 10px; font-size: 12px; }

.chase-count-badge { font-size: 11px; color: var(--muted); white-space: nowrap; }
.chase-count-badge-active { color: var(--rust); font-weight: 600; }
.chase-attempt-note { display: block; margin-top: 4px; font-size: 10.5px; color: var(--violet); max-width: 120px; }

/* Phase A (response-aware chasing, read side) — the Reply column + the
   row-level "this person responded" cue. A left accent bar (not a full
   background tint like chase-row-failed) so it reads as "worth a look",
   not an error/alarm — it can coexist with chase-row-failed on the same
   row (a send failed AND an unrelated earlier reply exists) without the
   two visually fighting. */
.chase-row-replied { box-shadow: inset 3px 0 0 var(--teal); }
.chase-reply-cell { min-width: 220px; max-width: 300px; }
.chase-reply-badge { display: inline-block; font-size: 11px; font-weight: 700; color: var(--teal); background: var(--teal-soft); padding: 2px 8px; border-radius: var(--r-sm); margin-bottom: 4px; }
.chase-reply-text { margin: 0; font-size: 13px; white-space: pre-wrap; }
.chase-reply-time { display: block; margin-top: 4px; font-size: 11px; color: var(--muted); }
.chase-reply-none { font-size: 12.5px; color: var(--muted); font-style: italic; }

/* Phase C (response-aware chasing, wired) — the reply-classifier's own
   badges, the needs_help queue, and the "done" set-aside treatment.
   Philosophy: the classifier ADVISES, HR decides — nothing here ever
   hides a person permanently or without a one-click way back. */
.chase-reply-ai-line { margin: 6px 0 0; font-size: 11.5px; }
.chase-reply-ai-reason { color: var(--muted); }
.badge-reply-done { background: var(--teal-soft); color: var(--teal); }
.badge-reply-willdo { background: var(--teal-soft); color: var(--teal); }
.badge-reply-needshelp { background: var(--rust-soft); color: var(--rust); }
.badge-reply-snooze { background: var(--violet-soft); color: var(--violet); }
.badge-reply-unclear { background: var(--line); color: var(--muted); }
.chase-reinclude-btn { margin-top: 6px; }

/* A "done" row stays in the table (never removed) but reads as set-aside:
   dimmed content, sorted to the bottom (sortDoneToBottom) — distinct from
   chase-row-failed's full rust-soft background (an error demands
   attention; a claimed-done row is the opposite, quietly out of the way
   until re-included). */
   The Messages tab's HELD rows (somebody who replied, kept out of the
   batch) read as exactly the same thing, so they share the rule rather
   than picking a second opacity — "quietly out of the way, still there"
   should look the same on both screens. */
.chase-row-done,
.chase-row-held { opacity: 0.6; }
.chase-row-done:hover,
.chase-row-held:hover { opacity: 0.85; }

.chase-needshelp-queue { margin-bottom: var(--sp-2); }
.chase-needshelp-title { font-family: var(--font-display); font-size: var(--fs-lg); color: var(--rust); margin: 0 0 var(--sp-2); }
.chase-needshelp-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--sp-3); }
.chase-needshelp-card {
  border: 1px solid var(--rust);
  background: var(--rust-soft);
  border-radius: var(--r-md);
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.chase-needshelp-card-handled { border-color: var(--line); background: var(--paper); opacity: 0.75; }
.chase-needshelp-card-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; }
.chase-needshelp-reply { margin: 0; font-size: 13.5px; font-style: italic; }
.chase-needshelp-card-actions { display: flex; gap: 8px; margin-top: 4px; }

.chase-result-cell { min-width: 140px; }
.chase-result { display: flex; align-items: center; gap: 6px; font-size: 12.5px; }
.chase-result-ok { color: var(--teal); }
.chase-result-fail { color: var(--rust); flex-wrap: wrap; }
.chase-row-failed { background: var(--rust-soft); }

.chase-send-count { color: var(--muted); font-size: 13px; }

/* "Draft all" bulk-compose progress — replaces the button itself while a
   batch runs (never both at once), so there's exactly one thing in that
   slot at any time: an idle button, or a running progress readout. */
.chase-draftall-progress { display: flex; align-items: center; gap: 8px; }
.chase-draftall-progress-label { font-size: 12.5px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.chase-draftall-progress-bar { width: 96px; height: 6px; border-radius: var(--r-sm); background: var(--line); overflow: hidden; }
.chase-draftall-progress-fill { height: 100%; background: var(--teal); transition: width var(--dur-fast) var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .chase-draftall-progress-fill { transition: none; }
}

/* The confirm-summary gate — the deliberate last stop before a real send.
   The count gets the same serif "KPI hero" treatment as this app's other
   big numbers (module titles, chargeability heroes): this is the one
   moment on the page where a number deserves to look important. */
.chase-confirm-modal { max-width: 420px; }
.chase-confirm-hero { font-family: var(--font-display); font-size: var(--fs-2xl); color: var(--teal); margin: 0 4px; }
.chase-confirm-testmode {
  background: var(--amber-soft);
  color: #6b4a06;
  border-radius: var(--r-sm);
  padding: 8px 12px;
  font-size: 12.5px;
  margin: 0 0 14px;
}
.chase-confirm-breakdown { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.chase-confirm-breakdown li { font-size: 13.5px; display: flex; align-items: center; gap: 6px; }
.chase-confirm-breakdown code { background: var(--paper); padding: 1px 6px; border-radius: 4px; font-size: 12px; }
.chase-confirm-breakdown-line { font-size: 13.5px; display: block; margin: 4px 0; }
.chase-confirm-breakdown-line code { background: var(--paper); padding: 1px 6px; border-radius: 4px; font-size: 12px; }

/* ---------------------------------------------------------------------
   Chase-UX restructure (2026-09) — Part 1: Tracking's "Contact"/"Again"
   action + touch-points. The action cell is deliberately compact (a
   table column, not a card) — status badges reuse the SAME badge-reply-*
   classes the reply classifier already established (BRIDGE.md: one signal,
   one color, everywhere it appears), never a new color for the same tag. */
.ts-contact-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; min-width: 120px; }
.ts-contact-touchpoints { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.ts-contact-anyway { opacity: 0.75; }
.ts-contact-anyway:hover { opacity: 1; }

.chaser-banner-row { display: flex; align-items: stretch; gap: var(--sp-3); }
.chaser-banner-toggle { flex: 1; }

/* ---------------------------------------------------------------------
   Chase-UX restructure — Part 1/2 shared: ChaseSendPanel, the ONE
   compose-review-send modal (Tracking's Contact/Again + "Contact all N",
   the Chase monitor's needs_help "Chase anyway"). A wider confirm-modal
   variant since it lists real per-person detail, not a single summary. */
.chase-send-panel { max-width: 640px; width: 100%; max-height: 86vh; display: flex; flex-direction: column; }
.chase-send-panel-header { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.chase-send-panel-header .confirm-modal-title { margin: 0; }
.chase-send-panel-people { overflow-y: auto; margin: var(--sp-3) 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.chase-send-panel-person {
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  background: var(--paper);
}
.chase-send-panel-person-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; font-size: 13px; margin-bottom: 6px; }
.chase-send-panel-reply { margin: 8px 0 0; font-style: italic; }
.chase-send-panel-blocked { margin: 8px 0 0; font-size: 12.5px; color: var(--rust); }
.chase-send-panel-footer { margin-top: 0; }
.chase-touchpoints-inline { font-weight: 400; color: var(--muted); }

/* ---------------------------------------------------------------------
   Chase monitor — rebuilt 2026-09-01 (branch feature/chase-monitor-open-
   only) into a tight OPEN-cases-only follow-up view. Replaces the old
   funnel/callout dashboard (which stretched an edge-to-edge bar chart
   across the module's full 1400px width, dominated by Resolved volume)
   with clickable KPI tiles (reusing .kpi-panel/.paw-stat-clickable —
   TrackingTab.jsx's own summary band, not a bespoke widget) plus a short
   table of just the still-open people. */
.chase-monitor-tab .chase-needshelp-queue { margin-bottom: var(--sp-4); }

.chase-open-kpi-row { flex-wrap: wrap; }

/* FIX (2026-09-01): "Hide notification"/"Chase anyway" are one-person
   actions now — a failure shows here, inline, WITHOUT hiding the rest of
   the already-loaded monitor the way the top-level .error-banner gate does
   for a genuine full-load failure. */
.chase-action-error { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }

.chase-monitor-reply-cell { min-width: 220px; max-width: 320px; }
.badge-monitor-waiting { background: var(--amber-soft); color: var(--amber); }
.badge-monitor-notcontacted { background: var(--line); color: var(--muted); }

/* Chase monitor "Hide notification" fix (2026-09-02) — the open-cases
   table's "Needs help" badge is now a real re-open control (clicking it
   opens NeedsHelpDrawer's reply/reason/actions), so it needs a button
   reset on top of .badge's own inline-pill look; every other badge in the
   app stays a plain, non-interactive <span>. */
.badge-clickable {
  border: none;
  cursor: pointer;
  font: inherit;
  appearance: none;
  -webkit-appearance: none;
}
.badge-clickable:hover { filter: brightness(0.93); }
.badge-clickable:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }

/* Placement-blindspot FIX 2 (2026-09-02) — PAW's bench table: a benched
   person with a real, already-signed future placement (benchRoster.js's
   incomingPlacement) gets this note instead of a bare "—", so bench never
   reads as "indefinitely idle" when a start date is already known. Amber
   (awareness, not alarm) — same token every other "worth a glance, not
   urgent" flag in the app already uses (e.g. the chase Foreign-number note). */
.badge-incoming-placement { background: var(--amber-soft); color: var(--amber); white-space: normal; }

@media (max-width: 720px) {
  .chase-send-panel { max-width: 100%; }
}

/* --- PAW week drill-down (2026-09-02) --------------------------------------
   A bar is now a control, and the panel below is that bar taken apart. The
   only new visual idea is the link between the two: the selected bar keeps
   full colour while its neighbours recede, and the panel's tabs carry the
   same three segment colours in the same stacking order. No new hues, no new
   radius, no new elevation — the segment encodings are load-bearing and never
   change (see BRIDGE §4). */

/* Selected column: one step stronger than the hover band, since selection
   persists while hover is momentary. */
.paw-chart-selected-col { fill: var(--paper); }
.paw-chart-xtick { stroke: var(--line); stroke-width: 1; }

.paw-chart-bar { cursor: pointer; }
/* Unselected bars recede rather than change colour — the encoding stays
   readable, the selection is unmistakable. */
.paw-chart-bar-dimmed { opacity: 0.5; }
.paw-chart-bar-marker { fill: var(--ink); }

@media (prefers-reduced-motion: no-preference) {
  .paw-chart-bar { transition: opacity var(--dur-fast) var(--ease); }
}

/* Keyboard focus: a teal ring around the whole column (the bar's hit target
   is full-height, so this reads as "this column"). Explicitly styled because
   the browser default renders as a heavy black box over the plot. */
.paw-chart-bar:focus { outline: none; }
.paw-chart-bar:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; border-radius: 2px; }

/* The drill-down is a collapsible section like the two above it (2026-09-03),
   so it inherits their header, chevron and spacing and only overrides the gap
   that separates it from the Growth section. */
.paw-week-panel.collapsible-section { margin-top: var(--sp-6); }

/* The panel's subtitle is three chips, not a sentence: bucket code, the
   snapshot date, and what that snapshot means (the last one hover-explained).
   Inline-flex so they sit on one line and wrap as a unit. */
.paw-week-meta {
  display: inline-flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-2);
}
.paw-week-meta-item { font-variant-numeric: tabular-nums; }
/* The one meta chip that hides an explanation says so, with the dotted rule
   that conventionally means "there is more here". The legend swatches get no
   such underline — a swatch beside a label is already an invitation, and four
   dotted labels across the top of a chart would be noise. */
.paw-week-meta-item.has-hint { border-bottom: 1px dotted var(--muted); }
.paw-week-meta-item strong { color: var(--ink); font-weight: 600; }

/* The engine's own bucket code, kept next to the human date so the panel and
   the bar's tooltip are provably the same bucket. */
.paw-week-code {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  border: 1px solid var(--line);
  border-radius: 3px; /* matches .paw-bar-swatch / .paw-chart-tooltip-key — the small-chip convention, not the 8px --r-sm */
  padding: 1px var(--sp-1);
  margin-right: var(--sp-2);
}

.paw-week-tabs { margin: var(--sp-3) 0 var(--sp-4); }
.paw-week-tabs button { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* The tab's segment key is the Legend's OWN swatch component (.paw-bar-swatch,
   above) reused verbatim — same size, same radius — so "On bench 15" in the
   panel, the rust band in the chart, and the legend entry all read as one
   thing. Nothing new to keep in sync. */

/* Bullhorn placement status (ui.jsx's PlacementStatusBadge) — two tones, not
   one per status. "open" (Approved/Submitted) is amber, the app's existing
   attention-not-alarm colour: on a bench row it means the end date lapsed
   while Bullhorn still has the placement open, so that person needs an
   EXTENSION rather than new work. A finished placement is neutral, because
   its status implies no action on its own. Same "colour always paired with
   the label" discipline as the timesheet and chargeability badges. */
.badge-placement-open { background: var(--amber-soft); color: var(--amber); }
/* On leave (2026-10-06): the attention tone, beside the status badge. An
   account nobody holds shows its kind in the Type column: neutral once IT
   has said what it is, amber while it is Bridge's guess or untagged. */
.badge-leave { background: var(--amber-soft); color: var(--amber); }
.badge-account-kind { background: var(--line); color: var(--ink); }
.badge-account-untagged { background: var(--amber-soft); color: var(--amber); }
.badge-placement-closed { background: var(--line); color: var(--muted); }

/* PAW week panel — two tab groups, because the panel carries two different
   KINDS of number. "Headcount" is stock (a snapshot of the reference date,
   whose parts sum to the bar); "Changes" is flow (joins/leaves during the
   period, which deliberately do not). The labels and the gap between the
   groups are what stop a reader adding one to the other. */
.paw-week-tabgroups {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-5);
  margin: var(--sp-3) 0 var(--sp-4);
}

.paw-week-tabgroup { display: flex; flex-direction: column; gap: var(--sp-1); }
.paw-week-tabgroup .paw-week-tabs { margin: 0; }

.paw-week-tabgroup-label {
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--muted);
}

/* --- PAW growth (2026-09-03) ---------------------------------------------
   Net weekly growth = joined − left. Two surfaces: a +/− label above each
   stacked bar, and a companion diverging bar chart under it.

   COLOUR: teal for growth, rust for shrink — this app's existing
   positive/attention pair, already adjacency-verified at ΔE2000 44.3 (see
   pawSeries.js). Deliberately NOT green: --pt IS green here and means
   Portugal, so a green "+3" beside an Entity filter would read as an entity
   tag. Zero draws nothing at all, which is what leaves room for the weeks
   that actually moved. */

.paw-growth-label {
  font-size: var(--fs-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  /* The tallest bar's label is clamped onto its own cap (see
     GROWTH_LABEL_MIN_Y in PawStackedBarChart.jsx), so every label carries a
     surface-coloured halo and paints the stroke UNDER the glyph — legible on
     the panel, on a teal cap, and on a rust one, without a plate that would
     punch a hole in the bar. */
  paint-order: stroke fill;
  stroke: var(--panel);
  stroke-width: 3px;
  stroke-linejoin: round;
}
.paw-growth-label-up { fill: var(--teal); }
.paw-growth-label-down { fill: var(--rust); }
/* The selected week's label is pinned regardless of crowding, so it also
   reads as deliberate rather than as one survivor of the thinning. */
.paw-growth-label-pinned { font-size: var(--fs-sm); }

.paw-growth-wrap { padding-top: var(--sp-3); }

/* The stat strip that replaced the section's subtitle and footnote
   (2026-09-03). Value over label, the app's existing .paw-stat idiom, so a
   row of them reads as figures rather than as a sentence broken into pieces.
   Two of the six are BUTTONS (biggest mover up, biggest mover down, plus the
   lead "now" figure): a summary you can click into the drill-down is worth
   more than one you can only read. They are styled identically to the static
   ones apart from the hover — the affordance is the pointer and the hover
   tint, not a different look, because they are the same kind of thing. */
.paw-growth-stats {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--sp-2) var(--sp-5);
  margin-bottom: var(--sp-3);
  padding-left: 44px; /* CHART_MARGIN.left — line up with the plot, not the panel */
}
.paw-growth-stat {
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 2px var(--sp-2) 2px 0;
  border: none;
  background: none;
  text-align: left;
  font: inherit;
}
button.paw-growth-stat {
  padding-left: var(--sp-2);
  margin-left: calc(var(--sp-2) * -1);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--dur-fast) var(--ease);
}
button.paw-growth-stat:hover { background: var(--paper); }
button.paw-growth-stat:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.paw-growth-stat-value {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.15;
  color: var(--ink);
}
/* Sign carries colour here exactly as it does on the bars — teal up, rust
   down — so the strip and the plot under it speak one language. */
.paw-growth-stat-value.is-up, .paw-growth-stat-value .is-up { color: var(--teal); }
.paw-growth-stat-value.is-down, .paw-growth-stat-value .is-down { color: var(--rust); }
.paw-growth-stat-sep { color: var(--line); font-weight: 400; margin: 0 2px; }
.paw-growth-stat-label {
  color: var(--muted);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
}
/* The window's headline number leads the row and is set apart by weight, not
   by a rule or a box — one more border on this page would be noise. */
.paw-growth-stat-lead .paw-growth-stat-value { font-size: var(--fs-xl); }

.paw-growth-bar-up { fill: var(--teal); }
.paw-growth-bar-down { fill: var(--rust); }

/* Zero is not one gridline among five — it is what the whole chart is
   measured against, so it gets ink weight while the others stay hairlines. */
.paw-growth-zeroline { stroke: var(--ink); stroke-width: 1.25; opacity: 0.45; }

.paw-growth-bar:focus-visible { outline: none; }
.paw-growth-bar:focus-visible .paw-growth-bar-up,
.paw-growth-bar:focus-visible .paw-growth-bar-down { stroke: var(--ink); stroke-width: 1.5; }

/* Flow in the bar tooltip — its own group under a rule, the same separation
   the week panel makes, so these are never read as part of the stack that
   just summed to Total. */
.paw-chart-tooltip-flow { margin-top: 6px; padding-top: 6px; border-top: 1px solid rgba(255,255,255,0.2); }
.paw-chart-tooltip-net { font-weight: 700; }

/* --- Chart "no matching data" state (2026-09-03) -------------------------
   A series of buckets that are ALL ZERO is a real answer, not a missing one.
   It used to render as invisible bars under a degenerate axis with nothing
   said, which read as a broken chart — the case that prompted this is
   Back-office + Nearshore=Only, legitimately zero in every bucket because
   back-office roles are never client-placed. Mirrors the week panel's own
   per-tab empty states rather than inventing a second convention. */
.paw-chart-empty-filtered {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-8) var(--sp-5);
}
.paw-chart-empty-icon {
  color: var(--muted);
  opacity: 0.55;
}
.paw-chart-empty-icon svg { width: 28px; height: 28px; display: block; }
.paw-chart-empty-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--fs-md);
  color: var(--ink);
}
/* Measured, not arbitrary: the hint carries the REASON (which filter, and
   why it can never match) and that sentence needs a readable measure rather
   than the full panel width. */
.paw-chart-empty-hint {
  margin: 0;
  max-width: 62ch;
  text-align: center;
  font-size: var(--fs-sm);
  line-height: 1.55;
  color: var(--muted);
}

/* --- Time Off drill-down (2026-09-04) --------------------------------------
   Clicking a Time Off row opens the composition panel: which exact days that
   person's "days off" number is made of, as a monthly calendar plus the flat
   list. Reuses .drawer/.drawer-backdrop wholesale (the Employee Card's own
   slide-in) — only the width and the calendar internals are new.

   COLOUR ENCODES CATEGORY, NEVER THE RAW CODE. PT alone has 27 absence codes;
   27 hues is not a legend anyone can read, and §2's existing ΔE2000 adjacency
   floor could not be met anywhere near that count. The five categories reuse
   tokens already in this palette, and the specific CODE is printed inside the
   day cell as text — so the code is never carried by colour alone, the same
   "never colour alone" rule the chargeability band bar already follows. */
/* Above the Employee Card drawer's own backdrop (z-index 25, itself above
   the plain drawer's 20) — since 2026-09-04 this same panel is opened from
   the card's Holiday balance section too, and a detail opened FROM a drawer
   has to land on top of it. Still below .picker-backdrop (30) and
   .confirm-modal-backdrop (40), which must always win over everything. */
/* Month explanation drill-down (2026-09-08) — the panel behind a click on a
   Monthly trend bar. Same drawer language as the Time Off drill-down beside
   it; everything below is spacing from the --sp-* scale and colour from the
   token block, nothing hand-picked. */
.drawer-backdrop.month-explain-backdrop { z-index: 28; }
.month-explain-drawer { width: 620px; }

.month-explain-empty { color: var(--muted); padding: var(--sp-5) 0; }
.month-explain-error { color: var(--rust); }

.month-explain-sub {
  display: flex; align-items: center; gap: var(--sp-3);
  font-size: var(--fs-sm); color: var(--muted);
  margin-bottom: var(--sp-5);
}
.month-explain-kind {
  display: inline-flex; align-items: center;
  font-size: var(--fs-xs); font-weight: 700;
  padding: 2px var(--sp-2); border-radius: var(--r-pill);
  border: 1px solid currentColor;
}
.month-explain-kind-forecast { color: var(--amber); background: var(--amber-soft); }
.month-explain-kind-closed { color: var(--teal); background: var(--teal-soft); }

.month-explain-sec { margin-bottom: var(--sp-5); }
.month-explain-sec:last-of-type { margin-bottom: 0; }
.month-explain-sec-label {
  margin: 0 0 var(--sp-3);
  font-size: var(--fs-xs); letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); font-weight: 600;
}

/* The ledger — the subtraction a reader can check themselves. */
.month-explain-calc { display: flex; flex-direction: column; }
.month-explain-calc-row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); padding: var(--sp-1) 0;
}
.month-explain-calc-k { font-size: var(--fs-sm); min-width: 0; }
.month-explain-calc-k em {
  display: block; font-style: normal;
  font-size: var(--fs-xs); color: var(--muted);
}
.month-explain-calc-v {
  font-variant-numeric: tabular-nums; font-weight: 600;
  white-space: nowrap; font-size: var(--fs-sm);
}
.month-explain-calc-sub .month-explain-calc-v { color: var(--rust); }
.month-explain-calc-total {
  border-top: 1px solid var(--line);
  margin-top: var(--sp-2); padding-top: var(--sp-2);
}
.month-explain-out {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-3); padding-top: var(--sp-3);
  border-top: 2px solid var(--ink);
}
.month-explain-out-k { font-family: var(--font-display); font-size: var(--fs-md); }
.month-explain-out-v {
  font-family: var(--font-display); font-size: var(--fs-xl);
  font-variant-numeric: tabular-nums; line-height: 1;
  display: flex; align-items: baseline; gap: var(--sp-2);
}
.month-explain-out-v small {
  font-family: var(--font-body); font-size: var(--fs-sm);
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.month-explain-out-high { color: var(--teal); }
.month-explain-out-mid { color: var(--amber); }
.month-explain-out-low { color: var(--rust); }

/* The AI paragraph — same violet the Employee Card's AI blocks use, so
   "a model wrote this" reads the same way everywhere in the app. */
.month-explain-ai {
  background: var(--violet-soft);
  border-left: 3px solid var(--violet);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  padding: var(--sp-3) var(--sp-4);
}
.month-explain-ai-tag {
  font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase;
  color: var(--violet); font-weight: 700; margin-bottom: var(--sp-1);
}
.month-explain-ai p { margin: 0; font-size: var(--fs-sm); line-height: 1.6; }
.month-explain-ai-missing { margin: 0; font-size: var(--fs-sm); color: var(--muted); }

/* Contributors — severity as a left rule, never a filled badge. */
.month-explain-group {
  font-size: var(--fs-xs); letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); margin: var(--sp-4) 0 var(--sp-1);
}
.month-explain-sec > div:first-of-type > .month-explain-group { margin-top: 0; }
.month-explain-line {
  display: flex; align-items: baseline; gap: var(--sp-3);
  padding: var(--sp-1) 0 var(--sp-1) var(--sp-3);
  border-bottom: 1px solid var(--line);
  border-left: 3px solid transparent;
  font-size: var(--fs-sm);
}
.month-explain-line-name {
  flex: 1 1 auto; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.month-explain-line-why { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
.month-explain-line-value { font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }
.month-explain-line-rust { border-left-color: var(--rust); }
.month-explain-line-amber { border-left-color: var(--amber); }
.month-explain-line-muted { border-left-color: var(--line); }

.month-explain-caveat {
  margin: 0 0 var(--sp-2);
  font-size: var(--fs-sm); color: var(--muted);
  border-left: 3px solid var(--line);
  padding-left: var(--sp-3);
}
.month-explain-line-total {
  border-bottom: 0;
  border-top: 1px solid var(--line-strong, var(--line));
  font-weight: 700;
}
.month-explain-population-note { margin-top: var(--sp-3); }
.month-explain-foot {
  margin-top: var(--sp-5); padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-size: var(--fs-xs); color: var(--muted);
}

/* The bars were already focusable and already hovered; this is what says
   they do something. */
.chargeability-bar-group-clickable { cursor: pointer; }
.chargeability-bar-group-clickable:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.chargeability-bar-group-clickable:hover rect:first-of-type { filter: brightness(0.94); }

.drawer-backdrop.timeoff-detail-backdrop { z-index: 28; }
.timeoff-detail-drawer { width: 720px; }

.timeoff-detail-identity { margin-bottom: var(--sp-4); }
.timeoff-detail-name {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 700;
  margin-bottom: var(--sp-2);
}
.timeoff-detail-badges { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.timeoff-detail-window {
  margin-top: var(--sp-2);
  font-size: 12px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.timeoff-detail-total {
  display: flex;
  align-items: baseline;
  gap: var(--sp-2);
  padding: var(--sp-3) 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  margin-bottom: var(--sp-4);
}
.timeoff-detail-total-value {
  font-family: var(--font-display);
  font-size: 30px;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.timeoff-detail-total-label { font-size: 13px; color: var(--muted); }
.timeoff-detail-subhead {
  margin: var(--sp-5) 0 var(--sp-2);
  font-family: var(--font-display);
  font-size: 13px;
  font-weight: 700;
}

/* Legend is built from this person's OWN codes only — see the component. */
.timeoff-cal-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 14px;
  margin-bottom: var(--sp-4);
  font-size: 11.5px;
}
.timeoff-cal-legend-item { display: inline-flex; align-items: center; gap: 5px; }
.timeoff-cal-swatch {
  width: 11px;
  height: 11px;
  border-radius: 3px;
  border: 1px solid var(--line);
  flex: 0 0 auto;
}

.timeoff-cal-months {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: var(--sp-4);
}
.timeoff-cal-month-empty { opacity: 0.55; }
.timeoff-cal-month-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 4px;
}
.timeoff-cal-month-label { font-size: 12px; font-weight: 700; }
.timeoff-cal-month-total { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }

.timeoff-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.timeoff-cal-weekday {
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  text-align: center;
  padding-bottom: 2px;
}
.timeoff-cal-cell {
  aspect-ratio: 1;
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
  font-variant-numeric: tabular-nums;
  background: var(--panel);
  overflow: hidden;
}
.timeoff-cal-pad { border: none; background: none; }
.timeoff-cal-weekend { background: var(--paper); }
.timeoff-cal-daynum { font-size: 10.5px; color: var(--muted); line-height: 1; }
.timeoff-cal-code { font-size: 8.5px; font-weight: 700; line-height: 1; letter-spacing: -0.01em; }
.timeoff-cal-part { font-size: 8px; line-height: 1; opacity: 0.75; }
/* A day that was taken off carries a solid category tint and its own darker
   ink — the day number stops being muted, so an off-day reads as filled even
   in greyscale or for a viewer who can't separate the hues. */
.timeoff-cal-off { border-width: 1px; font-weight: 700; }
.timeoff-cal-off .timeoff-cal-daynum { color: inherit; font-weight: 700; }

.leave-cat-vacation { background: var(--teal-soft); border-color: var(--teal); color: var(--teal); }
.leave-cat-sick { background: var(--rust-soft); border-color: var(--rust); color: var(--rust); }
.leave-cat-bench { background: var(--violet-soft); border-color: var(--violet); color: var(--violet); }
.leave-cat-other { background: var(--paper); border-color: var(--muted); color: var(--ink); }
/* Same dashed "not confirmed" grammar as .timeoff-category-chip-unspecified
   and .badge-provisional — a day with no absence code recorded in BC at all
   must never read as an ordinary leave type. */
.leave-cat-unspecified {
  background: transparent;
  border-style: dashed;
  border-color: var(--muted);
  color: var(--muted);
  font-style: italic;
}

.timeoff-daylist { margin-top: var(--sp-2); }

/* Time Off rows open the drill-down — the affordance has to be visible, but
   this is a dense data table, so it's a hover tint + pointer, not a per-row
   button column. */
.timeoff-row-clickable { cursor: pointer; }
.timeoff-row-clickable:hover { background: var(--teal-soft); }
.timeoff-row-clickable:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.timeoff-row-selected { background: var(--teal-soft); }
.timeoff-detail-total-entries { display: block; font-size: 11.5px; color: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   CONTROL ROOM — the Chargeability & Holidays module's layout language
   (2026-09-04). Colors, spacing, type, radius all come from the token block
   at the top of this file; what is new here is composition and density:
   hairlines instead of shadows, tabular figures, one command bar instead of
   two stacked filter fieldsets, and a trend inside every metric.

   Deliberately scoped to .cr — .paw-stat / .kpi-panel are still rendered by
   PAW and Timesheets and are untouched. Adopting the deck there is its own
   migration, not a side effect of this module.
   ═══════════════════════════════════════════════════════════════════════ */
.cr {
  /* One step lighter than --line: at this density a full-strength hairline
     on every cell reads as a grid rather than as separation. */
  --cr-line: #ece7db;
  --cr-rule: #d5cdb9;
  /* The GROUND is paper since 2026-09-15: panels are cards on it (see
     .cr-panel), so the bar, strip and deck paint their own white. */
  background: var(--paper);
  border: 1px solid var(--cr-line);
  border-radius: var(--r-sm);
  overflow: hidden;
}

/* ── command bar ── */
.cr-bar {
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  padding: var(--sp-2) var(--sp-4);
  border-bottom: 1px solid var(--cr-line);
  position: sticky; top: 0; z-index: 5; background: var(--panel);
}
.cr-brand { font-size: var(--fs-md); font-weight: 650; letter-spacing: -.012em; margin: 0; white-space: nowrap; }
.cr-tabs { display: flex; gap: 2px; background: var(--paper); border: 1px solid var(--cr-line); border-radius: var(--r-sm); padding: 2px; }
.cr-tab {
  font: inherit; font-size: var(--fs-sm); font-weight: 550; padding: 5px var(--sp-3);
  border: 0; background: none; border-radius: 4px; color: var(--muted); cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast, 120ms) ease, color var(--dur-fast, 120ms) ease;
}
.cr-tab:hover { color: var(--ink); }
.cr-tab-on { background: var(--panel); color: var(--ink); box-shadow: 0 1px 1px rgba(33, 31, 26, .06); }
.cr-tab:focus-visible, .cr-seg button:focus-visible, .cr-sel:focus-visible,
.cr-search:focus-visible, .cr-stack button:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.cr-sp { flex: 1 1 auto; min-width: var(--sp-2); }
.cr-fl { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cr-seg { display: flex; border: 1px solid var(--cr-line); border-radius: var(--r-sm); overflow: hidden; }
.cr-seg button {
  font: inherit; font-size: var(--fs-sm); padding: 5px 10px; border: 0;
  border-right: 1px solid var(--cr-line); background: var(--panel); color: var(--muted);
  cursor: pointer; white-space: nowrap;
}
.cr-seg button:last-child { border-right: 0; }
.cr-seg button:hover { background: var(--paper); color: var(--ink); }
.cr-seg button.on { background: var(--ink); color: var(--paper); }
.cr-sel {
  font: inherit; font-size: var(--fs-sm); height: 28px; padding: 0 26px 0 9px;
  border: 1px solid var(--cr-line); border-radius: var(--r-sm); background: var(--panel);
  color: var(--ink); appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='9' height='6'><path d='M0 0h9L4.5 6z' fill='%23756d5c'/></svg>");
  background-repeat: no-repeat; background-position: right 9px center;
}
/* CommandBar's optional title-row control (2026-10-07). */
.cr-bar-aside { display: flex; align-items: center; margin-left: var(--sp-3); }
.cr-bar-aside .cr-search { width: 260px; }
/* MultiSelect (2026-10-07): the closed control IS a .cr-sel, so it lines
   up with every Select in the command bar; the list drops below it. */
.cr-multi { position: relative; }
.cr-multi-btn { text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cr-multi-pop {
  position: absolute; top: 100%; left: 0; z-index: 20; margin-top: var(--sp-1);
  min-width: 100%; display: grid; gap: 2px; padding: var(--sp-2);
  background: var(--panel); border: 1px solid var(--cr-line); border-radius: var(--r-sm); box-shadow: var(--shadow-2);
}
.cr-multi-opt { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); padding: var(--sp-1) var(--sp-2); border-radius: 4px; cursor: pointer; white-space: nowrap; }
.cr-multi-opt:hover { background: var(--paper); }
.cr-multi-clear { justify-self: start; font: inherit; font-size: var(--fs-xs); color: var(--teal); background: none; border: 0; padding: var(--sp-1) var(--sp-2); cursor: pointer; }
.cr-multi-btn:focus-visible, .cr-multi-opt input:focus-visible, .cr-multi-clear:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.cr-subbar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 7px var(--sp-4); border-bottom: 1px solid var(--cr-line);
  background: var(--paper); font-size: var(--fs-sm); color: var(--muted);
}
.cr-subbar b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.cr-dot-sep { color: var(--cr-rule); }

/* ── metric deck ── */
.cr-deck { display: grid; border-bottom: 1px solid var(--cr-line); background: var(--panel); }
/* 2-up is what a 620px drawer can carry. */
.cr-deck-2 { grid-template-columns: repeat(2, 1fr); }
.cr-deck-3 { grid-template-columns: repeat(3, 1fr); }
.cr-deck-4 { grid-template-columns: repeat(4, 1fr); }
.cr-deck-5 { grid-template-columns: repeat(5, 1fr); }
.cr-m { padding: var(--sp-3) var(--sp-4) 13px; border-right: 1px solid var(--cr-line); min-width: 0; color: var(--teal); }
.cr-m:last-child { border-right: 0; }
.cr-m-lab { display: block; font-size: var(--fs-sm); color: var(--muted); line-height: 1.35; margin-bottom: 9px; }
.cr-m-row { display: flex; align-items: flex-end; justify-content: space-between; gap: 10px; }
.cr-m-val {
  font-family: var(--font-mono); font-size: 25px; font-weight: 600; letter-spacing: -.025em;
  font-variant-numeric: tabular-nums; line-height: 1; white-space: nowrap; color: var(--ink);
}
.cr-m-val small { font-size: 15px; opacity: .5; margin-left: 1px; }
.cr-m-sub { display: block; font-size: var(--fs-xs); color: var(--muted); margin-top: var(--sp-2); font-variant-numeric: tabular-nums; }
.cr-up { color: var(--teal); } .cr-down { color: var(--rust); }
.cr-spark { flex: none; overflow: visible; }
/* Tone is a left rule, never a filled panel — a metric that shouts is one
   a reader stops seeing. */
.cr-m-flag { border-left: 3px solid var(--rust); padding-left: 13px; }
.cr-m-flag .cr-m-val { color: var(--rust); }
.cr-m-quiet { border-left: 3px solid var(--cr-rule); padding-left: 13px; }
.cr-m-quiet .cr-m-val { color: var(--muted); }
/* No `overflow: hidden`: the fill carries its own pill radius, so nothing
   needed clipping, and the reference tick has to stand proud of a 4px bar to
   be seen at all. */
.cr-meter { height: 4px; background: var(--paper); border-radius: var(--r-pill); margin-top: 9px; position: relative; }
.cr-meter i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--teal); }
/* The reference line on a meter — a pace or a target on the fill's own scale.
   Muted, not a status colour: it is where the fill SHOULD be, never a
   verdict on where it is. */
.cr-meter b { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--muted); border-radius: 1px; }
.cr-meter-violet i { background: var(--violet); }
.cr-meter-rust i { background: var(--rust); }
.cr-meter-amber i { background: var(--amber); }

/* ── balance messages (the monthly SMS tab, 2026-09-09) ── */
/* The tick control. A button, not a checkbox: its third state — partly
   selected, because people inside a ticked group were removed by hand — is
   not something a checkbox can honestly show. State is a glyph AND a border,
   never colour alone. */
.bmsg-tick { flex: none; width: 17px; height: 17px; padding: 0; display: grid; place-items: center;
  font: inherit; font-size: var(--fs-xs); font-weight: 700; line-height: 1;
  border: 1.5px solid var(--muted); border-radius: var(--r-sm); background: var(--panel);
  color: var(--panel); cursor: pointer; }
.bmsg-tick:hover { border-color: var(--teal); }
.bmsg-tick-on { background: var(--teal); border-color: var(--teal); color: #fff; }
.bmsg-tick-partial { background: var(--teal-soft); border-color: var(--teal); color: var(--teal); }

.bmsg-g { display: flex; align-items: center; gap: var(--sp-3); }
/* The group column holds a whole sentence ("Portugal · Consultant · Payroll")
   plus its tick, and the 220px cap on `.cr-t td.nm` — there so a handful of
   long NAMES cannot set the width for 600 rows — truncated it to
   "Portugal · Consultant · Payrol…". Raised only for the cell that actually
   holds a group, so the people table in this same tab keeps the cap, where
   220px is right. Same reasoning as `.mileage-module .cr-t td.nm`. */
.bmsg .cr-t td.nm:has(.bmsg-g) { max-width: 340px; }
.bmsg-g-lab { white-space: nowrap; font-weight: 600; }
/* Clears a group focus. A plain text button in the panel head, not a chip:
   it appears only while a focus is on, and it says what it does. */
/* A second line under a cell's main value — the count of qualifying-period
   people in a mixed group, or the date a locked balance becomes usable. */
.bmsg-msg { display: flex; flex-direction: column; gap: 3px; align-items: flex-start; }
.bmsg-sub { display: block; font-size: var(--fs-xs); color: var(--muted); font-weight: 400; }

/* Holiday taken during the PT qualifying period — the one thing on this tab
   HR is asked to notice before sending. A left rule and a label, never a
   filled badge and never colour alone. */
.bmsg-flag { display: block; font-size: var(--fs-xs); color: var(--amber); font-weight: 600;
  border-left: 2px solid var(--amber); padding-left: 6px; margin-top: 2px; }

.bmsg-clear { font: inherit; font-size: var(--fs-sm); font-weight: 600; padding: 0;
  border: none; background: none; color: var(--teal); cursor: pointer; text-decoration: underline; }
/* The label is the rest of the hit area, so the whole row toggles — a 17px
   target is not one somebody hits reliably 8 rows down a list. */
.bmsg-g-lab { font: inherit; font-weight: 600; text-align: left; padding: 0;
  border: none; background: none; color: var(--ink); cursor: pointer; }
.bmsg-g-lab:hover { text-decoration: underline; }
.bmsg-p { display: flex; align-items: center; gap: var(--sp-3); }

/* Which wording a group gets. Paired with the word itself, never carried by
   hue: the amber/rust pair already fails colour separation (BRIDGE §6b), so
   a reader who cannot tell teal from violet still reads "Holidays". */
.bmsg-word { display: inline-block; font-size: var(--fs-xs); font-weight: 700;
  padding: 2px 8px; border-radius: var(--r-pill); white-space: nowrap; }
.bmsg-word-pay { color: var(--teal); background: var(--teal-soft); border: 1px solid rgba(13,110,110,.3); }
.bmsg-word-b2b { color: var(--violet); background: var(--violet-soft); border: 1px solid rgba(91,58,166,.3); }

.bmsg-send { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--cr-rule); background: var(--paper); }
.bmsg-send-t { font-size: var(--fs-sm); color: var(--muted); line-height: 1.5; }
.bmsg-send-b { margin-left: auto; flex: none; }

/* ── panels ── */
/* A PANEL IS A CARD (2026-09-15, the lead's ask on Risk: "make some padding
   between the tables"). Three tables stacked with one hairline between them
   read as one table — measured on Risk, Returning's last row and Exit risk's
   heading met with nothing between them. Now every panel in the control-room
   stack is bordered on all sides, with ONE spacing step (--sp-4) between and
   around panels, from the token — the same rhythm the Employee Card's
   sections keep with --card-section-space. Applies to every module that
   stacks panels; controlRoomPanelSpacing.test.js guards the rule. */
.cr .cr-panel {
  background: var(--panel);
  border: 1px solid var(--cr-line);
  border-radius: var(--r-sm);
  margin: var(--sp-4) var(--sp-4) 0;
  /* NEVER `overflow: hidden` here (2026-09-15, same day, the lead: "when
     hovering a bar I can't see the total"): PAW's hover tooltip is taller
     than its chart panel — 242px against a 224px card — and the card cut
     it off below the Total row. A panel's rounded corners are kept by the
     first/last child rules below, not by clipping everything inside. A
     table scrolls in its OWN box (.cr-tw), which clips only itself. */
  overflow: visible;
}
.cr .cr-panel:last-child { margin-bottom: var(--sp-4); }
.cr .cr-panel > :first-child { border-top-left-radius: var(--r-sm); border-top-right-radius: var(--r-sm); }
.cr .cr-panel > :last-child { border-bottom-left-radius: var(--r-sm); border-bottom-right-radius: var(--r-sm); }
.cr-ph {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  flex-wrap: wrap; padding: 11px var(--sp-4); border-bottom: 1px solid var(--cr-line);
}
.cr-ph-t { font-size: var(--fs-base); font-weight: 600; margin: 0; }
.cr-ph-n {
  font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted);
  background: var(--paper); border: 1px solid var(--cr-line); border-radius: var(--r-pill);
  padding: 2px 8px; margin-left: var(--sp-2); font-variant-numeric: tabular-nums;
}
.cr-ph-r { display: flex; align-items: center; gap: 10px; font-size: var(--fs-sm); color: var(--muted); flex-wrap: wrap; }
.cr-chart { padding: var(--sp-4) var(--sp-2) var(--sp-1); }
.cr-note { margin: 0; padding: 11px var(--sp-4); font-size: var(--fs-sm); color: var(--muted); line-height: 1.6; background: var(--paper); border-top: 1px solid var(--cr-line); }
/* A note that IS the panel's body, rather than a footnote under a table:
   its rule would land directly on the header's own, drawing two hairlines
   where the idiom has one. */
.cr-ph + .cr-note { border-top: 0; }
.cr-note b { color: var(--ink); }
/* A note that LEADS what it describes instead of closing it (2026-09-21): its
   rule goes below, and an amber left rule marks it as the thing to read before
   the rows. A left rule and not a filled panel — the same severity idiom the
   tables use, so a reader meets one vocabulary. The `.cr-ph +` rule above
   already removes the top border directly under a panel head; the override
   here covers a lead note anywhere else. */
.cr-note-lead { border-top: 0; border-bottom: 1px solid var(--cr-line); box-shadow: inset 3px 0 0 var(--amber); }
.cr-note-lead b { color: var(--ink); }
.cr-empty { padding: var(--sp-6) var(--sp-4); text-align: center; color: var(--muted); font-size: var(--fs-sm); }
.cr-muted { color: var(--muted); }

/* ── distribution ── */
.cr-dist { padding: var(--sp-3) var(--sp-4) var(--sp-4); display: grid; grid-template-columns: 1fr auto; gap: var(--sp-6); align-items: center; }
.cr-stack { display: flex; gap: 3px; height: 28px; }
.cr-stack > * { border-radius: 4px; display: grid; place-items: center; min-width: 24px; border: 0; padding: 0; font: inherit; }
.cr-stack button { cursor: pointer; }
.cr-stack button:hover { filter: brightness(1.08); }
.cr-stack .cr-seg-on { box-shadow: inset 0 0 0 2px var(--ink); }
.cr-stack b { font-family: var(--font-mono); font-size: 12px; font-weight: 700; color: #fff; }
.cr-dist-key { display: flex; flex-direction: column; gap: 6px; font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; }
.cr-dist-key i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; margin-right: 7px; vertical-align: -1px; }
.cr-dist-key b { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: 600; }
/* Band tones. Amber/rust separation is a known accessibility limitation
   (BRIDGE 6b) — mitigated the same way it is elsewhere: every tone is always
   paired with a label AND a number, never carried by color alone. */
.cr-sw-high, .cr-seg-high { background: var(--teal); }
.cr-sw-mid, .cr-seg-mid { background: #7fa89c; }
.cr-sw-low, .cr-seg-low { background: var(--rust); }
.cr-sw-watch, .cr-seg-watch { background: var(--amber); }
.cr-sw-burnout, .cr-seg-burnout { background: var(--violet); }
.cr-sw-over, .cr-seg-over { background: var(--rust); }
.cr-sw-healthy, .cr-seg-healthy { background: var(--teal); }
/* Matched to a Bullhorn USER rather than a placement (2026-09-07). A lighter
   sibling of teal, DERIVED from the token rather than hand-picked, because
   it is a kind of match and should read as related to `high` while still
   being separable from it. bcOnly keeps `mid` unchanged. */
.cr-sw-usermatch, .cr-seg-usermatch { background: color-mix(in srgb, var(--teal) 55%, var(--paper)); }

/* ── table ── */
/* Long rosters (Timesheets runs to 475 rows) get most of the viewport rather
   than a fixed 460px window that leaves the rest of the screen empty — the
   sticky header and sticky name column are what make a tall scroll region
   readable in the first place. */
.cr-tw { overflow: auto; max-height: min(72vh, 900px); }
.cr-t { width: 100%; border-collapse: separate; border-spacing: 0; font-size: var(--fs-sm); }
.cr-t th {
  position: sticky; top: 0; z-index: 2; background: var(--paper); text-align: left;
  font-weight: 600; color: var(--muted); font-size: var(--fs-xs);
  padding: 7px var(--sp-3); border-bottom: 1px solid var(--cr-rule); white-space: nowrap;
}
.cr-th-sort { cursor: pointer; user-select: none; }
.cr-th-sort:hover { color: var(--ink); }
.cr-t th.num, .cr-t td.num { text-align: right; }
.cr-t td.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 12px; }
.cr-ar { color: var(--ink); margin-left: 3px; font-size: 8px; }
.cr-t td { padding: 5px var(--sp-3); border-bottom: 1px solid var(--cr-line); white-space: nowrap; vertical-align: middle; }
.cr-t tbody tr:last-child td { border-bottom: 0; }
/* The name column stays put while the figures scroll — the row is unreadable
   the moment you lose track of whose it is. */
.cr-t .nm { position: sticky; left: 0; z-index: 1; background: var(--panel); font-weight: 550; border-right: 1px solid var(--cr-line); }
.cr-t th.nm { z-index: 3; background: var(--paper); }
/* The slack goes here, so the figure columns stay packed against each other
   and remain comparable down the column on any screen width. */
.cr-t .cr-t-sp { width: 100%; padding: 0; }
.cr-t tbody tr:hover td, .cr-t tbody tr:hover .nm { background: #fbfaf6; }
/* One totals row, in a real tfoot so sorting the body can never move it:
   the header band's own treatment (background, weight), separated from the
   body by a heavier line than the hairline row separators, and pinned to
   the bottom of the scroll container the way the header pins to the top. */
.cr-t tfoot td {
  position: sticky; bottom: 0; z-index: 2; background: var(--paper);
  border-top: 2px solid var(--cr-rule); border-bottom: 0; font-weight: 600;
}
.cr-t tfoot td.nm { z-index: 3; background: var(--paper); }
/* The empty state rendered INSIDE a table that carries a footer — the Total
   row of zeros stays up, so the "nothing here" sentence needs a body row. */
.cr-t td.cr-t-emptyrow { color: var(--muted); white-space: normal; padding: var(--sp-4) var(--sp-3); }
.cr-row-click { cursor: pointer; }
.cr-row-on td, .cr-row-on .nm { background: var(--teal-soft) !important; }
.cr-t tbody tr:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.cr-search {
  font: inherit; font-size: var(--fs-sm); height: 28px; width: 190px; padding: 0 10px;
  border: 1px solid var(--cr-line); border-radius: var(--r-sm); background: var(--paper); color: var(--ink);
}
/* A percentage column you can scan for outliers without reading digits. */
.cr-pct { display: inline-flex; align-items: center; justify-content: flex-end; gap: 9px; }
.cr-pct-track { width: 88px; height: 6px; background: var(--paper); border-radius: var(--r-pill); overflow: hidden; flex: none; }
.cr-pct-track i { display: block; height: 100%; border-radius: var(--r-pill); }
.cr-pct-n { width: 42px; text-align: right; }

@media (max-width: 900px) {
  .cr-deck-4, .cr-deck-5, .cr-deck-6 { grid-template-columns: repeat(2, 1fr); }
  .cr-deck-3 { grid-template-columns: 1fr; }
  .cr-m { border-bottom: 1px solid var(--cr-line); }
  .cr-dist { grid-template-columns: 1fr; gap: var(--sp-3); }
  .cr-bar { position: static; }
}
.cr-bars { display: block; width: 100%; height: auto; }
/* A clickable month column (2026-09-09) — the bar chart is the Time Off
   list's selector, so a bar has to look and behave like a control. The hit
   area is full height so a short bar is as easy to hit as a tall one. */
.cr-bar-click { cursor: pointer; }
.cr-bar-col { fill: transparent; }
.cr-bar-click:hover .cr-bar-col { fill: rgba(33, 31, 26, .04); }
.cr-bar-col-on { fill: var(--teal-soft); }
.cr-bar-click:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.cr-bar-val { font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; fill: var(--muted); }
.cr-bar-mon { font-size: 11px; fill: var(--muted); }
.cr-badge { font-size: 10px; font-weight: 600; padding: 2px 7px; border-radius: 3px; border: 1px solid transparent; white-space: nowrap; }
.cr-badge-fc { background: var(--teal-soft); color: var(--teal); border-color: rgba(13, 110, 110, .28); }
.cr-badge-act { background: var(--paper); color: var(--muted); border-color: var(--cr-line); }
.cr-badge-prov { background: var(--amber-soft); color: var(--amber); border-color: rgba(156, 107, 10, .28); }
.cr-date { font: inherit; font-size: var(--fs-sm); height: 28px; padding: 0 8px; border: 1px solid var(--cr-line); border-radius: var(--r-sm); background: var(--panel); color: var(--ink); }
.cr-row-expanded > td { background: var(--paper); padding: var(--sp-4); white-space: normal; }
/* A group heading row (DataTable `group`, 2026-09-25): the whole width, the
   paper ground so it reads as a divider and not as a person. The button is
   sticky on the left so its label stays in view when the table scrolls
   sideways under a pinned first column. */
.cr-row-group > td { background: var(--paper); padding: 0; border-bottom: 1px solid var(--cr-line); }
.cr-group-toggle {
  position: sticky; left: 0; display: inline-flex; align-items: baseline; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-3); border: 0; background: none; font: inherit; font-size: var(--fs-sm);
  font-weight: 600; color: var(--ink); cursor: pointer; text-align: left;
}
.cr-group-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.cr-group-chev { color: var(--muted); }
.cr-group-sum { font-weight: 400; color: var(--muted); }
.cr-legend { display: flex; gap: var(--sp-4); }
.cr-legend i { width: 9px; height: 9px; border-radius: 2px; display: inline-block; margin-right: 5px; vertical-align: -1px; }
.cr-sw-act { background: var(--teal); }
.cr-sw-fc { background: var(--teal-soft); box-shadow: inset 0 0 0 1px rgba(13, 110, 110, .5); }
/* Time with NO absence code in BC — real time away that cannot be attributed
   to a leave type. Hatched, never a flat colour: the same "unconfirmed"
   convention the Time Off category chip and the provisional chart bar use,
   so it can never be mistaken for identifiable leave (2026-09-09). */
.cr-sw-unspec {
  background: var(--line);
  background-image: repeating-linear-gradient(45deg, var(--muted) 0 2px, transparent 2px 4px);
}
.cr-neg { color: var(--rust); font-weight: 700; }
.cr-chip { font-size: var(--fs-xs); color: var(--muted); border: 1px solid var(--cr-line); border-radius: var(--r-pill); padding: 2px 10px; white-space: nowrap; }
.cr-chip-clear {
  font: inherit; font-size: var(--fs-xs); padding: 3px 9px; cursor: pointer;
  border: 1px solid var(--cr-line); border-radius: var(--r-sm);
  background: var(--paper); color: var(--muted); white-space: nowrap;
}
.cr-chip-clear:hover { background: var(--panel); color: var(--ink); border-color: var(--cr-rule); }
.cr-chip-clear:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.cr-field { display: inline-flex; align-items: center; gap: 6px; }
.cr-field-lab { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
/* Cells that hold a disclosure or an explanatory note, not a bare figure —
   without this they blow the column out on `white-space: nowrap` and push
   every later column off the right edge. */
.cr-t td.wrap { white-space: normal; max-width: 240px; min-width: 120px; }
/* The sticky name column is capped: a handful of very long names would
   otherwise set the width for all 600 rows and push the figures off-screen.
   The full name stays available on hover and to a screen reader. */
.cr-t td.nm { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.cr-t td.wrap { max-width: 170px; }
/* `wide` (2026-09-17): the ONE wrapping column a tab is organised around —
   Data quality's Reason and How — gets twice the cap, so the point of the
   tab is not the cell that wraps to four lines. Still a cap: the spacer
   keeps the slack. */
.cr-t td.wrap.wide { max-width: 360px; min-width: 240px; }
@media (max-width: 1024px) { .cr-t td.wrap.wide { max-width: 240px; min-width: 160px; } }
/* Every real column shrinks to its content; ONLY the spacer asks for the
   rest. Without the `width: 1%` the browser hands the slack to whichever
   column it likes — measured on Balances, "Status" (holding the word
   "Active") was rendering 197px wide while Balance and Holiday health sat
   off the right edge. */
.cr-t th, .cr-t td { width: 1%; }
.cr-t th.cr-t-sp, .cr-t td.cr-t-sp { width: 100%; }

/* A table inside the drawer (2026-09-09) — the country cross, moved off the
   tab. Drawer width, so it scrolls in its own container rather than pushing
   the drawer sideways; same hairline/tabular-figure treatment as .cr-t. */
.month-explain-tw { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-sm); }
.month-explain-t { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.month-explain-t th {
  background: var(--paper); text-align: left; padding: 6px 9px;
  font-size: var(--fs-xs); font-weight: 650; color: var(--muted);
  border-bottom: 1px solid var(--cr-rule); white-space: nowrap;
}
.month-explain-t th.num, .month-explain-t td.num {
  text-align: right; font-variant-numeric: tabular-nums;
}
.month-explain-t td.num { font-family: var(--font-mono); }
.month-explain-t td { padding: 5px 9px; border-bottom: 1px solid var(--cr-line); white-space: nowrap; }
.month-explain-t tbody tr:last-child td { border-bottom: 0; }
/* A group whose contract genuinely isn't known reads as unconfirmed. */
.month-explain-t-quiet td { color: var(--muted); font-style: italic; }
/* The one cell to read first: where the model assumes materially more leave
   than was measured, the projected figure moved because of the assumption. */
.month-explain-t-flag { color: var(--rust); font-weight: 700; }

/* ── the explanation panel, for a PERIOD (2026-09-09) ──
   A period's arithmetic is two halves rather than a ledger: measured and
   projected side by side, then the total labelled as both. Never one
   averaged figure, which would be neither. */
.month-explain-halves { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.month-explain-half {
  display: flex; flex-direction: column; gap: 3px;
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--r-sm); padding: var(--sp-3);
}
.month-explain-half-t { font-size: var(--fs-xs); color: var(--muted); }
.month-explain-half-v {
  font-family: var(--font-mono); font-size: 23px; font-weight: 600; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.month-explain-half-d { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }

/* Payroll vs B2B inside the panel — the same one-bar-per-contract shape the
   tab uses, at drawer width. */
.month-explain-sec-split { background: var(--paper); border-left: 3px solid var(--teal); }
.month-explain-split + .month-explain-split { margin-top: var(--sp-3); }
.month-explain-split-r {
  display: grid; grid-template-columns: minmax(120px, 1.2fr) minmax(60px, 1fr) 62px;
  align-items: center; gap: var(--sp-3);
}
.month-explain-split-n { font-size: var(--fs-sm); font-weight: 600; }
.month-explain-split-n em {
  display: block; font-style: normal; font-weight: 400;
  font-size: var(--fs-xs); color: var(--muted);
}
.month-explain-split-bar {
  height: 9px; background: var(--panel); border-radius: var(--r-pill);
  overflow: hidden; box-shadow: inset 0 0 0 1px var(--line);
}
.month-explain-split-bar i { display: block; height: 100%; border-radius: var(--r-pill); }
.month-explain-split-v {
  font-family: var(--font-mono); font-size: 17px; font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right;
}
.month-explain-split-d {
  font-size: var(--fs-xs); color: var(--muted); line-height: 1.55;
  margin-top: 3px; font-variant-numeric: tabular-nums;
}
.month-explain-split-d b { color: var(--ink); }

@media (max-width: 620px) {
  .month-explain-halves { grid-template-columns: 1fr; }
}

/* ── a metric tile carrying TWO figures (2026-09-09) ──
   The deck follows the filters: with both contracts in scope and the split
   asked for, every tile answers its question twice. The label is stated
   ONCE and each row names its contract, so neither number can be misread
   as the other's.

   A split tile keeps its numbers NEUTRAL even under .cr-m-flag: inside one
   tile the two contracts move in opposite directions, so tinting both rust
   would call the good figure a problem. The left rule says "look at this
   tile"; the rows say which way each contract went. */
.cr-m-rows { display: flex; flex-direction: column; gap: 3px; margin-top: 2px; }
.cr-m-r { display: flex; align-items: baseline; gap: var(--sp-3); }
.cr-m-r-k {
  flex: none; width: 30px; font-size: var(--fs-xs); font-weight: 700;
  letter-spacing: .04em; color: var(--muted);
}
.cr-m-r-v {
  font-family: var(--font-mono); font-size: 21px; font-weight: 600; letter-spacing: -.02em;
  font-variant-numeric: tabular-nums; line-height: 1.15; color: var(--ink);
}
.cr-m-r-v small { font-size: 13px; opacity: .5; margin-left: 1px; }
.cr-m-split.cr-m-flag .cr-m-r-v,
.cr-m-split.cr-m-quiet .cr-m-r-v { color: var(--ink); }

/* ── payroll vs B2B: one bar per contract (2026-09-09) ──
   Replaced a seven-column table of workings. What a reader wants here is the
   comparison — how big each group is, where it lands, and how many days
   nobody is projected to bill; the workings moved to the explanation. */
.cr-contract-rows {
  display: flex; flex-direction: column; gap: var(--sp-3);
  padding: var(--sp-4);
}
.cr-contract-row {
  display: grid; align-items: center; gap: var(--sp-3);
  grid-template-columns: 130px 62px minmax(120px, 1fr) 74px 96px 118px;
  font-size: var(--fs-sm);
}
.cr-contract-n { font-size: var(--fs-base); font-weight: 600; }
.cr-contract-p { color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.cr-contract-bar {
  height: 9px; background: var(--paper); border-radius: var(--r-pill);
  overflow: hidden; box-shadow: inset 0 0 0 1px var(--cr-line);
}
.cr-contract-bar i { display: block; height: 100%; border-radius: var(--r-pill); }
.cr-contract-v {
  font-family: var(--font-mono); font-size: 19px; font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right;
}
.cr-contract-v small, .cr-contract-i small { font-size: 11px; opacity: .55; margin-left: 2px; }
.cr-contract-i {
  font-family: var(--font-mono); color: var(--muted);
  font-variant-numeric: tabular-nums; text-align: right;
}
.cr-contract-note { color: var(--muted); font-style: italic; }
/* A group whose contract genuinely isn't known reads as unconfirmed — the
   same quiet convention "Unspecified" leave already uses. */
.cr-contract-row-unknown .cr-contract-n { color: var(--muted); font-style: italic; }

/* ── the one hand-off to the explanation (2026-09-09) ──
   A period the chart cannot carry (one month) would otherwise have no way
   in at all. A line of prose and one button, on the panel ground rather
   than as a filled banner. */
.cr-ask {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: 13px var(--sp-4); background: var(--paper);
  border-bottom: 1px solid var(--cr-line);
  font-size: var(--fs-sm); color: var(--muted);
}
.cr-ask-t { line-height: 1.5; }
.cr-ask-b {
  margin-left: auto; flex: none; font: inherit; font-size: var(--fs-sm); font-weight: 600;
  padding: 7px 15px; border-radius: var(--r-sm); border: 1px solid var(--teal);
  background: var(--panel); color: var(--teal); cursor: pointer; white-space: nowrap;
}
.cr-ask-b:hover { background: var(--teal-soft); }
.cr-ask-b:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }

@media (max-width: 900px) {
  .cr-contract-row { grid-template-columns: 1fr auto; }
  .cr-contract-bar, .cr-contract-note { display: none; }
}

/* ── small multiples: the contract split's charts (2026-09-09) ──
   ONE panel holding the shipped chargeability chart drawn once per contract.
   Spacing comes from the --sp-* scale, and the block reuses .cr-chart's own
   padding rule rather than picking its own — same discipline as the
   Employee Card's section token. */
.cr-multiples { display: flex; flex-direction: column; gap: var(--sp-5); padding: var(--sp-4) var(--sp-2) var(--sp-2); }
.cr-multiple { min-width: 0; }
/* Label, population, then the group's own headline pushed to the right
   edge — so the two charts' figures line up with each other and can be
   compared without reading across. */
.cr-multiple-head {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  padding: 0 var(--sp-3) var(--sp-1); font-size: var(--fs-sm);
}
.cr-multiple-head b { font-size: var(--fs-base); font-weight: 600; }
.cr-multiple-head .cr-ph-n { margin-left: auto; }

/* A group whose contract genuinely isn't known yet reads as unconfirmed —
   the same dashed/quiet convention the app already uses for "Unspecified"
   leave, rather than a colour that would imply a severity it doesn't have. */
.cr-row-unknown td { background: var(--paper); }
.cr-row-unknown td.nm { border-left: 3px solid var(--cr-rule); font-style: italic; color: var(--muted); }

/* ═══════════════════════════════════════════════════════════════════════════
   EMPLOYEE CARD — identity, employment standing, exceptions (2026-09-04).
   The card reads top-down by relevance: who this is and how to reach them,
   what their standing is, what needs attention, then the numbers, then the
   detail. Before this the header was a name and two chips, employment sat
   halfway down mixed in with a home address, and nothing said what was wrong
   until you opened five collapsed sections.
   ═══════════════════════════════════════════════════════════════════════ */
.employee-card-header {
  display: flex; align-items: center; gap: var(--sp-4);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--card-section-space); margin-bottom: var(--card-section-space);
  /* The one piece of elevation on the card, so the eye starts here rather
     than treating nine equal boxes as a list. */
  box-shadow: var(--shadow-1);
}
.employee-card-avatar {
  flex: none; width: 54px; height: 54px; border-radius: 50%;
  background: var(--teal-soft); color: var(--teal);
  display: grid; place-items: center; font-weight: 700; font-size: var(--fs-lg);
}
.employee-card-identity { min-width: 0; flex: 1; }
.employee-card-name {
  font-family: var(--font-display); font-size: var(--fs-2xl); font-weight: 600;
  margin: 0 0 var(--sp-2); letter-spacing: -.01em;
}
.employee-card-idrow { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
/* Two identifiers from two systems: unlabelled they read as one run-on
   string of numbers with no way to tell which belongs to what. */
.employee-card-idpair { display: inline-flex; align-items: center; gap: var(--sp-1); }
.employee-card-idlabel { font-size: var(--fs-xs); color: var(--muted); }
/* Contact is actionable and at the top, because reaching someone is what HR
   opens a card to do. It used to be plain text here and a duplicate list at
   the bottom of the page. */
.employee-card-contact { display: flex; flex-wrap: wrap; gap: var(--sp-4); margin-top: var(--sp-2); }
.employee-card-contact-item { display: inline-flex; align-items: baseline; gap: var(--sp-1); text-decoration: none; color: var(--ink); }
.employee-card-contact-label { font-size: var(--fs-xs); color: var(--muted); }
.employee-card-contact-value { font-size: var(--fs-sm); border-bottom: 1px solid var(--line); }
.employee-card-contact-item:hover .employee-card-contact-value { color: var(--teal); border-bottom-color: var(--teal); }
.employee-card-contact-item:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: 2px; }

/* Employment standing — the context every other figure is read against. */
.employee-card-empstrip {
  display: grid; grid-template-columns: repeat(5, 1fr);
  background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
  margin-bottom: var(--card-section-space); overflow: hidden;
}
.employee-card-empcell { padding: var(--sp-3) var(--sp-4); border-right: 1px solid var(--line); min-width: 0; }
.employee-card-empcell:last-child { border-right: 0; }
.employee-card-empcell-label { display: block; font-size: var(--fs-xs); color: var(--muted); margin-bottom: var(--sp-1); }
.employee-card-empcell-value { display: block; font-size: var(--fs-sm); font-weight: 550; }

/* Needs attention. Cream ground so it reads as a note ABOUT the card rather
   than another section of it. */
.employee-card-attention {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--card-section-space); margin-bottom: var(--card-section-space);
}
.employee-card-attention-head { font-size: var(--fs-sm); font-weight: 600; margin-bottom: var(--sp-3); }
.employee-card-attention-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.employee-card-attention-item {
  display: flex; gap: var(--sp-3); align-items: baseline; font-size: var(--fs-sm);
  padding-left: var(--sp-3); border-left: 3px solid var(--line);
}
.employee-card-attention-rust { border-left-color: var(--rust); }
.employee-card-attention-amber { border-left-color: var(--amber); }
.employee-card-attention-violet { border-left-color: var(--violet); }
.employee-card-attention-label { font-weight: 600; white-space: nowrap; }
.employee-card-attention-rust .employee-card-attention-label { color: var(--rust); }
.employee-card-attention-amber .employee-card-attention-label { color: var(--amber); }
.employee-card-attention-violet .employee-card-attention-label { color: var(--violet); }
.employee-card-attention-detail { color: var(--muted); }
.employee-card-attention-clear { font-size: var(--fs-sm); color: var(--teal); }
/* The written summary restated the exceptions in prose. It stays, subordinate
   to the list it duplicated, and opens on demand. */
.employee-card-aifold { margin-top: var(--sp-3); border-top: 1px solid var(--line); padding-top: var(--sp-3); }
.employee-card-aifold > summary { font-size: var(--fs-sm); color: var(--muted); cursor: pointer; list-style: none; }
.employee-card-aifold > summary::-webkit-details-marker { display: none; }
.employee-card-aifold > summary::before { content: '\25b8'; display: inline-block; width: 12px; color: var(--muted); font-size: 9px; }
.employee-card-aifold[open] > summary::before { content: '\25be'; }
.employee-card-aifold p { margin: var(--sp-3) 0 0; font-size: var(--fs-sm); line-height: 1.65; max-width: 78ch; }

/* label : value, one row each — the card used to stack them, two lines a
   fact, unreadable down the column. */
.employee-card-facts { margin: 0; font-size: var(--fs-sm); }
.employee-card-facts > div {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-4);
  padding: var(--sp-2) 0; border-bottom: 1px solid var(--line);
}
.employee-card-facts > div:first-child { padding-top: 0; }
.employee-card-facts > div:last-child { border-bottom: 0; padding-bottom: 0; }
.employee-card-facts dt { color: var(--muted); }
.employee-card-facts dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums; font-weight: 550; }
/* A row that is the SUM of the rows above it, not another input: a heavier
   rule above it and the ink colour on the label, so the facts list reads as
   a calculation with a result rather than four equal-weight lines. No new
   spacing value — it borrows the list's own padding. */
.employee-card-facts > div.employee-card-fact-total { border-top: 2px solid var(--line); }
.employee-card-facts > div.employee-card-fact-total dt { color: var(--ink); font-weight: 550; }
.employee-card-fact-sub { display: block; font-size: var(--fs-xs); color: var(--muted); font-weight: 400; }
.employee-card-fact-long { text-align: left; font-weight: 400; color: var(--muted); max-width: 60%; }
.employee-card-week-hours { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); margin-left: var(--sp-2); }
.employee-card-discrepancy { display: inline-flex; flex-direction: column; gap: 2px; text-align: right; font-weight: 400; }
.employee-card-discrepancy-src { font-size: var(--fs-xs); color: var(--muted); margin-right: var(--sp-1); }

/* The holiday bar runs PAST its own limit when the limit is exceeded. The one
   it replaces passed kpi-meter-fill-high hardcoded and clamped at 100%, so
   someone 4 days over their entitlement rendered as a full green bar. */
.employee-card-holidaybar { margin: var(--sp-4) 0; }
.employee-card-holidaybar-track { position: relative; height: 10px; background: var(--paper); border-radius: var(--r-pill); }
.employee-card-holidaybar-track > i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--teal); }
.employee-card-holidaybar-over > i { background: var(--rust); }
.employee-card-holidaybar-limit { position: absolute; top: -4px; width: 2px; height: 18px; background: var(--ink); }
.employee-card-holidaybar-note { margin: var(--sp-3) 0 0; font-size: var(--fs-xs); color: var(--muted); }

.employee-card-placement-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--sp-3); }

/* Two balanced columns of cards. Multi-column, not a two-track grid: the
   cards have genuinely different heights and any fixed assignment leaves one
   column short — the full page used to run ~2,700px of empty left column. */
.employee-card-columns {
  columns: 2;
  column-gap: var(--card-section-space);
  /* The container carries the gap to whatever follows it. A multi-column box
     is sized by its content, NOT by the last child's bottom margin — so
     without this the next section (Conversation) sits flush against the last
     card, measured at a 0px gap. */
  margin: 0 0 var(--card-section-space);
}
.employee-card-columns > * { break-inside: avoid; }

@media (max-width: 900px) {
  .employee-card-empstrip { grid-template-columns: repeat(2, 1fr); }
  .employee-card-columns { columns: 1; }
}
/* 620px carries two employment cells, not five. */
.employee-card-drawer .employee-card-empstrip { grid-template-columns: repeat(2, 1fr); }
.employee-card-drawer .employee-card-empcell { border-bottom: 1px solid var(--line); }
.employee-card-drawer .employee-card-empcell:nth-child(2n) { border-right: 0; }
.employee-card-drawer .employee-card-empcell:last-child { grid-column: 1 / -1; border-right: 0; border-bottom: 0; }
/* Three contact items wrap to two lines at 620px, so they sit tighter here:
   the full page's --sp-4 between items left the wrapped row reading as two
   unrelated blocks rather than one contact line. */
.employee-card-drawer .employee-card-contact { gap: var(--sp-1) var(--sp-3); }
.employee-card-drawer .employee-card-name { font-size: var(--fs-xl); }
.employee-card-drawer .employee-card-avatar { width: 46px; height: 46px; font-size: var(--fs-md); }

/* The metric deck, on a card surface. ControlRoom's own deck is one strip
   with hairline dividers, which is right inside a control-room panel; on the
   Employee Card the metrics are four discrete cards like everything else
   around them. The --cr-line token only exists inside .cr, so every border
   here is restated rather than inherited. */
.employee-fullpage .cr-deck,
.employee-card-drawer .cr-deck {
  border-bottom: 0;
  gap: var(--card-section-space);
  margin-bottom: var(--card-section-space);
}
.employee-fullpage .cr-m,
.employee-card-drawer .cr-m {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-4);
}
.employee-fullpage .cr-m-flag,
.employee-card-drawer .cr-m-flag { border-left: 3px solid var(--rust); }

/* The Bullhorn id leaves Bridge, so it looks like a link rather than like the
   PPID chip beside it. */
.employee-card-idpair .id-code-link {
  color: var(--teal);
  background: var(--teal-soft);
  border: 1px solid rgba(13, 110, 110, .28);
}
.employee-card-idpair .id-code-link:hover { text-decoration: underline; }

/* A selectable metric — Timesheets drives its table filter from these, and
   Chase does the same. Selection is a filled state plus aria-pressed, never
   colour alone; the whole tile is the target because a 5-up deck gives it
   plenty of room and a small chip would be a worse one. */
.cr-m-select {
  font: inherit; text-align: left; cursor: pointer; width: 100%;
  background: none; border: 0; border-right: 1px solid var(--cr-line);
  transition: background var(--dur-fast, 120ms) ease;
}
.cr-deck > .cr-m-select:last-child { border-right: 0; }
.cr-m-select:hover { background: var(--paper); }
.cr-m-select:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.cr-m-on { background: var(--teal-soft); }
.cr-m-on:hover { background: var(--teal-soft); }
.cr-m-on .cr-m-lab { color: var(--teal); font-weight: 600; }

/* The attention line — a full-width alarm banner is something a reader stops
   seeing, but the two controls that were on it both stay: the text toggles
   the table filter, the button opens the send panel for the whole worklist. */
.cr-attn {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: 10px var(--sp-4); border-bottom: 1px solid var(--cr-line);
  background: var(--paper); font-size: var(--fs-sm);
}
.cr-attn-toggle {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  font: inherit; font-size: var(--fs-sm); background: none; border: 0;
  padding: 2px 6px; margin-left: -6px; border-radius: var(--r-sm); cursor: pointer; text-align: left;
}
.cr-attn-toggle:hover { background: var(--panel); }
.cr-attn-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.cr-attn-toggle-on { background: var(--panel); box-shadow: inset 0 0 0 1px var(--cr-rule); }
.cr-attn-lab { font-weight: 600; color: var(--rust); }
.cr-attn-det { color: var(--muted); }
.cr-attn-action { margin-left: auto; }
.cr-attn-clear {
  padding: 10px var(--sp-4); border-bottom: 1px solid var(--cr-line);
  background: var(--paper); font-size: var(--fs-sm); color: var(--teal);
}
/* Chase's table header carries its own owner filter, next to the table it
   filters rather than orphaned above it. */
.chase-table-head { border-top: 1px solid var(--cr-line); }
/* A deck whose tile count varies at runtime (Chase hides zero-count statuses):
   tiles keep a readable width and pack left instead of stretching. */
.cr-deck-fit { grid-template-columns: repeat(auto-fit, minmax(200px, 260px)); }

/* The chaser ALERT. It is not a quiet note — it is the one thing on the tab
   that says work is outstanding, and it sits directly above the table it
   filters. It still carries BOTH controls the old full-width banner had:
   the text toggles the filter, the button opens the send panel. */
.cr-alert {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  padding: 10px var(--sp-4);
  border-bottom: 1px solid var(--cr-line);
  border-left: 3px solid var(--rust);
  background: var(--rust-soft);
}
.cr-alert-toggle {
  display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap;
  font: inherit; font-size: var(--fs-sm); background: none; border: 0;
  padding: 3px 8px; margin-left: -8px; border-radius: var(--r-sm);
  cursor: pointer; text-align: left; color: var(--rust);
}
.cr-alert-toggle:hover { background: rgba(255, 255, 255, .55); }
.cr-alert-toggle:focus-visible { outline: 2px solid var(--rust); outline-offset: 1px; }
.cr-alert-toggle-on { background: var(--panel); box-shadow: inset 0 0 0 1px var(--rust); }
.cr-alert-icon { font-size: var(--fs-base); }
.cr-alert-lab { font-weight: 700; }
.cr-alert-det { color: var(--rust); opacity: .8; }
.cr-alert-action {
  margin-left: auto; font: inherit; font-size: var(--fs-sm); font-weight: 600;
  padding: 5px 12px; cursor: pointer; white-space: nowrap;
  border: 1px solid var(--rust); border-radius: var(--r-sm);
  background: var(--panel); color: var(--rust);
}
.cr-alert-action:hover { background: var(--rust); color: var(--paper); }
.cr-alert-action:focus-visible { outline: 2px solid var(--rust); outline-offset: 1px; }
/* Nothing outstanding is itself a signal — same row, calm treatment. */
.cr-alert-clear {
  border-left-color: var(--teal); background: var(--teal-soft);
  color: var(--teal); font-size: var(--fs-sm);
}

/* ═══════════════════════════════════════════════════════════════════════════
   PAW, IN THE CONTROL ROOM (2026-09-04). The module moved off .paw-stat /
   .paw-filters wholesale; what is left here is the handful of things a
   chart-led module needs that a table-led one does not.

   Two real problems this fixes, both about the chart:
     * it rendered ~600px tall for a series whose bars all sit between 391
       and 427, so three quarters of that height carried nothing and the
       movement chart and the week drill-down fell below the fold. The
       geometry (a wide, short viewBox) lives in PawStackedBarChart.jsx —
       the SVG scales to the panel width, so the ASPECT sets the height.
     * the ±deltas were printed over all 36 bars. Movement has its own
       panel now, around its own zero line.

   Tokens only — colours, --sp-*, --fs-*, --r-* from the block at the top.
   ═══════════════════════════════════════════════════════════════════════ */

/* The chart was a free-standing CARD (border, shadow, its own padding) back
   when it sat on the page. Inside a control-room panel the panel IS the card,
   so the wrapper goes back to being a positioning context for the tooltip and
   nothing else. */
.cr .paw-chart-wrap {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
}
/* The ⓘ sits over the plot's top-right corner rather than in the panel
   header: the header carries the legend, and a header that holds a key AND a
   disclosure reads as two controls competing. Absolute so it costs the chart
   no height — the whole point of this pass. */
.cr .paw-chart-tools {
  position: absolute;
  top: 0;
  right: 0;
  z-index: 1;
}
.cr .paw-chart-tools + .paw-chart-wrap,
.cr .cr-chart { position: relative; }
.cr .paw-chart-empty { padding: var(--sp-6) var(--sp-4); }

/* The page-level action, at the far right of the command bar. Distinct from
   .cr-chip-clear (a quiet, in-table control) because this one is the thing
   the screen is FOR — but still a bar-height control, not a page button. */
.cr-action {
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: 600;
  height: 28px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--teal);
  border-radius: var(--r-sm);
  background: var(--teal);
  color: var(--panel);
  cursor: pointer;
  white-space: nowrap;
}
.cr-action:hover:not(:disabled) { background: var(--ink); border-color: var(--ink); }
.cr-action:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.cr-action:disabled { opacity: 0.45; cursor: default; }

/* A segmented control that carries a band's swatch and its count — the week
   panel's tabs ARE the bar's bands, so they wear the same colours, and the
   count is the number you clicked on the bar. */
.cr-seg button { display: inline-flex; align-items: center; gap: 6px; }
.cr-seg-sw { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.cr-seg-n {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
.cr-seg button.on .cr-seg-n { color: var(--paper); opacity: 0.8; }

/* The engine's own bucket code (W36 Aug), kept beside the human date so the
   panel and the bar's tooltip are provably the same bucket. */
.cr-code {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  background: var(--paper);
  border: 1px solid var(--cr-line);
  border-radius: var(--r-pill);
  padding: 2px var(--sp-2);
}

/* Two groups of tabs, labelled, because they are two different KINDS of
   number: a snapshot whose parts sum to the bar, and events during the
   period which deliberately do not. */
.paw-week-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--cr-line);
}
.paw-week-group { display: inline-flex; align-items: center; gap: var(--sp-2); }
.paw-week-group-lab { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; }
/* The Projected change beside the Changes tabs: a term of the sum, not a
   tab — its people are listed under the Projected band. */
.paw-week-proj { font-size: var(--fs-xs); color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
.paw-week-proj b { color: var(--ink); }

/* A foldable panel head. The caret is a sibling of the <h2>, so the heading
   still reads as the panel's name; the whole title is the hit target because
   a 12px triangle is not one. */
.cr-ph-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font: inherit;
  background: none;
  border: 0;
  padding: 0;
  margin: 0;
  cursor: pointer;
  color: inherit;
  text-align: left;
}
.cr-ph-caret { color: var(--muted); font-size: var(--fs-xs); line-height: 1; }
.cr-ph-toggle:hover .cr-ph-caret { color: var(--ink); }
.cr-ph-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: var(--r-sm); }

/* Loading/error/list states, on a panel surface rather than a page one. */
.cr .loading-state,
.cr .error-banner { margin: 0; padding: var(--sp-4); }
.cr .paw-untyped-list { margin: 0; padding: var(--sp-3) var(--sp-4); }

/* ── Extension, in the control room (2026-09-04) ─────────────────────────
   Everything this module renders is already in the shared .cr sheet above —
   deck, panel, table, .cr-pct meter, the existing badge vocabulary. These
   three rules are the only additions, and each exists because a cell here
   holds two things rather than one.

   .cr-t td is white-space: nowrap (a table meant to be scanned), and that
   out-specifies .inject-message's own white-space: normal — so without the
   first rule the "Mark ongoing" write's inline error would run off the row
   as a single line. The error must stay legible BESIDE the row: a failed
   write may never hide the person it failed on. */
.cr-t td .inject-message { white-space: normal; max-width: 22ch; }
/* Two lines in one cell (placement id over its end date) and two chips on
   one line (risk tier plus the "also Exit Risk" cross-flag). Spacing from
   the --sp-* scale, never a hand-picked value. */
.cr-cell-stack { display: inline-flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1); }
.cr-cell-inline { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* ── People, control room (2026-09-04) ───────────────────────────────────
   The amber sibling of .cr-m-flag. `tone="warn"` was already being passed
   by Timesheets' Missing deck and had no rule at all, so a metric asking
   for the softer of the two severities rendered as a plain tile. People's
   "Awaiting a PPID" is the same reading: a queue that needs working, not a
   thing that has already gone wrong (that is .cr-m-flag's rust).

   Severity is a left rule, never a filled tile — the same treatment
   .cr-m-flag/.cr-m-quiet use, and the same 3px + 13px pair, so the four
   tones line their labels up on one edge. */
.cr-m-warn { border-left: 3px solid var(--amber); padding-left: 13px; }
.cr-m-warn .cr-m-val { color: var(--amber); }

/* ── RISK, in the control room (2026-09-04) ─────────────────────────────
   Three things the shared .cr sheet did not already carry, all tokens only.
   ═══════════════════════════════════════════════════════════════════════ */

/* "Placement ends": the date, and what that date means relative to today.
   The pair exists because a signed day count printed on its own is what
   produced "−27d" under a column headed "Days since ended" for placements
   that had not ended. A date STILL AHEAD reads in teal — the same
   attention-without-alarm reading .cr uses elsewhere — and is never carried
   by colour alone: the words "in 27 days" say it too. */
.risk-when { display: flex; flex-direction: column; line-height: 1.3; }
.risk-when-date { font-variant-numeric: tabular-nums; }
.risk-when-rel { font-size: var(--fs-xs); color: var(--muted); }
.risk-when-ahead { color: var(--teal); font-weight: 600; }

/* BC terminated?, as a word plus a weight — "No" on an exit-risk row is the
   open item, so it carries the emphasis; "Yes" is the closed, quiet case. */
.risk-flag-open { color: var(--amber); font-weight: 600; }
.risk-flag-ok { color: var(--muted); }

/* Low tier stays behind a disclosure: a worklist, not an alarm. Styled as a
   table row's sibling rather than a panel of its own, so the section still
   reads as one model at two urgency levels. */

/* ---- Mileage module -------------------------------------------------- */
/* State on one line: a dot for the state, words and a number for what it
   means. Three stacked badges would run a row to ~100px against 30px for a
   single-state row, and the house rules ask for a dot over a filled badge
   for exactly that reason. Colour never travels alone — every dot has text
   beside it.

   The two BOXES that hold it are not Mileage's: the cell is a
   `.cr-cell-stack` of `.cr-cell-inline` rows, the shared pair Extension
   already composes ("two lines in one cell", "two chips on one line"), and
   their gaps come off the --sp-* scale. Mileage names only what is its own —
   the dot and the amount. */
.mileage-flag-eur { font-weight: 650; font-variant-numeric: tabular-nums; }
.mileage-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
/* Only ONE state declares its colour here, and that is the point. A dot in
   the table and a swatch in the bar above it are the same statement about
   the same state, so the three states the bar draws take their colour from
   the bar's own vocabulary — `cr-sw-*`, keyed by the tone STATUS_BANDS
   already names (mileageView.dotTone). Re-tone a band and the dots follow,
   so the key and the rows can never disagree.

   `settled` is the exception because it is in no band — payroll history,
   nothing to chase, and nothing shared to disagree with. Neutral for the
   same reason: it is the one state nobody can act on. (The three that are
   banded read teal / rust / amber: done, stuck, and waiting on nobody yet.
   "Needs the person" is rust rather than amber because someone is already
   held up by it.) */
.mileage-dot.is-settled { background: var(--muted); }
/* Compact on purpose: the bar is a proportion at a glance and the number
   beside it carries the precision, so it does not need to be wide — and at
   this width every view fits without horizontal scroll, including the month
   view, which carries two extra columns. */
.mileage-usage { display: flex; align-items: center; gap: 5px; min-width: 52px; }
/* Same as .cr-meter: the fill is rounded on its own, so the clip only ever
   stood between the pace tick and being visible at the ends of the track. */
.mileage-usage-track { flex: 1; height: 6px; background: var(--line); border-radius: 3px; position: relative; }
.mileage-usage-fill { display: block; height: 100%; background: var(--teal); border-radius: 3px; }
.mileage-usage-fill.is-over { background: var(--rust); }
/* How far through the person's own employed months we are. A reference, so
   muted and never a status colour — and 2px, because at 31 rows a heavier
   mark reads as a verdict. */
.mileage-usage-mark { position: absolute; top: -3px; bottom: -3px; width: 2px; margin-left: -1px; background: var(--muted); border-radius: 1px; }
.mileage-usage-pct { font-size: var(--fs-xs); color: var(--muted); min-width: 34px; text-align: right; font-variant-numeric: tabular-nums; }

/* The column rules under the balances table: one line each, so a reader
   can find the column they are looking at instead of parsing a paragraph.
   Spans rather than list items because PanelNote is a <p> and a list
   inside one is not valid HTML — the shared primitive keeps its shape,
   and .cr-note b already gives the label its ink colour. */
.mileage-colnote { display: block; margin-top: var(--sp-1); }

/* The PPID under the name, in the sticky column. A block so it sits on its
   own line: inline it would be pushed out of the cell by a long name, which
   is exactly why the name button truncates itself.
   The chip itself is the app's, unchanged: measured against Timesheets'
   "People with a timesheet" PPID column (2026-09-08), `.id-code` renders
   there at 12px ui-monospace on #f1ede2, padding 1px 6px, radius 4px, ink,
   and weight 400. The ONLY thing that differed here is the weight, which
   this cell inherits at 550 from the sticky column — that inheritance is the
   whole of what made it look bold, so that is the whole of what is
   overridden. Restyling the rest was an over-correction: an id should read
   the same in every table in Bridge. */
/* A row, so the code and its badge sit a small gap apart on one line — and
   `flex-wrap` so a long badge drops under the code rather than widening the
   sticky column. */
.mileage-ppid {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  margin-top: var(--sp-1); line-height: 1.2;
}
.mileage-ppid .id-code { font-weight: 400; }

/* A qualifier on a figure, never a figure itself: quiet, and inline so it
   cannot change a row's height. Rows of differing height are what makes a
   long table hard to scan down. */
.mileage-sub {
  display: block; margin-top: var(--sp-1);
  font-size: var(--fs-xs); color: var(--muted); font-weight: 400; white-space: nowrap;
}

/* An allowance recovered from the payroll import is not something BC would
   confirm today, so it does not read as flatly as a live figure.

   Scoped to .cr-t and to the SPAN the table actually renders. The rule used
   to read `.mileage-table td.is-from-import`, and both halves were wrong
   after the control-room port: nothing renders .mileage-table any more, and
   DataTable puts the class on a span inside the cell, not on the td. So a
   leaver's imported allowance looked identical to a live one — the same
   silent miss as `td-num` vs `num` (BRIDGE §3). The rest of the dead
   .mileage-table block went with it. */
.mileage-module .cr-t .is-from-import { font-style: italic; color: var(--muted); }

/* The one place Mileage overrides a shared primitive, and the reason it may:
   `.cr-t td.nm`'s 220px cap exists so a handful of long names cannot set the
   width for 600 rows. This table is 31 rows of PT staff, where the long name
   IS the common case ("CLÁUDIO FERNANDES DE SOUSA", plus a "left" badge),
   so the cap that protects a 600-row table just truncates this one. */
.mileage-module .cr-t td.nm { max-width: 320px; }
/* The name TRUNCATES ITSELF, inside its own button. Once the expander caret
   joined the cell, the cell's `text-overflow: ellipsis` had two inline
   children to choose between and dropped the atomic one whole — the two
   longest names rendered as a bare "…". Making the cell a flex row was worse:
   as a flex container a `<td>` stops contributing its content width to the
   column, and every name collapsed to its minimum. So the button carries the
   ellipsis, capped short of the cell to leave the caret its own room. */
.mileage-module .cr-t td.nm .person-name-link {
  display: inline-block; max-width: 88%; vertical-align: bottom;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── the month band ───────────────────────────────────────────────────────
   Twelve cells, one per month, drawing the running BALANCE against a zero
   line: above it the person has allowance banked, below it they have claimed
   further than the year has accrued. Colour carries only what the shape
   already says — the numbers are on the cells' own labels.
   Geometry (and the reasoning) is in frontend/mileageBand.js. */
/* Sized by its own attributes (see MileageModule.jsx). */
.mileage-band { display: block; }
/* ZERO: the one reference that means the same thing on every row. Above it a
   person has allowance banked, below it they have claimed further than the
   year has accrued. */
.mileage-band-zero { stroke: var(--muted); stroke-width: 1; }
/* The balance after each CLOSED month: teal above the line, rust below it.
   Nothing here is a projection — the band stops where the measuring does — so
   there is no forecast treatment to declare. */
.mileage-band-fill { fill: var(--teal); }
.mileage-band-over { fill: var(--rust); }
/* Where the payroll import hands over to BC. The halves are not equally
   reliable, so the join is visible. */
.mileage-band-split { stroke: var(--muted); stroke-width: 1; stroke-dasharray: 3 3; opacity: 0.6; }
/* The hover target: the whole cell, so a month with a 2px bar is as easy to
   point at as a full one. Invisible, but `pointer-events: all` — a
   `fill: transparent` rect is not reliably hit-tested on its own. */
.mileage-band-hit { fill: transparent; pointer-events: all; }

/* ── the message panel ───────────────────────────────────────────────────── */
.mileage-pick { display: inline-flex; align-items: center; gap: var(--sp-2); cursor: pointer; }
.mileage-pick input:disabled { cursor: not-allowed; }
.mileage-msg { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-4); }
.mileage-msg-text {
  width: 100%; resize: vertical; padding: var(--sp-3);
  border: 1px solid var(--cr-line); border-radius: var(--r-sm);
  background: var(--paper); color: var(--ink);
  font: inherit; font-size: var(--fs-sm); line-height: 1.5;
}
.mileage-msg-actions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }

/* An OPEN claim in the entries register: approved, not yet paid. Severity is
   a left rule, never a filled row (CLAUDE.md) — the rule sits on the sticky
   name cell so the arrears are findable without scrolling to the Open
   column, which still carries the word. */
.mileage-module .cr-t tr.mileage-row-open td.nm,
.mileage-module .cr-t tr.mileage-row-over td.nm { box-shadow: inset 3px 0 0 var(--rust); }

/* The entries panel's export controls: the scope select and the button sit
   together in the panel head, spaced off the shared scale. */
.mileage-module .mileage-export { display: inline-flex; align-items: center; gap: var(--sp-3); }
.mileage-module .mileage-export .btn svg { margin-right: var(--sp-1); vertical-align: -1px; }
.mileage-module .mileage-export .btn[aria-disabled='true'] { opacity: 0.55; cursor: not-allowed; }
/* The pre-flight warning beside the export: the file is about to leave the
   building, and the month it covers still has money unresolved per books. */
.mileage-module .mileage-export-warn { color: var(--rust); font-size: var(--fs-xs); font-weight: 600; }

.badge-nolimit { background: var(--amber-soft); color: var(--amber); }
/* Muted, like every other "this is context, not an exception" badge
   (badge-tier-low, badge-chase-cant, badge-placement-closed): being on
   leave is not something to fix. Its own name rather than badge-unknown,
   which means "we do not know" — this is a fact we do know. */
.badge-leave { background: var(--line); color: var(--muted); }

/* ── PAYROLL (2026-09-07) ───────────────────────────────────────────────
   The module reuses ControlRoom wholesale — command bar, context strip,
   metric deck, panels, DataTable, MonthlyBars. What is added here is only
   what those primitives don't carry: the action panel's body, a run's
   progress, and the module's own severity vocabulary. Tokens only; no
   hand-picked pixel value at a call site.
   ═══════════════════════════════════════════════════════════════════════ */

/* Severity named for what it MEANS in payroll, rather than borrowed from
   Holidays. A reader of the JSX shouldn't have to know that
   "badge-holiday-over" means "this change moves someone's pay". Colour is
   always paired with the label inside the badge, never carried by hue. */
.badge-payroll-pay { background: var(--rust-soft); color: var(--rust); }
.badge-payroll-file { background: var(--amber-soft); color: var(--amber); }
.badge-payroll-ok { background: var(--pt-soft); color: var(--pt); }
.badge-payroll-day { background: var(--teal-soft); color: var(--teal); }
.badge-payroll-alarm { background: var(--rust-soft); color: var(--rust); }

/* Row severity as a LEFT RULE, the house rule — never a filled row. Same
   mechanic .holiday-row-over already uses; it lives on DataTable's generic
   `tone` so the rule isn't re-invented per screen. */
.cr-row-tone-rust td:first-child { box-shadow: inset 3px 0 0 var(--rust); }
.cr-row-tone-amber td:first-child { box-shadow: inset 3px 0 0 var(--amber); }

/* The action panel's body: one statement, then one row of actions, then an
   optional hint. Nothing else — the detail lives in the tables below, and a
   panel that restated them would be saying each thing twice. */
.pr-body { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
.pr-lead { margin: 0; max-width: 74ch; font-size: var(--fs-base); line-height: 1.55; }
.pr-lead b { font-weight: 650; }
.pr-lead-bad { color: var(--rust); }
.pr-lead-quiet { color: var(--muted); }
.pr-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.pr-hint { font-size: var(--fs-sm); color: var(--muted); max-width: 74ch; margin: 0; }
.pr-progress { margin-top: 0; }

/* A download is an <a> so the browser does the saving — nothing pulls a few
   hundred KB of workbook through JS to hand it straight back. .btn was
   written for <button>, so an anchor wearing it needs the two properties a
   button has by default and a link does not. */
a.btn {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  line-height: 1;
}

/* Three column caps the shared sheet gets right for a 600-row table and
   wrong for these, same reasoning (and same mechanism) as
   .mileage-module's own override.

   `td.wrap`'s 170px squeezed "Why it moved" — a full sentence per row — into
   three lines while the spacer held a third of the table empty. `td.nm`'s
   220px truncated a filename, and on the Files table the filename IS the
   information, not a label for something else.

   The chart gets a max-width rather than a max-height: MonthlyBars scales x
   and y together, so capping its height would stretch its labels. Capping
   the width lets the SVG's own aspect set a sane height — the house rule that
   a chart's height belongs to its data (CLAUDE.md, learned on PAW). */
.payroll-module .cr-t td.wrap { min-width: 250px; max-width: 360px; }
.payroll-module .cr-t td.nm { max-width: 430px; }
.payroll-module .cr-bars { max-width: 780px; margin-inline: auto; }

/* The confirm-cutoff dialog's body (screen 14) — a list of what gets frozen,
   each line with its own count, and the name to record. Tokens only. */
.pr-freeze { display: flex; flex-direction: column; gap: var(--sp-3); text-align: left; margin-top: var(--sp-4); }
.pr-freeze-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); }
.pr-freeze-list li {
  display: grid;
  grid-template-columns: var(--sp-4) 1fr auto;
  gap: var(--sp-2);
  align-items: baseline;
  font-size: var(--fs-sm);
  line-height: 1.5;
}
.pr-freeze-tick { color: var(--pt); font-weight: 700; }
.pr-freeze-warn { color: var(--rust); }
.pr-freeze-warn .pr-freeze-tick { color: var(--rust); }
.pr-freeze-count {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--muted);
  white-space: nowrap;
}
.pr-freeze-by { gap: var(--sp-1); }
.pr-freeze-by .cr-field-lab { white-space: normal; line-height: 1.45; }
.pr-freeze-by .cr-search { width: 100%; }
.pr-textarea {
  width: 100%;
  font: inherit;
  font-size: var(--fs-sm);
  line-height: 1.5;
  padding: var(--sp-2) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--paper);
  color: var(--ink);
  resize: vertical;
}
.pr-textarea:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }

/* The upload target for the copy that was sent (screens 17/18). A dashed
   frame reads as "put something here" without pretending to be a drop zone
   the browser would actually accept a drag onto. */
.pr-drop {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4);
  border: 1.5px dashed var(--line);
  border-radius: var(--r-md);
  background: var(--paper);
}
.pr-drop-bad { border-color: var(--rust); background: var(--rust-soft); }
.pr-drop-text { font-size: var(--fs-sm); color: var(--muted); max-width: 76ch; }
.pr-drop-text b { display: block; color: var(--ink); font-size: var(--fs-base); margin-bottom: var(--sp-1); }
.pr-drop-bad .pr-drop-text b { color: var(--rust); }
.pr-drop-actions { margin-left: auto; }

/* A file input styled as a button: the native control cannot be styled, so it
   sits invisibly over its own label. Kept focusable — a keyboard user reaches
   the input, and the label shows the focus ring. */
label.btn { position: relative; overflow: hidden; cursor: pointer; }
.pr-file-input {
  position: absolute;
  inset: 0;
  opacity: 0;
  width: 100%;
  cursor: pointer;
}
label.btn:focus-within { outline: 2px solid var(--teal); outline-offset: 2px; }

/* Settings → Payroll. The Settings shell is not in the control-room idiom,
   so its tab content brings its own row spacing rather than inheriting a
   command bar's. */
.payroll-settings { margin-top: var(--sp-4); }
.pr-settings-row { align-items: flex-end; gap: var(--sp-4); }
.pr-day-input { width: 90px; }
.pr-reason-field { flex: 1; min-width: 240px; }
.pr-reason-field .cr-search { width: 100%; }

/* An old value, on its way to being a new one. */
.pr-was { color: var(--muted); text-decoration: line-through; }
.pr-filename { font-family: var(--font-mono); font-size: var(--fs-sm); }

/* Poland, and any other honest empty state: says what is missing rather than
   rendering a screen that looks broken. */
.pr-empty { padding: var(--sp-6) var(--sp-5); text-align: center; }
.pr-empty p { color: var(--muted); font-size: var(--fs-sm); max-width: 56ch; margin: 0 auto var(--sp-3); }
.pr-empty-lead {
  font-family: var(--font-display);
  font-size: var(--fs-lg);
  color: var(--ink) !important;
  margin-bottom: var(--sp-2) !important;
}
.pr-todo {
  list-style: none;
  margin: var(--sp-4) auto 0;
  padding: 0;
  display: inline-grid;
  gap: var(--sp-2);
  text-align: left;
  font-size: var(--fs-sm);
  color: var(--muted);
}
.pr-todo li { display: grid; grid-template-columns: var(--sp-4) 1fr; gap: var(--sp-2); }

/* The days under a change (2026-09-11): opened from the row of the total
   they explain, one line per day, in the row's expanded slot. */
.pr-days { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-1); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }
.pr-days-toggle { align-self: flex-start; }

/* Week chips on the "Timesheets not approved" panel (2026-09-11): the
   number is the label, the tone is paired with a title — never hue alone. */
.pr-wk { display: inline-flex; flex-wrap: nowrap; white-space: nowrap; gap: var(--sp-1); }
.pr-wk i { font-style: normal; font-size: var(--fs-xs); font-variant-numeric: tabular-nums; min-width: 26px; text-align: center; padding: 0 var(--sp-1); border-radius: var(--r-sm); border: 1px solid var(--line); color: var(--muted); }
.pr-wk-ok { background: var(--pt-soft); color: var(--pt); border-color: var(--pt-soft); }
.pr-wk-wait { background: var(--amber-soft); color: var(--amber); border-color: var(--amber-soft); }
.pr-wk-bad { background: var(--rust-soft); color: var(--rust); border-color: var(--rust-soft); }

/* A sub-heading inside a panel body, between two tables (2026-09-11). */
.pr-subhead { font-size: var(--fs-sm); font-weight: 650; color: var(--muted); margin: 0; padding: var(--sp-3) var(--sp-4) var(--sp-2); }

/* Decisions on changes (2026-09-11): two small buttons per pay row, the
   held reason under them, and the one-line hold form in the row's expanded
   slot. Tokens only; the "held" button is amber, never a third colour. */
.pr-decision { align-items: flex-start; }
.pr-decision-btns { display: inline-flex; gap: var(--sp-1); }
.pr-btn-hold { background: var(--amber); border-color: var(--amber); }
.pr-decision-reason { font-size: var(--fs-xs); color: var(--amber); white-space: normal; max-width: 28ch; }
.pr-decision-by { font-size: var(--fs-xs); }
.pr-hold { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3); }
.pr-hold .cr-field { flex: 1 1 32ch; }

/* What is waiting for a decision (2026-09-11): a count on the menu entry,
   a rust step in the stage bar, a rust figure in the All periods table.
   Always beside a label, never colour alone. */
.module-nav-count { margin-left: auto; font-size: var(--fs-xs); font-weight: 650; font-variant-numeric: tabular-nums; color: var(--rust); background: var(--rust-soft); border-radius: var(--r-pill); padding: 0 var(--sp-2); line-height: 1.6; }
/* A headcount, not an alarm (2026-09-14): PAW's active-this-week count. */
.module-nav-count.is-info { color: var(--muted); background: var(--paper); }
/* Collapsed rail: the pill sits at the icon's top-right corner and the full
   sentence stays in the tooltip (decision E). */
.shell-sidebar.is-collapsed .module-nav-count { position: absolute; top: 2px; right: var(--sp-1); margin: 0; font-size: 10px; padding: 0 var(--sp-1); line-height: 1.5; }
.pr-stage-flag { border-left-color: var(--rust); }
.pr-stage-flag .pr-stage-name { color: var(--rust); }
.pr-waiting { color: var(--rust); font-weight: 650; }

/* The stage bar (2026-09-11): four steps always in order, the stage read
   from the shape. Severity/progress is a LEFT RULE per step, never a filled
   banner; the current step alone gets the soft fill. */
.pr-stages {
  list-style: none; margin: 0;
  padding: var(--sp-3) var(--sp-4);
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--sp-2);
  background: var(--panel); border-bottom: 1px solid var(--line);
}
.pr-stage {
  display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0;
  padding: var(--sp-2) var(--sp-3) var(--sp-2) var(--sp-4);
  border-left: 3px solid var(--line); font-size: var(--fs-sm);
}
.pr-stage-name { font-weight: 650; color: var(--muted); }
.pr-stage-line { color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.pr-stage-next { color: var(--teal); font-weight: 650; font-size: var(--fs-xs); }
.pr-stage-done { border-left-color: var(--pt); }
.pr-stage-done .pr-stage-name { color: var(--ink); }
.pr-stage-now { border-left-color: var(--teal); background: var(--teal-soft); border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.pr-stage-now .pr-stage-name { color: var(--teal); }
.pr-stage-now .pr-stage-line { color: var(--ink); }
.pr-stage-undone { border-left-color: var(--amber); }
.pr-stage-back { color: var(--amber); }
.pr-stage-todo .pr-stage-name, .pr-stage-skipped .pr-stage-name { opacity: .6; }
@media (max-width: 720px) { .pr-stages { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* ── Mileage: To sign (2026-09-09) ─────────────────────────────────────────
   The month's claims a signature would NOT cover: amber, and never colour
   alone — the cell's title spells the money out and the panel note repeats
   the rule as visible text. */
.mileage-tosign-pending { color: var(--amber); font-weight: 600; }

/* The sheet-PDF preview drawer: the shared .drawer shell, widened for an A4
   page (the same kind of width override .employee-card-drawer makes) and
   turned into a column so the frame takes the remaining height. overflow
   hidden because the PDF viewer scrolls itself — two scrollbars fight. */
.mileage-pdf-drawer { width: 720px; display: flex; flex-direction: column; gap: var(--sp-4); overflow: hidden; }
.mileage-pdf-head { display: flex; align-items: center; gap: var(--sp-3); }
.mileage-pdf-title { flex: 1; margin: 0; font-size: var(--fs-md); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mileage-pdf-frame { flex: 1; width: 100%; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--panel); }

/* ── Mileage: To sign — the two-line row ───────────────────────────────────
   Every cell is two lines and the second is always present, so a row's
   height never depends on whether it happens to have something to say
   (2026-09-09). The stack itself is the shared .cr-cell-stack; these rules
   only set the sub-line's voice and right-align the numeric columns. */
.mileage-tosign-num { align-items: flex-end; }
.mileage-tosign-main { display: inline-flex; align-items: center; gap: var(--sp-2); }
.mileage-tosign-sub { font-size: var(--fs-xs); color: var(--muted); line-height: 1.2; }
/* The name cell's second line already owns its own gap from the stack. */
.mileage-tosign-sub .mileage-ppid { margin-top: 0; }

/* Where the signing link would go. Quiet: it is a fact about the row, not
   something to act on — the addresses are long and 17 of them down a column
   must not out-shout the names above them. */
.mileage-tosign-email { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 36ch; }

/* A row nothing can be sent to. Rust, and never the colour alone — the text
   says "cannot be sent" and the title says why. */
.mileage-tosign-blocked { color: var(--rust); font-weight: 600; }

/* Past the month's ceiling. Same rule: the word "over" or the percentage
   carries it, the colour only finds it. */
.mileage-tosign-over { color: var(--rust); font-weight: 600; }

/* Room for one state-dependent action beside Preview once sending exists
   (Send / Remind / Resend / Retry) — the gap is declared now so the second
   button lands on the scale instead of picking its own. */
.mileage-tosign-actions { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* ── Mileage: To sign — the column rhythm ──────────────────────────────────
   Measured 2026-09-09: the four figure columns took 471px between them for
   values of six characters or fewer, because `.cr-t th` is nowrap and the
   HEADING set the width — "Approved claims" was 113px above a single digit.
   Meanwhile the identity column, which now carries three things (name, PPID,
   the address a signing link goes to), was capped at 320px and clipping the
   longest address at 34 characters.
   Both are scoped to this table: every cell here is already two lines, so a
   two-line heading reads as the same rhythm rather than an exception. */
/* Do NOT cap `th` here to narrow the figure columns. At 9ch "Approved claims"
   renders mid-word ("Approve claims") and "Not on the sheet" breaks over four
   lines, taking the heading row from 31px to 79px. The headings stay on one
   line, like every other table in Bridge — and their width is not taken from
   the name column in any case: it comes out of the spacer. */

/* The name gets to set its own width here, and that is the actual fix.
   `td.nm .person-name-link`'s 88% cap is a percentage OF THE CELL, and this
   cell's width is set by its second line (the PPID plus a 34-character
   address, 293px) — so the name was handed 88% of a figure it does not
   control, 237px against the 269px the longest PT name needs, and truncated
   every row. A `ch` cap is content-relative, which is the honest unit for
   text, and lets the name contribute its own width to the column. */
.mileage-module .mileage-tosign-table .cr-t td.nm { max-width: 420px; }
.mileage-module .mileage-tosign-table .cr-t td.nm .person-name-link { max-width: 42ch; }

/* Chase — people whose sheet is owed but who must not be texted (2026-09-10).
   Deliberately quiet: this is not an alarm, it is a list of people who are
   away. Neutral rule, no severity colour, and the same table primitives the
   rest of the tab uses. */
.chase-onleave { margin-top: var(--sp-5); }
.chase-onleave-title {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  font-weight: 600;
  margin: 0 0 var(--sp-3);
  color: var(--muted);
}

/* Employee Card — leave and employment history (2026-09-10).
   The chips sit in the header beside the employment badge, because the leave
   is the context every figure below is read against, not an exception. Muted
   and amber respectively: neither is an alarm, and being on maternity leave
   is emphatically not a problem to fix. No spacing values here — the section
   itself is an EmployeeCardSection and inherits the card's own token. */
.employee-card-chip {
  border-radius: var(--r-pill);
  padding: 2px 9px;
  font-size: var(--fs-xs);
  font-weight: 600;
  border: 1px solid currentColor;
  white-space: nowrap;
}
.employee-card-chip-leave { color: var(--muted); background: var(--paper); }
/* A pointer record (secondaryRecords.js) is not a fault, so it carries no
   severity colour — the neutral pair the leave chip uses, for the same
   reason: it changes how the card is read, it is not something to fix. */
.employee-card-chip-secondary { color: var(--muted); background: var(--paper); }
.employee-card-chip-history { color: var(--amber); background: var(--amber-soft); }
.employee-card-chip-returning { color: var(--amber); background: var(--amber-soft); }
.employee-card-chip-returning-overdue { color: var(--rust); background: var(--rust-soft); }
/* The canonical engagement state's two exceptions (2026-10-07,
   person/engagement.js) — PAW's own colours: amber for a missing extension
   (a data task, presumed working), rust for the bench (idle, costing money).
   Always the word too, never the colour alone. */
.employee-card-chip-missing-extension { color: var(--amber); background: var(--amber-soft); }
.employee-card-chip-bench { color: var(--rust); background: var(--rust-soft); }
/* Risk › Returning to work (2026-09-15). State is a word with a dot; the
   chips say what each system currently holds. Overdue is the row's left rule. */
.risk-return-state { white-space: nowrap; }
.risk-return-state::before { content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; background: var(--muted); }
.risk-return-state-rust { color: var(--rust); font-weight: 600; } .risk-return-state-rust::before { background: var(--rust); }
.risk-return-state-amber { color: var(--amber); font-weight: 600; } .risk-return-state-amber::before { background: var(--amber); }
.risk-return-chips { display: inline-flex; gap: 6px; }
.risk-return-chip { font-size: var(--fs-xs); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 1px 8px; color: var(--muted); white-space: nowrap; }
.risk-return-chip-bad { color: var(--rust); border-color: var(--rust); }
.risk-return-chip-ok { color: var(--teal); border-color: var(--teal); }

.employee-card-empcell-sub {
  display: block;
  font-size: var(--fs-xs);
  color: var(--muted);
}

.employee-card-history { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.employee-card-history td {
  padding: var(--sp-2) var(--sp-3) var(--sp-2) 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.employee-card-history tr:last-child td { border-bottom: 0; }
.employee-card-history-period { font-variant-numeric: tabular-nums; white-space: nowrap; }
.employee-card-history-note { display: block; color: var(--muted); font-size: var(--fs-xs); }
/* The gap is a ROW, not a note on the period below it: "not employed for
   182 days" is a fact of the same kind as the periods either side. */
.employee-card-history-gap td { background: var(--amber-soft); color: var(--ink); }

/* PAW — the On-leave band's legend swatch. The stipple is defined as an SVG
   <pattern> in PawStackedBarChart.jsx for the bars themselves; this is the
   CSS equivalent for the legend, which is plain HTML. */
.paw-legend-swatch-onleave-stipple {
  background-color: var(--muted);
  background-image: radial-gradient(var(--paper) 0.9px, transparent 0.9px);
  background-size: 4px 4px;
}

/* Employment timeline — breaks in the BC Employment bar (2026-09-10).
   Drawn OVER the bar rather than by splitting it: the bar means "employed",
   which stays true through a leave, and cutting it would say they had left.
   The two kinds are deliberately different weights, because they are
   different facts — away, versus gone. */
.timeline-break {
  position: absolute;
  top: 0;
  bottom: 0;
  border-radius: 2px;
}
/* On leave: employed, not working. Reads as a gap punched in the bar —
   the paper ground showing through, with a hairline so a one-week break is
   still visible at this scale. */
.timeline-break-leave {
  background: var(--paper);
  box-shadow: inset 0 0 0 1px var(--line);
}
/* Not employed: the stronger statement, so it is hatched rather than plain
   — a reader should not have to check the legend to tell which is which. */
.timeline-break-not-employed {
  background: repeating-linear-gradient(
    45deg,
    var(--paper) 0 3px,
    var(--line) 3px 6px
  );
  box-shadow: inset 0 0 0 1px var(--muted);
}

.timeline-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  font-size: var(--fs-xs);
  color: var(--muted);
}
.timeline-legend-item { display: inline-flex; align-items: center; gap: var(--sp-2); }
.timeline-legend-swatch {
  display: inline-block;
  width: 14px;
  height: 10px;
  border-radius: 2px;
  position: static;
}

/* Timesheets → Messages (2026-09-10): the batch surface. Reuses the Holidays
   Messages tab's own tick control (.bmsg-tick) rather than defining a second
   one — the gesture is identical, and two controls for one action is how the
   two screens start to feel like different apps. */
.chase-actionbar {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  padding: var(--sp-3) var(--sp-4);
  /* Sticky for the same reason the Chase tab's own action bar is: a 240-row
     table means the count and the send button would otherwise scroll away
     from the selection they describe. */
  position: sticky;
  top: 0;
  z-index: 2;
}
.chase-actionbar-spacer { flex: 1; }

.chase-draft-progress {
  height: 8px;
  background: var(--line);
  border-radius: var(--r-pill);
  overflow: hidden;
  margin-bottom: var(--sp-3);
}
.chase-draft-progress-fill {
  height: 100%;
  background: var(--teal);
  transition: width var(--motion-fast, 120ms) linear;
}

/* Timesheets → Messages, the people table's own states (2026-09-11).
   A person unticked BY HAND shares the held row's opacity — "quietly out of
   the way, still there" should look the same whichever reason put them
   there — and the message cell says which reason in words, never by hue. */
.chase-row-removed td { opacity: 0.6; }
.chase-row-removed:hover td { opacity: 0.85; }
/* The empty placeholder a ticked group's held/cannot-text rows carry, so the
   names stay aligned with the rows that do have a tick. Not a control. */
.bmsg-tick-ghost { border-style: dashed; opacity: 0.45; cursor: default; pointer-events: none; }
/* Draft freshness, paired with the word beside it (today / date). */
.chase-draft-today { color: var(--teal); }
.chase-draft-stale { color: var(--amber); }
/* The Due column (2026-09-14). Each colour is paired with the word beside
   the date ("· late", "· today"), never carried by hue alone. */
.chase-due-late { color: var(--rust); font-weight: 600; }
.chase-due-today { color: var(--amber); font-weight: 600; }
/* The per-row Redraft button, on its own line under the message text. */
.chase-msg-actions { display: block; margin-top: var(--sp-1); }
/* Settings → Payroll → Absence policy: a checkbox and its word, read together. */
.pr-policy-flag { display: inline-flex; align-items: center; gap: var(--sp-1); }
.pr-policy .cr-tw { max-height: none; }
/* Employee Card → Holiday: "BC data as of …" + Recompute from BC (2026-09-14). */
.employee-card-freshness { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; font-size: var(--fs-sm); }
/* The strip's "BC data as of … · Recompute from BC" (2026-09-15). A fact and
   its action read as one unit; the button is tertiary so it never competes
   with the sidebar's secondary "Refresh data", which is a different job. */
.recompute-control { display: inline-flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.recompute-control .btn { display: inline-flex; align-items: center; gap: 6px; }
.recompute-control .btn:not(:disabled) { color: var(--teal); }
.recompute-control .btn:hover:not(:disabled) { color: var(--ink); }
.recompute-control .recompute-btn-danger:not(:disabled) { color: var(--rust); }
.recompute-btn-icon { display: inline-flex; width: 13px; height: 13px; }
.recompute-btn-icon svg { width: 100%; height: 100%; }
.recompute-error { color: var(--rust); }
.chargeability-needs-recompute-action { display: flex; justify-content: center; margin-top: var(--sp-3); }
/* Time Off's "Holiday left after <month>" (2026-09-15). Over is rust ink plus a
   dot AND the word "over" in the cell — colour never carries it alone. Locked
   (PT six-month rule) is muted text, never a negative figure. */
.tol-over { color: var(--rust); font-weight: 600; white-space: nowrap; }
.tol-over::before { content: ''; display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--rust); margin-right: 6px; vertical-align: 1px; }
/* Body face at the small size, not mono: "locked until 22/12/2026" in mono set the whole column to 190px. */
.tol-locked { color: var(--muted); font-weight: 500; white-space: nowrap; font-family: var(--font-body); font-size: var(--fs-xs); }
.timeoff-detail-balance { margin-top: var(--sp-2); font-size: 12px; color: var(--muted); display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: baseline; }
.timeoff-detail-balance b { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.timeoff-detail-balance-detail { font-family: var(--font-mono); font-size: var(--fs-xs); }
/* A heading's qualifier on a second, quieter line (DataTable `sub`, 2026-09-15). */
.cr-t th { vertical-align: bottom; }
.cr-th-sub { display: block; font-weight: 500; color: var(--muted); opacity: .8; font-size: var(--fs-xs); line-height: 1.3; }
/* Time Off's Composition cell (2026-09-15): one bar, then words — one line.
   Leave (Vacation, B2B leave) teal, Sick amber, no code hatched, other muted;
   each tone is always beside its word, never alone. */
.tocomp { display: inline-flex; align-items: center; gap: 10px; white-space: nowrap; }
.tocomp-bar { display: inline-flex; width: 96px; height: 6px; border-radius: var(--r-pill); overflow: hidden; background: var(--paper); flex: none; padding: 0; border: 0; cursor: pointer; }
.tocomp-bar:hover { outline: 2px solid var(--line); outline-offset: 2px; }
.tocomp-bar:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
/* The codes, on click: a fixed popover under the bar (the table scrolls in its
   own box, so absolute would clip on the last rows). One row per code. */
.tocomp-pop { position: fixed; z-index: 40; display: grid; gap: 4px; min-width: 260px; padding: var(--sp-2) var(--sp-3); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-sm); box-shadow: var(--shadow-2); font-size: var(--fs-sm); color: var(--ink); white-space: nowrap; }
.tocomp-pop-h { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; margin-bottom: 2px; }
.tocomp-pop-row { display: grid; grid-template-columns: auto auto 1fr auto; gap: var(--sp-2); align-items: center; }
.tocomp-pop-row code { font-family: var(--font-mono); font-size: var(--fs-xs); background: var(--paper); border-radius: 4px; padding: 0 5px; }
.tocomp-pop-l { color: var(--muted); font-size: var(--fs-xs); overflow: hidden; text-overflow: ellipsis; max-width: 260px; }
.tocomp-pop-row b { font-variant-numeric: tabular-nums; font-weight: 600; }
.tocomp-bar i { display: block; height: 100%; }
.tocomp-t { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.tocomp-t b { color: var(--ink); font-weight: 600; }
.tocomp-dot { display: inline-block; width: 7px; height: 7px; border-radius: 2px; margin-right: 4px; vertical-align: 0; }
.tocomp-leave { background: var(--teal); }
.tocomp-sick { background: var(--amber); }
.tocomp-other { background: var(--muted); opacity: .55; }
.tocomp-uncoded { background: repeating-linear-gradient(135deg, var(--muted) 0 2px, transparent 2px 4px); opacity: .7; }
.chase-actionbar-removed { color: var(--ink); }
/* The message column holds a whole SMS; the generic `.cr-t td.wrap` cap of
   170px would fold it into a ten-line ribbon. Scoped to the cell that holds
   a message, the same `:has` pattern `.bmsg .cr-t td.nm:has(.bmsg-g)` uses. */
.chase-tab .cr-t td:has(.chase-msg) { white-space: normal; min-width: 280px; max-width: 520px; }

/* ── Sign-in (2026-09-15) ──────────────────────────────────────────────── */
.login-shell { min-height: 100vh; display: grid; place-items: center; padding: var(--sp-6) var(--sp-4); background: var(--paper); }
.login-card { width: 100%; max-width: 400px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow-2); padding: var(--sp-6) var(--sp-6) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-3); }
.login-brand { font-family: var(--font-display); font-weight: 700; font-size: var(--fs-lg); color: var(--teal); }
.login-title { font-family: var(--font-display); font-weight: 500; font-size: var(--fs-xl); margin: 0; }
.login-lede { margin: 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.5; }
.login-field { display: flex; flex-direction: column; gap: var(--sp-1); font-size: var(--fs-xs); color: var(--muted); }
.login-field input { font: inherit; font-size: var(--fs-base); color: var(--ink); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 9px var(--sp-3); height: var(--control-h); }
.login-field input:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
/* The primary button on a sign-in card is the one control on the screen: a
   full step taller than a toolbar button, body-size type, the shadow the
   primary already carries. Spacing and type from the tokens, never a pixel. */
.login-submit { width: 100%; margin-top: var(--sp-2); padding: var(--sp-3) var(--sp-4); font-size: var(--fs-base); line-height: 1.2; }
.login-submit:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.login-submit:disabled { cursor: not-allowed; }
.login-form { display: flex; flex-direction: column; gap: var(--sp-3); }
/* Password field: the input keeps its box; Show/Hide sits inside its right edge. */
.login-input { position: relative; display: flex; }
.login-input input { flex: 1; padding-right: var(--sp-8); }
.login-toggle { position: absolute; right: var(--sp-1); top: 50%; transform: translateY(-50%); font: inherit; font-size: var(--fs-xs); font-weight: 600; color: var(--teal); background: none; border: 0; padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm); cursor: pointer; }
.login-toggle:hover { background: var(--teal-soft); }
.login-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: 0; }
/* The rule checklist under a NEW password: two columns, a hollow dot that fills as each rule is met. */
.login-rules { list-style: none; margin: var(--sp-1) 0 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-1) var(--sp-3); font-size: var(--fs-xs); color: var(--muted); }
.login-rules li { display: flex; align-items: center; gap: var(--sp-2); }
.login-rules li::before { content: ""; width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid var(--muted); flex: none; box-sizing: border-box; }
.login-rules li.login-rule-ok { color: var(--teal); }
.login-rules li.login-rule-ok::before { background: var(--teal); border-color: var(--teal); }
/* The row of text links under the button: "Forgot your password?", "Back to sign in". */
.login-links { display: flex; justify-content: center; gap: var(--sp-4); font-size: var(--fs-sm); }
/* A callout is a sentence that qualifies the whole card (a link that expired, a mail on its way): a soft panel with a left rule, never a filled banner. */
.login-callout { margin: 0; font-size: var(--fs-sm); line-height: 1.5; color: var(--ink); background: var(--teal-soft); border-left: 3px solid var(--teal); border-radius: 0 var(--r-sm) var(--r-sm) 0; padding: var(--sp-2) var(--sp-3); }
.login-callout-amber { background: var(--amber-soft); border-left-color: var(--amber); }
.login-ms { width: 100%; display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2); }
.login-ms-mark { width: 12px; height: 12px; background: conic-gradient(from 90deg at 50% 50%, #f25022 0 25%, #7fba00 0 50%, #00a4ef 0 75%, #ffb900 0); border-radius: 2px; }
.login-error { margin: 0; color: var(--rust); font-size: var(--fs-sm); }
.login-notice { margin: 0; color: var(--teal); font-size: var(--fs-sm); }
.login-link { font: inherit; font-weight: 600; color: var(--teal); background: none; border: 0; padding: 0; cursor: pointer; text-decoration: none; }
.login-link:hover { text-decoration: underline; text-underline-offset: 3px; }
.login-link:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: 2px; }
/* The signed-in person, in the sidebar footer above Refresh data. */
.user-chip { display: flex; align-items: center; gap: var(--sp-1); margin-bottom: var(--sp-3); }
/* The chip is the way to My account (2026-09-16): a quiet button in the nav's own vocabulary — a rounded row that tints on hover and reads as selected while the page is open. */
.user-chip-open { flex: 1; min-width: 0; display: flex; align-items: center; gap: var(--sp-2); padding: var(--sp-2); border: 1px solid transparent; border-radius: var(--r-sm); background: transparent; cursor: pointer; text-align: left; font: inherit; color: inherit; }
.user-chip-open:hover { background: var(--paper); border-color: var(--line); }
.user-chip-open.is-open { background: var(--teal-soft); border-color: var(--teal-soft); }
.user-chip-open.is-open .user-chip-sub, .user-chip-open.is-open .user-chip-caret { color: var(--teal); }
.user-chip-open:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.user-chip-text { min-width: 0; flex: 1; display: flex; flex-direction: column; }
.user-chip-label { font-size: var(--fs-sm); font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-chip-sub { font-size: var(--fs-xs); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.user-chip-caret { color: var(--muted); font-size: var(--fs-md); line-height: 1; }
.shell-sidebar.is-collapsed .user-chip-open, .shell-sidebar.is-collapsed .user-chip-out { display: none; }
/* My account (2026-09-16): control-room panels; the body keeps the same rhythm as a Settings tab. */
.account-page .module-header { display: flex; justify-content: space-between; align-items: flex-end; gap: var(--sp-4); padding-right: var(--sp-5); }
.account-body { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4) var(--sp-4); }
.account-row { display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap; }
.account-fields { display: flex; flex-direction: column; gap: var(--sp-3); max-width: 420px; }
.account-row > .account-lede { flex: 1 1 260px; align-self: center; }
.account-name { flex: 1 1 260px; max-width: 420px; }
.account-facts { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp-2) var(--sp-4); margin: 0; font-size: var(--fs-sm); }
.account-facts dt { color: var(--muted); }
.account-facts dd { margin: 0; color: var(--ink); }
.account-role { display: inline-block; font-weight: 600; color: var(--teal); background: var(--teal-soft); border-radius: var(--r-pill); padding: 0 var(--sp-2); margin-right: var(--sp-1); }
.account-pill { display: inline-block; font-size: var(--fs-xs); color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 0 var(--sp-2); margin-left: var(--sp-1); }
.account-lede { margin: 0; font-size: var(--fs-sm); color: var(--muted); line-height: 1.5; }
.account-error { margin: 0; font-size: var(--fs-sm); color: var(--rust); }
.account-note { margin: 0; font-size: var(--fs-sm); color: var(--teal); }
.cr-ph-r-note { font-size: var(--fs-sm); color: var(--muted); font-weight: 400; }
@media (max-width: 640px) { .account-facts { grid-template-columns: 1fr; gap: var(--sp-1); } .account-facts dt { margin-top: var(--sp-2); } }
/* Settings › Users */
.users-invite { display: flex; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-3) var(--sp-4); }
.users-invite .cr-search { min-width: 220px; }
.users-error { color: var(--rust); }
.users-status { white-space: nowrap; font-size: var(--fs-xs); }
.users-status-ok { color: var(--teal); } .users-status-amber { color: var(--amber); } .users-status-muted { color: var(--muted); }
.users-disable:not(:disabled) { color: var(--rust); }
.users-actions { display: inline-flex; gap: var(--sp-2); white-space: nowrap; }
/* The name cell is a quiet button (2026-09-16): the text as it is, a pencil that appears on hover; an inline form while editing. */
.users-name { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: text; display: inline-flex; align-items: center; gap: var(--sp-1); border-radius: var(--r-sm); }
.users-name-pen { color: var(--muted); opacity: 0; font-size: var(--fs-xs); transition: opacity var(--dur-fast) var(--ease); }
.users-name:hover .users-name-pen, .users-name:focus-visible .users-name-pen { opacity: 1; }
.users-name:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.users-name-edit { display: inline-flex; align-items: center; gap: var(--sp-1); }
.users-name-input { min-width: 180px; }
.users-name-input.users-name-half { min-width: 120px; }
.users-notices { display: grid; gap: var(--sp-2); }
/* ── Add a user — the drawer (2026-09-16) ─────────────────────────────── */
.add-user-drawer { display: flex; flex-direction: column; padding: 0; }
.au-form { display: contents; }
.users-head-right { display: inline-flex; align-items: center; gap: var(--sp-3); }
.users-name-cell { display: inline-flex; align-items: center; gap: var(--sp-1); }
.users-name-pen-btn { cursor: pointer; }
.users-name-cell:hover .users-name-pen, .users-name-pen-btn:focus-visible .users-name-pen { opacity: 1; }
.add-user-drawer .drawer-header { padding: var(--sp-4) var(--sp-5); margin: 0; border-bottom: 1px solid var(--line); }
.au-body { flex: 1; overflow-y: auto; padding: var(--sp-4) var(--sp-5); display: flex; flex-direction: column; gap: var(--sp-4); }
/* A long drawer scrolls; its parts never shrink. Without this a search's results list squashed to zero height once the body outgrew the drawer (2026-10-06, found on the seat-kind drawer's vendor and owner search). */
.au-body > * { flex-shrink: 0; }
.au-footer { display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--line); background: var(--panel); }
.au-step { font-size: var(--fs-xs); letter-spacing: .05em; text-transform: uppercase; color: var(--teal); font-weight: 600; }
.au-results { border: 1px solid var(--line); border-radius: var(--r-sm); overflow: hidden; }
.au-result { display: flex; align-items: center; gap: var(--sp-2); width: 100%; text-align: left; padding: var(--sp-2) var(--sp-3); border: 0; border-top: 1px solid var(--line); background: var(--panel); font: inherit; color: var(--ink); cursor: pointer; }
.au-result:first-child { border-top: 0; }
.au-result:hover, .au-result:focus-visible { background: var(--teal-soft); outline: none; }
.au-result-name { font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.au-empty { padding: var(--sp-3); color: var(--muted); font-size: var(--fs-sm); }
.au-picked { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--teal); border-radius: var(--r-sm); background: var(--teal-soft); }
.au-picked.is-manual { border-color: var(--line); background: var(--paper); }
.au-picked-text { flex: 1; min-width: 0; }
.au-picked-name { font-weight: 600; }
.au-picked-sub { font-size: var(--fs-xs); color: var(--muted); display: flex; gap: var(--sp-2); align-items: center; flex-wrap: wrap; }
.au-two { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-3); }
.au-roles { display: grid; gap: var(--sp-2); }
.au-role { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-1) var(--sp-3); align-items: start; padding: var(--sp-2) var(--sp-3); border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer; font-size: var(--fs-sm); }
.au-role.is-on { border-color: var(--teal); background: var(--teal-soft); }
.au-role input { margin: 3px 0 0; accent-color: var(--teal); }
.au-role b { font-weight: 600; }
.au-role span { grid-column: 2; color: var(--muted); font-size: var(--fs-xs); line-height: 1.45; }
.au-note { margin: 0; font-size: var(--fs-xs); color: var(--muted); line-height: 1.5; }
.au-ok { margin: 0; font-size: var(--fs-xs); color: var(--teal); }
.au-warn { margin: 0; font-size: var(--fs-xs); color: var(--amber); }
.au-error { margin: 0; font-size: var(--fs-sm); color: var(--rust); }
.au-done { display: flex; flex-direction: column; gap: var(--sp-3); }
.au-done h4 { margin: 0; font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 500; }
/* Payroll downloads behind the door (2026-09-16). */
.pr-download { display: inline-flex; align-items: center; gap: var(--sp-2); }
.pr-download-error { font-size: var(--fs-xs); color: var(--rust); }
/* Edit a user (2026-09-16): the same drawer, a person's facts and every account action in one place. */
.eu-facts { display: grid; grid-template-columns: max-content 1fr; gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-sm); }
.eu-facts dt { color: var(--muted); } .eu-facts dd { margin: 0; }
.eu-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.eu-danger { color: var(--rust); }
.users-role-word { font-weight: 600; }
/* Seat kinds (2026-10-06): the drawer's title is an address, and who set the tag sits left of the buttons. */
.sk-title { word-break: break-all; }
.sk-signed { margin-right: auto; display: inline-flex; align-items: center; gap: var(--sp-3); font-size: var(--fs-xs); color: var(--muted); }
/* Why Save is not available yet, beside it; and a disabled button in this
   drawer shell says "not allowed", never the wait cursor the shared .btn
   rule gives it — only a save in progress waits (2026-10-06, production). */
.sk-missing { font-size: var(--fs-xs); color: var(--amber); font-weight: 600; white-space: nowrap; }
/* The seat-kind drawer (2026-10-06 rework): the Employee Card's width, the six
   kinds in two columns, and a search's results scrolling inside a short box
   so the steps and the footer stay in view. */
.seat-kind-drawer { width: 620px; }
.sk-kinds { grid-template-columns: 1fr 1fr; }
.seat-kind-drawer .au-results { max-height: 216px; overflow-y: auto; }
@media (max-width: 700px) { .seat-kind-drawer { width: 100%; } .sk-kinds { grid-template-columns: 1fr; } }
.add-user-drawer .btn:disabled { cursor: not-allowed; }
.add-user-drawer .btn[aria-busy='true'] { cursor: wait; }
@media (max-width: 560px) { .au-two { grid-template-columns: 1fr; } .eu-facts { grid-template-columns: 1fr; } }
.mileage-pdf-error, .mileage-export-error { color: var(--rust); }
.users-issued { display: grid; gap: var(--sp-2); }
.users-issued-lede { margin: 0; color: var(--ink); }
.users-issued-url { display: block; font-family: var(--font-mono); font-size: var(--fs-xs); word-break: break-all; user-select: all; color: var(--ink); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-3); }

/* ── Data quality module (2026-09-17) ──────────────────────────────────────
   Built on the control-room primitives; these rules only shape the cells
   the primitives do not know: the two-line compare cell, the fix path, the
   expanded row and the acknowledge form. Tokens only.

   The expanded row (cleanup, 2026-09-17): the ROW is the fact sheet and the
   panel under it holds only Why (left, 3/5) and Do (right, 2/5) — nothing
   the row shows is repeated below it. The AI paragraph wears the Employee
   Card's own .ai-summary-* vocabulary (✨ label, italic text) behind a
   violet left rule, never a filled panel. */
.dq-badges { display: inline-flex; gap: var(--sp-1); flex-wrap: wrap; align-items: center; }
/* The compare cell must never be wider than its column: the table gives a
   wrapping column ~170px, and a min-width here made the values paint over
   the Fix column (measured 2026-09-17: 102px past the cell edge). minmax(0,
   1fr) + overflow-wrap lets a long email break inside the cell instead. */
.dq-cmp { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 1px var(--sp-2); align-items: baseline; min-width: 0; }
.dq-src { font-size: 10px; color: var(--muted); letter-spacing: .03em; text-transform: uppercase; }
.dq-val { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink); overflow-wrap: anywhere; min-width: 0; white-space: normal; }
.dq-val-blank { font-family: var(--font-body); font-style: italic; color: var(--muted); font-size: var(--fs-sm); }
/* The other records a shared-mobile row names (2026-10-09): one person per
   line, name link then badges, wrapping inside the cell. */
.dq-also { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.dq-also-row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2); min-width: 0; font-size: var(--fs-sm); }
.dq-also-state { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
/* In the row's values cell the chips sit at the cell's own small size, so a
   name, its PPID and its status take two lines rather than four. */
.dq-cmp .dq-also-row .badge, .dq-cmp .dq-also-row .id-code { font-size: var(--fs-xs); }
.dq-path { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--ink); display: block; min-width: 0; white-space: normal; overflow-wrap: anywhere; }
.dq-prio { font-size: var(--fs-xs); color: var(--muted); }
.dq-prio-blocking { color: var(--rust); font-weight: 600; }
.dq-prio-mismatch { color: var(--amber); font-weight: 600; }
/* The action column: the AI button on every row, and the row's own chevron. */
.dq-acts { display: inline-flex; align-items: center; gap: var(--sp-2); white-space: nowrap; }
.dq-chev { background: none; border: 0; padding: 0 var(--sp-1); color: var(--muted); font: inherit; font-size: var(--fs-sm); line-height: 1; cursor: pointer; }
.btn-ai { background: var(--violet-soft); color: var(--violet); border-color: var(--violet); white-space: nowrap; }
.btn-ai:hover:not(:disabled) { background: var(--panel); }
.btn-small { padding: 2px 9px; font-size: var(--fs-xs); }
/* The expanded row: Why · Do. */
.dq-exp { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: var(--sp-6); }
.dq-exp-solo { grid-template-columns: minmax(0, 1fr); }
.dq-why, .dq-do { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; font-size: var(--fs-sm); }
.dq-col-h { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
.dq-detail { margin: 0; font-size: var(--fs-base); line-height: 1.5; max-width: 60ch; color: var(--ink); }
.dq-ai { display: flex; flex-direction: column; gap: var(--sp-1); padding-left: var(--sp-3); box-shadow: inset 3px 0 0 var(--violet); }
.dq-ai .ai-summary-text { max-width: 60ch; }
.dq-seen { font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.dq-fix { display: flex; flex-direction: column; gap: var(--sp-2); }
.dq-guard { font-size: var(--fs-xs); }
.dq-or { display: flex; align-items: center; gap: var(--sp-2); color: var(--muted); font-size: var(--fs-xs); }
.dq-or::before, .dq-or::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.dq-ack { display: flex; flex-direction: column; gap: var(--sp-2); }
.dq-ack .dq-input { width: 100%; }
.dq-ack-row { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; font-size: var(--fs-xs); }
.dq-error { color: var(--rust); font-size: var(--fs-sm); }
.dq-note { color: var(--amber); font-size: var(--fs-sm); }
/* The closed tabs (2026-09-17): the reason as the widest cell, the expiry
   dot (colour always beside a date, never alone), the closure's before/after
   list. A parked row has no rule unless it expires within 7 days (amber);
   a fixed row has none. */
.dq-sub { font-size: var(--fs-xs); }
.dq-reason { font-style: italic; overflow-wrap: anywhere; }
.dq-soon { color: var(--amber); font-weight: 600; white-space: nowrap; }
.dq-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--amber); margin-right: var(--sp-2); vertical-align: 1px; }
.dq-kv { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--sp-1) var(--sp-3); margin: 0; font-size: var(--fs-sm); }
.dq-kv dt { color: var(--muted); }
.dq-kv dd { margin: 0; min-width: 0; }
@media (max-width: 1024px) { .dq-exp { grid-template-columns: minmax(0, 1fr); } }

/* ── Data quality — this table's own column budget (2026-09-17, measured) ──
   The shared caps are built for a table of FIGURES: every real column shrinks
   to its content and only the spacer asks for the rest (`.cr-t th, .cr-t td
   { width: 1% }`). This table is mostly PROSE, so the spacer won. Measured at
   1440, where the table has 1136px: the spacer held 212 of them while Check,
   the values and the fix path all sat on the 120px floor of `.cr-t td.wrap`,
   wrapping a 27-character fix path to four lines and breaking an email into
   four fragments; rows ran 78–140px tall. The same three columns on the two
   closed tabs sat on their own floor beside an 84px / 226px spacer.

   Payroll and Mileage already own blocks like this one, for the same reason.
   Keep the numbers here and nowhere else: a call site must not hand-pick a
   width, and every value below was read off a rendered page, not guessed.

   ORDER MATTERS in the two `wrap` rules: `.cr-t td.wrap.wide` (the one column
   a tab is organised around — Reason on Acknowledged, How on Fixed) carries
   the same specificity as a plain `td.wrap` rule and sits EARLIER in this
   file, so a blanket `.data-quality-module .cr-t td.wrap` would silently
   narrow it from 240px to 260's smaller sibling. Hence `:not(.wide)`, and an
   explicit pair of limits for `.wide` itself.

   2026-09-18 — the fix above was only ever true at 1440, the one width it was
   measured at. A fixed `max-width` on every real column left the SPACER as the
   only thing in the table that could still grow, so above ~1450 it took the
   whole surplus back: 546px of the 1616px table at 1920, 1186px of 2256 at
   2560, while Check, the compare cell and the fix path sat on their floors
   wrapping to three lines. Caps were the wrong instrument — they cannot
   express "grow, but not past readable".

   THE TRAP, which is why the first attempt looked right at the desk: in
   `table-layout: auto` a `width` on a cell is a HINT, and a `max-width`
   BESIDE one is ignored outright. Declaring both reads as a ceiling and is
   inert — measured at 2560, Check reached 451px and Reason 632px with their
   caps declared. So the proportions below carry `max-width: none` to say that
   out loud, and the one real ceiling sits on the TABLE, where it is honoured.

   One table serves all three tabs (columnsFor's three column sets), so every
   prose column any tab renders is covered here: `wrap` (Check, and Fixed's
   Was · other source), `wrap.wide` (Reason, How), and the two `:has()` pairs.
   Everything else — the person cell, Acknowledged by, Closed, and the three
   `num` columns — takes its content width and is deliberately given no hint. */

/* The person: the NAME takes the ellipsis so a truncation is visible, and the
   badge row wraps INSIDE the cell — before this, `white-space: nowrap` plus
   `overflow: hidden` on a flex child cut the name mid-word with no ellipsis
   at all and sliced the contract badge 31px past the cell edge.

   PINNED, not capped (2026-09-18). A `max-width` alone let the column size to
   its content, and the three tabs hold DIFFERENT people — so the identity
   column was a different width on each, and the whole table stepped sideways
   as you changed tab. Min and max are the same number on purpose: this is the
   sticky column every row is read from and the one the eye returns to, so it
   must not move. 320px fits the longest name in the live list (302px measured)
   with the badges beside it, and the ellipsis handles the rest honestly. */
.data-quality-module .cr-t td.nm,
.data-quality-module .cr-t th.nm { min-width: 320px; max-width: 320px; width: 320px; }
.data-quality-module .cr-t td.nm .cr-cell-stack { max-width: 100%; }
.data-quality-module .cr-t td.nm .person-name-link {
  display: inline-block; max-width: 100%; vertical-align: bottom;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The wrapping columns take the room the spacer was holding — and keep taking
   it as the window grows, which is the whole point of a proportion rather than
   a cap. The floors stay in px: below ~1450 there is no surplus to share and a
   percentage of a table narrower than its content is meaningless, so the min
   is what holds the layout there and the table scrolls between its two sticky
   ends exactly as before. */
.data-quality-module .cr-t .cr-t-sp { width: 0; }
.data-quality-module .cr-t td.wrap:not(.wide) { min-width: 190px; max-width: none; width: 20%; }
.data-quality-module .cr-t td.wrap.wide { min-width: 260px; max-width: none; width: 28%; }
/* The compare cell holds two values, one per line, so it earns the most of the
   three; the fix path is a place in BC rather than a value to read across, so
   it earns the least. */
.data-quality-module .cr-t td.wrap:not(.wide):has(.dq-cmp) { min-width: 190px; max-width: none; width: 22%; }
.data-quality-module .cr-t td.wrap:not(.wide):has(.dq-path) { min-width: 170px; max-width: none; width: 18%; }

/* No ceiling of its own. The module frame at the top of this file already caps
   every screen in Bridge at 1400px and centres it, so the table cannot run
   away on a wide monitor and a second number here would only be a competing
   answer to a question the app has already settled. An earlier pass on
   2026-09-18 did add one (1700px, on the table) while Data quality was missing
   from that frame — putting the module on the frame is the fix, and this is
   the number it replaces. */

/* A value breaks at a boundary now that it has the room; `anywhere` split
   `+48600000001` between its last two digits and an email into four pieces.
   The fix path is a page and a field, not a value to copy: body face. */
.data-quality-module .dq-val { overflow-wrap: break-word; word-break: normal; }
.data-quality-module .dq-path { font-family: var(--font-body); font-size: var(--fs-sm); }

/* The action cell sticks to the right edge, the way the person cell sticks to
   the left: below ~1400px this table scrolls sideways, and Explain was the
   thing that went off-screen (128px of overflow at 1100, 240px at 720). */
.data-quality-module .cr-t th:last-child,
.data-quality-module .cr-t td:last-child {
  position: sticky; right: 0; background: var(--panel); border-left: 1px solid var(--cr-line);
}
.data-quality-module .cr-t th:last-child { background: var(--paper); z-index: 3; }
.data-quality-module .cr-t tbody tr:hover td:last-child { background: #fbfaf6; }
.data-quality-module .cr-row-on td:last-child { background: var(--teal-soft); }
/* One column goes on a narrow screen, and WHICH one is per tab: Open's First
   seen and Fixed's Open for are both carried by the expanded panel's own line,
   so they can. Acknowledged's Until cannot — it is that tab's sort key, it
   holds the expiry dot, and it is the figure the tab exists to show — so
   Acknowledged scrolls instead, between the two sticky ends.

   Each threshold is that tab's own arithmetic, not a round number. Re-derived
   2026-09-18, when the module joined the shared frame: the page now gives a
   table `viewport − 400` (the frame's 48px gutters cost 96 of it) up to the
   1400px measure, where it settles at a constant 1268. Measured columns are
   1090 on Open and 1146 on Fixed, so each drops exactly where it stops
   fitting — 1490 and 1546.

   Below that each tab still has a floor it cannot go under (994 Open, 1072
   Fixed, 1123 Acknowledged), so a 1440 laptop holds Open with nothing to
   scroll and the other two scroll a little between the two sticky ends. That
   is the designed narrow state, not a fault: the person cell is sticky left
   and the action cell sticky right precisely so the row keeps its handles
   while the middle moves. */
@media (max-width: 1489px) {
  .dq-tab-open .cr-t thead th:nth-last-child(2),
  .dq-tab-open .cr-t tbody tr > td:nth-last-child(2) { display: none; }
}
@media (max-width: 1545px) {
  .dq-tab-fixed .cr-t thead th:nth-last-child(2),
  .dq-tab-fixed .cr-t tbody tr > td:nth-last-child(2) { display: none; }
}

/* Fixed carries one column more than the other two tabs (Closed AND Open for
   beside How), and landed 12px over a 1440 screen. Its values cell gives them
   back: a closed row's values are context, not the field being worked. */
.dq-tab-fixed .cr-t td.wrap:not(.wide):has(.dq-cmp) { min-width: 175px; }

/* The values on the Acknowledged panel: that tab's row no longer shows them
   (columnsFor), so this is their one home there. */
.data-quality-module .dq-found { display: flex; flex-direction: column; gap: var(--sp-1); }
.data-quality-module .dq-found-h { font-size: var(--fs-xs); color: var(--muted); }

/* The Employee Card's Data quality section: one line per standing row. */
.dq-card-chip { font-size: var(--fs-xs); padding: 1px 8px; border-radius: var(--r-pill); border: 1px solid var(--line); color: var(--muted); white-space: nowrap; }
.dq-card-chip-rust { background: var(--rust-soft); color: var(--rust); border-color: transparent; font-weight: 600; }
.dq-card-chip-amber { background: var(--amber-soft); color: var(--amber); border-color: transparent; font-weight: 600; }
.dq-card-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.dq-card-row { display: flex; flex-direction: column; gap: 2px; padding-left: var(--sp-3); box-shadow: inset 3px 0 0 var(--line); font-size: var(--fs-sm); }
.dq-card-row-blocking { box-shadow: inset 3px 0 0 var(--rust); }
.dq-card-row-mismatch { box-shadow: inset 3px 0 0 var(--amber); }
.dq-card-row-ack { opacity: .7; }
.dq-card-title { font-weight: 600; }
.dq-card-detail { color: var(--ink); }
.dq-card-meta { color: var(--muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }

/* The gates on the payroll download (2026-09-18). One file, and it cannot be
   downloaded while it would be wrong to send: each gate states its count and
   offers the one thing that clears it. A left rule carries the severity, never
   a filled banner — the house rule for 600-row screens applies at three. */
.pr-gates { display: flex; flex-direction: column; gap: var(--sp-2); margin: var(--sp-3) 0; }
.pr-gate {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line); border-left: 4px solid var(--rust);
  border-radius: var(--r-sm); background: var(--panel);
}
.pr-gate-n {
  flex: 0 0 auto; width: 22px; height: 22px; border-radius: var(--r-pill);
  background: var(--rust); color: #fff; font-size: var(--fs-xs); font-weight: 700;
  display: flex; align-items: center; justify-content: center;
}
.pr-gate-text { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-base); }
.pr-gate-sub { color: var(--muted); font-size: var(--fs-sm); }

/* Confirming the send (2026-09-18): the dialog where a month leaves Bridge's
   hands. The file is required, so the controls below it stay visibly inert
   until one is attached — a checkbox nobody can tick explains itself. */
.pr-confirm-sent { display: flex; flex-direction: column; gap: var(--sp-4); }
.pr-drop-empty { border-style: dashed; }
.pr-check { display: flex; align-items: flex-start; gap: var(--sp-3); font-size: var(--fs-base); }
.pr-check input { width: 18px; height: 18px; margin-top: 1px; accent-color: var(--teal); }
.pr-check-off { opacity: 0.55; }
.pr-sent-on { display: flex; align-items: center; gap: var(--sp-3); font-size: var(--fs-sm); flex-wrap: wrap; }
.pr-sent-on input {
  height: var(--control-h); border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 0 var(--sp-3); font-size: var(--fs-base); color: var(--ink); background: var(--panel);
}
.pr-confirm-does { background: var(--paper); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-4); }
.pr-confirm-does-h { font-size: var(--fs-xs); letter-spacing: 0.02em; color: var(--muted); margin-bottom: var(--sp-2); }
.pr-confirm-does ul { margin: 0; padding-left: var(--sp-5); font-size: var(--fs-base); line-height: 1.7; }

/* Issuing a correction (2026-09-18): which rows go to Crowe, not how many.
   Each block is a list a reader can check, with the money right-aligned and
   the day behind a figure indented under it. */
.pr-correction { display: flex; flex-direction: column; gap: var(--sp-4); }
.pr-correction-block { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.pr-correction-h {
  display: flex; align-items: baseline; gap: var(--sp-3);
  background: var(--paper); padding: var(--sp-2) var(--sp-4);
  font-size: var(--fs-xs); letter-spacing: 0.02em; color: var(--muted);
}
.pr-correction-net { margin-left: auto; color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.pr-correction-row {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  padding: var(--sp-3) var(--sp-4); border-top: 1px solid var(--line); font-size: var(--fs-base);
}
.pr-correction-who { flex: 1 1 auto; }
.pr-correction-money { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 600; }
.pr-correction-behind { flex: 1 0 100%; color: var(--muted); font-size: var(--fs-sm); }

/* The readiness panel's tones, and the month-in-one-line under the record of
   what Crowe received (2026-09-18). Severity is a left rule, never a fill. */
.pr-gate-amber { border-left-color: var(--amber); }
.pr-gate-amber .pr-gate-n { background: var(--amber); }
.pr-gate-ok { border-left-color: var(--pt); }
.pr-gate-ok .pr-gate-n { background: var(--pt); }
.pr-month-line { display: flex; flex-direction: column; gap: var(--sp-1); margin-top: var(--sp-4); }
.pr-month-line-row { display: flex; justify-content: space-between; font-size: var(--fs-base); }
.pr-month-line-total { border-top: 1px solid var(--line); padding-top: var(--sp-2); margin-top: var(--sp-1); }
.pr-matched { color: var(--pt); }

/* The reason a copy could not be matched — one line, required. */
.pr-input {
  height: var(--control-h); width: 100%; box-sizing: border-box;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  padding: 0 var(--sp-3); font-size: var(--fs-base); color: var(--ink); background: var(--panel);
}

/* Reference that earns its place by being asked for: a line people can open,
   rather than a wall under a table nobody reads (2026-09-18). */
.pr-more { margin-top: var(--sp-2); }
.pr-more > summary {
  cursor: pointer; color: var(--muted); font-size: var(--fs-sm);
  padding: var(--sp-1) 0; list-style-position: inside;
}
.pr-more > summary:hover { color: var(--ink); }
.pr-more p { margin: var(--sp-2) 0 0; color: var(--muted); font-size: var(--fs-sm); line-height: 1.6; }


/* ── People › Organigram (2026-09-25) ────────────────────────────────────
   Left to right, one column per level (decision C): the live tree is only
   three levels deep, but one level holds 31 people, which top-down would be
   ~7,000px wide. Each team stacks beside its manager, joined by hairline
   connectors drawn from the branch itself, so the lines can never drift from
   the cards they join. Severity stays a left rule (the house rule): amber on
   a card with no manager in BC; a pending move is dashed teal, with its
   "was: …" in words. */
/* 220 + 24 per level: four levels fit the 1,040px panel at 1440 (measured). */
.org-module { --org-card-w: 220px; --org-gap: var(--sp-5); --org-wire: var(--cr-rule); }
.org-board { overflow: auto; padding: var(--sp-4) var(--sp-4) var(--sp-5); max-height: 70vh; }
.org-levels { display: flex; margin-bottom: var(--sp-3); font-size: var(--fs-xs); font-weight: 600; color: var(--muted); }
.org-levels span { flex: none; width: calc(var(--org-card-w) + var(--org-gap)); }
.org-tree { display: flex; flex-direction: column; gap: var(--sp-3); width: max-content; }
.org-br { display: flex; align-items: flex-start; }
.org-kids { display: flex; flex-direction: column; gap: var(--sp-2); position: relative; padding-left: var(--org-gap); }
.org-kids::before { content: ""; position: absolute; left: 0; top: 22px; width: calc(var(--org-gap) / 2); border-top: 1px solid var(--org-wire); }
.org-kids > .org-br { position: relative; }
.org-kids > .org-br::before { content: ""; position: absolute; left: calc(var(--org-gap) / -2); top: 22px; width: calc(var(--org-gap) / 2); border-top: 1px solid var(--org-wire); }
.org-kids > .org-br::after { content: ""; position: absolute; left: calc(var(--org-gap) / -2); top: calc(var(--sp-2) / -2); bottom: calc(var(--sp-2) / -2); border-left: 1px solid var(--org-wire); }
.org-kids > .org-br:first-child::after { top: 22px; }
.org-kids > .org-br:last-child::after { bottom: auto; height: calc(22px + var(--sp-2) / 2); }
.org-kids > .org-br:only-child::after { display: none; }
.org-card {
  position: relative; flex: none; width: var(--org-card-w); display: flex; flex-direction: column; gap: 3px;
  padding: var(--sp-2) var(--sp-3); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-sm);
  transition: box-shadow var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), opacity var(--dur-fast) var(--ease);
}
.org-card[draggable="true"] { cursor: grab; }
.org-card-top { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.org-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; font-size: var(--fs-sm); }
.org-name .person-name-link { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.org-role { font-size: var(--fs-xs); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.org-meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1); }
.org-count { font-size: var(--fs-xs); color: var(--muted); }
.org-count b { color: var(--ink); }
.org-cons { border: 0; background: none; padding: 0; font: inherit; font-size: var(--fs-xs); color: var(--teal); text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
.org-move { flex: none; border: 1px solid var(--line); background: var(--paper); color: var(--ink); border-radius: var(--r-sm); font: inherit; font-size: var(--fs-xs); padding: 0 var(--sp-2); cursor: pointer; line-height: 1.6; }
.org-move:focus-visible, .org-cons:focus-visible, .org-link:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.org-top { box-shadow: inset 3px 0 0 var(--amber); }
.org-pending { border-style: dashed; border-color: var(--teal); background: var(--teal-soft); }
.org-was { font-size: var(--fs-xs); color: var(--teal); }
.org-dragging { opacity: 0.4; }
.org-drop-ok { border-color: var(--teal); box-shadow: 0 0 0 3px var(--teal-soft); }
.org-drop-no { border-color: var(--rust); box-shadow: 0 0 0 3px var(--rust-soft); }
.org-dim { opacity: 0.35; }
.org-hit { border-color: var(--amber); box-shadow: 0 0 0 3px var(--amber-soft); }
.org-why {
  position: absolute; left: 0; right: 0; top: 100%; margin-top: var(--sp-1); z-index: 5; pointer-events: none;
  font-size: var(--fs-xs); background: var(--ink); color: var(--panel); border-radius: var(--r-sm); padding: var(--sp-1) var(--sp-2);
}
.org-pick { display: flex; gap: var(--sp-1); margin-top: var(--sp-1); }
.org-pick select { flex: 1; min-width: 0; font: inherit; font-size: var(--fs-xs); border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--panel); color: var(--ink); }
.org-note { margin: 0 var(--sp-4) var(--sp-2); font-size: var(--fs-sm); color: var(--teal); }
.org-tray {
  position: sticky; bottom: 0; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4);
  margin: 0 var(--sp-3) var(--sp-3); padding: var(--sp-3) var(--sp-4); background: var(--panel);
  border: 1px solid var(--teal); border-radius: var(--r-md); box-shadow: var(--shadow-2); font-size: var(--fs-sm);
}
.org-tray ul { flex: 1 1 420px; list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.org-tray-act { display: flex; gap: var(--sp-2); }
.org-link { border: 0; background: none; padding: 0; font: inherit; font-size: var(--fs-xs); color: var(--teal); text-decoration: underline; cursor: pointer; }
.org-review-back { position: fixed; inset: 0; z-index: 60; background: rgba(33, 31, 26, 0.35); display: flex; align-items: flex-start; justify-content: center; padding: var(--sp-7) var(--sp-4); }
.org-review { width: 100%; max-width: 900px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--shadow-3); overflow: hidden; }
.org-review h3 { margin: 0; padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--line); font-size: var(--fs-md); }
.org-review-lede { margin: var(--sp-3) var(--sp-5); font-size: var(--fs-sm); color: var(--muted); }
.org-review-tw { overflow-x: auto; }
.org-review-t { width: 100%; min-width: 640px; border-collapse: collapse; font-size: var(--fs-sm); }
.org-review-t th { text-align: left; font-size: var(--fs-xs); color: var(--muted); background: var(--paper); padding: var(--sp-2) var(--sp-4); border-bottom: 1px solid var(--line); }
.org-review-t td { padding: var(--sp-2) var(--sp-4); border-bottom: 1px solid var(--cr-line); vertical-align: top; }
.org-review-t td:nth-child(-n+4) { white-space: nowrap; }
.org-ok { color: var(--teal); font-weight: 600; }
.org-no { color: var(--rust); font-weight: 600; }
.org-reason { font-size: var(--fs-xs); color: var(--muted); margin-top: 2px; }
.org-review-act { display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-5); border-top: 1px solid var(--line); background: var(--paper); }
@media (prefers-reduced-motion: reduce) { .org-card { transition: none; } }

/* ── People › Directory (2026-09-25) ────────────────────────────────────
   Geometry from the approved mockup: identity packs left, contact packs
   right, and the slack sits in ONE spacer between "Here since" and Email.
   The name column is pinned by its CONTENT — the cell asks for nothing and
   the fixed-width line inside it sets the column (the Access lesson, BRIDGE
   §3: in table-layout auto a cell's max-width is ignored). Every other
   column takes its content width; the text ones (client, manager, email)
   are one line capped with an ellipsis, the full text on hover. */
/* Widths the lead chose on 2026-09-25 from measured options: the mockup's
   caps (text 140), a 190px name, and Contact as two copy buttons instead of
   the address as text — the one layout that fits the 1,268px panel at 1920
   with every mockup column. Below that the table scrolls with the name
   pinned. */
.people-directory { --dir-id-w: 190px; --dir-text-max: 140px; }
.people-directory .cr-t td.nm,
.people-directory .cr-t th.nm { width: 1%; min-width: var(--dir-id-w); max-width: none; }
.people-directory .cr-t td.nm > .dir-line { width: calc(var(--dir-id-w) - 2 * var(--sp-3)); max-width: none; }
.people-directory .cr-t th:not(.cr-t-sp),
.people-directory .cr-t td:not(.nm):not(.cr-t-sp) { width: 1%; }
.people-directory .cr-t .cr-t-sp { width: 100%; }
.people-directory .dir-line { display: block; max-width: var(--dir-text-max); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The engagement state in the Client or role cell (2026-10-07): a word and
   a dot in PAW's colours — amber missing extension, rust bench, muted leave. */
.people-directory .dir-eng { font-weight: 600; }
.people-directory .dir-eng-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: var(--sp-1); vertical-align: 1px; background: currentColor; }
.people-directory .dir-eng-ext { color: var(--amber); }
.people-directory .dir-eng-bench { color: var(--rust); }
.people-directory .dir-eng-leave { color: var(--muted); }
.people-directory .dir-line .person-name-link { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
.people-directory .dir-since { display: inline-flex; align-items: center; gap: var(--sp-2); }
.people-directory .dir-contact { display: flex; align-items: center; gap: var(--sp-1); }
.people-directory .dir-copy {
  flex: none; border: 0; background: none; padding: 0 var(--sp-1); font: inherit; font-size: var(--fs-sm);
  color: var(--muted); cursor: pointer; border-radius: var(--r-sm); line-height: 1.4;
}
.people-directory .dir-copy:hover { color: var(--ink); background: var(--paper); }
.people-directory .dir-copy:focus-visible { outline: 2px solid var(--teal); outline-offset: 0; }
.people-directory .dir-copy-done { color: var(--teal); }
.people-directory .dir-go { color: var(--muted); }
/* The tenure badge sits in a figures cell (`td.num`, mono), but it is a badge
   and reads in the badges' face like every other one on the row. */
.people-directory .cr-t td.num .badge { font-family: var(--font-body); font-size: 11.5px; }
/* The Manager and Client selects list 18 and ~58 names: sized to their
   longest option they pushed the command bar onto a third row. A cap keeps
   the bar to the mockup's two; the open list still shows every name whole. */
.people-module .cr-field .cr-sel { max-width: 150px; }

/* ── Access (2026-09-24) ───────────────────────────────────────────────────
   IT's module: who needs a BC account made, whose should be closed. One
   DataTable serves four tabs whose column sets differ, and the table must
   not step sideways as the tab changes, so the identity column is PINNED at
   one width on every tab and every screen.

   Pinned by its CONTENT, not by the cell (2026-09-25). In `table-layout:
   auto` a cell's `max-width` is ignored and any spare width is shared out —
   the old `width/min/max: 300px` rule measured 300 on To onboard but 383 on
   All accounts at 1440 and 561 at 1920 (BRIDGE §3). So the cell asks for
   nothing (`width: 1%`) and the fixed-width line inside it sets the column.
   Tag columns ask for nothing either; the slack goes to the text columns
   (`wrap`). They need a PERCENTAGE to draw it: with `width: auto` Chrome
   spread the slack over every column, tags included (PPID 94 → 136 at
   1920). 50% is not a share of the table, it is "take the slack" — two of
   them split it by content, one takes it all. */
.access-module { --ax-id-w: 260px; --ax-line-max: 280px; --ax-text-min: 150px; --ax-who-w: 300px; }
.access-module .cr-t td.nm,
.access-module .cr-t th.nm { width: 1%; min-width: var(--ax-id-w); max-width: none; }
.access-module .cr-t td.nm > .ax-line { width: calc(var(--ax-id-w) - 2 * var(--sp-3)); max-width: none; }
.access-module .cr-t th:not(.cr-t-sp),
.access-module .cr-t td:not(.nm):not(.wrap):not(.cr-t-sp) { width: 1%; }
.access-module .cr-t td.wrap { width: 50%; min-width: var(--ax-text-min); max-width: none; }
.access-module .cr-t .cr-t-sp { width: 0; }
/* All accounts has ONE text column, which alone would take every spare pixel
   (515px at 1920) and push PPID and Status halfway across the screen. Who
   holds a name, so it gets a name's width and the spacer before the chevron
   takes the rest — identity packs left. */
.access-module.ax-tab-accounts .cr-t td.wrap { width: var(--ax-who-w); min-width: var(--ax-who-w); }
.access-module.ax-tab-accounts .cr-t .cr-t-sp { width: 100%; }

/* One line per cell that holds a name: the name gives way (ellipsis, full
   text on hover) and whatever sits beside it — a flag, "2 accounts" — never
   does. A reason (Why it missed) is plain text and may wrap. */
.access-module .ax-line { display: flex; align-items: center; gap: var(--sp-2); min-width: 0; max-width: var(--ax-line-max); white-space: nowrap; }
.access-module .ax-line > :first-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.access-module .ax-line > :not(:first-child) { flex: none; }
/* In the pinned column the NAME wins: it keeps its full width and the flags
   drop to a second line only when they do not fit beside it (OLD.SHELL with
   "no sign-in email" and "disabled" was cut to "OLD.SH…"). */
.access-module .cr-t td.nm > .ax-line { flex-wrap: wrap; row-gap: var(--sp-1); }
.access-module .cr-t td.nm > .ax-line > :first-child { flex-shrink: 0; max-width: 100%; }
.access-module .ax-line .person-name-link { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }

.access-module .ax-account { font-family: var(--font-mono); font-size: var(--fs-sm); font-weight: 600; }
/* To offboard's BC user cell: the name may break at its <wbr> after a dot,
   and the column (its one text column) may shrink below the usual text floor
   to do so — at 1440 the full name would push Equipment off the edge. */
.access-module .ax-account-break { white-space: normal; }
.access-module.ax-tab-offboard .cr-t td.wrap,
.access-module.ax-tab-onboard .cr-t td.wrap:has(.ax-account-col) { min-width: 0; }
.access-module .ax-email { font-family: var(--font-mono); font-size: var(--fs-xs); word-break: break-all; }
.access-module .ax-quiet { color: var(--muted); }
/* Continia (2026-09-25): a working setup, said in words and in teal — the
   one good-news state on a worklist, never carried by the colour alone. */
.access-module .ax-ok { color: var(--teal); font-weight: 600; font-size: var(--fs-sm); }
.access-module .ax-sub { font-size: var(--fs-xs); }
/* A shell account or a disabled one is named, never carried by colour alone. */
.access-module .ax-flag {
  font-size: var(--fs-xs); color: var(--rust); background: var(--rust-soft);
  border-radius: var(--r-pill); padding: 0 var(--sp-2);
}

/* The expanded row: what Bridge can see, and what it is waiting for. Two
   columns on a wide screen, one when there is no room for two. */
.access-module .ax-exp { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-5); padding: var(--sp-4); }
.access-module .ax-exp h4 { margin: 0 0 var(--sp-3); font-size: var(--fs-md); font-weight: 650; letter-spacing: -.01em; color: var(--ink); }
.access-module .ax-facts { display: grid; grid-template-columns: auto 1fr; gap: var(--sp-1) var(--sp-4); margin: 0; font-size: var(--fs-sm); }
.access-module .ax-facts dt { color: var(--muted); }
.access-module .ax-facts dd { margin: 0; font-family: var(--font-mono); font-size: var(--fs-xs); word-break: break-word; }
.access-module .ax-block-row { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); border-bottom: 1px solid var(--line); font-size: var(--fs-sm); }
.access-module .ax-block-row p { margin: 0; }
.access-module .ax-block-row:last-child { border-bottom: none; }
.access-module .ax-flash { outline: 2px solid var(--amber); outline-offset: 2px; border-radius: var(--r-md); }
.access-module .ax-inline-form { display: inline-flex; align-items: center; gap: var(--sp-2); }
.access-module .ax-inline-form input { font-size: var(--fs-xs); padding: var(--sp-1) var(--sp-2); border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--panel); color: var(--ink); min-width: 16ch; }
.access-module .ax-block-row .ax-actions label { display: inline-flex; align-items: center; gap: var(--sp-1); margin-right: var(--sp-3); font-size: var(--fs-sm); }
.access-module .ax-todo { margin: 0; padding-left: 1.1em; display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--fs-sm); }
.access-module .ax-ack { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); margin-top: var(--sp-4); font-size: var(--fs-sm); }
.access-module .ax-ack-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--sp-3); margin-top: var(--sp-4); }
.access-module .ax-field { display: flex; flex-direction: column; gap: var(--sp-1); flex: 1 1 260px; font-size: var(--fs-xs); color: var(--muted); }
.access-module .ax-field-narrow { flex: 0 1 160px; }
.access-module .ax-field input { height: var(--control-h); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 0 var(--sp-3); font: inherit; font-size: var(--fs-sm); color: var(--ink); background: var(--panel); }
.access-module .ax-ack-actions { display: flex; gap: var(--sp-2); }

/* Access › Today (2026-10-01): the onboarding chain as five numbered steps
   (M8) — done teal, to do rust, waiting quiet. The number is the label, so
   the state is never carried by colour alone; each glyph also has a title. */
.access-module .ax-steps { display: inline-flex; gap: var(--sp-1); align-items: center; }
.access-module .ax-st {
  display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: var(--r-pill);
  font-size: var(--fs-xs); font-weight: 700; border: 1.5px solid var(--teal); color: var(--teal); background: var(--teal-soft);
}
.access-module .ax-st-missing { border-color: var(--rust); color: var(--rust); background: var(--rust-soft); }
.access-module .ax-st-waiting { border-color: var(--line); color: var(--muted); background: var(--panel); }
/* Who owns a task: one chip, a hue per owner, always with its word. */
.access-module .ax-owner { font-size: var(--fs-xs); font-weight: 700; border-radius: var(--r-sm); padding: 0 var(--sp-2); color: var(--violet); background: var(--violet-soft); white-space: nowrap; }
.access-module .ax-owner-hr, .access-module .ax-owner-hr-assistant { color: var(--pl); background: var(--pl-soft); }
.access-module .ax-owner-bridge { color: var(--teal); background: var(--teal-soft); }
.access-module .ax-owner-recruiter { color: var(--amber); background: var(--amber-soft); }
.access-module .ax-owner-admin { color: var(--ink); background: var(--paper); border: 1px solid var(--line); }
.access-module .ax-sys { font-size: var(--fs-sm); font-weight: 600; }
/* A task's side (2026-10-01): the Side filter's own word, on every row. */
.access-module .ax-side { font-size: var(--fs-xs); font-weight: 600; white-space: nowrap; padding: 0 var(--sp-2); border-radius: var(--r-sm); border: 1px solid var(--line); }
.access-module .ax-side-onboard { color: var(--teal); background: var(--teal-soft); border-color: transparent; }
.access-module .ax-side-offboard { color: var(--rust); background: var(--rust-soft); border-color: transparent; }
.access-module .ax-side-cleanup { color: var(--muted); background: var(--paper); }
/* Today's tables carry more columns than the lists, so the identity column
   gives up 60px of its 260 there; identity badges sit on one line. */
.access-module.ax-tab-onboard, .access-module.ax-tab-offboard, .access-module.ax-tab-licences { --ax-id-w: 160px; }
/* Settings › Access policy (2026-10-01): number fields inside a table, the
   ones that differ from the code default marked by a rule AND a weight. */
.ap-settings .ap-days { width: 64px; height: var(--control-h); border: 1px solid var(--line); border-radius: var(--r-sm); padding: 0 var(--sp-2); font: inherit; text-align: right; font-variant-numeric: tabular-nums; }
.ap-settings .ap-days-changed { border-color: var(--teal); font-weight: 700; }
.ap-settings .ap-actions { margin: var(--sp-3) 0; }
.ap-settings.access-module { --ax-id-w: 280px; }
.access-module .ax-idc { display: inline-flex; gap: var(--sp-1); align-items: center; white-space: nowrap; }
.access-module .ax-steps { white-space: nowrap; }
.access-module .ax-two { display: flex; flex-direction: column; align-items: flex-start; }
.access-module .ax-pre { white-space: pre-line; }
.access-module .ax-stack { flex-direction: column; align-items: stretch; }
/* A task row's workflow (2026-10-01): the person's chain, each step named
   with its owner and state; the step they are stuck on carries a rule. The
   state is always a word, never colour alone. */
.access-module .ax-exp-wide { grid-column: 1 / -1; }
.access-module .ax-flow { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-1); font-size: var(--fs-sm); }
.access-module .ax-flow li { display: grid; grid-template-columns: 24px minmax(200px, 1.4fr) auto 120px 1fr; gap: var(--sp-2); align-items: center; padding: var(--sp-1) var(--sp-2); border-radius: var(--r-sm); }
.access-module .ax-flow-dot { display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: var(--r-pill); font-size: var(--fs-xs); font-weight: 700; border: 1.5px solid var(--teal); color: var(--teal); background: var(--teal-soft); }
.access-module .ax-flow-todo .ax-flow-dot { border-color: var(--amber); color: var(--amber); background: var(--amber-soft); }
.access-module .ax-flow-stuck .ax-flow-dot { border-color: var(--rust); color: var(--rust); background: var(--rust-soft); }
.access-module .ax-flow-waiting .ax-flow-dot { border-color: var(--line); color: var(--muted); background: var(--panel); }
.access-module .ax-flow-state { font-weight: 600; }
.access-module .ax-flow-stuck .ax-flow-state { color: var(--rust); }
.access-module .ax-flow-done .ax-flow-state, .access-module .ax-flow-line { color: var(--muted); }
.access-module .ax-flow-here { box-shadow: inset 3px 0 0 var(--rust); background: var(--rust-soft); }
.access-module .ax-flow-current .ax-flow-name { font-weight: 700; }
/* Access › People (2026-10-01, M2): a glyph per system, its word in the
   title and the key, a short label beside it. */
.access-module .ax-mx { display: inline-flex; align-items: center; gap: var(--sp-1); white-space: nowrap; font-size: var(--fs-xs); }
/* In the table the label sits UNDER its glyph, so six systems fit at 1440. */
.access-module .cr-t .ax-mx { flex-direction: column; align-items: flex-start; gap: 2px; }
.access-module .ax-mx-g { display: inline-grid; place-items: center; width: 20px; height: 20px; border-radius: var(--r-sm); font-weight: 700; font-size: var(--fs-sm); }
.access-module .ax-mx-ok .ax-mx-g { color: var(--teal); background: var(--teal-soft); }
.access-module .ax-mx-missing .ax-mx-g { color: var(--rust); background: var(--rust-soft); }
.access-module .ax-mx-extra .ax-mx-g { color: var(--amber); background: var(--amber-soft); }
.access-module .ax-mx-decide .ax-mx-g { color: var(--violet); background: var(--violet-soft); }
.access-module .ax-mx-na .ax-mx-g, .access-module .ax-mx-unknown .ax-mx-g { color: var(--muted); }
.access-module .ax-mx-l { color: var(--muted); max-width: 84px; overflow: hidden; text-overflow: ellipsis; }
.access-module .ax-mx-key { display: flex; flex-wrap: wrap; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); border-top: 1px solid var(--line); background: var(--paper); }
/* Access › Insight (2026-10-01, M10): one tile per licence. Each tile is a
   button (it opens Licences); its bar is the seats split by whose they are,
   always with the word on hover and in the key. */
.access-module .ins-wrap { display: grid; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); }
.access-module .ins-group { font-size: var(--fs-sm); font-weight: 700; color: var(--muted); margin: 0 0 var(--sp-2); }
.access-module .ins-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: var(--sp-3); }
.access-module .ins-tile { display: grid; gap: var(--sp-1); text-align: left; font: inherit; color: inherit; cursor: pointer; background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--sp-3) var(--sp-3) var(--sp-2); }
.access-module .ins-tile:hover { border-color: var(--teal); }
.access-module .ins-tile:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; }
.access-module .ins-flag { box-shadow: inset 3px 0 0 var(--rust); }
.access-module .ins-amber { box-shadow: inset 3px 0 0 var(--amber); }
.access-module .ins-h { display: flex; justify-content: space-between; align-items: baseline; gap: var(--sp-2); }
.access-module .ins-n { font-weight: 650; font-size: var(--fs-sm); }
.access-module .ins-sys { font-size: var(--fs-xs); font-weight: 700; color: var(--muted); }
.access-module .ins-v { font-family: var(--font-mono); font-size: var(--fs-xl); font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; }
.access-module .ins-v small { font-size: var(--fs-xs); color: var(--muted); font-weight: 500; }
.access-module .ins-bar { display: flex; height: 8px; border-radius: var(--r-pill); overflow: hidden; gap: 1px; background: var(--paper); }
.access-module .ins-bar i { display: inline-block; height: 100%; }
.access-module .b-work { background: var(--teal); } .access-module .b-grace { background: var(--teal-soft); } .access-module .b-past { background: var(--rust); }
.access-module .b-none { background: var(--amber); } .access-module .b-shared { background: var(--line); } .access-module .b-kept { background: var(--muted); } .access-module .b-use { background: var(--teal); } .access-module .b-hold { background: var(--line); } .access-module .b-back { background: var(--rust); }
/* Business Central as one pool tile (2026-10-06, revision R1): two hit areas
   side by side — the body filters the tables, the last row opens the four weeks. */
.access-module .ins-pool { padding: 0; gap: 0; cursor: default; overflow: hidden; grid-row: span 2; display: grid; grid-template-rows: 1fr auto; }
/* The pool tile stands two rows tall, so the tiles beside it keep their own height. */
.access-module .ins-grid { align-items: start; }
.access-module .ins-pool .ins-glance-cols { height: 72px; }
.access-module .ins-pool-main { align-content: start; }
.access-module .ins-pool:hover { border-color: var(--line); }
.access-module .ins-pool-main { display: grid; gap: var(--sp-1); text-align: left; font: inherit; color: inherit; background: none; border: 0; padding: var(--sp-3) var(--sp-3) var(--sp-2); cursor: pointer; }
.access-module .ins-pool-main:hover { background: var(--paper); }
.access-module .ins-pool-main:focus-visible, .access-module .ins-pool-act:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.access-module .ins-glance { display: grid; gap: 2px; margin-top: var(--sp-1); }
.access-module .ins-glance-cols { position: relative; display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2); align-items: end; height: 36px; }
.access-module .ins-glance-cols i { display: block; background: var(--teal); border-radius: 3px 3px 0 0; }
.access-module .ins-glance-cols i.is-over { background: var(--rust); }
.access-module .ins-glance-line { position: absolute; left: 0; right: 0; border-top: 1.5px dashed var(--ink); }
.access-module .ins-glance-labels { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-2); font-size: var(--fs-xs); color: var(--muted); text-align: center; font-variant-numeric: tabular-nums; }
.access-module .ins-pool-act { display: flex; justify-content: space-between; align-items: center; gap: var(--sp-2); width: 100%; font: inherit; font-size: var(--fs-sm); text-align: left; border: 0; border-top: 1px solid var(--line); padding: var(--sp-2) var(--sp-3); background: var(--panel); color: var(--muted); cursor: pointer; }
.access-module .ins-pool-act-bad { background: var(--rust-soft); color: var(--rust); font-weight: 600; }
.access-module .ins-pool-act:hover .ins-pool-more { text-decoration: underline; }
.access-module .ins-pool-more { white-space: nowrap; font-weight: 600; }

.access-module .b-leave { background: var(--amber-soft); box-shadow: inset 0 0 0 1px var(--amber); }
.access-module .ins-sub, .access-module .ins-cost { display: flex; justify-content: space-between; gap: var(--sp-2); font-size: var(--fs-xs); color: var(--muted); }
.access-module .ins-back { color: var(--rust); font-weight: 700; }
/* Line one of every tile keeps "N to take back" on one line; line two is the
   allocation and cost, in the same place on every tile (2026-10-06 audit). */
.access-module .ins-sub > span:last-child { white-space: nowrap; }
.access-module .ins-money { font-size: var(--fs-xs); color: var(--muted); border-top: 1px dashed var(--line); padding-top: var(--sp-1); }
.access-module .ins-money .is-over { color: var(--rust); font-weight: 600; }
.access-module .ins-glance-cap { font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
.access-module .ins-cost { border-top: 1px dashed var(--line); padding-top: var(--sp-1); }
.access-module .ins-spark { width: 100%; height: 18px; }
.access-module .ins-spark path { fill: none; stroke: var(--muted); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.access-module .ins-note { font-size: var(--fs-xs); color: var(--muted); font-style: italic; }
.ap-settings .ap-money { width: 80px; }
/* "How it works" (2026-10-01): the job in hand, in steps, under the bar. */
.access-module .ax-help { margin: var(--sp-3) var(--sp-4) 0; padding: var(--sp-3) var(--sp-4); border: 1px solid var(--line); border-left: 3px solid var(--teal); border-radius: var(--r-md); background: var(--panel); font-size: var(--fs-sm); }
.access-module .ax-help-t { font-size: var(--fs-md); margin: 0 0 var(--sp-2); }
.access-module .ax-help ol { margin: 0; padding-left: 1.2em; display: grid; gap: var(--sp-1); }
.access-module .ax-warn { font-weight: 600; }
.access-module .ax-warn-rust { color: var(--rust); }
.access-module .ax-warn-amber { color: var(--amber); }
.access-module.ax-tab-onboard .cr-t td.wrap { min-width: var(--ax-text-min); }
@media (max-width: 900px) {
  .access-module .ax-exp { grid-template-columns: 1fr; }
}

/* ── Home (2026-09-25) ──────────────────────────────────────────────────
   Two panels side by side: the pair owns the one token of space around and
   between them, so a panel inside it carries no margin of its own. */
.cr .home-pair {
  display: grid; grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  gap: var(--sp-4); margin: var(--sp-4) var(--sp-4) 0; align-items: start;
}
.cr .home-pair-even { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.cr .home-pair:last-child { margin-bottom: var(--sp-4); }
.cr .home-pair > .cr-panel { margin: 0; min-width: 0; }
@media (max-width: 980px) { .cr .home-pair, .cr .home-pair-even { grid-template-columns: minmax(0, 1fr); } }

.home-list { list-style: none; margin: 0; padding: 0; }
.home-list li + li { border-top: 1px solid var(--cr-line); }
.home-line {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--sp-3);
  width: 100%; padding: var(--sp-2) var(--sp-4); background: none; border: 0; text-align: left;
  font: inherit; color: inherit; cursor: pointer;
}
.home-line:hover { background: var(--paper); }
.home-line:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.home-line-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.home-line-t { font-size: var(--fs-base); }
.home-line-t b { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.home-line-w { font-size: var(--fs-xs); color: var(--muted); }
.home-who {
  display: inline-block; margin-right: var(--sp-2); padding: 0 var(--sp-2); border-radius: var(--r-pill);
  background: var(--cr-line); font-weight: 600;
}
.home-go { color: var(--teal); font-size: var(--fs-md); }
.home-dot { width: var(--sp-2); height: var(--sp-2); border-radius: 50%; background: var(--line); }
.home-dot-rust { background: var(--rust); }
.home-dot-amber { background: var(--amber); }
/* Coming up: the date column, then the line with its left rule. */
.home-line-dated { grid-template-columns: 13ch minmax(0, 1fr) auto; }
.home-when { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.home-rule { border-left: 2px solid var(--line); padding-left: var(--sp-3); }
.home-rule-rust { border-left-color: var(--rust); }
.home-rule-amber { border-left-color: var(--amber); }
.home-rule-teal { border-left-color: var(--teal); }
/* Home's two charts, as the approved mockup draws them. */
.home-chart { padding: var(--sp-3) var(--sp-4) var(--sp-1); }
.home-chart-svg { display: block; width: 100%; height: auto; }
.home-chart-svg text { fill: var(--muted); font-size: var(--fs-xs); font-family: var(--font-body); font-variant-numeric: tabular-nums; }
.home-chart-svg .home-chart-val { fill: var(--ink); font-weight: 600; }
.home-chart-grid { stroke: var(--cr-line); stroke-dasharray: 3 3; }
.home-chart-base { stroke: var(--line); }
.home-chart-bar { fill: var(--teal); }
.home-chart-bar-open { fill: none; stroke: var(--teal); stroke-dasharray: 3 2; }
.home-chart-line { fill: none; stroke: var(--teal); stroke-width: 2; stroke-linejoin: round; }
.home-chart-dot { fill: var(--teal); }
.home-chart-dot-open { fill: var(--panel); stroke: var(--teal); stroke-width: 1.5; }
.home-legend { display: flex; gap: var(--sp-4); flex-wrap: wrap; padding: 0 var(--sp-4) var(--sp-3); font-size: var(--fs-xs); color: var(--muted); }
.home-key { display: inline-block; width: var(--sp-3); height: var(--sp-3); border-radius: 2px; margin-right: var(--sp-1); vertical-align: -1px; background: var(--teal); }
.home-key-open { background: none; border: 1px dashed var(--teal); }

/* ——— Access rework (2026-10-01): one row per person, systems in words ——— */
/* Amber is the "beyond the policy" word, the pair of .ax-flag's "missing". */
.access-module .ax-amber {
  font-size: var(--fs-xs); color: var(--amber); background: var(--amber-soft);
  border-radius: var(--r-pill); padding: 0 var(--sp-2); white-space: nowrap;
}
/* The opened row's steps, by name, with their dates (the mockup): the
   state is a word on the right, and the step IT is on is the bold one. */
.access-module .ax-steplist { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-2); font-size: var(--fs-sm); }
.access-module .ax-steplist li { display: grid; grid-template-columns: var(--sp-4) 1fr auto; gap: var(--sp-2); align-items: baseline; }
.access-module .ax-step-now { font-weight: 700; }
.access-module .ax-steplist + .ax-facts { margin-top: var(--sp-4); }
.access-module .ax-kit { display: grid; gap: var(--sp-3); font-size: var(--fs-sm); }
.access-module .ax-kit p { margin: 0; }
.access-module .ax-kit-item { display: grid; gap: var(--sp-1); }
.access-module .ax-actions { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); }
.access-module .ax-btn-ink { background: var(--ink); border-color: var(--ink); color: var(--panel); }
.access-module .ax-btn-ink:hover:not(:disabled) { background: var(--ink); opacity: .88; }

/* ——— A cell is a word in the tone of its state (2026-10-01, the mockups) ———
   Not a pill: at hundreds of rows a filled badge in every cell is noise
   (CLAUDE.md: severity is a rule or a word). Rust is missing or late, amber
   is open or pending, teal is done; a quiet word is "nothing here". */
.access-module .ax-w-bad { color: var(--rust); font-weight: 600; }
.access-module .ax-w-warn { color: var(--amber); font-weight: 600; }
.access-module .ax-w-ok { color: var(--teal); }
.access-module .ax-w-quiet { color: var(--muted); }
/* The few real TAGS left — "needed now", "moved to PT?" — are small pills,
   so they read as a flag on the row rather than its state. */
.access-module .ax-tag { display: inline-block; font-size: var(--fs-xs); font-weight: 600; line-height: 1.6; border-radius: var(--r-pill); padding: 0 var(--sp-1); white-space: nowrap; }
.access-module .ax-tag-rust { color: var(--rust); background: var(--rust-soft); }
.access-module .ax-tag-amber { color: var(--amber); background: var(--amber-soft); }
.access-module .bp-chips { display: flex; flex-wrap: wrap; gap: var(--sp-1); }
/* Access › Permissions, Who has what (2026-10-07). Sets and groups as chips
   in the opened row; a family cell is a short code (G group, D direct),
   centred under its heading. Columns 6–12 are the seven families: headings
   may wrap so the matrix fits at 1440. */
.access-module.ax-perm-list .cr-t td.wrap { width: auto; min-width: var(--ax-text-min); max-width: 260px; }
.access-module.ax-perm-list .bp-chips .ax-tag { white-space: normal; }
/* The List's family columns (L2): each set on its own line, so a column reads
   down as who holds which sets of that family. */
.access-module .bp-sets { display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-1); }
.access-module.ax-perm-list .bp-sets .ax-tag { white-space: normal; border-radius: var(--r-sm); }
.access-module.ax-perm-list .cr-t td.wrap { min-width: 130px; }
.access-module .bp-chip { color: var(--ink); background: var(--paper); box-shadow: inset 0 0 0 1px var(--cr-rule); }
.access-module.ax-tab-permissions { --ax-id-w: 170px; }
.access-module.ax-tab-permissions .cr-t .cr-t-sp { width: 100%; }
.access-module .bp-cell { display: inline-block; min-width: 3ch; text-align: center; font-family: var(--font-mono); font-size: var(--fs-sm); font-weight: 600; }
.access-module.ax-perm-matrix .cr-t th:nth-child(n+6):nth-child(-n+12) { white-space: normal; max-width: 84px; text-align: center; line-height: 1.25; }
.access-module.ax-perm-matrix .cr-t td:nth-child(n+6):nth-child(-n+12) { text-align: center; }
/* To offboard's BC user: the account, then its state on the same line when
   there is room, beneath it when there is not. */
/* Access tables are worklists of words, not figures: one step less padding
   per cell lets ten columns sit in the 1040px panel at 1440 (the mockups). */
.access-module .cr-t th, .access-module .cr-t td { padding-left: var(--sp-2); padding-right: var(--sp-2); }
.access-module .cr-t th:first-child, .access-module .cr-t td:first-child { padding-left: var(--sp-3); }
.access-module .cr-t .cr-t-sp { padding: 0; }
.access-module .cr-search { width: 96px; }
/* Licences (2026-10-06, Option C): the panel's title is a two-sided switch,
   Out of policy | All licences, each with its count; the to-do's count stays
   rust while the other side is open. Each side's filters sit on a bar under it. */
.access-module .ax-vswitch { display: inline-flex; gap: var(--sp-1); padding: var(--sp-1); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--paper); }
.access-module .ax-vswitch button { display: inline-flex; align-items: baseline; gap: var(--sp-2); padding: var(--sp-1) var(--sp-3); border: 0; border-radius: var(--r-sm); background: none; font: inherit; color: var(--muted); cursor: pointer; }
.access-module .ax-vswitch button.is-on { background: var(--panel); color: var(--ink); box-shadow: 0 1px 0 var(--line); }
.access-module .ax-vswitch button:focus-visible { outline: 2px solid var(--teal); outline-offset: 1px; }
.access-module .ax-vswitch em { font-style: normal; font-family: var(--font-body); font-size: var(--fs-xs); font-weight: 700; padding: 0 var(--sp-2); border-radius: var(--r-pill); background: var(--line); color: var(--ink); }
.access-module .ax-vswitch button.is-todo em { background: var(--rust-soft); color: var(--rust); }
.access-module .ax-subbar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); border-bottom: 1px solid var(--line); }
.access-module .ax-badges { display: inline-flex; flex-direction: column; gap: var(--sp-1); align-items: flex-start; }
/* Licences' twelve columns at 1440: the wrapping Fix may go one step narrower. */
.access-module.ax-tab-licences { --ax-text-min: 120px; --ax-id-w: 140px; --ax-ms-w: 112px; }
/* A long licence list ("F3, BC Team Member") wraps inside its cell. */
.access-module .ax-ms { display: inline-block; max-width: var(--ax-ms-w); white-space: normal; }
.access-module .ax-note { display: block; }
.access-module .ax-filter { display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; max-width: 100%; }
/* At phone width the Show and Holder segments scroll inside their own row rather than off the panel. */
.access-module .ax-filter .cr-seg { min-width: 0; overflow-x: auto; }
.access-module .ax-linkword { font: inherit; background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.access-module .ax-linkword:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: var(--r-sm); }
/* Access's bar carries up to five filters beside four tabs, on one row at
   1440 (the mockups plus the lead's Status filter): one spacing step less
   between and inside the controls than the house bar, here only. */
.access-module .cr-bar { gap: var(--sp-3); }
.access-module .cr-fl { gap: var(--sp-1); margin-left: auto; }
.access-module .cr-bar > .cr-sp { display: none; }
.access-module .cr-tab { padding-left: var(--sp-2); padding-right: var(--sp-2); }
.access-module .cr-seg button { padding-left: var(--sp-2); padding-right: var(--sp-2); }
/* To onboard's Next for IT is the short step name; it may wrap narrower. */
.access-module.ax-tab-onboard { --ax-text-min: 100px; --ax-id-w: 140px; }
/* "How it works", at the end of the context strip. */
.access-module .ax-help-link { font: inherit; font-size: var(--fs-sm); font-weight: 600; color: var(--teal); background: none; border: 0; padding: 0; margin-left: var(--sp-3); cursor: pointer; }
.access-module .ax-help-link:hover { text-decoration: underline; }
.access-module .ax-help-link:focus-visible { outline: 2px solid var(--teal); outline-offset: 2px; border-radius: var(--r-sm); }
/* The Licences tiles: one row of three, the mockup's compact card. */
.access-module .ins-grid { grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.access-module .ins-row { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-3); }
.access-module .ins-row .ins-spark { width: 96px; flex: none; }
.access-module .ax-bcuser { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--sp-2); }
.access-module .ax-move { display: grid; gap: var(--sp-2); margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--cr-line); }
.access-module .ax-move h4 { margin-bottom: 0; }
.access-module .ax-move p { margin: 0; }
/* A tile picked as the policy table's filter. */
.access-module .ins-tile.ins-on { outline: 2px solid var(--teal); outline-offset: -2px; }
/* A cell holding a fact and its date, one under the other. */
.access-module .ax-next { display: grid; gap: var(--sp-1); justify-items: start; }
/* A sixth tile appears only when somebody has signed a row off. */
.cr-deck-6 { grid-template-columns: repeat(6, 1fr); }

/* Business Central, the next 4 weeks (2026-10-06, revision R2): the Employee
   Card's width, the answer first, then one row per week to the same scale as
   the pool tile's glance, a week opening its people in place. */
.seats-ahead-drawer { width: 620px; display: grid; align-content: start; gap: var(--sp-4); }
.seats-ahead-drawer .drawer-header { margin-bottom: 0; }
.sad-verdict { display: grid; gap: var(--sp-1); padding: var(--sp-3) var(--sp-4); border-left: 3px solid var(--line); border-radius: 0 var(--r-sm) var(--r-sm) 0; background: var(--paper); font-size: var(--fs-sm); color: var(--muted); }
.sad-verdict b { font-family: var(--font-display); font-size: var(--fs-lg); color: var(--ink); font-weight: 600; }
.sad-verdict-bad { border-left-color: var(--rust); background: var(--rust-soft); }
.sad-verdict-bad b { color: var(--rust); }
.sad-verdict .ax-linkword { justify-self: start; color: var(--teal); font-weight: 600; font: inherit; background: none; border: 0; padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.sad-filters { display: flex; flex-wrap: wrap; gap: var(--sp-4); align-items: center; }
.sad-filters .ax-filter { display: inline-flex; align-items: center; gap: var(--sp-2); }
.sad-hint { margin: 0; font-size: var(--fs-xs); color: var(--muted); }
.sad-weeks { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.sad-week { border-bottom: 1px solid var(--line); }
.sad-row { display: grid; grid-template-columns: 140px 1fr 48px; grid-template-areas: 'label track need' 'label sub sub'; gap: var(--sp-1) var(--sp-3); align-items: center; width: 100%; padding: var(--sp-3) var(--sp-1); background: none; border: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; }
.sad-row:hover { background: var(--paper); }
.sad-row:focus-visible { outline: 2px solid var(--teal); outline-offset: -2px; }
.sad-label { grid-area: label; font-weight: 600; font-size: var(--fs-sm); }
.sad-week.is-open .sad-label::after { content: ' ▾'; color: var(--muted); }
.sad-track { grid-area: track; position: relative; height: 12px; border-radius: var(--r-pill); background: var(--paper); }
.sad-fill { position: absolute; left: 0; top: 0; bottom: 0; border-radius: var(--r-pill); background: var(--teal); }
.sad-week.is-over .sad-fill { background: var(--rust); }
.sad-cap { position: absolute; top: -4px; bottom: -4px; border-left: 2px dashed var(--ink); }
.sad-need { grid-area: need; text-align: right; font-family: var(--font-mono); font-weight: 600; font-variant-numeric: tabular-nums; }
.sad-week.is-over .sad-need { color: var(--rust); }
.sad-sub { grid-area: sub; font-size: var(--fs-xs); color: var(--muted); }
.sad-people { list-style: none; margin: 0 0 var(--sp-3); padding: 0 0 0 var(--sp-3); border-left: 2px solid var(--line); display: grid; gap: var(--sp-1); }
.sad-people li { display: grid; grid-template-columns: minmax(0, 1fr) auto 190px 84px; gap: var(--sp-3); align-items: center; font-size: var(--fs-sm); }
.sad-who { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sad-tags { display: inline-flex; gap: var(--sp-1); }
.sad-paw { color: var(--ink); } .sad-paw.is-left { color: var(--teal); }
.sad-date { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; }
.sad-none { margin: 0 0 var(--sp-3); font-size: var(--fs-sm); color: var(--muted); }
.sad-key { margin: 0; font-size: var(--fs-xs); color: var(--muted); display: flex; align-items: center; gap: var(--sp-2); }
.sad-cap-key { display: inline-block; height: 12px; border-left: 2px dashed var(--ink); }
.sad-parts { margin: 0; font-size: var(--fs-xs); color: var(--muted); }
@media (max-width: 700px) {
  .seats-ahead-drawer { width: 100%; }
  .sad-row { grid-template-columns: 1fr 48px; grid-template-areas: 'label need' 'track track' 'sub sub'; }
  /* Name and badges on one line, what PAW says and the date on the next. */
  .sad-people li { grid-template-columns: minmax(0, 1fr) auto; row-gap: 0; padding-bottom: var(--sp-1); }
  .sad-paw { grid-column: 1; font-size: var(--fs-xs); color: var(--muted); }
  .sad-date { grid-column: 2; font-size: var(--fs-xs); }
}

/* Settings › Public holidays (2026-10-14). Spacing from the --sp-* scale,
   severity as a left rule and a dot, never a filled banner. */
.ph-settings { display: flex; flex-direction: column; gap: var(--sp-4); }
.ph-bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.ph-grow { flex: 1 1 auto; }
.ph-alert { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); background: var(--panel); border: 1px solid var(--cr-line); border-radius: var(--r-sm); box-shadow: inset 3px 0 0 var(--rust); font-size: var(--fs-sm); }
.ph-alert b { color: var(--ink); font-weight: 600; }
.ph-notice { display: inline-block; padding-left: var(--sp-2); }
.ph-notice-amber { box-shadow: inset 3px 0 0 var(--amber); color: var(--ink); }
.ph-notice-rust { box-shadow: inset 3px 0 0 var(--rust); color: var(--ink); }
.ph-form { display: flex; flex-wrap: wrap; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4); }
.ph-form .ph-name { flex: 1 1 28ch; }
.ph-actions { display: flex; justify-content: flex-end; gap: var(--sp-2); padding: var(--sp-3) var(--sp-4); }
.ph-quiet { color: var(--muted); }
.ph-small { padding: 2px var(--sp-2); font-size: var(--fs-xs); }
.ph-confirm { white-space: nowrap; font-size: var(--fs-xs); color: var(--ink); }
.ph-counts { display: inline-flex; align-items: center; gap: var(--sp-2); color: var(--muted); }
.ph-counts i { width: 7px; height: 7px; border-radius: 50%; background: var(--muted); opacity: .5; }
.ph-counts-on { color: var(--ink); }
.ph-counts-on i { background: var(--teal); opacity: 1; }
