/* Day theme. Every color on the page comes from these tokens; the night
   theme at the bottom of this file redefines them under
   :root[data-theme="dark"] (set by /theme.js before first paint).
   "-fill" tokens are backgrounds that carry white text (buttons, banners,
   toasts) — in the dark theme they stay deep while the plain tokens, used
   for text and outlines, become light enough to read on dark surfaces. */
:root {
  color-scheme: light;
  --bg: #f4f6f9;
  --surface: #ffffff;
  --surface-2: #f1f4f8;      /* hover, chips, disabled fields */
  --row-hover: #f7f9fc;
  --border: #d8dee7;
  --text: #1c2733;
  --text-muted: #5b6b7c;
  --text-faint: #b6c0cc;
  /* Example text in an empty field: light enough that "75201" or "0.00" can
     never be mistaken for something already keyed. */
  --placeholder: #8a96a5;
  --accent: #1f4e8c;
  --accent-fill: #1f4e8c;
  --accent-fill-hover: #173d70;
  --accent-soft: #e8eff8;
  --danger: #b3261e;
  --danger-fill: #b3261e;
  --danger-soft: #fce8e6;
  --danger-border: #e4b6b3;
  --warn-soft: #fdf3d7;
  --warn-text: #6b5500;
  --warn-badge: #8a6d00;
  --warn-border: #e8d48a;
  --ok: #1e7d3c;
  --ok-fill: #1e7d3c;
  --ok-soft: #e2f3e8;
  --topbar-bg: #16273c;
  --topbar-edge: transparent;
  --toast-bg: #1c2733;
  --radius: 8px;
  /* One height for every single-line field, dropdown and button, so a row of
     them lines up (they rendered at 31, 33 and 35px, which set each field's
     label at a different height and every button a little proud). */
  --control-h: 34px;
  --shadow: 0 1px 3px rgba(16, 30, 50, 0.08), 0 1px 2px rgba(16, 30, 50, 0.06);
  --dialog-shadow: 0 12px 40px rgba(10, 20, 40, 0.25);
  --backdrop: rgba(15, 25, 40, 0.45);
  /* Charts (Staff Performance). One blue for every bar — each column is a
     single measure, so color carries no identity — and a five-step ramp of
     the same blue for the staffer-by-day grid, lightest step still clear of
     the card (checked as an ordinal ramp against --surface, both themes). */
  --viz-bar: #2a78d6;
  --viz-bar-low: #b6c0cc;
  --viz-ref: #5b6b7c;
  --heat-0: #eef1f5;
  --heat-0-wkend: #f7f8fa;
  --heat-1: #86b6ef;
  --heat-2: #5598e7;
  --heat-3: #2a78d6;
  --heat-4: #1c5cab;
  --heat-5: #0d366b;
  /* Revenue's split trend: the validated eight-slot categorical order (each
     value owns a slot by a fixed list, never by rank — views/revenue.js), a
     grey for "Other", and a hairline grid one step off the card. */
  --viz-grid: #e8ecf1;
  --viz-axis: #c4ccd6;
  --rv-1: #2a78d6;
  --rv-2: #eb6834;
  --rv-3: #1baf7a;
  --rv-4: #eda100;
  --rv-5: #e87ba4;
  --rv-6: #008300;
  --rv-7: #4a3aa7;
  --rv-8: #e34948;
}

* { box-sizing: border-box; }
[hidden] { display: none !important; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-size: 14px;
  color: var(--text);
  background: var(--bg);
}

h1, h2, h3 { margin: 0 0 12px; }
h2 { font-size: 18px; }
h3 { font-size: 15px; }

a { color: var(--accent); }

/* ---------- Header / nav ---------- */
.topbar {
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 20px;
  background: var(--topbar-bg);
  box-shadow: inset 0 -1px 0 var(--topbar-edge);
  color: #fff;
  height: 58px;
  position: sticky;
  top: 0;
  z-index: 20;
}
.brand { white-space: nowrap; display: flex; align-items: center; }
.brand img { height: 38px; display: block; }
.nav { display: flex; gap: 4px; flex: 1; }
.nav button {
  background: none;
  border: none;
  color: #c3cfdd;
  font: inherit;
  font-weight: 500;
  padding: 8px 14px;
  border-radius: 6px;
  cursor: pointer;
  white-space: nowrap;
}
.nav button:hover { color: #fff; background: rgba(255, 255, 255, 0.08); }
.nav button.active { color: #fff; background: rgba(255, 255, 255, 0.16); }
.userbox { display: flex; align-items: center; gap: 14px; color: #c3cfdd; font-size: 13px; white-space: nowrap; min-width: 0; }
#whoami { overflow: hidden; text-overflow: ellipsis; }
/* Plain words on the bar, underlined only under the pointer: six underlined
   links in a row read as one long rule. */
.userbox .link-btn { color: #c3cfdd; text-decoration: none; }
.userbox .link-btn:hover { color: #fff; text-decoration: underline; }
/* Narrower screens: the bar tightens rather than wrapping "Data Entry" onto
   two lines and pushing Sign Out off the edge. The signed-in name is the
   slack: it narrows first, so each step starts where an admin's ten tabs
   (People and Revenue made it ten) would otherwise cut the name below a
   readable width or push Sign Out off — swept a pixel at a time from 1500px
   to 1000px. A role with fewer tabs just tightens a little early. */
@media (max-width: 1499px) {
  .topbar { gap: 16px; padding: 0 16px; }
  .nav { gap: 2px; }
  .nav button { padding: 8px 10px; }
  .userbox { gap: 10px; }
}
@media (max-width: 1356px) {
  .brand img { height: 32px; }
  .nav button { padding: 8px 8px; font-size: 13.5px; }
  .userbox { font-size: 12.5px; }
  #whoami { max-width: 140px; }
  .whoami-role { display: none; }
}
@media (max-width: 1265px) {
  .topbar { gap: 10px; padding: 0 12px; }
  .nav button { padding: 8px 6px; }
  .userbox { gap: 8px; }
  #whoami { display: none; }   /* a name cut to "De…" says nothing */
}
@media (max-width: 1110px) {
  .brand img { height: 28px; }
  .nav button { padding: 8px 5px; font-size: 13px; }
  .userbox { gap: 7px; font-size: 12px; }
}
@media (max-width: 1050px) {
  .topbar { gap: 8px; padding: 0 10px; }
  .nav { gap: 0; }
  .nav button { padding: 8px 4px; font-size: 12.5px; }
  .userbox { gap: 6px; }
}

/* Sub-navigation inside a tab (the Work tab's Daily Work / Performance / Billing) */
.subnav { display: flex; gap: 2px; margin: -4px 0 16px; border-bottom: 1px solid var(--border); }
.subnav button {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  padding: 8px 14px;
  font: inherit;
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
}
.subnav button:hover { color: var(--text); }
.subnav button.active { color: var(--accent); border-bottom-color: var(--accent); }

main { max-width: 1280px; margin: 0 auto; padding: 20px; }

/* ---------- Cards / layout ---------- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 16px;
  margin-bottom: 16px;
}
.card-title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 12px;
}
.card-title h2, .card-title h3 { margin: 0; }
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
.row + .row { margin-top: 10px; }          /* stacked field rows breathe */
.row + .table-wrap { margin-top: 12px; }
.row.top { align-items: flex-start; }      /* a hint under one field must not push the others down */
.spread { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.spread > h2, .spread > h3 { margin: 0; }  /* the row centers the heading; its own margin would skew it */
.spread-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Working Batch picker and its buttons: on the dropdown's line, not centred
   on the label-and-dropdown pair (which left the buttons 4px high). */
#view-entry > .card:first-child > .spread { align-items: flex-end; }
/* A card's header row is separated from whatever follows it (a hidden sibling doesn't count). */
.card > .spread:first-child + :not([hidden]) { margin-top: 12px; }
.muted { color: var(--text-muted); }
.small { font-size: 12.5px; }
.right { text-align: right; }
.mono { font-variant-numeric: tabular-nums; }
.nowrap, td.mono { white-space: nowrap; }  /* dates, times, and amounts never break mid-value */
.cell-sub { display: block; }              /* a cell's secondary line (place, net) under its main value */

/* ---------- Forms ---------- */
label.field { display: flex; flex-direction: column; gap: 4px; font-size: 12.5px; color: var(--text-muted); font-weight: 600; }
.field-hint { font-size: 11.5px; font-weight: 400; color: var(--text-muted); line-height: 1.4; }
/* A note under a field ("carried from the last statement") is a hint, not a
   second label: it does not take the label's weight. */
label.field > .muted, label.field > .small { font-weight: 400; }
input, select, textarea {
  font: inherit;
  color: var(--text);
  padding: 7px 9px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--surface);
  min-width: 0;
}
/* :where() keeps this at zero specificity, so any rule that sizes a control
   for its place (the scan box, a compact table filter) still wins. */
:where(input:not([type="checkbox"], [type="radio"]), select:not([multiple])) { height: var(--control-h); }
::placeholder { color: var(--placeholder); opacity: 1; }
/* The file picker's own button, drawn like the app's small buttons rather
   than the browser's grey bevel. */
input[type="file"] { padding: 0 9px 0 5px; line-height: calc(var(--control-h) - 2px); cursor: pointer; }
input[type="file"]::file-selector-button {
  font: inherit; font-size: 12.5px; font-weight: 600; color: var(--text);
  margin: 0 10px 0 0; padding: 4px 10px; line-height: 1.3;
  border: 1px solid var(--border); border-radius: 4px; background: var(--surface-2); cursor: pointer;
}
input[type="file"]:hover::file-selector-button { border-color: var(--text-muted); }
input:focus, select:focus, textarea:focus {
  outline: 2px solid var(--accent);
  outline-offset: -1px;
  border-color: var(--accent);
}
input:disabled, select:disabled { background: var(--surface-2); color: var(--text-muted); }
/* A part-keyed value (today: a phone short of 10 digits) — flagged while it
   is still on screen, before the save that would reject it. */
input.input-bad { border-color: var(--danger); background: var(--danger-soft); }

.entry-grid {
  display: grid;
  /* minmax(0, 1fr), not a bare 1fr: a bare 1fr is minmax(auto, 1fr), so a long
     label ("Gift Organization") sets a content minimum on whichever column it
     starts in and steals width from its neighbours. That made identical c2
     fields render anywhere from 115px to 344px depending on label length and
     row position. Flooring the minimum at 0 makes the twelve columns equal, so
     a cN span below is a real, predictable width. */
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: 10px 12px;
}
.entry-grid .field { grid-column: span 3; }
.entry-grid .field.c1 { grid-column: span 1; }
.entry-grid .field.c2 { grid-column: span 2; }
.entry-grid .field.c3 { grid-column: span 3; }
.entry-grid .field.c4 { grid-column: span 4; }
.entry-grid .field.c6 { grid-column: span 6; }

.scanbox {
  width: 100%;
  height: auto;
  font-size: 15px;
  padding: 12px 14px;
  border: 2px dashed var(--accent);
  background: var(--accent-soft);
  border-radius: var(--radius);
}
.scanbox:focus { outline: none; border-style: solid; background: var(--surface); }
/* Armed to rescan into the entry being edited, not to start the next gift. */
#scan-card.rescan .scanbox { border-color: var(--warn-badge); background: var(--warn-soft); }
#scan-card.rescan .scanbox:focus { background: var(--surface); }
/* What a rescan rewrote, until it is saved or retyped. */
#entry-form input.rescanned:not(.input-bad) { border-color: var(--ok); background: var(--ok-soft); }

/* ---------- Buttons ---------- */
a.btn,
button.btn {
  font: inherit;
  font-weight: 600;
  border: 1px solid transparent;
  border-radius: 6px;
  /* A fixed line, not the font's own: an emoji label (⚡ Auto-Match) used to
     stand taller than the plain button beside it. 7 + 18 + 7 + borders is
     --control-h. */
  padding: 7px 14px;
  line-height: 18px;
  white-space: nowrap;
  cursor: pointer;
  background: var(--accent-fill);
  color: #fff;
}
a.btn:hover,
button.btn:hover { background: var(--accent-fill-hover); }
button.btn:disabled { opacity: 0.5; cursor: not-allowed; }
a.btn.secondary,
button.btn.secondary {
  background: var(--surface);
  color: var(--text);
  border-color: var(--border);
}
a.btn.secondary:hover,
button.btn.secondary:hover { background: var(--surface-2); }
a.btn.danger,
button.btn.danger { background: var(--surface); color: var(--danger); border-color: var(--danger-border); }
a.btn.danger:hover,
button.btn.danger:hover { background: var(--danger-soft); }
a.btn.sm,
button.btn.sm { padding: 4px 10px; font-size: 12.5px; font-weight: 500; line-height: 15px; }
.link-btn {
  background: none; border: none; padding: 0; font: inherit;
  color: var(--accent); cursor: pointer; text-decoration: underline;
}
/* An action refused for now (its title says why) must not read as one to press. */
.link-btn:disabled { color: var(--text-muted); cursor: not-allowed; text-decoration: none; }

/* ---------- Tables ---------- */
.table-wrap { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; }
/* Baseline, not top: a row mixes 14px values, 12.5px small cells, badges and
   small buttons, and top-aligning them set each one's text at a different
   height. On the baseline the first line of every cell reads as one line. */
th, td { text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--border); vertical-align: baseline; }
th { font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-muted); white-space: nowrap; }
tbody tr:hover { background: var(--row-hover); }
tr.clickable { cursor: pointer; }
tr.selected { background: var(--accent-soft); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
tfoot td { font-weight: 700; border-top: 2px solid var(--border); }
td .btn.sm { margin: 2px 0; }
td .badge { margin: 1px 0; }
/* An action link in a table stays one phrase: "carry to a later week…" broke
   across two lines in every register row, "Undo (reopen)…" across two more. */
td .link-btn { white-space: nowrap; }

/* Column-filter row inside a table header */
thead tr.filters th { padding: 4px 6px; vertical-align: top; }
thead tr.filters input,
thead tr.filters select {
  width: 100%;
  height: 28px;
  font-size: 12.5px;
  font-weight: 400;
  text-transform: none;
  letter-spacing: normal;
  padding: 4px 6px;
}
thead tr.filters input { min-width: 70px; }
thead tr.filters .stack { display: flex; flex-direction: column; gap: 4px; }
thead tr.filters .stack input[type="date"] { min-width: 0; width: 100%; box-sizing: border-box; }

.import-preview-list { max-height: 50vh; overflow-y: auto; margin: 10px 0; }
/* The Account select of each client in the file with more than one bank account */
#dlg-import-accounts { margin-top: 12px; }
/* Batches the import will not file as they stand, and the live gifts they repeat */
.import-refused { margin: 10px 0; }
.import-refused .issue.confirmed { background: var(--warn-soft); }
.import-refused .issue.confirmed b { color: var(--warn-text); }
.import-refused .dup-matches { margin: 6px 0; padding-left: 20px; }
.import-refused .confirm-dup { display: flex; align-items: center; gap: 6px; margin-top: 4px; font-weight: 600; cursor: pointer; }

/* ---------- Badges ---------- */
.badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.badge.open { background: var(--accent-soft); color: var(--accent); }
.badge.submitted { background: var(--warn-soft); color: var(--warn-badge); }
.badge.exported, .badge.reconciled { background: var(--ok-soft); color: var(--ok); }
.badge.inactive { background: var(--danger-soft); color: var(--danger); }
.badge.admin { background: #ede7f6; color: #5e35b1; }
.badge.manager { background: #e0f2f1; color: #00695c; }
.badge.user { background: #eceff1; color: #455a64; }

/* Inline notice: read-only lock banners, default-rate warning */
.note {
  margin-bottom: 12px; padding: 8px 12px; border-radius: 6px; font-size: 13px;
  background: var(--warn-soft); color: var(--warn-text); border: 1px solid var(--warn-border);
}

/* Outstanding card: how old an item or its confirmation is, and the notice on
   one the close will not stand over as it is (expired, or declined by Stripe) */
.transit-age { font-size: 12px; color: var(--text-muted); }
#recon-outstanding-wrap .note { margin: 0 0 6px; padding: 6px 10px; }

/* ---------- Issues list ---------- */
.issues { max-height: 320px; overflow-y: auto; border: 1px solid var(--border); border-radius: 6px; }
.issue { padding: 8px 12px; border-bottom: 1px solid var(--border); background: var(--danger-soft); font-size: 13px; }
.issue:last-child { border-bottom: none; }
.issues > p { margin: 8px 12px; }
.issue b { color: var(--danger); }

/* ---------- Dialogs ---------- */
dialog {
  border: none;
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  box-shadow: var(--dialog-shadow);
  padding: 20px;
  width: min(560px, calc(100vw - 40px));
}
dialog.wide { width: min(760px, calc(100vw - 40px)); }
dialog::backdrop { background: var(--backdrop); }
dialog h3 { margin-bottom: 16px; }
.dialog-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 18px; }
/* Fields stacked straight in a dialog, outside a grid, get the grid's gap
   (a Note box sat 4px under the date above it). */
:is(dialog, dialog > form) > :is(label.field, .row, .form-grid) + :is(label.field, .row, .form-grid) { margin-top: 12px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.form-grid .full { grid-column: 1 / -1; }

/* ---------- Toasts ---------- */
/* A popover in the top layer (dialogs.js says why), which comes with the
   browser's popover box — centred, bordered, padded, painted — all undone
   here so the stack stays where it always was. Hidden while it is not shown:
   the display below would otherwise beat the browser's own rule for that. A
   browser without popovers drops that rule, as it ignores the attribute, and
   shows the stack as it always did. Drawn above an open dialog, it must not
   take that dialog's clicks: a toast lying over a button would make the
   button do nothing, so clicks go through the stack (and the toasts in it,
   which inherit this) to whatever is under it. */
#toasts {
  position: fixed; inset: auto 20px 20px auto; margin: 0; padding: 0; border: 0;
  background: transparent; color: inherit; overflow: visible;
  display: flex; flex-direction: column; gap: 8px; z-index: 100;
  pointer-events: none;
}
#toasts:not(:popover-open) { display: none; }
#toasts::backdrop { display: none; }
.toast {
  background: var(--toast-bg); color: #fff; padding: 10px 16px; border-radius: 6px;
  box-shadow: var(--shadow); font-size: 13.5px; max-width: 380px;
}
.toast.error { background: var(--danger-fill); }
/* The one part of a toast that takes a click: the stack lets clicks through
   to what is under it (#toasts above), and so does the rest of the toast. */
.toast-dismiss {
  pointer-events: auto; float: right; margin: -4px -8px 0 10px; padding: 0 6px;
  background: transparent; border: 0; color: inherit; font-size: 16px; line-height: 1.4; cursor: pointer;
}
/* Read out, never drawn: the toasts' live regions (dialogs.js announce). */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.rapid-unsaved { margin: 8px 0; }
.rapid-unsaved ul { margin: 6px 0 0; padding-left: 18px; }
.toast.ok { background: var(--ok-fill); }

/* ---------- Login page ---------- */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
.login-card {
  background: var(--surface); border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow); padding: 32px; width: 360px;
}
.login-logo { width: 230px; display: block; margin: 0 auto 20px; }
.login-logo.for-dark { display: none; }
.login-card .sub { color: var(--text-muted); margin-bottom: 20px; font-size: 13px; }
.login-card form { display: flex; flex-direction: column; gap: 12px; }
.login-error { color: var(--danger); font-size: 13px; min-height: 18px; margin-top: 8px; }
.login-error:empty { display: none; }   /* no error: the card's padding is the same all round */
/* A panel that could not load says so, where it used to disappear. */
.error-row { color: var(--danger); margin: 12px 0; line-height: 1.5; }
.error-row .btn { margin-left: 8px; }
/* A week the statement could not read is a fault, not a pricing note. */
.billing-alert { color: var(--danger); font-weight: 600; }
.danger { color: var(--danger); }

.batch-select { min-width: 280px; }
.client-logo { max-height: 28px; max-width: 120px; }
.mfa-qr { display: block; margin: 0 auto; border: 1px solid var(--border); border-radius: 6px; background: #fff; }

/* ---------- Search ---------- */
.eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--text-muted); margin-bottom: 4px;
}
.search-card { padding: 20px 24px; }
.search-head h2 { margin: 0; }
.search-blurb { max-width: 820px; margin: 6px 0 0; line-height: 1.5; }
.search-group { margin-top: 20px; }
/* The group's name, then a hairline running out to the card's edge. */
.search-group-title {
  display: flex; align-items: center; gap: 12px; margin: 0 0 12px;
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase; color: var(--text-muted);
}
.search-group-title::after { content: ""; flex: 1; height: 1px; background: var(--border); }
/* Four across, so both groups' eight criteria stand in the same columns. */
.search-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px 20px; }
.crit { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.crit-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; min-height: 22px; }
.crit-head > label { font-size: 12.5px; font-weight: 600; color: var(--text-muted); white-space: nowrap; }
.crit > input, .crit > select { width: 100%; }
.crit select:disabled { color: var(--text-muted); }
/* How a criterion matches: a small dropdown on its label's line, so the
   value keeps the field's full width. */
.crit-op {
  height: 22px; padding: 0 4px 0 8px; border-radius: 999px; border-color: transparent;
  background: var(--surface-2); color: var(--accent);
  font-size: 12px; font-weight: 600; cursor: pointer;
  field-sizing: content;   /* as wide as the match chosen, where supported */
}
.crit-op:hover { border-color: var(--border); }
.crit-dates { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
.crit-dates input { flex: 1 1 0; min-width: 0; font-variant-numeric: tabular-nums; }
/* "Between": the end date on a line of its own, each date at full width. */
.crit-date-to { display: flex; align-items: center; gap: 8px; flex: 1 0 100%; min-width: 0; }
.search-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 12px 24px; flex-wrap: wrap;
  margin-top: 20px; padding-top: 16px; border-top: 1px solid var(--border);
}
.search-bar-opts, .search-bar-actions { display: flex; align-items: center; gap: 12px 20px; flex-wrap: wrap; }
.search-bar-actions { gap: 10px; margin-left: auto; }
.search-bar-field { display: inline-flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 600; color: var(--text-muted); }
.search-bar-field select { font-weight: 400; }
.btn.search-go { padding-left: 20px; padding-right: 16px; }
.btn.search-go .kbd {
  display: inline-block; margin-left: 8px; padding: 0 5px; border: 1px solid rgba(255, 255, 255, 0.35); border-radius: 4px;
  font-size: 11px; font-weight: 500; line-height: 16px; vertical-align: 1px; opacity: 0.85;
}
.search-clear {
  background: none; border: none; padding: 8px 6px; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--text-muted);
}
.search-clear:hover { color: var(--danger); }
#search-results-card h3 { margin: 0; }
#search-results-card .spread { margin-bottom: 4px; }
/* Caging gifts first, then processor gifts: each block under its own name. */
#search-results tr.group-row > td {
  padding: 14px 10px 6px; background: none; border-bottom: 1px solid var(--border);
  font-size: 11.5px; font-weight: 700; letter-spacing: 0.6px; text-transform: uppercase; color: var(--text-muted);
}
#search-results tbody tr.group-row:hover { background: none; }
#search-results td.row-actions { white-space: nowrap; }
#search-results td.row-actions .link-btn + .link-btn { margin-left: 12px; }
@media (max-width: 1000px) {
  .search-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 560px) {
  .search-grid { grid-template-columns: minmax(0, 1fr); }
}
.badge.caging { background: var(--accent-soft); color: var(--accent); }
.badge.winred { background: #fde8e8; color: #b3261e; }
.badge.anedot { background: #e8f0fd; color: #1f4e8c; }
.badge.stripe, .badge.nuvei, .badge.other { background: #eceff1; color: #455a64; }
.mini-state { width: 56px; }
.mini-zip { flex: 1; }

/* ---------- Reconciliation: QB setup + equation ---------- */
.qb-question { font-size: 17px; font-weight: 600; margin: 14px 0 10px; }
.qb-link { color: var(--accent); }
.qb-eq {
  display: flex; gap: 24px; align-items: stretch; flex-wrap: wrap;
  border: 1px solid var(--border); border-radius: 10px; padding: 16px 20px; margin-bottom: 14px;
}
/* The equation keeps the width its figures need; the difference takes the
   rest. Splitting the box in half let "13 of 13 deposits" run under the
   difference on a narrower screen. */
.qb-eq-left { flex: 0 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 10px; justify-content: center; }
.qb-eq-row { display: flex; align-items: center; gap: 18px; }
.qb-eq-row .qb-cell { display: flex; flex-direction: column; align-items: center; min-width: 140px; }
.qb-eq-row .qb-cell b { font-size: 22px; font-variant-numeric: tabular-nums; }
.qb-eq-row .qb-cell span { font-size: 11px; color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.4px; }
.qb-eq-sub { border-top: 1px solid var(--border); padding-top: 10px; }
.qb-eq-sub .qb-cell b { font-size: 16px; }
.qb-op { font-size: 20px; color: var(--text-muted); }
.qb-eq .qb-diff {
  flex: 1 1 220px; min-width: 200px; align-items: center; justify-content: center; padding: 12px 20px;
}
.qb-eq .qb-diff b { font-size: 30px; font-variant-numeric: tabular-nums; }
.qb-eq .qb-diff span { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }
.qb-eq .qb-diff.zero b, .qb-eq .qb-diff.zero span { color: var(--ok); }
.qb-eq .qb-diff.off b, .qb-eq .qb-diff.off span { color: var(--danger); }
/* The bridge from the posted bank balance to what this week's books expect:
   beside the difference, never inside it. */
.qb-bridge {
  border: 1px dashed var(--border); border-radius: 10px; padding: 10px 20px; margin: -4px 0 14px;
  display: flex; flex-direction: column; gap: 6px;
}
.qb-bridge-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.qb-bridge-row b { font-variant-numeric: tabular-nums; }
.qb-bridge-row .qb-bridge-label { color: var(--text-muted); font-size: 12px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.4px; min-width: 260px; }
.qb-bridge-row.total { border-top: 1px solid var(--border); padding-top: 6px; }
.qb-bridge-items { font-size: 12px; color: var(--text-muted); }
.qb-tick {
  width: 26px; height: 26px; border-radius: 50%; cursor: pointer;
  border: 2px solid var(--border); background: var(--surface); color: #fff;
  font-size: 14px; font-weight: 700; line-height: 1; padding: 0;
}
.qb-tick:hover { border-color: var(--ok); }
.qb-tick.on { background: var(--ok-fill); border-color: var(--ok-fill); }
tr.row-cleared td { color: var(--text-muted); }

/* ---------- Reconciliation: difference box ---------- */
.qb-diff {
  flex: 1; min-width: 150px; border: 1px solid var(--border); border-radius: 8px;
  padding: 10px 14px; display: flex; flex-direction: column; gap: 3px;
}
.qb-diff span { font-size: 12px; color: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.4px; }
.qb-diff b { font-size: 24px; font-variant-numeric: tabular-nums; }
.qb-diff.zero { background: var(--ok-soft); border-color: var(--ok); }
.qb-diff.zero b, .qb-diff.zero span { color: var(--ok); }
.qb-diff.off { background: var(--danger-soft); border-color: var(--danger); }
.qb-diff.off b, .qb-diff.off span { color: var(--danger); }
.sys-cell { min-width: 200px; }   /* "Nuvei/PSQ payout 2026-08-27" kept whole, not broken at a hyphen */
.sys-target { font-size: 12.5px; font-weight: 600; color: var(--ok); margin-top: 3px; }
.sys-target.muted { color: var(--text-muted); font-weight: 500; }
tr.row-cleared .sys-target { color: var(--ok); }
.cand-row { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--border); background: var(--surface); cursor: pointer; }
.cand-row:hover { background: var(--row-hover); }
.cand-row .num { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 600; }
/* Match dialog: declarations a deposit may prove, above the ordinary candidates. */
#dlg-match-declared { margin-bottom: 16px; padding-bottom: 12px; border-bottom: 1px solid var(--border); }
#dlg-match-declared .spread { margin-top: 10px; }
#dlg-match-declared .note { margin: 10px 0 0; }
#dlg-find-deposit-list .cand-row { width: 100%; border: 0; border-bottom: 1px solid var(--border); text-align: left; font: inherit; color: inherit; }

/* The gifts a chargeback debit might be backing out (Add to System). */
#addsys-suggest { margin: 4px 0 12px; }
#addsys-suggest .cand-list { border: 1px solid var(--border); border-radius: 6px; overflow: hidden; max-height: 220px; overflow-y: auto; }
#addsys-suggest .cand-row:last-child { border-bottom: none; }
#addsys-suggest .cand-row.picked { background: var(--ok-soft); }
#addsys-suggest .badge { margin-left: 6px; }
#dispute-find-list .cand-list { border: 1px solid var(--border); border-radius: 6px; overflow: hidden; max-height: 300px; overflow-y: auto; }
#dispute-find-list .cand-row:last-child { border-bottom: none; }
#dispute-find-list .badge { margin-left: 6px; }
#recon-disputecheck td .btn.sm { margin-top: 4px; }
/* Stripe charges a person links to a card batch, grouped by the payout carrying them. */
#dlg-charge-link-list { border: 1px solid var(--border); border-radius: 6px; overflow: hidden; max-height: 360px; overflow-y: auto; }
#dlg-charge-link-list .charge-link-payout { padding: 6px 12px; border-bottom: 1px solid var(--border); }
#dlg-charge-link-list .badge { margin-left: 6px; }

/* ---------- Reconciliation ---------- */
.recon-grids { display: grid; grid-template-columns: 1fr 1fr; gap: 0 24px; margin-top: 16px; }
#recon-disputecheck { margin-top: 16px; }
/* Setup above, the finished weeks below: a rule between them. */
#recon-history-wrap { margin-top: 22px; border-top: 1px solid var(--border); }
#recon-history-wrap > .spread { margin-top: 14px; }
/* A cell's actions, one per line. */
.cell-acts { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
#work-subtitle { margin: 2px 0 0; }
.var-pos { color: var(--ok); font-weight: 600; }
.var-neg { color: var(--danger); font-weight: 600; }
.var-zero { color: var(--text-muted); }
td select.cat-select { font-size: 12px; height: auto; padding: 3px 5px; max-width: 190px; }
/* A narrower screen gives the register's memo its room back: the one-line
   actions and the wider match column would otherwise squeeze it to a word
   per line. */
@media (max-width: 1240px) {
  #recon-txn-wrap td .link-btn { white-space: normal; }
  #recon-txn-wrap .sys-cell { min-width: 160px; }
  #recon-txn-wrap td select.cat-select { max-width: 160px; }
}
/* The treasury register: the custodian's own line is what a person reads to
   decide what a row is, so it gets the room; the category select and the
   status stay compact. */
td select.tr-cat { font-size: 12px; height: auto; padding: 3px 5px; max-width: 170px; }
#tr-rows td.tr-line { min-width: 260px; }
#tr-rows td.sys-cell { max-width: 240px; }
#tr-rows tr.tr-sweep td { color: var(--text-muted); }
/* The tie-out against the Activity Summary: the sum is what has to tie. */
#tr-tie { margin: 4px 0 12px; }
#tr-tie tr.tr-tie-total td { border-top: 2px solid var(--border); }
/* The stock lot ledger: each lot's sales sit indented beneath it. */
#tr-ledger tr.tr-lot td { border-top: 2px solid var(--border); }
#tr-ledger tr.tr-lot-sale td { border-top: 0; padding-top: 2px; padding-bottom: 2px; }

/* ---------- Workflow guidance ---------- */
.workflow-steps {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
  font-size: 12.5px; color: var(--text-muted);
}
.wstep { display: inline-flex; align-items: center; gap: 6px; padding: 3px 10px; border-radius: 999px; background: var(--surface-2); }
.wstep b {
  display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; border-radius: 50%; font-size: 11px;
  background: var(--text-muted); color: var(--surface);
}
.wstep.done { background: var(--ok-soft); color: var(--ok); }
.wstep.done b { background: var(--ok-fill); color: #fff; }
.wstep.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }
.wstep.active b { background: var(--accent-fill); color: #fff; }
.wsep { color: var(--border); }
.howto { margin: 0 0 12px; padding-left: 20px; line-height: 1.9; }

/* ---------- Rapid scan (Zero batches) ---------- */
.chiprow { display: flex; gap: 8px; flex-wrap: wrap; }
.chip {
  font: inherit; font-size: 13px; font-weight: 600;
  padding: 7px 14px; border-radius: 999px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
}
.chip:hover { background: var(--surface-2); }
.chip.active { background: var(--accent-fill); border-color: var(--accent-fill); color: #fff; }
.rapid-status { margin: 12px 0 10px; }
.rapid-status .row { align-items: center; }
#rapid-count { font-size: 18px; }
.scanbox.flash-ok { animation: flashok 0.35s; }
@keyframes flashok { 0% { background: var(--ok-soft); border-color: var(--ok); } 100% { background: var(--accent-soft); } }
.row.actions-row { margin-top: 14px; }
.empty { padding: 28px; text-align: center; color: var(--text-muted); }
.app-version { max-width: 1280px; margin: 0 auto; padding: 4px 20px 16px; text-align: right; color: var(--text-faint); font-size: 11px; }
#update-banner {
  position: sticky; top: 0; z-index: 50;
  background: var(--warn-soft); color: var(--warn-text); border-bottom: 1px solid var(--warn-border);
  padding: 9px 20px; text-align: center; font-size: 13.5px;
}
#update-banner .btn { margin-left: 10px; }
/* Below the client build floor the page can no longer save: the offer to
   reload becomes an instruction, in the session banner's colours. */
#update-banner.required { background: var(--danger-fill); color: #fff; border-bottom-color: var(--danger-fill); font-weight: 500; }
#update-banner.required .btn { background: #fff; color: var(--danger-fill); }
#update-banner.required .btn:hover { background: var(--danger-soft); }
/* Session lapsed mid-entry: stays until the clerk signs in again (no close). */
#session-expired-banner {
  position: sticky; top: 0; z-index: 60;
  background: var(--danger-fill); color: #fff;
  padding: 9px 20px; text-align: center; font-size: 13.5px; font-weight: 500;
}
#session-expired-banner .btn { margin-left: 10px; background: #fff; color: var(--danger-fill); }
#session-expired-banner .btn:hover { background: var(--danger-soft); }
.login-card .draft-note { color: var(--warn-text); background: var(--warn-soft); border: 1px solid var(--warn-border); border-radius: 6px; padding: 8px 12px; }
/* What an admin changed since the last sign-in: shown once, before the app. */
.login-card .notice-list { color: var(--warn-text); background: var(--warn-soft); border: 1px solid var(--warn-border); border-radius: 6px; padding: 8px 12px 8px 28px; margin: 0 0 12px; font-size: 13.5px; }
.stat { font-size: 13px; color: var(--text-muted); }
.stat b { color: var(--text); font-size: 15px; }
.stats { display: flex; gap: 6px 28px; flex-wrap: wrap; align-items: center; }
.stat select { vertical-align: middle; }
.stats + .table-wrap, .stats + h3 { margin-top: 12px; }
/* The donor card's sample downloads, under its counts. */
#donor-samples { margin-top: 12px; }
/* The donor profile dialog: client chips and contact lines under the totals. */
.donor-chips, .donor-contacts { margin-top: 12px; }
.donor-contact { display: flex; flex-wrap: wrap; gap: 4px 16px; align-items: baseline; margin-top: 4px; }
.donor-contact > .muted:first-child { min-width: 72px; }
#dlg-donor .table-wrap { margin-top: 12px; }
/* Wider than .wide: a gift row (date, client, amount, method, mail code,
   batch, thank-you, Move…) stays on one line instead of stacking its last cell. */
#dlg-donor { width: min(880px, calc(100vw - 40px)); }
#dlg-donor h4 { margin: 16px 0 0; }
/* The donor review: two profiles side by side, each a column. */
dialog.xwide { width: min(1080px, calc(100vw - 40px)); }
.pair-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 20px; margin-top: 12px; }
.pair-side { border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; min-width: 0; }
.pair-side.on-file { border-color: var(--accent-fill); }
.pair-role { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--text-muted); margin-bottom: 2px; }
.pair-ask { margin: 10px 0 0; }
.pair-outcomes { margin: 10px 0 0; padding-left: 18px; }
.pair-outcomes li + li { margin-top: 2px; }
.pair-side h4 { margin: 0 0 4px; }
.pair-side .agree, .pair-legend .agree { color: var(--ok); }
.pair-legend { margin: 8px 0 0; }
.pair-side table { font-size: 13px; }
.pair-side .table-wrap { margin-top: 10px; }  /* the gifts sat flush under the last contact line */
#dlg-donor-pair .dialog-actions { align-items: center; flex-wrap: wrap; }
#dlg-donor-pair-pos { margin-right: auto; }
#donor-review-wrap tbody tr { cursor: pointer; }
@media (max-width: 760px) { .pair-grid { grid-template-columns: 1fr; gap: 12px; } }
/* Daily Work: what the Daily Export carries, and the open batches it leaves
   out (a .note while there are any); the override box sits on its own line. */
#daily-export-scope { margin-bottom: 12px; font-size: 13px; }
#daily-export-scope .daily-include-open { display: block; margin-top: 6px; }
#entry-batch-info { margin-top: 12px; }
.pager { display: flex; gap: 10px; align-items: center; justify-content: flex-end; margin-top: 10px; }
details.audit-details pre {
  margin: 6px 0 0; padding: 8px; background: var(--bg); border-radius: 6px;
  font-size: 12px; white-space: pre-wrap; word-break: break-word; max-width: 480px;
}

a.btn { text-decoration: none; display: inline-block; }

/* Partial-match amount input inside the match dialog */
.cand-amt { width: 88px; height: auto; padding: 2px 6px; font-size: 13px; }

/* Drill-down affordance on backed System Match cells */
.sys-cell.has-backing { cursor: pointer; }
.sys-cell.has-backing:hover .badge { text-decoration: underline; }

/* CSP-safe spacing (inline style attributes are blocked by style-src 'self') */
.backing-h { margin: 14px 0 4px; }
.mt-sm { margin-top: 10px; }
/* The label field takes the room the row has left; its floor is on the
   field, not the box inside it, which used to spill over the last-4 box. */
.tc-rate-row .field { flex: 1; min-width: 0; }
.acct-row .field:first-child { flex: 1; min-width: 180px; }
.tc-rate-row .field input, .acct-row .acct-label { width: 100%; }
.acct-row .acct-last4 { width: 96px; }
.acct-row .acct-kind { width: 118px; }
.acct-row .field.small { flex-direction: row; align-items: center; gap: 6px; min-height: 34px; }
.acct-del, .tc-del { padding-bottom: 9px; }
#acct-rows .row + .row, #tc-rates .row + .row { margin-top: 8px; }
/* Client dialog: each block's rows sit clear of the paragraph above them. */
#acct-rows, #tc-rates { margin-top: 6px; }
#dlg-client .field.full > b { margin-top: 4px; }

/* Empty-register diagnostic explanation */
.diag-why { margin-top: 8px; max-width: 70ch; }

.field-error { outline: 2px solid var(--bad, #a33f2b); outline-offset: 1px; }

/* Utility classes (CSP forbids inline style attributes) */
.upper { text-transform: uppercase; }
/* Email is stored lowercase; show that in the box rather than rewriting what
   the staffer is still typing. Placeholders are lowercase already. */
.lower { text-transform: lowercase; }
.mt6 { margin-top: 6px; }
/* Where each of a client's accounts stands (Reconciliation setup): clear of
   the upload row beneath it. */
.accounts-standing { margin-bottom: 12px; }
.my6 { margin: 6px 0 2px; }

/* Finish-reconciliation dialog + history documents */
.form-grid.cols3 { grid-template-columns: 1fr 1fr 1fr; }
/* A scrolling list reads as one when it has an edge: without it the last
   batch was cut off mid-row with the totals right under it. */
.finish-list { max-height: 240px; overflow: auto; margin-bottom: 12px; border: 1px solid var(--border); border-radius: 6px; }
.finish-list table tr:last-child td { border-bottom: 0; }
/* A zero difference the bank's own balances disagree with: the refusal, said
   in the dialog, and the reason an override has to give. */
.finish-override p { margin: 0 0 8px; line-height: 1.45; }
.finish-override input { width: 100%; }
dialog h4 { margin: 14px 0 6px; font-size: 14px; }
.doc-links { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.badge.voided { background: #eceff1; color: #78909c; }
/* A week before the system (Work → Transfers): its manual workbook is the record. */
.badge.historical { background: #f3eee4; color: #6d5a33; }
tr.historical > td { font-style: italic; }
.hist-batches td { background: var(--bg, #f6f7f9); }
.hist-batches > td, .hist-journal > td { padding: 10px 14px 14px; }
.field.locked input[readonly] { background: var(--surface-2); color: var(--text-muted); cursor: not-allowed; }

/* ---------- Staff Performance: team figures, comparison, grid ---------- */
/* Refetch keeps the frame: the last render stays, dimmed, until the new one lands. */
#work-perf.is-loading #perf-kpis,
#work-perf.is-loading #perf-visuals,
#work-perf.is-loading #perf-table-wrap { opacity: 0.55; transition: opacity 0.15s; }

.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin-top: 12px; }
.kpi { border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; }
.kpi-label { font-size: 12.5px; font-weight: 600; color: var(--text-muted); }
.kpi-value { font-size: 22px; font-weight: 600; margin-top: 4px; line-height: 1.2; }
.kpi-unit { font-size: 12.5px; font-weight: 500; color: var(--text-muted); }
.kpi-delta { font-size: 12px; margin-top: 4px; }
.kpi-move { font-weight: 600; color: var(--text-muted); margin-right: 2px; }
.kpi-move.good { color: var(--ok); }
.kpi-move.bad { color: var(--danger); }

.segmented { display: inline-flex; border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }
.segmented button {
  background: var(--surface); border: 0; border-left: 1px solid var(--border);
  padding: 5px 12px; font: inherit; font-size: 13px; color: var(--text-muted); cursor: pointer;
}
.segmented button:first-child { border-left: 0; }
.segmented button:hover { background: var(--surface-2); color: var(--text); }
.segmented button.active { background: var(--accent-soft); color: var(--accent); font-weight: 600; }

/* The comparison: a table whose figure cells carry a bar. Each column is
   scaled to its own largest value; the hairline through it is the team's. */
.bar-table th, .bar-table td { vertical-align: middle; }
.bar-table th.bar-th { min-width: 150px; }
.sort-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit; font-weight: 600;
}
.sort-btn:hover { color: var(--text); }
.sort-arrow { font-size: 9px; margin-left: 2px; }
.ref-key {
  display: flex; align-items: center; gap: 5px; margin-top: 3px;
  font-size: 11.5px; font-weight: 500; text-transform: none; letter-spacing: 0;
}
.ref-swatch { display: inline-block; width: 1px; height: 12px; background: var(--viz-ref); }
.bar-name { white-space: nowrap; }
td.bar-cell { padding-top: 0; padding-bottom: 0; }
.bar-row { display: flex; align-items: center; gap: 8px; height: 34px; }
.bar-val { flex: 0 0 auto; min-width: 48px; text-align: right; font-variant-numeric: tabular-nums; }
.bar-track { position: relative; flex: 1 1 auto; min-width: 60px; align-self: stretch; }
.bar {
  position: absolute; left: 0; top: 50%; height: 12px; margin-top: -6px; min-width: 2px;
  background: var(--viz-bar); border-radius: 0 4px 4px 0;   /* square at the baseline, rounded at the value */
}
.bar.low { background: var(--viz-bar-low); }
.bar-cell:hover .bar { filter: brightness(1.12); }
.bar-ref { position: absolute; top: 0; bottom: 0; width: 1px; margin-left: -0.5px; background: var(--viz-ref); }

/* The staffer-by-day grid. Columns are set from script (the CSP allows no
   inline style attributes); 2px of card shows between cells. */
.heatmap { display: grid; gap: 2px; min-width: min-content; }
.hm-col {
  display: flex; flex-direction: column; justify-content: flex-end; align-items: center;
  font-size: 11px; line-height: 1.3; color: var(--text-muted); padding-bottom: 4px; min-width: 0;
}
.hm-month { align-self: flex-start; font-weight: 600; white-space: nowrap; min-height: 14px; }
.hm-day { font-variant-numeric: tabular-nums; white-space: nowrap; min-height: 14px; }
.hm-wd { color: var(--text-faint); }
.hm-col.wkend .hm-day { color: var(--text-faint); }
.hm-corner { display: flex; align-items: flex-end; padding-bottom: 4px; }
.hm-name { display: flex; align-items: center; min-width: 0; padding-right: 8px; }
.hm-name .link-btn { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; text-align: left; }
.hm-cell { height: 24px; border-radius: 3px; background: var(--heat-0); cursor: pointer; }
.hm-cell.wkend.b0 { background: var(--heat-0-wkend); }
.hm-cell:hover { outline: 2px solid var(--text); outline-offset: -2px; }
.hm-cell.b1, .hm-swatch.b1 { background: var(--heat-1); }
.hm-cell.b2, .hm-swatch.b2 { background: var(--heat-2); }
.hm-cell.b3, .hm-swatch.b3 { background: var(--heat-3); }
.hm-cell.b4, .hm-swatch.b4 { background: var(--heat-4); }
.hm-cell.b5, .hm-swatch.b5 { background: var(--heat-5); }
.hm-swatch.b0 { background: var(--heat-0); }
.hm-legend { display: flex; align-items: center; gap: 3px; flex-wrap: wrap; margin-top: 12px; }
.hm-legend > .muted { margin: 0 4px; }
.hm-legend .hm-gap { margin-left: 14px; }
.hm-swatch { display: inline-block; width: 18px; height: 12px; border-radius: 2px; }

.viz-tip {
  position: fixed; z-index: 1000; pointer-events: none; max-width: 300px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 4px 16px rgba(10, 20, 40, 0.18); padding: 7px 10px; font-size: 12.5px;
}
.viz-tip-value { font-weight: 700; font-size: 14px; }
.viz-tip-line { color: var(--text-muted); margin-top: 2px; }

/* ---------- Revenue (admins) ---------- */
#view-revenue.is-loading #rv-kpis,
#view-revenue.is-loading #rv-trend,
#view-revenue.is-loading .rv-grid,
#view-revenue.is-loading #rv-by-band,
#view-revenue.is-loading #rv-codes { opacity: 0.55; transition: opacity 0.15s; }
#rv-scope { margin-top: 2px; }
.rv-filters { margin-top: 12px; }
.rv-filters + .rv-filters { margin-top: 10px; }
.rv-filters #rv-mailcode { width: 150px; }
#rv-clear { align-self: center; margin-bottom: 8px; }
#view-revenue .card > p.muted { margin: 14px 0 0; max-width: 980px; line-height: 1.45; }
.kpi-sub { margin-top: 2px; }
.rv-inline { display: inline-flex; align-items: center; gap: 6px; }
.rv-inline select { width: auto; }
.rv-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 20px; }
/* Below this a half-width card clips its Share column. */
@media (max-width: 1240px) { .rv-grid { grid-template-columns: minmax(0, 1fr); } }
.rv-grid > .card h3 { margin-bottom: 6px; }
.rv-table td.bar-cell { width: 55%; }
.rv-table td.rv-name { max-width: 220px; overflow: hidden; text-overflow: ellipsis; }
.rv-table td.rv-name .link-btn { white-space: nowrap; text-align: left; }
#rv-codes .rv-table td.bar-cell { width: 34%; }
.rv-more { margin-top: 10px; }

.rv-trend { margin-top: 12px; min-height: 280px; }
.rv-trend-svg { display: block; max-width: 100%; height: auto; font-family: inherit; }
.rv-trend-svg .rv-gridline { stroke: var(--viz-grid); stroke-width: 1; }
.rv-trend-svg .rv-axis { stroke: var(--viz-axis); stroke-width: 1; }
.rv-trend-svg .rv-tick { fill: var(--text-muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.rv-trend-svg .rv-peak { fill: var(--text); font-size: 12px; font-weight: 600; }
.rv-trend-svg .rv-hit { fill: transparent; cursor: default; }
.rv-trend-svg .rv-hover-band { fill: var(--surface-2); }
.rv-trend-table { margin-top: 12px; max-height: 360px; overflow-y: auto; }

/* Series fills: the marks carry the color; every label stays in text ink. */
.rv-c0 { fill: var(--viz-bar); background: var(--viz-bar); }
.rv-c1 { fill: var(--rv-1); background: var(--rv-1); }
.rv-c2 { fill: var(--rv-2); background: var(--rv-2); }
.rv-c3 { fill: var(--rv-3); background: var(--rv-3); }
.rv-c4 { fill: var(--rv-4); background: var(--rv-4); }
.rv-c5 { fill: var(--rv-5); background: var(--rv-5); }
.rv-c6 { fill: var(--rv-6); background: var(--rv-6); }
.rv-c7 { fill: var(--rv-7); background: var(--rv-7); }
.rv-c8 { fill: var(--rv-8); background: var(--rv-8); }
.rv-cother { fill: var(--viz-bar-low); background: var(--viz-bar-low); }
.rv-trend-svg .rv-partial { opacity: 0.45; }
.rv-table .bar[data-w="0"] { display: none; }
.rv-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-top: 10px; font-size: 12.5px; color: var(--text-muted); }
.rv-legend-item { display: inline-flex; align-items: center; gap: 6px; }
.rv-swatch { display: inline-block; width: 12px; height: 12px; border-radius: 3px; }
/* A tooltip row's series key: a short stroke, not a box. */
.viz-tip-key { display: inline-block; width: 12px; height: 3px; border-radius: 2px; margin-right: 6px; vertical-align: middle; }

/* ---------- People (js/views/people.js; notes and notices in js/donor-dialog.js) ---------- */
/* A count beside a nav or sub-nav label: review pairs, letters waiting. */
.nav-count {
  display: inline-block; min-width: 18px; padding: 0 5px; margin-left: 4px; border-radius: 999px;
  background: var(--warn-soft); color: var(--warn-text); font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
}
label.field.grow { flex: 1; min-width: 240px; }
label.check { display: flex; align-items: center; gap: 6px; font-size: 13px; }
.checks { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 16px; margin-top: 10px; }
#people-pager:not(:empty) { margin-top: 12px; align-items: center; }
/* A directory row opens its donor anywhere on it, not only on the name. */
#people-results tbody tr { cursor: pointer; }
/* Each client's buttons end at the same edge, whether or not it has a queue. */
#acks-overview td.actions { text-align: right; }
/* Opening a client's queue scrolls it up to just under the sticky top bar. */
#acks-queue-card { scroll-margin-top: 72px; }
#acks-queue-title { margin: 0; }
#acks-queue-card details { margin-top: 14px; }
#acks-queue-card details > summary { cursor: pointer; font-weight: 600; font-size: 13px; }
#acks-queue-card h4 { margin: 16px 0 6px; }
.person-link { text-align: left; }

/* A client's letter and alert settings, beside a preview of the letter. */
.settings-cols { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); gap: 20px; }
.settings-cols textarea { width: 100%; font-family: inherit; }
.letter-preview {
  border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface-2);
  padding: 18px 20px; font-family: Georgia, "Times New Roman", serif; font-size: 13px; line-height: 1.5;
}
.letter-preview p { margin: 0 0 10px; }
@media (max-width: 860px) { .settings-cols { grid-template-columns: 1fr; } }

/* The donor dialog's People parts: notices under the title, and notes. */
.donor-notices { margin-bottom: 10px; }
.donor-note-form { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 8px; }
.donor-note-form textarea { flex: 1; font-family: inherit; }
.donor-note { border-bottom: 1px solid var(--border); padding: 6px 0; }
.donor-mail-form { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin: 6px 0 4px; }
.donor-mail-form input[name="address1"] { flex: 2 1 180px; }
.donor-mail-form input[name="address2"], .donor-mail-form input[name="city"] { flex: 1 1 110px; }
.donor-mail-form p { flex-basis: 100%; margin: 0; }
.donor-note p { margin: 0 0 2px; white-space: pre-wrap; font-size: 13px; }

/* A gift's pledge (public/js/pledge-dialog.js): the line under its amount,
   and the dialog's status and Paid-by choices. */
.pledge-line { white-space: nowrap; }
td.num .pledge-line { margin-left: auto; }
.pledge-status { border: 0; padding: 0; margin: 10px 0 12px; display: grid; gap: 6px; }
.pledge-status legend { padding: 0; margin-bottom: 6px; color: var(--text-muted); }
.pledge-status label, .pledge-choice { display: flex; align-items: center; gap: 8px; }
.pledge-choices { display: grid; gap: 6px; max-height: 240px; overflow-y: auto; margin: 4px 0 12px; }

/* ---------- Night theme ----------
   /theme.js sets <html data-theme="dark"> when the user picked Dark, or
   picked Auto and the device is in dark appearance. Only the tokens (and a
   few one-off badge palettes) change; layout is untouched. The top bar keeps
   its navy so the app still reads as itself at night. */
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg: #0f151e;
  --surface: #171f2b;
  --surface-2: #202a38;
  --row-hover: #1c2634;
  --border: #2c3849;
  --text: #e4e9f0;
  --text-muted: #9aa8ba;
  --text-faint: #56657a;
  --placeholder: #6c7b8f;
  --accent: #8fb8f2;
  --accent-fill: #2d6cc2;
  --accent-fill-hover: #3b7dd6;
  --accent-soft: #1a2b46;
  --danger: #ff8f86;
  --danger-fill: #b83a30;
  --danger-soft: #3a1c1a;
  --danger-border: #6b2f2a;
  --warn-soft: #362c12;
  --warn-text: #efd07a;
  --warn-badge: #efd07a;
  --warn-border: #665420;
  --ok: #6fd48f;
  --ok-fill: #1f8a44;
  --ok-soft: #13301e;
  --topbar-bg: #0c1420;
  --topbar-edge: #2c3849;
  --toast-bg: #2b3b52;
  --shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 1px 2px rgba(0, 0, 0, 0.4);
  --dialog-shadow: 0 12px 40px rgba(0, 0, 0, 0.65);
  --backdrop: rgba(0, 0, 0, 0.6);
  /* At night more reads lighter: the ramp runs from deep to pale. */
  --viz-bar: #3987e5;
  --viz-bar-low: #56657a;
  --viz-ref: #9aa8ba;
  --heat-0: #202a38;
  --heat-0-wkend: #1b2430;
  --heat-1: #184f95;
  --heat-2: #256abf;
  --heat-3: #3987e5;
  --heat-4: #6da7ec;
  --heat-5: #b7d3f6;
  --viz-grid: #243042;
  --viz-axis: #3a4759;
  --rv-1: #3987e5;
  --rv-2: #d95926;
  --rv-3: #199e70;
  --rv-4: #c98500;
  --rv-5: #d55181;
  --rv-6: #008300;
  --rv-7: #9085e9;
  --rv-8: #e66767;
}
:root[data-theme="dark"] dialog { border: 1px solid var(--border); }
:root[data-theme="dark"] .login-logo.for-light { display: none; }
:root[data-theme="dark"] .login-logo.for-dark { display: block; }
/* Client logos are mostly dark marks on transparent backgrounds. */
:root[data-theme="dark"] .client-logo { background: #fff; padding: 2px 5px; border-radius: 4px; }
:root[data-theme="dark"] .badge.admin { background: #2d2447; color: #cdb8f5; }
:root[data-theme="dark"] .badge.manager { background: #123331; color: #7fdacd; }
:root[data-theme="dark"] .badge.user,
:root[data-theme="dark"] .badge.stripe,
:root[data-theme="dark"] .badge.nuvei,
:root[data-theme="dark"] .badge.other,
:root[data-theme="dark"] .badge.voided { background: #263039; color: #b6c3ce; }
:root[data-theme="dark"] .badge.historical { background: #352e22; color: #e0cfa8; }
:root[data-theme="dark"] .badge.winred { background: var(--danger-soft); color: var(--danger); }
:root[data-theme="dark"] .badge.anedot { background: var(--accent-soft); color: var(--accent); }

/* Journal entries (accounting import) */
dialog.xwide { width: min(1180px, calc(100vw - 40px)); }
.je-source { margin-top: 12px; }
.je-source .je-head { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.je-source .je-head label { display: flex; align-items: center; gap: 6px; font-weight: 600; }
.je-source .je-templates { margin-top: 6px; }
.je-source .je-templates .field { min-width: 150px; flex: 1; }
.je-rules { margin-top: 8px; }
.je-rules table { font-size: 12px; }
.je-rules input, .je-rules select { width: 100%; min-width: 64px; height: auto; font-size: 12px; padding: 3px 5px; }
.je-rules td { padding: 3px 4px; }
.je-rules .je-account { min-width: 70px; }
.je-rules .je-name { min-width: 150px; }
.je-rules .je-match { min-width: 100px; }
.je-rules .je-cash-name { min-width: 150px; }
.je-rules .je-cash-account { min-width: 110px; }
.je-source.off .je-templates, .je-source.off .je-rules { opacity: 0.55; }
.je-lines { font-size: 12px; margin: 4px 0 8px; }
.je-lines td { padding: 2px 6px; }
.je-entry-head td { background: var(--surface-2); font-weight: 600; }
.je-list details summary { cursor: pointer; font-weight: 600; }
.je-list ul { margin: 4px 0 8px 18px; }
.hist-journal td { background: var(--bg, #f6f7f9); }
.badge.qbo { background: #e3f2e1; color: #2e7d32; }
.badge.sage { background: #e8f0fd; color: #1f4e8c; }

/* Setup hints wrap under their field instead of stretching it across the row. */
#setup-begin-hint, #setup-ending-hint { display: block; max-width: 340px; line-height: 1.35; }

/* A batch's slip-level exceptions, hanging under the row they belong to. */
tr.sub > td { background: var(--bg-subtle, rgba(127, 127, 127, 0.06)); border-top: 0; }
ul.tight { margin: 2px 0; padding-left: 18px; }
ul.tight li { margin: 2px 0; }
