/* ==========================================================================
   Component layer — cards, buttons, badges, tables, skeletons, toasts,
   patient cards, chair board, KPIs, charts, modals
   ========================================================================== */

/* --- Cards --- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
  overflow: hidden;
  transition: box-shadow var(--duration) var(--ease), border-color var(--duration) var(--ease);
}
.card--hover:hover { box-shadow: var(--shadow); border-color: var(--border-strong); }
.card__header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border);
}
.card__title {
  font-size: var(--text-base); font-weight: var(--weight-semibold);
  /* Package and patient names can be very long; the card's own actions must
     stay reachable, so the title truncates instead of winning the fight. */
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.card__subtitle { overflow-wrap: anywhere; }
.card__subtitle { font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px; }
.card__body { padding: var(--space-5); }
.card__body--flush { padding: 0; }
.card__footer {
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--border); background: var(--surface-2);
}

/* --- Grid helpers --- */

.grid { display: grid; gap: var(--space-4); }
.grid--kpi   { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.grid--2     { grid-template-columns: repeat(auto-fit, minmax(380px, 1fr)); }
.grid--3     { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.grid--cards { grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); }
.grid--chairs{ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.stack { display: flex; flex-direction: column; gap: var(--space-4); }
.row { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.spacer { flex: 1; }

/* --- Buttons --- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
  height: 36px; padding: 0 var(--space-4);
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font-size: var(--text-md); font-weight: var(--weight-medium); white-space: nowrap;
  transition: background var(--duration-fast) var(--ease),
              border-color var(--duration-fast) var(--ease),
              transform var(--duration-fast) var(--ease);
}
.btn:hover { background: var(--surface-hover); border-color: var(--border-strong); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
.btn svg { width: 16px; height: 16px; }

.btn--primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.btn--primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.btn--accent { background: var(--accent); border-color: var(--accent); color: #10310a; }
.btn--accent:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn--danger { background: var(--red-500); border-color: var(--red-500); color: #fff; }
.btn--danger:hover { background: var(--red-600); border-color: var(--red-600); }
.btn--ghost { background: transparent; border-color: transparent; color: var(--text-secondary); }
.btn--ghost:hover { background: var(--surface-hover); color: var(--text); }
.btn--sm { height: 30px; padding: 0 var(--space-3); font-size: var(--text-sm); }
.btn--xs { height: 26px; padding: 0 var(--space-2); font-size: var(--text-xs); border-radius: var(--radius-sm); }
.btn--icon { width: 30px; padding: 0; }
.btn--block { width: 100%; }

/* --- Segmented control (filters, calendar views) --- */

.segmented {
  display: inline-flex; padding: 3px; gap: 2px;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); flex-wrap: wrap;
}
.segmented__btn {
  padding: 5px var(--space-3); border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--text-secondary);
  font-size: var(--text-sm); font-weight: var(--weight-medium); white-space: nowrap;
  transition: background var(--duration-fast) var(--ease), color var(--duration-fast) var(--ease);
}
.segmented__btn:hover { color: var(--text); }
.segmented__btn[aria-pressed="true"] {
  background: var(--surface); color: var(--text);
  box-shadow: var(--shadow-xs); font-weight: var(--weight-semibold);
}

/* --- Badges & pills --- */

.badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: var(--radius-full);
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  letter-spacing: 0.01em; white-space: nowrap;
  background: var(--tone-neutral-bg); color: var(--tone-neutral-fg);
  /* A badge carrying a long insurer or condition name used to stretch its
     row until the buttons beside it were pushed off the card. It now caps
     its own width and tells the truth on hover. */
  max-width: 22ch; overflow: hidden; text-overflow: ellipsis;
  min-width: 0; flex-shrink: 1;
}
.badge svg { width: 11px; height: 11px; }
.badge--ok      { background: var(--tone-ok-bg);      color: var(--tone-ok-fg); }
.badge--warn    { background: var(--tone-warn-bg);    color: var(--tone-warn-fg); }
.badge--danger  { background: var(--tone-danger-bg);  color: var(--tone-danger-fg); }
.badge--info    { background: var(--tone-info-bg);    color: var(--tone-info-fg); }
.badge--brand   { background: var(--tone-brand-bg);   color: var(--tone-brand-fg); }
.badge--muted   { background: var(--tone-muted-bg);   color: var(--tone-muted-fg); }
.badge--neutral { background: var(--tone-neutral-bg); color: var(--tone-neutral-fg); }
.badge--dot::before {
  content: ''; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex-shrink: 0;
}

/* --- KPI tiles --- */

.kpi {
  padding: var(--space-4) var(--space-5);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-xs);
  transition: box-shadow var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.kpi:hover { box-shadow: var(--shadow-sm); transform: translateY(-1px); }
.kpi__label {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-xs); font-weight: var(--weight-medium);
  color: var(--text-muted); text-transform: uppercase; letter-spacing: var(--tracking-wide);
  min-width: 0; overflow-wrap: anywhere;
}
.kpi__meta { overflow-wrap: anywhere; }
/* The tile is a flex/grid child; without this it refuses to shrink at all. */
.kpi { min-width: 0; }
.kpi__value {
  margin-top: var(--space-2);
  font-size: var(--text-3xl); font-weight: var(--weight-bold);
  letter-spacing: -0.03em; line-height: 1.1;
  font-variant-numeric: tabular-nums;
  /* J$99,999,999.99 and "Chronic kidney disease" have to live in the same
     box. clamp() lets the figure shrink with the tile instead of spilling
     out of it, and long words break rather than push the card wide. */
  font-size: clamp(var(--text-xl), 2.2vw + 0.4rem, var(--text-3xl));
  overflow-wrap: anywhere; word-break: break-word;
}
.kpi__value--sm { font-size: var(--text-2xl); }
.kpi__meta { margin-top: var(--space-1); font-size: var(--text-xs); color: var(--text-secondary); }
.kpi--brand  { border-left: 3px solid var(--brand); }
.kpi--accent { border-left: 3px solid var(--accent); }
.kpi--warn   { border-left: 3px solid var(--tone-warn-border); }
.kpi--danger { border-left: 3px solid var(--tone-danger-border); }
.kpi__delta { font-weight: var(--weight-semibold); }
.kpi__delta--up   { color: var(--tone-ok-fg); }
.kpi__delta--down { color: var(--tone-danger-fg); }

/* --- Avatars --- */

.avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 38px; height: 38px; flex-shrink: 0;
  border-radius: var(--radius-full);
  font-size: var(--text-sm); font-weight: var(--weight-bold);
  color: #fff; letter-spacing: 0.02em; user-select: none;
}
.avatar--sm { width: 28px; height: 28px; font-size: var(--text-2xs); }
.avatar--lg { width: 52px; height: 52px; font-size: var(--text-lg); }
.avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }

/* --- Patient card --- */

.patient-card {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--tone-neutral-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-xs);
  transition: box-shadow var(--duration) var(--ease), transform var(--duration) var(--ease);
}
.patient-card:hover { box-shadow: var(--shadow); transform: translateY(-2px); }
.patient-card--clickable { cursor: pointer; }
.patient-card[data-status="arrived"]         { border-left-color: var(--tone-info-border); }
.patient-card[data-status="waiting"]         { border-left-color: var(--tone-warn-border); }
.patient-card[data-status="in_consultation"] { border-left-color: var(--brand); }
.patient-card[data-status="completed"]       { border-left-color: var(--tone-ok-border); }
.patient-card[data-status="cancelled"]       { border-left-color: var(--tone-muted-border); opacity: 0.65; }
.patient-card[data-status="no_show"]         { border-left-color: var(--tone-danger-border); }

.patient-card__top { display: flex; align-items: flex-start; gap: var(--space-3); }
.patient-card__id { min-width: 0; flex: 1; }
.patient-card__name {
  font-size: var(--text-base); font-weight: var(--weight-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.patient-card__demo { font-size: var(--text-xs); color: var(--text-muted); margin-top: 1px; }
.patient-card__time {
  font-size: var(--text-md); font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.patient-card__meta {
  display: grid; grid-template-columns: auto 1fr; gap: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
}
.patient-card__meta dt { color: var(--text-muted); }
.patient-card__meta dd { margin: 0; color: var(--text-secondary); font-weight: var(--weight-medium); }
.patient-card__badges { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.patient-card__actions {
  display: flex; gap: var(--space-1); padding-top: var(--space-3);
  border-top: 1px solid var(--border); flex-wrap: wrap;
}

/* --- Queue columns --- */

.queue { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-4); }
.queue__col { display: flex; flex-direction: column; gap: var(--space-3); min-width: 0; }
.queue__head {
  display: flex; align-items: center; justify-content: space-between;
  padding-bottom: var(--space-2); border-bottom: 2px solid var(--tone-neutral-border);
}
.queue__head[data-tone="info"]   { border-bottom-color: var(--tone-info-border); }
.queue__head[data-tone="warn"]   { border-bottom-color: var(--tone-warn-border); }
.queue__head[data-tone="brand"]  { border-bottom-color: var(--brand); }
.queue__head[data-tone="ok"]     { border-bottom-color: var(--tone-ok-border); }
.queue__head[data-tone="danger"] { border-bottom-color: var(--tone-danger-border); }
.queue__title {
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
}
.queue__count {
  font-size: var(--text-xs); font-weight: var(--weight-bold);
  color: var(--text-muted); font-variant-numeric: tabular-nums;
}

/* --- Dialysis chair board --- */

.chair {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-4);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-xs);
  transition: box-shadow var(--duration) var(--ease);
}
.chair:hover { box-shadow: var(--shadow); }
.chair[data-state="in_progress"] { border-color: var(--brand-border); background: linear-gradient(180deg, var(--brand-soft), var(--surface) 60%); }
.chair[data-state="available"]   { border-style: dashed; }
.chair[data-state="maintenance"] { border-color: var(--tone-danger-border); background: var(--tone-danger-bg); }
.chair__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); }
.chair__no {
  display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--radius);
  background: var(--surface-3); color: var(--text);
  font-size: var(--text-sm); font-weight: var(--weight-bold);
}
.chair[data-state="in_progress"] .chair__no { background: var(--brand); color: #fff; }
.chair__patient { font-size: var(--text-base); font-weight: var(--weight-semibold); }
.chair__machine { font-size: var(--text-xs); color: var(--text-muted); font-family: var(--font-mono); }
.chair__grid {
  display: grid; grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2) var(--space-3); font-size: var(--text-xs);
}
.chair__cell dt { color: var(--text-muted); }
.chair__cell dd { margin: 0; font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums; }
/* Container the live ticker patches in place each minute. */
.chair__progress { margin-top: auto; }
.chair__empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-2);
  padding: var(--space-6) 0; color: var(--text-muted); text-align: center;
}

/* --- Progress bars --- */

.progress {
  height: 8px; border-radius: var(--radius-full);
  background: var(--surface-3); overflow: hidden;
}
.progress__bar {
  height: 100%; border-radius: inherit;
  background: linear-gradient(90deg, var(--brand), var(--accent));
  transition: width var(--duration-slow) var(--ease-out);
}
.progress--warn .progress__bar { background: linear-gradient(90deg, var(--amber-500), var(--red-500)); }
.progress__meta {
  display: flex; justify-content: space-between;
  font-size: var(--text-2xs); color: var(--text-muted); margin-top: var(--space-1);
  font-variant-numeric: tabular-nums;
}

/* Bar for inventory stock level. */
.stock-bar { height: 6px; border-radius: var(--radius-full); background: var(--surface-3); overflow: hidden; }
.stock-bar__fill { height: 100%; background: var(--accent); border-radius: inherit; transition: width var(--duration) var(--ease); }
.stock-bar--low  .stock-bar__fill { background: var(--amber-500); }
.stock-bar--out  .stock-bar__fill { background: var(--red-500); }

/* --- Tables --- */

.table-wrap { overflow-x: auto; max-width: 100%; }
/* Cards and grid children default to min-width:auto, which refuses to shrink
   below their content — the reason a wide table pushed whole layouts sideways
   instead of scrolling inside its own card. */
.card, .view, .stack > * { min-width: 0; }
.table { width: 100%; border-collapse: collapse; font-size: var(--text-md); }
.table th {
  position: sticky; top: 0; z-index: 1;
  padding: var(--space-3) var(--space-4);
  background: var(--surface-2); border-bottom: 1px solid var(--border);
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--text-muted); text-align: left; white-space: nowrap;
}
.table td {
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
  vertical-align: middle;
  /* A pasted paragraph in a description used to make the table wider than the
     card and hide the action buttons off the right edge. Cells now wrap. */
  overflow-wrap: anywhere;
}
/* Columns that must never wrap say so explicitly (.nowrap, .mono). */
.table td.nowrap, .table th.nowrap { overflow-wrap: normal; }
/*
 * A table whose columns are decided by the LAYOUT, not by the data.
 *
 * This is the only construction that actually keeps a promise like "all the
 * columns fit". Default (automatic) table layout sizes every column from its
 * widest content, so one long value anywhere in the column quietly takes width
 * from all the others — which is what the supplier alias line did once the
 * merge gave Aval eleven former spellings. `overflow-wrap: anywhere` on the
 * cell was not enough: it stops text OVERFLOWING, it does not stop the column
 * being GREEDY, and a percentage width on the cell is only a hint that
 * automatic layout is free to overrule.
 *
 * Under fixed layout the browser reads the widths off the <col> elements and
 * stops looking at the content, so the columns are what they say they are and
 * long text wraps inside them. The cost is that the widths have to be chosen
 * deliberately, per table — which is the right trade for the two tables where
 * the data is unbounded.
 */
.table--fixed { table-layout: fixed; }
.table--fixed td { overflow-wrap: anywhere; }

/* ---- Adjustable columns (principal only) --------------------------------
   The grip is invisible until the header is hovered. A row of permanent
   handles on every table would read as clutter to the four people who cannot
   use them and cannot make them go away. */
.table--adjustable th { position: relative; }
.table--adjustable .col-grip {
  position: absolute;
  top: 0; right: -3px; bottom: 0;
  width: 7px;
  cursor: col-resize;
  opacity: 0;
  transition: opacity .12s ease, background .12s ease;
  z-index: 2;
}
.table--adjustable th:hover .col-grip { opacity: 1; background: var(--border); }
.table--adjustable .col-grip:hover,
.table--adjustable .col-grip:active { background: var(--accent, #0f6e5c); opacity: 1; }
.table--adjustable th.is-dragging { opacity: .45; }
/* The drop target is marked on its leading edge — a whole-cell highlight does
   not say WHERE the column is about to land. */
.table--adjustable th.is-drop { box-shadow: inset 2px 0 0 var(--accent, #0f6e5c); }
.table--adjustable th[draggable="true"] { cursor: grab; }
.table--adjustable th[draggable="true"]:active { cursor: grabbing; }

@media (prefers-reduced-motion: reduce) {
  .table--adjustable .col-grip { transition: none; }
}
/*
 * Row actions in a narrow column.
 *
 * These used to be `flex-wrap: nowrap`, which forced the column as wide as
 * every button laid end to end — on the Billing list that meant Issue, Take
 * payment, View, Void and Delete on one line, taking a quarter of the table
 * and squeezing the money columns until the amounts wrapped. Wrapping the
 * BUTTONS instead of the FIGURES is the right way round: a button on a second
 * line is still legible, an amount broken across two lines gets misread.
 *
 * Right-aligned so the buttons sit against the edge of the table rather than
 * drifting in the middle of a half-empty cell.
 */
.row--actions {
  display: flex; flex-wrap: wrap; gap: 4px; justify-content: flex-end;
}
/* The alias list runs onto as many lines as it needs. It is secondary text, so
   it is set tighter than the name above it. */
.cell-aliases { white-space: normal; overflow-wrap: anywhere; line-height: 1.35; }
.table tbody tr { transition: background var(--duration-fast) var(--ease); }
.table tbody tr:hover { background: var(--surface-2); }
.table tbody tr:last-child td { border-bottom: 0; }
/*
 * Headings must sit over their own column.
 *
 * `.table__num` is one class (specificity 0,1,0) while `.table th` is a class
 * AND an element (0,1,1) — so `text-align: left` on the heading rule beat the
 * right-alignment on every numeric HEADING, while the cells below it obeyed.
 * Every money and quantity column in the app has therefore been reading with
 * its title hard left and its figures hard right, which is exactly the thing
 * the eye notices and the tests never could.
 *
 * Naming both elements raises this to 0,2,1 and settles it. Written out
 * rather than with :where() so the intent survives a future edit.
 */
.table th.table__num,
.table td.table__num,
.table tfoot td.table__num {
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.table__num { text-align: right; font-variant-numeric: tabular-nums; }

/* Same trap, same fix: a centred column keeps its heading centred. */
.table th.table__center,
.table td.table__center { text-align: center; }

/*
 * The last column is almost always actions. Right-aligning it keeps the
 * buttons off the ragged right edge of variable-width content instead of
 * floating in the middle of whatever the previous cell happened to be.
 */
.table td.table__actions,
.table th.table__actions { text-align: right; white-space: nowrap; }

/* --- Email list --- */

.mail {
  display: flex; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
  transition: background var(--duration-fast) var(--ease);
}
.mail:last-child { border-bottom: 0; }
.mail:hover { background: var(--surface-2); }
.mail--unread { background: var(--brand-soft); }
.mail--urgent { border-left: 3px solid var(--red-500); }
.mail__body { flex: 1; min-width: 0; }
.mail__top { display: flex; align-items: baseline; gap: var(--space-2); }
.mail__sender {
  font-size: var(--text-md); font-weight: var(--weight-semibold);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail__date { margin-left: auto; font-size: var(--text-2xs); color: var(--text-muted); white-space: nowrap; }
.mail__subject {
  font-size: var(--text-md); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mail__snippet {
  font-size: var(--text-xs); color: var(--text-muted); margin-top: 2px;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.mail__actions { display: flex; gap: var(--space-1); margin-top: var(--space-2); }

/* --- Calendar --- */

.cal-grid { display: grid; gap: 1px; background: var(--border); border: 1px solid var(--border); border-radius: var(--radius-md); overflow: hidden; }
.cal-grid--month { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-grid--week  { grid-template-columns: repeat(7, minmax(0, 1fr)); }
.cal-head {
  padding: var(--space-2); background: var(--surface-2); text-align: center;
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  text-transform: uppercase; letter-spacing: var(--tracking-caps); color: var(--text-muted);
}
.cal-cell { min-height: 106px; padding: var(--space-2); background: var(--surface); }
.cal-cell--muted { background: var(--surface-2); }
.cal-cell--today { background: var(--brand-soft); }
.cal-cell__date { font-size: var(--text-xs); font-weight: var(--weight-semibold); color: var(--text-muted); }
.cal-cell--today .cal-cell__date { color: var(--text-brand); }
.cal-event {
  display: block; width: 100%; margin-top: 3px; padding: 2px 6px;
  border-radius: var(--radius-sm); border: 0; border-left: 3px solid var(--brand);
  background: var(--brand-soft); color: var(--text);
  font-size: var(--text-2xs); text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cal-event[data-loc="molynes"] { border-left-color: var(--violet-500); background: var(--violet-50); }
.cal-event[data-loc="tele"]    { border-left-color: var(--accent);      background: var(--accent-soft); }
[data-theme="dark"] .cal-event[data-loc="molynes"] { background: rgb(139 92 246 / 15%); }

/* Timeline (day) view */
.timeline { display: grid; grid-template-columns: 64px 1fr; gap: 0 var(--space-3); }
.timeline__hour {
  padding-top: var(--space-1); font-size: var(--text-2xs); color: var(--text-muted);
  text-align: right; border-top: 1px solid var(--border); min-height: 58px;
  font-variant-numeric: tabular-nums;
}
.timeline__slot { border-top: 1px solid var(--border); min-height: 58px; padding: 3px 0; }
.timeline__event {
  display: block; width: 100%; padding: var(--space-2) var(--space-3); margin-bottom: 3px;
  border: 0; border-left: 3px solid var(--brand); border-radius: var(--radius-sm);
  background: var(--brand-soft); text-align: left;
}
.timeline__event-title { font-size: var(--text-sm); font-weight: var(--weight-semibold); }
.timeline__event-meta { font-size: var(--text-2xs); color: var(--text-muted); }

/* --- Charts --- */

.chart-wrap { position: relative; width: 100%; height: 260px; }
.chart-wrap--tall { height: 320px; }

/* Pure-CSS bar chart, used where Chart.js would be overkill. */
.bars { display: flex; align-items: flex-end; gap: var(--space-2); height: 180px; padding-top: var(--space-4); }
.bars__col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: var(--space-2); min-width: 0; }
.bars__bar {
  width: 100%; max-width: 42px; border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: linear-gradient(180deg, var(--brand), var(--blue-700));
  min-height: 3px; transition: height var(--duration-slow) var(--ease-out);
}
.bars__label { font-size: var(--text-2xs); color: var(--text-muted); white-space: nowrap; }

/* --- Skeletons --- */

.skeleton {
  border-radius: var(--radius-sm);
  background: linear-gradient(90deg, var(--surface-3) 25%, var(--surface-2) 37%, var(--surface-3) 63%);
  background-size: 400% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer { from { background-position: 100% 50%; } to { background-position: 0 50%; } }
.skeleton--text { height: 12px; margin-bottom: var(--space-2); }
.skeleton--title { height: 18px; width: 45%; margin-bottom: var(--space-3); }
.skeleton--card { height: 128px; border-radius: var(--radius-lg); }
.skeleton--kpi { height: 96px; border-radius: var(--radius-lg); }

/* --- Empty & error states --- */

.state {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--space-3); padding: var(--space-12) var(--space-6); text-align: center;
}
.state__icon {
  display: flex; align-items: center; justify-content: center;
  width: 52px; height: 52px; border-radius: var(--radius-full);
  background: var(--surface-3); color: var(--text-muted);
}
.state__icon svg { width: 24px; height: 24px; }
.state--error .state__icon { background: var(--tone-danger-bg); color: var(--tone-danger-fg); }
.state__title { font-size: var(--text-base); font-weight: var(--weight-semibold); }
.state__text { font-size: var(--text-md); color: var(--text-secondary); max-width: 400px; }

/* --- Toasts --- */

.toast-stack {
  position: fixed; right: var(--space-5); bottom: var(--space-5);
  display: flex; flex-direction: column; gap: var(--space-2);
  z-index: var(--z-toast); max-width: min(400px, calc(100vw - 2 * var(--space-5)));
}
.toast {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--surface); border: 1px solid var(--border);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg);
  animation: toast-in var(--duration) var(--ease-out);
}
.toast--exit { animation: toast-out var(--duration) var(--ease) forwards; }
.toast--ok     { border-left-color: var(--tone-ok-border); }
.toast--warn   { border-left-color: var(--tone-warn-border); }
.toast--danger { border-left-color: var(--tone-danger-border); }
.toast__body { flex: 1; min-width: 0; }
.toast__title { font-size: var(--text-md); font-weight: var(--weight-semibold); }
.toast__text { font-size: var(--text-xs); color: var(--text-secondary); margin-top: 2px; }
@keyframes toast-in  { from { opacity: 0; transform: translateX(24px) scale(0.97); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(24px) scale(0.97); } }

/* --- Modal / drawer --- */

.modal-backdrop {
  position: fixed; inset: 0; z-index: var(--z-modal);
  display: flex; align-items: center; justify-content: center; padding: var(--space-5);
  background: var(--overlay); animation: fade-in var(--duration) var(--ease);
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
.modal {
  width: 100%; max-width: 620px; max-height: 88vh; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-lg);
  animation: modal-in var(--duration) var(--ease-out);
}
.modal--wide { max-width: 860px; }
@keyframes modal-in { from { opacity: 0; transform: translateY(14px) scale(0.985); } to { opacity: 1; transform: none; } }
.modal__header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-4) var(--space-5); border-bottom: 1px solid var(--border);
}
.modal__body { padding: var(--space-5); overflow-y: auto; flex: 1; }
.modal__footer {
  display: flex; justify-content: flex-end; gap: var(--space-2);
  padding: var(--space-4) var(--space-5); border-top: 1px solid var(--border); background: var(--surface-2);
}

/* --- Forms --- */

.field { margin-bottom: var(--space-4); }
.field__label {
  display: block; margin-bottom: var(--space-2);
  font-size: var(--text-sm); font-weight: var(--weight-medium); color: var(--text-secondary);
}
.input, .textarea, .select {
  width: 100%; padding: var(--space-2) var(--space-3);
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); color: var(--text);
  font-family: inherit; font-size: var(--text-md);
  transition: border-color var(--duration-fast) var(--ease), box-shadow var(--duration-fast) var(--ease);
}
.input:focus, .textarea:focus, .select:focus {
  outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-soft);
}
.input[aria-invalid="true"], .textarea[aria-invalid="true"] { border-color: var(--red-500); }
.textarea { min-height: 84px; resize: vertical; line-height: var(--leading-normal); }
.field__error { margin-top: var(--space-1); font-size: var(--text-xs); color: var(--tone-danger-fg); }
.field__hint  { margin-top: var(--space-1); font-size: var(--text-xs); color: var(--text-muted); }

/* --- Profile menu --- */

.profile { display: flex; align-items: center; gap: var(--space-3); width: 100%; padding: var(--space-2); border: 0; border-radius: var(--radius); background: transparent; text-align: left; }
.profile:hover { background: var(--surface-hover); }
.profile__meta { min-width: 0; flex: 1; }
.profile__name { font-size: var(--text-sm); font-weight: var(--weight-semibold); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.profile__role { font-size: var(--text-2xs); color: var(--text-muted); text-transform: capitalize; }

.menu {
  position: absolute; min-width: 210px; padding: var(--space-2);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); box-shadow: var(--shadow-lg); z-index: var(--z-dropdown);
}
.menu__item {
  display: flex; align-items: center; gap: var(--space-3);
  width: 100%; padding: var(--space-2) var(--space-3);
  border: 0; border-radius: var(--radius); background: transparent;
  font-size: var(--text-md); text-align: left; color: var(--text);
}
.menu__item:hover { background: var(--surface-hover); }
.menu__item svg { width: 16px; height: 16px; color: var(--text-muted); }
.menu__sep { height: 1px; margin: var(--space-2) 0; background: var(--border); }

/* --- Alert strip --- */

.alert {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-radius: var(--radius);
  border: 1px solid var(--tone-info-border); background: var(--tone-info-bg); color: var(--tone-info-fg);
  font-size: var(--text-md);
}
.alert--warn   { border-color: var(--tone-warn-border);   background: var(--tone-warn-bg);   color: var(--tone-warn-fg); }
.alert--danger { border-color: var(--tone-danger-border); background: var(--tone-danger-bg); color: var(--tone-danger-fg); }
.alert--ok     { border-color: var(--tone-ok-border);     background: var(--tone-ok-bg);     color: var(--tone-ok-fg); }
.alert svg { width: 17px; height: 17px; flex-shrink: 0; margin-top: 1px; }

/* --- Sign-in screen --- */

.signin { display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: var(--space-5); background: var(--bg); }
.signin__card {
  width: 100%; max-width: 420px; padding: var(--space-8);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-xl); box-shadow: var(--shadow-md); text-align: center;
}
.signin__logo { width: 56px; height: 56px; margin: 0 auto var(--space-4); border-radius: var(--radius-md); }
.signin__title { font-size: var(--text-xl); font-weight: var(--weight-bold); }
.signin__text { font-size: var(--text-md); color: var(--text-secondary); margin: var(--space-2) 0 var(--space-6); }
.signin__divider {
  display: flex; align-items: center; gap: var(--space-3);
  margin: var(--space-5) 0; font-size: var(--text-xs); color: var(--text-muted);
}
.signin__divider::before, .signin__divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* --- Utilities --- */

.text-muted { color: var(--text-muted); }
.text-sm { font-size: var(--text-sm); }
.text-xs { font-size: var(--text-xs); }
.text-2xs { font-size: var(--text-2xs); }
.text-right { text-align: right; }
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Set by core/fitText.js on text that still will not fit at the smallest size
   we are willing to show. The full value stays available as a title and an
   aria-label, so nothing is actually lost — it is just not all on screen at
   once, which is the honest outcome rather than unreadable type. */
.is-clipped {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nowrap { white-space: nowrap; }
[hidden] { display: none !important; }

/* --- Pagination --- */

.pagination {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border); background: var(--surface-2);
}
.pagination__info { font-size: var(--text-xs); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.pagination__controls { display: flex; align-items: center; gap: 3px; flex-wrap: wrap; }
.pagination__btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 32px; height: 32px; padding: 0 var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums;
  transition: background var(--duration-fast) var(--ease), border-color var(--duration-fast) var(--ease);
}
.pagination__btn:hover:not(:disabled) { background: var(--surface-hover); border-color: var(--border-strong); }
.pagination__btn:disabled { opacity: 0.4; cursor: not-allowed; }
.pagination__btn--current {
  background: var(--brand); border-color: var(--brand); color: #fff;
  font-weight: var(--weight-semibold);
}
.pagination__btn--current:hover { background: var(--brand-hover); }
.pagination__btn--arrow { padding: 0; }
.pagination__gap { padding: 0 4px; color: var(--text-muted); font-size: var(--text-sm); }

@media (max-width: 560px) {
  .pagination { justify-content: center; }
  .pagination__info { width: 100%; text-align: center; }
}

/* --- Invoice document (screen + print) --- */

.invoice-doc { position: relative; background: var(--surface); color: var(--text); font-size: var(--text-md); }
.invoice-doc__head {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: var(--space-5); padding-bottom: var(--space-4);
  border-bottom: 2px solid var(--brand); margin-bottom: var(--space-4);
}
/*
 * Shown once the balance hits zero — the same "settled" rule the hosted PDF
 * already stamps with. Sits over the items area (not the letterhead or the
 * totals) so it reads as a mark on the document rather than covering figures
 * a patient needs to check.
 */
.invoice-doc__stamp {
  position: absolute; top: 132px; left: 24px; z-index: 1; pointer-events: none;
  padding: var(--space-2) var(--space-5);
  border: 3px solid var(--tone-ok-fg); border-radius: var(--radius-md);
  color: var(--tone-ok-fg); font-size: var(--text-2xl); font-weight: var(--weight-bold);
  letter-spacing: 0.15em; text-transform: uppercase;
  transform: rotate(-8deg); opacity: 0.85;
}
.invoice-doc__org { font-size: var(--text-xl); font-weight: var(--weight-bold); margin-top: var(--space-2); }
.invoice-doc__addr { font-size: var(--text-xs); color: var(--text-muted); }
.invoice-doc__title {
  font-size: var(--text-2xl); font-weight: var(--weight-bold);
  letter-spacing: 0.08em; color: var(--brand);
}
.invoice-doc__meta {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5);
  margin-bottom: var(--space-4);
}
.invoice-doc__label {
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  text-transform: uppercase; letter-spacing: var(--tracking-caps);
  color: var(--text-muted); margin-bottom: var(--space-1);
}
.invoice-doc__items { width: 100%; margin-bottom: var(--space-4); }

/* Older bills still owing. Set apart from the invoice totals on purpose — it
   is a statement of account, not part of what this bill is for, and it must
   never read as though it had been added in. Muted and outlined rather than
   filled, so it does not compete with the payment block. */
.invoice-doc__prior {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-md);
}
.invoice-doc__prior .table { width: 100%; }
.table--tight td, .table--tight th {
  padding-top: var(--space-1); padding-bottom: var(--space-1);
  font-size: var(--text-xs);
}
.invoice-doc__pay {
  padding: var(--space-4); border: 1px solid var(--brand-border);
  border-radius: var(--radius-md); background: var(--brand-soft);
  margin-top: var(--space-4);
}
.invoice-doc__paycol { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-5); }
.invoice-doc__acct {
  padding: var(--space-2) 0; border-bottom: 1px dashed var(--border);
}
.invoice-doc__acct:last-child { border-bottom: 0; }
.invoice-doc__url { word-break: break-all; margin-top: 4px; }
.invoice-doc__foot {
  margin-top: var(--space-5); padding-top: var(--space-3);
  border-top: 1px solid var(--border); font-size: var(--text-sm);
}

@media (max-width: 640px) {
  .invoice-doc__meta, .invoice-doc__paycol { grid-template-columns: 1fr; }
}

/* Printing an invoice: hide the app chrome, show only the document. */
body.printing-invoice > *:not(.print-root) { display: none !important; }
body.printing-invoice .print-root { display: block; }
.print-root { display: none; }
/* ---------- Signatures at the foot of a bill ---------- */

.invoice-doc__signatures {
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  margin-top: var(--space-5);
  break-inside: avoid; page-break-inside: avoid;
}
.invoice-doc__sigcol { min-width: 0; }
/* Room to sign by hand when there is no stored signature. */
.invoice-doc__sigspace { height: 46px; }
.invoice-doc__sigimg {
  display: block; height: 46px; width: auto; max-width: 100%;
  object-fit: contain; object-position: left bottom;
}
.invoice-doc__sigrule { border-bottom: 1px solid var(--text-primary); }
.invoice-doc__siglabel {
  margin-top: var(--space-1);
  font-size: var(--text-sm); font-weight: var(--weight-medium);
}
.invoice-doc__sigsub { font-size: var(--text-2xs); color: var(--text-muted); }

/*
   PRINTING A BILL — one letter page.

   The whole document is scaled proportionally rather than any part of it being
   dropped, so a long bill stays complete and legible instead of losing its
   payment instructions. Everything shrinks together; nothing is summarised
   away.

   `@page` margins are tight because the letterhead already provides visual
   margin, and every millimetre saved is another line item that fits.
*/
@media print {
  body.printing-invoice .modal-backdrop,
  body.printing-invoice .toast-stack { display: none !important; }

  @page { size: letter; margin: 10mm; }

  .invoice-doc {
    font-size: 9.5pt;
    line-height: 1.35;
  }
  .invoice-doc__head { margin-bottom: 8px; }
  .invoice-doc__meta { gap: var(--space-3); margin-bottom: 8px; }
  .invoice-doc__items { margin-bottom: 8px; }
  .invoice-doc__items th,
  .invoice-doc__items td { padding: 2.5px 5px !important; }

  /* Payment instructions must print in full — a receipt without them is
     useless — but they do not need to shout. */
  .invoice-doc__pay {
    background: #fff !important;
    border: 1px solid #999;
    padding: 6px 8px !important;
    margin-top: 8px;
    font-size: 8.5pt;
  }
  .invoice-doc__paycol { gap: 4px; }

  .invoice-doc__prior { padding: 6px 8px; margin-top: 8px; font-size: 8.5pt; }
  .invoice-doc__signatures { margin-top: 14px; }
  .invoice-doc__sigspace, .invoice-doc__sigimg { height: 38px; }

  /* Never split the document across a page it did not need. */
  .invoice-doc { break-inside: avoid-page; }

  .btn { display: none !important; }
}

/* Delivery / read-receipt strip above the invoice document. */
.delivery-panel {
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
}
@media print { .delivery-panel { display: none !important; } }

/* ---------- Clickable KPI tiles (Customers) ---------- */
.kpi--clickable { cursor: pointer; transition: border-color .15s, box-shadow .15s, transform .1s; }
.kpi--clickable:hover { border-color: var(--brand); box-shadow: var(--shadow-md); }
.kpi--clickable:active { transform: translateY(1px); }
.kpi--clickable:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.kpi--active { border-color: var(--brand); box-shadow: inset 0 0 0 1px var(--brand); }

/* Rows that open a record */
.row--clickable { cursor: pointer; }
.row--clickable:hover > td { background: var(--surface-2); }

.card--spaced { margin-top: var(--space-4); }

/* ---------- Booking board ---------- */
.slot {
  display: flex; gap: var(--space-3); align-items: center;
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
}
.slot:last-child { border-bottom: 0; }
.slot__time {
  min-width: 62px; font-family: var(--font-mono); font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
}
.slot__body { flex: 1; min-width: 0; }
.slot__name { font-weight: var(--weight-medium); }
.slot--now { background: color-mix(in srgb, var(--brand) 7%, transparent); }

/* Booking tab — 30-minute slot availability strip */
.slot-strip { display: flex; flex-wrap: wrap; gap: 6px; padding: var(--space-4); }
.slot-chip {
  min-width: 58px; text-align: center; padding: 5px var(--space-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: var(--text-xs); font-weight: var(--weight-medium);
  background: var(--surface); color: var(--text-muted); cursor: default;
}
.slot-chip--free {
  cursor: pointer; color: var(--tone-ok-fg); border-color: var(--tone-ok-bg);
}
.slot-chip--free:hover { background: var(--tone-ok-bg); }
.slot-chip--booked { background: var(--tone-brand-bg); color: var(--tone-brand-fg); border-color: transparent; }
.slot-chip--external { background: var(--tone-muted-bg); color: var(--tone-muted-fg); border-color: transparent; }

/* Reception visit clock */
.visit-clock { display: flex; gap: var(--space-4); flex-wrap: wrap; }
.visit-clock__step { display: flex; gap: 6px; align-items: baseline; font-size: var(--text-sm); }
.visit-clock__label { color: var(--text-muted); font-size: var(--text-xs); }
.visit-clock__value { font-family: var(--font-mono); font-weight: var(--weight-semibold); }

/* ---------- Security console ---------- */
.sev-critical { color: var(--tone-danger-fg); font-weight: var(--weight-semibold); }
.sev-warn { color: var(--tone-warn-fg); }
.sev-info { color: var(--text-muted); }
.integrity-row {
  display: flex; gap: var(--space-3); align-items: center;
  padding: var(--space-2) 0; border-bottom: 1px dashed var(--border);
}
.integrity-row:last-child { border-bottom: 0; }
.integrity-row__state { margin-left: auto; font-size: var(--text-sm); }

/* ---------- Row hover actions ----------
   Archiving hides until the row is hovered, so it is never the button someone
   hits by accident. It must stay reachable without a mouse, so keyboard focus
   reveals it too, and it is never `display:none` — a hidden button is
   invisible to screen readers, and "mark this patient deceased" is not a
   control anyone should have to hunt for. */
.row-action {
  opacity: 0;
  transition: opacity .12s ease;
}
tr:hover .row-action,
tr:focus-within .row-action,
.row-action:focus-visible {
  opacity: 1;
}
@media (hover: none) {
  /* Touch devices have no hover state at all. */
  .row-action { opacity: 1; }
}

/* Expense periods: a collapsible day, month or year with its total. The bar
   makes a heavy period obvious at a glance without replacing the figure. */
.period-group { border-bottom: 1px solid var(--border); }
.period-group:last-child { border-bottom: 0; }
.period-row {
  display: flex; align-items: center; gap: var(--space-4);
  width: 100%; padding: var(--space-3) var(--space-4);
  border: 0; background: transparent; text-align: left;
}
.period-row:hover { background: var(--surface-hover); }
.period-row__label { flex: 1 1 auto; min-width: 0; }
.period-row__bar {
  flex: 0 1 160px; height: 6px; border-radius: var(--radius-full);
  background: var(--surface-hover); overflow: hidden;
}
.period-row__bar span {
  display: block; height: 100%; background: var(--brand); border-radius: inherit;
}
.period-row__total { flex: 0 0 auto; font-weight: var(--weight-semibold); }
.period-row[aria-expanded="true"] { background: var(--surface-hover); }
@media (max-width: 700px) { .period-row__bar { display: none; } }

/* A tile that filters. Only tiles with something to show become buttons. */
.kpi--clickable { cursor: pointer; text-align: left; width: 100%; font: inherit; color: inherit; }
.kpi--clickable:hover { border-color: var(--border-strong); background: var(--surface-hover); }
.kpi--active { border-color: var(--brand); box-shadow: 0 0 0 1px var(--brand) inset; }
.kpi--active .kpi__label { color: var(--text-brand); }

/* Tab permissions: a person, and the tabs they are offered. */
.row--between { justify-content: space-between; align-items: flex-start; }
.choice { display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--text-sm); cursor: pointer; }
.choice input[type="checkbox"] { accent-color: var(--brand); cursor: pointer; }
.choice input:disabled { cursor: default; opacity: .6; }
.rule { border: 0; border-top: 1px solid var(--border); margin: var(--space-3) 0; }

/* ============================================================
   Operations dashboard
   ============================================================ */

.dash-grid {
  display: grid; gap: var(--space-4);
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  margin-top: var(--space-4); align-items: start;
}
.dash-col { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
@media (max-width: 1100px) { .dash-grid { grid-template-columns: 1fr; } }

/* A tile still being fetched holds its space so the grid does not jump. */
.kpi--loading { min-height: 108px; position: relative; overflow: hidden; }
.kpi--loading::after {
  content: ''; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, var(--surface-hover), transparent);
  animation: kpi-sweep 1.4s var(--ease) infinite;
}
@keyframes kpi-sweep { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.kpi__spark { margin-top: var(--space-2); opacity: .85; }

/* Quick actions */
.quick {
  display: grid; gap: var(--space-3); margin-top: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
}
.quick__item {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3); text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); color: inherit; font: inherit;
  transition: border-color var(--duration-fast) var(--ease),
              box-shadow var(--duration-fast) var(--ease),
              transform var(--duration-fast) var(--ease);
}
.quick__item:hover {
  border-color: var(--brand-border); box-shadow: var(--shadow-sm); transform: translateY(-1px);
}
.quick__item:active { transform: translateY(0); }
.quick__icon {
  display: grid; place-items: center; flex: 0 0 36px; height: 36px;
  border-radius: var(--radius); background: var(--brand-soft); color: var(--brand);
}
.quick__text { display: flex; flex-direction: column; min-width: 0; }
.quick__label { font-weight: var(--weight-semibold); font-size: var(--text-md); }
.quick__hint {
  font-size: var(--text-xs); color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Waiting room */
.wait-list { display: flex; flex-direction: column; }
.wait-row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
}
.wait-row:last-child { border-bottom: 0; }
.wait-row__dot {
  flex: 0 0 8px; height: 8px; border-radius: var(--radius-full); background: var(--text-muted);
}
.wait-row--ok     .wait-row__dot { background: var(--tone-ok-fg); }
.wait-row--warn   .wait-row__dot { background: var(--tone-warn-fg); }
.wait-row--danger .wait-row__dot { background: var(--tone-danger-fg); }
.wait-row--brand  .wait-row__dot { background: var(--brand); animation: pulse-dot 2s var(--ease) infinite; }
@keyframes pulse-dot { 0%,100% { opacity: 1; } 50% { opacity: .35; } }
.wait-row__who { flex: 1 1 auto; min-width: 0; }
.wait-row__name { font-weight: var(--weight-medium); }
.wait-row__time {
  display: flex; align-items: center; gap: var(--space-2);
  font-variant-numeric: tabular-nums; font-weight: var(--weight-semibold);
}

/* Timeline */
/*
 * ONE SOURCE FOR THE COLUMN GEOMETRY.
 *
 * The connecting thread used to be pinned at a hard-coded `left: 62px` while
 * the dot's real position was padding + time column + gap + half the mark.
 * They did not agree, so the thread was drawn straight THROUGH the appointment
 * time — which is what made the schedule look like overlapping text. These
 * variables are used by both the grid and the thread, so the two cannot drift
 * apart again.
 */
.timeline {
  list-style: none; margin: 0; padding: 0;
  --tl-time: 56px;
  --tl-mark: 16px;
  --tl-gap: var(--space-2);
  --tl-pad: var(--space-4);
}
.timeline__item {
  display: grid;
  grid-template-columns: var(--tl-time) var(--tl-mark) 1fr;
  align-items: start;
  gap: var(--tl-gap); padding: var(--space-3) var(--tl-pad);
  position: relative;
}
/* Each appointment is its own row, so two of them cannot read as one. */
.timeline__item + .timeline__item { border-top: 1px solid var(--border); }
.timeline__time {
  font-size: var(--text-sm); color: var(--text);
  font-weight: var(--weight-medium);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  padding-top: 1px; text-align: right;
}
.timeline__mark {
  width: 10px; height: 10px; margin-top: 6px; border-radius: var(--radius-full);
  background: var(--surface); border: 2px solid var(--border-strong);
  /* Above the thread, so the thread appears to run behind the dot. */
  position: relative; z-index: 1;
}
/* The thread joining one appointment to the next — centred on the dot. */
.timeline__item::before {
  content: ''; position: absolute;
  left: calc(var(--tl-pad) + var(--tl-time) + var(--tl-gap) + (var(--tl-mark) / 2) - 1px);
  top: calc(var(--space-3) + 16px); bottom: 0;
  width: 2px; background: var(--border);
}
.timeline__item:last-child::before { display: none; }
.timeline__item--done .timeline__mark { background: var(--tone-ok-fg); border-color: var(--tone-ok-fg); }
.timeline__item--done .timeline__name,
.timeline__item--done .timeline__time { color: var(--text-muted); }
.timeline__item--live .timeline__mark { background: var(--brand); border-color: var(--brand); }
.timeline__item--live { background: var(--brand-soft); }
.timeline__item--next .timeline__mark { border-color: var(--brand); }
/* min-width:0 lets a long patient name wrap instead of pushing the row wide. */
.timeline__body { min-width: 0; }
.timeline__name {
  display: flex; align-items: center; gap: var(--space-1) var(--space-2);
  flex-wrap: wrap; font-weight: var(--weight-medium);
  line-height: var(--leading-snug, 1.35);
  overflow-wrap: anywhere;
}
.timeline__meta {
  font-size: var(--text-2xs); color: var(--text-muted);
  margin-top: 2px; overflow-wrap: anywhere;
}

/* Activity feed */
.feed { display: flex; flex-direction: column; }
.feed__row {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  padding: var(--space-3) var(--space-4); background: transparent; border: 0;
  border-bottom: 1px solid var(--border); text-align: left; cursor: pointer;
  color: inherit; font: inherit; transition: background var(--duration-fast) var(--ease);
}
.feed__row:last-child { border-bottom: 0; }
.feed__row:hover { background: var(--surface-hover); }
.feed__icon {
  display: grid; place-items: center; flex: 0 0 28px; height: 28px;
  border-radius: var(--radius-full);
  background: var(--tone-neutral-bg); color: var(--tone-neutral-fg);
}
.feed__icon--ok    { background: var(--tone-ok-bg); color: var(--tone-ok-fg); }
.feed__icon--warn  { background: var(--tone-warn-bg); color: var(--tone-warn-fg); }
.feed__icon--info  { background: var(--tone-info-bg); color: var(--tone-info-fg); }
.feed__icon--muted { background: var(--tone-muted-bg); color: var(--tone-muted-fg); }
.feed__text { flex: 1 1 auto; min-width: 0; font-size: var(--text-md); }
.feed__time { flex: 0 0 auto; font-variant-numeric: tabular-nums; }

/* Alerts */
.alert-list { display: flex; flex-direction: column; }
.alert-row {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  padding: var(--space-3) var(--space-4); background: transparent;
  border: 0; border-bottom: 1px solid var(--border); border-left: 3px solid transparent;
  text-align: left; cursor: pointer; color: inherit; font: inherit;
  transition: background var(--duration-fast) var(--ease);
}
.alert-row:last-child { border-bottom: 0; }
.alert-row:hover { background: var(--surface-hover); }
.alert-row--danger { border-left-color: var(--tone-danger-fg); }
.alert-row--warn   { border-left-color: var(--tone-warn-fg); }
.alert-row--info   { border-left-color: var(--tone-info-fg); }
.alert-row--neutral{ border-left-color: var(--border-strong); }
.alert-row__icon { flex: 0 0 auto; color: var(--text-muted); }
.alert-row__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.alert-row__title { font-weight: var(--weight-medium); font-size: var(--text-md); }
.alert-row__detail { font-size: var(--text-xs); color: var(--text-muted); }
.alert-row__go { flex: 0 0 auto; color: var(--text-muted); }

/* Money snapshot */
.money-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.money-cell { padding: var(--space-3); border-radius: var(--radius); background: var(--surface-2); }
.money-cell--ok    { background: var(--tone-ok-bg); }
.money-cell--warn  { background: var(--tone-warn-bg); }
.money-cell__label { font-size: var(--text-xs); color: var(--text-muted); }
.money-cell__value {
  font-size: var(--text-xl); font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
}
.spark-wrap { display: flex; flex-direction: column; gap: var(--space-1); }
.mini-stats { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; }
.mini-stat { display: flex; justify-content: space-between; gap: var(--space-3); }
.mini-stat dt { font-size: var(--text-sm); color: var(--text-muted); }
.mini-stat dd {
  margin: 0; font-weight: var(--weight-semibold); font-variant-numeric: tabular-nums;
}
.chip-row { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.text-ok { color: var(--tone-ok-fg); }
.text-danger { color: var(--tone-danger-fg); }

/* Nobody should be made motion-sick by a dashboard. */
@media (prefers-reduced-motion: reduce) {
  .kpi--loading::after, .wait-row--brand .wait-row__dot { animation: none; }
  .quick__item:hover { transform: none; }
}

/* ============================================================
   Command palette
   ============================================================ */

.search-trigger {
  display: flex; align-items: center; gap: var(--space-2);
  flex: 1 1 320px; max-width: 460px; min-width: 0;
  padding: var(--space-2) var(--space-3); cursor: text;
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--radius); color: var(--text-muted);
  font: inherit; font-size: var(--text-md); text-align: left;
  transition: border-color var(--duration-fast) var(--ease),
              background var(--duration-fast) var(--ease);
}
.search-trigger:hover { border-color: var(--border-strong); background: var(--surface); }
.search-trigger__text {
  flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.search-trigger__kbd, .palette__hint kbd {
  flex: 0 0 auto; padding: 2px 6px; border-radius: var(--radius-sm);
  border: 1px solid var(--border); background: var(--surface);
  font-family: var(--font-mono); font-size: var(--text-2xs); color: var(--text-muted);
}
@media (max-width: 720px) {
  .search-trigger__text, .search-trigger__kbd { display: none; }
  .search-trigger { flex: 0 0 auto; }
}

.palette-backdrop {
  position: fixed; inset: 0; z-index: var(--z-modal);
  background: var(--overlay); backdrop-filter: blur(3px);
  display: flex; justify-content: center; align-items: flex-start;
  padding: 12vh var(--space-4) var(--space-4);
  animation: fade-in var(--duration-fast) var(--ease);
}
.palette {
  width: min(640px, 100%); max-height: 66vh; display: flex; flex-direction: column;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); overflow: hidden;
  animation: palette-rise var(--duration) var(--ease-out);
}
@keyframes palette-rise {
  from { opacity: 0; transform: translateY(-8px) scale(.985); }
  to   { opacity: 1; transform: none; }
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }

.palette__field {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4); border-bottom: 1px solid var(--border); color: var(--text-muted);
}
.palette__input {
  flex: 1 1 auto; border: 0; background: transparent; outline: none;
  font: inherit; font-size: var(--text-lg); color: var(--text);
}
.palette__list { flex: 1 1 auto; overflow-y: auto; padding: var(--space-2); }
.palette__group {
  padding: var(--space-2) var(--space-3) var(--space-1);
  font-size: var(--text-2xs); font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-caps); text-transform: uppercase; color: var(--text-muted);
}
.palette__row {
  display: flex; align-items: center; gap: var(--space-3); width: 100%;
  padding: var(--space-2) var(--space-3); border: 0; border-radius: var(--radius);
  background: transparent; color: inherit; font: inherit; text-align: left; cursor: pointer;
}
.palette__row.is-active { background: var(--brand-soft); color: var(--text-brand); }
.palette__icon { flex: 0 0 auto; color: var(--text-muted); display: grid; place-items: center; }
.palette__row.is-active .palette__icon { color: var(--brand); }
.palette__label {
  flex: 1 1 auto; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; font-size: var(--text-md);
}
.palette__sub { flex: 0 0 auto; font-size: var(--text-xs); color: var(--text-muted); }
.palette__empty { padding: var(--space-4); color: var(--text-muted); font-size: var(--text-md); }
.palette__hint {
  display: flex; gap: var(--space-4); padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--border); background: var(--surface-2);
  font-size: var(--text-xs); color: var(--text-muted);
}
.palette__hint kbd { margin-right: 2px; }

/* ============================================================
   Grouped navigation
   ============================================================ */

.nav-group__label--toggle {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  background: transparent; border: 0; cursor: pointer; color: inherit;
  font: inherit; text-align: left;
}
.nav-group__label--toggle .nav-group__chev {
  transition: transform var(--duration-fast) var(--ease); opacity: .6;
}
.nav-group.is-closed .nav-group__chev { transform: rotate(-90deg); }
.nav-group__items { display: flex; flex-direction: column; }
.nav-item { position: relative; }
.nav-item__pin {
  position: absolute; right: var(--space-2); top: 50%; transform: translateY(-50%);
  display: grid; place-items: center; width: 22px; height: 22px;
  border-radius: var(--radius-sm); color: var(--text-muted);
  opacity: 0; transition: opacity var(--duration-fast) var(--ease);
}
.nav-item:hover .nav-item__pin, .nav-item__pin:focus-visible { opacity: 1; }
.nav-item__pin.is-pinned { opacity: 1; color: var(--brand); }
.nav-item__pin:hover { background: var(--surface-hover); color: var(--brand); }
/* A collapsed rail has no room for a star, and no room to mis-click one. */
.app[data-collapsed="true"] .nav-item__pin { display: none; }

@media (prefers-reduced-motion: reduce) {
  .palette, .palette-backdrop { animation: none; }
}

/* ============================================================
   Notification centre
   ============================================================ */

.notif {
  position: absolute; top: calc(100% - var(--space-2)); right: var(--space-4);
  width: min(380px, calc(100vw - var(--space-8)));
  max-height: min(520px, 70vh); display: flex; flex-direction: column;
  background: var(--bg-elevated); border: 1px solid var(--border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  z-index: var(--z-dropdown); overflow: hidden;
  animation: palette-rise var(--duration) var(--ease-out);
}
.notif__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border);
}
.notif__list { flex: 1 1 auto; overflow-y: auto; }
.notif__row {
  display: flex; align-items: flex-start; gap: var(--space-3);
  padding: var(--space-3) var(--space-4); border-bottom: 1px solid var(--border);
}
.notif__row:last-child { border-bottom: 0; }
.notif__row.is-unread { background: var(--brand-soft); }
.notif__dot {
  flex: 0 0 8px; height: 8px; margin-top: 6px;
  border-radius: var(--radius-full); background: var(--text-muted);
}
.notif__dot--brand  { background: var(--brand); }
.notif__dot--danger { background: var(--tone-danger-fg); }
.notif__dot--muted  { background: var(--border-strong); }
.notif__body { flex: 1 1 auto; min-width: 0; }
.notif__title {
  display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap;
  font-size: var(--text-md); font-weight: var(--weight-medium);
}
.notif__detail { font-size: var(--text-sm); color: var(--text-secondary); }
.notif__when { font-size: var(--text-2xs); color: var(--text-muted); margin-top: 2px; }
.icon-btn--sm { width: 26px; height: 26px; flex: 0 0 auto; }
.topbar { position: relative; }

@media (prefers-reduced-motion: reduce) { .notif { animation: none; } }

/* ---------- Out-of-date banner ----------
   Sits above the whole shell rather than inside a view: a client running
   against a newer database can be wrong on any screen, so the warning must
   not be something you can navigate away from. */
.update-banner {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  border-bottom: 1px solid var(--border);
}
.update-banner--info {
  background: var(--info-soft, #eaf2ff);
  color: var(--info-strong, #14396b);
}
.update-banner--danger {
  background: var(--danger-soft, #fdecec);
  color: var(--danger-strong, #7a1c1c);
  font-weight: var(--weight-medium);
}
.update-banner button { margin-left: auto; flex: none; }

/* ---------- Registration form ----------
   The practice's own sheet asks about thirty questions. Laid out in one column
   it is a form nobody completes; behind folded headings, with the two a walk-in
   actually needs already open, it is one that gets filled in.

   The grid does NOT reuse .grid--2/3: those take a 380px minimum, sized for
   cards on a full-width page. Inside a 860px dialog with padding there is about
   780px of usable width, so a 380px minimum silently collapses three columns
   into one and the form becomes the tall thing it was meant not to be. These
   minima are measured against that width. */
.reg-grid       { display: grid; gap: var(--space-3); }
.reg-grid--2    { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.reg-grid--3    { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.reg-grid--4    { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

.reg-section {
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  background: var(--surface);
}
.reg-section + .reg-section { margin-top: var(--space-3); }
.reg-section__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); cursor: pointer; list-style: none;
  padding: var(--space-3) var(--space-4);
  font-weight: var(--weight-medium); font-size: var(--text-sm);
}
.reg-section__head::-webkit-details-marker { display: none; }
.reg-section[open] .reg-section__head { border-bottom: 1px solid var(--border); }
.reg-section__chev { color: var(--text-muted); display: flex; transition: transform var(--duration); }
.reg-section[open] .reg-section__chev { transform: rotate(180deg); }
.reg-section__body { padding: var(--space-4); }

.reg-check { display: flex; align-items: center; gap: 8px; font-size: var(--text-sm); cursor: pointer; }
.reg-check input { width: 15px; height: 15px; flex: none; accent-color: var(--brand); }
.reg-checks { display: grid; gap: var(--space-2); grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }

/* The reader's state line. The dot is the whole affordance: green means the
   swipe was read, and the person at the desk needs to know that without
   reading a sentence. */
.reg-swipe {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: var(--space-3); border-radius: var(--radius-md);
  background: var(--surface-2, rgba(0,0,0,.03));
}
.reg-swipe__dot {
  width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--text-muted); animation: reg-pulse 1.6s ease-in-out infinite;
}
.reg-swipe__dot--ok { background: var(--ok, #1f9d55); animation: none; }
@keyframes reg-pulse { 0%,100% { opacity: .35; } 50% { opacity: 1; } }

/* ---------- The swiped-patient summary ----------
   The allergy band is the reason this screen exists. It is given colour, an
   icon and a whole row to itself so that it cannot be skimmed past, and it
   prints "none known" rather than going quiet — a blank space and an answered
   question look identical, and telling them apart is the point. */
.summary-band {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  margin-top: var(--space-3);
  font-size: var(--text-sm);
  line-height: 1.45;
}
.summary-band svg { flex: none; margin-top: 2px; }
.summary-band--danger { background: var(--tone-danger-bg); color: var(--tone-danger-fg); }
.summary-band--warn   { background: var(--tone-warn-bg);   color: var(--tone-warn-fg); }
.summary-band--ok     { background: var(--tone-ok-bg);     color: var(--tone-ok-fg); }
