/* ── Design tokens ───────────────────────────────────────────────────────────── */
:root {
  --paper:         #f4f1ec;
  --paper-2:       #efeae3;
  --surface:       #fffefb;
  --surface-2:     #faf7f2;
  --surface-sunk:  #f1ece4;
  --border:        #e4ddd2;
  --border-2:      #d6cdbf;
  --border-strong: #c7bcab;

  --ink:    #2a2723;
  --ink-2:  #524d45;
  --muted:  #8b847a;
  --faint:  #aaa498;

  --accent:       oklch(0.55 0.072 175);
  --accent-strong:oklch(0.48 0.078 175);
  --accent-soft:  oklch(0.95 0.022 175);
  --accent-soft-2:oklch(0.90 0.030 175);
  --accent-ink:   oklch(0.40 0.060 175);
  --on-accent:    #fbfdfc;

  /* ── OPT-839: the mobile floating-widget ladder, as numbers instead of prose ──
     Below 640px div.nav becomes a fixed bottom tab bar and a stack of pills
     floats above it (device chip, account chip, then the conditional licence
     panel and invoice toast). Those offsets were hardcoded as 74 / 130 / 186px
     in four places with a comment explaining the arithmetic; they are expressed
     here instead, because the page's own bottom padding has to be derived from
     the SAME numbers and a fifth hardcoded copy is how they drift apart.

         slot n bottom = tabbar + gap + n * (pill + gap)
         slot 0 = 74px    slot 1 = 130px    slot 2 = 186px

     — identical to what was already there, which is the point. */
  --tabbar-h:  62px;   /* the fixed bottom nav's height at ≤640px */
  --fw-pill-h: 44px;   /* a floating pill: the 44px touch target (OPT-814) */
  --fw-gap:    12px;
  --fw-slot-0: calc(var(--tabbar-h) + var(--fw-gap));                          /*  74px */
  --fw-slot-1: calc(var(--fw-slot-0) + var(--fw-pill-h) + var(--fw-gap));      /* 130px */
  --fw-slot-2: calc(var(--fw-slot-1) + var(--fw-pill-h) + var(--fw-gap));      /* 186px */
  /* What the page must keep clear so content is never trapped under a pill:
     the tab bar plus the ALWAYS-present slot-0 row. */
  --fw-clearance: calc(var(--fw-slot-0) + var(--fw-pill-h) + var(--fw-gap));   /* 130px */

  --sand:      #cdbba6;
  --sand-soft: #efe7da;

  --ok:         oklch(0.58 0.072 155);
  --ok-soft:    oklch(0.95 0.024 155);
  --ok-strong:  oklch(0.44 0.078 155);
  --info:       oklch(0.56 0.080 245);
  --info-soft:  oklch(0.95 0.026 245);
  --warn:       oklch(0.66 0.090 75);
  --warn-soft:  oklch(0.95 0.040 80);
  --danger:     oklch(0.56 0.110 28);
  --danger-soft:oklch(0.95 0.030 28);

  /* GOS3 voucher yellow (real NHS GOS3 form colour; distinct from the more
     orange --warn amber so the badge/section fills read as true yellow) */
  --gos-yellow:      oklch(0.82 0.150 97);
  --gos-yellow-ink:  oklch(0.32 0.060 75);

  --serif: 'Newsreader', Georgia, serif;
  --sans:  'IBM Plex Sans', system-ui, sans-serif;
  --mono:  'IBM Plex Mono', ui-monospace, monospace;

  --r-sm: 6px;
  --r:    9px;
  --r-lg: 14px;
  --r-xl: 18px;
  --shadow-sm: 0 1px 2px rgba(58,49,36,.05), 0 1px 1px rgba(58,49,36,.04);
  --shadow:    0 1px 3px rgba(58,49,36,.06), 0 6px 18px -8px rgba(58,49,36,.14);
  --shadow-lg: 0 12px 40px -12px rgba(58,49,36,.28);
  --ring:      0 0 0 3px var(--accent-soft-2);

  --field-max: 480px;
}

[data-theme="dark"] {
  --paper:         #16140f;
  --paper-2:       #100e0a;
  --surface:       #201d17;
  --surface-2:     #1a1813;
  --surface-sunk:  #14120d;
  --border:        #322d24;
  --border-2:      #3d3729;
  --border-strong: #4a4234;

  --ink:   #efe9df;
  --ink-2: #c9c1b4;
  --muted: #938b7d;
  --faint: #6f685b;

  --accent:       oklch(0.70 0.075 175);
  --accent-strong:oklch(0.78 0.080 175);
  --accent-soft:  oklch(0.30 0.045 175);
  --accent-soft-2:oklch(0.36 0.050 175);
  --accent-ink:   oklch(0.82 0.070 175);
  --on-accent:    #0f1513;

  --sand:      #8a7c66;
  --sand-soft: #2c271d;

  --ok-soft:    oklch(0.30 0.040 155);
  --info-soft:  oklch(0.30 0.045 245);
  --warn-soft:  oklch(0.32 0.055 80);
  --danger-soft:oklch(0.32 0.055 28);
  --shadow-sm: 0 1px 2px rgba(0,0,0,.4);
  --shadow:    0 1px 3px rgba(0,0,0,.4), 0 8px 22px -10px rgba(0,0,0,.6);
  --shadow-lg: 0 16px 48px -12px rgba(0,0,0,.7);
}

/* ── Reset ───────────────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
  height: 100%;
  background: var(--paper);
  color: var(--ink);
  font-size: 13px;
  line-height: 1.45;
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
::selection { background: var(--accent-soft-2); }

/* ── Global utilities ────────────────────────────────────────────────────────── */
.field-max { max-width: var(--field-max); }
.page-pad  { padding: 20px 24px; }

/* ── App shell ───────────────────────────────────────────────────────────────── */
div.page {
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}

/* ── Topbar / Nav ────────────────────────────────────────────────────────────── */
div.nav {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 0 18px;
  height: 50px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  font-weight: 600;
  letter-spacing: .2px;
  font-size: 14px;
  color: var(--ink);
}

.mark {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 2.5px solid var(--accent);
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
}
.mark::after {
  content: '';
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
}

div.nav-links { display: flex; gap: 2px; margin-left: 6px; }

button.nav {
  font-size: 13px;
  color: var(--muted);
  padding: 6px 11px;
  border-radius: var(--r-sm);
  font-weight: 500;
  cursor: pointer;
  border: none;
  background: transparent;
  font-family: var(--sans);
  transition: background .12s, color .12s;
}
button.nav:hover { background: var(--surface-sunk); color: var(--ink-2); }
button.nav.nav-active { background: var(--accent-soft); color: var(--accent-ink); }

div.nav-user { display: flex; align-items: center; gap: 8px; }

span.nav-name { color: var(--muted); font-size: 13px; }

.chip-user {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--ink-2);
  padding: 5px 8px 5px 10px;
  border-radius: 40px;
  border: 1px solid var(--border);
}
.chip-user .av {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 600;
}

/* ── OPT-314: device identity chip (nav-bar) ─────────────────────────────────── */
.device-chip { display: flex; align-items: center; position: relative; }

.device-chip-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: 40px;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 500;
  color: var(--muted);
  font-family: var(--sans);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.device-chip-btn:hover { background: var(--surface-sunk); border-color: var(--border-2); color: var(--ink-2); }

.device-chip-name { max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.device-chip-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 10px;
  font-weight: 700;
  line-height: 1;
}

.device-chip-input {
  border: 1px solid var(--accent);
  border-radius: 40px;
  padding: 4px 10px;
  font-size: 12px;
  font-family: var(--sans);
  color: var(--ink);
  background: var(--surface);
  width: 100%;
  box-sizing: border-box;
}
.device-chip-input:focus { outline: none; box-shadow: 0 0 0 2px var(--accent-soft); }

/* ── OPT-315: device-chip popover (rename + send + pending-request actions) ──── */
.device-chip-scrim {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: transparent;
}

.device-chip-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 310;
  width: 260px;
  max-height: 70vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md, 10px);
  box-shadow: 0 12px 32px rgba(0,0,0,.18);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-align: left;
}

.dcp-section + .dcp-section { border-top: 1px solid var(--border); padding-top: 12px; }

.dcp-heading {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  margin-bottom: 6px;
}

.dcp-empty { font-size: 12px; color: var(--muted); margin: 0; }

.dcp-device-list, .dcp-inbox-list { display: flex; flex-direction: column; gap: 6px; }

.dcp-device-btn {
  display: block;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 6px 10px;
  font-size: 12px;
  font-family: var(--sans);
  color: var(--ink-2);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.dcp-device-btn:hover { background: var(--surface-sunk); border-color: var(--border-2); }

.dcp-inbox-row {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 8px;
  background: var(--surface-sunk);
  border-radius: var(--r-sm);
}
.dcp-inbox-text { font-size: 12px; color: var(--ink-2); }
.dcp-inbox-row .btn.sm { align-self: flex-start; }

/* -- OPT-787: global-search results overlay (nav-level box, see the OPT-666
   section in optos.views) -- same scrim/popover elevation language as the
   .device-chip-popover block just above: fixed transparent click-away scrim
   behind an absolutely-positioned, anchored panel with a border + soft
   shadow + house radius. */
.global-search { position: relative; }

.global-search-scrim {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: transparent;
}

.global-search-dropdown {
  position: absolute;
  top: calc(100% + 6px);
  /* right-aligned by default, not left -- the OPT-666 nav (see nav-bar in
     optos.views) has a long run of page links ahead of the search box, so the
     box itself normally sits well over toward the right edge of the nav;
     anchoring the panel from its RIGHT edge keeps it growing back over
     that already-occupied space instead of off the browser edge, which a
     left:0 default would do (measured live: at a realistic 1440px viewport
     a left-anchored 34rem panel ran past the viewport edge and got silently
     clipped by the div.page overflow:hidden ancestor -- invisible content,
     not just an ugly overflow). */
  right: 0;
  z-index: 310;
  min-width: 34rem;
  max-width: min(34rem, calc(100vw - 36px));
  max-height: 60vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md, 10px);
  box-shadow: var(--shadow-lg);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.gs-group-label {
  padding: 8px 10px 4px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}
.gs-group-label:not(:first-child) {
  margin-top: 4px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}

.gs-result-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: none;
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-family: var(--sans);
  cursor: pointer;
  transition: background .1s;
}
.gs-result-row:hover,
.gs-result-row.gs-highlight { background: var(--accent-soft); }

.gs-result-icon {
  flex: none;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--sand-soft);
  color: var(--accent-ink);
  display: grid;
  place-items: center;
  font-family: var(--serif);
  font-size: 12px;
  font-weight: 600;
  user-select: none;
}
.gs-result-row:hover .gs-result-icon,
.gs-result-row.gs-highlight .gs-result-icon { background: var(--accent-soft-2); }

.gs-result-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}
.gs-result-primary {
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.gs-result-secondary {
  font-size: 11.5px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.gs-empty {
  padding: 16px 10px;
  font-size: 12.5px;
  color: var(--muted);
  text-align: center;
}

/* ── OPT-400: nav attention badge (today's outstanding payments + unordered
   frames/lenses/CLs) — structure mirrors the .device-chip / .dcp rules above,
   but the badge itself is red (like the ordering page .ord-count.has-orders)
   since this is always something that needs chasing today, never a neutral
   count. NOTE: never put a bare asterisk-slash or an apostrophe in comment
   text here — the first ends the comment early and the second opens a CSS
   string in the orphaned text; together they once swallowed every rule to
   end-of-file (the OPT-400 release failure). ── */
.notif-bell { display: flex; align-items: center; position: relative; }

.notif-bell-btn {
  display: flex;
  align-items: center;
  background: transparent;
  border: none;
  padding: 4px;
  cursor: pointer;
  line-height: 1;
}

.notif-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--danger, #d6453d);
  color: #fff;
  font-size: 12px;
  font-weight: 700;
  line-height: 1;
  box-shadow: 0 0 0 3px rgba(214, 69, 61, .18);
}

.notif-scrim {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: transparent;
}

/* ── OPT-812: mobile account chip ──────────────────────────────────────────
   Hidden outright above 640px: the desktop nav keeps using .nav-user, and this
   exists ONLY to restore the sign-out that the bottom-tab-bar rule takes away
   (see the ≤640px block, and `account-chip` in views.cljs for the why). The
   mobile block below re-declares display, so this must stay a plain,
   non-!important rule for that override to win. */
.account-chip { display: none; position: relative; }

.notif-popover {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 310;
  width: 300px;
  max-height: 70vh;
  overflow-y: auto;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-md, 10px);
  box-shadow: 0 12px 32px rgba(0,0,0,.18);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-align: left;
}

.notif-section + .notif-section { border-top: 1px solid var(--border); padding-top: 12px; }

.notif-heading {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  margin-bottom: 6px;
}

.notif-list { display: flex; flex-direction: column; gap: 4px; }

.notif-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  padding: 6px 8px;
  font-size: 12px;
  font-family: var(--sans);
  color: var(--ink-2);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.notif-row:hover { background: var(--surface-sunk); border-color: var(--border-2); }

.notif-row-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.notif-row-detail { color: var(--muted); flex-shrink: 0; white-space: nowrap; }

/* ── OPT-315: outgoing send status toast-lines (under the chip) ─────────────── */
.transfer-toast-list {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 200;
  display: flex;
  flex-direction: column;
  gap: 6px;
  width: max-content;
  max-width: 280px;
}

.transfer-toast {
  display: flex;
  align-items: center;
  gap: 8px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 6px 10px;
  font-size: 12px;
  color: var(--ink-2);
  box-shadow: 0 6px 18px rgba(0,0,0,.14);
}
.transfer-toast-text { flex: 1; }
.transfer-toast-dismiss {
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
}
.transfer-toast-dismiss:hover { color: var(--ink); }

/* ── OPT-315: incoming-transfer banner — fixed top-right, non-blocking (NOT a
   modal: no scrim, doesn't block the rest of the app) ────────────────────────── */
.transfer-inbox-banner {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 320;
  display: flex;
  flex-direction: column;
  gap: 10px;
  width: 320px;
  max-width: calc(100vw - 32px);
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r-md, 10px);
  box-shadow: 0 16px 40px rgba(0,0,0,.22);
  padding: 14px 16px;
}
.transfer-inbox-banner-text { font-size: 13px; color: var(--ink-2); line-height: 1.4; }
.transfer-inbox-banner-actions { display: flex; gap: 8px; }

button.theme-toggle, button.nav-signout {
  background: transparent;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 4px 10px;
  font-size: 12px;
  cursor: pointer;
  color: var(--muted);
  font-family: var(--sans);
  transition: background .12s;
}
button.theme-toggle:hover, button.nav-signout:hover {
  background: var(--surface-sunk);
  border-color: var(--border-2);
}

/* ── Error panel ─────────────────────────────────────────────────────────────── */
div.error {
  background: var(--danger-soft);
  border-left: 3px solid var(--danger);
  padding: 5px 12px;
  font-size: 12px;
  color: var(--danger);
  flex-shrink: 0;
}
.error-banner {
  display: flex; align-items: flex-start; gap: 8px;
  background: var(--danger-soft);
  border-left: 3px solid var(--danger);
  padding: 10px 14px;
  flex-shrink: 0;
  animation: errslide .15s ease;
}
@keyframes errslide { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; } }
.error-icon { font-size: 14px; line-height: 1.4; flex-shrink: 0; }
.error-msg {
  flex: 1; font-size: 13px; font-weight: 500; color: var(--danger);
  line-height: 1.45; word-break: break-word; white-space: pre-wrap;
}
.error-dismiss {
  flex-shrink: 0; background: none; border: none; cursor: pointer;
  color: var(--danger); font-size: 18px; line-height: 1; padding: 0 2px;
  opacity: .7;
}
.error-dismiss:hover { opacity: 1; }

/* ── Patient page layout ─────────────────────────────────────────────────────── */

/* ── Exam page layout ────────────────────────────────────────────────────────── */

/* ── Inner panel boxes ───────────────────────────────────────────────────────── */
div.pxinfo {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 8px 10px;
  overflow-y: auto;
  grid-row: 1 / -1;
}

/* ── Diary ───────────────────────────────────────────────────────────────────── */
div.diaries {
  display: grid;
  background: transparent;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  overflow-y: auto;
  border-radius: var(--r-lg);
}
div.diaries > div {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 4px 6px;
  overflow-y: auto;
}
div.diary {
  display: grid;
  grid-template-columns: 22% 54% 12% 12%;
  align-items: center;
}

button.move {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 2px 5px;
  font-size: 12px;
  color: var(--muted);
}
button.move:hover { background: var(--accent-soft); color: var(--accent-ink); }

button.move-here {
  background: var(--accent-soft);
  border: none;
  border-radius: 0;
  font-size: 11px;
  padding: 2px 5px;
  color: var(--accent-ink);
  white-space: nowrap;
  flex-shrink: 0;
}
button.move-here:hover { background: var(--accent-soft-2); }

div.diary-slot {
  display: flex;
  align-items: center;
}
div.diary-slot button.book {
  flex: 1;
  width: auto;
}

/* ── DB / stock page ─────────────────────────────────────────────────────────── */

/* ── Forms ───────────────────────────────────────────────────────────────────── */
div.pxform, div.rxform, div.dispform { background: transparent; }
form { margin: 0; }
div.formrow { display: flex; align-items: center; padding: 2px 0; }
div.buttons { display: grid; grid-template-columns: auto auto auto; column-gap: 4px; }

/* ── Global inputs ───────────────────────────────────────────────────────────── */
input, select, textarea {
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 4px 7px;
  font-size: 13px;
  outline: none;
  margin-left: 4px;
  color: var(--ink);
  font-family: var(--sans);
  transition: border-color .12s, box-shadow .12s;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent);
  box-shadow: var(--ring);
}
textarea {
  resize: vertical;
  min-height: 56px;
  width: calc(100% - 4px);
  border-radius: var(--r-sm);
  line-height: 1.4;
}
input.rx {
  text-align: center;
  width: 54px;
  margin: 1px;
}

/* ── Rx grid (exam page) ─────────────────────────────────────────────────────── */
div.rxgrid { margin: 2px 0; }
div.rx-row { display: flex; flex-direction: row; align-items: center; padding: 1px 0; }
span.rx-label {
  display: inline-block;
  width: 28px;
  font-weight: 600;
  font-size: 12px;
  text-align: right;
  margin-right: 4px;
  color: var(--ink);
}
span.rx-head {
  width: 58px;
  text-align: center;
  font-size: 11px;
  color: var(--muted);
  margin: 0 1px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .3px;
}

/* ── Labels ──────────────────────────────────────────────────────────────────── */
label {
  font-size: 13px;
  color: var(--ink-2);
  margin: 1px 4px 1px 0;
}
label.uuid {
  font-size: 10px;
  color: var(--muted);
  padding: 1px 2px;
  background: var(--surface-sunk);
  word-break: break-all;
  display: block;
}
label.view { font-size: 13px; padding: 1px 3px; }
label.highlight {
  font-size: 13px;
  padding: 2px 6px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  border-radius: var(--r-sm);
  font-weight: 600;
}
label.help {
  font-size: 13px;
  color: var(--muted);
  display: block;
  text-align: center;
  padding: 4px 0;
}

/* ── Global buttons ──────────────────────────────────────────────────────────── */
button {
  padding: 5px 11px;
  background: var(--surface);
  border-radius: var(--r-sm);
  border: 1px solid var(--border-2);
  font-size: 13px;
  cursor: pointer;
  color: var(--ink-2);
  font-family: var(--sans);
  transition: background .12s, border-color .12s;
}
button:hover { background: var(--surface-sunk); border-color: var(--border-strong); }

button.book {
  background: var(--surface-sunk);
  border: none;
  border-bottom: 2px solid var(--border-2);
  border-radius: 0;
  font-size: 12px;
  padding: 2px 4px;
  text-align: left;
  width: 100%;
  color: var(--ink-2);
}
button.book:hover { background: var(--surface-2); }

button.appt {
  background: var(--accent-soft);
  border: none;
  border-radius: 0;
  font-size: 12px;
  padding: 2px 4px;
  text-align: left;
  width: 100%;
  color: var(--accent-ink);
}
button.appt:hover { background: var(--accent-soft-2); }

button.del {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 2px 5px;
  font-size: 12px;
  color: var(--danger);
}
button.del:hover { background: var(--danger-soft); }

/* ── Headings ────────────────────────────────────────────────────────────────── */
h2.banner {
  font-family: var(--serif);
  font-size: 14px;
  font-weight: 500;
  text-align: center;
  color: var(--ink-2);
  padding: 6px;
  background: var(--surface-sunk);
  border-radius: var(--r-sm) var(--r-sm) 0 0;
}
h2.bar {
  font-family: var(--serif);
  font-size: 14px;
  font-weight: 500;
  padding: 2px 4px;
  color: var(--ink);
  margin-bottom: 4px;
}
h3 {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 4px;
}

/* ── Login page ──────────────────────────────────────────────────────────────── */
div.login-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100vh;
  gap: 16px;
  background: var(--paper);
}
div.login-page h2.banner {
  background: none;
  font-size: 28px;
  font-weight: 500;
  font-family: var(--serif);
  color: var(--ink);
  letter-spacing: .1px;
  text-align: left;
}
div.login-form {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-xl);
  padding: 24px 28px;
  min-width: 280px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  box-shadow: var(--shadow);
}
div.login-form label {
  font-size: 11px;
  color: var(--muted);
  margin: 0;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .3px;
}
div.login-form input { width: 100%; margin: 0 0 4px; }
div.login-form button {
  margin-top: 6px;
  width: 100%;
  padding: 9px;
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 600;
}
div.login-form button:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}
p.error { color: var(--danger); font-size: 12px; margin: 2px 0; }

/* ── Search panel ────────────────────────────────────────────────────────────── */
div.search-panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 10px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
div.search-panel form { display: flex; flex-direction: column; gap: 2px; }
div.search-panel label {
  font-size: 11px;
  color: var(--muted);
  margin: 0;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .3px;
}
div.search-panel input { width: 100%; margin: 0 0 4px; }
div.search-panel button { width: 100%; margin-top: 4px; }

/* ── Admin page ──────────────────────────────────────────────────────────────── */
div.admin { padding: 24px; max-width: 900px; margin: 0 auto; overflow-y: auto; flex: 1; min-height: 0; min-width: 0; width: 100%; }
.admin-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px; }

/* ── Admin page tabs (OPT-338) — same pattern as .pd-tabs / .pd-tab ─────────── */
/* OPT-617: wraps onto extra rows so all tabs stay visible without scrolling
   at tablet/desktop widths; the mobile block below (max-width 640px) pins
   this back to a single scrollable row, where a scroll affordance still
   beats a tall stack of tab rows eating the viewport. */
.admin-tabs {
  display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 22px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
  scrollbar-width: none;
}
.admin-tabs::-webkit-scrollbar { display: none; }
.admin-tab {
  border: none; background: transparent; cursor: pointer;
  font-family: var(--sans); font-size: 13px; font-weight: 600; color: var(--muted);
  padding: 10px 16px; margin-bottom: -1px; border-bottom: 2px solid transparent;
  transition: color .12s, border-color .12s;
  flex: 0 0 auto; white-space: nowrap;
}
.admin-tab:hover { color: var(--ink); }
.admin-tab.active { color: var(--accent-ink); border-bottom-color: var(--accent); }
.admin-tab-body { display: flex; flex-direction: column; gap: 20px; }

div.admin-form {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 12px;
  margin-bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
/* ── NFC tag pool (admin section + stock-form chip) ─────────────────────────── */
.nfc-scan-row { display: inline-flex; align-items: center; gap: 10px; margin: 0 0 10px; }
.nfc-scan-live { font-size: 12px; color: var(--accent-ink); font-weight: 600; }
.nfc-result { font-size: 12px; margin: 8px 0 0; }
.nfc-result.ok  { color: var(--ok-strong); }
.nfc-result.err { color: var(--danger); }
span.nfc-result.err { margin: 0 0 0 8px; }
.nfc-tag-table-wrap { overflow-x: auto; }
table.nfc-tag-table { width: 100%; border-collapse: collapse; font-size: 13px; }
table.nfc-tag-table th {
  text-align: left; padding: 6px 8px;
  font-size: 11px; font-weight: 600; color: var(--muted);
  border-bottom: 1px solid var(--border-2);
}
table.nfc-tag-table td { padding: 6px 8px; border-bottom: 1px solid var(--border); vertical-align: middle; }
td.nfc-num { font-weight: 700; color: var(--accent-ink); white-space: nowrap; }
td.nfc-uid { font-family: monospace; font-size: 12px; word-break: break-all; }
.nfc-status { font-size: 11px; font-weight: 600; }
.nfc-status.active     { color: var(--ok-strong); }
.nfc-status.unassigned { color: var(--muted); }
.nfc-status.sold       { color: var(--warn); }
.nfc-status.retired    { color: var(--faint); }
.nfc-auth-row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 8px; }
.nfc-auth-row input { max-width: 160px; }

/* ── Counted stock take (frame checklist) ── */
.stockcheck { display: flex; flex-direction: column; gap: 12px; }
.sc-controls { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.sc-controls input { max-width: 260px; }
.sc-count { white-space: nowrap; font-weight: 600; }
.sc-count.ok    { color: var(--ok-strong); }
.sc-count.short { color: var(--danger); }
.sc-count.over  { color: var(--warn); }
.sc-reason { width: 100%; min-width: 140px; font-size: 12px; }
.nfc-chip {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border-2); border-radius: 999px;
  padding: 4px 10px; font-size: 12px; font-weight: 600;
  background: var(--surface); color: var(--accent-ink);
}
.nfc-chip-x { border: none; background: transparent; cursor: pointer; color: var(--muted); font-size: 14px; padding: 0 2px; line-height: 1; }
.nfc-chip-x:hover { color: var(--danger); }
.hint-inline { font-size: 12px; color: var(--muted); }

/* -- Booking-link client status card (OPT-425) -- */
.bl-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 14px 16px;
  max-width: 420px;
}
.bl-chip-row { display: flex; align-items: center; gap: 10px; margin: 8px 0; }
.bl-chip {
  display: inline-flex; align-items: center; gap: 6px;
  border: 1px solid var(--border-2); border-radius: 999px;
  padding: 4px 10px; font-size: 12px; font-weight: 600;
  background: var(--surface-2); color: var(--ink-2);
}
.bl-dot { width: 8px; height: 8px; border-radius: 999px; display: inline-block; background: var(--faint); }
.bl-chip-green { color: var(--ok-strong); border-color: var(--ok); background: var(--ok-soft); }
.bl-chip-green .bl-dot { background: var(--ok-strong); }
.bl-chip-amber { color: var(--warn); background: var(--warn-soft); }
.bl-chip-amber .bl-dot { background: var(--warn); }
.bl-chip-grey { color: var(--muted); background: var(--surface-2); }
.bl-chip-grey .bl-dot { background: var(--faint); }
.bl-age { font-size: 12px; color: var(--muted); }
.bl-error { font-size: 12px; color: var(--danger); margin: 6px 0 0; }

div.admin-form label {
  font-size: 11px;
  color: var(--muted);
  margin: 0;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .3px;
}
div.admin-form input, div.admin-form select { width: 100%; margin: 0 0 2px; }
div.staff-list {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 10px;
}
div.staff-row {
  display: flex;
  gap: 8px;
  padding: 4px 0;
  border-bottom: 1px solid var(--border);
  align-items: center;
}
div.staff-row label { flex: 1; margin: 0; }
div.prof-add { display: flex; gap: 4px; margin-bottom: 6px; align-items: center; }
div.prof-add input { flex: 1; margin: 0; }

/* ── Audit log ────────────────────────────────────────────────────────────── */
.audit-panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); overflow: hidden;
}
.audit-hd {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.audit-hd h3 { margin: 0; font-size: 13px; font-weight: 700; }
.audit-empty { color: var(--muted); font-size: 13px; padding: 16px; margin: 0; }
.audit-list { max-height: 480px; overflow-y: auto; }
.audit-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 16px; border-bottom: 1px solid var(--border); font-size: 12.5px;
}
.audit-row:last-child { border-bottom: none; }
.audit-pill {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  padding: 2px 7px; border-radius: 40px; flex-shrink: 0; min-width: 52px; text-align: center;
}
.audit-view   { background: var(--info-soft);    color: var(--info); }
.audit-search { background: var(--surface-sunk); color: var(--muted); }
.audit-create { background: var(--ok-soft);      color: var(--ok); }
.audit-update { background: var(--warn-soft);    color: var(--warn); }
.audit-delete { background: var(--danger-soft);  color: var(--danger); }
.audit-resource { font-weight: 600; color: var(--ink); min-width: 90px; }
.audit-who { color: var(--muted); font-weight: 600; flex: 1; }
.audit-px { font-family: var(--mono); font-size: 11px; color: var(--muted); }
.audit-time { font-family: var(--mono); font-size: 11px; color: var(--faint); margin-left: auto; white-space: nowrap; }

/* ── Audit search (OPT-403) ──────────────────────────────────────────────── */
.audit-search-row {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px; border-bottom: 1px solid var(--border);
}
.audit-search-input {
  flex: 1; margin: 0; font-size: 12.5px;
}
.audit-search-count {
  display: flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--muted); white-space: nowrap; flex-shrink: 0;
}
.audit-search-badge {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  padding: 2px 7px; border-radius: 40px;
  background: var(--info-soft); color: var(--info);
}
.audit-search-all {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  padding: 10px 16px; border-bottom: 1px solid var(--border);
  background: var(--surface-sunk);
}
.audit-search-all-label {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); margin-right: 2px;
}
.audit-search-all-to { font-size: 12px; color: var(--muted); }
.audit-search-select, .audit-search-date { margin: 0; font-size: 12.5px; }

/* ── Notification log table (OPT-130) ────────────────────────────────────── */
.notif-log-table {
  width: 100%; border-collapse: collapse; font-size: 12.5px;
}
.notif-log-table th {
  text-align: left; padding: 8px 12px;
  background: var(--surface-2); border-bottom: 1px solid var(--border);
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted);
}
.notif-log-table td {
  padding: 7px 12px; border-bottom: 1px solid var(--border);
  vertical-align: top; font-family: var(--mono); font-size: 11.5px;
}
.notif-log-table tr:last-child td { border-bottom: none; }
.notif-row-failed td { color: var(--danger); }

/* ── GOS forms section (dispense page) ────────────────────────────────────── */
.gos-forms-empty {
  font-size: 12.5px; color: var(--muted); padding: 10px 2px; font-style: italic;
}
.gos-forms-list { display: flex; flex-direction: column; gap: 6px; }
.gos-form-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 8px 12px; box-shadow: var(--shadow-sm);
}
.gos-form-badge {
  font-size: 10.5px; font-weight: 800; letter-spacing: .5px;
  background: #003087; color: #fff; padding: 2px 7px; border-radius: 4px; flex-shrink: 0;
}
/* per-form-type colour (GOS1 blue default above; GOS3 yellow, GOS4 green — OPT-348) */
.gos-form-badge.gos3 { background: var(--gos-yellow); color: var(--gos-yellow-ink); }
.gos-form-badge.gos4 { background: var(--ok-strong); color: #fff; }
.gos-form-meta {
  font-size: 12px; color: var(--ink-2); flex: 1; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ── NHS eligibility hints (dispense NHS Forms section) ────────────────────────
   GOS1/GOS3/GOS4 buttons are auto-detected from the px's NHS-eligibility data:
   when eligible they get a highlight (DISTINCT hues so GOS1 (blue), GOS3 (amber)
   and GOS4 (green) read as different at a glance); when NOT eligible they render
   greyed-out and disabled via nhs-elig-ineligible. GOS4 is automatic only for
   patients under 16 (repair/replacement age rule; OPT-341). */
.btn.sm.nhs-elig-gos1 {
  background: var(--info-soft);
  border-color: color-mix(in oklch, var(--info) 55%, var(--border));
  color: var(--info);
  box-shadow: 0 0 0 2px color-mix(in oklch, var(--info) 22%, transparent);
}
.btn.sm.nhs-elig-gos1:hover {
  background: color-mix(in oklch, var(--info-soft) 80%, var(--info) 12%);
}
.btn.sm.nhs-elig-gos3 {
  background: var(--warn-soft);
  border-color: color-mix(in oklch, var(--warn) 55%, var(--border));
  color: color-mix(in oklch, var(--warn) 78%, var(--ink));
  box-shadow: 0 0 0 2px color-mix(in oklch, var(--warn) 22%, transparent);
}
.btn.sm.nhs-elig-gos3:hover {
  background: color-mix(in oklch, var(--warn-soft) 80%, var(--warn) 12%);
}
.btn.sm.nhs-elig-gos4 {
  background: var(--ok-soft);
  border-color: color-mix(in oklch, var(--ok) 55%, var(--border));
  color: var(--ok);
  box-shadow: 0 0 0 2px color-mix(in oklch, var(--ok) 22%, transparent);
}
.btn.sm.nhs-elig-gos4:hover {
  background: color-mix(in oklch, var(--ok-soft) 80%, var(--ok) 12%);
}
.btn.sm.nhs-elig-ineligible,
.btn.sm.nhs-elig-ineligible:hover {
  background: var(--surface-sunk);
  border-color: var(--border);
  color: var(--muted);
  box-shadow: none;
  opacity: .55;
  cursor: not-allowed;
}
.nhs-elig-chip {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px;
  margin: 8px 0 2px; padding: 6px 10px;
  background: var(--accent-soft); border: 1px solid var(--accent-soft-2);
  border-radius: var(--r); font-size: 12px;
}
.nhs-elig-chip-label {
  font-size: 10px; font-weight: 800; letter-spacing: .5px;
  text-transform: uppercase; color: var(--accent-ink); flex-shrink: 0;
}
.nhs-elig-chip-reasons { color: var(--ink-2); min-width: 0; }

/* ── NHS eligibility banner (px page, above NHS Details — OPT-351) ────────────
   Same auto-detection as the dispense-page chip above, but rendered as a more
   prominent bar on the patient record so a clinician sees today's GOS1/GOS3/
   GOS4 hint before touching the exemption-reason buttons in nhs-info. Reuses
   .nhs-elig-chip for the headline line; warm-paper accent, not alarm-red — a
   HINT, never a gate (nothing here disables anything). */
.nhs-elig-banner {
  display: flex; flex-direction: column; gap: 6px;
  margin: 4px 0 10px; padding: 8px 10px;
  background: var(--accent-soft); border: 1px solid var(--accent-soft-2);
  border-radius: var(--r-lg);
}
.nhs-elig-banner .nhs-elig-chip {
  margin: 0; padding: 0; background: transparent; border: none; font-size: 13px;
}
.nhs-elig-banner-reasons { display: flex; flex-wrap: wrap; gap: 6px; }
.nhs-elig-banner-chip {
  font-size: 11px; padding: 2px 8px; border-radius: 10px;
  background: var(--surface); color: var(--accent-ink);
  border: 1px solid var(--accent-soft-2);
}

/* ── NHS exemption-reason editor (shared component, optos.nhs-reasons — OPT-343) ──
   One `reasons-editor` renders in two contexts: the px-record NHS Details form
   (.pxform, no card chrome of its own) and the exam-page .exam-nhs-card (a
   proper section card, styled below). The component itself carries only
   class names — style lives here so both call sites stay visually in sync
   (OPT-367; previously the auto-chips + suggestion ring were inline styles). */
.nhs-reasons-editor { display: flex; flex-direction: column; gap: 8px; }
.nhs-reasons-btns { display: flex; flex-wrap: wrap; gap: 6px; }

/* Age-derived read-only chips (Under 16 / 60+) — same pill shape/size as
   .nhs-elig-banner-chip above, but a quiet neutral tone (not accent): these
   are plain facts, not an eligibility hint. */
.nhs-auto-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.nhs-auto-chip {
  font-size: 11px; padding: 2px 8px; border-radius: 10px;
  background: var(--surface-sunk); color: var(--muted);
  border: 1px solid var(--border);
}

/* 16-18-in-education suggestion: a soft accent ring/glow (the same --ring
   token used for focus rings), never a colour swap — the choice stays
   entirely manual. */
.nhs-reasons-editor .btn.sm.suggested { box-shadow: var(--ring); }

.nhs-reasons-legacy { font-size: 11px; color: var(--muted); }

/* ── Exam-page NHS exemption card (.exam-nhs-card, exam-views — OPT-343/367) ──
   Wraps the shared reasons-editor above in the same section-card chrome as
   the exam page's other cards (.exam-history's header treatment: a bordered
   .section-head with an uppercase muted .sh-label). Sits below .ex-grid,
   sized to its content rather than scrolling. */
.exam-nhs-card { flex-shrink: 0; }
.exam-nhs-card .section-head {
  padding: 10px 14px 8px;
  margin-bottom: 0;
  border-bottom: 1px solid var(--border);
}
.exam-nhs-card .section-head .sh-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .7px;
  text-transform: uppercase;
  color: var(--muted);
}

/* ── GOS3 Rx table ─────────────────────────────────────────────────────────── */
.gos3-rx-wrap { overflow-x: auto; margin-bottom: 16px; }

/* ── OPT-816: the shared wide-table wrapper ───────────────────────────────────
   The app already had this pattern four times over — .gos3-rx-wrap above,
   .gos3-claim-wrap, .nfc-tag-table-wrap, .debtors-table-wrap — plus two inline
   overflow-x styles, and it was simply forgotten on the rest. That inconsistency
   IS the incoherence this program exists to remove, so this is deliberately ONE
   utility rather than a fifth bespoke wrapper class.

   Now applied to the eight call sites that had nothing: .recalls-table ×6 (the
   most-reused unfixed table in the app — Recalls ×2 and Collections ×4, each row
   carrying a patient block plus two or three action buttons) and table.rx ×2
   (the 6-column prescription grid, ~366-420px of min-content inside a .card
   whose padding eats 36px, and the same grid reused inside the outside-Rx modal
   where only the modal's own overflow:auto was containing it — an unlabelled
   scrollbar rather than a designed affordance).

   The existing bespoke wrappers are left alone: they work, and folding them in
   would touch files outside this ticket's scope. New tables should use this.

   -webkit-overflow-scrolling is for iOS momentum scrolling; harmless elsewhere. */
.table-scroll {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.gos3-rx-tbl {
  width: 100%; border-collapse: collapse;
  font-size: 12.5px; font-family: var(--sans);
}
.gos3-rx-tbl th {
  font-size: 10.5px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .4px;
  padding: 4px 8px; border-bottom: 2px solid var(--border);
  text-align: center; white-space: nowrap;
}
.gos3-rx-tbl td {
  padding: 4px 4px; border-bottom: 1px solid var(--border);
  text-align: center;
}
.gos3-rx-tbl th:first-child, .gos3-rx-tbl td:first-child { text-align: left; padding-left: 0; }
.gos3-rx-eye {
  font-family: var(--serif); font-size: 14px; font-weight: 600;
  color: var(--ink); width: 28px; padding: 0 8px 0 0 !important;
}
.gos3-rx-cell {
  width: 72px; border: 1px solid var(--border-2);
  border-radius: var(--r-sm); padding: 5px 6px;
  font-family: var(--mono); font-size: 13px; color: var(--ink);
  background: var(--surface-sunk); outline: none; text-align: center;
  transition: border-color .12s;
}
.gos3-rx-cell:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

/* ── GOS3 claim table ──────────────────────────────────────────────────────── */
.gos3-claim-wrap { overflow-x: auto; margin-bottom: 16px; }
.gos3-claim-tbl {
  width: 100%; border-collapse: collapse;
  font-size: 12.5px; font-family: var(--sans);
}
.gos3-claim-tbl th {
  font-size: 10.5px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .4px;
  padding: 4px 10px; border-bottom: 2px solid var(--border);
  text-align: center; white-space: nowrap;
}
.gos3-claim-tbl th:first-child { text-align: left; }
.gos3-claim-tbl td {
  padding: 6px 8px; border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
.gos3-claim-tbl td:first-child {
  font-size: 12px; color: var(--ink-2); max-width: 320px;
}
.gos3-claim-tbl td:not(:first-child) { text-align: center; white-space: nowrap; }
.gos3-claim-total-row td { font-weight: 700; color: var(--ink); background: var(--surface-sunk); }
.gos3-claim-tbl .gos3-rx-cell { width: 90px; }

/* ══════════════════════════════════════════════════════════════════════
   DISPENSES PAGE — design system
   ══════════════════════════════════════════════════════════════════════ */

/* ── Workspace layout ────────────────────────────────────────────────────────── */
.workspace {
  flex: 1;
  min-height: 0;
  display: grid;
  grid-template-columns: 300px 1fr;
}

/* OPT-322: dispense page's 3rd column (combined Total+Payments card + NHS
   forms sidebar, see .disp-side below). Scoped to `.workspace.disp-workspace`
   (two classes → higher specificity than the plain `.workspace` rule above
   and the `.workspace` responsive overrides below) so other pages that use
   the generic 2-column `.workspace` grid are unaffected. */
.workspace.disp-workspace {
  grid-template-columns: 300px 1fr 320px;
}

/* ── Extras rail (dispense page right sidebar) ───────────────────────────────── */
.extras-rail {
  border-left: 1px solid var(--border);
  background: var(--surface-2);
  overflow-y: auto;
  padding: 16px 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.er-head {
  font-size: 11px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: var(--muted);
}
.er-empty { font-size: 12.5px; color: var(--muted); line-height: 1.4; }
.er-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 6px;
}
.er-tile {
  display: flex; flex-direction: column; align-items: flex-start; gap: 2px;
  padding: 8px 9px; border-radius: var(--r-sm);
  border: 1px solid var(--border-2); background: var(--surface);
  cursor: pointer; text-align: left;
  transition: border-color .12s, background .12s, box-shadow .12s;
}
.er-tile:hover { border-color: var(--accent); background: var(--accent-soft); }
.er-tile.active {
  background: var(--accent-soft); border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.er-tile-name {
  font-size: 11.5px; font-weight: 600; color: var(--ink); line-height: 1.2;
}
.er-tile.active .er-tile-name { color: var(--accent-ink); }
.er-tile-price { font-size: 10.5px; color: var(--muted); font-family: var(--mono); }
.er-tile.active .er-tile-price { color: var(--accent-ink); opacity: .75; }
.er-tile.blocked {
  opacity: .35; cursor: not-allowed;
  background: var(--surface-sunk); border-color: var(--border);
}
.er-tile.blocked:hover { background: var(--surface-sunk); border-color: var(--border); }
.rail {
  border-right: 1px solid var(--border);
  background: var(--surface-2);
  overflow-y: auto;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.main { overflow-y: auto; padding: 22px 26px; display: flex; flex-direction: column; }
.main-inner { max-width: 960px; margin: 0 auto; width: 100%; display: flex; flex-direction: column; flex: 1; }

/* OPT-322: dispense page right sidebar (Total+Payments card + NHS forms).
   Same "grid column stretches to the workspace's height, scrolls internally"
   idiom as .rail above — independently scrollable on desktop without a
   separate position:sticky mechanism. */
.disp-side {
  border-left: 1px solid var(--border);
  background: var(--surface-2);
  overflow-y: auto;
  padding: 16px 14px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

/* ── Card ────────────────────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}

/* ── Page title ──────────────────────────────────────────────────────────────── */
/* OPT-817: ONE canonical .page-title. It was defined three times at equal
   specificity (here, on the stock page, and again as "shared"), so the browser
   silently composed the three by source order. These declarations are that
   composition, computed rather than guessed, so this consolidation changes
   NOTHING visually — it just makes the real rule readable in one place.

   `flex: 1` came from the stock-page copy. Note it applies to EVERY page title
   in the app, not just that one: no later rule sets `flex`, so it was never
   overridden. That is almost certainly unintended (the stock page only needs it
   to push `.add-toggle-btn`, which already has `margin-left: auto` and works
   without it) — but scoping it to `.stk-title .page-title` would change layout
   on every other page, which needs a browser to check. Left as-is deliberately;
   see the follow-up ticket. */
.page-title {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 500;
  color: var(--ink);
  letter-spacing: -.2px;
  line-height: 1.2;
  margin: 0;
  flex: 1;
}

/* ── Section header ──────────────────────────────────────────────────────────── */
.section { margin-bottom: 20px; }
/* flex-fill: Notes and Extras sections expand to fill remaining viewport height */
.section.flex-fill { flex: 1; display: flex; flex-direction: column; margin-bottom: 0; }
.section.flex-fill .card { flex: 1; display: flex; flex-direction: column; }
.section-row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 20px; }
.section-row > .section { margin-bottom: 20px; }
.section-head {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-bottom: 10px;
}
.section-head h2 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 18px;
  margin: 0;
  color: var(--ink);
  letter-spacing: .1px;
}
.section-head .sub { font-size: 12px; color: var(--muted); }
.section-head .line { flex: 1; height: 1px; background: var(--border); }

/* ── Patient card (rail) ─────────────────────────────────────────────────────── */
.patient {
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.patient .top { display: flex; align-items: center; gap: 12px; }
.patient .pav {
  width: 46px;
  height: 46px;
  border-radius: 13px;
  background: linear-gradient(150deg, var(--accent-soft-2), var(--sand-soft));
  color: var(--accent-ink);
  display: grid;
  place-items: center;
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 600;
  flex: none;
  user-select: none;
}
.patient .name {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 600;
  line-height: 1.2;
  color: var(--ink);
}
.patient .meta { font-size: 12px; color: var(--muted); margin-top: 2px; }

.kv-list { display: flex; flex-direction: column; gap: 1px; }
.kv {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: 8px;
  align-items: center;
  padding: 5px 7px;
  border-radius: var(--r-sm);
  font-size: 12px;
}
.kv:hover { background: var(--surface-sunk); }
.kv .k { color: var(--muted); font-size: 11px; }
.kv .v { color: var(--ink-2); font-family: var(--mono); font-size: 11.5px; }

.gos-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 2px; }
.gos-tag {
  font-size: 11px;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 40px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  border: 1px solid transparent;
}
.gos-tag.off {
  background: transparent;
  color: var(--faint);
  border-color: var(--border);
}
button.gos-tag { cursor: pointer; transition: background .12s, box-shadow .12s; }
button.gos-tag:hover { box-shadow: 0 0 0 2px var(--accent-soft); background: var(--accent); color: #fff; }

/* ── GOS1 form overlay ─────────────────────────────────────────────────────── */
.gos1-overlay {
  position: fixed; inset: 0; z-index: 500;
  background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
  display: flex; justify-content: center; align-items: flex-start;
  overflow-y: auto; padding: 32px 16px 64px;
}
.gos1-sheet {
  background: var(--surface); border-radius: var(--r-lg);
  width: 860px; max-width: 100%;
  box-shadow: 0 24px 80px rgba(0,0,0,.35);
  display: flex; flex-direction: column;
}
.gos1-hd {
  display: flex; align-items: center; gap: 12px; padding: 16px 24px;
  border-bottom: 1px solid var(--border);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  background: var(--surface);
}
.gos1-hd-badge {
  background: #003087; color: #fff; font-weight: 800; font-size: 12px;
  padding: 3px 9px; border-radius: 4px; letter-spacing: .5px; flex-shrink: 0;
}
.gos1-hd-title { font-size: 14px; font-weight: 700; color: var(--ink); flex: 1; }
.gos1-hd-close {
  width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--border);
  background: transparent; cursor: pointer; font-size: 17px; color: var(--muted);
  display: grid; place-items: center; transition: background .1s; flex-shrink: 0;
}
.gos1-hd-close:hover { background: var(--surface-sunk); color: var(--ink); }
.gos1-tabs {
  display: flex; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.gos1-tab {
  padding: 10px 18px; font-size: 12px; font-weight: 600; color: var(--muted);
  border: none; background: transparent; cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color .1s, border-color .1s; font-family: var(--sans);
}
.gos1-tab:hover { color: var(--ink); }
.gos1-tab.active { color: #003087; border-bottom-color: #003087; }
.gos1-body { padding: 24px; overflow-y: auto; }
.gos1-sh {
  display: block; font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .6px; color: #fff; background: #003087;
  padding: 5px 10px; border-radius: 3px; margin: 20px 0 10px;
}
.gos1-body > *:first-child > .gos1-sh:first-child,
.gos1-sh:first-child { margin-top: 0; }
.gos1-help { font-size: 12px; color: var(--muted); margin: -6px 0 8px; font-style: italic; }
.gos1-decl { font-size: 11.5px; color: var(--muted); line-height: 1.55; margin-bottom: 12px; }

/* form rows & fields */
.gos1-row { display: flex; gap: 10px; align-items: flex-end; margin-bottom: 6px; flex-wrap: wrap; }
.gos1-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.gos1-label { font-size: 10.5px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: .4px; white-space: nowrap; }
.gos1-input {
  height: 32px; padding: 0 8px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface-sunk);
  font-family: var(--sans); font-size: 13px; color: var(--ink);
  outline: none; transition: border-color .1s, box-shadow .1s; min-width: 0; width: 100%;
}
.gos1-input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.gos1-input[type=date] { font-size: 12px; }
select.gos1-input { cursor: pointer; }

/* checkboxes / radios */
.gos1-check {
  display: flex; align-items: flex-start; gap: 7px; cursor: pointer;
  padding: 3px 0; font-size: 13px; color: var(--ink); line-height: 1.35; user-select: none;
}
.gos1-check input[type=checkbox], .gos1-check input[type=radio] {
  width: 14px; height: 14px; margin-top: 2px; flex-shrink: 0;
  cursor: pointer; accent-color: #003087;
}

/* voucher rows */
.gos1-voucher-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 4px 0; }
.gos1-vcol { font-size: 12px; font-weight: 600; color: var(--ink); width: 145px; flex-shrink: 0; }
.gos1-supp { font-size: 12px; color: var(--muted); white-space: nowrap; }

/* ethnicity grid */
.gos1-eth-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 16px; margin-top: 8px; }
.gos1-eth-head { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin-bottom: 5px; }

/* footer */
.gos1-ft {
  display: flex; align-items: center; gap: 10px; padding: 14px 24px;
  border-top: 1px solid var(--border); background: var(--surface-2);
  border-radius: 0 0 var(--r-lg) var(--r-lg);
}

/* ── GOS3 (yellow) / GOS4 (green) overlay colour schemes ────────────────────────
   GOS1 stays NHS blue (#003087, unchanged above). The real NHS GOS3 voucher is
   yellow and the GOS4 voucher is green, so the overlay root carries a modifier
   class (.gos1-overlay.gos3 / .gos1-overlay.gos4) that scopes overrides of the
   shared .gos1-* rules — same structure, different palette (OPT-348). */
.gos1-overlay.gos3 .gos1-hd-badge { background: var(--gos-yellow); color: var(--gos-yellow-ink); }
.gos1-overlay.gos3 .gos1-tab.active { color: var(--gos-yellow-ink); border-bottom-color: var(--gos-yellow-ink); }
.gos1-overlay.gos3 .gos1-sh { background: var(--gos-yellow); color: var(--gos-yellow-ink); }
.gos1-overlay.gos3 .gos1-check input[type=checkbox],
.gos1-overlay.gos3 .gos1-check input[type=radio] { accent-color: var(--gos-yellow-ink); }

.gos1-overlay.gos4 .gos1-hd-badge { background: var(--ok-strong); color: #fff; }
.gos1-overlay.gos4 .gos1-tab.active { color: var(--ok-strong); border-bottom-color: var(--ok-strong); }
.gos1-overlay.gos4 .gos1-sh { background: var(--ok-strong); color: #fff; }
.gos1-overlay.gos4 .gos1-check input[type=checkbox],
.gos1-overlay.gos4 .gos1-check input[type=radio] { accent-color: var(--ok-strong); }

/* ── GOS signature capture (OPT-532) ──────────────────────────────────────────
   optos.signature-pad: a hand-rolled canvas signature pad wired into every
   GOS1/GOS3/GOS4 signature slot, each preceded by its declaration notice (the
   GOS1/GOS3 patient texts are long, hence the own scrollable block rather
   than letting them dominate the form — reuses the existing .gos1-decl text
   style inside it). A `.pending` block (wording not yet supplied by the
   practice — a plain placeholder, never invented NHS text) gets the warm
   --warn palette instead of the neutral --surface-sunk, so it reads as
   visibly provisional at a glance. */
.gos1-decl-block {
  max-height: 92px; overflow-y: auto; padding: 8px 10px; margin-bottom: 10px;
  background: var(--surface-sunk); border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.gos1-decl-block .gos1-decl { margin-bottom: 0; }
.gos1-decl-block.pending { background: var(--warn-soft); border-color: var(--warn); }
.gos1-decl-pending-tag {
  display: block; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .4px; color: var(--warn); margin-bottom: 4px;
}

.sig-pad-wrap { margin-bottom: 16px; }
.sig-pad-canvas {
  display: block; width: 100%; max-width: 480px; touch-action: none;
  border: 1px solid var(--border); border-radius: var(--r-sm);
  background: #fff; cursor: crosshair;
}
.sig-pad-controls { display: flex; align-items: center; gap: 8px; margin-top: 6px; flex-wrap: wrap; }
.sig-pad-meta { font-size: 11px; color: var(--muted); }
.sig-pad-preview {
  display: flex; align-items: center; gap: 8px; margin-bottom: 6px; font-size: 11px; color: var(--muted);
}
.sig-pad-preview img {
  height: 34px; max-width: 120px; border: 1px solid var(--border); border-radius: 3px; background: #fff;
}

/* ── GOS1 / GOS3 / GOS4 form print (OPT-664) ──────────────────────────────────
   None of the three GOS overlays had ANY print support before this ticket:
   printing one (Ctrl+P -- there is no in-app print trigger here, since
   gos1_views.cljs / gos3_views.cljs / gos4_views.cljs sit outside this
   ticket's file scope, see CLAUDE.md's ticket-scope discipline) produced a
   BLANK page, because the "Print foundation" block above (`body * {
   visibility: hidden }`) hides everything by default and nothing ever
   restored visibility for `.gos1-overlay`. GOS3/GOS4 reuse the exact same
   `.gos1-*` markup (a `.gos3`/`.gos4` modifier class on `.gos1-overlay`, see
   the palette block above), so this ONE reveal rule covers all three forms.
   Every var(--ink)/var(--muted)/var(--border)/var(--surface-*) colour the
   form's everyday on-screen styling reads is already forced print-safe by
   the `:root` override in the "Print foundation" block, so nothing further
   is hardcoded here -- this block only strips the modal chrome (backdrop,
   shadow, close/tabs/footer buttons) and un-fixes the overlay's layout.

   KNOWN LIMITATION (not fixable from style.css alone): GOS1's body only
   renders whichever of its three tabs is currently active (`case tab ...`
   in gos1-dialog), so printing captures ONLY that tab, not the full form.
   Making all three tabs print together needs gos1_views.cljs to render them
   simultaneously -- a cljs change outside this ticket's file scope; flagged
   here as a follow-up rather than worked around. */
@media print {
  .gos1-overlay, .gos1-overlay * { visibility: visible; }
  .gos1-overlay {
    position: static;
    display: block;
    padding: 0;
    background: #fff;
    backdrop-filter: none;
  }
  .gos1-sheet {
    width: 100%;
    max-width: none;
    box-shadow: none;
    border-radius: 0;
  }
  .gos1-hd { border-radius: 0; }
  .gos1-hd-close,
  .gos1-tabs,
  .gos1-ft {
    display: none !important;
  }
}

/* ── Message composer (OPT-404) ──────────────────────────────────────────────
   Free-form email/SMS a staff member writes to a patient. Same overlay/sheet
   shape as .gos1-overlay/.gos1-sheet above, just a smaller footprint. */
.composer-overlay {
  position: fixed; inset: 0; z-index: 500;
  background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
  display: flex; justify-content: center; align-items: flex-start;
  overflow-y: auto; padding: 32px 16px 64px;
}
.composer-sheet {
  background: var(--surface); border-radius: var(--r-lg);
  width: 480px; max-width: 100%;
  box-shadow: 0 24px 80px rgba(0,0,0,.35);
  display: flex; flex-direction: column;
}
.composer-hd {
  display: flex; align-items: center; gap: 12px; padding: 14px 20px;
  border-bottom: 1px solid var(--border);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
}
.composer-hd-title { font-size: 14px; font-weight: 700; color: var(--ink); flex: 1; }
.composer-hd-close {
  width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--border);
  background: transparent; cursor: pointer; font-size: 16px; color: var(--muted);
  display: grid; place-items: center; transition: background .1s; flex-shrink: 0;
}
.composer-hd-close:hover { background: var(--surface-sunk); color: var(--ink); }
.composer-tabs {
  display: flex; border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
.composer-tab {
  padding: 9px 16px; font-size: 12px; font-weight: 600; color: var(--muted);
  border: none; background: transparent; cursor: pointer;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
  transition: color .1s, border-color .1s; font-family: var(--sans);
}
.composer-tab:hover:not(:disabled) { color: var(--ink); }
.composer-tab.active { color: var(--accent-ink); border-bottom-color: var(--accent-ink); }
.composer-tab:disabled { color: var(--faint); cursor: not-allowed; }
.composer-body { padding: 18px 20px; display: flex; flex-direction: column; gap: 12px; }
.composer-field { display: flex; flex-direction: column; gap: 4px; }
.composer-label {
  font-size: 10.5px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .4px;
}
.composer-to,
.composer-subject {
  height: 34px; padding: 0 10px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font-size: 13px; font-family: var(--sans);
}
.composer-to { background: var(--surface-sunk); color: var(--muted); }
.composer-textarea {
  padding: 8px 10px; border-radius: var(--r-sm);
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font-size: 13px; font-family: var(--sans); resize: vertical; min-height: 110px;
}
.composer-hint { font-size: 11.5px; color: var(--muted); font-style: italic; margin: 0; }
.composer-counter { font-size: 11.5px; color: var(--muted); margin: 0; }
.composer-error { font-size: 12px; color: var(--danger); margin: 0; }
.composer-actions {
  display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px;
  border-top: 1px solid var(--border);
}

/* ── Dispense history (rail) ─────────────────────────────────────────────────── */
.hist-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 2px 8px;
}
.hist-head .t {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .6px;
}
.hist-list { display: flex; flex-direction: column; gap: 8px; }
.hist {
  text-align: left;
  width: 100%;
  cursor: pointer;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 11px 12px;
  display: flex;
  flex-direction: column;
  gap: 7px;
  font-family: var(--sans);
  transition: border-color .12s, box-shadow .12s;
}
.hist:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.hist.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.hist .row1 { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.hist .id { font-family: var(--mono); font-size: 12px; color: var(--ink); font-weight: 500; }
.hist .row2 { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.hist .date { font-size: 11.5px; color: var(--muted); }
.hist .type { font-size: 12px; color: var(--ink-2); font-weight: 500; }

.new-disp {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  width: 100%;
  border: 1px dashed var(--border-2);
  background: transparent;
  color: var(--accent-ink);
  font-family: var(--sans);
  font-weight: 600;
  font-size: 12.5px;
  padding: 10px;
  border-radius: var(--r);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.new-disp:hover { background: var(--accent-soft); border-color: var(--accent); }

/* ── Status pills ────────────────────────────────────────────────────────────── */
.status {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  font-weight: 600;
  padding: 3px 8px 3px 7px;
  border-radius: 40px;
  white-space: nowrap;
}
.status .dot { width: 6px; height: 6px; border-radius: 50%; }
.status.draft   { background: var(--surface-sunk); color: var(--muted); }
.status.draft .dot { background: var(--faint); }
.status.ordered { background: var(--info-soft); color: var(--info); }
.status.ordered .dot { background: var(--info); }
.status.fitting { background: var(--warn-soft); color: var(--warn); }
.status.fitting .dot { background: var(--warn); }
.status.collected { background: var(--ok-soft); color: var(--ok); }
.status.collected .dot { background: var(--ok); }
.status.ok { background: var(--ok-soft); color: var(--ok); }

/* ── Btn system (global) ─────────────────────────────────────────────────────── */
.btn {
  font-family: var(--sans);
  font-size: 13px;
  font-weight: 500;
  border-radius: var(--r-sm);
  padding: 6px 14px;
  cursor: pointer;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-2);
  display: inline-flex;
  align-items: center;
  gap: 7px;
  transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { background: var(--surface-sunk); border-color: var(--border-strong); }
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--on-accent);
}
.btn.primary:hover {
  background: var(--accent-strong);
  border-color: var(--accent-strong);
}
.btn.ghost {
  border-color: transparent;
  background: transparent;
  color: var(--ink-2);
}
.btn.ghost:hover { background: var(--surface-sunk); border-color: transparent; }
.btn.warn {
  background: var(--warn-soft);
  border-color: var(--warn);
  color: var(--warn);
}
.btn.warn:hover {
  background: var(--warn);
  color: #fff;
}
.btn.sm { padding: 4px 9px; font-size: 12px; }

/* ── Editable fields ─────────────────────────────────────────────────────────── */
.field { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.field .lbl {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: .3px;
  text-transform: uppercase;
}
.editable {
  position: relative;
  border-radius: var(--r-sm);
  cursor: text;
  padding: 7px 9px;
  min-height: 34px;
  display: flex;
  align-items: center;
  border: 1px solid transparent;
  background: var(--surface-sunk);
  font-size: 14px;
  color: var(--ink);
  transition: border-color .12s, background .12s, box-shadow .12s;
}
.editable:hover { background: var(--surface-2); border-color: var(--border); }
.editable.mono { font-family: var(--mono); }
.editable input, .editable select {
  border: none !important;
  outline: none;
  background: transparent;
  box-shadow: none !important;
  width: 100%;
  font-family: inherit;
  font-size: inherit;
  color: var(--ink);
  padding: 0;
  margin: 0;
}

/* ── Appointment / recall summary table ─────────────────────────────────────── */
table.recall-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
  margin-top: 6px;
}
table.recall-table td {
  padding: 5px 8px;
  border-bottom: 1px solid var(--border);
  color: var(--ink);
}
table.recall-table tr:last-child td { border-bottom: none; }
td.recall-label {
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  width: 80px;
}

/* ── Appointment recall rows (patient panel) ─────────────────────────────────── */
.recall-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 4px 0;
  font-size: 13px;
  border-bottom: 1px solid var(--border);
}
.recall-row:last-child { border-bottom: none; }
.recall-row .recall-label {
  color: var(--muted);
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  min-width: 72px;
}

/* ── Rx table (dispenses page) ───────────────────────────────────────────────── */
table.rx {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
}
table.rx th, table.rx td { text-align: center; padding: 0; }
table.rx thead th {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  letter-spacing: .4px;
  text-transform: uppercase;
  padding: 4px 0 8px;
}
table.rx thead th.eye-h { text-align: left; width: 86px; }
table.rx tbody td { padding: 3px; }
table.rx .eye-c {
  text-align: left;
  font-weight: 600;
  font-size: 13px;
  color: var(--ink);
  display: flex;
  flex-direction: column;
  gap: 0;
  padding-left: 4px;
}
table.rx .eye-c .ab { font-family: var(--mono); font-size: 13px; }
table.rx .eye-c .full { font-size: 10.5px; color: var(--muted); font-weight: 500; }
.rx-cell .editable {
  background: var(--surface-sunk);
  justify-content: center;
  padding: 7px 4px;
  font-family: var(--mono);
  font-size: 13px;
  min-height: 36px;
}
.rx-cell .editable input { text-align: center; }
.rx-cell.empty .editable { color: var(--faint); }

/* ── Product cards (frame / lens) ────────────────────────────────────────────── */
.prod {
  display: grid;
  grid-template-columns: 118px 1fr;
  gap: 16px;
  padding: 16px;
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  background: var(--surface-2);
}
.prod-thumb {
  border-radius: var(--r);
  overflow: hidden;
  background: repeating-linear-gradient(135deg, var(--surface-sunk) 0 9px, var(--surface-2) 9px 18px);
  border: 1px solid var(--border);
  aspect-ratio: 1 / 0.82;
  display: grid;
  place-items: center;
  text-align: center;
}
.prod-thumb .ph-t {
  font-family: var(--mono);
  font-size: 10px;
  color: var(--faint);
  padding: 4px 6px;
  line-height: 1.4;
}
.prod-thumb.lensglyph {
  background: radial-gradient(120% 100% at 50% 0%, var(--accent-soft) 0%, var(--surface-2) 70%);
}
.prod-thumb.lensglyph svg { width: 64%; height: 64%; color: var(--accent); opacity: .85; }
.prod-body { display: flex; flex-direction: column; min-width: 0; }
.prod-body .ptitle {
  display: flex;
  align-items: baseline;
  gap: 10px;
  justify-content: space-between;
}
.prod-body .pname {
  font-family: var(--serif);
  font-size: 18px;
  font-weight: 600;
  color: var(--ink);
}
.prod-body .pmfr { font-size: 12px; color: var(--muted); font-weight: 500; }
.prod-body .pprice {
  font-family: var(--mono);
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
  white-space: nowrap;
}
.spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 8px 14px;
  margin-top: 12px;
}
.spec { display: flex; flex-direction: column; gap: 1px; }
.spec .sk {
  font-size: 10.5px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: .3px;
  font-weight: 600;
}
.spec .sv { font-size: 13px; color: var(--ink-2); font-family: var(--mono); }

.empty-pick {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 18px;
  border: 1px dashed var(--border-2);
  border-radius: var(--r-lg);
  background: var(--surface-2);
}
.empty-pick .ep-t { font-weight: 600; color: var(--ink-2); font-size: 13px; }
.empty-pick .ep-s { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* ── Picker modal ────────────────────────────────────────────────────────────── */
.modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(30,26,20,.42);
  display: grid;
  place-items: center;
  z-index: 60;
  padding: 24px;
  animation: fade .14s ease;
}
[data-theme="dark"] .modal-scrim { background: rgba(0,0,0,.6); }
@keyframes fade { from { opacity: 0; } }
.modal {
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  width: min(760px, 100%);
  max-height: 84vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  animation: pop .16s ease;
}
@keyframes pop { from { transform: translateY(8px) scale(.99); opacity: 0; } }
.modal-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 18px;
  border-bottom: 1px solid var(--border);
}
.modal-h h3 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 18px;
  margin: 0;
  color: var(--ink);
}
.modal-list {
  overflow-y: auto;
  padding: 10px;
  display: grid;
  gap: 8px;
  grid-template-columns: 1fr 1fr;
}
.modal-empty {
  padding: 32px;
  text-align: center;
  color: var(--muted);
  grid-column: 1 / -1;
  font-size: 13px;
}
.pick {
  text-align: left;
  cursor: pointer;
  display: grid;
  grid-template-columns: 56px 1fr;
  gap: 12px;
  padding: 11px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  font-family: var(--sans);
  transition: border-color .12s, box-shadow .12s;
}
.pick:hover { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.pick.blocked { opacity: .38; cursor: not-allowed; }
.pick.blocked:hover { border-color: var(--border); box-shadow: none; }
.pick-thumb {
  border-radius: var(--r-sm);
  background: repeating-linear-gradient(135deg, var(--surface-sunk) 0 7px, var(--surface-2) 7px 14px);
  border: 1px solid var(--border);
  aspect-ratio: 1 / .82;
  display: grid;
  place-items: center;
}
.pick-thumb.lensglyph {
  background: radial-gradient(120% 100% at 50% 0, var(--accent-soft) 0, var(--surface-2) 70%);
}

/* ══════════════════════════════════════════════════════════════════════
   DIARY PAGE — 3-column layout
   ══════════════════════════════════════════════════════════════════════ */

.pd-workspace {
  flex: 1; min-height: 0; display: grid;
  grid-template-columns: 340px 1fr 300px;
}
.pd-col { overflow-y: auto; min-height: 0; }
.pd-col.left  { border-right: 1px solid var(--border); background: var(--surface-2); }
.pd-col.right { border-left: 1px solid var(--border); background: var(--surface-2); }
.pd-col.center { padding: 20px 22px; }
.pd-pad { padding: 16px; display: flex; flex-direction: column; gap: 14px; }

/* ── OPT-434/435/437: staff-on-shift spines + right-panel roster key ────────── */
/* Each scheduled shift is a vertical coloured SPINE spanning its start->end time
   on the day timeline (same time->px space as the appointment cards, so it lines
   up with the hours). Overlapping shifts stagger into lanes via an inline `right`
   offset; each spine's little letter tab reveals the staff name on hover. The
   labelled key (colour -> name -> hours) lives in the right-panel Roster tab. */
.staff-spine-layer {
  position: absolute; inset: 0;
  pointer-events: none;               /* only the spines catch the mouse */
  z-index: 6;
}
.staff-spine {
  position: absolute;
  width: 8px;
  min-height: 8px;
  border-radius: 4px;
  box-shadow: -1px 0 3px rgba(42, 39, 35, 0.2);
  pointer-events: auto;
  cursor: default;
}
.staff-spine.leave {
  opacity: 0.5;
  background-image: repeating-linear-gradient(
    45deg, rgba(255,255,255,0.5) 0 3px, transparent 3px 6px);
}
/* the protruding letter tab at the top of each spine */
.spine-tab {
  position: absolute;
  top: -2px; left: 6px;               /* sticks out to the RIGHT of the spine, away from the diary */
  min-width: 18px; height: 18px; padding: 0 3px;
  border-radius: 0 4px 4px 0;
  display: grid; place-items: center;
  background: inherit;                /* takes the spine's rtab colour */
  color: #fff; font-size: 10.5px; font-weight: 800; line-height: 1;
  box-shadow: 1px 1px 3px rgba(42, 39, 35, 0.28);
  white-space: nowrap;
}
.staff-spine:hover { filter: brightness(1.08); }
/* the native `title` reveals the staff name on hover */

.roster-key { display: flex; flex-direction: column; gap: 8px; }
.roster-key-row { display: flex; align-items: center; gap: 9px; }
.roster-swatch { width: 14px; height: 14px; border-radius: 3px; flex: none; }
.roster-key-name { font-weight: 600; color: var(--ink); font-size: 13px; }
.roster-key-hours { margin-left: auto; font-size: 12px; color: var(--muted); font-family: var(--mono); }

/* 8-colour palette — distinct medium hues that read on both themes. */
.rtab-c0 { background: #b5654a; }
.rtab-c1 { background: #4a7fb5; }
.rtab-c2 { background: #5b9e6f; }
.rtab-c3 { background: #9a6fb0; }
.rtab-c4 { background: #c0912f; }
.rtab-c5 { background: #3fa3a3; }
.rtab-c6 { background: #c25f7c; }
.rtab-c7 { background: #7a8450; }

/* ── Right sidebar tabs (Patients / Diary) ───────────────────────────────────── */
.pd-right { display: flex; flex-direction: column; min-height: 100%; }
.pd-tabs {
  display: flex; gap: 2px; padding: 10px 12px 0; position: sticky; top: 0;
  background: var(--surface-2); z-index: 1;
}
.pd-tab {
  flex: 1; border: none; background: transparent; cursor: pointer;
  font-family: var(--sans); font-size: 12.5px; font-weight: 600; color: var(--muted);
  padding: 9px 6px 10px; border-bottom: 2px solid transparent;
  transition: color .12s, border-color .12s;
}
.pd-tab:hover { color: var(--ink); }
.pd-tab.active { color: var(--accent-ink); border-bottom-color: var(--accent); }
.pd-tab-body { flex: 1; min-height: 0; border-top: 1px solid var(--border); }

/* ── Patient detail panel ────────────────────────────────────────────────────── */
.px-head { display: flex; align-items: center; gap: 13px; }
.px-head .pav {
  width: 52px; height: 52px; border-radius: 14px; flex: none;
  background: linear-gradient(150deg, var(--accent-soft-2), var(--sand-soft));
  color: var(--accent-ink); display: grid; place-items: center;
  font-family: var(--serif); font-size: 21px; font-weight: 600;
}
.px-head .px-name { font-family: var(--serif); font-size: 20px; font-weight: 600; line-height: 1.1; color: var(--ink); }
.px-head .px-meta { font-size: 12px; color: var(--muted); margin-top: 3px; }

/* Field group cards */
.fgroup {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 13px 14px; box-shadow: var(--shadow-sm);
}
.fgroup > .gh { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.fgroup > .gh .gt { font-size: 11px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; color: var(--muted); }
.fgroup > .gh .gline { flex: 1; height: 1px; background: var(--border); }
.frow { display: grid; gap: 8px 10px; }
.frow.c2 { grid-template-columns: 1fr 1fr; }
.frow.c3 { grid-template-columns: 1fr 1fr 1fr; }
.fg-field { display: flex; flex-direction: column; gap: 3px; min-width: 0; margin-bottom: 8px; }
.fg-field:last-child { margin-bottom: 0; }
.fg-field .lbl { font-size: 10.5px; font-weight: 700; color: var(--muted); letter-spacing: .4px; text-transform: uppercase; }
.fg-field input {
  background: var(--surface-sunk); border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 6px 8px; font-size: 12.5px; color: var(--ink); font-family: var(--sans);
  outline: none; margin: 0; width: 100%;
  transition: border-color .12s, background .12s, box-shadow .12s;
}
.fg-field input:hover { background: var(--surface-2); border-color: var(--border); }
.fg-field input:focus { background: var(--surface); border-color: var(--accent); box-shadow: var(--ring); }
.frow .fg-field { margin-bottom: 0; }

/* ── Diary head + date nav ───────────────────────────────────────────────────── */
/* OPT-377: nowrap (was wrap) — a wrapped header pushes the grid's measured
   top down, which OPT-366's compute-slot-h reads as less available height
   than there really is, so keeping this single-line protects vertical fit
   too, not just tidiness. overflow-x is a last-resort safety valve (harmless
   no-op once the row actually fits, which the OPT-377 breakpoints below aim
   for down to 1024px) rather than a primary mechanism. The ≤640px mobile
   block further down still switches this back to flex-wrap: wrap, where
   stacking is fine because the whole page scrolls there. */
.diary-head {
  display: flex; align-items: center; gap: 10px; margin-bottom: 6px;
  border-bottom: 1px solid var(--border); padding-bottom: 10px;
  flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin;
}
.diary-head .btn { white-space: nowrap; }
/* full/short label pairs (diary_views.cljs reminders-panel + print button) —
   short form is swapped in once the row runs out of room, see the ≤1280px
   breakpoint below. */
.diary-head .lbl-short { display: none; }
.diary-title { font-family: var(--serif); font-size: 22px; font-weight: 500; white-space: nowrap; color: var(--ink); }
.date-nav { display: flex; align-items: center; gap: 4px; margin-left: auto; }
.date-nav input[type=date] {
  padding: 0 10px; height: 30px; border-radius: var(--r-sm);
  border: 1px solid var(--border-2); background: var(--surface);
  font-size: 12.5px; font-weight: 500; color: var(--ink-2); cursor: pointer; margin: 0;
  font-family: var(--sans); outline: none;
}
.date-nav input[type=date]:focus { border-color: var(--accent); box-shadow: var(--ring); }
.dnav-btn {
  height: 30px; min-width: 28px; padding: 0 6px;
  border: 1px solid var(--border-2); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink-2);
  font-size: 14px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: border-color .12s, background .12s;
}
.dnav-btn:hover { border-color: var(--accent); background: var(--surface-2); color: var(--ink); }
.day-lbl {
  font-size: 12px; font-weight: 600; color: var(--muted);
  letter-spacing: .3px; white-space: nowrap; padding: 0 4px;
  text-transform: uppercase;
}

/* ── Now line ────────────────────────────────────────────────────────────────── */
.now-line {
  position: absolute; left: 56px; right: 0; height: 2px;
  background: var(--accent); z-index: 10; pointer-events: none;
}
.now-line::before {
  content: ''; position: absolute; left: -5px; top: -4px;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--accent);
}

/* ── Current appointment highlight ──────────────────────────────────────────── */
.tl-appt.is-now {
  box-shadow: 0 0 0 2px var(--accent);
  z-index: 2;
}

/* ── Clinician tabs ──────────────────────────────────────────────────────────── */
/* OPT-286: compact, centered clinician (optom) selector pills.
   OPT-377: nowrap + its own horizontal scroll (same reasoning as .diary-head
   above — a wrapped row pushes the grid's measured top down and defeats
   OPT-366's vertical-fit math) — a practice with many clinicians scrolls
   this row sideways rather than growing it to N lines. */
.clin-tabs {
  display: flex; justify-content: center; gap: 6px; margin-bottom: 12px;
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
}
.clin-tab {
  display: flex; align-items: center; gap: 6px; cursor: pointer;
  padding: 4px 10px 4px 5px; border-radius: 40px; border: 1px solid var(--border);
  background: var(--surface); transition: border-color .12s, background .12s;
  font-family: var(--sans); text-align: left; flex: none;
}
/* OPT-377: nowrap — inside a nowrap/scrolling .clin-tabs row a long name
   should truncate, not wrap and inflate the pill's height. */
.clin-tab .cn { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 22vw; }
.clin-tab:hover { border-color: var(--border-strong); }
.clin-tab.active { border-color: var(--accent); background: var(--accent-soft); }
.clin-tab .cav {
  width: 21px; height: 21px; border-radius: 50%; background: var(--sand-soft);
  color: var(--ink-2); display: grid; place-items: center; font-size: 9.5px; font-weight: 700; flex: none;
}
.clin-tab.active .cav { background: var(--accent); color: var(--on-accent); }
.clin-tab .cn { font-size: 11.5px; font-weight: 600; color: var(--ink); }
.clin-tab .cnt {
  margin-left: 3px; font-size: 10px; font-weight: 700; color: var(--muted);
  background: var(--surface-sunk); border-radius: 40px; padding: 1px 6px;
}
.clin-tab.active .cnt { background: var(--accent-soft-2); color: var(--accent-ink); }

/* ── Grid measurement frame (OPT-366) ────────────────────────────────────────── */
/* Plain block wrapping the day/week grid — its top edge IS the grid's top
   edge, giving diary-grid-frame's getBoundingClientRect a stable anchor to
   derive slot-h from the room actually left below the pills. No visual
   styling of its own. */
.diary-grid-frame { display: block; }

/* ── Timeline grid ───────────────────────────────────────────────────────────── */
.timeline {
  position: relative; border: 1px solid var(--border); border-radius: var(--r-lg);
  background: var(--surface); box-shadow: var(--shadow-sm); overflow: hidden;
}
.tl-row {
  height: 46px; border-bottom: 1px solid var(--border);
  display: grid; grid-template-columns: 56px 1fr;
}
.tl-row:last-child { border-bottom: none; }
.tl-time {
  font-family: var(--mono); font-size: 11px; color: var(--faint);
  padding: 5px 0 0 10px; border-right: 1px solid var(--border);
}
.tl-row.onhour .tl-time { color: var(--muted); font-weight: 500; }
.tl-track { position: relative; }
.tl-add {
  position: absolute; inset: 3px 8px 3px 8px; border-radius: var(--r-sm); cursor: pointer;
  display: flex; align-items: center; gap: 7px; padding-left: 10px; color: transparent;
  font-size: 12px; font-weight: 600; transition: background .12s, color .12s;
}
.tl-track:hover .tl-add { background: var(--accent-soft); color: var(--accent-ink); }
.tl-track:hover .tl-add.move-mode { background: var(--warn-soft); color: var(--warn); }

/* ── Appointment cards (timeline) ────────────────────────────────────────────── */
.tl-appt-layer {
  position: absolute; left: 56px; right: 0; top: 0; bottom: 0; pointer-events: none;
}
.tl-appt {
  position: absolute; left: 8px; right: 10px;
  border-radius: var(--r); padding: 6px 10px; cursor: pointer; overflow: hidden;
  border: 1px solid var(--border); border-left: 3px solid var(--muted);
  background: var(--surface-2); box-shadow: var(--shadow-sm);
  display: flex; flex-direction: column; gap: 1px; z-index: 2; pointer-events: auto;
  transition: box-shadow .12s; font-family: var(--sans); text-align: left;
}
.tl-appt:hover { box-shadow: var(--shadow); }
/* OPT-370: one flex row (.a-meta-row) — time, type, then the patient name
   taking whatever width is left, then the action buttons. .a-time/.a-meta-sep
   /.a-type/.a-actions keep flex:none (their intrinsic width); .a-patient is
   the only item that grows/shrinks (min-width:0 + ellipsis) so long names
   truncate instead of wrapping or pushing the actions off the block. Single
   row means no vertical stacking at any slot height (see diary_views.cljs). */
.tl-appt .a-meta-row { display: flex; align-items: center; gap: 5px; width: 100%; overflow: hidden; }
.tl-appt .a-time { font-family: var(--mono); font-size: 10.5px; color: var(--ink-2); font-weight: 500; white-space: nowrap; flex: none; }
.tl-appt .a-meta-sep { font-size: 10px; color: var(--faint); flex: none; }
.tl-appt .a-type { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; white-space: nowrap; flex: none; }
.tl-appt .a-patient {
  font-size: 13px; font-weight: 600; color: var(--ink); line-height: 1.25;
  flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  margin-left: 2px;
}
.tl-appt .a-actions { display: flex; gap: 4px; flex: none; }
.tl-appt .a-act-btn {
  width: 20px; height: 20px; border-radius: var(--r-sm); border: 1px solid transparent;
  background: transparent; cursor: pointer; display: grid; place-items: center;
  font-size: 11px; color: var(--muted); font-family: var(--sans);
  transition: background .1s, color .1s;
}
.tl-appt .a-act-btn:hover { background: rgba(0,0,0,.08); color: var(--ink); }
.tl-appt .a-act-btn.danger:hover { background: var(--danger-soft); color: var(--danger); }
.tl-appt .a-act-btn.arrived:hover { background: var(--ok-soft); color: var(--ok); }
.tl-appt .a-act-btn.arrived.active { background: var(--ok-soft); color: var(--ok); border-color: var(--ok); }
.tl-appt .a-act-btn.dna:hover { background: var(--danger-soft); color: var(--danger); }
.tl-appt .a-act-btn.dna.active { background: var(--danger-soft); color: var(--danger); border-color: var(--danger); }
.tl-appt.s-arrived { border-left-color: var(--ok) !important; }
.tl-appt.s-dna     { border-left-color: var(--danger) !important; opacity: .7; }
/* OPT-414: "confirmed"/"cancelled" are patient-self-service statuses (same
   :appt/status field as arrived/dna, so always mutually exclusive with them).
   Cancelled mirrors the dna treatment (muted + danger border) plus a
   strike-through on the patient name; confirmed is a quiet ok-tinted border,
   the tick badge (below) carries the rest of the signal. */
.tl-appt.s-confirmed { border-left-color: var(--ok) !important; }
.tl-appt.s-cancelled { border-left-color: var(--danger) !important; opacity: .7; }
.tl-appt.s-cancelled .a-patient { text-decoration: line-through; }

/* appointment tones */
.tl-appt.t-accent { border-left-color: var(--accent); background: var(--accent-soft); }
.tl-appt.t-accent .a-type { color: var(--accent-ink); }
.tl-appt.t-info   { border-left-color: var(--info);   background: var(--info-soft); }
.tl-appt.t-info   .a-type { color: var(--info); }
.tl-appt.t-ok     { border-left-color: var(--ok);     background: var(--ok-soft); }
.tl-appt.t-ok     .a-type { color: var(--ok); }
.tl-appt.t-warn   { border-left-color: var(--warn);   background: var(--warn-soft); }
.tl-appt.t-warn   .a-type { color: var(--warn); }
.tl-appt.t-slate  { border-left-color: #6b7686; background: var(--surface-sunk); }
.tl-appt.t-slate  .a-type { color: var(--muted); }

/* OPT-414: confirmed/cancelled badge (day timeline + week grid). Fixed size,
   flex:none, no border/box-shadow of its own so it never grows the row and
   never wraps it to a second line -- appended as the last item in each row
   so it only ever competes with the already-shrinkable name for space. */
.appt-status-badge {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 14px; height: 14px; border-radius: 50%;
  font-size: 9px; line-height: 1; font-family: var(--sans); font-weight: 700;
}
.appt-status-badge.asb-confirmed { background: var(--ok-soft);     color: var(--ok); }
.appt-status-badge.asb-cancelled { background: var(--danger-soft); color: var(--danger); }

/* block periods */
.tl-block-layer { position: absolute; inset: 0; pointer-events: none; }
.tl-block {
  position: absolute; left: 50px; right: 4px; border-radius: 3px;
  background: repeating-linear-gradient(135deg, rgba(100,100,100,.08) 0px, rgba(100,100,100,.08) 4px, transparent 4px, transparent 8px);
  border: 1px solid rgba(100,100,100,.25); display: flex; align-items: flex-start;
  padding: 3px 6px; gap: 6px; pointer-events: auto; z-index: 1;
}
.block-reason { font-size: 11px; color: var(--muted); font-style: italic; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.block-rm { background: none; border: none; cursor: pointer; font-size: 13px; color: var(--muted); padding: 0 2px; line-height: 1; }
.block-rm:hover { color: var(--danger); }
.block-add-wrap { padding: 6px 0 0; }
.block-form { display: flex; flex-direction: column; gap: 8px; padding: 10px; background: var(--surface-2); border-radius: var(--r-sm); border: 1px solid var(--border-2); }
.block-form-row { display: flex; gap: 10px; align-items: flex-end; }
.block-form-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* view toggle (Day / Week) */
.view-toggle { display: flex; gap: 0; border: 1px solid var(--border-2); border-radius: var(--r-sm); overflow: hidden; }
.vtog {
  padding: 4px 12px; font-size: 12px; font-weight: 500; background: var(--surface);
  color: var(--muted); border: none; cursor: pointer; transition: background .15s, color .15s;
  font-family: var(--sans);
}
.vtog:hover { background: var(--surface-2); color: var(--ink); }
.vtog.active { background: var(--accent); color: var(--on-accent); }

/* week grid */
.week-grid {
  display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px;
  min-height: 300px; align-items: start;
}
.week-col { display: flex; flex-direction: column; min-width: 0; }
.week-col-hd {
  padding: 7px 6px; text-align: center; cursor: pointer;
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: var(--surface-2); border: 1px solid var(--border-2);
  border-bottom: none; transition: background .15s;
}
.week-col-hd:hover { background: var(--accent-soft); }
.week-day-name { font-size: 10px; font-weight: 700; letter-spacing: .5px; text-transform: uppercase; color: var(--muted); }
.week-day-date { font-size: 16px; font-weight: 600; color: var(--ink); margin-top: 1px; }
.week-col-body {
  border: 1px solid var(--border-2); border-top: none; border-radius: 0 0 var(--r-sm) var(--r-sm);
  padding: 4px; display: flex; flex-direction: column; gap: 3px; min-height: 60px;
  background: var(--surface);
}
.week-chip {
  padding: 3px 5px; border-radius: 4px; font-size: 11px; cursor: pointer;
  border-left: 3px solid transparent; display: flex; flex-direction: column; gap: 1px;
  background: var(--surface-2); transition: opacity .12s;
}
.week-chip:hover { opacity: .8; }
/* OPT-414: wc-row keeps the badge on the same line as the time (chip stays
   two lines -- time row, name row -- whether or not a badge is present) so
   adding it never grows the chip and never pushes chips below it down. */
.wc-row { display: flex; align-items: center; justify-content: space-between; gap: 4px; }
.wc-time { font-size: 10px; font-weight: 600; color: var(--muted); }
.wc-name { font-size: 11px; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.week-chip.t-accent { border-left-color: var(--accent); background: var(--accent-soft); }
.week-chip.t-info   { border-left-color: var(--info);   background: var(--info-soft); }
.week-chip.t-ok     { border-left-color: var(--ok);     background: var(--ok-soft); }
.week-chip.t-warn   { border-left-color: var(--warn);   background: var(--warn-soft); }
.week-chip.t-slate  { border-left-color: #6b7686; background: var(--surface-sunk); }
/* OPT-414: same confirmed/cancelled treatment as the day timeline (s-confirmed
   / s-cancelled on .tl-appt above) -- overrides the type-tone border colour
   since a self-service status is the more important signal here. */
.week-chip.s-confirmed { border-left-color: var(--ok) !important; }
.week-chip.s-cancelled { border-left-color: var(--danger) !important; opacity: .7; }
.week-chip.s-cancelled .wc-name { text-decoration: line-through; }

/* right panel quick search */
.qsearch { position: relative; }
.qsearch input {
  width: 100%; border: 1px solid var(--border-2); border-radius: var(--r-sm);
  padding: 9px 12px 9px 34px; font-family: var(--sans); font-size: 13.5px;
  background: var(--surface); color: var(--ink); outline: none; margin: 0;
}
.qsearch .si { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }
.qsearch input:focus { border-color: var(--accent); box-shadow: var(--ring); }
.qres { margin-top: 8px; display: flex; flex-direction: column; gap: 5px; }
.qrow {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface); cursor: pointer;
  min-height: 44px;
  transition: border-color .12s;
}
.qrow:hover { border-color: var(--accent); }
.qrow .qav {
  width: 28px; height: 28px; border-radius: 50%; background: var(--sand-soft);
  color: var(--ink-2); display: grid; place-items: center; font-size: 10.5px; font-weight: 700; flex: none;
}
.qrow .qn { font-size: 13px; font-weight: 600; color: var(--ink); }
.qrow .qd { font-size: 11px; color: var(--muted); }

/* ── Right panel section separators (OPT-150) ────────────────────────────────── */
.qsearch-section { padding: 14px 0 0; border-top: 1px solid var(--border); }
.qsearch-section:first-child { padding-top: 0; border-top: none; }
.qsearch-section-head {
  font-size: 11px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: var(--muted); margin-bottom: 10px;
}


/* ── Empty state (OPT-150) ──────────────────────────────────────────────────── */
/* OPT-817: ONE canonical .empty-state. It was defined three times at equal
   specificity (OPT-150, OPT-141, then OPT-151's exam-page copy), so the browser
   composed them by source order. These declarations ARE that composition,
   computed rather than guessed — so this changes nothing visually.

   `height: 100%` came from the exam-page copy and is the only height any of the
   three set, so it currently applies to EVERY empty state in the app, including
   ones whose parent was never given a definite height. In those it resolves to
   `auto` and is inert, which is why the leak has gone unnoticed; where a parent
   IS definite it stretches. Scoping it to the exam page is the right fix but
   needs a browser to confirm which parents are definite — deliberately left,
   see the follow-up ticket. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 40px 16px;
  text-align: center;
  color: var(--muted);
  height: 100%;
}
.empty-state-icon {
  width: 44px; height: 44px; border-radius: 50%;
  background: var(--surface-sunk); border: 1px solid var(--border);
  display: grid; place-items: center; margin-bottom: 4px;
  color: var(--faint); font-size: 20px;
}
.empty-state-title {
  font-size: 13px; font-weight: 600; color: var(--ink-2);
}
.empty-state-hint {
  font-size: 12px; color: var(--muted); line-height: 1.45;
}

/* ══════════════════════════════════════════════════════════════════════
   STOCK PAGE — 2-column layout
   ══════════════════════════════════════════════════════════════════════ */

.stk-workspace {
  flex: 1; min-height: 0; display: grid;
  grid-template-columns: 260px 1fr;
}
.stk-col { overflow-y: auto; min-height: 0; }
.stk-col.left {
  border-right: 1px solid var(--border); background: var(--surface-2);
  padding: 14px; display: flex; flex-direction: column; gap: 6px;
}
.stk-col.center { padding: 20px 24px; }

/* ── Category nav ────────────────────────────────────────────────────────────── */
.cat-head { display: flex; align-items: center; justify-content: space-between; padding: 2px 4px 6px; }
.cat-head .t { font-size: 11px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase; color: var(--muted); }
.cat-item {
  display: flex; align-items: center; gap: 10px; width: 100%; cursor: pointer; text-align: left;
  padding: 10px 11px; border-radius: var(--r); border: 1px solid transparent; background: transparent;
  color: var(--ink-2); transition: background .12s, border-color .12s; font-family: var(--sans);
}
.cat-item:hover { background: var(--surface); border-color: var(--border); }
.cat-item.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }
.cat-item .ci {
  width: 32px; height: 32px; border-radius: 9px; flex: none; display: grid; place-items: center;
  background: var(--surface-sunk); color: var(--muted); border: 1px solid var(--border);
}
.cat-item.active .ci { background: var(--accent); color: var(--on-accent); border-color: transparent; }
.cat-item .cl { flex: 1; font-size: 13.5px; font-weight: 600; }
.cat-item .cc { font-family: var(--mono); font-size: 11.5px; color: var(--faint); }
.cat-item.active .cc { color: var(--accent-ink); }

/* ── Stock center ────────────────────────────────────────────────────────────── */
.stk-title { display: flex; align-items: center; gap: 12px; margin-bottom: 14px; }
.stk-title .ti {
  width: 40px; height: 40px; border-radius: 11px; background: var(--accent-soft);
  color: var(--accent-ink); display: grid; place-items: center; border: 1px solid var(--border); flex: none;
}
.stk-title h1 { font-family: var(--serif); font-weight: 500; font-size: 24px; margin: 0; color: var(--ink); }
.stk-title .sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
/* OPT-817: the second .page-title definition lived here. Its font-size:24px was
   already dead (overridden by the later "shared" copy), and its margin/flex are
   now carried by the single canonical rule. */
.stk-title-count { font-size: 12px; color: var(--muted); }
.add-toggle-btn { margin-left: auto; flex: none; }

@keyframes slidedown {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.add-form-panel { animation: slidedown .15s ease; margin-bottom: 14px; }

.stk-search { position: relative; margin-bottom: 10px; }
.stk-search .si { position: absolute; left: 14px; top: 50%; transform: translateY(-50%); color: var(--faint); pointer-events: none; }
.stk-search input {
  width: 100%; border: 1px solid var(--border-2); border-radius: var(--r);
  padding: 11px 14px 11px 40px; font-family: var(--sans); font-size: 14px;
  background: var(--surface); color: var(--ink); outline: none; box-shadow: var(--shadow-sm); margin: 0;
}
.stk-search input:focus { border-color: var(--accent); box-shadow: var(--ring); }

.stk-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.stk-count { font-size: 12.5px; color: var(--muted); }
.stk-count strong { color: var(--ink); font-weight: 600; }
.seg { display: flex; gap: 2px; padding: 3px; background: var(--surface-sunk); border-radius: var(--r-sm); border: 1px solid var(--border); }
.seg button { border: none; background: transparent; font-family: var(--sans); font-size: 12px; font-weight: 600; color: var(--muted); padding: 4px 10px; border-radius: 5px; cursor: pointer; }
.seg button.on { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }

/* ── Stock results ───────────────────────────────────────────────────────────── */
.res-list { display: flex; flex-direction: column; gap: 8px; }

/* ── Extras add form (stock page extras tab) ─────────────────────────────────── */
.extras-add-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 13px 14px;
  box-shadow: var(--shadow-sm); margin-bottom: 14px;
}
.extras-add-head {
  font-size: 11px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: var(--muted); margin-bottom: 10px;
}
.extras-add-row { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
.extras-fg { flex: 1; min-width: 100px; margin-bottom: 0; }
.extras-fg.narrow { flex: 0 0 80px; min-width: 70px; }
.extras-add-btn { flex-shrink: 0; align-self: flex-end; }
.res {
  display: grid; grid-template-columns: 46px 1fr auto auto; gap: 14px; align-items: center;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 11px 14px; box-shadow: var(--shadow-sm); transition: border-color .12s, box-shadow .12s;
}
.res:hover { border-color: var(--border-strong); box-shadow: var(--shadow); }
.res-thumb {
  width: 46px; height: 38px; border-radius: var(--r-sm); border: 1px solid var(--border);
  display: grid; place-items: center; color: var(--faint);
  background: repeating-linear-gradient(135deg, var(--surface-sunk) 0 7px, var(--surface-2) 7px 14px);
  flex: none;
}
.res-thumb.lensglyph { background: radial-gradient(120% 100% at 50% 0, var(--accent-soft) 0, var(--surface-2) 75%); color: var(--accent); }
.res-thumb.extras-thumb { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.res .r-name { font-size: 14px; font-weight: 600; color: var(--ink); }
.res .r-brand { font-size: 12px; color: var(--muted); margin-top: 1px; }
.res .r-meta { font-size: 11.5px; color: var(--ink-2); font-family: var(--mono); margin-top: 2px; }
/* OPT-301: inline blank-sizes editor on a lens row (label + compact input) */
.res .r-blank-sizes { display: flex; align-items: center; gap: 6px; margin-top: 5px; }
.res .r-blank-sizes .r-bs-lbl {
  font-size: 10px; font-weight: 700; color: var(--muted); letter-spacing: .3px;
  text-transform: uppercase; white-space: nowrap;
}
.res .r-blank-sizes input {
  width: 100%; max-width: 160px; padding: 3px 7px; border: 1px solid var(--border);
  border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
  font-size: 11.5px; font-family: var(--mono);
}
.res .r-blank-sizes input:hover { background: var(--surface-2); border-color: var(--border); }
.res .r-blank-sizes input:focus { background: var(--surface); border-color: var(--accent); box-shadow: var(--ring); outline: none; }
.res .r-right { text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 4px; }
.res .r-price { font-family: var(--mono); font-size: 14px; font-weight: 500; color: var(--ink); }
.stock-pill { font-size: 10.5px; font-weight: 700; padding: 2px 8px; border-radius: 40px; white-space: nowrap; }
.stock-pill.in  { background: var(--ok-soft);     color: var(--ok); }
.stock-pill.low { background: var(--warn-soft);    color: var(--warn); }
.stock-pill.out { background: var(--danger-soft);  color: var(--danger); }
.stock-pill.ord { background: var(--accent-soft);  color: var(--accent-ink); }
.res .r-sku { font-family: var(--mono); font-size: 10.5px; color: var(--faint); }
.res .r-add {
  width: 30px; height: 30px; border-radius: var(--r-sm); border: 1px solid var(--border-2);
  background: var(--surface); color: var(--ink-2); cursor: pointer;
  display: grid; place-items: center; padding: 0; flex: none;
  transition: background .12s, border-color .12s, color .12s;
}
.res .r-add:hover { background: var(--accent); color: var(--on-accent); border-color: var(--accent); }

.res-load-more { display: flex; justify-content: center; padding: 14px 0 4px; }
.res-empty { text-align: center; padding: 52px 20px; color: var(--muted); }
.res-empty .ee-t { font-family: var(--serif); font-size: 18px; color: var(--ink-2); margin-bottom: 4px; }
.res-empty .ee-s { font-size: 13px; }

/* ── DNA badge ──────────────────────────────────────────────────────────── */
.dna-badge {
  display: inline-block;
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 10px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 40px;
  margin-top: 2px;
  letter-spacing: 0.3px;
}

/* ── DNA report panel ────────────────────────────────────────────────────── */
.dna-report-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 14px;
}
.dna-head {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
  margin-bottom: 8px;
}
.dna-empty {
  font-size: 12px;
  color: var(--muted);
}
.dna-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.dna-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 12px;
}
.dna-name {
  color: var(--ink);
  font-weight: 500;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  flex: 1;
}
.dna-count {
  background: var(--danger-soft);
  color: var(--danger);
  font-size: 10px;
  font-weight: 700;
  padding: 1px 6px;
  border-radius: 40px;
  flex-shrink: 0;
  margin-left: 6px;
}

/* ── Re-order view ─────────────────────────────────────────────────────── */
.reorder-controls {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
}
.reorder-lbl {
  font-size: 13px;
  color: var(--ink-2);
}
.reorder-threshold {
  width: 64px;
  padding: 4px 8px;
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink);
  font-size: 13px;
  font-family: var(--mono);
}
.reorder-section-head {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
  margin: 14px 0 6px;
}
.reorder-row {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 10px 12px;
}
.reorder-thumb {
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  flex-shrink: 0;
}
.reorder-info {
  flex: 1;
  min-width: 0;
}
.reorder-name {
  font-weight: 600;
  font-size: 13px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.reorder-meta {
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 1px;
}
.reorder-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.pick-thumb.lensglyph svg { width: 58%; color: var(--accent); }
.pick .pk-name { font-weight: 600; font-size: 13.5px; color: var(--ink); }
.pick .pk-mfr { font-size: 11.5px; color: var(--muted); }
.pick .pk-meta { font-size: 11.5px; color: var(--ink-2); margin-top: 4px; font-family: var(--mono); }
.pick .pk-price { font-family: var(--mono); font-size: 12.5px; color: var(--ink); margin-top: 2px; }

.icon-btn {
  width: 32px;
  height: 32px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--ink-2);
  display: grid;
  place-items: center;
  cursor: pointer;
  padding: 0;
  transition: background .12s, border-color .12s;
}
.icon-btn:hover { background: var(--surface-sunk); border-color: var(--border-2); }

/* ── Responsive ──────────────────────────────────────────────────────────────── */
@media (max-width: 880px) {
  .workspace { grid-template-columns: 1fr; }
  .rail { display: none; }
  .extras-rail { display: none; }
  /* OPT-322: 3rd-column sidebar stacks below .main, full-width. The
     .workspace.disp-workspace selector is needed (not plain .workspace)
     because it has higher specificity than the base 3-column rule above. */
  .workspace.disp-workspace {
    grid-template-columns: 1fr;
    display: flex;
    flex-direction: column;
    overflow-y: auto;
  }
  .disp-workspace .disp-side {
    border-left: none;
    border-top: 1px solid var(--border);
  }

  /* OPT-813: .gos1-eth-grid (the GOS1 ethnicity declaration, 5 columns of long
     UK ethnicity labels on a regulatory NHS form) is safe to override from
     here because its base rule is defined ABOVE this block. Every other grid
     this ticket fixes is defined BELOW it, so its override is co-located with
     the component instead — see the SOURCE-ORDER note at .sales-workspace. */
  .gos1-eth-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ══════════════════════════════════════════════════════════════════════
   SALES PAGE
   ══════════════════════════════════════════════════════════════════════ */

.sales-workspace {
  flex: 1; min-height: 0; display: grid;
  grid-template-columns: 280px 1fr 290px;
}
.sales-col.right {
  border-left: 1px solid var(--border); background: var(--surface-2);
  padding: 16px 14px; display: flex; flex-direction: column; gap: 14px; overflow-y: auto;
}

/* ── OPT-813: Sales narrow-screen rules ───────────────────────────────────────
   SOURCE ORDER, READ BEFORE MOVING THIS. These live HERE, immediately after the
   rules they override, and NOT in the shared @media blocks around line 3033/3413
   — because those blocks sit ABOVE `.sales-workspace`'s own definition. A media
   query adds NO specificity, so with equal specificity the LAST declaration in
   source order wins: an override written in the earlier shared block is silently
   dead, and computes to `280px 1fr 290px` at 375px exactly as if it were never
   written. That was verified by computing the winning declaration per property,
   not assumed. The same trap applies to `.ex-grid` and `.aud-grid` (both defined
   even further down) — see their own co-located blocks. Any future narrow-screen
   rule for a component defined below line 3413 belongs next to the component.

   .sales-workspace was the ONLY major page shell with no mobile handling at all:
   a literal `280px 1fr 290px`, ~570px of fixed columns against a 375px viewport.

   It follows `.disp-side`'s precedent (stack, swap the border) rather than
   `.rail`'s (hide outright): those rails are hidden because they are
   supplementary, whereas Sales' rails carry the day panel and the
   analytics/cashup/VAT/debtors/DNA stack — substantive content that must stay
   reachable. Whether a phone should instead DROP or collapse them, per the
   condensed-single-column direction, is a product call owned by OPT-822/OPT-825.
   This ticket only stops the page overflowing. */
@media (max-width: 880px) {
  .sales-workspace { grid-template-columns: 1fr; }
  .sales-col.right {
    border-left: none;
    border-top: 1px solid var(--border);
  }
}
@media (max-width: 640px) {
  /* Release the fixed-height flex clamp: at this width div.page is a scrolling
     document, so the workspace must grow with its content. */
  .sales-workspace { min-height: auto; }
  /* ...and the right rail must stop being its own scroll container, or a thumb
     swipe starting over the analytics/cashup stack never moves the page. */
  .sales-col.right { overflow-y: visible; }
}
.cashup-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 14px; display: flex; flex-direction: column; gap: 0;
}
.cu-head {
  font-size: 11px; font-weight: 700; letter-spacing: .7px; text-transform: uppercase;
  color: var(--muted); margin-bottom: 12px;
}
.cu-methods { display: flex; flex-direction: column; gap: 4px; }
.cu-row { display: flex; align-items: center; gap: 6px; padding: 5px 0;
          border-bottom: 1px solid var(--border-2); }
.cu-row.refund .cu-total { color: var(--danger); }
.cu-label { font-size: 12px; font-weight: 600; color: var(--ink); flex: 1; text-transform: capitalize; }
.cu-count { font-size: 11px; color: var(--muted); }
.cu-total { font-size: 13px; font-weight: 600; color: var(--ink); min-width: 58px; text-align: right; }
.cu-divider { font-size: 10px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted);
              margin: 10px 0 4px; }
.cu-grand { display: flex; justify-content: space-between; align-items: center;
            padding: 10px 0 0; margin-top: 6px; border-top: 2px solid var(--border); }
.cu-grand-val { font-size: 16px; font-weight: 700; color: var(--ink); }

/* ── Takings CSV export control (OPT-412) ──────────────────────────────────── */
.cu-export { margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border-2); }
.cu-export-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.cu-export-date {
  flex: 1; min-width: 0; border: 1px solid var(--border); background: var(--surface);
  border-radius: var(--r-sm); padding: 4px 6px; font-size: 11.5px; color: var(--ink);
  font-family: var(--mono);
}
.cu-export-date:disabled { opacity: .6; cursor: not-allowed; }
.cu-export-to { font-size: 11px; color: var(--muted); }
.cu-export-btn { white-space: nowrap; }
.cu-export-btn:disabled { opacity: .55; cursor: default; }
.cu-export-error { margin-top: 6px; font-size: 11.5px; color: var(--danger); }
.sales-col { overflow-y: auto; min-height: 0; }
.sales-col.left {
  border-right: 1px solid var(--border); background: var(--surface-2);
  padding: 16px 14px; display: flex; flex-direction: column; gap: 14px;
}
.sales-col.center { padding: 24px 28px; display: flex; flex-direction: column; gap: 0; }

/* ── Date navigator ────────────────────────────────────────────────────────── */
.sales-date-nav {
  display: flex; align-items: center; gap: 6px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 7px 10px; box-shadow: var(--shadow-sm);
}
.sdn-btn {
  border: none; background: transparent; color: var(--ink-2);
  cursor: pointer; font-size: 12px; padding: 3px 6px;
  border-radius: var(--r-sm); line-height: 1;
  transition: background .12s, color .12s;
}
.sdn-btn:hover { background: var(--surface-sunk); color: var(--ink); }
.sdn-input {
  flex: 1; border: none; background: transparent; font-family: var(--mono);
  font-size: 12.5px; color: var(--ink); outline: none; min-width: 0; cursor: pointer;
}
.sdn-today {
  border: 1px solid var(--border); background: var(--surface-sunk);
  color: var(--ink-2); border-radius: var(--r-sm); font-family: var(--sans);
  font-size: 11px; font-weight: 600; padding: 3px 8px; cursor: pointer;
  white-space: nowrap; transition: background .12s, border-color .12s;
}
.sdn-today:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

/* ── Day summary stats ─────────────────────────────────────────────────────── */
.sales-stats {
  display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
}
.ss-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 12px 14px; box-shadow: var(--shadow-sm);
}
.ss-val { font-family: var(--mono); font-size: 18px; font-weight: 600; color: var(--ink); }
.ss-lbl { font-size: 11px; color: var(--muted); margin-top: 2px; text-transform: uppercase; letter-spacing: .5px; font-weight: 600; }

/* ── Day sale list ─────────────────────────────────────────────────────────── */
.sales-list { display: flex; flex-direction: column; gap: 6px; }
.sale-row {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 10px 12px; box-shadow: var(--shadow-sm);
  transition: border-color .12s;
}
.sale-row:hover { border-color: var(--border-strong); }
.sr-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sr-name { font-size: 13.5px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sr-items { font-size: 11.5px; color: var(--ink-2); }
.sr-total { font-family: var(--mono); font-size: 14px; font-weight: 600; color: var(--ink); flex: none; }
.sales-empty { font-size: 13px; color: var(--muted); text-align: center; padding: 32px 0; }

/* ── Chart panel ────────────────────────────────────────────────────────────── */
.chart-head {
  display: flex; align-items: center; justify-content: space-between;
  margin-bottom: 20px;
}
.chart-title {
  font-family: var(--serif); font-weight: 500; font-size: 22px;
  color: var(--ink); margin: 0;
}
.chart-tabs {
  display: flex; gap: 2px; padding: 3px;
  background: var(--surface-sunk); border: 1px solid var(--border);
  border-radius: var(--r-sm);
}
.ctab {
  border: none; background: transparent; font-family: var(--sans);
  font-size: 12.5px; font-weight: 600; color: var(--muted);
  padding: 5px 14px; border-radius: 5px; cursor: pointer;
  transition: background .12s, color .12s, box-shadow .12s;
}
.ctab.active { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }

.chart-section { flex: 1; display: flex; flex-direction: column; gap: 14px; }
.chart-nav {
  display: flex; align-items: center; gap: 12px;
}
.cn-btn {
  border: 1px solid var(--border); background: var(--surface); color: var(--ink-2);
  border-radius: var(--r-sm); font-size: 12px; padding: 4px 10px; cursor: pointer;
  transition: background .12s, border-color .12s, color .12s;
}
.cn-btn:hover { background: var(--surface-sunk); border-color: var(--border-2); color: var(--ink); }
.cn-period { font-family: var(--serif); font-size: 20px; font-weight: 500; color: var(--ink); }

.chart-scroll {
  overflow-x: auto; overflow-y: hidden;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 20px 16px 10px;
  box-shadow: var(--shadow-sm);
  flex: 0 0 auto;
  display: flex; align-items: flex-end;
}
.sales-chart-svg { display: block; overflow: visible; width: 100%; }

/* SVG bar elements */
.sbar     { fill: var(--accent-soft); transition: fill .15s; }
.sbar-hi  { fill: var(--accent); }
.sbar:hover { fill: var(--accent-soft-2); }
.sbar-val {
  font-family: var(--mono); font-size: 9px; fill: var(--muted);
  pointer-events: none;
}
.sbar-lbl {
  font-family: var(--sans); font-size: 9px; fill: var(--muted);
  pointer-events: none;
}

/* OPT-455 sales breakdown (1D pivot) controls */
.breakdown .bd-controls {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 10px;
}
.bd-select, .bd-date {
  font-family: var(--sans); font-size: 12px; color: var(--ink);
  background: var(--surface); border: 1px solid var(--border-2);
  border-radius: 6px; padding: 4px 8px;
}
.bd-select { cursor: pointer; }
.bd-to { font-size: 12px; color: var(--muted); }

/* OPT-457 grouped multi-series chart legend */
.chart-legend {
  display: flex; flex-wrap: wrap; gap: 4px 14px; margin-bottom: 8px;
}
.leg-item { display: inline-flex; align-items: center; gap: 5px; font-size: 11px; color: var(--muted); }
.leg-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }

/* OPT-467 sales-chart y-axis scale */
.chart-grid { stroke: var(--border-2); stroke-width: 1; opacity: 0.5; }
.axis-tick  { font-family: var(--sans); font-size: 9px; fill: var(--muted); }

/* OPT-464 outside-Rx: unlinked actions, external marker, and the entry modal */
.ep-actions { display: flex; gap: 8px; flex-wrap: wrap; }
.orx-marker {
  display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
}
.orx-tag {
  font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 10px;
  background: var(--accent-soft); color: var(--accent);
}
.orx-detail { font-size: 12px; color: var(--muted); }

.orx-scrim {
  position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 200;
  display: flex; align-items: center; justify-content: center;
}
.orx-modal {
  background: var(--surface); color: var(--ink); border-radius: 12px;
  border: 1px solid var(--border-2); padding: 18px 20px; width: min(560px, 94vw);
  max-height: 90vh; overflow: auto; box-shadow: 0 12px 40px rgba(0,0,0,0.3);
}
.orx-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.orx-head h2 { margin: 0; font-size: 17px; }
.orx-x { background: none; border: none; font-size: 22px; line-height: 1; cursor: pointer; color: var(--muted); }
.orx-scan {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--border-2);
}
.orx-scan-btn { cursor: pointer; }
.orx-scan-hint { font-size: 12px; color: var(--muted); }
.orx-table { width: 100%; margin-bottom: 12px; }
.orx-in {
  width: 100%; box-sizing: border-box; font-family: var(--sans); font-size: 13px;
  padding: 4px 6px; border: 1px solid var(--border-2); border-radius: 6px;
  background: var(--surface); color: var(--ink);
}
.orx-meta { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin-bottom: 12px; }
.orx-f { display: flex; flex-direction: column; gap: 3px; font-size: 12px; color: var(--muted); }
.orx-f input {
  font-family: var(--sans); font-size: 13px; padding: 5px 8px;
  border: 1px solid var(--border-2); border-radius: 6px; background: var(--surface); color: var(--ink);
}
.orx-check { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--ink); }
.orx-err { color: var(--danger, #c0392b); font-size: 12px; margin-bottom: 10px; }
.orx-actions { display: flex; justify-content: flex-end; gap: 8px; }

/* ════════════════════════════════════════════════════════════════════
   DIARY RESPONSIVE (OPT-377, widened by OPT-378) — laptop / narrow-window
   widths, ABOVE the 640px mobile stack below. Three problems, addressed
   independently:

   1. Header control rows (.diary-head, .clin-tabs, .type-picker) are
      nowrap as of the base rules above; these breakpoints progressively
      compact padding/gaps and swap in short labels (diary_views.cljs
      .lbl-full/.lbl-short pairs) so the rows keep fitting as the window
      narrows, down to 1024px.

   2. Appointment blocks (OPT-370's one-row .a-meta-row: time · type ·
      name · actions) get progressively narrower as the center column
      shrinks. Below ~1280px there isn't room for all of time+type+name+
      actions, so type sheds first, then time — .a-patient (the name) is
      the survivor, per the ticket. The :title tooltip (OPT-369) still
      carries the full time/type/name for anyone who needs it.

   3. OPT-378: .pd-workspace's side columns (left patient panel, right
      quick-search) were fixed at 340px/300px until ≤1024px, so at
      1280-1440px the center diary column stayed squeezed no matter how
      much the header/appt-block compaction above helped — a long patient
      name ("Priya Ramachandran") still ellipsized in a 15-min block at
      1280x720. Side columns now start narrowing at ≤1440px and get
      substantially narrower at ≤1280px (field-row grids collapse to a
      single column there so labels/inputs stay legible instead of
      crowding two columns into ~100px each). The diary's own chrome
      (time-gutter column, appt-block padding/inset, clin-tab/type-chip
      "pill" padding) tightens alongside the side columns at 1280/1024 so
      every freed pixel reaches the grid rather than sitting in gutter.

   4. Below 1024px, .pd-col.center also gets a horizontal-scroll floor
      (.timeline/.week-grid min-width) instead of continuing to squeeze
      the grid — a squeezed time column and appt row is how blocks end up
      too thin to show any text at all. Scrolling is internal to the
      center column (overflow-x on .pd-col.center), not the page — OPT-366's
      vertical-fit math (slot-h from available height) is untouched by
      any of this, since none of it changes .diary-grid-frame's height.
   ════════════════════════════════════════════════════════════════════ */

@media (max-width: 1440px) {
  .diary-head  { gap: 8px; }
  .clin-tabs   { gap: 5px; }
  .clin-tab    { padding: 4px 8px 4px 4px; }
  .type-picker { gap: 5px; }

  /* OPT-378: side columns start narrowing here already — waiting until
     ≤1024px (as OPT-377 did) left the 1280-1440px band squeezed even
     though the header itself fit. 340/300 → 300/260, +80px to the grid. */
  .pd-workspace { grid-template-columns: 300px 1fr 260px; }
}

@media (max-width: 1280px) {
  .diary-title { font-size: 18px; }
  .view-toggle .vtog { padding: 4px 8px; }
  .dnav-btn { min-width: 24px; padding: 0 4px; }
  .date-nav { gap: 3px; }
  .date-nav input[type=date] { padding: 0 6px; font-size: 12px; height: 27px; }

  /* short labels: "Print day sheet" → "Print", "Reminders" → "Remind" */
  .diary-head .lbl-full  { display: none; }
  .diary-head .lbl-short { display: inline; }

  /* appt block: shed the type first (with its separator) — name survives */
  .tl-appt .a-type,
  .tl-appt .a-meta-sep { display: none; }

  /* OPT-378: side columns substantially narrower (300/260 → 220/200) —
     field-row grids give up 2/3-across layout here rather than waiting
     for ≤1024px, or labels/inputs in both side panels crowd into
     illegibility at this width. */
  .pd-workspace { grid-template-columns: 220px 1fr 200px; }
  .pd-col.left .frow.c2, .pd-col.left .frow.c3,
  .pd-col.right .frow.c2 { grid-template-columns: 1fr; }
  .pd-pad { padding: 12px; gap: 10px; }
  .fg-field .lbl { font-size: 10px; }

  /* OPT-378: tighten the diary's own chrome so the width reclaimed from
     the side columns actually reaches the grid — narrower time-gutter
     column, tighter appt-block padding/inset, tighter pill-row padding. */
  .tl-row         { grid-template-columns: 46px 1fr; }
  .tl-time        { padding-left: 6px; }
  .tl-appt-layer  { left: 46px; }
  .tl-block       { left: 40px; }
  .tl-appt        { left: 5px; right: 6px; padding: 6px 7px; }
  .tl-appt .a-meta-row { gap: 4px; }
  .clin-tab       { padding: 4px 6px 4px 4px; }
  .type-chip      { padding: 3px 7px; }
}

@media (max-width: 1024px) {
  /* OPT-378: one more notch narrower (220/200 → 190/170) — the appt
     progressive-disclosure below (type already gone, time going next)
     means the center column needs less headroom per-block, but the side
     columns still give up more space than they did pre-OPT-378. */
  .pd-workspace { grid-template-columns: 190px 1fr 170px; }
  /* narrower side columns need the field-row grids to give up 2/3-across
     layout sooner than the shared ≤640px mobile breakpoint below, or
     labels/inputs in the side panels crowd into illegibility. (Already
     single-column from ≤1280px above; restated here as a floor.) */
  .pd-col.left .frow.c2,
  .pd-col.left .frow.c3,
  .pd-col.right .frow.c2 { grid-template-columns: 1fr; }

  .clin-tab      { padding: 4px 5px; gap: 3px; }
  .clin-tab .cn  { display: none; } /* avatar + count only; :title carries the name */
  .type-chip     { padding: 3px 6px; }

  /* appt block: also shed the time — only name + actions remain */
  .tl-appt .a-time { display: none; }

  /* OPT-378: gutter/padding tighten one more notch at the narrowest floor */
  .tl-row        { grid-template-columns: 40px 1fr; }
  .tl-time       { padding-left: 4px; font-size: 10px; }
  .tl-appt-layer { left: 40px; }
  .tl-block      { left: 34px; }
  .tl-appt       { padding: 5px 6px; }

  /* width floor for the day/week grid: below this the CENTER column
     scrolls horizontally instead of continuing to squeeze the time
     column and appt row into unreadable slivers. Vertical fit (slot-h)
     is unaffected — it's derived from height, not width (OPT-366). */
  .pd-col.center { overflow-x: auto; }
  .timeline      { min-width: 420px; }
  .week-grid     { min-width: 620px; }

  /* ── OPT-814: stop the nav clipping SILENTLY between 641px and desktop ────
     The nav had exactly two states — the full desktop bar, and the ≤640px
     bottom tab bar — with nothing between, even though this file already has
     880/1024/1280/1440 steps for the diary and other pages. On a tablet that
     leaves up to sixteen page buttons plus brand, global search, notif bell,
     device chip and the user block in one row that is `flex` with NO
     `flex-wrap` and NO `overflow-x`, inside `div.page { overflow: hidden }`
     and marked `flex-shrink: 0`. The overflow is therefore CLIPPED with no
     scrollbar and no other affordance: the buttons are not merely cramped,
     they are unreachable and there is nothing on screen to suggest they exist.

     Making the link row scroll is the honest minimum — it trades a silent
     failure for a visible one. It is NOT the endgame: OPT-824 carries the
     actual fix (a small primary set plus a More/overflow sheet), because
     sixteen destinations cannot fit a phone at any touch-target size worth
     having. Deliberately scoped to `.nav-links` and not `div.nav` itself, so
     the brand/search/bell/chip keep their fixed positions and only the page
     links scroll. */
  div.nav-links {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  div.nav-links::-webkit-scrollbar { display: none; }
  button.nav { flex-shrink: 0; }   /* or they compress instead of scrolling */
}

/* ════════════════════════════════════════════════════════════════════
   MOBILE  (≤ 640 px)
   Pure-CSS transform of the desktop layout — no new JS components.
   ════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {

  /* ── Admin page: tighter gutters so the scrollable tab row fits more,
     and stack the two-column grid so it can't force a 650px min width ── */
  div.admin { padding: 16px 12px; }
  .admin-tab { padding: 10px 12px; }
  /* OPT-617: at this width a tall wrapped stack of ten tabs costs more
     vertical space than it saves, so keep the single scrollable row. */
  .admin-tabs { flex-wrap: nowrap; }
  .admin-cols { grid-template-columns: 1fr; }
  /* the diary-rules table has a fixed min-content width — scroll it in place */
  .diary-rules-section { overflow-x: auto; }

  /* ── Page shell: scrollable document instead of fixed viewport ── */
  div.page {
    height: auto;
    min-height: 100dvh;
    overflow-y: auto;
    /* OPT-839: clear the tab bar AND the always-present floating pills. 62px
       cleared only the bar, so the device chip (bottom-right) and account chip
       (bottom-left) sat ON TOP of the last rows of every page — a table row
       read as "n Frankel" with the name hidden behind the avatar, and it was
       untappable as well as unreadable. Found by screenshotting the running app
       at 375px; the overflow measurements reported these pages CLEAN, because
       an overlay does not widen the document. */
    padding-bottom: var(--fw-clearance);
  }

  /* ── Bottom tab bar ─────────────────────────────────────────────── */
  div.nav {
    position: fixed;
    bottom: 0; left: 0; right: 0; top: auto;
    height: 62px;
    padding: 0; gap: 0;
    border-top: 1px solid var(--border);
    border-bottom: none;
    z-index: 300;
    background: var(--surface);
    justify-content: stretch;
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
  }
  /* hide everything in the nav except the page-link buttons — and the
     device chip + notif bell, which float above the tab bar (session
     transfers / today's outstanding items must stay one tap away on
     phones: scan-tag / lens-options hand-offs, payment-collection chases) */
  div.nav > *:not(.nav-links):not(.device-chip):not(.notif-bell):not(.account-chip) { display: none !important; }

  /* ── OPT-812: account chip — floating pill, bottom LEFT ──────────────────
     The right edge is already a stack: device chip at 74px, notif bell at
     130px, and the bell only renders when it has something to flag. A third
     right-edge pill would collide or hover over a gap depending on state, so
     this takes the free left edge instead — which is also the conventional
     place for an account control.

     This is the ONLY route to sign out at this width: the rule immediately
     above hides .nav-user, which holds the desktop sign-out. Do not drop
     .account-chip from that whitelist without providing another one. */
  .account-chip {
    display: block;
    position: fixed;
    left: 12px;
    bottom: var(--fw-slot-0);   /* ladder slot 0 — see :root */
    z-index: 310;
  }
  .account-chip-btn {
    min-height: 44px;         /* touch target */
    min-width: 44px;
    display: grid;
    place-items: center;
    padding: 6px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border-2);
    box-shadow: 0 6px 20px -6px rgba(58,49,36,.35);
  }
  /* .av is otherwise only styled as `.chip-user .av`, so it needs its own rule
     here or the initial renders as bare text */
  .account-chip-btn .av {
    width: 30px;
    height: 30px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--on-accent);
    display: grid;
    place-items: center;
    font-size: 13px;
    font-weight: 600;
  }
  /* opens UPWARD and anchored LEFT — mirrors the device chip's popover, which
     opens upward but anchors right */
  .account-popover {
    position: absolute;
    top: auto;
    bottom: calc(100% + 8px);
    left: 0;
    z-index: 310;
    width: min(220px, calc(100vw - 24px));
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md, 10px);
    box-shadow: 0 12px 32px rgba(0,0,0,.18);
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 10px;
    text-align: left;
  }
  .account-popover-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--ink-2);
    padding-bottom: 8px;
    border-bottom: 1px solid var(--border);
  }
  /* the popover's own controls are touch targets too — .btn.sm is ~24px */
  .account-popover .btn { min-height: 44px; }

  /* ── Device chip: floating pill above the bottom tab bar ── */
  .device-chip {
    position: fixed;
    right: 12px;
    bottom: var(--fw-slot-0);   /* ladder slot 0 — see :root */
    z-index: 310;
  }
  .device-chip-btn {
    min-height: 44px;         /* touch target */
    padding: 10px 16px;
    border-radius: 999px;
    background: var(--surface);
    border: 1px solid var(--border-2);
    box-shadow: 0 6px 20px -6px rgba(58,49,36,.35);
    font-size: 13px;
  }
  /* popover + toasts open UPWARD from the floating chip */
  .device-chip-popover,
  .transfer-toast-list {
    top: auto;
    bottom: calc(100% + 8px);
  }
  .device-chip-popover {
    max-height: 60vh;
    width: min(300px, calc(100vw - 24px));
  }

  /* ── OPT-400: notif bell — floating pill stacked ABOVE the device chip
     (rather than beside it) so it never overlaps regardless of how long the
     device name is; also opens upward, same as the chip's popover ── */
  /* OPT-827: TOP of the screen, not floating above the tab bar. It was placed
     in the bottom ladder with the device chip on the reasoning that outstanding
     items should be one thumb-tap away; seen in the running app that reads as
     clutter competing with the tab bar, and a to-do count is something you
     GLANCE at rather than reach for. Nothing to tuck under at this width —
     div.nav has become the bottom tab bar, so the top edge is free. */
  .notif-bell {
    position: fixed;
    right: 12px;
    top: 12px;
    bottom: auto;
    z-index: 310;
  }
  .notif-bell-btn { min-height: 44px; min-width: 44px; justify-content: center; padding: 8px; }
  /* OPT-827: opens DOWNWARD again now the bell is top-anchored. The upward flip
     existed only because the bell used to sit near the bottom of the screen;
     kept as-is it would open off the top edge and be unreachable. */
  .notif-popover {
    top: calc(100% + 8px);
    bottom: auto;
    max-height: 60vh;
    width: min(300px, calc(100vw - 24px));
  }

  div.nav-links {
    flex: 1;
    margin: 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    justify-content: stretch;
    scrollbar-width: none;
  }
  div.nav-links::-webkit-scrollbar { display: none; }

  button.nav {
    flex: 1;
    min-width: 54px;
    height: 62px;
    padding: 6px 4px;
    border-radius: 0;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .2px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    touch-action: manipulation;
  }
  button.nav.nav-active {
    background: var(--accent-soft);
    color: var(--accent-ink);
    border-radius: 0;
  }
  /* dot indicator on active tab */
  button.nav.nav-active::before {
    content: '';
    position: absolute;
    top: 0; left: 50%; transform: translateX(-50%);
    width: 24px; height: 2.5px;
    background: var(--accent);
    border-radius: 0 0 3px 3px;
  }
  button.nav { position: relative; }

  div.diaries {
    grid-template-columns: 1fr;
    max-height: none;
    overflow: visible;
  }
  div.diaries > div { max-height: 280px; overflow-y: auto; }

  /* ── Diary / Exam 3-col layout → stacked ───────────────────────── */
  .pd-workspace {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
    height: auto;
    min-height: 0;
    overflow: visible;
    flex: none;
  }
  .pd-col {
    overflow: visible;
    min-height: 0;
    height: auto;
  }
  .pd-col.left {
    border-right: none;
    border-bottom: 1px solid var(--border);
    /* compact patient panel — limit height and allow internal scroll */
    max-height: 280px;
    overflow-y: auto;
  }
  .pd-col.right {
    border-left: none;
    border-top: 1px solid var(--border);
  }
  .pd-col.center { padding: 14px 12px; }
  .pd-pad { padding: 12px; gap: 10px; }

  /* ── Dispense workspace (2-col → stacked) ───────────────────────── */
  .workspace {
    display: flex;
    flex-direction: column;
    height: auto;
    overflow: visible;
    grid-template-columns: none;
    flex: none;
  }
  .extras-rail { display: none; }
  /* show the rail again (hidden by the 880px rule) as compact strip */
  .rail {
    display: flex !important;
    flex-direction: column;
    max-height: 220px;
    overflow-y: auto;
    border-right: none;
    border-bottom: 1px solid var(--border);
    padding: 10px 12px;
    gap: 10px;
    scrollbar-width: thin;
  }
  /* hide verbose patient card in dispense rail — already in diary panel */
  .rail .card.patient { display: none; }
  /* OPT-322: 3rd-column sidebar stacks below .main, full-width, flowing with
     the page's own scroll (div.page becomes the scrollable document at this
     breakpoint — see above) rather than its own internal scroll region. */
  .workspace.disp-workspace {
    display: flex;
    flex-direction: column;
    height: auto;
    overflow: visible;
    grid-template-columns: none;
    flex: none;
  }
  .disp-workspace .disp-side {
    border-left: none;
    border-top: 1px solid var(--border);
    max-height: none;
    overflow-y: visible;
    padding: 12px 12px;
  }
  /* dispense items: horizontal scroll row */
  .hist-list {
    display: flex;
    flex-direction: row;
    gap: 8px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-bottom: 2px;
  }
  .hist-list::-webkit-scrollbar { display: none; }
  button.hist { flex: 0 0 auto; min-width: 130px; }
  .main { padding: 14px 12px; }

  /* ── Stock page (2-col → stacked) ──────────────────────────────── */
  .stk-workspace {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
    height: auto;
    overflow: visible;
    flex: none;
  }
  .stk-col.left {
    border-right: none;
    border-bottom: 1px solid var(--border);
    flex-direction: row;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    padding: 10px 12px;
    gap: 6px;
    scrollbar-width: none;
  }
  .stk-col.left::-webkit-scrollbar { display: none; }
  .cat-head { display: none; }            /* hide "Categories" label */
  .cat-item {
    flex: 0 0 auto;
    width: auto;
    padding: 8px 14px;
  }
  .cat-item .ci { display: none; }        /* hide icon in compact bar */
  .cat-item .cl { font-size: 13px; }
  .stk-col.center { padding: 14px 12px; }
  .stk-title { margin-bottom: 10px; }


  /* ── Field grid rows → single column ───────────────────────────── */
  .frow.c2,
  .frow.c3 { grid-template-columns: 1fr; }

  /* ── Rx table — compact ─────────────────────────────────────────── */
  table.rx { font-size: 11px; }
  table.rx th, table.rx td { padding: 1px; }
  table.rx thead th { font-size: 9.5px; }
  .rx-cell .editable { padding: 2px; }
  .rx-cell .editable input { font-size: 12px; }

  /* ── Dispense header bar (row → stacked) ──────────────────────── */
  .disp-header { flex-direction: column; align-items: flex-start; gap: 8px; }
  .disp-header-coll { width: 100%; }

  /* ── Login page ─────────────────────────────────────────────────── */
  .login-page { padding: 20px 16px; }
  .login-form { max-width: none; }

  /* ── Prevent iOS font-size zoom on input focus ──────────────────── */
  .fg-field input,
  .fg-field select,
  .fg-field textarea,
  input, select, textarea { font-size: 16px !important; }

  /* ── Touch targets: minimum 44 px ──────────────────────────────── */
  /* OPT-814: this said 44 and set 40. Now it sets what it says — 44px is the
     figure both Apple's and Google's guidance land on, and the comment has
     claimed it since the block was written. */
  .btn, .btn.sm {
    min-height: 44px;
    padding: 8px 14px;
    font-size: 13px;
  }
  button.appt { min-height: 44px; font-size: 12px; }
  .qs-val { font-size: 14px; min-width: 32px; }

  /* OPT-814: diary appointment actions (arrived / DNA / delete) are 20x20 in
     `.tl-appt .a-act-btn` — tapped constantly during clinic, on the most
     touch-heavy page in the app. Matched at the base rule's own specificity
     (0,2,0); a bare `.a-act-btn` selector would lose to it and silently do
     nothing. The glyph stays small — only the hit area grows. Confined to
     ≤640px on purpose: at tablet widths the appointment card is dense and
     three 44px targets in a row would reflow it, which is a visual-regression
     risk this ticket has no way to check. */
  .tl-appt .a-act-btn { min-width: 44px; min-height: 44px; }

  /* ── Stock result rows ──────────────────────────────────────────── */
  .res { padding: 10px 12px; }
  .r-right { flex-direction: column; align-items: flex-end; gap: 5px; }

  /* ── DB item rows ───────────────────────────────────────────────── */
  .db-item { flex-wrap: wrap; gap: 8px; }
  .di-right { width: 100%; justify-content: flex-end; }

  /* ── Appointment slots ──────────────────────────────────────────── */
  .clin-tabs { gap: 5px; }
  .clin-tab { padding: 5px 9px 5px 5px; }
  .clin-tab .cav { width: 22px; height: 22px; font-size: 10px; }

  /* ── Section headers ────────────────────────────────────────────── */
  .section-head { gap: 8px; }
  .section { margin-bottom: 14px; }
  .section-row { grid-template-columns: 1fr; }

  /* ── Patient panel compact view ─────────────────────────────────── */
  .px-head .pav { width: 40px; height: 40px; font-size: 17px; border-radius: 11px; }
  .px-head .px-name { font-size: 17px; }

  /* ── Diary timeline ─────────────────────────────────────────────── */
  .diary-head { flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
  .diary-title { font-size: 18px; }
  .date-nav input[type=date] { font-size: 14px; }

  /* OPT-813: see the 880px block above — only .gos1-eth-grid is defined early
     enough to be overridden from here. */
  .gos1-eth-grid { grid-template-columns: 1fr; gap: 10px; }
}

/* ── Dispense total / extras sections ────────────────────────────────────────── */
.total-breakdown { display: flex; flex-direction: column; gap: 6px; }
.total-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 5px 8px; border-radius: var(--r-sm); font-size: 13px; color: var(--ink-2);
}
.total-row:hover { background: var(--surface-sunk); }
.total-calc {
  border-top: 1px solid var(--border); margin-top: 4px; padding-top: 9px;
  font-weight: 600; color: var(--ink);
}
.total-charged {
  border-top: 1px dashed var(--border-2); margin-top: 2px; padding-top: 8px;
  color: var(--accent-ink); font-weight: 600;
}
.charged-row {
  background: var(--accent-soft);
  border-radius: var(--r-sm);
  padding: 8px 12px;
}
.total-charged input {
  width: 100px; text-align: right; font-family: var(--mono); font-size: 13px;
  font-weight: 600; color: var(--accent-ink); background: var(--accent-soft);
  border: 1px solid transparent; border-radius: var(--r-sm); padding: 3px 7px;
  outline: none; margin: 0; transition: border-color .12s, box-shadow .12s;
}
.total-charged input:focus { border-color: var(--accent); box-shadow: var(--ring); }

/* OPT-325: compact dispense header bar (dispensed + collection dates), pinned
   above jobs-section on the dispense page. Replaces the old full-section
   "Extras & Dates" card — a slim row, not a `.section`/`.card` composite. */
.disp-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 24px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  padding: 10px 16px;
  margin-bottom: 16px;
}
.disp-header-item { display: flex; align-items: baseline; gap: 8px; }
.disp-header-lbl {
  font-size: 10.5px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .4px;
}
.disp-header-val { font-size: 13px; color: var(--ink-2); font-family: var(--mono); }
.disp-header-coll { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* ══════════════════════════════════════════════════════════════════════
   EXAM PAGE — field extensions
   ══════════════════════════════════════════════════════════════════════ */

/* textarea and select inside fg-field cards — match input styling */
.fg-field textarea,
.fg-field select {
  background: var(--surface-sunk); border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 6px 8px; font-size: 12.5px; color: var(--ink); font-family: var(--sans);
  outline: none; margin: 0; width: 100%;
  transition: border-color .12s, background .12s, box-shadow .12s;
}
.fg-field textarea:hover,
.fg-field select:hover { background: var(--surface-2); border-color: var(--border); }
.fg-field textarea:focus,
.fg-field select:focus { background: var(--surface); border-color: var(--accent); box-shadow: var(--ring); }
.fg-field textarea { resize: vertical; min-height: 64px; line-height: 1.45; }

/* ── Sight test list (right panel) ──────────────────────────────────────────── */
.st-item {
  display: flex; align-items: center; padding: 8px 10px;
  border: 1px solid var(--border); border-radius: var(--r); background: var(--surface);
  cursor: pointer; text-align: left; font-family: var(--sans);
  transition: border-color .12s, box-shadow .12s;
}
.st-item:hover { border-color: var(--border-strong); box-shadow: var(--shadow-sm); }
.st-item.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: var(--accent-soft); }
.st-date { font-family: var(--mono); font-size: 12.5px; color: var(--ink-2); font-weight: 500; }
.st-item.active .st-date { color: var(--accent-ink); }

/* ── FOH "Other" free-text input ────────────────────────────────────────────── */
input.foh-other-input {
  margin-top: 7px; width: 100%;
  background: var(--surface-sunk); border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 6px 9px; font-size: 12.5px; color: var(--ink); font-family: var(--sans);
  outline: none; transition: border-color .12s, box-shadow .12s;
}
input.foh-other-input:focus { border-color: var(--accent); box-shadow: var(--ring); background: var(--surface); }

/* ── Rx subsection (worn / objective / given) ────────────────────────────────── */
.rx-sub { margin-bottom: 10px; }
.rx-sub:last-of-type { margin-bottom: 0; }
.rx-sub-head {
  display: block; font-size: 10.5px; font-weight: 700; color: var(--muted);
  letter-spacing: .4px; text-transform: uppercase; margin-bottom: 5px;
}

/* ── Worn-Rx drag-and-drop (OPT-365) ─────────────────────────────────────────── */
.worn-rx-chip-row { display: flex; flex-wrap: wrap; gap: 5px; margin: 2px 0 4px; }
.worn-rx-chip {
  font-size: 11px; font-weight: 500; padding: 3px 9px; border-radius: 40px;
  border: 1px dashed var(--border-2); background: var(--surface); color: var(--ink-2);
  cursor: grab; font-family: var(--sans); white-space: nowrap;
  transition: background .1s, border-color .1s, color .1s;
}
.worn-rx-chip:hover { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }
.worn-rx-chip:active { cursor: grabbing; }
.worn-rx-chip-locked { opacity: .5; cursor: not-allowed; border-style: solid; }
.worn-rx-chip-locked:hover { background: var(--surface); border-color: var(--border-2); color: var(--ink-2); }
.worn-rx-drop-zone {
  border-radius: var(--r-sm); border: 1px dashed transparent; padding: 4px;
  margin: -4px; transition: background .12s, border-color .12s;
}
.worn-rx-drop-over {
  background: var(--accent-soft); border-color: var(--accent);
  box-shadow: var(--ring);
}

/* ── Multiselect pills (FOH etc.) ────────────────────────────────────────────── */
.ms-pills { display: flex; flex-wrap: wrap; gap: 5px; }
.ms-pill {
  font-size: 11.5px; font-weight: 500; padding: 4px 10px; border-radius: 40px;
  border: 1px solid var(--border-2); background: var(--surface); color: var(--ink-2);
  cursor: pointer; font-family: var(--sans);
  transition: background .1s, border-color .1s, color .1s;
}
.ms-pill:hover { background: var(--surface-sunk); border-color: var(--border-strong); }
.ms-pill.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }

/* ── Chip selector (OPT-133) ─────────────────────────────────────────────────── */
.chip-group { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 6px; }
.chip {
  font-size: 11.5px; font-weight: 500; padding: 4px 10px; border-radius: 40px;
  border: 1px solid var(--border-2); background: var(--surface); color: var(--ink-2);
  cursor: pointer; font-family: var(--sans);
  transition: background .1s, border-color .1s, color .1s;
}
.chip:hover:not(:disabled) { background: var(--surface-sunk); border-color: var(--border-strong); }
.chip.active { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
.chip:disabled { opacity: .6; cursor: default; }
.chip-add { display: flex; gap: 5px; margin-top: 4px; }
.chip-add input {
  flex: 1; font-size: 12px; padding: 4px 8px; border-radius: var(--r-sm);
  border: 1px dashed var(--border-2); background: var(--surface-sunk); color: var(--ink-1);
  font-family: var(--sans);
}
.chip-add input:focus { outline: none; border-color: var(--accent); background: var(--surface); }
.chip-add-btn {
  font-size: 14px; font-weight: 700; padding: 2px 10px; border-radius: 40px;
  border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent-ink);
  cursor: pointer;
}
.chip-add-btn:hover { background: var(--accent); color: var(--on-accent); }
.hx-legacy { margin-top: 8px; }
.hx-legacy summary { user-select: none; }

/* ── Vocab combobox (OPT-344) — manufacturer/brand dropdown + add-new ───────── */
.vocab-combo-wrap { position: relative; }
.vocab-combo-dropdown {
  position: absolute; top: calc(100% + 3px); left: 0; right: 0; z-index: 40;
  max-height: 190px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-sm);
  box-shadow: var(--shadow); padding: 4px;
}
.vocab-combo-option {
  font-size: 12.5px; padding: 6px 8px; border-radius: var(--r-sm);
  color: var(--ink-2); cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.vocab-combo-option:hover,
.vocab-combo-option.hi { background: var(--accent-soft); color: var(--accent-ink); }
.vocab-combo-option.vocab-combo-add {
  color: var(--accent-ink); font-weight: 600; border-top: 1px solid var(--border); margin-top: 3px; padding-top: 7px;
}

/* ── Yes / No boolean toggle ─────────────────────────────────────────────────── */
.yn-toggle {
  font-size: 12.5px; font-weight: 600; padding: 5px 14px; border-radius: var(--r-sm);
  border: 1px solid var(--border-2); background: var(--surface); color: var(--ink-2);
  cursor: pointer; font-family: var(--sans);
  transition: background .1s, border-color .1s, color .1s;
}
.yn-toggle:hover { background: var(--surface-sunk); border-color: var(--border-strong); }
.yn-toggle.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-ink); }

/* ── Empty state (no patient / no sight test) ────────────────────────────────── */
.ex-empty {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  height: 100%; color: var(--muted); font-size: 13.5px; text-align: center; gap: 8px;
}

/* ── Unified empty-state component (OPT-141) ─────────────────────────────────── */
/* OPT-817: second .empty-state definition removed — folded into the canonical
   rule above (its padding was already overridden). */
.empty-state .es-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--faint);
  color: var(--muted);
  font-size: 20px;
  margin-bottom: 4px;
  flex-shrink: 0;
}
.empty-state .es-heading {
  font-family: var(--sans);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-2);
  margin: 0;
}
.empty-state .es-hint {
  font-family: var(--sans);
  font-size: 12.5px;
  color: var(--muted);
  margin: 0;
}

/* ── Exam center column (no-scroll host) ─────────────────────────────────────── */
.pd-col.ex-col { overflow: hidden; padding: 0; display: flex; flex-direction: column; }

.ex-center {
  flex: 1; display: flex; flex-direction: column;
  overflow: hidden; padding: 18px 20px; gap: 12px; min-height: 0;
}

.ex-head { display: flex; align-items: baseline; gap: 10px; flex-shrink: 0; }
.ex-title { font-family: var(--serif); font-size: 22px; font-weight: 500; color: var(--ink); }
.ex-date { font-size: 12px; color: var(--muted); font-family: var(--mono); }
.ex-head .btn { margin-left: auto; }

/* ── Section card grid ───────────────────────────────────────────────────────── */
.ex-grid {
  flex: 1; display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 1fr 1fr 1fr;
  gap: 8px; min-height: 0;
}
.ex-grid > .ex-card:last-child { grid-column: 1 / -1; }

/* ── OPT-813: exam section-card grid on narrow screens ────────────────────────
   Co-located deliberately — `.ex-grid` is defined well BELOW the shared @media
   blocks, so an override written up there is dead on arrival. See the
   source-order note at `.sales-workspace`.

   `.pd-col.center` (this grid's container) is already stacked at 640px, but the
   grid INSIDE it never was: three clinical section cards at ~100px each on a
   375px phone.

   `grid-template-rows: none` matters as much as the column count. The base rule
   pins three equal rows to fill a viewport-height page; once div.page becomes a
   scrolling document the rows must size to their content, or the cards are
   squashed into equal slices of a height that no longer means anything.
   `min-height: auto` releases the matching flex clamp. */
@media (max-width: 880px) {
  .ex-grid {
    grid-template-columns: repeat(2, 1fr);
    grid-template-rows: none;
  }
}
@media (max-width: 640px) {
  .ex-grid {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    min-height: auto;
  }
}

.ex-card {
  display: flex; flex-direction: column; gap: 5px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 12px 13px;
  box-shadow: var(--shadow-sm); cursor: pointer; text-align: left;
  transition: border-color .12s, box-shadow .12s, background .1s;
  min-height: 0; overflow: hidden; font-family: var(--sans);
}
.ex-card:hover {
  border-color: var(--border-strong);
  box-shadow: var(--shadow);
  background: var(--surface-2);
}

.ex-card-top { display: flex; align-items: center; justify-content: space-between; }
.ex-card-icon {
  width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 700; letter-spacing: .2px;
}
.ex-card-dot { width: 7px; height: 7px; border-radius: 50%; }

.ex-card-label {
  font-size: 12px; font-weight: 700; color: var(--ink); line-height: 1.2;
}
.ex-card-summary {
  font-size: 11.5px; color: var(--muted); line-height: 1.35;
  overflow: hidden; display: -webkit-box;
  -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}

/* ── Section overlay ─────────────────────────────────────────────────────────── */
.ex-scrim {
  position: fixed; inset: 0;
  background: rgba(30,26,20,.46);
  display: grid; place-items: center;
  z-index: 80; padding: 24px;
  animation: fade .14s ease;
}
[data-theme="dark"] .ex-scrim { background: rgba(0,0,0,.65); }

.ex-overlay {
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: var(--r-xl);
  box-shadow: var(--shadow-lg);
  width: min(720px, 100%);
  max-height: 86vh;
  display: flex; flex-direction: column;
  overflow: hidden;
  animation: pop .16s ease;
}

.ex-ov-head {
  display: flex; align-items: center; gap: 10px;
  padding: 13px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.ex-ov-icon {
  width: 30px; height: 30px; border-radius: 8px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 700;
}
.ex-ov-title {
  font-family: var(--serif); font-size: 17px; font-weight: 500;
  color: var(--ink); margin: 0; flex: 1;
}
.ex-ov-close {
  width: 26px; height: 26px; border-radius: var(--r-sm); flex-shrink: 0;
  border: 1px solid var(--border-2); background: var(--surface-2);
  color: var(--muted); font-size: 17px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background .1s, color .1s;
}
.ex-ov-close:hover { background: var(--surface-sunk); color: var(--ink); }

.ex-ov-body {
  overflow-y: auto; padding: 16px 18px;
  display: flex; flex-direction: column; gap: 0;
}

/* ── DB page panels ──────────────────────────────────────────────────────────── */
.db-panel {
  flex: 1; min-width: 0; overflow-y: auto; max-height: 100%;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg); padding: 14px;
}
.db-list { margin-top: 12px; display: flex; flex-direction: column; gap: 6px; }
.db-item {
  display: flex; align-items: center; gap: 10px; padding: 8px 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r-sm);
}
.di-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.di-name { font-size: 13px; font-weight: 600; color: var(--ink); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.di-meta { font-size: 11.5px; color: var(--muted); }
.di-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.di-price { font-size: 12px; font-weight: 600; color: var(--ink-2); }
.di-del {
  font-size: 15px; line-height: 1; background: none; border: none; cursor: pointer;
  color: var(--muted); padding: 2px 6px; border-radius: var(--r-sm);
}
.di-del:hover { color: var(--danger); background: var(--danger-soft); }

/* ── Stock quantity spinbox ───────────────────────────────────────────────────── */
.qty-spin { display: flex; align-items: center; gap: 2px; }
.qty-spin.sm .qs-val { min-width: 22px; font-size: 12px; }
.qty-spin.sm .qs-btn { width: 22px; height: 22px; font-size: 14px; }
.qs-btn {
  width: 26px; height: 26px; border-radius: var(--r-sm); border: 1px solid var(--border-2);
  background: var(--surface); color: var(--ink-2); font-size: 16px; line-height: 1;
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  transition: background .1s, border-color .1s;
}
.qs-btn:hover { background: var(--surface-sunk); border-color: var(--border-strong); }

/* ── OPT-814: quantity steppers on touch ──────────────────────────────────────
   Co-located, and this one is worth explaining because the previous attempt at
   it silently did nothing. The shared ≤640px block used to carry
   `.qs-btn { width: 36px; height: 36px }` — but it sits ABOVE the base rule
   here, and a media query adds no specificity, so at equal specificity the
   LATER declaration wins and these buttons computed to 26px on a phone, not
   36px. The mobile rule had never taken effect. It has been removed from that
   block and rewritten here, where it can actually win.

   `.qty-spin.sm .qs-btn` (specificity 0,3,0) must be named explicitly too: a
   bare `.qs-btn` override (0,1,0) loses to it regardless of order, so the small
   variant would otherwise stay at 22px. */
@media (max-width: 640px) {
  .qs-btn,
  .qty-spin.sm .qs-btn {
    width: 44px;
    height: 44px;
    font-size: 18px;
  }
}
.qs-val {
  min-width: 28px; text-align: center; font-size: 12.5px; font-weight: 600;
  color: var(--ink); font-family: var(--mono);
}

/* ── Page title (shared) ─────────────────────────────────────────────────────── */
/* OPT-817: the third .page-title definition lived here, duplicating the canonical
   one verbatim except for line-height. Folded into that single rule. */

/* ── Compatibility matrix page ───────────────────────────────────────────────── */
.compat-page {
  display: flex; flex-direction: column; flex: 1;
  padding: 20px 24px; gap: 16px; background: var(--paper-2);
  overflow: hidden;
}
.compat-header { flex-shrink: 0; }
.compat-subtitle { font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.compat-card {
  display: flex; flex-direction: column; flex: 1; overflow: hidden;
  padding: 0;
}
.cx-tabs {
  display: flex; gap: 0; flex-shrink: 0;
  border-bottom: 2px solid var(--border);
  padding: 0 4px;
}
.cx-tab {
  padding: 8px 16px; font-size: 13px; font-weight: 500;
  color: var(--muted); background: transparent; border: none;
  border-bottom: 2px solid transparent; cursor: pointer;
  font-family: var(--sans); transition: color .12s, border-color .12s;
  margin-bottom: -2px;
}
.cx-tab:hover { color: var(--ink); background: transparent; border-color: var(--border-2); }
.cx-tab.active { color: var(--accent-ink); border-bottom-color: var(--accent); font-weight: 600; }
.cx-scroll {
  flex: 1; overflow: auto; padding: 20px 24px;
}
.cx-matrix {
  border-collapse: separate; border-spacing: 0;
  font-size: 12.5px;
}
/* frozen corner */
.cx-corner {
  position: sticky; top: 0; left: 0; z-index: 4;
  background: var(--surface-2);
  border-right: 1px solid var(--border); border-bottom: 1px solid var(--border);
  min-width: 130px;
}
/* rotated column headers */
.cx-col-head {
  position: sticky; top: 0; z-index: 3;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  height: 110px; width: 40px; max-width: 40px;
  vertical-align: bottom; padding: 0 0 6px 0;
}
.cx-col-label {
  display: block;
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  white-space: nowrap;
  font-size: 11.5px; font-weight: 500; color: var(--ink-2);
  padding: 0 4px;
  max-height: 100px; overflow: hidden; text-overflow: ellipsis;
}
/* row label (sticky left) */
.cx-row-label {
  position: sticky; left: 0; z-index: 2;
  background: var(--surface-2);
  border-right: 1px solid var(--border);
  padding: 0 12px 0 0; text-align: right;
  font-size: 12px; font-weight: 500; color: var(--ink-2);
  white-space: nowrap; max-width: 160px;
  overflow: hidden; text-overflow: ellipsis;
}
/* data cells */
.cx-cell {
  width: 40px; height: 36px; text-align: center; vertical-align: middle;
  border: 1px solid var(--border); border-top: none; border-left: none;
  cursor: pointer; font-size: 14px; font-weight: 700;
  transition: background .1s, color .1s;
  user-select: none;
}
.cx-cell:hover { background: var(--surface); }
.cx-cell.cx-ok {
  background: var(--ok-soft); color: var(--ok);
}
.cx-cell.cx-ok:hover { background: var(--ok-soft); filter: brightness(.93); }
.cx-cell.cx-no {
  background: var(--danger-soft); color: var(--danger);
}
.cx-cell.cx-no:hover { background: var(--danger-soft); filter: brightness(.93); }
.cx-na {
  background: var(--surface-sunk); color: var(--faint);
  cursor: default; font-size: 12px; font-weight: 400;
}
.cx-na:hover { background: var(--surface-sunk); filter: none; }
/* legend */
.cx-legend {
  display: flex; gap: 20px; align-items: center;
  padding: 10px 24px; border-top: 1px solid var(--border);
  background: var(--surface); flex-shrink: 0;
}
.cx-leg-item {
  display: flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--ink-2);
}
.cx-swatch {
  display: inline-grid; place-items: center;
  width: 24px; height: 24px; border-radius: var(--r-sm);
  font-size: 12px; font-weight: 700; border: 1px solid var(--border-2);
  background: var(--surface);
}
.cx-swatch.cx-ok { background: var(--ok-soft);     color: var(--ok);     border-color: transparent; }
.cx-swatch.cx-no { background: var(--danger-soft);  color: var(--danger); border-color: transparent; }
/* empty state */
.cx-empty {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 6px; padding: 40px;
}
.cx-empty-t { font-size: 15px; font-weight: 600; color: var(--ink-2); }
.cx-empty-s { font-size: 13px; color: var(--muted); }

/* ── Clinical auth modal ─────────────────────────────────────────────────────── */
.clinical-modal {
  width: min(400px, 100%);
  padding: 0;
}
.modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.modal-head h3 {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 17px;
  margin: 0;
  color: var(--ink);
}
.modal-close {
  background: none; border: none; cursor: pointer;
  font-size: 20px; color: var(--muted); padding: 0 4px;
}
.modal-close:hover { color: var(--ink); }
.modal-sub {
  font-size: 13px; color: var(--muted);
  margin: 0; padding: 12px 20px 0;
}
.clinical-modal .fg-field { padding: 0 20px; }
.auth-error {
  margin: 8px 20px 0;
  padding: 8px 12px;
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: var(--r-sm);
  font-size: 13px;
}
.modal-actions {
  display: flex; justify-content: flex-end; gap: 8px;
  padding: 16px 20px;
}

/* ── Exam lock banner ─────────────────────────────────────────────────────────── */
.ex-lock-banner {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 18px;
  background: var(--warning-soft, #fef3c7);
  border-bottom: 1px solid var(--warning-border, #fde68a);
  font-size: 12px; color: var(--ink-2);
  gap: 10px;
}
[data-theme="dark"] .ex-lock-banner {
  background: rgba(251,191,36,.1);
  border-color: rgba(251,191,36,.2);
}

/* OPT-746: signed-off variant — deliberately NOT the amber "please sign in"
   look (nothing to sign in FOR, the record stays read-only regardless), and
   NOT the green "signed in" look either (that means editable). Slate/ink
   instead: a calm, final, "this is settled" tone, no call to action. */
.ex-lock-banner.ex-signoff-banner {
  background: var(--surface-2);
  border-bottom-color: var(--border-strong);
  color: var(--ink-2);
  justify-content: flex-start;
}
[data-theme="dark"] .ex-lock-banner.ex-signoff-banner {
  background: var(--surface-2);
  border-bottom-color: var(--border-strong);
}

/* ── Exam auth badge ──────────────────────────────────────────────────────────── */
.ex-auth-badge {
  display: flex; align-items: center; gap: 6px;
}
.ex-auth-name {
  font-size: 12px; font-weight: 600;
  color: var(--ok, #16a34a);
  background: var(--ok-soft, #dcfce7);
  padding: 3px 8px; border-radius: 99px;
}
[data-theme="dark"] .ex-auth-name {
  background: rgba(22,163,74,.15);
}
.btn.xs {
  padding: 2px 8px; font-size: 11px; border-radius: var(--r-sm);
}

/* ── OPT-746: signed-off badge pill ───────────────────────────────────────────── */
/* Exam header (next to .ex-title/.ex-date) + a smaller `.sm` variant for the
   st-list / cl-exam-list history rows -- same slate/ink tone as
   .ex-signoff-banner above, so the two read as one visual language for
   "this record is signed off" wherever it shows up on the page. */
.signoff-badge {
  display: inline-flex; align-items: center;
  font-size: 11px; font-weight: 700;
  letter-spacing: .2px;
  color: var(--ink-2);
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  padding: 3px 9px; border-radius: 99px;
  white-space: nowrap;
}
.signoff-badge.sm {
  margin-left: 8px;
  font-size: 10px; font-weight: 600;
  padding: 2px 7px;
}

/* ── Locked field style ───────────────────────────────────────────────────────── */
input.locked, textarea.locked {
  background: var(--surface-2, var(--bg-2));
  color: var(--muted);
  cursor: default;
}

/* ── Jobs section ─────────────────────────────────────────────────────────────── */
.job-empty {
  font-size: 13px; color: var(--muted);
  padding: 12px 0; text-align: center;
}
.jobs-list { display: flex; flex-direction: column; gap: 8px; }

/* Job card (accordion) */
.job-card {
  border: 1px solid var(--border);
  border-radius: var(--r);
  background: var(--surface);
  overflow: hidden;
  transition: border-color .15s, box-shadow .15s;
}
.job-card.open {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
.job-card-head {
  display: flex; align-items: stretch;
}
.job-card-toggle {
  flex: 1; min-width: 0;
  display: flex; align-items: center; gap: 8px;
  padding: 10px 12px;
  background: transparent; border: none; cursor: pointer; text-align: left;
}
.job-card-toggle:hover { background: var(--surface-sunk); }
.job-chevron {
  flex-shrink: 0; color: var(--muted); margin-left: auto;
  transition: transform .15s;
}
.job-card.open .job-chevron { transform: rotate(180deg); }
.job-type-tag {
  font-size: 10px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
  padding: 2px 7px; border-radius: 99px;
  background: var(--accent-soft); color: var(--accent);
  flex-shrink: 0;
}
.job-type-tag.fee {
  background: rgba(139,92,246,.12); color: #7c3aed;
}
[data-theme="dark"] .job-type-tag.fee { background: rgba(139,92,246,.2); color: #a78bfa; }
.job-type-tag.cl {
  background: rgba(16,185,129,.12); color: #059669;
}
[data-theme="dark"] .job-type-tag.cl { background: rgba(16,185,129,.2); color: #34d399; }
.job-cl-info {
  display: flex; flex-direction: column; gap: 4px;
  padding: 8px 12px; background: var(--surface-sunk);
  border-radius: var(--r-sm); margin-bottom: 4px;
}
.job-cl-row {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 12px; color: var(--muted);
}
.job-cl-row span:first-child { font-weight: 600; text-transform: uppercase; font-size: 10px; letter-spacing: .3px; }
.job-desc {
  flex: 1; min-width: 0;
  font-size: 13px; font-weight: 500; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.job-price {
  font-size: 13px; font-weight: 600; color: var(--ink-2); flex-shrink: 0;
  font-family: var(--mono);
}
.icon-btn.danger { color: var(--err, #dc2626); }
.icon-btn.danger:hover { background: rgba(220,38,38,.08); border-color: rgba(220,38,38,.3); }

/* Job body (expanded) */
.job-body {
  border-top: 1px solid var(--border);
  padding: 0 16px 16px;
  display: flex; flex-direction: column; gap: 0;
}
.fee-body { padding: 16px 16px; gap: 12px; display: flex; flex-direction: column; }

/* Frame / lens pick rows inside job */
.job-prod-row {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 0;
  border-bottom: 1px dashed var(--border);
}
.job-prod-row:last-of-type { border-bottom: none; }
.job-prod-lbl {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); width: 42px; flex-shrink: 0;
}
.job-prod-thumb {
  width: 32px; height: 32px; border-radius: var(--r-sm);
  background: var(--surface-sunk); border: 1px solid var(--border);
  display: grid; place-items: center; flex-shrink: 0;
  font-size: 9px; color: var(--muted); overflow: hidden;
}
.job-prod-info { flex: 1; min-width: 0; }
.job-prod-name {
  font-size: 13px; font-weight: 600; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.job-prod-meta { font-size: 11.5px; color: var(--muted); }
.job-prod-price {
  font-size: 12.5px; font-weight: 500; color: var(--ink-2);
  flex-shrink: 0; font-family: var(--mono);
}
.job-prod-empty { font-size: 12.5px; color: var(--muted); flex: 1; }

/* Add-ons grid inside job */
.job-addons { padding: 12px 0 4px; border-bottom: 1px dashed var(--border); }
.job-addons-lbl {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px;
  color: var(--muted); margin-bottom: 8px;
}

/* Subtotal block inside job */
.job-subtotal { padding: 12px 0 0; }
.job-sub-line {
  display: flex; justify-content: space-between; align-items: center;
  font-size: 12.5px; color: var(--ink-2); padding: 3px 0;
}
.job-sub-total {
  border-top: 1px solid var(--border);
  padding-top: 8px; margin-top: 4px;
  font-weight: 700; color: var(--ink);
}
.job-sub-price { padding-top: 6px; }
.job-sub-price input {
  width: 110px; text-align: right;
  border: 1px solid var(--border-2); border-radius: var(--r-sm);
  padding: 4px 8px; font-size: 12.5px; font-family: var(--mono);
  background: var(--surface-sunk); color: var(--ink); outline: none;
  transition: border-color .12s, box-shadow .12s;
}
.job-sub-price input:focus {
  border-color: var(--accent); background: var(--surface); box-shadow: var(--ring);
}


/* ── Recalls page ────────────────────────────────────────────────────────────── */
.recalls-page {
  padding: 20px 24px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* The collections page reuses the ordering page's pill tabs verbatim
   (.ord-page-tabs / .ord-tab / .ord-count, from the app-wide ordering.css) so
   both pages read identically. That strip supplies its own page gutter for
   .ord-page; .recalls-page already has one, so drop it here to avoid a double
   indent and space the strip off the section below instead. */
.recalls-page .ord-page-tabs { padding: 0; margin-bottom: 16px; }

.recalls-toolbar {
  display: flex;
  align-items: center;
  gap: 16px;
  margin: 12px 0 18px;
  flex-wrap: wrap;
}

.recall-month-nav {
  display: flex;
  align-items: center;
  gap: 8px;
}

.recall-month-label {
  font-family: var(--mono);
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  min-width: 80px;
  text-align: center;
}

.recall-filter-bar {
  display: flex;
  gap: 6px;
}

table.recalls-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13.5px;
}

table.recalls-table th {
  text-align: left;
  padding: 8px 12px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}

table.recalls-table td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
  height: 44px;
}

tr.recall-row {
  cursor: pointer;
  transition: background 0.1s;
  min-height: 44px;
}

tr.recall-row:hover {
  background: var(--surface-2);
}

tr.recall-row--contacted {
  opacity: 0.55;
}

tr.recall-row--contacted:hover {
  opacity: 0.75;
}

.recalls-empty {
  color: var(--muted);
  font-size: 14px;
  padding: 24px 0;
}

.recall-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.recalls-section {
  margin-bottom: 32px;
}

.status-badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .4px;
}
.status-badge.ordered {
  background: #fff3cd;
  color: #856404;
}
.status-badge.glazing {
  background: #cfe2ff;
  color: #084298;
}
.status-badge.ready {
  background: #d1e7dd;
  color: #0a3622;
}

/* ── Payments ────────────────────────────────────────────────────────────── */

table.payments-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

table.payments-table th {
  text-align: left;
  font-weight: 600;
  font-size: 11px;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--muted);
  padding: 0 0 6px;
  border-bottom: 1px solid var(--border);
}

table.payments-table td {
  padding: 6px 0;
  border-bottom: 1px solid var(--surface-2);
}

tr.payment-row--refund td {
  color: var(--danger, #c0392b);
}

.payments-empty {
  color: var(--muted);
  font-size: 14px;
}

.payment-summary {
  border-top: 1px solid var(--border);
  padding-top: 10px;
}

.balance-owed {
  color: var(--accent-ink);
  font-weight: 600;
}

.payment-add-row {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

.payment-amount-input {
  width: 110px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 13px;
  background: var(--surface);
  color: var(--fg);
}

.payment-method-select {
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 4px;
  font-size: 13px;
  background: var(--surface);
  color: var(--fg);
  cursor: pointer;
}

/* ── Status pipeline ─────────────────────────────────────────────────────── */

.status-pipeline {
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
}

.status-btn {
  padding: 6px 14px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--surface);
  color: var(--fg);
  font-size: 13px;
  cursor: pointer;
}

.status-btn:hover:not(:disabled) {
  background: var(--surface-2);
}

.status-btn--active {
  background: var(--accent, #2563eb);
  color: #fff;
  border-color: var(--accent, #2563eb);
  font-weight: 600;
  cursor: default;
}

/* ── Collections board ───────────────────────────────────────────────────── */

.coll-name {
  font-weight: 500;
}

.coll-phone {
  font-size: 12px;
  color: var(--muted);
}

/* ── Print foundation (OPT-373) ──────────────────────────────────────────── *
 * Shared groundwork for every print surface (Rx, dispense order form, till
 * receipt, referral letter, ordering lab sheet, diary day sheet). Each
 * surface still owns its own layout rules below / in ordering.css — this
 * block only holds what used to be copy-pasted per surface:
 *   - the @page box (size/margins for the printed sheet)
 *   - the practice-letterhead component's classes (optos.print-views)
 *   - the ONE `body * { visibility: hidden }` hide-everything rule (this
 *     used to be repeated in the day-sheet block below and in ordering.css;
 *     both now rely on this single declaration since style.css and
 *     ordering.css are always loaded together — see index.html)
 *   - `.no-print`, previously only declared inside the day-sheet block even
 *     though it's used app-wide to hide chrome (e.g. the diary's "Print day
 *     sheet" button) while printing
 * Surfaces that need to pick one of several print areas present on the same
 * page (receipt vs order form, referral vs Rx) still gate their reveal with
 * a body class — that per-surface logic is unchanged.
 *
 * OPT-664 adds the `:root` override below: every custom property the dark
 * theme ([data-theme="dark"] above) redefines is forced back to a light,
 * print-safe value for the whole document while printing, regardless of
 * which theme is active on screen. The Rx/dispense/receipt/referral/VAT-
 * invoice surfaces already sidestepped this by hardcoding literal colours
 * (#000/#fff/#ccc/...) inside their own @media print rules rather than
 * reading these tokens at all, so this override is a no-op for them; it is
 * what makes surfaces that DO read var(--ink)/var(--surface)/etc for their
 * everyday styling (the KB article print below, the GOS1/GOS3/GOS4 forms)
 * safe to print as-is without hardcoding every colour a second time.
 */

@page {
  size: A4;
  margin: 15mm;
}

@media print {
  /* hide everything; print areas selectively restore visibility */
  body * {
    visibility: hidden;
  }

  .no-print {
    display: none !important;
  }

  :root {
    --paper:          #fff !important;
    --paper-2:        #fff !important;
    --surface:        #fff !important;
    --surface-2:      #fff !important;
    --surface-sunk:   #f2f2f2 !important;
    --border:         #ccc !important;
    --border-2:       #aaa !important;
    --border-strong:  #888 !important;
    --ink:            #000 !important;
    --ink-2:          #222 !important;
    --muted:          #555 !important;
    --faint:          #777 !important;
    --accent:         #000 !important;
    --accent-strong:  #000 !important;
    --accent-soft:    #eee !important;
    --accent-soft-2:  #ddd !important;
    --accent-ink:     #000 !important;
    --on-accent:      #fff !important;
    --sand:           #999 !important;
    --sand-soft:      #eee !important;
    --ok-soft:        #eee !important;
    --info-soft:      #eee !important;
    --warn-soft:      #eee !important;
    --danger-soft:    #eee !important;
  }
}

/* ── practice-letterhead (optos.print-views) ─────────────────────────────── */
.print-letterhead {
  margin-bottom: 14px;
}
.print-letterhead .pl-name {
  font-family: var(--serif);
  font-size: 17px;
  font-weight: 600;
  color: #000;
}
.print-letterhead .pl-address {
  margin-top: 3px;
}
.print-letterhead .pl-line {
  font-family: var(--sans);
  font-size: 11px;
  color: #333;
  line-height: 1.5;
}

/* OPT-409: on-screen-only caption mounted beside a print-trigger button when
   practice-letterhead has nothing to render (Admin > Practice is blank), so
   the empty letterhead is discoverable instead of read as a bug. Already
   .no-print at the call site; kept muted and small to match the other hint
   text in this file (see .dcp-empty, .cl-rx-note). */
.letterhead-hint {
  font-size: 11px;
  color: var(--muted);
  margin-left: 4px;
}

/* ── shared print typographic hierarchy (OPT-374) ────────────────────────── *
 * Used by both the Rx print (exam-views) and dispense order form print
 * (disp-views), which share the letterhead above plus the same section-label
 * / numeral treatment. Kept generic (not `.rx-print-*` / `.disp-print-*`
 * scoped) so both surfaces read as one consistent print language; harmless
 * outside `@media print` since their parent print areas are `display:none`
 * on screen (see `.rx-print-area, .disp-print-area, ...` below).
 */
.print-section {
  margin-bottom: 18px;
}
.print-section-lbl {
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: #555;
  margin-bottom: 6px;
  border-bottom: 1px solid #ccc;
  padding-bottom: 3px;
}
.print-num {
  font-family: var(--mono);
}

/* ── Print Rx ────────────────────────────────────────────────────────────── */

.rx-print-area,
.disp-print-area,
.receipt-print-area,
.referral-letter-area {
  display: none;
}

@media print {
  /* ── Rx print ── */
  .rx-print-area {
    display: block !important;
    visibility: visible;
    position: fixed;
    top: 0; left: 0;
    width: 100%;
    padding: 32px;
    font-family: var(--serif);
    color: #000;
    background: #fff;
  }
  .rx-print-area * {
    visibility: visible;
  }

  /* ── Dispense order form print ── */
  .disp-print-area {
    display: block !important;
    visibility: visible;
    position: fixed;
    top: 0; left: 0;
    width: 100%;
    padding: 32px;
    font-family: var(--sans);
    color: #000;
    background: #fff;
  }
  .disp-print-area * {
    visibility: visible;
  }

  .rx-print-header {
    border-bottom: 2px solid #000;
    padding-bottom: 10px;
    margin-bottom: 18px;
  }

  .rx-print-title {
    font-family: var(--sans);
    font-size: 13px;
    font-weight: 700;
    color: #000;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    margin-top: 10px;
  }

  table.rx-print-px {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
  }

  table.rx-print-px td {
    padding: 4px 8px 4px 0;
  }

  .rx-px-lbl {
    font-weight: 600;
    width: 130px;
  }

  table.rx-print-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13px;
  }

  table.rx-print-table th,
  table.rx-print-table td {
    border: 1px solid #aaa;
    padding: 6px 10px;
    text-align: center;
  }

  table.rx-print-table th {
    font-family: var(--sans);
    background: #f0f0f0;
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
  }

  table.rx-print-table th:first-child,
  table.rx-print-table td:first-child {
    text-align: left;
    font-weight: 600;
    background: #f8f8f8;
  }

  .rx-print-recall {
    font-size: 12px;
    color: #555;
    margin-bottom: 20px;
  }

  .rx-print-footer {
    border-top: 1px solid #aaa;
    padding-top: 12px;
    font-size: 11px;
    color: #666;
  }

  .rx-print-no-rx {
    font-size: 13px;
    color: #666;
    font-style: italic;
    margin-bottom: 16px;
  }

  /* ── Dispense order form styles ── */
  .disp-print-header {
    border-bottom: 2px solid #000;
    padding-bottom: 10px;
    margin-bottom: 18px;
  }
  .disp-print-doctitle-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    margin-top: 10px;
  }
  .disp-print-doctitle {
    font-family: var(--sans);
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: #000;
  }
  .disp-print-meta {
    font-size: 12px;
    color: #555;
    text-align: right;
    line-height: 1.5;
  }
  .disp-print-patient-name {
    font-size: 15px;
    font-weight: 700;
    margin-bottom: 2px;
  }
  table.disp-print-rx {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
  }
  table.disp-print-rx th,
  table.disp-print-rx td {
    border: 1px solid #aaa;
    padding: 5px 8px;
    text-align: center;
  }
  table.disp-print-rx th:first-child,
  table.disp-print-rx td:first-child {
    text-align: left;
    font-weight: 600;
    background: #f8f8f8;
    width: 60px;
  }
  table.disp-print-jobs-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
  }
  table.disp-print-jobs-table th,
  table.disp-print-jobs-table td {
    border: 1px solid #ccc;
    padding: 5px 8px;
    vertical-align: top;
  }
  table.disp-print-jobs-table td:last-child {
    text-align: right;
    white-space: nowrap;
  }
  .disp-print-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    background: #f2f2f2;
    border-top: 2px solid #000;
    border-bottom: 1px solid #000;
    padding: 8px 10px;
    margin-bottom: 20px;
  }
  .disp-print-total-lbl {
    font-family: var(--sans);
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
  }
  .disp-print-total-val {
    font-family: var(--mono);
    font-size: 16px;
    font-weight: 700;
  }
  .disp-print-notes {
    font-size: 12px;
    color: #333;
    line-height: 1.5;
  }
  .disp-print-footer {
    border-top: 1px solid #aaa;
    padding-top: 10px;
    font-size: 10px;
    color: #777;
    text-align: right;
  }

  /* ── shared table header treatment: both the Rx table and jobs table ── */
  table.disp-print-rx th,
  table.disp-print-jobs-table th {
    font-family: var(--sans);
    background: #f0f0f0;
    font-weight: 600;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.3px;
  }

  /* ── Till receipt print ── */
  body.print-receipt .receipt-print-area {
    display: block !important;
    visibility: visible;
    position: fixed;
    top: 0; left: 0;
    width: 100%;
    max-width: 400px;
    padding: 24px 28px;
    font-family: var(--mono);
    font-size: 12px;
    color: #000;
    background: #fff;
  }
  body.print-receipt .receipt-print-area * {
    visibility: visible;
  }
  body.print-receipt .disp-print-area {
    display: none !important;
  }

  /* header harmonised with .rx-print-header / .disp-print-header: letterhead
     (serif, via .print-letterhead) followed by a title row */
  .rp-header {
    border-bottom: 2px solid #000;
    padding-bottom: 10px;
    margin-bottom: 12px;
  }
  .rp-title-row {
    display: flex;
    justify-content: space-between;
    align-items: flex-end;
    margin-top: 10px;
  }
  .rp-title {
    font-family: var(--sans);
    font-size: 13px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    color: #000;
  }
  .rp-meta {
    font-size: 11px;
    color: #555;
    text-align: right;
    line-height: 1.5;
  }
  .rp-patient {
    font-size: 11px;
    margin-bottom: 8px;
  }
  .rp-divider {
    border-top: 1px dashed #aaa;
    margin: 8px 0;
  }
  .rp-item {
    margin-bottom: 8px;
  }
  .rp-item-row {
    display: flex;
    justify-content: space-between;
    font-size: 12px;
    font-weight: 600;
  }
  .rp-item-type {
    flex: 1;
    padding-right: 8px;
  }
  .rp-item-price {
    white-space: nowrap;
  }
  .rp-item-detail {
    font-size: 10px;
    color: #555;
    padding-left: 8px;
    line-height: 1.5;
  }
  .rp-total-row {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    font-weight: 700;
    padding: 4px 0;
  }
  .rp-total-val {}
  .rp-payment-row {
    display: flex;
    justify-content: space-between;
    font-size: 11px;
    padding: 2px 0;
  }
  .rp-balance-row {
    display: flex;
    justify-content: space-between;
    font-size: 13px;
    font-weight: 700;
    padding: 4px 0;
  }
  .rp-balance-val {}
  .rp-footer {
    margin-top: 14px;
    font-size: 11px;
    color: #555;
    text-align: center;
  }

  /* ── Referral letter print ── */
  body.print-referral .referral-letter-area {
    display: block !important;
    visibility: visible;
    position: fixed;
    top: 0; left: 0;
    width: 100%;
    padding: 40px 48px;
    font-family: var(--sans);
    font-size: 12px;
    line-height: 1.6;
    color: #000;
    background: #fff;
  }
  body.print-referral .referral-letter-area * {
    visibility: visible;
  }
  body.print-referral .rx-print-area,
  body.print-referral .disp-print-area,
  body.print-referral .receipt-print-area {
    display: none !important;
  }

  /* header harmonised with .rx-print-header: letterhead (serif, via
     .print-letterhead) followed by a title line */
  .ref-header {
    border-bottom: 2px solid #000;
    padding-bottom: 10px;
    margin-bottom: 18px;
  }
  .ref-title {
    font-family: var(--sans);
    font-size: 13px;
    font-weight: 700;
    color: #000;
    text-transform: uppercase;
    letter-spacing: 0.6px;
    margin-top: 10px;
  }
  .ref-date {
    text-align: right;
    font-size: 11px;
    color: #555;
    margin-bottom: 18px;
  }
  .ref-gp-name {
    font-weight: 600;
  }
  .ref-gp-surgery {
    font-size: 11px;
    color: #444;
  }
  .ref-re {
    background: #f5f5f5;
    padding: 8px 12px;
    border-left: 3px solid #000;
    font-size: 12px;
    margin-bottom: 18px;
  }
  .ref-re-label {
    font-weight: 700;
  }
  .ref-divider {
    border-top: 1px solid #ccc;
    margin: 14px 0;
  }
  .ref-opening {
    margin-bottom: 10px;
  }
  .ref-body {
    margin-bottom: 14px;
  }
  .ref-finding {
    font-size: 12px;
    margin-bottom: 4px;
  }
  .ref-lbl {
    font-weight: 600;
  }
  .ref-referral-text {
    font-size: 12px;
    white-space: pre-line;
  }
  .ref-close {
    margin-top: 24px;
    margin-bottom: 36px;
  }
  .ref-sig-name {
    font-weight: 700;
    border-top: 1px solid #000;
    padding-top: 4px;
    display: inline-block;
    min-width: 160px;
  }
  .ref-sig-title {
    font-size: 11px;
    color: #555;
  }
}

/* ── Practice details settings ─────────────────────────────────────── */
.practice-settings {
  margin-top: 24px;
  padding: 16px 20px;
  background: var(--surface);
  border-radius: 6px;
}

.practice-settings h3 {
  margin: 0 0 14px 0;
  font-size: 14px;
  font-weight: 600;
  color: var(--text-primary);
}
.practice-card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 16px;
}

.practice-card-head h3 {
  margin: 0;
}

.practice-section {
  margin-bottom: 14px;
}

.practice-inline {
  display: flex;
  flex-direction: column;
  gap: 3px;
  margin-bottom: 0;
}

.practice-inline label {
  font-size: 12px;
  font-weight: 500;
  color: var(--text-secondary);
}

.practice-inline input {
  padding: 5px 8px;
  font-size: 13px;
  width: 100%;
  box-sizing: border-box;
}


.practice-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 8px;
}

.practice-row label {
  width: 130px;
  font-size: 13px;
  color: var(--text-secondary);
  flex-shrink: 0;
}

.practice-row input {
  flex: 1;
  padding: 5px 8px;
  font-size: 13px;
}

/* ── Patient alert banner ───────────────────────────────────────────── */
.px-alert-banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  margin-bottom: 10px;
  background: #fff3cd;
  border: 1px solid #ffc107;
  border-radius: 4px;
  font-size: 13px;
  font-weight: 600;
  color: #856404;
}

[data-theme="dark"] .px-alert-banner {
  background: #3d2c00;
  border-color: #b58900;
  color: #ffd966;
}

.px-alert-icon {
  font-size: 14px;
}

/* ── Patient notes textarea ─────────────────────────────────────────── */
.px-notes {
  width: 100%;
  min-height: 72px;
  resize: vertical;
  font-size: 12px;
  padding: 6px 8px;
  border-radius: 3px;
  font-family: var(--sans);
}

/* ── Search mode tabs ────────────────────────────────────────────────── */
.search-mode {
  margin: 8px 0 0 0;
}

.search-mode-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 6px;
}

.mode-tab {
  padding: 3px 10px;
  font-size: 12px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
}

.mode-tab.active {
  background: var(--accent);
  color: #fff;
  border-color: var(--accent);
}

.search-mode-input input {
  width: 100%;
  padding: 5px 8px;
  font-size: 13px;
}

/* ── Reminders panel (OPT-017) ───────────────────────────────────────── */
/* OPT-410: Reminders opens as a fixed overlay modal (same shape as
   .composer-overlay) — the OPT-285 anchored popover sat at z-index 40 and
   was covered by later stacking contexts in the diary when expanded. */
.reminders-wrap {
  position: relative;
}

.reminders-overlay {
  position: fixed; inset: 0; z-index: 500;
  background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
  display: flex; justify-content: center; align-items: flex-start;
  overflow-y: auto; padding: 32px 16px 64px;
}

.reminders-sheet {
  background: var(--surface); border-radius: var(--r-lg);
  width: 560px; max-width: 100%;
  max-height: calc(100vh - 96px);
  box-shadow: 0 24px 80px rgba(0,0,0,.35);
  display: flex; flex-direction: column;
  overflow: hidden;
}

.reminders-body {
  overflow-y: auto;
}

.reminders-hd {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  padding: 10px 14px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  border-radius: var(--r-lg) var(--r-lg) 0 0;
  text-transform: uppercase;
  letter-spacing: .04em;
}

.reminders-hd-title {
  flex: 1;
}

.reminders-close {
  width: 26px; height: 26px; border-radius: 50%; border: 1px solid var(--border);
  background: transparent; cursor: pointer; font-size: 16px; color: var(--muted);
  display: grid; place-items: center; flex-shrink: 0;
  transition: background .1s;
}

.reminders-close:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

.reminders-empty {
  padding: 12px;
  font-size: 13px;
  color: var(--text-secondary);
  margin: 0;
}

.reminder-row {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.reminder-row:last-child {
  border-bottom: none;
}

.reminder-info {
  display: flex;
  gap: 8px;
  align-items: baseline;
}

.reminder-time {
  font-size: 12px;
  font-weight: 600;
  color: var(--accent);
  min-width: 38px;
}

.reminder-name {
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.reminder-type {
  font-size: 11px;
  color: var(--text-secondary);
  margin-left: auto;
}

.reminder-msg {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.5;
  background: var(--surface-sunk);
  border-radius: 3px;
  padding: 5px 8px;
  white-space: pre-wrap;
  word-break: break-word;
}

.reminder-actions {
  display: flex;
  gap: 6px;
  margin-top: 2px;
}

/* ── Practice settings ────────────────────────────────────────────────── */
.reminder-tpl-input {
  width: 100%;
  resize: vertical;
  font-size: 12px;
  font-family: var(--mono);
  padding: 6px 8px;
  border-radius: 3px;
}

.practice-hint {
  font-size: 11px;
  color: var(--text-secondary);
  margin: 2px 0 8px;
}

/* OPT-420: reminder template variable chips - click to insert at cursor */
.tpl-var-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 4px 0;
}

.tpl-var-chip {
  font-family: var(--mono);
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background .1s, color .1s;
}

.tpl-var-chip:hover {
  background: var(--surface-sunk);
  color: var(--ink);
}

/* ── Analytics panel (OPT-019) ─────────────────────────────────────────────── */
.analytics-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  padding: 14px; display: flex; flex-direction: column; gap: 10px;
}
.ap-head {
  font-size: 11px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: var(--muted);
}
.ap-section { display: flex; flex-direction: column; gap: 4px; }
.ap-label { font-size: 11px; color: var(--muted); font-weight: 600; }
.ap-rate { font-size: 26px; font-weight: 700; color: var(--ink); font-family: var(--mono); }
.ap-sub  { font-size: 11px; color: var(--muted); }
.ap-empty { font-size: 12px; color: var(--muted); }
.ap-table {
  width: 100%; border-collapse: collapse; font-size: 12px;
}
.ap-table th {
  text-align: left; font-weight: 600; color: var(--muted);
  padding: 3px 6px; border-bottom: 1px solid var(--border);
  font-size: 10px; text-transform: uppercase; letter-spacing: .5px;
}
.ap-table td { padding: 4px 6px; border-bottom: 1px solid var(--border-soft); }
.ap-table tr:last-child td { border-bottom: none; }
.ap-table td:nth-child(2), .ap-table td:nth-child(3), .ap-table td:nth-child(4),
.ap-table th:nth-child(2), .ap-table th:nth-child(3), .ap-table th:nth-child(4) { text-align: right; }
.ap-table td:first-child, .ap-table th:first-child {
  max-width: 96px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ap-table td:last-child { font-family: var(--mono); white-space: nowrap; }

/* ── Exam tabs (OPT-018) ────────────────────────────────────────────────────── */
.exam-tabs {
  display: flex; gap: 0; border-bottom: 2px solid var(--border);
  flex-shrink: 0; margin-bottom: 0;
}
.exam-tab {
  padding: 8px 18px; font-size: 13px; font-weight: 600;
  border: none; background: transparent; cursor: pointer;
  color: var(--muted); border-bottom: 2px solid transparent; margin-bottom: -2px;
  font-family: var(--sans); transition: color .12s;
}
.exam-tab:hover { color: var(--ink); }
.exam-tab.active { color: var(--accent-ink); border-bottom-color: var(--accent); }

/* ── CL exam form ────────────────────────────────────────────────────────────── */
.cl-exam-center { flex: 1; overflow-y: auto; display: flex; flex-direction: column; }
.cl-exam-form { flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 16px; }

.cl-sidebar-hd {
  font-size: 11px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: var(--muted);
}
.cl-type-badge {
  margin-left: auto; font-size: 10px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .5px; color: var(--muted);
  background: var(--surface-sunk); border-radius: 3px; padding: 2px 5px;
}
.st-item .cl-type-badge { margin-left: 8px; }
.cl-exam-sidebar { display: flex; flex-direction: column; gap: 10px; }

/* ── Print day sheet (OPT-020, OPT-407) ───────────────────────────────────── */
/* OPT-373: `.no-print` and the `body * { visibility: hidden }` hide-everything
   rule are now declared once in the "Print foundation" block above — this
   surface only restores its own reveal + layout.
   OPT-407: the day sheet now mounts practice-letterhead (optos.print-views)
   and the shared .print-section / .print-section-lbl classes above at its
   top. The `.day-sheet-print *` reveal below is a universal descendant
   selector, so it already un-hides those nested elements along with
   everything else in this print area — no separate rule was needed to
   extend the reveal to them. */
.day-sheet-print { display: none; }

@media print {
  .day-sheet-print, .day-sheet-print * { visibility: visible; }
  .day-sheet-print {
    position: absolute; top: 0; left: 0; width: 100%;
    padding: 20px; box-sizing: border-box;
  }
  .ds-header { margin-bottom: 16px; }
  .ds-titlerow { display: flex; justify-content: space-between; align-items: flex-end; margin-top: 10px; }
  .ds-title {
    font-family: var(--sans); font-size: 13px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.6px; color: #000;
  }
  .ds-meta { display: flex; gap: 24px; font-size: 12px; color: #555; }
  .ds-table { width: 100%; border-collapse: collapse; font-size: 13px; }
  .ds-table th, .ds-table td {
    border: 1px solid #ccc; padding: 6px 10px; text-align: left;
  }
  .ds-table th { font-weight: 700; background: #f0f0f0; }
  .ds-table tr:nth-child(even) { background: #fafafa; }
  .ds-notes { min-width: 120px; }
}

/* ── Appointment type picker (OPT-024) ────────────────────────────────────── */
/* OPT-366: sits directly above the diary grid (day + week) now, so it no
   longer needs its own bottom border — the grid box supplies that edge.
   OPT-377: nowrap + its own horizontal scroll — same reasoning as
   .diary-head/.clin-tabs above (a wrapped row pushes the grid's measured
   top down and defeats OPT-366's vertical-fit math). */
.type-picker {
  display: flex; flex-wrap: nowrap; gap: 6px;
  padding: 4px 2px; margin-bottom: 8px;
  overflow-x: auto; overflow-y: hidden; scrollbar-width: thin;
}
.type-chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: 600;
  border: none; cursor: pointer; font-family: var(--sans);
  transition: opacity .12s, outline .1s;
  outline: 2px solid transparent;
  white-space: nowrap; flex: none;
}
.type-chip:hover { opacity: .85; }
.type-chip.selected { outline: 2px solid currentColor; outline-offset: 1px; }
.type-dur { font-weight: 400; opacity: .75; font-size: 11px; }

/* ── Appointment types admin (OPT-024) ────────────────────────────────────── */
.appt-types-section { padding: 18px; }
.appt-types-section h3 { margin: 0 0 14px; font-size: 14px; }
.at-form { margin-bottom: 14px; }
.at-row {
  display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap;
}
.at-type-row {
  padding: 6px 10px; background: var(--surface);
  border: 1px solid var(--border-2); border-radius: var(--r-sm);
  align-items: center;
}
.at-dur { font-size: 12px; color: var(--muted); flex: 1; }
.at-empty { color: var(--muted); font-size: 13px; margin: 0; }
.at-list { display: flex; flex-direction: column; gap: 6px; }
.at-price-label { display: flex; align-items: center; gap: 3px; font-size: 13px; color: var(--muted); }
.at-price-input { width: 72px; font-size: 13px; padding: 2px 4px; }

/* ── Attachments (OPT-023) ────────────────────────────────────────────────── */
.att-upload-row {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
}
.hidden-file-input { display: none; }
.att-empty { color: var(--muted); font-size: 13px; margin: 0; }
.att-list {
  display: flex; flex-direction: column; gap: 8px;
}
.att-row {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; background: var(--surface);
  border: 1px solid var(--border-2); border-radius: var(--r-sm);
}
.att-preview {
  width: 48px; height: 48px; object-fit: cover;
  border-radius: 4px; border: 1px solid var(--border);
  flex-shrink: 0;
}
.att-meta {
  display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0;
}
.att-name {
  font-size: 13px; font-weight: 600; color: var(--accent-ink);
  text-decoration: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.att-name:hover { text-decoration: underline; }
.att-date { font-size: 11px; color: var(--muted); }
.att-by { font-size: 11px; color: var(--faint); }

/* ── Diary defaults admin (OPT-111) ──────────────────────────────────────── */
.diary-defaults-section { padding: 18px; }
.diary-defaults-section h3 { margin: 0 0 6px; font-size: 14px; }
.diary-defaults-section .hint { color: var(--muted); font-size: 12px; margin: 0 0 14px; }
.dds-row {
  display: flex; align-items: center; gap: 10px; margin-bottom: 10px;
}
.dds-row label {
  min-width: 140px; font-size: 13px; color: var(--ink); font-weight: 500;
}
.dds-row input {
  padding: 5px 8px; border: 1px solid var(--border-2);
  border-radius: var(--r-sm); font-size: 13px; background: var(--surface);
  color: var(--ink);
}

/* ── Diary Rules editor (OPT-110) ────────────────────────────────────────── */
.diary-rules-section { padding: 18px; }
.diary-rules-section h3 { margin: 0 0 8px; font-size: 14px; }
.diary-rules-section .hint { font-size: 12px; color: var(--muted); margin: 0 0 12px; }

.drs-clinician-select {
  display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 14px;
}
.drs-clinician-select .btn.active {
  background: var(--accent); color: var(--accent-ink); border-color: var(--accent);
}

table.drs-table {
  border-collapse: collapse; width: 100%; font-size: 13px;
}
table.drs-table thead th {
  text-align: left; padding: 4px 8px;
  font-size: 11px; font-weight: 600; color: var(--muted);
  border-bottom: 1px solid var(--border-2);
}
table.drs-table tbody td {
  padding: 4px 8px; border-bottom: 1px solid var(--border);
  vertical-align: middle;
}
table.drs-table input[type="time"],
table.drs-table input[type="number"] {
  font-size: 12px; padding: 2px 4px;
  border: 1px solid var(--border-2); border-radius: var(--r-sm);
  background: var(--surface); color: var(--ink);
  width: auto;
}
table.drs-table input[type="number"] { width: 60px; }
table.drs-table input[type="checkbox"] { cursor: pointer; }

/* ── Diary exclusions (OPT-112) ──────────────────────────────────────────── */
.excl-section { border-top: 1px solid var(--border-2); margin-top: 16px; padding-top: 12px; }
.exclusion-panel, .excl-list { padding: 0 0 12px; }
.excl-heading {
  font-size: 11px; font-weight: 700; letter-spacing: .6px;
  text-transform: uppercase; color: var(--muted); margin: 0 0 8px;
}
.excl-row { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; flex-wrap: wrap; }
.excl-lbl { font-size: 11px; font-weight: 600; color: var(--muted); white-space: nowrap; flex-shrink: 0; }
.excl-input {
  flex: 1; min-width: 80px; font-size: 12px;
  padding: 4px 7px; border: 1px solid var(--border-2);
  border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
}
.excl-input:focus { outline: none; border-color: var(--accent); }
.excl-conflicts {
  margin-top: 10px; background: var(--warn-soft, oklch(0.97 0.04 90));
  border: 1px solid var(--warn, oklch(0.75 0.12 85));
  border-radius: var(--r-sm); padding: 8px 10px;
}
.excl-conflict-hdr { font-size: 12px; font-weight: 700; color: var(--warn-ink, oklch(0.45 0.12 60)); margin: 0 0 6px; }
.excl-conflict-row { display: flex; align-items: center; gap: 8px; font-size: 12px; padding: 2px 0; color: var(--ink); }
.excl-name { font-weight: 500; }
.excl-ph   { color: var(--muted); font-size: 11px; }
.excl-list { margin-top: 12px; }
.excl-item {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 6px 8px; background: var(--surface);
  border: 1px solid var(--border); border-radius: var(--r-sm); margin-bottom: 6px;
}
.excl-dates { display: flex; gap: 4px; color: var(--ink); font-weight: 500; flex: 1; }
.excl-reason { color: var(--muted); font-style: italic; flex: 1; }
.btn.ghost.danger-ghost { color: var(--danger); }
.btn.ghost.danger-ghost:hover { background: var(--danger-soft); border-color: transparent; color: var(--danger); }

/* OPT-113 — diary closed day indicator */
.diary-closed {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 120px;
  font-style: italic;
  color: var(--muted);
  font-size: 14px;
}

/* OPT-126 — appointment reminder email button + feedback */
.tl-appt .a-act-btn.email-reminder:hover { background: var(--info-soft, #e8f4fd); color: var(--info, #1a73e8); }
.appt-email-feedback {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: var(--r-sm);
  margin: 4px 0;
  display: inline-block;
}
.appt-email-feedback.feedback-ok      { background: var(--ok-soft); color: var(--ok); }
.appt-email-feedback.feedback-pending { background: var(--surface-alt, #f5f5f5); color: var(--muted); }
.appt-email-feedback.feedback-err     { background: var(--danger-soft); color: var(--danger); }

/* ── OPT-151 Exams page: empty state + right-panel history card ────────────── */
/* OPT-817: third .empty-state definition removed — folded into the canonical
   rule above; it was the one supplying the effective padding and height. */
.es-glyph { font-size: 32px; opacity: .45; line-height: 1; }
.es-head  { font-size: 15px; font-weight: 600; color: var(--ink-2); }
.es-hint  { font-size: 13px; color: var(--muted); }

/* ── Exam history card (right panel) ──────────────────────────────────────── */
.exam-history {
  overflow-y: auto;
  flex: 1;
  display: flex;
  flex-direction: column;
}
.exam-history .section-head {
  padding: 10px 14px 8px;
  margin-bottom: 0;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.exam-history .section-head .sh-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .7px;
  text-transform: uppercase;
  color: var(--muted);
}
.exam-history .pd-pad { flex: 1; overflow-y: auto; }
.exam-history-empty {
  color: var(--muted);
  font-size: 13px;
  padding: 8px 0;
  font-style: italic;
}

/* ── Audiology (OPT-309) ──────────────────────────────────────────────────────
   Section-card grid is 2x2 (only 4 sections vs. the exam page's 7), everything
   else reuses .ex-* / .fg-field / .yn-toggle etc. verbatim. The audiogram
   chart reuses the app's existing --danger (right ear, clinical red) and
   --info (left ear, clinical blue) tokens rather than inventing new ones. */
.aud-grid {
  flex: 1; display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-template-rows: repeat(2, 1fr);
  gap: 8px; min-height: 0;
}

/* OPT-813: audiology section cards — same shape and same reasoning as
   `.ex-grid` above, one degree milder (2 columns rather than 3). Co-located for
   the same source-order reason: this rule is ~2600 lines BELOW the shared 640px
   block, so an override written there never applies. */
@media (max-width: 640px) {
  .aud-grid {
    grid-template-columns: 1fr;
    grid-template-rows: none;
    min-height: auto;
  }
}

.aud-chart-wrap { display: flex; flex-direction: column; gap: 6px; margin-bottom: 10px; }

.aud-pen-toggle { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.aud-pen-btn {
  padding: 5px 10px; border-radius: 999px;
  border: 1px solid var(--border-2); background: var(--surface-2);
  font-family: var(--sans); font-size: 11px; font-weight: 600;
  color: var(--ink-2); cursor: pointer; transition: background .1s, border-color .1s, color .1s;
}
.aud-pen-btn:hover:not(:disabled) { background: var(--surface-sunk); }
.aud-pen-btn:disabled { opacity: .55; cursor: default; }
.aud-pen-btn.aud-pen-r-ac.on, .aud-pen-btn.aud-pen-r-bc.on {
  background: var(--danger-soft); border-color: var(--danger); color: var(--danger);
}
.aud-pen-btn.aud-pen-l-ac.on, .aud-pen-btn.aud-pen-l-bc.on {
  background: var(--info-soft); border-color: var(--info); color: var(--info);
}
.aud-clear-btn { margin-left: auto; }

.aud-chart {
  display: block; width: 100%;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); box-shadow: var(--shadow-sm);
}
.aud-gridline { stroke: var(--border); stroke-width: 1; }
.aud-axis-label {
  font-family: var(--mono); font-size: 9.5px; fill: var(--muted);
}
.aud-axis-title {
  font-family: var(--sans); font-size: 9.5px; font-weight: 700;
  letter-spacing: .5px; text-transform: uppercase; fill: var(--muted);
}

.aud-line { fill: none; stroke-width: 1.75; }
.aud-line-r { stroke: var(--danger); }
.aud-line-l { stroke: var(--info); }

.aud-pt-r { fill: var(--surface); stroke: var(--danger); stroke-width: 2; }
.aud-pt-l line { stroke: var(--info); stroke-width: 2; stroke-linecap: round; }

.aud-bc {
  fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 3 2;
}
.aud-bc-r { stroke: var(--danger); }
.aud-bc-l { stroke: var(--info); }

.aud-click-target { fill: transparent; cursor: crosshair; }

.aud-hint { font-size: 11px; color: var(--muted); margin: 0 2px; }

/* ── Lens advisor edge-thickness visualisation (OPT-303) ─────────────────────
   Per-card, per-eye SVG diagram built from GET /lens-thickness/:rx-uuid. The
   "band" is one evenodd-fill path (true contour + an outward-offset contour)
   so the space between the two reads as a variable-width edge; the R/L
   accent colours deliberately reuse the audiogram's --danger (right)/--info
   (left) convention above rather than inventing new ones. Kept calm/muted —
   this is patient-facing. */
.lens-thick-wrap { margin-top: 20px; }

.lens-thick-eyes {
  display: flex; flex-wrap: wrap; gap: 24px; justify-content: flex-start;
}

.lens-thick-eye { display: flex; flex-direction: column; align-items: center; gap: 4px; }

.lens-thick-svg { display: block; flex: 0 0 auto; }

.lens-thick-blank {
  fill: none; stroke: var(--border-2); stroke-width: 1.5; stroke-dasharray: 4 3;
}

.lens-thick-contour { fill: none; stroke: var(--ink-2); stroke-width: 1; }

.lens-thick-band { stroke-width: 1.5; fill-opacity: .16; }
.lens-thick-band.lens-thick-r { stroke: var(--danger); fill: var(--danger); }
.lens-thick-band.lens-thick-l { stroke: var(--info); fill: var(--info); }

.lens-thick-oc { stroke: var(--ink-1); stroke-width: 1.5; }

.lens-thick-label {
  font-family: var(--mono); font-size: 11px; font-weight: 500;
  fill: var(--muted); dominant-baseline: middle;
}
.lens-thick-label-max { font-size: 13px; font-weight: 700; fill: var(--danger); }

.lens-thick-eye-name {
  font-size: 13px; font-weight: 600; color: var(--muted); margin: 0;
}
.lens-thick-centre {
  font-size: 13px; font-weight: 600; color: var(--ink-2); margin: 0;
}

.lens-thick-status {
  font-size: 16px; color: var(--muted); display: flex; align-items: center; gap: 12px;
}
.lens-thick-retry { flex: 0 0 auto; }

.lens-thick-badge {
  margin-top: 14px; padding: 8px 14px; border-radius: var(--r-sm);
  background: var(--warn-soft); color: var(--warn); font-size: 14px; font-weight: 600;
  display: inline-block;
}

.lens-thick-warnings { margin-top: 10px; }
.lens-thick-warning { font-size: 13px; color: var(--muted); font-style: italic; margin: 2px 0; }

.lens-thick-collapsed {
  margin: 10px 0 0; font-size: 15px; font-weight: 600; color: var(--ink-2);
}

/* ── Document scanner ─────────────────────────────────────────────────────── */
.scan-page {
  flex: 1; min-height: 0;
  display: flex; flex-direction: column;
  background: #000; color: #fff;
  position: relative; overflow: hidden;
}
.scan-viewport {
  flex: 1; min-height: 0;
  position: relative; overflow: hidden;
}
.scan-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.scan-overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
  z-index: 2;
}
.scan-frame {
  position: relative;
  width: min(80vw, 480px);
  aspect-ratio: 3 / 4;
}
/* corner bracket marks */
.sc {
  position: absolute;
  width: 22px; height: 22px;
  border-color: rgba(255,255,255,0.85);
  border-style: solid;
}
.sc-tl { top: 0; left: 0;  border-width: 2px 0 0 2px; }
.sc-tr { top: 0; right: 0; border-width: 2px 2px 0 0; }
.sc-bl { bottom: 0; left: 0;  border-width: 0 0 2px 2px; }
.sc-br { bottom: 0; right: 0; border-width: 0 2px 2px 0; }
.scan-toolbar {
  flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  padding: 20px 24px 28px;
  background: rgba(0,0,0,0.6);
  backdrop-filter: blur(4px);
}
.scan-capture {
  width: 64px; height: 64px; border-radius: 50%;
  background: #fff; border: 3px solid rgba(255,255,255,0.4);
  box-shadow: 0 0 0 6px rgba(255,255,255,0.15);
  cursor: pointer;
  padding: 0;
  display: flex; align-items: center; justify-content: center;
  color: #555; font-size: 10px; font-weight: 700; letter-spacing: 0.02em;
  transition: transform 80ms ease;
}
.scan-capture:active { transform: scale(0.93); }
.scan-error {
  flex: 1; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 10px;
  color: rgba(255,255,255,0.7); font-size: 15px; text-align: center;
  padding: 32px;
}
.scan-error-icon { font-size: 48px; }
.scan-error-hint { font-size: 13px; color: rgba(255,255,255,0.45); max-width: 280px; }
.scan-starting {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: rgba(255,255,255,0.5); font-size: 14px;
  pointer-events: none;
}
.scan-frozen {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  z-index: 1;
}
@keyframes scan-spin { to { transform: rotate(360deg); } }
.scan-spinner {
  width: 36px; height: 36px;
  border: 3px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: scan-spin 0.7s linear infinite;
  margin: 4px auto 2px;
}
/* ── Scan result overlay ───────────────────────────────────────────────────── */
.scan-result-overlay {
  position: absolute; inset: 0;
  background: rgba(0,0,0,0.55);
  display: flex; flex-direction: column;
  align-items: stretch; justify-content: flex-end;
  z-index: 10;
}
.scan-result-card {
  background: var(--surface);
  color: var(--ink);
  border-radius: 16px 16px 0 0;
  padding: 20px 20px max(28px, env(safe-area-inset-bottom));
  display: flex; flex-direction: column; gap: 14px;
}
.scan-result-label {
  font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-2); margin: 0;
}
.scan-result-text {
  font-size: 15px; line-height: 1.55;
  color: var(--ink); margin: 0;
}
.scan-result-actions {
  display: flex; gap: 10px;
}
.scan-result-actions .btn { flex: 1; justify-content: center; }

/* ── Previous-exam per-field preview (OPT-238, Option A) ──────────────────── */
.prev-wrap { position: relative; }
.prev-ghost {
  margin-top: 2px;
  padding: 0;
  border: none;
  background: none;
  font-family: var(--mono);
  font-size: 10.5px;
  line-height: 1.3;
  color: var(--muted);
  opacity: .72;
  cursor: pointer;
  text-align: left;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  transition: opacity .12s ease, color .12s ease;
}
.prev-ghost:hover,
.prev-ghost:focus-visible {
  opacity: 1;
  color: var(--accent-ink);
  outline: none;
}
.prev-ghost:focus-visible { text-decoration: underline; }

.prev-popover {
  position: absolute;
  z-index: 30;
  top: calc(100% + 4px);
  left: 0;
  min-width: 220px;
  max-width: 320px;
  padding: 8px;
  background: var(--surface);
  border: 1px solid var(--border-2);
  border-radius: 8px;
  box-shadow: var(--shadow);
}
.prev-pop-head {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .4px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 6px;
}
.prev-pop-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 2px;
  border-top: 1px solid var(--border);
}
.prev-pop-row:first-of-type { border-top: none; }
.prev-pop-date {
  flex: none;
  font-family: var(--mono);
  font-size: 10.5px;
  color: var(--muted);
}
.prev-pop-val {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 12px;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.prev-pop-import {
  flex: none;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--on-accent, #fff);
  background: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 5px;
  cursor: pointer;
}
.prev-pop-import:hover { background: var(--accent-strong); border-color: var(--accent-strong); }

/* ── OPT-402: CL exam prescription + trial-lens multi-select ──────────────────
   The BVD-compensated CL Rx grid (per-eye R/L rows of sph/cyl/axis/add), a
   record-only note, and the searchable trial-lens chips/candidates (chip
   styling mirrors the .nhs-reasons btn pattern — class-carrying only). */
.cl-rx-grid { display: flex; flex-direction: column; gap: 6px; }
.cl-rx-row { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.cl-rx-eye {
  min-width: 18px; font-weight: 700; font-family: var(--mono);
  color: var(--muted); padding-bottom: 6px;
}
.cl-rx-cell { display: flex; flex-direction: column; gap: 2px; }
.cl-rx-cell > span { font-size: 10px; text-transform: uppercase; color: var(--muted); }
.cl-rx-cell > input { width: 74px; font-family: var(--mono); }
.cl-rx-note { font-size: 11px; color: var(--muted); margin: 4px 0 0; line-height: 1.4; }

.cl-trial-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 6px; }
.cl-trial-candidates { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }

/* Dispense CL picker heading + source toggle (in-date exam lenses vs full
   catalogue). */
.cl-pk-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 8px; padding: 4px 2px 8px;
}
.cl-pk-heading {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--muted); font-weight: 700;
}

/* OPT-405: split marketing consent toggles (patient contact-info) */
.consent-block {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 8px;
}
.consent-label {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
  cursor: pointer;
}
.consent-label input.consent-email,
.consent-label input.consent-sms {
  width: auto;
  margin: 0;
  cursor: pointer;
}

/* == Knowledge base (OPT-613) ================================================
   Page shell, browse/search, article read view, edit form, history panel --
   plus the OPT-612 editor component classes (kb-editor/kb-toolbar/kb-doc/
   kb-caret/kb-selected/kb-shadow-input/kb-link-dialog), which shipped with
   no CSS of their own until this ticket gave the editor a real page to live
   on. Warm-paper tokens throughout (var(--surface), var(--border), ...) so
   both themes fall out for free -- see the design tokens block at the top
   of this file. */

.kb-page {
  padding: 20px 24px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

.kb-toolbar-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}

/* -- Search box -------------------------------------------------------- */
.kb-search-box {
  position: relative;
  max-width: var(--field-max);
  margin-bottom: 14px;
}
.kb-search-box input {
  width: 100%;
  padding-right: 28px;
}
.kb-search-box .icon-btn {
  position: absolute;
  right: 2px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  border: none;
  background: transparent;
}

/* -- Active filter badges ------------------------------------------------ */
.kb-active-filters {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}
.kb-filter-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11.5px;
  font-weight: 600;
  padding: 4px 8px 4px 10px;
  border-radius: 40px;
  background: var(--accent-soft);
  color: var(--accent-ink);
  border: 1px solid var(--accent-soft-2);
}
.kb-filter-x {
  border: none;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: 13px;
  line-height: 1;
  padding: 0 2px;
}
.kb-filter-x:hover { opacity: .65; }

/* -- Empty / seed state --------------------------------------------------- */
.kb-empty-seed { display: flex; flex-direction: column; align-items: center; gap: 16px; padding: 20px 0; }

.kb-template-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 10px;
  padding: 14px;
}
.kb-template-modal { width: min(620px, 100%); }
.kb-template-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  text-align: left;
  padding: 12px 14px;
  border-radius: var(--r);
  border: 1px solid var(--border-2);
  background: var(--surface);
  cursor: pointer;
  font-family: var(--sans);
  transition: background .12s, border-color .12s;
}
.kb-template-btn:hover { background: var(--surface-sunk); border-color: var(--accent); }
.kb-template-label { font-size: 13px; font-weight: 600; color: var(--ink); }
.kb-template-cat { font-size: 11px; color: var(--muted); }

/* -- Browse: category sections -------------------------------------------- */
.kb-sections { display: flex; flex-direction: column; gap: 20px; }
.kb-section { display: flex; flex-direction: column; gap: 6px; }
.kb-cat-header {
  align-self: flex-start;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 2px 0;
}
.kb-cat-header:hover { color: var(--ink-2); }
.kb-cat-header.active { color: var(--accent-ink); }

.kb-article-list, .kb-search-results { display: flex; flex-direction: column; gap: 8px; }

.kb-article-row {
  padding: 12px 14px;
  border-radius: var(--r);
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.kb-article-row:hover { background: var(--surface-sunk); border-color: var(--border-strong); }
.kb-article-row-main {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.kb-article-title { font-size: 14px; font-weight: 600; color: var(--ink); }
.kb-article-snippet { font-size: 12.5px; color: var(--ink-2); margin: 4px 0 0; line-height: 1.4; }
.kb-tags-row { margin: 6px 0 0; }
.kb-tags-row:empty { display: none; }
.kb-article-meta { font-size: 11px; color: var(--muted); margin-top: 6px; }

.kb-category-badge {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .03em;
  padding: 2px 8px;
  border-radius: 40px;
  background: var(--sand-soft);
  color: var(--ink-2);
}

/* -- Article (read) view --------------------------------------------------- */
.kb-article { max-width: 780px; }
.kb-article-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.kb-article-actions { display: flex; gap: 8px; }
.kb-article-subhead {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 8px 0 2px;
  flex-wrap: wrap;
}
.kb-meta-line { font-size: 11.5px; color: var(--muted); margin-bottom: 16px; }

/* -- Rendered markdown body (shared by the read view, the WYSIWYG editor
   surface, and the history preview -- all three mount the [:div.kb-doc ...]
   output of optos.kb.markdown/render or optos.kb.editor). -- */
.kb-doc-wrap { padding-top: 4px; }
.kb-doc { font-size: 13.5px; line-height: 1.6; color: var(--ink); }
.kb-doc h1, .kb-doc h2, .kb-doc h3 {
  font-family: var(--serif);
  font-weight: 500;
  color: var(--ink);
  margin: 18px 0 8px;
  line-height: 1.25;
}
.kb-doc h1:first-child, .kb-doc h2:first-child, .kb-doc h3:first-child { margin-top: 0; }
.kb-doc h1 { font-size: 22px; }
.kb-doc h2 { font-size: 18px; }
.kb-doc h3 { font-size: 15px; }
.kb-doc p { margin: 0 0 10px; }
.kb-doc ul, .kb-doc ol { margin: 0 0 10px; padding-left: 22px; }
.kb-doc li { margin-bottom: 3px; }
.kb-doc strong { font-weight: 600; }
.kb-doc em { font-style: italic; }
.kb-doc code {
  font-family: var(--mono);
  font-size: 12px;
  background: var(--surface-sunk);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
}
.kb-doc a { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px; }
.kb-doc img { max-width: 100%; border-radius: var(--r-sm); margin: 6px 0; }
.kb-doc table { border-collapse: collapse; width: 100%; margin: 0 0 12px; font-size: 12.5px; }
.kb-doc th, .kb-doc td { border: 1px solid var(--border-2); padding: 6px 9px; text-align: left; }
.kb-doc th { background: var(--surface-2); font-weight: 600; }
.kb-doc blockquote.kb-callout {
  margin: 0 0 10px;
  padding: 8px 12px;
  border-left: 3px solid var(--accent);
  background: var(--accent-soft);
  color: var(--ink-2);
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
}

/* -- Article print (OPT-664) ----------------------------------------------
   Ctrl+P / the article-view "Print" button below (kb_views.cljs) print the
   article that is CURRENTLY open only -- browse/edit/history are separate
   `case` branches of kb-page, so nothing else is ever mounted alongside
   `.kb-article` to gate against, unlike the Rx/referral pair in the "Print
   foundation" block above (which DOES need a body class, since both areas
   mount on the same exam page at once). `.kb-article-header` (Back /
   History / Print / Edit) is marked `.no-print` at the call site rather
   than re-selected here -- the same belt-and-braces convention
   `print-views/letterhead-hint` already uses. Every var(--ink)/
   var(--border)/var(--surface-*)/var(--accent*) colour `.kb-doc` reads for
   its everyday on-screen styling is forced print-safe by the `:root`
   override in the "Print foundation" block (OPT-664) -- this block only
   adds what that override can't: layout/position, page-break behaviour,
   and print-specific sizing. */
@media print {
  .kb-article, .kb-article * { visibility: visible; }
  .kb-article {
    position: fixed;
    top: 0; left: 0;
    width: 100%;
    max-width: none;
    padding: 24px 32px;
    background: #fff;
  }
  .kb-article .page-title {
    font-family: var(--serif);
    font-size: 22px;
    color: #000;
    margin-bottom: 4px;
  }
  .kb-meta-line { color: #555; }
  .kb-doc { font-size: 12.5px; }
  .kb-doc h1, .kb-doc h2, .kb-doc h3 {
    break-after: avoid-page;
    page-break-after: avoid;
  }
  .kb-doc table, .kb-doc blockquote.kb-callout, .kb-doc pre {
    page-break-inside: avoid;
  }
  .kb-doc tr {
    break-inside: avoid;
    page-break-inside: avoid;
  }
  .kb-doc img { max-width: 100% !important; }
  .kb-doc, .kb-doc * {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

/* -- Edit form -------------------------------------------------------------- */
.kb-edit { max-width: 780px; }
.kb-edit-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 14px;
}
.kb-edit-actions { display: flex; gap: 8px; }

.kb-chip-field { margin-bottom: 14px; }
.kb-editable-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  cursor: default;
}
.kb-chip-x {
  border: none;
  background: transparent;
  color: inherit;
  cursor: pointer;
  font-size: 12px;
  line-height: 1;
  padding: 0;
  opacity: .7;
}
.kb-chip-x:hover { opacity: 1; color: var(--danger); }

.kb-editor-field { margin: 4px 0 14px; }

.kb-advanced {
  margin-top: 6px;
  border: 1px solid var(--border);
  border-radius: var(--r);
  padding: 2px 12px;
  background: var(--surface-2);
}
.kb-advanced summary {
  cursor: pointer;
  padding: 10px 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-2);
  user-select: none;
}
.kb-advanced[open] { padding-bottom: 12px; }
.kb-data-textarea {
  width: 100%;
  min-height: 90px;
  font-family: var(--mono);
  font-size: 12px;
  resize: vertical;
}

/* -- History panel ------------------------------------------------------ */
.kb-history-header {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-bottom: 14px;
}
.kb-revision-list { display: flex; flex-direction: column; gap: 6px; max-width: 620px; }
.kb-revision-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface);
  cursor: pointer;
}
.kb-revision-row:hover { background: var(--surface-sunk); border-color: var(--border-strong); }
.kb-revision-title { font-size: 13px; font-weight: 600; color: var(--ink); }
.kb-revision-meta { font-size: 11px; color: var(--muted); }
.kb-revision-preview { max-width: 780px; }
.kb-revision-preview-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 10px;
}
.kb-revision-preview-h h2 { font-family: var(--serif); font-weight: 500; font-size: 18px; color: var(--ink); }

/* == OPT-612 editor component (kb-editor) -- unstyled until now =========== */
.kb-editor {
  border: 1px solid var(--border-2);
  border-radius: var(--r);
  background: var(--surface);
  overflow: hidden;
  position: relative;
}
.kb-toolbar {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  flex-wrap: wrap;
}
.kb-toolbar button {
  font-family: var(--sans);
  font-size: 12px;
  font-weight: 600;
  padding: 4px 9px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
}
.kb-toolbar button:hover { background: var(--surface-sunk); border-color: var(--border-2); color: var(--ink); }
.kb-link-dialog {
  display: flex;
  gap: 6px;
  padding: 8px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  flex-wrap: wrap;
}
.kb-link-dialog input {
  flex: 1;
  min-width: 120px;
  font-size: 12px;
  padding: 5px 8px;
}
.kb-link-dialog button {
  font-size: 12px;
  padding: 5px 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border-2);
  background: var(--surface);
  cursor: pointer;
}
.kb-surface {
  padding: 14px 16px;
  min-height: 220px;
  max-height: 55vh;
  overflow-y: auto;
  cursor: text;
}
.kb-selected { background: var(--accent-soft-2); border-radius: 2px; }
.kb-caret {
  display: inline-block;
  width: 1px;
  margin-left: -1px;
  height: 1.1em;
  vertical-align: text-bottom;
  background: var(--accent);
  animation: kb-blink 1s step-end infinite;
}
@keyframes kb-blink { 50% { opacity: 0; } }
.kb-shadow-input { position: absolute; opacity: 0; width: 1px; height: 1px; padding: 0; border: none; overflow: hidden; }

/* ══════════════════════════════════════════════════════════════════════
   FLOATING WIDGETS ABOVE THE MOBILE TAB BAR (OPT-815)
   ══════════════════════════════════════════════════════════════════════
   Two widgets were added later than the ≤640px bottom-tab-bar work and never
   joined its stacking ladder, because both carried their position as an INLINE
   style — which no media query can override. That is the whole defect:

     - the Sales invoice-ready toast sat at bottom:18px z-index:50, i.e. UNDER
       the z-index:300 tab bar. Its Print button was unreachable on a phone.
     - the licence trial/grace panel sat at bottom:16px, directly over the bar.
       It is showing to real users during the trial right now.

   THE LADDER — offsets above the 62px tab bar, 56px apart (44px target + gap):

     slot 0   bottom  74px   .device-chip     (hardcoded in the ≤640px block)
     slot 1   bottom 130px   .license-panel
     slot 2   bottom 186px   .invoice-print-toast

   OPT-827 moved .notif-bell OUT of this ladder to the top of the screen (user
   feedback: a to-do count is glanced at, not reached for) and the remaining two
   closed up rather than leaving a hole at 130px — a gap that later reads as a
   free slot is exactly how two widgets end up stacked on each other.

   Slots 0 and 1 predate this and keep their hardcoded offsets: their markup
   lives in views.cljs/transfers.cljs, outside this ticket's file scope. Folding
   all four onto one shared base class (the --fw-order idea in the ticket) needs
   those files, so it is left to OPT-822, which owns shared patterns. A base
   class applied to only half the members would be a worse lie than none.
   ANY NEW floating widget takes the next slot and is listed above.

   Desktop is deliberately UNCHANGED: same offsets and the same z-index each
   widget had inline (50 and 800). 800 is far above this stylesheet's maximum of
   320, so the licence panel still outranks every modal exactly as before —
   changing that is not this ticket's call. */

.invoice-print-toast {
  position: fixed;
  bottom: 18px;
  right: 18px;
  z-index: 50;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 14px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.15);
  display: flex;
  gap: 10px;
  align-items: center;
}

.license-panel {
  position: fixed;
  bottom: 16px;
  right: 16px;
  z-index: 800;
  background: var(--bg, #faf7f0);
  color: var(--text-primary, #2c2a25);
  border: 1px solid var(--border, #ddd6c8);
  border-radius: var(--r-md, 10px);
  box-shadow: 0 6px 20px rgba(0,0,0,0.18);
  width: 260px;
  max-width: 90vw;
  font-family: var(--sans);
}

@media (max-width: 640px) {
  .license-panel {
    bottom: var(--fw-slot-1);                 /* slot 1 (was 2, see ladder) */
    right: 12px;
    width: min(260px, calc(100vw - 24px));
  }
  .invoice-print-toast {
    bottom: var(--fw-slot-2);                 /* slot 2 (was 3, see ladder) */
    right: 12px;
    z-index: 310;                             /* clear the z-index:300 tab bar */
    width: min(280px, calc(100vw - 24px));
  }
}

/* ══════════════════════════════════════════════════════════════════════
   ACCORDION LIST — the mobile form of every desktop list (OPT-822)
   ══════════════════════════════════════════════════════════════════════
   Product direction: mobile is a CONDENSED view, not a reflowed desktop. A
   desktop list row becomes a PREVIEW that fits between the margins — never
   sideways-scrolling — which the user expands individually for the full record.

   BUILT ON <details>/<summary>, deliberately:
     - no state, no re-frame plumbing, no JS, so it cannot desync from app state
     - keyboard and screen-reader behaviour is native and correct for free
     - it survives with JavaScript disabled
   These are dense CLINICAL lists; a hand-rolled toggle would be more code and
   worse behaviour.

   THE MARKUP THIS EXPECTS (applied per page — this file cannot add markup, which
   is why OPT-822 is CSS-only and OPT-823 applies it to Collections first):

     [:div.acc-list                          ; narrow screens only
      [:details.acc-item
       [:summary.acc-summary
        [:span.acc-title   "Patient name"]   ; line 1
        [:span.acc-context "Ready · £120"]]  ; line 2 — ONE line, ellipsised
       [:div.acc-detail  ...full record + actions...]]]
     [:div.table-scroll.acc-wide  [:table ...]]   ; wide screens only

   .acc-list and .acc-wide are a matched pair: exactly one is visible at any
   width, so a page renders both and lets CSS choose. That costs some duplicate
   DOM, which is the right trade for these list sizes against the alternative of
   a JS width-switch that can disagree with the CSS.

   FITTING BETWEEN THE MARGINS is the fiddly part and the whole point: every text
   child needs min-width:0 (a grid/flex child's default min-width is auto, i.e.
   its content, so long patient names would push the row wider than the viewport
   no matter what overflow rules say) plus ellipsis. */

.acc-list { display: none; }
.acc-wide { display: block; }

@media (max-width: 640px) {
  .acc-list {
    display: flex;
    flex-direction: column;
    gap: 6px;              /* condensed: desktop rows breathe more than this */
  }
  .acc-wide { display: none; }
}

.acc-item {
  border: 1px solid var(--border);
  border-radius: var(--r-sm, 8px);
  background: var(--surface);
  overflow: hidden;        /* keeps the summary's focus ring inside the radius */
}
.acc-item + .acc-item { margin-top: 0; }   /* spacing comes from .acc-list's gap */

.acc-summary {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 7px 10px;       /* condensed */
  min-height: 44px;        /* touch target (OPT-814) */
  cursor: pointer;
  list-style: none;        /* Firefox's default marker */
}
.acc-summary::-webkit-details-marker { display: none; }   /* Safari/Chrome's */
.acc-summary:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* Both lines truncate rather than wrap: a preview that grows to two or three
   lines stops being a preview, and wrapping is what makes a list feel long. */
.acc-title,
.acc-context {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.acc-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink);
  line-height: 1.25;
}
.acc-context {
  grid-column: 1;          /* under the title, not beside it */
  font-size: 12px;
  color: var(--muted);
  line-height: 1.3;
}

/* Chevron: rotates on open. Lives in column 2, spanning both rows. */
.acc-summary::after {
  content: "";
  grid-column: 2;
  grid-row: 1 / -1;
  width: 8px;
  height: 8px;
  border-right: 2px solid var(--muted);
  border-bottom: 2px solid var(--muted);
  transform: rotate(45deg);
  transform-origin: center;
  transition: transform .12s ease;
  margin-right: 4px;
}
.acc-item[open] > .acc-summary::after { transform: rotate(-135deg); }

.acc-detail {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 2px 10px 10px;
  border-top: 1px solid var(--border);
  font-size: 13px;
}
/* Label/value pairs inside an expanded record. */
.acc-field { display: flex; gap: 8px; align-items: baseline; }
.acc-field > .acc-label {
  flex: 0 0 auto;
  min-width: 74px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .3px;
  color: var(--muted);
}
.acc-field > .acc-value { min-width: 0; overflow-wrap: anywhere; }
/* Action buttons sit on their own row and wrap rather than overflow. */
.acc-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.acc-actions .btn { flex: 1 1 auto; }

/* ── OPT-823: Collections page tab bar on narrow screens ─────────────────────
   User report: "divs hang over the edge of a narrow screen" on this page. The
   three tabs — Jobs in Progress / Awaiting Collection / Collected, two of them
   carrying a count badge — sit in a plain `display:flex` row with no wrap and no
   overflow handling, so at 375px they run off the side with nothing to reveal
   them.

   SPECIFICITY, not source order, is what makes these apply. `.ord-page-tabs` and
   `.ord-tab` are defined in ordering.css, which index.html loads AFTER style.css
   (fonts, style, ordering, timesheet), so an equal-specificity rule written here
   would lose no matter where in this file it sits. Scoping to `.recalls-page`
   (0,3,0 vs ordering.css's 0,2,0) wins on specificity and confines the change to
   this page — ordering.css itself is outside this ticket's file scope.

   A single scrollable row follows the precedent OPT-617 set for the admin tab
   bar: at this width a wrapped stack of tabs costs more vertical space than it
   saves, and vertical space is the thing the accordion work is buying back. */
@media (max-width: 640px) {
  .recalls-page .ord-page-tabs {
    padding: 12px 12px 0;
    gap: 4px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .recalls-page .ord-page-tabs::-webkit-scrollbar { display: none; }
  .recalls-page .ord-page-tabs .ord-tab {
    flex-shrink: 0;              /* or they compress instead of scrolling */
    padding: 10px 12px;
    min-height: 44px;            /* touch target, per OPT-814 */
    white-space: nowrap;
  }
  /* The section below the tabs gets the page's gutters back. */
  .recalls-page .recalls-section { padding-left: 12px; padding-right: 12px; }
  .recalls-page .recalls-toolbar { flex-wrap: wrap; gap: 8px; }
}

/* ══════════════════════════════════════════════════════════════════════
   DIARY PAGE ON A PHONE — diary-first, tabbed (OPT-828)
   ══════════════════════════════════════════════════════════════════════
   The diary is the main thing on a phone. The three columns used to stack at
   ≤640px, so reaching the timeline meant scrolling past the entire patient
   record form — which was itself capped at 280px with its own nested scrollbar,
   a scroll trap inside a scrolling page. Now exactly ONE column shows at a time.

   SCOPED TO .diary-workspace ON PURPOSE. .pd-workspace is shared with the exam
   page (exam_views.cljs:1781) and audiology (audiology_views.cljs:631), both of
   which mount the same patient-panel in their own .pd-col.left. Rules written
   against .pd-workspace would silently restyle both of those pages too; only the
   diary page carries the extra .diary-workspace class.

   Visibility comes from a class on the workspace rather than conditional
   rendering, so one markup tree serves both widths and no JS width-switch can
   disagree with this stylesheet. Above 640px every rule below is inert and the
   3-column grid is exactly as it was. */

.diary-mtabs { display: none; }
.px-search-overlay { display: none; }

@media (max-width: 640px) {
  .diary-mtabs {
    display: flex;
    gap: 4px;
    align-items: center;
    padding: 8px 12px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    overflow-x: auto;
    scrollbar-width: none;
    flex-shrink: 0;
  }
  .diary-mtabs::-webkit-scrollbar { display: none; }
  .dm-tab, .dm-search {
    flex-shrink: 0;
    min-height: 44px;              /* touch target, per OPT-814 */
    padding: 8px 14px;
    border-radius: 999px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--ink-2);
    font-family: var(--sans);
    font-size: 13px;
    cursor: pointer;
  }
  .dm-tab.active {
    border-color: var(--accent);
    background: var(--accent-soft);
    color: var(--accent-ink, var(--accent));
    font-weight: 600;
  }
  .dm-search { margin-left: auto; }

  /* Exactly one column at a time. Direct-child selectors so a nested
     .pd-col (the exam page mounts one inside its own layout) is never hit. */
  .diary-workspace.m-tab-diary   > .pd-col.left,
  .diary-workspace.m-tab-diary   > .pd-col.right,
  .diary-workspace.m-tab-patient > .pd-col.center,
  .diary-workspace.m-tab-patient > .pd-col.right,
  .diary-workspace.m-tab-more    > .pd-col.left,
  .diary-workspace.m-tab-more    > .pd-col.center { display: none; }

  /* The surviving column owns the viewport: no 280px cap, no nested scrollbar,
     no leftover column borders now that nothing sits beside it. */
  .diary-workspace > .pd-col.left {
    max-height: none;
    overflow-y: visible;
    border-bottom: none;
  }
  .diary-workspace > .pd-col.right { border-top: none; }

  /* ── Patient search: an overlay sheet, not a column ── */
  .px-search-overlay {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 900;                  /* over the 300 tab bar and the 310 widgets;
                                      below the licence lock overlay's 9999 */
    background: rgba(20,18,14,.45);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .px-search-sheet {
    background: var(--surface);
    border-top-left-radius: var(--r-lg, 14px);
    border-top-right-radius: var(--r-lg, 14px);
    margin-top: 48px;
    min-height: calc(100% - 48px);
    padding-bottom: 76px;          /* clear the fixed bottom tab bar */
  }
  .px-search-sheet-head {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--border);
    background: var(--surface);
    font-weight: 600;
  }
}

/* ── OPT-830: stock-take page shell ──────────────────────────────────────────
   .stocktake-page (stocktake_views.cljs:385) had NO rule in ANY stylesheet —
   style.css, ordering.css and timesheet.css all checked — so it inherited
   nothing and its content sat flush against the viewport edge.

   Values are taken from the page shells that already exist rather than invented:
   .recalls-page and .compat-page both use 20px 24px, and div.admin drops to
   16px 12px at ≤640px. Matching them is the point — a third set of gutters is
   exactly the incoherence this program is removing.

   flex/min-height/overflow mirror .recalls-page too: the page is a flex child of
   div.page, so without min-height:0 it cannot shrink and its own scrollbar never
   appears. Its inner .section blocks carry only a margin, no padding, so there
   is nothing here to double up with. */
.stocktake-page {
  padding: 20px 24px;
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}
@media (max-width: 640px) {
  .stocktake-page { padding: 16px 12px; }
}
