/* LOMN Checker — styled 1:1 to medwork-scheduling
   (libs/frontend/ui tailwind.preset.js + shell-layout.tsx + login.tsx).
   Gray palette, white sidebar, primary-50 active nav, rounded-xl cards. */
:root {
  /* medwork-scheduling tokens */
  --primary-50: #eff6ff; --primary-100: #dbeafe; --primary-200: #bfdbfe;
  --primary-500: #3b82f6; --primary-600: #2563eb; --primary-700: #1d4ed8;
  --gray-50: #f9fafb; --gray-100: #f3f4f6; --gray-200: #e5e7eb;
  --gray-300: #d1d5db; --gray-400: #9ca3af; --gray-500: #6b7280;
  --gray-600: #4b5563; --gray-700: #374151; --gray-800: #1f2937; --gray-900: #111827;
  --green-100: #dcfce7; --green-500: #22c55e; --green-600: #16a34a; --green-800: #166534;
  --yellow-100: #fef9c3; --yellow-800: #854d0e;
  --red-100: #fee2e2; --red-600: #dc2626; --red-800: #991b1b;
  --blue-100: #dbeafe; --blue-600: #2563eb; --blue-800: #1e40af;
  --purple-100: #f3e8ff; --purple-800: #6b21a8;
  --orange-50: #fff7ed; --orange-600: #ea580c;
  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
  --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
}
* { box-sizing: border-box; }
body {
  margin: 0; background: var(--gray-50); color: var(--gray-900);
  font: 14px/1.5 Inter, system-ui, -apple-system, BlinkMacSystemFont,
        "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}
/* Global safety cap: an inline <svg> with a viewBox but no matching context
   `… svg { width }` rule would otherwise stretch to fill its container (the
   blown-up icons, 7/20). Default every icon to 16px; the many context rules
   below (.side-item svg, .stat-icon svg, …) still override this to their own
   size. */
svg { width: 16px; height: 16px; flex: none; }
/* Same defense for raster images (the blown-up logo, 7/20): the shipped logo
   PNG is 5692x3200 natural, so an <img> whose context rule doesn't apply (e.g.
   a stale cached stylesheet after a deploy) would fill the screen. Never let an
   image exceed its container; the context rules (.login-logo, .avatar img) and
   the intrinsic width/height attributes on the elements still set the real
   size. */
img { max-width: 100%; }
.hidden { display: none !important; }
/* The `hidden` ATTRIBUTE has to hide things too (issue #427). The browser's own
   `[hidden] { display: none }` lives in the user-agent stylesheet, and ANY
   author rule that sets `display` outranks it no matter how unspecific it is.
   So an element built as el("div", { class: "status-row", hidden: "" }) stayed
   on screen, because .status-row's `display: flex` beat the UA rule: that is
   how the Rejected reason and IC wait/Post req'd pickers came to show on every
   case regardless of the chosen status. Declaring it here, in the author origin
   and !important like .hidden above, makes the attribute mean what app.js
   assumes it means everywhere, not just on elements whose classes happen not to
   set a display. */
[hidden] { display: none !important; }

/* ============ login (login.tsx: bg-gray-50 page, white card shadow-md) ===== */
.login-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center;
  background: var(--gray-50); padding: 48px 16px;
}
.login-outer { max-width: 28rem; width: 100%; }
.login-head { text-align: center; display: flex; flex-direction: column; align-items: center; }
.login-logo { height: 56px; max-width: 260px; object-fit: contain; margin-bottom: 16px; }
.powered { display: flex; align-items: center; gap: 6px; margin-bottom: 12px; }
.powered-label { font-size: 12px; color: var(--gray-400); text-transform: uppercase; letter-spacing: .05em; }
.module-badge {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--primary-50); border: 1px solid var(--primary-200);
  padding: 8px 16px; border-radius: 8px; margin-bottom: 12px;
  font-size: 14px; font-weight: 600; color: var(--primary-700);
  text-transform: uppercase; letter-spacing: .025em;
}
.module-badge svg { width: 20px; height: 20px; color: var(--primary-600); }
.login-sub { margin: 0 0 24px; color: var(--gray-600); font-size: 20px; font-weight: 400; }
.login-card {
  background: white; border-radius: 8px; box-shadow: var(--shadow-md); padding: 32px;
}
.login-card form { text-align: left; display: flex; flex-direction: column; gap: 24px; }
.login-card label { display: block; font-size: 14px; color: var(--gray-700); font-weight: 500; }
.login-card input {
  display: block; width: 100%; margin-top: 6px; padding: 8px 12px;
  border: 1px solid var(--gray-300); border-radius: 8px; font: inherit;
  box-shadow: var(--shadow-sm);
}
.login-card input::placeholder { color: var(--gray-400); }
.login-card input:focus { outline: 2px solid var(--primary-500); outline-offset: 0; border-color: var(--primary-500); }
.login-error {
  background: #fef2f2; color: #b91c1c; border: 1px solid #fecaca;
  border-radius: 8px; padding: 16px; font-size: 14px;
}
.btn-block { width: 100%; justify-content: center; padding: 10px 16px; font-size: 14px; }
.login-foot { color: var(--gray-500); font-size: 13px; margin: 16px 0 0; text-align: center; }

/* SSO (login.tsx: 'Or continue with' divider + white Google button) */
.sso-divider {
  position: relative; text-align: center; margin: 24px 0 20px;
}
.sso-divider::before {
  content: ""; position: absolute; inset: 50% 0 auto 0;
  border-top: 1px solid var(--gray-300);
}
.sso-divider span {
  position: relative; background: white; padding: 0 12px;
  font-size: 12px; color: var(--gray-500);
}
.btn-google {
  width: 100%; display: inline-flex; align-items: center; justify-content: center;
  gap: 12px; padding: 10px 16px; border: 1px solid var(--gray-300);
  border-radius: 8px; background: white; font: inherit; font-size: 14px;
  font-weight: 500; color: var(--gray-700); cursor: pointer;
  box-shadow: var(--shadow-sm); transition: background-color .15s;
}
.btn-google:hover { background: var(--gray-50); }
.btn-google:disabled { opacity: .5; cursor: not-allowed; }

/* medwork iO wordmark (logo.tsx: gray-700 MEDWORK + blue bubble) */
.mw-mark { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; color: var(--gray-700); font-size: 13px; letter-spacing: .02em; }
.mw-bubble {
  background: var(--primary-500); color: white; border-radius: 8px 8px 8px 2px;
  font-size: 10px; font-weight: 700; padding: 1px 5px;
}

/* ============ shell (shell-layout.tsx: white sidebar w-64, border-r) ======= */
.shell { display: flex; min-height: 100vh; background: var(--gray-50); }
.sidebar {
  width: 256px; flex: 0 0 256px; background: white;
  border-right: 1px solid var(--gray-200);
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; z-index: 50;
}
.side-brand {
  display: flex; gap: 8px; align-items: center; height: 64px; padding: 0 16px;
  border-bottom: 1px solid var(--gray-200);
}
.side-logo { height: 36px; max-width: 150px; object-fit: contain; }
/* MEDWORK.iO wordmark (logo.tsx: 36x28 bubble + 1.25rem gray-700 bold text) */
.mw-logo { width: 36px; height: 28px; flex: 0 0 36px; }
.mw-word { font-size: 1.25rem; font-weight: 700; color: var(--gray-700); letter-spacing: .01em; }
.mw-module {
  margin-left: 2px; background: var(--primary-50); border: 1px solid var(--primary-200);
  color: var(--primary-700); font-size: 10.5px; font-weight: 700;
  letter-spacing: .05em; padding: 2px 7px; border-radius: 6px;
}
.side-title { font-weight: 700; color: var(--gray-900); font-size: 15px; line-height: 1.2; }
.side-sub { font-size: 11px; color: var(--gray-500); }
.side-nav { flex: 1; padding: 16px 8px; overflow-y: auto; }
.side-sect {
  font-size: 12px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--gray-400); margin: 8px 12px 4px; padding: 8px 0 0;
}
.side-nav > .side-sect:first-child { margin-top: 0; padding-top: 0; }
.side-item {
  /* justify-content pins icon+label LEFT on every item. Without it, items that
     carry no count badge (Overview/System) inherit the <button> default of
     centered flex content, so they sat indented from the count-bearing Work
     Queue items whose margin-left:auto count absorbed the free space (field
     report #7, first live run). */
  display: flex; align-items: center; justify-content: flex-start; gap: 12px; width: 100%;
  padding: 8px 12px; margin: 2px 0; border: 0; border-radius: 8px;
  background: transparent; color: var(--gray-600); font: inherit;
  font-size: 14px; font-weight: 500; cursor: pointer; text-align: left;
  transition: color .15s, background-color .15s;
}
.side-item:hover { background: var(--gray-100); color: var(--gray-900); }
.side-item.active { background: var(--primary-50); color: var(--primary-700); }
.side-item svg { width: 20px; height: 20px; flex: 0 0 20px; }
.side-item .count {
  margin-left: auto; background: var(--gray-100); color: var(--gray-600);
  border-radius: 999px; font-size: 11px; padding: 0 8px; line-height: 18px; font-weight: 600;
}
.side-item.active .count { background: var(--primary-100); color: var(--primary-700); }
.side-foot {
  border-top: 1px solid var(--gray-200); padding: 16px;
  display: flex; align-items: center; gap: 10px;
}
.avatar {
  width: 32px; height: 32px; border-radius: 999px; background: var(--primary-100);
  color: var(--primary-600); font-weight: 500; font-size: 13px;
  display: flex; align-items: center; justify-content: center; flex: 0 0 32px;
  overflow: hidden;
}
.avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 999px; }
.side-user { flex: 1; min-width: 0; }
.side-user .side-name {
  font-size: 14px; font-weight: 500; color: var(--gray-900);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.side-user .side-role {
  display: block; font-size: 12px; color: var(--gray-500); font-weight: 400;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.side-logout {
  border: 0; background: transparent; color: var(--gray-400); cursor: pointer;
  padding: 6px; border-radius: 8px; display: flex;
}
.side-logout:hover { color: var(--gray-600); background: var(--gray-100); }
.side-logout svg { width: 18px; height: 18px; }

.content { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.pagebar {
  display: flex; align-items: center; background: white;
  border-bottom: 1px solid var(--gray-200);
  padding: 0 24px; height: 64px; position: sticky; top: 0; z-index: 40;
}
.pagebar h1 { font-size: 18px; font-weight: 600; color: var(--gray-900); margin: 0; }
.pagebar-right { margin-left: auto; display: flex; gap: 10px; align-items: center; }
.container { max-width: 1240px; width: 100%; margin: 0 auto; padding: 24px; }

/* ============ cards (Card.tsx: rounded-xl border-gray-100 shadow-sm) ====== */
.card {
  background: white; border: 1px solid var(--gray-100);
  border-radius: 12px; box-shadow: var(--shadow-sm); overflow: hidden;
}
.card + .card { margin-top: 16px; }
.card-head {
  display: flex; align-items: center; gap: 12px; padding: 16px 24px;
  border-bottom: 1px solid var(--gray-100);
}
.card-head h2 { margin: 0; font-size: 16px; font-weight: 600; color: var(--gray-900); }
.card-body { padding: 20px 24px; }

/* Collapsible editable "Case fields" card (#260): a <details> whose card-head is
   the <summary>. Collapsed by default; click the header to expand the form. The
   corrected chip + a hint live on the always-visible header. Mirrors the
   disclosure-marker treatment on .gapv-why. */
.fields-summary { cursor: pointer; list-style: none; }
/* matches .card-head h2 — the summary title is a phrasing <span>, not a heading */
.fields-title { font-size: 16px; font-weight: 600; color: var(--gray-900); }
.fields-summary::-webkit-details-marker { display: none; }
.fields-summary::before {
  content: "\25B8"; /* ▸ */ font-size: 12px; color: var(--gray-400);
  transition: transform .15s; margin-right: -2px;
}
.fields-details[open] .fields-summary::before { transform: rotate(90deg); }
.fields-summary:hover { background: var(--gray-50, #f9fafb); }
.fields-details[open] .fields-summary { border-bottom: 1px solid var(--gray-100); }
.fields-details:not([open]) .fields-summary { border-bottom: none; }
.fields-hint { margin-left: auto; font-size: 12px; color: var(--gray-400); }
.fields-details[open] .fields-hint { display: none; }
.muted { color: var(--gray-500); }
.small { font-size: 12.5px; color: var(--gray-400); }
.toolbar { display: flex; align-items: center; gap: 10px; margin: 0 0 16px; flex-wrap: wrap; }
.toolbar .spacer { flex: 1; }

/* inputs (Input.tsx) */
select, input[type="text"], input[type="email"], input[type="password"],
input[type="date"], input[type="number"], textarea {
  border: 1px solid var(--gray-200); border-radius: 8px; padding: 8px 12px;
  background: white; color: var(--gray-900); transition: border-color .15s;
  /* Spell out the Inter stack: a bare `font: inherit` on <textarea>/<input
     type=date> loses to the UA monospace/system default in some engines
     (see the .fb-input fix, commit 476a542). */
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
               Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px; line-height: 1.5;
}
textarea { resize: vertical; box-sizing: border-box; }
select:hover, input:hover, textarea:hover { border-color: var(--gray-300); }
select:focus, input:focus, textarea:focus {
  outline: 2px solid var(--primary-500); border-color: transparent;
}

/* Submission-details card (issue #131): lay the fields out as labeled columns
   that wrap, so the free-note textarea and Save button never crowd each other. */
.subm-body { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
.subm-label { display: flex; flex-direction: column; gap: 4px; }
.subm-ref, .subm-mode { min-width: 200px; }
.subm-note { min-width: 280px; min-height: 44px; }
/* "No ref # supplied" attestation (issue #358). The checkbox sits inline right
   after the ref # it stands in for. It reveals NOTHING (issue #365): ticking it
   must not change the card's height, so there is no note row to lay out — it only
   disables the ref # input, which reads as inactive rather than broken. */
.subm-check { display: flex; align-items: center; gap: 6px; align-self: flex-end;
  padding-bottom: 6px; cursor: pointer; }
.subm-check input { cursor: pointer; }
.subm-ref:disabled { background: var(--gray-100); color: var(--gray-400);
  cursor: not-allowed; }
/* Free note + both save buttons share a column so the buttons sit directly under
   the textarea they read with (issue #365) — outside the <label>, which must wrap
   only its own control. "Save note" is the smaller, secondary one: it writes just
   the note, so it should not compete with the button that submits the case. */
.subm-note-col { display: flex; flex-direction: column; align-items: flex-start;
  gap: 8px; }
.subm-note-btns { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.btn-sm { padding: 4px 10px; font-size: 12.5px; }

/* Case-tracking export card (issue #131): keep the date filter + button on one
   tidy row below the description. */
.exp-body { display: flex; flex-direction: column; gap: 12px; align-items: flex-start; }
.exp-label { display: flex; flex-direction: column; gap: 4px; max-width: 520px; }
.exp-since { max-width: 200px; }

/* buttons (Button.tsx: rounded-lg, px-4 py-2 text-sm font-medium) */
button, .btn {
  border: 1px solid var(--gray-300); border-radius: 8px; padding: 8px 16px;
  background: transparent; font: inherit; font-size: 14px; font-weight: 500;
  color: var(--gray-700); cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: background-color .15s, color .15s;
}
button:hover, .btn:hover { background: var(--gray-50); }
.btn-primary { background: var(--primary-600); border-color: transparent; color: white; }
.btn-primary:hover { background: var(--primary-700); }
.btn-success { background: #059669; border-color: transparent; color: white; }
.btn-success:hover { background: #047857; }
button:disabled { opacity: .5; cursor: not-allowed; }

/* ============ tables (Table.tsx) ========================================== */
table { width: 100%; border-collapse: collapse; }
thead { background: var(--gray-50); border-bottom: 1px solid var(--gray-100); }
th {
  text-align: left; padding: 12px 24px; font-size: 12px; font-weight: 500;
  text-transform: uppercase; letter-spacing: .05em; color: var(--gray-500);
}
td { text-align: left; padding: 16px 24px; border-bottom: 1px solid var(--gray-100); font-size: 14px; color: var(--gray-600); }
td strong { color: var(--gray-900); }
tbody tr:last-child td { border-bottom: 0; }
tbody tr.rowlink { cursor: pointer; transition: background-color .15s; }
tbody tr.rowlink:hover { background: var(--gray-50); }
td.center, th.center { text-align: center; }

/* ============ badges (Badge.tsx: rounded-full px-2.5 py-0.5 text-xs, -100/-800) */
.chip {
  display: inline-flex; align-items: center; gap: 6px; border-radius: 999px;
  padding: 2px 10px; font-size: 12px; font-weight: 500; white-space: nowrap;
}
/* An errored chip is a real link to the errored view (#465): same chip look,
   underline on hover so the click-through is discoverable. */
a.chip-link { text-decoration: none; cursor: pointer; }
a.chip-link:hover { text-decoration: underline; }
/* The five AI outcome buckets (issue #558, the LOMN v2 goal doc's Part 1):
   Passed (ready), Passed Limitation, Needs review, Missing info (needs_info)
   and Multiple Issues share ONE solid navy — #1e3a8a on #ffffff, decided
   2026-07-31. Solid rather than the pastel -100/-800 chip idiom on purpose:
   it differs from every other chip in LIGHTNESS and not only in hue (so the
   family survives a color vision deficiency), and it cannot be confused with
   .chip.editing, which owns --blue-100 for "In progress" and would otherwise
   read as a sixth bucket. Contrast 8.6:1 on the page background. needs_review
   had NO rule at all before this (it rendered transparent); the two new slugs
   get their first rule here too. */
.chip.ready, .chip.passed_limitation, .chip.needs_review,
.chip.needs_info, .chip.multiple_issues { background: #1e3a8a; color: #ffffff; }
.chip.editing    { background: var(--blue-100); color: var(--blue-800); }
/* submitted_to_payer (issue #126) + transferred_to_medwork (issue #188): the
   terminal done buckets — reuse the submitted/delivered purple so all the
   "done/sent" states read alike. */
.chip.submitted, .chip.delivered, .chip.submitted_to_payer,
.chip.transferred_to_medwork { background: var(--purple-100); color: var(--purple-800); }
/* info_requested (issue #126): a workable awaiting-info bucket (provider asked
   to add the field) — reuse the needs_info yellow, its sibling bucket. */
.chip.info_requested { background: var(--yellow-100); color: var(--yellow-800); }
/* corrections_ready (issue #353): the ANSWER half of info_requested — the clinic
   fixed the note and the case is waiting on its next export to overwrite + re-run
   it. Same amber corrections family as chip.info_requested / .ctr-mark so the two
   halves read as one flow, but paler with an explicit border so "they answered" is
   never mistaken at a glance for "we are still waiting on them". Deliberately NOT
   green: nothing has been re-checked yet, so it must not read as cleared. */
.chip.corrections_ready {
  background: var(--amber-50, #fffbeb); color: var(--amber-800, #92400e);
  border: 1px solid var(--amber-300, #fcd34d);
}
/* ready_for_pa (w3): a distinct teal so the handed-to-PA state reads apart from
   both editing (blue) and submitted (purple). */
.chip.ready_for_pa { background: #ccfbf1; color: #115e59; }
.chip.held       { background: var(--yellow-100); color: var(--yellow-800); }
/* ic_limitation_hold (issue #421): the same yellow waiting family as .chip.held,
   because it IS a hold and should read as one at a glance, with a border to mark
   it as the tracked variety (a coded wait time and what the wait follows, versus
   Hold's free-form external reason). Without this rule the chip would render
   unstyled, since chip() derives its class straight from the status slug. */
.chip.ic_limitation_hold {
  background: var(--yellow-100); color: var(--yellow-800);
  border: 1px solid var(--yellow-400, #facc15);
}
.chip.error, .chip.rejected, .chip.inactive { background: var(--red-100); color: var(--red-800); }
/* Medwork-sync error statuses (issue #190): the five retryable transfer-failure
   states. Orange, deliberately APART from the engine-error/rejected red — the
   letter itself is fine; only the hand-off to Medwork failed. conn_failed is a
   softer tint than the four escalated (human-work) classes. */
.chip.medwork_conn_failed { background: var(--yellow-100); color: #9a3412; }
.chip.medwork_data_mismatch, .chip.medwork_patient_not_found,
.chip.medwork_duplicate,
.chip.medwork_internal_error { background: #ffedd5; color: #9a3412; }
/* Hard block (missing absolute-required field, e.g. UHC group number) — a
   distinct red-tinted pill so a letter-less case never reads like a workable
   one (Dr. Mike + Joe, 7/19). */
.chip.blocked { background: var(--red-100); color: var(--red-800); }
/* Urgent priority (case-priority feature): a distinct, high-contrast red pill
   so an urgent case reads at a glance in every queue/case list — visually apart
   from the yellow/amber status buckets. A subtle border reinforces it as an
   attention flag rather than a status. */
.chip.urgent {
  background: var(--red-600); color: #fff; font-weight: 700;
  border: 1px solid var(--red-800);
}
.wq-status-cell { display: inline-flex; align-items: center; gap: 6px; }
/* Medwork sync, rendered INSIDE the screens the case already has: a chip beside
   the status in every queue row, and a banner on the case screen carrying the
   field-level detail (it can run to several field lines) with its actions. */
.mw-err-detail div { line-height: 1.35; }
.chip.mw-chip { background: var(--blue-100); color: var(--blue-800); }
.chip.mw-chip-ok { background: var(--purple-100); color: var(--purple-800); }
.chip.mw-chip-claimed { background: var(--yellow-100); color: var(--yellow-800); }
.mw-banner-actions { margin-left: auto; display: inline-flex; gap: 6px; }
.case-medwork-error { border-left: 3px solid var(--red-600); }
.case-medwork-ok { border-left: 3px solid var(--purple-800); }
.case-medwork-claimed { border-left: 3px solid var(--yellow-800); }
.mw-bulk-retry { margin-left: 6px; }
/* Medwork Errors page: an All tile plus one filter tile per error class, over one
   card per class, each card headed by the class chip and the fix that clears it.
   The selected tile is the filter, and it is held in the URL (?error=<slug>). */
.mw-err-tiles { margin-bottom: 16px; }
.kpi.kpi-on { box-shadow: inset 0 0 0 2px var(--kpi); }
.mw-err-section { border-left: 4px solid var(--kpi, var(--gray-300)); margin-bottom: 16px; }
.mw-err-action {
  color: var(--gray-600); padding: 12px 24px; max-width: 900px;
  border-bottom: 1px solid var(--gray-100);
}
.mw-err-cell { min-width: 260px; max-width: 380px; }
/* the detail column can outgrow the card on a narrow window: scroll the table
   rather than clip the Retry button off the right edge. */
.mw-err-scroll { overflow-x: auto; }
.mw-err-health {
  padding: 10px 12px; margin-bottom: 14px; border-radius: 8px;
  background: var(--gray-50); color: var(--gray-700);
}
.mw-err-health.mw-err-warn {
  background: var(--red-50, #fef2f2); color: var(--red-800);
}
/* AI-Passed-with-bug badge (issue #148): an amber audit flag on the admin queue,
   visually distinct from the red Urgent chip — it marks a checker MISS that was
   caught + corrected, not a work priority. Admin-only (server-scrubbed for
   workers), so it only ever appears in the admin view. */
.chip.ai-bug {
  background: var(--amber-100, #fef3c7); color: var(--amber-800, #92400e);
  font-weight: 700; border: 1px solid var(--amber-300, #fcd34d);
}
/* Data-error badge (issue #313): corrupted stored data — the case needs its
   source file re-uploaded. Red-on-light (distinct from the solid-red Urgent
   chip: this marks broken DATA, not work priority). Worker-visible. */
.chip.data-error {
  background: var(--red-50, #fef2f2); color: var(--red-700, #b91c1c);
  font-weight: 700; border: 1px solid var(--red-300, #fca5a5);
}
/* Data-error banner (issue #313): the red alert above the All Cases table
   (count + one-click filter) and at the top of a corrupted case's page. */
.dq-banner {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  margin: 0 0 12px; padding: 10px 14px; border-radius: 8px;
  background: var(--red-50, #fef2f2); border: 1px solid var(--red-300, #fca5a5);
  color: var(--red-700, #b91c1c); font-weight: 600;
}
.dq-banner::before { content: "⚠"; font-size: 15px; line-height: 1; }
/* The Medwork-sync variants of the banner. `.dq-banner` is a data-quality
   WARNING -- red fill, red text, warning glyph -- and these states reuse its
   layout without sharing its meaning, so each restates the whole palette and
   its own glyph. Kept AFTER the `::before` rule on purpose: same specificity, so
   only source order lets the glyph be overridden at all. Colours match the
   corresponding `.mw-chip-*` so a case reads the same in a queue row and on its
   own page. */
.case-medwork-ok {
  background: var(--purple-100); border-color: var(--purple-100);
  border-left-color: var(--purple-800); color: var(--purple-800);
}
.case-medwork-ok::before { content: "✓"; }
.case-medwork-claimed {
  background: var(--yellow-100); border-color: var(--yellow-100);
  border-left-color: var(--yellow-800); color: var(--yellow-800);
}
.case-medwork-pending {
  background: var(--blue-100); border-color: var(--blue-100);
  border-left-color: var(--blue-800); color: var(--blue-800);
}
.case-medwork-pending::before { content: "↻"; }
.dq-banner .dq-banner-btn { margin-left: auto; }
.btn.dq-banner-btn.on {
  background: var(--gray-800); border-color: transparent;
  color: #fff; font-weight: 600;
}
/* Urgent-only filter buttons (worker + admin queues): the "on" state matches the
   other active filter chips (dark fill), so a live filter is obvious. The
   AI-Passed-bug filter (admin-only, #148) shares the same active treatment. */
.btn.qurgent-btn.on,
.btn.qaibug-btn.on { background: var(--gray-800); border-color: transparent; color: #fff; font-weight: 600; }
.wq-urgent-filter {
  border: 1px solid var(--gray-300); background: #fff; border-radius: 999px;
  padding: 4px 12px; font: inherit; font-size: 13px; cursor: pointer;
}
.wq-urgent-filter:hover { background: var(--gray-50); }
.wq-urgent-filter.on { background: var(--gray-800); border-color: transparent; color: #fff; font-weight: 600; }
.block-banner { border-left: 4px solid var(--red-600); }
.block-banner .card-body { color: var(--red-800); }
/* Universal missing-Group-ID SOFT flag (issue #151): yellow "collect it, never
   holds the case" notice — visually distinct from the red block banner. */
.flag-banner { border-left: 4px solid #f59e0b; }
.flag-banner .card-body { color: #92400e; }
/* Other active LOMNs (#626): an amber staff notice, one row per other case. */
.other-lomn-notices {
  display: flex; flex-direction: column; gap: 8px; margin: 0 0 16px;
}
.other-lomn-notice {
  padding: 10px 14px; border-radius: 8px;
  background: var(--amber-50, #fffbeb); color: var(--amber-800, #92400e);
  border: 1px solid var(--amber-300, #fcd34d); border-left: 4px solid #f59e0b;
}
/* Status dropdown + Submit (issue #126): the one control that sets the case's
   tracking status. A calm, compact card — dropdown + Submit on one row, an
   optional note + Add-note on the next. */
.status-card .status-body { display: flex; flex-direction: column; gap: 10px; }
.status-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.status-label { display: flex; align-items: center; gap: 8px; font-weight: 600; color: var(--gray-700); }
.status-select { padding: 6px 10px; border: 1px solid var(--gray-300); border-radius: 8px; font: inherit; }
.status-note { flex: 1; min-width: 180px; padding: 6px 10px; border: 1px solid var(--gray-300); border-radius: 8px; font: inherit; }
.status-hint { margin-top: 2px; }
/* Insurance-ref audit warning (issue #358): an amber advisory on the Set status
   card when the chosen target needs the ref #/attestation and the case has
   neither. Amber, not red — it is a "do this first", not a failure. */
.status-refwarn { padding: 8px 10px; border-radius: 8px;
  background: var(--amber-50, #fffbeb); color: var(--amber-800, #92400e);
  border: 1px solid var(--amber-300, #fcd34d); }
.chip.processing, .chip.viewer { background: var(--gray-100); color: var(--gray-600); }
.chip.active { background: var(--green-100); color: var(--green-800); }
.chip.admin { background: var(--purple-100); color: var(--purple-800); }
.chip.provider { background: var(--blue-100); color: var(--blue-800); }
.chip.agent { background: var(--green-100); color: var(--green-800); }
.score { font-weight: 700; }
.score.hi { color: var(--green-600); } .score.mid { color: #d97706; } .score.lo { color: var(--red-600); }
.verdict { font-weight: 700; font-size: 12.5px; }
.verdict.pass { color: var(--green-600); } .verdict.fail { color: var(--red-600); } .verdict.open { color: #d97706; }

/* ============ dashboard triage (clinical-dashboard.tsx pattern) ========== */
.dash-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin: 0 0 14px;
}
.dash-sub { font-size: 12.5px; color: var(--gray-400); }
.readiness-bar {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 16px;
}
@media (max-width: 800px) { .readiness-bar { grid-template-columns: repeat(2, 1fr); } }
.readiness {
  text-align: left; padding: 14px 16px; border-radius: 16px; border: 1px solid;
  cursor: pointer; transition: box-shadow .15s, transform .05s; font: inherit;
  display: block;
}
.readiness:hover { box-shadow: var(--shadow-md); }
.readiness.zero { opacity: .4; cursor: default; }
.readiness-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.readiness-dot { width: 8px; height: 8px; border-radius: 999px; }
.readiness-count { font-size: 24px; font-weight: 900; font-variant-numeric: tabular-nums; }
.readiness-label { font-size: 12px; font-weight: 600; line-height: 1.2; }
/* idle palettes (soft bg + colored text), active = solid fill + white text */
.readiness.all    { background: white;    border-color: var(--gray-200); }
.readiness.all    .readiness-count { color: var(--gray-900); }
.readiness.all    .readiness-label { color: var(--gray-500); }
.readiness.red    { background: #fef2f2;  border-color: #fecaca; }
.readiness.red    .readiness-dot { background: #ef4444; }
.readiness.red    .readiness-count { color: #b91c1c; }
.readiness.red    .readiness-label { color: #ef4444; }
.readiness.yellow { background: #fffbeb;  border-color: #fde68a; }
.readiness.yellow .readiness-dot { background: #fbbf24; }
.readiness.yellow .readiness-count { color: #92400e; }
.readiness.yellow .readiness-label { color: #d97706; }
.readiness.green  { background: #ecfdf5;  border-color: #a7f3d0; }
.readiness.green  .readiness-dot { background: #10b981; }
.readiness.green  .readiness-count { color: #065f46; }
.readiness.green  .readiness-label { color: #059669; }
.readiness.on { border-color: transparent; box-shadow: var(--shadow-md); }
.readiness.all.on    { background: var(--gray-900); }
.readiness.red.on    { background: #dc2626; }
.readiness.yellow.on { background: #f59e0b; }
.readiness.green.on  { background: #059669; }
.readiness.on .readiness-count, .readiness.on .readiness-label { color: white; }
.readiness.on .readiness-label { opacity: .85; }
.readiness.on .readiness-dot { background: rgba(255,255,255,.6); }

/* ============ management throughput tracker (front page, Joe 07/22) ====== */
.thru-card .card-body { display: flex; flex-direction: column; gap: 14px; }
.kpi-row {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px;
}
@media (max-width: 900px) { .kpi-row { grid-template-columns: repeat(2, 1fr); } }
.kpi {
  border: 1px solid var(--gray-200); border-left: 4px solid var(--kpi, var(--gray-300));
  border-radius: 12px; padding: 12px 14px; background: white;
}
/* scope eyebrow (issue #333): tells the reader whether a tile is Today's count,
   a window figure, or a live total, so a single-day value isn't misread as a
   running total. Tinted with the tile accent (--kpi) to tie it to its metric. */
.kpi-scope {
  font-size: 10px; font-weight: 800; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--kpi, var(--gray-500)); margin-bottom: 1px;
}
.kpi-value {
  font-size: 28px; font-weight: 900; line-height: 1.1; color: var(--gray-900);
  font-variant-numeric: tabular-nums;
}
.kpi-label { font-size: 12.5px; font-weight: 700; color: var(--gray-700); margin-top: 2px; }
.kpi-hint { font-size: 11px; color: var(--gray-500); margin-top: 1px; }
/* a clickable KPI (issue #119): the outreach tiles drill into their queue */
.kpi.rowlink { cursor: pointer; }
.kpi.rowlink:hover { background: var(--gray-50); }
/* overdue-chase alert tile: tinted red so the count doesn't blend in */
.kpi.kpi-alert { background: var(--red-100); }
.kpi.kpi-alert:hover { background: #fbd0d0; }
.kpi.kpi-alert .kpi-value { color: var(--red-800); }
.thru-averages { font-size: 12.5px; color: var(--gray-600); font-weight: 600; }
.thru-chart-head { color: var(--gray-500); font-weight: 600; }
.straggler-block { display: flex; flex-direction: column; gap: 6px; }
.straggler-list { display: flex; flex-direction: column; }
.straggler {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 8px 10px; border-top: 1px solid var(--gray-100); cursor: pointer;
}
.straggler:hover { background: var(--gray-50); }
.straggler-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
/* number + patient name share one line (#507); a long name truncates rather
   than wrapping, so the row never grows past its two text lines. */
.straggler-id { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.straggler-name {
  color: var(--gray-700); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
.straggler-reason { color: var(--gray-500); }
.straggler-meta {
  display: flex; align-items: center; gap: 8px; color: var(--gray-500);
  flex: 0 0 auto; white-space: nowrap;
}
.straggler-age { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--gray-700); }

/* "Accuracy Analysis" card (issue #389): one 100%-wide bar per AI verdict, split
   into where those cases went NEXT. The bar is a PROPORTION (always full width),
   never a magnitude, so the arms are comparable to each other no matter how many
   cases each one saw. The count lives in the legend beside it. */
.acc-arm { padding: 12px 0; border-top: 1px solid var(--gray-100); }
.acc-arm:first-child { border-top: 0; }
.acc-arm-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 6px;
}
.acc-arm-name { font-size: 13.5px; font-weight: 700; color: var(--gray-900); }
.acc-arm-n {
  font-size: 12px; color: var(--gray-500); flex: 0 0 auto; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.acc-track {
  display: flex; width: 100%; height: 26px; border-radius: 6px;
  overflow: hidden; background: var(--gray-100);
}
.acc-seg {
  height: 100%; min-width: 2px; display: flex; align-items: center;
  justify-content: center; overflow: hidden;
}
.acc-seg-pct {
  font-size: 11px; font-weight: 700; color: white; white-space: nowrap;
  padding: 0 4px;
}
.acc-legend {
  display: flex; flex-wrap: wrap; gap: 6px 18px; margin-top: 7px;
}
.acc-legend-row {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px;
  color: var(--gray-600);
}
.acc-legend-row .legend-name { font-size: 12.5px; font-weight: 500; }
.acc-legend-val { font-variant-numeric: tabular-nums; color: var(--gray-500); }
/* the re-ran slice: not a direction the case took, so it reads muted and last */
.acc-legend-row.acc-rerun .legend-name { font-weight: 400; color: var(--gray-500); }

/* ============ charts (executive-dashboard visual language) =============== */
.chart-legend { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 8px; }
.legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--gray-600); }
.legend-dot { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 10px; }
.donut-wrap { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.donut-legend { flex: 1; min-width: 180px; }
.legend-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; border-bottom: 1px dashed var(--gray-100); }
.legend-row:last-child { border-bottom: 0; }
.legend-name { font-weight: 500; color: var(--gray-900); font-size: 13.5px; }
.legend-val { margin-left: auto; font-size: 12.5px; color: var(--gray-500); }
.funnel-row { display: flex; align-items: center; gap: 12px; margin: 10px 0; }
.funnel-label { width: 130px; font-size: 13px; font-weight: 500; color: var(--gray-700); }
.funnel-track { flex: 1; background: var(--gray-100); border-radius: 8px; height: 30px; overflow: hidden; }
.funnel-fill {
  height: 100%; border-radius: 8px; display: flex; align-items: center;
  justify-content: flex-end; padding-right: 8px; min-width: 34px;
  transition: width .3s ease;
}
.funnel-count { color: white; font-size: 12.5px; font-weight: 700; }

/* ============ dashboard (StatsCard.tsx: icon box + title + value) ========= */
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-bottom: 16px; }
.stat {
  background: white; border: 1px solid var(--gray-100); border-radius: 12px;
  box-shadow: var(--shadow-sm); padding: 20px 24px;
  display: flex; align-items: center; gap: 16px;
}
.stat-icon {
  padding: 12px; border-radius: 8px; display: flex; flex: 0 0 auto;
}
.stat-icon svg { width: 22px; height: 22px; }
.stat-icon.blue   { background: #eff6ff; color: var(--blue-600); }
.stat-icon.green  { background: #f0fdf4; color: #16a34a; }
.stat-icon.yellow { background: #fefce8; color: #ca8a04; }
.stat-icon.red    { background: #fef2f2; color: var(--red-600); }
.stat-icon.purple { background: #faf5ff; color: #9333ea; }
.stat-icon.orange { background: var(--orange-50); color: var(--orange-600); }
.stat-icon.gray   { background: var(--gray-50); color: var(--gray-600); }
.stat-body { flex: 1; min-width: 0; }
.stat .stat-label { font-size: 14px; color: var(--gray-500); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stat .stat-value { font-size: 24px; font-weight: 700; color: var(--gray-900); }
.dash-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 980px) { .dash-grid { grid-template-columns: 1fr; } }
.hbar-row { display: grid; grid-template-columns: 130px 1fr 74px; align-items: center; gap: 10px; margin: 7px 0; }
.hbar-track { background: var(--gray-100); border-radius: 6px; height: 16px; overflow: hidden; }
.hbar-fill { background: var(--primary-500); height: 100%; border-radius: 6px; }
.hbar-label { font-weight: 500; font-size: 13px; color: var(--gray-700); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbar-val { font-size: 12px; color: var(--gray-500); text-align: right; }

/* ============ detail / editor ============================================= */
.detail-grid { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1fr); gap: 16px; }
@media (max-width: 980px) { .detail-grid { grid-template-columns: 1fr; } }
.kv { display: grid; grid-template-columns: 150px 1fr; row-gap: 8px; }
.kv dt { color: var(--gray-500); font-size: 13px; }
.kv dd { margin: 0; color: var(--gray-900); font-size: 14px; }

/* patient-identity banner — name + click-to-copy Nextech-lookup identifiers,
   pinned at the top of the case page (Dr. Mike, 7/18). */
.pid-banner {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 20px;
  background: white; border: 1px solid var(--gray-200); border-radius: 12px;
  padding: 14px 20px; margin: 0 0 16px; box-shadow: var(--shadow-sm);
}
.pid-name { font-size: 20px; font-weight: 700; color: var(--gray-900); margin-right: 8px; }
.pid-ids { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.pid-chip {
  /* align-items:center (not baseline) + a modest radius: a short value like
     "—" must never round the box into a giant ellipse (7/20). */
  display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px;
  border: 1px solid var(--gray-200); border-radius: 8px; background: var(--gray-50);
  font: inherit; line-height: 1.2; cursor: pointer; white-space: nowrap;
  transition: background-color .12s, border-color .12s;
}
.pid-chip:hover { background: var(--primary-50); border-color: var(--primary-200); }
/* NB: use `pid-empty`, NOT the global `.empty` utility (which is the 48px-padded
   empty-PAGE state) — sharing that class ballooned the chip into a giant
   ellipse (7/20). */
.pid-chip.pid-empty { cursor: default; opacity: .7; }
.pid-chip.pid-empty:hover { background: var(--gray-50); border-color: var(--gray-200); }
.pid-k { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--gray-500); }
.pid-v { font-size: 14px; font-weight: 600; color: var(--gray-900); font-variant-numeric: tabular-nums; }
.pid-copy { font-size: 12px; color: var(--gray-400); }
.editor {
  width: 100%; min-height: 480px; resize: vertical;
  border: 1px solid var(--gray-200); border-radius: 8px; padding: 14px;
  font: 13px/1.55 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  color: var(--gray-900); background: var(--gray-50);
}
.editor:focus { outline: 2px solid var(--primary-500); border-color: transparent; background: white; }
.editor[readonly] { background: var(--gray-50); color: var(--gray-500); }
.letter-frame {
  width: 100%; height: 825px; border: 1px solid var(--gray-200);
  border-radius: 8px; background: white; display: block;
}

/* letter view with the to-review rail (t17 port). Inside the full-width
   letters-card the letter is the page's centerpiece: rail 300px, letter
   fills the rest, 1010px tall (owner 7/16: "large, prominent, front and
   center"; +33% height 7/22 for a taller box). The same classes serve the
   compact fallback card. */
.letter-slot:empty { display: none; }
.letter-slot { margin-bottom: 16px; }
.letters-card .card-head { flex-wrap: wrap; gap: 10px; }
.letters-body { display: flex; gap: 0; align-items: stretch; min-height: 420px; }
.letters-body .letter-frame { flex: 1; width: auto; min-width: 0; }
.letters-card .letters-body .letter-frame {
  border: 0; border-radius: 0; height: auto; min-height: 1010px;
}
.letters-card .flag-rail {
  border: 0; border-right: 1px solid var(--gray-100); border-radius: 0;
  flex: 0 0 300px; max-height: 1010px;
}
.flag-rail {
  flex: 0 0 260px; border: 1px solid var(--gray-200); border-right: 0;
  border-radius: 8px 0 0 8px; padding: 14px 16px; overflow-y: auto;
  max-height: 825px; background: white;
}
.flag-rail + .letter-frame { border-radius: 0 8px 8px 0; }
.flag-rail-head { margin-bottom: 8px; }
.flag-row {
  display: flex; align-items: center; gap: 8px; padding: 9px 8px;
  border-radius: 8px; cursor: pointer; border: 1px solid transparent;
}
.flag-row:hover { background: var(--gray-50, #f9fafb); }
.flag-row.on { background: var(--blue-50, #eff6ff); border-color: var(--blue-100); }
.flag-label { flex: 1; font-size: 13.5px; color: var(--gray-900); }
.flagdot {
  display: inline-block; width: 10px; height: 10px; border-radius: 50%;
  flex: 0 0 10px;
}
.flagdot.red { background: #dc2626; }
.flagdot.yellow { background: #f59e0b; }
.flagdot.settled { background: var(--green-500, #22c55e); }

/* Unified To-Review panel (issue #168) — the flag-rail rendered inside a plain
   card (not the letters split layout), so it is full-width with no side border. */
.review-panel .flag-rail {
  flex: none; border: 0; border-radius: 0; padding: 0; max-height: none;
  background: transparent; overflow: visible;
}
.review-panel .flag-label > div { line-height: 1.35; }

/* click-to-jump highlight (#168): a brief ring on the card/field a panel item
   points to, so the reviewer sees exactly where the issue is. */
.jump-flash {
  outline: 3px solid var(--primary-500, #2563eb); outline-offset: 2px;
  border-radius: 6px; transition: outline .2s ease;
}

/* settled letter flags moved out of the review list into a secondary,
   collapsed disclosure on the letter card (#168) — resolved work that stays
   UNDO-able but is not review-list material and is never counted. */
.resolved-disclosure { margin: 6px 0 4px; }
.resolved-disclosure > summary {
  cursor: pointer; font-size: 12.5px; font-weight: 600; color: var(--gray-500, #6b7280);
  padding: 4px 2px; list-style: revert;
}
.flag-row.settled { cursor: default; }
.flag-row.settled:hover { background: transparent; }

/* Clinic-list marker on a review row (#338, replacing the #321 checkbox + note
   input). Membership on the Clinical Team Requests list is automatic now, so this
   is a passive badge, not a control — deliberately tiny, because the rail is 300px
   and the review LABEL needs that width (the old tick + note field pushed labels
   down to one word per line). Amber echoes the Corrections Requested language
   elsewhere (chip.info_requested), tying it to that same flow. */
.ctr-mark {
  flex: 0 0 auto; white-space: nowrap; padding: 1px 5px; border-radius: 5px;
  background: var(--amber-50, #fffbeb); color: var(--amber-800, #92400e);
  border: 1px solid var(--amber-300, #fcd34d); font-size: 11px;
  letter-spacing: .02em;
}
/* (The editable per-item clinic-note field that lived here was removed in #353 with
   the rest of the per-item controls — the block-level "Note Ready" button replaced
   them. A note written under the old flow still renders read-only; see
   .ctr-note-kept.) */

/* Clinical Team Requests tally page (#321, Phase 3): grouped corrections work
   list. Group cards each carry a header (facility/clinical team + a count chip)
   over a body of clickable item rows that drill into the case. */
.ctr-group { margin-bottom: 16px; }
.ctr-group-head { justify-content: space-between; }
.ctr-group .card-body { padding: 6px 12px; }
.ctr-item {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 10px 12px; border-radius: 8px; cursor: pointer;
  border-bottom: 1px solid var(--gray-100);
}
.ctr-item:last-child { border-bottom: 0; }
.ctr-item:hover { background: var(--gray-50, #f9fafb); }
.ctr-item-main { flex: 1; min-width: 0; }
.ctr-item-label { font-size: 13.5px; color: var(--gray-900); }
.ctr-item-note { margin-top: 2px; }
.ctr-item-detail { margin-top: 2px; }
.ctr-item-meta {
  display: flex; align-items: center; gap: 14px; flex: 0 0 auto;
  white-space: nowrap; padding-top: 1px;
}
/* An item a PARTIAL corrections round brought back around (#545). The date beside
   it is already the re-raise, so this only says which kind of date it is: the clinic
   sent some of the fix, this piece is still owed, and they are being asked again. */
.ctr-item-reraised {
  font-size: 11px; font-weight: 600; letter-spacing: .02em;
  padding: 1px 7px; border-radius: 999px;
  color: var(--amber-700, #b45309);
  background: var(--amber-50, #fffbeb);
  border: 1px solid var(--amber-200, #fde68a);
}
/* The yellow lower-priority section inside a group (#338): soft "worth checking"
   asks that ride along on the same outreach call without diluting the red ones. */
.ctr-subhead {
  padding: 10px 12px 4px; border-top: 1px solid var(--gray-100);
  margin-top: 4px; font-weight: 600;
}
/* The collapsed "not for clinic" tail per patient (#338), read-only since #353
   removed the per-item dismiss/restore controls. Dimmed and behind a toggle so it
   never competes with the live outreach list, but still reachable: those items are
   open on the case, and hiding them entirely would read as "fixed". */
.ctr-dismissed-toggle {
  display: block; margin: 8px 12px 4px; padding: 2px 0; border: 0;
  background: transparent; color: var(--gray-500, #6b7280); cursor: pointer;
  text-decoration: underline;
}
.ctr-dismissed-toggle:hover { color: var(--gray-900); }
.ctr-dismissed-list { border-top: 1px solid var(--gray-100); }
.ctr-item-dismissed { background: var(--gray-50, #f9fafb); }
.ctr-item-dismissed .ctr-item-label {
  color: var(--gray-500, #6b7280); text-decoration: line-through;
}

/* Per-PATIENT blocks inside a group card (#342). The audience is the medical
   director working the list WITH a physician, so the unit on screen is a chart:
   one identity header (name + click-to-copy lookup ids) over that case's items,
   instead of one flat row per missing field repeating the same patient. The left
   rule + inset ties the nested items visually to the patient they belong to. */
.ctr-case {
  margin: 10px 0 14px; padding: 0 0 2px 10px;
  border-left: 3px solid var(--gray-200, #e5e7eb); border-radius: 2px;
}
.ctr-case:last-child { margin-bottom: 4px; }
.ctr-case-head { padding: 4px 12px 2px; border-radius: 8px; cursor: pointer; }
.ctr-case-head:hover { background: var(--gray-50, #f9fafb); }
.ctr-case-name {
  font-size: 15px; font-weight: 700; color: var(--gray-900);
}
/* A nameless case says "Name missing" rather than borrowing the case id, so the
   name slot never reads as if an id were the patient. Muted + normal weight so it
   cannot be mistaken for a real name at a glance. */
.ctr-case-noname { font-weight: 500; font-style: italic; color: var(--gray-500, #6b7280); }
/* The context line: labelled Clinic / Team / Practice facts. Labelled because a
   group heading can be a "Payer: X" fallback, and an unlabelled string there would
   read as the clinic. An absent one is a dash, never a silent omission. */
.ctr-case-context { margin-top: 1px; display: flex; flex-wrap: wrap; gap: 10px; }
.ctr-fact { display: inline-flex; align-items: baseline; gap: 4px; }
.ctr-fact-k {
  font-size: 10px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--gray-400, #9ca3af);
}
.ctr-fact-v { color: var(--gray-600, #4b5563); }
/* Same convention as the id chips: `pid-empty` marks an absent value, so the dash
   reads as "we don't have this" rather than as data. (The chip rule is scoped to
   .pid-chip, hence the sibling rule here.) */
.ctr-fact-v.pid-empty { color: var(--gray-400, #9ca3af); }
/* Smaller than the case page's banner chips: here they sit many-to-a-page under
   every patient, so they must read as a compact reference strip, not a headline. */
.ctr-case-ids { padding: 6px 12px 4px; gap: 6px; }
.ctr-case-ids .pid-chip { padding: 3px 8px; gap: 5px; }
.ctr-case-ids .pid-k { font-size: 10px; }
.ctr-case-ids .pid-v { font-size: 12.5px; }
/* The one action per patient block (#353): "Note Ready". Sits at the FOOT of the
   block, after the items, because that is the reading order of the decision — see
   what the clinic owes, then say it is done. Indented to the items' inset so it
   reads as belonging to this chart and not to the group card. */
.ctr-case-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 12px 4px; border-top: 1px solid var(--gray-100); margin-top: 4px;
}
/* Deliberately the primary button on the block: it is the ONLY action here now, and
   the whole page exists to get it clicked. Compact so a long list of patients does
   not become a wall of buttons. */
.ctr-note-ready { padding: 4px 14px; font-weight: 600; }
.ctr-foot-hint { min-width: 0; }
/* The post-click foot: no control, just the state + how long it has been waiting.
   Amber-tinted like the corrections family so the block still reads as part of that
   flow, and dimmer than an open one so the eye skips it when scanning for work. */
.ctr-foot-awaiting { border-top-color: var(--amber-300, #fcd34d); }
/* A whole awaiting block, dimmed one step: these are finished conversations sharing
   the page with live ones, so they must not compete for attention. */
.ctr-case-awaiting { border-left-color: var(--amber-300, #fcd34d); }
.ctr-case-awaiting .ctr-item { opacity: .85; }
/* The awaiting-upload card (#353) — last on the page, past every outreach group. */
.ctr-awaiting-card { border-color: var(--amber-300, #fcd34d); }
.ctr-awaiting-note { padding: 8px 12px 2px; }
.ctr-index-awaiting {
  border-color: var(--amber-300, #fcd34d); background: var(--amber-50, #fffbeb);
  color: var(--amber-800, #92400e);
}
/* A clinic note left under the old per-item flow (#338), now read-only (#353): shown
   because somebody wrote it FOR the clinic, but styled as quoted text rather than as
   the editable amber field it used to be, so nobody waits for it to accept a click. */
.ctr-note-kept {
  margin-top: 3px; padding-left: 8px; color: var(--gray-600, #4b5563);
  border-left: 2px solid var(--amber-300, #fcd34d);
}
/* Find-a-patient box (#342): the director often works from a name said out loud,
   and the list spans every location at once. */
.ctr-filter-row { margin: 10px 0 6px; }
.ctr-filter {
  width: 100%; max-width: 520px; box-sizing: border-box; padding: 7px 10px;
  border: 1px solid var(--gray-200); border-radius: 8px; font: inherit;
  color: var(--gray-900); background: white;
}
.ctr-filter:focus { outline: 2px solid var(--blue-100); border-color: var(--blue-100); }
/* Jump index over the group cards — patients + open items per group. */
.ctr-index { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 12px; }
.ctr-index-chip {
  padding: 3px 9px; border: 1px solid var(--gray-200); border-radius: 999px;
  background: white; color: var(--gray-700); text-decoration: none;
  white-space: nowrap; cursor: pointer;
}
.ctr-index-chip:hover { background: var(--blue-100); color: var(--blue-800); }

/* letter editor: tabs + fix panel (t17 port) */
.letter-tabs { display: flex; gap: 6px; margin-left: 14px; }
.letter-tab {
  border: 1px solid var(--gray-200); background: white; border-radius: 8px;
  padding: 4px 12px; font-size: 13px; cursor: pointer; color: var(--gray-600);
}
.letter-tab.on { background: var(--blue-100); color: var(--blue-800); border-color: var(--blue-100); font-weight: 600; }
.fixpanel {
  margin: 4px 6px 12px 16px; padding: 14px 16px;
  background: #fff; border: 1px solid var(--gray-200);
  border-left: 3px solid var(--gray-300, #d1d5db); border-radius: 0 10px 10px 0;
  box-shadow: 0 1px 2px rgba(16, 24, 40, .04);
}
.fixpanel.red { border-left-color: #dc2626; }
.fixpanel.yellow { border-left-color: #f59e0b; }
.fixlabel {
  font-size: 12px; font-weight: 600; color: var(--gray-600); margin-bottom: 6px;
}
.fixinput {
  display: block; width: 100%; box-sizing: border-box; font-size: 13.5px;
  padding: 8px 10px; margin-bottom: 8px;
}
.fixactions { display: flex; justify-content: flex-end; }
.fixbtn { white-space: nowrap; }
.fixor {
  display: flex; align-items: center; gap: 10px; margin: 12px 0;
  color: var(--gray-400, #9ca3af); font-size: 11.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em;
}
.fixor::before, .fixor::after {
  content: ""; flex: 1; border-top: 1px solid var(--gray-100);
}
/* inline laterality side-picker (issue #282): the left/right/bilateral buttons
   that fill the missing side straight from the To-Review row / letter mark,
   writing through the real edit_requested path. Lives in a .fixpanel shell. */
.lat-picker-btns { display: flex; flex-wrap: wrap; gap: 8px; }
.lat-btn { flex: 1; min-width: 84px; text-align: center; }
@media (max-width: 980px) {
  .letters-body { flex-direction: column; }
  .flag-rail { flex: none; border-right: 1px solid var(--gray-200); border-radius: 8px 8px 0 0; max-height: 300px; }
  .flag-rail + .letter-frame { border-radius: 0 0 8px 8px; }
}

/* requested-procedures editor (issue #128) — reuses the fixinput look */
.proc-lines { display: flex; flex-direction: column; gap: 10px; }
.proc-row {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px;
  padding: 10px; border: 1px solid var(--gray-200); border-radius: 8px;
  background: var(--gray-50, #f9fafb);
}
.proc-row-ro { padding: 6px 2px; border: none; background: none; }
.proc-field { display: flex; flex-direction: column; gap: 4px; }
.proc-field .proc-in { margin-bottom: 0; }
.proc-flabel {
  font-size: 11px; font-weight: 600; color: var(--gray-500);
  text-transform: uppercase; letter-spacing: .04em;
}
.proc-in.proc-in { width: auto; }
.proc-row .proc-field:nth-child(1) .proc-in { width: 140px; }
.proc-row .proc-field:nth-child(2) .proc-in { width: 130px; }
.proc-row .proc-field:nth-child(3) .proc-in { width: 130px; }
.proc-row .proc-field:nth-child(4) .proc-in { width: 90px; }
.proc-rm-wrap { margin-left: auto; }
.proc-rm { color: var(--red-600, #dc2626); border-color: var(--red-200, #fecaca); }
/* laterality suggestion (#281): a full-width hint under the line's fields, only
   when the HPI names a side the line is missing. Applying it just fills the
   dropdown — nothing is saved until the worker confirms. */
.proc-row-wrap { display: flex; flex-direction: column; gap: 8px; }
.proc-sug {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  padding: 8px 10px; border: 1px dashed var(--yellow-300, #fcd34d);
  border-radius: 8px; background: var(--yellow-50, #fffbeb);
}
.proc-sug-text { font-size: 12.5px; color: var(--gray-700, #374151); }
.proc-sug-quote { color: var(--gray-500, #6b7280); font-style: italic; }
.proc-sug-apply { margin-left: auto; }
.proc-note { margin-top: 12px; }
.proc-actions { display: flex; align-items: center; margin-top: 12px; }

/* readability pass (t17 port): gaps, plan requirements, journal */
.gapv {
  display: flex; align-items: flex-start; gap: 14px; padding: 12px 0;
  border-bottom: 1px solid var(--gray-100);
}
.gapv:last-child { border-bottom: 0; }
.gapv-main { flex: 1; min-width: 0; }
.gapv-title { font-weight: 600; font-size: 13.5px; color: var(--gray-900); }
.gapv-ctx {
  font-weight: 500; font-size: 11.5px; color: var(--gray-500, #6b7280);
  margin-left: 8px;
}
/* triage tier (issue #106): red = required (holds the case); yellow =
   "missing — not required" (a warning that never holds). */
.gap-tier {
  display: inline-block; margin-left: 8px; padding: 1px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 600; text-transform: none; vertical-align: middle;
}
.gap-tier.red    { background: var(--red-100); color: var(--red-800); }
.gap-tier.yellow { background: var(--yellow-100); color: var(--yellow-800); }
.gapv.not-required .gapv-title { color: var(--gray-700, #374151); }
.gapv-sum {
  font-size: 12.5px; color: var(--gray-600); margin-top: 3px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.gapv-why { margin-top: 5px; }
.gapv-why summary {
  cursor: pointer; font-size: 12px; font-weight: 600; color: var(--blue-600, #2563eb);
  list-style: none; display: inline-flex; align-items: center; gap: 4px;
}
.gapv-why summary::-webkit-details-marker { display: none; }
.gapv-why summary::before { content: "▸"; font-size: 10px; transition: transform .15s; }
.gapv-why[open] summary::before { transform: rotate(90deg); }
.gapv-why .gapv-note {
  margin-top: 6px; padding: 10px 12px; background: var(--gray-50, #f9fafb);
  border: 1px solid var(--gray-100); border-radius: 8px;
  font-size: 12.5px; color: var(--gray-600); line-height: 1.5;
}
.gapv-action { flex: 0 0 auto; }

.vpill {
  flex: 0 0 92px; text-align: center; font-size: 11.5px; font-weight: 700;
  border-radius: 999px; padding: 3px 0; letter-spacing: .01em; white-space: nowrap;
}
.vpill.pass { background: var(--green-100); color: var(--green-800); }
.vpill.fail { background: var(--red-100); color: var(--red-800); }
.vpill.open { background: var(--yellow-100); color: var(--yellow-800); }

.crit-row {
  display: flex; align-items: flex-start; gap: 12px; padding: 9px 0;
  border-bottom: 1px solid var(--gray-100);
}
.crit-row:last-child { border-bottom: 0; }
.crit-main { flex: 1; min-width: 0; }
.crit-title { font-weight: 600; font-size: 13.5px; color: var(--gray-900); }
.crit-row .evidence { margin-top: 2px; }
.crit-ctx { font-size: 12px; font-weight: 600; color: var(--gray-500, #6b7280); }
.back { color: var(--primary-600); text-decoration: none; font-weight: 500; font-size: 14px; }
.back:hover { color: var(--primary-700); }
/* "Next case" sits beside the Back link in the case toolbar (issue #171) —
   a light control that reads as a peer of Back, not a primary action. */
.next-case { font-size: 14px; font-weight: 500; }
.gap-row { display: flex; align-items: center; gap: 10px; padding: 10px 0; border-bottom: 1px solid var(--gray-100); }
.gap-row:last-child { border-bottom: 0; }
.gap-field { font-weight: 500; color: var(--gray-900); font-size: 14px; }
.gap-note { color: var(--gray-500); flex: 1; font-size: 13px; }
.evidence { color: var(--gray-500); font-size: 13px; }
.empty { padding: 48px 24px; text-align: center; color: var(--gray-400); }

/* ============ admin ======================================================= */
.form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; align-items: end; }
.form-grid label { font-size: 14px; color: var(--gray-700); font-weight: 500; }
.form-grid input, .form-grid select { display: block; width: 100%; margin-top: 6px; }
.perm {
  display: inline-block; background: var(--gray-100); border-radius: 6px;
  padding: 1px 8px; font-size: 11.5px; margin: 1px 2px; color: var(--gray-600); font-weight: 500;
}

/* toast */
.toast {
  position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%);
  background: var(--gray-900); color: white; border-radius: 8px; padding: 10px 18px;
  box-shadow: var(--shadow-md); z-index: 60; font-weight: 500; font-size: 14px;
}
.toast.err { background: var(--red-600); }

/* ============ decision-mode banner (B2/B9, 7/15) ========================== */
.mode-banner {
  position: sticky; top: 0; z-index: 900;
  background: #fef3c7; color: #7b5410; border-bottom: 1px solid #f0d98d;
  padding: 8px 16px; text-align: center; font-size: 14px; font-weight: 600;
}

/* ============ all-clear modal + confetti (B4, 7/15) ======================= */
.modal-overlay {
  position: fixed; inset: 0; z-index: 950;
  background: rgba(15, 23, 42, .45);
  display: flex; align-items: center; justify-content: center;
}
.modal-box {
  background: #fff; border-radius: 14px; padding: 26px 30px;
  max-width: 420px; width: calc(100% - 40px);
  box-shadow: 0 20px 50px rgba(15, 23, 42, .25); text-align: center;
}
.modal-box h2 { margin: 0 0 10px; font-size: 20px; }
.modal-box p { margin: 0 0 18px; color: var(--gray-500); }
.modal-actions { display: flex; gap: 10px; justify-content: center; }

/* correction-email draft modal — wider, left-aligned form */
.card-head .spacer { flex: 1; }
.modal-box.ce-box {
  max-width: 640px; text-align: left;
}
.ce-box h2 { text-align: left; }
.ce-box p { margin: 0 0 16px; }
.ce-label {
  display: block; font-size: 12px; font-weight: 600; color: var(--gray-500);
  text-transform: uppercase; letter-spacing: .04em; margin: 12px 0 4px;
}
.ce-select, .ce-field, .ce-body {
  width: 100%; border: 1px solid var(--gray-300); border-radius: 8px;
  padding: 9px 12px; font: inherit; font-size: 14px; background: #fff;
  color: var(--gray-800); box-sizing: border-box;
}
.ce-field[readonly] { background: var(--gray-50); color: var(--gray-600); }
.ce-body {
  min-height: 220px; resize: vertical; white-space: pre-wrap;
  background: var(--gray-50); color: var(--gray-700); line-height: 1.45;
}
.ce-box .modal-actions { justify-content: flex-end; margin-top: 18px; }
.confetti { position: fixed; inset: 0; z-index: 990; pointer-events: none; overflow: hidden; }
.confetti-piece {
  position: absolute; top: -12px; width: 9px; height: 14px; border-radius: 2px;
  animation-name: confetti-fall; animation-timing-function: ease-in;
  animation-fill-mode: forwards;
}
@keyframes confetti-fall {
  0%   { transform: translateY(-10px) rotate(0deg); opacity: 1; }
  100% { transform: translateY(105vh) rotate(660deg); opacity: .85; }
}
.staff-copy { border-color: #f0d98d; background: #fef3c7; color: #7b5410; }

/* build stamp (owner ask, 7/16): quiet, bottom of the dashboard */
.build-stamp {
  margin: 28px 0 6px; text-align: center;
  font-size: 11.5px; color: var(--gray-400, #9ca3af);
}

/* ============ worker queue (owner + Shondra, 7/16: worker-simple) ========= */
/* One horizontal control bar (7/20): tabs · search · sort · group · quick
   filters all on a single axis, wrapping only when the window is too narrow.
   A thin divider separates the tab group from the tools so the row reads as
   two calm clusters, not one long jumble. */
.wq-controls {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
  margin: 0 0 8px;
}
.wq-controls > .wq-header { padding-left: 12px; margin-left: 2px; border-left: 1px solid var(--gray-200); }
.wq-tabs { display: flex; gap: 8px; }
.wq-tab {
  border: 1px solid var(--gray-200); background: white; border-radius: 999px;
  padding: 7px 16px; font-size: 14px; font-weight: 500; color: var(--gray-600);
  cursor: pointer; white-space: nowrap;
}
.wq-countline { margin: 0 0 12px; }
.wq-tab:hover { background: var(--gray-50); }
.wq-tab.on { background: var(--primary-600); border-color: transparent; color: white; font-weight: 600; }
.wq-tab.on:hover { background: var(--primary-700); }
.wq-pill { background: var(--gray-100); color: var(--gray-600); }
.wq-incomplete {
  background: #fef3c7; color: #7b5410; border: 1px solid #f0d98d;
  border-radius: 8px; padding: 10px 14px; margin: 0 0 12px; font-size: 13.5px;
}

/* ---- queue search (worker + admin) + To-do quick filter ---- */
.qsearch-bar { display: flex; align-items: center; gap: 10px; margin: 0 0 14px; }
.qsearch-bar .spacer { flex: 1; }
/* search field with an inline magnifier glyph (data-URI, no icon dep) */
/* The search field is the row's shock absorber (issue #467): it SURRENDERS width
   first so the tail of the toolbar (Columns, Export CSV, the count) stays on
   screen instead of being pushed off the right edge. Two parts to that, and both
   are load-bearing:
     - `min-width: 0` overrides the flex `min-width: auto` default, which pins an
       item at its intrinsic content width and is the actual reason the tail was
       pushed out. Without it `flex-shrink` never gets to act.
     - the 180px floor became `min-width: 0`, because a floor is just the same
       bug with a smaller number: at 900px the row had 180px of search it was not
       allowed to give back.
   Nothing wraps and nothing moves; only this field gets narrower. The Sort
   control below is the second shock absorber, because the search field alone
   cannot cover the shortfall at 900px. Guarded by
   app/tests/test_467_toolbar_no_overflow.py, which reads these declarations. */
.qsearch-input {
  flex: 1 1 220px; min-width: 0; max-width: 340px; padding: 9px 12px 9px 36px; font-size: 14px;
  border: 1px solid var(--gray-300); border-radius: 10px; background: white
    no-repeat 12px center / 16px 16px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><circle cx='11' cy='11' r='8'/><path d='m21 21-4.3-4.3'/></svg>");
  color: var(--gray-800); transition: border-color .12s, box-shadow .12s;
}
.qsearch-input::placeholder { color: var(--gray-400); }
.qsearch-input:focus {
  outline: none; border-color: var(--primary-400);
  box-shadow: 0 0 0 3px var(--primary-100);
}
.qsearch-count { font-size: 12.5px; color: var(--gray-500); font-variant-numeric: tabular-nums; white-space: nowrap; }
.qsort { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--gray-500); white-space: nowrap; }
/* Sort is the row's SECOND shock absorber (issue #467). Letting only the search
   field shrink moved the breakpoint from ~1130px to ~1010px and no further: at a
   900px window the toolbar has 581px of column for 885px of controls, and the
   search field can return at most ~130px of that (its own padding and border
   floor it at 50px). Proven, not assumed: forcing the search field to width 0
   with no padding still overflowed 900px by 70px. Sort was then the widest item
   in the row at 217px, and `white-space: nowrap` plus the flex `min-width: auto`
   default meant it never gave a pixel back.
   The floor goes on the WRAPPER and the zero on the SELECT, and that split is the
   whole point. `min-width: 0` on the wrapper as well looks tidier and is a bug:
   the wrapper's box then shrinks below its own contents, and since its overflow
   is visible the select keeps painting at full size OUTSIDE that box, where the
   Urgent-only button (later in DOM order, also static) paints on top of it. The
   dropdown stays visible and stops being clickable, which is worse than the
   overflow it was meant to fix: measured 50% of its width hit-testable at 900px
   and 0% at 820px. So the wrapper keeps a floor big enough to hold the "Sort"
   label plus the collapsed select, and only the select inside it is allowed to
   go to zero. The select's own padding/border floors it at ~26px, so the value
   below is that sum, not a magic number.
   Still ONE row, still nothing moves; the dropdown just gets narrower and shows
   less of the sort name.
   `.qsort` is worn by TWO wrappers, not one: app.js renders `class: "qsort"` for
   Sort and `class: "qsort qproc"` for the Procedure filter on Pending AI Analysis.
   So both halves have to be spelled out per control, and the floors differ because
   the LABELS differ: "Sort" is 24.6px of text, "Procedure" is 60.4px. A single
   shared 72px floor squeezes the Procedure wrapper below its own contents and
   walks straight back into the trap described above, with `btn qurgent-btn`
   covering the dropdown (measured 73% of its width hit-testable at 1400px, 17% at
   900px). Each floor is label + 6px gap + the collapsed select's 26px of padding
   and border, rounded up: 24.6+6+26 -> 72, 60.4+6+26 -> 96. `min-content` is not
   an option for either; it still counts the select's full intrinsic width and
   leaves the wrapper at 217px, i.e. it silently does nothing. */
.qsort { min-width: 72px; }
.qsort.qproc { min-width: 96px; }
.qsort-select, .qproc-select { min-width: 0; }
.qsort-select, .qproc-select, .qcol-btn {
  padding: 8px 12px; font: inherit; font-size: 13px; color: var(--gray-700);
  border: 1px solid var(--gray-300); border-radius: 8px; background: white; cursor: pointer;
}
.qsort-select:focus, .qproc-select:focus { outline: none; border-color: var(--primary-400); box-shadow: 0 0 0 3px var(--primary-100); }
/* Procedure-type filter (issue #388), Pending AI Analysis only. With a family
   selected the control reads as ACTIVE (dark border, bold), the same way the
   Urgent / AI-Passed chips signal a live filter. It matters more here, because
   this filter also scopes what the run button will run. */
.qproc-select.on { border-color: var(--gray-800); color: var(--gray-900); font-weight: 600; }
.qcol-btn:hover { background: var(--gray-50); }
/* sortable header: pointer + hover + active color, arrow appended in text */
th.qsortable { cursor: pointer; user-select: none; white-space: nowrap; }
th.qsortable:hover { color: var(--gray-700); background: var(--gray-100); }
th.qsortable.on { color: var(--primary-700); }
/* columns show/hide popover.
   The BUTTON is the positioning context, not the toolbar (issue #458). With the
   toolbar as the context and no `top`, an absolutely positioned flex child takes
   its static position from `align-items: center`, so the popover came out
   vertically CENTERED on the 37px toolbar: a 12-row list is 427px tall, half of
   it laid out above the toolbar, off the top of the screen at every window
   height. Case and Patient were unreachable at every height, and Insurance was
   the next one up, which is what the report named; Insurance is only switchable
   from here, so the column could not be turned on at all. Measured on the real
   app in headless Chrome before the fix: menu top -55px, left 280px while the
   button sat at 1113px, so it was not anchored horizontally either.
   So it hangs off .qcol-wrap below the button, with an explicit `top` and `right`
   rather than any static position, and a height capped to the room actually
   below the button so a short window scrolls the list instead of clipping it.
   The cap is only as good as the measurement behind it, so app.js also CLOSES
   this menu on scroll and on resize: both invalidate the geometry it was placed
   with, and a menu that outlives its measurement re-creates the bug (it travels
   with the button until it paints over the sticky .pagebar, which is z-index 40
   against this 60). */
.qcol-wrap { position: relative; display: inline-flex; }
.qcol-menu {
  position: absolute; z-index: 60; top: calc(100% + 4px); right: 0; left: auto;
  background: white;
  border: 1px solid var(--gray-200); border-radius: 10px; box-shadow: var(--shadow-md);
  padding: 8px; min-width: 180px; display: flex; flex-direction: column; gap: 2px;
  /* --qcol-space is set by app.js at open time (the distance from the button to
     the bottom of the viewport, less a 16px margin); the 60vh fallback keeps this
     sane on its own if the script half ever stops running.
     NO MINIMUM HERE, on purpose, and two tries at one were wrong before this:
     any floor is a promise the viewport cannot keep. A 120px floor overflowed the
     bottom edge by 39px at a 260px window height, and a 72px floor still
     overflowed by 31px at 220px, both putting the tail entries back out of reach,
     which is #458 from the other end. Letting the box be honestly small keeps
     every row reachable through its own scroller instead. Measured in Chrome at
     400/340/300/260/220/200px: all 12 entries reachable at every one. */
  max-height: min(60vh, var(--qcol-space, 60vh));
  overflow-y: auto; overscroll-behavior: contain;
}
.qcol-item {
  display: flex; align-items: center; gap: 8px; padding: 6px 8px;
  border-radius: 6px; font-size: 13.5px; color: var(--gray-700); cursor: pointer;
  /* Rows keep their own height inside a scrolling column box: as flex items they
     would otherwise shrink to fit the cap, squashing 12 rows into the space for
     7 instead of scrolling. */
  flex: 0 0 auto;
}
.qcol-item:hover { background: var(--gray-50); }
/* inline in the single control bar now — no own row / bottom margin */
.wq-todo-filter { display: flex; gap: 6px; align-items: center; padding-left: 12px; border-left: 1px solid var(--gray-200); }
.wq-todo-filter:empty { display: none; }
.wq-fchip {
  border: 1px solid var(--gray-200); background: white; border-radius: 999px;
  padding: 5px 14px; font-size: 12.5px; font-weight: 500; color: var(--gray-600);
  cursor: pointer;
}
.wq-fchip:hover { background: var(--gray-50); }
.wq-fchip.on { background: var(--gray-800); border-color: transparent; color: white; font-weight: 600; }

/* ============ w2: claim-until-done + supervisor queue enrichments ========= */
/* The worker queue is a TABLE (shares the admin table styles: table/td/th/
   .rowlink). These rules cover the worker-specific cells only. */
.wq-ref {
  font-size: 12.5px; font-weight: 600; color: var(--gray-500);
  font-variant-numeric: tabular-nums; cursor: pointer;
  border-bottom: 1px dashed var(--gray-300);
}
.wq-ref:hover { color: var(--primary-700); border-bottom-color: var(--primary-400); }
/* status cell: pill + any difficulty/pending badges sit inline */
td .wq-badge {
  display: inline-block; margin-left: 6px; vertical-align: middle;
  font-size: 11px; font-weight: 600; border-radius: 999px; padding: 2px 8px;
  letter-spacing: .01em;
}
.wq-badge.clinical { background: var(--blue-100); color: var(--blue-800); }
.wq-badge.admin    { background: var(--gray-100); color: var(--gray-600); }
.wq-badge.pending  { background: var(--yellow-100); color: var(--yellow-800); }
/* provider outreach has gone quiet past the chase threshold (issue #119) */
.wq-badge.chase    { background: var(--red-100); color: var(--red-800); }
.wq-outreach { margin-left: 6px; font-size: 12px; white-space: nowrap; }
.wq-claim-cell { text-align: right; white-space: nowrap; }
.wq-claim { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.wq-claimant { font-size: 12.5px; font-weight: 600; color: var(--gray-700); }
.wq-held { font-size: 12px; color: var(--primary-700); font-weight: 500; }
.wq-prog { font-size: 12px; color: var(--gray-400); }
.wq-claim-btn, .wq-release {
  border: 1px solid var(--gray-200); background: white; border-radius: 8px;
  padding: 5px 14px; font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.wq-claim-btn { color: var(--primary-700); border-color: var(--primary-200); }
.wq-claim-btn:hover { background: var(--primary-50); }
.wq-release { color: var(--red-800); border-color: #f0c4c4; }
.wq-release:hover { background: var(--red-100); }
.wq-header { display: inline-flex; align-items: center; gap: 12px; }
.wq-total { font-size: 13px; font-weight: 600; color: var(--gray-600); }
.wq-group {
  border: 1px solid var(--gray-200); background: white; border-radius: 999px;
  padding: 5px 14px; font-size: 12.5px; font-weight: 600; color: var(--gray-600);
  cursor: pointer;
}
.wq-group.on { background: var(--primary-600); border-color: transparent; color: white; }
/* group-by-insurance section row inside the table */
.wq-group-row td {
  padding: 8px 24px; font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--gray-500); background: var(--gray-50);
}
.wq-group-ct {
  margin-left: 8px; font-weight: 600; color: var(--gray-400); background: white;
  border-radius: 999px; padding: 0 8px; font-size: 11.5px;
}

/* claim badge in the case toolbar */
.claim-badge {
  font-size: 12.5px; font-weight: 600; color: var(--blue-800);
  background: var(--blue-100); border-radius: 999px; padding: 4px 12px;
}
.claim-held { font-weight: 500; color: var(--primary-700); }

/* team notes / feedback card */
.fb-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.fb-item {
  background: var(--gray-50); border: 1px solid var(--gray-100);
  border-radius: 10px; padding: 10px 14px;
}
.fb-meta { font-size: 12px; color: var(--gray-400); margin-bottom: 3px; }
.fb-text { font-size: 14px; color: var(--gray-800); white-space: pre-wrap; line-height: 1.5; }

/* Activity history (issue #170) — the per-case who-did-what timeline. */
.activity-list { display: flex; flex-direction: column; gap: 8px; }
.activity-item {
  border-left: 2px solid var(--gray-200); padding: 4px 0 4px 12px;
}
.activity-item.note { border-left-color: var(--primary-700); }
.activity-meta { font-size: 12px; color: var(--gray-400); display: flex; gap: 8px; }
.activity-who { font-weight: 600; color: var(--gray-700); }
.activity-text { font-size: 14px; color: var(--gray-800); line-height: 1.45; }

/* Owner + stale flag (issue #170) — queue owner column + Claimed cases view. */
.owner-cell { display: inline-flex; flex-direction: column; gap: 1px; }
.owner-who { font-size: 12.5px; font-weight: 600; color: var(--gray-700); }
.owner-held { font-size: 12px; color: var(--gray-400); }
.owner-stale {
  display: inline-block; margin-left: 6px; font-size: 10.5px; font-weight: 700;
  letter-spacing: 0.04em; color: var(--red-800); background: var(--red-100);
  border-radius: 6px; padding: 1px 6px; vertical-align: middle;
}
.stale-row { background: #fff7f7; }
.fb-hint { font-size: 12.5px; color: var(--gray-400); margin: 0 0 8px; }
.fb-input {
  width: 100%; min-height: 88px; border: 1px solid var(--gray-200);
  border-radius: 8px; padding: 10px 12px;
  /* Spell out the app font: a bare `font: inherit` on a <textarea> loses to the
     UA monospace default in some engines, which made this box render as a
     cramped monospace field out of line with the rest of the UI (#115). */
  font-family: Inter, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
               Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px; line-height: 1.5; color: var(--gray-900);
  resize: vertical; box-sizing: border-box; background: white;
  transition: border-color .15s;
}
.fb-input:hover { border-color: var(--gray-300); }
.fb-input::placeholder { color: var(--gray-400); }
.fb-input:focus { outline: 2px solid var(--primary-500); outline-offset: 0; border-color: transparent; }
.fb-actions { margin-top: 10px; display: flex; justify-content: flex-end; }

/* agent metrics table */
.metrics-summary { font-size: 14px; color: var(--gray-600); margin: 0 0 14px; }

/* ============ intake upload -> stage -> run (admin-only) ================== */
.up-drop {
  border: 2px dashed var(--gray-300); border-radius: 12px;
  padding: 32px 24px; text-align: center; cursor: pointer;
  background: var(--gray-50); transition: border-color .15s, background-color .15s;
}
.up-drop:hover { border-color: var(--primary-500); background: var(--primary-50); }
.up-drop.over { border-color: var(--primary-600); background: var(--primary-50); }
.up-drop-title { font-size: 15px; font-weight: 600; color: var(--gray-700); }
.up-file-input { display: none; }
.up-file-name {
  margin-top: 12px; display: inline-block; padding: 4px 12px; border-radius: 999px;
  background: var(--primary-100); color: var(--primary-700); font-size: 13px; font-weight: 600;
}
.up-note {
  margin-top: 12px; font-size: 13px; color: var(--gray-500);
}
.up-note.warn {
  background: #fef3c7; color: #7b5410; border: 1px solid #f0d98d;
  border-radius: 8px; padding: 10px 14px;
}
/* #460: the upload error report, a requested row with no vein or no leg. Louder
   than .warn on purpose: Dr. Mike's rule is that this should not happen and gets
   fixed in the export before upload, so it must not read as an accepted figure.
   Same red as .up-conform.bad, which is already the screen's "this blocks you". */
.up-note.bad {
  background: var(--red-100); color: var(--red-800); border: 1px solid #f5b5b5;
  border-radius: 8px; padding: 10px 14px; font-weight: 500;
}
.up-conform {
  border-radius: 8px; padding: 10px 14px; font-size: 13.5px; font-weight: 500;
  margin-bottom: 12px;
}
.up-conform.ok { background: var(--green-100); color: var(--green-800); }
.up-conform.bad { background: var(--red-100); color: var(--red-800); }
.up-cols { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 10px; }
.up-cols-label { font-size: 12.5px; color: var(--gray-500); font-weight: 600; }
.up-col-chip {
  font-size: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--gray-100); color: var(--gray-700);
  border-radius: 6px; padding: 2px 8px;
}
.up-counts { display: flex; flex-wrap: wrap; gap: 12px; margin: 16px 0 8px; }
.up-tile {
  flex: 1 1 90px; min-width: 90px; border-radius: 10px; padding: 14px 12px;
  text-align: center; border: 1px solid var(--gray-100);
}
.up-tile-n { font-size: 26px; font-weight: 800; line-height: 1; }
.up-tile-l {
  margin-top: 6px; font-size: 11px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--gray-500); font-weight: 600;
}
.up-tile.new  { background: var(--green-100); }
.up-tile.new  .up-tile-n { color: var(--green-800); }
.up-tile.update { background: var(--blue-100); }
.up-tile.update .up-tile-n { color: var(--blue-800); }
.up-tile.amb  { background: var(--yellow-100); }
.up-tile.amb  .up-tile-n { color: var(--yellow-800); }
.up-tile.rej  { background: var(--red-100); }
.up-tile.rej  .up-tile-n { color: var(--red-800); }
.up-tile.restage  { background: var(--purple-100); }
.up-tile.restage  .up-tile-n { color: var(--purple-800); }
/* source-reconcile / refresh (#324): a distinct teal so an in-place source
   backfill of a worked case reads apart from update (blue) and restage
   (purple) — same teal as the ready_for_pa chip it most often refreshes. */
.up-tile.reconcile  { background: #ccfbf1; }
.up-tile.reconcile  .up-tile-n { color: #115e59; }
.up-tile.conflict { background: var(--yellow-100); }
.up-tile.conflict .up-tile-n { color: var(--yellow-800); }
.up-tile.dup { background: var(--gray-100); }
.up-tile.dup .up-tile-n { color: var(--gray-600); }
.up-tile.blocked { background: var(--red-100); }
.up-tile.blocked .up-tile-n { color: var(--red-800); }

/* #586 per-bucket table: what this file does to each bucket, before Confirm.
   Colour carries the four answers and nothing else, because that distinction is
   the one the owner's rules turn on and the one an operator misreads: green for
   absorbed (the data lands and the case re-runs), teal for in place (it lands, the
   case does not move, the same teal .up-tile.reconcile already uses for exactly
   that outcome), amber for not absorbed, and plain grey for nothing to do. Grey
   deliberately: the no-op is the biggest answer on an ordinary day and it needs no
   attention at all, so giving it a colour would make a quiet file look busy. */
.up-bwrap { margin: 18px 0 8px; }
.up-btable { overflow-x: auto; }
.up-btable-key { display: flex; flex-wrap: wrap; gap: 8px; margin: 6px 0 10px; }
.up-bkey {
  font-size: 12px; font-weight: 600; border-radius: 6px; padding: 2px 8px;
  white-space: nowrap;
}
.up-bkey.absorbed { background: var(--green-100); color: var(--green-800); }
.up-bkey.in_place { background: #ccfbf1; color: #115e59; }
.up-bkey.refused  { background: var(--yellow-100); color: var(--yellow-800); }
.up-bkey.noop     { background: var(--gray-100); color: var(--gray-600); }
.up-btable-t { width: 100%; border-collapse: collapse; font-size: 13px; }
.up-btable-t th, .up-btable-t td {
  padding: 6px 8px; text-align: left; vertical-align: top;
  border-bottom: 1px solid var(--gray-100);
}
.up-btable-t thead th {
  font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--gray-500); font-weight: 700;
}
/* Numbers right-aligned so a column of counts can be scanned down and compared,
   which is most of what this table is for. The first, answer and destination
   columns stay left. */
.up-btable-t td:nth-child(n+2):not(.up-bans):not(.up-bdest),
.up-btable-t th:nth-child(n+2):not(.up-bans):not(.up-bdest) { text-align: right; }
.up-brow-sec th {
  background: var(--gray-100); color: var(--gray-700); font-size: 11.5px;
  text-transform: uppercase; letter-spacing: .05em; font-weight: 700;
  text-align: left;
}
/* A bucket with nothing arriving is dimmed, never hidden: an empty bucket has to be
   seen to be empty (same requirement as the sidebar's showZero), and hiding the
   quiet rows would make the reconciliation unverifiable by eye. */
.up-brow.quiet td { color: var(--gray-400); }
.up-bname { font-weight: 600; }
.up-bclaim {
  display: block; margin-top: 2px; font-size: 11px; font-weight: 600;
  color: var(--yellow-800);
}
/* The folded-statuses note under a bucket name. Normal weight against the bold row
   label, because it qualifies the name rather than being part of it. */
.up-bfold { font-weight: 400; color: var(--gray-500); margin-top: 2px; }
/* #595: the arithmetic behind one row's change, on the row it belongs to. Same
   treatment as the folded note, normal weight under the bold label, because it too
   qualifies the row rather than being part of its name. */
.up-bmath { font-weight: 400; color: var(--gray-500); margin-top: 2px; }
.up-bans { display: table-cell; }
.up-bans .up-bkey { margin-right: 4px; }
.up-bans .small, .up-bdest { color: var(--gray-500); font-size: 12px; }
.up-barr { margin-top: 3px; }
.up-bchg { font-weight: 700; }
.up-btable-t tfoot th {
  border-top: 2px solid var(--gray-300); font-weight: 800; font-size: 13px;
  border-bottom: none;
}
.up-bclaimed {
  margin: 8px 0; font-size: 13px; font-weight: 500; border-radius: 8px;
  padding: 8px 12px; background: var(--yellow-100); color: var(--yellow-800);
}
.up-bcount { border-collapse: collapse; font-size: 13.5px; margin: 4px 0 10px; }
.up-bcount td, .up-bcount th { padding: 4px 16px 4px 0; text-align: left; }
.up-bcount td:last-child, .up-bcount th:last-child {
  text-align: right; font-weight: 600; font-variant-numeric: tabular-nums;
}
.up-bcount-total th { border-top: 2px solid var(--gray-300); font-weight: 800; }
/* #595: the headings that break the block up, and the break has to be visible,
   because a list that looks addable has to BE addable. Two of them: one over the
   breakdown of "cases we hold now" (three lines that sum to that figure, not to the
   total), and one over the file's LINE counts, which are a different unit entirely and
   used to sit in the case list being called cases. */
.up-bcount-sub th {
  padding-top: 12px; font-size: 11px; text-transform: uppercase;
  letter-spacing: .04em; color: var(--gray-500); font-weight: 700;
}
/* The rows under a sub-heading, indented so they read as belonging to it rather than
   continuing the sum above. Only the label cell: the figures stay in their column so
   they are still comparable down the block. */
.up-bcount-part td:first-child { padding-left: 14px; }
/* The which-buckets note under a patient-count label: same normal-weight, grey
   treatment as the row notes in the table, so a qualifier never reads as a count. */
.up-bcnote { color: var(--gray-500); font-weight: 400; }
.up-brec {
  border-radius: 8px; padding: 8px 12px; font-size: 13px; font-weight: 500;
}
.up-brec.ok { background: var(--green-100); color: var(--green-800); }
.up-brec.bad { background: var(--red-100); color: var(--red-800); font-weight: 700; }

/* human-readable bucket summary (feature 1) */
.up-summary-lines { margin: 8px 0 4px; padding-left: 18px; }
.up-summary-lines li { font-size: 13.5px; color: var(--gray-700); margin: 2px 0; }

/* headline unmapped-column alert (feature 5) */
.up-unmapped-alert { font-weight: 600; }

/* field-level change preview (feature 2) — PHI-redacted old→new lines */
.up-diff { margin-top: 8px; }
.up-diff-case { padding: 6px 0; border-bottom: 1px solid var(--gray-100); }
.up-diff-case-h { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.up-diff-case-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px; color: var(--gray-600);
}
.up-diff-row {
  display: flex; align-items: baseline; gap: 8px; padding: 2px 0 2px 14px;
  font-size: 13px;
}
.up-diff-field { color: var(--gray-600); font-weight: 600; }
.up-diff-from { color: var(--gray-500); }
.up-diff-arrow { color: var(--gray-400); }
.up-diff-to { color: var(--blue-800); font-weight: 600; }
.up-diff-row.added .up-diff-to { color: var(--green-800); }
.up-diff-row.removed .up-diff-to { color: var(--red-800); }

/* per-bucket case_id drill-down (feature 3) */
.up-drilldowns { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
.up-drill-list { display: flex; flex-wrap: wrap; gap: 6px; padding: 6px 0; }
.up-drill-id {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  background: var(--gray-100); color: var(--gray-700);
  border-radius: 6px; padding: 2px 8px;
}
.up-sub-h {
  font-size: 12px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--gray-500); font-weight: 600; margin: 16px 0 8px;
}
.up-gaps { margin-top: 8px; }
.up-gap-row {
  display: flex; align-items: center; gap: 10px; padding: 6px 0;
  border-bottom: 1px solid var(--gray-100);
}
.up-gap-field { flex: 1; min-width: 0; font-size: 13.5px; color: var(--gray-700); }
.up-gap-count { font-size: 13px; font-weight: 700; color: var(--gray-600); }
.up-sev {
  font-size: 11px; font-weight: 600; border-radius: 6px; padding: 2px 8px;
  text-transform: uppercase; letter-spacing: .03em;
}
.up-sev.required { background: var(--red-100); color: var(--red-800); }
.up-sev.conditional { background: var(--yellow-100); color: var(--yellow-800); }
.up-sev.exception, .up-sev.not_required { background: var(--gray-100); color: var(--gray-600); }
.up-rejected { margin-top: 8px; }
.up-rej-row {
  display: flex; align-items: baseline; gap: 12px; padding: 6px 0;
  border-bottom: 1px solid var(--gray-100); font-size: 13px;
}
.up-rej-num { flex: 0 0 auto; font-weight: 700; color: var(--gray-700); min-width: 96px; }
.up-rej-reasons { color: var(--red-800); }
.up-actions { display: flex; align-items: center; gap: 12px; margin-top: 18px; }
.up-run-controls { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
.up-limit-label { display: flex; align-items: center; gap: 8px; color: var(--gray-600); }
.up-limit { width: 80px; }
.up-run-progress:empty { display: none; }
/* the upload processing banner's mount — collapses when nothing is in flight so
   the drop zone keeps its own bottom spacing (#269) */
.up-stage-progress:empty { display: none; }
.up-bar {
  height: 12px; border-radius: 999px; background: var(--gray-100);
  overflow: hidden; margin: 8px 0;
}
.up-bar-fill {
  height: 100%; background: var(--primary-500); border-radius: 999px;
  transition: width .3s ease;
}
.up-bar-fill.done { background: var(--green-500); }
.up-run-line {
  display: flex; align-items: center; gap: 10px; font-size: 13.5px;
  color: var(--gray-600); flex-wrap: wrap;
}
.up-run-sep { color: var(--gray-300); }
.up-run-fail { color: var(--red-800); font-weight: 600; }
.up-run-cur { color: var(--gray-500); }
.up-done {
  margin-top: 10px; background: var(--green-100); color: var(--green-800);
  border-radius: 8px; padding: 10px 14px; font-size: 13.5px; font-weight: 600;
}
.up-run-err {
  background: var(--red-100); color: var(--red-800); border-radius: 8px;
  padding: 10px 14px; font-size: 13px; margin-bottom: 8px;
}
.up-staged-line { font-size: 13.5px; font-weight: 600; color: var(--gray-600); margin-top: 4px; }

/* processing state: disabled drop zone + spinner banner (field report #4) */
.up-drop.busy {
  cursor: progress; opacity: .6; border-color: var(--gray-300);
  background: var(--gray-100); pointer-events: none;
}
.up-processing {
  display: flex; align-items: center; gap: 10px; margin-top: 12px;
  background: var(--blue-100); color: var(--blue-800); border-radius: 8px;
  padding: 10px 14px; font-size: 13.5px; font-weight: 600;
}
.up-spin {
  display: inline-block; width: 14px; height: 14px; flex: 0 0 14px;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 999px; animation: up-spin .6s linear infinite;
}
@keyframes up-spin { to { transform: rotate(360deg); } }

/* one-line summary panel (field report #5 — a summary, not a row dump) */
.up-summary {
  font-size: 14px; font-weight: 600; color: var(--gray-800);
  background: var(--gray-50); border: 1px solid var(--gray-100);
  border-radius: 8px; padding: 10px 14px; margin-bottom: 4px;
}
.up-file-tag {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--gray-500);
}

/* derived-orders summary block */
.up-derived { margin-top: 12px; }
.up-derived-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 5px 0; border-bottom: 1px solid var(--gray-100); font-size: 13px;
}
.up-derived-l { color: var(--gray-600); }
.up-derived-v { font-weight: 700; color: var(--gray-800); }

/* per-row detail expander + download (field report #5) */
.up-rowtools { display: flex; flex-wrap: wrap; align-items: center; gap: 16px; margin-top: 14px; }
.up-rowdetail { flex: 1 1 auto; }
.up-rowdetail > summary {
  cursor: pointer; font-size: 13px; font-weight: 600; color: var(--primary-700);
  user-select: none;
}
.up-rowdetail-list { margin-top: 8px; }
.up-rej-out {
  flex: 0 0 auto; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .03em; border-radius: 6px; padding: 1px 8px;
  background: var(--gray-100); color: var(--gray-600);
}
.up-rej-out.rejected { background: var(--red-100); color: var(--red-800); }
.up-rej-out.conflict { background: var(--yellow-100); color: var(--yellow-800); }
.up-rej-out.ambiguous { background: var(--yellow-100); color: var(--yellow-800); }
.up-rej-out.restage { background: var(--purple-100); color: var(--purple-800); }
.up-rej-out.refresh { background: #ccfbf1; color: #115e59; }
.up-download {
  border: 1px solid var(--gray-300); background: var(--surface, #fff);
  color: var(--gray-700); border-radius: 8px; padding: 6px 14px;
  font: inherit; font-size: 13px; font-weight: 600; cursor: pointer;
}
.up-download:hover { border-color: var(--primary-500); color: var(--primary-700); }

/* unmissable stage-success panel + staged-queue link (field report #2b/#3) */
.up-success {
  background: var(--green-100); border: 1px solid var(--green-500);
  border-radius: 10px; padding: 14px 16px;
}
.up-success-h { font-size: 17px; font-weight: 800; color: var(--green-800); }
.up-link {
  border: 0; background: transparent; color: var(--primary-700);
  font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; padding: 0;
}
.up-link:hover { color: var(--primary-800); text-decoration: underline; }
.up-link.up-undo { color: var(--red-600); margin-left: auto; }
.up-link.up-undo:hover { color: var(--red-800); }

/* past uploads (issue #384 F2): one row per batch, its refusal verdict, and the
   stored report opened on demand underneath it. The verdict is red ONLY when
   rows were actually refused, so a scan down the list finds the batches that
   need chasing without reading every line. */
.up-history-row {
  padding: 10px 0; border-bottom: 1px solid var(--gray-100);
}
.up-history-row:last-child { border-bottom: 0; }
.up-history-meta { font-size: 13px; color: var(--gray-500); }
.up-history-outcome {
  font-size: 14px; font-weight: 600; color: var(--gray-700); margin-top: 2px;
}
.up-history-outcome.refused { color: var(--red-800); }
.up-history-report:not(:empty) { margin-top: 8px; }

/* ---------- trash bin (admin-only soft-delete UI) ---------- */
.tr-delete { color: var(--red-600); border-color: var(--red-100); }
.tr-delete:hover { background: var(--red-100); border-color: var(--red-600); }
.tr-empty { padding: 32px 24px; text-align: center; color: var(--gray-400); }
.tr-table td, .tr-table th { vertical-align: middle; }
.tr-restore { color: var(--primary-700); }
.tr-restore:hover { background: var(--primary-50); border-color: var(--primary-500); }
.tr-purge { color: var(--red-600); }
.tr-purge:hover { background: var(--red-100); border-color: var(--red-600); }
.tr-clear-card { border-color: var(--red-100); }
.tr-clear-card .card-head { border-bottom-color: var(--red-100); }
.tr-danger {
  color: var(--red-800); background: var(--red-100);
  border-radius: 6px; padding: 2px 8px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .04em; font-size: 11px;
}
.tr-clear-explain {
  margin: 0 0 14px; font-size: 13.5px; color: var(--gray-600); line-height: 1.5;
}
.tr-clear-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.tr-clear-input {
  flex: 1; min-width: 240px; font-family: inherit; letter-spacing: .02em;
}
.tr-clear-btn { background: var(--red-600); border-color: transparent; color: white; }
.tr-clear-btn:hover:not(:disabled) { background: var(--red-800); }
.tr-clear-btn:disabled { opacity: .5; cursor: not-allowed; }

/* Duplicate cases audit (issue #450). The layout carries the safety rule: a
   group with a clear keeper reads as actionable (the KEEP row tinted green, a
   tick box for the batch), while a TIE is visually set apart in amber with NO
   tick box and no preselected row at all — the app must never look like it has
   an opinion about which of two equally-worked copies to delete. */
.dup-explain {
  margin: 0 0 12px; font-size: 13.5px; color: var(--gray-600); line-height: 1.5;
}
.dup-bulkrow {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin: 0 0 16px; padding: 10px 12px;
  background: var(--gray-100); border-radius: 8px;
}
.dup-all-l {
  display: flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 600; color: var(--gray-700);
}
.dup-bulk:disabled { opacity: .5; cursor: not-allowed; }
.dup-group {
  border: 1px solid var(--gray-100); border-radius: 10px;
  padding: 12px 14px; margin: 0 0 12px;
}
.dup-group.tie { border-color: var(--yellow-800); background: var(--yellow-100); }
.dup-g-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 8px;
}
.dup-g-head .spacer { flex: 1; }
.dup-reason { color: var(--green-800); font-weight: 600; }
.dup-tie-tag { color: var(--yellow-800); font-weight: 600; }
.dup-tie-h { color: var(--yellow-800); }
.dup-table { margin: 0; }
.dup-table td, .dup-table th { font-size: 13px; }
.dup-keeper { background: var(--green-100); }
.dup-keep-tag {
  color: var(--green-800); background: white;
  border-radius: 6px; padding: 2px 8px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; font-size: 11px;
}
.dup-prot { margin-left: 6px; color: var(--gray-600); }
.dup-differs { margin-top: 8px; color: var(--gray-600); }
.dup-keepbtn { color: var(--primary-700); }
.dup-keepbtn:hover { background: var(--primary-50); border-color: var(--primary-500); }

/* Data quality audit (issue #341): admin report of how often staff override a
   check or hand-enter data the doctor's note didn't carry. Layout goals — the
   counting-basis note must read as prose beside the headline (it is what stops
   the number being misread), the excluded category must look visibly excluded,
   and the group tables must expand in place for the PHI drill-down. */
.dq-window { margin-bottom: 10px; }
.dq-basis { margin-bottom: 14px; background: var(--gray-50); }
.dq-basis .card-body { padding: 10px 14px; }
.dq-basis p { margin: 0 0 6px; }
.dq-basis p:last-child { margin-bottom: 0; }
.dq-cats { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 14px; }
.dq-cat { flex: 1 1 180px; margin: 0; }
.dq-cat .card-body { padding: 12px 14px; }
.dq-cat-n { font-size: 26px; font-weight: 600; color: var(--gray-900); line-height: 1.1; }
/* The deliberately-excluded category: dashed + muted so it never reads as part
   of the headline total sitting right above it. */
.dq-cat-out { border-style: dashed; background: var(--gray-50); }
.dq-cat-out .dq-cat-n { color: var(--gray-500); }
.dq-side { margin-bottom: 14px; }
.dq-side .card-body { padding: 10px 14px; }
.dq-side-gap { margin-top: 10px; }
.dq-table { width: 100%; }
.dq-caret-cell { width: 24px; }
.dq-caret { color: var(--gray-400); font-size: 11px; }
/* Rows are TALL now that each field names where it lives in the letter (#351), so
   the figures align to the TOP — centred, they would float halfway down the
   template context and stop reading as a row. */
.dq-grouprow td { vertical-align: top; }
/* Where this field sits in the letter. Deliberately quiet and monospaced: it is
   reference text under the field name, not a second column of data. */
.dq-loc {
  margin-top: 4px; font-size: 11px; line-height: 1.5; color: var(--gray-500);
  cursor: text;
}
.dq-loc-head { color: var(--gray-600); }
/* "Why there is no location" (#357). Italic and unmonospaced so it never reads as
   a quotation from the letter the way a real locator line does. */
.dq-loc-none { font-style: italic; color: var(--gray-400); }
.dq-loc-line {
  display: flex; flex-wrap: wrap; gap: 5px; align-items: baseline;
  font-family: var(--mono, ui-monospace, SFMono-Regular, Menlo, monospace);
}
.dq-loc-line + .dq-loc-head { margin-top: 5px; }
/* The neighbouring letter text is context, so it sits BEHIND the field itself. */
.dq-loc-adj { color: var(--gray-400); }
.dq-loc-field { color: var(--gray-800); font-weight: 600; }
.dq-drill > td { background: var(--gray-50); padding: 10px 14px; }
.dq-cases { display: flex; flex-direction: column; gap: 2px; }
/* One drill-down row: case id, patient, what happened, item, who, status, when.
   Identifiers only — the server never sends the edited values. */
.dq-case {
  display: grid; gap: 12px; align-items: center; padding: 6px 8px;
  grid-template-columns:
    minmax(140px, 1.2fr) minmax(120px, 1fr) 1.1fr 1.2fr 1fr 0.9fr auto;
  border-radius: 6px; cursor: pointer;
}
.dq-case:hover { background: var(--gray-100); }
.dq-case-id { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.dq-more { margin-top: 8px; }
/* Sub-heading inside the weekly card, which holds two tables (workload + cohort). */
.dq-sub {
  font-size: 13px; font-weight: 600; color: var(--gray-700);
  margin: 18px 0 6px;
}
.dq-sub:first-child { margin-top: 0; }
.dq-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 8px 4px; border-bottom: 1px solid var(--gray-100);
}
.dq-row:last-child { border-bottom: none; }
.dq-adjacent { margin-top: 16px; }
.dq-export-btns { display: flex; flex-wrap: wrap; gap: 10px; }
@media (max-width: 900px) {
  .dq-case { grid-template-columns: 1fr 1fr; }
}
