/* ===========================================================================
   KHC Message Center — "Premium Healthcare Calm" design system
   Warm surfaces · sage/teal accent · gold reserved for rewards · calm motion.
   One scale for type, spacing, radius, shadow. Light + dark. Reduced-motion safe.
   =========================================================================== */

@font-face {
  font-family: 'Inter';
  src: url('/fonts/inter-var.woff2') format('woff2');
  font-weight: 100 900;
  font-display: swap;
  font-style: normal;
}

:root {
  /* ---- Type scale (single ramp; tabular numerals for counts/timers) ---- */
  --font: 'Inter', ui-sans-serif, -apple-system, "Segoe UI", Roboto, sans-serif;
  --t-xs: 12px; --t-sm: 13px; --t-base: 14px; --t-md: 16px; --t-lg: 20px; --t-xl: 24px;

  /* ---- Spacing scale ---- */
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px;

  /* ---- Radius scale ---- */
  --r-sm: 6px; --r-md: 10px; --r-lg: 16px; --r-full: 999px;

  /* ---- Light theme surfaces (warm, airy) ---- */
  --bg: #f1f4f2;
  --surface: #ffffff;
  --surface-2: #f6f8f6;
  --surface-3: #eef2ef;
  --ink: #17251f;
  --ink-soft: #33443c;
  --muted: #64756c;
  --line: #e3e9e4;
  --line-strong: #d3dcd5;

  /* ---- Sage/teal primary (calm, medical-grade) ---- */
  --primary: #1f8a76;
  --primary-hover: #1a7365;
  --primary-ink: #0f5d52;
  --primary-soft: rgba(31,138,118,.10);
  --primary-softer: rgba(31,138,118,.055);
  --on-primary: #ffffff;

  /* ---- Channel accents (calm, distinguishable) ---- */
  --email: #2a7d8c;   /* teal-blue for email */
  --text-c: #5a8f5f;  /* sage-green for text */

  /* ---- Status (muted, not neon) ---- */
  --good: #2f8f6f;
  --warn: #b9822e;
  --danger: #bf5a4f;
  --danger-soft: #fbeeec;

  /* ---- Reward gold — used ONLY for coins/celebration ---- */
  --gold: #bf9130;
  --gold-2: #d8b054;
  --gold-soft: #f4ead0;
  --gold-ink: #6f5210;
  /* Podium — 2nd and 3rd place, so the top of a board reads instantly. Deliberately desaturated:
     these sit next to patient work all day and must not shout. */
  --silver: #8b979e;
  --silver-2: #b6c0c6;
  --silver-ink: #46525a;
  --bronze: #a26c3c;
  --bronze-2: #c48f5d;
  --bronze-ink: #67421f;

  /* ---- Inbox tints — one calm accent per inbox in the left panel -------------
     Owner 2026-08-10: "make the inbox names a little more distinct from each other".
     Twelve hues held at roughly the SAME lightness and saturation so no inbox shouts
     louder than its neighbours — this is identity, not status. Each row picks one via
     the --tc custom property (see .tab / tabTint() in app.js); the tint drives the
     icon, the name, the active pill and the guide line beside its nested folders.
     Status colours (--good/--warn/--danger) are deliberately NOT in this set, so a
     coloured inbox name can never be mistaken for an alert. */
  --tint-teal:   #1c8878;
  --tint-blue:   #2c6f9e;
  --tint-indigo: #5a62ad;
  --tint-violet: #7b57a4;
  --tint-plum:   #95538a;
  --tint-rose:   #b0556a;
  --tint-clay:   #ad6a46;
  --tint-amber:  #96802e;
  --tint-olive:  #6b8739;
  --tint-green:  #3d8a58;
  --tint-cyan:   #257f92;
  --tint-slate:  #5b6f7c;

  /* ---- Shadows (three soft levels) ---- */
  --sh-1: 0 1px 2px rgba(22,40,34,.05), 0 1px 3px rgba(22,40,34,.04);
  --sh-2: 0 2px 8px rgba(22,40,34,.06), 0 4px 16px rgba(22,40,34,.05);
  --sh-3: 0 8px 24px rgba(22,40,34,.10), 0 2px 8px rgba(22,40,34,.06);
  --ring: 0 0 0 3px rgba(31,138,118,.20);

  /* ---- Motion ---- */
  --ease: cubic-bezier(.2,.6,.2,1);
  --dur: 160ms;
}

/* ---- Dark theme (auto by OS, or forced via [data-theme]) ---- */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0e1815; --surface: #16221d; --surface-2: #1b2823; --surface-3: #21302a;
    --ink: #e9efeb; --ink-soft: #c2ccc6; --muted: #91a099; --line: #263630; --line-strong: #32453d;
    --primary: #3fb59e; --primary-hover: #55c2ad; --primary-ink: #8fd9cb;
    --primary-soft: rgba(63,181,158,.14); --primary-softer: rgba(63,181,158,.07); --on-primary: #06231d;
    --email: #4aa5b5; --text-c: #7fb884; --good: #45b48f; --warn: #d3a24e;
    --danger: #e08a7f; --danger-soft: rgba(224,138,127,.12);
    --gold: #d8b054; --gold-2: #e6c877; --gold-soft: rgba(216,176,84,.16); --gold-ink: #f0dca6;
    --silver: #9fabb2; --silver-2: #c6cfd4; --silver-ink: #dbe3e7;
    --bronze: #bd8850; --bronze-2: #d6a374; --bronze-ink: #ecd0af;
    /* Inbox tints, lifted for a dark surface — same twelve hues, same even weighting. */
    --tint-teal:   #4bc0a9; --tint-blue:   #6aa8db; --tint-indigo: #949cea; --tint-violet: #b08fdf;
    --tint-plum:   #d38cc6; --tint-rose:   #e5919e; --tint-clay:   #dda078; --tint-amber:  #cbb45f;
    --tint-olive:  #a6c06d; --tint-green:  #6ec288; --tint-cyan:   #5dbccd; --tint-slate:  #9aaeba;
    --sh-1: 0 1px 2px rgba(0,0,0,.28); --sh-2: 0 2px 10px rgba(0,0,0,.34); --sh-3: 0 10px 30px rgba(0,0,0,.44);
    --ring: 0 0 0 3px rgba(63,181,158,.28);
  }
}
:root[data-theme="dark"] {
  --bg: #0e1815; --surface: #16221d; --surface-2: #1b2823; --surface-3: #21302a;
  --ink: #e9efeb; --ink-soft: #c2ccc6; --muted: #91a099; --line: #263630; --line-strong: #32453d;
  --primary: #3fb59e; --primary-hover: #55c2ad; --primary-ink: #8fd9cb;
  --primary-soft: rgba(63,181,158,.14); --primary-softer: rgba(63,181,158,.07); --on-primary: #06231d;
  --email: #4aa5b5; --text-c: #7fb884; --good: #45b48f; --warn: #d3a24e;
  --danger: #e08a7f; --danger-soft: rgba(224,138,127,.12);
  --gold: #d8b054; --gold-2: #e6c877; --gold-soft: rgba(216,176,84,.16); --gold-ink: #f0dca6;
  --silver: #9fabb2; --silver-2: #c6cfd4; --silver-ink: #dbe3e7;
  --bronze: #bd8850; --bronze-2: #d6a374; --bronze-ink: #ecd0af;
  /* Inbox tints, lifted for a dark surface — keep in step with the @media block above. */
  --tint-teal:   #4bc0a9; --tint-blue:   #6aa8db; --tint-indigo: #949cea; --tint-violet: #b08fdf;
  --tint-plum:   #d38cc6; --tint-rose:   #e5919e; --tint-clay:   #dda078; --tint-amber:  #cbb45f;
  --tint-olive:  #a6c06d; --tint-green:  #6ec288; --tint-cyan:   #5dbccd; --tint-slate:  #9aaeba;
  --sh-1: 0 1px 2px rgba(0,0,0,.28); --sh-2: 0 2px 10px rgba(0,0,0,.34); --sh-3: 0 10px 30px rgba(0,0,0,.44);
  --ring: 0 0 0 3px rgba(63,181,158,.28);
}
:root { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

/* ---------- Base ---------- */
* { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  font-family: var(--font);
  font-size: var(--t-base);
  color: var(--ink);
  background: var(--bg);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow: hidden;
  font-feature-settings: "cv02","cv03","cv04","ss01";
}
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(60% 50% at 15% 0%, var(--primary-softer), transparent 70%),
    radial-gradient(50% 45% at 100% 100%, rgba(191,145,48,.05), transparent 70%);
}
h1, h2, h3 { letter-spacing: -.01em; }
.num, .coin-pill span, .lb-coins, .count, .level-badge { font-variant-numeric: tabular-nums; }
button { font: inherit; cursor: pointer; border: none; background: none; color: inherit;
  transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease); }
input, select, textarea { font: inherit; color: var(--ink); }
a { color: var(--primary-ink); }
.hidden { display: none !important; }
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-sm); }

* { scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: var(--r-full); border: 2px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-track { background: transparent; }

/* ---------- Login ---------- */
.login-wrap { min-height: 100%; display: grid; place-items: center; padding: var(--s5); }
.login-card { width: min(400px, 100%); background: var(--surface); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); padding: var(--s6); border: 1px solid var(--line); position: relative; }
.login-card h1 { margin: 0 0 var(--s2); font-size: var(--t-xl); font-weight: 700; color: var(--ink); }
.login-brandmark { width: 44px; height: 44px; border-radius: var(--r-md); background: var(--primary);
  display: grid; place-items: center; margin-bottom: var(--s4); box-shadow: var(--sh-2); }
.login-brandmark svg { width: 24px; height: 24px; color: var(--on-primary); }
.login-card p.sub { margin: 0 0 var(--s5); color: var(--muted); font-size: var(--t-base); }
.field { margin-bottom: var(--s4); }
.field label { display: block; font-size: var(--t-xs); color: var(--muted); margin-bottom: var(--s2); font-weight: 600; letter-spacing: .02em; }
.field input { width: 100%; padding: 12px var(--s4); border-radius: var(--r-md); border: 1px solid var(--line-strong);
  background: var(--surface-2); color: var(--ink); transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.field input:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.btn-primary { width: 100%; padding: 13px; background: var(--primary); color: var(--on-primary); font-weight: 600; border-radius: var(--r-md); box-shadow: var(--sh-1); }
.btn-primary:hover { background: var(--primary-hover); }
/* "Signing in…" busy state: the button keeps its full-strength green (it's working, not
   unavailable) but can't be clicked again. The spinner reuses the app's existing `.btn-spin`. */
.btn-primary:disabled { opacity: .92; cursor: default; }
.err { color: var(--danger); font-size: var(--t-sm); margin: var(--s3) 0 0; min-height: 18px; }
/* "First time here? Set up my account" / "Back to sign in" link rows on the login + setup cards. */
.login-alt { text-align: center; margin: var(--s3) 0 0; font-size: var(--t-sm); color: var(--muted); }
.login-alt .linklike { color: var(--primary-ink); font-weight: 600; text-align: center; }
.login-alt .linklike:hover { text-decoration: underline; }
.login-note { width: min(400px, 100%); margin: var(--s4) auto 0; text-align: center; color: var(--muted);
  font-size: var(--t-xs); line-height: 1.5; display: flex; align-items: center; justify-content: center; gap: 6px; }
.login-note svg { width: 14px; height: 14px; flex: none; }
/* Ownership / authorship credit — this software is OWNED & OPERATED by Credence Management
   Services LLC, not built by the clinic it serves. Shown on the sign-in card, the top bar, and
   printable guide footers so attribution is unmistakable everywhere. */
.owner-credit { margin: 0 0 var(--s4); font-size: var(--t-sm); color: var(--muted); }
.owner-credit strong { color: var(--ink); font-weight: 650; }
.login-legal { width: min(400px, 100%); margin: var(--s3) auto 0; text-align: center;
  color: var(--muted); font-size: var(--t-xs); line-height: 1.5; opacity: .85; }
/* Gentle entrance for the sign-in card (first paint only; reduced-motion safe below). */
.login-card { animation: loginCardIn .6s var(--ease) both; }

/* ---------- Sign-in boot ("boots to a heartbeat") ----------
   A full-screen loader shown over a real sign-in while the app fetches its data, then
   faded out to reveal the workspace. Uses the design tokens above, so it follows light/dark
   automatically. All motion collapses to a quiet fade under prefers-reduced-motion. */
.boot {
  position: fixed; inset: 0; z-index: 200;
  display: grid; place-items: center; padding: var(--s5);
  background:
    radial-gradient(70% 55% at 15% 0%, var(--primary-softer), transparent 70%),
    radial-gradient(60% 50% at 100% 100%, rgba(191,145,48,.05), transparent 70%),
    var(--bg);
  opacity: 1; transition: opacity .45s var(--ease);
}
.boot.boot-out { opacity: 0; }
.boot-inner { width: min(520px, 100%); text-align: center; }

.boot-mark {
  width: 58px; height: 58px; margin: 0 auto var(--s5); border-radius: var(--r-lg);
  background: var(--primary); display: grid; place-items: center; box-shadow: var(--sh-3);
  position: relative; animation: bootMarkIn .6s var(--ease) both;
}
.boot-mark svg { width: 32px; height: 32px; color: var(--on-primary); }
.boot-mark::after {
  content: ""; position: absolute; inset: 0; border-radius: var(--r-lg);
  border: 2px solid var(--primary); opacity: 0;
}
.boot.boot-ready .boot-mark::after { animation: bootRing .85s var(--ease) forwards; }

.ecg { width: 100%; height: auto; display: block; margin-bottom: var(--s2); overflow: visible; }
.ecg-track { fill: none; stroke: var(--line-strong); stroke-width: 2; }
.ecg-trace {
  fill: none; stroke: var(--primary); stroke-width: 3; stroke-linecap: round; stroke-linejoin: round;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--primary) 55%, transparent));
}
.ecg-blip {
  fill: var(--primary);
  filter: drop-shadow(0 0 7px color-mix(in srgb, var(--primary) 70%, transparent));
}

.boot-readout { display: flex; align-items: baseline; justify-content: center; gap: var(--s3); margin: var(--s3) 0 var(--s5); }
.boot-pct { font-size: 38px; font-weight: 750; letter-spacing: -.03em; line-height: 1; color: var(--ink); font-variant-numeric: tabular-nums; }
.boot-u { font-size: 19px; color: var(--muted); font-weight: 600; margin-left: 2px; }
.boot-routed { font-size: var(--t-sm); color: var(--muted); background: var(--surface-2); border: 1px solid var(--line);
  padding: 5px 11px; border-radius: var(--r-full); font-variant-numeric: tabular-nums; }
.boot-routed b { color: var(--ink); font-weight: 650; }

.boot-steps { list-style: none; margin: 0 auto; padding: 0; width: min(320px, 100%); display: flex; flex-direction: column; gap: var(--s3); text-align: left; }
.boot-step { display: flex; align-items: center; gap: var(--s3); font-size: var(--t-sm); color: var(--muted); transition: color var(--dur) var(--ease); }
.boot-tick { flex: none; width: 20px; height: 20px; border-radius: 50%; border: 1.5px solid var(--line-strong);
  display: grid; place-items: center; position: relative; transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); }
.boot-tick svg { width: 12px; height: 12px; stroke: var(--on-primary); stroke-width: 3; fill: none;
  stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 20; stroke-dashoffset: 20; }
.boot-spin { position: absolute; inset: -1.5px; border-radius: 50%; border: 1.5px solid transparent; border-top-color: var(--primary); opacity: 0; }
.boot-step.active { color: var(--ink-soft); }
.boot-step.active .boot-spin { opacity: 1; animation: bootSpin .7s linear infinite; }
.boot-step.done { color: var(--ink); }
.boot-step.done .boot-tick { background: var(--primary); border-color: var(--primary); }
.boot-step.done .boot-spin { opacity: 0; animation: none; }
.boot-step.done .boot-tick svg { animation: bootCheck .35s .04s forwards; }

.boot-skip { margin-top: var(--s5); font-size: var(--t-xs); color: var(--muted); background: transparent;
  border: 1px solid var(--line); border-radius: var(--r-full); padding: 6px 14px;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.boot-skip:hover { color: var(--ink); border-color: var(--line-strong); }

@keyframes loginCardIn { from { opacity: 0; transform: translateY(10px) scale(.99); } to { opacity: 1; transform: none; } }
@keyframes bootMarkIn { from { opacity: 0; transform: translateY(8px) scale(.96); } to { opacity: 1; transform: none; } }
@keyframes bootRing { 0% { opacity: .55; transform: scale(1); } 100% { opacity: 0; transform: scale(1.85); } }
@keyframes bootSpin { to { transform: rotate(360deg); } }
@keyframes bootCheck { to { stroke-dashoffset: 0; } }

@media (prefers-reduced-motion: reduce) {
  .login-card, .boot-mark { animation: none; }
  .boot-mark::after, .boot-spin { animation: none !important; }
  .boot-step.done .boot-tick svg { animation: none; stroke-dashoffset: 0; }
  .ecg-trace { filter: none; }
}

/* ---------- Birthday sign-in ----------
   On the one day a year it is somebody's birthday, the heartbeat above is swapped for balloons and
   a cake whose candles they blow out. Seen ONLY by the person whose birthday it is.

   Deliberately built from the SAME tokens as everything else — the balloons are the inbox tints,
   which are already held at one lightness so no colour shouts. A party in a clinic app still has
   to look like the clinic app; primary-coloured confetti beats a bag of neon every time.

   ⚠ Every rule here is scoped to `.boot.boot-bday` or `.bday`, so nothing in this block can touch
   a normal sign-in. Removing the class removes the feature. */
.boot.boot-bday {
  background:
    radial-gradient(64% 46% at 50% 108%, color-mix(in srgb, var(--gold) 12%, transparent), transparent 70%),
    radial-gradient(70% 55% at 15% 0%, var(--primary-softer), transparent 70%),
    var(--bg);
}
/* The heartbeat's own parts stand down; the Skip button stays, pinned out of the cake's way. */
.boot.boot-bday .boot-mark,
.boot.boot-bday .ecg,
.boot.boot-bday .boot-readout,
.boot.boot-bday .boot-steps { display: none; }
.boot.boot-bday .boot-inner {
  position: absolute; left: 0; right: 0; bottom: calc(var(--s5) + 4px);
  width: auto; margin: 0; z-index: 3;
}
.boot.boot-bday .boot-skip { margin-top: 0; }

.bday { position: absolute; inset: 0; display: grid; place-items: center; z-index: 2; overflow: hidden; }
.bday[hidden] { display: none; }

/* ---- The filmed scene ----
   A 5-second clip: candles burning, then blown out in a curl of smoke, then confetti. It fills the
   whole overlay, so the light/dark theme stops mattering — the film IS the background — and the
   greeting sits over a scrim at the bottom where it stays readable against any frame.
   `.is-video` is added by JavaScript only once the clip is genuinely PLAYING, so a device that
   blocks autoplay, or a morning when the file will not load, is left on the drawn scene below
   rather than on a black rectangle. */
.bday-video {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
  opacity: 0; transition: opacity .5s var(--ease); z-index: 0; pointer-events: none;
}
.bday.is-video .bday-video { opacity: 1; }
/* Reduced motion gets the film's own opening frame, held perfectly still. */
.bday.is-still {
  background: #f3e6cf center/cover no-repeat;      /* the still's own warm tone, until it loads */
}
.bday.is-still .bday-video { display: none; }

/* Legibility scrim — only under the film. The bottom of every frame is a wooden table in warm
   light, and white text on warm light wood is exactly the combination that goes unreadable. */
.bday-scrim { position: absolute; inset: 0; z-index: 1; opacity: 0; pointer-events: none;
  transition: opacity .5s var(--ease);
  background: linear-gradient(180deg, rgba(22,15,8,.34) 0%, rgba(22,15,8,.04) 24%,
                                      rgba(22,15,8,.10) 42%, rgba(22,15,8,.52) 72%,
                                      rgba(22,15,8,.84) 100%); }
.bday.is-video .bday-scrim, .bday.is-still .bday-scrim { opacity: 1; }

/* With the film up, the drawn cake and balloons stand down and the greeting moves to the lower
   third, in white, over the scrim. */
.bday.is-video .bday-cake, .bday.is-video .bday-balloons,
.bday.is-still .bday-cake, .bday.is-still .bday-balloons { display: none; }
.bday.is-video .bday-inner, .bday.is-still .bday-inner { align-self: end; padding-bottom: min(9vh, 76px); }
.bday.is-video .bday-inner::before, .bday.is-still .bday-inner::before { content: none; }
.bday.is-video .bday-hi, .bday.is-still .bday-hi { color: #fff; text-shadow: 0 2px 18px rgba(20,12,4,.55); }
.bday.is-video .bday-hi span, .bday.is-still .bday-hi span { color: #ffe7a8; }
.bday.is-video .bday-sub, .bday.is-still .bday-sub { color: rgba(255,255,255,.92); text-shadow: 0 1px 12px rgba(20,12,4,.5); }
.bday.is-video .bday-from, .bday.is-still .bday-from { color: rgba(255,255,255,.78); text-shadow: 0 1px 10px rgba(20,12,4,.5); }
/* The Skip button sits on the film too, so it needs the same treatment. */
.boot.boot-bday.boot-film .boot-skip { color: rgba(255,255,255,.85); border-color: rgba(255,255,255,.4); }
.boot.boot-bday.boot-film .boot-skip:hover { color: #fff; border-color: rgba(255,255,255,.8); }
/* Nothing to see behind a full-bleed film — drop the overlay's own gradient so no edge shows. */
.boot.boot-bday.boot-film { background: #1a120a; }
.bday-inner {
  position: relative; z-index: 2; text-align: center; padding: var(--s5);
  animation: bdayIn .7s var(--ease) both;
}
/* ⚠ Clear space for the cake and the greeting. Without this a balloon drifts straight across
   "Happy birthday, Maria" and the one line that matters becomes the hardest thing to read. A soft
   halo of the page's own background dissolves the balloons out behind the words instead of boxing
   the text in a card — the balloons still pass behind, they just stop competing. */
.bday-inner::before {
  content: ""; position: absolute; inset: -6% -14%; z-index: -1; pointer-events: none;
  background: radial-gradient(closest-side,
    var(--bg) 52%, color-mix(in srgb, var(--bg) 72%, transparent) 76%, transparent 100%);
}

/* ---- Balloons. Each one is a div positioned by JS-set custom properties:
   --x (left %), --dur, --delay, --sway, --scale, --hue (its tint colour). ---- */
.bday-balloons { position: absolute; inset: 0; z-index: 1; pointer-events: none; }
.bday-balloon {
  position: absolute; left: var(--x); bottom: -22vh; width: 54px; height: 68px;
  transform: scale(var(--scale, 1));
  animation: bdayRise var(--dur, 13s) linear var(--delay, 0s) infinite;
  will-change: transform;
}
.bday-balloon i {                                    /* the balloon body + its sway */
  display: block; width: 100%; height: 100%; border-radius: 50% 50% 48% 48% / 56% 56% 44% 44%;
  background:
    radial-gradient(30% 26% at 32% 26%, rgba(255,255,255,.62), transparent 62%),
    linear-gradient(165deg, color-mix(in srgb, var(--hue) 88%, white), var(--hue));
  box-shadow: 0 6px 16px color-mix(in srgb, var(--hue) 26%, transparent);
  animation: bdaySway calc(var(--dur, 13s) / 5) ease-in-out infinite alternate;
  position: relative;
}
.bday-balloon i::after {                             /* the knot */
  content: ""; position: absolute; left: 50%; bottom: -5px; width: 8px; height: 7px;
  transform: translateX(-50%); background: var(--hue);
  clip-path: polygon(50% 100%, 0 0, 100% 0); opacity: .9;
}
.bday-balloon b {                                    /* the string */
  position: absolute; left: 50%; top: 100%; width: 1.5px; height: 74px; margin-left: -.75px;
  background: linear-gradient(180deg, color-mix(in srgb, var(--hue) 55%, transparent), transparent);
  border-radius: var(--r-full);
}

/* ---- The cake ---- */
.bday-cake {
  width: min(300px, 74vw); height: auto; display: block; margin: 0 auto var(--s4);
  cursor: pointer; overflow: visible;
  filter: drop-shadow(0 12px 26px rgba(22,40,34,.16));
  transition: transform .25s var(--ease);
}
.bday-cake:hover { transform: translateY(-2px); }
.bday-cake:active { transform: translateY(0) scale(.99); }
.bday-plate { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1.5; }
.bday-tier  { fill: var(--gold-soft); stroke: color-mix(in srgb, var(--gold) 42%, transparent); stroke-width: 1.5; }
.bday-tier-top { fill: color-mix(in srgb, var(--gold-soft) 72%, white); }
.bday-icing { fill: var(--surface); stroke: var(--line); stroke-width: 1.2; }
.bday-sprinkles rect { fill: var(--primary); opacity: .55; }
.bday-sprinkles rect:nth-child(2n) { fill: var(--tint-rose); }
.bday-sprinkles rect:nth-child(3n) { fill: var(--tint-blue); }
.bday-wax    { fill: var(--surface); stroke: var(--line-strong); stroke-width: 1.2; }
.bday-stripe { fill: var(--tint-rose); opacity: .75; }
.bday-wick   { stroke: var(--ink-soft); stroke-width: 2; stroke-linecap: round; }

/* Flames: lit by default, flickering out of step with each other (--i staggers them). */
.bday-fire { transform-origin: center bottom; transition: opacity .28s var(--ease), transform .34s var(--ease); }
.bday-flame    { fill: var(--gold-2); }
.bday-flame-in { fill: #fff6dd; }
.bday-glow     { fill: var(--gold); opacity: .22; }
.bday-candle .bday-fire { animation: bdayFlicker calc(1.1s + var(--i) * .17s) ease-in-out infinite alternate; }
.bday-smoke path {
  fill: none; stroke: var(--muted); stroke-width: 2.2; stroke-linecap: round; opacity: 0;
}

/* Blown out: flames drop away, two wisps of smoke curl up and fade. */
.bday.is-out .bday-fire { opacity: 0; transform: scaleY(.15) translateY(6px); animation: none; }
.bday.is-out .bday-smoke path { animation: bdaySmoke 1.5s var(--ease) forwards; }
.bday.is-out .bday-smoke path:nth-child(2) { animation-delay: .12s; }

.bday-hi {
  margin: 0 0 var(--s2); font-size: clamp(24px, 5.2vw, 34px); font-weight: 750;
  letter-spacing: -.02em; color: var(--ink);
}
.bday-hi span { color: var(--primary-ink); }
.bday-sub  { margin: 0 0 var(--s3); color: var(--muted); font-size: var(--t-md); transition: opacity .3s var(--ease); }
.bday-from { margin: 0; color: var(--muted); font-size: var(--t-xs); opacity: .85; }
.bday.is-out .bday-hi { animation: bdayPop .5s var(--ease); }

@keyframes bdayIn    { from { opacity: 0; transform: translateY(12px) scale(.985); } to { opacity: 1; transform: none; } }
@keyframes bdayPop   { 40% { transform: scale(1.045); } to { transform: scale(1); } }
@keyframes bdayRise  { to { transform: translateY(-135vh) scale(var(--scale, 1)); } }
@keyframes bdaySway  { from { transform: translateX(calc(var(--sway, 14px) * -1)) rotate(-4deg); }
                       to   { transform: translateX(var(--sway, 14px)) rotate(4deg); } }
@keyframes bdayFlicker {
  0%   { transform: scaleY(1)    scaleX(1)    translateY(0); }
  50%  { transform: scaleY(1.09) scaleX(.95)  translateY(-1px); }
  100% { transform: scaleY(.94)  scaleX(1.05) translateY(.5px); }
}
@keyframes bdaySmoke { 0% { opacity: 0; stroke-dashoffset: 0; transform: translateY(2px); }
                       25% { opacity: .5; }
                       100% { opacity: 0; transform: translateY(-26px); } }

/* ⚠ Reduced motion: the cake, the greeting and the blow-out all stay — a birthday you cannot see
   is not an accessible birthday. What goes is everything that MOVES for its own sake: the rising
   balloons hold still near the bottom of the screen as decoration, and the flames stop flickering. */
@media (prefers-reduced-motion: reduce) {
  .bday-inner { animation: none; }
  .bday-balloon { animation: none; bottom: 6vh; opacity: .55; }
  .bday-balloon i { animation: none; }
  .bday-candle .bday-fire { animation: none; }
  .bday.is-out .bday-hi { animation: none; }
  .bday.is-out .bday-smoke path { animation: none; opacity: 0; }
  .bday-cake { filter: none; transition: none; }
}

/* ---------- App shell ---------- */
.app { display: grid; grid-template-rows: auto 1fr; height: 100%; }
.topbar { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s5);
  background: color-mix(in srgb, var(--surface) 88%, transparent); backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 20; }
.brand { font-weight: 700; font-size: var(--t-md); display: flex; align-items: center; gap: var(--s2); color: var(--ink); }
.brand .logo { width: 30px; height: 30px; border-radius: var(--r-sm); background: var(--primary); display: grid; place-items: center; color: var(--on-primary); flex: none; }
.brand .logo svg { width: 17px; height: 17px; }
.brand .brand-name { display: flex; flex-direction: column; line-height: 1.1; }
.brand .brand-owner { font-size: 10px; font-weight: 500; letter-spacing: .01em; color: var(--muted); white-space: nowrap; }
.spacer { flex: 1; }

.icon { width: 18px; height: 18px; display: inline-block; vertical-align: -.18em; flex: none; stroke-width: 2; }
.icon-sm { width: 15px; height: 15px; }
.tab .icon { width: 17px; height: 17px; }

.hud { display: flex; align-items: center; gap: var(--s2); padding: var(--s1) var(--s2); border-radius: var(--r-full); background: var(--surface-2); border: 1px solid var(--line); }
.level-badge { width: 30px; height: 30px; border-radius: var(--r-full); display: grid; place-items: center; font-weight: 700; font-size: var(--t-sm); color: var(--on-primary); background: var(--primary); }
.xp-wrap { width: 84px; height: 6px; border-radius: var(--r-full); background: var(--surface-3); overflow: hidden; }
.xp-fill { height: 100%; width: 0; background: var(--primary); border-radius: var(--r-full); transition: width .5s var(--ease); }
.coin-pill { display: flex; align-items: center; gap: var(--s2); background: var(--gold-soft); color: var(--gold-ink); font-weight: 700; padding: var(--s1) var(--s3); border-radius: var(--r-full); border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent); }
.role-badge { font-size: var(--t-xs); font-weight: 600; padding: var(--s1) var(--s3); border-radius: var(--r-full); background: var(--surface-2); border: 1px solid var(--line); letter-spacing: .01em; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; }
.role-owner { color: var(--primary-ink); border-color: color-mix(in srgb, var(--primary) 35%, transparent); }
.role-superadmin { color: var(--email); } .role-admin { color: var(--primary-ink); }
.icon-btn { background: var(--surface-2); border: 1px solid var(--line); width: 38px; height: 38px; border-radius: var(--r-md); display: grid; place-items: center; color: var(--ink-soft); }
.icon-btn:hover { background: var(--surface-3); color: var(--ink); }
.icon-btn svg { width: 18px; height: 18px; }

/* The left rail carries an extra 16px (owner 2026-08-10) so a full department name — "Medical
   Assistant" — fits beside its caret, icon and unread count instead of ellipsing away.

   ⚠ The width is --rail-w, read by EVERY .body grid below (split reading view, water, AP, minimized
   game rail). It used to be typed out in each of them, which meant a change had to be made five
   times or the layouts silently disagreed — and it is what the minimize strip re-points at 26px. */
.body { --rail-w: 256px; display: grid; grid-template-columns: var(--rail-w) 1fr 320px; gap: var(--s4); padding: var(--s4); min-height: 0; }
@media (max-width: 1100px){ .body { --rail-w: 232px; grid-template-columns: var(--rail-w) 1fr; } .rail-right{ display:none; } }

/* WIDE WORKSPACES. Clinical Compliance renders a log with the same eleven columns as the printed
   TCL-1 form, and in the 1fr middle of a 256/1fr/320 grid that is ~570px — the table survives only
   by scrolling sideways inside itself, which reads as "the table is missing".
   
   ⚠ Driven by :has() rather than a JS class ON PURPOSE. A class on .body has to be removed by
   every other view that might follow, and the one that forgets makes the leaderboard vanish for
   that user until they reload. This selector stops matching the instant #list holds something
   else, so there is no state to leak. Same pattern as the reader-open rule below. */
.body:has(#list .wtr-wrap) { grid-template-columns: var(--rail-w) 1fr; }
.body:has(#list .wtr-wrap) .rail-right { display: none; }
/* Editing a bill at signing time is the same kind of workspace: an invoice that must stay readable
   AND a six-column expense-split table, side by side. In the 1fr middle of a 256/1fr/320 grid the
   form pane is 306–523px on every ordinary screen, which trips the .ap-form container query and
   stacks each field onto its own labelled row (owner 2026-08-27: "all stacked; I want it to look
   like it does when we're creating a bill from an invoice"). Create Bill gets the tabular layout
   for exactly one reason — it takes over the whole window. So this one does too, while editing.
   Measured: the invoice column comes out the SAME OR WIDER than before at every viewport from
   1200px up — 669px → 838px at 1920 — so nothing is traded away to get the table back. */
.body:has(#list .ap-sign-wrap.ap-sign-editing) { grid-template-columns: var(--rail-w) 1fr; }
.body:has(#list .ap-sign-wrap.ap-sign-editing) .rail-right { display: none; }
@media (max-width: 760px){ .body { grid-template-columns: 1fr; } .rail-left{ display:none; } }

.rail-left { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s2) var(--s2) 52px; height: fit-content; max-height: calc(100vh - var(--topbar-h, 60px) - 32px); overflow-y: auto; box-shadow: var(--sh-1);
  scrollbar-width: thin; scrollbar-color: var(--line-strong) transparent;
  /* Height of the pinned chrome row that carries the hide-the-panel arrow — a fixed 24px button
     plus its 2px gap, so it is a constant rather than something to measure. The pinned inbox names
     stop just below it. The AP workspace hides that row, and zeroes this so nothing floats. */
  --rail-chrome-h: 26px; }
/* A long rail (many departments + their folders) scrolls; keep that scrollbar quiet so it does not
   compete with the inbox colours. */
.rail-left::-webkit-scrollbar { width: 10px; }
.rail-left::-webkit-scrollbar-track { background: transparent; }
.rail-left::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: var(--r-full); border: 3px solid var(--surface); }
.rail-left::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ---- Minimize the inbox panel (owner 2026-08-14: "I want anyone to be able to minimize the inbox
   rail"). Until now only the AP Invoices workspace slimmed it, automatically, with no say in it;
   this is the same 26px strip on EVERY screen, by choice, remembered per browser. No capability
   gate — it is a view preference, not a permission. The AP workspace is excluded because it
   already slims the rail its own way (with a hover-to-peek overlay) and the two would fight. */
.body.left-rail-min:not(:has(.ap-inbox-layout)) { --rail-w: 26px; }
.body.left-rail-min:not(:has(.ap-inbox-layout)) .rail-left { padding: 0; overflow: hidden; }
.body.left-rail-min:not(:has(.ap-inbox-layout)) .rail-left > *:not(.tabs-restore) { display: none; }
.body:has(.ap-inbox-layout) .tabs-rail-head { display: none; } /* a button that could do nothing here */
.body:has(.ap-inbox-layout) .rail-left { --rail-chrome-h: 0px; } /* no chrome row → nothing to clear */
/* PINNED (owner 2026-08-25: "I don't see any way to close it when I have scrolled down"). This row
   is the only doorway to hiding the panel, and it used to scroll away with the inboxes — so from
   anywhere but the very top there was no way to shut the panel, and no reminder the arrow exists.
   It now holds the top of the panel no matter how far down the list you are. Opaque, so the rows
   pass underneath it; z-index above the pinned inbox names, which stop below it. */
.tabs-rail-head { display: flex; justify-content: flex-end; margin-bottom: 2px;
  position: sticky; top: 0; z-index: 5; background: var(--surface); }
/* Same trick as the inbox names: a scrolling panel clips at its PADDING edge, so without this strip
   rows would flicker through the 8px of breathing room above this row. */
.tabs-rail-head::before { content: ""; position: absolute; left: 0; right: 0; bottom: 100%;
  height: var(--s2); background: var(--surface); }
.tabs-min-btn { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-md); color: var(--muted); background: transparent; border: 1px solid transparent; }
.tabs-min-btn:hover { color: var(--ink); background: var(--surface-2); border-color: var(--line); }
/* The chevron points at the edge it tucks into, same language as the game rail's arrow. */
.tabs-min-btn svg { width: 16px; height: 16px; transform: rotate(180deg); }
.tabs-restore { display: none; }
.body.left-rail-min:not(:has(.ap-inbox-layout)) .tabs-restore {
  display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 0;
  width: 100%; color: var(--muted); background: transparent; border: 0; }
.body.left-rail-min:not(:has(.ap-inbox-layout)) .tabs-restore:hover { color: var(--ink); background: var(--surface-2); }
.tabs-restore svg { width: 16px; height: 16px; }
.tabs-restore-label { writing-mode: vertical-rl; font-size: var(--t-xs); font-weight: 700; letter-spacing: .1em; text-transform: uppercase; }

/* Small-caps divider naming each half of the panel — the inboxes you work out of, then the
   workspaces and oversight screens (owner 2026-08-10, "easier to read"). Drawn by buildTabs(). */
.tab-group { display: flex; align-items: center; gap: var(--s2); margin: var(--s4) 10px var(--s2);
  font-size: 10px; font-weight: 800; letter-spacing: .11em; text-transform: uppercase; color: var(--ink-soft); }
.tab-group:first-child { margin-top: var(--s1); }
.tab-group::after { content: ""; flex: 1; height: 1px; background: var(--line-strong); }
/* The hover slide is decoration, not information — drop it (not just its duration) when the person
   has asked for reduced motion, so nothing shifts under the pointer. */
@media (prefers-reduced-motion: reduce) {
  .tab:hover, .tab.active:hover, .tab.drop-hot { transform: none; }
}

/* Every row carries its inbox's accent in --tc (set inline by buildTabs → tabTint). Nested folder
   rows inherit their PARENT inbox's tint, so a whole inbox — name, folders and guide line — reads
   as one coloured family. --tc falls back to the house sage if a row is ever rendered without one. */
/* The 6px gap and 7px side padding (were 9px each) are what make the wrapped name below read as
   "Billing / Department" instead of "Billing / Departmen / t". With 9px, the caret, icon chip and
   unread pill left the name only 78px in a 214px rail — narrower than the word "Department" — so it
   had to break mid-word and orphan a letter. Reclaiming those 13px is the whole difference, and it
   costs 2px of row padding that nothing else in the panel depends on. */
.tab { --tc: var(--primary);
  display: flex; align-items: center; gap: 6px; width: 100%; text-align: left; padding: 7px 7px;
  background: transparent; color: var(--ink-soft); border-radius: var(--r-md); margin-bottom: 2px;
  font-weight: 600; font-size: var(--t-base); position: relative;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
/* The name WRAPS onto a second line rather than truncating (owner 2026-09-01: "wrap the text" —
   "Billing Department" was reading as "Billing Depar…" and could only be finished by hovering for
   the tooltip). `overflow-wrap: anywhere` is the guard rail: a name with no spaces in it (a mailbox
   -address folder) would otherwise refuse to break, push the row wider than the panel and bring
   back a sideways scroll. .tab is align-items:center, so the icon chip and the unread pill stay
   centred against a two-line name. buildTabs() still sets the button's title, harmlessly. */
.tab .tab-label { flex: 1 1 auto; min-width: 0; white-space: normal; overflow-wrap: anywhere; line-height: 1.3; }
.tab .icon { color: var(--muted); transition: color var(--dur) var(--ease), background var(--dur) var(--ease); }
.tab:hover { background: color-mix(in srgb, var(--tc) 11%, var(--surface-2)); color: var(--ink); transform: translateX(2px); }
.tab:hover .icon { color: var(--tc); }

/* SELECTED ROW — a solid pill in the inbox's own colour (owner 2026-08-10: "bolder, more
   contrasting"). The 85/15 mix with --ink is what makes one rule work in both themes and at every
   hue: in light mode --ink is near-black so each tint DARKENS enough to carry white text, and in
   dark mode --ink is near-white so it LIGHTENS enough to carry the dark --on-primary. Mixing per
   theme like this is why an amber row is as readable as a violet one. */
.tab.active { background: color-mix(in srgb, var(--tc) 85%, var(--ink)); color: var(--on-primary);
  font-weight: 700; box-shadow: 0 2px 10px color-mix(in srgb, var(--tc) 34%, transparent); }
.tab.active:hover { transform: translateX(2px); background: color-mix(in srgb, var(--tc) 92%, var(--ink)); }
.tab.active .icon { color: var(--on-primary); }
.tab .count { flex: none; font-size: var(--t-xs); background: var(--surface-3); color: var(--ink-soft); border-radius: var(--r-full); padding: 1px var(--s2); min-width: 22px; text-align: center; font-weight: 700; }
/* An inbox with unread mail wears its own colour, so a full inbox is spottable without reading. */
.tab-head .count { background: color-mix(in srgb, var(--tc) 16%, transparent); color: color-mix(in srgb, var(--tc) 78%, var(--ink)); }
.tab.active .count { background: color-mix(in srgb, var(--on-primary) 26%, transparent); color: var(--on-primary); }
/* Mailbox sub-folder nested under its parent dept (e.g. Invoice under AP): indented further than
   before (owner 2026-08-10) and traced by a guide line in the parent inbox's colour, so it is
   unmistakably "inside" the inbox above it. */
.tab-sub { margin-left: 26px; width: calc(100% - 26px); font-weight: 500; font-size: var(--t-sm); color: var(--ink-soft); }
.tab-sub .icon { width: 15px; height: 15px; }
/* The guide runs the full height of the row PLUS the gap below it, so consecutive folder rows join
   into one unbroken line. The segments must meet exactly and never overlap — the colour is
   semi-transparent, so any overlap would stack into a darker blob. */
.tab-sub::after { content:""; position:absolute; left:-14px; top:0; bottom:-2px; width:0;
  border-left: 2px solid color-mix(in srgb, var(--tc) 55%, transparent); }
/* First folder in a group: the line reaches up into the inbox row above, tying the group to its
   parent instead of floating beneath it. */
.tab-sub.tab-branch-start::after { top: -18px; }
/* Last folder in a group: the line stops halfway and curves in, closing the bracket. */
.tab-sub.tab-branch-end::after { bottom: 50%; width: 9px;
  border-bottom: 2px solid color-mix(in srgb, var(--tc) 55%, transparent); border-bottom-left-radius: 7px; }
/* Main inbox NAMES read as headings — set in their own colour at full strength, above the folder
   rows nested beneath them, so the panel's hierarchy is obvious at a glance (owner 2026-07-30,
   colours added 2026-08-10). Placed after .tab-sub on purpose: a SuperAdmin person's oversight
   inbox is both nested (indented) AND a heading.
   65/35 with --ink: strong enough that the hue is unmistakable, dark enough (light theme) / light
   enough (dark theme) to stay well above the 4.5:1 reading threshold on the panel. */
.tab-head { font-weight: 750; font-size: var(--t-base); color: color-mix(in srgb, var(--tc) 65%, var(--ink)); margin-top: 7px; letter-spacing: -.005em;
  /* Opaque, so folder rows slide UNDER the name once it pins itself to the top of the panel (see
     .tab-fold below) instead of showing through it. It is the panel's own background, so a row
     sitting in ordinary flow looks exactly as it did before. Deliberately left at ONE class:
     .tab:hover and .tab.active are two each, so they still win and a selected inbox keeps its
     solid colour pill. */
  background: var(--surface); }
/* The inbox's icon sits on a soft chip of its own colour — the single strongest "which inbox is
   this" cue in the panel, readable long before the name is. Nested folder rows deliberately get no
   chip, so a glance separates inboxes from the folders inside them. */
.tab-head > .icon { color: var(--tc); box-sizing: content-box; padding: 4px; border-radius: 8px;
  background: color-mix(in srgb, var(--tc) 15%, transparent); }
.tab-head:hover > .icon { background: color-mix(in srgb, var(--tc) 24%, transparent); }
.tab.active.tab-head > .icon { color: var(--on-primary); background: color-mix(in srgb, var(--on-primary) 22%, transparent); }
/* Second-level nesting: Drafts/Sent/Junk under a person's inbox inside the SuperAdmin folder. */
.tab-sub2 { margin-left: 48px; width: calc(100% - 48px); }
/* Disclosure caret on a collapsible parent folder (AP/AR): points down when open, right when folded. */
.tab-caret { display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; margin:-2px -2px -2px -3px; flex:none; opacity:.45; transition:transform .15s ease, opacity .15s ease, background .15s ease; transform:rotate(90deg); border-radius:var(--r-sm); }
.tab-caret.collapsed { transform:rotate(0deg); }
.tab-caret:hover { opacity:1; background: color-mix(in srgb, var(--tc) 16%, transparent); }
.tab-caret svg { width:13px; height:13px; }
/* Rows with nothing to fold (Escalated to me, All Completed, Admin…) hold the caret's place, so
   every icon in the panel starts on the same vertical line instead of stepping in and out. */
.tab-caret-spacer { pointer-events: none; }
/* A folded inbox keeps a hint of its colour so you can still pick it out of a collapsed list. */
.tab-head:has(.tab-caret.collapsed) > .icon { opacity: .8; }

/* ---- The inbox NAME pins to the top of the panel while you are inside its folders (owner
   2026-08-25: "this arrow that nests the inboxes is only on the top, so when I scroll all the way
   down and then want to close the inbox I have to scroll all the way up — sometimes I forget that I
   can even close it since I don't see the arrow"). buildTabs() draws every collapsible inbox and
   its folders inside their own .tab-fold box; a pinned row is held by its own parent, so the name
   sticks for exactly as long as that inbox's folders are on screen and lets go the instant the next
   inbox starts. Without the box, each name would stay pinned for the rest of the panel and they
   would pile up over the Workspaces list. The caret rides along with the name, so closing an inbox
   is always one click away no matter how far down its folders you are.
   One accepted cost: the first folder's guide line used to poke up INTO the name row, and the row's
   opaque background now clips it at the row's bottom edge — the same whether pinned or not, so the
   bracket still reads as one piece, and a name that follows its folders ties the group together far
   harder than that stub did. */
.rail-left .tab-fold > .tab-head { position: sticky; top: var(--rail-chrome-h); z-index: 3; }
/* A scrolling panel clips at its PADDING edge, not its border, so rows keep sliding through the 8px
   of breathing room at the top of the panel — which left a sliver of the row above flickering over
   a pinned name. This strip carries the pinned row's own background up through that gap and closes
   it. It rides in the pinned row's layer, so it covers the rows underneath; at rest it lands in the
   7px margin above the name and touches nothing. */
.rail-left .tab-fold > .tab-head::before { content: ""; position: absolute; left: 0; right: 0;
  bottom: 100%; height: var(--s2); background: var(--surface); }
/* Second level — a SuperAdmin person's inbox inside the SuperAdmin folder. It pins one row lower so
   the folder it belongs to stays readable above it, and paints under it. --rail-head-h is measured
   by buildTabs() rather than hard-coded, because the row's height follows the browser's text size. */
.rail-left .tab-fold-2 > .tab-head { top: calc(var(--rail-chrome-h) + var(--rail-head-h, 36px)); z-index: 2; }
/* Drag-to-file: an email row can be dragged onto the Invoice folder tab (makeInvoiceDropTarget). */
.msg.dragging { opacity: .5; }
.tab.drop-hot { background: color-mix(in srgb, var(--tc) 22%, transparent); color: color-mix(in srgb, var(--tc) 65%, var(--ink)); outline: 2px dashed var(--tc); outline-offset: -2px; transform: translateX(2px); }

.rail-center { min-width: 0; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); display: flex; flex-direction: column; overflow: hidden; }
.list-head { padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.list-head h2 { margin: 0; font-size: var(--t-lg); font-weight: 700; }
/* New Message sits at the top of the inbox list (right edge) — composing belongs to the inbox. */
.list-head #composeBtn { margin-left: auto; flex-shrink: 0; white-space: nowrap; }
/* "Take the tour" chip — lives in the header of each AP workspace (Invoice, AP Bills, Sign payments).
   A calm soft-accent pill; hovering (or keyboard-focusing) reveals a one-line explanation bubble. */
.ap-tour-chip { margin-left: auto; position: relative; flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px;
  background: var(--primary-soft); color: var(--primary-ink); border: 1px solid color-mix(in srgb, var(--primary) 22%, transparent);
  font-weight: 650; font-size: var(--t-xs); padding: 6px 12px; border-radius: var(--r-full); cursor: pointer; white-space: nowrap;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.ap-tour-chip:hover { background: color-mix(in srgb, var(--primary) 16%, var(--surface)); border-color: color-mix(in srgb, var(--primary) 40%, transparent); }
.ap-tour-chip svg { width: 14px; height: 14px; color: var(--primary); }
/* The hover bubble: a soft speech bubble that drops below the chip, with a small arrow pointing up at it. */
.ap-tour-tip { position: absolute; top: calc(100% + 9px); right: 0; width: 250px; z-index: 30; text-align: left;
  white-space: normal; /* the chip is nowrap; the bubble must wrap its own text */
  background: var(--surface); color: var(--ink-soft); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--sh-2); padding: 10px 12px; font-weight: 500; font-size: var(--t-xs); line-height: 1.45;
  opacity: 0; visibility: hidden; transform: translateY(-4px); pointer-events: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease), visibility var(--dur) var(--ease); }
.ap-tour-tip b { color: var(--ink); font-weight: 700; }
.ap-tour-tip::before { content:""; position: absolute; top: -5px; right: 18px; width: 9px; height: 9px;
  background: var(--surface); border-left: 1px solid var(--line); border-top: 1px solid var(--line); transform: rotate(45deg); }
.ap-tour-chip:hover .ap-tour-tip, .ap-tour-chip:focus-visible .ap-tour-tip { opacity: 1; visibility: visible; transform: translateY(0); }
.mode-toggle { display: inline-flex; gap: 2px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-full); padding: 2px; }
.mode-toggle button { border: 0; background: transparent; color: var(--muted); font-weight: 600; font-size: var(--t-xs); padding: 4px 12px; border-radius: var(--r-full); cursor: pointer; }
.mode-toggle button.on { background: var(--primary-soft); color: var(--primary-ink); }
/* "Overdue" filter toggle — sits at the right of any inbox header; everyone can use it. */
.overdue-toggle { margin-left: auto; display: inline-flex; align-items: center; gap: 5px; background: var(--surface-2); border: 1px solid var(--line); color: var(--muted); font-weight: 600; font-size: var(--t-xs); padding: 5px 12px; border-radius: var(--r-full); cursor: pointer; white-space: nowrap; }
.overdue-toggle:hover { color: var(--ink); border-color: var(--line-strong); }
.overdue-toggle svg { width: 14px; height: 14px; }
.overdue-toggle .cnt { background: var(--danger); color: #fff; border-radius: var(--r-full); padding: 0 6px; font-weight: 700; }
.overdue-toggle.on { background: color-mix(in srgb, var(--danger) 14%, var(--surface)); border-color: var(--danger); color: var(--danger); }
.cat-filter { display: flex; flex-wrap: wrap; gap: var(--s2); padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); }
.cat-filter button { padding: 6px var(--s3); border-radius: var(--r-full); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-soft); font-weight: 600; font-size: var(--t-sm); white-space: nowrap; display: inline-flex; align-items: center; gap: 5px; }
.cat-filter button:hover { background: var(--surface-3); }
.cat-filter button.active { background: var(--primary); color: var(--on-primary); border-color: transparent; }
.list { overflow-y: auto; padding: var(--s2); }
.msg { display: grid; grid-template-columns: 42px 1fr auto; gap: var(--s3); padding: var(--s3); margin-bottom: var(--s2); border-radius: var(--r-md); cursor: pointer; border: 1px solid transparent; position: relative; background: var(--surface); transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.msg:hover { background: var(--surface-2); border-color: var(--line); }
.msg.selected { background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 32%, transparent); }
.msg:focus-visible { box-shadow: var(--ring); }
.msg.overdue { border-color: color-mix(in srgb, var(--warn) 45%, var(--line)); background: color-mix(in srgb, var(--warn) 6%, var(--surface)); }
.msg.overdue::before { background: var(--warn); }
.overdue-flag { color: var(--warn); font-weight: 700; font-size: var(--t-xs); display: inline-flex; align-items: center; gap: 3px; }
.avatar { width: 42px; height: 42px; border-radius: var(--r-md); display: grid; place-items: center; font-weight: 700; color: var(--on-primary); font-size: var(--t-base); }
.avatar svg { width: 18px; height: 18px; }
.av-email { background: var(--email); }
.av-text { background: var(--text-c); }
.msg::before { content:""; position:absolute; left:0; top:12px; bottom:12px; width:3px; border-radius:0 var(--r-full) var(--r-full) 0; background: var(--email); }
.msg.text-type::before { background: var(--text-c); }
.msg .prev .type-tag { font-weight: 600; }
.msg.email-type .who .type-tag { color: var(--email); } .msg.text-type .who .type-tag { color: var(--text-c); }
.msg .who { font-weight: 600; display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; color: var(--ink); min-width: 0; }
/* When the name + channel/folder pills can't fit on one line, let them wrap to a second
   line INSIDE this column instead of spilling sideways onto the status/time column. Each
   pill stays whole (never breaks mid-word) and keeps its natural size, just drops down. */
.msg .who > .chip { flex: 0 0 auto; white-space: nowrap; max-width: 100%; }
.msg .prev { color: var(--muted); font-size: var(--t-sm); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.msg .prev b { color: var(--ink-soft); font-weight: 600; }
.msg .meta { text-align: right; color: var(--muted); font-size: var(--t-xs); display:flex; flex-direction:column; gap:6px; align-items:flex-end; }
.chip { font-size: var(--t-xs); font-weight: 600; padding: 2px var(--s2); border-radius: var(--r-full); display:inline-flex; align-items:center; gap:4px; }
.chip-new { background: var(--primary-soft); color: var(--primary-ink); }
.chip-pending { background: color-mix(in srgb, var(--warn) 14%, var(--surface)); color: var(--warn); }
.chip-resolved { background: color-mix(in srgb, var(--good) 14%, var(--surface)); color: var(--good); }
.chip-notes { background: var(--surface-3); color: var(--muted); }
.chip-loc { background: var(--surface-3); color: var(--muted); }
/* "Somebody is on this" markers (2026-08-14). Both are deliberately CALM — they report that work
   is happening, so they must not compete with the red overdue flag or the green Resolved.
   .chip-worked = internal notes exist (count + who wrote the last one).
   .chip-waiting = parked on an outside party, still inside the promised date. Once that date
   passes the row switches to .chip-late instead, which is the one that asks for attention. */
.chip-worked { background: var(--primary-softer); color: var(--primary-ink); border: 1px solid var(--primary-soft); }
.chip-waiting { background: color-mix(in srgb, var(--warn) 12%, var(--surface)); color: var(--warn); }
/* Both use the same optical size as the other inline chip icons. */
.chip-worked .icon, .chip-waiting .icon { width: 12px; height: 12px; }
/* Department-tag pills (multi-select) + the divider between topic and tag chips. */
.cat-sep { width: 1px; align-self: stretch; background: var(--line-strong); margin: 2px 4px; flex: 0 0 1px; }
.tag-row { flex-wrap: wrap; gap: 6px; }
.tag-pill { font-size: var(--t-xs); font-weight: 600; padding: 3px 10px; border-radius: var(--r-full); border: 1px solid var(--line-strong); background: var(--surface); color: var(--muted); cursor: pointer; }
.tag-pill:hover { color: var(--ink); }
.tag-pill.on { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
/* Inline "+ tag" input for personal-inbox tags — matches the pill shape. */
.tag-add-input { font-size: var(--t-xs); font-weight: 600; padding: 3px 10px; width: 84px; border-radius: var(--r-full); border: 1px dashed var(--line-strong); background: var(--surface); color: var(--ink); }
.tag-add-input:focus { outline: none; border-style: solid; border-color: var(--primary); width: 120px; }
/* Staff invite / presence status. */
.stat { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-xs); font-weight: 600; white-space: nowrap; }
.stat svg { width: 13px; height: 13px; }
.stat-dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: 0 0 8px; }
.stat-online { color: #16a34a; }
.stat-active { color: var(--muted); }
.stat-pending { color: #b45309; }
.stat-ok { color: #16a34a; }
.stat-locked { color: var(--danger); }
/* The staff-editor status line can hold a full sentence ("Locked out — clears in..."), so let it wrap. */
.ue-status { margin-bottom: 12px; }
.ue-status .stat { white-space: normal; align-items: flex-start; line-height: 1.4; }
.ue-status .stat-dot { margin-top: 4px; }
.stat-resend { display: block; margin-top: 3px; font-size: var(--t-xs); font-weight: 600; color: var(--primary); }
.stat-resend:hover { text-decoration: underline; }
/* Voicemail: channel chip, in-thread audio player, on-call forward form. */
.chip-ch-vm { background: color-mix(in srgb, var(--gold) 18%, transparent); color: var(--gold-ink); }
.attach-audio { display: block; margin-top: 8px; width: 100%; max-width: 360px; height: 40px; }
.oncall-panel { margin-top: 10px; padding: 10px; border: 1px solid var(--line-strong); border-radius: var(--r-md); background: var(--surface-2); }
.oncall-title { font-weight: 700; font-size: var(--t-sm); display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
.oncall-lbl { display: block; font-size: var(--t-xs); font-weight: 600; color: var(--muted); margin-bottom: 3px; }
.act-list { display: flex; flex-direction: column; gap: 0; max-height: 340px; overflow-y: auto; }
.act-row { display: flex; gap: 10px; padding: 7px 2px; border-bottom: 1px solid var(--line); font-size: 13px; }
.act-row:last-child { border-bottom: 0; }
.act-when { flex: 0 0 108px; color: var(--muted); font-size: 12px; font-variant-numeric: tabular-nums; }
.act-what { flex: 1; color: var(--ink); }
/* Events the software wrote by itself: present, clearly labelled, visually quiet. */
.act-row.act-auto .act-what { color: var(--muted); }
.act-tag { font-size: 10px; font-weight: 700; letter-spacing: .02em; color: var(--muted);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 6px; margin-left: 4px; font-style: italic; }
.act-more { display: block; margin: 8px 0 2px; font-size: var(--t-xs); text-align: left; }
.chip-callback { background: color-mix(in srgb, var(--danger) 15%, transparent); color: var(--danger); font-weight: 700; }
.callback-banner { display: flex; align-items: center; gap: 8px; background: color-mix(in srgb, var(--danger) 10%, transparent); border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent); color: var(--danger); border-radius: var(--r-md); padding: 8px 10px; margin-bottom: var(--s3); font-size: var(--t-sm); }
.callback-banner svg { width: 16px; height: 16px; flex: 0 0 16px; }
.callback-info { display: flex; align-items: center; gap: 8px; background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: 8px 10px; margin-bottom: var(--s3); font-size: var(--t-sm); color: var(--ink-soft); }
.callback-info svg { width: 16px; height: 16px; flex: 0 0 16px; color: var(--primary); }
/* Clickable staff name + the per-user editor modal. */
.linklike { background: none; border: 0; padding: 0; cursor: pointer; color: var(--ink); text-align: left; }
.linklike:hover strong { color: var(--primary); text-decoration: underline; }
.ue-field { margin-bottom: var(--s4); }
.ue-field > label { display: block; font-size: var(--t-xs); font-weight: 700; letter-spacing: .02em; color: var(--muted); margin-bottom: 4px; }
.ue-caps { display: grid; gap: 4px; max-height: 300px; overflow-y: auto; border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s2); }
.ue-cap { display: flex; align-items: center; gap: var(--s3); justify-content: space-between; padding: 4px 6px; }
.ue-cap:not(:last-child) { border-bottom: 1px solid var(--line); }
.ue-cap-label { display: flex; align-items: flex-start; gap: 6px; font-size: var(--t-sm); font-weight: 600; min-width: 0; }
.ue-cap-label svg { width: 13px; height: 13px; color: var(--muted); flex: 0 0 13px; margin-top: 2px; }
.ue-cap-note { display: block; font-weight: 400; color: var(--muted); font-size: var(--t-xs); margin-top: 1px; }
.ue-cap-sel { flex: 0 0 auto; }
.chip-chart { background: color-mix(in srgb, var(--email) 14%, var(--surface)); color: var(--email); }
.chip-esc { background: color-mix(in srgb, var(--danger) 12%, var(--surface)); color: var(--danger); }
/* Footer on a list the server had to truncate — tells you the badge's number is bigger than the
   rows on screen, so a capped box never reads as lost mail. Quiet by design: it is a note, not an
   alert, and must not compete with the conversation rows above it. */
.list-more { padding: var(--s4) var(--s5); text-align: center; color: var(--muted); font-size: var(--t-xs); border-top: 1px solid var(--line); }
.empty { padding: 56px var(--s5); text-align: center; color: var(--muted); }
.empty svg { width: 34px; height: 34px; opacity: .5; margin-bottom: var(--s3); }
.skel-row { display:grid; grid-template-columns:42px 1fr; gap:var(--s3); padding:var(--s3); margin-bottom:var(--s2); }
.skel { background: var(--surface-3); border-radius: var(--r-sm); position: relative; overflow: hidden; }
.skel::after { content:""; position:absolute; inset:0; background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--surface) 60%, transparent), transparent); animation: shimmer 1.3s infinite; }
@keyframes shimmer { to { transform: translateX(100%); } }
.skel-av { width:42px; height:42px; border-radius: var(--r-md); }
.skel-line { height: 11px; margin-bottom: 8px; }

/* The right rail SCROLLS, exactly like .rail-left. <body> is overflow:hidden and .body is a
   height-constrained grid row, so a rail taller than the viewport is simply clipped with no way
   to reach the bottom — which is what happened the moment the leaderboard became a STACK of team
   boards instead of one short list. */
.rail-right { display: flex; flex-direction: column; gap: var(--s4); min-width: 0;
  max-height: calc(100vh - var(--topbar-h, 60px) - 32px); overflow-y: auto; overscroll-behavior: contain;
  padding-bottom: 2px; scrollbar-gutter: stable; }
.card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: var(--s5); }
.card h3 { margin: 0 0 var(--s4); font-size: var(--t-base); font-weight: 700; display: flex; align-items: center; gap: var(--s2); color: var(--ink); }
.card h3 svg { width: 17px; height: 17px; color: var(--muted); }
/* ---- Leaderboard: one board per job team ----------------------------------------------------
   Read top to bottom, a row answers "who / how much / how am I doing": a podium rank chip, the
   name and score on one line, and a slim score bar under them. The name is NOT printed over the
   bar any more — over a variable-width fill it becomes unreadable at exactly the moment you're
   behind, which is when you most want to look. */
.lb-row { display: grid; grid-template-columns: 24px 1fr; align-items: start; gap: var(--s3); margin-bottom: 11px; }
.lb-mid { min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.lb-line { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.lb-name { font-size: var(--t-sm); font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-coins { margin-left: auto; padding-left: var(--s2); font-weight: 700; font-size: var(--t-sm); color: var(--gold-ink); }

/* Rank chip. Plain and quiet by default; the podium fills arrive only once someone has scored. */
.lb-rank { display: flex; align-items: center; justify-content: center; width: 24px; height: 24px; margin-top: -1px;
  border-radius: var(--r-full); background: var(--surface-3); color: var(--muted);
  font-size: var(--t-xs); font-weight: 700; }
.lb-p1 .lb-rank { background: linear-gradient(145deg, var(--gold-2), var(--gold)); color: var(--gold-ink); box-shadow: 0 1px 3px color-mix(in srgb, var(--gold) 45%, transparent); }
.lb-p2 .lb-rank { background: linear-gradient(145deg, var(--silver-2), var(--silver)); color: var(--silver-ink); }
.lb-p3 .lb-rank { background: linear-gradient(145deg, var(--bronze-2), var(--bronze)); color: var(--bronze-ink); }

/* Score bar. Everyone is measured against their OWN team's leader, so a small team's bars are
   never flattened by a bigger team's best day. */
.lb-track { height: 7px; border-radius: var(--r-full); background: var(--surface-3); overflow: hidden; }
.lb-fill { height: 100%; border-radius: var(--r-full); min-width: 3px;
  background: linear-gradient(90deg, color-mix(in srgb, var(--primary) 45%, var(--surface-3)), color-mix(in srgb, var(--primary) 72%, var(--surface-3)));
  transition: width .6s var(--ease); }
.lb-p1 .lb-fill { background: linear-gradient(90deg, var(--gold-2), var(--gold)); }
.lb-p2 .lb-fill { background: linear-gradient(90deg, var(--silver-2), var(--silver)); }
.lb-p3 .lb-fill { background: linear-gradient(90deg, var(--bronze-2), var(--bronze)); }
.lb-me .lb-fill { background: linear-gradient(90deg, var(--primary), var(--primary-hover)); }
.lb-me.lb-p1 .lb-fill { background: linear-gradient(90deg, var(--gold-2), var(--gold)); }

.lb-crown { display: inline-flex; color: var(--gold); flex: none; }
.lb-crown svg { width: 13px; height: 13px; }
/* YOU. A ring and a quiet chip rather than inverted text — your row has to stay readable whether
   you're first or last, and it's the row you look for. */
.lb-me { background: var(--primary-softer); border-radius: var(--r-md); box-shadow: inset 2px 0 0 var(--primary); padding: 6px var(--s2) 7px 6px; margin-left: -6px; margin-right: -6px; }
.lb-you { flex: none; font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--primary); background: var(--primary-soft); border-radius: var(--r-full); padding: 1px 6px; }
/* The gap line: the one number you could act on before lunch. */
.lb-gap { font-size: var(--t-xs); font-weight: 600; color: var(--primary); }

/* Movement since yesterday — a board with a past reads as a race, not a roster. */
.lb-move { flex: none; display: inline-flex; align-items: center; gap: 1px; font-size: 11px; font-weight: 700; }
.lb-move svg { width: 11px; height: 11px; }
.lb-move.up { color: var(--good); }
.lb-move.down { color: var(--muted); }
.lb-move.down svg { transform: rotate(180deg); }

/* ---- The stack of boards (a manager sees several) ---- */
.lb-group + .lb-group { margin-top: var(--s4); padding-top: var(--s4); border-top: 1px solid var(--line); }
.lb-head { display: flex; align-items: center; gap: var(--s2); width: 100%; padding: 0 0 11px; background: none; border: 0; cursor: pointer; text-align: left; }
.lb-team { font-size: var(--t-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); line-height: 1.3; }
.lb-head:hover .lb-team { color: var(--ink); }
.lb-group.mine .lb-team { color: var(--ink); }
/* The team's combined score — something to be part of even on a day you don't win. */
.lb-total { margin-left: auto; flex: none; font-size: var(--t-xs); font-weight: 700; color: var(--gold-ink); background: var(--gold-soft); border-radius: var(--r-full); padding: 2px 8px; font-variant-numeric: tabular-nums; }
.lb-total.zero { color: var(--muted); background: var(--surface-3); font-weight: 600; }
.lb-chev { display: inline-flex; align-items: center; justify-content: center; width: 14px; height: 14px; flex: none; color: var(--muted); opacity: .6; transition: transform .15s var(--ease); transform: rotate(90deg); }
.lb-chev svg { width: 13px; height: 13px; }
.lb-group.shut .lb-chev { transform: rotate(0deg); }
.lb-group.shut .lb-head { padding-bottom: 0; }
.lb-group.shut .lb-body { display: none; }
.lb-note { font-size: var(--t-xs); color: var(--muted); line-height: 1.45; margin: 0 0 var(--s3); }
.lb-more { background: none; border: 0; padding: 0; margin-top: 2px; cursor: pointer; font-size: var(--t-xs); font-weight: 600; color: var(--primary); }
.lb-more:hover { text-decoration: underline; }
/* Someone outside the top few still sees themselves, below a hairline. */
.lb-cut { border-top: 1px dashed var(--line); margin: var(--s2) 0 var(--s3); }
.lb-empty { color: var(--muted); font-size: var(--t-sm); }

.detail { position: fixed; top: 0; right: 0; height: 100%; width: min(480px, 100%); background: var(--surface); border-left: 1px solid var(--line); box-shadow: var(--sh-3); z-index: 40; transform: translateX(100%); transition: transform .28s var(--ease); display: flex; flex-direction: column; }
.detail.open { transform: translateX(0); }
/* Split reading view (wide screens): opening a message SPLITS the workspace instead of the reading
   pane hovering over the conversation list. The reading pane takes the right HALF of the page and
   sits below the top bar (so the toolbar stays usable); the list ("previews") shrinks into the left
   column and stays fully visible. Full-screen "expand" (maximize) is unaffected (:not(.expanded)). */
@media (min-width: 1101px) {
  /* Email-style 3-pane (like Apple Mail / Outlook): nav | slim message-list column | WIDE reading
     pane. The reading pane fills ~58% of the width, capped so the list column never drops below
     ~320px on smaller wide screens. minmax() floors the list so it can't be squeezed to nothing. */
  .app { --reader-w: min(58vw, calc(100vw - 616px)); }
  .detail:not(.expanded) { top: var(--topbar-h, 60px); height: calc(100% - var(--topbar-h, 60px)); width: var(--reader-w); }
  .app:has(.detail.open:not(.expanded)) .body { grid-template-columns: var(--rail-w) minmax(320px, 1fr); margin-right: var(--reader-w); }
  .app:has(.detail.open:not(.expanded)) .rail-right { display: none; }
  /* Draggable divider so each person sets the width that works for them (double-click resets). */
  .app:has(.detail.open:not(.expanded)) .reader-w-resize {
    display: block; position: fixed; top: var(--topbar-h, 60px); bottom: 0;
    right: var(--reader-w); width: 12px; margin-right: -6px; cursor: col-resize; z-index: 41;
  }
  .reader-w-resize::before {
    content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px;
    background: var(--line-strong); transition: background .15s ease, width .15s ease;
  }
  .reader-w-resize:hover::before, .reader-w-resize.dragging::before { width: 4px; margin-left: -2px; background: var(--primary); }
}
/* Divider is hidden unless the split view is active; suppress text selection while dragging. */
.reader-w-resize { display: none; }
.col-resizing, .col-resizing * { cursor: col-resize !important; user-select: none !important; }
.detail-head { padding: var(--s4); border-bottom: 1px solid var(--line); display: flex; gap: var(--s3); align-items: center; background: var(--surface-2); }
.detail-body { padding: var(--s4); overflow-y: auto; flex: 1; }
.detail-body .from { font-weight: 700; }
.thread-subject { font-weight: 700; font-size: var(--t-md); margin-bottom: var(--s4); }
.bubble { max-width: 82%; padding: var(--s3) var(--s4); border-radius: var(--r-lg); margin-bottom: var(--s3); line-height: 1.5; }
.bubble-meta { font-size: var(--t-xs); color: var(--muted); margin-bottom: var(--s1); }
/* AI translation inside a bubble — quiet, clearly labeled, toggleable. */
.trans-link { display: inline-flex; align-items: center; gap: 5px; margin-top: 6px; font-size: var(--t-xs); font-weight: 600; color: var(--muted); background: none; border: none; padding: 0; cursor: pointer; }
.trans-link:hover { color: var(--ink); }
.trans-link svg { width: 13px; height: 13px; }
.bubble-trans { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--line); }
.bubble-trans .trans-label { font-size: var(--t-xs); color: var(--muted); font-weight: 600; margin-bottom: 2px; }
.bubble-trans .trans-text { white-space: pre-wrap; }
.bubble-body { white-space: pre-wrap; word-wrap: break-word; }
.bubble.in { background: var(--surface-2); border: 1px solid var(--line); border-bottom-left-radius: var(--r-sm); margin-right: auto; }
.bubble.out { background: var(--primary); color: var(--on-primary); border-bottom-right-radius: var(--r-sm); margin-left: auto; }
.bubble.out .bubble-meta { color: color-mix(in srgb, var(--on-primary) 82%, transparent); }
.bubble.email-bubble { max-width: 100%; background: transparent; border: 0; padding: 0; }
.bubble.email-bubble .bubble-meta { color: var(--muted); margin-bottom: var(--s2); }
/* Text delivery receipt shown on our own sent bubbles. Inherits the light meta color on the
   primary-colored outbound bubble; delivered = calm green, failed = the app's danger red. */
.delivery-badge { font-weight: 700; white-space: nowrap; }
.delivery-badge.delivery-ok { color: color-mix(in srgb, var(--good) 70%, var(--on-primary)); }
.delivery-badge.delivery-fail { color: #fff; background: var(--danger); border-radius: var(--r-full); padding: 0 6px; }
.delivery-badge.delivery-pending { opacity: .8; }
.email-frame { width: 100%; border: 1px solid var(--line); border-radius: var(--r-md); background: #fff; min-height: 120px; }
.attach-img { max-width: 240px; max-height: 240px; border-radius: var(--r-md); margin-top: var(--s2); display: block; cursor: pointer; border: 1px solid var(--line); }
.attach-chip { display: inline-flex; align-items: center; gap: 6px; margin: var(--s2) var(--s2) 0 0; padding: var(--s2) var(--s3); border-radius: var(--r-md); background: var(--surface-2); border: 1px solid var(--line); color: var(--ink); font-weight: 600; font-size: var(--t-sm); text-decoration: none; }
.attach-chip:hover { background: var(--surface-3); }
.attach-chip svg { width: 15px; height: 15px; color: var(--muted); }
.attach-chip { cursor: pointer; }
/* Small "Preview" pill on a previewable (PDF) attachment chip. */
.attach-hint { margin-left: 4px; padding: 1px 7px; border-radius: 999px; background: var(--primary-soft); color: var(--primary); font-size: 11px; font-weight: 700; }

/* In-app attachment preview window (image / PDF). */
.att-overlay { position: fixed; inset: 0; z-index: 3200; background: rgba(0,0,0,.6); display: flex; align-items: center; justify-content: center; padding: var(--s4); }
.att-card { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: 0 24px 60px rgba(0,0,0,.40); width: min(1000px, 94vw); height: min(88vh, 900px); display: flex; flex-direction: column; overflow: hidden; }
.att-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); flex: 0 0 auto; }
.att-name { display: inline-flex; align-items: center; gap: 8px; min-width: 0; font-weight: 700; font-size: var(--t-sm); color: var(--ink); }
.att-name svg { color: var(--muted); flex: 0 0 auto; }
.att-name-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.att-actions { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.att-btn { display: inline-flex; align-items: center; gap: 6px; padding: 7px 12px; border-radius: var(--r-md); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink); font-weight: 600; font-size: var(--t-sm); cursor: pointer; }
.att-btn:hover { border-color: var(--primary); background: var(--primary-soft); }
.att-x { padding: 7px; }
.att-x svg { width: 16px; height: 16px; }
.att-body { flex: 1 1 auto; min-height: 0; background: var(--bg); display: flex; align-items: center; justify-content: center; overflow: auto; }
.att-img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.att-frame { width: 100%; height: 100%; border: 0; background: #fff; }
.att-loading { color: var(--muted); font-size: var(--t-sm); padding: var(--s4); text-align: center; }
.reply-input-wrap { position: relative; }
.reply-input-wrap textarea { width: 100%; min-height: 66px; padding: var(--s3) 46px var(--s3) var(--s3); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); resize: vertical; }
.reply-input-wrap textarea:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.reply-send { position: absolute; right: var(--s2); bottom: var(--s2); width: 34px; height: 34px; border-radius: var(--r-sm); background: var(--primary); color: var(--on-primary); display: grid; place-items: center; }
.reply-send:hover { background: var(--primary-hover); }
.reply-send svg { width: 16px; height: 16px; }
.notes-box { background: var(--gold-soft); border: 1px solid color-mix(in srgb, var(--gold) 30%, transparent); border-radius: var(--r-md); padding: var(--s3); margin: var(--s4) 0 var(--s2); }
/* Inline tag editor (topic + folder/clinic tags) in the reading pane */
.tag-editor { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; padding: 6px 0; }
.te-field { display: inline-flex; align-items: center; gap: 8px; }
.te-tags { flex: 1 1 auto; min-width: 200px; }
.te-label { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); color: var(--muted); font-weight: 600; white-space: nowrap; }
.te-topic { min-width: 150px; }
.te-chips { display: inline-flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.dtag-chip { display: inline-flex; align-items: center; gap: 2px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-full); padding: 2px 4px 2px 10px; font-size: var(--t-xs); }
.dtag-x { border: 0; background: transparent; color: var(--muted); cursor: pointer; font-size: 15px; line-height: 1; padding: 0 5px; border-radius: var(--r-full); }
.dtag-x:hover { background: var(--surface-3); color: var(--ink); }
.dtag-add { border: 1px dashed var(--line-strong); background: transparent; color: var(--muted); border-radius: var(--r-full); padding: 2px 10px; font-size: var(--t-xs); cursor: pointer; }
.dtag-add:hover { color: var(--primary-ink); border-color: var(--primary); }
.dtag-input { border: 1px solid var(--primary); border-radius: var(--r-full); padding: 2px 10px; font-size: var(--t-xs); min-width: 140px; outline: none; }
.notes-title { font-weight: 700; font-size: var(--t-sm); color: var(--gold-ink); margin-bottom: var(--s2); }
.note-item { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); padding: var(--s2) var(--s3); margin-bottom: 6px; }
.note-item .note-meta { font-size: var(--t-xs); color: var(--muted); margin-bottom: 2px; }
/* eCW patient chart card — quiet clinical context above the thread. */
.ecw-card { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s2); margin-bottom: var(--s3); }
.ecw-title { font-weight: 700; font-size: var(--t-sm); color: var(--ink); margin-bottom: var(--s2); display: flex; align-items: center; gap: 6px; }
.ecw-title svg { color: var(--muted); }
.ecw-tag { margin-left: auto; font-size: var(--t-xs); font-weight: 600; color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-full); padding: 1px var(--s2); letter-spacing: .03em; }
.ecw-body { display: grid; gap: var(--s2); }
.ecw-muted { color: var(--muted); font-size: var(--t-sm); }
/* A clinical section that could NOT be fetched. Warm amber, not alarm red: the data is missing,
   which is worth noticing before you rely on it, but nothing is wrong with the patient. */
.ecw-note-warn { color: var(--warn); font-weight: 600; }
.ecw-patient { display: flex; align-items: center; gap: var(--s3); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-sm); padding: var(--s2) var(--s3); }
.ecw-patient .ecw-info { flex: 1; min-width: 0; }
.ecw-name { font-weight: 600; font-size: var(--t-base); }
/* COMPACT linked-patient row (owner request 2026-08-12: "make the eCW link to patient smaller so
   they have more room to see the email"). This card is inserted at the TOP of #dThread, so every
   pixel it spends pushes the email itself further down the screen. Only the LINKED state is
   tightened — the unlinked/search state keeps its roomier padding because those are real hit
   targets (inputs and buttons), whereas this row is read-only text plus one rarely-used Unlink. */
.ecw-patient.linked { border-color: var(--line-strong); gap: var(--s2); padding: var(--s1) var(--s2); }
.ecw-patient.linked .ecw-name { font-size: var(--t-sm); line-height: 1.3; }
.ecw-patient.linked .ecw-muted { font-size: var(--t-xs); line-height: 1.3; }
.ecw-patient.linked .ecw-unlink { flex: none; padding: 3px var(--s2); font-size: var(--t-xs); }
.ecw-visits { display: grid; gap: 2px; }
.ecw-visit { font-size: var(--t-sm); color: var(--ink-soft); padding-left: var(--s2); border-left: 2px solid var(--line); }
/* PROTECTED CHART — a staff member's own medical record, hidden from everyone but the owner and
   their treating provider. Styled calm and neutral ON PURPOSE: this is not an error and not a
   warning about the patient, it is a boundary working as intended. Amber or red here would read
   as "something is wrong", and staff would escalate a chart that is simply closed. */
.ecw-protected { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3); }
.ecw-protected-head { display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: var(--t-sm); color: var(--ink); margin-bottom: var(--s2); }
.ecw-protected-head svg { color: var(--muted); }
.ecw-protected-body { font-size: var(--t-sm); color: var(--ink-soft); line-height: 1.45; }
/* The standing line under chart search. Quieter than .ecw-muted's normal use — it is a fact
   about the system, not a result of the search that was just run. */
.ecw-protected-hint { font-size: var(--t-xs); margin-top: var(--s2); opacity: .8; }
.ecw-search { display: block; }
/* .ecw-body is a one-column grid, so a bare <button> child stretches edge to edge and reads as a
   heavy call to action at the top of every message. These are all small, occasional actions
   ("Link patient", "Try again"), so pin them to their own width at the left. */
.ecw-body > .btn, .ecw-body > .ghost-btn, .ecw-search-host > .btn { justify-self: start; }
/* Holds either the small "Link patient" button or, once pressed, the 3 search boxes in its place. */
.ecw-search-host { display: grid; gap: var(--s2); }

/* ---- ONE-LINE card (owner 2026-08-14: "it's still too big"). Every unlinked state — no match, a
   strip of possible matches, the Link patient button — collapses onto a single row beside the
   title. The card only unfolds for the states that genuinely need the room: a real chart, the
   protected notice, and the 3 search boxes once someone opens them (see ecwUncompact). It is
   pinned to the TOP of the thread, so its height comes straight out of the message below it. */
.ecw-card.ecw-compact { display: flex; align-items: center; gap: var(--s2); padding: 5px var(--s2); }
.ecw-card.ecw-compact .ecw-title { margin-bottom: 0; flex: none; }
.ecw-card.ecw-compact .ecw-body { display: flex; align-items: center; gap: var(--s2); flex: 1; min-width: 0; }
/* The eCW badge rides margin-left:auto inside the title; on one line that would shove the title
   away from its own body, so it goes to the END of the row instead. */
.ecw-card.ecw-compact .ecw-title .ecw-tag { display: none; }
.ecw-card.ecw-compact::after {
  content: 'eCW'; margin-left: auto; flex: none; font-size: var(--t-xs); font-weight: 600;
  color: var(--muted); border: 1px solid var(--line); border-radius: var(--r-full);
  padding: 1px var(--s2); letter-spacing: .03em; }
/* The matches strip is a full-width disclosure in the stacked card; inline it must not stretch. */
.ecw-card.ecw-compact .ecw-clinical { flex: 0 1 auto; min-width: 0; margin: 0; }
.ecw-card.ecw-compact .ecw-disclosure { padding: 2px var(--s2); }
.ecw-card.ecw-compact .ecw-search-host { display: contents; }
.ecw-card.ecw-compact .ecw-muted { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Opening the matches list unfolds the card (ecwCollapsedMatches) — a list of people cannot live
   on one line, and faking it with absolute positioning would float names over the message. */
/* Shared 3-box eCW patient search (Last name / First name / Date of birth) used everywhere:
   conversation-panel chart search, the Hospital Rounding picker and the provider dialer. */
.ecw-fields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.ecw-fields .ecw-field-dob { grid-column: 1 / -1; }
.ecw-field { min-width: 0; box-sizing: border-box; padding: 7px var(--s3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); color: var(--ink); font-size: var(--t-sm); }
.ecw-field:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-softer); }
/* Clinical summary (ecw.clinical only) — same quiet rail grammar as the visit list.
   Collapsed by default behind a click-to-open disclosure so it never buries the message. */
.ecw-clinical { display: grid; gap: var(--s4); }
.ecw-disclosure { display: flex; align-items: center; gap: var(--s2); width: 100%; padding: 6px var(--s2);
  background: none; border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--ink);
  font: inherit; font-size: var(--t-sm); font-weight: 600; cursor: pointer; text-align: left; }
.ecw-disclosure:hover { background: var(--surface); border-color: var(--line-strong); }
.ecw-caret { color: var(--muted); transition: transform .15s ease; }
.ecw-disclosure[aria-expanded="true"] .ecw-caret { transform: rotate(90deg); }
.ecw-disclosure-hint { margin-left: auto; font-weight: 400; font-size: var(--t-xs); color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em; }
.ecw-clinical-panel { display: grid; gap: var(--s2); padding-top: var(--s2); }
/* Candidate chart matches collapse behind the same disclosure when there are 2+. */
.ecw-suggestions-panel { display: grid; gap: var(--s2); padding-top: var(--s2); }
/* A `display` rule beats the UA `[hidden]{display:none}`, so the collapsed panels would stay
   visible. Re-assert display:none when the `hidden` attribute is set (higher specificity wins). */
.ecw-clinical-panel[hidden], .ecw-suggestions-panel[hidden] { display: none; }
/* Chart sections: a header (label + count) over a list of items. Each item = a bold lead line,
   an optional muted sub-line, and an optional right-aligned value (labs/vitals) — reads like a
   tidy medical summary instead of crammed one-liners. */
.ecw-sec { display: grid; gap: var(--s2); }
.ecw-sec-head { display: flex; align-items: center; gap: var(--s2); padding-bottom: 4px; border-bottom: 1px solid var(--line); }
.ecw-sec-title { font-size: var(--t-xs); font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; }
.ecw-sec-count { font-size: 10px; font-weight: 700; color: var(--muted); background: var(--surface-3); border-radius: var(--r-full); padding: 1px 7px; }
.ecw-items { display: grid; }
.ecw-item { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s3); padding: 5px 0; min-width: 0; }
.ecw-item + .ecw-item { border-top: 1px solid color-mix(in srgb, var(--line) 55%, transparent); }
.ecw-item-main { min-width: 0; }
.ecw-lead { font-size: var(--t-sm); color: var(--ink); font-weight: 500; }
.ecw-sub { font-size: var(--t-xs); color: var(--muted); margin-top: 1px; }
.ecw-tail { flex: none; font-size: var(--t-sm); color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ecw-more { font-size: var(--t-xs); color: var(--muted); padding-top: 2px; }
.ecw-line { font-size: var(--t-sm); color: var(--ink-soft); } /* legacy fallback */
/* Upcoming appointment: a calm sage lead — future, actionable. */
.ecw-item--up .ecw-lead { color: var(--primary-ink); font-weight: 600; }
.ecw-item--doc { cursor: pointer; border-radius: 6px; }
.ecw-item--doc:hover { background: var(--surface-2); }
.ecw-doc-open { color: var(--primary-ink); font-weight: 600; }
/* Referrals & orders: the scheduling-facing block — top of the card, bold, gently boxed to stand out. */
.ecw-orders { border: 1px solid color-mix(in srgb, var(--primary-ink) 28%, var(--line)); background: color-mix(in srgb, var(--primary-ink) 6%, var(--surface)); border-radius: var(--r-md); padding: 8px 10px; margin: 2px 0 10px; }
.ecw-orders .ecw-sec-title { color: var(--primary-ink); }
.ecw-order-lead { font-weight: 700; }
.ecw-order-chip { display: inline-block; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: 1px 6px; border-radius: var(--r-full); margin-right: 6px; background: var(--primary-soft); color: var(--primary-ink); vertical-align: middle; }
/* Insurance: the top-of-chart block — payer + Active badge, then a tidy key/value grid. */
.ecw-insurance { border: 1px solid var(--line-strong); background: var(--surface-2); border-radius: var(--r-md); padding: 8px 10px; margin: 2px 0 10px; }
.ecw-ins-card + .ecw-ins-card { margin-top: 8px; border-top: 1px solid var(--line); padding-top: 8px; }
.ecw-ins-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ecw-ins-payer { font-weight: 700; color: var(--ink); }
.ecw-ins-rank { font-size: var(--t-xs); font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
.ecw-ins-status { margin-left: auto; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: 1px 8px; border-radius: var(--r-full); }
.ecw-ins-status.is-active { background: var(--primary-soft); color: var(--primary-ink); }
.ecw-ins-status.is-off { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
.ecw-ins-grid { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px; margin-top: 5px; font-size: var(--t-sm); }
.ecw-ins-k { color: var(--muted); white-space: nowrap; }
.ecw-ins-v { color: var(--ink); font-variant-numeric: tabular-nums; word-break: break-word; }
/* Allergy: a small amber dot before the substance — noticeable, not alarmist. */
.ecw-item--warn .ecw-lead::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%;
  background: var(--warn); margin-right: 6px; vertical-align: middle; }
/* Missed / no-show appointment: a calm coral alert (not arcade-red) — needs a reschedule call. */
.ecw-missed { background: color-mix(in srgb, var(--danger) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger) 30%, transparent); border-radius: var(--r-md); padding: var(--s2) var(--s3); }
.ecw-missed .ecw-sec-head { border-bottom-color: color-mix(in srgb, var(--danger) 25%, transparent); }
.ecw-missed .ecw-sec-title { color: var(--danger); }
.ecw-missed .ecw-lead { color: var(--danger); font-weight: 600; }
.ecw-missed .ecw-item + .ecw-item { border-top-color: color-mix(in srgb, var(--danger) 20%, transparent); }
/* Billers' insurance-only card: same quiet section styling as the rest of the chart card. */
.ecw-billing { display: grid; gap: var(--s2); }
/* Chart preview (View-without-Link): quiet indented panel under a result row. */
.ecw-preview { display: grid; gap: var(--s4); border-left: 2px solid var(--line-strong); padding: var(--s2) 0 var(--s2) var(--s3); margin: 2px 0 var(--s2); }
/* Chart pop-out (View): its own WIDE ~half-page drawer so the chart has room without widening the
   reading window. Sits just left of the 480px reading slide-over; below 820px it goes full-width.
   The `max-width` clamp guarantees it never runs off the left edge on in-between widths. */
/* Chart pop-out: a calm FLOATING card docked just LEFT of the reading pane. No dimming backdrop —
   it's a working panel, not a modal, so the message + reply composer stay fully visible and usable
   for drafting side by side. Its `right` is set inline from the real reader width
   (repositionChartDrawer in app.js) so it never overlaps the message; the value here is the fallback. */
.ecw-chart-drawer { position: fixed; top: calc(var(--topbar-h, 60px) + 10px); bottom: 10px; right: min(480px, 100%);
  width: min(46vw, 600px); max-width: calc(100vw - 24px);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); z-index: 45; display: flex; flex-direction: column; overflow: hidden;
  transform: translateY(8px) scale(.985); opacity: 0; transform-origin: bottom right;
  transition: transform .2s var(--ease), opacity .2s var(--ease); }
.ecw-chart-drawer.open { transform: none; opacity: 1; }
.ecw-chart-drawer-head { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, var(--surface-2), var(--surface)); display: flex; align-items: flex-start; gap: var(--s3); }
.ecw-chart-drawer-title { font-weight: 700; font-size: var(--t-md); color: var(--ink);
  display: flex; align-items: center; gap: var(--s2); line-height: 1.25; }
.ecw-chart-drawer-title::before { content: ''; width: 9px; height: 9px; border-radius: 3px;
  background: var(--primary); box-shadow: 0 0 0 3px var(--primary-soft); flex: none; }
.ecw-chart-drawer-sub { font-size: var(--t-xs); color: var(--muted); margin-top: 3px; }
.ecw-chart-drawer-head .icon-btn { margin-left: auto; flex: none; }
.ecw-chart-drawer .ecw-preview { margin: 0; border-left: 0; padding: var(--s5); overflow-y: auto; flex: 1; align-content: start; }
/* Narrow screens: reading pane is full-width, so the chart becomes a full-screen sheet. */
@media (max-width: 820px) { .ecw-chart-drawer { top: var(--topbar-h, 56px); bottom: 0; right: 0 !important;
  left: 0; width: 100%; max-width: 100%; border: 0; border-radius: 0; } }
.detail-actions { padding: var(--s4); border-top: 1px solid var(--line); display: grid; gap: var(--s3); background: var(--surface-2); }
.action-row { display: flex; gap: var(--s2); flex-wrap: wrap; }

/* Reading panel (NON-expanded / stacked view): cap the composer's height so the message above it
   stays readable — the composer scrolls inside its cap. The cap (--composer-h) is user-adjustable
   via the drag handle below the message, and saved per person. Default: a bit over half the panel. */
.detail:not(.expanded) .detail-actions { flex: 0 0 auto; max-height: var(--composer-h, 52vh); overflow-y: auto; }
/* Drag handle between the message and the composer. Hidden in the two-pane expanded view (where
   the message already fills the whole left column). */
.reader-resize {
  flex: 0 0 auto; height: 14px; cursor: ns-resize; touch-action: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-2); position: relative; z-index: 2;
}
.reader-resize::before {
  content: ""; width: 46px; height: 4px; border-radius: var(--r-full);
  background: var(--line-strong); transition: background .15s var(--ease);
}
.reader-resize:hover::before, .reader-resize.dragging::before { background: var(--primary); }
.detail.expanded .reader-resize { display: none; }

.btn { padding: var(--s3) var(--s4); border-radius: var(--r-md); font-weight: 600; font-size: var(--t-base); border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); display: inline-flex; align-items: center; justify-content: center; gap: 6px; }
.btn:hover { background: var(--surface-2); }
.btn:active { transform: translateY(.5px); }
.btn svg { width: 16px; height: 16px; }
.btn-done { background: var(--primary); color: var(--on-primary); border-color: transparent; }
.btn-done:hover { background: var(--primary-hover); }
.btn-good { background: var(--good); color: #fff; border-color: transparent; }
.btn-warn { background: var(--warn); color: #fff; border-color: transparent; }
.btn-esc  { background: var(--surface); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--line)); }
.btn-esc:hover { background: var(--danger-soft); }
.btn-good:hover { filter: brightness(.96); } .btn-warn:hover { filter: brightness(.96); }
/* Junk / Promo — deliberately muted so it never competes with the green Resolved. Neutral at
   rest, a calm amber on hover (a "set aside", not an alarm). */
.btn-junk { color: var(--muted); }
.btn-junk .icon { color: var(--muted); }
.btn-junk:hover {
  color: var(--warn);
  border-color: color-mix(in srgb, var(--warn) 42%, var(--line-strong));
  background: color-mix(in srgb, var(--warn) 8%, var(--surface));
}
.btn-junk:hover .icon { color: var(--warn); }
/* The "junk / promo" archive chip + the Completed-box view toggle when it's active. */
.chip-junk { background: color-mix(in srgb, var(--muted) 16%, var(--surface)); color: var(--muted); }
.ct-junk-btn { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; cursor: pointer; }
.ct-junk-btn.on {
  border-color: var(--warn); color: var(--warn);
  background: color-mix(in srgb, var(--warn) 10%, var(--surface));
}

.admin-table { width: 100%; border-collapse: collapse; }
.admin-table th, .admin-table td { text-align: left; padding: var(--s3); border-bottom: 1px solid var(--line); font-size: var(--t-base); }
.admin-table th { color: var(--muted); font-weight: 600; font-size: var(--t-xs); letter-spacing: .02em; }
.pill-select { padding: var(--s2) var(--s3); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); }
.pill-select:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
/* Admin portal section nav: jump-chips at the top, sticky so long scrolls stay navigable. */
.admin-nav { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; position: sticky; top: 0; z-index: 5; background: var(--surface); padding: 8px 0; }
.admin-nav button { cursor: pointer; font-weight: 600; }
.admin-nav button:hover { color: var(--primary); border-color: var(--primary); }

/* Inbox search + sort (list header): a real search field, not a squished pill. On phones both
   drop to their own full-width row under the title so they're impossible to miss. */
.box-search { flex: 1 1 200px; min-width: 160px; max-width: 340px; }
#boxSort { flex-shrink: 0; }
@media (max-width: 760px) {
  .box-search { flex-basis: 100%; max-width: none; order: 9; }
  #boxSort { order: 10; margin-left: auto; }
}

.toasts { position: fixed; top: 70px; left: 50%; transform: translateX(-50%); z-index: 3500; display: flex; flex-direction: column; gap: var(--s2); align-items: center; pointer-events: none; }
.toast { background: var(--surface); border: 1px solid var(--line); box-shadow: var(--sh-3); border-radius: var(--r-full); padding: var(--s3) var(--s5); font-weight: 600; font-size: var(--t-base); color: var(--ink); display: flex; align-items: center; gap: var(--s2); animation: toast-in .22s var(--ease); }
.toast.celebrate { background: var(--primary); color: var(--on-primary); }
.toast.house { font-size: var(--t-base); }
.toast.house-plus { background: var(--gold-soft); color: var(--gold-ink); border-color: color-mix(in srgb, var(--gold) 30%, transparent); }
.toast.house-minus { background: var(--surface); color: var(--ink); }
/* A refusal has to be readable ON TOP of the panel that raised it. Vendor panels are
   .chooser-overlay at z-index 3000, so the old z-index:60 painted every error toast BEHIND the
   dark backdrop: a rejected bank-details save looked exactly like nothing happening at all
   (2026-09-02 - a vendor's banking was retyped several times and never once reached the server). */
.toast.toast-bad { background: color-mix(in srgb, var(--danger) 12%, var(--surface)); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 38%, transparent); }
@keyframes toast-in { from { transform: translateY(-8px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
.coin { display:inline-block; width:18px; height:18px; border-radius:50%; background: radial-gradient(circle at 35% 30%, var(--gold-2), var(--gold) 60%, var(--gold-ink)); border: 1px solid color-mix(in srgb, var(--gold-ink) 50%, transparent); }
.coin.spin { animation: coinspin .7s var(--ease); }
@keyframes coinspin { 0% { transform: rotateY(0); } 100% { transform: rotateY(360deg); } }

.fxlayer { position: fixed; inset: 0; pointer-events: none; z-index: 65; overflow: hidden; }
/* ⚠ The sign-in overlay sits at z-index 200 with an OPAQUE background, so birthday confetti thrown
   while it is up would fall behind it and be invisible. beginBoot().birthday() adds this class for
   exactly as long as the cake is on screen, and finish() takes it off again. */
body.bday-fx .fxlayer { z-index: 260; }
/* ⚠ And nothing else may stand in front of the cake. Both of these are pinned to a screen corner
   at a z-index ABOVE the sign-in overlay, and the birthday scene deliberately stays up several
   times longer than the heartbeat — long enough that the "Turn on alerts" card reliably lands on
   top of the candles, and long enough for a websocket that has not connected yet to post
   "Reconnecting…" over the Skip button. Both are only delayed: the class comes off the moment the
   scene does, and each reappears on the workspace where it belongs. */
body.bday-fx .mc-inst,
body.bday-fx .reconn-pill { display: none !important; }
.fxlayer i { position: absolute; }
.confetti-p { width: 8px; height: 12px; top: -20px; opacity: .9; animation: fall linear forwards; }
@keyframes fall { to { transform: translateY(105vh) rotate(540deg); } }
.spark { width: 6px; height: 6px; border-radius: 50%; animation: spark ease-out forwards; }
@keyframes spark { 0% { transform: translate(0,0) scale(1); opacity: 1; } 100% { transform: translate(var(--dx), var(--dy)) scale(.2); opacity: 0; } }
.sparkle { position: absolute; animation: sparklefloat ease-out forwards; opacity: .9; }
@keyframes sparklefloat { 0% { transform: translateY(0) scale(.4); opacity: 0; } 30% { opacity: 1; } 100% { transform: translateY(-64px) scale(1.1); opacity: 0; } }
.balloon { width: 36px; height: 46px; border-radius: 50% 50% 50% 50% / 55% 55% 45% 45%; bottom: -70px; opacity: .9; animation: floatup ease-in forwards; }
.balloon::after { content:""; position:absolute; left:50%; top:100%; width:1px; height:36px; background: rgba(0,0,0,.15); }
@keyframes floatup { 0% { transform: translateY(0) rotate(0); } 100% { transform: translateY(-115vh) rotate(var(--rot)); opacity: 0; } }

.office-choices { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.office-choices button { padding: var(--s4); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); font-weight: 600; }
.office-choices button:hover { background: var(--primary-soft); color: var(--primary-ink); border-color: color-mix(in srgb, var(--primary) 30%, transparent); }

/* Docked bottom-LEFT and compact, so it tucks into the gap beside the sidebar and never
   covers the conversation panel or its reply box (which live on the right). */
.team-dock { position: fixed; left: 16px; bottom: 0; width: 240px; max-width: calc(100vw - 32px); z-index: 50; background: var(--surface); border: 1px solid var(--line); border-bottom: 0; border-radius: var(--r-md) var(--r-md) 0 0; box-shadow: var(--sh-3); }
.team-head { display: flex; align-items: center; gap: var(--s2); padding: 6px var(--s3); cursor: pointer; font-weight: 700; font-size: var(--t-sm); color: var(--on-primary); background: var(--primary); border-radius: var(--r-md) var(--r-md) 0 0; }
.team-head svg { width: 15px; height: 15px; }
.team-badge { background: var(--danger); color: #fff; border-radius: var(--r-full); font-size: var(--t-xs); padding: 1px 7px; }
/* Comfortable again while a chat is actually open. */
.team-dock:has(.team-body:not(.hidden)) { width: 300px; }
/* --- "New chat came in" attention cues (staff keep missing the little dock) --- */
/* (1) A firm one-time SHAKE the instant a chat lands — grabs the eye, then settles. */
.team-dock.attn { animation: dockattn .5s ease-in-out 3; }
@keyframes dockattn {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-6px) rotate(-1.2deg); }
  30% { transform: translateX(6px) rotate(1.2deg); }
  45% { transform: translateX(-5px); }
  60% { transform: translateX(4px); }
  80% { transform: translateX(-2px); }
}
/* (2) A persistent pulsing HALO + warmer header + name-in-bar while anything is unread —
   the whole dock reads differently at a glance, not just a tiny number. Stops on read. */
.team-dock.unread { animation: dockglow 1.6s ease-in-out infinite; }
@keyframes dockglow {
  0%, 100% { box-shadow: var(--sh-3); }
  50% { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 55%, transparent),
                    0 10px 30px color-mix(in srgb, var(--danger) 35%, transparent); }
}
.team-dock.unread .team-head { background: var(--danger); }
.team-dock.unread .team-badge { background: #fff; color: var(--danger); animation: badgepulse 1.1s ease-in-out infinite; }
@keyframes badgepulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.28); } }
/* The momentary shake takes over while it plays (higher specificity), then the glow resumes. */
.team-dock.attn.unread { animation: dockattn .5s ease-in-out 3; }
/* Respect "reduce motion": no movement, but keep a solid highlight so it's still obvious. */
@media (prefers-reduced-motion: reduce) {
  .team-dock.attn, .team-dock.unread, .team-dock.attn.unread { animation: none; }
  .team-dock.unread .team-badge { animation: none; }
  .team-dock.unread { box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 55%, transparent), var(--sh-3); }
}
.team-body { height: 380px; display: flex; flex-direction: column; }
#teamRoster { overflow-y: auto; flex: 1; }
.roster-row { display: flex; align-items: center; gap: var(--s3); padding: var(--s3); border-bottom: 1px solid var(--line); }
.roster-row .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--good); flex: none; }
.roster-row .ri { flex: 1; min-width: 0; } .roster-row .rn { font-weight: 600; } .roster-row .rmeta { font-size: var(--t-xs); color: var(--muted); }
.roster-row .rb { display: flex; gap: 6px; }
.roster-row .rb button { width: 32px; height: 32px; border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-soft); position: relative; display: grid; place-items: center; }
.roster-row .rb button:hover { background: var(--surface-3); }
.roster-row .rb button svg { width: 15px; height: 15px; }
.roster-empty { padding: var(--s6) var(--s4); text-align: center; color: var(--muted); font-size: var(--t-base); }
.mini-badge { position: absolute; top: -5px; right: -5px; background: var(--danger); color: #fff; border-radius: var(--r-full); font-size: 10px; padding: 0 5px; }
#teamChat { display: flex; flex-direction: column; height: 100%; }
.team-chat-head { display: flex; align-items: center; gap: var(--s2); padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); }
/* Chat header identity: name on top, "role · location" muted underneath (so nothing truncates). */
.tc-peer { flex: 1; min-width: 0; line-height: 1.2; }
.tc-peer-name { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tc-peer-meta { font-size: var(--t-xs); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tc-peer-meta:empty { display: none; }
.team-chat-msgs { flex: 1; overflow-y: auto; padding: var(--s3); display: flex; flex-direction: column; gap: 6px; }
.tc-msg { max-width: 80%; padding: var(--s2) var(--s3); border-radius: var(--r-md); font-size: var(--t-base); line-height: 1.4; }
.tc-msg.me { background: var(--primary); color: var(--on-primary); align-self: flex-end; border-bottom-right-radius: var(--r-sm); }
.tc-msg.them { background: var(--surface-2); border: 1px solid var(--line); align-self: flex-start; border-bottom-left-radius: var(--r-sm); }
.team-chat-input { display: flex; gap: 6px; padding: var(--s2); border-top: 1px solid var(--line); }
.team-chat-input input { flex: 1; padding: var(--s3); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); }
.team-chat-input input:focus { outline: none; border-color: var(--primary); }

/* Conversation list (reachable whether or not the coworker is online) */
.roster-row .dot.off { background: var(--muted); opacity: .5; }
.team-list-top { display: flex; align-items: center; gap: 6px; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); }
.team-newchat { flex: 1; padding: 6px var(--s3); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); font-weight: 600; font-size: var(--t-sm); cursor: pointer; }
.team-newchat:hover { background: var(--surface-3); border-color: var(--primary); }
.team-search { flex: 1; min-width: 0; padding: 6px var(--s3); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); font-size: var(--t-sm); }
.team-search:focus { outline: none; border-color: var(--primary); }
.team-linkbtn { background: none; border: 0; color: var(--muted); font-size: var(--t-sm); cursor: pointer; padding: 4px; }
.team-linkbtn:hover { color: var(--ink); }
.roster-row.tc-row { cursor: pointer; }
.roster-row.tc-row:hover { background: var(--surface-2); }
.roster-row.tc-row.unread .rn { font-weight: 800; }
.roster-row.tc-row.unread .rmeta { color: var(--ink-soft); font-weight: 600; }
.tc-unread { background: var(--primary); color: var(--on-primary); border-radius: var(--r-full); font-size: var(--t-xs); font-weight: 700; min-width: 18px; text-align: center; padding: 1px 6px; }
.tc-time { font-size: var(--t-xs); color: var(--muted); margin: -3px 3px 2px; }
.tc-time.me { align-self: flex-end; text-align: right; }
.tc-time.them { align-self: flex-start; }
/* Sender caption above a run of messages: "Full Name, Job Title (Location):" */
.tc-cap { font-size: var(--t-xs); color: var(--muted); font-weight: 700; max-width: 85%; margin: 6px 2px 1px; line-height: 1.25; }
.tc-cap.me { align-self: flex-end; text-align: right; }
.tc-cap.them { align-self: flex-start; }
.roster-row .rn { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Read receipt under your latest message */
.tc-status { align-self: flex-end; font-size: var(--t-xs); color: var(--muted); margin: -2px 2px 2px 0; }
/* "typing…" animated dots */
.tc-typing { align-self: flex-start; display: inline-flex; gap: 4px; padding: 8px 12px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); border-bottom-left-radius: var(--r-sm); }
.tc-typing span { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); display: inline-block; animation: tcbounce 1.2s infinite ease-in-out; }
.tc-typing span:nth-child(2) { animation-delay: .18s; }
.tc-typing span:nth-child(3) { animation-delay: .36s; }
@keyframes tcbounce { 0%, 60%, 100% { transform: translateY(0); opacity: .4; } 30% { transform: translateY(-4px); opacity: 1; } }

.modal-overlay { position: fixed; inset: 0; background: rgba(20,32,28,.4); backdrop-filter: blur(4px); z-index: 70; display: grid; place-items: start center; padding-top: 8vh; }
.modal { width: min(520px, 94vw); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-3); overflow: hidden; }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: var(--s4) var(--s5); border-bottom: 1px solid var(--line); }
.modal-head h3 { margin: 0; font-weight: 700; display: flex; align-items: center; gap: var(--s2); }
.modal-head h3 svg { width: 17px; height: 17px; color: var(--muted); }
.modal-body { padding: var(--s4) var(--s5); max-height: calc(88vh - 56px); overflow-y: auto; }
/* Full rich email composer inside the New Message modal — wider, and scrolls if it gets tall. */
.modal.compose-wide { width: min(760px, 96vw); }
.modal.compose-wide .modal-body { max-height: 82vh; overflow-y: auto; }
.modal.compose-wide #composeRich .composer { border: 0; }
.modal.compose-wide #composeRich .compose-editor { min-height: 220px; }
.field-input { width: 100%; padding: var(--s3) var(--s4); border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); margin-bottom: var(--s2); }
.field-input:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.contact-results { max-height: 320px; overflow-y: auto; }
.contact-row { display: flex; align-items: center; gap: var(--s3); padding: var(--s3); border-radius: var(--r-md); }
.contact-row:hover { background: var(--surface-2); }
.contact-row .ci { flex: 1; min-width: 0; } .contact-row .cn { font-weight: 600; } .contact-row .cd { font-size: var(--t-sm); color: var(--muted); }
.contact-row .ca { display: flex; gap: 6px; }
.contact-row .ca button { padding: var(--s2) var(--s3); border-radius: var(--r-sm); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink); }
.contact-row .ca button:hover { background: var(--surface-3); }
.compose-to { font-weight: 600; padding: var(--s2) 0; }
/* Channel-first compose: pick Email / Text / Call up front, then type who it's for. */
.compose-channels { display: flex; gap: var(--s3); margin-bottom: var(--s3); }
.compose-ch { flex: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 14px 8px 11px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); cursor: pointer;
  color: var(--ink); font-weight: 600; font-size: var(--t-sm);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease), transform var(--dur) var(--ease); }
.compose-ch:hover { border-color: var(--primary); background: var(--primary-soft); transform: translateY(-1px); }
.compose-ch svg { width: 22px; height: 22px; color: var(--primary-ink); }
.compose-ch small { font-weight: 500; font-size: var(--t-xs); color: var(--muted); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.compose-or { text-align: center; color: var(--muted); font-size: var(--t-xs); margin: 0 0 var(--s3); }
.compose-to-row { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s3); position: relative; }
.compose-to-row label { font-size: var(--t-sm); color: var(--muted); font-weight: 600; }
/* Live contact lookup under a recipient field (compose To / Text / Call). */
.suggest-pop { position: absolute; top: calc(100% + 4px); left: 0; right: 0; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--sh-3); z-index: 30; overflow: hidden; }
.suggest-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding: 8px 12px; cursor: pointer; font-size: var(--t-sm); }
.suggest-row:hover { background: var(--primary-soft); }
.suggest-row .sr-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.suggest-row .sr-sub { color: var(--muted); font-size: var(--t-xs); flex-shrink: 0; }

.call-panel { position: fixed; top: 24px; left: 50%; transform: translateX(-50%); z-index: 80; width: min(320px, 92vw); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: var(--s5); text-align: center; }
.call-avatar { width: 64px; height: 64px; border-radius: 50%; margin: 0 auto var(--s3); display: grid; place-items: center; font-size: 26px; font-weight: 700; color: var(--on-primary); background: var(--primary); }
.call-avatar svg { width: 30px; height: 30px; }
.call-name { font-weight: 700; font-size: var(--t-lg); }
.call-status { color: var(--muted); margin: var(--s1) 0 var(--s4); font-weight: 600; }
.call-actions { display: flex; gap: var(--s3); justify-content: center; }
.call-actions .btn { padding: var(--s3) var(--s5); }

/* Screen share — a larger floating window showing the live shared screen (view-only, 45-min cap). */
.share-panel { position: fixed; top: 24px; left: 50%; transform: translateX(-50%); z-index: 85; width: min(720px, 94vw); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: var(--s3); }
.share-head { display: flex; align-items: center; gap: var(--s3); margin-bottom: var(--s2); padding: 0 var(--s1); }
.share-name { font-weight: 700; font-size: var(--t-lg); }
.share-status { color: var(--muted); font-weight: 600; font-size: var(--t-sm); }
.share-actions { margin-left: auto; display: flex; gap: var(--s2); }
.share-actions .btn { padding: var(--s2) var(--s4); }
.share-video { width: 100%; aspect-ratio: 16 / 9; background: #0b0f14; border-radius: var(--r-md); display: block; object-fit: contain; }

/* Video call — remote camera fills the stage, your own camera sits as a small inset (mirrored). */
.video-panel { position: fixed; top: 24px; left: 50%; transform: translateX(-50%); z-index: 85; width: min(720px, 94vw); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: var(--s3); }
.vc-stage { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #0b0f14; border-radius: var(--r-md); overflow: hidden; }
.vc-remote { width: 100%; height: 100%; object-fit: cover; display: block; }
.vc-local { position: absolute; right: var(--s3); bottom: var(--s3); width: 28%; max-width: 168px; aspect-ratio: 4 / 3; object-fit: cover; border-radius: var(--r-sm); border: 2px solid var(--surface); box-shadow: var(--sh-2); background: #0b0f14; transform: scaleX(-1); }
.vc-head { display: flex; align-items: center; gap: var(--s3); margin-top: var(--s2); padding: 0 var(--s1); }
.vc-name { font-weight: 700; font-size: var(--t-lg); }
.vc-status { color: var(--muted); font-weight: 600; font-size: var(--t-sm); }
.vc-actions { margin-left: auto; display: flex; gap: var(--s2); }
.vc-actions .btn { padding: var(--s2) var(--s4); }

.reconn-pill { position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%) translateY(60px); background: var(--ink); color: var(--surface); padding: var(--s2) var(--s4); border-radius: var(--r-full); font-size: var(--t-sm); font-weight: 600; opacity: 0; pointer-events: none; transition: transform var(--dur) var(--ease), opacity var(--dur) var(--ease); z-index: 9999; display: flex; align-items: center; gap: var(--s2); }
.reconn-pill.show { opacity: .96; transform: translateX(-50%) translateY(0); }
.reconn-pill .dot { width: 7px; height: 7px; border-radius: 50%; background: var(--warn); }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
  .fxlayer { display: none !important; }
}

/* ===========================================================================
   Phase 2 — Responsiveness & interaction surfaces
   =========================================================================== */

/* ---- Keyboard focus on list rows ---- */
.msg { outline: none; }
.msg.kbfocus { box-shadow: var(--ring); border-color: color-mix(in srgb, var(--primary) 30%, transparent); }

/* ---- List head hint ---- */
.list-head .hint { margin-left: auto; display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--t-xs); }

/* ---- Skeleton loading ---- */
.skel-wrap { padding: var(--s2); }

/* ---- Error state (an outage must never look like an empty inbox) ---- */
.error-state { padding: 48px var(--s5); text-align: center; color: var(--muted); }
.error-state svg { width: 32px; height: 32px; color: var(--warn); margin-bottom: var(--s3); }
.error-state .msg-text { color: var(--ink); font-weight: 600; margin-bottom: var(--s1); }
.error-state .btn { margin-top: var(--s4); }

/* ---- Undo toast (must accept clicks; .toasts is pointer-events:none) ---- */
.toast-undo { pointer-events: auto; gap: var(--s4); }
.undo-btn { background: transparent; color: var(--primary-ink); font-weight: 700; padding: 3px 10px;
  border-radius: var(--r-sm); border: 1px solid transparent; }
.undo-btn:hover { background: var(--primary-soft); }

/* ---- Command palette (Cmd/Ctrl+K) ---- */
.cmdk-overlay { position: fixed; inset: 0; background: rgba(20,32,28,.45); backdrop-filter: blur(6px);
  z-index: 90; display: grid; place-items: start center; padding-top: 12vh; }
.cmdk { width: min(560px, 94vw); background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--sh-3); overflow: hidden; }
.cmdk-input { width: 100%; padding: var(--s4) var(--s5); border: 0; background: transparent;
  font-size: var(--t-md); outline: none; color: var(--ink); }
.cmdk-list { max-height: 46vh; overflow-y: auto; border-top: 1px solid var(--line); }
.cmdk-item { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s5);
  cursor: pointer; color: var(--ink); font-size: var(--t-base); }
.cmdk-item svg { width: 16px; height: 16px; color: var(--muted); flex: none; }
.cmdk-item .cmdk-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cmdk-item.sel, .cmdk-item:hover { background: var(--primary-soft); }
.cmdk-item .sub { color: var(--muted); font-size: var(--t-xs); margin-left: auto; flex: none; }
.cmdk-empty { padding: var(--s5); text-align: center; color: var(--muted); }
.cmdk-foot { display: flex; gap: var(--s4); padding: var(--s2) var(--s5); border-top: 1px solid var(--line);
  background: var(--surface-2); color: var(--muted); font-size: var(--t-xs); }

/* ---- Keyboard shortcuts overlay (?) ---- */
.shortcuts-overlay { position: fixed; inset: 0; background: rgba(20,32,28,.5); backdrop-filter: blur(6px);
  z-index: 95; display: grid; place-items: center; padding: var(--s4); }
.shortcuts-card { width: min(520px, 94vw); background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: var(--s5); }
.shortcuts-card h3 { margin: 0 0 var(--s4); font-size: var(--t-md); font-weight: 700; }
.sc-row { display: flex; justify-content: space-between; align-items: center; padding: var(--s2) 0; border-bottom: 1px solid var(--line); }
.sc-row:last-child { border-bottom: 0; }
.sc-row span { color: var(--ink-soft); }
kbd { font-family: var(--font); font-size: var(--t-xs); font-weight: 600; background: var(--surface-2);
  border: 1px solid var(--line-strong); border-bottom-width: 2px; border-radius: var(--r-sm);
  padding: 2px 7px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* ---- "Your Progress" card ---- */
.goal-head { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: var(--s2); }
.goal-num { font-weight: 700; font-size: var(--t-lg); color: var(--ink); font-variant-numeric: tabular-nums; }
.goal-target { color: var(--muted); font-size: var(--t-sm); }
.goal-bar { height: 8px; background: var(--surface-3); border-radius: var(--r-full); overflow: hidden; }
.goal-fill { height: 100%; width: 0; background: var(--primary); border-radius: var(--r-full); transition: width .6s var(--ease); }
.goal-fill.reached { background: var(--gold); }
.goal-note { margin-top: var(--s3); color: var(--muted); font-size: var(--t-sm); line-height: 1.45; }
.goal-note strong { color: var(--good); font-weight: 600; }

/* ---- Mobile bottom navigation (the left rail disappears below 760px) ---- */
.mobile-nav { display: none; }
@media (max-width: 760px) {
  .mobile-nav {
    display: flex; position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
    background: color-mix(in srgb, var(--surface) 94%, transparent); backdrop-filter: blur(12px);
    border-top: 1px solid var(--line);
    padding: 6px 4px calc(6px + env(safe-area-inset-bottom, 0px));
  }
  .mobile-nav button { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 3px;
    padding: 6px 2px; border-radius: var(--r-sm); color: var(--muted); font-size: 10px; font-weight: 600; position: relative; }
  .mobile-nav button.active { color: var(--primary-ink); background: var(--primary-soft); }
  .mobile-nav svg { width: 20px; height: 20px; }
  .mobile-nav .mcount { position: absolute; top: 1px; right: 22%; background: var(--primary); color: var(--on-primary);
    border-radius: var(--r-full); font-size: 9px; font-weight: 700; padding: 0 4px; min-width: 15px; text-align: center; }
  .body { padding-bottom: 74px; }
  .detail { width: 100%; }
  .detail-actions { padding-bottom: calc(var(--s4) + env(safe-area-inset-bottom, 0px)); }
  .team-dock { display: none; }
  .topbar { padding: var(--s2) var(--s3); gap: var(--s2); }
  .hud, #officeChip { display: none; }
  .toasts { top: 64px; }
  .cmdk-overlay { padding-top: 6vh; }
}

/* Fix: the right rail's hide-on-narrow rule must come AFTER its base display:flex.
   (In the original file the media query preceded the base rule, so it never won.) */
@media (max-width: 1100px) { .rail-right { display: none; } }
@media (max-width: 760px)  { .rail-left { display: none; } html, body { overflow-x: hidden; } }

/* ==========================================================================
   Phase 3 — Game engine: claims, streaks, shields, badges, adaptive goal
   ========================================================================== */

/* Streak flame in the top bar. Warm gold only when the streak is alive. */
.streak-pill {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 10px; border-radius: var(--r-full);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--muted); font-weight: 700; font-size: var(--t-sm);
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.streak-pill.lit { color: var(--gold-ink); background: var(--gold-soft); border-color: var(--gold-2); }
.streak-pill.lit .icon { color: var(--gold); }

/* "Who's on it" chip in the list rows. Teal = you, quiet gray = a coworker. */
.chip-claim {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--surface-3); color: var(--muted);
  border: 1px solid var(--line);
}
.chip-claim-me { background: var(--primary-soft); color: var(--primary-ink); border-color: transparent; }

/* Banner in the thread panel when a coworker holds the claim. */
.claim-banner {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3); margin-bottom: var(--s2);
  border-radius: var(--r-md); font-size: var(--t-sm); color: var(--ink-soft);
  background: var(--surface-2); border: 1px dashed var(--line-strong);
}
.claim-banner .icon { color: var(--primary); flex: none; }
/* Same banner, but for a wait whose promised date has passed — it asks to be chased, so it warms
   up rather than staying neutral. Solid border (not dashed) reads as "this one is on the clock". */
.claim-banner.waiting-late { border-style: solid; border-color: color-mix(in srgb, var(--warn) 45%, var(--line-strong)); background: color-mix(in srgb, var(--warn) 8%, var(--surface-2)); }
.claim-banner.waiting-late .icon { color: var(--warn); }

/* Claim button — inviting but not shouty. */
.btn-claim { background: var(--primary-soft); color: var(--primary-ink); border-color: transparent; }
.btn-claim:hover { background: var(--primary); color: var(--on-primary); }

/* Paused tools while someone else works the thread. */
.detail-actions button[disabled], .detail-actions textarea[disabled] {
  opacity: .45; cursor: not-allowed; pointer-events: none;
}

/* Progress card: streak line + shield dots + badge shelf. */
.streak-line {
  display: flex; align-items: center; gap: 6px;
  margin-top: var(--s3); font-size: var(--t-sm); color: var(--ink-soft);
}
.streak-line .icon { color: var(--gold); }
.streak-best { color: var(--muted); font-size: var(--t-xs); }
.shield-dots { display: inline-flex; gap: 2px; margin-left: auto; }
.shield-dot .icon { color: var(--line-strong); }
.shield-dot.full .icon { color: var(--primary); }

.badge-row {
  display: flex; align-items: center; gap: var(--s1);
  margin-top: var(--s2); padding-top: var(--s2); border-top: 1px solid var(--line);
}
.badge-tile {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: var(--r-sm);
  background: var(--gold-soft); color: var(--gold-ink);
  border: 1px solid var(--gold-2);
}
.badge-tile .icon { width: 14px; height: 14px; }
.badge-count { margin-left: auto; color: var(--muted); font-size: var(--t-xs); font-weight: 700; }

/* ==========================================================================
   Phase 4 — Manager command center
   ========================================================================== */
.cc-note { color: var(--muted); font-size: var(--t-xs); margin: 8px 2px 0; line-height: 1.5; }
.cc-card { margin-top: 12px; box-shadow: none; }
.cc-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; }
.cc-head h3 { margin: 0; }
/* Collapsible Command Center cards — click the header to expand / minimize. The big reports
   default to collapsed so the section reads as a short, scannable list of headings. */
.cc-card.cc-collapsible > .cc-head { cursor: pointer; user-select: none; }
.cc-card.cc-collapsible > .cc-head h3::before {
  content: '▾'; display: inline-block; width: 0.9em; margin-right: 3px;
  color: var(--muted); font-size: 0.8em; vertical-align: middle;
}
.cc-card.cc-collapsible.collapsed > .cc-head h3::before { content: '▸'; }
.cc-card.cc-collapsible.collapsed > *:not(.cc-head) { display: none; }
.cc-card.cc-collapsible.collapsed > .cc-head .action-row { display: none; }
.btn-sm { padding: 5px 10px; font-size: var(--t-xs); }

/* Clinic pulse tiles */
.pulse-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s2); margin-bottom: var(--s2);
}
.pulse-stat {
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s3); box-shadow: var(--sh-1);
}
.pulse-stat.good { border-left: 3px solid var(--good); }
.pulse-stat.warn { border-left: 3px solid var(--warn); }
.pulse-stat.bad  { border-left: 3px solid var(--danger); }
.ps-num { font-size: var(--t-xl); font-weight: 800; color: var(--ink); line-height: 1.1; }
.ps-lbl { display: flex; align-items: center; gap: 4px; color: var(--muted); font-size: var(--t-xs); margin-top: 4px; }
.ps-lbl .icon { width: 13px; height: 13px; }
.trend { font-size: 10px; font-weight: 700; margin-left: auto; padding-left: 6px; }
.trend.up { color: var(--good); }
.trend.down { color: var(--warn); }

/* Aging list */
.aging-list { display: flex; flex-direction: column; }
.aging-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: 9px 4px; border-bottom: 1px solid var(--line); cursor: pointer;
}
.aging-row:last-child { border-bottom: none; }
.aging-row:hover { background: var(--surface-2); }
.aging-who { font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aging-age { color: var(--muted); font-size: var(--t-sm); white-space: nowrap; }
.aging-age.bad { color: var(--danger); font-weight: 700; }

/* Scorecard metric cell colours (manager-only, advisory) */
.sc-table td.m-good { color: var(--good); font-weight: 700; }
.sc-table td.m-warn { color: var(--warn); font-weight: 700; }
.sc-table td.m-bad  { color: var(--danger); font-weight: 700; }

/* Audit log */
.audit-list { margin-top: 8px; max-height: 340px; overflow-y: auto; }
.audit-list.au-inline { max-height: none; overflow: visible; }
.audit-row {
  display: grid; grid-template-columns: 116px 104px 74px 1fr 16px; gap: var(--s2);
  padding: 7px 6px; border-bottom: 1px solid var(--line); font-size: var(--t-xs); align-items: center;
  border-radius: var(--r-sm);
}
.au-time { color: var(--muted); font-variant-numeric: tabular-nums; }
.au-actor { font-weight: 700; display: flex; align-items: center; gap: 6px; min-width: 0; }
.au-actor .who-dot { flex: 0 0 auto; }
.au-what { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.audit-row.au-hasnote .au-what { white-space: normal; }
.au-action { color: var(--primary-ink); font-weight: 600; }
.au-detail { color: var(--ink-soft); }
/* WHAT it was set to — the tag, the reason, the person it went to. The answer, not the label. */
.au-value {
  margin-left: 6px; padding: 1px 7px; border-radius: var(--r-sm); font-weight: 700;
  color: var(--ink); background: var(--surface-3); border: 1px solid var(--line);
}
/* The words someone actually typed. */
.au-note { color: var(--ink-soft); font-style: italic; margin-left: 6px; }
.act-what .au-note { display: block; margin: 3px 0 0 0; font-style: normal;
  padding: 5px 8px; border-left: 2px solid var(--line-strong); background: var(--surface-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; white-space: pre-wrap; }
.au-cid { color: var(--muted); font-size: 0.85em; font-variant-numeric: tabular-nums; }

/* What kind of event this was: real work, just looking, or the system acting on its own. */
.au-kind {
  justify-self: start; padding: 1px 7px; border-radius: 999px; font-size: 10px; font-weight: 700;
  letter-spacing: .02em; white-space: nowrap; border: 1px solid transparent;
}
.au-kind.k-work { color: var(--good); background: color-mix(in srgb, var(--good) 12%, transparent); border-color: color-mix(in srgb, var(--good) 26%, transparent); }
.au-kind.k-view { color: var(--muted); background: var(--surface-2); border-color: var(--line); }
.au-kind.k-review { color: var(--muted); background: transparent; border-color: var(--line-strong); }
.au-kind.k-auto { color: var(--muted); background: transparent; border-color: var(--line); font-style: italic; }
/* Automatic rows sit back visually — present for the record, never competing for attention. */
.audit-row.k-auto .au-action, .audit-row.k-auto .au-actor,
.audit-row.k-review .au-action, .audit-row.k-review .au-actor { font-weight: 500; color: var(--muted); }

/* A row you can open pulls its own task up; the rest stay flat. */
.au-go { color: var(--muted); display: flex; justify-content: flex-end; opacity: 0; transition: opacity .12s ease; }
.audit-row.au-click { cursor: pointer; }
.audit-row.au-click:hover, .audit-row.au-click:focus-visible { background: var(--surface-2); outline: none; }
.audit-row.au-click:hover .au-go, .audit-row.au-click:focus-visible .au-go { opacity: 1; }

/* The headline strip: how much of this was actually work. */
.au-summary {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3);
  margin-top: var(--s3); padding: var(--s3); border: 1px solid var(--line);
  border-radius: var(--r-md); background: var(--surface-2); font-size: var(--t-xs);
}
.au-stat { color: var(--muted); white-space: nowrap; }
.au-stat b { font-size: var(--t-md); font-weight: 700; margin-right: 4px; font-variant-numeric: tabular-nums; }
.au-stat.k-work b { color: var(--good); }
.au-stat.k-view b { color: var(--ink); }
.au-stat.k-review b { color: var(--muted); }
.au-stat.k-auto b { color: var(--muted); }
.au-toggle { margin-left: auto; display: flex; align-items: center; gap: 6px; color: var(--ink-soft); cursor: pointer; white-space: nowrap; }
.au-toggle input { accent-color: var(--primary); }
.au-explain { margin-top: var(--s2); }

@media (max-width: 640px) {
  .audit-row { grid-template-columns: 1fr auto; row-gap: 2px; }
  .au-time { order: 1; } .au-kind { order: 2; justify-self: end; }
  .au-actor { order: 3; } .au-go { order: 4; opacity: 1; }
  .au-what { order: 5; grid-column: 1 / -1; white-space: normal; }
  .au-toggle { margin-left: 0; }
}

/* Staff-to-staff chat, HR review — a readable transcript of coworker DMs */
.sc-log { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; max-height: 460px; overflow-y: auto; }
.sc-msg { padding: 8px 10px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.sc-meta { display: flex; align-items: baseline; gap: 6px; font-size: var(--t-xs); }
.sc-from { font-weight: 700; color: var(--ink); }
.sc-arrow { color: var(--muted); }
.sc-to { font-weight: 600; color: var(--ink-soft); }
.sc-time { margin-left: auto; color: var(--muted); }
.sc-body { margin-top: 3px; font-size: var(--t-sm); color: var(--ink); white-space: pre-wrap; word-break: break-word; }

/* Weekly digest preview */
.digest-pre {
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s3); font-size: var(--t-xs); line-height: 1.55; white-space: pre-wrap;
  max-height: 320px; overflow-y: auto; color: var(--ink-soft); font-family: var(--font);
}

/* Canned reply picker (in the reply box) */
.canned-menu {
  margin-top: 6px; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--sh-2); max-height: 260px; overflow-y: auto;
  /* ⚠ align-self is LOAD-BEARING, not cosmetic. .detail-actions is a grid, and a scroll
     container (overflow-y:auto) contributes ZERO to an auto-sized grid row — so this row
     collapsed and the grid's default `stretch` squashed the menu to 2px. The templates were
     all there, rendered, and invisible. `start` makes it take its own content height instead.
     Verified on prod 2026-08-14: 2px -> 103px. Do not remove without re-testing the picker. */
  align-self: start;
}
.canned-loading { padding: var(--s3); color: var(--muted); font-size: var(--t-sm); }
.canned-item {
  display: flex; flex-direction: column; gap: 2px; width: 100%; text-align: left;
  padding: 9px var(--s3); border: none; border-bottom: 1px solid var(--line);
  background: transparent; cursor: pointer; color: var(--ink);
}
.canned-item:last-child { border-bottom: none; }
.canned-item:hover { background: var(--primary-soft); }
.canned-title { font-weight: 700; font-size: var(--t-sm); }
.canned-body { color: var(--muted); font-size: var(--t-xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Canned reply manager (admin portal) */
.canned-manage-row {
  display: flex; align-items: center; gap: var(--s3);
  padding: 9px 0; border-bottom: 1px solid var(--line);
}
.canned-manage-row:last-child { border-bottom: none; }

/* ==========================================================================
   v-next: text vs email distinction, resolved-late, overdue report, window buttons
   ========================================================================== */
/* Channel chips — texts (sage) vs emails (teal-blue), unmistakable at a glance. */
.chip-ch { font-weight: 700; letter-spacing: .01em; }
.chip-ch .icon { width: 12px; height: 12px; }
.chip-ch-text  { background: color-mix(in srgb, var(--text-c) 16%, transparent); color: var(--text-c); }
.chip-ch-email { background: color-mix(in srgb, var(--email) 16%, transparent); color: var(--email); }
/* Reinforce with a stronger left rail + faint row tint per channel. */
.msg.text-type  { border-left: 3px solid var(--text-c); }
.msg.email-type { border-left: 3px solid var(--email); }
.msg.text-type::before, .msg.email-type::before { content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .04; border-radius: inherit; }
.msg.text-type::before  { background: var(--text-c); }
.msg.email-type::before { background: var(--email); }
.msg { position: relative; }

/* "Resolved late" — permanent amber marker in the Completed box. */
.chip-late { background: var(--gold-soft); color: var(--gold-ink); border: 1px solid var(--gold-2); font-weight: 700; }
.chip-late .icon { width: 12px; height: 12px; }

/* Completed-box filter bar: "Completed by" multi-select, date range, running count */
.completed-tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s3); padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); }
/* Deleted folder explainer strip. Quiet and factual — it states the retention rule and the fact
   that anyone can restore, so nobody has to be told twice or guess how long they have. */
.deleted-banner { display: flex; align-items: flex-start; gap: 8px; padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line); background: var(--surface-2, var(--surface));
  color: var(--muted); font-size: .86rem; line-height: 1.45; }
.deleted-banner svg { flex: none; margin-top: 2px; opacity: .75; }
.deleted-banner b { color: var(--ink); font-weight: 700; }
.ct-staff { position: relative; }
.ct-staff-btn { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; cursor: pointer; max-width: 340px; }
.ct-staff-btn #ctStaffLabel { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ct-staff-btn.on { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-soft); }
.ct-pop { position: absolute; top: calc(100% + 6px); left: 0; z-index: 50; width: 300px; background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-md); box-shadow: 0 12px 34px rgba(0,0,0,.20); padding: var(--s2); }
.ct-pop-search { width: 100%; margin-bottom: var(--s2); }
.ct-pop-list { max-height: 264px; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }
.ct-opt { display: flex; align-items: center; gap: 8px; padding: 6px var(--s2); border-radius: var(--r-sm); cursor: pointer; }
.ct-opt:hover { background: var(--surface-2); }
.ct-opt.on { background: var(--primary-soft); }
.ct-check { width: 16px; flex: none; display: inline-flex; justify-content: center; color: var(--primary); }
.ct-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; font-size: var(--t-sm); }
.ct-n { flex: none; color: var(--muted); font-size: var(--t-xs); font-weight: 700; font-variant-numeric: tabular-nums; }
.ct-pop-foot { border-top: 1px solid var(--line); margin-top: var(--s2); padding-top: var(--s2); text-align: right; }
.ct-empty { color: var(--muted); padding: 12px; text-align: center; font-size: var(--t-sm); }
.ct-group { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--muted); padding: 8px var(--s2) 4px; }
.ct-date { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--t-sm); font-weight: 600; }
.ct-date input { color: var(--ink); }
.ct-count { margin-left: auto; color: var(--muted); font-size: var(--t-sm); font-weight: 700; font-variant-numeric: tabular-nums; }
.linkbtn { background: none; border: none; color: var(--primary-ink); font-weight: 600; cursor: pointer; font-size: var(--t-sm); padding: 0; }
.linkbtn:hover { text-decoration: underline; }
.who-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; }
.chip-by { background: var(--surface-3); color: var(--ink-soft); font-weight: 600; }

/* Collapsible admin card (minimized by default) */
.card.collapsible > .card-toggle { cursor: pointer; user-select: none; }
.card.collapsible > .card-toggle .card-caret { margin-left: auto; color: var(--muted); transition: transform .15s ease; display: inline-flex; }
.card.collapsible:not(.collapsed) > .card-toggle .card-caret { transform: rotate(90deg); }
.card.collapsible.collapsed > .card-toggle { margin-bottom: 0; }
.card.collapsible.collapsed > .card-body { display: none; }

/* "Choose a person" modal — used when an inbox has 2+ admins/SuperAdmins to escalate to */
.chooser-overlay { position: fixed; inset: 0; z-index: 3000; background: rgba(0,0,0,.45); display: flex; align-items: center; justify-content: center; padding: var(--s4); }
.chooser-card { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: 0 20px 50px rgba(0,0,0,.30); padding: var(--s5); width: 100%; max-width: 380px; }
.chooser-card h3 { margin: 0 0 4px; font-size: var(--t-base); font-weight: 700; color: var(--ink); }
.chooser-sub { margin: 0 0 var(--s3); color: var(--muted); font-size: var(--t-sm); }

/* Docked, non-blocking variant of a chooser overlay — the New-vendor panel uses it so it can sit
   open BESIDE the invoice PDF. The backdrop is transparent and pointer-events:none, so clicks and
   text-selection pass straight THROUGH to the PDF behind it (you can grab text off the invoice
   into the form), and the panel never light-dismisses — only Cancel / Create / Esc close it, so a
   stray click on the page can never discard what you've typed. The card itself re-enables clicks. */
.chooser-overlay.docked-panel { background: transparent; pointer-events: none; justify-content: flex-end; align-items: flex-start; }
.chooser-overlay.docked-panel > .chooser-card { pointer-events: auto; box-shadow: 0 24px 60px rgba(0,0,0,.38); }
/* Stacked docked panels (Banking opened on top of Edit vendor) step down and in, so the panel
   behind stays visible instead of vanishing under a pixel-perfect twin. */
.chooser-overlay.docked-panel.docked-depth-1 > .chooser-card { margin: 22px 22px 0 0; }
.chooser-overlay.docked-panel.docked-depth-2 > .chooser-card { margin: 44px 44px 0 0; }
@media (max-width: 820px) {
  .chooser-overlay.docked-panel { justify-content: center; }
  /* The depth offsets are 4-class selectors — repeat them here so this reset actually outranks them
     on a phone, where there is no room beside the PDF to step a stack sideways. */
  .chooser-overlay.docked-panel.docked-depth-1 > .chooser-card,
  .chooser-overlay.docked-panel.docked-depth-2 > .chooser-card { margin: 0; }
}
.chooser-list { display: flex; flex-direction: column; gap: 6px; }
.chooser-opt { display: flex; align-items: center; gap: 10px; padding: 10px var(--s3); border-radius: var(--r-md); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink); font-weight: 600; font-size: var(--t-sm); cursor: pointer; text-align: left; }
.chooser-opt:hover { border-color: var(--primary); background: var(--primary-soft); }
.chooser-foot { margin-top: var(--s3); text-align: right; }

/* "Before you resolve" pop-up — quick reason chips + a free-text note. */
.resolve-card { max-width: 440px; }
.resolve-chips { display: flex; flex-direction: column; gap: 8px; margin: 0 0 var(--s3); }
.resolve-chip { display: flex; align-items: center; gap: 10px; padding: 11px var(--s3); border-radius: var(--r-md); border: 1px solid var(--line); background: var(--surface-2); color: var(--ink); font-weight: 600; font-size: var(--t-sm); cursor: pointer; text-align: left; transition: border-color .12s ease, background .12s ease; }
.resolve-chip:hover { border-color: var(--primary); background: var(--primary-soft); }
.resolve-chip.on { border-color: var(--primary); background: var(--primary-soft); box-shadow: inset 0 0 0 1px var(--primary); }
.resolve-chip .icon-sm { color: var(--muted); flex: 0 0 auto; }
.resolve-chip.on .icon-sm { color: var(--primary); }
.resolve-note { width: 100%; min-height: 68px; padding: 10px; border-radius: var(--r-md); border: 1px solid var(--line); background: var(--bg); color: var(--ink); font: inherit; font-size: var(--t-sm); resize: vertical; box-sizing: border-box; }
.resolve-note:focus { outline: none; border-color: var(--primary); }
.resolve-foot { display: flex; justify-content: flex-end; align-items: center; gap: 10px; margin-top: var(--s3); }
.resolve-go[disabled] { opacity: .5; cursor: not-allowed; }

/* "What are you waiting on?" pop-up — same chips as the resolve gate, but eight of them, so they
   go two-up instead of stacking into a card taller than the viewport. Collapses to one column on a
   phone, where two columns would truncate the labels. */
.wait-chips { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.wait-chips .resolve-chip { padding: 10px var(--s2); }
@media (max-width: 480px) { .wait-chips { grid-template-columns: 1fr; } }
.wait-lbl { display: block; font-size: var(--t-xs); font-weight: 600; color: var(--muted); margin: 0 0 4px; }
.wait-date { width: 100%; padding: 9px 10px; margin: 0 0 var(--s3); border-radius: var(--r-md); border: 1px solid var(--line); background: var(--bg); color: var(--ink); font: inherit; font-size: var(--t-sm); box-sizing: border-box; }
.wait-date:focus { outline: none; border-color: var(--primary); }

/* Overdue report table emphasis */
.od-late { color: var(--danger); font-weight: 700; }
.od-open { color: var(--warn); font-weight: 700; }

/* Detail window: expanded (full-screen) mode + button row */
.detail.expanded {
  left: 0; width: 100vw; max-width: 100vw; border-radius: 0;
}
.detail-head .win-btns { display: inline-flex; gap: 2px; margin-left: auto; }

/* Expanded on a roomy screen → TWO PANES: the conversation fills the LEFT half (a comfortable
   reading column) and the reply / full email composer + action buttons fill the RIGHT half.
   Each pane scrolls on its own. Below 900px the expanded view stays single-column stacked. */
@media (min-width: 900px) {
  .detail.expanded {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto auto 1fr;
    grid-template-areas: "head head" "editor editor" "body actions";
  }
  .detail.expanded > .detail-head    { grid-area: head; }
  .detail.expanded > .contact-editor { grid-area: editor; }
  .detail.expanded > .detail-body    { grid-area: body;  min-height: 0; padding: var(--s5) var(--s6); }
  .detail.expanded > .detail-actions {
    grid-area: actions; min-height: 0; overflow-y: auto; align-content: start;
    border-top: 0; border-left: 1px solid var(--line);
  }
  /* Give the composer a settled reading width inside its (potentially very wide) pane. */
  .detail.expanded > .detail-actions > * { max-width: 760px; }
}

/* ==========================================================================
   Full email composer — a real "compose email" surface (subject, CC/BCC,
   formatting toolbar, rich-text body, attachments, signature).
   ========================================================================== */
.composer { display: grid; gap: var(--s3); }
.composer-fields {
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden;
}
.composer-row {
  display: flex; align-items: center; gap: var(--s2);
  padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line);
}
.composer-row:last-child { border-bottom: 0; }
.composer-row > label {
  flex: 0 0 auto; min-width: 46px; color: var(--muted);
  font-size: var(--t-sm); font-weight: 600;
}
.composer-row input {
  flex: 1 1 auto; min-width: 0; border: 0; background: transparent;
  color: var(--ink); font-size: var(--t-base); padding: 2px 0;
}
.composer-row input:focus { outline: none; }
.composer-row select.composer-from {
  flex: 1 1 auto; min-width: 0; border: 0; background: transparent;
  color: var(--ink); font-size: var(--t-base); font-weight: 600; padding: 2px 0;
  cursor: pointer;
}
.composer-row select.composer-from:focus { outline: none; }
.composer-row .to-fixed { flex: 1; color: var(--ink); font-weight: 600; font-size: var(--t-base); }
.composer-ccbcc-toggle { display: inline-flex; gap: var(--s3); margin-left: auto; }
/* Real, tappable chips (they used to be bare text — nobody found Cc on an iPad). */
.composer-ccbcc-toggle button {
  background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  color: var(--muted); font-size: var(--t-sm);
  font-weight: 600; cursor: pointer; padding: 4px 10px; min-height: 28px;
}
.composer-ccbcc-toggle button:hover { color: var(--primary); border-color: var(--primary); }

/* Formatting toolbar */
.compose-toolbar {
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
  padding: var(--s1); border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface-2);
}
.compose-toolbar .tb-btn {
  width: 32px; height: 32px; display: grid; place-items: center;
  border-radius: var(--r-sm); background: transparent; color: var(--ink-soft);
  border: 0; cursor: pointer;
}
.compose-toolbar .tb-btn:hover { background: var(--surface-3); color: var(--ink); }
.compose-toolbar .tb-btn svg { width: 17px; height: 17px; }
.compose-toolbar .tb-sep { width: 1px; align-self: stretch; margin: 4px 4px; background: var(--line); }

/* Rich-text body */
.compose-editor {
  min-height: 160px; max-height: 44vh; overflow-y: auto;
  padding: var(--s3); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink); line-height: 1.55;
}
.compose-editor:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.compose-editor:empty:before { content: attr(data-placeholder); color: var(--muted); }
.compose-editor p { margin: 0 0 var(--s2); }
.compose-editor ul, .compose-editor ol { margin: 0 0 var(--s2) var(--s5); }
.compose-editor a { color: var(--primary); }
.detail.expanded > .detail-actions .compose-editor { max-height: 52vh; }

/* Attachment chips (with a remove button) */
.compose-attach-list { display: flex; flex-wrap: wrap; gap: var(--s2); }
.compose-attach-chip {
  display: inline-flex; align-items: center; gap: 6px; flex-wrap: nowrap;
  padding: var(--s1) var(--s2) var(--s1) var(--s3); border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--ink); font-size: var(--t-sm); font-weight: 600; max-width: 280px;
}
.compose-attach-chip svg { width: 14px; height: 14px; color: var(--muted); flex: 0 0 auto; }
.compose-attach-chip .ca-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 1 auto; min-width: 0; }
.compose-attach-chip .ca-size { color: var(--muted); font-weight: 500; white-space: nowrap; flex: 0 0 auto; }
.compose-attach-chip .ca-x {
  border: 0; background: none; color: var(--muted); cursor: pointer;
  font-size: 16px; line-height: 1; padding: 0 2px; flex: 0 0 auto;
}
.compose-attach-chip .ca-x:hover { color: var(--danger); }

/* Signature panel */
.compose-sig { border: 1px dashed var(--line-strong); border-radius: var(--r-md); padding: var(--s3); background: var(--surface-2); }
.compose-sig-head { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); }
.compose-sig-head .cs-title { font-weight: 700; font-size: var(--t-sm); }
.compose-sig-head .cs-status { margin-left: auto; color: var(--muted); font-size: var(--t-xs); }
.compose-sig textarea {
  width: 100%; min-height: 64px; padding: var(--s2) var(--s3); resize: vertical;
  border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink); font: inherit;
}
.compose-sig textarea:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }

/* Send row: big primary send + a hidden native file input */
.compose-send-row { display: flex; align-items: center; gap: var(--s2); }
.compose-send-row .btn-send-email {
  margin-left: auto; display: inline-flex; align-items: center; gap: var(--s2);
  background: var(--primary); color: var(--on-primary); font-weight: 700;
  padding: var(--s2) var(--s5); border-radius: var(--r-md); border: 0; cursor: pointer;
}
.compose-send-row .btn-send-email:hover { background: var(--primary-hover); }
.compose-send-row .btn-send-email svg { width: 16px; height: 16px; }
.compose-file-input { display: none; }

/* ==========================================================================
   Overdue: RED + gentle bounce to demand attention in the inbox.
   Placed LAST so it wins over the channel-color left border / ::before tint.
   ========================================================================== */
@keyframes overdue-bounce {
  0%, 100% { transform: translateY(0); }
  35%      { transform: translateY(-4px); }
  70%      { transform: translateY(-1px); }
}
.msg.overdue {
  border-color: color-mix(in srgb, var(--danger) 55%, var(--line));  /* top/right/bottom */
  background: color-mix(in srgb, var(--danger) 13%, var(--surface));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--danger) 30%, transparent);
  animation: overdue-bounce 1.1s var(--ease) infinite;
  border-left: 4px solid var(--danger);   /* LAST so the true-red left rail isn't reset by border-color */
}
.msg.overdue::before { background: var(--danger); opacity: .1; }
.msg.overdue.selected { animation: none; }          /* don't bob the thread you're reading */
.overdue-flag { color: var(--danger); }
.overdue-flag .icon { color: var(--danger); }
@media (prefers-reduced-motion: reduce) { .msg.overdue { animation: none; } }

/* ---- KHC University (training / knowledge base) ---------------------------------------- */
.uni-intro { padding: var(--s4) var(--s4) var(--s2); color: var(--muted); font-size: var(--t-base); }
.uni-list { display: flex; flex-direction: column; gap: var(--s3); padding: var(--s2) var(--s4) var(--s5); }
.uni-card {
  display: flex; align-items: center; gap: var(--s3); text-align: left; width: 100%;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  padding: var(--s4); box-shadow: var(--sh-1); cursor: pointer;
  transition: border-color var(--dur) var(--ease), transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.uni-card:hover { border-color: color-mix(in srgb, var(--primary) 45%, var(--line)); transform: translateY(-1px); box-shadow: var(--sh-2); }
.uni-card:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.uni-ic { flex: none; width: 42px; height: 42px; border-radius: var(--r-md); display: grid; place-items: center;
  background: color-mix(in srgb, var(--primary) 12%, var(--surface)); color: var(--primary); }
.uni-ic svg { width: 22px; height: 22px; }
.uni-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.uni-title { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); font-weight: 700; color: var(--ink); font-size: var(--t-md); }
.uni-desc { color: var(--muted); font-size: var(--t-sm); line-height: 1.45; }
.uni-lock {
  display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); font-weight: 600;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); border-radius: var(--r-full);
  padding: 2px 9px; letter-spacing: .01em; white-space: nowrap;
}
.uni-lock svg { width: 12px; height: 12px; }
.uni-arrow { flex: none; color: var(--muted); display: grid; place-items: center; }
.uni-arrow svg { width: 18px; height: 18px; }

/* Document viewer: sticky toolbar + isolated iframe holding the guide */
.uni-doc-bar {
  display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--surface); z-index: 2;
}
.uni-doc-bar .btn { display: inline-flex; align-items: center; gap: 6px; }
.uni-doc-title { flex: 1; min-width: 0; font-weight: 700; color: var(--ink); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.uni-frame { width: 100%; height: calc(100vh - 168px); min-height: 420px; border: 0; background: var(--surface); display: block; }
@media (max-width: 720px) { .uni-frame { height: calc(100vh - 210px); } }

/* ---- University search + Ask + Roles matrix ---- */
.uni-tools { display: flex; gap: var(--s3); flex-wrap: wrap; padding: var(--s4) var(--s4) var(--s2); }
.uni-field { display: flex; align-items: center; gap: var(--s2); flex: 1; min-width: 220px;
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: 0 var(--s3); }
.uni-field svg { width: 16px; height: 16px; color: var(--muted); flex: none; }
.uni-field input { flex: 1; border: none; background: transparent; padding: 10px 0; outline: none; min-width: 0; }
.uni-field:focus-within { border-color: var(--primary); box-shadow: var(--ring); }
.uni-askfield { flex: 1.3; }
.uni-askfield .btn { padding: var(--s2) var(--s4); flex: none; }

.uni-answer { margin: 0 var(--s4) var(--s2); border: 1px solid var(--line); border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--primary) 5%, var(--surface)); box-shadow: var(--sh-1); overflow: hidden; }
.uni-answer-q { display: flex; align-items: center; gap: var(--s2); font-weight: 700; padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line); color: var(--ink); }
.uni-answer-q svg { width: 16px; height: 16px; color: var(--primary); flex: none; }
.uni-answer-body { padding: var(--s3) var(--s4); }
.uni-answer-text { line-height: 1.6; color: var(--ink); }
.uni-answer-meta { margin-top: var(--s3); padding-top: var(--s2); border-top: 1px dashed var(--line);
  font-size: var(--t-xs); color: var(--muted); }
.uni-typing { color: var(--muted); font-style: italic; }

.rm-scroll { overflow-x: auto; }
.roles-matrix { min-width: 540px; font-size: var(--t-sm); }
.roles-matrix th { text-align: center; vertical-align: bottom; font-size: var(--t-xs); font-weight: 600;
  color: var(--muted); padding: var(--s2); line-height: 1.25; }
.roles-matrix th svg { width: 11px; height: 11px; vertical-align: middle; color: var(--warn); }
.roles-matrix th:first-child, .roles-matrix td.rm-role { text-align: left; }
.roles-matrix td { text-align: center; padding: var(--s2); border-top: 1px solid var(--line); }
.roles-matrix tbody tr:hover { background: var(--surface-2); }
.roles-matrix input[type="checkbox"] { width: 16px; height: 16px; cursor: pointer; accent-color: var(--primary); }
.roles-matrix input:disabled { cursor: not-allowed; opacity: .4; }

/* Clickable source links under an AI answer */
.uni-sources { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin-top: var(--s3); }
.uni-sources-label { font-size: var(--t-xs); font-weight: 600; color: var(--muted); letter-spacing: .02em; }
.uni-source { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-sm); font-weight: 600;
  color: var(--primary-ink); background: var(--primary-soft); border: 1px solid color-mix(in srgb, var(--primary) 30%, transparent);
  border-radius: var(--r-full); padding: 4px 11px; cursor: pointer;
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.uni-source:hover { background: color-mix(in srgb, var(--primary) 18%, var(--surface)); border-color: var(--primary); }
.uni-source svg { width: 13px; height: 13px; }
.uni-source:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ---- Add / edit contact editor (drops under the detail header) ---- */
.contact-editor { display: flex; flex-direction: column; gap: var(--s2); padding: var(--s3) var(--s4);
  border-bottom: 1px solid var(--line); background: var(--surface-2); }
.contact-editor .ce-row { display: flex; gap: var(--s2); align-items: center; }
.contact-editor .ce-row .field-input { flex: 1; margin: 0; }
.contact-editor .field-input { margin: 0; }
.contact-editor #contactAiBtn { flex: none; white-space: nowrap; display: inline-flex; align-items: center; gap: 5px; }
.contact-editor .ce-actions { display: flex; gap: var(--s2); }
.contact-editor .ce-actions .btn { flex: 1; }

/* ---- AI usage & costs card (Command Center) ---- */
.ai-cost-table { min-width: 480px; font-size: var(--t-sm); }
.ai-cost-table th, .ai-cost-table td { padding: var(--s2) var(--s3); border-top: 1px solid var(--line); vertical-align: top; }
.ai-cost-table th { color: var(--muted); font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .04em; font-weight: 600; border-top: none; text-align: left; }
.ai-cost-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.ai-cost-table tfoot td { border-top: 2px solid var(--line-strong); }
.ai-cat-note { color: var(--muted); font-size: 12px; }
.ai-cat-paused { background: color-mix(in srgb, var(--danger) 7%, transparent); }
.ai-paused-chip { background: var(--danger); color: #fff; border: none; font-size: 11px; }
.ai-cost-table .ai-ctl { white-space: nowrap; }

/* ---- eCW chart-writing costs -------------------------------------------------------
   The eCW bill is a staircase, not a meter: the month's whole transaction fee is set by
   which band the total write count lands in, and one write can move it. So this card is
   built around the headroom bar — how much room is left before the price steps up —
   rather than the running total the AI card leads with. */
.ecw-headline { display: flex; flex-wrap: wrap; gap: var(--s4); align-items: flex-end;
  padding: var(--s3); border-radius: var(--r-md); background: var(--surface-2); margin-bottom: var(--s3); }
.ecw-stat { display: flex; flex-direction: column; gap: 2px; min-width: 120px; flex: 1 1 120px; }
.ecw-stat-n { font-size: 22px; font-weight: 650; letter-spacing: -.01em; font-variant-numeric: tabular-nums; }
.ecw-stat-l { color: var(--muted); font-size: 12px; line-height: 1.35; }
/* The bar fills as the month eats its band. Amber past 80%, red once the projection has
   already crossed the warning line — the colour IS the warning, before anyone reads a number. */
.ecw-meter { flex: 1 1 100%; height: 6px; border-radius: 999px; background: var(--line); overflow: hidden; }
.ecw-meter-fill { height: 100%; background: var(--good); border-radius: 999px; transition: width .3s ease; }
.ecw-headline.ecw-warn .ecw-meter-fill { background: var(--warn); }
.ecw-headline.ecw-alert { background: color-mix(in srgb, var(--danger) 8%, var(--surface-2)); }
.ecw-headline.ecw-alert .ecw-meter-fill { background: var(--danger); }
.ecw-alert-line { color: var(--danger); font-size: var(--t-sm); line-height: 1.5; margin: 0 0 var(--s3); }
.ecw-alert-line svg { vertical-align: -2px; }

.ecw-sub { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
  font-weight: 650; margin: var(--s4) 0 var(--s2); }
.ecw-off { background: color-mix(in srgb, var(--danger) 7%, transparent); }
.ecw-tier-head td { background: var(--surface-2); border-top: 2px solid var(--line-strong) !important; }

.ecw-dial { display: flex; flex-wrap: wrap; gap: var(--s2); }
.ecw-planner { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3); margin-bottom: var(--s3); }
.ecw-planner > summary { cursor: pointer; font-weight: 600; font-size: var(--t-sm); }
.ecw-plan-row { display: flex; flex-wrap: wrap; gap: var(--s3); margin-top: var(--s3); }
.ecw-plan-row label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.ecw-plan-row input { width: 96px; }
.ecw-plan-out { margin-top: var(--s3); }
.ecw-plan-total { font-size: 20px; font-weight: 650; font-variant-numeric: tabular-nums; }
.ecw-tight { color: var(--danger); font-weight: 600; }
/* Phones: the four headline stats stack into two columns rather than a single tall column,
   and the planner inputs go full width so they stay tappable. */
@media (max-width: 700px) {
  .ecw-stat { flex: 1 1 42%; min-width: 0; }
  .ecw-stat-n { font-size: 19px; }
  .ecw-plan-row label { flex: 1 1 100%; }
  .ecw-plan-row input { width: 100%; }
}

/* ---- Bulk select (SuperAdmin+): tick boxes + action bar ---------------------------- */
.ghost-btn { padding: 7px var(--s3); border-radius: var(--r-md); font-weight: 600; font-size: var(--t-sm);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft);
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.ghost-btn:hover { background: var(--surface-2); color: var(--ink); }
.ghost-btn svg { width: 15px; height: 15px; }

/* An extra leading column holds the tick box while selecting. */
.list.select-mode .msg { grid-template-columns: 24px 42px 1fr auto; }
.row-check { align-self: center; width: 20px; height: 20px; border-radius: var(--r-sm);
  border: 1.5px solid var(--line-strong); display: grid; place-items: center; color: var(--on-primary);
  background: var(--surface); transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.row-check.on { background: var(--primary); border-color: var(--primary); }
.row-check svg { width: 13px; height: 13px; }
.msg.row-checked { background: var(--primary-soft); border-color: color-mix(in srgb, var(--primary) 32%, transparent); }

.bulk-bar { flex: none; display: flex; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); border-top: 1px solid var(--line); background: var(--surface-2); }
.bulk-bar[hidden] { display: none; }
.bulk-count { font-weight: 600; font-size: var(--t-sm); color: var(--muted); }
.bulk-link { background: none; border: none; color: var(--primary-ink); font-weight: 600;
  font-size: var(--t-sm); padding: 4px 6px; border-radius: var(--r-sm); cursor: pointer; }
.bulk-link:hover { background: var(--surface-3); }
.bulk-bar .btn { margin-left: auto; }

/* Danger icon button (remove staff). */
.icon-btn.danger { color: var(--danger); }
.icon-btn.danger:hover { background: var(--danger-soft); color: var(--danger); }
.icon-btn:disabled { opacity: .5; cursor: default; }
/* Desktop-alert bell: a soft pulse invites the one-time "Allow" click; solid brand color once on. */
.icon-btn.on { color: var(--primary); border-color: var(--primary); }
.icon-btn.needs-attn { color: var(--primary); border-color: var(--primary); animation: bellpulse 1.8s ease-in-out infinite; }
@keyframes bellpulse { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--primary) 40%, transparent); } 50% { box-shadow: 0 0 0 4px color-mix(in srgb, var(--primary) 22%, transparent); } }
@media (prefers-reduced-motion: reduce) { .icon-btn.needs-attn { animation: none; } }

/* ===========================================================================
   Guided tour ("Tutorial") — spotlight overlay + step card. Auto-plays on first
   login / after a password reset, and replays from the top-bar Tutorial button.
   =========================================================================== */
.tour-overlay { position: fixed; inset: 0; z-index: 300; }
body.tour-active { overflow: hidden; }
/* The spotlight: a transparent box over the highlighted element, with a huge outward shadow
   that dims the rest of the screen. Non-interactive — the tour is view-only while it runs. */
.tour-spot { position: fixed; display: none; border-radius: var(--r-md); pointer-events: none;
  box-shadow: 0 0 0 9999px rgba(12, 20, 17, .60); outline: 2px solid var(--primary); outline-offset: 2px;
  transition: left .28s var(--ease), top .28s var(--ease), width .28s var(--ease), height .28s var(--ease); }
.tour-pop { position: fixed; width: min(360px, calc(100vw - 24px)); background: var(--surface); color: var(--ink);
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-3); padding: var(--s5);
  z-index: 301; animation: tourPop var(--dur) var(--ease); }
@keyframes tourPop { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; } }
.tour-x { position: absolute; top: 10px; right: 10px; background: none; border: none; color: var(--muted);
  cursor: pointer; padding: 4px; border-radius: var(--r-sm); display: inline-flex; }
.tour-x:hover { background: var(--surface-3); color: var(--ink); }
.tour-step { font-size: var(--t-xs); font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--primary-ink); }
.tour-title { margin: 6px 34px 8px 0; font-size: var(--t-lg); font-weight: 750; line-height: 1.2; }
.tour-text { color: var(--ink-soft); font-size: var(--t-base); line-height: 1.55; }
.tour-text b { color: var(--ink); font-weight: 650; }
.tour-nav { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s5); }
.tour-grow { flex: 1; }
.tour-btn { border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink);
  border-radius: var(--r-md); padding: 8px 15px; font: inherit; font-weight: 600; font-size: var(--t-sm); cursor: pointer; }
.tour-btn:hover { background: var(--surface-2); }
.tour-btn.primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.tour-btn.primary:hover { background: var(--primary-hover); }
.tour-skip { border: none; background: none; color: var(--muted); font: inherit; font-weight: 600; font-size: var(--t-sm); cursor: pointer; padding: 8px 2px; }
.tour-skip:hover { color: var(--ink); text-decoration: underline; }
@media (prefers-reduced-motion: reduce) { .tour-spot { transition: none; } .tour-pop { animation: none; } }

/* ============================================================================
   AP module (Bill.com replacement). Same calm language as the rest of the app:
   muted status colors, soft shadows, no neon, no motion beyond a gentle fade. */
/* Bill Builder drawer — the chart-drawer pattern: floating card docked left of the reader. */
.ap-bill-drawer { position: fixed; top: calc(var(--topbar-h, 60px) + 10px); bottom: 10px; right: min(480px, 100%);
  width: min(46vw, 620px); max-width: calc(100vw - 24px);
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--sh-3); z-index: 45; display: flex; flex-direction: column; overflow: hidden;
  transform: translateY(8px) scale(.985); opacity: 0; transform-origin: bottom right;
  transition: transform .2s var(--ease), opacity .2s var(--ease); }
.ap-bill-drawer.open { transform: none; opacity: 1; }
.ap-drawer-head { padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, var(--surface-2), var(--surface)); display: flex; align-items: flex-start; gap: var(--s3); }
.ap-drawer-title { font-weight: 700; font-size: var(--t-md); color: var(--ink); display: flex; align-items: center; gap: var(--s2); }
.ap-drawer-sub { font-size: var(--t-xs); color: var(--muted); margin-top: 3px; }
.ap-drawer-head .icon-btn { margin-left: auto; flex: none; }
.ap-drawer-body { overflow-y: auto; flex: 1; padding: var(--s4); }
@media (max-width: 820px) { .ap-bill-drawer { top: var(--topbar-h, 56px); bottom: 0; right: 0 !important;
  left: 0; width: 100%; max-width: 100%; border: 0; border-radius: 0; } }

/* Bill form */
.ap-form { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s3); }
.ap-form h4 { margin: var(--s2) 0 0; font-size: var(--t-sm); display: flex; align-items: center; gap: var(--s2); color: var(--ink); }
.ap-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2) var(--s3); }
.ap-grid2 label, .ap-grid2 .ap-cell { display: grid; gap: 4px; font-size: var(--t-xs); color: var(--muted); }
.ap-low { border-color: var(--warn) !important; box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 18%, transparent); }
/* Bill.com-style line table: (Class first) | Account | Amount | Description | Type — with a
   labeled header row. .has-class variants render only for companies with QB class tracking on;
   the class leads because it decides the company, the paying bank, and the account list. */
.ap-line { display: grid; grid-template-columns: minmax(120px, 1.2fr) minmax(84px, .6fr) 1.4fr minmax(64px, auto) auto; gap: var(--s2); align-items: center; margin-bottom: var(--s2); }
.ap-line.has-class { grid-template-columns: minmax(96px, .9fr) minmax(120px, 1.2fr) minmax(84px, .6fr) 1.2fr minmax(64px, auto) auto; }
.ap-lines-head { display: grid; grid-template-columns: minmax(120px, 1.2fr) minmax(84px, .6fr) 1.4fr minmax(64px, auto) auto; gap: var(--s2); padding: 2px 0 4px; font-size: var(--t-xs); font-weight: 700; color: var(--muted); border-bottom: 1px solid var(--line); margin-bottom: var(--s2); }
.ap-lines-head.has-class { grid-template-columns: minmax(96px, .9fr) minmax(120px, 1.2fr) minmax(84px, .6fr) 1.2fr minmax(64px, auto) auto; }
/* Class-first gating: the account picker sleeps until the line has a class; an empty class
   select carries a soft amber ring as the "start here" cue. */
.ap-acc-locked { opacity: .55; pointer-events: none; }
.ap-l-class.ap-class-missing { box-shadow: inset 0 0 0 1.5px var(--warn, #b58a2a); }
/* Each expense-line field is wrapped in a labelled cell (.ap-lcell). On desktop the wrapper
   is display:contents so the field stays a direct grid item (grid unchanged) and the caption
   is hidden — the column header row already labels the columns. On phones (mobile.css) the
   header is hidden and each cell shows its own caption above the field. */
.ap-lcell { display: contents; }
.ap-lcell-lbl { display: none; }
/* ---- A NARROW FORM PANE, on a wide screen ----------------------------------------------------
   The row above is a 6-column grid whose five non-Description tracks carry pixel minimums adding up
   to ~400px. Description is the only track with no minimum of its own, so it is the one that
   collapses when the pane is tight: measured at a 430px pane it got 26px and the text rendered ONE
   CHARACTER PER LINE (owner 2026-08-25, editing a bill imported from Bill.com — a long account name
   like "3-113.4 Physician Health Organization Fees" makes it worse).
   The stacked, labelled layout that already exists for phones (mobile.css) is the right answer here
   too — but it was gated on VIEWPORT width, and this pane is narrow while the screen is wide. A
   container query gates it on the PANE, which is the thing that is actually too small. */
.ap-form { container-type: inline-size; }
@container (max-width: 640px) {
  .ap-lines-head { display: none; }
  .ap-line { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: end; }
  .ap-line .ap-lcell { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  .ap-line .ap-lcell-lbl { display: block; font-size: var(--t-xs); font-weight: 600; color: var(--muted); }
  .ap-line .ap-lcell:has(.ap-l-class) { flex: 1 1 46%; }
  .ap-line .ap-lcell:has(.ap-acc-combo), .ap-line .ap-lcell:has(.ap-l-acct) { flex: 1 1 100%; }
  .ap-line .ap-lcell:has(.ap-l-amt) { flex: 1 1 42%; }
  .ap-line .ap-lcell:has(.ap-l-desc) { flex: 1 1 100%; }
  .ap-line .ap-lcell:has(.ap-l-kind) { flex: 1 1 55%; }
  .ap-line .ap-l-del { flex: 0 0 auto; align-self: end; margin-bottom: 2px; }
  .ap-line.ap-payrow .ap-p-bank { flex: 1 1 100%; }
  .ap-line.ap-payrow .ap-p-amt { flex: 1 1 60%; }
}
/* Belt and braces for the grid layout itself: a grid item defaults to min-width:auto, so a <select>
   holding a very long account name pushes its own track wider and squeezes its neighbours. */
.ap-line > .field-input, .ap-line .ap-lcell > .field-input, .ap-line .ap-acc-combo { min-width: 0; }
/* "Why this account?" — shown only on a line that was re-coded away from the app's own suggestion.
   The sentence typed here becomes this vendor's remembered rule, so it sits inline under the line
   it explains rather than in a modal that would break the entry rhythm. Full width in BOTH layouts:
   grid-column spans the desktop grid, flex-basis 100% wraps it onto its own row on phones, where
   .ap-form .ap-line switches to flex (mobile.css). Colours are theme variables, so it follows
   light and dark without a second rule. */
.ap-line-why { grid-column: 1 / -1; flex: 1 1 100%; display: flex; align-items: flex-start; gap: var(--s2); margin: 2px 0 6px; padding: var(--s2) var(--s3); border-radius: var(--r-md); background: color-mix(in srgb, var(--primary) 7%, var(--surface-2)); border: 1px solid color-mix(in srgb, var(--primary) 22%, transparent); }
.ap-why-lbl { display: inline-flex; align-items: center; gap: 4px; flex: none; padding-top: 10px; font-size: var(--t-xs); font-weight: 700; color: var(--primary-ink); white-space: nowrap; }
.ap-line-why .ap-l-why { flex: 1; min-width: 0; resize: none; overflow: hidden; line-height: 1.35; min-height: 0; }
.ap-why-x { flex: none; padding-top: 10px; font-size: var(--t-xs); color: var(--muted); }
.ap-why-acts { flex: none; display: flex; align-items: center; gap: var(--s2); padding-top: 6px; }
.ap-why-apply { white-space: nowrap; }
/* Proof the correction landed. It saves on its own, so this only ever confirms — never a button. */
.ap-why-saved { display: inline-flex; align-items: center; gap: 3px; font-size: var(--t-xs); font-weight: 600; color: var(--good); white-space: nowrap; }
.ap-why-saved[hidden] { display: none; }

/* ---- Per-line spend strip -------------------------------------------------------------------
   Sits with its own line (full width in the desktop grid AND in the mobile flex row), so the
   history you are reading is never ambiguous about which item it belongs to. Quiet by default —
   it is background reassurance most of the time — and only the unusual-month chip is allowed
   any colour. Click anywhere on the strip to open the full chart underneath. */
.ap-line-spend { grid-column: 1 / -1; flex: 1 1 100%; margin: 0 0 6px; }
.ap-ls-strip { display: flex; align-items: center; gap: var(--s3); width: 100%; text-align: left;
  padding: 6px var(--s3); border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink); cursor: pointer; }
.ap-ls-strip:hover { border-color: var(--line-strong); background: var(--surface-2); }
.ap-ls-strip.open { border-bottom-left-radius: 0; border-bottom-right-radius: 0; background: var(--surface-2); }
.viz-spark { flex: none; display: block; }
.viz-spark-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.viz-spark-usual { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.viz-spark-dot { stroke: var(--surface); stroke-width: 1.5; }
.viz-spark-flag { fill: none; stroke: var(--warn); stroke-width: 1.8; }
.ap-ls-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.ap-ls-what { font-size: var(--t-sm); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-ls-at { font-weight: 400; color: var(--muted); }
.ap-ls-nums { font-size: var(--t-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.ap-ls-flag { flex: none; display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px;
  border-radius: var(--r-full); font-size: var(--t-xs); font-weight: 700; white-space: nowrap;
  color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 38%, transparent); }
.ap-ls-caret { flex: none; color: var(--muted); font-size: 11px; }
.ap-ls-full { border: 1px solid var(--line); border-top: 0; border-radius: 0 0 var(--r-md) var(--r-md);
  background: var(--surface-2); padding: var(--s2) var(--s3) 0; }
.ap-ls-load, .ap-ls-none { display: flex; align-items: center; gap: 6px; padding: 5px 2px;
  font-size: var(--t-xs); color: var(--muted); }
/* "By the account, or by the item?" — one bill line is really two questions and only the person
   reading it knows which one they are asking: "9-101 Supplies HD at GDC" is every supply that
   clinic buys, which is the right question for a budget and the wrong one for a drug. A quiet
   segmented pair rather than two buttons competing for the click — the live one is filled, so it
   reads as the state the chart is IN rather than as an offer. */
.ap-ls-views { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 0 0 var(--s2); }
.ap-ls-views-lbl { flex: none; font-size: var(--t-xs); color: var(--muted); }
.ap-ls-view { display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%;
  padding: 4px 10px; border: 1px solid var(--line); border-radius: var(--r-full);
  background: var(--surface); color: var(--muted); font-size: var(--t-xs); cursor: pointer; }
.ap-ls-view:hover { border-color: var(--line-strong); color: var(--ink); }
.ap-ls-view.on { background: var(--primary-soft); color: var(--primary-ink);
  border-color: color-mix(in srgb, var(--primary) 38%, transparent); }
.ap-ls-view-t { flex: none; font-weight: 700; }
/* The item's NAME is the half that runs long — a 60-character product code must not push the chip
   off the row — so it is the only part allowed to be cut, and never the word that says which
   view this is. Capped in vw as well as px because this strip is read on a phone. */
.ap-ls-view-v { min-width: 0; max-width: min(240px, 52vw);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* What the chart folded together, printed under the switch: a near-match is only trustworthy if
   you can see the wordings it counted, so a wrong one is caught rather than believed. */
.ap-ls-note { margin: 0 0 var(--s2); }
.ap-ls-chart { min-width: 0; }
/* Waiting for the other view. The chart being read stays legible and stays PUT — it used to
   collapse to a one-line "checking…", which moved the switch out from under the pointer mid-click —
   and it stops taking clicks, so an impatient second tap cannot land on whichever chip slid into
   its place. */
.ap-ls-busy { opacity: .5; pointer-events: none; transition: opacity .12s var(--ease); }
/* The wash under the full chart's line, and the dot marking the most recent month. */
.viz-area { stroke: none; }
.viz-last { stroke: var(--surface); stroke-width: 2; }
/* "Why this location?" — the CLASS half of the same idea as the account box, but it also asks
   WHERE the answer was read. A reason on its own can't be checked or reused; the source is what
   tells the next invoice (and the class picker) where to look. */
/* Five controls is too many for one line, so this box wraps: label + fields on the first row,
   the mapped-spot confirmation and the actions on the next. Without an explicit wrap the select
   and the text box collapse to nothing as soon as the map button joins them. */
.ap-line-classwhy { flex-wrap: wrap; align-items: flex-start;
  background: color-mix(in srgb, var(--email, #4aa5b5) 8%, var(--surface-2));
  border-color: color-mix(in srgb, var(--email, #4aa5b5) 26%, transparent); }
.ap-line-classwhy .ap-why-lbl { color: var(--ink-soft); padding-top: 8px; }
.ap-cw-fields { flex: 1 1 420px; min-width: 0; display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; }
.ap-cw-src { flex: 1 1 210px; min-width: 150px; font-size: var(--t-xs); }
.ap-cw-reason { flex: 2 1 240px; min-width: 180px; font-size: var(--t-sm); }
.ap-cw-map { flex: none; white-space: nowrap; }
/* Confirms which words on the page this vendor's next invoice will be read at. */
.ap-cw-mapped { flex: 1 1 100%; order: 5; display: inline-flex; align-items: center; gap: 4px;
  padding-left: 2px; font-size: var(--t-xs); font-weight: 600; color: var(--good); }
.ap-line-classwhy .ap-why-acts { padding-top: 4px; }
/* ---- Spend-history chart ------------------------------------------------------------------
   Eight categorical hues in a FIXED order — the order is the colourblind-safety mechanism, not a
   style choice, so do not re-order or add a 9th (a 9th folds into --viz-other). Both columns were
   run through the palette validator against THIS app's own surfaces (#ffffff light, #16221d dark):
   every check passes, worst adjacent colourblind separation 9.1 light / 8.4 dark. Three light hues
   sit under 3:1 contrast, which is why the panel always ships a legend and a "Show numbers" table —
   colour is never the only way to read it. Dark is its own set of steps, not an automatic flip. */
:root {
  --viz-1: #2a78d6; --viz-2: #eb6834; --viz-3: #1baf7a; --viz-4: #eda100;
  --viz-5: #e87ba4; --viz-6: #008300; --viz-7: #4a3aa7; --viz-8: #e34948;
  --viz-other: #8a8f8d;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --viz-1: #3987e5; --viz-2: #d95926; --viz-3: #199e70; --viz-4: #c98500;
    --viz-5: #d55181; --viz-6: #008300; --viz-7: #9085e9; --viz-8: #e66767;
    --viz-other: #91a099;
  }
}
:root[data-theme="dark"] {
  --viz-1: #3987e5; --viz-2: #d95926; --viz-3: #199e70; --viz-4: #c98500;
  --viz-5: #d55181; --viz-6: #008300; --viz-7: #9085e9; --viz-8: #e66767;
  --viz-other: #91a099;
}
.ap-spend { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); padding: var(--s3); }
.viz-head { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: var(--s2); margin-bottom: var(--s2); }
.viz-title { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); }
.viz-controls { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.viz-sel { width: auto; max-width: 240px; font-size: var(--t-xs); padding: 6px 8px; }
.viz-sel-sm { max-width: 130px; }
.viz-toggle { font-size: var(--t-xs); color: var(--muted); white-space: nowrap; }
/* The unusual-month callout. Icon + words carry the meaning; the ring on the chart only points. */
.viz-alerts { display: grid; gap: 6px; margin-bottom: var(--s2); }
.viz-alert { display: flex; align-items: flex-start; gap: 6px; padding: var(--s2) var(--s3); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--warn) 12%, var(--surface)); border: 1px solid color-mix(in srgb, var(--warn) 34%, transparent);
  font-size: var(--t-sm); line-height: 1.45; color: var(--ink); }
.viz-alert svg { flex: none; margin-top: 2px; color: var(--warn); }
.viz-plot { position: relative; width: 100%; }
.viz-svg { display: block; width: 100%; height: auto; touch-action: pan-y; }
.viz-grid { stroke: var(--line); stroke-width: 1; }
.viz-axis { fill: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
.viz-line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.viz-cross { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 3 3; }
.viz-dot { stroke: var(--surface); stroke-width: 2; }   /* 2px surface ring so overlapping dots stay separate */
.viz-flag { fill: none; stroke: var(--warn); stroke-width: 2; }
.viz-tip { position: absolute; top: 4px; z-index: 5; pointer-events: none; min-width: 150px; max-width: 260px;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-md); box-shadow: var(--sh-2);
  padding: 8px 10px; font-size: var(--t-xs); }
.viz-tip-h { font-weight: 700; color: var(--ink); margin-bottom: 4px; }
.viz-tip-r { display: flex; align-items: center; gap: 6px; padding: 1px 0; color: var(--ink-soft); }
.viz-tip-l { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viz-tip-v { font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink); }
.viz-tip-none { color: var(--muted); font-style: italic; }
.viz-sw { flex: none; width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
/* On the combined chart the tooltip names every line that moved that month, so each row needs a
   swatch, a name that can be truncated, and a right-aligned figure. */
.viz-tip-n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.viz-tip-r .viz-tip-v { margin-left: auto; }
.viz-tip-tot { border-top: 1px solid var(--line); margin-top: 3px; padding-top: 3px; font-weight: 700; }
.viz-tip { max-width: 300px; }

/* ---- Legend for the combined chart ----------------------------------------------------------
   Eight coloured squiggles with no key is a decoration, not a chart — so every line gets a row
   naming it, what it usually runs and what it last ran. The row is also the way IN: clicking it
   opens that one line on its own ("select it item by item"). */
.viz-legend { display: grid; gap: 2px; margin-top: var(--s2); }
.viz-leg-row { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 4px 6px; border: 1px solid transparent; border-radius: var(--r-sm);
  background: none; color: var(--ink-soft); font: inherit; font-size: var(--t-xs); cursor: pointer; }
.viz-leg-row:hover:not([disabled]) { background: var(--surface-2); border-color: var(--line); }
.viz-leg-row[disabled] { cursor: default; }
.viz-leg-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-weight: 600; color: var(--ink); }
.viz-leg-nums { flex: none; color: var(--muted); font-variant-numeric: tabular-nums; }
.viz-leg-flag { flex: none; color: var(--warn); font-weight: 700; }
@media (max-width: 620px) {
  .viz-leg-row { flex-wrap: wrap; }
  .viz-leg-name { flex: 1 1 100%; }
}

/* The heading IS the legend when there is only ONE series — its name sits in the title beside its
   colour swatch rather than in a separate legend box. */
.viz-what { font-weight: 700; }
.viz-at { color: var(--muted); font-weight: 400; }
.viz-where { display: inline-flex; align-items: center; gap: 5px; font-weight: 700; }
.viz-sub { margin: 0 0 var(--s2); }
.viz-usual { stroke: var(--line-strong); stroke-width: 1; stroke-dasharray: 4 4; }
.viz-usual-lbl { fill: var(--muted); font-size: 10px; font-variant-numeric: tabular-nums; }
.viz-foot { margin-top: 6px; }
.viz-table-wrap { overflow-x: auto; }
.viz-table { border-collapse: collapse; font-size: var(--t-xs); width: 100%; }
.viz-table th, .viz-table td { padding: 5px 8px; text-align: right; white-space: nowrap; border-bottom: 1px solid var(--line); font-variant-numeric: tabular-nums; }
.viz-table th:first-child, .viz-table td:first-child { text-align: left; position: sticky; left: 0; background: var(--surface); }
.viz-table thead th { color: var(--muted); font-weight: 700; }
.viz-table td:first-child { display: table-cell; }
.viz-table .viz-sw { margin-right: 6px; vertical-align: middle; }

/* ---- Vendor payment history, at the signature -----------------------------------------------
   Sits in the signing panel between "how this pays" and the Sign button, because that is the moment
   the question gets asked. Deliberately quiet: tiles read as facts, and only the comparison sentence
   is allowed to raise its voice — the panel exists to be read every time, and a section that shouts
   every time stops being read. */
.ap-hist { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
  padding: var(--s3); margin-top: var(--s3); }
.ap-hist-load { display: flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--t-xs); }
.ap-hist-none { display: flex; align-items: flex-start; gap: 6px; font-size: var(--t-sm); color: var(--ink-soft); }
.ap-hist-none svg { flex: none; margin-top: 2px; color: var(--warn); }
.ap-hist-verdict { display: flex; align-items: flex-start; gap: 6px; padding: var(--s2) var(--s3);
  border-radius: var(--r-md); background: var(--surface-2); font-size: var(--t-sm); margin-bottom: var(--s2); }
.ap-hist-verdict svg { flex: none; margin-top: 2px; color: var(--good); }
/* Same amber wash the unusual-month callout uses, so "look at this" means one thing app-wide. */
.ap-hist-verdict.warn { background: color-mix(in srgb, var(--warn) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warn) 34%, transparent); }
.ap-hist-verdict.warn svg { color: var(--warn); }
/* Auto-fit rather than a fixed column count: the same tiles have to sit in a narrow signing panel
   on a laptop and full width on a phone without a second breakpoint chasing them. */
.ap-hist-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--s2); margin-bottom: var(--s3); }
.ap-hist-tile { display: flex; flex-direction: column; gap: 1px; padding: var(--s2);
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); }
.ap-hist-tv { font-size: 1.05rem; font-weight: 700; font-variant-numeric: tabular-nums; }
.ap-hist-tl { font-size: var(--t-xs); color: var(--muted); }
.ap-hist-ts { font-size: var(--t-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.viz-bar { transition: opacity .12s ease; }
.viz-plot:hover .viz-bar { opacity: .85; }
.ap-hist-recent { margin-top: var(--s3); }
.ap-hist-rh { font-size: var(--t-xs); color: var(--muted); font-weight: 700; margin-bottom: 4px; }
/* Date and amount shrink to exactly what they need (`width: 1%` with auto layout), so ALL the slack
   goes to the middle column — the invoice number and how it was paid, which is the part that is
   actually read. Fixed columns were tried first and starved that column on a phone. */
.ap-hist-table { font-size: var(--t-xs); }
.ap-hist-table td { vertical-align: top; padding: 6px 6px; }
.ap-hist-table td:first-child, .ap-hist-table td:last-child { width: 1%; }
/* Shrink-to-fit lands a hair narrower than a short company name, which then broke onto three lines
   right next to a date that fits on one. A floor of a few characters more settles it. */
.ap-hist-table td:first-child { min-width: 7rem; }
.ap-hist-table td:last-child { white-space: nowrap; }
.ap-hist-table td:nth-child(2) { overflow-wrap: anywhere; }
/* Only the date itself is protected from wrapping — a broken date stops reading as a date — while
   the company name under it wraps, which is what stopped it overflowing onto the next column. */
.ap-hist-d { white-space: nowrap; }
.ap-hist-sub { color: var(--muted); font-size: var(--t-xs); margin-top: 1px; }
.ap-hist-open { color: var(--warn); font-weight: 600; }
.ap-hist-more { margin-top: 4px; }
/* ---- opening what the row describes ---------------------------------------------------------
   A row whose invoice we can actually show is a target: the whole line highlights, because the line
   is what the signer is already reading, and the pill inside it is what says the line can be opened
   at all. A row we cannot open says why in its own words instead of quietly refusing the click. */
.ap-hist-table tr.ap-hist-row { cursor: pointer; }
.ap-hist-table tr.ap-hist-row:hover { background: var(--surface-2); }
.ap-hist-docs { margin-top: 3px; }
.ap-hist-doc { display: inline-flex; align-items: center; gap: 4px; margin: 2px 6px 0 0; padding: 2px 8px;
  border: 1px solid var(--line-strong); border-radius: var(--r-full); background: var(--surface);
  color: var(--primary-ink); font-size: var(--t-xs); font-weight: 600; cursor: pointer; }
.ap-hist-doc:hover { background: var(--primary-soft); border-color: var(--primary); }
.ap-hist-doc svg { flex: none; }
.ap-hist-nodoc { font-style: italic; }
/* Said once, under the chart, rather than repeated on every row it applies to. */
.ap-hist-hint { font-size: var(--t-xs); color: var(--muted); margin-top: 4px; }
.ap-hist-back { margin-top: 6px; }
/* The clicked month, marked on the chart it was clicked on — a wash behind the bar, not a colour
   change to the bar itself, so "unusually high" (the second hue) keeps meaning only that. */
.viz-pick { fill: color-mix(in srgb, var(--primary) 15%, transparent);
  stroke: color-mix(in srgb, var(--primary) 45%, transparent); stroke-width: 1; }
.viz-pickable { cursor: pointer; }

/* ---- PO ↔ supply order match ---------------------------------------------------------------
   Sits under the invoice facts while the bill is being entered: what was ordered and signed off,
   next to what is being billed. The over-billed line is the whole point of the panel, so it is the
   one thing here allowed to shout. */
.ap-po-hint { display: inline; margin: 0 0 0 4px; font-weight: 400; }
.ap-po-match { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); padding: var(--s3); margin-top: var(--s2); }
.ap-po-head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; font-size: var(--t-sm); }
.ap-po-who { color: var(--muted); font-size: var(--t-xs); }
.ap-po-status { text-transform: uppercase; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  padding: 2px 7px; border-radius: var(--r-full); border: 1px solid var(--line-strong); color: var(--muted); }
.ap-po-approved { color: var(--good); border-color: color-mix(in srgb, var(--good) 45%, transparent); }
.ap-po-pending { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, transparent); }
.ap-po-rejected, .ap-po-canceled { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); }
.ap-po-over { display: flex; align-items: flex-start; gap: 6px; margin: var(--s2) 0; padding: var(--s2) var(--s3);
  border-radius: var(--r-md); background: color-mix(in srgb, var(--danger) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--danger) 38%, transparent); font-size: var(--t-sm); line-height: 1.45; }
.ap-po-over svg { flex: none; margin-top: 2px; color: var(--danger); }
.ap-po-sum { margin: 4px 0 var(--s2); }
.ap-po-lines { width: 100%; font-size: var(--t-xs); }
.ap-po-lines th { color: var(--muted); font-weight: 700; text-align: left; }
.ap-po-lines th.ap-num, .ap-po-lines td.ap-num { text-align: right; font-variant-numeric: tabular-nums; }
.ap-po-cut { color: var(--warn); font-weight: 700; }   /* approver cut this line back */
.ap-po-none { display: flex; align-items: flex-start; gap: 6px; font-size: var(--t-sm); color: var(--ink-soft); }
.ap-po-none svg { flex: none; margin-top: 2px; color: var(--warn); }
/* Batch emails: documents that already became a bill show a quiet "entered" check and can't be
   re-selected; the row's meta line counts progress ("3 of 68 entered"). */
.ap-inv-doc.entered { opacity: .6; }
.ap-inv-doc.entered .ap-inv-docname { text-decoration: line-through; text-decoration-thickness: 1px; }
.ap-doc-entered { color: var(--good, #2C7A3F); font-weight: 600; white-space: nowrap; text-decoration: none; display: inline-flex; align-items: center; gap: 2px; }
.ap-line.ap-payrow { grid-template-columns: 1.6fr minmax(96px, .8fr) auto; }
.ap-line .ap-l-amt, .ap-line .ap-p-amt { text-align: right; }
/* Searchable account picker (Bill.com-look): type to filter; sub-accounts indent under parents. */
.ap-acc-combo { position: relative; display: flex; align-items: center; min-width: 0; }
.ap-acc-combo .ap-acc-in { width: 100%; padding-right: 26px; text-overflow: ellipsis; }
.ap-acc-caret { position: absolute; right: 4px; border: 0; background: none; padding: 2px; color: var(--muted); cursor: pointer; display: grid; place-items: center; }
.ap-acc-caret svg { transform: rotate(90deg); }
.ap-acc-pop { position: absolute; top: calc(100% + 4px); left: 0; min-width: 100%; width: max-content; max-width: min(440px, 70vw); max-height: 300px; overflow: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--sh-3); z-index: 40; padding: 4px 0; }
.ap-acc-row { display: flex; align-items: baseline; gap: 6px; padding: 7px 12px 7px 0; font-size: var(--t-sm); cursor: pointer; white-space: nowrap; }
.ap-acc-row.active, .ap-acc-row:hover { background: var(--primary-soft); }
.ap-acc-num { font-variant-numeric: tabular-nums; flex-shrink: 0; }
.ap-acc-dash { color: var(--muted); flex-shrink: 0; }
.ap-acc-name { overflow: hidden; text-overflow: ellipsis; }
.ap-acc-empty { padding: 10px 12px; color: var(--muted); font-size: var(--t-sm); }
.ap-recon { font-size: var(--t-xs); color: var(--muted); padding: var(--s2); background: var(--surface-2); border-radius: var(--r-sm); }
.ap-ok { color: var(--good); font-weight: 700; }
.ap-bad { color: var(--danger); font-weight: 600; }
/* The one box a save is actually waiting on. A refusal that says "street, city, state and ZIP are
   required" is no help when three of the four are filled in — this puts the answer on the box
   itself, so the eye lands on it before the sentence is even read (owner 2026-07-30). */
.field-input.field-bad,
.field-input.field-bad:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 18%, transparent);
  background: color-mix(in srgb, var(--danger) 5%, var(--surface));
}
.ap-mode-row { display: flex; gap: var(--s4); font-size: var(--t-sm); }
.ap-form-actions { margin-top: var(--s2); }

/* Vendor picker (Create Bill) — live search over the QuickBooks mirror */
.ap-vendor-wrap { position: relative; display: block; }
/* Full-width vendor row at the top of the form (Bill.com layout) + browse-all chevron */
.ap-form > .ap-vendor-cell { display: grid; gap: 6px; font-size: var(--t-sm); font-weight: 700; color: var(--ink); }
.ap-vendor-wrap .field-input { padding-right: 40px; }
.ap-vendor-ddbtn {
  position: absolute; right: 6px; top: 50%; transform: translateY(-50%); width: 30px; height: 30px;
  display: inline-flex; align-items: center; justify-content: center; border: 0; background: none;
  color: var(--muted); cursor: pointer; border-radius: var(--r-sm);
}
.ap-vendor-ddbtn:hover { color: var(--primary); background: var(--primary-soft); }
.ap-vendor-ddbtn svg { transform: rotate(90deg); } /* chevron points down */
.ap-terms-default { display: flex; align-items: center; gap: 6px; margin-top: 3px; font-size: var(--t-xs); font-weight: 500; color: var(--muted); cursor: pointer; }
.ap-terms-default input { margin: 0; }
.ap-cbcfg {
  display: inline-flex; align-items: center; gap: 4px; margin-left: 8px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-full);
  padding: 3px 10px; font-size: var(--t-xs); font-weight: 600; color: var(--ink-soft); vertical-align: middle;
}
.ap-cbcfg:hover { color: var(--primary); border-color: var(--primary); }
.ap-memo { display: grid; gap: 6px; font-size: var(--t-sm); font-weight: 700; color: var(--ink); }
.ap-memo input { font-weight: 400; }
.ap-memo .cc-note { font-weight: 400; }
.ap-memo-count { font-weight: 400; font-size: var(--t-xs); color: var(--muted); margin-left: 6px; }
.ap-vendor-dd { position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 70; display: block;
  background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-md);
  box-shadow: var(--sh-3); max-height: 300px; overflow: auto; padding: 4px; }
.ap-vendor-dd[hidden] { display: none; }
.ap-vend-row { display: grid; gap: 3px; padding: var(--s2) var(--s3); border-radius: var(--r-sm); cursor: pointer; }
.ap-vend-row:hover { background: var(--primary-softer); }
.ap-vend-name { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.ap-vend-meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; font-size: var(--t-xs); color: var(--muted); }
.ap-vend-badge { display: inline-flex; font-size: 10.5px; font-weight: 700; padding: 1px 7px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--primary) 10%, transparent); color: var(--primary-ink);
  border: 1px solid color-mix(in srgb, var(--primary) 24%, transparent); }
.ap-vend-badge.off { opacity: .55; text-decoration: line-through; }
.ap-vend-new { display: block; margin-top: 2px; padding: var(--s2) var(--s3); border-top: 1px dashed var(--line-strong);
  font-size: var(--t-sm); font-weight: 600; color: var(--primary); }
.ap-vendor-status { display: block; min-height: 0; font-size: var(--t-xs); color: var(--muted); line-height: 1.5; }
.ap-vendor-status.ok { color: var(--ink-soft); }
.ap-vendor-status.warn { color: var(--warn); }
.ap-vendor-status a { color: var(--primary); font-weight: 600; }
.ap-vendor-status a:hover { text-decoration: underline; }
.ap-vendor-status .ap-vend-suggest { font-weight: 700; } /* the closest-match picks are the primary action */
/* Always-visible "New vendor" button directly under the vendor field in the Create Bill form */
.ap-newvendor-btn { justify-self: start; margin-top: 2px; }
.ap-vend-bits { color: var(--muted); }

/* New Vendor modal — scoped to the modal ONLY: the in-form vendor info card reuses the bare
   .ap-vendor-card class, and this rule's 560px cap used to leak onto it (the "card stuck at
   ~60% width" bug). The modal markup carries both classes (chooser-card ap-vendor-card). */
/* "Working on it" spinner sitting inside a button. Vendor saves write to QuickBooks once per
   company that has the vendor, so they take seconds — the button has to look alive the instant it
   is pressed, or it reads as frozen and gets clicked again. Reuses the boot screen's rotation. */
.btn-spin { display: inline-block; width: 13px; height: 13px; margin-right: 7px; vertical-align: -2px;
  border: 2px solid currentColor; border-top-color: transparent; border-radius: 50%; opacity: .8;
  animation: bootSpin .7s linear infinite; }
.ap-saving { color: var(--muted); font-style: italic; }
.ap-save-note:empty { display: none; }
@media (prefers-reduced-motion: reduce) { .btn-spin { animation: none; } }

.chooser-card.ap-vendor-card { max-width: 560px; max-height: min(86vh, 780px); overflow: auto; }
.ap-vendor-modal { display: grid; gap: var(--s3); margin: var(--s3) 0; }
/* Per-company account numbers: one row per company, tick = "save this one". Each of our companies
   is a separate customer of the vendor, so a vendor can carry a different number in each. */
.ap-acct-grid { display: grid; gap: 6px; }
.ap-acct-row { display: grid; grid-template-columns: auto minmax(0, 1fr) minmax(0, 1.1fr); align-items: center;
  gap: 9px; padding: 5px 7px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); }
.ap-acct-row:focus-within { border-color: var(--primary); }
.ap-acct-co { font-size: var(--t-sm); font-weight: 600; color: var(--ink); overflow: hidden; text-overflow: ellipsis; }
.ap-acct-num { min-width: 0; }
.ap-acct-row input[type="checkbox"] { flex: none; }
@media (max-width: 560px) {
  .ap-acct-row { grid-template-columns: auto minmax(0, 1fr); }
  .ap-acct-num { grid-column: 2 / -1; }
}
/* Notes under the memo. The quiet form is informational (which company's number each check quotes). */
.ap-memo-warn { display: flex; align-items: flex-start; gap: 7px; margin-top: 6px; padding: 7px 10px;
  border-radius: var(--r-md); background: var(--warn-soft, color-mix(in srgb, var(--warn) 12%, transparent));
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent); font-size: var(--t-sm); color: var(--ink-soft); }
.ap-memo-warn svg { flex: none; margin-top: 2px; color: var(--warn); }
.ap-memo-warn a { color: var(--primary-ink); font-weight: 700; }
/* The HARD form: a real check is about to be mailed with no account number on it, so the vendor may
   not be able to match the payment. Owner asked for this to be unmissable when it's right — heavier
   weight, a solid accent rule, and the fix as an actual button. Still calm, not alarming-red. */
.ap-memo-warn.ap-memo-warn-hard { align-items: center; gap: 11px; padding: 12px 14px;
  border: 1.5px solid var(--warn); border-left: 5px solid var(--warn);
  background: color-mix(in srgb, var(--warn) 20%, var(--surface));
  color: var(--ink); font-size: var(--t-base); line-height: 1.45; box-shadow: var(--sh-2, 0 2px 8px rgba(0,0,0,.10));
  animation: apMemoWarnIn .28s var(--ease, ease) both; }
.ap-memo-warn.ap-memo-warn-hard svg { width: 22px; height: 22px; margin-top: 0; color: var(--warn); stroke-width: 2.4; }
.ap-memo-warn.ap-memo-warn-hard strong { font-weight: 800; }
.ap-memo-warn.ap-memo-warn-hard .btn { margin-left: 8px; vertical-align: baseline; white-space: nowrap; }
@keyframes apMemoWarnIn { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }
/* The $0-invoice explainer: reassuring, not alarming — this is a normal thing that is being handled
   properly, and the note says exactly what will happen to it. */
.ap-zero-note { display: flex; align-items: flex-start; gap: 9px; margin-top: 8px; padding: 11px 13px;
  border-radius: var(--r-md); border: 1px solid color-mix(in srgb, var(--good, var(--primary)) 45%, transparent);
  background: color-mix(in srgb, var(--good, var(--primary)) 12%, var(--surface));
  font-size: var(--t-sm); line-height: 1.5; color: var(--ink); }
.ap-zero-note svg { flex: none; margin-top: 2px; width: 18px; height: 18px; color: var(--good, var(--primary)); }
@media (prefers-reduced-motion: reduce) { .ap-memo-warn.ap-memo-warn-hard { animation: none; } }
.ap-nv-field { display: grid; gap: 4px; font-size: var(--t-xs); color: var(--muted); }
.ap-nv-label { font-size: var(--t-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); margin-top: var(--s1); }
.ap-nv-comps { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s4); }
.ap-nv-comp { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); color: var(--ink); }
.ap-nv-results { display: grid; gap: 4px; font-size: var(--t-sm); }
.ap-nv-results:empty { display: none; }
/* AI "read the invoice & fill in" row at the top of the New-vendor panel */
.ap-nv-airow { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: var(--s3); border: 1px solid var(--primary); border-radius: var(--r-md); background: var(--primary-soft); }
.ap-nv-airow .btn { flex: none; white-space: nowrap; }
.ap-nv-airow .cc-note { flex: 1; min-width: 160px; margin: 0; }

/* Status chips + banners */
.ap-status-chip { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); font-weight: 700;
  padding: 3px 9px; border-radius: var(--r-full); background: var(--surface-2); color: var(--muted); border: 1px solid var(--line); }
.ap-st-good { background: color-mix(in srgb, var(--good) 12%, transparent); color: var(--good); border-color: color-mix(in srgb, var(--good) 30%, transparent); }
.ap-st-warn { background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
.ap-st-bad { background: color-mix(in srgb, var(--danger) 12%, transparent); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 30%, transparent); }
.ap-st-info { background: color-mix(in srgb, var(--primary) 10%, transparent); color: var(--primary); border-color: color-mix(in srgb, var(--primary) 26%, transparent); }
.paid-chip { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); font-weight: 800; letter-spacing: .04em;
  padding: 3px 10px; border-radius: var(--r-full); background: color-mix(in srgb, var(--good) 16%, transparent); color: var(--good); }
/* A banner is a SENTENCE with an icon, not a row of boxes. It used to be display:flex — which made
   every <strong> and every run of plain text its own flex item, so "Signing now <strong>schedules</strong>
   it" laid out as three side-by-side columns and the words read out of order on the signing screen
   (owner 2026-08-25). Block flow keeps the sentence a sentence. Nothing here may go back to flex
   without scrambling the wording again. */
.ap-banner { display: block; font-size: var(--t-sm); padding: var(--s2) var(--s3);
  border-radius: var(--r-sm); margin-bottom: var(--s2); }
/* The hanging indent past the icon, kept from the flex version. Gated on :has() so a banner with no
   icon gets no empty gutter — and so a browser without :has() drops BOTH rules together and simply
   renders the icon inline, which is tidy rather than overlapped. */
.ap-banner:has(> .icon:first-child) { position: relative; padding-left: calc(var(--s3) + 18px + var(--s2)); }
.ap-banner:has(> .icon:first-child) > .icon:first-child { position: absolute; left: var(--s3); top: calc(var(--s2) + 2px); }
/* .btn-primary is width:100% for the login form's stacked buttons (same trap as the bulk bars at
   #abBulk below). Flex used to hide it here; in block flow it would span the whole banner. */
.ap-banner .btn-primary { width: auto; padding: var(--s3) var(--s4); }
.ap-banner-info { background: color-mix(in srgb, var(--primary) 8%, transparent); color: var(--ink); }
.ap-banner-warn { background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--ink); border-left: 3px solid var(--warn); }
.ap-banner-bad { background: color-mix(in srgb, var(--danger) 10%, transparent); color: var(--ink); border-left: 3px solid var(--danger); }
/* "View bill N's invoice" link inside a Guardian banner — pops the cited bill's document. */
.ap-flag-ref { border: 0; background: none; padding: 0; color: var(--primary); font: inherit; font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer; white-space: nowrap; }
.ap-flag-ref:hover { color: color-mix(in srgb, var(--primary) 75%, var(--ink)); }
.ap-flag-ref[disabled] { opacity: .6; cursor: wait; }

/* Status panel + tables */
.ap-status-panel { display: grid; gap: var(--s3); }
.ap-facts .ap-k { display: block; font-size: var(--t-xs); color: var(--muted); }
/* Vendor contact + payment card, shown under the vendor in the bill panels + sign screen. */
.ap-vendor-meta { display: flex; flex-direction: column; gap: 4px; padding: 9px 11px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); font-size: var(--t-xs); color: var(--ink-soft); }
.ap-vmeta-row { display: flex; align-items: center; gap: 7px; min-width: 0; }
.ap-vmeta-row svg { flex: none; color: var(--muted); }
.ap-vmeta-row span { overflow: hidden; text-overflow: ellipsis; }
.ap-vmeta-row a { color: var(--primary-ink); text-decoration: none; overflow: hidden; text-overflow: ellipsis; }
.ap-vmeta-row a:hover { text-decoration: underline; }
/* "Ask the vendor for it" — the two ways of asking for direct-deposit details, docked under the
   payment lines they answer. Deliberately NOT a .ap-vmeta-row: that rule ellipsis-clips its spans,
   which would quietly eat a button. Wraps rather than overflows, because the bill drawer can be a
   narrow pane. .btn-primary is width:100% app-wide, so these stay plain .btn.btn-sm. */
.ap-vmeta-ask { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-top: 7px; padding-top: 8px; border-top: 1px solid var(--line); }
.ap-vmeta-asklabel { color: var(--muted); font-weight: 600; }
.ap-vmeta-ask .btn { flex: none; }
.ap-vmeta-ask .btn[disabled] { opacity: .5; cursor: not-allowed; }
/* "Edit:" sits above "Ask the vendor for it:" when a vendor has no banking, so the second row drops
   its rule — one divider separates the facts from the actions, not one per row of buttons. */
.ap-vmeta-ask + .ap-vmeta-ask { margin-top: 6px; padding-top: 0; border-top: 0; }
/* A vendor-submitted bank account that would REPLACE one we already pay. It carries weight because
   the reviewer's three buttons look identical either way, and only one of the two decisions can
   send next month's money to a stranger. */
.ap-ach-replaces { display: flex; align-items: flex-start; gap: 6px; margin-top: 6px; padding: 7px 9px;
  border-radius: var(--r-sm); background: var(--warn-bg, rgba(161, 92, 0, .08));
  color: var(--warn, #a15c00); font-size: var(--t-xs); line-height: 1.5; }
.ap-ach-replaces svg { flex: none; margin-top: 2px; }
/* One-press "Send & request signature" progress — calm, immediate feedback during the ~15s QBO call. */
.ap-send-overlay { position: absolute; inset: 0; z-index: 6; background: var(--surface); display: flex; align-items: center; justify-content: center; overflow: auto; }
.ap-send-progress { display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 44px 20px; text-align: center; }
.ap-send-plane { color: var(--primary); animation: apsend-fly 1.5s ease-in-out infinite; }
.ap-send-plane svg { width: 40px; height: 40px; }
@keyframes apsend-fly { 0%, 100% { transform: translateX(-7px); opacity: .65; } 50% { transform: translateX(7px); opacity: 1; } }
.ap-send-title { font-weight: 700; font-size: var(--t-md); color: var(--ink); }
.ap-send-steps { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 11px; align-items: flex-start; }
.ap-send-item { display: flex; align-items: center; gap: 10px; font-size: var(--t-sm); color: var(--muted); transition: color .2s ease; }
.ap-send-item.active { color: var(--ink); font-weight: 600; }
.ap-send-item.done { color: var(--good); }
.ap-send-dot { width: 18px; height: 18px; border-radius: 50%; flex: none; display: inline-flex; align-items: center; justify-content: center; border: 2px solid var(--line-strong); box-sizing: border-box; }
.ap-send-item.active .ap-send-dot { border-color: var(--primary); border-top-color: transparent; animation: apsend-spin .75s linear infinite; }
.ap-send-item.done .ap-send-dot { border-color: var(--good); background: var(--good); color: #fff; }
.ap-send-item.done .ap-send-dot svg { width: 12px; height: 12px; }
@keyframes apsend-spin { to { transform: rotate(360deg); } }
.ap-table { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.ap-table td, .ap-table th { padding: 6px 8px; border-bottom: 1px solid var(--line); text-align: left; }
.ap-table .ap-num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Mailed-check paper trail — Mercury's tracking number, printed check number, scanned check image
   and generated receipt, gathered onto the bill so nobody has to open Mercury to see them. */
.ap-check-card { display: grid; gap: var(--s2); padding: var(--s3); border: 1px solid var(--line);
  border-radius: var(--r-md); background: var(--surface-2); }
.ap-check-head { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-sm); }
.ap-check-head svg { flex: none; color: var(--muted); }
.ap-check-head .linkbtn { margin-left: auto; }
.ap-check-row { display: grid; gap: var(--s2); padding: var(--s3); border: 1px solid var(--line);
  border-radius: var(--r-sm); background: var(--surface); }
.ap-check-top { display: flex; align-items: center; flex-wrap: wrap; gap: var(--s2); font-size: var(--t-sm); }
.ap-check-top svg { flex: none; color: var(--muted); }
.ap-check-stage { margin-left: auto; font-size: var(--t-xs); color: var(--muted); }
.ap-check-facts { font-size: var(--t-sm); font-variant-numeric: tabular-nums; }
.ap-check-acts { display: flex; flex-wrap: wrap; gap: var(--s2); }
/* Payment tracking timeline (bill view) — the Bill.com-style journey, drawn from our own facts.
   Rung states: done (green tick), pending (hollow), warn (amber !), bad (red x). The connecting
   line is per-step (::before below the dot), green while the journey is still on track. */
.ap-trk { display: grid; padding: 2px 0 0; }
.ap-trk-step { position: relative; padding: 0 0 var(--s3) 30px; }
.ap-trk-step:last-child { padding-bottom: var(--s1); }
.ap-trk-step::before { content: ''; position: absolute; left: 9px; top: 22px; bottom: 0; width: 2px;
  background: var(--line-strong); border-radius: 1px; }
.ap-trk-step:last-child::before { display: none; }
.ap-trk-step.done::before { background: color-mix(in srgb, var(--good) 45%, var(--line-strong)); }
.ap-trk-dot { position: absolute; left: 0; top: 2px; width: 20px; height: 20px; border-radius: 50%;
  display: grid; place-items: center; font-size: 11px; font-weight: 700; line-height: 1;
  background: var(--surface); border: 2px solid var(--line-strong); color: transparent; }
.ap-trk-step.done .ap-trk-dot { background: var(--good); border-color: var(--good); color: #fff; }
.ap-trk-step.warn .ap-trk-dot { background: var(--warn); border-color: var(--warn); color: #fff; }
.ap-trk-step.bad .ap-trk-dot { background: var(--danger); border-color: var(--danger); color: #fff; }
.ap-trk-date { font-size: var(--t-xs); color: var(--muted); font-variant-numeric: tabular-nums; }
.ap-trk-label { font-size: var(--t-sm); font-weight: 600; color: var(--ink); }
.ap-trk-step.pending .ap-trk-label { color: var(--muted); font-weight: 500; }
.ap-trk-step.bad .ap-trk-label { color: var(--danger); }
.ap-trk-step.warn .ap-trk-label { color: var(--warn); }
.ap-trk-sub { font-size: var(--t-xs); color: var(--muted); margin-top: 1px; overflow-wrap: anywhere; }
.ap-trk-h { font-size: var(--t-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin: var(--s2) 0 2px; }
.ap-check-row .ap-trk-h:first-child { margin-top: 0; }
.ap-check-acts .btn[disabled] { opacity: .5; cursor: not-allowed; }
.ap-check-acts a.btn { text-decoration: none; display: inline-flex; align-items: center; gap: 6px; }
.ap-plan-list { display: grid; gap: var(--s1); }
.ap-payline { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-sm); }
.ap-checklist ol { margin: var(--s2) 0 0 var(--s4); padding: 0; display: grid; gap: 4px; }

/* Sign-payments screen */
.ap-queue { display: grid; gap: var(--s2); padding: var(--s3); }
.ap-queue-item { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); text-align: left;
  padding: var(--s3); border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); cursor: pointer;
  transition: border-color .15s var(--ease), box-shadow .15s var(--ease); }
.ap-queue-item:hover { border-color: var(--primary); box-shadow: var(--sh-1); }
.ap-queue-amt { font-variant-numeric: tabular-nums; white-space: nowrap; display: flex; align-items: center; gap: var(--s2); }
/* Sign-queue row is now [checkbox] + [clickable open button] so a payment can be ticked for a batch
   without opening it. The open button carries all the row's click-to-view behaviour. */
.ap-queue-check { display: flex; align-items: center; padding: 0 2px 0 2px; cursor: pointer; flex: none; }
.ap-queue-check input { width: 16px; height: 16px; accent-color: var(--primary); cursor: pointer; }
.ap-queue-open { flex: 1; min-width: 0; display: flex; justify-content: space-between; align-items: center; gap: var(--s3);
  background: none; border: 0; padding: 0; margin: 0; text-align: left; cursor: pointer; color: inherit; font: inherit; }
.ap-queue-main { min-width: 0; }
/* Bolder section header — used on the AP Bills list AND the Sign-payments queue so they match
   (owner: make the header bolder and put the same one on the sign portal). */
.card h3.ap-strong-head { font-size: var(--t-lg); font-weight: 800; letter-spacing: -.02em;
  padding-bottom: var(--s2); margin-bottom: var(--s3); border-bottom: 2px solid var(--line); }
.card h3.ap-strong-head svg { width: 20px; height: 20px; color: var(--primary); }
/* Bulk-action bar shared by AP Bills (send several for signature) and the Sign queue (sign / reject
   a batch). Sits above the list; the count pushes the action buttons to the right edge. */
.ap-bulkbar { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: var(--s2) var(--s3); margin: var(--s2) var(--s3); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-md); }
.ap-bulkbar[hidden] { display: none; }
.ap-bulkbar .btn { margin: 0; }
/* "Back to bills" / "Back to payments" — larger and on its own row above the rest (owner 2026-07-23). */
.ap-back-btn { font-size: var(--t-md); padding: 10px 18px; font-weight: 700; }
.ap-back-btn svg { width: 18px; height: 18px; }
.ap-bulkbar .bulk-count { margin-right: auto; font-weight: 600; font-size: var(--t-sm); color: var(--muted); }
/* SIGN PAYMENTS — the bulk controls live in the SCREEN HEADER (owner 2026-08-27, second pass:
   "i want just those buttons to be on the frozen part"). .list-head is a sibling of #list, and #list
   is the only thing that scrolls, so anything in that row is frozen for free — no sticky, no
   z-index, no offsets to keep in step with padding. It also hands the table back the row of height
   the in-list bar used to cost.

   ⚠ .list-head is SHARED by every screen and nothing clears widgets left in it (this is how Admin
     and University once wore the last inbox's toggles). So the visibility is decided HERE, by
     :has(), not by remembering to call a teardown from every screen-change path — the selector
     stops matching the moment #list stops holding the sign queue, and an abandoned #apSignActions
     simply goes display:none wherever it is. Same trick as .body:has(#list .wtr-wrap) above.
   ⚠ The :empty rule matters: with filters matching no rows, wireApSignBulk empties the element
     rather than removing it, and a bare flex box would still draw its gaps into the header row. */
#apSignActions { display: none; }
/* ⚠ ITS OWN FULL-WIDTH ROW (flex-basis 100%), not squeezed in beside the title. Sharing the row was
   tried first and does not survive a normal laptop: title + select-all + count + Clear + Sign +
   Reject + the tour chip need ~890px and the centre panel is ~760px at 1400px wide, so the chip
   wrapped to a second line and landed on the LEFT, under the title, looking broken. A dedicated row
   is the same height in every case and never reflows as the count text grows. */
.rail-center:has(#list #asqBody) .list-head #apSignActions {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; flex: 1 1 100%; }
.rail-center:has(#list #asqBody) .list-head #apSignActions:empty { display: none; }
/* On phones mobile.css already sends the tour chip to the back of the header with `order: 7`.
   Ours has to go behind it, or the chip ends up stranded under the action buttons. */
@media (max-width: 760px) { .list-head #apSignActions { order: 8; } }
#apSignActions .btn { margin: 0; }
/* The bill view's Back + Prev/Next pager, relocated into the screen header (owner 2026-08-28:
   "the bills and the next bills needs to be up top on the header"). .list-head sits OUTSIDE #list,
   the element that actually scrolls, so these are frozen by construction. Same :has() lifecycle as
   #apSignActions above: the node stays in the header permanently and simply stops displaying the
   moment #list no longer holds the bill view (#abvBody) — no teardown calls to forget. Unlike the
   sign bar it shares the title's row: four compact controls fit where six wide ones did not. */
#abvHeadBar { display: none; }
.rail-center:has(#list #abvBody) .list-head #abvHeadBar {
  display: inline-flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
#abvHeadBar .btn { margin: 0; }
/* Phones: fall in behind the tour chip (order 7 in mobile.css), same as the sign bar. */
@media (max-width: 760px) { .list-head #abvHeadBar { order: 8; } }
/* Count on the left, buttons on the right — the layout the bar already had, just relocated. */
#apSignActions .bulk-count { margin-right: auto; font-weight: 600; font-size: var(--t-sm); color: var(--muted); }
/* Sign / Reject stay put and grey out while nothing is ticked, rather than appearing and
   disappearing — a header that changes width every time you tick a box is worse than a dull one. */
#apSignActions .btn[disabled], .ap-bulkbar .btn[disabled] { opacity: .45; cursor: not-allowed; }

/* FROZEN COLUMN HEADERS on the sign queue (owner: "make the header frozen too as we scroll down").
   Same three-part recipe as the supplies table and the personnel worksheet — see those blocks:
   ⚠ THE WRAPPER MUST SCROLL VERTICALLY OR NOTHING STICKS. `position: sticky; top: 0` pins to the
     nearest SCROLLING ancestor. .ap-queue-tablewrap already scrolls sideways, and a box that
     scrolls in one axis scrolls in both as far as CSS is concerned — so the header sticks to the
     WRAPPER, not to #list. Vertically the wrapper just grew and #list scrolled instead, which would
     have carried the header away with it. Giving it a height is what makes the pin mean something.
   ⚠ border-collapse: SEPARATE. Under `collapse` a border belongs to the collapsed grid, not the
     cell, and Safari drops it on sticky cells — and the people signing do it on iPads. The header's
     bottom rule moves to an inset box-shadow, which is painted by the cell and travels with it.
     border-spacing: 0 keeps the rest of the table pixel-identical to `collapse`.
   ⚠ OPAQUE background, or the rows slide visibly through the header.
   Scoped by .ap-queue-frozen so the three other .ap-queue-tablewrap tables are untouched. */
.ap-queue-tablewrap.ap-queue-frozen { max-height: calc(100vh - 260px); overflow: auto; }
.ap-queue-frozen .ap-table { border-collapse: separate; border-spacing: 0; }
.ap-queue-frozen .ap-table thead th { position: sticky; top: 0; z-index: 3;
  background: var(--surface); border-bottom: 0; box-shadow: inset 0 -1px 0 var(--line); }
@media (max-width: 760px) { .ap-queue-tablewrap.ap-queue-frozen { max-height: calc(100vh - 200px); } }
.ap-selall { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); font-weight: 600; color: var(--ink); cursor: pointer; flex: none; }
.ap-selall input { width: 16px; height: 16px; accent-color: var(--primary); cursor: pointer; }
/* Checkbox column in the AP Bills table (only pre-signature bills carry one). */
.ap-archive th.ap-selcol, .ap-archive td.ap-selcol { width: 34px; text-align: center; padding-left: 8px; padding-right: 4px; }
.ap-selcol input { width: 16px; height: 16px; accent-color: var(--primary); cursor: pointer; }
/* Scrollable list of payments inside the "Sign N payments?" confirmation. */
.ap-confirm-list { display: grid; gap: 4px; max-height: 220px; overflow: auto; margin: var(--s3) 0;
  padding: var(--s3); background: var(--surface-2); border-radius: var(--r-md); font-size: var(--t-sm); }
.ap-confirm-list > div { display: flex; justify-content: space-between; gap: var(--s3); }
.ap-sign-wrap { display: grid; grid-template-columns: minmax(320px, 1.2fr) var(--ap-sign-form-w, minmax(340px, 1fr)); gap: var(--s3); padding: var(--s3); align-items: start; position: relative; }
@media (max-width: 1000px) { .ap-sign-wrap { grid-template-columns: 1fr; } }
.ap-sign-left { min-height: 420px; }
/* THE INVOICE IS THE HALF THAT HOLDS STILL (owner 2026-08-27: "make it so the pdf screen scrolls
   down when i scroll down so i can always view the pdf"). The right column carries the payment
   facts, the vendor history, the spend chart — several screens of things you read WHILE checking
   them against the document. It used to be the sticky one, so reading any of it scrolled the
   invoice clean off the top and left a tall empty column where the document had been. The pin
   belongs to the thing being compared against, not to the thing doing the comparing.
   This is also what makes editing-at-signing work: the bill form is several screens tall, and
   before the pin moved, scrolling down to Send put the invoice 1674px above a 900px window — back
   to correcting an invoice you cannot see. Both modes now share one rule.
   ⚠ THE SCROLLPORT HERE IS #list, NOT THE WINDOW (.list is overflow-y:auto; .app/.body never
   scroll). Sticky offsets are measured from the top of the LIST area, so they must not include the
   topbar height. The old `top: calc(var(--topbar-h) + 8px)` on the right column was written as if
   the window scrolled; it never showed up as a bug only because that column is the taller of the
   two, and a sticky box with no travel inside its own grid row never moves at all.
   ⚠ ONLY ONE COLUMN MAY BE STICKY. A sticky right column looks harmless while its content fills
   the grid row, but a short panel in a tall row drifts down the page as you scroll.
   Two-column layouts only: below 1000px the wrap is a single column (and mobile.css stacks it),
   where each item is its own grid row and sticky has nothing to travel within. */
@media (min-width: 1001px) {
  /* The offset is .list's own 8px of padding plus .ap-sign-wrap's 12px — i.e. exactly where the
     invoice already sits before you scroll. Any other value and it visibly jumps the moment the
     first pixel scrolls. */
  .ap-sign-left { position: sticky; top: calc(var(--s2) + var(--s3)); }
}
/* While EDITING, the form leads and the invoice takes what is left — the reverse of reading, where
   the invoice is the wider half. 676px is not a taste: .ap-form carries `container-type: inline-size`
   and stacks at ≤640px, and .ap-sign-right spends 34px on padding, so anything under 676 hands back
   the stacked layout this rule exists to avoid. Measured at 1400px: 642px of form → the Description
   column gets 144px instead of collapsing.
   ⚠ THE 1340px FLOOR IS THE POINT OF BALANCE, not a round number. Below it, 676px for the form
   would start eating into the invoice's own 320px minimum — and on a screen that small the stacked
   layout is genuinely the better answer, which is what the container query already does by itself.
   At 1340 and above the invoice measures the same or wider than it did before this rule existed. */
@media (min-width: 1340px) {
  .ap-sign-wrap.ap-sign-editing { grid-template-columns: minmax(320px, 1fr) var(--ap-sign-form-w, clamp(676px, 46%, 1100px)); }
}
/* Drag the split (owner 2026-08-27: "it is not letting me adjust the size of the invoice"). The
   widths above are only a starting point — a bill with an eight-way class split wants a wide form,
   a scanned invoice in small print wants a wide document, and only the person reading it knows
   which. Mirrors the Create-Bill divider (.ap-form-resize) and the reading-pane one
   (.reader-w-resize), including double-click to reset. The handle is positioned in JS because the
   default track is a clamp()/minmax() the CSS cannot resolve to a `right:` offset — see
   apSignPlaceDivider(). Hidden below 1001px, where the wrap is a single column. */
.ap-sign-resize { display: none; }
@media (min-width: 1001px) {
  .ap-sign-resize { display: block; position: absolute; top: 0; bottom: 0; width: 12px; cursor: col-resize; z-index: 6; }
  .ap-sign-resize[hidden] { display: none; }
  .ap-sign-resize::before {
    content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px;
    background: var(--line-strong); transition: background .15s ease, width .15s ease;
  }
  .ap-sign-resize:hover::before, .ap-sign-resize.dragging::before { width: 4px; margin-left: -2px; background: var(--primary); }
}
/* Sized to the LIST scrollport, not to the window: 100vh less the topbar, less .body's 16px of
   padding top and bottom, less .rail-center's two 1px borders, less the ~57px .list-head, less the
   12px sticky offset above and a matching 12px of breathing room below. Too tall and the bottom of
   the document — where a PDF viewer puts its own controls — sits permanently below the fold with
   no way to scroll to it, because a sticky box taller than its scrollport can never be scrolled
   past. Measured at 1400x900 by scripts/apsigndoc-test.mjs, which fails if it stops fitting. */
.ap-sign-frame { width: 100%; height: calc(100vh - var(--topbar-h, 60px) - 176px); min-height: 420px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); }
.ap-sign-right { padding: var(--s4); }
/* The bill view's frozen action bar (owner 2026-08-28: "back to bills up top frozen"). Sticks to
   the top of #list — the app's real scrollport — NOT the window (see the scroll note above). The
   negative margins let it span the card edge-to-edge and cover content sliding beneath it. */
.ap-bv-sticky { position: sticky; top: 0; z-index: 7; display: grid; gap: 6px;
  background: var(--surface); border-bottom: 1px solid var(--line);
  margin: calc(-1 * var(--s4)) calc(-1 * var(--s4)) var(--s3);
  padding: var(--s3) var(--s4); border-radius: var(--r-lg) var(--r-lg) 0 0; }
.ap-sign-total { font-size: 1.6rem; font-weight: 800; margin: var(--s2) 0; font-variant-numeric: tabular-nums; }
/* The bill's own facts, restated at the signature. Memo and Class run the full width — both can be
   a sentence long, and both are the fields a signer is most likely to be checking. */
.ap-sign-facts { margin: var(--s2) 0 var(--s3); font-size: var(--t-sm); }
.ap-sign-facts .ap-fact-wide { grid-column: 1 / -1; }
/* "Why this due date": a term that departs from what we agreed with the vendor, or a due date typed
   by hand, is the thing the signer is actually checking — so it carries weight rather than sitting
   in the same grey as the rest of the sentence. */
.ap-term-odd { color: var(--warn); font-weight: 700; }
/* Why "Send & request signature" is not clickable yet. Sits directly above the buttons, because a
   disabled control with no reason reads as a broken one. */
.ap-send-why { display: flex; align-items: flex-start; gap: 7px; margin: 0 0 var(--s2);
  color: var(--warn); font-weight: 600; }
/* "Bill.com already paid this" on an archive row whose QuickBooks bill still shows a balance.
   Deliberately not a button: the action has to be asked for, not offered. */
.ap-arc-paidwarn { display: flex; align-items: flex-start; gap: 6px; margin-top: 4px;
  padding: 6px 8px; border-radius: var(--r-sm); font-size: var(--t-xs); line-height: 1.4;
  background: color-mix(in srgb, var(--warn) 12%, transparent); color: var(--ink); max-width: 34rem; }
.ap-arc-paidwarn svg { flex: none; margin-top: 2px; color: var(--warn); }
.ap-arc-paidwarn .linkbtn { color: var(--muted); text-decoration: underline; font-weight: 600; }
/* The read-only historical bill view: label/value rows on the right-hand card. */
.ap-hv-rows { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.ap-hv-row { display: flex; justify-content: space-between; gap: 12px; font-size: var(--t-sm);
  border-bottom: 1px solid var(--line); padding-bottom: 6px; }
.ap-hv-row span { color: var(--muted); flex: none; }
.ap-hv-row b { font-weight: 600; text-align: right; overflow-wrap: anywhere; }
/* ---- the rest of a catalogue record: coded lines, payments, the Bill.com original ----
   Loaded after the view paints, so it needs a quiet "working" line and headings that separate
   the sections without shouting. */
.ap-hv-loading { margin-top: 12px; font-size: var(--t-sm); color: var(--muted); }
.ap-hv-h { margin: 16px 0 6px; font-size: var(--t-xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--muted); }
.ap-hv-lines { width: 100%; font-size: var(--t-sm); }
.ap-hv-lines td, .ap-hv-lines th { padding: 5px 6px; }
/* One payment. A payment that did NOT go through is marked in the danger colour and says so in
   words — never folded into a cheerful "paid" (see the cancelled-payment rule in apprint.js). */
.ap-hv-pay { border: 1px solid var(--line); border-radius: var(--r-sm); padding: 8px 10px; margin-bottom: 6px; }
.ap-hv-pay.is-dead { border-color: var(--danger, #b91c1c); }
.ap-hv-pay-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; font-size: var(--t-sm); }
.ap-hv-pay-top span { color: var(--muted); }
.ap-hv-pay-top em { margin-left: auto; font-style: normal; font-weight: 700; font-size: var(--t-xs);
  text-transform: uppercase; letter-spacing: .03em; }
.ap-hv-pay.is-dead .ap-hv-pay-top em { color: var(--danger, #b91c1c); }
.ap-hv-pay-sub { margin-top: 3px; font-size: var(--t-xs); color: var(--muted); overflow-wrap: anywhere; }
.ap-hv-pay-sub.is-dead { color: var(--danger, #b91c1c); }
/* The file list, when a record carries more than one document — click to swap the left pane. */
.ap-hv-docs { display: flex; flex-direction: column; gap: 4px; }
.ap-hv-doc { display: flex; align-items: center; gap: 6px; width: 100%; text-align: left;
  background: none; border: 1px solid var(--line); border-radius: var(--r-sm); padding: 6px 8px;
  font: inherit; font-size: var(--t-sm); color: var(--ink); cursor: pointer; }
.ap-hv-doc:hover, .ap-hv-doc.is-on { background: var(--surface-2); }
.ap-hv-doc svg { flex: none; color: var(--muted); }
.ap-hv-doc span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* A Bill.com payment ATTEMPT that never went — cancelled, void, failed or returned. It keeps its
   link to the invoice, so without this it renders exactly like money that left the bank. Struck
   AND worded: the strike alone is easy to miss on a phone, and colour alone is not a message. */
.ap-pay-void { text-decoration: line-through; text-decoration-thickness: 1px; opacity: .6; }
.ap-pay-voidwhy { color: var(--warn); font-weight: 700; white-space: nowrap; }
.ap-send-why svg { flex: none; margin-top: 2px; }
.ap-term-vend { color: var(--muted); }
/* Coded lines carry a header now that they name a site as well as an account. */
.ap-sign-lines thead th { color: var(--muted); font-size: var(--t-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .01em; }
.ap-plan-head { margin-top: var(--s2); }
.ap-ack { display: flex; gap: var(--s2); align-items: center; font-size: var(--t-sm); margin: var(--s2) 0; }
.ap-prompt-card textarea { width: 100%; margin: var(--s2) 0; }
.ap-confirm-body { font-size: var(--t-sm); color: var(--ink); }

/* Bills archive */
.ap-filters { flex-wrap: wrap; padding: 0 var(--s3) var(--s2); }
.ap-filters .field-input { max-width: 180px; }
.ap-archive { font-size: var(--t-sm); }
/* Column-header row: bolder + darker so the labels read as a strong header (owner 2026-07-23). */
.ap-archive thead th { color: var(--ink); font-weight: 800; font-size: var(--t-xs); letter-spacing: .01em; text-transform: uppercase; }
.ap-archive tbody tr:hover { background: var(--surface-2); }
/* Sign-payments queue is now this same table; let it scroll sideways on a narrow screen. */
.ap-queue-tablewrap { overflow-x: auto; }

/* ---- Bills screen: our bills / the Bill.com years ---------------------------------------------
   One screen, two eras. The switch is a pair of pills rather than a dropdown because the choice is
   binary and has to be VISIBLE — the whole reason the archive was unusable is that it lived
   somewhere nobody thought to look. */
.ap-srcpills { display: flex; gap: 6px; padding: 0 var(--s3) var(--s2); flex-wrap: wrap; }
.ap-srcpill { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; cursor: pointer;
  border: 1px solid var(--line-strong); border-radius: var(--r-full); background: var(--surface);
  color: var(--ink-soft); font-size: var(--t-xs); font-weight: 600; }
.ap-srcpill:hover { background: var(--surface-2); }
.ap-srcpill.on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-ink); }
.ap-srcpill svg { flex: none; }
.ap-arc-tally { padding: var(--s2) var(--s3); font-size: var(--t-xs); color: var(--muted); }
.ap-arc-wrap { overflow-x: auto; }
.ap-arc-table tr.ap-arc-row { cursor: pointer; }
.ap-arc-doc { white-space: nowrap; }
.ap-arc-more { margin: var(--s2) var(--s3); }
.ap-arc-foot { padding: 0 var(--s3) var(--s3); }
/* The empty-search bridge: said where the person is already looking, with the switch attached. */
.ap-arc-hint { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: var(--s2);
  padding: var(--s2) var(--s3); border-radius: var(--r-md); background: var(--surface-2);
  border: 1px solid var(--line); font-size: var(--t-sm); color: var(--ink-soft); }
.ap-arc-hint svg { flex: none; color: var(--primary); }

/* ---- Click-a-header Sort + Filter (AP Bills + Sign-payments) --------------------
   Every column header is a button. A muted caret hints it's clickable; a coloured
   triangle shows the active sort; a dot shows the column is filtered. Clicking opens
   the .ap-colmenu popover (Sort ↑/↓ + a filter for that column). All colours come
   from the theme variables, so light + dark both work. */
.ap-archive thead th.ap-col { padding: 0; }
.ap-colbtn { display: inline-flex; align-items: center; gap: 5px; width: 100%; background: none; border: 0; margin: 0;
  padding: 6px 8px; font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit; font-weight: inherit;
  cursor: pointer; text-align: left; border-radius: var(--r-sm); transition: background .12s var(--ease), color .12s var(--ease); }
.ap-col.ap-num .ap-colbtn { justify-content: flex-end; text-align: right; }
.ap-colbtn:hover { background: var(--surface-3); color: var(--ink); }
.ap-col-on .ap-colbtn { color: var(--primary); }
.ap-collabel { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The "click me" caret — hidden once the column is the active sort (the triangle takes over).
   It sat at opacity .3, which read as decoration rather than a control: on 2026-08-25 the owner
   asked for sort and filter without realising every heading already had both. Legible at rest now,
   full strength on hover. */
.ap-colbtn::after { content: ""; width: 0; height: 0; flex: none; border-left: 3px solid transparent; border-right: 3px solid transparent;
  border-top: 4px solid var(--muted); opacity: .6; transition: opacity .12s var(--ease); }
.ap-colbtn:hover::after { opacity: 1; }
.ap-col-on .ap-colbtn::after { display: none; }
/* Active-sort triangle (up = ascending, down = descending). */
.ap-colsort { display: inline-block; width: 0; height: 0; flex: none; }
.ap-colsort.ap-col-asc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 5px solid var(--primary); }
.ap-colsort.ap-col-desc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 5px solid var(--primary); }
/* Filtered-column dot. */
.ap-coldot { width: 6px; height: 6px; border-radius: 50%; background: var(--primary); flex: none; }
/* Empty state row when filters match nothing. */
.ap-view-empty { text-align: center; color: var(--muted); padding: 20px 12px !important; }

/* The popover menu itself (appended to <body>, positioned under the header in JS). */
.ap-colmenu { position: absolute; z-index: 3200; min-width: 212px; max-width: 288px; padding: 6px;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: 0 14px 36px rgba(0,0,0,.20); font-size: var(--t-sm); color: var(--ink);
  /* A flex column with a JS-set max-height (see openApColMenu). Everything is fixed EXCEPT the
     tick-box list, so Sort / Filter / Apply keep their place and a long vendor list scrolls inside
     itself rather than off the bottom of the window, where nothing could reach it. overflow-y is
     the last resort for a window too short to hold even the fixed parts. */
  display: flex; flex-direction: column; overflow-y: auto; }
.ap-colmenu > * { flex: none; }
.ap-cm-sort { display: flex; align-items: center; gap: 9px; width: 100%; background: none; border: 0; padding: 7px 8px;
  border-radius: var(--r-sm); font: inherit; color: var(--ink); cursor: pointer; text-align: left; }
.ap-cm-sort:hover { background: var(--surface-2); }
.ap-cm-sort.on { color: var(--primary); font-weight: 700; }
.ap-cm-arrow { width: 0; height: 0; flex: none; border-left: 4px solid transparent; border-right: 4px solid transparent; }
.ap-cm-arrow.asc { border-bottom: 6px solid currentColor; }
.ap-cm-arrow.desc { border-top: 6px solid currentColor; }
.ap-cm-div { height: 1px; background: var(--line); margin: 6px 4px; }
.ap-cm-label { font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .04em; color: var(--muted); padding: 2px 8px 4px; font-weight: 700; }
.ap-cm-flt { padding: 2px 6px; }
/* Only the enum (tick-box) filter may shrink — the text/number/date boxes would squash. */
.ap-cm-flt[data-kind="enum"] { display: flex; flex-direction: column; min-height: 0; flex: 0 1 auto; }
.ap-cm-search { width: 100%; margin-bottom: 6px; flex: none; }
.ap-cm-list { max-height: 216px; min-height: 64px; overflow: auto; display: grid; gap: 1px; flex: 0 1 auto; }
.ap-cm-opt { display: flex; align-items: center; gap: 8px; padding: 5px 6px; border-radius: var(--r-sm); cursor: pointer; }
.ap-cm-opt:hover { background: var(--surface-2); }
.ap-cm-opt input { width: 15px; height: 15px; accent-color: var(--primary); flex: none; cursor: pointer; }
.ap-cm-opt span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-cm-empty { color: var(--muted); padding: 6px 8px; }
.ap-cm-range { display: flex; align-items: center; gap: 7px; }
.ap-cm-range .field-input { width: 100%; }
.ap-cm-dash { color: var(--muted); flex: none; }
.ap-cm-acts { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 6px 4px; }
.ap-cm-acts .btn { margin: 0; }

/* The "how it opens" bar above the table: what's active + Save-as-default / Reset. */
.ap-viewbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 8px 14px;
  background: var(--surface-2); border-bottom: 1px solid var(--line); font-size: var(--t-sm); }
.ap-viewbar-sum { margin-right: auto; color: var(--muted); }
.ap-viewbar-sum strong { color: var(--ink); font-weight: 700; }
.ap-viewbar-saved { color: var(--muted); font-size: var(--t-xs); }
.ap-viewbar .btn, .ap-viewbar .linkbtn { margin: 0; }
.ap-viewbar-reset { color: var(--muted); }

/* ---- Search box above a bill table (AP Bills, and a vendor's history) -------------------
   One plain box that looks through everything on screen, sitting beside the column menus
   rather than replacing them: the columns are for "show me only X", this is for "find X". */
/* One quiet line telling people the headings are controls — cheaper than making every heading
   shout, and it is the thing nobody discovers on their own. */
.ap-searchhint { padding: 0 var(--s3) var(--s2); font-size: var(--t-xs); color: var(--muted); }
.ap-searchrow { display: flex; align-items: center; gap: var(--s2); margin: 0 0 var(--s3); }
.ap-searchrow svg { flex: none; color: var(--muted); }
.ap-searchrow .field-input { flex: 1; min-width: 0; margin: 0; }
.ap-searchrow .ap-search-clear { flex: none; color: var(--muted); }
.ap-searchrow .ap-search-clear[hidden] { display: none; }
.ap-searchrow-sm { margin: 4px 0 var(--s2); }
.ap-searchrow-sm .field-input { padding-top: 6px; padding-bottom: 6px; font-size: var(--t-sm); }

/* ---- Print-type menu (Print bill / invoice / both / with payment info) ------------------
   Same popover shell as the column Sort+Filter menu so the two read as one family; each row
   carries a plain-English note, because "print both" alone doesn't say both of WHAT. */
.ap-printmenu { min-width: 268px; max-width: 320px; }
.ap-pm-opt { display: block; width: 100%; background: none; border: 0; margin: 0; padding: 8px 9px;
  border-radius: var(--r-sm); font: inherit; color: var(--ink); cursor: pointer; text-align: left; }
.ap-pm-opt:hover { background: var(--surface-2); }
.ap-pm-label { display: block; font-weight: 700; }
.ap-pm-note { display: block; margin-top: 1px; font-size: var(--t-xs); color: var(--muted); line-height: 1.35; }
/* The Excel choice is not a print, so it sits below a labelled hairline rather than pretending to
   be a fifth kind of paper. Same small-caps voice as the menu's own heading. */
.ap-pm-sep { margin: 6px 0 2px; padding: 6px 9px 0; border-top: 1px solid var(--line);
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }

/* ---- A vendor's bill history: the same ticking + sorting the Bills screen has -----------
   The tick column and sortable headers were written for .ap-archive; the vendor tables want
   both, so the rules are shared here rather than duplicated. */
.apv-tablewrap th.ap-selcol, .apv-tablewrap td.ap-selcol { width: 34px; text-align: center; padding-left: 8px; padding-right: 4px; }
.apv-tablewrap thead th.ap-col { padding: 0; }
.apv-memocell { max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.apv-noclass { color: var(--muted); }
#apvBillsSec .ap-bulkbar { margin-left: 0; margin-right: 0; }
/* .btn-primary is width:100% for the login form's stacked buttons. In a bulk bar that made the
   "Send for signature" button take a whole second row once Print joined it on the first. Scoped
   to these two bars only, so the Sign-payments bulk bar keeps the look it has always had. */
#abBulk .btn-primary, #apvBillBulk .btn-primary { width: auto; padding: var(--s3) var(--s4); }

/* Connection card */
.ap-co-list { display: grid; gap: 0; }
.ap-co-row { display: flex; justify-content: space-between; align-items: center; gap: var(--s3);
  padding: var(--s3) var(--s4); border-top: 1px solid var(--line); }
.ap-conv-row { align-items: center; gap: var(--s2); }
.ap-conv-status { display: inline-flex; gap: var(--s2); align-items: center; }
.ap-errlog-row { padding: var(--s3) var(--s4); border-top: 1px solid var(--line); }
.phone-banner { display: flex; align-items: center; gap: var(--s3); padding: var(--s2) var(--s4);
  background: color-mix(in srgb, var(--primary) 8%, var(--surface)); border-bottom: 1px solid var(--line);
  font-size: var(--t-sm); }
.phone-banner input { max-width: 180px; }
.phone-banner .icon-btn { margin-left: auto; }
/* Merged Admin hub (2026-07-17): plain-English section labels separating Staff & access /
   Performance & activity / Finance, so a long list of cards still reads as organized groups. */
.admin-group-label { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  color: var(--muted); margin: var(--s5) 14px var(--s2); padding-top: var(--s3); border-top: 1px solid var(--line); }
.admin-group-label:first-child { margin-top: 0; padding-top: 0; border-top: none; }
/* Protected charts (owner-only Admin card): staff members' own medical records. ONE aligned
   list, one row per employee, sitting directly under the staff table it mirrors. */
.prot-section-head { font-size: var(--t-sm); font-weight: 700; color: var(--ink); margin: var(--s4) 0 var(--s2); }
.prot-section-head:first-child { margin-top: 0; }
/* Counts + the scan button, across the top. */
.prot-summary { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  padding: var(--s3); margin-bottom: var(--s3); background: var(--surface-2);
  border: 1px solid var(--line); border-radius: var(--r-md); font-size: var(--t-sm); }
.prot-summary .warn { color: var(--warn); }
.prot-summary strong { font-size: var(--t-base); }
/* The Chart CELL inside the staff table. Kept narrow so adding this column doesn't push the
   roster sideways — the status chip carries it, and detail sits underneath in small type. */
td.prot-cell { vertical-align: top; min-width: 190px; max-width: 340px; }
td.prot-cell .prot-actions { margin-top: 4px; }
/* A row owing a decision gets a quiet amber gutter so it is findable while scrolling a long list. */
td.prot-cell.prot-choose { box-shadow: inset 3px 0 0 var(--warn); }
/* Status chip. Neutral by default — "not found" is a fact, not a failure. Amber only where a
   decision is actually owed, so the eye lands on the rows that need work. */
.prot-chip { display: inline-block; font-size: var(--t-xs); font-weight: 700; white-space: nowrap;
  border: 1px solid var(--line); border-radius: var(--r-full); padding: 1px var(--s2); color: var(--muted); }
.prot-chip.ok { color: var(--good); border-color: var(--good); }
.prot-chip.warn { color: var(--warn); border-color: var(--warn); }
/* A row that owes a decision gets a quiet amber gutter so it is findable while scrolling. */
.prot-chart-line { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  font-size: var(--t-sm); padding: 2px 0; }
.prot-chart-line > span:first-child { flex: 1; min-width: 150px; }
.prot-sub { font-size: var(--t-xs); color: var(--muted); line-height: 1.45; max-width: 46ch; }
.prot-actions { white-space: nowrap; display: flex; gap: 6px; flex-wrap: wrap; }
/* The drawer row itself is a full-width container, not a data row — drop the usual cell padding
   so the search panel sits flush under the employee it belongs to. */
tr.prot-findrow > td { padding: 0 0 var(--s3); border-top: none; }
/* The manual "find this person's chart" drawer, opened inline beneath an employee's row. */
.prot-find { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: var(--s3); display: grid; gap: var(--s2); }
.prot-find .ecw-fields { max-width: 460px; }
.prot-find-results { display: grid; gap: 2px; max-height: 280px; overflow-y: auto; }
/* AP company account editor */
.ap-company-card { max-width: 560px; width: 92vw; max-height: 86vh; display: flex; flex-direction: column; }
.ap-bank-list { overflow-y: auto; max-height: 46vh; display: grid; gap: 6px; padding: var(--s2); background: var(--surface-2); border-radius: var(--r-sm); }
.ap-bank-row { display: grid; grid-template-columns: auto minmax(120px, 1fr) minmax(120px, 1fr); gap: var(--s2); align-items: center; }
.ap-bank-active { display: flex; align-items: center; gap: 4px; font-size: var(--t-xs); color: var(--muted); white-space: nowrap; }
.ap-bank-qbo { font-size: var(--t-xs); color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Invoices review — Bill.com-style: invoice list left, PDF auto-shown right, Create Bill in place.
   Takes the full center+right width (leaderboard + the redundant list header are hidden) so the
   PDF has real room, like a document inbox. */
.body:has(.ap-inbox-layout) { grid-template-columns: var(--rail-w) 1fr; }
.body:has(.ap-inbox-layout) .rail-right { display: none; }
.rail-center:has(.ap-inbox-layout) .list-head,
.rail-center:has(.ap-inbox-layout) .cat-filter { display: none; }
.rail-center:has(.ap-inbox-layout) .list { padding: 0; flex: 1; overflow: hidden; }
.ap-inbox-layout { display: grid; grid-template-columns: 360px 1fr; height: 100%; min-height: 400px; }
.ap-inbox-list { overflow-y: auto; border-right: 1px solid var(--line); background: var(--surface); }
.ap-inbox-listhead { display: flex; justify-content: space-between; align-items: center; padding: 10px 12px; border-bottom: 1px solid var(--line); position: sticky; top: 0; background: var(--surface); z-index: 1; }
/* Filing-cabinet folder tabs — "To enter" (working queue) and "Past invoices" (full history). The
   active tab blends into the drawer below it, like a manila folder pulled to the front. */
.ap-cabinet-tabs { display: flex; gap: 3px; padding: 8px 10px 0; border-bottom: 1px solid var(--line); background: var(--surface); position: sticky; top: 0; z-index: 2; }
.ap-cab-tab { display: inline-flex; align-items: center; gap: 5px; padding: 7px 14px 8px; margin-bottom: -1px; font-size: var(--t-xs); font-weight: 600; color: var(--muted); cursor: pointer; background: var(--surface-2); border: 1px solid var(--line); border-radius: 9px 9px 0 0; }
.ap-cab-tab:hover { color: var(--ink); background: color-mix(in srgb, var(--primary) 6%, var(--surface-2)); }
.ap-cab-tab.active { color: var(--primary); background: var(--surface); border-bottom-color: var(--surface); }
/* Spam drawer reads as "review", not a normal action — amber when active. */
.ap-cab-tab.ap-cab-spam.active { color: var(--warn, #b45309); }
/* Per-drawer search — type a vendor/sender/subject to look up invoices within the current drawer. */
.ap-inbox-searchbar { display: flex; align-items: center; gap: 6px; padding: 8px 12px; border-bottom: 1px solid var(--line); color: var(--muted); }
.ap-inbox-searchbar input { flex: 1; min-width: 0; border: 1px solid var(--line); border-radius: var(--r-sm); padding: 6px 9px; font-size: var(--t-xs); background: var(--surface); color: var(--ink); }
.ap-inbox-searchbar input:focus { outline: none; border-color: var(--primary); }
.ap-inbox-searchbar button { display: inline-flex; align-items: center; background: none; border: 0; cursor: pointer; color: var(--muted); padding: 2px; }
.ap-inbox-searchbar button:hover { color: var(--ink); }
.ap-chip-done { display: inline-block; font-size: 11px; padding: 0 6px; border-radius: var(--r-full); background: color-mix(in srgb, var(--good) 16%, var(--surface)); color: var(--good); font-weight: 600; }
/* Each invoice email is a GROUP: a clickable header row + an expandable list of its documents
   (checkboxes to combine onto the bill; click a name to view it big on the right). */
.ap-inv-group { border-bottom: 1px solid var(--line); }
.ap-inv-group.focused { background: color-mix(in srgb, var(--primary) 6%, var(--surface)); }
.ap-inv-group.focused > .ap-inv-row { box-shadow: inset 3px 0 0 var(--primary); }
.ap-inv-row { display: block; width: 100%; text-align: left; padding: 10px 12px; border: 0; background: transparent; cursor: pointer; }
.ap-inv-row:hover { background: var(--surface-2); }
.ap-inv-row.has-bill { opacity: .72; }
.ap-inv-top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.ap-inv-vendor { font-weight: 700; color: var(--ink); }
.ap-inv-amt { font-variant-numeric: tabular-nums; font-weight: 700; white-space: nowrap; }
/* Pre-flight chips (Bill.com parity): shown BEFORE a bill exists, from the cached AI read.
   Calm soft-fill pills (not loud banners) — the row stays scannable; hover shows the full reason. */
.ap-inv-badges { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 4px; }
.ap-badge { display: inline-flex; align-items: center; font-size: 11px; line-height: 1.5; padding: 0 7px; border-radius: var(--r-full); font-weight: 600; letter-spacing: .01em; white-space: nowrap; border: 1px solid transparent; }
.ap-badge-dup  { background: color-mix(in srgb, var(--danger) 16%, var(--surface)); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 32%, transparent); }
.ap-badge-warn { background: color-mix(in srgb, var(--warn) 16%, var(--surface)); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 32%, transparent); }
.ap-badge-new  { background: color-mix(in srgb, var(--primary) 13%, var(--surface)); color: var(--primary-ink); border-color: color-mix(in srgb, var(--primary) 30%, transparent); }
/* "Invoice deleted" — a bill was made from this invoice and then thrown away; it's back to re-enter.
   Deliberately MUTED (not danger): nothing is wrong, the bill's just gone. Icon needs a small gap. */
.ap-badge-del  { gap: 3px; background: color-mix(in srgb, var(--muted) 15%, var(--surface)); color: var(--muted); border-color: color-mix(in srgb, var(--muted) 30%, transparent); }
/* Icons inside a chip ride at the chip's own size, not the full 15px icon-sm. */
.ap-badge svg { width: 12px; height: 12px; }
/* A clickable duplicate chip: it opens the side-by-side compare. Gap for its magnifier, a pointer
   cursor + gentle hover/focus lift so it reads as "you can open this", unlike the plain label chips. */
.ap-badge-clickable { gap: 3px; cursor: pointer; transition: filter var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.ap-badge-clickable:hover { filter: brightness(.97); box-shadow: 0 1px 4px rgba(0,0,0,.12); }
.ap-badge-clickable svg { opacity: .8; }

/* ── "Is this the same invoice?" SPLIT-SCREEN compare ────────────────────────────────────────────
   A wide panel that shows the two invoices side by side — this invoice's document on the left, the
   matched bill's on the right — each topped by its key facts (amber where they differ, green where
   they match). Sits above the app but BELOW the .att-overlay preview (z 3200), so "open larger" can
   pop a full-page PDF/image on top, then return here on Escape. */
.ap-cmp-overlay { position: fixed; inset: 0; z-index: 3100; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; padding: var(--s3); }
.ap-cmp-card { background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-lg); box-shadow: 0 24px 60px rgba(0,0,0,.40); width: min(1500px, 98vw); height: min(94vh, 1100px); display: flex; flex-direction: column; overflow: hidden; }
.ap-cmp-head { display: flex; align-items: center; gap: var(--s3); padding: var(--s3) var(--s4); border-bottom: 1px solid var(--line); }
.ap-cmp-head h3 { margin: 0; font-size: var(--t-md); font-weight: 700; display: flex; align-items: center; gap: var(--s2); white-space: nowrap; }
.ap-cmp-head h3 svg { color: var(--muted); }
.ap-cmp-lead { margin: 0; flex: 1; min-width: 0; font-size: var(--t-sm); color: var(--muted); line-height: 1.4; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-cmp-x { flex: 0 0 auto; }
/* The split: two equal panes, each a column of head → facts → document viewer. */
.ap-cmp-split { flex: 1; display: flex; min-height: 0; }
.ap-cmp-pane { flex: 1 1 50%; min-width: 0; display: flex; flex-direction: column; min-height: 0; }
.ap-cmp-pane + .ap-cmp-pane { border-left: 1px solid var(--line); }
.ap-cmp-pane-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); background: var(--surface-2, var(--surface)); }
.ap-cmp-pane-title { font-weight: 700; font-size: var(--t-sm); color: var(--ink); }
.ap-cmp-pane-title span { display: block; font-weight: 500; font-size: var(--t-xs); color: var(--muted); }
.ap-cmp-facts { display: flex; flex-wrap: wrap; gap: 6px; padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); }
.ap-cmp-fact { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-xs); border: 1px solid var(--line); border-radius: var(--r-full); padding: 2px 9px; background: var(--surface); }
.ap-cmp-fact .k { color: var(--muted); }
.ap-cmp-fact .v { font-weight: 600; color: var(--ink); display: inline-flex; align-items: center; gap: 3px; }
.ap-cmp-fact .v svg { color: var(--primary); }
/* Differences jump out amber; confirmed matches read calm/green. Neutral = unknown on a side. */
.ap-cmp-fact.is-diff  { background: color-mix(in srgb, var(--warn) 14%, var(--surface)); border-color: color-mix(in srgb, var(--warn) 40%, transparent); }
.ap-cmp-fact.is-diff .v { color: var(--warn); }
.ap-cmp-fact.is-match { background: color-mix(in srgb, var(--primary) 8%, var(--surface)); border-color: color-mix(in srgb, var(--primary) 26%, transparent); }
.ap-cmp-doc { flex: 1; min-height: 0; background: var(--surface-3, #f4f5f7); overflow: auto; display: flex; }
.ap-cmp-frame { width: 100%; height: 100%; border: 0; }
.ap-cmp-img { max-width: 100%; height: auto; margin: auto; object-fit: contain; }
.ap-cmp-nodoc { margin: auto; padding: 24px; color: var(--muted); font-size: var(--t-sm); text-align: center; }
.ap-cmp-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; padding: var(--s3) var(--s4); border-top: 1px solid var(--line); background: var(--surface-2, var(--surface)); }
.ap-cmp-hint { display: inline-flex; align-items: center; gap: var(--s2); font-size: var(--t-xs); color: var(--muted); }
.ap-cmp-hint svg { color: var(--muted); }
.ap-cmp-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }
/* Narrow screens: stack the two panes vertically and let the header wrap. */
@media (max-width: 820px) {
  .ap-cmp-split { flex-direction: column; }
  .ap-cmp-pane + .ap-cmp-pane { border-left: 0; border-top: 1px solid var(--line); }
  .ap-cmp-head { flex-wrap: wrap; }
  .ap-cmp-lead { flex-basis: 100%; order: 3; white-space: normal; }
}
@media (max-width: 560px) { .ap-cmp-foot { flex-direction: column; align-items: stretch; } .ap-cmp-actions { flex-direction: column; } }
.ap-doc-deleted { color: var(--muted); font-weight: 600; white-space: nowrap; text-decoration: none; display: inline-flex; align-items: center; gap: 2px; }
.ap-inv-sub { font-size: var(--t-xs); color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-top: 2px; }
.ap-inv-meta { font-size: var(--t-xs); color: var(--muted); margin-top: 3px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Minimize/expand strip on multi-invoice emails; chevron points right (folded) or down (open). */
.ap-inv-fold { display: flex; align-items: center; gap: 5px; width: 100%; padding: 0 12px 7px; border: 0; background: none; cursor: pointer; font-size: var(--t-xs); color: var(--muted); text-align: left; }
.ap-inv-fold:hover { color: var(--primary); }
.ap-inv-fold svg { transition: transform .15s ease; flex: none; }
.ap-inv-fold.open svg { transform: rotate(90deg); }
/* user-select off: Shift-click range-selection must not smear a text highlight over the list. */
.ap-inv-docs { padding: 2px 12px 8px 12px; display: flex; flex-direction: column; gap: 2px; user-select: none; }
.ap-inv-docs[hidden] { display: none; }
.ap-inv-doc { display: flex; align-items: center; gap: 7px; padding: 3px 6px; border-radius: var(--r-sm); }
.ap-inv-doc:hover { background: var(--surface-2); }
.ap-inv-doc input { margin: 0; flex: none; cursor: pointer; }
/* One-line reminder above a multi-document email's checkboxes: ticking several = one combined bill. */
.ap-inv-dochint { display: flex; align-items: flex-start; gap: 6px; padding: 3px 6px 6px; font-size: var(--t-xs); color: var(--muted); line-height: 1.4; }
.ap-inv-dochint svg { flex: none; margin-top: 1px; color: var(--primary); }
.ap-inv-dochint strong { color: var(--ink); font-weight: 650; }
/* Combine-into-one vs. separate-bills choice, shown in the toolbar when 2+ documents are ticked. */
.ap-billchoice { display: inline-flex; gap: 6px; align-items: center; }
.ap-inv-docname { display: inline-flex; align-items: center; gap: 5px; font-size: var(--t-xs); background: none; border: 0; cursor: pointer; color: var(--ink); text-align: left; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.ap-inv-docname:hover { color: var(--primary); }
.ap-inv-docname.viewing { font-weight: 700; color: var(--primary); }
/* Flex column (not a fixed-row grid) so the PDF viewer always fills the space left by the bar +
   optional document-tabs row — a grid with a fixed row count collapsed the viewer when the tabs
   row was absent. */
.ap-inbox-main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.ap-inbox-bar { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 14px; border-bottom: 1px solid var(--line); flex: none; }
.ap-inbox-barvendor { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-inbox-docs { display: flex; gap: 6px; padding: 6px 12px; border-bottom: 1px solid var(--line); flex: none; }
.ap-doc-chip { font-size: var(--t-xs); padding: 3px 10px; border-radius: var(--r-full); border: 1px solid var(--line); background: var(--surface); cursor: pointer; }
.ap-doc-chip.active { background: var(--primary-soft); border-color: var(--primary); color: var(--primary); }
.ap-inbox-viewer { display: grid; grid-template-columns: 1fr; flex: 1; min-height: 0; overflow: hidden; position: relative; }
/* When building a bill, the form sits in a right panel (wider default so nothing is cut off) while
   the PDF keeps the rest — and the invoice list collapses (below) to give both more room. The
   width is a CSS variable so each person can drag the divider to resize it; their choice is saved
   to their profile (ui_prefs.apBillFormWidth) and re-applied here on the next Create Bill. */
/* Default: the PDF dominates — the invoice fills the whole left side and the form takes a fixed
   comfortable column (owner rule 2026-07-21, supersedes the earlier 56% "Bill.com proportions"
   default). Anyone can still drag the divider; a dragged width (saved per person) always wins.
   The one default lives in --ap-form-default so the drag handle sits EXACTLY on the divider. */
.ap-inbox-viewer.building { --ap-form-default: max(480px, min(34%, 620px)); grid-template-columns: 1fr var(--ap-form-w, var(--ap-form-default)); }
.ap-inbox-layout.building { grid-template-columns: 1fr; position: relative; }
/* Tucked away, not gone (owner 2026-07-21): while a bill is being built the invoice list slides
   off-canvas so the PDF gets the full left side, but a 14px sliver stays as the hover handle —
   glide onto it and the list rides back over the PDF; mouse away and it tucks itself again. */
.ap-inbox-layout.building .ap-inbox-list {
  position: absolute; left: 0; top: 0; bottom: 0; width: 360px; z-index: 40;
  transform: translateX(calc(14px - 100%));
  transition: transform .22s var(--ease);
  box-shadow: 0 8px 28px rgba(0, 0, 0, .18);
}
.ap-inbox-layout.building .ap-inbox-list:hover,
.ap-inbox-layout.building .ap-inbox-list:focus-within { transform: translateX(0); }
.ap-inbox-pdf { min-height: 0; overflow: auto; background: var(--surface-2); display: grid; }
.ap-inbox-frame { width: 100%; height: 100%; border: 0; background: var(--surface-2); }
.ap-inbox-img { max-width: 100%; height: auto; margin: auto; display: block; }
.ap-email-body { padding: 20px; overflow-y: auto; white-space: pre-wrap; font-size: var(--t-sm); color: var(--ink); background: var(--surface); }
.ap-inbox-form { overflow-y: auto; border-left: 1px solid var(--line); padding: 14px; background: var(--surface); }
/* Draggable divider so each person sets the Create-Bill panel width that suits them (double-click
   resets to the default). It straddles the border between the PDF and the form, and only appears
   while a bill is being built. Mirrors the reading-pane divider (.reader-w-resize). */
.ap-form-resize { display: none; }
.ap-inbox-viewer.building .ap-form-resize {
  display: block; position: absolute; top: 0; bottom: 0;
  right: var(--ap-form-w, var(--ap-form-default, 480px)); width: 12px; margin-right: -6px; cursor: col-resize; z-index: 6;
}
.ap-form-resize::before {
  content: ''; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px; margin-left: -1px;
  background: var(--line-strong); transition: background .15s ease, width .15s ease;
}
.ap-form-resize:hover::before, .ap-form-resize.dragging::before { width: 4px; margin-left: -2px; background: var(--primary); }

/* ---- Source-highlight viewer (Bill.com-style "where did that number come from?") ----------- */
.ap-inbox-pdf { position: relative; }
.ap-inbox-pdf.hl-mode { display: block; overflow: hidden; }
.ap-hl-scroll { height: 100%; overflow: auto; padding: 6px; cursor: grab; }
.ap-hl-scroll.panning { cursor: grabbing; user-select: none; }
/* width:max-content + auto margins (NOT align-items:center): centered pages when they fit, and a
   fully reachable scroll area when zoomed — flex centering clips the left edge of overflow. */
.ap-hl-pages { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; width: max-content; min-width: 100%; }
.ap-hl-page { position: relative; background: #fff; box-shadow: var(--sh-2); border-radius: 2px; margin-left: auto; margin-right: auto; }
/* Where one document ends and the next begins inside a COMBINED bill's single scroll — without it a
   long scroll gives no clue which invoice you're looking at. Sticks to the top while you scroll it. */
.ap-hl-docsep { position: sticky; top: 0; z-index: 2; align-self: stretch; display: flex; align-items: center;
  gap: 10px; margin: 10px 0 2px; padding: 5px 2px; }
.ap-hl-docsep::before, .ap-hl-docsep::after { content: ""; flex: 1; height: 1px; background: var(--line-strong); }
.ap-hl-docsep span { flex: none; padding: 3px 12px; border-radius: var(--r-full); background: var(--surface);
  border: 1px solid var(--line-strong); font-size: var(--t-xs); font-weight: 700; color: var(--muted);
  white-space: nowrap; max-width: 60%; overflow: hidden; text-overflow: ellipsis; box-shadow: var(--sh-1, 0 1px 3px rgba(0,0,0,.08)); }
.ap-hl-docsep:first-child { margin-top: 0; }
.ap-hl-overlay { position: absolute; inset: 0; pointer-events: none; }
.ap-hl-box {
  position: absolute; border-radius: 5px; pointer-events: none;
  background: color-mix(in srgb, var(--primary) 27%, transparent);
  box-shadow: 0 0 0 2.5px var(--primary),
              0 0 0 5px color-mix(in srgb, var(--primary) 30%, transparent),
              0 0 20px color-mix(in srgb, var(--primary) 45%, transparent);
  animation: apHlIn .18s ease-out;
}
@keyframes apHlIn { from { transform: scale(1.12); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.ap-hl-zoom {
  position: absolute; top: 10px; right: 14px; display: flex; gap: 1px; z-index: 5;
  border-radius: var(--r-full); overflow: hidden; border: 1px solid var(--line-strong); box-shadow: var(--sh-2);
}
.ap-hl-zoom button { border: 0; background: var(--surface); color: var(--ink-soft); width: 34px; height: 26px; font-size: var(--t-sm); font-weight: 600; cursor: pointer; }
.ap-hl-zoom button:hover { background: var(--surface-3); color: var(--ink); }

/* ---- Full-screen invoice viewer -------------------------------------------------------------
   The corner button (top-left of the viewer, mirroring the zoom cluster at top-right) promotes the
   preview pane to fill the whole screen so the ENTIRE invoice is reachable. This is the fix for
   "on my phone I can only see the top of the invoice": the inline pane is short and its inner
   scroll fights the page scroll; full screen gives the invoice the whole viewport with one natural
   scroll. Positioned relative to .ap-inbox-viewer (position:relative), so it survives the pane's
   content being re-rendered by any of the viewers. */
.ap-fs-btn {
  position: absolute; top: 10px; left: 12px; z-index: 6;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 28px; padding: 0 10px;
  background: var(--surface); color: var(--ink-soft);
  border: 1px solid var(--line-strong); border-radius: var(--r-full);
  box-shadow: var(--sh-2); cursor: pointer; font-size: var(--t-xs); font-weight: 700;
}
.ap-fs-btn:hover { background: var(--surface-3); color: var(--ink); }
.ap-fs-btn[hidden] { display: none; }        /* class-level display beats the [hidden] UA rule */
.ap-fs-lbl { display: none; }                /* icon-only on desktop; the label shows on phones + in full screen */
/* The pane fills the viewport; its inner scroll (or the image / iframe inside) now owns the whole
   screen. No transformed ancestors, so position:fixed anchors to the viewport as intended. */
.ap-inbox-pdf.ap-fs {
  position: fixed; inset: 0; z-index: 200;
  width: 100vw; height: 100vh; height: 100dvh;   /* dvh: never clipped by mobile browser chrome */
}
/* While full screen the corner button rides up into that layer as the (labelled) exit control. */
body.ap-fs-open .ap-fs-btn { position: fixed; top: 14px; left: 14px; z-index: 201; height: 34px; padding: 0 14px; }
body.ap-fs-open .ap-fs-lbl { display: inline; }
body.ap-fs-open { overflow: hidden; }        /* lock the page behind the full-screen invoice */
/* Fields whose value the AI can point to on the document get a quiet teal base line; hovering
   (or focusing) them lights the source up on the invoice. */
.field-input.ap-hl-src { border-bottom: 2px solid color-mix(in srgb, var(--primary) 55%, transparent); }
.field-input.ap-hl-src:hover, .field-input.ap-hl-src:focus { border-color: var(--primary); }

/* ---- Click-to-capture: grab text off the invoice, paste into a form box -------------------- */
.ap-grab-box { background: color-mix(in srgb, var(--gold) 30%, transparent); box-shadow: 0 0 0 2px var(--gold), 0 0 12px color-mix(in srgb, var(--gold) 40%, transparent); }
.ap-grab-band { position: absolute; border: 1.5px dashed var(--primary); background: color-mix(in srgb, var(--primary) 10%, transparent); pointer-events: none; border-radius: 3px; }
.ap-grab-chip {
  position: absolute; left: 12px; bottom: 12px; z-index: 7; display: flex; align-items: center; gap: 8px;
  max-width: calc(100% - 24px); background: var(--surface); border: 1px solid var(--line-strong);
  border-left: 4px solid var(--primary); border-radius: var(--r-md); box-shadow: var(--sh-3);
  padding: 8px 12px; font-size: var(--t-xs);
}
.ap-grab-chip .ap-grab-txt { font-weight: 700; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 40%; }

/* ---- "Split this stack" table (statement-dump emails) -------------------------------------- */
.ap-stack { display: grid; gap: var(--s3); }
.ap-stack-table { font-size: var(--t-sm); }
.ap-stack-table td { vertical-align: middle; }
.ap-stack-table .ap-stk-act { white-space: nowrap; }
.ap-stk-view { background: none; border: 0; padding: 0; cursor: pointer; color: var(--primary-ink); text-decoration: underline; text-underline-offset: 2px; font-size: var(--t-sm); }
.ap-stk-view:hover { color: var(--primary); }
.ap-stk-chip { display: inline-flex; font-size: 10.5px; font-weight: 700; padding: 2px 8px; border-radius: var(--r-full); }
.ap-stk-ready { background: color-mix(in srgb, var(--good) 15%, transparent); color: var(--good); }
.ap-stk-reading { background: var(--surface-3); color: var(--muted); }
.ap-stk-unread { background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn); }
.ap-stk-billed { background: var(--primary-soft); color: var(--primary-ink); }
.ap-stack-back { justify-self: start; margin-bottom: var(--s2); }

/* ---- Prev / Next invoice navigation in the top bar ----------------------------------------- */
.ap-nav { display: inline-flex; align-items: center; gap: 6px; margin-right: 4px; }
.ap-nav-pos { font-size: var(--t-xs); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.ap-nav .btn[disabled] { opacity: .45; cursor: default; pointer-events: none; }

/* ---- Bill.com-style slim sidebar: in the Invoices screen the LEFT inbox rail minimizes to a
   thin strip so the whole work area (document + bill form) gets the width. Hovering the strip
   slides the full inbox list back over; clicking an inbox there navigates as usual. ----------- */
@media (min-width: 761px) { /* on phones the rail is hidden anyway — don't leave a dead strip */
  .body:has(.ap-inbox-layout) { grid-template-columns: 26px 1fr; position: relative; }
  /* Hovering the strip turns .rail-left into an absolute overlay, which removes it from the
     grid flow — without an explicit column the work area would auto-place into the empty 26px
     first column and vanish behind the overlay (owner: "hover clears out the whole page"). */
  .body:has(.ap-inbox-layout) .rail-center { grid-column: 2; }
  .body:has(.ap-inbox-layout) .rail-left {
    width: 26px; min-width: 26px; padding: 0; overflow: hidden; position: relative;
    height: auto; align-self: stretch; cursor: pointer;
  }
  .body:has(.ap-inbox-layout) .rail-left > * { visibility: hidden; }
  .body:has(.ap-inbox-layout) .rail-left::after {
    content: 'Inboxes'; position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
    writing-mode: vertical-rl; transform: rotate(180deg); font-size: var(--t-xs); font-weight: 700;
    letter-spacing: .1em; text-transform: uppercase; color: var(--muted);
  }
  .body:has(.ap-inbox-layout) .rail-left:hover {
    /* Literal 256px, NOT var(--rail-w): this is the panel at FULL size, floated over the work area.
       Reading the variable would make the peek overlay 26px wide — i.e. invisible — the moment the
       rail is minimized, which is exactly when you need to peek at it. */
    position: absolute; left: var(--s4); top: var(--s4); bottom: var(--s4); width: 256px; min-width: 256px;
    z-index: 60; overflow-y: auto; padding: var(--s2); box-shadow: var(--sh-3); cursor: default;
  }
  .body:has(.ap-inbox-layout) .rail-left:hover > * { visibility: visible; }
  .body:has(.ap-inbox-layout) .rail-left:hover::after { display: none; }
}

/* ---- Bill.com-scale form typography: bigger, bolder, airier (scoped to the bill form).
   Metrics matched against the owner's Bill.com screenshot (2026-07-21): ~15px semibold labels,
   ~17px text in tall rounded bubbles (~52px), ~24px row rhythm, 20px section headings, and the
   expense-line table gets the SAME bubbles one notch smaller. ------------------------------- */
.ap-inbox-form { padding: 26px 30px; }
.ap-form { gap: 24px; }
.ap-grid2 { gap: 22px 22px; }
.ap-grid2 label, .ap-grid2 .ap-cell { font-size: 15px; font-weight: 600; color: var(--ink); gap: 8px; }
.ap-req { color: var(--danger); font-weight: 700; }
.ap-form .field-input {
  padding: 14px 16px; font-size: 17px; line-height: 1.3; border-radius: 10px;
  background: var(--surface); margin-bottom: 0;
}
.ap-form .ap-ack { display: flex; flex-direction: row; align-items: center; gap: 10px; font-size: 15px; font-weight: 500; }
.ap-form .ap-ack input { width: 18px; height: 18px; accent-color: var(--primary); flex: none; }
/* "Apply as default for vendor" — checkbox INLINE with its words, Bill.com style. */
.ap-form .ap-terms-default { display: flex; flex-direction: row; align-items: center; gap: 10px; font-size: 14px; font-weight: 500; color: var(--ink-soft); margin-top: 6px; }
.ap-form .ap-terms-default input { width: 17px; height: 17px; accent-color: var(--primary); flex: none; }
.ap-form h4 { font-size: 20px; font-weight: 700; margin-top: var(--s3); }
.ap-form .ap-form-actions .btn { padding: 12px 22px; font-size: 16px; font-weight: 700; }
/* Expense-line table: same bubble language, one notch smaller so five columns breathe. */
.ap-form .ap-lines-head { font-size: 13px; font-weight: 700; letter-spacing: .01em; padding: 2px 0 8px; margin-bottom: var(--s3); }
.ap-form .ap-line { gap: 10px; margin-bottom: 10px; align-items: start; }
.ap-form .ap-line .field-input { font-size: 15px; padding: 12px 12px; }
/* Description grows with its text so nothing is ever cut off (auto-height via JS). */
.ap-form .ap-line .ap-l-desc { resize: none; overflow: hidden; line-height: 1.35; min-height: 0; display: block; }
/* Account picker is a textarea now (wraps to show the full account name); auto-grown by JS. */
.ap-form .ap-acc-combo { align-items: start; }
.ap-form .ap-acc-combo .ap-acc-in { font-size: 15px; resize: none; overflow: hidden; white-space: normal; line-height: 1.35; min-height: 0; display: block; text-overflow: clip; }
.ap-form .ap-acc-caret { top: 8px; }

/* "New version ready" corner nudge — shown when the server was redeployed under this open tab */
.update-nudge {
  position: fixed; right: 16px; bottom: 16px; z-index: 400; display: flex; gap: 12px; align-items: center;
  background: var(--surface); border: 1px solid var(--line-strong); border-left: 4px solid var(--primary);
  border-radius: var(--r-md); box-shadow: var(--sh-3); padding: 12px 16px; font-size: var(--t-sm); color: var(--ink);
}
.update-nudge button {
  border: 0; background: var(--primary); color: var(--on-primary); border-radius: var(--r-full);
  padding: 7px 16px; font-weight: 700; cursor: pointer; font-size: var(--t-sm);
}
.update-nudge button:hover { background: var(--primary-hover); }

/* ---- Vendor card in the Create Bill form (contact · balance · last bills · how we paid) ----
   Styled after Bill.com's Enter-a-bill vendor panel: big underlined vendor name, right-aligned
   "Vendor balance", a pill "Last 5 bills" toggle, and a roomy history table. */
.ap-vendor-card {
  border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface-2);
  padding: 16px 18px; margin: 2px 0 6px; display: flex; flex-direction: column; gap: 12px;
  width: 100%; max-width: none; justify-self: stretch; box-sizing: border-box; /* full form width, always */
}
.ap-vc-table { width: 100%; }
.ap-vc-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.ap-vc-head strong {
  font-size: 20px; font-weight: 800; color: var(--primary-ink);
  text-decoration: underline; text-underline-offset: 3px; margin-right: 6px;
}
.ap-vc-balance { font-size: 15px; color: var(--ink-soft); white-space: nowrap; }
.ap-vc-balance strong { font-size: 17px; }
.ap-vc-contact { display: flex; flex-direction: column; gap: 5px; font-size: 15px; color: var(--ink-soft); }
.ap-vc-contact > span { display: inline-flex; gap: 8px; align-items: flex-start; }
.ap-vc-contact svg { flex: none; margin-top: 3px; color: var(--muted); }
/* "Check:" / "ACH:" — one line per way this vendor can be paid, so what's on file and what's
   missing reads at a glance. The label is the quiet part; the value carries the weight. */
.ap-vc-paylabel { color: var(--muted); font-weight: 700; margin-right: 3px; }
/* A gap you can act on: it reads as a call to action, not as an error. */
.ap-vc-missing { color: var(--primary-ink); font-weight: 600; text-decoration: none; }
a.ap-vc-missing:hover { text-decoration: underline; }
/* A detail that IS on file is editable too, but it is a fact first and a control second: it keeps
   the card's own ink and carries a hairline instead of turning every stored address and bank
   account into blue link text. (.ap-vmeta-row a paints anchors --primary-ink; this wins on
   specificity, and restores the ellipsis that rule relies on for a long address.) */
.ap-vmeta-row a.ap-vc-edit { color: inherit; text-decoration: none; cursor: pointer;
  border-bottom: 1px dashed var(--line-strong); }
.ap-vmeta-row a.ap-vc-edit:hover, .ap-vmeta-row a.ap-vc-edit:focus-visible {
  color: var(--primary-ink); border-bottom-color: currentColor; text-decoration: none; }
/* Marks the method money will actually leave by, so the two lines can't be read as equal options. */
.ap-vc-active { display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: var(--r-full);
  background: var(--primary-soft); color: var(--primary-ink); font-size: var(--t-xs); font-weight: 700;
  vertical-align: 1px; white-space: nowrap; }
.ap-vc-toggle {
  align-self: flex-end; display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  background: var(--surface); border: 2px solid var(--primary); border-radius: var(--r-md);
  padding: 7px 16px; font-size: var(--t-sm); font-weight: 700; color: var(--primary-ink);
}
.ap-vc-toggle:hover { background: var(--primary-soft); }
.ap-vc-caret { display: inline-flex; }
.ap-vc-caret svg { transition: transform .15s ease; transform: rotate(90deg); }  /* closed: points down */
.ap-vc-caret.open svg { transform: rotate(-90deg); }                             /* open: points up */
.ap-vc-bills { overflow-x: auto; }
.ap-vc-table { font-size: var(--t-sm); }
.ap-vc-table th { color: var(--muted); font-weight: 700; }
.ap-vc-table td, .ap-vc-table th { padding: 8px 10px 8px 0; }
.ap-vc-paid { color: var(--good); display: inline-flex; align-items: center; gap: 3px; white-space: nowrap; font-weight: 600; }
.ap-vc-open { color: var(--warn); font-weight: 700; }
@media (max-width: 900px) {
  .ap-nav-pos { display: none; }
}
/* Document picker — the row of selectable attachments (+ the email) above the preview. */
.ap-doc-picker { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 12px; border-bottom: 1px solid var(--line); flex: none; }
.ap-doc-lbl { font-size: var(--t-xs); color: var(--muted); font-weight: 600; }
.ap-doc-item { display: inline-flex; align-items: center; gap: 5px; padding: 3px 8px 3px 6px; border: 1px solid var(--line); border-radius: var(--r-full); background: var(--surface); }
.ap-doc-item.inc { border-color: var(--primary); background: var(--primary-soft); }
.ap-doc-item input { margin: 0; }
.ap-doc-name { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); background: none; border: 0; cursor: pointer; color: var(--ink); max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ap-doc-name.viewing { font-weight: 700; color: var(--primary); }
@media (max-width: 900px) {
  .ap-inbox-layout { grid-template-columns: 1fr; height: auto; }
  .ap-inbox-list { max-height: 40vh; border-right: 0; border-bottom: 1px solid var(--line); }
  .ap-inbox-pdf { height: 60vh; }
  .ap-inbox-viewer.building { grid-template-columns: 1fr; }
  .ap-inbox-viewer.building .ap-form-resize { display: none; } /* stacked layout — nothing to drag */
  .ap-inbox-form { border-left: 0; border-top: 1px solid var(--line); }
  /* On a phone the pane is short, so make the full-screen affordance an obvious labelled button. */
  .ap-fs-btn { height: 30px; padding: 0 12px; }
  .ap-fs-lbl { display: inline; }
}
/* "Add from another email" document picker */
.ap-doc-add { flex: none; }
.ap-doc-item.ext { border-style: dashed; }
.ap-doc-ext { color: var(--primary); font-weight: 700; }
.ap-doc-rm { background: none; border: 0; cursor: pointer; color: var(--muted); display: inline-flex; padding: 0 2px; }
.ap-doc-rm:hover { color: var(--danger); }
.ap-adddocs-card { max-width: 560px; width: 92vw; max-height: 82vh; display: flex; flex-direction: column; }
.ap-adddocs-list { overflow-y: auto; max-height: 52vh; margin-top: 8px; display: grid; gap: 8px; }
.ap-adddocs-email { border: 1px solid var(--line); border-radius: var(--r-md); padding: 8px 10px; }
.ap-adddocs-head { margin-bottom: 6px; }
.ap-adddocs-doc { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 4px 0; font-size: var(--t-sm); }
.ap-adddocs-doc > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ============================================================================
   Vendors screen — the searchable QuickBooks mirror (src/lib/qbosync.js).
   Two-pane: vendor list (left) + selected vendor's full spend history (right). */
.apv-sync { display: flex; align-items: center; gap: 10px; font-size: var(--t-xs); color: var(--muted); flex-wrap: wrap; }
.apv-sync-txt { display: inline-flex; align-items: center; gap: 6px; }
.apv-spin { animation: apv-spin 1.2s linear infinite; }
@keyframes apv-spin { to { transform: rotate(360deg); } }
.apv-wrap { display: flex; gap: 16px; margin-top: 12px; align-items: flex-start; }
.apv-list { flex: 0 0 320px; max-width: 320px; min-width: 0; }
.apv-detail { flex: 1 1 auto; min-width: 0; border: 1px solid var(--line); border-radius: var(--r-lg); padding: 16px; background: var(--surface-2); }
.apv-search { display: flex; gap: 8px; margin-bottom: 10px; }
.apv-search .field-input { flex: 1 1 auto; min-width: 0; }
.apv-rows { display: flex; flex-direction: column; gap: 4px; max-height: 72vh; overflow: auto; }
.apv-rows.empty { color: var(--muted); padding: 16px 4px; }
.apv-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; text-align: left; width: 100%;
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 8px 10px; cursor: pointer; }
.apv-row:hover { background: var(--surface-2); }
.apv-row.active { border-color: var(--good); background: color-mix(in srgb, var(--good) 8%, var(--surface)); }
.apv-row-main { min-width: 0; }
.apv-row-name { display: block; font-weight: 600; color: var(--ink); font-size: var(--t-sm); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.apv-row-sub { display: block; color: var(--muted); font-size: var(--t-xs); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.apv-open-chip { flex: none; font-size: var(--t-xs); font-weight: 700; color: var(--warn);
  background: color-mix(in srgb, var(--warn) 12%, transparent); border-radius: var(--r-full); padding: 2px 8px; white-space: nowrap; }
.apv-inactive { font-size: var(--t-xs); font-weight: 600; color: var(--muted); background: var(--surface-3); border-radius: var(--r-full); padding: 1px 6px; }
.apv-more { color: var(--muted); font-size: var(--t-xs); padding: 8px 4px; }
.apv-blank, .apv-none { color: var(--muted); }
.apv-blank { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; min-height: 260px; text-align: center; }
.apv-blank svg { width: 34px; height: 34px; opacity: .4; }
.apv-head { margin-bottom: 10px; }
.apv-name { margin: 0; font-size: var(--t-lg); display: flex; align-items: center; gap: 8px; color: var(--ink); }
.apv-co { color: var(--muted); font-size: var(--t-sm); margin-top: 2px; }
.apv-contact { display: flex; flex-wrap: wrap; gap: 6px 18px; margin: 8px 0 14px; }
.apv-contact-row { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); color: var(--ink-soft); }
.apv-contact-row svg { color: var(--muted); width: 14px; height: 14px; }
.apv-contact-row a { color: inherit; text-decoration: none; }
.apv-contact-row a:hover { text-decoration: underline; }
.apv-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 16px; }
.apv-tile { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 12px; }
.apv-tile-v { font-weight: 700; color: var(--ink); font-size: var(--t-base); font-variant-numeric: tabular-nums; }
.apv-tile-l { color: var(--muted); font-size: var(--t-xs); margin-top: 2px; }
.apv-tile-v.apv-good { color: var(--good); }
.apv-tile-v.apv-openv { color: var(--warn); }
.apv-sec { margin-bottom: 16px; }
.apv-sec h4 { margin: 0 0 6px; font-size: var(--t-sm); color: var(--ink); display: flex; align-items: center; gap: 8px; }
.apv-seccount { font-weight: 600; color: var(--muted); background: var(--surface-3); border-radius: var(--r-full); padding: 1px 8px; font-size: var(--t-xs); }
.apv-tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-md); }
.apv-tablewrap .ap-table td, .apv-tablewrap .ap-table th { padding: 6px 10px; }
.apv-tablewrap .ap-table thead th { background: var(--surface-3); color: var(--muted); font-size: var(--t-xs); font-weight: 700; }
.apv-tablewrap .ap-table tr:last-child td { border-bottom: none; }
.apv-open { color: var(--warn); font-weight: 700; }
.apv-paidtag { color: var(--good); font-weight: 600; }
.apv-none { font-size: var(--t-sm); padding: 4px 2px; }
/* Bill.com history — a clicked row expands into the bill's full record, in place. */
.apv-bc-row { cursor: pointer; }
.apv-bc-row:hover td { background: var(--surface-2); }
.apv-bc-row.open td { background: var(--surface-3); }
.apv-bc-detail > td { padding: 0; border-bottom: 1px solid var(--line); }
.apv-bc-panel { padding: 10px 12px; background: var(--surface-2); display: grid; gap: 8px; }
.apv-bc-facts { display: flex; flex-wrap: wrap; gap: 6px 18px; }
.apv-bc-fact { display: flex; flex-direction: column; gap: 1px; }
.apv-bc-fact span { font-size: var(--t-xs); color: var(--muted); font-weight: 600; }
.apv-bc-fact strong { font-size: var(--t-sm); color: var(--ink); font-weight: 600; }
.apv-bc-desc { font-size: var(--t-sm); color: var(--ink); white-space: pre-wrap; }
.apv-bc-desc em { color: var(--muted); }
.apv-bc-nofile { font-size: var(--t-sm); color: var(--muted); }
.apv-bc-embed { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; background: var(--surface-3); }
.apv-bc-frame { display: block; width: 100%; height: 480px; border: 0; }
.apv-bc-img { display: block; max-width: 100%; max-height: 480px; margin: 0 auto; }
/* sync summary line on the Command Center QuickBooks card */
.ap-sync-cc { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap;
  margin: 10px 12px 0; padding-top: 10px; border-top: 1px solid var(--line); font-size: var(--t-xs); color: var(--muted); }
@media (max-width: 900px) {
  .apv-wrap { flex-direction: column; }
  .apv-list { flex-basis: auto; max-width: none; width: 100%; }
  .apv-rows { max-height: none; }
  .apv-tiles { grid-template-columns: repeat(2, 1fr); }
}

/* AP screens (Bills / Vendors / Sign / invoice review): the game rail (leaderboard + Your
   Progress) tucks away into a slim strip so documents and money get the room. Per-user,
   remembered locally; regular inboxes keep the rail as-is. */
.rail-right { position: relative; }
.rail-min-btn { position: absolute; top: 6px; right: 6px; z-index: 2; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); color: var(--muted); }
.rail-min-btn:hover { color: var(--ink); background: var(--surface-2); }
.rail-min-btn[hidden] { display: none; } /* a display rule beats UA [hidden]{display:none}; restore it so the JS hidden flag actually hides the arrow (cf .ecw-clinical-panel[hidden]) */
.body.ap-rail-min { grid-template-columns: var(--rail-w) 1fr 36px; }
@media (max-width: 1100px) { .body.ap-rail-min { grid-template-columns: var(--rail-w) 1fr; } }
@media (max-width: 760px)  { .body.ap-rail-min { grid-template-columns: 1fr; } }
/* The invoice workspace has its OWN slim left strip (26px — the Bill.com-style sidebar media
   block above) — the minimized-game-rail grid must match that geometry exactly: this selector's
   higher specificity otherwise re-widens the left column and leaves a ~200px dead gutter next
   to the PDF (owner screenshot 2026-07-21). */
@media (min-width: 761px) {
  .body.ap-rail-min:has(.ap-inbox-layout) { grid-template-columns: 26px 1fr; }
}
@media (max-width: 760px) {
  .body.ap-rail-min:has(.ap-inbox-layout) { grid-template-columns: 1fr; }
}
.body.ap-rail-min .rail-right > .card { display: none; }
.rail-restore { display: none; }
.body.ap-rail-min .rail-restore { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 12px 4px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); color: var(--muted); height: fit-content; width: 100%; box-shadow: var(--sh-1); }
.rail-restore:hover { color: var(--ink); background: var(--surface-2); }
.rail-restore-label { writing-mode: vertical-rl; font-size: var(--t-xs); font-weight: 600; letter-spacing: .05em; }

/* ---- Vendors screen: edit-in-place + tidy-up-names (2026-07-21) ----------------------- */
/* The Vendors screen stopped being read-only: header actions, an edit modal, and the
   name-cleanup review list. Everything stays in the calm AP visual language — quiet borders,
   small type, color only where a decision is being made. */
.apv-actions { display: flex; gap: 8px; }
.apv-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; }
.apv-bankline { display: flex; align-items: center; gap: 8px; font-size: var(--t-sm); color: var(--ink);
  padding: 8px 10px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
.apv-bankline button { margin-left: auto; }
.apc-card.chooser-card { max-width: 720px; }
.apc-groups { display: grid; gap: 12px; max-height: 56vh; overflow: auto; padding-right: 2px; }
.apc-group { border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 12px; display: grid; gap: 6px; }
.apc-canon { display: grid; gap: 4px; margin-bottom: 4px; }
.apc-canon-input { font-weight: 700; }
.apc-loose { display: flex; align-items: center; gap: 6px; font-size: var(--t-xs); color: var(--warn, #a15c00); }
.apc-row { display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto auto auto; column-gap: 10px;
  align-items: start; padding: 6px 8px; border-radius: var(--r-sm); font-size: var(--t-sm); cursor: pointer; }
.apc-row:hover { background: var(--surface-2); }
.apc-row input { grid-row: 1 / span 3; margin-top: 3px; }
.apc-row-name { font-weight: 600; }
.apc-row-co { color: var(--muted); font-size: var(--t-xs); }
.apc-row-act { color: var(--muted); font-size: var(--t-xs); font-style: italic; }
.apc-row.apc-dup .apc-row-act { color: var(--warn, #a15c00); font-style: normal; }
.apc-note { display: grid; gap: 2px; }

/* ---- Combine-duplicates (merge) flow — replaces the tick-to-rename UI (2026-07-21 pm) --- */
.apc-line { display: grid; grid-template-columns: minmax(140px, auto) auto 1fr; gap: 4px 12px; align-items: baseline;
  padding: 5px 8px; border-radius: var(--r-sm); font-size: var(--t-sm); }
.apc-line:nth-child(even) { background: var(--surface-2); }
.apc-chips { display: flex; gap: 4px; flex-wrap: wrap; justify-self: end; }
.apc-chip { font-size: var(--t-xs); padding: 1px 8px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--line); color: var(--ink); white-space: nowrap; }
.apc-chip-empty { color: var(--muted); border-style: dashed; background: transparent; }
.apc-done { display: flex; align-items: center; gap: 8px; color: var(--good); font-size: var(--t-sm); }
.apc-loose-tag { font-size: var(--t-xs); color: var(--warn, #a15c00); font-weight: 600; }
.apc-merge { display: grid; gap: 8px; max-height: 60vh; overflow: auto; padding-right: 2px; }
.apc-merge .ap-nv-label { margin-top: 8px; }
.apc-opt { display: grid; grid-template-columns: auto 1fr; column-gap: 10px; align-items: baseline;
  padding: 6px 8px; border: 1px solid var(--line); border-radius: var(--r-sm); font-size: var(--t-sm); cursor: pointer; }
.apc-opt:hover { background: var(--surface-2); }
.apc-opt:has(input:checked) { border-color: var(--accent, #2563eb); background: var(--surface-2); }
.apc-opt input { grid-row: 1 / span 2; align-self: start; margin-top: 3px; }
.apc-opt-src { grid-column: 2; color: var(--muted); font-size: var(--t-xs); }
.apc-custom-name { grid-column: 2; margin-top: 4px; }
.apc-field { display: grid; gap: 4px; }
.apc-field-label { font-size: var(--t-xs); font-weight: 700; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; }
.apc-autos { display: grid; gap: 2px; padding: 8px 10px; background: var(--surface-2); border-radius: var(--r-md); }
.apc-auto { font-size: var(--t-sm); color: var(--ink); }
/* ===== AP funding dashboard (Sign screen) — read-only money picture ===================== */
.ap-fund { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: var(--s4) var(--s5); margin-bottom: var(--s4); }
.ap-fund-loading { margin: 0; display: flex; align-items: center; gap: 6px; }
.ap-fund-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; }
.ap-fund-title { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-base); color: var(--ink); }
.ap-fund-title svg { width: 17px; height: 17px; color: var(--muted); }
.ap-fund-head .cc-note { margin: 0; }
/* "as of 10:42 pm" and the button that re-reads QuickBooks + Mercury sit on one baseline. */
.ap-fund-asof { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.ap-fund-refresh { font-weight: 600; }

/* OVERDRAWN — a QuickBooks funding account is in the red. The loudest thing on the AP screens on
   purpose: it is the one state where nothing else on this panel can be acted on. */
.ap-fund-overdrawn { margin-top: var(--s3); display: flex; align-items: flex-start; gap: var(--s2);
  padding: var(--s3); border-radius: var(--r-md); font-size: var(--t-sm); color: var(--ink);
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent); }
.ap-fund-overdrawn > svg { width: 20px; height: 20px; flex: 0 0 auto; color: var(--danger); margin-top: 1px; }
.ap-fund-overdrawn strong { color: var(--danger); }
.ap-fund-overdrawn .cc-note { margin: var(--s2) 0 0; }
.ap-fund-od-list { margin-top: 4px; font-size: var(--t-sm); line-height: 1.6; }
.ap-fund-od-amt { font-weight: 700; color: var(--danger); font-variant-numeric: tabular-nums; }
.ap-fund-od-co { color: var(--muted); font-size: var(--t-xs); }
.ap-fund-off { margin: var(--s2) 0 0; }
/* headline — the single action number */
.ap-fund-topline { margin-top: var(--s3); font-size: var(--t-sm); font-weight: 600; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: var(--s2) var(--s3); border-radius: var(--r-md); }
.ap-fund-topline svg { flex: 0 0 auto; width: 15px; height: 15px; }
.ap-fund-topline.need { color: var(--danger); background: color-mix(in srgb, var(--danger) 8%, transparent); border: 1px solid color-mix(in srgb, var(--danger) 24%, transparent); }
.ap-fund-topline.ok { color: var(--good); background: color-mix(in srgb, var(--good) 8%, transparent); border: 1px solid color-mix(in srgb, var(--good) 22%, transparent); }
.ap-fund-topline strong { font-weight: 700; }
.ap-fund-tl-transit { color: var(--warn); font-weight: 600; }
/* funding-on-the-way strip */
.ap-fund-transit { margin-top: var(--s2); font-size: var(--t-sm); color: var(--ink); background: color-mix(in srgb, var(--warn) 10%, transparent); border: 1px solid color-mix(in srgb, var(--warn) 26%, transparent); border-radius: var(--r-md); padding: var(--s2) var(--s3); display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ap-fund-transit svg { color: var(--warn); flex: 0 0 auto; }
/* grid of per-account cards — each is a column of three rows */
.ap-fund-grid { display: flex; gap: var(--s3); margin-top: var(--s3); overflow-x: auto; padding-bottom: var(--s2); scroll-snap-type: x proximity; overscroll-behavior-x: contain; }
.ap-fac { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); overflow: hidden; display: flex; flex-direction: column; flex: 0 0 272px; scroll-snap-align: start; }
.ap-fac.is-need { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); box-shadow: inset 3px 0 0 var(--danger); }
/* Overdrawn outranks "needs funding": a solid red edge plus a tinted card, so it reads as different
   in kind from the cards beside it, not just further along the same scale. */
.ap-fac.is-overdrawn { border-color: var(--danger); box-shadow: inset 4px 0 0 var(--danger), var(--sh-1);
  background: color-mix(in srgb, var(--danger) 5%, var(--surface)); }
.ap-fac.is-overdrawn .ap-fac-title { background: color-mix(in srgb, var(--danger) 13%, var(--surface)); color: var(--danger); }
.ap-fac.is-overdrawn .ap-fac-row.r-truist { background: color-mix(in srgb, var(--danger) 8%, transparent); }
.ap-fac-title { font-size: var(--t-xs); font-weight: 700; color: var(--ink); padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); background: var(--surface-2); }
.ap-fac-co { font-weight: 400; color: var(--muted); font-size: 11px; }
.ap-fac-row { padding: var(--s2) var(--s3); display: flex; flex-direction: column; gap: 1px; }
.ap-fac-row + .ap-fac-row { border-top: 1px solid var(--line); }
.ap-fac-row.r-truist { background: color-mix(in srgb, var(--primary) 4%, var(--surface)); }
/* the small-print "why this amount" strip at the bottom of each card */
.ap-fac-row.r-math { margin-top: auto; background: color-mix(in srgb, var(--surface-2) 55%, var(--surface)); border-top-style: dashed; }
.ap-fac-row.r-math .rsub strong { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }
.rsub.rwarn { color: var(--warn); font-weight: 600; display: flex; align-items: center; gap: 4px; }
.rsub.rwarn svg { width: 12px; height: 12px; flex: 0 0 auto; }
.rlbl { font-size: 11px; color: var(--muted); font-weight: 600; text-transform: uppercase; letter-spacing: .03em; display: flex; align-items: center; gap: 5px; }
.rlbl svg { width: 13px; height: 13px; flex: 0 0 auto; }
.rval { font-size: var(--t-md); font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.25; }
.rval.need { color: var(--danger); }
.rval.rval-od { color: var(--danger); }
.rsub.rod { color: var(--danger); font-weight: 700; display: flex; align-items: center; gap: 4px; text-transform: uppercase; letter-spacing: .02em; font-size: 11px; }
.rsub.rod svg { width: 12px; height: 12px; flex: 0 0 auto; }
.rval.ok { color: var(--good); }
.rsub { font-size: var(--t-xs); color: var(--muted); line-height: 1.4; }
.ap-fac-fundbtn { align-self: flex-start; margin-top: 6px; }
.ap-fac-incoming { display: flex; align-items: center; flex-wrap: wrap; gap: 4px; font-size: var(--t-xs); color: var(--warn); font-weight: 600; margin-top: 4px; }
.ap-fac-incoming svg { width: 12px; height: 12px; flex: 0 0 auto; }
.ap-fac-incoming-from { color: var(--muted); font-weight: 400; flex-basis: 100%; padding-left: 16px; }
.ap-fund-foot { margin-top: var(--s3); }
/* fund-transfer modal */
.ap-fund-modal-note { margin: 0 0 var(--s3); }
.ap-fund-mercury-link { margin-bottom: var(--s3); display: inline-flex; }
.ap-fund-form { display: grid; gap: var(--s3); }
.ap-err { color: var(--danger); }
/* funding-account picker + set-link */
.ap-fac-chg { margin-left: 6px; font-weight: 600; text-transform: none; letter-spacing: 0; }
.rval-set { font-size: var(--t-sm); font-weight: 600; }
.ap-pick-list { display: flex; flex-direction: column; gap: 4px; margin: var(--s2) 0; max-height: 320px; overflow-y: auto; }
.ap-pick-row { display: flex; align-items: center; gap: var(--s2); padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--r-sm); cursor: pointer; font-size: var(--t-sm); }
.ap-pick-row:hover { background: var(--surface-2); }
.ap-pick-row input { margin: 0; flex: 0 0 auto; }
.ap-pick-name { flex: 1 1 auto; color: var(--ink); }
.ap-pick-name .cc-note { display: inline; margin: 0; }
.ap-pick-bal { flex: 0 0 auto; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }

/* ===================== Hospital Rounding: provider capture kiosk ===================== */
body.provider-kiosk .topbar, body.provider-kiosk .workspace, body.provider-kiosk #teamDock,
body.provider-kiosk .rail-left, body.provider-kiosk .rail-right, body.provider-kiosk .rail-center { display: none !important; }
#providerKiosk { position: fixed; inset: 0; z-index: 50; overflow-y: auto; background: var(--bg); color: var(--ink); font-family: var(--font); }
.pk-top { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; background: var(--surface); border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 2; }
.pk-brand { display: flex; align-items: center; gap: 8px; font-weight: 700; color: var(--primary-ink); }
.pk-brand svg { width: 22px; height: 22px; }
.pk-user { display: flex; align-items: center; gap: 12px; color: var(--muted); font-size: var(--t-sm); }
.pk-logout { border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); border-radius: var(--r-full); padding: 6px 14px; cursor: pointer; font-size: var(--t-sm); }
.pk-logout:hover { background: var(--surface-3); }
.pk-wrap { max-width: 640px; margin: 0 auto; padding: 24px 18px 60px; }
.pk-h1 { font-size: var(--t-xl); margin: 8px 0 4px; color: var(--ink); }
.pk-h2 { font-size: var(--t-md); margin: 0 0 10px; color: var(--ink-soft); }
.pk-sub { color: var(--muted); font-size: var(--t-base); margin: 0 0 20px; line-height: 1.5; }
.pk-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); padding: 20px; box-shadow: var(--sh-1); }
.pk-field { margin-bottom: 18px; }
.pk-label { display: block; font-size: var(--t-sm); font-weight: 600; color: var(--ink-soft); margin-bottom: 8px; }
.pk-hospitals { display: flex; flex-wrap: wrap; gap: 8px; }
.pk-hchip { padding: 10px 16px; border-radius: var(--r-full); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); font-size: var(--t-base); cursor: pointer; transition: all var(--dur) var(--ease); }
.pk-hchip:hover { border-color: var(--primary); }
.pk-hchip.on { background: var(--primary); border-color: var(--primary); color: var(--on-primary); font-weight: 600; }
.pk-select { width: 100%; padding: 12px; border-radius: var(--r-md); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); font-size: var(--t-md); }
.pk-photos { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 10px; }
.pk-thumb { position: relative; aspect-ratio: 1; border-radius: var(--r-md); overflow: hidden; border: 1px solid var(--line); }
.pk-thumb img { width: 100%; height: 100%; object-fit: cover; }
.pk-del { position: absolute; top: 4px; right: 4px; width: 26px; height: 26px; border-radius: var(--r-full); border: none; background: rgba(0,0,0,.55); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.pk-add { aspect-ratio: 1; border: 1.5px dashed var(--line-strong); border-radius: var(--r-md); background: var(--surface-2); color: var(--muted); cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; font-size: var(--t-xs); }
.pk-add:hover { border-color: var(--primary); color: var(--primary); }
.pk-add svg { width: 22px; height: 22px; }
.pk-submit { width: 100%; margin-top: 4px; padding: 14px; border: none; border-radius: var(--r-md); background: var(--primary); color: var(--on-primary); font-size: var(--t-md); font-weight: 700; cursor: pointer; transition: background var(--dur) var(--ease); }
.pk-submit:hover:not(:disabled) { background: var(--primary-hover); }
.pk-submit:disabled { opacity: .45; cursor: not-allowed; }
.pk-mine { margin-top: 28px; }
.pk-minelist { display: flex; flex-direction: column; gap: 8px; }
.pk-batch { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 12px 14px; }
.pk-batch-top { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.pk-hosp { font-weight: 600; color: var(--ink); }
.pk-batch-sub { color: var(--muted); font-size: var(--t-sm); margin-top: 3px; }
.pk-status { font-size: var(--t-xs); font-weight: 600; padding: 3px 10px; border-radius: var(--r-full); white-space: nowrap; }
.pk-status.ok { background: var(--primary-soft); color: var(--primary-ink); }
.pk-status.wait { background: var(--gold-soft); color: var(--gold-ink); }
.pk-status.warn { background: var(--danger-soft); color: var(--danger); }
.pk-muted { color: var(--muted); font-size: var(--t-sm); }

/* ===================== Hospital Rounding: biller worklist ===================== */
.rounds-wrap { display: flex; flex-direction: column; height: 100%; }
.rounds-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px; border-bottom: 1px solid var(--line); flex-wrap: wrap; }
.rounds-sub { color: var(--muted); font-size: var(--t-sm); }
.rounds-tools { display: flex; align-items: center; gap: 12px; }
.rc-toggle { display: flex; align-items: center; gap: 6px; font-size: var(--t-sm); color: var(--ink-soft); cursor: pointer; }
.rounds-scroll { overflow: auto; flex: 1; }
.rounds-table { border-collapse: separate; border-spacing: 0; font-size: var(--t-xs); white-space: nowrap; }
.rounds-table th { position: sticky; top: 0; z-index: 2; background: var(--surface-3); color: var(--ink-soft); font-weight: 600; text-align: left; padding: 8px; border-bottom: 1px solid var(--line-strong); border-right: 1px solid var(--line); }
.rounds-table td { padding: 2px 4px; border-bottom: 1px solid var(--line); border-right: 1px solid var(--line); vertical-align: middle; }
.rc-row:hover td { background: var(--primary-softer); }
.rc-verified td { background: var(--surface-2); opacity: .85; }
.rc-ro { text-align: right; color: var(--muted); font-variant-numeric: tabular-nums; padding: 2px 8px; }
.rc-in { border: 1px solid transparent; background: transparent; color: var(--ink); font-size: var(--t-xs); padding: 5px 6px; border-radius: var(--r-sm); min-width: 90px; width: 100%; font-family: var(--font); }
.rc-in:hover { border-color: var(--line); }
.rc-in:focus { outline: none; border-color: var(--primary); background: var(--surface); box-shadow: var(--ring); }
.rc-money .rc-in { text-align: right; font-variant-numeric: tabular-nums; min-width: 70px; }
.rc-cell { position: relative; }
.rc-low .rc-in { box-shadow: inset 0 -2px 0 var(--gold-2); }
.rc-low::after { content: "verify"; position: absolute; top: -1px; right: 2px; font-size: 9px; color: var(--gold-ink); background: var(--gold-soft); padding: 0 3px; border-radius: 3px; pointer-events: none; }
.rc-photo { text-align: center; }
.rc-photobtn { display: inline-flex; align-items: center; gap: 2px; border: 1px solid var(--line-strong); background: var(--surface-2); border-radius: var(--r-sm); padding: 4px 6px; cursor: pointer; color: var(--ink-soft); }
.rc-photobtn:hover { border-color: var(--primary); color: var(--primary); }
.rc-phactions { display: inline-flex; gap: 2px; position: absolute; right: 2px; top: 50%; transform: translateY(-50%); }
.rc-phactions a, .rc-text { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: var(--r-sm); border: none; background: var(--surface-3); color: var(--primary); cursor: pointer; }
.rc-ecwpick { position: absolute; right: 2px; top: 50%; transform: translateY(-50%); font-size: 9px; border: 1px solid var(--gold-2); background: var(--gold-soft); color: var(--gold-ink); border-radius: 4px; padding: 1px 4px; cursor: pointer; }
.rc-ecw { font-size: 10px; padding: 2px 7px; border-radius: var(--r-full); font-weight: 600; }
.rc-ecw-matched { background: var(--primary-soft); color: var(--primary-ink); }
.rc-ecw-ambiguous { background: var(--gold-soft); color: var(--gold-ink); cursor: pointer; }
.rc-suggest { text-align: center; white-space: nowrap; }
.rc-suggestbtn { border: 1px solid var(--primary); background: var(--primary-softer); color: var(--primary-ink); border-radius: var(--r-full); padding: 3px 10px; font-size: 11px; font-weight: 600; cursor: pointer; font-variant-numeric: tabular-nums; }
.rc-suggestbtn:hover { background: var(--primary-soft); }
.rc-ecw-none, .rc-ecw-error, .rc-ecw-disabled { background: var(--surface-3); color: var(--muted); }
.rc-ecwcell, .rc-donecell { text-align: center; }
.rc-del-row { border: none; background: transparent; color: var(--muted); cursor: pointer; padding: 4px; border-radius: var(--r-sm); }
.rc-del-row:hover { color: var(--danger); background: var(--danger-soft); }
.rc-lightbox { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.6); display: flex; align-items: center; justify-content: center; padding: 20px; }
.rc-lb-inner { position: relative; background: var(--surface); border-radius: var(--r-lg); max-width: 900px; max-height: 90vh; overflow: auto; padding: 40px 20px 20px; box-shadow: var(--sh-3); }
.rc-lb-close { position: absolute; top: 10px; right: 10px; border: none; background: var(--surface-2); border-radius: var(--r-full); width: 32px; height: 32px; cursor: pointer; color: var(--ink-soft); display: flex; align-items: center; justify-content: center; }
.rc-lb-imgs { display: flex; flex-direction: column; gap: 14px; }
.rc-lb-imgs img { max-width: 100%; border-radius: var(--r-md); border: 1px solid var(--line); }
.rc-lb-card { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.rc-lb-canvas { max-width: 100%; height: auto; border-radius: var(--r-md); border: 1px solid var(--line); }
.rc-lb-full { align-self: flex-start; border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); border-radius: var(--r-full); padding: 4px 12px; font-size: var(--t-xs); cursor: pointer; }
.rc-lb-full:hover { border-color: var(--primary); color: var(--primary); }
.rc-pickbox { min-width: 340px; }
.rc-pickbox h3 { margin: 0 0 14px; }
/* A dialog you have to answer BY reading what is behind it (the card photo) can be dragged aside by
   its heading, and doing so fades the backdrop so the card is actually legible. */
.rc-draghandle { cursor: grab; user-select: none; -webkit-user-select: none; touch-action: none; }
.rc-draghandle:active, .rc-dragging .rc-draghandle { cursor: grabbing; }
.rc-dragging { transition: none; box-shadow: var(--sh-3), 0 0 0 2px var(--primary-softer); }
.rc-lightbox.rc-backdrop-faded { background: rgba(0,0,0,.12); transition: background .18s ease; }
.rc-cands { display: flex; flex-direction: column; gap: 8px; }
.rc-cand { text-align: left; border: 1px solid var(--line-strong); background: var(--surface-2); border-radius: var(--r-md); padding: 10px 14px; cursor: pointer; display: flex; flex-direction: column; gap: 2px; }
.rc-cand:hover { border-color: var(--primary); background: var(--primary-softer); }
.rc-cand b { color: var(--ink); }
.rc-cand span { color: var(--muted); font-size: var(--t-xs); }

/* ---- Hospital Rounds billing monitor: status control, aging, filter bar, by-biller summary ---- */
/* Hospital Rounds — the search box + quick filters above the worklist. It WRAPS rather than pushing
   the page sideways; the wide table underneath is the only thing here allowed to scroll across. */
.rc-searchbar { padding: 10px 4px 0; }
.rc-searchrow { margin-bottom: 8px; }
.rc-quickrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; font-size: var(--t-xs); color: var(--muted); }
.rc-qf { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; max-width: 100%; }
.rc-qf > span { font-weight: 600; color: var(--ink-soft); white-space: nowrap; }
/* On a phone the two native date boxes plus their label are wider than the screen. They wrap onto a
   second line rather than spilling off the edge, and min-width:0 lets them shrink if even that is
   tight - a control half off-screen is a control nobody can use. */
.rc-qsel, .rc-qdate { border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); border-radius: var(--r-sm); padding: 4px 8px; font-size: var(--t-xs); max-width: 220px; min-width: 0; }
.rc-qsel:focus, .rc-qdate:focus { outline: none; border-color: var(--primary); }
.rc-qdash { color: var(--muted); }
.rc-qcount { margin-left: auto; color: var(--ink-soft); }
.rc-qcount b { color: var(--ink); font-variant-numeric: tabular-nums; }
.rc-qwarn { color: var(--warn); }
.rc-filterbar { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px 4px 4px; }
.rc-fchip { border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); border-radius: var(--r-full); padding: 5px 12px; font-size: var(--t-xs); font-weight: 600; cursor: pointer; display: inline-flex; align-items: center; gap: 6px; }
.rc-fchip:hover { border-color: var(--primary); }
.rc-fchip.is-on { background: var(--primary-softer); border-color: var(--primary); color: var(--primary-ink); }
.rc-fchip .rc-fn { background: var(--surface-3); color: var(--muted); border-radius: var(--r-full); padding: 0 7px; font-weight: 700; font-variant-numeric: tabular-nums; }
.rc-fchip.is-on .rc-fn { background: var(--primary-soft); color: var(--primary-ink); }
.rc-fchip-alert { border-color: var(--danger); color: var(--danger); }
.rc-fchip-alert .rc-fn { background: var(--danger); color: #fff; }

.rc-summary { display: flex; flex-wrap: wrap; gap: 22px; padding: 10px 12px; margin: 4px 0 2px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
.rc-sumcol { display: flex; flex-direction: column; gap: 5px; }
.rc-sumh { font-size: var(--t-xs); color: var(--muted); font-weight: 600; letter-spacing: .02em; }
.rc-sumline { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.rc-btag { font-size: var(--t-xs); color: var(--ink-soft); background: var(--surface-3); border-radius: var(--r-full); padding: 2px 9px; font-weight: 600; }
.rc-btag b { color: var(--ink); font-variant-numeric: tabular-nums; }

.rc-statuscell { min-width: 108px; }
.rc-bstatus { width: 100%; border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); border-radius: var(--r-sm); padding: 3px 4px; font-size: 11px; font-weight: 600; cursor: pointer; }
.rc-billmeta { font-size: 10px; color: var(--muted); margin-top: 2px; overflow: hidden; text-overflow: ellipsis; max-width: 130px; }
.rc-bs-billed { background: color-mix(in srgb, var(--good) 10%, var(--surface)); }
.rc-bs-billed .rc-bstatus { border-color: color-mix(in srgb, var(--good) 55%, var(--line)); color: var(--good); }
.rc-bs-hold { background: color-mix(in srgb, var(--warn) 9%, var(--surface)); }
.rc-bs-not_billable { background: var(--surface-3); }

.rc-aging { text-align: center; }
.rc-agechip { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 700; border-radius: var(--r-full); padding: 2px 8px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.rc-age-ok { background: color-mix(in srgb, var(--good) 14%, var(--surface)); color: var(--good); }
.rc-age-warn { background: color-mix(in srgb, var(--warn) 16%, var(--surface)); color: var(--warn); }
.rc-age-over { background: color-mix(in srgb, var(--danger) 16%, var(--surface)); color: var(--danger); }
.rc-age-done { background: transparent; color: var(--good); }
.rc-row.rc-overdue > td:first-child { box-shadow: inset 3px 0 0 var(--danger); }

/* Import-claims reconciliation result modal */
.rc-importresult { min-width: 360px; max-width: 460px; }
.rc-importresult h3 { margin: 0 0 2px; }
.rc-implist { list-style: none; margin: 14px 0 18px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.rc-implist li { font-size: var(--t-sm); color: var(--ink-soft); padding: 8px 12px; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); }
.rc-implist li b { color: var(--ink); font-variant-numeric: tabular-nums; }
.rc-imp-ok { border-color: color-mix(in srgb, var(--good) 45%, var(--line)) !important; }
.rc-imp-warn { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)) !important; }
.rc-importresult .btn-primary { width: 100%; }
.rc-imp-fileerr { font-size: var(--t-xs); color: var(--warn); margin: 6px 0 0; }

/* Hospital Rounds — reorder (drag headers) / sort (click headers) / per-column filter row */
.rc-th { cursor: grab; user-select: none; }
.rc-th:active { cursor: grabbing; }
.rc-th-lbl { display: inline-flex; align-items: center; gap: 3px; }
.rc-th-sort .rc-th-lbl { cursor: pointer; }
.rc-th.rc-th-active { color: var(--primary-ink); background: var(--primary-softer); }
.rc-th-dragging { opacity: .45; }
.rc-th-over { box-shadow: inset 3px 0 0 var(--primary); }
.rounds-table thead .rc-filterrow th { position: static; top: auto; z-index: 1; background: var(--surface-2); border-bottom: 1px solid var(--line-strong); padding: 3px 4px; }
.rc-colfilter { width: 100%; min-width: 58px; box-sizing: border-box; border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); border-radius: var(--r-sm); padding: 2px 6px; font-size: 11px; cursor: text; }
.rc-colfilter:focus { outline: none; border-color: var(--primary); }
.rc-colrange { display: flex; gap: 3px; align-items: center; }
.rc-colfilter-r { min-width: 0; flex: 1 1 0; padding: 2px 4px; }
.rc-colrange input[type="date"] { min-width: 92px; }
.ghost-btn.is-on { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-softer); }
.rc-viewbar { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 6px 4px 2px; font-size: var(--t-xs); color: var(--muted); }
.rc-viewbar-sum { color: var(--ink-soft); }
.rc-viewbar-save { border: 1px solid var(--primary); background: var(--primary-softer); color: var(--primary-ink); border-radius: var(--r-full); padding: 3px 12px; font-size: var(--t-xs); font-weight: 600; cursor: pointer; }
.rc-viewbar-save:hover { background: var(--primary-soft); }
.rc-viewbar-saved { color: var(--good); font-weight: 600; }

/* Reconciliation filter chips + "claims without a card" searchable list */
.rc-fsep { width: 1px; align-self: stretch; background: var(--line-strong); margin: 0 3px; }
.rc-fchip-claims { border-style: dashed; }
.rc-umbox { min-width: min(880px, 94vw); max-width: 94vw; }
.rc-umbox h3 { margin: 0 0 4px; }
.rc-umsearch { width: 100%; box-sizing: border-box; margin: 10px 0; border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); border-radius: var(--r-md); padding: 8px 12px; font-size: var(--t-sm); }
.rc-umsearch:focus { outline: none; border-color: var(--primary); }
.rc-umscroll { max-height: 60vh; overflow: auto; border: 1px solid var(--line); border-radius: var(--r-md); }
.rc-umtable { border-collapse: separate; border-spacing: 0; font-size: var(--t-xs); width: 100%; white-space: nowrap; }
.rc-umtable th { position: sticky; top: 0; background: var(--surface-3); color: var(--ink-soft); text-align: left; padding: 7px 10px; border-bottom: 1px solid var(--line-strong); font-weight: 600; }
.rc-umtable td { padding: 6px 10px; border-bottom: 1px solid var(--line); }
.rc-umtable tbody tr:hover { background: var(--surface-2); }
.rc-um-num { text-align: right; font-variant-numeric: tabular-nums; }
.rc-umfoot { display: flex; align-items: center; justify-content: space-between; margin-top: 10px; font-size: var(--t-xs); color: var(--muted); }
.rc-umfoot-actions { display: flex; gap: 12px; align-items: center; }
/* eCW patient live-search in the rounds "which patient" picker (uses the shared .ecw-fields boxes) */
.rc-pickbox .ecw-fields { margin: 6px 0 4px; }
.rc-ecwsearchhint { font-size: var(--t-xs); margin: 6px 0 8px; }
.rc-cands-label { font-size: var(--t-xs); color: var(--muted); font-weight: 600; margin: 4px 0 2px; }
.rc-ecwresults { max-height: 52vh; overflow: auto; }

/* Live in-app camera for rapid card capture (provider kiosk) */
.pk-cam { position: fixed; inset: 0; z-index: 300; background: #000; overflow: hidden; }
.pk-cam-video { width: 100%; height: 100%; object-fit: cover; background: #000; }
.pk-cam-flash { position: absolute; inset: 0; background: #fff; opacity: 0; pointer-events: none; }
.pk-cam-flash.on { animation: pkflash .18s ease-out; }
@keyframes pkflash { from { opacity: .82; } to { opacity: 0; } }
.pk-cam-top { position: absolute; top: 0; left: 0; right: 0; padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 14px; color: #fff; font-size: 14px; display: flex; align-items: center; justify-content: space-between; gap: 12px; background: linear-gradient(#000a, #0000); }
.pk-cam-count { font-weight: 700; font-size: 17px; }
/* Small centered guidance, sitting just above the bottom bar (kept out of the top row so Done fits). */
.pk-cam-hint { position: absolute; left: 0; right: 0; bottom: calc(env(safe-area-inset-bottom, 0px) + 124px); text-align: center; color: #fff; font-size: 13px; opacity: .85; pointer-events: none; text-shadow: 0 1px 3px #000a; }
.pk-cam-bar { position: absolute; left: 0; right: 0; bottom: 0; display: flex; align-items: center; justify-content: space-between; padding: 18px 24px calc(env(safe-area-inset-bottom, 0px) + 22px); background: linear-gradient(#0000, #000b); }
.pk-cam-spacer { width: 72px; }
.pk-cam-shutter { width: 74px; height: 74px; border-radius: 50%; background: #fff; border: 5px solid rgba(255,255,255,.55); box-shadow: 0 0 0 2px #0007; cursor: pointer; padding: 0; }
.pk-cam-shutter:active { transform: scale(.92); }
.pk-cam-done { min-width: 72px; background: rgba(255,255,255,.18); color: #fff; border: 1px solid rgba(255,255,255,.45); border-radius: 22px; padding: 11px 18px; font-size: 15px; font-weight: 600; cursor: pointer; }
/* Running thumbnail of the last shot (bottom-left) — makes it obvious each picture was captured.
   Fixed 72px so it balances the "Photos" button on the right and keeps the shutter centered. */
.pk-cam-thumb { width: 72px; height: 60px; border-radius: 12px; border: 2px dashed rgba(255,255,255,.5); flex: 0 0 auto; position: relative; background: rgba(255,255,255,.06); }
.pk-cam-thumb.has { border-style: solid; border-color: rgba(255,255,255,.8); }
.pk-cam-thumbimg { position: absolute; inset: 0; border-radius: 10px; overflow: hidden; }
.pk-cam-thumbimg img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pk-cam-thumbn { position: absolute; top: -8px; right: -8px; min-width: 22px; height: 22px; padding: 0 6px; border-radius: 11px; background: var(--primary, #2f6df6); color: #fff; font-size: 12px; font-weight: 700; display: flex; align-items: center; justify-content: center; box-shadow: 0 0 0 2px #000; }
/* Camera roll (opposite corner): pick existing photos, one or many, without leaving the camera. */
.pk-cam-roll { width: 72px; min-width: 72px; flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; gap: 3px; background: rgba(255,255,255,.16); color: #fff; border: 1px solid rgba(255,255,255,.4); border-radius: 14px; padding: 9px 0; font-size: 11px; font-weight: 600; cursor: pointer; }
.pk-cam-roll svg { width: 22px; height: 22px; }
.pk-cam-roll:active { transform: scale(.94); }
/* Native Date of Service picker — inherits .pk-select; color-scheme makes the OS picker theme-match. */
.pk-date { color-scheme: light dark; min-height: 46px; }

/* ---- Hospital Rounds: eCW charge packet (Level 1 — cut the keystrokes) ---------------------- */
.rc-rowactions { white-space: nowrap; }
.rc-packet-btn { background: none; border: none; color: var(--muted); cursor: pointer; padding: 4px; border-radius: var(--r-md); vertical-align: middle; }
.rc-packet-btn:hover { color: var(--primary); background: var(--primary-softer); }
.rc-pk-box { min-width: 520px; width: 640px; max-width: 94vw; }
.rc-pk-head { margin: 0 0 16px; }
.rc-pk-head h3 { display: flex; align-items: center; gap: 8px; margin: 0 0 4px; font-size: var(--t-lg, 17px); }
.rc-pk-sub { color: var(--muted); font-size: var(--t-xs); }
.rc-pk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 20px; }
.rc-pk-fld { display: flex; align-items: baseline; gap: 8px; padding: 7px 0; border-bottom: 1px solid var(--line); min-width: 0; }
.rc-pk-lbl { flex: 0 0 40%; color: var(--ink-soft); font-size: var(--t-xs); }
.rc-pk-hint { display: block; color: var(--muted); font-size: 10px; font-weight: 400; }
.rc-pk-val { flex: 1 1 auto; color: var(--ink); font-weight: 600; font-size: var(--t-sm, 14px); overflow-wrap: anywhere; }
.rc-pk-val.rc-pk-empty { color: var(--muted); font-weight: 400; }
.rc-pk-copy { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; background: var(--surface-2); border: 1px solid var(--line-strong); color: var(--ink-soft); border-radius: var(--r-md); padding: 3px 8px; font-size: 11px; font-weight: 600; cursor: pointer; }
.rc-pk-copy:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-softer); }
.rc-pk-copy.rc-pk-copied { border-color: var(--good); color: var(--good); }
.rc-pk-sec { margin: 18px 0 0; }
.rc-pk-sech { font-size: var(--t-xs); font-weight: 700; color: var(--ink); text-transform: uppercase; letter-spacing: .03em; margin: 0 0 8px; }
.rc-pk-sech .rc-pk-hint { display: inline; text-transform: none; letter-spacing: 0; margin-left: 8px; color: var(--muted); font-weight: 400; }
.rc-pk-dxrow { display: flex; gap: 8px; }
.rc-pk-dx { flex: 1 1 auto; border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: 8px 10px; font-size: var(--t-sm, 14px); background: var(--surface); color: var(--ink); }
.rc-pk-dx:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-softer); }
.rc-pk-dxsug { margin: 8px 0 0; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; font-size: var(--t-xs); }
.rc-pk-suglbl { color: var(--muted); flex: 0 0 100%; margin-bottom: 2px; }
.rc-pk-sugchip { border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); border-radius: var(--r-full); padding: 3px 10px; font-size: 11px; cursor: pointer; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-pk-sugchip:hover { border-color: var(--primary); background: var(--primary-softer); color: var(--primary); }
.rc-pk-sugchip b { color: var(--ink); }
.rc-pk-cpts { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.rc-pk-cptline { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--line); }
.rc-pk-cptline:last-child { border-bottom: none; }
.rc-pk-cptdate { flex: 0 0 92px; color: var(--ink-soft); font-size: var(--t-xs); font-variant-numeric: tabular-nums; }
.rc-pk-cpt { flex: 0 0 auto; font-weight: 700; color: var(--primary); font-size: var(--t-sm, 14px); font-variant-numeric: tabular-nums; }
.rc-pk-cptlbl { flex: 1 1 auto; color: var(--ink); font-size: var(--t-xs); }
.rc-pk-cptpos { flex: 0 0 auto; color: var(--muted); font-size: 10px; }
.rc-pk-multi .rc-pk-val { line-height: 1.7; }
/* The CPT/HCPCS grid, laid out to match eCW's own claim screen column for column so a biller reads
   straight across while keying. One row per DATE OF SERVICE — never a date range on a single row. */
.rc-pk-cpttbl { width: 100%; border-collapse: collapse; font-size: var(--t-xs); font-variant-numeric: tabular-nums; }
.rc-pk-cpttbl th { text-align: left; font-weight: 600; color: var(--muted); font-size: 10px;
  text-transform: uppercase; letter-spacing: .04em; padding: 4px 8px; border-bottom: 1px solid var(--line, rgba(0,0,0,.1)); white-space: nowrap; }
.rc-pk-cpttbl td { padding: 6px 8px; border-bottom: 1px solid var(--line, rgba(0,0,0,.06)); white-space: nowrap; }
.rc-pk-cpttbl tbody tr:last-child td { border-bottom: 0; }
.rc-pk-cpttbl .rc-pk-n { color: var(--muted); width: 24px; }
.rc-pk-cpttbl .rc-pk-cpt { font-weight: 700; color: var(--primary); }
.rc-pk-cpttbl .rc-pk-fee { text-align: right; }
/* The trailing description ("Subsequent care") is context, not a keyed field — let it shrink and
   ellipsis rather than push the eCW-shaped columns out of the box and get clipped mid-word. */
.rc-pk-cpttbl .rc-pk-lbl2 { color: var(--muted); max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.rc-pk-cpttbl { table-layout: auto; }
/* One block per CLAIM. A claim carries a single rendering provider, so a stay covered by two
   providers is billed as two claims and is boxed separately here. */
/* overflow-x keeps a narrow window (or a long provider name) from clipping the grid — it scrolls
   inside its own box instead of spilling out of the packet. */
.rc-pk-claim { border: 1px solid var(--line, rgba(0,0,0,.12)); border-radius: 8px; padding: 10px 12px; margin: 0 0 12px; overflow-x: auto; }
.rc-pk-claim:last-child { margin-bottom: 0; }
.rc-pk-claimhd { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; margin: 0 0 8px; }
.rc-pk-claimn { font-weight: 700; font-size: var(--t-xs); text-transform: uppercase; letter-spacing: .04em; color: var(--primary); }
.rc-pk-claimwho { font-size: var(--t-xs); color: var(--ink); }
.rc-pk-claimdays { font-size: var(--t-xs); margin-left: auto; }
.rc-pk-copysm { padding: 0 4px; margin-left: 6px; opacity: .55; }
.rc-pk-copysm:hover { opacity: 1; }
/* The numbered diagnosis key under the grid — matches eCW's ICD1..ICD4 pointers on each CPT line. */
.rc-pk-icdkey { display: flex; flex-wrap: wrap; gap: 4px 14px; margin: 8px 2px 0; font-size: var(--t-xs); }
.rc-pk-icdkey b { color: var(--primary); margin-right: 4px; }
/* Worklist "Dates Of Service": every rounded day, with a marker where a day was skipped. */
/* Dates of service are full MM/DD/YYYY values that get keyed onto a claim, so they WRAP onto more
   lines rather than being cut off when the column is narrowed. break-word keeps a single date whole
   where it can; the row simply grows taller. */
.rc-days { font-variant-numeric: tabular-nums; font-size: var(--t-xs); color: var(--ink);
  white-space: normal; overflow-wrap: break-word; line-height: 1.5; padding-top: 5px; padding-bottom: 5px; }
.rc-daygap { color: var(--warn, #b45309); font-weight: 700; }
/* A date another provider also rounded. It is a BUTTON: click it to choose whose line keeps it,
   because a flag you cannot act on is just noise. */
.rc-daydup { color: var(--warn, #b45309); font-weight: 600; background: none; border: 0; padding: 0;
  font: inherit; cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px; }
.rc-daydup:hover { color: var(--ink); background: color-mix(in srgb, var(--warn, #b45309) 18%, transparent); border-radius: 4px; }
/* Old-system card: the dates in this cell are the first day of the stay, not all of it. Sits on its
   own line under the dates so it reads as a caveat on the whole list rather than on the last date. */
.rc-dayemr { display: block; margin-top: 4px; font-size: var(--t-2xs, 11px); font-weight: 600;
  color: var(--warn, #b45309); text-transform: none; letter-spacing: .01em; }
/* The same choice offered inside the eCW packet, one button per contested date. */
/* Briefly marks the row that just changed after a repaint, so a biller can find their place again. */
@keyframes rc-flash-fade { from { background: color-mix(in srgb, var(--primary) 30%, transparent); } to { background: transparent; } }
.rc-row.rc-flash > td { animation: rc-flash-fade 1.6s ease-out; }

/* Post-discharge follow-up state. "needs call" is the one that means work — a patient texted 3+ days
   ago with still nothing booked — so it is the only one that shouts. */
/* Date + time straight from eCW, e.g. "08/12/2026 4:00 PM". */
.rc-trunc { color: var(--warn, #b45309); font-weight: 600; }
.rc-stamp { white-space: nowrap; font-size: var(--t-xs); font-variant-numeric: tabular-nums; color: var(--ink); }
/* Each date of service paired with the code that date earns — click a code to make it billable. */
.rc-dayrow { display: inline-flex; align-items: baseline; gap: 2px; margin-right: 8px; white-space: nowrap; }
.rc-daysep { color: var(--muted); }
.rc-daycpt { border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--primary);
  font-weight: 700; font-size: 11px; border-radius: var(--r-full); padding: 1px 7px; cursor: pointer;
  font-variant-numeric: tabular-nums; }
.rc-daycpt:hover { background: var(--primary-softer); border-color: var(--primary); }
.rc-dayall { border: 1px dashed var(--line-strong); background: transparent; color: var(--ink-soft);
  font-size: 10px; border-radius: var(--r-full); padding: 1px 8px; cursor: pointer; }
.rc-dayall:hover { border-color: var(--primary); color: var(--primary); }

.rc-outreach { white-space: nowrap; font-size: var(--t-xs); }
.rc-outreach span + .rc-odate { margin-left: 6px; color: var(--muted); font-variant-numeric: tabular-nums; }
.rc-osent, .rc-obooked, .rc-ocall, .rc-oskip { display: inline-block; border-radius: var(--r-full); padding: 2px 9px; font-weight: 600; font-size: 11px; }
.rc-osent  { background: var(--surface-2); color: var(--ink-soft); }
.rc-obooked{ background: color-mix(in srgb, var(--ok, #1f7a55) 16%, transparent); color: var(--ok, #1f7a55); }
.rc-ocall  { background: color-mix(in srgb, var(--danger, #b3402f) 18%, transparent); color: var(--danger, #b3402f); }
.rc-oskip  { background: transparent; color: var(--muted); font-weight: 500; }

.rc-pk-warnpick { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; font-size: var(--t-xs); }
.rc-pk-daybtn { border: 1px solid color-mix(in srgb, var(--warn, #b45309) 45%, transparent);
  background: var(--surface); color: var(--ink); border-radius: var(--r-full); padding: 3px 11px;
  font-size: var(--t-xs); font-variant-numeric: tabular-nums; cursor: pointer; }
.rc-pk-daybtn:hover { background: color-mix(in srgb, var(--warn, #b45309) 16%, transparent); }
.rc-sdboth { border-style: dashed; }

/* Provider colour coding. A stay split across providers becomes several adjacent lines; a steady
   hue per provider makes those groups legible without reading the name each time. Kept to a thin
   left edge and a very light wash so the table stays calm, and it is never the ONLY signal — the
   provider's name is always in its own column. --rc-phue is set per row from the ADMISSION, so every
   line of one stay carries the same colour and the next stay carries a different one. */
.rc-row.rc-hasprov > td:first-child { box-shadow: inset 5px 0 0 hsl(var(--rc-phue) 62% 42%); }
.rc-row.rc-hasprov > td { background: hsl(var(--rc-phue) 58% 48% / .13); }
.rc-row.rc-hasprov:hover > td { background: hsl(var(--rc-phue) 58% 48% / .22); }
/* Stay grouping. One patient's admission can be several lines (one per rendering provider). They all
   share the admission's colour and are bracketed together, and the NEXT admission takes a different
   colour — that boundary is what tells a biller "these lines are all the same stay". */
.rc-row.rc-stay-first > td { border-top: 2px solid var(--ink-soft, #64748b); }
.rc-row.rc-stay-last > td { border-bottom: 2px solid var(--ink-soft, #64748b); }
.rc-row.rc-stay > td:first-child::after {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 5px;
  background: hsl(var(--rc-phue) 62% 42%);
}
@media (prefers-color-scheme: dark) {
  .rc-row.rc-hasprov > td { background: hsl(var(--rc-phue) 52% 58% / .16); }
  .rc-row.rc-hasprov:hover > td { background: hsl(var(--rc-phue) 52% 58% / .26); }
  .rc-row.rc-hasprov > td:first-child { box-shadow: inset 5px 0 0 hsl(var(--rc-phue) 55% 60%); }
}
/* A line holding a date another provider also rounded gets a dashed underline on its day cell. */
.rc-row.rc-shared .rc-days { text-decoration: underline dotted var(--warn, #b45309); text-underline-offset: 3px; }

/* Packet warning for a shared date — the biller has to confirm it before submitting. */
.rc-pk-warn { display: flex; gap: 10px; align-items: flex-start; margin: 0 0 12px;
  padding: 10px 12px; border-radius: 8px; font-size: var(--t-xs); line-height: 1.5;
  background: color-mix(in srgb, var(--warn, #b45309) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn, #b45309) 30%, transparent); color: var(--ink); }
.rc-pk-warn svg { flex: 0 0 auto; margin-top: 1px; color: var(--warn, #b45309); }
/* Who else rounded, and when — the biller picks which provider bills each shared date. */
.rc-pk-warnlist { margin: 6px 0; padding-left: 18px; }
.rc-pk-warnlist li { margin: 2px 0; }
.rc-pk-total { margin: 8px 2px 0; color: var(--ink-soft); font-size: var(--t-xs); }
.rc-pk-foot { margin: 20px 0 0; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.rc-pk-copyall { display: inline-flex; align-items: center; gap: 6px; }
.rc-pk-footnote { flex: 1 1 220px; font-size: var(--t-xs); }
@media (max-width: 640px) { .rc-pk-box { min-width: 0; width: 100%; } .rc-pk-grid { grid-template-columns: 1fr; } }

/* Hospital Rounds — INLINE card-photo viewer (replaces the full-screen rc-lightbox for photos).
   A full-width expand-row drops in under the patient's row; the panel is sticky-left so the photo
   stays visible no matter how far the wide worklist is scrolled sideways. */
.rc-photo-inline > td { padding: 0; background: var(--surface-2); border-bottom: 2px solid var(--line-strong); }
.rc-inline-panel { position: sticky; left: 0; box-sizing: border-box; width: min(620px, 94vw); padding: 12px 14px 14px 42px; white-space: normal; }
.rc-inline-close { position: absolute; top: 8px; left: 8px; width: 26px; height: 26px; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong); background: var(--surface); border-radius: var(--r-full); color: var(--ink-soft); cursor: pointer; }
.rc-inline-close:hover { border-color: var(--primary); color: var(--primary); }
.rc-inline-imgs { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-start; }
.rc-inline-card { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; }
.rc-inline-img { max-width: min(560px, 90vw); max-height: 70vh; width: auto; height: auto; display: block;
  border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-3); cursor: zoom-in; }
.rc-inline-spin { width: 26px; height: 26px; margin: 22px; border: 3px solid var(--line); border-top-color: var(--primary);
  border-radius: 50%; animation: apv-spin 1s linear infinite; }
.rc-photobtn.rc-open { border-color: var(--primary); color: var(--primary); background: var(--primary-softer); }

/* Hospital Rounds — resizable, width-persistent columns.
   table-layout:fixed makes the <colgroup> widths authoritative; each th carries a drag handle on its
   right edge. Widths save into the user's roundsView default (order/sort/filters already do). */
.rounds-table { table-layout: fixed; }
.rounds-table th, .rounds-table td { overflow: hidden; }
.rounds-table .rc-in { min-width: 0; }            /* fixed layout controls width; let inputs fill the column */
.rounds-table .rc-colfilter { width: 100%; box-sizing: border-box; min-width: 0; }
.rc-th { position: relative; }
.rc-th .rc-th-lbl { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rc-th-resize { position: absolute; top: 0; right: 0; width: 8px; height: 100%; cursor: col-resize; z-index: 3; }
.rc-th-resize:hover { background: var(--primary); opacity: .4; }
body.rc-resizing, body.rc-resizing * { cursor: col-resize !important; user-select: none !important; }
.rc-workedby { color: var(--ink-soft); white-space: nowrap; text-overflow: ellipsis; }
/* Keep the inline card-photo row exempt from the clip above, so its sticky-left panel still tracks
   the horizontal scroll instead of being trapped in an overflow:hidden cell. */
.rc-photo-inline > td { overflow: visible; }
/* ---------- CCM (chronic care portal) ---------- */
/* All tokens; dark mode comes free. Every class is .ccm- so the section is self-contained. */

.ccm-wrap { display: flex; flex-direction: column; height: 100%; overflow: auto; padding: var(--s4) var(--s5) var(--s6); }
.ccm-muted { color: var(--muted); font-size: var(--t-sm); }
.ccm-body { display: flex; flex-direction: column; min-height: 0; }

/* -- tab strip + running-timer chip -- */
.ccm-tabs { display: flex; align-items: center; gap: var(--s2); margin: 0 0 var(--s4); flex-wrap: wrap; }
.ccm-tab { display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft); border-radius: var(--r-full); padding: 8px 16px; font-size: var(--t-sm); font-weight: 600; }
.ccm-tab:hover { border-color: var(--primary); color: var(--primary-ink); }
.ccm-tab.is-on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-ink); }
.ccm-timerchip { margin-left: auto; display: inline-flex; align-items: center; gap: 7px; border: 1px solid var(--good); background: var(--surface); color: var(--good); border-radius: var(--r-full); padding: 7px 14px; font-size: var(--t-xs); font-weight: 600; box-shadow: var(--sh-1); }
.ccm-timerchip b { font-variant-numeric: tabular-nums; }

/* -- roster: summary strip, guidance line, call-next rail -- */
.ccm-stats { display: grid; grid-template-columns: repeat(4, minmax(140px, 1fr)); gap: var(--s3); margin: 0 0 var(--s3); }
.ccm-stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: var(--s4) var(--s5); }
.ccm-stat-n { font-size: var(--t-xl); font-weight: 700; letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--ink); }
.ccm-stat-l { color: var(--muted); font-size: var(--t-xs); margin-top: 2px; }
.ccm-stat-good .ccm-stat-n { color: var(--good); }
.ccm-stat-warn .ccm-stat-n { color: var(--warn); }
.ccm-guide { color: var(--muted); font-size: var(--t-sm); margin: 0 0 var(--s4); max-width: 760px; line-height: 1.5; }

.ccm-callnext { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: var(--s4) var(--s5); margin: 0 0 var(--s4); }
.ccm-cn-head { display: flex; align-items: center; gap: var(--s2); font-weight: 700; font-size: var(--t-sm); margin: 0 0 var(--s3); color: var(--ink); }
.ccm-cn-head .ccm-muted { font-weight: 400; }
.ccm-cn-list { display: flex; gap: var(--s3); flex-wrap: wrap; }
.ccm-cn-row { display: flex; align-items: center; gap: var(--s3); border: 1px solid var(--line); background: var(--surface-2); border-radius: var(--r-md); padding: var(--s2) var(--s3); }
.ccm-cn-main { display: flex; flex-direction: column; min-width: 0; }
.ccm-cn-main b { font-size: var(--t-sm); }
.ccm-cn-main .ccm-muted { font-size: var(--t-xs); }
.ccm-cn-open { border: 1px solid var(--primary); color: var(--primary-ink); background: var(--primary-softer); border-radius: var(--r-full); padding: 4px 14px; font-size: var(--t-xs); font-weight: 600; }
.ccm-cn-open:hover { background: var(--primary-soft); }

/* -- filter chips + toolbar -- */
.ccm-toolrow { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin: 0 0 var(--s3); }
.ccm-chips { display: flex; gap: var(--s2); flex-wrap: wrap; flex: 1 1 auto; }
.ccm-chip { border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); border-radius: var(--r-full); padding: 5px 12px; font-size: var(--t-xs); font-weight: 600; display: inline-flex; align-items: center; gap: 6px; }
.ccm-chip:hover { border-color: var(--primary); }
.ccm-chip.is-on { background: var(--primary-softer); border-color: var(--primary); color: var(--primary-ink); }
.ccm-chip-n { background: var(--surface-3); color: var(--muted); border-radius: var(--r-full); padding: 0 7px; font-weight: 700; font-variant-numeric: tabular-nums; }
.ccm-chip.is-on .ccm-chip-n { background: var(--primary-soft); color: var(--primary-ink); }
.ccm-chip-warn { border-color: var(--warn); color: var(--warn); }
/* New arrivals: the same calm accent as a linked chart, so "something came in" reads without shouting. */
.ccm-chip-new { border-color: var(--primary); color: var(--primary-ink); }
.ccm-newtag { display: inline-block; border-radius: var(--r-full); padding: 0 7px; font-size: 10px; font-weight: 700; letter-spacing: .02em; background: var(--primary-softer); color: var(--primary-ink); vertical-align: 1px; }
.ccm-tools { display: flex; gap: var(--s2); }

/* -- shared buttons / inputs -- */
.ccm-btn { display: inline-flex; align-items: center; gap: 6px; border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink-soft); border-radius: var(--r-md); padding: 7px 14px; font-size: var(--t-sm); font-weight: 600; }
.ccm-btn:hover { border-color: var(--primary); color: var(--primary-ink); background: var(--primary-softer); }
.ccm-btn:disabled { opacity: .5; cursor: not-allowed; }
.ccm-btn-primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.ccm-btn-primary:hover { background: var(--primary-hover); border-color: var(--primary-hover); color: var(--on-primary); }
.ccm-btn-good { border-color: var(--good); color: var(--good); }
.ccm-btn-good:hover { background: var(--good); color: #fff; border-color: var(--good); }
.ccm-btn-danger { border-color: var(--danger); color: var(--danger); }
.ccm-btn-danger:hover { background: var(--danger); color: #fff; border-color: var(--danger); }
.ccm-btn-stop { border-color: var(--good); color: var(--good); }
.ccm-btn-quiet { border-color: transparent; background: transparent; color: var(--muted); }
.ccm-btn-quiet:hover { color: var(--ink-soft); background: var(--surface-2); border-color: var(--line); }
.ccm-input { border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: 8px 10px; font-size: var(--t-sm); background: var(--surface); color: var(--ink); }
.ccm-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-softer); }
.ccm-ta { width: 100%; resize: vertical; box-sizing: border-box; }
.ccm-lbl { display: block; margin: 0 0 var(--s3); font-size: var(--t-xs); color: var(--ink-soft); font-weight: 600; }
.ccm-lbl .ccm-input { display: block; width: 100%; box-sizing: border-box; margin-top: 4px; font-weight: 400; }
.ccm-cklbl { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-sm); color: var(--ink-soft); margin: 0 0 var(--s3); }
.ccm-monthsel { border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: 6px 10px; font-size: var(--t-sm); background: var(--surface); color: var(--ink); font-weight: 400; }

/* -- roster table (colgroup + explicit width; scrolls sideways inside .ccm-scroll) -- */
.ccm-scroll { overflow: auto; flex: 1; border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); box-shadow: var(--sh-1); }
.ccm-table { border-collapse: separate; border-spacing: 0; table-layout: fixed; font-size: var(--t-sm); }
.ccm-table th { position: sticky; top: 0; z-index: 2; background: var(--surface-3); color: var(--ink-soft); font-weight: 600; text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line-strong); font-size: var(--t-xs); }
.ccm-table td { padding: 10px 12px; border-bottom: 1px solid var(--line); vertical-align: middle; overflow: hidden; }
.ccm-row { cursor: pointer; }
.ccm-row:hover td { background: var(--primary-softer); }
.ccm-pt-name { font-weight: 600; color: var(--ink); display: flex; align-items: center; gap: 7px; flex-wrap: wrap; }
.ccm-pt-link { cursor: pointer; }
.ccm-pt-link:hover { color: var(--primary-ink); text-decoration: underline; }
.ccm-pt-dob { color: var(--muted); font-size: var(--t-xs); margin-top: 1px; }

/* -- chips used in table cells -- */
.ccm-st { display: inline-block; border-radius: var(--r-full); padding: 1px 9px; font-size: 11px; font-weight: 600; background: var(--surface-3); color: var(--muted); }
.ccm-st-enrolled { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good); }
.ccm-st-consented, .ccm-st-invited { background: var(--primary-softer); color: var(--primary-ink); }
.ccm-st-paused { background: color-mix(in srgb, var(--warn) 14%, transparent); color: var(--warn); }
.ccm-st-declined, .ccm-st-disenrolled { background: var(--danger-soft); color: var(--danger); }
.ccm-icd { display: inline-block; background: var(--surface-3); color: var(--ink-soft); border-radius: var(--r-sm); padding: 1px 7px; font-size: 11px; font-weight: 600; margin: 1px 3px 1px 0; font-variant-numeric: tabular-nums; }
.ccm-icd-more { background: transparent; color: var(--muted); }
.ccm-cchip, .ccm-planchip, .ccm-bchip { display: inline-block; border-radius: var(--r-full); padding: 2px 10px; font-size: 11px; font-weight: 600; }
.ccm-cchip-ok, .ccm-planchip-ok { background: color-mix(in srgb, var(--good) 12%, transparent); color: var(--good); }
.ccm-cchip-none { background: var(--danger-soft); color: var(--danger); }
.ccm-planchip-warn { background: color-mix(in srgb, var(--warn) 14%, transparent); color: var(--warn); }
.ccm-bchip-billed { background: color-mix(in srgb, var(--good) 12%, transparent); color: var(--good); }
.ccm-bchip-open, .ccm-bchip-ready { background: var(--primary-softer); color: var(--primary-ink); }
.ccm-bchip-hold { background: color-mix(in srgb, var(--warn) 14%, transparent); color: var(--warn); }
.ccm-bchip-skipped { background: var(--surface-3); color: var(--muted); }
.ccm-code { display: block; font-weight: 700; color: var(--primary-ink); font-variant-numeric: tabular-nums; }
.ccm-code-est { display: block; color: var(--muted); font-size: var(--t-xs); }

/* -- minutes progress bar with 20/40/60 tick marks -- */
.ccm-mins { display: flex; align-items: center; gap: var(--s2); margin: var(--s2) 0; }
.ccm-mins-tight { margin: 0; }
.ccm-mins-n { flex: 0 0 52px; font-weight: 700; font-variant-numeric: tabular-nums; font-size: var(--t-xs); color: var(--ink-soft); }
.ccm-bar { position: relative; flex: 1 1 auto; height: 8px; background: var(--surface-3); border-radius: var(--r-full); overflow: hidden; min-width: 70px; }
.ccm-bar-fill { height: 100%; background: var(--warn); border-radius: var(--r-full); transition: width var(--dur) var(--ease); }
.ccm-bar-fill.is-goal { background: var(--good); }
.ccm-bar-tick { position: absolute; top: 0; bottom: 0; width: 2px; background: var(--surface); opacity: .9; }

/* -- patient workspace -- */
.ccm-phead { display: flex; align-items: flex-start; gap: var(--s4); margin: 0 0 var(--s4); flex-wrap: wrap; }
.ccm-back { display: inline-flex; align-items: center; gap: 6px; color: var(--muted); font-size: var(--t-sm); font-weight: 600; padding: 6px 10px; border-radius: var(--r-md); }
.ccm-back:hover { color: var(--primary-ink); background: var(--primary-softer); }
.ccm-phead-main { flex: 1 1 auto; min-width: 0; }
.ccm-phead-edit { flex: 0 0 auto; align-self: center; }
.ccm-checkline { display: flex; align-items: center; gap: var(--s2); margin: var(--s3) 0; font-size: var(--t-sm); color: var(--ink); cursor: pointer; }
.ccm-checkline input { width: 16px; height: 16px; accent-color: var(--primary); }
.ccm-phead-name { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-lg); font-weight: 700; letter-spacing: -.01em; color: var(--ink); flex-wrap: wrap; }
.ccm-phead-sub { color: var(--muted); font-size: var(--t-sm); margin-top: 3px; }
/* "Link chart" sits inline with the patient's name, so it stays small and never fights the title. */
.ccm-linkecw-top { padding: 3px 10px; font-size: var(--t-xs); vertical-align: 2px; }
.ccm-ecwtag { display: inline-block; border-radius: var(--r-full); padding: 1px 9px; font-size: 11px; font-weight: 600; background: var(--surface-3); color: var(--muted); }
.ccm-ecwtag.is-linked { background: var(--primary-softer); color: var(--primary-ink); }
.ccm-pgrid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); align-items: start; }
.ccm-col { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.ccm-card { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: var(--s5); }
.ccm-card h3 { margin: 0 0 var(--s4); font-size: var(--t-base); font-weight: 700; display: flex; align-items: center; gap: var(--s2); color: var(--ink); }
.ccm-card h3 svg { width: 17px; height: 17px; color: var(--muted); }
.ccm-card h3 .ccm-monthsel { margin-left: auto; font-weight: 400; }
.ccm-kv { display: flex; align-items: baseline; gap: var(--s3); padding: 5px 0; border-bottom: 1px solid var(--line); font-size: var(--t-sm); flex-wrap: wrap; }
.ccm-kv:last-of-type { border-bottom: none; }
.ccm-kv > span { flex: 0 0 130px; color: var(--muted); font-size: var(--t-xs); }
.ccm-kv > b { color: var(--ink); font-weight: 600; overflow-wrap: anywhere; }

/* -- guidance checklist -- */
.ccm-ready { display: flex; align-items: center; gap: var(--s2); background: color-mix(in srgb, var(--good) 10%, transparent); color: var(--good); border: 1px solid color-mix(in srgb, var(--good) 35%, transparent); border-radius: var(--r-md); padding: var(--s3) var(--s4); font-weight: 600; font-size: var(--t-sm); margin: 0 0 var(--s3); }
.ccm-check-row { display: flex; align-items: baseline; gap: var(--s2); padding: 7px 0; border-bottom: 1px solid var(--line); font-size: var(--t-sm); }
.ccm-check-row:last-of-type { border-bottom: none; }
.ccm-ck-ic { flex: 0 0 auto; display: inline-flex; align-self: center; }
.ccm-ck-ok .ccm-ck-ic { color: var(--good); }
.ccm-ck-warn .ccm-ck-ic { color: var(--warn); }
.ccm-ck-bad .ccm-ck-ic { color: var(--danger); }
.ccm-ck-l { font-weight: 600; color: var(--ink); flex: 0 0 auto; }
.ccm-ck-d { color: var(--muted); font-size: var(--t-xs); min-width: 0; overflow-wrap: anywhere; }
.ccm-blockers { margin-top: var(--s3); background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3) var(--s4); }
.ccm-blockers-h { font-size: var(--t-xs); font-weight: 700; color: var(--ink-soft); margin: 0 0 4px; }
.ccm-blockers ul { margin: 0; padding-left: 18px; color: var(--muted); font-size: var(--t-sm); }
.ccm-blockers li { margin: 3px 0; }

/* -- timer card -- */
.ccm-timer { text-align: center; }
.ccm-timer h3 { justify-content: center; }
.ccm-tick { font-size: 44px; font-weight: 700; letter-spacing: -.01em; font-variant-numeric: tabular-nums; color: var(--muted); margin: var(--s2) 0 var(--s3); }
.ccm-tick.is-live { color: var(--ink); }
.ccm-timer.is-goal .ccm-tick.is-live { color: var(--good); }
.ccm-timer.is-goal { border-color: color-mix(in srgb, var(--good) 40%, var(--line)); }
.ccm-timer-btns { display: flex; justify-content: center; gap: var(--s2); }
.ccm-timer-month { margin-top: var(--s3); font-size: var(--t-sm); color: var(--ink-soft); }
.ccm-timer-hint { color: var(--muted); font-size: var(--t-xs); margin: 4px 0 0; }
.ccm-timer.is-goal .ccm-timer-hint { color: var(--good); }
.ccm-timer-warn { color: var(--warn); font-size: var(--t-xs); margin: var(--s2) 0 0; }
.ccm-stopform { margin-top: var(--s3); display: flex; flex-direction: column; gap: var(--s2); text-align: left; background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3); }
.ccm-stopform-h { font-size: var(--t-xs); font-weight: 700; color: var(--ink-soft); }
.ccm-stopform-btns { display: flex; gap: var(--s2); }

/* -- actions -- */
.ccm-actions { display: flex; gap: var(--s2); flex-wrap: wrap; }
.ccm-actions-status { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }
.ccm-actnote { color: var(--muted); font-size: var(--t-xs); margin: var(--s2) 0 0; line-height: 1.5; }

/* -- time ledger -- */
.ccm-actchips { display: flex; gap: var(--s2); flex-wrap: wrap; margin: var(--s2) 0; }
.ccm-actchip { background: var(--surface-3); color: var(--ink-soft); border-radius: var(--r-full); padding: 2px 10px; font-size: 11px; font-weight: 600; }
.ccm-ledger-scroll { overflow-x: auto; margin: var(--s2) 0; }
.ccm-ledger { width: 100%; border-collapse: collapse; font-size: var(--t-sm); }
.ccm-ledger th { text-align: left; color: var(--muted); font-size: var(--t-xs); font-weight: 600; padding: 6px 8px; border-bottom: 1px solid var(--line-strong); white-space: nowrap; }
.ccm-ledger td { padding: 7px 8px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.ccm-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.ccm-locked-row td { opacity: .72; }
.ccm-src { display: inline-flex; align-items: center; background: var(--surface-3); color: var(--muted); border-radius: var(--r-full); padding: 1px 8px; font-size: 11px; font-weight: 600; }
.ccm-src-call { background: var(--primary-softer); color: var(--primary-ink); }
.ccm-src-timer { background: color-mix(in srgb, var(--good) 12%, transparent); color: var(--good); }
.ccm-note-cell { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
.ccm-del { color: var(--muted); padding: 4px; border-radius: var(--r-sm); display: inline-flex; }
.ccm-del:hover { color: var(--danger); background: var(--danger-soft); }
.ccm-addrow { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; margin-top: var(--s3); padding-top: var(--s3); border-top: 1px dashed var(--line-strong); }
.ccm-input-min { width: 74px; }
.ccm-input-note { flex: 1 1 160px; min-width: 120px; }
.ccm-bhist-row { margin-top: var(--s3); display: flex; gap: var(--s2); flex-wrap: wrap; align-items: baseline; font-size: var(--t-xs); }
.ccm-bhist { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-full); padding: 2px 10px; color: var(--ink-soft); }

/* -- care plan editor -- */
.ccm-planmeta { color: var(--muted); font-size: var(--t-xs); margin: calc(-1 * var(--s2)) 0 var(--s3); }
.ccm-planmeta-warn { color: var(--warn); }
.ccm-plan-sec { margin: 0 0 var(--s4); }
.ccm-plan-h { font-size: var(--t-xs); font-weight: 700; color: var(--ink); text-transform: uppercase; letter-spacing: .03em; margin: 0 0 var(--s2); }
.ccm-pchips { display: flex; flex-wrap: wrap; gap: var(--s2); margin: 0 0 var(--s2); }
.ccm-pchip { display: inline-flex; align-items: center; gap: 5px; background: var(--surface-2); border: 1px solid var(--line-strong); color: var(--ink-soft); border-radius: var(--r-full); padding: 3px 6px 3px 11px; font-size: var(--t-xs); font-weight: 600; }
.ccm-pchip-x { display: inline-flex; color: var(--muted); padding: 1px; border-radius: var(--r-full); }
.ccm-pchip-x:hover { color: var(--danger); }
.ccm-plan-addrow { display: flex; gap: var(--s2); flex-wrap: wrap; }
.ccm-plan-addrow .ccm-input { flex: 1 1 180px; min-width: 140px; }
.ccm-listrow { display: flex; gap: var(--s2); margin: 0 0 var(--s2); align-items: center; }
.ccm-listrow .ccm-goal-in, .ccm-listrow .ccm-int-in { flex: 1 1 auto; min-width: 0; }
.ccm-listrow .ccm-int-who { flex: 0 0 110px; }
.ccm-plan-foot { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; margin-top: var(--s4); padding-top: var(--s3); border-top: 1px solid var(--line); }
.ccm-copygiven { display: inline-flex; gap: var(--s1); align-items: center; }

/* -- consent + signature -- */
.ccm-sigwrap { margin-top: var(--s3); background: #fff; border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: var(--s2); }
.ccm-sig { display: block; max-width: 100%; max-height: 110px; }
.ccm-script { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3) var(--s4); margin: 0 0 var(--s4); }
.ccm-script-h { font-size: var(--t-xs); font-weight: 700; color: var(--ink-soft); margin: 0 0 4px; }
.ccm-script ul { margin: 0; padding-left: 18px; color: var(--ink-soft); font-size: var(--t-sm); line-height: 1.5; }
.ccm-script li { margin: 4px 0; }

/* -- eCW snapshot -- */
.ccm-condlist { margin: 0 0 var(--s3); max-height: 190px; overflow: auto; }
.ccm-cond { padding: 4px 0; border-bottom: 1px solid var(--line); font-size: var(--t-sm); color: var(--ink-soft); }
.ccm-cond:last-child { border-bottom: none; }
.ccm-cond b { color: var(--ink); font-variant-numeric: tabular-nums; }
/* Active medications pulled from eCW, shown under the problem list on the chart card. */
.ccm-medhead { font-size: var(--t-xs); font-weight: 600; color: var(--ink); margin: var(--s2) 0 2px; }
.ccm-medlist { margin: 0 0 var(--s3); max-height: 190px; overflow: auto; }
.ccm-med { padding: 4px 0; border-bottom: 1px solid var(--line); font-size: var(--t-sm); color: var(--ink-soft); }
.ccm-med:last-child { border-bottom: none; }
.ccm-med b { color: var(--ink); }

/* -- add-patient modal search results -- */
.ccm-ecwresults { display: flex; flex-direction: column; gap: var(--s2); margin: var(--s3) 0; max-height: 260px; overflow: auto; }
.ccm-cand { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; text-align: left; border: 1px solid var(--line-strong); background: var(--surface-2); border-radius: var(--r-md); padding: var(--s2) var(--s3); }
.ccm-cand:hover { border-color: var(--primary); background: var(--primary-softer); }
.ccm-cand span { color: var(--muted); font-size: var(--t-xs); }
.ccm-manual { margin-top: var(--s3); border-top: 1px solid var(--line); padding-top: var(--s3); }
.ccm-manual summary { cursor: pointer; color: var(--muted); font-size: var(--t-sm); font-weight: 600; margin-bottom: var(--s3); }
/* CCM Add/Link modals: the shared 3-box eCW search (Last name / First name / Date of birth). */
.ccm-ecwfields { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.ccm-ecwfields .ccm-ecwdob { grid-column: 1 / -1; }
.ccm-ecwfields .ccm-input { width: 100%; box-sizing: border-box; }

/* -- billing tab -- */
.ccm-billhead { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin: 0 0 var(--s2); }
.ccm-billhead-l { display: flex; align-items: center; gap: var(--s3); flex: 1 1 auto; flex-wrap: wrap; }
.ccm-billtable .ccm-brow.is-billed td { background: color-mix(in srgb, var(--good) 4%, transparent); }
.ccm-mcks { white-space: nowrap; }
.ccm-mck { display: inline-flex; margin-right: 5px; vertical-align: middle; }
.ccm-mck-ok { color: var(--good); }
.ccm-mck-warn { color: var(--warn); }
.ccm-mck-bad { color: var(--danger); }
.ccm-bsel { border: 1px solid var(--line-strong); border-radius: var(--r-md); padding: 5px 8px; font-size: var(--t-xs); background: var(--surface); color: var(--ink); }
.ccm-billmeta { color: var(--muted); font-size: 11px; margin-top: 3px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ccm-pkbtn { color: var(--muted); padding: 6px; border-radius: var(--r-md); display: inline-flex; border: 1px solid transparent; }
.ccm-pkbtn:hover { color: var(--primary-ink); background: var(--primary-softer); border-color: var(--primary); }

/* -- modal overlay (rounds lightbox idiom) -- */
.ccm-overlay { position: fixed; inset: 0; z-index: 200; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; padding: 20px; }
.ccm-box { position: relative; background: var(--surface); border-radius: var(--r-lg); width: 480px; max-width: 94vw; max-height: 90vh; overflow: auto; padding: var(--s5); box-shadow: var(--sh-3); }
.ccm-box h3 { display: flex; align-items: center; gap: var(--s2); margin: 0 0 var(--s3); font-size: var(--t-md); color: var(--ink); padding-right: 34px; }
.ccm-box h3 svg { width: 18px; height: 18px; color: var(--muted); }
.ccm-x { position: absolute; top: 12px; right: 12px; color: var(--muted); padding: 6px; border-radius: var(--r-full); display: inline-flex; }
.ccm-x:hover { color: var(--ink); background: var(--surface-2); }
.ccm-modalfoot { display: flex; justify-content: flex-end; gap: var(--s2); margin-top: var(--s4); }

/* -- charge packet (rc-pk idiom, .ccm- names) -- */
.ccm-pk-box { width: 620px; }
.ccm-pk-head { margin: 0 0 var(--s4); }
.ccm-pk-head h3 { margin: 0 0 2px; }
.ccm-pk-sub { color: var(--muted); font-size: var(--t-xs); }
.ccm-pk-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px 20px; }
.ccm-pk-fld { display: flex; align-items: baseline; gap: 8px; padding: 7px 0; border-bottom: 1px solid var(--line); min-width: 0; }
.ccm-pk-lbl { flex: 0 0 42%; color: var(--ink-soft); font-size: var(--t-xs); }
.ccm-pk-hint { display: block; color: var(--muted); font-size: 10px; font-weight: 400; }
.ccm-pk-val { flex: 1 1 auto; color: var(--ink); font-weight: 600; font-size: var(--t-sm); overflow-wrap: anywhere; }
.ccm-pk-val.ccm-pk-empty { color: var(--muted); font-weight: 400; }
.ccm-pk-copy { display: inline-flex; align-items: center; gap: 4px; flex: 0 0 auto; background: var(--surface-2); border: 1px solid var(--line-strong); color: var(--ink-soft); border-radius: var(--r-md); padding: 3px 8px; font-size: 11px; font-weight: 600; }
.ccm-pk-copy:hover { border-color: var(--primary); color: var(--primary); background: var(--primary-softer); }
.ccm-pk-copy.ccm-pk-copied { border-color: var(--good); color: var(--good); }
.ccm-pk-sec { margin: var(--s4) 0 0; }
.ccm-pk-sech { font-size: var(--t-xs); font-weight: 700; color: var(--ink); text-transform: uppercase; letter-spacing: .03em; margin: 0 0 var(--s2); }
.ccm-pk-sech .ccm-pk-hint { display: inline; text-transform: none; letter-spacing: 0; margin-left: 8px; }
.ccm-pk-cpts { border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
.ccm-pk-cptline { display: flex; align-items: center; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--line); }
.ccm-pk-cptline:last-child { border-bottom: none; }
.ccm-pk-cpt { flex: 0 0 auto; font-weight: 700; color: var(--primary-ink); font-variant-numeric: tabular-nums; }
.ccm-pk-cptu { flex: 0 0 auto; color: var(--ink-soft); font-size: var(--t-xs); font-variant-numeric: tabular-nums; }
.ccm-pk-cptlbl { flex: 1 1 auto; color: var(--ink); font-size: var(--t-xs); }
.ccm-pk-cptpos { flex: 0 0 auto; color: var(--muted); font-size: 10px; }
.ccm-pk-foot { margin: var(--s5) 0 0; display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.ccm-pk-footnote { flex: 1 1 220px; font-size: var(--t-xs); }

/* -- small screens: stack the patient view, let tables scroll sideways -- */
@media (max-width: 760px) {
  .ccm-wrap { padding: var(--s3); }
  .ccm-stats { grid-template-columns: repeat(2, minmax(120px, 1fr)); }
  .ccm-pgrid { grid-template-columns: 1fr; }
  .ccm-cn-list { flex-direction: column; }
  .ccm-cn-row { justify-content: space-between; }
  .ccm-scroll { -webkit-overflow-scrolling: touch; }
  .ccm-tick { font-size: 38px; }
  .ccm-timerchip { margin-left: 0; }
  .ccm-pk-grid { grid-template-columns: 1fr; }
  .ccm-kv > span { flex-basis: 108px; }
  .ccm-listrow { flex-wrap: wrap; }
  .ccm-listrow .ccm-int-who { flex: 1 1 100%; }
  .ccm-addrow .ccm-input-note { flex-basis: 100%; }
  .ccm-box { padding: var(--s4); }
}
.ccm-cchip-link { cursor: pointer; border-bottom: 1px dotted currentColor; }
.ccm-cchip-link:hover { filter: brightness(.92); }

/* ---- Website Forms archive (Forms tab) ------------------------------------- */
.forms-head { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s3); }
.forms-tabs { display: flex; gap: 2px; background: var(--surface-2); border-radius: var(--r-full); padding: 3px; }
.forms-tab { border: 0; background: transparent; padding: 6px 14px; border-radius: var(--r-full); cursor: pointer; font-weight: 600; font-size: var(--t-sm); color: var(--muted); transition: background var(--dur) var(--ease); }
.forms-tab.active { background: var(--surface); color: var(--text); box-shadow: var(--sh-1); }
.forms-filters { display: flex; gap: var(--s2); flex-wrap: wrap; align-items: center; margin-bottom: var(--s3); }
.forms-filters .field-input { max-width: 260px; }
.forms-table-wrap { overflow-x: auto; }
.forms-note { color: var(--muted); font-size: var(--t-xs); }
/* A resume whose HR email was deleted (or already erased). Same small type as .forms-note, but it
   reads as a warning rather than a caption — this is a file that is on its way to being gone. */
.forms-warn { color: var(--warn-ink, var(--danger, #b45309)); font-weight: 600; }
.forms-bulk { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; padding: var(--s2) 0 var(--s3); }
.chip-mode-new { background: var(--primary-soft); color: var(--primary-ink); }
.chip-mode-ret { background: var(--surface-3); color: var(--muted); }

/* ---- Careers: one row per applicant, expanding to their whole hiring journey ----
   The summary row is clickable; the row beneath it holds the journey and stays hidden until
   asked for. Everything is drawn from theme tokens so light and dark need no separate block. */
.forms-appl { cursor: pointer; }
.forms-appl:hover { background: var(--surface-2); }
.forms-caret { display: inline-flex; vertical-align: -2px; margin-right: 6px; color: var(--muted);
  transition: transform var(--dur) var(--ease); }
.forms-caret.open { transform: rotate(90deg); }
.forms-detail > td { background: var(--surface-2); }
/* A vertical thread down the steps, so the journey reads as a sequence rather than stacked cards. */
.forms-journey { display: grid; gap: var(--s3); padding: var(--s3) 0 var(--s3) 24px; }
.forms-step { border-left: 2px solid var(--line); padding-left: var(--s3); }
/* Dashed = a step that has NOT happened yet (no full application on file). */
.forms-step-todo { border-left-style: dashed; }
.forms-step-h { display: flex; align-items: baseline; gap: var(--s2); flex-wrap: wrap;
  font-weight: 700; font-size: var(--t-sm); margin-bottom: var(--s2); }
.forms-kv { display: grid; grid-template-columns: max-content 1fr; gap: 2px var(--s3);
  margin: 0 0 var(--s2); font-size: var(--t-sm); }
.forms-kv dt { color: var(--muted); }
.forms-kv dd { margin: 0; }
.forms-step-files { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
/* Infinite-scroll sentinel + running count. Quiet on purpose — it is a progress marker, not a
   control, and it also states the total so a long list never feels bottomless. */
.forms-more { padding: var(--s3) 0; text-align: center; color: var(--muted); font-size: var(--t-xs); }

/* ---- Split view: the list on the left, the document beside it ------------------
   Same idea as the invoice reader — you work down a column of submissions without a new browser
   tab per person. The pane only exists once a document is open, so a wide screen isn't permanently
   giving up half its width to nothing. Below 1100px there is no room for two columns, so the pane
   is never shown and the code falls back to opening the file in a tab. */
.forms-split { display: block; }
.forms-doc-pane { display: none; }
@media (min-width: 1100px) {
  .forms-split.open { display: grid; grid-template-columns: minmax(0, 1fr) minmax(420px, 44%); gap: var(--s4); align-items: start; }
  .forms-split.open .forms-doc-pane { display: flex; flex-direction: column; position: sticky; top: var(--s3);
    height: calc(100vh - 190px); min-height: 420px;
    border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface-2); overflow: hidden; }
}
.forms-doc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s2);
  padding: var(--s3); border-bottom: 1px solid var(--line); background: var(--surface); }
.forms-doc-title { font-weight: 600; font-size: var(--t-sm); min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.forms-doc-acts { display: flex; align-items: center; gap: var(--s2); flex-shrink: 0; }
.forms-doc-body { flex: 1; min-height: 0; background: var(--surface-3); }
.forms-doc-frame { width: 100%; height: 100%; border: 0; display: block; }
/* A contact message is free text and can be long — keep it readable without letting one row push
   the table into a horizontal scroll. */
.forms-msg { max-width: 460px; white-space: pre-wrap; word-break: break-word; font-size: var(--t-sm); }

/* Permanent-delete dialog. The list of what is about to be destroyed is the point of the screen,
   so it gets real weight; the warning box is the one loud element on an otherwise calm surface. */
.forms-del-list { margin: 0 0 var(--s3); padding-left: var(--s5); font-size: var(--t-sm); }
.forms-del-list li { margin-bottom: 4px; }
.forms-del-warn { padding: var(--s3); border-radius: var(--r-md); font-size: var(--t-sm);
  color: var(--danger); background: var(--danger-soft, color-mix(in srgb, var(--danger) 10%, var(--surface)));
  border: 1px solid color-mix(in srgb, var(--danger) 35%, var(--line)); }
/* Sits under the journey steps, separated so it reads as an action ON the whole applicant. */
.forms-journey-foot { padding-top: var(--s2); border-top: 1px dashed var(--line); margin-top: var(--s2); }

/* Interview score. Text stars (★ ☆), never emoji — same characters the HR interview PDF prints. */
.forms-stars { color: var(--gold-ink, var(--warn)); letter-spacing: 1px; font-size: var(--t-sm); white-space: nowrap; }
.forms-stars-off { color: var(--line); }
/* Narrow screens: the label/value grid stacks instead of squeezing the value into a sliver. */
@media (max-width: 700px) {
  .forms-kv { grid-template-columns: 1fr; gap: 0 0; }
  .forms-kv dt { margin-top: var(--s2); }
}

/* ---- Performance coaching notices (must-acknowledge popups) ------------------ */
/* Calm and unmissable, not arcade: a dimmed sheet, one centered card, a single
   acknowledge action. Positive notices lead with the primary sage; negative ones
   with the muted danger red. Sits ABOVE every other layer (modals top out ~1000). */
.coach-overlay {
  position: fixed; inset: 0; z-index: 1600;
  display: flex; align-items: center; justify-content: center;
  background: rgba(23, 37, 31, .45); backdrop-filter: blur(3px);
  padding: var(--s4);
  animation: coachFade .22s var(--ease);
}
@keyframes coachFade { from { opacity: 0; } to { opacity: 1; } }
.coach-card {
  width: min(460px, 94vw); max-height: 86vh; overflow-y: auto;
  background: var(--surface); border-radius: var(--r-lg);
  border: 1px solid var(--line); border-top: 4px solid var(--primary);
  box-shadow: 0 24px 64px rgba(0, 0, 0, .28);
  padding: var(--s5) var(--s5) var(--s4);
  animation: coachRise .26s var(--ease);
}
@keyframes coachRise { from { opacity: 0; transform: translateY(14px) scale(.98); } to { opacity: 1; transform: none; } }
.coach-card.coach-neg { border-top-color: var(--danger); }
.coach-eyebrow {
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
  color: var(--primary-ink); margin-bottom: var(--s2);
}
.coach-neg .coach-eyebrow { color: var(--danger); }
.coach-title { margin: 0 0 var(--s2); font-size: var(--t-lg); line-height: 1.3; color: var(--ink); }
.coach-coins {
  display: inline-block; font-weight: 700; font-size: var(--t-md);
  border-radius: var(--r-full); padding: 4px 14px; margin-bottom: var(--s3);
  font-variant-numeric: tabular-nums;
}
.coach-coins.up   { background: var(--gold-soft);   color: var(--gold-ink); }
.coach-coins.down { background: var(--danger-soft); color: var(--danger); }
.coach-body { color: var(--ink-soft); font-size: var(--t-base); line-height: 1.6; }
.coach-body p { margin: 0 0 var(--s3); }
.coach-actions { display: flex; justify-content: flex-end; padding-top: var(--s2); }
.coach-overlay.coach-leaving { opacity: 0; transition: opacity .18s var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .coach-overlay, .coach-card { animation: none; }
}

/* ---- Supply catalogs (owner console: discover vendors -> build items -> map -> publish) ---- */
.sup-wrap { padding: var(--s4); }
.sup-head { display: flex; align-items: flex-start; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s4); }
.sup-title { font-size: var(--t-lg); font-weight: 650; color: var(--ink); }
.sup-sub { color: var(--muted); font-size: var(--t-sm); margin-top: 2px; }
.sup-actions { margin-left: auto; display: flex; gap: var(--s2); align-items: center; }
.sup-live { color: var(--primary-ink); }
.sup-spacer { flex: 1; }

.sup-filters { display: flex; gap: 2px; background: var(--surface-2); border-radius: var(--r-full);
  padding: 3px; margin-bottom: var(--s3); width: fit-content; flex-wrap: wrap; }
.sup-chip { border: 0; background: transparent; padding: 6px 14px; border-radius: var(--r-full);
  cursor: pointer; font-weight: 600; font-size: var(--t-sm); color: var(--muted);
  transition: background var(--dur) var(--ease); }
.sup-chip.is-on { background: var(--surface); color: var(--ink); box-shadow: var(--sh-1); }
.sup-count { color: var(--muted); font-weight: 500; }
.sup-chip.is-on .sup-count { color: var(--primary-ink); }

.sup-note { color: var(--muted); font-size: var(--t-sm); line-height: 1.55; max-width: 70ch;
  margin-bottom: var(--s3); }
.sup-empty { color: var(--muted); font-size: var(--t-sm); padding: var(--s4); text-align: center; }

/* Wide tables scroll inside their own box so the page itself never scrolls sideways. */
.sup-tablewrap { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-md);
  background: var(--surface); }
.sup-table { min-width: 780px; }
/* DRAG-TO-RESIZE COLUMNS.
   Item numbers and long product names were being cut off with no way to see the rest. A grip on
   each heading's right edge widens that column; the width is remembered per table so it survives
   the redraws that follow every edit. `table-layout: fixed` is what makes a set width actually
   hold — without it the browser re-balances every column on the next render and the drag appears
   to do nothing. */
.sup-table.is-sized { table-layout: fixed; }
.sup-table th { position: relative; }
.sup-grip { position: absolute; top: 0; right: 0; width: 9px; height: 100%; cursor: col-resize;
  user-select: none; touch-action: none; }
.sup-grip::after { content: ''; position: absolute; top: 20%; right: 4px; height: 60%;
  border-right: 2px solid transparent; }
.sup-grip:hover::after, .sup-grip.is-drag::after { border-right-color: var(--primary); }
.sup-table.is-sized th, .sup-table.is-sized td { overflow: hidden; text-overflow: ellipsis; }
/* A resized column must let its input use the full width it was given. */
.sup-table.is-sized td input.sup-in { width: 100%; max-width: 100%; box-sizing: border-box; }
body.col-resizing { cursor: col-resize; user-select: none; }
.sup-table th { white-space: nowrap; }
.sup-table td { vertical-align: middle; }

/* CLICK TO SORT, DRAG TO MOVE — the item table's headings.
   The arrow appears on the sorted column alone. A permanent up/down pair on every heading makes all
   eleven look sorted and leaves the one that IS sorted nothing to distinguish it. The drop marker is
   an inset shadow rather than a border, because a border would widen the heading mid-drag and shove
   every column sideways under the pointer; it also has to re-state the header's own bottom line,
   since one box-shadow replaces another. */
.sup-th { cursor: pointer; user-select: none; }
.sup-th:hover { background: var(--surface-2); }
.sup-th.is-sorted { color: var(--ink); }
.sup-sortmark { margin-left: 5px; color: var(--primary); font-size: var(--t-xs); }
.sup-th.is-dragcol { opacity: .4; }
.sup-th.is-dropbefore { box-shadow: inset 2px 0 0 var(--primary), inset 0 -1px 0 var(--line); }
.sup-th.is-dropafter { box-shadow: inset -2px 0 0 var(--primary), inset 0 -1px 0 var(--line); }
/* The resize grip lives in the same heading and must not read as part of the drag handle. */
.sup-th .sup-grip { cursor: col-resize; }
.sup-cb { width: 34px; }
.sup-vname { font-weight: 600; color: var(--ink); }
.sup-aka { color: var(--muted); font-size: var(--t-xs); margin-top: 2px; }
.sup-src { color: var(--muted); font-size: var(--t-xs); }
/* Why a site is on the ship-to list when its company was never ticked. Quiet, but present, so the
   list never looks like it invented a row. */
.sup-why { font-style: italic; opacity: .85; }
.sup-check-lbl { display: inline-flex; align-items: center; gap: 8px; font-size: var(--t-sm);
  color: var(--muted); cursor: pointer; }
.sup-check-lbl input { width: 17px; height: 17px; cursor: pointer; }
/* Line over the ship-to table saying how many sites are set up for this vendor. */
.sup-acct-bar { display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  flex-wrap: wrap; margin: 0 0 var(--s2); }
.sup-shipcol { width: 60px; text-align: center; }
.sup-shipbox { width: 19px; height: 19px; cursor: pointer; }
/* A site this vendor does not serve stays legible — it is a row you may want to switch ON, not a
   disabled control. Only the identifying text dims. */
.sup-site-off td:not(.sup-shipcol) { opacity: .55; }
/* A found account that has been put onto a site: still listed, plainly finished. */
.sup-acct-done td { opacity: .62; }
.sup-done-tag { font-size: var(--t-xs); font-weight: 700; color: var(--ok, var(--primary-ink));
  background: color-mix(in srgb, var(--ok, var(--primary)) 14%, transparent);
  border-radius: var(--r-sm); padding: 1px 6px; }
.sup-link-quiet { color: var(--muted); }
.sup-ignored { margin: var(--s2) 0; }
/* The live icon beside a department's icon picker — same colour as body text so it reads as the
   real thing rather than as decoration. */
.org-icon-preview { display: inline-flex; align-items: center; color: var(--ink); }
.org-icon-preview .icon { width: 20px; height: 20px; }
/* This vendor's own ordering rules, one row per company. The approver cell holds a picker button,
   so the rows need more height than a plain text table. */
.sup-vrules td { vertical-align: middle; }
.sup-vrules .sup-pickrow { margin: 0; }
.sup-fallbackpick { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }
.sup-seen { color: var(--muted); font-size: var(--t-xs); white-space: nowrap; }
/* Not bought in over a year — the rows worth questioning before they go on an order form. Amber
   rather than red: it is a prompt to look, not an error. */
.sup-stale { color: var(--warn, #a86400); font-weight: 600; }
/* Per-unit price movement on a mined row. The TRAP case — pack price down, unit price up — is the
   one a human reading totals waves through, so it is the one that shouts. */
.sup-priceup   { color: var(--warn, #a86400); font-weight: 600; }
.sup-pricedown { color: var(--good, #1a7f4b); font-weight: 600; }
.sup-pricetrap { color: var(--bad, #b3261e); font-weight: 700;
  background: color-mix(in srgb, var(--bad, #b3261e) 8%, transparent);
  border-radius: var(--r-sm); padding: 2px 6px; margin-top: 3px; }
.sup-rowact { white-space: nowrap; text-align: right; }
.sup-rowact .btn { margin-left: var(--s1); }
/* Vendors that look like rent/payroll/utilities: still listed, just visually de-emphasised. */
.sup-unlikely td { opacity: .62; }

.sup-tabs { display: flex; gap: 2px; background: var(--surface-2); border-radius: var(--r-full);
  padding: 3px; width: fit-content; margin-bottom: var(--s3); }
.sup-tab { border: 0; background: transparent; padding: 6px 16px; border-radius: var(--r-full);
  cursor: pointer; font-weight: 600; font-size: var(--t-sm); color: var(--muted); }
.sup-tab.is-on { background: var(--surface); color: var(--ink); box-shadow: var(--sh-1); }

.sup-bar { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; margin: var(--s3) 0; }
.sup-selinfo { color: var(--muted); font-size: var(--t-sm); }
.sup-search { padding: 7px 12px; border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface); color: var(--ink); font-size: var(--t-sm); min-width: 220px; }

/* Inline editors: quiet until touched, so a 90-row catalog reads as a table and not as a form. */
.sup-in { width: 100%; border: 1px solid transparent; background: transparent; color: var(--ink);
  border-radius: var(--r-sm); padding: 5px 7px; font-size: var(--t-sm); font-family: inherit; }
.sup-in:hover { border-color: var(--line); }
.sup-in:focus { border-color: var(--primary); background: var(--surface); outline: none; }
.sup-in-desc { min-width: 240px; }
.sup-in-sm { min-width: 90px; }
.sup-in-xs { min-width: 62px; }
.sup-price { text-align: right; font-variant-numeric: tabular-nums; }
.sup-who { font-size: var(--t-xs); color: var(--ink-soft); }
.sup-who-sep { color: var(--muted); margin: 0 6px; }
.sup-nobody { font-size: var(--t-xs); color: var(--muted); font-style: italic; }
/* Half-mapped: the save landed, the gate is still shut. Reads as progress, not as an error. */
.sup-halfset { font-size: var(--t-xs); color: var(--ink-soft); }
.sup-halfset-tag { display: inline-block; margin-left: var(--s2); font-size: var(--t-xs);
  color: var(--warn); background: color-mix(in srgb, var(--warn) 12%, transparent);
  border-radius: var(--r-full); padding: 1px 8px; white-space: nowrap; }
.sup-mapgate-warn { color: var(--warn); font-weight: 600; }
/* Look-alike rows the item number holds apart — shown so the refusal to merge has a reason. */
.sup-dupepair { font-variant-numeric: tabular-nums; margin-top: 2px; }

/* Mined rows awaiting a human. Tinted so it is obvious nothing here is live yet. */
.sup-staged { border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--primary-softer); padding: var(--s3); margin-bottom: var(--s4); }
.sup-staged-head { display: flex; gap: var(--s3); align-items: flex-start; flex-wrap: wrap;
  margin-bottom: var(--s3); }
.sup-staged-head > div:last-child { margin-left: auto; display: flex; gap: var(--s2); }
.sup-staged-tbl { max-height: 420px; overflow-y: auto; }
.sup-srctag { font-size: var(--t-xs); color: var(--muted); white-space: nowrap; }
.sup-tag { display: inline-block; margin-left: var(--s2); font-size: var(--t-xs);
  color: var(--primary-ink); background: var(--primary-soft); border-radius: var(--r-full);
  padding: 1px 8px; }

.sup-mappanel { border: 1px solid var(--line-strong); border-radius: var(--r-md);
  background: var(--surface); padding: var(--s3); margin-bottom: var(--s3); }
.sup-mapcols { display: grid; grid-template-columns: minmax(200px, 1fr) minmax(260px, 2fr);
  gap: var(--s4); }
.sup-maphead { font-weight: 600; font-size: var(--t-sm); color: var(--ink); margin-bottom: var(--s2); }
.sup-jobgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 2px; }
.sup-check { display: flex; align-items: center; gap: 8px; font-size: var(--t-sm);
  color: var(--ink-soft); padding: 4px 0; cursor: pointer; }
.sup-mapfoot { display: flex; align-items: center; gap: var(--s2); margin-top: var(--s3);
  padding-top: var(--s3); border-top: 1px solid var(--line); flex-wrap: wrap; }

.sup-setup { max-width: 620px; }
.sup-field, .sup-fieldwide { display: block; margin-bottom: var(--s4); }
.sup-flabel { display: block; font-weight: 600; font-size: var(--t-sm); color: var(--ink);
  margin-bottom: 4px; }
.sup-fin { width: 100%; padding: 8px 12px; border: 1px solid var(--line-strong);
  border-radius: var(--r-md); background: var(--surface); color: var(--ink); font-size: var(--t-base);
  font-family: inherit; }
.sup-fin:focus { border-color: var(--primary); outline: none; }
.sup-fhint { display: block; color: var(--muted); font-size: var(--t-xs); margin-top: 4px;
  line-height: 1.5; }
.sup-acct { min-width: 0; }

@media (max-width: 760px) {
  .sup-wrap { padding: var(--s3); }
  .sup-mapcols { grid-template-columns: 1fr; }
  .sup-actions { margin-left: 0; width: 100%; }
}

/* ==============================================================================================
   CCM LIVE CALL COACHING
   The full-screen panel a care manager works from while a chronic-care patient is on the line:
   the transcript writing itself on the left, the talking points ticking green on the right.
   Deliberately calm — a call is stressful enough. Nothing flashes, nothing bounces; the only
   motion is a slow pulse on the "we are listening" dot and a gentle fade as points complete.
   ============================================================================================== */
body.ccl-open { overflow: hidden; }

.ccl { position: fixed; inset: 0; z-index: 900; display: flex; flex-direction: column;
  background: var(--bg); animation: cclIn .22s ease; }
@keyframes cclIn { from { opacity: 0; transform: scale(.995); } to { opacity: 1; transform: none; } }

/* -- header ---------------------------------------------------------------------------------- */
.ccl-top { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  padding: var(--s4) var(--s5); background: var(--surface); border-bottom: 1px solid var(--line); box-shadow: var(--sh-1); }
.ccl-who { display: flex; align-items: center; gap: var(--s3); min-width: 0; flex: 1 1 auto; }
.ccl-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--muted); flex: none; }
.ccl-dot.is-live { background: var(--good); animation: cclPulse 2s infinite; }
@keyframes cclPulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--good) 45%, transparent); }
  70%  { box-shadow: 0 0 0 8px color-mix(in srgb, var(--good) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--good) 0%, transparent); }
}
.ccl-name { font-size: var(--t-md); font-weight: 700; letter-spacing: -.01em; color: var(--ink); }
.ccl-sub { font-size: var(--t-xs); color: var(--muted); margin-top: 2px; }
.ccl-clock { font-size: 30px; font-weight: 700; font-variant-numeric: tabular-nums;
  letter-spacing: -.02em; color: var(--ink); flex: none; }
.ccl-month { font-size: var(--t-sm); color: var(--ink-soft); flex: none; padding-left: var(--s4);
  border-left: 1px solid var(--line); }
.ccl-month b { font-size: var(--t-md); font-variant-numeric: tabular-nums; }
.ccl-month.is-goal { color: var(--good); }
.ccl-end { flex: none; display: inline-flex; align-items: center; gap: 7px; border: 0;
  background: var(--danger); color: #fff; border-radius: var(--r-md); padding: 11px 18px;
  font-size: var(--t-base); font-weight: 700; cursor: pointer; box-shadow: var(--sh-1); }
.ccl-end:hover { filter: brightness(1.06); }
.ccl-end svg { width: 16px; height: 16px; }

/* -- two columns ------------------------------------------------------------------------------ */
.ccl-body { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) 400px; gap: 0; min-height: 0; }
.ccl-script { display: flex; flex-direction: column; min-height: 0; min-width: 0; padding: var(--s5); }
.ccl-script-h, .ccl-points-h { display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); margin-bottom: var(--s3); }
.ccl-script-h svg, .ccl-points-h svg { width: 15px; height: 15px; }
.ccl-points-h { justify-content: space-between; }
.ccl-points-h > span:first-child { display: inline-flex; align-items: center; gap: var(--s2); }
.ccl-count { font-variant-numeric: tabular-nums; letter-spacing: 0; text-transform: none; }

/* -- transcript ------------------------------------------------------------------------------- */
.ccl-lines { flex: 1; overflow-y: auto; min-height: 0; padding-right: var(--s3);
  scroll-behavior: smooth; }
.ccl-line { display: grid; grid-template-columns: 78px minmax(0, 1fr); gap: var(--s3);
  padding: 9px 0; font-size: var(--t-md); line-height: 1.55; animation: cclLine .18s ease; }
@keyframes cclLine { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
.ccl-sp { font-size: var(--t-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); padding-top: 5px; }
.ccl-patient .ccl-sp { color: var(--primary); }
.ccl-tx { color: var(--ink); overflow-wrap: anywhere; }
.ccl-patient .ccl-tx { font-weight: 500; }
.ccl-line.is-partial .ccl-tx { color: var(--muted); font-style: italic; }
.ccl-empty { color: var(--muted); font-size: var(--t-sm); margin: 0; }

/* -- coaching column -------------------------------------------------------------------------- */
.ccl-coach { display: flex; flex-direction: column; min-height: 0; overflow-y: auto;
  padding: var(--s5); background: var(--surface); border-left: 1px solid var(--line); }
.ccl-nudge, .ccl-say { display: flex; flex-direction: column; gap: 5px; border-radius: var(--r-md);
  padding: var(--s3) var(--s4); margin-bottom: var(--s3); font-size: var(--t-sm); line-height: 1.5;
  animation: cclLine .2s ease; }
.ccl-nudge { background: color-mix(in srgb, var(--warn) 12%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warn) 34%, var(--line)); color: var(--ink); }
.ccl-nudge-k, .ccl-say-k { display: inline-flex; align-items: center; gap: 6px; font-size: 11px;
  font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--warn); }
.ccl-nudge-k svg { width: 13px; height: 13px; }
.ccl-say { background: var(--primary-softer); border: 1px solid color-mix(in srgb, var(--primary) 26%, var(--line)); }
.ccl-say-k { color: var(--primary-ink); }
.ccl-say > span:last-child { color: var(--ink); font-size: var(--t-md); line-height: 1.5; }

/* -- talking points --------------------------------------------------------------------------- */
.ccl-points { display: flex; flex-direction: column; gap: 2px; }
.ccl-point { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: var(--s3);
  align-items: start; padding: 11px var(--s3); border-radius: var(--r-md); cursor: pointer;
  border: 1px solid transparent; transition: background .15s ease, border-color .15s ease, opacity .3s ease; }
.ccl-point:hover { background: var(--surface-2); }
.ccl-point:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
.ccl-pk { display: flex; justify-content: center; padding-top: 2px; }
.ccl-pk svg { width: 17px; height: 17px; color: var(--good); }
.ccl-ring { width: 15px; height: 15px; border: 2px solid var(--line-strong); border-radius: 50%; display: block; }
.ccl-pl { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ccl-pl b { font-size: var(--t-base); font-weight: 600; color: var(--ink); line-height: 1.4; }
.ccl-say-line { font-size: var(--t-sm); color: var(--muted); font-style: normal; line-height: 1.45; }
.ccl-ev { font-size: var(--t-xs); color: var(--good); font-style: italic; line-height: 1.45; }

.ccl-point.is-done { opacity: .62; background: color-mix(in srgb, var(--good) 8%, transparent); }
.ccl-point.is-done b { color: var(--ink-soft); text-decoration: line-through;
  text-decoration-color: color-mix(in srgb, var(--good) 55%, transparent); text-decoration-thickness: 1.5px; }
.ccl-point.is-next { border-color: color-mix(in srgb, var(--primary) 40%, var(--line));
  background: var(--primary-softer); opacity: 1; }
.ccl-point.is-memory .ccl-pl b::before { content: 'From last time · '; font-weight: 700;
  font-size: 10px; letter-spacing: .06em; text-transform: uppercase; color: var(--primary-ink); }

.ccl-ask { margin-top: var(--s4); display: inline-flex; align-items: center; justify-content: center;
  gap: 7px; border: 1px solid var(--line); background: var(--surface-2); color: var(--ink);
  border-radius: var(--r-md); padding: 10px 14px; font-size: var(--t-sm); font-weight: 600; cursor: pointer; }
.ccl-ask:hover:not(:disabled) { border-color: var(--primary); color: var(--primary-ink); }
.ccl-ask:disabled { opacity: .5; cursor: default; }
.ccl-ask svg { width: 15px; height: 15px; }
.ccl-foot { margin: var(--s3) 0 0; font-size: 11.5px; line-height: 1.5; color: var(--muted); }

/* -- memory rows (shared with the "Before you call" workspace card) ---------------------------- */
.ccl-memlist { display: flex; flex-direction: column; gap: 6px; margin-top: var(--s2); }
.ccl-mem { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--s3);
  align-items: start; padding: 9px var(--s3); border: 1px solid var(--line);
  border-radius: var(--r-md); background: var(--surface-2); font-size: var(--t-sm); }
.ccl-mem.is-follow { border-color: color-mix(in srgb, var(--primary) 30%, var(--line));
  background: var(--primary-softer); }
.ccl-mem-kind { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); background: var(--surface); border-radius: var(--r-full); padding: 3px 8px;
  white-space: nowrap; margin-top: 1px; }
.ccl-mem-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.ccl-mem-body b { font-weight: 600; color: var(--ink); line-height: 1.45; overflow-wrap: anywhere; }
.ccl-mem-detail { color: var(--muted); font-size: var(--t-xs); line-height: 1.45; }
.ccl-mem-ask { display: inline-flex; align-items: center; gap: 5px; color: var(--primary-ink);
  font-size: var(--t-xs); font-weight: 600; line-height: 1.4; }
.ccl-mem-ask svg { width: 12px; height: 12px; flex: none; }
.ccl-memdel { border: 0; background: none; color: var(--muted); cursor: pointer; padding: 2px;
  border-radius: var(--r-sm); opacity: 0; transition: opacity .15s ease, color .15s ease; }
.ccl-mem:hover .ccl-memdel, .ccl-memdel:focus-visible { opacity: 1; }
.ccl-memdel:hover { color: var(--danger); }
.ccl-memdel svg { width: 14px; height: 14px; display: block; }
.ccl-memdel:disabled { opacity: .4; cursor: default; }

/* -- "Before you call" card (patient workspace) ------------------------------------------------ */
.ccm-brief-last { border-left: 2px solid var(--primary); padding-left: var(--s3); margin-bottom: var(--s4); }
.ccm-brief-k { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); }
.ccm-brief-summary { margin: 4px 0 0; font-size: var(--t-sm); line-height: 1.55; color: var(--ink); }
.ccm-brief-h { display: flex; align-items: center; gap: 6px; margin: var(--s4) 0 0; font-size: 11px;
  font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.ccm-brief-h svg { width: 13px; height: 13px; }

/* -- after-call recap -------------------------------------------------------------------------- */
.ccl-recap-back { position: fixed; inset: 0; z-index: 950; display: flex; align-items: center;
  justify-content: center; padding: var(--s5); background: rgba(12, 22, 18, .55);
  backdrop-filter: blur(3px); animation: cclIn .18s ease; }
.ccl-recap { width: min(680px, 100%); max-height: 88vh; overflow-y: auto; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-2);
  padding: var(--s5) var(--s5) var(--s4); }
.ccl-recap h2 { margin: 0 0 4px; display: flex; align-items: center; gap: var(--s2);
  font-size: var(--t-lg); font-weight: 700; letter-spacing: -.01em; color: var(--ink); }
.ccl-recap h2 svg { width: 20px; height: 20px; color: var(--good); }
.ccl-recap-sub { margin: 0 0 var(--s4); font-size: var(--t-sm); color: var(--muted); }
.ccl-recap-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s4); }
.ccl-recap-grid h4 { margin: 0 0 var(--s2); font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); }
.ccl-recap-grid h4 span { font-variant-numeric: tabular-nums; }
.ccl-recap-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.ccl-recap-list li { font-size: var(--t-sm); line-height: 1.45; color: var(--ink); padding-left: 16px; position: relative; }
.ccl-recap-list li::before { position: absolute; left: 0; top: 0; font-weight: 700; }
.ccl-recap-list.is-ok li::before { content: '✓'; color: var(--good); }
.ccl-recap-list.is-miss li::before { content: '·'; color: var(--muted); font-size: 18px; line-height: 1; }
.ccl-recap-mem { margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--line); }
.ccl-recap-mem-h { display: flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; color: var(--muted); }
.ccl-recap-mem-h svg { width: 13px; height: 13px; }
.ccl-recap-summary { margin: var(--s2) 0 0; font-size: var(--t-base); line-height: 1.6; color: var(--ink); }
.ccl-recap-note { margin: var(--s2) 0 0; font-size: 11.5px; color: var(--muted); }
.ccl-recap-btns { display: flex; justify-content: flex-end; margin-top: var(--s4); }
.ccl-recap .ccl-memdel { opacity: 1; }

/* -- narrow screens: the transcript stacks above the coach ------------------------------------- */
@media (max-width: 900px) {
  .ccl-body { grid-template-columns: 1fr; grid-template-rows: minmax(0, 1fr) minmax(0, 1fr); }
  .ccl-coach { border-left: 0; border-top: 1px solid var(--line); }
  .ccl-top { gap: var(--s3); padding: var(--s3) var(--s4); }
  .ccl-clock { font-size: 22px; }
  .ccl-month { padding-left: var(--s3); font-size: var(--t-xs); }
  .ccl-end { padding: 9px 14px; font-size: var(--t-sm); }
  .ccl-recap-grid { grid-template-columns: 1fr; }
}

/* "View invoice" — the provenance link on a mined catalog row. */
.sup-link { border: 0; background: none; padding: 0; cursor: pointer; font-size: var(--t-xs);
  color: var(--primary-ink); text-decoration: underline; font-family: inherit; }
.sup-link:hover { color: var(--primary); }
.sup-doclink { margin-top: 2px; }

/* ---- Supply approvals: queue, order review, cover, and the offboarding hand-off ---- */
.sup-why { color: var(--primary-ink); }
.sup-age { font-size: var(--t-xs); color: var(--muted); white-space: nowrap; }
.sup-age.is-warn { color: #9a6b00; font-weight: 600; }
.sup-age.is-late { color: var(--danger, #b3261e); font-weight: 600; }
.sup-lt { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sup-decide { border-top: 1px solid var(--line); margin-top: var(--s4); padding-top: var(--s3); }
.sup-decide .sup-fin { max-width: 620px; }

/* Hand-drawn trend marks. No chart library can be loaded, so these are inline SVG. */
.sup-spark { display: block; overflow: visible; }
.sup-spark polyline { stroke: var(--primary); }
.sup-spark circle { fill: var(--primary); }
.sup-spark rect { fill: var(--primary); opacity: .55; }
.sup-sparkwrap { margin-bottom: 2px; }
.sup-nochart { font-size: var(--t-xs); color: var(--muted); font-style: italic; }

/* Person picker (reuses the app's .ct-pop dropdown shell). */
.sup-pickrow { position: relative; display: inline-block; }
.sup-pick-btn { min-width: 220px; text-align: left; }
.sup-pick-pop { position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; min-width: 280px; }
.sup-pick-sub { display: block; font-size: var(--t-xs); color: var(--muted); }

/* Offboarding hand-off dialog. */
.handover-card { max-width: 520px; text-align: left; }
.handover-list { max-height: 220px; overflow-y: auto; border: 1px solid var(--line);
  border-radius: var(--r-md); margin: var(--s2) 0; }
.handover-opt { display: flex; align-items: center; gap: var(--s2); width: 100%; border: 0;
  background: none; padding: 8px 10px; cursor: pointer; font-size: var(--t-sm); color: var(--ink);
  text-align: left; }
.handover-opt:hover { background: var(--surface-2); }
.handover-opt.is-on { background: var(--primary-soft); }
.handover-title { margin-left: auto; color: var(--muted); font-size: var(--t-xs); }
.handover-kind { display: grid; gap: 6px; margin: var(--s3) 0; }
.handover-kind label { display: flex; gap: 8px; align-items: flex-start; font-size: var(--t-sm);
  color: var(--ink-soft); line-height: 1.45; }
.handover-until { margin-bottom: var(--s2); font-size: var(--t-sm); color: var(--ink-soft); }
.handover-until .field-input { margin-top: 4px; }
.sup-addrow { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; margin: var(--s3) 0 var(--s5); }
.sup-addrow .sup-in, .sup-addrow .sup-fin { border: 1px solid var(--line-strong); background: var(--surface); }
.sup-rulehead { margin-top: var(--s5); font-size: var(--t-md); }
.sup-in-desc { min-width: 300px; }
.sup-table td .sup-link { white-space: nowrap; }

/* Effective approver, per company — what would happen right now, not just what is stored. */
.sup-routing { display: grid; gap: 4px; margin: var(--s2) 0 var(--s3); }
.sup-routerow { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-sm);
  color: var(--ink-soft); }
.sup-routeco { min-width: 120px; font-weight: 600; color: var(--ink); }
.sup-routearrow { display: inline-flex; color: var(--muted); }
.sup-routearrow svg { width: 14px; height: 14px; }
.sup-routewho { font-weight: 600; color: var(--primary-ink); }
.sup-routewhy { color: var(--muted); font-size: var(--t-xs); }
.sup-inline-note { margin: var(--s2) 0; }
/* Ship-to table: one row per possible site, grouped by company. */
.sup-cogroup td { background: var(--surface-2); font-weight: 600; font-size: var(--t-xs);
  color: var(--muted); letter-spacing: .02em; }
.sup-acct { min-width: 620px; }

/* States what is already configured, so empty checkboxes never read as "nothing is set". */
.sup-statebox { border-left: 3px solid var(--primary); background: var(--primary-softer);
  padding: var(--s2) var(--s3); border-radius: var(--r-sm); margin: var(--s2) 0 var(--s3);
  font-size: var(--t-sm); color: var(--ink-soft); line-height: 1.5; max-width: 70ch; }
.sup-statebox b { color: var(--ink); }
.sup-statebox .sup-sub { margin-top: 4px; }

/* Ship-to rows: address is editable because some vendors deliver everything to one place. */
.sup-addrcell { min-width: 300px; }
.sup-in-addr { min-width: 240px; }
.sup-in-st { min-width: 54px; }
.sup-addrline { display: flex; gap: 4px; align-items: center; margin-top: 2px; }

/* The plain-English name staff order by (the vendor's own wording stays on the purchase order). */
.sup-in-staff { min-width: 190px; }
.sup-in-staff::placeholder { font-style: italic; opacity: .7; }
.sup-in-staff { min-width: 230px; }
.sup-in-staff:focus { min-width: 300px; position: relative; z-index: 2; }
.sup-table td input.sup-in { text-overflow: ellipsis; }

/* Internal stock: shelf levels, who is still waiting, and inter-site charges. */
.sup-low { color: #b3261e; font-weight: 600; }
.sup-can { color: var(--primary-ink); font-weight: 600; }
.sup-lowrow td { background: rgba(179,38,30,.045); }
.sup-stockgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s3); margin: var(--s2) 0 var(--s3); }
.sup-stockgrid .sup-field { margin-bottom: 0; }

/* The internal-stock decision, readable in the row itself. */
.sup-stockcell { min-width: 150px; }
.sup-stockon, .sup-stockset { text-align: left; text-decoration: none; line-height: 1.3; }
.sup-stockon b { display: block; color: var(--primary-ink); }
.sup-stocksub { display: block; font-size: var(--t-xs); color: var(--muted); }
.sup-stockset { font-style: italic; opacity: .75; }
.sup-stockon:hover b, .sup-stockset:hover { text-decoration: underline; }

/* Frozen table headers.
   These wrappers already scroll sideways (the tables are wider than the screen), and a box that
   scrolls in one axis scrolls in both as far as CSS is concerned — so a sticky header inside one
   sticks to the WRAPPER, not the page. Giving the wrapper its own height is what makes that
   meaningful: the table scrolls within it and the header stays put. Short tables are unaffected,
   since max-height only bites once there is more table than room. */
.sup-tablewrap { max-height: calc(100vh - 300px); overflow: auto; }
.sup-table thead th {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface);
  /* A real border would scroll away with the cell; an inset shadow is painted by the sticky
     header itself and so stays with it. */
  box-shadow: inset 0 -1px 0 var(--line);
}
/* The company sub-headings inside the ship-to table sit below the frozen header, not above it. */
.sup-table tbody tr.sup-cogroup td { position: sticky; top: 34px; z-index: 2; }
/* The mined-rows panel sets its own height, so it only needs the sticky header itself. */
.sup-staged-tbl { max-height: 420px; }
@media (max-width: 760px) { .sup-tablewrap { max-height: calc(100vh - 220px); } }

/* Editing a row's stock settings underneath the row itself, so you never lose your place. */
.sup-editrow > td { background: var(--primary-softer); padding: var(--s3) var(--s4);
  box-shadow: inset 0 1px 0 var(--line), inset 0 -1px 0 var(--line); }
.sup-editwrap { max-width: 900px; }
.sup-editttl { font-size: var(--t-sm); color: var(--ink); margin-bottom: var(--s2); }
.sup-editttl .sup-sub { display: inline; margin-left: var(--s2); }
.sup-editgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--s2) var(--s3); align-items: end; }
.sup-editgrid label { display: block; }
.sup-editgrid label span { display: block; font-size: var(--t-xs); color: var(--muted);
  margin-bottom: 3px; }
.sup-editgrid .sup-fin { padding: 6px 9px; font-size: var(--t-sm); }
.sup-editacts { display: flex; gap: var(--s2); align-items: end; }
#er-note { margin-top: var(--s2); }
@media (max-width: 760px) { .sup-editgrid { grid-template-columns: 1fr 1fr; } }

/* Visible confirmation that an inline edit was stored. */
.sup-in.sup-saving { border-color: var(--primary); opacity: .7; }
.sup-in.sup-saved { border-color: var(--primary); background: var(--primary-soft); }

/* Bigger, easier-to-hit selection boxes.
   The default 13px box is a small target in a dense table, so the box itself is enlarged and the
   whole first cell is made clickable — you can aim anywhere in the column. */
.sup-table td.sup-cb, .sup-table th.sup-cb { width: 46px; text-align: center; padding-left: 6px; padding-right: 6px; }
.sup-table td.sup-cb { cursor: pointer; }
.sup-table td.sup-cb:hover { background: var(--surface-2); }
.sup-table .sup-cb input[type="checkbox"] {
  width: 19px; height: 19px; cursor: pointer; accent-color: var(--primary); margin: 0;
}

/* ---- Unanswered calls from our own offices (Command Center) ----------------------------
   A share bar per site plus an hour-of-day column chart. Deliberately quiet: one accent
   colour, no gridlines, no chart library — the numbers carry it. Both themes inherit from
   the same vars, so dark mode needs no overrides. */
.ic-bar {
  height: 6px; border-radius: 3px; background: var(--line);
  overflow: hidden; margin-bottom: 3px;
}
.ic-bar > span { display: block; height: 100%; background: var(--brand, var(--good)); border-radius: 3px; }
.ic-hours-wrap { margin-top: var(--s3); }
.ic-hours-title { font-size: var(--t-xs); color: var(--muted); font-weight: 700; margin-bottom: 6px; }
.ic-hours {
  display: flex; align-items: flex-end; gap: 3px;
  height: 96px; padding: 4px 0; overflow-x: auto;
}
.ic-hcol { flex: 1 1 0; min-width: 16px; display: flex; flex-direction: column; align-items: center; height: 100%; }
.ic-hbar {
  width: 100%; min-height: 3px; margin-top: auto; border-radius: 3px 3px 0 0;
  background: var(--brand, var(--good)); opacity: .85;
}
.ic-hcol:hover .ic-hbar { opacity: 1; }
.ic-hlbl { font-size: 9px; color: var(--muted); margin-top: 4px; white-space: nowrap; }

/* ---- Forwarded caller: their real number, and who you are actually naming ---------------
   A call routed through one of our office lines shows the OFFICE's caller ID, so the row and
   the Save-contact panel both have to say whose number is really in play. */
.chip-cbnum { background: var(--surface-2, rgba(127,148,138,.14)); color: var(--ink-soft, var(--muted)); border: 1px solid var(--line); }
.ce-who {
  display: flex; align-items: flex-start; gap: 6px;
  background: var(--surface-2, rgba(127,148,138,.10));
  border: 1px solid var(--line); border-left: 3px solid var(--brand, var(--good));
  border-radius: var(--r-sm, 6px); padding: 8px 10px; margin-bottom: var(--s2, 8px);
  color: var(--ink-soft, var(--muted)); font-size: var(--t-xs); line-height: 1.45;
}
.ce-who .icon { width: 14px; height: 14px; flex: none; margin-top: 1px; }
.ce-who.hidden { display: none; }
/* Caption under the editable callback number — says plainly what changing it will do. */
.ce-cap { color: var(--muted); font-size: var(--t-xs); line-height: 1.4; margin: -2px 0 2px; }
.ce-cap.hidden { display: none; }

/* ============================================================================================
   Clinical Compliance — the wall-station kiosk, the DO-NOT-TREAT banner, and the dashboard.
   Built after the 4th Immediate Jeopardy (2026-08-11).

   The kiosk is designed for a specific situation, not a generic screen: a flat-mounted iPad at
   eye level in a water treatment room, read from two feet away, tapped with gloved and often wet
   hands, at 5:30am. So: very large targets, very high contrast, one decision per screen, and no
   competing chrome. Nitrile gloves work on capacitive glass; small targets do not.
   ============================================================================================ */

/* The DO-NOT-TREAT strip. Lives on <body>, ABOVE everything, on every screen — the rail is
   capability-gated and the mobile nav is capped at five, so neither can be relied on here. */
.water-banner {
  position: sticky; top: 0; z-index: 9000;
  display: flex; flex-direction: column; gap: 2px;
  padding: 10px 16px;
  background: #b3261e; color: #fff;
  font: 600 var(--t-sm)/1.4 var(--font); letter-spacing: .01em;
  box-shadow: 0 2px 10px rgba(0,0,0,.25);
}
.water-banner b { font-weight: 800; letter-spacing: .04em; }

/* ---- Kiosk shell -------------------------------------------------------------------------- */
body.water-kiosk { overflow: hidden; background: var(--bg); }
body.water-kiosk #app, body.water-kiosk #login { display: none !important; }

#waterKiosk { position: fixed; inset: 0; z-index: 9500; background: var(--bg); }
.wk {
  height: 100%; display: flex; flex-direction: column;
  padding: clamp(20px, 4vh, 48px) clamp(20px, 5vw, 64px);
  font-family: var(--font); color: var(--ink);
  -webkit-user-select: none; user-select: none;
  position: relative;   /* anchors the "Recording" badge */
}
.wk-head { flex: 0 0 auto; margin-bottom: clamp(16px, 3vh, 32px); }
.wk-title { font-size: clamp(28px, 4.2vh, 46px); font-weight: 700; letter-spacing: -.02em; line-height: 1.15; }
.wk-sub { margin-top: 6px; font-size: clamp(16px, 2.2vh, 22px); color: var(--muted); }
.wk-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: clamp(12px, 2vh, 24px); overflow-y: auto; }
.wk-foot { flex: 0 0 auto; padding-top: 16px; text-align: center; }
.wk-foot-note { font-size: var(--t-md); color: var(--muted); }
.wk-empty { font-size: clamp(17px, 2.4vh, 22px); color: var(--muted); line-height: 1.5; max-width: 40ch; }
.wk-err { margin-top: 12px; font-size: clamp(16px, 2.2vh, 20px); font-weight: 600; color: var(--danger); }

/* The rolling deadline, on the name screen. Calm by default — this is a clock, not an alarm, and
   it is on screen all day. It only raises its voice once the check is actually late, which is the
   first rung of the escalation ladder and the one that can still be fixed in ninety seconds. */
.wk-due {
  flex: 0 0 auto; display: flex; flex-direction: column; gap: 2px;
  padding: clamp(10px, 1.6vh, 16px) clamp(14px, 2vw, 20px);
  border: 1px solid var(--line); border-radius: 14px; background: var(--panel);
}
.wk-due strong { font-size: clamp(18px, 2.6vh, 24px); font-weight: 650; }
.wk-due span { font-size: clamp(14px, 1.9vh, 18px); color: var(--muted); }
.wk-due-idle { color: var(--muted); font-size: clamp(15px, 2vh, 19px); }
.wk-due-late {
  border-color: var(--danger); background: color-mix(in srgb, var(--danger) 10%, transparent);
}
.wk-due-late strong { color: var(--danger); }
/* "Start it now" — the half hour before the deadline. Amber, not red: nothing is late yet, and
   colouring it like a failure would train people that red means "carry on". */
.wk-due-soon {
  border-color: var(--warn, #b45309);
  background: color-mix(in srgb, var(--warn, #b45309) 10%, transparent);
}
.wk-due-soon strong { color: var(--warn, #b45309); }

/* Deliberately quiet, and deliberately not a .wk-btn. Ending the day SUPPRESSES a requirement, so
   it must never be the easiest thing on screen to hit by accident with a wet glove. */
.wk-endday {
  /* ⚠ Sized to be pressed with a gloved thumb on a wall-mounted iPad, and to read as the deliberate
     end of the day rather than a footnote. Owner, 2026-08-14: he pressed the old one and thought it
     had not worked. Declarations are listed ONCE — an earlier attempt left the old padding and
     font-size below the new ones in the same rule, where they silently won. */
  display: block; margin: 18px auto 0;
  padding: 15px 30px; min-height: 56px;
  border: 2px solid var(--line); border-radius: 13px; background: transparent;
  font: inherit; font-size: 17px; font-weight: 700; letter-spacing: .03em;
  color: var(--ink); cursor: pointer;
}
.wk-endday:hover { color: var(--ink); border-color: var(--ink); }

/* Big, calm, unmistakable. 72px minimum height everywhere a wet glove has to land. */
.wk-btn {
  display: block; width: 100%; min-height: 88px; margin-top: auto;
  border: 0; border-radius: var(--r-lg);
  background: var(--primary); color: var(--on-primary);
  font: 700 clamp(19px, 2.6vh, 26px)/1.2 var(--font); letter-spacing: .01em;
  cursor: pointer; transition: background .12s ease, transform .06s ease;
}
.wk-btn:active { transform: translateY(1px); background: var(--primary-hover); }
.wk-btn[disabled] { opacity: .55; }
.wk-btn-quiet { background: transparent; color: var(--muted); border: 2px solid var(--line-strong); min-height: 64px; font-size: var(--t-md); }
.wk-btn-ghost { background: rgba(255,255,255,.14); color: #fff; border: 2px solid rgba(255,255,255,.5); }

/* Name grid. The roster IS the competency gate — nobody without water.test appears here. */
.wk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
.wk-tile {
  min-height: 116px; padding: 16px;
  border: 2px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); color: var(--ink);
  font: 600 clamp(18px, 2.4vh, 23px)/1.25 var(--font);
  cursor: pointer; transition: border-color .12s ease, background .12s ease;
}
.wk-tile:active { border-color: var(--primary); background: var(--primary-softer); }

/* PIN. The tile identified them; this only proves it's them. */
.wk-pin { text-align: center; font-size: clamp(34px, 5vh, 52px); letter-spacing: .35em; color: var(--primary-ink); min-height: 1.4em; }
.wk-keys { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; max-width: 460px; margin: 0 auto; width: 100%; }
.wk-key {
  min-height: 84px; border: 2px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); color: var(--ink);
  font: 600 clamp(24px, 3.2vh, 32px)/1 var(--font); cursor: pointer;
}
.wk-key:active { background: var(--surface-3); }
.wk-key-alt { color: var(--muted); font-size: var(--t-lg); }

/* Choice rows — stock, ports. */
.wk-rows { display: flex; flex-direction: column; gap: 14px; }
.wk-row {
  display: flex; flex-direction: column; gap: 4px; align-items: flex-start;
  padding: 20px 22px; min-height: 88px;
  border: 2px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); color: var(--ink); text-align: left; cursor: pointer;
}
.wk-row b { font-size: clamp(18px, 2.4vh, 22px); font-weight: 600; }
.wk-row span { font-size: var(--t-md); color: var(--muted); }
/* ⚠ ROW, NOT COLUMN. Inheriting .wk-row's column direction dropped the status pill onto a second
   line, so every port became a near-empty 88px slab with its answer stranded underneath. Label
   left, state right; the tap target stays full height for gloved hands. */
.wk-row-lg {
  flex-direction: row; align-items: center; justify-content: space-between;
  gap: clamp(10px, 1.5vw, 18px); font-size: clamp(19px, 2.6vh, 24px); font-weight: 600;
}
/* The port the day's sequence expects. Marked, never enforced — the other ports stay live, because
   a kiosk that refuses the wrong answer produces no sample rather than the right one. */
/* ⚠ WAS `var(--brand, #0f766e)`, AND `--brand` IS DEFINED NOWHERE — so it fell through to a
   hardcoded teal that is not the app's #1f8a76, and stayed that dark teal in dark mode while
   every surface around it went light. Use the real token. */
.wk-row-want {
  border-color: var(--primary); background: var(--primary-softer);
  box-shadow: inset 0 0 0 2px var(--primary);
}
/* ⚠ THESE SELECTORS MUST OUT-SPECIFY `.wk-row span`, WHICH IS (0,1,1). As a bare `.wk-want`
   (0,1,0) the pill lost BOTH its white text and its 12px size to that rule — the iPad showed
   muted grey letters on a teal ground, clipped by padding sized for a font it no longer had.
   Any new pill in this module needs the same `.wk-row` prefix or it washes out the same way. */
.wk-want, .wk-row .wk-want, .wk-done, .wk-row .wk-done {
  flex: 0 0 auto; padding: 5px 12px; border-radius: 999px;
  font-size: clamp(11px, 1.4vh, 13px); font-weight: 700; line-height: 1.5;
  letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
}
/* NEXT is the only actionable state on the screen, so it is the only one wearing the accent. */
.wk-want, .wk-row .wk-want { background: var(--primary); color: var(--on-primary); }
/* ⚠ DONE IS DELIBERATELY QUIET. Painted the same teal as NEXT, three finished ports shouted as
   loudly as the one port still to be drawn, and the eye had nothing to land on. */
.wk-done, .wk-row .wk-done {
  background: transparent; color: var(--muted);
  border: 1px solid var(--line); font-weight: 600; padding: 4px 11px;
}
.wk-row:active { border-color: var(--primary); background: var(--primary-softer); }

/* Live camera preview. One stream serves the frame burst AND the colour-chart photo. */
.wk-cam { flex: 1 1 auto; min-height: 220px; border-radius: var(--r-lg); overflow: hidden; background: #0b1210; display: flex; }
.wk-video { width: 100%; height: 100%; object-fit: cover; }
.wk-cam-run { flex: 0 0 22vh; min-height: 0; opacity: .85; }

/* The countdown. Enormous on purpose — it has to be readable across the room, and the tech is
   holding a beaker in one hand and a strip in the other. Tabular figures so it never jitters. */
.wk-run { align-items: center; justify-content: center; text-align: center; gap: 8px; }
.wk-run-label { font-size: clamp(20px, 3vh, 30px); font-weight: 600; }
.wk-run-sub { font-size: clamp(15px, 2vh, 20px); color: var(--muted); margin-bottom: 12px; }
.wk-clock {
  font-size: clamp(120px, 30vh, 300px); font-weight: 800; line-height: .95;
  color: var(--primary-ink); font-variant-numeric: tabular-nums; letter-spacing: -.04em;
}

/* Reading entry: colour swatches, never a keyboard. "0.01" vs "0.1" must be untypable. */
.wk-swatches { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 16px; }
.wk-swatch {
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  padding: 20px 12px; min-height: 160px;
  border: 2px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); cursor: pointer;
}
.wk-swatch-chip { width: 100%; height: 66px; border-radius: var(--r-md); background: var(--sw); border: 1px solid rgba(0,0,0,.08); }
.wk-swatch b { font-size: clamp(20px, 2.8vh, 26px); font-weight: 700; color: var(--ink); }
.wk-swatch:active { border-color: var(--primary); }

/* Voided + done. A void is a normal, blameless outcome — the copy says so deliberately. */
.wk-void { font-size: clamp(19px, 2.6vh, 25px); font-weight: 600; line-height: 1.4; color: var(--danger); }
.wk-void-note { font-size: var(--t-md); color: var(--muted); line-height: 1.55; max-width: 46ch; }
.wk-done { font-size: clamp(64px, 12vh, 120px); font-weight: 800; text-align: center; }
.wk-done.ok { color: var(--good); }
.wk-done.bad { color: var(--danger); }
.wk-done-note { text-align: center; font-size: clamp(16px, 2.2vh, 21px); color: var(--muted); line-height: 1.5; }
.wk-code {
  width: 100%; padding: 22px; border: 2px solid var(--line-strong); border-radius: var(--r-lg);
  font: 700 clamp(24px, 3.4vh, 34px)/1 var(--font); text-align: center; letter-spacing: .3em;
  background: var(--surface); color: var(--ink);
}

/* The interlock. Full-screen, undismissable, and the only way out is the protocol. */
/* ⚠ AN ALARM SCREEN MUST NEVER PUSH ITS BUTTON OFF THE BOTTOM OF THE GLASS.
   It did exactly that. `.wk-alarm-in` is centred in a full-height flex column, so once the content
   grew taller than the iPad the overflow went off BOTH ends and the action went with it — measured
   at 1080x810, the redo button started at y=814 in an 810px viewport, 93px below the screen, with
   no way to scroll to it. The screen looked complete: headline, explanation, closing line, and a
   control that simply was not there. Reported twice as "still no button".
   The text region now shrinks and scrolls; the action sits in its own row that cannot be pushed. */
.wk-alarm { background: #b3261e; color: #fff; align-items: center; justify-content: center; text-align: center; }
.wk-alarm .wk-alarm-in { flex: 0 1 auto; min-height: 0; overflow-y: auto; width: 100%; }
/* The action row: outside the scrolling text, so it is always on screen. */
.wk-alarm-act {
  flex: 0 0 auto; width: 100%; max-width: 42ch;
  display: flex; flex-direction: column; gap: 10px; align-items: center;
  padding-top: clamp(10px, 2vh, 20px);
}
.wk-alarm-act .wk-btn { margin-top: 0; }
/* Four lines of 88px headline is most of an iPad. Big enough to read across a room, small enough
   to leave room for the thing you have to press. */
.wk-lapsedfull h1 { font-size: clamp(30px, 5.2vh, 58px); }
.wk-alarm-in { max-width: 42ch; display: flex; flex-direction: column; gap: 14px; align-items: center; }
.wk-alarm-bang { width: 92px; height: 92px; }
.wk-alarm-bang svg { width: 100%; height: 100%; }
.wk-alarm h1 { font-size: clamp(44px, 8vh, 88px); font-weight: 800; letter-spacing: .02em; margin: 0; }
.wk-alarm-why { font-size: clamp(19px, 2.8vh, 26px); font-weight: 600; }
.wk-alarm-val { font-size: clamp(22px, 3.2vh, 30px); }
.wk-alarm-next { font-size: var(--t-md); line-height: 1.55; opacity: .92; }

/* ---- Staff dashboard ---------------------------------------------------------------------- */
.wtr-wrap { padding: var(--s5); display: flex; flex-direction: column; gap: var(--s5); height: 100%; overflow: auto; }

/* ---- Dialysis Compliance: the five sections -------------------------------------------------
   ⚠ STICKY. .wtr-wrap is itself the scroll container, so the strip must pin to the top of it —
   Water Check alone can be thousands of pixels tall, and without this the only route to the other
   four sections (including Audit Alerts) scrolls out of sight with no way back but a long scroll.
   ⚠ Use --ink and --primary. There is no --text or --accent in this stylesheet; an undefined
   custom property is invalid at computed-value time, so the selected tab would have lost both its
   colour and its underline — the only thing showing which of five sections you are looking at. */
.wtr-tabs {
  position: sticky; top: 0; z-index: 2;
  display: flex; flex-wrap: wrap; gap: var(--s4);
  margin: 0; padding: var(--s2) 0 0;
  background: var(--surface); border-bottom: 1px solid var(--line);
}
.wtr-tab {
  appearance: none; background: none; border: 0; cursor: pointer;
  padding: var(--s3) 2px calc(var(--s3) - 2px);
  border-bottom: 2px solid transparent;
  font: inherit; font-size: var(--t-sm); font-weight: 600; color: var(--muted);
  display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
}
.wtr-tab:hover { color: var(--ink); }
.wtr-tab.on { color: var(--ink); border-bottom-color: var(--primary); }
.wtr-tabn {
  min-width: 18px; padding: 0 5px; border-radius: 999px;
  background: var(--primary); color: #fff;
  font-size: 11px; font-weight: 700; line-height: 18px; text-align: center;
}
.wtr-body { display: flex; flex-direction: column; gap: var(--s5); }

/* Clinic folder tabs — the SECOND level, inside a section.
   Deliberately a different shape from .wtr-tab above: those are underlined text, these are raised
   folder tabs sitting on a line. Two levels of navigation that look the same is how somebody loses
   track of which one they just clicked. */
.wtr-folders {
  display: flex; flex-wrap: wrap; gap: 4px;
  margin: 0 0 calc(var(--s3) * -1); padding: 0;
  border-bottom: 1px solid var(--line);
}
.wtr-folder {
  appearance: none; cursor: pointer;
  padding: 7px 13px;
  border: 1px solid var(--line); border-bottom: 0;
  border-radius: 7px 7px 0 0;
  background: var(--bg, #f6f8f7); color: var(--muted);
  font: inherit; font-size: 12px; font-weight: 600; white-space: nowrap;
  position: relative; top: 1px;
}
.wtr-folder:hover { color: var(--ink); }
.wtr-folder.on {
  background: var(--surface); color: var(--ink);
  border-color: var(--line); box-shadow: 0 -2px 0 var(--primary) inset;
}
/* The one-sentence "what lives here" line under each section heading. */
.wtr-lede { margin: 0; color: var(--ink); font-size: var(--t-sm); line-height: 1.55; max-width: 78ch; }
.wtr-empty { margin: 0; color: var(--muted); font-size: var(--t-sm); line-height: 1.55; }

/* A soft alarm on the nurses' station — a banner, or a badge that bounces.
   ⚠ Deliberately NOT the red of the blocking alarm. If a gentle notice looked like an emergency,
   the emergency would stop reading as one within a week. */
.nk-soft {
  display: flex; align-items: center; gap: 14px; cursor: pointer;
  margin: 0 0 14px; padding: 14px 18px; border-radius: 12px;
  background: #fff4d6; border: 2px solid #e0a500; color: #5c4200;
  font-size: 20px; font-weight: 600;
}
.nk-soft > span { font-weight: 500; font-size: 17px; }
.nk-soft-more { margin-left: auto; opacity: .8; }
.nk-soft-go {
  margin-left: auto; background: #e0a500; color: #fff;
  padding: 6px 14px; border-radius: 8px; font-size: 16px;
}
.nk-soft-more + .nk-soft-go { margin-left: 0; }
.nk-bounce { animation: nkBounce 1.1s ease-in-out infinite; }
@keyframes nkBounce {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-7px); }
}
/* ⚠ Staff who have asked their device not to animate get a steady border pulse instead of motion
   — the alarm still catches the eye without triggering anybody's vestibular trouble. */
@media (prefers-reduced-motion: reduce) {
  .nk-bounce { animation: nkPulse 1.4s ease-in-out infinite; }
  @keyframes nkPulse { 0%, 100% { border-color: #e0a500; } 50% { border-color: #8a6600; } }
}

/* The built-in system alarms, folded away.
   ⚠ --panel is NOT a token in this stylesheet — use --card, which is what every other surface
   in the water module resolves to. A --panel background is transparent and the fold would sit
   on whatever happens to be behind it. */
.wtr-fold {
  margin: 14px 0 12px; border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); padding: 0 12px;
}
.wtr-fold > summary {
  cursor: pointer; padding: 10px 0; font-size: var(--t-sm); color: var(--muted);
  list-style: none; user-select: none;
}
.wtr-fold > summary::-webkit-details-marker { display: none; }
.wtr-fold > summary::before { content: '▸ '; color: var(--muted); }
.wtr-fold[open] > summary::before { content: '▾ '; }
.wtr-fold > summary:hover { color: var(--ink); }
.wtr-fold[open] > summary { border-bottom: 1px solid var(--line); margin-bottom: 10px; }
.wtr-fold > :last-child { margin-bottom: 12px; }

/* "Who gets the alert" and "How the alert arrives" on the audit setup form. */
.wtr-sub {
  border: 1px solid var(--line); border-radius: 10px; background: var(--card);
  padding: 10px 12px; display: grid; gap: 6px;
}
.wtr-sub > b { font-size: var(--t-sm); }
.wtr-targets, .wtr-styles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 8px; margin-top: 4px;
}
.wtr-targets .wtr-check, .wtr-styles .wtr-check {
  align-items: flex-start; gap: 8px; padding: 8px 10px;
  border: 1px solid var(--line); border-radius: 8px; line-height: 1.4;
}
.wtr-targets .wtr-check input, .wtr-styles .wtr-check input { margin-top: 3px; flex: 0 0 auto; }
.wtr-targets .wtr-check:hover, .wtr-styles .wtr-check:hover { border-color: var(--accent); }
#auStaff { min-height: 130px; }

/* The ESRD personnel worksheet — a dense editable grid, so it gets its own tighter metrics.
   It scrolls sideways inside .wtr-logwrap like the TCL-1 log does; the page never scrolls. */
.wtr-psheet th, .wtr-psheet td { padding: 5px 6px; font-size: 12px; vertical-align: top; white-space: nowrap; }
.wtr-psheet input[type="date"], .wtr-psheet select {
  font: inherit; font-size: 11px; padding: 3px 5px;
  border: 1px solid var(--line); border-radius: 5px;
  background: var(--surface); color: var(--ink); max-width: 130px;
}
.wtr-psheet input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--primary); }
/* Autosave feedback — a cell that saved should say so without a toast covering the next cell. */
.wtr-psheet .saving { border-color: var(--primary); opacity: .6; }
.wtr-psheet .saved { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary) inset; }
.wtr-psheet tr.bad td { background: color-mix(in srgb, var(--danger, #b42318) 7%, transparent); }

/* A flagged photograph somebody has just dealt with — struck through and dimmed for a moment
   before it goes, so a colleague watching sees WHY the row left rather than it vanishing. */
.wtr-done td { opacity: .45; text-decoration: line-through; transition: opacity .3s ease; }
.wtr-done td .wtr-exp-msg { opacity: 1; text-decoration: none; font-weight: 600; }
.wtr-danger { border-color: var(--danger, #b42318); color: var(--danger, #b42318); }
/* The per-row reply on a flagged photograph. It sits in a table cell under two buttons, so it needs
   to be a block of its own — inline it collides with the buttons and reads as part of them. */
.wtr-tbl td .wtr-exp-msg { display: block; margin-top: 4px; font-size: 12px; color: var(--primary); font-weight: 600; }
.wtr-loading { color: var(--muted); }
.wtr-blocked { display: flex; flex-direction: column; gap: 4px; padding: 14px 18px; border-radius: var(--r-md); background: #b3261e; color: #fff; font-size: var(--t-base); }
.wtr-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s3); }
.wtr-card { padding: 16px 18px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); display: flex; flex-direction: column; gap: 6px; }
.wtr-card span { font-size: var(--t-sm); color: var(--muted); }
.wtr-card b { font-size: var(--t-xl); font-weight: 700; }
.wtr-card.warn b { color: var(--warn); }
.wtr-card.bad b { color: var(--danger); }
.wtr-h { margin: 0; font-size: var(--t-md); font-weight: 700; }
.wtr-note { margin: -12px 0 0; font-size: var(--t-sm); color: var(--muted); line-height: 1.5; max-width: 70ch; }

/* The soak-time histogram. Every bar at 30s is the proof the practice actually changed. */
.wtr-hist { display: flex; align-items: flex-end; gap: 3px; height: 180px; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.wtr-bar { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; height: 100%; gap: 4px; }
.wtr-bar i { display: block; width: 100%; background: var(--primary); border-radius: 3px 3px 0 0; min-height: 2px; }
.wtr-bar u { font-size: 9px; color: var(--muted); text-decoration: none; }

.wtr-tbl { width: 100%; border-collapse: collapse; font-size: var(--t-sm); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); }
.wtr-tbl th { text-align: left; padding: 10px 12px; font-weight: 600; color: var(--muted); border-bottom: 1px solid var(--line); }
.wtr-tbl td { padding: 10px 12px; border-bottom: 1px solid var(--surface-3); }
.wtr-tbl tr.bad td { background: var(--danger-soft); }
.wtr-tbl tr.warn td { background: color-mix(in srgb, var(--warn) 12%, transparent); }
.wtr-card i { font-style: normal; font-size: var(--t-xs); color: var(--muted); }

/* The evidence packet controls. Wide tables and this row must scroll inside themselves rather than
   pushing the page sideways. */
.wtr-export {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s3);
  padding: 14px 16px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface);
}
.wtr-export label { display: flex; flex-direction: column; gap: 4px; font-size: var(--t-xs); color: var(--muted); }
.wtr-export input, .wtr-export select {
  font: inherit; font-size: var(--t-sm); padding: 8px 10px; min-height: 38px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg); color: var(--ink);
}
.wtr-btn {
  font: inherit; font-size: var(--t-sm); font-weight: 600; padding: 9px 16px; min-height: 38px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg); color: var(--ink); cursor: pointer;
}
.wtr-btn:hover:not(:disabled) { border-color: var(--ink); }
.wtr-btn:disabled { opacity: .55; cursor: default; }
.wtr-exp-msg { flex: 1 1 100%; font-size: var(--t-xs); color: var(--muted); }
.wtr-exp-msg.bad { color: var(--danger); font-weight: 600; }
/* A row that is listed and working but cannot fire until something is entered. */
.wtr-frac.bad { color: var(--danger); font-weight: 600; display: block; margin-top: 4px; }
/* ⚠ A note inside a table cell has to stay a note. One row's explanation grew to a full screen and
   pushed every other alarm out of view — the cell now has a floor width and the text a ceiling. */
.wtr-cellnote { display: block; max-width: 46ch; line-height: 1.45; }
.wtr-tbl td:nth-child(2) { min-width: 260px; }

/* A short note under a field on the nurses'-station kiosk. Big enough to read at arm's length —
   this is a wall-mounted iPad, not a desktop form. */
.nk-hint {
  margin: -6px 0 14px; font-size: 15px; line-height: 1.45; color: var(--muted);
}
.nk-num:disabled { opacity: .45; }

/* The reading being countersigned — big enough to read at arm's length while holding the strip. */
.nk-verify-val {
  margin: 6px 0 18px; padding: 18px 22px; border-radius: 12px;
  border: 2px solid var(--line); background: var(--card);
  font-size: 22px; color: var(--muted);
}
.nk-verify-val span { font-size: 54px; font-weight: 700; color: var(--ink); line-height: 1; }
.nk-verify-val small { display: block; margin-top: 8px; font-size: 17px; }
.nk-verify-val.bad { border-color: var(--danger); }
.nk-verify-val.bad span { color: var(--danger); }
/* The one 3-minute escape on the blocking countersignature screen. Deliberately the quiet button:
   signing is the primary action, and the snooze must never be the easiest thing to reach. */
#nkVsnooze { margin-top: 10px; opacity: .85; }
#nkVsnooze small { display: block; margin-top: 4px; font-size: 15px; opacity: .8; }
/* The photograph of the strip, on the countersignature screen. Big — she is matching a colour
   across a room, and a thumbnail is worse than nothing because it invites a guess. */
.nk-strip {
  margin: 0 0 18px; min-height: 120px; border-radius: 12px; overflow: hidden;
  background: rgba(0,0,0,.18); display: flex; align-items: center; justify-content: center;
}
.nk-strip img { width: 100%; max-height: 46vh; object-fit: contain; display: block; }
.nk-strip-wait { padding: 22px; font-size: 17px; opacity: .85; text-align: center; line-height: 1.5; }
.nk-strip-wait.bad { font-weight: 600; }
/* The excuse action on an overdue clinic card. Small and last — it suppresses a required check. */
.wtr-clinic-excuse {
  margin-top: 8px; align-self: flex-start; font-size: var(--t-xs);
  opacity: .75;
}
.wtr-clinic-excuse:hover { opacity: 1; }

/* ---- The run photo viewer ---------------------------------------------------------------------
   ⚠ Hover enlarges in place; click opens the slide. Two levels of "look closer": the soak frames
   are for scanning quickly, the strip photo is for studying. */
.wtr-shots-grid figure.wtr-shot-open { cursor: zoom-in; transition: transform .12s ease, box-shadow .12s ease; }
.wtr-shots-grid figure.wtr-shot-open:hover {
  transform: scale(1.9); z-index: 5; position: relative;
  box-shadow: 0 12px 40px rgba(0,0,0,.45);
}
.wtr-shots-grid figure.wtr-shot-open:hover img { image-rendering: auto; }

.wtr-slide {
  position: fixed; inset: 0; z-index: 60; background: rgba(0,0,0,.92);
  display: flex; align-items: center; justify-content: center;
}
.wtr-slide.hidden { display: none; }
.wtr-slide > img { max-width: 88vw; max-height: 84vh; object-fit: contain; }
.wtr-slide-cap {
  position: absolute; bottom: 18px; left: 0; right: 0; text-align: center;
  color: #fff; font-size: 15px; opacity: .9;
}
.wtr-slide-x {
  position: absolute; top: 14px; right: 18px; z-index: 2;
  background: transparent; border: 0; color: #fff; font-size: 40px; line-height: 1;
  cursor: pointer; opacity: .8;
}
.wtr-slide-x:hover { opacity: 1; }
/* ⚠ Big hit areas — this gets paged through on an iPad with gloves on as often as with a mouse. */
.wtr-slide-nav {
  position: absolute; top: 0; bottom: 0; width: 22%; z-index: 1;
  background: transparent; border: 0; color: #fff; font-size: 56px; cursor: pointer;
  opacity: .55; padding: 0 18px;
}
.wtr-slide-nav:hover { opacity: 1; background: rgba(255,255,255,.06); }
.wtr-slide-nav.prev { left: 0; text-align: left; }
.wtr-slide-nav.next { right: 0; text-align: right; }
@media (prefers-reduced-motion: reduce) {
  .wtr-shots-grid figure.wtr-shot-open { transition: none; }
}
/* The log thumbnail advertises that it opens something. */
.wtr-shot img[data-shots] { cursor: zoom-in; }
.wtr-shot img[data-shots]:hover { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ---- The inline filmstrip ---------------------------------------------------------------------
   ⚠ Lives INSIDE the log table. The point is a look without leaving the page, so it must not feel
   like a panel that opened — it is the row, unrolled. */
/* ⚠ THE CELL IS POSITIONED AND THE STRIP IS ABSOLUTE INSIDE IT — this is the whole fix.
   A flex row of thirteen 132px thumbnails placed normally inside a <td> contributes its full
   intrinsic width to the table, so the TABLE grows to ~1,800px instead of the strip scrolling
   inside it. The row "just kept going", the page scrolled sideways, and the arrows were pushed off
   the edge where nobody could reach them. Absolutely positioned children contribute NO intrinsic
   width, so the table keeps the width it had and the strip is forced to scroll within it. */
.wtr-strip-row > td {
  padding: 0 !important; background: var(--bg-soft, rgba(0,0,0,.03));
  position: relative; height: 132px; overflow: hidden;
}
.wtr-strip {
  position: absolute; inset: 0;
  display: flex; align-items: center; gap: 6px; padding: 10px 8px;
  box-sizing: border-box;
}
.wtr-strip-scroll {
  display: flex; gap: 10px; overflow-x: auto; scroll-behavior: smooth;
  flex: 1 1 auto; min-width: 0;          /* ⚠ or the flex child refuses to shrink and overflows */
  height: 100%; align-items: center; padding: 2px;
  /* Arrow-driven, so the bar itself is noise. */
  scrollbar-width: none; -ms-overflow-style: none;
}
.wtr-strip-scroll::-webkit-scrollbar { display: none; }
.wtr-strip-shot {
  flex: 0 0 auto; margin: 0; width: 132px; cursor: zoom-in;
  transition: transform .12s ease;
}
.wtr-strip-shot img {
  width: 132px; height: 96px; object-fit: cover; display: block;
  border-radius: 8px; border: 1px solid var(--line); background: rgba(0,0,0,.06);
}
/* Hover lifts it enough to read without covering its neighbours — see them all, look at one. */
.wtr-strip-shot:hover { transform: scale(1.06); }
.wtr-strip-shot:hover img { border-color: var(--accent); }
.wtr-strip-shot figcaption {
  font-size: var(--t-xs); color: var(--muted); text-align: center; margin-top: 3px;
}
.wtr-strip-shot.bad img { border-color: var(--danger); }
.wtr-strip-nav {
  flex: 0 0 auto; width: 40px; height: 104px; border: 1px solid var(--line);
  border-radius: 8px; background: var(--card); color: var(--ink);
  font-size: 28px; line-height: 1; cursor: pointer;
}
.wtr-strip-nav:hover { border-color: var(--accent); background: var(--accent); color: #fff; }
/* ⚠ Dimmed, not hidden, at the ends. A control that vanishes makes the row jump sideways under
   the cursor mid-click; one that greys out simply stops. */
.wtr-strip-nav[disabled] { opacity: .25; cursor: default; }
.wtr-strip-nav[disabled]:hover { background: var(--card); color: var(--ink); border-color: var(--line); }
.wtr-strip-wait { padding: 26px 12px; color: var(--muted); font-size: var(--t-sm); }
.wtr-strip-wait.bad { color: var(--danger); font-weight: 600; }
/* The flagged-review table sits on a warm background; its strip should not look like a hole. */
.wtr-tbl tr.warn + .wtr-strip-row > td { background: rgba(0,0,0,.045); }
/* ⚠ The flagged row's trigger is a BUTTON, not a thumbnail — it must not inherit the zoom cursor
   the log's <img> uses, or it reads as a picture you can click into. */
button[data-shots] { cursor: pointer; }

/* The inline write-up box, unrolled under a flagged row. Same shape as the filmstrip: the decision
   and the evidence stay on one screen. */
.wtr-wu-row > td { padding: 0 !important; background: rgba(0,0,0,.045); }
.wtr-wu { padding: 14px 16px; display: grid; gap: 8px; max-width: 760px; }
.wtr-wu > b { font-size: var(--t-sm); }
.wtr-wu .wtr-ta {
  width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  font: inherit; line-height: 1.5; resize: vertical;
}
.wtr-wu .wtr-modal-actions { display: flex; gap: 8px; margin-top: 2px; }
@media (prefers-reduced-motion: reduce) {
  .wtr-strip-shot { transition: none; }
  .wtr-strip-scroll { scroll-behavior: auto; }
}

/* The nurse's own photograph and her signature, on the countersignature screen. */
.nk-vshot {
  margin: 6px 0 10px; border-radius: 12px; overflow: hidden;
  background: rgba(0,0,0,.2); min-height: 180px;
  display: flex; align-items: center; justify-content: center;
}
.nk-vshot video, .nk-vshot img { width: 100%; max-height: 34vh; object-fit: contain; display: block; }
.nk-vshot .hidden { display: none; }
/* ⚠ White, not transparent — a signature drawn in near-black on the dark alarm background is
   invisible while she draws it, and she signs it twice thinking the first did not take. */
.nk-sig {
  width: 100%; height: 150px; margin: 6px 0 8px;
  background: #fff; border-radius: 12px; border: 2px solid var(--line);
  touch-action: none;                     /* or the iPad pans the page instead of drawing */
}
/* ⚠ The shutter. Deliberately the loudest control on the screen: it is the step between a nurse
   and clearing a blocking alarm, and on the red alarm background a subtle button disappears. */
.nk-shoot {
  /* ⚠ ICON + WORDS, not an emoji. An emoji renders at a different size on every device and reads as
     decoration; this is the one control between a nurse and clearing a blocking alarm. */
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; margin: 4px 0 6px; padding: 16px 20px;
  border-radius: 12px; border: 2px solid rgba(255,255,255,.85);
  background: rgba(255,255,255,.16); color: inherit;
  font-size: clamp(17px, 2.5vh, 22px); font-weight: 700; cursor: pointer;
}
.nk-shoot:hover { background: rgba(255,255,255,.26); }
.nk-shoot:active { transform: scale(.99); }
.nk-shoot svg { width: 26px; height: 26px; flex: 0 0 auto; }
/* ⚠ THE COUNTDOWN HAS TO BE READABLE FROM ARM'S LENGTH, because her hands are full and her eyes are
   on the strip, not the screen. Big, centred, and it does not move anything else when it changes. */
.nk-count {
  font-size: clamp(56px, 12vh, 96px); font-weight: 800; line-height: 1;
  text-align: center; margin: 2px 0 8px; font-variant-numeric: tabular-nums;
  animation: nk-count-pulse 1s ease-out infinite;
}
@keyframes nk-count-pulse { 0% { opacity: 1; transform: scale(1); } 70% { opacity: .75; transform: scale(.94); } 100% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .nk-count { animation: none; } }
/* Off the alarm screen it sits on a light background, so it needs real contrast there too. */
/* ---- "Water check ready" — the nurses' station fetch screen -----------------------------------
   ⚠ It is read from across a room by somebody deciding whether to walk over now. So: one number
   big enough to see at that distance, one sentence, one button, and a clock that says how long it
   has been waiting. Everything else is noise on a screen that only has one job. */
.nk-ready {
  display: flex; flex-direction: column; gap: clamp(10px, 2vh, 20px);
  max-width: 760px; margin: 0 auto; padding: clamp(16px, 4vh, 48px) 4vw;
  min-height: 100%; justify-content: center;
}
.nk-ready-top { display: flex; justify-content: space-between; align-items: baseline; opacity: .85; }
.nk-ready-eyebrow {
  font-size: clamp(13px, 1.8vh, 17px); letter-spacing: .09em; text-transform: uppercase;
  font-weight: 700;
}
.nk-ready-clock { font-size: clamp(13px, 1.8vh, 17px); font-variant-numeric: tabular-nums; }
.nk-ready-h { margin: 0; font-size: clamp(30px, 6.4vh, 60px); line-height: 1.05; }
.nk-ready-val {
  display: flex; align-items: baseline; gap: 14px;
  padding: clamp(12px, 2.4vh, 22px) clamp(16px, 3vw, 28px);
  border-radius: 16px; border: 2px solid rgba(255,255,255,.35); background: rgba(255,255,255,.10);
}
.nk-ready-val span { font-size: clamp(46px, 11vh, 104px); font-weight: 800; line-height: 1; }
.nk-ready-val small { font-size: clamp(14px, 2.2vh, 20px); opacity: .85; }
.nk-ready-lead { margin: 0; font-size: clamp(16px, 2.5vh, 22px); line-height: 1.45; opacity: .95; }
.nk-ready-late {
  margin: 0; padding: 12px 16px; border-radius: 10px;
  background: rgba(0,0,0,.22); font-weight: 700;
  font-size: clamp(15px, 2.2vh, 20px); line-height: 1.4;
}
/* ⚠ White on the red alarm, so the one action is unmistakably the brightest thing on the screen. */
.nk-ready-go {
  margin-top: clamp(4px, 1.5vh, 14px);
  background: #fff; color: #7f1d1d; border: 0; font-weight: 800;
  font-size: clamp(19px, 3vh, 28px); padding: clamp(16px, 2.8vh, 26px);
}
.nk-ready-go:hover { background: #f4f4f4; }
.nk-ready-foot { margin: 0; font-size: clamp(13px, 1.9vh, 17px); opacity: .75; }

/* The water room's "nobody signed the last one off" banner. Loud, and honest about the cost. */
/* The water room's full-screen "nobody signed the last one off". Same red as DO-NOT-TREAT because
   it is the same class of problem: the clinic is running on a reading nobody verified. */
.wk-lapsedfull .wk-btn-go {
  margin-top: clamp(14px, 3vh, 30px); background: #fff; color: #7f1d1d; border: 0;
  font-weight: 800; font-size: clamp(17px, 2.6vh, 24px); padding: clamp(14px, 2.4vh, 22px) 28px;
}
.wk-lapsedfull .wk-btn-go:hover { background: #f4f4f4; }

/* ⚠ Present ONLY before anything is recorded — see the ⚠ in shell(). Top-left, out of the way of
   the answer buttons: it must be findable when somebody realises they mis-tapped, and never the
   thing a hand lands on by accident while reaching for the real control. */
/* ⚠ IN FLOW, NOT ABSOLUTE. Positioned absolutely it landed exactly on top of the step title —
   "← Back" and "Where did you draw the sample?" printed through each other and neither could be
   read. It already renders before .wk-head, so simply flowing it puts it above the title;
   align-self stops the column flex from stretching it the full width of the iPad. */
.wk-back {
  align-self: flex-start; flex: 0 0 auto; margin-bottom: clamp(10px, 1.6vh, 18px);
  background: var(--surface-2, rgba(0, 0, 0, .04)); border: 1px solid var(--line);
  color: var(--muted); border-radius: 999px; cursor: pointer;
  padding: clamp(9px, 1.4vh, 13px) clamp(16px, 2vw, 22px);
  font-size: clamp(14px, 1.9vh, 17px); font-weight: 600;
  transition: color .15s, border-color .15s;
}
.wk-back:hover { color: var(--ink); border-color: var(--line-strong, var(--line)); }
.wk { position: relative; }

/* ⚠ The way out of a filmed step. Quiet and last — it VOIDS the attempt, so it must never be the
   control a hand lands on while reaching for "Rinsed and dumped". But it has to be visible, because
   the alternative for a technician who mis-tapped a port is running a test they know is wrong. */
.wk-abort-film {
  display: block; width: 100%; margin-top: clamp(10px, 2vh, 18px);
  background: rgba(0, 0, 0, .34); color: #fff; border: 1px solid rgba(255, 255, 255, .35);
  font-size: clamp(13px, 1.9vh, 17px); font-weight: 600; opacity: .82;
  padding: clamp(10px, 1.6vh, 15px);
}
.wk-abort-film:hover { opacity: 1; background: rgba(0, 0, 0, .46); }

/* "Show all sample points" — the way to reach a port already drawn today. Quiet, and last. */
.wk-row-quiet {
  flex-direction: row; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 0; margin-top: 6px;
  border-style: dashed; border-width: 1px; background: transparent; color: var(--muted);
  font-weight: 600;
  font-size: clamp(14px, 2vh, 18px) !important;
  padding: clamp(12px, 1.8vh, 18px) clamp(16px, 2vw, 22px) !important;
}
.wk-row-quiet:hover { color: var(--ink); border-color: var(--line-strong, var(--line)); }

/* ---- The soak-timer teaching fact -------------------------------------------------------------
   ⚠ Owner design rule: no emoji — the "Did you know" eyebrow does the job. The card drifts corner
   to corner on two out-of-phase CSS animations (the classic screensaver trick: different X and Y
   periods trace a bouncing path with zero JavaScript). Slow enough to read twice in 30 seconds. */
/* ⚠ AN ORBIT, NOT A DRIFT. Free-drifting corner to corner the card wandered over the countdown
   and the instruction and printed through both. On an arm that circles the clock it is always
   beside the number and never on it — at any screen size, with no measurements to keep in sync.
   Three nested elements: the arm rotates, the position pushes out to the radius, and the card
   counter-rotates at the same rate so the words stay upright while it travels. */
.wk-fact {
  position: absolute; inset: 0; display: grid; place-items: center;
  overflow: hidden;
  pointer-events: none;                  /* taps go through to the real controls */
  z-index: 1;                            /* under the clock and the buttons, always */
}
.wk-fact-arm { animation: wkOrbit 26s linear infinite; }
.wk-fact-pos { transform: translateX(clamp(150px, 27vw, 330px)); }
.wk-fact-pos > .wk-fact-y { animation: wkOrbitUpright 26s linear infinite; }
.wk-fact-y {
  width: min(300px, 34vw);
  background: rgba(0, 0, 0, .58); color: #fff;
  border-radius: 14px; padding: clamp(10px, 1.8vh, 16px) clamp(12px, 1.8vw, 18px);
  font-size: clamp(13px, 1.9vh, 17px); line-height: 1.45; font-weight: 500;
  box-shadow: 0 6px 24px rgba(0, 0, 0, .3);
}
@keyframes wkOrbit        { to { transform: rotate(360deg); } }
@keyframes wkOrbitUpright { to { transform: rotate(-360deg); } }
.wk-fact-eyebrow {
  display: block; font-size: clamp(10px, 1.4vh, 12px); letter-spacing: .12em;
  text-transform: uppercase; opacity: .75; margin-bottom: 4px; font-weight: 700;
}

/* ⚠ Motion off = pinned, not gone. The teaching is the point; the bouncing is the garnish. */
@media (prefers-reduced-motion: reduce) {
  .wk-fact-arm, .wk-fact-pos > .wk-fact-y { animation: none; }
  /* ⚠ PINNED, NOT HIDDEN. The fact is the teaching; the orbit is only decoration. */
  .wk-fact-pos { transform: none; }
}

#nurseKiosk:not(.nk-alarm) .nk-shoot {
  background: var(--accent); color: #fff; border-color: var(--accent);
}

/* ---- Patient grievances ---------------------------------------------------------------------
   ⚠ Deliberately calm. This screen holds what a patient said about their own care; it should not
   look like an alarm console. The only colour is on the two states that mean somebody is late. */
.grv-card { margin-bottom: 12px; }
.grv-late { border-left: 3px solid var(--danger); }
.grv-head {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 14px;
  cursor: pointer;
}
.grv-head:hover b { text-decoration: underline; }
.grv-state { display: flex; gap: 6px; flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }
.grv-pill {
  font-size: var(--t-xs); padding: 2px 9px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
}
.grv-pill.ok  { border-color: var(--ok, #2f7d55); color: var(--ok, #2f7d55); }
.grv-pill.bad { border-color: var(--danger); color: var(--danger); font-weight: 600; }
.grv-pill.esc { border-color: #b57d00; color: #b57d00; }
.grv-steps { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 10px; }
.grv-step { font-size: var(--t-xs); color: var(--muted); }
.grv-step.done { color: var(--ok, #2f7d55); }
.grv-body { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 14px; }
.grv-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px 18px; margin-bottom: 14px;
}
.grv-facts > div { display: flex; flex-direction: column; gap: 2px; }
.grv-said {
  margin: 0 0 14px; padding: 10px 12px; border-left: 2px solid var(--line);
  color: var(--ink); white-space: pre-wrap; line-height: 1.5;
}
.grv-text {
  width: 100%; box-sizing: border-box; padding: 10px 12px; border-radius: 8px;
  border: 1px solid var(--line); background: var(--card); color: var(--ink);
  font: inherit; line-height: 1.5; resize: vertical;
}
.grv-text:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.wtr-row-between { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 12px; }
.wtr-seg { display: inline-flex; border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.wtr-segb {
  padding: 6px 14px; border: 0; background: transparent; color: var(--muted);
  font: inherit; cursor: pointer;
}
.wtr-segb.on { background: var(--accent); color: #fff; }

/* The two "what happens next" boxes on the grievance form. Big enough to hit reliably on a wall
   iPad with gloves on — a mis-tap here files a complaint as resolved that nobody resolved. */
.nk-check.nk-big {
  display: flex; align-items: flex-start; gap: 14px;
  padding: 16px 18px; margin-bottom: 12px;
  border: 2px solid var(--line); border-radius: 12px;
  font-size: 21px; line-height: 1.35; cursor: pointer;
}
.nk-check.nk-big input { width: 26px; height: 26px; margin-top: 2px; flex: 0 0 auto; }
.nk-check.nk-big small { display: block; font-size: 16px; color: var(--muted); margin-top: 4px; }
.nk-check.nk-big:has(input:checked) { border-color: var(--accent); }
.wtr-mini {
  font: inherit; font-size: var(--t-xs); padding: 5px 10px; white-space: nowrap;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg); color: var(--ink); cursor: pointer;
}
.wtr-mini:hover:not(:disabled) { border-color: var(--ink); }
.wtr-concerns { margin: 4px 0 0; padding-left: 18px; color: var(--muted); font-size: var(--t-xs); }

/* The flagged-photo viewer. A full overlay because the point is to LOOK — a thumbnail strip in a
   table cell is exactly the thing people skim past. */
.wtr-shots {
  position: fixed; inset: 0; z-index: 60; overflow: auto;
  background: color-mix(in srgb, var(--bg) 92%, black); padding: var(--s4);
}
.wtr-shots-head { display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap; margin-bottom: var(--s4); }
.wtr-shots-head b { font-size: var(--t-lg); }
.wtr-shots-head span { flex: 1 1 auto; font-size: var(--t-sm); color: var(--muted); }
.wtr-shots-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s3); }
.wtr-shots-grid figure { margin: 0; }
.wtr-shots-grid img { width: 100%; border-radius: var(--r-sm); background: var(--surface-3); display: block; }
.wtr-shots-grid figcaption { margin-top: 4px; font-size: var(--t-xs); color: var(--muted); }

/* ---- Clinical Compliance: step types added when the real P&P was modelled ------------------- */

/* Precondition "No" — the honest option. Warm rather than alarming: waiting for the RO to run is
   the CORRECT action, not a failure, and the styling should not punish choosing it. */
.wk-row-warn { border-color: var(--warn); color: var(--ink); }
.wk-row-skip { justify-content: center; color: var(--muted); font-size: var(--t-md); min-height: 64px; }

/* Counted step progress. Dots, not a number alone — countable at a glance from arm's length while
   holding a container in the other hand. */
.wk-count { display: flex; align-items: center; justify-content: center; gap: 14px; }
.wk-count b { font-size: clamp(18px, 2.4vh, 24px); font-weight: 700; color: var(--primary-ink); }
.wk-dot { width: 26px; height: 26px; border-radius: 50%; border: 3px solid var(--line-strong); background: transparent; }
.wk-dot.on { background: var(--primary); border-color: var(--primary); }

/* Numeric reading (conductivity, pH). Continuous values, so swatches cannot express them. */
.wk-num {
  text-align: center; font-size: clamp(48px, 9vh, 90px); font-weight: 800;
  color: var(--primary-ink); font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.wk-num i { font-size: .4em; font-style: normal; color: var(--muted); margin-left: .25em; letter-spacing: 0; }
.wk-expect { text-align: center; font-size: var(--t-md); color: var(--muted); margin: -4px 0 8px; }

/* ⚠ THE STOP SCREEN. NO RINSEBACK is the largest element on the display, above the explanation,
   because it is the one instruction that cannot be missed or deferred — blood in the circuit has
   been exposed to chloraminated dialysate and must not be returned to the patient. */
.wk-stop { background: #8c1008; }
.wk-norinse {
  margin: 6px 0 10px; padding: 14px 26px;
  border: 4px solid #fff; border-radius: var(--r-md);
  font-size: clamp(30px, 6vh, 62px); font-weight: 900; letter-spacing: .04em; line-height: 1.05;
  animation: wk-pulse 1.1s ease-in-out infinite;
}
@keyframes wk-pulse { 0%,100% { opacity: 1; } 50% { opacity: .55; } }
@media (prefers-reduced-motion: reduce) { .wk-norinse { animation: none; } }

/* Corrective action in progress — real, but NOT a stop. Deliberately amber rather than red: if
   this screen shouted DO NOT TREAT for a retest, people would learn the screen cries wolf. */
.wk-action { background: #8a5a00; }

.wtr-acting { display: flex; flex-direction: column; gap: 4px; padding: 14px 18px; border-radius: var(--r-md); background: var(--warn); color: #17251f; font-size: var(--t-base); }

/* A commitment that measures itself. Marked plainly, because the distinction between an audit a
   person filled in and one the records answered is the whole argument of the module. */
.wtr-auto { font-size: var(--t-xs); color: var(--good, var(--muted)); font-weight: 600; }
.wtr-frac { color: var(--muted); font-weight: 400; }

/* Signed off to test, but no PIN set yet. Shown rather than hidden: a name missing from the grid
   reads as "the system is broken" and sends people back to paper, which is the one outcome this
   module cannot afford. */
.wk-tile-nopin { opacity: .55; }
.wk-tile-nopin small { display: block; margin-top: 4px; font-size: .6em; font-weight: 400; }

/* "Choose your PIN" — for staff onboarded before the PIN joined account setup. Dismissible on
   purpose: a modal nobody can escape at 5am with a patient waiting is a modal that gets the whole
   app called broken. It comes back on the next load. */
.pin-prompt { position: fixed; inset: 0; z-index: 70; display: grid; place-items: center;
  background: color-mix(in srgb, var(--bg) 80%, black); padding: var(--s4); }
.pin-prompt-card { width: min(440px, 100%); background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); padding: var(--s4); box-shadow: var(--shadow-lg, 0 10px 40px rgba(0,0,0,.3)); }
.pin-prompt-card h2 { margin: 0 0 8px; font-size: var(--t-lg); }
.pin-prompt-card p { margin: 0 0 14px; font-size: var(--t-sm); color: var(--muted); line-height: 1.5; }
.pin-prompt-card label { display: block; margin-bottom: 12px; font-size: var(--t-xs); color: var(--muted); }
.pin-prompt-card input { display: block; width: 100%; margin-top: 4px; font: inherit; font-size: var(--t-md);
  padding: 10px 12px; min-height: 42px; border: 1px solid var(--line); border-radius: var(--r-sm);
  background: var(--bg); color: var(--ink); }
.pin-prompt-card .pin-why { font-size: var(--t-xs); margin-bottom: 6px; }
.pin-actions { display: flex; justify-content: space-between; align-items: center; gap: var(--s3); margin-top: 8px; }

/* Clinical Compliance setup. Plain forms in the order a clinic is actually brought up — the screen
   without which every route in the module is unreachable by a human being. */
.wtr-switch { display: flex; justify-content: flex-end; }
.wtr-h2 { margin: 0; font-size: var(--t-lg); font-weight: 700; }
.wtr-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: var(--s3);
  align-items: end; padding: 14px 16px; border: 1px solid var(--line);
  border-radius: var(--r-md); background: var(--surface);
}
.wtr-form label { display: flex; flex-direction: column; gap: 4px; font-size: var(--t-xs); color: var(--muted); }
.wtr-form label.wtr-wide { grid-column: 1 / -1; }
.wtr-form input, .wtr-form select, .wtr-form textarea {
  font: inherit; font-size: var(--t-sm); padding: 8px 10px; min-height: 38px;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg); color: var(--ink);
}
.wtr-form textarea { min-height: 66px; resize: vertical; }
.wtr-form .wtr-exp-msg { grid-column: 1 / -1; }
/* An enrolment code is single-use and dies in 30 minutes, so it is shown once, big, and never
   stored anywhere for someone to write down. */
.wtr-code { font-family: var(--font-mono, ui-monospace, monospace); font-size: var(--t-lg); letter-spacing: .12em; }

/* Signature pad. touch-action:none is load-bearing — without it the first stroke scrolls the page
   instead of drawing, and the technician concludes the screen is broken. */
.wk-sig {
  width: 100%; max-width: 560px; height: clamp(140px, 22vh, 220px);
  border: 2px dashed var(--line); border-radius: var(--r-md); background: #fff;
  touch-action: none; cursor: crosshair; align-self: center;
}
.wk-sig-row { display: flex; gap: var(--s3); justify-content: center; margin-top: 12px; }

/* The on-screen log. Deliberately the same columns, in the same order, as the printed TCL-1 — what
   a manager reads on screen and what a surveyor is handed should be the one document, not two
   views that have to be reconciled. Scrolls inside itself; the page never scrolls sideways. */
/* ⚠ flex: 0 0 auto IS LOAD-BEARING — without it the log renders at ZERO HEIGHT.
   .wtr-wrap is a flex column of fixed height. A flex item whose overflow is not `visible` gets an
   automatic minimum size of 0 instead of its content size, so this wrapper — the only table on the
   page inside an overflow box — was squashed flat while the table inside it was 2,800px tall. The
   heading and the note rendered, the table did not, and nothing in the DOM looked wrong. */
.wtr-logwrap { flex: 0 0 auto; overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
/* Two blocks side by side, sixteen columns — it will not fit any screen, so the wrapper scrolls
   sideways and the page never does. That is the same trade the paper sheet makes by being wide. */
.wtr-log { width: 100%; min-width: 1560px; border-collapse: collapse; font-size: var(--t-xs); }
.wtr-tankhead th { text-align: center; font-size: var(--t-sm); letter-spacing: .04em;
  border-bottom: 1px solid var(--line); background: var(--surface-3, var(--panel)); }
.wtr-log .wtr-gutter { width: 14px; min-width: 14px; padding: 0; background: var(--bg);
  border-top: none; border-bottom: none; }
.wtr-day td { background: var(--surface-2, var(--panel)); font-weight: 700; padding: 5px 8px;
  border-top: 1px solid var(--line); }
.wtr-log .wtr-val.bad { color: var(--danger); }
.wtr-log th {
  text-align: left; padding: 7px 8px; font-weight: 700; color: var(--ink);
  background: var(--surface-2, var(--panel)); border-bottom: 1px solid var(--line); vertical-align: top;
}
.wtr-log th small { display: block; font-weight: 400; font-size: .85em; color: var(--muted); }
.wtr-log td { padding: 6px 8px; border-bottom: 1px solid var(--surface-3); vertical-align: middle; }
.wtr-log tr.bad td { background: var(--danger-soft); }
.wtr-log tr.bad td b { color: var(--danger); }
.wtr-val { font-size: var(--t-md); font-weight: 700; }
/* A check voided because nobody countersigned it inside the six-minute clock, and run again. It
   stays on the sheet — how often nobody came is the pattern a surveyor most wants to see — but it
   must never read as a live result: the row goes quiet and the reading is struck through. */
.wtr-log td.wtr-void { color: var(--muted); background: var(--surface-2, var(--panel)); }
.wtr-log td.wtr-void.wtr-val { font-weight: 400; text-decoration: line-through; color: var(--muted); }
.wtr-log td.wtr-void img { opacity: .45; filter: grayscale(1); }
.wtr-log td.wtr-void i { color: var(--muted); }
.wtr-shot img { display: block; width: 76px; height: 57px; object-fit: cover; border-radius: 4px; background: var(--surface-3); }
.wtr-sign { min-width: 116px; }
.wtr-sign img { display: block; height: 30px; max-width: 116px; object-fit: contain; }
.wtr-sign i { display: block; font-style: normal; font-size: .9em; color: var(--muted); border-top: 1px solid var(--line); padding-top: 2px; margin-top: 2px; }

/* "Recording" badge. A technician must never be unsure whether the camera is running — the whole
   value of a continuous take is that everyone knows it is continuous. */
.wk-rec {
  position: absolute; top: clamp(10px, 2vh, 20px); right: clamp(12px, 2vw, 24px);
  display: flex; align-items: center; gap: 7px;
  font-size: var(--t-sm); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: var(--danger);
}
.wk-rec i { width: 11px; height: 11px; border-radius: 50%; background: var(--danger); animation: wkrec 1.4s ease-in-out infinite; }
@keyframes wkrec { 0%,100% { opacity: 1; } 50% { opacity: .25; } }
@media (prefers-reduced-motion: reduce) { .wk-rec i { animation: none; } }

/* Typing the expiry printed on the bottle, right under the camera showing it. */
.wk-expiry { display: flex; flex-direction: column; gap: 6px; align-self: center; width: min(460px, 100%);
  font-size: clamp(15px, 2vh, 19px); color: var(--muted); }
.wk-expiry input {
  font: inherit; font-size: clamp(22px, 3.4vh, 32px); font-weight: 700; text-align: center;
  padding: 12px; min-height: 64px; letter-spacing: .06em;
  border: 2px solid var(--line); border-radius: var(--r-md); background: var(--bg); color: var(--ink);
}

/* The timer, armed but not running. The camera is already filming; what has not started is the
   countdown — because a countdown that begins while the strip is still in someone's hand measures
   the wrong thing, and every second of that is a second the record claims it was soaking. */
.wk-armed { gap: clamp(12px, 2vh, 20px); }
.wk-armed-sub { font-size: clamp(16px, 2.4vh, 22px); color: var(--muted); text-align: center; max-width: 44ch; }
.wk-btn-go { font-size: clamp(20px, 3vh, 28px); min-height: 84px; padding: 0 clamp(24px, 5vw, 56px); }

/* The nurse's counter-signature. Two photographs side by side — what the technician saw, and what
   she is being shown — because that pairing is the entire point of a second person. */
.wtr-verify { display: grid; grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); gap: var(--s3); }
.wtr-vcard { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); padding: var(--s3); }
.wtr-vhead { display: flex; align-items: baseline; gap: var(--s3); flex-wrap: wrap; margin-bottom: 8px; }
.wtr-vhead b { font-size: var(--t-xl); }
.wtr-vhead span { font-size: var(--t-xs); color: var(--muted); }
.wtr-vshots { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s2); }
.wtr-vshots figure { margin: 0; }
.wtr-vshots img, .wtr-vcam { width: 100%; aspect-ratio: 4/3; object-fit: cover; border-radius: var(--r-sm); background: var(--surface-3); display: block; }
.wtr-vcam video { width: 100%; height: 100%; object-fit: cover; border-radius: var(--r-sm); }
.wtr-vshots figcaption { margin-top: 3px; font-size: var(--t-xs); color: var(--muted); }
.wtr-vsig { width: 100%; height: 96px; margin-top: var(--s2); background: #fff; touch-action: none;
  border: 2px dashed var(--line); border-radius: var(--r-sm); cursor: crosshair; display: block; }
.wtr-vrow { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; margin-top: var(--s2); }
/* The nurse's photograph, above her signature in the Nurse cell. */
/* ⚠ .wtr-nphoto is gone: the nurse's photograph moved out of her signature cell into its own
   column, so it is sized by .wtr-shot like the technician's and needs no override. Removed rather
   than left behind — a rule matching nothing is a rule somebody later trusts. */
/* The running count of stills taken during the soak. The deterrent is that a technician can watch
   it climb — and unlike a bluff, it is true, so nobody can ever discover otherwise. */
.wk-shots { font-size: clamp(14px, 2vh, 18px); color: var(--muted); letter-spacing: .04em; }

/* THE FILMING VIEW. Camera edge to edge, instruction over the top, controls at the bottom — it is
   meant to read as a camcorder, because a technician who believes they are being filmed does the
   job properly, and that belief carries more weight here than any control could.
   ⚠ No video is recorded. The pretence lives on this screen and nowhere else — see the ⚠ block at
   the top of public/water.js, and never let it reach a record or an export. */
.wk-film { position: relative; padding: 0; overflow: hidden; background: #000; }
.wk-film-cam { position: absolute; inset: 0; }
.wk-film-cam video, .wk-film-cam .wk-video {
  width: 100%; height: 100%; object-fit: cover; display: block;
}
.wk-film-top {
  position: absolute; top: 0; left: 0; right: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between;
  padding: clamp(12px, 2.4vh, 22px) clamp(16px, 3vw, 28px);
  background: linear-gradient(to bottom, rgba(0,0,0,.55), transparent);
}
.wk-film-top .wk-rec { position: static; color: #fff; }
.wk-film-top .wk-rec i { background: #ef4444; }
.wk-tc { font-variant-numeric: tabular-nums; font-size: clamp(15px, 2.2vh, 20px); font-weight: 700;
  color: #fff; letter-spacing: .1em; text-shadow: 0 1px 3px rgba(0,0,0,.6); }
.wk-film-body {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: clamp(10px, 1.8vh, 18px);
  padding: clamp(20px, 4vh, 44px) clamp(16px, 4vw, 40px) clamp(22px, 4vh, 44px);
  background: linear-gradient(to top, rgba(0,0,0,.78) 55%, transparent);
  text-align: center;
}
.wk-film-label { font-size: clamp(20px, 3.2vh, 30px); font-weight: 700; color: #fff;
  text-shadow: 0 1px 4px rgba(0,0,0,.5); max-width: 26ch; }
.wk-film-help { font-size: clamp(14px, 2vh, 18px); color: rgba(255,255,255,.86); max-width: 40ch; line-height: 1.4; }
.wk-film .wk-count { color: #fff; }
.wk-film .wk-dot { border-color: rgba(255,255,255,.6); }
.wk-film .wk-dot.on { background: #fff; border-color: #fff; }
.wk-film .wk-expiry { color: rgba(255,255,255,.86); }
.wk-film .wk-expiry input { background: rgba(255,255,255,.94); color: #0f172a; border-color: transparent; }
.wk-film .wk-err { color: #fecaca; }
/* The countdown, over the picture. Big enough to read across a water room. */
/* ⚠ NO LONGER `position:absolute; inset:0`. Pinned dead-centre of the iPad while the instructions
   grew upward from the bottom, the countdown and "Swish the strip — keep it moving" printed
   straight through each other and neither was readable. It is a row in a column now: the stage
   holds the clock, the body holds the words, and no screen height can make them collide. */
.wk-film-clock {
  font-size: clamp(88px, 22vh, 190px); font-weight: 800; font-variant-numeric: tabular-nums;
  line-height: 1; color: #fff; text-shadow: 0 4px 24px rgba(0,0,0,.6); pointer-events: none;
}

/* The band between the REC bar and the instructions: the clock, the rule, and the orbiting fact. */
.wk-film-run .wk-film-stage {
  position: relative; flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: clamp(8px, 1.6vh, 16px); padding: clamp(8px, 2vh, 20px);
}

/* ⚠ THE RULE WE KEEP BEING CITED ON, ON THE SCREEN WHERE THE COUNTING HAPPENS. Not a caption —
   it sits directly under the number, in the accent, so the count and its rule are read as one
   thing. "Not more" earns its place: an over-soaked strip reads high, gets re-run, and quietly
   teaches a technician that the number is a suggestion. */
.wk-film-rule {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; justify-content: center;
  padding: 7px 18px; border-radius: 999px;
  background: rgba(31,138,118,.22); border: 2px solid var(--primary);
  color: #fff; text-shadow: 0 1px 3px rgba(0,0,0,.5); text-align: center;
}
.wk-film-rule b { font-size: clamp(17px, 2.6vh, 24px); font-weight: 800; letter-spacing: .01em; }
.wk-film-rule span { font-size: clamp(14px, 2.1vh, 19px); font-weight: 600;
  text-transform: uppercase; letter-spacing: .08em; opacity: .95; }

/* ⚠ THE HARM, NAMED. Owner, 2026-08-17: "they have to know it can cause Hemolysis." The 2026
   technician knew the number was thirty and did twenty — knowing the number was never what
   failed. What makes somebody wait when they are behind is knowing what the count is for. */
.wk-film-harm {
  max-width: 52ch; text-align: center;
  font-size: clamp(14px, 2vh, 18px); line-height: 1.5;
  color: #fde8e8; background: rgba(127,29,29,.45);
  border: 1px solid rgba(248,113,113,.5); border-radius: var(--r-md, 10px);
  padding: 10px 16px;
}
.wk-film-harm b { color: #fff; font-weight: 800; }
.wk-film-run { display: flex; flex-direction: column; }
.wk-film-run .wk-film-top { position: static; flex: 0 0 auto; }
/* margin-top:auto keeps the instructions pinned to the bottom even on the "saving the
   photographs" view, which has no stage row at all. */
.wk-film-run .wk-film-body {
  position: static; margin-top: auto; flex: 0 0 auto;
  background: linear-gradient(to top, rgba(0,0,0,.72) 50%, transparent);
}

/* ⚠ THE CAMERA LAYER. It sits under the interface and is NEVER replaced by a render — iOS pauses a
   <video> the moment it is moved in the DOM, and rebuilding the screen around it froze the picture.
   One element, mounted once, for the whole test. */
.wk-filmlayer { position: absolute; inset: 0; z-index: 0; background: #000; display: none; }
.wk-filmlayer video, .wk-filmlayer .wk-video { width: 100%; height: 100%; object-fit: cover; display: block; }
.water-kiosk .wk-filming .wk-filmlayer, #waterKiosk.wk-filming .wk-filmlayer { display: block; }
.wk-ui { position: relative; z-index: 1; height: 100%; }
.wk-camfail { color: #fecaca; font-size: clamp(16px, 2.4vh, 22px); padding: var(--s5); text-align: center; }
/* ⚠ DO NOT ADD `#waterKiosk { position: relative }` HERE. It was added to anchor the camera layer,
   and because it sits LATER in this file than the real rule at the top of the kiosk block, it
   overrode `position: fixed` — so the kiosk stopped being a full-screen overlay, collapsed to zero
   height, and the whole station screen went blank. `position: fixed` already establishes the
   containing block that absolutely-positioned children need. */

/* The filming screens are transparent so the layer below shows through. */
.wk-filming .wk-film { background: transparent; }

/* MORE PROMINENT: the technician must be in no doubt they are being filmed, and must be able to
   follow the instruction from across the room without asking anyone. */
.wk-film-top { padding: clamp(16px, 3vh, 28px) clamp(18px, 3.5vw, 34px); }
.wk-film-top .wk-rec {
  font-size: clamp(18px, 2.8vh, 26px); letter-spacing: .16em;
  background: rgba(239,68,68,.16); border: 2px solid #ef4444; border-radius: 999px;
  padding: 6px 16px 6px 12px; gap: 10px;
}
.wk-film-top .wk-rec i { width: 15px; height: 15px; }
.wk-tc { font-size: clamp(18px, 2.6vh, 24px); }
.wk-film-label { font-size: clamp(26px, 4.4vh, 42px); line-height: 1.15; max-width: 22ch; }
.wk-film-help {
  font-size: clamp(17px, 2.6vh, 24px); line-height: 1.45; max-width: 34ch;
  color: #fff; background: rgba(0,0,0,.42); border-radius: var(--r-md);
  padding: 10px 16px;
}

/* The expired-bottle fork, and any other full-screen decision. Deliberately NOT the red DO-NOT-
   TREAT screen — this is a thing to sort out, not a patient-safety stop, and using the same
   styling for both would blunt the one that matters. */
.wk-stopcard { display: grid; place-items: center; background: var(--bg); }
.wk-stopcard-in { text-align: center; max-width: 32ch; display: flex; flex-direction: column;
  align-items: center; gap: clamp(12px, 2vh, 20px); }
.wk-stopcard-in h1 { font-size: clamp(28px, 5vh, 46px); margin: 0; }
.wk-stop-typed { font-size: clamp(17px, 2.4vh, 22px); color: var(--muted); margin: 0; }
.wk-stop-typed b { font-size: 1.3em; color: var(--ink); letter-spacing: .06em; }
.wk-stop-why { font-size: clamp(16px, 2.2vh, 20px); color: var(--muted); line-height: 1.5; margin: 0; }
.wk-btn-danger { background: var(--danger); border-color: var(--danger); color: #fff; }

/* ALERTS. Above the button, in the path of the hand — small red text underneath gets pressed
   straight through. Loud enough to stop somebody who is already reaching for the control. */
.wk-alert {
  display: flex; align-items: center; gap: 12px; align-self: stretch;
  max-width: 44ch; margin: 0 auto;
  padding: clamp(12px, 2vh, 18px) clamp(14px, 2.4vw, 22px);
  background: var(--danger); color: #fff; border-radius: var(--r-md);
  font-size: clamp(16px, 2.4vh, 21px); font-weight: 650; line-height: 1.35; text-align: left;
  box-shadow: 0 6px 24px rgba(0,0,0,.35);
  animation: wkAlertIn .18s ease-out both;
}
.wk-alert-bang {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
  background: rgba(255,255,255,.22); display: grid; place-items: center;
  font-weight: 800; font-size: 19px;
}
@keyframes wkAlertIn { from { opacity: 0; transform: translateY(8px) scale(.97); } }
@media (prefers-reduced-motion: reduce) { .wk-alert { animation: none; } }
/* On the filming screens the alert sits above the button in the bottom overlay. */
.wk-film-body .wk-alert { order: -1; }

/* THE HAND-OFF. The test is not finished when the reading is saved — the nurse still has to be
   given the strip and the bottle, and if this screen does not say so, the strip goes in the bin. */
.wk-handoff {
  align-self: center; max-width: 42ch; text-align: left;
  border: 2px solid var(--accent, var(--ink)); border-radius: var(--r-md);
  padding: clamp(14px, 2.4vh, 20px) clamp(16px, 3vw, 24px);
  background: var(--surface);
}
.wk-handoff-h { font-size: clamp(18px, 2.8vh, 24px); font-weight: 700; margin-bottom: 8px; }
.wk-handoff-steps { margin: 0 0 8px; padding-left: 20px; font-size: clamp(15px, 2.2vh, 19px); line-height: 1.5; }
.wk-handoff-steps li { margin-bottom: 4px; }
.wk-handoff-why { margin: 0; font-size: clamp(13px, 1.9vh, 16px); color: var(--muted); line-height: 1.45; }

/* OUT OF RANGE. Loud, but deliberately NOT the DO-NOT-TREAT red — that screen means blood must not
   be returned to a patient, and if an elevated first reading looks identical to a stop, the stop
   stops meaning anything. Amber: something is wrong, and here is exactly what you do about it. */
.wk-oor { display: grid; place-items: center; background: #7c2d12; color: #fff; }
.wk-oor-in { text-align: center; max-width: 34ch; display: flex; flex-direction: column;
  align-items: center; gap: clamp(8px, 1.6vh, 16px); padding: var(--s4); }
.wk-oor-bang { width: clamp(48px, 8vh, 74px); height: clamp(48px, 8vh, 74px);
  display: grid; place-items: center; border-radius: 50%; background: rgba(255,255,255,.16); }
.wk-oor-bang svg { width: 60%; height: 60%; }
.wk-oor-value { font-size: clamp(52px, 11vh, 96px); font-weight: 800; line-height: 1; letter-spacing: -.02em; }
.wk-oor-value small { font-size: .28em; font-weight: 600; opacity: .8; margin-left: .3em; }
.wk-oor h1 { margin: 0; font-size: clamp(24px, 4.2vh, 40px); letter-spacing: .06em; }
.wk-oor-limit { margin: 0; font-size: clamp(14px, 2vh, 18px); opacity: .8; }
.wk-oor h2 { margin: clamp(8px, 1.6vh, 16px) 0 0; font-size: clamp(19px, 3vh, 27px); }
.wk-oor-detail { margin: 0; font-size: clamp(15px, 2.2vh, 19px); line-height: 1.5; opacity: .92; }
.wk-oor .wk-btn { background: #fff; color: #7c2d12; border-color: #fff; margin-top: clamp(6px, 1.4vh, 14px); }
.wk-alert-amber { background: #b45309; }

/* ---- Nurses' station kiosk -------------------------------------------------------------------
   Sized in vh/vw throughout, because this is read from across a room by somebody who is walking
   toward it — not from 40cm away like a phone. Every number here is deliberately larger than it
   looks right on a laptop.

   ⚠ The red state must never be reachable by CSS alone. `.nk-alarm` is set from the board data,
   so a stylesheet edit cannot make a clinic look calm. */
#nurseKiosk { position: fixed; inset: 0; z-index: 9500; background: var(--bg); overflow: auto;
  font-family: var(--font); color: var(--ink); -webkit-user-select: none; user-select: none; }
#nurseKiosk.nk-alarm { background: #7f1d1d; color: #fff; }

/* The alarm screen. */
.nk-red { min-height: 100%; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: clamp(12px, 2.4vh, 28px); text-align: center;
  padding: clamp(20px, 4vh, 56px) clamp(20px, 5vw, 72px); }
.nk-red-head h1 { margin: 0; font-size: clamp(28px, 6vh, 64px); font-weight: 800; letter-spacing: .01em; }
.nk-bang { width: clamp(52px, 9vh, 96px); height: clamp(52px, 9vh, 96px); margin: 0 auto clamp(8px, 1.6vh, 18px);
  border-radius: 50%; background: #fff; color: #7f1d1d; font-size: clamp(32px, 6vh, 62px);
  font-weight: 900; line-height: clamp(52px, 9vh, 96px);
  animation: nkPulse 1.1s ease-in-out infinite; }
@keyframes nkPulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.09); opacity: .82; } }
@media (prefers-reduced-motion: reduce) { .nk-bang { animation: none; } }
.nk-value { margin: clamp(4px, 1vh, 10px) 0 0; font-size: clamp(20px, 3.6vh, 38px); font-weight: 700; }
.nk-since { margin: 0; font-size: clamp(13px, 1.9vh, 18px); opacity: .85; }
.nk-do { background: rgba(0, 0, 0, .22); border-radius: 14px; padding: clamp(14px, 2.4vh, 26px) clamp(18px, 3vw, 34px);
  max-width: 62ch; text-align: left; }
.nk-do h2 { margin: 0 0 clamp(6px, 1.2vh, 12px); font-size: clamp(15px, 2.2vh, 21px); text-transform: uppercase;
  letter-spacing: .08em; opacity: .9; }
.nk-do ol { margin: 0; padding-left: 1.3em; }
.nk-do li { font-size: clamp(15px, 2.4vh, 22px); line-height: 1.5; margin-bottom: .45em; }
.nk-promise { margin: 0; font-size: clamp(15px, 2.3vh, 21px); line-height: 1.5; }
.nk-answer { background: #fff; color: #7f1d1d; border: 0; border-radius: 12px; font-weight: 800;
  font-size: clamp(17px, 2.8vh, 26px); padding: clamp(14px, 2.4vh, 24px) clamp(28px, 5vw, 56px);
  cursor: pointer; box-shadow: 0 8px 24px rgba(0, 0, 0, .28); }
.nk-answer:active { transform: translateY(1px); }
.nk-more { margin: 0; font-size: clamp(13px, 1.9vh, 17px); opacity: .8; }

/* Signing off. */
.nk-answer-wrap { max-width: 780px; margin: 0 auto; padding: clamp(18px, 3.5vh, 44px) clamp(18px, 5vw, 40px);
  display: flex; flex-direction: column; gap: clamp(8px, 1.4vh, 14px); }
.nk-answer-wrap h1 { margin: 0; font-size: clamp(20px, 3.4vh, 32px); }
.nk-back { align-self: flex-start; background: none; border: 0; color: inherit; opacity: .8;
  font-size: clamp(14px, 2vh, 18px); cursor: pointer; padding: 6px 0; }
.nk-lbl { font-size: clamp(13px, 1.9vh, 17px); font-weight: 700; opacity: .9; margin-top: clamp(4px, .8vh, 10px); }
.nk-names { display: flex; flex-wrap: wrap; gap: 10px; }
.nk-name { background: rgba(255, 255, 255, .12); color: inherit; border: 2px solid transparent;
  border-radius: 12px; padding: 12px 18px; font-size: clamp(15px, 2.2vh, 20px); font-weight: 600; cursor: pointer; }
#nurseKiosk:not(.nk-alarm) .nk-name { background: var(--panel); border-color: var(--line); }
.nk-name.on { border-color: currentColor; background: rgba(255, 255, 255, .28); }
.nk-name.off { opacity: .45; cursor: not-allowed; }
.nk-name small { display: block; font-size: 11px; font-weight: 500; opacity: .8; }
.nk-pin, .nk-num, .nk-text { width: 100%; border-radius: 10px; border: 2px solid var(--line);
  padding: 12px 14px; font-size: clamp(16px, 2.4vh, 22px); font-family: inherit;
  background: var(--panel); color: var(--ink); }
.nk-pin { letter-spacing: .5em; }
.nk-row { display: flex; gap: 14px; }
.nk-row > div { flex: 1; }
.nk-submit { margin-top: clamp(8px, 1.6vh, 18px); background: #047857; color: #fff; border: 0;
  border-radius: 12px; font-weight: 800; font-size: clamp(16px, 2.6vh, 22px);
  padding: clamp(13px, 2.2vh, 20px); cursor: pointer; }
.nk-submit[disabled] { opacity: .6; cursor: default; }
.nk-err { margin: 0; color: #fecaca; font-weight: 700; font-size: clamp(14px, 2vh, 18px); }
#nurseKiosk:not(.nk-alarm) .nk-err { color: #b91c1c; }

/* The quiet board. */
.nk-board { max-width: 1000px; margin: 0 auto; padding: clamp(24px, 5vh, 64px) clamp(20px, 5vw, 48px); }
.nk-head h1 { margin: 0; font-size: clamp(26px, 5vh, 46px); }
.nk-head p { margin: 4px 0 clamp(18px, 3.4vh, 36px); opacity: .75; font-size: clamp(14px, 2vh, 18px); }
.nk-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: clamp(12px, 2vw, 20px); }
.nk-tile { display: flex; flex-direction: column; align-items: flex-start; gap: 4px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 16px;
  padding: clamp(16px, 2.6vh, 26px); cursor: pointer; text-align: left; color: inherit; font-family: inherit; }
.nk-tile[disabled] { opacity: .5; cursor: default; }
.nk-tile-n { font-size: clamp(30px, 5vh, 46px); font-weight: 800; line-height: 1; }
.nk-tile-l { font-size: clamp(15px, 2.2vh, 19px); font-weight: 700; }
.nk-tile-note { font-size: clamp(12px, 1.7vh, 15px); opacity: .7; }
.nk-due { list-style: none; margin: clamp(18px, 3vh, 30px) 0 0; padding: 0; }
.nk-due li { padding: 10px 0; border-bottom: 1px solid var(--line); font-size: clamp(14px, 2vh, 18px); }
.nk-empty { opacity: .8; font-size: clamp(14px, 2vh, 18px); line-height: 1.5; }
.nk-lead { margin: 0 0 clamp(6px, 1.2vh, 12px); font-size: clamp(14px, 2vh, 18px); line-height: 1.5; opacity: .8; }
.nk-check { display: flex; align-items: center; gap: 10px; font-size: clamp(14px, 2vh, 18px); margin-top: 6px; }
.nk-check input { width: 22px; height: 22px; }
.nk-sent { align-items: center; text-align: center; }
.nk-ok { width: clamp(56px, 9vh, 92px); height: clamp(56px, 9vh, 92px); border-radius: 50%;
  background: #047857; color: #fff; font-size: clamp(30px, 5vh, 52px); line-height: clamp(56px, 9vh, 92px); }
/* Expiry rows on the monthly check. Big targets — she is standing at a cart holding a box. */
.nk-item { display: flex; gap: 10px; align-items: center; margin-bottom: 8px; }
.nk-it-prod { flex: 2 1 0; min-width: 0; }
.nk-it-exp { flex: 1 1 0; min-width: 0; }
.nk-item input[type="text"], .nk-item input[type="date"] {
  border-radius: 10px; border: 2px solid var(--line); padding: 11px 12px;
  font-size: clamp(15px, 2.2vh, 19px); font-family: inherit; background: var(--panel); color: var(--ink); }
.nk-item input[readonly] { opacity: .75; }
.nk-it-gone { display: flex; align-items: center; gap: 6px; font-size: 14px; white-space: nowrap; }
.nk-it-gone input { width: 20px; height: 20px; }
.nk-it-bad { color: #b91c1c; font-weight: 800; font-size: 12px; white-space: nowrap; }
#nurseKiosk.nk-alarm .nk-it-bad { color: #fecaca; }
.nk-addrow { background: none; border: 2px dashed var(--line); border-radius: 10px; color: inherit;
  padding: 10px; font-size: clamp(14px, 2vh, 17px); cursor: pointer; width: 100%; margin-bottom: 6px; }
/* Emergency cart list. State in WORDS as well as colour — read across a room, not at 40cm. */
.nk-cartwrap { max-width: 900px; }
.nk-cart { list-style: none; margin: 12px 0 0; padding: 0; }
.nk-cl { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 12px;
  border: 1px solid var(--line); margin-bottom: 8px; background: var(--panel); }
.nk-cl-main { flex: 1 1 auto; min-width: 0; }
.nk-cl-main b { display: block; font-size: clamp(15px, 2.2vh, 19px); }
.nk-cl-note { display: block; font-size: 12px; opacity: .7; }
.nk-cl-state { flex: 0 0 auto; text-align: right; }
.nk-cl-tag { display: block; font-weight: 800; font-size: clamp(12px, 1.8vh, 15px); letter-spacing: .04em; }
.nk-cl-exp { display: block; font-size: 12px; opacity: .75; }
.nk-cl-btn { flex: 0 0 auto; border: 1px solid var(--line); background: none; color: inherit;
  border-radius: 10px; padding: 10px 16px; font-size: clamp(13px, 2vh, 16px); cursor: pointer; }
.nk-cl-expired, .nk-cl-missing, .nk-cl-on_order { border-color: #b91c1c; background: #fef2f2; }
.nk-cl-expired .nk-cl-tag, .nk-cl-missing .nk-cl-tag, .nk-cl-on_order .nk-cl-tag { color: #b91c1c; }
.nk-cl-expiring { border-color: #b45309; background: #fffbeb; }
.nk-cl-expiring .nk-cl-tag { color: #b45309; }
:root[data-theme="dark"] .nk-cl-expired, :root[data-theme="dark"] .nk-cl-missing,
:root[data-theme="dark"] .nk-cl-on_order { background: #3f1414; }
:root[data-theme="dark"] .nk-cl-expiring { background: #3a2a0a; }
.nk-submit.nk-alt { background: #1d4ed8; }
.nk-submit.nk-warn { background: #b45309; }
/* Drill-in for one audit. */
.wtr-modal { position: fixed; inset: 0; z-index: 9600; background: rgba(0,0,0,.45);
  display: flex; align-items: center; justify-content: center; padding: 20px; }
/* ⚠ `--panel` HAS NEVER BEEN DEFINED. It is not a token this stylesheet declares anywhere — the
   real surface token is `--surface` — so `background: var(--panel)` resolved to *nothing* and this
   card has been fully TRANSPARENT since the water module shipped. It went unnoticed because every
   other element using the same typo sits directly on a pale page, where transparent and white are
   indistinguishable. A modal is the one place it cannot hide: the dialog's text lands on top of the
   dashboard behind it and both are readable at once.
   Fixed here with a fallback rather than by renaming, so the day `--panel` is ever introduced for
   real it still wins. ⚠ 14 OTHER RULES carry the same typo, including the nurse-station kiosk
   (.nk-*) — deliberately left alone rather than restyling live wall iPads as a side effect. */
.wtr-modal-card { background: var(--panel, var(--surface)); border-radius: 14px; padding: 22px 24px;
  max-width: 760px; width: 100%; max-height: 86vh; overflow: auto; position: relative;
  box-shadow: 0 18px 48px rgba(0,0,0,.22); }
.wtr-modal-card h3 { margin: 0 24px 10px 0; }
/* ⚠ `.wtr-note` carries `margin: -12px 0 0` — a NEGATIVE top margin, tuned for the dashboard where
   these notes sit under a heading with a large bottom margin and need pulling back up. Inside a
   modal there is no such gap to reclaim, so the note climbed 12px and printed straight ON TOP of
   the dialog's own title. Neutralised here rather than in .wtr-note itself, because the dashboard
   spacing that negative margin exists for is correct and must not move. */
.wtr-modal-card .wtr-note { margin: 0 0 10px; }
.wtr-modal-card .wtr-note.warn { color: var(--ink); background: rgba(180,83,9,.08);
  border-left: 3px solid #b45309; border-radius: 0 8px 8px 0; padding: 10px 12px; }
.wtr-modal-x { position: absolute; top: 10px; right: 14px; background: none; border: 0;
  font-size: 26px; line-height: 1; color: inherit; cursor: pointer; opacity: .6; }
.wtr-clickable { cursor: pointer; }
.wtr-clickable:hover { background: var(--hover, rgba(127,127,127,.08)); }
.wtr-inline { width: 100%; border: 1px solid transparent; background: transparent; color: inherit;
  font: inherit; padding: 6px 8px; border-radius: 8px; }
.wtr-inline:focus { border-color: var(--line); background: var(--panel); outline: none; }
.wtr-linkish { background: none; border: 0; padding: 0; font: inherit; font-weight: 700;
  color: var(--accent, #1d4ed8); cursor: pointer; text-align: left; }
.wtr-linkish:hover { text-decoration: underline; }
.wtr-itemsbox { margin-top: 18px; padding: 16px 18px; border: 1px solid var(--line); border-radius: 12px; }
.wtr-h4 { margin: 0 0 6px; font-size: 15px; }

/* ---- Clinical Compliance redesign ------------------------------------------------------------
   The board answers "can each floor treat, and what needs a human" before anything else. All
   colors ride the existing tokens so both themes keep working. */
.wtr-clinics { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 12px; margin: 6px 0 4px; }
.wtr-clinic { border: 1px solid var(--line); border-radius: 14px; padding: 14px 16px;
  display: flex; flex-direction: column; gap: 3px; background: var(--panel); }
.wtr-clinic-name { font-weight: 700; font-size: 14px; }
.wtr-clinic-state { font-weight: 800; font-size: 17px; letter-spacing: .02em; }
.wtr-clinic-detail { font-size: 12px; opacity: .85; }
.wtr-clinic-meta { font-size: 12px; opacity: .7; margin-top: 4px; }
.wtr-clinic.ok { border-color: #047857; }
.wtr-clinic.ok .wtr-clinic-state { color: #047857; }
.wtr-clinic.warn { border-color: #b45309; background: #fffbeb; }
.wtr-clinic.warn .wtr-clinic-state { color: #b45309; }
.wtr-clinic.bad { border-color: #b91c1c; background: #fef2f2; }
.wtr-clinic.bad .wtr-clinic-state { color: #b91c1c; }
.wtr-clinic.idle .wtr-clinic-state { opacity: .55; }
:root[data-theme="dark"] .wtr-clinic.warn { background: #3a2a0a; }
:root[data-theme="dark"] .wtr-clinic.bad { background: #3f1414; }
.wtr-statline { margin: 2px 0 10px; font-size: 12.5px; opacity: .65; }

/* The one queue. Severity is a left edge, not a novelty layout per problem. */
.wtr-queue { display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.wtr-qrow { display: flex; gap: 10px; align-items: baseline; padding: 9px 12px;
  border: 1px solid var(--line); border-left-width: 4px; border-radius: 10px;
  background: var(--panel); font-size: 13.5px; }
.wtr-qrow b { flex: 0 0 auto; font-size: 12.5px; }
.wtr-qrow span { opacity: .9; }
/* ⚠ sev0 is the overdue-check row and it is the ONLY one that gets a tinted background. It is the
   row that corresponds to a phone actually ringing, and it has to be findable at a glance on a
   screen somebody opened at 6am because a text woke them. */
.wtr-qrow.sev0 { border-left-color: #b91c1c; background: rgba(185,28,28,.07); }
.wtr-qrow.sev0 b { color: #b91c1c; }
.wtr-qrow.sev1 { border-left-color: #b91c1c; }
.wtr-qrow.sev2 { border-left-color: #b45309; }
.wtr-qrow.sev3, .wtr-qrow.sev4 { border-left-color: #64748b; }
.wtr-qrow.ok { border-left-color: #047857; }
/* The queue row is a baseline-aligned flex line; a button in it must not stretch or wrap. */
.wtr-qrow .wtr-btn { flex: 0 0 auto; margin-left: auto; align-self: center; padding: 6px 12px; min-height: 32px; }

/* The one destructive-ish action on this screen: ending a treatment day from a desk. Amber, not
   red — red on this page means DO NOT TREAT, and this must never be mistaken for that. */
.wtr-btn-warn { border-color: #b45309; color: #b45309; }
.wtr-btn-warn:hover:not(:disabled) { background: #b45309; color: #fff; border-color: #b45309; }
.wtr-lbl { display: block; margin: 14px 0 6px; font-size: var(--t-sm); font-weight: 600; }
.wtr-ta { width: 100%; font: inherit; font-size: var(--t-sm); padding: 9px 11px; resize: vertical;
  border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--bg); color: var(--ink); }
.wtr-ta:focus { outline: none; border-color: var(--ink); }
.wtr-modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 16px; }
.wtr-err { margin: 10px 0 0; font-size: var(--t-sm); font-weight: 600; color: var(--danger); }

.wtr-chip-danger { color: #b91c1c; font-weight: 800; }

/* Admin readiness board. Each chip is a named gap with the section that fixes it. */
.wtr-ready { margin: 4px 0 10px; display: flex; flex-direction: column; gap: 6px; }
.wtr-ready-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  border: 1px solid var(--line); border-radius: 10px; padding: 9px 12px; background: var(--panel); }
.wtr-ready-row.warn { border-color: #b45309; }
.wtr-ready-row.bad { border-color: #b91c1c; }
.wtr-ready-row > b { min-width: 120px; }
.wtr-chips { display: flex; gap: 6px; flex-wrap: wrap; flex: 1; }
.wtr-chip { font-size: 11.5px; padding: 3px 9px; border-radius: 999px; border: 1px solid var(--line); }
.wtr-chip.ok { color: #047857; border-color: #04785755; }
.wtr-chip.todo { color: #b45309; border-color: #b4530955; font-weight: 700; }
.wtr-ready-sum { font-size: 12px; font-weight: 700; opacity: .7; }

/* The edit-mode banner on the clinic form — the scroll animation alone was missable. */
/* ⚠ SPANS THE WHOLE ROW. .wtr-form is a grid of ~190px columns, so without this the "Editing
   <clinic>" banner is squeezed into ONE column and its Cancel button lands on top of the label —
   which is exactly what it looked like on screen. width:100% does nothing about that; a grid child
   is sized by its track, not by its own width. */
.wtr-editmode { grid-column: 1 / -1;
  display: flex; gap: 10px; align-items: center; flex-wrap: wrap;
  padding: 8px 12px; border-radius: 10px; background: #eff6ff; border: 1px solid #1d4ed855; }
.wtr-editmode b { flex: 1 1 auto; min-width: 0; }
:root[data-theme="dark"] .wtr-editmode { background: #172554; }
.wtr-editmode.hidden { display: none; }

/* The "why this matters" line on a kiosk step. Quieter than the instruction above it — it is there
   for the technician who wonders, not to compete with what they have to do next. */
/* ⚠ A DECISION BUTTON HAS TO LOOK LIKE ONE. The owner pressed "End the day", nothing happened
   (it was disabled pending a typed reason) and he concluded it was broken. Size is part of the
   fix — the confirming action should be the largest thing in the dialog, not the same weight as
   Cancel beside it. */
.wtr-btn-big {
  font-size: 16px; font-weight: 700; letter-spacing: .02em;
  padding: 14px 26px; border-radius: 12px; min-height: 52px;
}
.wtr-modal-actions .wtr-btn-big { flex: 1 1 auto; }

/* "Not right now" on the nurses' station. Deliberately quieter than the action above it — this is
   the lesser of the two choices and should not compete with doing the audit. Still thumb-sized:
   every control on a wall iPad is pressed with a glove on. */
/* Small numeric cells in the alarm-order table. */
.wtr-num { width: 72px; text-align: center; }
/* The reorder arrows in the combined audit/alarm list. */
.wtr-ord { white-space: nowrap; }
.wtr-ord .wtr-mini { padding: 2px 7px; line-height: 1.1; }
/* A checkbox that reads as a field beside the others rather than floating above its label. */
.wtr-check { flex-direction: row !important; align-items: center; gap: 8px; }

/* The audit library — nine collapsible checklists. Only one open at a time, because 96 lines of the
   initial PCT sheet on screen beside 64 of the RN one is the wall of text this screen keeps being
   accused of being. */
.wtr-lib { display: flex; flex-direction: column; gap: 2px; }
.wtr-lib-row { padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); }
.wtr-lib-name { font-size: var(--t-md); font-weight: 700; }
.wtr-lib-body { margin-top: 12px; border-top: 1px solid var(--line); padding-top: 12px; }
/* A checklist line is a sentence, not a label — the numbers that make it auditable live at the end
   of it, so the box has to show more than one line at a time. */
.wtr-lib-ta { width: 100%; min-width: 320px; resize: vertical; font-size: 12px; line-height: 1.45; }

.nk-snooze {
  display: block; width: 100%; margin-top: 14px;
  padding: 16px 20px; min-height: 60px;
  border: 2px solid rgba(255,255,255,.45); border-radius: 14px;
  background: transparent; color: #fff;
  font: inherit; font-size: 18px; font-weight: 600; cursor: pointer;
}
.nk-snooze:active { background: rgba(255,255,255,.12); }

.wk-why {
  margin-top: 10px; padding: 9px 12px;
  border-left: 3px solid var(--primary); border-radius: 0 8px 8px 0;
  background: rgba(255,255,255,.06); color: #dfe9e5;
  font-size: 15px; line-height: 1.5; max-width: 46ch; text-align: left;
}
/* The one-time station credentials. Deliberately loud and monospaced — somebody is copying these
   onto an iPad by hand, and the password is never shown again. */
.wtr-cred { border: 2px solid #1d4ed8; border-radius: 12px; padding: 14px 16px; margin: 10px 0; }
.wtr-cred > b { display: block; margin-bottom: 4px; }
.wtr-cred p { margin: 6px 0; font-size: 13px; }
.wtr-credrow { display: flex; align-items: baseline; gap: 12px; margin: 6px 0; }
.wtr-credrow span { min-width: 84px; font-size: 12px; opacity: .7; }
.wtr-credrow code { font-size: 20px; font-weight: 700; letter-spacing: .04em;
  background: var(--panel); border: 1px solid var(--line); border-radius: 8px; padding: 4px 12px;
  user-select: all; }
/* ⚠ THE KIOSK PREVIEW IS NEVER MIRRORED. The wall iPad uses its FRONT camera (the rear one faces
   plasterboard), and the instinct with a front camera is to mirror it like a bathroom mirror. Do
   not: the technician is holding up a strip bottle and reading the printed colour chart on its
   label, and a mirrored preview shows that label backwards. The saved photograph is unmirrored
   either way — drawImage ignores CSS — so mirroring would only ever make the screen disagree with
   the evidence. Same reason on the nurse's verification camera. */
.wk-filmlayer video, .wk-filmlayer .wk-video, .wtr-vcam video { transform: none; }

/* ===================== Newsletter pipeline dashboard ===================== */
.nl-head-note { font-weight: 700; margin-right: auto; }
.nl-issue { display: flex; gap: 14px; border: 1px solid rgba(100, 116, 139, .25); border-radius: 12px; padding: 14px; margin-bottom: 12px; background: rgba(255, 255, 255, .55); }
.nl-hero { width: 108px; height: 108px; object-fit: cover; border-radius: 10px; flex: none; }
.nl-body { flex: 1; min-width: 0; }
.nl-toprow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.nl-title { font-size: 16px; font-weight: 800; color: inherit; text-decoration: none; }
.nl-title:hover { text-decoration: underline; }
.nl-sub { font-size: 12.5px; opacity: .65; margin: 2px 0 10px; }
.nl-steps { display: flex; flex-direction: column; gap: 7px; }
.nl-step { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.nl-step-label { font-size: 12.5px; font-weight: 700; width: 150px; flex: none; opacity: .8; }
.nl-chip { display: inline-block; font-size: 12.5px; font-weight: 700; padding: 3px 10px; border-radius: 999px; border: 0; }
.nl-ok   { background: #dcfce7; color: #15803d; }
.nl-wait { background: #fef3c7; color: #b45309; }
.nl-warn { background: #ffedd5; color: #c2410c; }
.nl-err  { background: #fee2e2; color: #b91c1c; animation: nl-pulse 2s infinite; }
.nl-na   { background: #f1f5f9; color: #64748b; font-weight: 600; }
.nl-click { cursor: pointer; }
.nl-click:hover { filter: brightness(.95); }
@keyframes nl-pulse { 50% { opacity: .65; } }
.nl-reels { display: inline-flex; gap: 6px; flex-wrap: wrap; }
.nl-link { font-size: 12.5px; font-weight: 700; }
.nl-comments { font-size: 12.5px; font-style: italic; opacity: .75; width: 100%; margin-left: 158px; }
/* Next month's topic — the physician's answer to "what should next month's article focus on?"
   on the review page, captured at sign-off (2026-09-07). A quiet quote card, not a status pill. */
.nl-ideas { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; flex: 1; min-width: 0; }
.nl-idea { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; max-width: 100%; padding: 7px 12px; border-radius: 10px; background: var(--primary-soft, #eef2ff); color: var(--primary-ink, inherit); border-left: 3px solid var(--primary, #4f46e5); font-size: 13px; line-height: 1.45; }
.nl-idea-text { font-weight: 600; overflow-wrap: anywhere; }
.nl-idea-by { font-size: 12px; opacity: .7; }
.nl-actions { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.nl-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, .72); z-index: 10000; display: flex; align-items: center; justify-content: center; padding: 18px; }
.nl-player { position: relative; max-width: min(420px, 92vw); }
.nl-player video { width: 100%; max-height: 78vh; border-radius: 14px; background: #000; }
.nl-cap { color: #e2e8f0; font-size: 12.5px; margin-top: 8px; max-height: 72px; overflow: auto; }
.nl-close { position: absolute; top: -14px; right: -14px; z-index: 1; width: 32px; height: 32px; border-radius: 50%; border: 0; background: #fff; font-size: 15px; cursor: pointer; box-shadow: 0 2px 10px rgba(0, 0, 0, .35); }
.nl-picksheet { position: relative; background: #fff; border-radius: 14px; padding: 20px; max-width: 380px; width: 100%; }
.nl-picksheet h3 { margin: 0 0 6px; }
.nl-picknote { font-size: 13px; opacity: .75; margin: 0 0 10px; }
.nl-cands { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; max-height: 40vh; overflow: auto; }
.nl-cand { display: flex; justify-content: space-between; gap: 10px; padding: 9px 12px; border: 1px solid rgba(100, 116, 139, .3); border-radius: 9px; background: #f8fafc; cursor: pointer; font-size: 13.5px; }
.nl-cand:hover { background: #eef2ff; }
.nl-manual { display: flex; gap: 8px; }
.nl-manual input { flex: 1; padding: 8px 10px; border: 1px solid rgba(100, 116, 139, .35); border-radius: 8px; font-size: 14px; }
.nl-preview { font-size: 11.5px; opacity: .6; margin: 10px 0 0; }
@media (max-width: 640px) {
  .nl-issue { flex-direction: column; }
  .nl-hero { width: 100%; height: 120px; }
  .nl-step-label { width: 100%; }
  .nl-comments { margin-left: 0; }
}

/* Review rotation panel */
.nl-rot { border: 1px solid rgba(100, 116, 139, .25); border-radius: 12px; padding: 12px 14px; margin-bottom: 14px; background: rgba(255, 255, 255, .55); }
.nl-rot-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.nl-rot-hint { font-size: 12px; opacity: .6; }
.nl-rot-row { display: flex; align-items: center; gap: 10px; padding: 6px 0; border-top: 1px solid rgba(100, 116, 139, .12); flex-wrap: wrap; }
.nl-rot-off .nl-rot-name, .nl-rot-off .nl-rot-email, .nl-rot-off .nl-rot-count { opacity: .38; text-decoration: line-through; }
.nl-rot-ctrl { display: inline-flex; gap: 2px; }
.nl-rot-btn { width: 24px; height: 24px; border-radius: 6px; border: 1px solid rgba(100, 116, 139, .3); background: #f8fafc; cursor: pointer; font-size: 12px; line-height: 1; }
.nl-rot-btn:disabled { opacity: .3; cursor: default; }
.nl-rot-name { font-weight: 700; font-size: 13.5px; min-width: 210px; }
.nl-rot-email { flex: 1; min-width: 180px; padding: 5px 9px; border: 1px solid rgba(100, 116, 139, .3); border-radius: 7px; font-size: 12.5px; }
.nl-rot-count { font-size: 12px; opacity: .65; white-space: nowrap; }
.nl-rot-active { font-size: 12.5px; display: inline-flex; gap: 5px; align-items: center; white-space: nowrap; }
.nl-rot-foot { display: flex; gap: 8px; margin-top: 10px; }
.nl-rot-save { background: #16a34a; color: #fff; border-color: #16a34a; }
@media (max-width: 640px) { .nl-rot-name { min-width: 0; width: 100%; } }

/* Newsletter filter bar */
.nl-filterbar { display: flex; gap: 8px; margin: 0 0 12px; flex-wrap: wrap; }
.nl-filterbar select { padding: 6px 12px; border-radius: 8px; border: 1px solid rgba(100, 116, 139, .3); font-size: 13px; background: #fff; color: inherit; }

/* ---- Soak education: the admin deck, the pronunciation check, the personnel panel ------------- */

/* ⚠ Every colour here comes from a token. A literal would look right in one theme and be
   unreadable in the other — see the `--brand` trap in the kiosk rules above. */
.wa-edu { width: 100%; }
.wa-edu td { vertical-align: top; }
.wa-edu-t .wtr-sub { display: block; margin-top: 3px; color: var(--muted); font-size: var(--t-sm); line-height: 1.45; }
.wa-edu-n { white-space: nowrap; color: var(--muted); font-size: var(--t-sm); }
.wa-edu-a { white-space: nowrap; text-align: right; }
.wa-edu-a .wtr-btn { margin-left: 6px; }
.wa-edu-hdr { padding-top: 18px !important; color: var(--muted); font-weight: 600; font-size: var(--t-sm); }
/* A retired education is dimmed, never hidden — it is still on somebody's personnel file. */
.wa-edu-off td { opacity: .62; }
.wa-edu-form textarea {
  width: 100%; min-height: 120px; resize: vertical; font: inherit; line-height: 1.5;
  padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-md, 10px);
  background: var(--surface); color: var(--ink);
}

/* Employee setup — "how to say their name" */
.ue-say-row { display: flex; gap: 8px; align-items: center; }
.ue-say-row .field-input { flex: 1 1 auto; min-width: 0; }
.ue-say-row .btn { flex: 0 0 auto; white-space: nowrap; }
.ue-say-msg { margin-top: 5px; line-height: 1.45; }

/* The personnel file's education panel */
.wtr-edulink {
  display: inline-block; margin-top: 5px; padding: 2px 8px;
  border: 1px solid var(--line); border-radius: 999px; background: transparent;
  color: var(--muted); font: inherit; font-size: var(--t-xs); cursor: pointer;
}
.wtr-edulink:hover { color: var(--ink); border-color: var(--line-strong, var(--line)); }
.wtr-edulink.on { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
/* ⚠ TWO INHERITED RULES WRECKED THIS PANEL, AND BOTH NEEDED UNDOING BY HAND.
   1. `.wtr-note` carries `margin: -12px 0 0` — a NEGATIVE top margin, correct where it sits tight
      under a heading, and the reason every line in here printed straight through the one above.
   2. `.wtr-psheet th, td` sets `white-space: nowrap`, right for a grid of dates and wrong for a
      paragraph — the "not yet given" list ran off the side of the page instead of wrapping.
   A panel dropped inside somebody else's table inherits that table's rules; it has to say so. */
/* ⚠⚠ THE PANEL HAS TO FOLLOW THE HORIZONTAL SCROLL, OR IT IS INVISIBLE.
   Reported 2026-08-25 three times as "not working": a blank grey band with no text in it. The
   panel was rendering perfectly — its <td> spans all thirteen columns, so its CONTENT begins at the
   table's far-left edge. Measured with the sheet scrolled right the way the screenshot showed it:
   the heading sat at x = -463 while the visible area started at x = 16. Four hundred and seventy
   nine pixels off-screen, with a full-width empty band showing where it should have been.
   ⚠ THIS IS A CONSEQUENCE OF THE FROZEN COLUMNS. Before the sheet scrolled sideways inside its own
   wrapper there was nothing to scroll out of. Sticky-left pins the content to the scrollport, so it
   is in view wherever the sheet is scrolled to. Its width is set in JS from the wrapper's own
   clientWidth — a CSS guess like 100vw is wrong here, because the wrapper is not the viewport. */
.wtr-edurow .wtr-edu, .wtr-edurow > td > .wtr-note {
  position: sticky; left: 0;
  box-sizing: border-box;
  /* ⚠ THE PADDING BELONGS TO THE STICKY ELEMENT, NOT THE CELL. Left on the <td>, it sits OUTSIDE
     the thing being pinned, so `left: 0` parked the content 13px past the visible edge and clipped
     the first characters. Measured, not guessed. */
  padding: 14px 16px;
}
.wtr-edurow > td {
  background: var(--surface-2, var(--surface));
  padding: 0 !important;
  white-space: normal !important;
  font-size: var(--t-base);
}
.wtr-edu > b { display: block; font-size: var(--t-md); }
.wtr-edu .wtr-note { margin: 8px 0 0 !important; max-width: 78ch; white-space: normal; }
.wtr-edu .wtr-note.warn { color: var(--danger); }
/* The topic list scrolls inside itself rather than stretching the sheet sideways. */
.wtr-edutblwrap { overflow-x: auto; margin-top: 4px; }
.wtr-edu .wtr-frac { display: block; margin: 2px 0 10px; color: var(--muted); }
.wtr-edutbl { width: auto; min-width: min(560px, 100%); }
.wtr-edutbl td, .wtr-edutbl th {
  padding: 5px 14px 5px 0; font-size: var(--t-sm); white-space: nowrap; vertical-align: top;
}
.wtr-edutbl td:first-child, .wtr-edutbl th:first-child { white-space: normal; min-width: 22ch; }
.wtr-edu-off td { opacity: .62; }
/* The voice picker sits above the deck — it changes how every script sounds. */
.wa-voice { align-items: center; margin-bottom: 18px; padding-bottom: 16px; border-bottom: 1px solid var(--line); }
.wa-voice select { min-width: 9rem; }
/* The tone each script is spoken in. Colour-coded so the deck can be scanned for balance —
   a list that is all "serious" has lost the contrast that makes any of it land. */
.wa-tone {
  display: inline-block; padding: 2px 9px; border-radius: 999px; margin-bottom: 4px;
  font-size: var(--t-xs); font-weight: 700; letter-spacing: .04em;
  border: 1px solid var(--line); color: var(--muted); white-space: nowrap;
}
.wa-tone-serious    { color: var(--danger, #b42318); border-color: color-mix(in srgb, var(--danger, #b42318) 35%, transparent); }
.wa-tone-direct     { color: #b45309; border-color: color-mix(in srgb, #b45309 35%, transparent); }
.wa-tone-compassion { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 35%, transparent); }
.wa-tone-upbeat     { color: #2a7d8c; border-color: color-mix(in srgb, #2a7d8c 35%, transparent); }

/* ⚠ "The alarm cannot make a sound." Full width, above everything, on every phase.
   An iPad will not play audio until the glass has been touched, and these kiosks reload themselves
   when a build ships — so a station can sit showing a red alarm in silence. The quiet reads as
   "nothing is wrong", which is the most dangerous thing this screen could imply. Tapping it is
   itself the gesture that unlocks the sound. */
/* ⚠ TWO STATES, ONE BUTTON. Quiet all day (the morning tap), red and pulsing only when an alarm
   is actually trying to make a noise it cannot make. A strip that screams every morning is one
   people learn to dismiss without reading, which would cost exactly the tap it is asking for. */
.nk-soundoff {
  display: block; width: 100%; border: 0; cursor: pointer;
  padding: 14px 18px; background: var(--surface-3, #21302a); color: #fff;
  font: inherit; font-size: clamp(15px, 2.2vh, 20px); font-weight: 700;
  letter-spacing: .03em; text-align: center; line-height: 1.35;
}
.nk-soundoff-now {
  background: #b42318; font-weight: 800; letter-spacing: .04em;
  animation: nkSoundPulse 1.1s ease-in-out infinite;
}
@keyframes nkSoundPulse { 50% { background: #7f1d1d; } }
@media (prefers-reduced-motion: reduce) { .nk-soundoff-now { animation: none; } }

/* The in-app confirmation on the six-minute redo screen. ⚠ Deliberately NOT window.confirm():
   a bolted-down iPad in Guided Access never shows the native dialog, so the button silently did
   nothing and no request ever left the tablet. */
.wk-alarm-confirm { display: flex; flex-direction: column; gap: 12px; align-items: center; }
.wk-alarm-confirm p { max-width: 46ch; margin: 0 0 4px; line-height: 1.45; }

/* The nurses' station "send it back to be redone" control, on a lapsed countersignature.
   ⚠ She is standing in front of a screaming alarm she cannot answer any other way — before this
   the screen only told her the work had to happen in the water room and gave her no button. */
.nk-ready-late { margin: 14px 0; padding: 14px 16px; border-radius: 12px;
  background: rgba(0, 0, 0, .18); text-align: left; }
.nk-ready-late p { margin: 0 0 10px; line-height: 1.45; }
.nk-redo-warn { color: #fde8e8; }
.nk-ghost { display: block; width: 100%; margin-top: 8px; padding: 12px 16px;
  border: 2px solid rgba(255, 255, 255, .55); border-radius: 12px;
  background: transparent; color: #fff; font: inherit; font-weight: 700; cursor: pointer; }
.nk-ghost:active { background: rgba(255, 255, 255, .12); }
.nk-redo-yes { margin-top: 4px; }

/* The "what should the first bill say?" choices — real wording, picked by eye rather than by
   answering a question about offsets. Same token discipline as the rest of the .ar- block. */
.ar-choice { display: flex; gap: 10px; align-items: flex-start; border: 1px solid var(--line);
  border-radius: 10px; padding: 10px 12px; margin: 0 0 8px; cursor: pointer; background: var(--bg); }
.ar-choice:hover { border-color: var(--primary); }
.ar-choice.on { border-color: var(--primary); background: var(--surface);
  box-shadow: 0 0 0 1px var(--primary) inset; }
.ar-choice input { margin: 3px 0 0; flex: none; }
.ar-choice-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ar-choice-main { font-size: 16px; font-weight: 700; color: var(--ink); }
.ar-choice.on .ar-choice-main { color: var(--primary); }
.ar-choice-hint { font-size: 12px; color: var(--muted); line-height: 1.45; }

/* ---- Recurring bills (standing orders) -------------------------------------------------
   ⚠️ RE-ADDED 2026-08-18 after a concurrent styles.css deploy from another session dropped it,
   which left the whole preview panel rendering as unstyled text. If these rules go missing
   again, that is what happened — graft them back onto the CURRENT live file, never overwrite.
   Everything is scoped to .ar-, and only tokens actually DEFINED in this file are used
   (an undefined token fails silently and leaves the surface transparent). */
.ar-kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 10px 18px; }
.ar-kv > div { display: flex; flex-direction: column; gap: 2px; }
.ar-kv span { font-size: 12px; color: var(--muted); }
.ar-kv strong { font-size: 15px; color: var(--ink); }

.ar-warn { border: 1px solid var(--danger); background: var(--danger-soft); color: var(--ink);
  border-radius: 10px; padding: 10px 12px; font-size: 14px; }
.ar-note { border: 1px solid var(--line); background: var(--surface); border-radius: 10px;
  padding: 10px 12px; font-size: 13px; color: var(--muted); }
.ar-danger { color: var(--danger); border-color: var(--danger); }
.ar-danger:hover { background: var(--danger-soft); }

.ar-split { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 980px) { .ar-split { grid-template-columns: minmax(0, 1fr); } }
.ar-sticky { position: sticky; top: 12px; }

.ar-field { margin: 0 0 14px; }
.ar-field > label { display: block; font-weight: 600; font-size: 13px; color: var(--ink); margin: 0 0 3px; }
.ar-field .ar-help { font-size: 12px; color: var(--muted); margin: 0 0 5px; line-height: 1.45; }
.ar-row { display: flex; gap: 10px; flex-wrap: wrap; }
.ar-row > .ar-field { flex: 1 1 150px; margin-bottom: 14px; }

.ar-tokens { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 0; }
.ar-token { border: 1px solid var(--line); background: var(--bg); color: var(--ink); border-radius: 999px;
  padding: 3px 10px; font-size: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; cursor: pointer; }
.ar-token:hover { border-color: var(--primary); color: var(--primary); }

/* The live echo under each pattern box: "becomes → the real wording". */
.ar-echo { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; margin: 6px 0 0; min-height: 18px; }
.ar-echo-arrow { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); flex: none; }
.ar-echo-text { font-size: 14px; font-weight: 600; color: var(--ink); word-break: break-word; }
.ar-echo-count { font-size: 11px; color: var(--muted); flex: none; }
.ar-echo-bad { color: var(--danger); }
.ar-echo-dim { font-size: 12px; color: var(--muted); font-style: italic; }

/* ---- the preview panel: a stack of the actual bills, newest first ---------------------- */
.ar-preview { border: 1px solid var(--line); border-radius: 14px; background: var(--surface); padding: 16px 16px 6px; }
.ar-preview > h4 { margin: 0 0 2px; font-size: 15px; }
.ar-preview .ar-sub { font-size: 12px; color: var(--muted); margin: 0 0 12px; line-height: 1.45; }

.ar-pv-list { display: flex; flex-direction: column; gap: 10px; margin: 0 0 12px; }
.ar-pv-row { display: grid; grid-template-columns: 26px minmax(0, 1fr); gap: 10px;
  border: 1px solid var(--line); border-radius: 11px; background: var(--bg); padding: 11px 12px; }
.ar-pv-n { width: 22px; height: 22px; border-radius: 50%; background: var(--surface); color: var(--muted);
  border: 1px solid var(--line); font-size: 11px; font-weight: 700; display: flex; align-items: center;
  justify-content: center; margin-top: 1px; }
.ar-pv-row:first-child .ar-pv-n { background: var(--primary); border-color: var(--primary); color: #fff; }
.ar-pv-desc { font-size: 15px; font-weight: 650; color: var(--ink); line-height: 1.35; word-break: break-word; }
.ar-pv-meta { display: flex; flex-wrap: wrap; gap: 4px 6px; margin-top: 7px; }
.ar-pv-tag { font-size: 11px; color: var(--muted); background: var(--surface); border: 1px solid var(--line);
  border-radius: 6px; padding: 2px 7px; white-space: nowrap; }
.ar-pv-tag b { font-weight: 600; color: var(--ink); }
.ar-pv-memo { font-size: 12px; color: var(--muted); margin-top: 6px; word-break: break-word; }
.ar-pv-memo b { font-weight: 600; color: var(--ink); }

.ar-errs { border: 1px solid var(--warn); border-radius: 10px; padding: 10px 12px; margin: 0 0 12px;
  font-size: 13px; color: var(--ink); }
.ar-errs ul { margin: 6px 0 0; padding-left: 18px; }
.ar-errs li { margin: 3px 0; }
.ar-ok { display: inline-flex; align-items: center; gap: 6px; color: var(--good); font-weight: 600;
  font-size: 12px; margin: 0 0 12px; }

/* A field the schedule fills in for you — shown so you can read it, locked so you can't type
   something that would be overwritten on every bill. Used for the expense-line descriptions. */
/* --bg (the page behind the card), so the box reads as RECESSED in both themes — a normal
   .field-input sits lighter than the page in light mode and lighter again in dark, so borrowing
   --surface here made it look MORE prominent than an editable field rather than less. */
.field-input.ar-derived { background: var(--bg); color: var(--muted); cursor: default; }
.field-input.ar-derived:focus { outline: none; border-color: var(--line); box-shadow: none; }
.field-input.ar-derived::placeholder { font-style: italic; }
/* It is not typeable, so it gets no resize grip — and it must GROW to fit rather than clip: the
   wording is the thing being checked, and a description cut off half way is worse than useless. */
textarea.field-input.ar-derived { resize: none; overflow: hidden; height: auto; }
/* The second disposition on the expired-bottle hand-off. Both answers close it — discarded here,
   or separated and returned to Lawrenceville with the courier (the clinic's own procedure) — so
   neither may look like the lesser option. */
.nk-submit-alt { background: transparent; box-shadow: inset 0 0 0 2px currentColor; }

/* ---- Station branding: Nephron's mark, and who owns the software ------------------------------
   ⚠ CALM SCREENS ONLY. Never rendered on an alarm, a stop, the countdown or the camera view —
   a logo next to "STOP DIALYSIS NOW" competes with the instruction, and every pixel added to an
   alarm screen is a pixel that can push its button off the bottom of the glass.
   ⚠ flex: 0 0 auto and pointer-events: none — it can never grow into the content or eat a tap. */
.wk-brand, .nk-brand {
  flex: 0 0 auto; pointer-events: none;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  width: 100%; margin-bottom: clamp(8px, 1.6vh, 18px);
  opacity: .8;
}
.wk-brand-logo, .nk-brand-logo {
  height: clamp(22px, 3.4vh, 38px); width: auto; max-width: 42vw;
  object-fit: contain; display: block;
}
.wk-brand-owner, .nk-brand-owner {
  font-size: clamp(9px, 1.3vh, 12px); letter-spacing: .04em; line-height: 1.3;
  text-align: right; color: var(--muted);
  /* ⚠ 56ch, not 26 — the credit line is 51 characters and 26ch stacked it three deep next to
     the logo. Wide enough for one line on the wall iPad, still narrow enough to wrap rather
     than collide with the mark on a phone. */
  max-width: 56ch;
}
/* ⚠ Belt and braces: if a brand strip is ever rendered inside an alarm, hide it rather than let it
   take space away from the alarm's own button. */
.wk-alarm .wk-brand, .nk-alarm .nk-brand, .wk-film .wk-brand { display: none; }

/* ---- The stations are ALWAYS white ------------------------------------------------------------
   ⚠ FORCED LIGHT, NOT INHERITED. Both kiosks painted var(--bg), so a tablet somebody had flipped
   to dark mode rendered a dark station — and the Nephron mark, which is a PNG on a white ground,
   became a white rectangle floating on it. Owner, 2026-08-24: "our logo is on white background,
   make the screen background white so it looks seamless."

   ⚠ THE THEME IS PINNED IN JS, NOT HERE. renderWaterStation() and the nurses' board both set
   data-theme="light" on <html> before they paint (app.js / nurse.js). That was chosen over
   restating the palette in this block: the first draft of this fix redefined a dozen tokens and
   still missed --danger, --good, --warn and --primary-hover, which would have left dark-mode
   salmon error text on a white card. A partial palette is worse than none, because it looks
   finished. Setting the attribute defeats the dark block wholesale — it is guarded as
   :root:not([data-theme="light"]) at the top of this file — so nothing can drift out of step.

   So all that is left here is the white itself, plus one genuinely missing token.

   ⚠ --panel IS DEFINED AT LAST. The nurses' name tiles paint var(--panel), which is declared
   NOWHERE in this file, so those tiles have always been transparent. Invisible-ish on the old grey
   ground; on a white one they would have vanished completely, taking the "tap your name" grid with
   them. Every kiosk surface that reads it is listed in the water-kiosk-modal-css note.

   ⚠ Alarm screens set their own red and white explicitly and are untouched by any of this. */
#waterKiosk, #nurseKiosk, body.water-kiosk, body.nurse-kiosk {
  --bg: #ffffff;
  --panel: #ffffff;
  background: #ffffff;
}
/* The name tiles need a real edge now that both they and the page behind them are white. */
#nurseKiosk:not(.nk-alarm) .nk-name { background: #fff; border: 2px solid var(--line); }
#nurseKiosk:not(.nk-alarm) .nk-name.on { border-color: var(--primary); box-shadow: inset 0 0 0 2px var(--primary); }

/* The mark itself, large — the logo IS the branding and the credit line is a footnote to it.
   ⚠ Capped at 116px: measured at 1080x810, which is the iPad in the wall mount. */
.wk-brand-logo, .nk-brand-logo { height: clamp(58px, 9.5vh, 116px); max-width: 46vw; }
.wk-brand, .nk-brand { opacity: 1; }
.wk-brand-owner, .nk-brand-owner { color: var(--muted); }

/* ---- Supplies: price comparison ---------------------------------------------------------------
   Per-unit prices are the whole point of this screen, so they are the only figure given weight;
   pack prices stay quiet beside them. */
.sup-cmp-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--s3); margin: var(--s3) 0 var(--s4); }
.sup-cmp-tile { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3);
  background: var(--surface); }
.sup-cmp-tile.is-warn { border-color: color-mix(in srgb, var(--warn) 45%, var(--line));
  background: color-mix(in srgb, var(--warn) 6%, var(--surface)); }
.sup-cmp-n { font-size: var(--t-xl, 1.5rem); font-weight: 700; color: var(--ink);
  font-variant-numeric: tabular-nums; line-height: 1.1; }
.sup-cmp-l { font-size: var(--t-xs); color: var(--muted); margin-top: 2px; }
.sup-cmp-sec { margin-top: var(--s5); }
.sup-cmp-group { border: 1px solid var(--line); border-radius: var(--r-md); padding: var(--s3);
  margin-bottom: var(--s3); background: var(--surface); }
.sup-cmp-head { display: flex; gap: var(--s3); align-items: flex-start; flex-wrap: wrap;
  margin-bottom: var(--s2); }
.sup-cmp-act { margin-left: auto; display: flex; gap: var(--s2); flex-wrap: wrap; }
.sup-cmp-save { color: var(--good); font-weight: 600; }
.sup-cmp-uom { color: var(--muted); font-size: var(--t-xs); }
/* The cheapest row is the answer the screen exists to give — tinted, not just bolded. */
.sup-cmp-best { background: color-mix(in srgb, var(--good) 8%, transparent); }
.sup-cmp-tag { display: inline-block; margin-left: var(--s2); font-size: var(--t-xs);
  color: var(--good); background: color-mix(in srgb, var(--good) 14%, transparent);
  border-radius: var(--r-full); padding: 1px 8px; white-space: nowrap; }
/* A row with no pack size is not a competitor — it is a gap. Recede it rather than hide it. */
.sup-cmp-nopack { opacity: .72; }
.sup-cmp-online { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }
.sup-cmp-online a { color: var(--primary-ink, var(--ink)); }
.sup-cmp-search { width: 100%; max-width: 420px; margin-bottom: var(--s3); }
/* Amazon is compared on every item, so it is tagged and sorted first — and its ABSENCE is stated
   rather than left as a silent gap. */
.sup-amz-tag { display: inline-block; margin-left: var(--s2); font-size: var(--t-xs);
  color: var(--warn); background: color-mix(in srgb, var(--warn) 14%, transparent);
  border-radius: var(--r-full); padding: 1px 8px; white-space: nowrap; }
.sup-cmp-noamz { margin-bottom: var(--s2); color: var(--muted); }
/* Approval screen: the per-single-item cost, which is what tells an approver a pack is wrong. */
.sup-each { font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- Supplies: comparison row polish -----------------------------------------------------------
   The per-item cost is the answer this table exists to give, so it is the only figure with weight;
   the pack price beside it stays quiet. */
.sup-n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
th.sup-n { text-align: right; }
.sup-cmp-unit { font-size: var(--t-md); font-weight: 700; color: var(--ink); }
.sup-cmp-pack { color: var(--ink-soft); }
.sup-cmp-over { font-size: var(--t-xs); color: var(--danger); font-variant-numeric: tabular-nums; }
.sup-cmp-eq { color: var(--good); }
.sup-cmp-vend { font-weight: 600; color: var(--ink); line-height: 1.3; }
.sup-cmp-desc { color: var(--ink-soft); font-size: var(--t-xs); line-height: 1.4;
  max-width: 30ch; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
/* ⚠ The two row actions were rendered adjacent with no separator and read as one word —
   "Check onlineRemove". A flex row with a gap is the fix; margins on siblings are not, because the
   second button is conditional and would leave a stray gap when absent. */
.sup-acts { display: flex; align-items: center; justify-content: flex-end; gap: var(--s3);
  flex-wrap: wrap; white-space: nowrap; }
.sup-link.sup-danger { color: var(--danger); }
.sup-cmp-progress:not(:empty) { font-size: var(--t-sm); color: var(--ink-soft);
  padding: var(--s2) 0; font-variant-numeric: tabular-nums; }
/* A supplier somebody else added, waiting on the owner. Amber because it BLOCKS ordering. */
.sup-pending { border-left: 3px solid var(--warn); background: color-mix(in srgb, var(--warn) 7%, var(--surface)); }
.sup-pending-row { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  padding: var(--s2) 0; border-top: 1px solid var(--line); margin-top: var(--s2); }
.sup-pending-row > div:first-child { flex: 1 1 240px; min-width: 0; }

/* ---- Competency sign-offs -------------------------------------------------------------------
   ⚠ Column 9 of the personnel worksheet shows WHERE ITS DATE CAME FROM. A typed date and one
   stamped by a signed competency sheet are the same pixels otherwise, and that identity is the
   2022 finding itself — four audit grids that read the same whether the work happened or not. */
.wtr-comp-ok  { color: var(--good); font-weight: 650; font-size: 11px; }
.wtr-comp-bad { color: var(--danger); font-weight: 650; font-size: 11px; }
.wtr-comp-open{ color: var(--warn); font-weight: 650; font-size: 11px; }
.wtr-comp-pick { white-space: nowrap; text-align: right; }
/* The pass/fail/na buttons: only the chosen one is filled, so a half-answered sheet is obvious
   at a glance rather than needing to be counted. */
.wtr-comp-pick .wtr-mini { margin-left: 4px; min-width: 44px; }
.wtr-comp-pick .wtr-mini.on { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }
.sup-chan-add { margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }

/* ---- The document behind each date on the personnel worksheet --------------------------------
   ⚠ Deliberately quiet. The DATE is the fact the sheet is read for; the link is how you check it.
   Making these compete with the dates would turn a worksheet into a file browser. */
.wtr-docrow { margin-top: 3px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; line-height: 1; }
/* ⚠ A BUTTON, NOT A LINK — and it has to look like one anyway. The API is header-authenticated,
   so an <a href> to it 401s; these fetch with the token and open a blob. See authBlob() in water.js. */
.wtr-doclink {
  font-size: 10px; font-weight: 650; color: var(--primary); text-decoration: none;
  background: none; border: 0; padding: 0; cursor: pointer; font-family: inherit;
}
.wtr-doclink:hover { text-decoration: underline; }
/* ⚠ .wtr-complink shares the LOOK and nothing else. It used to share the class too, which meant
   the upload handler bound to it and "complete Water checklist" opened a file picker. Behaviour is
   bound to data attributes now; classes are appearance only. */
.wtr-docadd, .wtr-docx, .wtr-complink {
  font-size: 10px; padding: 1px 5px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: transparent; color: var(--muted); cursor: pointer;
}
.wtr-docadd:hover, .wtr-docx:hover, .wtr-complink:hover { border-color: var(--line-strong); color: var(--ink); }
/* The checklist button is the action on that cell — give it a little more presence. */
.wtr-complink { color: var(--primary); border-color: color-mix(in srgb, var(--primary) 35%, var(--line)); font-weight: 650; }
.wtr-docx[data-armed="1"] { border-color: var(--danger); color: var(--danger); }

/* ---- The personnel worksheet: frozen header + frozen name column, and visible compliance ------
   Owner, 2026-08-25: "make the header frozen and the first row with the employee's names so we can
   easily scroll through the list without getting confused. make it more visually obvious when
   something is entered and compliant, vs not entered, and worse expired."

   ⚠ THE WRAPPER HAS TO SCROLL VERTICALLY OR NOTHING STICKS. `position: sticky; top: 0` pins to the
   nearest SCROLLING ancestor, and .wtr-logwrap only ever scrolled sideways — vertically it just
   grew and the page scrolled instead, so a sticky header would have scrolled away with it. Giving
   it a max-height is what makes the header have something to stick to.

   ⚠ border-collapse: SEPARATE. With `collapse`, Safari drops the borders on sticky cells and the
   frozen column loses its edge as it scrolls — and every station here is an iPad. Borders move to
   box-shadow, which travels with a sticky cell instead of belonging to the collapsed grid. */
.wtr-psheet-wrap { max-height: min(72vh, 760px); overflow: auto; }
.wtr-psheet {
  border-collapse: separate; border-spacing: 0;
  /* ⚠ NO BORDER OF ITS OWN. .wtr-tbl draws one, and it belongs to the TABLE, which scrolls — so it
     left a 1px slit above the pinned header that every row visibly slid through. Measured: the gap
     between the wrapper's inner top edge and the header's was exactly 1px, the border width. The
     wrapper already draws the outline. */
  border: 0;
}
.wtr-psheet th, .wtr-psheet td { box-shadow: inset 0 -1px 0 var(--surface-3); }

.wtr-psheet thead th {
  position: sticky; top: 0; z-index: 3;
  /* ⚠ OPAQUE. A sticky cell with a transparent background shows the rows sliding underneath it. */
  background: var(--surface-2, #f6f8f6);
  box-shadow: inset 0 -1px 0 var(--line);
}
/* The tick box and the name travel with the row as it scrolls sideways. Two columns, so the second
   is offset by the width of the first — fixed widths because a sticky offset cannot be "auto". */
.wtr-psheet .wtr-pfix { position: sticky; z-index: 2; background: var(--surface); }
.wtr-psheet .wtr-pfix-1 { left: 0; width: 34px; min-width: 34px; }
.wtr-psheet .wtr-pfix-2 { left: 34px; min-width: 190px; box-shadow: inset -1px -1px 0 var(--line); }
/* ⚠ The two corner cells are BOTH sticky, so they need to outrank both bars or the header slides
   over the name column and the name column slides over the header. */
.wtr-psheet thead .wtr-pfix { z-index: 4; background: var(--surface-2, #f6f8f6); }
/* A striped row must keep its colour under the frozen column, or the name sits on a white island. */
.wtr-psheet tr.bad .wtr-pfix { background: color-mix(in srgb, var(--danger, #b42318) 7%, var(--surface)); }

/* ---- Entered and compliant vs not entered vs EXPIRED -----------------------------------------
   ⚠ EXPIRED IS THE LOUDEST, and that is the whole point. An empty box is obviously unanswered; a
   date that has quietly gone out of date LOOKS answered, and a document that reads the same whether
   the work happened or not is the finding this module exists to prevent.
   ⚠ Colour is never the only signal — each state also carries a left edge of its own weight, so it
   survives a colour-blind reader and a bad projector. Colour vision is itself a column here. */
.wtr-st { border-left: 3px solid transparent; }
.wtr-st-ok       { border-left-color: color-mix(in srgb, var(--good) 55%, transparent); }
.wtr-st-due      { border-left-color: var(--warn); background: color-mix(in srgb, var(--warn) 9%, transparent); }
.wtr-st-expired  { border-left-color: var(--danger); background: color-mix(in srgb, var(--danger) 16%, transparent); }
.wtr-st-missing  { border-left-color: var(--line-strong); background: repeating-linear-gradient(
                     -45deg, transparent 0 5px, color-mix(in srgb, var(--muted) 8%, transparent) 5px 10px); }
.wtr-st-blocked  { border-left-color: #b3261e; background: color-mix(in srgb, #b3261e 22%, transparent); }
/* An expired date is the one value that is worse than an empty box — say so in the text too. */
.wtr-st-expired input[type="date"] { border-color: var(--danger); color: var(--danger); font-weight: 650; }

.wtr-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 14px; margin: 6px 0 8px; font-size: 11px; color: var(--muted); }
.wtr-legend span { display: inline-flex; align-items: center; gap: 5px; }
.wtr-key { width: 12px; height: 12px; border-radius: 3px; border-left: 3px solid; display: inline-block; }

/* ---- Signing in person, or as a scribe -------------------------------------------------------- */
.wtr-sign { margin: 10px 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); }
.wtr-signrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 6px; font-size: var(--t-sm); }
.wtr-signrow input[type="text"], .wtr-signrow input:not([type]), .wtr-signrow select { padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--r-sm); font-size: var(--t-sm); }
.wtr-signrow input[placeholder] { min-width: 200px; }

/* ---- Wet signature pads ------------------------------------------------------------------------
   ⚠ `touch-action: none` is LOAD-BEARING. Without it a finger stroke scrolls the page instead of
   drawing, which on the wall iPads means the pad simply does not work. */
.wtr-sigpair { display: flex; flex-wrap: wrap; gap: 12px; margin: 10px 0 4px; }
.wtr-sigbox { flex: 1 1 260px; min-width: 240px; display: flex; flex-direction: column; gap: 6px; }
.wtr-sigbox > b { font-size: var(--t-sm); }
.wtr-sigbox input { padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--r-sm); font-size: var(--t-sm); }
.wtr-sigpad {
  width: 100%; height: 90px; touch-action: none; cursor: crosshair;
  border: 1px dashed var(--line-strong); border-radius: var(--r-sm);
  background: var(--surface); display: block;
}
.wtr-sigbox .wtr-mini { align-self: flex-start; }

/* ==== Dialysis Compliance dashboard refresh (2026-08-25) =====================================
   Appended as one block so the whole redesign is a single, reviewable region. Same class names
   as the rules earlier in the file; being later in the file, these win at equal specificity.
   Token-driven throughout, so light and dark both come from the palette above. */

/* Page header */
.wtr-hero { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s4); flex-wrap: wrap; }
.wtr-hero h2 { margin: 0; font-size: 26px; font-weight: 800; letter-spacing: -0.02em; color: var(--ink); }
.wtr-hero p { margin: 4px 0 0; font-size: var(--t-sm); color: var(--muted); }
.wtr-hero .wtr-btn { display: inline-flex; align-items: center; gap: 8px; }
.wtr-hero .wtr-btn svg { width: 16px; height: 16px; }

/* The stopped-floor banner stays the loudest thing on the page — just better finished. */
.wtr-blocked {
  padding: 16px 20px; border-radius: var(--r-lg);
  background: linear-gradient(135deg, #a31d16, #c0392b); color: #fff;
  box-shadow: var(--sh-2); gap: 6px;
}
.wtr-blocked b { font-size: var(--t-md); letter-spacing: .01em; }
.wtr-blocked span { opacity: .94; }

/* Needs-attention queue: one card per row, an icon per severity, sentence in calm type. */
.wtr-queue { gap: 8px; }
.wtr-qrow {
  align-items: center; gap: 12px; padding: 12px 14px;
  border: 1px solid var(--line); border-left-width: 1px; border-radius: var(--r-md);
  background: var(--surface); box-shadow: var(--sh-1); font-size: var(--t-sm);
}
.wtr-qico {
  flex: 0 0 auto; width: 30px; height: 30px; border-radius: var(--r-full);
  display: inline-flex; align-items: center; justify-content: center; align-self: flex-start;
}
.wtr-qico svg { width: 16px; height: 16px; }
.wtr-qtext { flex: 1 1 auto; min-width: 0; }
.wtr-qtext b { display: block; font-size: var(--t-sm); margin-bottom: 1px; }
.wtr-qtext span { color: var(--muted); line-height: 1.5; }
.wtr-qrow.sev0, .wtr-qrow.sev1 { border-color: color-mix(in srgb, var(--danger) 32%, var(--line)); background: color-mix(in srgb, var(--danger) 4%, var(--surface)); }
.wtr-qrow.sev0 .wtr-qico, .wtr-qrow.sev1 .wtr-qico { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
.wtr-qrow.sev0 .wtr-qtext b, .wtr-qrow.sev1 .wtr-qtext b { color: var(--danger); }
.wtr-qrow.sev2 .wtr-qico { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.wtr-qrow.sev3 .wtr-qico, .wtr-qrow.sev4 .wtr-qico { background: var(--surface-3); color: var(--muted); }
.wtr-qrow.ok { border-color: color-mix(in srgb, var(--good) 30%, var(--line)); }
.wtr-qrow.ok .wtr-qico { background: color-mix(in srgb, var(--good) 14%, transparent); color: var(--good); }
.wtr-qrow .wtr-btn { flex: 0 0 auto; margin-left: auto; align-self: center; }

/* Section tabs: a segmented control. Sticky, with a soft blur so content slides under it. */
.wtr-tabs {
  position: sticky; top: 0; z-index: 2;
  display: inline-flex; flex-wrap: wrap; gap: 2px; align-self: flex-start;
  margin: 0; padding: 4px;
  background: color-mix(in srgb, var(--surface-3) 88%, transparent);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid var(--line); border-bottom: 1px solid var(--line); border-radius: var(--r-md);
  box-shadow: var(--sh-1);
}
.wtr-tab {
  padding: 8px 14px; border-bottom: 0; border-radius: calc(var(--r-md) - 3px);
  font-size: var(--t-sm); font-weight: 600; color: var(--muted);
  transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.wtr-tab:hover { color: var(--ink); background: color-mix(in srgb, var(--surface) 55%, transparent); }
.wtr-tab.on { color: var(--ink); background: var(--surface); box-shadow: var(--sh-1); border-bottom: 0; }

/* Clinic pills — the second navigation level, deliberately lighter than the segments above. */
.wtr-folders { border-bottom: 0; gap: 6px; margin: 0 0 calc(var(--s3) * -1); }
.wtr-folder {
  border: 1px solid var(--line); border-radius: var(--r-full);
  background: var(--surface); color: var(--muted);
  padding: 6px 13px; top: 0;
  transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.wtr-folder:hover { color: var(--ink); border-color: var(--line-strong); }
.wtr-folder.on {
  background: var(--primary-soft); color: var(--primary-ink);
  border-color: color-mix(in srgb, var(--primary) 42%, transparent); box-shadow: none;
}

/* Clinic status cards */
.wtr-clinics { gap: var(--s3); }
.wtr-clinic {
  border: 1px solid var(--line); border-radius: var(--r-lg); padding: 16px 18px;
  background: var(--surface); box-shadow: var(--sh-1); gap: 7px;
}
.wtr-clinic-name { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.wtr-clinic-state {
  align-self: flex-start; display: inline-flex; align-items: center; gap: 7px;
  font-size: 11.5px; font-weight: 700; letter-spacing: .05em;
  padding: 4px 11px; border-radius: var(--r-full);
  background: var(--surface-3); color: var(--muted);
}
.wtr-clinic-state::before { content: ''; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.wtr-clinic-detail { font-size: var(--t-xs); color: var(--ink-soft); opacity: 1; line-height: 1.45; }
.wtr-clinic-meta { font-size: var(--t-xs); color: var(--muted); opacity: 1; margin-top: 2px; }
.wtr-clinic.ok { border-color: var(--line); }
.wtr-clinic.ok .wtr-clinic-state { background: color-mix(in srgb, var(--good) 13%, transparent); color: var(--good); }
.wtr-clinic.warn { border-color: color-mix(in srgb, var(--warn) 38%, var(--line)); background: color-mix(in srgb, var(--warn) 4%, var(--surface)); }
.wtr-clinic.warn .wtr-clinic-state { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }
.wtr-clinic.bad { border-color: color-mix(in srgb, var(--danger) 45%, var(--line)); background: color-mix(in srgb, var(--danger) 5%, var(--surface)); }
.wtr-clinic.bad .wtr-clinic-state { background: color-mix(in srgb, var(--danger) 14%, transparent); color: var(--danger); }
.wtr-clinic.idle .wtr-clinic-state { opacity: 1; }
:root[data-theme="dark"] .wtr-clinic.warn { background: color-mix(in srgb, var(--warn) 7%, var(--surface)); }
:root[data-theme="dark"] .wtr-clinic.bad { background: color-mix(in srgb, var(--danger) 8%, var(--surface)); }

/* Section headings and the quiet line of 30-day figures */
.wtr-h { font-size: 15px; font-weight: 700; letter-spacing: -0.01em; }
.wtr-h4 { font-size: var(--t-sm); font-weight: 700; margin: 0; }
.wtr-statline { margin: -14px 0 0; font-size: var(--t-xs); color: var(--muted); opacity: 1; }

/* Teaching text folds away behind "How this works"; safety text stays in a visible callout. */
.wtr-help { margin: -14px 0 0; align-self: flex-start; max-width: 78ch; }
.wtr-help > summary {
  cursor: pointer; list-style: none; user-select: none; display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-xs); font-weight: 600; color: var(--muted);
  padding: 3px 0; border-radius: var(--r-sm);
}
.wtr-help > summary::-webkit-details-marker { display: none; }
.wtr-help > summary::before { content: '▸'; font-size: 10px; transition: transform var(--dur) var(--ease); }
.wtr-help[open] > summary::before { transform: rotate(90deg); }
.wtr-help > summary:hover { color: var(--ink); }
.wtr-help > div {
  margin-top: 8px; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: var(--t-sm); color: var(--muted); line-height: 1.55;
}
.wtr-callout {
  margin: -14px 0 0; max-width: 78ch; padding: 12px 14px; border-radius: var(--r-md);
  border: 1px solid color-mix(in srgb, var(--warn) 38%, var(--line));
  background: color-mix(in srgb, var(--warn) 6%, var(--surface));
  font-size: var(--t-sm); color: var(--ink-soft); line-height: 1.55;
}

/* Tables: soft cards with quiet headers. border-spacing 0 keeps the radius honest. */
.wtr-tbl {
  border-collapse: separate; border-spacing: 0;
  border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--sh-1);
}
.wtr-tbl th {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); background: var(--surface-2); padding: 10px 14px;
}
.wtr-tbl td { padding: 12px 14px; }
.wtr-tbl tbody tr:last-child td { border-bottom: 0; }
.wtr-tbl tbody tr:hover td { background: color-mix(in srgb, var(--surface-2) 55%, transparent); }
.wtr-tbl tbody tr.bad:hover td { background: color-mix(in srgb, var(--danger) 10%, var(--surface)); }
.wtr-tbl tbody tr.warn:hover td { background: color-mix(in srgb, var(--warn) 15%, var(--surface)); }

/* The TCL-1 log keeps its paper layout — the container just matches the new cards. */
.wtr-logwrap { border-radius: var(--r-lg); box-shadow: var(--sh-1); }
.wtr-log th { background: var(--surface-2); }

/* Export panel and forms */
.wtr-export, .wtr-form { border-radius: var(--r-lg); box-shadow: var(--sh-1); }
.wtr-export input, .wtr-export select, .wtr-form input, .wtr-form select, .wtr-form textarea {
  background: var(--surface); border-radius: var(--r-sm);
}

/* Buttons */
.wtr-btn {
  border-radius: 10px; background: var(--surface);
  box-shadow: var(--sh-1);
  transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background var(--dur) var(--ease);
}
.wtr-btn:hover:not(:disabled) { border-color: var(--line-strong); box-shadow: var(--sh-2); }
.wtr-btn-primary { background: var(--primary); border-color: var(--primary); color: var(--on-primary); }
.wtr-btn-primary:hover:not(:disabled) { background: var(--primary-hover); border-color: var(--primary-hover); box-shadow: var(--sh-2); }
.wtr-btn-warn { border-color: color-mix(in srgb, var(--warn) 55%, var(--line)); color: var(--warn); background: var(--surface); }
.wtr-btn-warn:hover:not(:disabled) { background: var(--warn); border-color: var(--warn); color: #fff; }
.wtr-mini { border-radius: 8px; background: var(--surface); }
.wtr-mini:hover:not(:disabled) { border-color: var(--line-strong); background: var(--surface-2); }

/* Countersignature cards */
.wtr-vcard { border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: var(--s4); }
.wtr-vhead b { font-size: 22px; letter-spacing: -0.01em; }
.wtr-vshots img, .wtr-vcam { border-radius: var(--r-md); }
.wtr-vsig { border: 1px dashed var(--line-strong); border-radius: var(--r-md); }

/* Empty states read as guidance, not as a broken screen. */
.wtr-empty {
  padding: 14px 16px; border: 1px dashed var(--line-strong); border-radius: var(--r-md);
  background: color-mix(in srgb, var(--surface) 60%, transparent); max-width: 78ch;
}

/* Phones: the queue's action button drops below its sentence instead of crushing it. */
@media (max-width: 700px) {
  .wtr-qrow { flex-wrap: wrap; }
  .wtr-qrow .wtr-btn { margin-left: 42px; flex: 0 1 auto; }
  .wtr-hero h2 { font-size: 22px; }
}

/* Phones: keep the segmented tabs readable despite mobile.css's tighter padding. The extra
   .wtr-tabs ancestor outweighs mobile.css's single-class rule regardless of load order. */
@media (max-width: 860px) {
  .wtr-tabs .wtr-tab { padding: 7px 10px; font-size: 12px; }
}

/* ---- Colour-vision plates ---------------------------------------------------------------------
   ⚠ The plate must NOT be resized by the browser's smoothing into something easier or harder to
   read than it is. Fixed pixel size, no filters, no zoom. */
.wtr-plate { display: flex; gap: 16px; align-items: flex-start; padding: 6px 0; }
.wtr-plate img { border-radius: 50%; flex: 0 0 auto; image-rendering: auto; }
.wtr-platebox { display: flex; flex-direction: column; gap: 6px; min-width: 220px; }
.wtr-platebox input { padding: 6px 10px; border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  font-size: var(--t-md); width: 90px; text-align: center; letter-spacing: .12em; }

/* ==== Worksheet + sticky-bar polish (2026-08-25, round 2) ====================================
   The owner's screenshots: the translucent tab bar let the grid print through it, and the
   "nothing on file" stripes covered most of a mostly-empty sheet — wallpaper, not signal. */

/* The section tabs go opaque, with a ring of page colour so content slides under cleanly.
   left:0 also pins the bar if anything ever makes the board scroll sideways. */
.wtr-tabs {
  left: 0;
  background: var(--surface-3);
  backdrop-filter: none; -webkit-backdrop-filter: none;
  box-shadow: var(--sh-1), 0 0 0 8px var(--bg);
}

/* The personnel sheet: calm until a value needs attention. */
.wtr-psheet-wrap { border-radius: var(--r-lg); box-shadow: var(--sh-1); }
.wtr-psheet th { padding: 9px 10px; }
.wtr-psheet td { padding: 8px 10px; vertical-align: middle; }
/* Empty inputs are the majority of a young sheet — they whisper until touched.
   ⚠⚠ THE NEXT LINE USED TO CLAIM "`.wtr-st-expired`'s own input rule is more specific and stays
   loud on purpose." THAT WAS FALSE AND IT COST THE SHEET ITS LOUDEST SIGNAL FOR FOUR HOURS.
   `.wtr-st-expired input[type="date"]` (:6774) and the rule below are BOTH (0,2,1) — one class,
   one attribute, one type each. An exact tie, so source order decides, and this one is 249 lines
   later. Every expired credential date rendered in ordinary --ink-soft with no border. Restored
   explicitly at (0,5,2) in the round-6 block at the end of this file; do not rely on this
   comment's arithmetic again — measure it. */
.wtr-psheet input[type="date"], .wtr-psheet select {
  border-color: transparent; background: transparent; color: var(--ink-soft);
  transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.wtr-psheet input[type="date"]:hover, .wtr-psheet select:hover,
.wtr-psheet input[type="date"]:focus, .wtr-psheet select:focus {
  border-color: var(--line-strong); background: var(--surface); color: var(--ink);
}
/* The autosave pulse must survive the transparent-border quieting above. */
.wtr-psheet input.saving, .wtr-psheet select.saving { border-color: var(--primary); background: var(--surface); }
.wtr-psheet input.saved, .wtr-psheet select.saved { border-color: var(--primary); background: var(--surface); }

/* "Nothing on file": a dashed edge and the faintest wash — visible, never wallpaper. */
.wtr-st-missing {
  border-left-style: dashed; border-left-color: var(--line-strong);
  background: color-mix(in srgb, var(--muted) 5%, transparent);
}

/* Cell chips step back; the row you are on offers them at full strength. */
.wtr-psheet .wtr-docadd { opacity: .5; }
.wtr-psheet tr:hover .wtr-docadd, .wtr-psheet .wtr-docadd:focus-visible { opacity: 1; }
.wtr-docadd, .wtr-docx, .wtr-complink { border-radius: var(--r-full); }
.wtr-edulink { border-radius: var(--r-full); }

/* Legend as labelled pills. */
.wtr-legend { gap: 8px; }
.wtr-legend > span {
  border: 1px solid var(--line); border-radius: var(--r-full);
  background: var(--surface); padding: 4px 11px 4px 7px;
}
.wtr-legend > span.wtr-frac, .wtr-legend > span:not(:has(.wtr-key)) { border: 0; background: none; padding: 4px 0; }
.wtr-key { width: 10px; height: 10px; }

/* ⚠ The score has to be readable across a desk — the person assessed is standing there and the only
   question that matters is whether they passed. */
.wtr-score { display: block; margin: 2px 0 8px; font-size: var(--t-md); font-weight: 700; letter-spacing: .01em; }
.wtr-score.ok  { color: var(--good); }
.wtr-score.bad { color: var(--danger); }

/* ---- The competency dialog ---------------------------------------------------------------------
   ⚠ CENTRED AND FIXED, so it belongs to the SCREEN rather than to the table. It began life as a row
   spliced into the worksheet, which put its content at the table's far-left edge — off-screen the
   moment the sheet was scrolled sideways, and unreachable by sticky positioning once it was the
   WINDOW doing the scrolling. Owner, 2026-08-25: "it should be centered in the screen, make this
   mobile friendly too." */
.wtr-cmp-card { max-width: 900px; }
/* A plate is 200px and its question sits beside it — below ~560px there is no room for both. */
@media (max-width: 620px) {
  .wtr-modal { padding: 0; align-items: stretch; }
  .wtr-cmp-card {
    max-width: none; width: 100%; max-height: 100vh; height: 100%;
    border-radius: 0; padding: 16px 14px 28px;
  }
  /* ⚠ Stack, do not shrink. A colour-vision plate that has been squeezed is a different test. */
  .wtr-plate { flex-direction: column; align-items: center; text-align: center; }
  .wtr-platebox { min-width: 0; width: 100%; align-items: center; }
  .wtr-sigpair { flex-direction: column; }
  .wtr-sigbox { min-width: 0; }
  .wtr-signrow { flex-direction: column; align-items: stretch; }
  .wtr-signrow label { display: flex; flex-direction: column; gap: 4px; }
  .wtr-cmp-card .wtr-edutbl td { display: block; width: 100%; }
  .wtr-cmp-card .wtr-comp-pick { text-align: left; padding-top: 0; }
}
/* ⚠ The signature pad must stay a comfortable size for a finger even on a phone. */
@media (max-width: 620px) { .wtr-sigpad { height: 110px; } }

/* ==== REGRESSION FIX (2026-08-25, round 3) ====================================================
   ⚠ `overflow: hidden` ON A TABLE KILLS EVERY `position: sticky` INSIDE IT.
   The round-1 refresh above gave `.wtr-tbl` `border-radius` + `overflow: hidden` so the corners
   would clip. `.wtr-psheet` also carries `.wtr-tbl`, and it holds THREE sticky things:
     · `.wtr-psheet thead th`      — the frozen header the owner asked for
     · `.wtr-psheet .wtr-pfix`     — the frozen tick + name columns
     · `.wtr-edurow .wtr-edu`      — the education panel, pinned sticky-left so it stays on screen
   overflow:hidden makes the table its own clipping scrollport, so all three stopped pinning and
   were clipped out of sight. Measured on the deployed file: with it, the header sat 199px ABOVE
   the card and the name column 256px to its LEFT; without it, +1px and +35px — correct.
   That is what produced the empty band with a small floating white box inside the worksheet.
   ⚠ The wrapper `.wtr-psheet-wrap` already draws the rounded card and the shadow, so the table
   needs neither. NEVER put overflow:hidden on a table that contains sticky cells. */
.wtr-tbl.wtr-psheet { overflow: visible; border-radius: 0; box-shadow: none; }

/* ==== Section navigation: UN-PINNED (2026-08-25, round 3) =====================================
   Owner: "dont pin it to the top, becasue it is looking very confusing when it is over our
   employee panel, or figure a better way out."

   They are right, and it is worse than cosmetic. The Employee Audit worksheet has its OWN frozen
   header and frozen name column pinned to the top of its card. A page-level strip pinned to the
   top of the scroll container lands in exactly the same place — two competing frozen zones, one
   covering the other, measured at 39,138px² of the worksheet obscured at every width and in both
   themes. No amount of styling fixes a second thing pinned over the first.

   So the strip now scrolls with the page like any other content. What that costs — reaching a
   section from deep inside a long log — is already covered elsewhere: the left rail and ⌘K both
   open this module directly, and ⌘K has its own "Audit Alerts" entry (see openWaterLazy in app.js).

   ⚠ These four declarations each override something currently live; do not drop one as redundant.
   ⚠ Stateless and markup-free ON PURPOSE. openWater() re-renders this whole strip on every
   realtime tick (any of 8 clinics finishing a test), so anything holding DOM-only state — an open
   menu, a scroll offset, a roving tabindex — would be destroyed mid-use. */
.wtr-tabs {
  position: static;                 /* was: sticky */
  top: auto; left: auto;            /* inert once static, but stated so the intent survives a merge */
  background: var(--surface-3);     /* was: 88% color-mix — no longer needs to hide content behind it */
  backdrop-filter: none; -webkit-backdrop-filter: none;
  box-shadow: none;                 /* was: sh-1 + an 8px ring of --bg, both only there to mask overlap */
  z-index: auto;
}

/* ⚠ KEEP THE STRIP ON SCREEN AFTER A TAB CLICK. The click handler ends with
   `bodyEl.scrollIntoView({ block: 'nearest' })`, which aligns the body's top edge with the top of
   the scrollport — so without a scroll margin the strip you just used scrolls out of view above,
   and consecutive switches each cost a scroll back. The margin reserves the strip's own height
   (~46px) plus the flex gap, so it stays visible directly above the section you landed on.
   This also fixes a defect that is live today: with the strip pinned, that same scrollIntoView
   parked the first ~50px of EVERY section underneath it. */
.wtr-body { scroll-margin-top: 68px; }

/* The strip is content now, not chrome, so it sits on the page rather than floating above it. */
.wtr-tabs { border: 1px solid var(--line); }
.wtr-tab.on { background: var(--surface); box-shadow: var(--sh-1); }

/* ==== Dialysis Compliance: the section nav moves BESIDE the work, not on top of it ============
   (2026-08-25, round 4 — supersedes the round-3 un-pin block above at >=1360px only)

   Owner: "dont pin it to the top, becasue it is looking very confusing when it is over our
   employee panel, or figure a better way out."

   Round 3 (lines 7103-7142) did the first half: it un-pinned the strip. That removed the overlap,
   and it must stay — but measured on the deployed file it left the navigation unreachable. The
   hero, the DIALYSIS STOPPED banner and the eight-clinic "Needs attention" queue are 1,043px tall
   at 1440x900, and the scrollport is 790px, so the section buttons now open 253px BELOW THE FOLD
   (519px at 1024, 998px at 430) and then scroll away for the rest of a 4,852px log. The only route
   to Audit Alerts from the bottom of the Water Check log is a full scroll back to the top.

   THE BETTER WAY OUT. The strip did not need to stop being reachable — it needed to stop being ON
   TOP OF anything. A pinned bar and the personnel worksheet share one column, so by construction
   they fight for the same pixels: .wtr-psheet has its OWN frozen header and frozen name column
   pinned to its own card, and a page-level bar pinned to the scrollport lands in the same place.
   That is a layout fact, not a styling bug — no z-index or opacity fixes it. (It is also the ONLY
   section with a competing frozen zone: .wtr-log, the TCL-1 table, has no sticky thead. The
   owner's complaint is precise.)

   So above 1360px the board becomes two columns and the nav gets one of its own. The worksheet's
   column starts where the nav's ends, so there is nothing left for the nav to cover, and it can
   then stay put while you read — macOS/iOS Settings, not a floating toolbar. Measured on the
   8-clinic harness: overlap with .wtr-psheet-wrap is 0px^2 at EVERY scroll position on every
   section, the nav is visible at rest (y=184, above the 790px fold) and still hit-testable as a
   TAB at scrollTop 4,370 of a 5,160px page.

   Below 1360px there is not enough width for a second column, so NOTHING here applies and the
   board renders exactly as round 3 leaves it: the strip in flow, un-pinned, never over the
   worksheet. That is the owner's instruction taken literally, and it is why this whole block is
   inside one media query.

   VERIFIED, appended in this position, against the live 7,142-line file:
     · sideways overflow is IDENTICAL to production at all 19 widths tested from 1920 to 390
       (0 above 1100px; 13/89/213/253/345/423/463 below it — all pre-existing, none made worse);
     · the worksheet's frozen header and frozen name column are byte-identical, +1px and +35px
       from the card's edges, at 1440 / 1024 / 430, exactly as without this block;
     · compPanel()'s `.wtr-cmp-overlay` (.wtr-modal, position: fixed) still measures 0,0 1440x900
       — the full viewport. `display: grid` does NOT create a containing block for fixed
       descendants (only transform / filter / contain / will-change / backdrop-filter do), which
       is why this is a grid and NOT `container-type: inline-size`. A container query is the
       textbook answer here and it is WRONG: `container-type` applies layout containment, which
       WOULD re-anchor that dialog to the column and un-centre it from the screen — the exact
       defect the competency overlay was just rewritten to fix. */

@media (min-width: 1360px) {

  /* ⚠ OVERRIDES styles.css:5009 `.wtr-wrap { display: flex; flex-direction: column; gap: --s5 }`.
     `height: 100%; overflow: auto` are NOT touched — .wtr-wrap stays this board's scroll
     container, which is what the nav's `position: sticky` pins to.

     ⚠ row-gap MUST be 0 and the 24px re-applied as margin-bottom below. The nav spans a large
     fixed number of rows (see `grid-row`), and any row-gap would be charged for every EMPTY
     implicit row that span creates — measured at +24px of dead page per unused row. With
     row-gap: 0 an unused row costs exactly 0, so one span value is safe for every combination of
     the two conditional children. Spacing is unchanged: measured row heights 75/99/43/748 =
     content + exactly 24px, the same rhythm as the flex gap it replaces. */
  .wtr-wrap {
    display: grid;
    /* 176px, NOT 188px. Measured: at 188px the Environmental Audit section pushes .wtr-wrap 3px
       sideways at 1360 and 13px at 1350 — a regression against production. At 176px every one of
       the six sections is clean at 1360 and only 1px over at 1350, so the breakpoint has real
       headroom and a 1366x768 laptop (very common) gets the sidebar. Do not widen this without
       re-running the six-section overflow sweep. */
    grid-template-columns: 176px minmax(0, 1fr);
    column-gap: var(--s4);
    row-gap: 0;
    /* ⚠ BOTH are load-bearing. `align-content: start` stops the rows stretching to fill the
       100%-height container. `align-items: start` stops the nav being stretched to its full row
       height — and a flex/grid item that is already as tall as its area has nowhere to travel, so
       `position: sticky` would silently do nothing. */
    align-content: start;
    align-items: start;
  }

  /* Everything except the hero lives in the right-hand column; the hero keeps the full width so
     the page title and the Setup button still read as the header of the whole board.
     ⚠ (0,1,0) on purpose — the two rules below are (0,2,0) and override it. */
  .wtr-wrap > * { grid-column: 2; margin-bottom: var(--s5); }
  .wtr-wrap > .wtr-hero { grid-column: 1 / -1; }
  .wtr-wrap > .wtr-body {
    /* ⚠ min-width: 0 IS LOAD-BEARING. A grid item's automatic minimum size is its content's
       min-content width, so a section holding a wide table would widen this column instead of
       scrolling inside its own wrapper, and the whole board would scroll sideways. */
    min-width: 0;
    margin-bottom: 0;
    /* ⚠ OVERRIDES styles.css:7138 `.wtr-body { scroll-margin-top: 68px }`. That margin exists so
       the STATIC strip stays visible after the click handler's `bodyEl.scrollIntoView()`
       (water.js:3988). With the nav in its own column it is always visible, so the reservation is
       just a 68px blank gap under the tab you pressed. It stays live below 1360px. */
    scroll-margin-top: 0;
  }

  .wtr-wrap > .wtr-tabs {
    grid-column: 1;
    /* ⚠ THE SPAN IS WHAT MAKES THE STICKY WORK, AND IT IS NOT COSMETIC. A grid item's containing
       block is its GRID AREA, and sticky can only travel inside it. Left to auto-placement the
       nav lands in one short row — measured at 19px tall — and a conforming browser unsticks it
       after 19px, i.e. it scrolls away exactly as it does today. (Chromium currently keeps it
       pinned anyway; that is browser leniency, not the spec, and every station here is an iPad,
       so it is not something to ship on.) Starting at row 2 puts the nav directly under the
       full-width hero, and the deliberately generous span guarantees the area reaches the LAST
       row — .wtr-body — whichever of `.wtr-blocked` and the `<h3>Needs attention` happen to be
       rendered. Verified in all four combinations: nav at y=184 at rest, pinned at y=109 at the
       bottom of the page, hit-test returns a TAB. Empty spanned rows cost 0px (row-gap: 0). */
    grid-row: 2 / span 40;

    /* ⚠ OVERRIDES styles.css:7122-7129 (round 3's `position: static; top/left: auto`) — but ONLY
       here. Sticky is safe now precisely because the nav is not in the worksheet's column.
       z-index 1 is enough for the same reason; it is beside the sheet, never over it. For the
       record, the numbers it would have had to beat: .wtr-psheet thead th is 3 and
       .wtr-psheet thead .wtr-pfix is 4 (styles.css:6744, 6756), and .wtr-psheet-wrap sets no
       z-index and no transform, so it opens no stacking context and those cells would have
       competed with a pinned bar directly — a pinned strip at z-index 2 loses that fight and is
       invisible AND unclickable exactly where it overlaps. */
    position: sticky; top: 0; left: auto; z-index: 1;

    /* ⚠ OVERRIDES styles.css:6846-6853 (`inline-flex`, `flex-wrap: wrap`, `align-self: flex-start`,
       `padding: 4px`, `border-radius: --r-md`) and :7010-7015 (`left: 0`, `--surface-3`,
       the `0 0 0 8px var(--bg)` ring). The horizontal segmented control becomes a vertical list. */
    display: flex; flex-direction: column; flex-wrap: nowrap; gap: 2px;
    align-self: start;
    margin: 0; padding: var(--s2);

    /* Matches .rail-left (styles.css:338) so the board reads as one app rather than a page that
       grew its own furniture. --surface, not --surface-3: this is a panel now, not a control. */
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-1);
    /* ⚠ Kills the blur from :6849. An opaque panel must not let the log print through it — that
       translucency is what the owner's earlier screenshots showed as "the grid printing through". */
    backdrop-filter: none; -webkit-backdrop-filter: none;

    /* Six sections never come close to this. It exists so a SEVENTH cannot make the last row
       unreachable: a sticky box taller than the scrollport cannot be scrolled to. 132px is the
       measured distance from the top of the window to the pinned card (109px) plus breathing room.
       ⚠ overscroll-behavior: contain stops a flick inside the nav from chaining to the board. */
    max-height: calc(100dvh - 132px);
    overflow-y: auto;
    overscroll-behavior: contain;
  }

  /* Rows. ⚠ (0,3,0) so it beats styles.css:6992's `.wtr-tabs .wtr-tab` (0,2,0) and mobile.css's
     later-loading `.wtr-tab` (0,1,0) regardless of load order. */
  .wtr-wrap > .wtr-tabs .wtr-tab {
    display: flex; width: 100%; align-items: center; gap: var(--s2);
    justify-content: flex-start; text-align: left;
    /* "Emergency Crash Cart Audit" wraps to two lines here instead of being squeezed onto one —
       a column has room a 44px strip never did. ⚠ overrides `white-space: nowrap` at :5030. */
    white-space: normal; line-height: 1.35;
    padding: 9px 10px 9px 11px;
    /* ⚠ overrides the underline tab at :5024-5030 (`border-bottom: 2px solid transparent`) and the
       pill at :6855-6858 (`border-radius: calc(--r-md - 3px)`). */
    border: 0; border-left: 3px solid transparent; border-radius: var(--r-sm);
    font-size: var(--t-sm); font-weight: 600; color: var(--ink-soft);
    outline-offset: -2px;          /* the focus ring stays inside the scrolling card */
    box-shadow: none;
  }
  .wtr-wrap > .wtr-tabs .wtr-tab:hover { background: var(--surface-2); color: var(--ink); }
  /* ⚠ (0,4,0), which is what it takes to beat styles.css:7142 `.wtr-tab.on` (0,2,0) — round 3's
     white pill, which on a white panel would leave the selected section with no marking at all.
     ⚠ THREE cues, only one of them hue: a filled row (lightness), a solid left bar (shape) and
     heavier text (weight). Deuteranopia flattens sage against grey; it does not flatten a filled
     box against an empty one, or 700 against 600. This screen is a colour-vision audit tool. */
  .wtr-wrap > .wtr-tabs .wtr-tab.on {
    background: var(--primary-soft); color: var(--primary-ink); font-weight: 700;
    border-left-color: var(--primary); box-shadow: none;
  }
  /* The outstanding-alerts count sits flush right instead of hugging the label. */
  .wtr-wrap > .wtr-tabs .wtr-tabn { margin-left: auto; flex: 0 0 auto; }
}

/* ==== Row hover must never make a frozen cell see-through (2026-08-25, round 5) ===============
   Owner, on hovering a staff name: "look how when my mouse is on a staff's name, it looks weird."
   The row's own scrolled-away cells were printing through the frozen name column.

   ⚠ A STICKY CELL WITH A TRANSLUCENT BACKGROUND SHOWS WHATEVER SCROLLS UNDER IT. The frozen
   columns are opaque by design — `.wtr-psheet .wtr-pfix { background: var(--surface) }`
   (styles.css:6751, specificity 0,2,0). Round 1 added
   `.wtr-tbl tbody tr:hover td { background: color-mix(in srgb, var(--surface-2) 55%, transparent) }`
   (styles.css:6942) at 0,2,3 — MORE specific — so on hover it repainted those same cells at 55%
   opacity and the sheet's own columns showed through them.

   ⚠ THE OLDER CODE ALREADY KNEW: styles.css:6758 blends into `var(--surface)`, not into
   `transparent`, for exactly this reason. Blend hover tints into a real colour, never into
   transparent, anywhere a sticky cell can be involved. */

/* 1. The generic row hover goes opaque. Same tint, blended into the surface instead of into
      nothing. `background-color` (not the shorthand) so a cell's own background-image survives. */
.wtr-tbl tbody tr:hover td { background-color: color-mix(in srgb, var(--surface-2) 55%, var(--surface)); }

/* 2. The frozen columns, restated opaque at (0,3,2) so nothing at (0,2,x) can thin them again.
      They take the hover tint too — the mouse is on the name, so the name is what should light up. */
.wtr-psheet tbody tr:hover .wtr-pfix { background-color: color-mix(in srgb, var(--surface-2) 92%, var(--surface)); }
.wtr-psheet tbody tr.bad:hover .wtr-pfix { background-color: color-mix(in srgb, var(--danger) 12%, var(--surface)); }

/* 3. ⚠ THE STATUS TINTS MUST SURVIVE THE HOVER. Rule 1 repaints every cell in the row, which would
      wash "expired" and "due" back to grey under the pointer — on a sheet whose entire purpose is
      showing what has lapsed, and where the row being hovered is the row being read. These are the
      same colours as styles.css:6767-6772, blended into --surface instead of transparent. */
.wtr-psheet tbody tr:hover td.wtr-st-due     { background-color: color-mix(in srgb, var(--warn) 9%, var(--surface)); }
.wtr-psheet tbody tr:hover td.wtr-st-expired { background-color: color-mix(in srgb, var(--danger) 16%, var(--surface)); }
.wtr-psheet tbody tr:hover td.wtr-st-blocked { background-color: color-mix(in srgb, #b3261e 22%, var(--surface)); }
.wtr-psheet tbody tr:hover td.wtr-st-missing { background-color: color-mix(in srgb, var(--muted) 5%, var(--surface)); }

/* 4. The education panel spans every column and its content is pinned sticky-left, so the same
      bleed-through applies to it. */
.wtr-psheet tbody tr.wtr-edurow:hover > td { background-color: var(--surface-2); }

/* ==== Round 6: seven regressions from today's own styling (2026-08-25) ========================
   Found by a four-lens audit of everything appended today, each lens's findings then handed to a
   second reviewer whose job was to REFUTE them. Seven survived; all but one are mine.

   The shared cause, for the third time today: a COSMETIC rule I added silently defeating an OLDER
   FUNCTIONAL one — usually by an exact specificity TIE decided on source order, which is the case
   that reads as "more specific" when you eyeball it and is not.
   ⚠ The lesson is not "be careful". It is: when a new rule touches a property an older rule set
   deliberately, MEASURE the computed value in a browser. Every one of these was invisible in
   review and obvious in getComputedStyle. */

/* ---- 1. An EXPIRED credential date printed in ordinary grey. breaks-a-feature. --------------
   The single loudest signal on a survey worksheet, lost to the tie described at :7022.
   Measured: border rgba(0,0,0,0) and colour --ink-soft, i.e. identical to an in-date cell.
   ⚠ `:not(.saving):not(.saved)` is LOAD-BEARING — without it this (0,5,2) rule also outranks
   `.wtr-psheet input.saving/.saved` (:7032-7033) and swallows the autosave pulse, which is the
   very same mistake one rung down. Excluding the two states lets them fall through. */
.wtr-psheet td.wtr-st-expired input[type="date"]:not(.saving):not(.saved),
.wtr-psheet td.wtr-st-expired input[type="date"]:not(.saving):not(.saved):hover,
.wtr-psheet td.wtr-st-expired input[type="date"]:not(.saving):not(.saved):focus {
  border-color: var(--danger); background: var(--surface);
  color: var(--danger); font-weight: 650;
}

/* ---- 2. Keyboard focus was invisible on every button and tab. breaks-a-feature. -------------
   `:focus-visible { outline: none; box-shadow: var(--ring) }` (:167) is the app's ONLY focus
   indicator — the outline is switched off globally. Round 1 gave `.wtr-btn` a resting
   `box-shadow: var(--sh-1)` at the same (0,1,0) but later, so focusing a button changed nothing
   at all; round 4's `box-shadow: none` did the same to the section tabs. Measured: 8 consecutive
   Tab stops with no visible indicator.
   ⚠ At >=1360px the nav column has `overflow-y: auto`, which CLIPS an outset ring — so the
   sidebar tabs get an INSET ring instead. */
.wtr-btn:focus-visible:not(:disabled) { box-shadow: var(--ring), var(--sh-1); }
.wtr-tab:focus-visible, .wtr-folder:focus-visible, .wtr-mini:focus-visible { box-shadow: var(--ring); }
@media (min-width: 1360px) {
  .wtr-wrap > .wtr-tabs .wtr-tab:focus-visible { box-shadow: inset 0 0 0 2px var(--primary); }
}

/* ---- 3. The 131 "+ file" chips sat at 1.99:1. visible-defect. -------------------------------
   Round 2 faded them to `opacity: .5` to quiet a busy sheet, which also faded their 10px label:
   4.88:1 -> 1.99:1, against a 4.5:1 floor. ⚠ Fade the chip's EDGE, not the whole chip, so the
   label never leaves full contrast and the "step back until you are on this row" intent survives.
   ⚠ The :hover line is required: a bare `.wtr-psheet .wtr-docadd` appended here is (0,2,0) and
   would otherwise defeat `.wtr-docadd:hover` (:6714) at the same weight — the same trap again. */
.wtr-psheet .wtr-docadd { opacity: 1; border-color: color-mix(in srgb, var(--line) 55%, transparent); }
.wtr-psheet tr:hover .wtr-docadd,
.wtr-psheet .wtr-docadd:hover,
.wtr-psheet .wtr-docadd:focus-visible { border-color: var(--line-strong); }

/* ---- 4. The recorded pass/fail answer went white-on-white under the pointer. ----------------
   Round 1's `.wtr-mini:hover:not(:disabled) { background: var(--surface-2) }` (:6968, (0,3,0))
   ties `.wtr-comp-pick .wtr-mini.on` (:6693, (0,3,0)) and wins on order — it replaced the teal
   fill but not the white text: 1.07:1. Worse than a hover nuisance: the click handler re-renders
   the cell in place, so the answer you JUST recorded appears under the stationary pointer, and on
   an iPad :hover sticks until you tap elsewhere. */
.wtr-comp-pick .wtr-mini.on:hover:not(:disabled) {
  background: var(--primary); border-color: var(--primary); color: var(--on-primary);
}

/* ---- 5. Half the "Needs attention" cards kept an orphaned coloured left edge. cosmetic. -----
   Round 1 moved the severity cue to the icon disc and re-stated the border for sev0/sev1/ok but
   not sev2/3/4, leaving the pre-redesign `#b45309` / `#64748b` left bars alive at (0,2,0) — on the
   LESS urgent rows only, so the surviving colour actively inverted the hierarchy. */
.wtr-qrow.sev2, .wtr-qrow.sev3, .wtr-qrow.sev4 { border-left-color: var(--line); }

/* ---- 6. Wider table padding pushed the board sideways on smaller laptops. cosmetic. ---------
   Round 1's 14px side padding (:6938-6940) beats the older 12px on order. `.wtr-wrap` is the
   scroll container and the Water Check tables sit directly in it, so the hero and the DIALYSIS
   STOPPED banner slid with them. Measured 1120px: 17px of sideways scroll vs 0 before today.
   ⚠ `:not(.wtr-psheet)` — the worksheet has its own tighter metrics and its own scroll wrapper. */
.wtr-tbl:not(.wtr-psheet) th,
.wtr-tbl:not(.wtr-psheet) td { padding-left: 12px; padding-right: 12px; }

/* ---- 7. On a phone, every dialog became a top-anchored slab. visible-defect. NOT MINE. -------
   The competency-overlay rewrite (another session, same day — see the block at :7063) made
   `.wtr-modal` full-bleed under 620px so a colour-vision plate would not be squeezed. That class
   is shared by every dialog in the module (water.js:2970, 3023, 3078), so "Excuse this check" and
   friends became a full-height card with ~332px of blank white under their buttons.
   Scoped away from the competency overlay ONLY, so their intent is preserved exactly. */
@media (max-width: 620px) {
  .wtr-modal:not(.wtr-cmp-overlay) { padding: 20px; align-items: center; }
}

/* Deleting an unfinished test — quiet until it is armed, then unmistakably destructive. */
.wtr-cmpdel { color: var(--muted); }
.wtr-cmpdel:hover { color: var(--danger); border-color: var(--danger); }
.wtr-cmpdel[data-armed="1"] { color: var(--danger); border-color: var(--danger); font-weight: 700; }

/* ==== CREDENCE COLOUR SCHEME (2026-08-26) =====================================================
   Owner: "fix company color scheme, not a big fan of teal anyways, you actually picked it for me.
   i prefer greens and blues" — then: "i want credence to be separate then the others but
   connected, likeing blues and greens".

   ⚠ THE TEAL WAS NEVER YOURS. I sampled all four company logos (public/brand/*.png): Kidney
   Hypertension Clinic, Nephron Dialysis Centers, Nephron Pharmacy and AccuLab all share ONE navy,
   #18306C, plus a red-orange; AccuLab adds a cyan. There is no green and no teal anywhere in the
   real branding. The old --primary #1f8a76 matched nothing the clinics actually print.

   SEPARATE, BUT CONNECTED. Credence Management Services LLC owns this software; the clinics are
   who it serves, and the existing rule is that patient- and vendor-facing mail stays clinic-branded
   (see the ownership-branding note). So the split is: the APP wears Credence's colours, outgoing
   clinic mail keeps the clinic navy. Credence's blue is deliberately the clinic navy's lighter,
   more open sibling — same blue family, its own voice — and its green is the second half of the
   "blues and greens" the owner asked for.

   ⚠ WHY BLUE LEADS RATHER THAN GREEN. The status green that means "in date / cleared for today"
   (--good, hue 160) sat only 9 degrees from the teal. Any green brand lands 4-6 degrees away, i.e.
   the same colour, and "on brand" stops being distinguishable from "this is passing" on a
   COMPLIANCE screen. Credence blue sits 47 degrees away, so green is freed to mean exactly one
   thing. Measured, not guessed.

   ⚠ BONUS, AND IT WAS A REAL DEFECT: white on the old teal button measured 4.24:1 against a 4.5
   floor. On Credence blue it is 7.03:1.

   ⚠ APPENDED, NEVER EDITED IN PLACE. The tokens are declared at :root (~:15) and in TWO dark
   blocks (~:102 '@media (prefers-color-scheme: dark) :root:not([data-theme="light"])' and ~:121
   ':root[data-theme="dark"]'). Overriding by appending at equal specificity wins on source order
   and leaves the originals readable — and another session has this file open.
   ⚠ BOTH dark blocks must be restated or the toggle and the OS setting disagree.
   ⚠ STATUS COLOURS ARE NOT TOUCHED. --good / --warn / --danger keep their hues; a "danger" that
   drifts toward the brand is a safety problem on this product. Neither are the reward metals or
   the colour-blind-validated chart series (--viz-*), which were validated against these surfaces.
   ⚠ THE NEUTRAL GREYS ARE ALSO LEFT ALONE, deliberately. They carry a faint sage tint, which now
   reads as the "greens" half of the palette — and re-tinting every surface would change every
   screen in the product, including ones I cannot see in the preview harness. */

:root {
  --primary: #1D5B96;          /* Credence blue — the clinic navy's lighter sibling */
  --primary-hover: #174C80;
  --primary-ink: #1A5387;      /* accent TEXT on a pale accent ground */
  --primary-soft: rgba(29,91,150,.11);
  --primary-softer: rgba(29,91,150,.055);
  --on-primary: #ffffff;
  --ring: 0 0 0 3px rgba(29,91,150,.22);
  --email: #1F7A93;            /* email channel — cyan-blue, 16deg off the brand so the pair reads */
  --text-c: #4E8F63;           /* SMS channel — the green half */
  /* Identity only: the wordmark, the sign-in mark, print. ⚠ NEVER a status colour. */
  --brand-navy: #18306C;       /* shared with all four clinic logos — this is the "connected" part */
  --brand-green: #2E7D5A;      /* Credence's own green */
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --primary: #6BA5DB; --primary-hover: #85B5E4; --primary-ink: #AECBEE;
    --primary-soft: rgba(107,165,219,.15); --primary-softer: rgba(107,165,219,.075);
    --on-primary: #08192B; --ring: 0 0 0 3px rgba(107,165,219,.28);
    --email: #4FA8C0; --text-c: #7FB88C;
    --brand-navy: #7E9BD8; --brand-green: #5FB88F;
  }
}
:root[data-theme="dark"] {
  --primary: #6BA5DB; --primary-hover: #85B5E4; --primary-ink: #AECBEE;
  --primary-soft: rgba(107,165,219,.15); --primary-softer: rgba(107,165,219,.075);
  --on-primary: #08192B; --ring: 0 0 0 3px rgba(107,165,219,.28);
  --email: #4FA8C0; --text-c: #7FB88C;
  --brand-navy: #7E9BD8; --brand-green: #5FB88F;
}

/* ==== The board must not scroll sideways on narrow screens (2026-08-26) =======================
   ⚠ ONE UNSCOPED min-width CAUSED ALL OF IT. `.wtr-tbl td:nth-child(2) { min-width: 260px }`
   floors column 2 of EVERY table on the board. The note that justified it (.wtr-cellnote) exists
   in exactly one place — the Setup alert-routing table — where column 2 is a rule name plus a long
   explanation. On the board column 2 is "Tag", "Item" or "Clinic", whose own min-content is
   48-108px, so the rule spent 152-212px per table on nothing. Measured: the Environmental audits
   table is 789px wide against 678 available at 1024, and 260 of those 789 px are this floor.
   A CSS table cannot lay out below its min-content width, so the `width: 100%` above is powerless
   and the `overflow: hidden` added for rounded corners clips INSIDE the table, not the table box.
   Restored precisely where it earns its keep. */
.wtr-tbl td:nth-child(2) { min-width: 0; }
.wtr-tbl td:nth-child(2):has(.wtr-cellnote) { min-width: 260px; }

/* Below ~940px seven columns of real words genuinely cannot fit, so the SECTION scrolls inside
   itself rather than dragging the whole board — the hero and the alert queue stay put.
   ⚠ `flex: 0 0 auto` IS LOAD-BEARING, the same trap .wtr-logwrap documents. .wtr-wrap is a flex
   column of height:100%; a flex item whose overflow is not `visible` loses its automatic minimum
   size, and .wtr-body collapses to 0px tall — the whole section disappears into its own scrollbar.
   Measured at 1024: body height 4831 -> 0 without it.
   ⚠ Gated below 1360 on purpose: at and above it .wtr-wrap is the two-column grid and there is
   ~277px of headroom, so the rule is unnecessary there and would clip 24px off the grid. */
@media (max-width: 1359px) {
  .wtr-body { flex: 0 0 auto; overflow-x: auto; overscroll-behavior-x: contain; }
}

/* ==== The "Education received" panel sizes itself again (2026-08-26) ==========================
   fitPanel() in water.js writes an inline pixel width from the wrapper's clientWidth, and its own
   comment claims it runs "again on scroll/resize". It does not — both call sites are inside the
   click that CREATES the panel. Measured: opened at 1440 then narrowed to 430, it kept
   width:874px inside a 354px card, 518px hanging off the right edge.
   ⚠ 100% IS WRONG here — the panel's <td> spans thirteen columns, so a percentage resolves against
   the whole ~1,750px table. 100cqi is the scrollport's inline size: exactly what fitPanel was
   measuring by hand.
   ⚠ container-type IS SAFE ON THIS ELEMENT AND ONLY THIS ELEMENT. It applies layout containment,
   which WOULD re-anchor a position:fixed descendant — the reason the nav grid refuses it. The
   competency overlay is appended to #wtrPersonnel, a SIBLING of this wrapper, not a descendant,
   so containment cannot reach it.
   ⚠ !important is what lets this beat the stale inline width, so NO water.js edit is needed —
   which matters while that file is being edited elsewhere. On a browser without container queries
   the declaration is dropped at parse time and today's behaviour returns; never worse. */
.wtr-psheet-wrap { container-type: inline-size; }
.wtr-edurow .wtr-edu, .wtr-edurow > td > .wtr-note { width: max(320px, 100cqi) !important; }

/* ---- Intercompany deposits card (2026-08-26) ------------------------------------------- */
.ic-row{display:flex;justify-content:space-between;align-items:flex-start;gap:10px;
  padding:8px 12px;border-top:1px solid var(--border,#e3e7ee)}
.ic-row .ic-note{margin-top:2px}
.ic-list{max-height:340px;overflow-y:auto}
.ap-st-warn{background:#f6ead2;color:#7a5b16}

.ic-body{padding:4px 12px 12px}
.ic-line{display:grid;grid-template-columns:1fr 1fr auto;gap:6px;margin-bottom:6px}
.ic-wp{border-collapse:collapse;min-width:280px}
.ic-wp td{padding:3px 8px;border-bottom:1px solid var(--border,#e3e7ee)}
.ic-detail{margin:6px 0 2px;padding:8px 10px;background:var(--surface-2,#f4f6f5);border-radius:6px;
  font-size:.82rem;line-height:1.55}
.ic-det-line{margin:1px 0}
.ic-det-k{font-size:.68rem;text-transform:uppercase;letter-spacing:.06em;color:var(--muted,#7a8a85)}

/* ==== The SETUP screen is not the board — it keeps one column (2026-08-28) ====================
   Owner: "what happened to the dialysis compliance setup page, its all messy".

   Round 4 above (:7565) turns `.wtr-wrap` into a 176px + 1fr grid at >=1360px so the board's
   section nav can sit beside the work. But `.wtr-wrap` is not only the board: wateradmin.js
   renders the Setup screen inside the same class, with no nav strip and — the part that hurt —
   its TABLES as direct children. On the board every table lives inside `.wtr-body`; on Setup
   each `<table class="wtr-tbl">` became a grid item in its own right.

   ⚠ A TABLE WITH `overflow: hidden` AS A GRID ITEM GETS A ROW THE HEIGHT OF ONE LINE. Round 1
   (:7347) gave `.wtr-tbl` `overflow: hidden` for rounded corners. Measured at 1440 on the deployed
   file: the Station iPads table is 488px tall and its grid row is 26px, so the next item starts
   26px below the table's TOP and the table paints straight over the "Add a station" button, the
   audit library and section 5 — 1,298,452px² of direct children overlapping each other, plus a
   176px empty column on the left where the nav would have been. With `overflow: visible` on the
   table the row is 512px and nothing overlaps, which is how the fault was pinned to that pairing.
   Below 1360px none of this applies and the screen was always fine — so it "broke" on a wide
   monitor and nowhere else.

   The fix is scoping, not undoing round 4: a `.wtr-wrap` WITHOUT a `.wtr-tabs` strip (Setup, the
   "Loading…" and error placeholders) is a plain column again, exactly as the base rule at :5424
   draws it. The board keeps its nav and its grid untouched. `:has()` is already load-bearing in
   this stylesheet (`.body:has(#list .wtr-wrap)`, :334), so this adds no new browser requirement.
   ⚠ `align-items` / `align-content: normal` are NOT optional: round 4's `start` values apply to
   flex too, and in a column flexbox `align-items: start` shrinks every form and readiness card to
   its content width instead of the full column. ⚠ `.wtr-ready` gets its own 10px back (:6583);
   no other direct child carries a bottom margin of its own, so 0 restores the base layout. */
@media (min-width: 1360px) {
  .wtr-wrap:not(:has(> .wtr-tabs)) {
    display: flex; flex-direction: column; gap: var(--s5);
    align-items: normal; align-content: normal;
  }
  .wtr-wrap:not(:has(> .wtr-tabs)) > * { grid-column: auto; margin-bottom: 0; }
  .wtr-wrap:not(:has(> .wtr-tabs)) > .wtr-ready { margin-bottom: 10px; }
}

/* While here: the readiness board's closing line ("N people have a PIN…") sat ON the last card's
   border, because `.wtr-note`'s -12px top margin (:5577) is tuned for notes under a heading, not
   for a note that follows a bordered row inside a 6px-gap column. It gets the column's own gap. */
.wtr-ready > .wtr-note { margin-top: 0; }

/* The education preview's raw/room toggle — quiet, next to Listen. */
.wa-edu-raw { display: inline-flex; align-items: center; gap: 6px; font-size: var(--t-sm); color: var(--muted); margin: 0 4px; }

/* ---- A strip photograph, enlarged IN PLACE ------------------------------------------------------
   ⚠ Clicking one used to open a full-screen viewer over the dashboard, which loses the row you were
   reading — and the reason to look at a strip photo is almost always to compare it with the reading
   in that row. Owner, 2026-08-28. */
.wtr-strip-shot { cursor: zoom-in; transition: width .18s ease, height .18s ease; }
/* ⚠ width: auto, or the frame stays 132px wide and a 760px photograph spills out of it and lies OVER
   the next five thumbnails instead of pushing them along. And no hover lift on the big one — a 6%
   scale on a 560px photograph is 34px of it hidden under the cell's edge. */
.wtr-strip-shot.big { cursor: zoom-out; width: auto; }
.wtr-strip-shot.big:hover { transform: none; }
.wtr-strip-shot.big img {
  /* Bounded by the viewport so it can never grow taller than the screen and push the row off. */
  max-height: min(62vh, 560px); height: auto; width: auto; max-width: min(86vw, 760px);
  border-radius: var(--r-md);
}
.wtr-strip-shot.big figcaption { font-size: var(--t-sm); font-weight: 650; }
/* ⚠ The cell is a FIXED 132px box with overflow hidden (`.wtr-strip-row > td` above — that is what
   keeps thirteen thumbnails from widening the table), and the strip is absolutely positioned inside
   it, so an enlarged photograph cannot make the cell taller by itself. 2026-09-08: it was simply cut
   off at 132px — a band of foreheads, "I need to actually be able to see the full picture".
   water.js (fitRow) now measures the enlarged frame and sets the cell's height inline; this rule
   only makes that growth ease instead of snap. */
.wtr-strip-row > td { transition: height .18s ease; }
@media (prefers-reduced-motion: reduce) {
  .wtr-strip-row > td { transition: none; }
}

/* Clearing an alert by hand — quiet until armed, then unmistakably deliberate. */
.wtr-qclear {
  align-self: center; margin-left: auto; flex: 0 0 auto;
  font-size: 11px; padding: 3px 9px; border-radius: var(--r-sm);
  border: 1px solid var(--line); background: transparent; color: var(--muted); cursor: pointer;
}
.wtr-qclear:hover { border-color: var(--line-strong); color: var(--ink); }
.wtr-qclear[data-armed="1"] { border-color: var(--danger); color: var(--danger); font-weight: 700; }

/* ---- One invoice, several bills: the linked-bill banner (2026-09-01) --------------------------
   Shown on EVERY part of a split invoice, whatever status the other parts are in — see
   apSplitBannerHtml() in app.js for why: McKesson 91156167 read as though the AI had missed two
   lines, because the half holding them was already signed and had simply vanished from the draft.
   Each company name is a link to that part's own page. */
.ap-split .ap-split-head { font-weight: 600; margin-bottom: var(--s2); }
.ap-split .ap-split-parts { margin: 0 0 var(--s2); }
/* ↔ One part per line, WRAPPING rather than squeezing. This banner sits in the bill panel, only
   ~440px wide on a laptop, where a four-column table crushed "Accu Lab LLC" into a 58px column
   three lines tall. Flex-wrap holds up at every width with nothing scrolling sideways. */
.ap-split .ap-split-part { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); padding: 3px 0; }
.ap-split .ap-split-amt { font-variant-numeric: tabular-nums; font-weight: 600; }
.ap-split .ap-split-what { flex-basis: 100%; margin: 0; }   /* what it covers, always its own line */
.ap-split .ap-split-foot { border-top: 1px solid var(--line); padding-top: var(--s2); }
/* The vendor's own name in the bill's vendor card, as the way into their whole history. */
.ap-vc-name { font-size: inherit; font-weight: 700; }

/* ===================== Documents (scanned mail): per-department queue + upload ===================== */
/* One .docs-* family, all tokens — dark mode comes free. Density matches the rounds worklist
   (.rc-*): this is a working queue, not a report. Cells WRAP (never nowrap on long text) so the
   table always fits its wrapper — the no-sideways-scroll rule is enforced by engage.css §7, which
   includes .docs-scroll in its wrapper lists. Appended 2026-09-01 by the Documents feature. */
.docs-wrap { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.docs-muted { color: var(--muted); font-size: var(--t-sm); }
.docs-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px 8px; flex-wrap: wrap; }
.docs-sub { color: var(--muted); font-size: var(--t-sm); }
.docs-tools { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.docs-search { border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); border-radius: var(--r-full); padding: 7px 14px; font-size: var(--t-sm); min-width: 210px; font-family: var(--font); transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.docs-search:focus { outline: none; border-color: var(--primary); box-shadow: var(--ring); }
.docs-banner { display: flex; align-items: center; gap: 8px; margin: 0 16px 8px; padding: 9px 14px; border: 1px solid color-mix(in srgb, var(--primary) 30%, var(--line)); background: var(--primary-softer); color: var(--primary-ink); border-radius: var(--r-md); font-size: var(--t-sm); }
.docs-chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 0 16px 8px; }
.docs-summary { display: grid; grid-template-columns: repeat(4, minmax(120px, 1fr)); gap: 10px; padding: 0 16px 10px; }
.docs-sumcard { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); box-shadow: var(--sh-1); padding: 10px 14px; }
.docs-sumn { font-size: var(--t-lg); font-weight: 700; font-variant-numeric: tabular-nums; color: var(--ink); }
.docs-suml { color: var(--muted); font-size: var(--t-xs); margin-top: 1px; }
.docs-sumcard.is-warn .docs-sumn { color: var(--warn); }

.docs-scroll { overflow: auto; flex: 1; min-height: 0; padding: 0 16px 16px; }
.docs-table { border-collapse: separate; border-spacing: 0; width: 100%; font-size: var(--t-xs); }
.docs-table th { position: sticky; top: 0; z-index: 2; background: var(--surface-3); color: var(--ink-soft); font-weight: 600; text-align: left; padding: 8px; border-bottom: 1px solid var(--line-strong); }
.docs-table td { padding: 7px 8px; border-bottom: 1px solid var(--line); vertical-align: top; overflow-wrap: break-word; }
.docs-th-sort { cursor: pointer; user-select: none; }
.docs-th-sort:hover { color: var(--primary-ink); }
.docs-th-active { color: var(--primary-ink); background: color-mix(in srgb, var(--primary) 8%, var(--surface-3)); }
tr.docs-row { cursor: pointer; }
tr.docs-row:hover td { background: var(--primary-softer); }
tr.docs-row.is-open td { background: var(--primary-softer); border-bottom-color: transparent; }
tr.docs-row.is-handled td { color: var(--muted); }
.docs-ck { width: 30px; }
.docs-ck input { margin: 0; }
.docs-when, .docs-pg { white-space: nowrap; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
.docs-type { font-weight: 600; color: var(--ink); }
.docs-sum { color: var(--ink-soft); }
.docs-sum2 { color: var(--muted); font-size: 11px; margin-top: 2px; font-weight: 400; }
.docs-filterrow th { position: sticky; top: 33px; z-index: 1; background: var(--surface-2); padding: 4px 6px; }
.docs-colfilter { max-width: 100%; box-sizing: border-box; border: 1px solid var(--line-strong); background: var(--surface); color: var(--ink); border-radius: var(--r-sm); padding: 3px 6px; font-size: 11px; margin: 1px 0; }
.docs-colfilter:focus { outline: none; border-color: var(--primary); }

/* Status chips + the amber "check the split" dot + the last-send chip. */
.docs-st { display: inline-flex; align-items: center; gap: 4px; font-size: 10px; font-weight: 600; padding: 2px 8px; border-radius: var(--r-full); white-space: nowrap; }
.docs-st-new { background: var(--primary-soft); color: var(--primary-ink); }
.docs-st-assigned { background: color-mix(in srgb, var(--tint-blue) 14%, var(--surface)); color: var(--tint-blue); }
.docs-st-follow_up { background: var(--gold-soft); color: var(--gold-ink); }
.docs-st-handled { background: color-mix(in srgb, var(--good) 12%, var(--surface)); color: var(--good); }
.docs-st-moved, .docs-st-superseded { background: var(--surface-3); color: var(--muted); }
.docs-st-prot { background: var(--danger-soft); color: var(--danger); }
.docs-st .icon-sm { width: 11px; height: 11px; }
.docs-flag { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: var(--warn); margin-left: 6px; vertical-align: middle; }
.docs-send { display: inline-flex; font-size: 10px; color: var(--muted); background: var(--surface-3); border-radius: var(--r-full); padding: 2px 8px; margin-left: 6px; white-space: nowrap; }
.docs-send.is-bad { background: var(--danger-soft); color: var(--danger); }
.docs-stcell > * { margin-top: 2px; }

/* ---- inline viewer (the rounds .rc-photo-inline recipe: ONE panel, right under its row) ---- */
tr.docs-inline > td { padding: 0; background: var(--surface-2); border-bottom: 2px solid var(--line-strong); }
.docs-inline-box { padding: 12px 16px 16px; }
.docs-inline-head { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px; font-size: var(--t-sm); color: var(--ink-soft); margin-bottom: 10px; }
.docs-inline-head b { color: var(--ink); }
.docs-back { display: none; } /* phone-only escape hatch — mobile.css shows it */
.docs-flagchip { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); font-weight: 600; background: var(--gold-soft); color: var(--gold-ink); border-radius: var(--r-full); padding: 3px 10px; }
.docs-hint { display: inline-flex; align-items: center; gap: 4px; border: 1px dashed var(--primary); background: var(--primary-softer); color: var(--primary-ink); border-radius: var(--r-full); padding: 3px 10px; font-size: var(--t-xs); font-weight: 600; cursor: pointer; transition: background var(--dur) var(--ease); }
.docs-hint:hover { background: var(--primary-soft); }
.docs-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.docs-act[disabled] { opacity: .45; cursor: not-allowed; }
.docs-act[data-act="handled"] { border: 1px solid var(--primary); color: var(--primary-ink); background: var(--primary-softer); border-radius: var(--r-md); }
.docs-act[data-act="handled"]:not([disabled]):hover { background: var(--primary-soft); }
.docs-splitbar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 0 0 10px; padding: 8px 12px; border: 1px dashed var(--line-strong); border-radius: var(--r-md); background: var(--surface); color: var(--ink-soft); font-size: var(--t-sm); }
.docs-warn { background: var(--gold-soft); color: var(--gold-ink); border-radius: var(--r-md); padding: 8px 12px; font-size: var(--t-sm); margin: 0 0 10px; }
.docs-thumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; max-height: 62vh; overflow-y: auto; }
.docs-thumb { position: relative; width: 150px; min-height: 100px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); overflow: hidden; cursor: zoom-in; transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.docs-thumb:hover { border-color: var(--primary); box-shadow: var(--sh-1); }
.docs-thumb canvas { display: block; max-width: 100%; }
.docs-thumb-ph { display: flex; align-items: center; justify-content: center; height: 100px; color: var(--muted); font-size: var(--t-xs); }
.docs-thumb-n { position: absolute; bottom: 2px; right: 4px; font-size: 9px; color: var(--muted); background: color-mix(in srgb, var(--surface) 85%, transparent); border-radius: 3px; padding: 0 4px; }
.docs-cut { align-self: stretch; display: flex; align-items: center; justify-content: center; width: 26px; border: 1px dashed var(--line-strong); border-radius: var(--r-sm); background: var(--surface-2); color: var(--muted); cursor: pointer; transition: all var(--dur) var(--ease); }
.docs-cut:hover { border-color: var(--warn); color: var(--warn); }
.docs-cut.is-cut { border: 2px dashed var(--warn); background: var(--gold-soft); color: var(--gold-ink); }
.docs-more { align-self: center; }

/* Full-page reader over the thumbnails (prev / next / Escape). */
.docs-page-overlay { position: fixed; inset: 0; z-index: 3100; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; padding: 16px; }
.docs-page-card { background: var(--surface); border-radius: var(--r-lg); box-shadow: var(--sh-3); max-width: 94vw; max-height: 94vh; display: flex; flex-direction: column; overflow: hidden; }
.docs-page-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: var(--t-sm); color: var(--ink-soft); }
.docs-page-nav { display: flex; gap: 6px; }
.docs-page-body { overflow: auto; display: flex; align-items: flex-start; justify-content: center; padding: 10px; }
.docs-page-body canvas { border: 1px solid var(--line); border-radius: var(--r-sm); }

/* ---- dialogs (chooser-card additions) ---- */
.docs-card { max-width: 460px; }
.docs-card .docs-warn { margin: 10px 0 0; }
.docs-card .action-row { margin-top: 12px; }
.docs-pills { display: flex; flex-wrap: wrap; gap: 8px; margin: 4px 0 10px; }
.docs-pill { border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink-soft); border-radius: var(--r-full); padding: 7px 14px; font-size: var(--t-sm); font-weight: 600; cursor: pointer; transition: all var(--dur) var(--ease); }
.docs-pill:hover { border-color: var(--primary); }
.docs-pill.is-on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-ink); }
.docs-note { width: 100%; box-sizing: border-box; resize: vertical; }
.docs-lbl { font-size: var(--t-xs); font-weight: 600; color: var(--muted); margin: 8px 0 4px; }
.docs-confirmline { font-size: var(--t-sm); color: var(--ink); font-weight: 600; margin: 6px 0 4px; overflow-wrap: anywhere; }
.docs-firsttime { display: flex; align-items: flex-start; gap: 8px; font-size: var(--t-sm); color: var(--ink-soft); margin-top: 8px; }
.docs-chipsin { display: flex; flex-wrap: wrap; gap: 6px; border: 1px solid var(--line-strong); background: var(--surface); border-radius: var(--r-md); padding: 6px 8px; cursor: text; margin-bottom: 6px; }
.docs-chipsin input { border: none; background: none; color: var(--ink); flex: 1 1 140px; min-width: 120px; font: inherit; font-size: var(--t-sm); }
.docs-chipsin input:focus { outline: none; }
.docs-chipin { display: inline-flex; align-items: center; gap: 4px; background: var(--primary-soft); color: var(--primary-ink); border-radius: var(--r-full); padding: 2px 4px 2px 10px; font-size: var(--t-xs); font-weight: 600; overflow-wrap: anywhere; }
.docs-chipin button { border: none; background: none; color: inherit; cursor: pointer; font-size: 13px; padding: 0 4px; }
.docs-assign-sel { width: 100%; box-sizing: border-box; margin: 4px 0 2px; }

/* ---- sticky bulk bar (appears when rows are checked) ---- */
.docs-bulkbar { position: sticky; bottom: 0; display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 16px; background: var(--surface); border-top: 1px solid var(--line-strong); box-shadow: var(--sh-2); }
.docs-bulkn { font-size: var(--t-sm); font-weight: 700; color: var(--ink); }

/* ---- states ---- */
.docs-empty { display: flex; flex-direction: column; align-items: center; gap: 12px; padding: 48px 20px; color: var(--muted); text-align: center; }
.docs-empty svg { width: 34px; height: 34px; opacity: .6; }

/* ---- Undo toast (a toast with one clickable action) ---- */
.docs-toast { display: flex; align-items: center; gap: 12px; }
.docs-toast-act { border: 1px solid currentColor; background: none; color: inherit; border-radius: var(--r-full); padding: 3px 12px; font-weight: 700; font-size: var(--t-xs); cursor: pointer; }

/* ---- upload screen ---- */
.docs-upload-wrap { padding: 18px 20px 40px; max-width: 860px; margin: 0 auto; box-sizing: border-box; }
.docs-up { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-1); padding: 20px 22px; }
.docs-up h2 { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; font-size: var(--t-lg); color: var(--ink); }
.docs-drop { display: flex; flex-direction: column; align-items: center; gap: 8px; border: 2px dashed var(--line-strong); border-radius: var(--r-lg); background: var(--surface-2); padding: 28px 18px; margin: 14px 0; text-align: center; color: var(--ink-soft); transition: border-color var(--dur) var(--ease), background var(--dur) var(--ease); }
.docs-drop.is-over { border-color: var(--primary); background: var(--primary-softer); }
.docs-drop svg { width: 30px; height: 30px; color: var(--muted); }
.docs-drop p { margin: 0; }
.docs-up-file { display: flex; align-items: flex-start; gap: 14px; margin: 14px 0; padding: 12px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface-2); }
.docs-up-thumb { width: 84px; border: 1px solid var(--line); border-radius: var(--r-sm); background: var(--surface); }
.docs-up-thumb-ph { height: 108px; }
.docs-up-facts { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.docs-up-facts b { overflow-wrap: anywhere; color: var(--ink); }
.docs-up-facts .linkbtn { align-self: flex-start; }
.docs-dept-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 8px; margin: 4px 0 6px; }
.docs-dept-grid-sm { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); max-height: 40vh; overflow-y: auto; }
.docs-dtile { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); border-radius: var(--r-md); padding: 11px 12px; font-size: var(--t-sm); font-weight: 600; cursor: pointer; text-align: left; transition: all var(--dur) var(--ease); }
.docs-dtile:hover:not([disabled]) { border-color: var(--primary); }
.docs-dtile.is-on { background: var(--primary-soft); border-color: var(--primary); color: var(--primary-ink); box-shadow: inset 0 0 0 1px var(--primary); }
.docs-dtile[disabled] { opacity: .5; cursor: not-allowed; }
.docs-dtile i { flex-basis: 100%; font-style: normal; font-weight: 400; font-size: 10px; color: var(--muted); }
.docs-def-hint { margin: 2px 0 8px; font-size: var(--t-xs); }
.docs-up-note { width: 100%; box-sizing: border-box; margin: 8px 0 2px; }
.docs-up .docs-warn { margin: 10px 0 0; }
.docs-progress { height: 8px; border-radius: var(--r-full); background: var(--surface-3); overflow: hidden; margin: 14px 0 6px; }
.docs-progress-fill { height: 100%; background: var(--primary); border-radius: var(--r-full); transition: width .2s var(--ease); }
.docs-progress-lbl { margin: 0; font-variant-numeric: tabular-nums; }
.docs-up-done { display: flex; gap: 14px; align-items: flex-start; margin-top: 10px; }
.docs-up-done > svg { width: 30px; height: 30px; color: var(--good); flex: 0 0 auto; }
.docs-up-done b { color: var(--ink); }
.docs-up-done p { margin: 4px 0 10px; }
.docs-mine { margin-top: 24px; }
.docs-mine h3 { margin: 0 0 10px; font-size: var(--t-md); color: var(--ink); }
.docs-mine-list { display: flex; flex-direction: column; gap: 8px; }
.docs-mine-row { display: flex; align-items: center; gap: 12px; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 10px 14px; }
.docs-mine-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.docs-mine-main b { overflow-wrap: anywhere; color: var(--ink); font-size: var(--t-sm); }
/* ---- multi-file upload (2026-09-04): one PDF, several, or a dropped/picked folder. Rows are
   replaced in place as each file is checked and sent; the pill vocabulary is pk-status. ---- */
.docs-drop-btns { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.docs-drop.is-compact { padding: 12px 16px; gap: 6px; margin-bottom: 0; }
.docs-drop.is-compact svg { width: 22px; height: 22px; }
.docs-up-list { margin: 10px 0 4px; border: 1px solid var(--line); border-radius: var(--r-md); background: var(--surface); overflow: hidden; }
.docs-up-listhead { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 12px; font-size: var(--t-xs); font-weight: 600; color: var(--ink-soft); background: var(--surface-2); border-bottom: 1px solid var(--line); font-variant-numeric: tabular-nums; }
.docs-up-row { display: flex; align-items: center; gap: 12px; padding: 8px 12px; border-bottom: 1px solid var(--line); background: var(--surface); transition: background var(--dur) var(--ease); }
.docs-up-row:last-child { border-bottom: 0; }
.docs-up-row .docs-up-thumb { width: 40px; height: 52px; object-fit: cover; object-position: top; flex: 0 0 auto; }
.docs-up-row .docs-up-thumb-ph { height: 52px; }
.docs-up-row .docs-up-facts { flex: 1 1 auto; gap: 1px; }
.docs-up-row .docs-up-facts b { font-size: var(--t-sm); }
.docs-up-row .docs-up-facts .docs-muted { font-size: var(--t-xs); }
.docs-up-msg { font-size: var(--t-xs); color: var(--danger); overflow-wrap: anywhere; }
.docs-up-row.is-refused, .docs-up-row.is-failed { background: var(--danger-soft); }
.docs-up-row.is-done { opacity: .72; }
.docs-up-row .pk-status { flex: 0 0 auto; }
.docs-up-x { flex: 0 0 auto; border: 0; background: none; color: var(--muted); border-radius: var(--r-full); width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: background var(--dur) var(--ease), color var(--dur) var(--ease); }
.docs-up-x:hover { background: var(--surface-3); color: var(--ink); }
.docs-up-x svg { width: 16px; height: 16px; }
.docs-up-skipped { margin: 6px 0 0; font-size: var(--t-xs); }
.docs-up-done .docs-warn { margin-top: 0; }
.docs-up-done .docs-warn ul { margin: 6px 0 0; padding-left: 18px; }
.docs-up-done .docs-warn li { margin: 2px 0; overflow-wrap: anywhere; }
/* A batch status can be a whole sentence ("Automatic splitting failed — the stack is available whole.");
   the pill vocabulary is nowrap, which on a phone made that one pill wider than the screen. */
.docs-mine-row .pk-status { white-space: normal; max-width: 100%; box-sizing: border-box; text-align: center; }
@media (max-width: 760px) {
  .docs-up-row { gap: 8px; padding: 8px 10px; }
  .docs-up-row .docs-up-thumb { width: 32px; height: 42px; }
  .docs-up-row .docs-up-thumb-ph { height: 42px; }
  /* My uploads rows: name on its own line, status + Discard beneath — a 390px phone cannot fit all three across. */
  .docs-mine-row { flex-wrap: wrap; row-gap: 6px; }
  .docs-mine-main { flex: 1 1 100%; }
}
/* Integrator additions (2026-09-01). count-busy: refreshDocsBadge() sets it on a department's
   rail badge while a stack is still being read — a colour shift, not a spinner, because the
   badge must never invent a number and the rail bans infinite animation. docs-page-lbl: the
   "Stack page N · x of y" line in the inline viewer's pager. */
.tab .count.count-busy { background: var(--primary-soft); color: var(--primary-ink); }
.docs-page-lbl { color: var(--muted); font-size: var(--t-xs); font-variant-numeric: tabular-nums; }
/* Fixer additions (2026-09-01). An author display rule beats the UA [hidden]{display:none} —
   the .ap-fs-btn / .rail-min-btn trap — so the two flex containers the JS toggles via .hidden
   need the reset spelled out, or the split bar and the first-time-fax checkbox never hide.
   docs-fax-confirm / docs-email-confirm are the step-2 retype fields whose value travels as
   confirm_to (the server's real wrong-recipient check). */
.docs-splitbar[hidden] { display: none; }
.docs-firsttime[hidden] { display: none; }
.docs-fax-confirm, .docs-email-confirm { width: 100%; box-sizing: border-box; margin: 2px 0 8px; }

/* AP invoice upload (2026-09-04): "Upload invoices" + a folder button side by side above the
   "To enter" queue. The folder button is hidden on iOS, which has no folder picker. */
.ap-uprow { display: flex; gap: 6px; margin: 6px 8px; }
.ap-uprow .btn { flex: 1 1 auto; justify-content: center; min-width: 0; }
.ap-uprow .btn + .btn { flex: 0 0 auto; }

/* ---- Bill conversation: the private staff thread on a bill (2026-09-05) --------------------
   Owner: "send a message to the person who created the bill or the AP department … they can respond
   back to me … it would stay in the bill record … the vendor would never see it." One quiet card on
   the sign screen and the bill view, above the Sign / Reject row so a question is asked from the same
   place the decision is made. Bubbles like staff chat; yours on the right in the accent colour. */
.apt { border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--surface); padding: var(--s3) var(--s4); margin-top: var(--s3); display: grid; gap: var(--s3); }
.apt-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s2); flex-wrap: wrap; }
.apt-title { display: inline-flex; align-items: center; gap: 6px; font-weight: 700; font-size: var(--t-sm); }
.apt-hcount { font-size: var(--t-xs); font-weight: 700; padding: 1px 8px; border-radius: var(--r-full); background: var(--surface-3); color: var(--ink-soft); }
.apt-hcount.apt-new, .apt-chip.apt-new, .tab .count.apt-badge { background: var(--primary); color: var(--on-primary); }
.apt-priv { display: inline-flex; align-items: center; gap: 4px; font-size: var(--t-xs); color: var(--muted); }
.apt-list { display: grid; gap: var(--s2); max-height: 380px; overflow-y: auto; overflow-x: hidden; padding: 2px 0; }
.apt-empty { display: grid; gap: var(--s2); }
.apt-empty-text { font-size: var(--t-sm); color: var(--ink-soft); line-height: 1.5; }
.apt-quick { display: flex; flex-wrap: wrap; gap: 6px; }
.apt-q { font: inherit; font-size: var(--t-xs); text-align: left; padding: 6px 10px; border-radius: var(--r-full); border: 1px solid var(--line-strong); background: var(--surface-2); color: var(--ink); cursor: pointer; transition: background .15s ease, border-color .15s ease; }
.apt-q:hover { background: var(--primary-soft); border-color: var(--primary); }
.apt-msg { max-width: 86%; min-width: 0; padding: var(--s2) var(--s3); border-radius: var(--r-lg); background: var(--surface-2); border: 1px solid var(--line); border-bottom-left-radius: var(--r-sm); justify-self: start; }
.apt-msg.mine { background: var(--primary); color: var(--on-primary); border-color: transparent; border-bottom-left-radius: var(--r-lg); border-bottom-right-radius: var(--r-sm); justify-self: end; }
.apt-msg.unread:not(.mine) { border-color: var(--primary); box-shadow: 0 0 0 2px var(--primary-soft); }
.apt-meta { display: flex; align-items: center; gap: var(--s2); font-size: var(--t-xs); color: var(--muted); margin-bottom: 2px; }
.apt-msg.mine .apt-meta { color: color-mix(in srgb, var(--on-primary) 80%, transparent); }
.apt-who { font-weight: 700; }
.apt-del { margin-left: auto; background: none; border: 0; padding: 2px; color: inherit; opacity: .55; cursor: pointer; border-radius: var(--r-sm); display: inline-flex; }
.apt-del:hover { opacity: 1; background: color-mix(in srgb, currentColor 12%, transparent); }
.apt-body { white-space: pre-wrap; overflow-wrap: anywhere; font-size: var(--t-sm); line-height: 1.45; }
.apt-compose { display: flex; gap: var(--s2); align-items: flex-end; }
.apt-input { flex: 1 1 auto; min-height: 42px; max-height: 160px; resize: none; margin-bottom: 0; line-height: 1.4; }
.apt-send { flex: none; width: auto; min-height: 42px; }
.apt-to { display: block; line-height: 1.6; }
.apt-to .icon { vertical-align: -3px; margin-right: 2px; }
.apt-toname { white-space: nowrap; }
.apt-why { color: var(--muted); font-weight: 400; }
.apt-chip { margin-left: 4px; }
.apt-strip { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; padding: var(--s2) var(--s3); margin-bottom: var(--s2); border-radius: var(--r-md); background: var(--primary-soft); color: var(--primary-ink); font-size: var(--t-sm); font-weight: 600; }
.apt-strip > span { display: inline-flex; align-items: center; gap: 6px; }
.apt-toast { pointer-events: auto; max-width: min(92vw, 560px); gap: var(--s3); border-radius: var(--r-lg); }
.apt-ttext { display: grid; gap: 1px; font-weight: 500; min-width: 0; }
.apt-tprev { font-size: var(--t-xs); color: var(--muted); font-weight: 400; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.apt-topen { width: auto; flex: none; }
