/* ============================================================================
   Cerno HMS — Design System  (rev 3 — post design-debate)
   Changes vs v2: IBM Plex Sans + Plex Mono (mono for machine identifiers),
   unified 2px stroke icons on a 16/20/24 grid, 36px token-driven density,
   deeper medical blue, perceptual neutrals, flat brand mark (no gradient),
   hairline + inset-ring elevation, 140ms micro-motion, 4-up clinical KPIs.
   Signature stays the "status spine"; identity now rides spine + mono, no logo gloss.
   ============================================================================ */

/* ----- Tokens --------------------------------------------------------------- */
:root {
  /* Perceptual slate neutrals */
  --canvas:      #f6f8fb;
  --surface:     #ffffff;
  --surface-2:   #eef1f6;
  --surface-3:   #e4e9f0;

  --ink:         #11202f;
  --ink-soft:    #46566e;
  --ink-faint:   #5a6b82;

  --line:        #dbe2ec;
  --line-strong: #c7d1de;
  /* Button boundary. --line-strong is 1.53:1 against a white bar and .btn is painted --surface, so a
     secondary button was white on white with a border too faint to see: WCAG 2.1 SC 1.4.11 wants 3:1
     for a UI component boundary. This is 3.3:1. */
  --line-btn: #7f8ea6;

  /* Deeper medical blue (off the generic corporate blue) */
  --primary:     #1a56a8;
  --primary-700: #16467f;
  --primary-050: #e8f0fb;
  --btn-fill:    #1a56a8;
  --teal:        #0e8fa0;       /* demoted: data-viz / secondary only */

  /* Clinical semantics — generated as siblings; -tx all AA on -bg */
  --critical: #e5484d; --critical-bg: #fdeceb; --critical-tx: #b42318;
  --urgent:   #e08a00; --urgent-bg:   #fcefe0; --urgent-tx:   #b54708;
  --stable:   #2e9e6b; --stable-bg:   #e7f5ee; --stable-tx:   #197a4e;
  --info:     #1a56a8; --info-bg:     #e8f0fb; --info-tx:     #16467f;

  /* Operational (non-clinical) ramp — near-grey, never the triage colours */
  --op-occupied: #e7ebf3; --op-occupied-tx: #3f5573;
  --op-cleaning: #eaedf2; --op-cleaning-tx: #5a6b82;

  /* Elevation: hairline + faint shadow + inset light ring ("cut from glass") */
  /* Premium gradient system (blue -> indigo) */
  --accent:     #5b5bf0;
  --grad-brand: linear-gradient(140deg, #2f6fd0 0%, #5b5bf0 100%);
  --grad-btn:   linear-gradient(180deg, #2a67bf 0%, #194f97 100%);
  --grad-nav:   linear-gradient(135deg, rgba(91,91,240,.14), rgba(26,86,168,.10));
  --grad-sheen: linear-gradient(180deg, rgba(255,255,255,.5), rgba(255,255,255,0) 44%);
  --glow-brand: 0 5px 16px rgba(91,91,240,.32);
  --stat-grad:  linear-gradient(135deg, #3f7fe0 0%, #5b5bf0 100%);

  --shadow-card: 0 1px 2px rgba(17,32,47,.04), 0 6px 18px rgba(17,32,47,.06);
  --card-ring:   inset 0 0 0 1px rgba(255,255,255,.6);
  --shadow-md:   0 4px 12px rgba(17,32,47,.10), 0 16px 40px rgba(17,32,47,.12);  /* overlays only */

  --r-sm: 6px; --r-md: 10px; --r-lg: 14px;
  --unit: 4px;
  --row-h: 36px;            /* Compact (default). 32 = Dense, 40 = Comfortable */
  --hit: 44px;              /* standalone tap controls only */
  --spine-w: 5px;
  --icon-sm: 16px; --icon-md: 20px; --icon-lg: 24px;

  --ease: cubic-bezier(.2, 0, 0, 1);
  --dur: 140ms;

  --font: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;

  --sidebar-w: 248px;
  --topbar-h: 60px;
}

[data-theme="dark"] {
  --canvas:      #0c1422;
  --surface:     #131d2e;
  --surface-2:   #1a2740;
  --surface-3:   #223150;

  --ink:         #e8eef7;
  --ink-soft:    #aebfd4;
  --ink-faint:   #8c9fb6;

  --line:        #233146;
  --line-strong: #2d3f59;
  --line-btn: #5f7ca8;   /* 3.97:1 against the dark bar */

  --primary:     #5c9bee;
  --primary-700: #7cb0f2;
  --primary-050: #15294a;
  --btn-fill:    #2f6fce;
  --teal:        #34b3c4;

  --critical: #ff6168; --critical-bg: #34191c; --critical-tx: #ff8a8e;
  --urgent:   #f5a524; --urgent-bg:   #322412; --urgent-tx:   #f5b651;
  --stable:   #45c486; --stable-bg:   #122a20; --stable-tx:   #5fd49b;
  --info:     #5c9bee; --info-bg:     #15294a; --info-tx:     #88b6f2;

  --op-occupied: #1d2c45; --op-occupied-tx: #9fb4d2;
  --op-cleaning: #1b2536; --op-cleaning-tx: #8c9fb6;

  --grad-brand: linear-gradient(140deg, #3f7fe0 0%, #6d6bff 100%);
  --grad-btn:   linear-gradient(180deg, #3a78d8 0%, #265fb0 100%);
  --grad-nav:   linear-gradient(135deg, rgba(109,107,255,.24), rgba(63,127,224,.16));
  --grad-sheen: linear-gradient(180deg, rgba(255,255,255,.06), rgba(255,255,255,0) 44%);
  --glow-brand: 0 6px 20px rgba(109,107,255,.40);
  --stat-grad:  linear-gradient(135deg, #356fd6 0%, #5048e6 100%);

  --shadow-card: 0 1px 2px rgba(0,0,0,.35), 0 8px 22px rgba(0,0,0,.40);
  --card-ring:   inset 0 0 0 1px rgba(255,255,255,.04);
  --shadow-md:   0 8px 28px rgba(0,0,0,.5), 0 16px 48px rgba(0,0,0,.4);
}

/* ----- Reset ---------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; height: 100%; }
body {
  font-family: var(--font);
  background: var(--canvas);
  color: var(--ink);
  font-size: 13px; line-height: 1.4;
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
}
/* Ambient glow as a fixed, GPU-composited layer. Replaces `background-attachment: fixed`, which repaints
   the entire viewport on every scroll frame — barely noticeable on short pages but enough to make a tall
   page (e.g. the dashboard) feel like it won't scroll on slower hardware. A fixed pseudo-element gives the
   same "glow pinned to the viewport" look without the per-frame repaint. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(820px 460px at 6% -12%, rgba(91,91,240,.07), transparent 60%),
    radial-gradient(760px 460px at 104% -8%, rgba(14,143,160,.05), transparent 56%);
}
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
[inert] { pointer-events: none; }

/* Machine identifiers (MRN, IC, vitals, times) in mono — a clinical-safety cue */
.data { font-family: var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.num, .kpi__value, .alert__n { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* Unified icon system: thin premium line, round caps/joins */
svg:not(.chart) { stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.nav__ico, .alert__ico { stroke-width: 1.4; }   /* even thinner at larger sizes */

/* ----- App frame ------------------------------------------------------------ */
.app { display: grid; grid-template-columns: var(--sidebar-w) 1fr; min-height: 100vh; }

/* ----- Sidebar -------------------------------------------------------------- */
/* The sidebar itself does not scroll; the nav list scrolls inside it so the collapse button stays a fixed
   footer that never overlays the last nav link (WCAG 2.2 target-size). */
.sidebar { background: var(--surface); border-right: 1px solid var(--line); display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; overflow: hidden; }
/* flex: none is load-bearing. .brand is an item of the column-flex .sidebar, so the default
   flex-shrink:1 quietly collapsed this declared height to ~42px — the brand bar's rule never lined up
   with the topbar's, which is what the shared --topbar-h was there to guarantee. */
.brand { display: flex; align-items: center; gap: 10px; padding: 0 18px; height: var(--topbar-h); flex: none; border-bottom: 1px solid var(--line); }
/* .brand__mark / .brand__name / .brand__logo were the pre-component lockup (a gradient "C" tile, a
   gradient-clipped wordmark, and a raster <img>). Every surface now renders <CernoLogo>, so they are
   deleted rather than left behind — a stale brand rule sitting in the stylesheet is exactly how a
   sixth variant gets reintroduced by the next person who greps for "brand". */
.brand__sub { font-size: 11.5px; color: var(--ink-faint); margin-top: -1px; }
/* Brand mark image (the official Cerno "C"). Square, theme-agnostic PNG — reads on both light and dark chrome. */

/* Signed-in identity strip.

   HISTORY, because it took three passes. It began as ONE row — name, role and two icon buttons all
   sharing 247px — which left the name 97px and wrapped "Dr.Shawn Moodley" across two lines mid-name.
   Moving the controls onto their own row fixed the wrap but left an L-shaped block: the third row used
   only column two, so there was a hole of dead space under the avatar and the panel had a ragged
   inner edge.

   Now: identity on top (avatar + name + role), and a FULL-WIDTH action bar under it spanning both
   columns, separated by a hairline. Two rectangles instead of an L. */
.who { display: grid; grid-template-columns: 34px minmax(0, 1fr);
    grid-template-rows: auto auto auto; column-gap: 11px; row-gap: 1px;
    padding: 12px 16px 0; border-bottom: 1px solid var(--line); }
.who__av { grid-column: 1; grid-row: 1 / 3; align-self: center;
    width: 34px; height: 34px; border-radius: 50%;
    display: grid; place-items: center; font-size: 12px; font-weight: 700;
    background: var(--surface-2); color: var(--ink-soft); }
/* One line, with the full name on the title attribute. A truncated name is still recognisable;
   a name broken across two lines mid-word is not. */
.who__name { grid-column: 2; grid-row: 1; font-weight: 600; font-size: 12.5px; color: var(--ink);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.who__role { grid-column: 2; grid-row: 2; font-size: 11px; color: var(--ink-soft);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Spans BOTH columns — that is the whole point. Confined to column two it left the area beneath the
   avatar empty, which is what made the block look unfinished. The hairline groups the identity above
   and separates it from the controls, so the two halves read as deliberate rather than as text that
   happened to run on. */
/* The negative side margins absorb the buttons OWN padding, so the moon glyph lines up with the
   avatar's left edge and the last letter of "Sign out" with the panel's right edge. Without them both
   controls sat 6px inside the column the name and avatar establish, which reads as a ragged edge. The
   hover box still bleeds into that padding, which is what a hover box is for. */
.who__actions { grid-column: 1 / -1; grid-row: 3; display: flex; align-items: center;
    margin: 9px -6px 0; padding: 5px 0 7px; border-top: 1px solid var(--line); }
.who__actions form { display: flex; margin-left: auto; }

.who__btn { display: inline-flex; align-items: center; gap: 5px; border: 0; background: none;
    color: var(--ink-faint); cursor: pointer; padding: 3px 6px; border-radius: var(--r-sm);
    font-size: 11px; font-weight: 600; line-height: 1;
    transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.who__btn:hover { background: var(--surface-2); color: var(--ink); }
.who__btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; }
/* Sign out carries a WORD as well as a glyph. On a shared clinical workstation it is the most
   consequential control in this rail — whatever the next person writes is written under whoever is
   still signed in — and it was a 16px icon indistinguishable from the theme toggle beside it. */
.who__btn--out span { white-space: nowrap; }
.who__btn--out:hover { background: var(--critical-bg); color: var(--critical-tx); }

/* The nav list is the scroll region (flex:1 + overflow); min-height:0 lets it shrink to scroll. */
.nav { padding: 8px 10px 24px; flex: 1 1 auto; overflow-y: auto; min-height: 0; }
.nav__group { display: flex; align-items: center; gap: 8px; width: 100%; background: none; border: 0; cursor: pointer; font-family: inherit; font-size: 11px; font-weight: 700; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft); padding: 14px 10px 5px; transition: color var(--dur) var(--ease); }
.nav__group > span { flex: 1; text-align: left; }
.nav__group:hover { color: var(--ink); }
.nav__chev { width: 13px; height: 13px; flex: none; opacity: .65; transition: transform .22s var(--ease); }
.nav__items { display: grid; grid-template-rows: 1fr; transition: grid-template-rows .24s var(--ease); }
.nav__items__in { overflow: hidden; min-height: 0; transition: visibility 0s; }
.nav__cat.is-collapsed .nav__items { grid-template-rows: 0fr; }
/* A collapsed group's links are clipped to 0 height but were still focusable targets (a keyboard/AX trap, and
   what trips Lighthouse WCAG 2.2 target-size on the last nav item). Hide them from the a11y tree + tab order once
   collapsed — delayed by the .24s collapse animation so the visuals still animate smoothly; instant on expand. */
.nav__cat.is-collapsed .nav__items__in { visibility: hidden; transition: visibility 0s linear .24s; }
.nav__cat.is-collapsed .nav__chev { transform: rotate(-90deg); }
/* height is 44px in every visible state; min-height makes the SC 2.5.8 24px target-size floor explicit and robust. */
.nav__item { display: flex; align-items: center; gap: 11px; padding: 0 10px; height: var(--hit); min-height: 24px; border-radius: var(--r-sm); color: var(--ink-soft); font-size: 13px; font-weight: 500; position: relative; transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.nav__item:hover { background: var(--surface-2); color: var(--ink); }
.nav__item.is-active { background: var(--grad-nav); color: var(--primary); font-weight: 600; }
.nav__item.is-active::before { content: ""; position: absolute; left: 0; top: 9px; bottom: 9px; width: var(--spine-w); background: var(--grad-brand); border-radius: 3px; }
.nav__ico { width: var(--icon-md); height: var(--icon-md); flex: none; }
.nav__badge { margin-left: auto; font-size: 10.5px; font-weight: 700; background: var(--critical-tx); color: #fff; border-radius: 20px; padding: 1px 7px; min-width: 18px; text-align: center; }
.nav__badge--muted { background: var(--surface-3); color: var(--ink-soft); }

/* Menu icons — duotone (blue) thin-line + hover-only slow animation */
.nav__ico { transition: transform .25s var(--ease); transform-origin: center; color: #2f7df6; }
[data-theme="dark"] .nav__ico { color: #5e9bf2; }
.nav__item:hover .nav__ico { animation: navHover 2.2s ease-in-out infinite; }
@keyframes navHover { 0%, 100% { transform: scale(1.05); } 50% { transform: scale(1.16); } }

/* Duotone medical icon set (blue + light-blue + white) */
.nico { width: 22px; height: 22px; background-repeat: no-repeat; background-position: center; background-size: contain; }
.nico--dashboard  { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='8' height='8' rx='2.2' fill='%231f6fe0'/><rect x='13' y='3' width='8' height='8' rx='2.2' fill='%23bcd6fb'/><rect x='3' y='13' width='8' height='8' rx='2.2' fill='%23bcd6fb'/><rect x='13' y='13' width='8' height='8' rx='2.2' fill='%231f6fe0'/></svg>"); }
.nico--patients   { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='8' r='4.2' fill='%23bcd6fb'/><path d='M3.6 20.6c0-4.6 3.9-7.1 8.4-7.1s8.4 2.5 8.4 7.1v.3a1 1 0 0 1-1 1.1H4.6a1 1 0 0 1-1-1.1z' fill='%231f6fe0'/></svg>"); }
.nico--appts      { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='16' rx='3' fill='%231f6fe0'/><rect x='3' y='5' width='18' height='5' rx='3' fill='%231a4f9c'/><rect x='7' y='3' width='2.4' height='4.6' rx='1.2' fill='%23bcd6fb'/><rect x='14.6' y='3' width='2.4' height='4.6' rx='1.2' fill='%23bcd6fb'/><path d='M12 12.6v5M9.5 15.1h5' stroke='%23ffffff' stroke-width='1.8' stroke-linecap='round'/></svg>"); }
.nico--admissions { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M4 21V9.2l8-5 8 5V21a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1z' fill='%231f6fe0'/><rect x='9' y='13.5' width='6' height='8.5' fill='%23bcd6fb'/><path d='M12 6.4v3.6M10.2 8.2h3.6' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round'/></svg>"); }
.nico--emergency  { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='3' width='18' height='18' rx='6' fill='%231f6fe0'/><path d='M12 7.2v9.6M7.2 12h9.6' stroke='%23ffffff' stroke-width='2.6' stroke-linecap='round'/></svg>"); }
.nico--lab        { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M9.2 3h5.6v1.8h-1V9.2l4.4 7.6a2 2 0 0 1-1.7 3H7.5a2 2 0 0 1-1.7-3L10.2 9.2V4.8h-1z' fill='%231f6fe0'/><path d='M8.1 14.6h7.8l1.6 2.8a1.4 1.4 0 0 1-1.2 2.2H7.7a1.4 1.4 0 0 1-1.2-2.2z' fill='%23bcd6fb'/></svg>"); }
.nico--pharmacy   { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='6' y='8.5' width='12' height='12.5' rx='2.4' fill='%231f6fe0'/><rect x='5' y='4.5' width='14' height='4.4' rx='1.6' fill='%231a4f9c'/><path d='M12 12.2v5M9.5 14.7h5' stroke='%23ffffff' stroke-width='1.8' stroke-linecap='round'/></svg>"); }
.nico--bed        { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M3 10.6h11a4.2 4.2 0 0 1 4.2 4.2v.2H3z' fill='%23bcd6fb'/><path d='M2.2 7.2a1.2 1.2 0 0 1 2.4 0V14h15.2a1.6 1.6 0 0 1 1.6 1.6v4.6a1.2 1.2 0 0 1-2.4 0v-3.4H4.6v3.4a1.2 1.2 0 0 1-2.4 0z' fill='%231f6fe0'/><circle cx='7.6' cy='11.4' r='1.5' fill='%23ffffff'/></svg>"); }
.nico--ot         { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><circle cx='12' cy='12' r='9' fill='%231f6fe0'/><path d='M12 7v5.2l3.4 2' stroke='%23ffffff' stroke-width='1.8' stroke-linecap='round' fill='none'/></svg>"); }
.nico--queue      { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><rect x='3' y='5' width='18' height='3.4' rx='1.7' fill='%231f6fe0'/><rect x='3' y='10.3' width='13' height='3.4' rx='1.7' fill='%23bcd6fb'/><rect x='3' y='15.6' width='9' height='3.4' rx='1.7' fill='%231f6fe0'/></svg>"); }
.nico--inventory  { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 3.4l9 4-9 4-9-4z' fill='%23bcd6fb'/><path d='M3 7.6l9 4v9l-9-4z' fill='%231f6fe0'/><path d='M21 7.6l-9 4v9l9-4z' fill='%231a4f9c'/></svg>"); }
.nico--billing    { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M12 3l9 4.2v2.3H3V7.2z' fill='%231f6fe0'/><rect x='5' y='10.4' width='2.6' height='7.4' fill='%23bcd6fb'/><rect x='10.7' y='10.4' width='2.6' height='7.4' fill='%23bcd6fb'/><rect x='16.4' y='10.4' width='2.6' height='7.4' fill='%23bcd6fb'/><rect x='3.4' y='18.6' width='17.2' height='2.6' rx='1' fill='%231f6fe0'/></svg>"); }
.nico--reports    { background-image:url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M5.5 3h8L19 8.5V20a1 1 0 0 1-1 1H5.5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z' fill='%231f6fe0'/><path d='M13.5 3L19 8.5h-4.5a1 1 0 0 1-1-1z' fill='%23bcd6fb'/><path d='M7.5 12.5h7M7.5 15.8h7M7.5 9.2h3.2' stroke='%23ffffff' stroke-width='1.5' stroke-linecap='round'/></svg>"); }

/* ----- Sidebar collapse toggle (static footer below the scrolling nav) ------- */
.nav-collapse { flex: none; display: flex; align-items: center; gap: 10px;
    width: 100%; padding: 11px 18px; background: var(--surface); border: 0; border-top: 1px solid var(--line);
    cursor: pointer; font-family: inherit; font-size: 12px; font-weight: 600; color: var(--ink-soft);
    transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.nav-collapse:hover { background: var(--surface-2); color: var(--ink); }
.nav-collapse:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
.nav-collapse__ico { flex: none; transition: transform .22s var(--ease); }
.nav-collapse__label { white-space: nowrap; }

/* ----- Collapsed sidebar (icon rail) ---------------------------------------- */
/* data-rail is set on <html> before first paint (App.razor), so the rail never flashes expanded. The width
   shrinks to an icon-only rail and every text label is hidden but LEFT IN THE DOM (font-size:0 / display:none),
   so screen readers still announce each item. The app-frame width animates. */
.app { transition: grid-template-columns .22s var(--ease); }
:root[data-rail="1"] { --sidebar-w: 74px; }
:root[data-rail="1"] .brand { justify-content: center; padding: 0; gap: 0; }
:root[data-rail="1"] .brand__text,
:root[data-rail="1"] .brand .cerno-logo__word { display: none; }   /* rail keeps the mark, drops the wordmark */
/* The identity strip is a grid now, not a flex row, so the rail restacks it rather than reversing a
   flex direction: one column, avatar over actions, with the name and role dropped. Both controls stay
   reachable — signing out must never require expanding the sidebar first. The sign-out LABEL goes but
   its title attribute remains, so the control is still announced and still hoverable. */
:root[data-rail="1"] .who { grid-template-columns: 1fr; grid-template-rows: auto auto;
    justify-items: center; row-gap: 8px; padding: 12px 0; }
:root[data-rail="1"] .who__av { grid-column: 1; grid-row: 1; }
:root[data-rail="1"] .who__name,
:root[data-rail="1"] .who__role { display: none; }
:root[data-rail="1"] .who__actions { grid-column: 1; grid-row: 2; margin: 0; padding: 0;
    border-top: 0; justify-content: center; }
:root[data-rail="1"] .who__actions form { margin-left: 0; }
/* Both labels go in the rail; the title attributes remain, so each control is still announced. */
:root[data-rail="1"] .who__btn span { display: none; }
:root[data-rail="1"] .nav { padding: 8px 8px 24px; }
:root[data-rail="1"] .nav__group { display: none; }                        /* category headers hidden in the rail */
:root[data-rail="1"] .nav__cat .nav__items { grid-template-rows: 1fr; }     /* force every group open so no icon is hidden */
:root[data-rail="1"] .nav__cat + .nav__cat { margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line); }
:root[data-rail="1"] .nav__item { justify-content: center; gap: 0; padding: 0; font-size: 0; }
:root[data-rail="1"] .nav__item.is-active::before { top: 6px; bottom: 6px; }
/* the count badge collapses to a small dot — "needs attention" still reads without the number */
:root[data-rail="1"] .nav__badge { position: absolute; top: 7px; right: 13px; margin: 0; min-width: 0;
    width: 8px; height: 8px; padding: 0; font-size: 0; line-height: 0; border-radius: 50%; }
/* the muted (grey) badge's own surface-3 fill is ~1.2:1 on the sidebar — invisible as an 8px dot; give it a
   perceivable tone so "items waiting" still reads in the rail (the critical/red dot is already fine). */
:root[data-rail="1"] .nav__badge--muted { background: var(--ink-faint); }
:root[data-rail="1"] .nav-collapse { justify-content: center; padding: 11px 0; }
:root[data-rail="1"] .nav-collapse__label { display: none; }
:root[data-rail="1"] .nav-collapse__ico { transform: rotate(180deg); }

/* ----- Topbar --------------------------------------------------------------- */
.main { display: flex; flex-direction: column; min-width: 0; }
.topbar { height: var(--topbar-h); position: sticky; top: 0; z-index: 20; background: color-mix(in srgb, var(--surface) 90%, transparent); backdrop-filter: saturate(1.4) blur(8px); border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 14px; padding: 0 22px; }
.topbar__title { font-size: 16px; font-weight: 600; letter-spacing: -.01em; margin: 0; line-height: 1.3; }
/* Skip-to-content link: off-screen until keyboard focus, then pinned top-left over the app chrome. */
.skip-link { position: fixed; left: 8px; top: -60px; z-index: 1000; background: var(--primary); color: #fff; padding: 8px 14px; border-radius: var(--r-sm); font-size: 13px; font-weight: 600; text-decoration: none; transition: top var(--dur) var(--ease); }
.skip-link:focus { top: 8px; }
/* Dark mode brightens --primary so it can be READ as text on a dark ground (#5e9bf2). The skip link
   uses it the other way round -- as a background under white -- and white on #5e9bf2 measures 2.85:1,
   under the 4.5 AA floor. Measured on a canvas, not inferred from the token. Dark ink on the bright
   accent restores it, and this is the only place in the app that paints white on --primary. */
[data-theme="dark"] .skip-link { color: #0c1422; }
.topbar__crumb { color: var(--ink-faint); font-size: 12px; }
.topbar__crumb .live { color: var(--stable-tx); font-weight: 600; }

/* Signature: the vital pulse — the hospital's live heartbeat (used once) */
.pulse { position: relative; width: 124px; height: 28px; flex: none; margin-left: 2px;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 82%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 14%, #000 82%, transparent); }
.pulse__svg { position: absolute; left: 0; top: 0; width: 240px; height: 28px; animation: ecg 4.5s linear infinite; }
.pulse__line { fill: none; stroke: var(--teal); stroke-width: 1.5; filter: drop-shadow(0 0 3px var(--teal)); }
.pulse__dot { position: absolute; right: 16%; top: 50%; width: 6px; height: 6px; border-radius: 50%; background: var(--teal); transform: translateY(-50%); box-shadow: 0 0 8px var(--teal); }
.pulse__label { position: absolute; right: 0; bottom: -2px; font-size: 9px; letter-spacing: .08em; text-transform: uppercase; color: var(--teal); font-weight: 700; opacity: .9; }
@keyframes ecg { from { transform: translateX(0); } to { transform: translateX(-60px); } }
.search { margin-left: 10px; flex: 1; max-width: 440px; display: flex; align-items: center; gap: 9px; background: var(--surface-2); border: 1px solid transparent; border-radius: var(--r-sm); padding: 0 10px 0 12px; height: 38px; color: var(--ink-soft); transition: border-color var(--dur) var(--ease), background-color var(--dur) var(--ease); }
.search:focus-within { border-color: var(--primary); background: var(--surface); }
.search svg { width: var(--icon-sm); height: var(--icon-sm); }
.search input { border: 0; background: none; outline: none; color: var(--ink); font-size: 13px; width: 100%; }
.kbd { margin-left: auto; font: 600 10.5px var(--font); background: var(--surface-3); color: var(--ink-soft); border-radius: 5px; padding: 2px 6px; letter-spacing: .02em; }
.topbar__spacer { flex: 1; }
.iconbtn { width: var(--hit); height: var(--hit); border-radius: var(--r-sm); border: 1px solid var(--line-btn); background: var(--surface-2); color: var(--ink-soft); display: grid; place-items: center; position: relative; transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.iconbtn:hover { color: var(--ink); border-color: var(--line-strong); background: var(--surface-2); }
.iconbtn svg { width: var(--icon-md); height: var(--icon-md); }
.iconbtn .dot { position: absolute; top: 10px; right: 11px; width: 7px; height: 7px; border-radius: 50%; background: var(--critical-tx); border: 2px solid var(--surface); }

.btn { display: inline-flex; align-items: center; justify-content: center; gap: 7px; height: var(--hit); padding: 0 15px; border-radius: var(--r-sm); border: 1px solid var(--line-btn); background: var(--surface-2); color: var(--ink); font-size: 13px; font-weight: 600; transition: background-color var(--dur) var(--ease), border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.btn:hover { border-color: var(--ink-faint); background: var(--surface-3); }   /* was --surface-2, which is now the resting fill */
.btn--primary { background: var(--grad-btn); border-color: transparent; color: #fff; box-shadow: inset 0 1px 0 rgba(255,255,255,.18), 0 1px 2px rgba(17,32,47,.14); }
.btn--primary:hover { background: var(--grad-btn); border-color: transparent; filter: brightness(1.08); }
.btn--sm { height: 28px; padding: 0 11px; font-size: 12px; }
.btn__ico { width: var(--icon-sm); height: var(--icon-sm); }
/* The third tier, under primary and secondary: a tertiary action that should not compete for the eye —
   Cancel, Clear, Reset, the ✕ on a bill line. Used on 8 screens and defined nowhere, so every one of
   them rendered as a full secondary button and a row of them read as a row of equal choices.
   KioskLayout already had the right idea in its own .k-btn--ghost { background: transparent }.
   The boundary returns on hover and focus rather than never existing: --ink-soft on --surface is
   7.45:1, so the resting state carries itself on text contrast, and SC 1.4.11 wants the 3:1 component
   boundary only where the boundary is what identifies the control. */
.btn--ghost { background: transparent; border-color: transparent; color: var(--ink-soft); box-shadow: none; }
.btn--ghost:hover { background: var(--surface-2); border-color: var(--line-btn); color: var(--ink); }
/* 128 buttons across 91 screens take a disabled attribute and NOTHING said so. Because .btn sets its
   own background and colour, the browser's default greying never applies: a button that does nothing
   looked exactly like a button that works, and the only feedback for clicking it was silence. */
.btn:disabled, .btn[disabled] { opacity: .45; cursor: not-allowed; box-shadow: none; }
.btn:disabled:hover, .btn[disabled]:hover { background: var(--surface-2); border-color: var(--line-btn); }
.btn--primary:disabled:hover, .btn--primary[disabled]:hover { background: var(--grad-btn); border-color: transparent; filter: none; }
.btn--ghost:disabled:hover, .btn--ghost[disabled]:hover { background: transparent; border-color: transparent; }

/* ----- Content -------------------------------------------------------------- */
.content { padding: 16px 20px 36px; display: flex; flex-direction: column; gap: 12px; width: 100%; max-width: 1760px; margin-left: auto; margin-right: auto; }

/* ----- Needs-attention cards (premium semantic cards w/ icon badge) --------- */
.attention { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.alert { display: flex; align-items: center; gap: 14px; padding: 15px 16px 15px 20px; border-radius: var(--r-lg); position: relative; overflow: hidden; border: 1px solid var(--line); box-shadow: var(--shadow-card), var(--card-ring); text-align: left; width: 100%; color: inherit; transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.alert:hover { transform: translateY(-2px); box-shadow: var(--shadow-card), 0 12px 26px rgba(17,32,47,.10); }
.alert::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--spine-w); }
.alert--critical { background: linear-gradient(118deg, var(--critical-bg) 0%, var(--surface) 70%); border-color: color-mix(in srgb, var(--critical) 22%, var(--line)); }
.alert--urgent   { background: linear-gradient(118deg, var(--urgent-bg) 0%, var(--surface) 70%);   border-color: color-mix(in srgb, var(--urgent) 22%, var(--line)); }
.alert--info     { background: linear-gradient(118deg, var(--info-bg) 0%, var(--surface) 70%);     border-color: color-mix(in srgb, var(--info) 22%, var(--line)); }
.alert--critical::before { background: var(--critical-tx); }
.alert--urgent::before   { background: var(--urgent-tx); }
.alert--info::before     { background: var(--info-tx); }
/* Form-message aliases. The component vocabulary is critical/urgent/info, but 20 places across the app
   were written as alert--error / alert--warn / alert--ok, which matched NO rule — so every validation and
   save failure in Cerno rendered as a neutral grey box. An error that does not look like an error is a
   real problem on a screen where somebody is deciding whether the save worked. Aliased rather than
   renamed at 20 call sites, so both vocabularies keep working. */
.alert--error { background: linear-gradient(118deg, var(--critical-bg) 0%, var(--surface) 70%); border-color: color-mix(in srgb, var(--critical) 22%, var(--line)); }
.alert--warn  { background: linear-gradient(118deg, var(--urgent-bg) 0%, var(--surface) 70%);   border-color: color-mix(in srgb, var(--urgent) 22%, var(--line)); }
.alert--ok    { background: linear-gradient(118deg, var(--stable-bg) 0%, var(--surface) 70%);   border-color: color-mix(in srgb, var(--stable) 22%, var(--line)); }
.alert--error::before { background: var(--critical-tx); }
.alert--warn::before  { background: var(--urgent-tx); }
.alert--ok::before    { background: var(--stable-tx); }

.alert__badge { width: 46px; height: 46px; border-radius: 50%; display: grid; place-items: center; flex: none; box-shadow: inset 0 1px 0 rgba(255,255,255,.3); }
.alert__badge svg { width: 23px; height: 23px; stroke-width: 1.6; fill: none; }
.alert--critical .alert__badge { background: color-mix(in srgb, var(--critical) 17%, var(--surface)); color: var(--critical-tx); }
.alert--urgent   .alert__badge { background: color-mix(in srgb, var(--urgent) 20%, var(--surface));   color: var(--urgent-tx); }
.alert--info     .alert__badge { background: color-mix(in srgb, var(--info) 15%, var(--surface));     color: var(--info-tx); }
.alert__main { flex: 1; min-width: 0; }
.alert__top { display: flex; align-items: baseline; gap: 9px; }
.alert__n { font-size: 28px; font-weight: 700; letter-spacing: -.02em; line-height: 1; }
.alert__label { font-size: 13px; font-weight: 600; }
.alert__meta { font-size: 11.5px; color: var(--ink-soft); margin-top: 3px; }
.alert__go { width: var(--icon-sm); height: var(--icon-sm); color: var(--ink-faint); flex: none; }

/* ----- KPI strip (flat tiles — quieter than the hero) ----------------------- */
.kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; margin-top: 6px; }
.kpi { background: var(--grad-sheen), var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 12px 14px; transition: box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease); }
.kpi:hover { box-shadow: var(--shadow-card), 0 10px 24px rgba(91,91,240,.12); transform: translateY(-1px); }
.kpi__label { font-size: 11.5px; color: var(--ink-soft); font-weight: 500; }
.kpi__value { font-size: 20px; font-weight: 700; letter-spacing: -.02em; margin-top: 4px; line-height: 1; }
.kpi__value small { font-size: 12px; font-weight: 600; color: var(--ink-faint); }
.kpi__foot { display: flex; align-items: center; gap: 6px; margin-top: 6px; font-size: 11.5px; }
.delta { display: inline-flex; align-items: center; gap: 4px; font-weight: 700; }
.delta__ico { width: 13px; height: 13px; }
.delta--up { color: var(--stable-tx); } .delta--down { color: var(--critical-tx); } .delta--flat { color: var(--ink-soft); }

/* ----- Premium stat cards (gradient + circular thin-line icon badge) --------- */
.stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
/* Three-card rows. Bare .grid has no template columns, so a stat row placed in it stacks one card
   per row at full width - which is how nine board screens shipped before this was noticed. */
.stats--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.stat { position: relative; overflow: hidden; border-radius: var(--r-lg); padding: 15px 18px; color: #fff; background: var(--stat-grad); box-shadow: 0 8px 20px rgba(63,111,224,.30); display: flex; align-items: center; justify-content: space-between; gap: 10px; transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.stat:hover { transform: translateY(-2px); box-shadow: 0 13px 30px rgba(63,111,224,.40); }
.stat::after  { content: ""; position: absolute; right: -22px; top: -30px; width: 110px; height: 110px; border-radius: 50%; background: rgba(255,255,255,.12); }
.stat::before { content: ""; position: absolute; right: -54px; bottom: -54px; width: 120px; height: 120px; border-radius: 50%; background: rgba(255,255,255,.07); }
.stat__main { position: relative; z-index: 1; }
.stat__label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em; font-weight: 600; opacity: .92; }
.stat__value { font-size: 26px; font-weight: 700; margin-top: 5px; letter-spacing: -.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.stat__badge { position: relative; z-index: 1; width: 52px; height: 52px; border-radius: 50%; background: rgba(255,255,255,.2); display: grid; place-items: center; flex: none; box-shadow: inset 0 1px 0 rgba(255,255,255,.28); }
.stat__badge svg { width: 26px; height: 26px; stroke: #fff; stroke-width: 1.5; fill: none; }

/* ----- Card ----------------------------------------------------------------- */
.grid { display: grid; gap: 12px; }
.grid--2-1 { grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr); }
.card { background: var(--grad-sheen), var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-card), var(--card-ring); display: flex; flex-direction: column; }
.card__head { display: flex; align-items: center; gap: 10px; padding: 10px 16px; border-bottom: 1px solid var(--line); min-height: 44px; }
.card__title { font-size: 14px; font-weight: 700; letter-spacing: -.01em; }
.card__sub { font-size: 11.5px; color: var(--ink-soft); }
.card__tools { margin-left: auto; display: flex; gap: 6px; }
.card__body { padding: 14px; }
.pill { font-size: 11px; font-weight: 600; padding: 3px 9px; border-radius: 20px; background: var(--surface-2); color: var(--ink-soft); }
.tab { display: inline-flex; align-items: center; min-height: 32px; font-size: 12px; font-weight: 600; padding: 0 11px; border-radius: var(--r-sm); color: var(--ink-soft); transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.tab:hover { background: var(--surface-2); }
.tab.is-active { background: var(--primary-050); color: var(--primary); }

/* ----- Chart ---------------------------------------------------------------- */
.chart { width: 100%; height: 188px; display: block; }
.chart__legend { display: flex; gap: 18px; padding: 0 16px 14px; font-size: 11.5px; }
.legend { display: flex; align-items: center; gap: 7px; color: var(--ink-soft); }
.legend i { width: 9px; height: 9px; border-radius: 3px; }

/* ----- Status spine list ---------------------------------------------------- */
.spinelist { display: flex; flex-direction: column; }
.srow { display: grid; grid-template-columns: var(--spine-w) 30px 1fr auto; align-items: center; gap: 11px; padding: 7px 14px 7px 0; border-bottom: 1px solid var(--line); min-height: 48px; transition: background-color var(--dur) var(--ease); }
.srow:last-child { border-bottom: 0; }
.srow:hover { background: var(--surface-2); }
.spine { width: var(--spine-w); align-self: stretch; }
.spine--critical { background: var(--critical-tx); }
.spine--urgent   { background: var(--urgent-tx); }
.spine--stable   { background: var(--stable-tx); }
.spine--info     { background: var(--info-tx); }
.srow__av { width: 30px; height: 30px; border-radius: 50%; background: var(--surface-3); display: grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--ink-soft); margin-left: 10px; }
.srow__name { font-weight: 600; font-size: 13px; }
.srow__meta { font-size: 11.5px; color: var(--ink-soft); }
.srow__right { text-align: right; display: flex; align-items: center; gap: 8px; }

.tag { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 700; padding: 3px 9px; border-radius: 20px; background: var(--surface-2); }
.tag i { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.tag--critical { color: var(--critical-tx); } .tag--critical i { background: var(--critical-tx); }
.tag--urgent   { color: var(--urgent-tx); }   .tag--urgent i   { background: var(--urgent-tx); }
.tag--stable   { color: var(--stable-tx); }   .tag--stable i   { background: var(--stable-tx); }
.tag--info     { color: var(--info-tx); }     .tag--info i     { background: var(--info-tx); }
/* Neutral, for a fact NOBODY HAS RECORDED. Deliberately not --stable green: an unrecorded allergy history
   must never read as "checked and clear", which is exactly how the old "No known allergy" chip read. */
.tag--muted    { color: var(--ink-faint); }   .tag--muted i    { background: var(--ink-faint); }
.tag--warn     { color: var(--urgent-tx); }   .tag--warn i     { background: var(--urgent-tx); }

/* ----- Premium list (rev 4) — ring-avatar rows + soft pills ----------------- */
/* Used for dashboard list cards; the avatar/pill/mono vocabulary is shared with
   the data tables so the whole app reads as one premium family. */
.plist { display: flex; flex-direction: column; padding: 4px; }
.prow { display: flex; align-items: center; gap: 12px; padding: 9px 12px; border-radius: 11px; position: relative; color: inherit;
        transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), background-color var(--dur) var(--ease); }
.prow:hover { background: linear-gradient(120deg, color-mix(in srgb, var(--accent) 5%, var(--surface)), transparent);
              box-shadow: 0 7px 18px rgba(91,91,240,.12); transform: translateY(-1px); }
/* very thin, faint hairline between rows; fades on the hovered row */
.prow:not(:last-child)::after { content: ""; position: absolute; left: 12px; right: 12px; bottom: 0; height: 1px; background: color-mix(in srgb, var(--ink) 6%, transparent); }
.prow:hover::after { opacity: 0; }
.prow__main { flex: 1; min-width: 0; }
.prow__name { font-size: 13.5px; font-weight: 600; letter-spacing: -.01em; }
.prow__meta { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--ink-soft); margin-top: 1px; }
.prow__meta .sep { width: 3px; height: 3px; border-radius: 50%; background: var(--ink-faint); opacity: .5; flex: none; }
.prow__go { width: 30px; height: 30px; border-radius: 50%; display: grid; place-items: center; color: var(--ink-faint); flex: none;
            opacity: 0; transition: opacity var(--dur) var(--ease), background var(--dur) var(--ease), color var(--dur) var(--ease); }
.prow__go svg { width: 15px; height: 15px; }
.prow:hover .prow__go { opacity: 1; }
.prow__go:hover { background: var(--grad-brand); color: #fff; }

/* Status avatar — rounded square, soft status-tinted fill + status-coloured glyph/initials.
   Carries the row's status (replaces the old left colour-spine). Radius follows --r-md (cards/inputs). */
.lav { width: 32px; height: 32px; border-radius: var(--r-md); flex: none; display: grid; place-items: center;
       background: var(--lav-bg, var(--primary-050));
       border: 1.5px solid var(--lav-ring, color-mix(in srgb, var(--primary) 30%, var(--line)));
       color: var(--lav-fg, var(--primary));
       box-shadow: 0 1px 2px rgba(17,32,47,.05);
       transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease); }
.lav__in { width: 100%; height: 100%; display: grid; place-items: center; font-weight: 700; color: inherit; font-size: 12px; letter-spacing: -.02em; }
.lav__in svg { width: 20px; height: 20px; stroke: currentColor; stroke-width: 1.7; fill: none; }
.lav--stable { --lav-bg: var(--stable-bg);   --lav-ring: color-mix(in srgb, var(--stable) 42%, var(--line));   --lav-fg: var(--stable-tx); }
.lav--info   { --lav-bg: var(--info-bg);     --lav-ring: color-mix(in srgb, var(--info) 42%, var(--line));     --lav-fg: var(--info-tx); }
.lav--crit   { --lav-bg: var(--critical-bg); --lav-ring: color-mix(in srgb, var(--critical) 50%, var(--line)); --lav-fg: var(--critical-tx); }
.lav--urgent { --lav-bg: var(--urgent-bg);   --lav-ring: color-mix(in srgb, var(--urgent) 48%, var(--line));   --lav-fg: var(--urgent-tx); }
.lav--muted  { --lav-bg: var(--surface-2);   --lav-ring: var(--line);                                          --lav-fg: var(--ink-faint); }
/* compact table variant: smaller + tighter radius so it sits inside a 36px row */
.lav--sm { width: 26px; height: 26px; border-radius: 7px; }
.lav--sm .lav__in { font-size: 11px; }
.lav--sm .lav__in svg { width: 16px; height: 16px; }

/* Soft status pill with a glowing status dot */
.spill { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; font-weight: 600; padding: 4px 10px; border-radius: 20px; white-space: nowrap; }
.spill .gd { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.spill--stable { background: var(--stable-bg);   color: var(--stable-tx); }   .spill--stable .gd { background: var(--stable-tx);   box-shadow: 0 0 6px var(--stable-tx); }
.spill--info   { background: var(--info-bg);     color: var(--info-tx); }     .spill--info .gd   { background: var(--info-tx);     box-shadow: 0 0 6px var(--info-tx); }
.spill--crit   { background: var(--critical-bg); color: var(--critical-tx); } .spill--crit .gd   { background: var(--critical-tx); box-shadow: 0 0 6px var(--critical-tx); }
.spill--urgent { background: var(--urgent-bg);   color: var(--urgent-tx); }   .spill--urgent .gd { background: var(--urgent-tx);   box-shadow: 0 0 6px var(--urgent-tx); }
/* Neutral, for something NOT RECORDED. Never --stable green: "we have no allergy information" must not
   look like "checked and clear", which is how the old "Stable" chip read on an unasked patient. */
.spill--muted  { background: var(--surface-2);   color: var(--ink-faint); }   .spill--muted .gd  { background: var(--ink-faint); box-shadow: none; }

/* Mono data chip (bed numbers, short codes) */
.mchip { display: inline-flex; align-items: center; font: 600 11px var(--font-mono); padding: 3px 8px; border-radius: 7px; background: var(--surface-2); color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* Tables adopt the same vocabulary: a quiet leading avatar cell */
.table .av-cell { width: 44px; padding-right: 0; }

/* ----- Bed board ------------------------------------------------------------ */
.beds { display: grid; grid-template-columns: repeat(8, minmax(0, 1fr)); gap: 6px; padding: 2px; }
.bed { aspect-ratio: 1; min-height: 38px; border-radius: var(--r-sm); display: grid; place-items: center; font-size: 12px; font-weight: 700; border: 1px solid var(--line); color: var(--ink); background: var(--surface); position: relative; transition: transform var(--dur) var(--ease); }
.bed:hover { transform: translateY(-1px); }
.bed--occupied { background: var(--op-occupied); color: var(--op-occupied-tx); border-color: transparent; }
.bed--cleaning { background: var(--op-cleaning); color: var(--op-cleaning-tx); border-color: transparent; }
.bed--cleaning::after { content: "⟳"; position: absolute; top: 2px; right: 4px; font-size: 9px; }
.bed--available { background: var(--surface); border: 1px dashed var(--line-strong); color: var(--ink-faint); }
.bed--critical { background: var(--critical-bg); color: var(--critical-tx); border-color: transparent; }
.bed--critical::after { content: "!"; position: absolute; top: 1px; right: 5px; font-size: 10px; font-weight: 800; }
.bedkey { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 12px; }
.bedkey span { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--ink-soft); }
.bedkey i { width: 12px; height: 12px; border-radius: 4px; border: 1px solid var(--line); display: grid; place-items: center; font-size: 8px; }

/* ----- Table ---------------------------------------------------------------- */
.table { width: 100%; border-collapse: collapse; }
.table th { text-align: left; font-size: 11px; font-weight: 700; color: var(--ink-soft); padding: 9px 14px; border-bottom: 1px solid var(--line); white-space: nowrap; text-transform: uppercase; letter-spacing: .05em; }
/* sticky column headers — pin under the topbar when scrolling a long worklist. A tinted band (not the
   card's white) + an uppercase label treatment so the header reads as a heading, not a first data row. */
.table thead th { position: sticky; top: var(--topbar-h); z-index: 11; background: var(--surface-2); box-shadow: inset 0 -1px 0 var(--line-strong); }
/* inside a scroll-boxed card region the header must pin to the BOX top, not offset under the topbar
   (the topbar offset would overlay the first rows and swallow their clicks) */
.scrollbox { overflow: auto; }
/* A table inside an inline horizontal-scroll wrapper (`overflow-x:auto`) is its OWN scroll context, so the
   viewport/topbar offset (60px) sticks the header 60px *into* the wrapper — it floats mid-content and reads
   like a stray menu row. Pin it to the wrapper top (0) instead. Attribute selectors match the inline style,
   so this covers all such wrappers without touching each page. */
.scrollbox .table thead th,
/* .tablewrap is the same scroll context expressed as a class rather than an inline style, so the
   attribute selectors below cannot see it. Without this line its header keeps the topbar offset and
   sticks 60px INTO the wrapper - it floats over the first rows, which is exactly the symptom the
   comment above describes. Added after it happened on /reports/cost-and-cash. */
.tablewrap .table thead th,
[style*="overflow-x:auto"] .table thead th,
[style*="overflow-x: auto"] .table thead th,
[style*="overflow:auto"] .table thead th,
[style*="overflow: auto"] .table thead th { top: 0; }
/* Small screens only: ~47 pages render a wide table directly in a card with no scroll wrapper, so at narrow
   widths (small laptops, tablets, split windows) the table pushed the whole page sideways. Give the card that
   directly holds a table its own horizontal scroll instead, and pin the header to the scroll top so it does
   not float. Desktop (>1100px) is untouched — it already has no overflow. */
@media (max-width: 1100px) {
  .card:has(> .table) { overflow-x: auto; }
  .card:has(> .table) > .table thead th { top: 0; }
}
@media (max-width: 720px) { .canteen-grid { grid-template-columns: 1fr; } }
.table td { padding: 0 14px; border-bottom: 1px solid var(--line); font-size: 13px; height: var(--row-h); }
.table tbody tr { cursor: pointer; transition: background-color var(--dur) var(--ease); }
.table tbody tr:hover { background: var(--surface-2); }
/* critical-row emphasis — faint full-row wash + left accent so risk rows pop in a long list */
.table tbody tr.row--crit { background: color-mix(in srgb, var(--critical-bg) 55%, var(--surface)); }
.table tbody tr.row--crit:hover { background: color-mix(in srgb, var(--critical-bg) 80%, var(--surface)); }
.table tbody tr.row--crit > td:first-child { box-shadow: inset 3px 0 0 var(--critical-tx); }
.table .spine-cell { width: 7px; padding: 0; position: relative; }
/* refined status tick: thin, vertically inset, rounded — not a full-height block */
.table .spine-cell i { position: absolute; left: 0; top: 8px; bottom: 8px; width: 3px; border-radius: 0 3px 3px 0; }
.table .rowlink { display: inline-grid; place-items: center; width: var(--hit); height: var(--hit); color: var(--ink-faint); border-radius: var(--r-sm); opacity: .4; transition: opacity var(--dur) var(--ease), background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.table tbody tr:hover .rowlink, .table .rowlink:focus-visible { opacity: 1; }
.table .rowlink:hover { background: var(--surface-3); color: var(--ink); }
.table .rowlink svg { width: var(--icon-sm); height: var(--icon-sm); }

/* ----- Form ----------------------------------------------------------------- */
.form-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px 14px; }
.field { display: flex; flex-direction: column; gap: 5px; }
.field--full { grid-column: 1 / -1; }
.field label { font-size: 12px; font-weight: 600; color: var(--ink-soft); }
.field label .req { color: var(--critical-tx); font-weight: 800; }
/* .input is the same box outside a .field — a control in a flex filter row or a table cell, where the
   descendant selector cannot reach. It was used on 9 sites and defined nowhere, and two billing pages
   had each pasted a private `.cerno-input` into their own <style> block to work around that. One rule
   now serves all three. */
.field input, .field select, .input { height: var(--hit); border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--surface); color: var(--ink); padding: 0 12px; font-size: 13px; outline: none; transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
.field input:focus, .field select:focus, .input:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-050); }

/* A checkbox is not a text field, and the rule above does not know that. `.field input` matched
   [type=checkbox] too, so on 19 sites the box was handed height:44px and 12px of side padding: the
   "Anonymous" tick on /complaints/record measured 13 x 44, a 13px control stretched down a 44px
   column. Excluded here rather than by adding [type=text] above, because the fields being styled are
   text, number, date, datetime-local, email and tel, and an exclusion cannot be forgotten on a new one. */
.field input[type="checkbox"], .field input[type="radio"] { width: 16px; height: 16px; border: 0; padding: 0; background: none; flex: none; accent-color: var(--primary); }

/* A checkbox and its words are one line and one hit target, not a caption above a control. .check was
   used on 6 screens and .field--check on 1, both undefined, so the tick and its text sat unaligned with
   no gap. The second selector also has to out-specify `.field label`, which is the small bold caption
   style and wrong for a choice. */
.check, .field--check label { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 500; color: var(--ink); cursor: pointer; }
.check input, .field--check input { width: 16px; height: 16px; margin: 0; flex: none; accent-color: var(--primary); }

/* A base rule, not a .field one: of the 32 textareas in this app only 12 sit in a .field, and until
   now NONE of them was styled anywhere — clinical notes, the discharge summary and the operation
   note all rendered a hairline monospace box beside Cerno-styled inputs. Every site carried an
   inline resize:vertical, which is the tell: authors were patching one property each because there
   was no rule to inherit.
   font: inherit is the load-bearing declaration. A textarea defaults to monospace, and in this
   system mono is the machine-identifier voice (.data, .mchip) — precisely the wrong register for a
   sentence a nurse typed. Height is left to rows=; a fixed one would clip it. Anything wanting the
   mono voice deliberately, like the CSV box on the trend dashboard, sets it inline and still wins. */
textarea { font: inherit; font-size: 13px; line-height: 1.5; color: var(--ink); background: var(--surface); border: 1px solid var(--line-strong); border-radius: var(--r-sm); padding: 8px 12px; width: 100%; resize: vertical; outline: none; transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease); }
textarea:focus { border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-050); }
textarea[readonly] { background: var(--surface-2); color: var(--ink-soft); }
/* Two names for the hint under a control: the descendant form for one inside a .field, and the BEM
   form for one that is a sibling of the field rather than a child — the registration screen puts these
   under a heading, outside any .field. Only the first was ever defined, so six .field__hint sites each
   carried an inline font-size:11px to compensate, which is the same one-property-at-a-time patching
   that left textarea unstyled. */
.field .hint, .field__hint { font-size: 11px; color: var(--ink-soft); }
.seg { display: inline-flex; background: var(--surface-2); border-radius: var(--r-sm); padding: 3px; gap: 3px; }
/* Anchors as well as buttons: a segmented control on a STATIC-rendered page has to navigate, and an
   @onclick there renders correctly and does nothing. Both forms get the identical box so the two are
   indistinguishable to a reader. min-height 36px is the secondary-control target floor. */
.seg button, .seg a { display: inline-flex; align-items: center; border: 0; background: none; min-height: 36px; padding: 0 15px; border-radius: 5px; font-size: 12.5px; font-weight: 600; color: var(--ink-soft); text-decoration: none; transition: background-color var(--dur) var(--ease), color var(--dur) var(--ease); }
.seg button.is-on, .seg a.is-on { background: var(--surface); color: var(--primary); box-shadow: var(--shadow-card); }
.seg a:hover { color: var(--ink); }
.allergy-confirm { display: flex; align-items: center; gap: 10px; margin-top: 9px; padding: 10px 12px; background: var(--surface-2); border-radius: var(--r-sm); font-size: 12.5px; font-weight: 600; color: var(--ink); }
.allergy-confirm input { width: 17px; height: 17px; accent-color: var(--primary); }

/* ----- Drawer (overlay — gets the real shadow) ------------------------------ */
.drawer { position: fixed; inset: 0; display: none; z-index: 50; }
.drawer.is-open { display: block; }
.drawer__scrim { position: absolute; inset: 0; background: rgba(11,18,30,.5); }
.drawer__panel { position: absolute; right: 0; top: 0; height: 100%; width: min(620px, 94vw); background: var(--surface); box-shadow: var(--shadow-md); display: flex; flex-direction: column; animation: slidein .2s var(--ease); }
.drawer__head { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--line); }
.drawer__title { font-size: 15px; font-weight: 700; }
.drawer__body { padding: 20px; overflow-y: auto; flex: 1; }
.drawer__foot { padding: 14px 20px; border-top: 1px solid var(--line); display: flex; gap: 10px; justify-content: flex-end; }
@keyframes slidein { from { transform: translateX(18px); opacity: .7; } to { transform: none; opacity: 1; } }

/* ----- Utilities ------------------------------------------------------------ */
.row { display: flex; align-items: center; gap: 10px; }
.muted { color: var(--ink-soft); }

/* Both of these were used across a dozen screens and defined nowhere, so they styled nothing.
   .tablewrap is the one that mattered: without it a wide table forces the whole page to scroll
   sideways on a narrow screen instead of scrolling inside its own box. */
.tablewrap { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
.eyebrow { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); }
.sec-title { font-size: 12px; font-weight: 700; color: var(--ink); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* ===== Patient chart ======================================================== */
.pulse--crit .pulse__line { stroke: var(--critical-tx); filter: drop-shadow(0 0 3px var(--critical-tx)); }
.pulse--crit .pulse__dot  { background: var(--critical-tx); box-shadow: 0 0 8px var(--critical-tx); }

/* Patient header — the wristband */
.phead { position: relative; overflow: hidden; display: flex; align-items: center; gap: 16px; padding: 16px 18px 16px 24px; }
.phead::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: var(--spine-w); }
.phead--critical::before { background: var(--critical-tx); }
.phead--urgent::before   { background: var(--urgent-tx); }
.phead--stable::before   { background: var(--stable-tx); }
.phead--muted::before    { background: var(--ink-faint); }
.phead__av { width: 56px; height: 56px; border-radius: 14px; background: var(--grad-brand); color: #fff; display: grid; place-items: center; font-weight: 700; font-size: 19px; flex: none; box-shadow: var(--glow-brand); }
.phead__id { flex: 1; min-width: 0; }
.phead__name { font-size: 18px; font-weight: 700; letter-spacing: -.02em; }
.phead__sub { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.barcode { height: 15px; width: 78px; opacity: .5; background-image: repeating-linear-gradient(90deg, var(--ink-soft) 0 1px, transparent 1px 2px, var(--ink-soft) 2px 4px, transparent 4px 6px, var(--ink-soft) 6px 7px, transparent 7px 9px); }
.phead__meta { display: flex; flex-wrap: wrap; gap: 7px 12px; margin-top: 9px; font-size: 12px; color: var(--ink-soft); }
.phead__actions { display: flex; gap: 8px; align-items: center; flex: none; }
.chip { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; font-weight: 600; padding: 4px 10px; border-radius: 20px; background: var(--surface-2); color: var(--ink-soft); }
.chip b { color: var(--ink); }

.allergy-banner { display: flex; align-items: center; gap: 10px; padding: 11px 16px; border-radius: var(--r-md); background: color-mix(in srgb, var(--critical) 12%, var(--surface)); border: 1px solid color-mix(in srgb, var(--critical) 30%, var(--line)); color: var(--critical-tx); font-weight: 600; font-size: 12.5px; box-shadow: var(--shadow-card); }
.allergy-banner svg { width: 18px; height: 18px; flex: none; }
.allergy-banner b { font-weight: 700; }
/* The informational sibling. It lived in ChangePassword.razor's own <style> block while THREE other
   screens used it — the claims monitor, the e-invoice monitor and the OTP page — where a page's inline
   block does not reach. So on those screens a role="status" notice fell back to the bare
   .allergy-banner and rendered in critical red: an ordinary "here is how this works" line shown as an
   alarm. Expressed in the --info token family rather than the rgba(91,91,240) it was written with,
   which was a purple-blue belonging to no part of this palette. */
.allergy-banner--info { background: color-mix(in srgb, var(--info) 10%, var(--surface)); border-color: color-mix(in srgb, var(--info) 28%, var(--line)); color: var(--info-tx); }

/* Vitals strip — the honest vital pulse */
.vitals { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; }
.vital { background: var(--grad-sheen), var(--surface); border: 1px solid var(--line); border-radius: var(--r-md); padding: 12px 14px; box-shadow: var(--shadow-card), var(--card-ring); }
.vital__label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--ink-soft); font-weight: 600; }
.vital__val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 21px; font-weight: 600; margin-top: 5px; line-height: 1; letter-spacing: -.01em; }
.vital__val small { font-size: 11px; color: var(--ink-faint); font-weight: 500; font-family: var(--font); }
.vital__foot { margin-top: 7px; font-size: 11px; color: var(--ink-soft); }
.vital__foot .flag { font-weight: 700; }
.vital--warn { background: linear-gradient(135deg, var(--urgent-bg), var(--surface) 72%); border-color: color-mix(in srgb, var(--urgent) 30%, var(--line)); }
.vital--warn .vital__val, .vital--warn .flag { color: var(--urgent-tx); }
.vital--crit { background: linear-gradient(135deg, var(--critical-bg), var(--surface) 72%); border-color: color-mix(in srgb, var(--critical) 32%, var(--line)); }
.vital--crit .vital__val, .vital--crit .flag { color: var(--critical-tx); }
.vital__ecg { height: 24px; margin-top: 7px; position: relative; overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent); }
.vital__ecg svg { position: absolute; left: 0; top: 0; width: 240px; height: 24px; animation: ecg 3.6s linear infinite; }
.vital__ecg path { fill: none; stroke: var(--urgent-tx); stroke-width: 1.5; }

/* Tabs */
.ptabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); padding: 0 6px; overflow-x: auto; }
.ptab { padding: 12px 14px; font-size: 13px; font-weight: 600; color: var(--ink-soft); position: relative; white-space: nowrap; }
.ptab:hover { color: var(--ink); }
.ptab.is-active { color: var(--primary); }
.ptab.is-active::after { content: ""; position: absolute; left: 12px; right: 12px; bottom: -1px; height: 2px; background: var(--grad-brand); border-radius: 2px; }

/* Lists / timeline / care team */
.subsec { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--ink-faint); font-weight: 700; margin: 2px 0 8px; }
.cli { display: flex; align-items: center; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--line); }
.cli:last-child { border-bottom: 0; }
.cli__main { flex: 1; min-width: 0; }
.cli__title { font-size: 13px; font-weight: 600; }
.cli__sub { font-size: 11.5px; color: var(--ink-soft); margin-top: 1px; }
.cli__meta { font-size: 11.5px; color: var(--ink-soft); text-align: right; white-space: nowrap; }
.timeline { position: relative; padding-left: 18px; margin-top: 4px; }
.timeline::before { content: ""; position: absolute; left: 4px; top: 4px; bottom: 6px; width: 2px; background: var(--line); }
.tl { position: relative; padding-bottom: 14px; }
.tl:last-child { padding-bottom: 0; }
.tl::before { content: ""; position: absolute; left: -18px; top: 3px; width: 9px; height: 9px; border-radius: 50%; background: var(--primary); box-shadow: 0 0 0 3px var(--surface); }
.tl__time { font-size: 10.5px; color: var(--ink-faint); font-family: var(--font-mono); }
.tl__title { font-size: 12.5px; font-weight: 600; margin-top: 1px; }
.tl__body { font-size: 12px; color: var(--ink-soft); margin-top: 2px; }
.kv { display: flex; align-items: center; gap: 11px; padding: 8px 0; border-bottom: 1px solid var(--line); }
.kv:last-child { border-bottom: 0; }
.kv__av { width: 32px; height: 32px; border-radius: 50%; background: var(--surface-3); display: grid; place-items: center; font-size: 11px; font-weight: 700; color: var(--ink-soft); flex: none; }
.kv__main { flex: 1; min-width: 0; }
.kv__t { font-size: 12.5px; font-weight: 600; }
.kv__s { font-size: 11px; color: var(--ink-soft); }

@media (max-width: 1180px) { .vitals { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 860px) { .vitals { grid-template-columns: repeat(2, 1fr); } .phead { flex-wrap: wrap; } }

/* ----- Responsive ----------------------------------------------------------- */
@media (max-width: 1180px) { .kpis, .stats, .stats--3 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .attention { grid-template-columns: 1fr; } .grid--2-1 { grid-template-columns: 1fr; } .beds { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
@media (max-width: 860px) { .app { grid-template-columns: 1fr; } .sidebar { display: none; } .form-grid { grid-template-columns: 1fr; } .beds { grid-template-columns: repeat(4, 1fr); } }
@media (prefers-reduced-motion: reduce) { * { animation: none !important; transition: none !important; } }

/* Stronger ambient glow in dark mode (premium) — same fixed-layer approach as light. */
[data-theme="dark"] body::before {
  background:
    radial-gradient(940px 540px at 4% -14%, rgba(109,107,255,.14), transparent 60%),
    radial-gradient(860px 540px at 106% -8%, rgba(14,143,160,.10), transparent 56%);
}

:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }

/* ----- Modal dialog (ModalPopup -> CernoDialog) ----------------------------- */
.cdialog { position: fixed; inset: 0; z-index: 60; display: grid; place-items: center; padding: 24px; }
.cdialog__scrim { position: absolute; inset: 0; background: rgba(11,18,30,.5); backdrop-filter: blur(2px); animation: cdfade .15s var(--ease); }
.cdialog__panel { position: relative; width: min(520px, 96vw); max-height: 90vh; overflow: auto; background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-md); animation: cdpop .18s var(--ease); }
.cdialog__head { display: flex; align-items: center; gap: 10px; padding: 15px 18px; border-bottom: 1px solid var(--line); }
.cdialog__title { font-size: 15px; font-weight: 700; letter-spacing: -.01em; }
.cdialog__x { margin-left: auto; background: none; border: 0; color: var(--ink-faint); cursor: pointer; display: grid; place-items: center; padding: 6px; border-radius: var(--r-sm); }
.cdialog__x:hover { background: var(--surface-2); color: var(--ink); }
.cdialog__body { padding: 18px; }
.cdialog__foot { padding: 14px 18px; border-top: 1px solid var(--line); display: flex; gap: 10px; justify-content: flex-end; }
@keyframes cdfade { from { opacity: 0; } to { opacity: 1; } }
@keyframes cdpop { from { transform: translateY(8px) scale(.98); opacity: .6; } to { transform: none; opacity: 1; } }

/* ----- Empty states -------------------------------------------------------- */
.empty { display: grid; place-items: center; text-align: center; gap: 3px; padding: 46px 20px; }
.empty__ico { width: 46px; height: 46px; border-radius: var(--r-md); display: grid; place-items: center; background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-faint); margin-bottom: 9px; }
.empty__ico svg { width: 22px; height: 22px; stroke: currentColor; stroke-width: 1.5; fill: none; }
.empty__title { font-size: 14px; font-weight: 700; color: var(--ink); }
.empty__msg { font-size: 12.5px; color: var(--ink-soft); max-width: 340px; line-height: 1.5; }
.empty .btn { margin-top: 12px; }
.table .empty-cell { padding: 0; }

/* ----- Skeleton loading ---------------------------------------------------- */
.skel { background: linear-gradient(100deg, var(--surface-2) 28%, var(--surface-3) 50%, var(--surface-2) 72%); background-size: 200% 100%; animation: skelshimmer 1.3s ease-in-out infinite; border-radius: 6px; display: inline-block; }
.skel-av { width: 26px; height: 26px; border-radius: 7px; }
.skel-line { height: 11px; vertical-align: middle; }
@keyframes skelshimmer { from { background-position: 200% 0; } to { background-position: -200% 0; } }

/* ----- Command palette (Cmd / Ctrl-K) -------------------------------------- */
.cmdk { position: fixed; inset: 0; z-index: 80; display: grid; place-items: start center; padding: 12vh 16px 16px; }
.cmdk[hidden] { display: none; }
.cmdk__backdrop { position: absolute; inset: 0; background: rgba(11,18,30,.42); backdrop-filter: blur(2px); animation: cdfade .12s var(--ease); }
.cmdk__panel { position: relative; width: min(560px, 96vw); background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--shadow-md); overflow: hidden; animation: cdpop .16s var(--ease); }
.cmdk__search { display: flex; align-items: center; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--line); }
.cmdk__search svg { width: 18px; height: 18px; stroke: var(--ink-faint); stroke-width: 1.8; fill: none; flex: none; }
.cmdk__search input { flex: 1; border: 0; background: none; outline: none; color: var(--ink); font-size: 14px; }
.cmdk__list { max-height: 46vh; overflow-y: auto; padding: 6px; }
.cmdk__sec { font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-faint); padding: 11px 10px 4px; }
.cmdk__item { display: flex; align-items: center; gap: 11px; padding: 0 11px; height: 40px; border-radius: var(--r-sm); color: var(--ink); font-size: 13px; font-weight: 500; cursor: pointer; }
.cmdk__item .cmdk__meta { margin-left: auto; font: 500 11px var(--font-mono, monospace); color: var(--ink-faint); }
.cmdk__item.is-active, .cmdk__item:hover { background: var(--grad-nav); color: var(--primary); }
.cmdk__item.is-active .cmdk__meta, .cmdk__item:hover .cmdk__meta { color: color-mix(in srgb, var(--primary) 70%, var(--ink-faint)); }
.cmdk__empty { padding: 30px; text-align: center; color: var(--ink-soft); font-size: 13px; }
.cmdk__foot { display: flex; gap: 14px; padding: 9px 14px; border-top: 1px solid var(--line); font-size: 11px; color: var(--ink-faint); }
.cmdk__foot kbd { font: 600 10px var(--font); background: var(--surface-3); color: var(--ink-soft); border-radius: 4px; padding: 1px 5px; }

/* ----- Dashboard bed-capacity hero ----------------------------------------- */
.capstrip { padding: 17px 20px 18px; }
.capstrip__top { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 13px; }
.capstrip__label { font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-soft); font-weight: 600; }
.capstrip__big { font: 800 32px/1 var(--font); letter-spacing: -.025em; color: var(--ink); display: flex; align-items: baseline; gap: 9px; margin-top: 5px; }
.capstrip__big small { font-size: 13px; font-weight: 600; color: var(--ink-soft); letter-spacing: 0; }
.capstrip__legend { display: flex; gap: 18px; font-size: 12px; }
.capstrip__legend span { display: inline-flex; align-items: center; gap: 7px; font-weight: 600; color: var(--ink); }
.capstrip__legend small { color: var(--ink-soft); font-weight: 500; }
.capstrip__legend i { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.dot--occ { background: linear-gradient(90deg, var(--primary), color-mix(in srgb, var(--accent) 70%, var(--primary))); }
.dot--free { background: var(--surface-3); border: 1px solid var(--line-strong); }

/* Blazor's unhandled-error banner (shown by the framework when a circuit faults; hidden inline until then).
   Styled here since the stock template's scoped MainLayout.razor.css was removed — its purple-gradient .sidebar
   rule silently repainted the app sidebar once static web assets were enabled. */
#blazor-error-ui { position: fixed; left: 0; right: 0; bottom: 0; z-index: 1000; padding: 10px 16px; background: var(--critical-bg, #fef2f2); color: var(--critical-tx, #b91c1c); border-top: 1px solid var(--line-strong, #fecaca); font-size: 13px; box-shadow: 0 -2px 8px rgba(17,32,47,.12); }
#blazor-error-ui .reload { font-weight: 600; color: inherit; text-decoration: underline; }

/* ============================================================================
   Brand logo — the Cerno concentric-arc mark (see CernoLogo.razor). Colours are
   tokens so the whole mark inverts for dark mode; `--on-dark` forces the bright
   treatment on always-dark surfaces (the login identity panel).
   ============================================================================ */
/* --logo-slate tints the ARCS (a graphic, judged at the 3:1 non-text bar). --logo-sub is a separate
   token for the wordmark suffix, because that is TEXT and has to clear 4.5:1: reusing the arc slate on
   it measured 2.27:1 on canvas, which axe flags and which is genuinely hard to read at the 13px the
   suffix renders at. Splitting them lets the arcs keep their tint without dragging the text down. */
:root            { --logo-teal: #2c7a78; --logo-slate: #93a9c1; --logo-ink: #26374a; --logo-sub: #5a6b82; }  /* sub 5.11:1 on --canvas */
[data-theme="dark"] { --logo-teal: #43b6b0; --logo-slate: #a9c3da; --logo-ink: #eaf1f8; --logo-sub: #a9c3da; }

.cerno-logo { --logo-size: 30px; display: inline-flex; align-items: center; gap: 11px; }
.cerno-logo--on-dark { --logo-teal: #43b6b0; --logo-slate: #a9c3da; --logo-ink: #eef4fa; --logo-sub: #a9c3da; }
.cerno-logo__mark { width: var(--logo-size); height: var(--logo-size); flex: none; display: block; }
.cerno-logo__arc { transition: stroke var(--dur) var(--ease); }
.cerno-logo__arc--teal  { stroke: var(--logo-teal); }
.cerno-logo__arc--slate { stroke: var(--logo-slate); }
/* ONE lockup, on every surface: mark + "Cerno" + the suffix, on a single baseline. The app previously
   carried four variations of this — the sidebar bolded "HMS", the change-password card bolded "Cerno",
   login used a third weight pairing, and the kiosk stacked them — which is the kind of drift nobody
   notices per-screen and everybody notices side by side. Anything CONTEXTUAL (the facility name, the
   terminal name, "Change password") is rendered by the surface, outside this component: it is not part
   of the brand and must not compete with it. */
.cerno-logo__word { display: flex; align-items: baseline; gap: .34em; line-height: 1; white-space: nowrap; }
.cerno-logo__name { font-weight: 700; font-size: calc(var(--logo-size) * .5); letter-spacing: -.01em; color: var(--logo-ink); }
.cerno-logo__sub  { font-weight: 500; font-size: calc(var(--logo-size) * .5); letter-spacing: -.01em; color: var(--logo-sub); }

/* ============================================================================
   MIS DASHBOARD — "read the house like a monitor"
   Built on the existing tokens and the app's clinical-pressure vernacular. The
   one expressive device is the CLINICAL PRESSURE SCALE: bed load maps to
   stable→urgent→critical, so colour carries meaning, never decoration.
   ============================================================================ */

/* The old "state of the house" hero + meter moved to the shared <BedBanner> component
   (styles in dashboard.css) so /modules and /dashboard render one identical bed banner. */

/* ----- Ward-pressure ladder (SIGNATURE) ------------------------------------ */
/* Each ward is a capacity track filled to live occupancy and coloured by the
   clinical pressure tier — the single element the page is remembered by. */
.ward { display: flex; flex-direction: column; padding: 6px; }
.wrow { display: grid; grid-template-columns: 148px 1fr 62px; align-items: center; gap: 14px; padding: 9px 12px; border-radius: 11px; position: relative;
        transition: background-color var(--dur) var(--ease); }
.wrow:hover { background: var(--surface-2); }
.wrow__name { min-width: 0; }
.wrow__ward { font-size: 13px; font-weight: 600; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wrow__tier { font: 600 10.5px var(--font); letter-spacing: .04em; text-transform: uppercase; margin-top: 2px; }
.wrow__tier--calm { color: var(--stable-tx); } .wrow__tier--busy { color: var(--urgent-tx); } .wrow__tier--full { color: var(--critical-tx); }
.wtrack { height: 13px; border-radius: 7px; background: var(--surface-3); overflow: hidden; box-shadow: inset 0 1px 1px rgba(17,32,47,.07); }
.wtrack__fill { height: 100%; border-radius: 7px; transition: width .85s var(--ease); min-width: 5px; }
.wtrack__fill--calm { background: var(--stable); }
.wtrack__fill--busy { background: var(--urgent); }
.wtrack__fill--full { background: var(--critical); }
.wrow__pct { text-align: right; }
.wrow__pctv { font: 700 15px/1 var(--font-mono); font-variant-numeric: tabular-nums; letter-spacing: -.02em; color: var(--ink); }
.wrow__cap { font: 500 10.5px var(--font-mono); color: var(--ink-faint); margin-top: 2px; }

/* ----- Seasonality trace (registrations by month) -------------------------- */
.trace__wrap { padding: 14px 12px 4px; }
.trace__foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 16px 14px; font-size: 11.5px; color: var(--ink-soft); }
.trace__foot b { color: var(--ink); font-weight: 600; }
.trace__foot .data { color: var(--ink); }
.tracesvg { width: 100%; height: auto; display: block; overflow: visible; }
.tracesvg .gridline { stroke: var(--line); stroke-width: 1; }
.tracesvg .area { fill: url(#traceGrad); }
.tracesvg .line { fill: none; stroke: var(--primary); stroke-width: 2.25; stroke-linejoin: round; stroke-linecap: round; }
.tracesvg .peak { fill: var(--surface); stroke: var(--primary); stroke-width: 2.5; }
.tracesvg .peakhalo { fill: color-mix(in srgb, var(--primary) 22%, transparent); }
.tracesvg .mlabel { fill: var(--ink-faint); font: 500 9.5px var(--font); text-anchor: middle; }

/* ----- Right-column stack: trace card over a quiet patient-mix card --------- */
.rstack { display: flex; flex-direction: column; gap: 12px; }
.rstack > .card:last-child { flex: 1; }
.mix { display: flex; flex-direction: column; gap: 13px; padding: 15px 16px; justify-content: center; height: 100%; }
.mixrow { display: grid; grid-template-columns: 74px 1fr 62px; align-items: center; gap: 12px; }
.mixrow__k { font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.mixrow__bar { height: 10px; border-radius: 6px; background: var(--surface-3); overflow: hidden; box-shadow: inset 0 1px 1px rgba(17,32,47,.06); }
.mixrow__fill { height: 100%; border-radius: 6px; transition: width .8s var(--ease); min-width: 4px;
                background: linear-gradient(90deg, var(--primary), color-mix(in srgb, var(--accent) 70%, var(--primary))); }
.mixrow__fill--alt { background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 60%, var(--primary)), var(--accent)); }
.mixrow__v { text-align: right; font: 600 13px var(--font-mono); font-variant-numeric: tabular-nums; color: var(--ink); }
.mixrow__v small { color: var(--ink-faint); font-weight: 500; margin-left: 3px; }

@media (max-width: 1180px) {
  .wrow { grid-template-columns: 124px 1fr 58px; }
}
@media (prefers-reduced-motion: reduce) {
  .wtrack__fill { transition: none; }
}

/* ----- Data visualisation (TrendChart) -------------------------------------
   Series hues are their OWN tokens, not the UI's --primary/--teal/--accent.
   Two reasons: (1) the UI blues sit outside the categorical lightness band in
   dark mode and land only ΔE 10.7 apart under normal vision (the CVD/normal
   -vision gates want >= 15), and (2) status colours (--critical/--urgent/
   --stable) must never impersonate a series. These three steps are validated
   per mode — light on --surface #fff, dark on #131d2e — and pass the lightness
   band, chroma floor, protan/deutan separation, normal-vision floor and 3:1
   contrast checks. Re-validate before re-stepping any of them.
   Slot order is fixed: a series keeps its hue when a filter removes another. */
:root {
  --viz-1: #1a56a8;   /* blue   — same step as --primary */
  --viz-2: #0f8b5f;   /* green  */
  --viz-3: #8b4bc7;   /* violet */
  --viz-other: var(--ink-faint);   /* the folded tail: not an identity */
  --viz-grid: var(--line);
}
[data-theme="dark"] {
  --viz-1: #4b90e8;
  --viz-2: #16a97f;
  --viz-3: #a568e6;
}

.viz { margin: 0; }
/* Capped width: the viewBox is ~700 units wide, so letting the card stretch it to 1200px would blow the
   11px tick text up to 19px. The cap keeps the scale factor near 1 in both a 1- and a 2-column grid. */
.viz__svg { display: block; width: 100%; max-width: 860px; margin: 0 auto; height: auto; overflow: visible; }
.viz__grid { stroke: var(--viz-grid); stroke-width: 1; shape-rendering: crispEdges; }
.viz__axis { stroke: var(--line-strong); stroke-width: 1; shape-rendering: crispEdges; }
.viz__tick { fill: var(--ink-faint); font-size: 11px; font-variant-numeric: tabular-nums; }
.viz__tick--x { text-anchor: middle; }
.viz__tick--x-tilt { text-anchor: end; font-variant-numeric: normal; }
.viz__tick--y { text-anchor: end; }
.viz__line { fill: none; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
.viz__area { stroke: none; opacity: .1; }
.viz__dot { stroke: var(--surface); stroke-width: 2; }
/* A lone observation among drawn lines: a wider surface ring so it reads as its own mark rather than the
   end of a line that ran off the plot. */
.viz__dot--lone { stroke-width: 3; }
.viz__note { fill: var(--ink); font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums; }
.viz__note-bg { fill: var(--surface); opacity: .82; }
/* Hover column: a band-wide hit target (never a pinpoint) that reveals a crosshair.
   The value itself is in the native <title> tooltip, the legend and the table view,
   so hover only ever enhances. */
.viz__hit { fill: transparent; }
.viz__cross { stroke: var(--ink-faint); stroke-width: 1; opacity: 0; pointer-events: none; }
.viz__hitcol:hover .viz__cross, .viz__hitcol:focus-visible .viz__cross { opacity: .5; }
.viz__hitcol:focus-visible { outline: none; }
.viz__bar { transition: opacity var(--dur) var(--ease); }
.viz__bar:hover { opacity: .82; }
.viz__empty { fill: var(--ink-faint); font-size: 12px; text-anchor: middle; }
/* The insufficient-data state: headline on faint ink, the value line a step brighter so the one number
   that DOES exist stays readable. Both inherit the theme's ink tokens, so it follows light/dark. */
.viz__empty-detail { fill: var(--ink-soft); font-size: 11.5px; text-anchor: middle; font-variant-numeric: tabular-nums; }

.viz__legend { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; }
.viz__key { display: inline-flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--ink-soft); }
.viz__swatch { width: 12px; height: 3px; border-radius: 2px; flex: none; }
.viz__keyval { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
/* Says in words why a series in a populated chart has a marker and no line. */
.viz__keynote { color: var(--ink-faint); font-size: 10.5px; }
.viz__caption { font-size: 11.5px; color: var(--ink-soft); margin-top: 8px; }
.viz__tableview { margin-top: 10px; font-size: 11.5px; color: var(--ink-soft); }
.viz__tableview > summary { cursor: pointer; user-select: none; }
.viz__tableview table { width: 100%; border-collapse: collapse; margin-top: 8px; }
.viz__tableview th, .viz__tableview td { text-align: right; padding: 4px 8px; border-bottom: 1px solid var(--line); white-space: nowrap; }
.viz__tableview th:first-child, .viz__tableview td:first-child { text-align: left; }
.viz__tableview th { color: var(--ink-soft); font-weight: 700; }
.viz__tableview td { color: var(--ink); font-variant-numeric: tabular-nums; }
.viz__scroll { max-height: 260px; overflow: auto; }
@media (prefers-reduced-motion: reduce) {
  .viz__bar { transition: none; }
}

/* ---- action refusal ------------------------------------------------------------------------------
   Shown by ActionRefusalNotice when a privilege-guarded write refuses. Fixed rather than inline: the
   page that refused is still on screen behind it and must stay readable, and the notice has to appear
   the same way on all 66 pages that can raise it. Below the command palette, above everything else. */
.refusal {
  position: fixed; z-index: 900; left: 50%; transform: translateX(-50%);
  bottom: 24px; width: min(560px, calc(100vw - 32px));
  animation: refusalIn .22s var(--ease) both;
}
@keyframes refusalIn { from { opacity: 0; transform: translate(-50%, 10px); } to { opacity: 1; transform: translate(-50%, 0); } }
.refusal__box { align-items: flex-start; }
.refusal__box:hover { transform: none; }          /* .alert lifts on hover; a fixed banner must not */
.refusal__ico { color: var(--critical-tx); flex: 0 0 auto; margin-top: 2px; }
.refusal__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.refusal__text strong { font-size: 14px; }
.refusal__text span { font-size: 13px; color: var(--ink-soft); }
.refusal__hint { color: var(--ink-faint) !important; }
.refusal__close {
  margin-left: auto; flex: 0 0 auto; background: none; border: 0; cursor: pointer;
  color: var(--ink-faint); border-radius: var(--r-sm); display: grid; place-items: center;
  /* 36px is the brand floor for a secondary control; padding alone gave 28 and it measured short. */
  min-width: 36px; min-height: 36px;
}
.refusal__close:hover { color: var(--ink); background: var(--surface-2); }
.refusal__close:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .refusal { animation: none; } }

/* ── Shared column system for worklist and report tables ─────────────────────────────────────────
   ONE set of column widths for the ~23 worklist/report tables, not 23 copies of the same numbers.
   "Patient" appears on 20 of them, an MRN/UHID/Reg. no. on 18, a date on all 23 — per-page rules
   would be the same values restated and would drift apart the first time one was tuned.

   WHY table-layout:fixed is the whole fix. Under the default auto layout the browser renegotiates
   every column against its content on every render, and it optimises for fitting, not for reading.
   Measured on /admissions at 1366px: an 11-character MRN was squeezed to 82px and wrapped on all 96
   rows, while "Ward / Floor" was handed 330px it had no use for. Because .table td carries a fixed
   height: var(--row-h) of 36px and the line-height is 18px, a wrapped cell does NOT grow the row —
   it crams two lines into the same 36px. That is why it reads as broken rather than as merely tall.

   The numbers below are minimum-content widths, not taste: .table th is nowrap and uppercase, so a
   column can never be narrower than its own heading without clipping it. Each was set from the
   longer of (heading width, longest value) and then verified in the browser at 1366px. */
.dtbl { table-layout: fixed; }

/* Fixed columns — content whose length is bounded by its format. */
.dtbl .c-id    { width: 116px; }   /* MRN / UHID / Reg. no.  — "HWMC-000330" */
.dtbl .c-no    { width: 122px; }   /* document numbers       — heading "ADMISSION NO." is the binding constraint */
.dtbl .c-date  { width: 110px; }   /* dd-MM-yyyy */
.dtbl .c-when  { width: 152px; }   /* date + time on one line */
.dtbl .c-age   { width: 116px; }   /* "Age / gender" — the heading, again, not the value */
.dtbl .c-mini  { width: 82px;  }   /* bed, room, floor, from, to, type, mode */
.dtbl .c-tag   { width: 104px; }   /* status chips */
.dtbl .c-money { width: 138px; text-align: right; }   /* fits the heading "RECEIVED AMOUNT", which is wider than any figure under it */
.dtbl .c-money-h { text-align: right; }
/* Same reason as .c-money: digits line up down the column and a total becomes scannable. .c-num is the
   most-used column class in this app — 71 sites across 11 screens — and was the ONLY one of the sixteen
   c-* column classes with no rule anywhere, so every figure in every dense table sat left-aligned
   against its neighbour's prose. Deliberately no width: these headings ("PER CASE USED",
   "CONTRIBUTION") are wider than the figures under them, and the leftover-division the column gets
   today already fits them — pinning a width here is what clipped headings elsewhere in this file. */
.dtbl .c-num { text-align: right; }
.dtbl .c-sn    { width: 58px;  }
.dtbl .c-chk   { width: 42px;  }   /* a checkbox column with no width gets whatever is left over — which was 30px, and clipped the box itself */
.dtbl .c-act   { width: 108px; }   /* row buttons */

/* Flexible columns share whatever is left and truncate rather than wrap. Measured on the admitted /
   discharged report at 1366px: its columns want 1532px and the content area is 1061px, so something has
   to give. A truncated line you can hover beats two lines crammed into a 36px row — and horizontal
   scroll was rejected because any overflow ancestor turns the sticky column header off, which a
   96-row worklist needs more than it needs the last 8 characters of a ward name. */
/* auto, NOT a percentage. Under table-layout:fixed a percentage column cannot absorb leftover space,
   so on a sparse table the fixed px columns swallow it instead: /radiology/acknowledge gave "Lab no"
   219px for a 3-character value while the 41-character patient name got 159. An auto column takes the
   slack, which is exactly what a prose column should do. The cost is that prose columns share equally
   when space is tight rather than ranking the name first — a fair split beats a broken one. */
.dtbl .c-name, .dtbl .c-text, .dtbl .c-wide { width: auto; }
/* Every cell truncates. With table-layout:fixed the TH already fixes the column width, so this needs no
   per-cell class — and a rule that has to be repeated on every td is a rule that will be forgotten on one. */
.dtbl td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* One opt-out, for a column whose content IS the point rather than a label. The board views carry a
   "how to read it" column holding the sentence that explains why a figure is or is not publishable;
   truncating that to "for a p..." removes the only thing the column is for. The row grows instead. */
.dtbl td.c-prose { white-space: normal; overflow: visible; text-overflow: clip; }
/* Money right-aligns via .dtbl .c-money above, which covers th and td both; the td-only duplicate that
   used to sit here said the same thing twice. */
/* Tighter gutters than the general .table (14px each side). These are dense worklists with 7–11
   columns, and 8px per column recovered across nine columns is 72px of content — enough that
   "DEPARTMENT" stopped being clipped by 2px without stealing width from the column beside it. */
.dtbl th, .dtbl td { padding-left: 10px; padding-right: 10px; }

/* The explanatory line under a form or an action. Used in 15 places across 5 screens and never defined
   anywhere, so it rendered as unstyled body text at full size and full width — the sentence explaining
   the form competed with the form for attention, and on /identity/duplicates it was the loudest thing
   under the button. Capped at 76ch because a hint that runs the full width of a 1600px card is read as
   a paragraph, not a footnote. */
.note { margin: 12px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-soft); max-width: 76ch; }
/* .note is one explanatory sentence; .prose is several paragraphs of it — the "how to use this" panel
   on the duplicate review, where the text IS the screen's content and not a caption under a control.
   Same 76ch measure and the same reason: a paragraph spanning a 1600px card is skipped, not read. */
.prose { max-width: 76ch; font-size: 13px; line-height: 1.6; color: var(--ink-soft); }
.prose p { margin: 0 0 10px; }
.prose p:last-child { margin-bottom: 0; }
.prose strong { color: var(--ink); }
/* A form's submit belongs away from the last field it submits, not welded to its bottom edge. */
.form-grid + .btn, .form-grid + button.btn { margin-top: 16px; }


/* A four-column form row, for screens whose fields are mostly short numbers — a triage set is eight
   readings, and eight half-width inputs is a very tall form for something a nurse fills in at speed.
   Field spans keep every row on the same 25% ticks: the alternative, which /emergency/triage shipped
   with, was four separate inline grids (2fr 1fr, then 4, then 1fr 1fr) whose column edges agreed with
   each other nowhere, so the form read as ragged even though each row on its own was even. */
.form-grid--4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.field--2 { grid-column: span 2; }
@media (max-width: 860px) { .form-grid--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } .field--2 { grid-column: span 2; } }

/* ── Page measure ────────────────────────────────────────────────────────────────────────────────
   Content fills the page. No card width cap, no centring.

   HISTORY, so this does not get "fixed" back: these screens each carried their own inline max-width —
   660, 760, 820, 880, 900, 940, 960 — with nothing distinguishing them but the day each was written.
   Two narrower schemes were then tried and rejected in turn. A 960px tier left 177px of dead space
   hard against the right edge at a 1440 viewport. Centring the 760px cards split that into 196px down
   each side, which read as a card floating in the page rather than as a deliberate measure.

   The textbook argument for capping a single-column form is real — a lone input stretched across the
   screen makes the eye travel from label to field — but it was overruled deliberately, twice, in
   favour of content matching the page width the way the tables do. If it ever bites, cap the INPUT
   inside .field rather than the card, so the card still lines up with the tables beside it. */

/* ── Flash: the one-line result of a form submission ─────────────────────────────────────────────
   "Saved", "Could not save", "Bed already occupied". Distinct from .alert, which is the dashboard's
   clinical alert CARD — 46px badge, large radius, hover-lift. That lift is an affordance meaning
   "clickable", which a status line is not, and .alert was the wrong component to reach for here.

   These were 417 hardcoded hex literals across 76 pages: #ecfdf5 on #065f46 for success, #fef2f2 on
   #991b1b or #b91c1c for failure. Every one was a LIGHT-theme value that never re-reads the theme, so
   in dark mode they rendered dark-on-dark. It survived because a flash only appears AFTER you submit
   something — nobody browsing in dark mode ever sees one until the moment it matters. */
.flash { padding: 10px 12px; border-radius: var(--r-sm); font-size: 13px; line-height: 1.45; border: 1px solid; }
.flash--ok    { background: var(--stable-bg);   color: var(--stable-tx);   border-color: color-mix(in srgb, var(--stable) 35%, var(--line)); }
.flash--error { background: var(--critical-bg); color: var(--critical-tx); border-color: color-mix(in srgb, var(--critical) 35%, var(--line)); }
.flash--warn  { background: var(--urgent-bg);   color: var(--urgent-tx);   border-color: color-mix(in srgb, var(--urgent) 35%, var(--line)); }

/* ----- Rules that were stranded in a page's own <style> block ------------------------------------ */
/* A <style> tag inside a .razor page is present only while that page is rendered — there is no CSS
   isolation in this project. Each of the rules below was written into one page and then used from
   others, where it silently did nothing. They are moved here because more than one screen needs them,
   which is the definition of shared. */

/* The plain centred shell for the auth side-flows: change-password and OTP. (The sign-in page proper
   uses its own two-panel .lgn, correctly page-scoped, because nothing else shares it.) This lived in
   ChangePassword.razor, so the OTP screen — the one a locked-out user actually lands on — had no
   background, no centring and no min-height at all. */
.login-bg {
  min-height: 100vh; display: grid; place-items: center; padding: 24px;
  background:
    radial-gradient(900px 480px at 10% -10%, color-mix(in srgb, var(--primary) 10%, transparent), transparent 60%),
    radial-gradient(820px 480px at 100% 110%, color-mix(in srgb, var(--teal) 8%, transparent), transparent 56%),
    var(--canvas);
}

/* The charge-line table on the two IPD billing screens. Defined in Bill.razor, used by CancelCharge
   too — where the row contents sat top-aligned against the buttons beside them. */
.cerno-lines td { vertical-align: middle; }
