/* =============================================================================
   VENHIMS — design system (self-contained, offline).

   Borrows the parts of IBM Carbon that are genuinely good engineering — the 8px
   spacing grid, the functional type scale, square 2px radii, and the g10/g100
   neutral ramps — and stops there. What a user actually looks at is ours: the
   icon set is original artwork on a 24 grid (core/View/Icons.php), and the
   module palette is derived from brand blue #1E6FB8 and navy #0A2540 rather than
   from Carbon's electric blues (config/modules.php). The result should read as
   VENHIMS, not as a Carbon demo.

   No CDN, no external fonts: everything needed to render is here, so the UI
   looks identical with zero connectivity (offline-first).
   Theme follows the OS by default and can be overridden with [data-theme].

   TYPE SCALE
   ----------
   Sizes are in rem against a 16px root, so the whole interface scales from one
   number. [data-textsize] on <html> moves that root: clinics running shared
   tablets at arm's length, or with older staff, switch to Large/Larger from the
   header and every screen follows — no separate "accessibility mode" to
   maintain, and no page that forgot to scale. 16px is also the floor that stops
   iOS auto-zooming form fields on focus.
   ========================================================================== */

:root {
  /* Brand */
  --navy:   #0A2540;
  --blue:   #1E6FB8;
  --accent: #2E86E0;
  --grey:   #5A6B73;

  /* Light theme (Carbon g10) */
  --bg:            #f4f4f4;
  --layer:         #ffffff;
  --layer-hover:   #e8e8e8;
  --layer-accent:  #eef4fb;
  --border:        #dcdcdc;
  --border-strong: #c6c6c6;
  --text:          #161616;
  --text-secondary:#525252;
  --text-muted:    #6f6f6f;
  --text-on-brand: #ffffff;

  --interactive:   var(--blue);
  --interactive-hover: #185a97;
  --focus:         var(--accent);

  /* Siblings of the module palette rather than Carbon's, so a success tick and
     a Pharmacy tile read as the same green. */
  --support-success:#1E7A42;
  --support-warning:#8A6100;
  --support-error:  #BE3227;
  --support-info:   var(--accent);

  --header-bg:     var(--navy);

  /* Spacing — Carbon 8px grid, in rem so it scales with the type size. */
  --sp-1: .25rem; --sp-2: .5rem;  --sp-3: .75rem; --sp-4: 1rem;
  --sp-5: 1.5rem; --sp-6: 2rem;   --sp-7: 3rem;   --sp-8: 4rem;

  /* Type scale */
  --t-caption: .75rem;    /* 12px */
  --t-label:   .8125rem;  /* 13px */
  --t-sm:      .875rem;   /* 14px */
  --t-body:    1rem;      /* 16px — body floor */
  --t-lg:      1.125rem;  /* 18px */
  --t-h4:      1.25rem;   /* 20px */
  --t-h3:      1.375rem;  /* 22px */
  --t-h2:      1.75rem;   /* 28px */
  --t-h1:      2.25rem;   /* 36px */
  --t-display: 3rem;      /* 48px — the clock */

  --radius: 2px;
  --radius-lg: 6px;
  --header-h: 3.5rem;
  --sidenav-w: 17.5rem;
  --tap: 2.75rem;         /* 44px minimum touch target (WCAG 2.5.5 / Apple HIG) */

  --font: 'IBM Plex Sans', 'Segoe UI', system-ui, -apple-system, Roboto, Helvetica, Arial, sans-serif;
  --mono: 'IBM Plex Mono', 'Cascadia Code', Consolas, monospace;
  --shadow: 0 2px 6px rgba(10, 37, 64, .08);
  --shadow-md: 0 4px 14px rgba(10, 37, 64, .12);
  --shadow-lg: 0 8px 24px rgba(10, 37, 64, .16);

  /* Launcher tile tint strength, re-declared per theme below. */
  --tile-tint: .10;
  --tile-tint-hover: .18;

  /* Icon rendering. --icon-weight is the stroke weight on the 24 grid; raise it
     to ~2.25 if a site asks for heavier glyphs, and the whole set follows.
     --icon-sec fades the secondary wash inside two-tone glyphs. */
  --icon-weight: 2;
  --icon-sec: .34;
}

@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) { color-scheme: dark;
  --bg:#161616; --layer:#262626; --layer-hover:#333333; --layer-accent:#1d2b3a;
  --border:#393939; --border-strong:#525252; --text:#f4f4f4; --text-secondary:#c6c6c6;
  --text-muted:#a8a8a8; --header-bg:#0A2540; --interactive:#4f9fe0; --interactive-hover:#78b6ea;
  --shadow:0 2px 6px rgba(0,0,0,.4); --shadow-md:0 4px 14px rgba(0,0,0,.45); --shadow-lg:0 8px 24px rgba(0,0,0,.5);
  --tile-tint: .18; --tile-tint-hover: .28;
  /* On charcoal the deep tone would disappear, so the dark theme uses the
     module's light tone and puts DARK ink on it. This is the pair the manifest
     calls color_dark; it used to be emitted and then never read, which left the
     dark theme wearing the light theme's colours. */
  --icon-sec: .42;
  /* The light-theme support tones are far too dark to read as text on charcoal
     — #198038 lands near 3:1 — so each is lifted to its AA equivalent. */
  --support-success:#42BE65; --support-warning:#D9A93A;
  --support-error:#F0898E;   --support-info: var(--accent);
} }
:root[data-theme="dark"] { color-scheme: dark;
  --bg:#161616; --layer:#262626; --layer-hover:#333333; --layer-accent:#1d2b3a;
  --border:#393939; --border-strong:#525252; --text:#f4f4f4; --text-secondary:#c6c6c6;
  --text-muted:#a8a8a8; --header-bg:#0A2540; --interactive:#4f9fe0; --interactive-hover:#78b6ea;
  --shadow:0 2px 6px rgba(0,0,0,.4); --shadow-md:0 4px 14px rgba(0,0,0,.45); --shadow-lg:0 8px 24px rgba(0,0,0,.5);
  --tile-tint: .18; --tile-tint-hover: .28;
  /* On charcoal the deep tone would disappear, so the dark theme uses the
     module's light tone and puts DARK ink on it. This is the pair the manifest
     calls color_dark; it used to be emitted and then never read, which left the
     dark theme wearing the light theme's colours. */
  --icon-sec: .42;
  /* The light-theme support tones are far too dark to read as text on charcoal
     — #198038 lands near 3:1 — so each is lifted to its AA equivalent. */
  --support-success:#42BE65; --support-warning:#D9A93A;
  --support-error:#F0898E;   --support-info: var(--accent);
}
:root[data-theme="light"] { color-scheme: light;
  --bg:#f4f4f4; --layer:#ffffff; --layer-hover:#e8e8e8; --layer-accent:#eef4fb;
  --border:#dcdcdc; --border-strong:#c6c6c6; --text:#161616; --text-secondary:#525252;
  --text-muted:#6f6f6f; --header-bg:#0A2540; --interactive:#1E6FB8;
  --tile-tint: .10; --tile-tint-hover: .18;
  --icon-sec: .34;
}

/* ---- Root sizing (the one knob the text-size control turns) -------------- */
html { font-size: 16px; }
html[data-textsize="large"]  { font-size: 18px; }
html[data-textsize="xlarge"] { font-size: 20px; }

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--text);
  font-size: var(--t-body); line-height: 1.55;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--interactive); text-decoration: none; }
a:hover { text-decoration: underline; }
h1,h2,h3,h4 { font-weight: 600; margin: 0 0 var(--sp-3); letter-spacing: -.01em; line-height: 1.25; }
h1 { font-size: var(--t-h1); } h2 { font-size: var(--t-h2); } h3 { font-size: var(--t-h3); }
h4 { font-size: var(--t-h4); }
/* ---- Icons ---------------------------------------------------------------
   The set is stroked rather than filled (see core/View/Icons.php), which makes
   weight a single knob instead of a redraw. `stroke-width` here beats the SVG's
   own presentation attribute, so --icon-weight retunes all 63 glyphs at once.

   Weight is set in *user units on the 24 grid*, so it scales with the icon: 2
   renders as 1.67px at the 20px nav size and 2.67px on a 32px tile chip. That
   lands deliberately heavier than the thin engineered look it replaced, which
   is what keeps the glyphs legible on a cheap tablet in daylight. */
.vh-icon {
  display: inline-block; vertical-align: middle; flex: none;
  stroke-width: var(--icon-weight);
}
/* Secondary washes: the only filled shapes in the set. They are decoration —
   an icon still reads correctly if this rule never arrives — so they are
   deliberately faded rather than competing with the outline on top of them. */
.vh-icon .s { fill: currentColor; stroke: none; opacity: var(--icon-sec); }
.muted { color: var(--text-muted); }
.text-secondary { color: var(--text-secondary); }
:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; border-radius: 2px; }

/* ---- App shell ----------------------------------------------------------- */
.vh-header {
  position: fixed; top: 0; left: 0; right: 0; height: var(--header-h); z-index: 40;
  background: var(--header-bg); color: #fff;
  display: flex; align-items: center; gap: var(--sp-3); padding: 0 var(--sp-4);
  border-bottom: 1px solid rgba(255,255,255,.08);
}
.vh-header .brand { display:flex; align-items:center; gap: var(--sp-3); font-weight:600; letter-spacing:.02em; font-size: var(--t-body); }
.vh-header .brand:hover { text-decoration: none; }
.vh-header .brand b { color:#fff; } .vh-header .brand .accent { color: var(--accent); }
/* Declared here rather than inline on the element: an inline style would beat
   the responsive rule that hides the wordmark on narrow screens. */
.vh-header .brand__text { display:flex; flex-direction:column; line-height:1.15; }
.vh-header .brand small { color: rgba(255,255,255,.6); font-weight:400; font-size: var(--t-caption); letter-spacing:.08em; text-transform:uppercase; }
.vh-header .spacer { flex: 1; }
.vh-header .hbtn {
  background:none; border:0; color:#fff; cursor:pointer; border-radius: var(--radius);
  display:flex; align-items:center; justify-content:center; gap:6px;
  min-width: var(--tap); min-height: var(--tap); padding: 0 var(--sp-2);
}
.vh-header .hbtn:hover { background: rgba(255,255,255,.14); text-decoration:none; }
.vh-header .hbtn[aria-pressed="true"] { background: rgba(255,255,255,.2); }

/* The nav toggle only does anything while the sidenav is off-canvas, which is
   below 768px. On desktop the sidenav is always visible, so the button was a
   control that did nothing — hidden rather than left to be clicked. */
#navToggle { display: none; }

/* Text-size control — three steps, always visible, never hidden in a menu. */
.vh-textsize { display:flex; align-items:center; background: rgba(255,255,255,.1); border-radius: var(--radius); padding: 2px; gap: 2px; }
.vh-textsize button {
  background:none; border:0; color:#fff; cursor:pointer; font-family:inherit; font-weight:600;
  border-radius: var(--radius); min-width: 2rem; min-height: 2rem; line-height:1; padding: 0 .375rem;
}
.vh-textsize button:hover { background: rgba(255,255,255,.16); }
.vh-textsize button[aria-pressed="true"] { background: var(--accent); }
.vh-textsize .s1 { font-size: .75rem; } .vh-textsize .s2 { font-size: .9375rem; } .vh-textsize .s3 { font-size: 1.125rem; }

.vh-sidenav {
  position: fixed; top: var(--header-h); bottom: 0; left: 0; width: var(--sidenav-w); z-index: 30;
  background: var(--layer); border-right: 1px solid var(--border); overflow-y: auto; padding: var(--sp-3) 0;
  overscroll-behavior: contain;
}
.vh-navgroup { padding: var(--sp-2) 0; }
.vh-navgroup > .label { padding: var(--sp-2) var(--sp-4); font-size: var(--t-caption); text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); font-weight: 600; }
.vh-navitem {
  display:flex; align-items:center; gap: var(--sp-3); padding: var(--sp-3) var(--sp-4);
  color: var(--text-secondary); border-left: 4px solid transparent; min-height: var(--tap);
  font-size: var(--t-sm);
}
.vh-navitem:hover { background: var(--layer-hover); text-decoration:none; color: var(--text); }
.vh-navitem.active { background: var(--layer-accent); border-left-color: var(--interactive); color: var(--interactive); font-weight: 600; }
.vh-navitem .vh-icon { color: var(--nav-accent, currentColor); }
.vh-navitem .dot { margin-left:auto; width:7px;height:7px;border-radius:50%; background: var(--support-success); flex:none; }

.vh-main { margin-top: var(--header-h); margin-left: var(--sidenav-w); padding: var(--sp-6); min-height: calc(100vh - var(--header-h)); }
.vh-page-head { display:flex; align-items:flex-end; justify-content:space-between; gap: var(--sp-4); margin-bottom: var(--sp-5); flex-wrap: wrap; }
.vh-page-head .sub { color: var(--text-muted); font-size: var(--t-sm); }

/* =============================================================================
   HOME / APP LAUNCHER
   ========================================================================== */

/* ---- Greeting hero ------------------------------------------------------- */
.vh-hero {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-5);
  flex-wrap: wrap;
  /* Flat brand navy, no gradient — the brand is one colour. */
  background: var(--navy);
  color: #fff; border-radius: var(--radius-lg);
  /* Roomier than a header band. With the metric strip gone this is the first
     thing on the page, and the space around it is doing the work the removed
     tiles used to do badly. */
  padding: var(--sp-7) var(--sp-6);
  margin-bottom: var(--sp-5);
}
.vh-hero__greet { display:flex; align-items:center; gap: var(--sp-3); color: #9dc9f5; font-size: var(--t-lg); font-weight: 500; }
.vh-hero h1 { color:#fff; font-size: var(--t-h1); margin: var(--sp-2) 0 var(--sp-2); }
.vh-hero__meta { color: rgba(255,255,255,.72); font-size: var(--t-body); display:flex; align-items:center; gap: var(--sp-2); flex-wrap: wrap; }
.vh-hero__meta .sep { opacity:.45; }

.vh-clock { text-align: right; flex: none; }
.vh-clock__time {
  font-size: var(--t-display); font-weight: 300; line-height: 1; color:#fff;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.vh-clock__date { color: rgba(255,255,255,.72); font-size: var(--t-body); margin-top: var(--sp-2); }
.vh-clock__zone { color: rgba(255,255,255,.5); font-size: var(--t-caption); text-transform: uppercase; letter-spacing:.08em; margin-top: 2px; }

/* ---- Stat strip (secondary to the apps, deliberately) -------------------- */
.vh-stats { display:grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr)); gap: var(--sp-4); margin-bottom: var(--sp-6); }
.vh-stat {
  background: var(--layer); border:1px solid var(--border); border-radius: var(--radius-lg);
  padding: var(--sp-4) var(--sp-5); display:flex; align-items:center; gap: var(--sp-4);
}
/* A tinted plate with the glyph in the same hue. These used to be inline Carbon
   hexes repeated across seven templates, which meant the palette could not be
   retuned and the dark theme could not lift the tone. Now they are semantic
   classes: pick the meaning, not the colour. */
.vh-stat__icon {
  width: 3rem; height: 3rem; border-radius: var(--radius-lg); flex:none;
  display:flex; align-items:center; justify-content:center;
  background: var(--tone-bg, var(--layer-hover)); color: var(--tone-fg, var(--text-secondary));
}
.vh-tone--brand  { --tone-bg: rgba(30,111,184,.14); --tone-fg: #1E6FB8; }
.vh-tone--teal   { --tone-bg: rgba(14,124,134,.14); --tone-fg: #0E7C86; }
.vh-tone--green  { --tone-bg: rgba(30,122,66,.14);  --tone-fg: #1E7A42; }
.vh-tone--amber  { --tone-bg: rgba(138,97,0,.16);   --tone-fg: #8A6100; }
.vh-tone--clay   { --tone-bg: rgba(168,80,30,.14);  --tone-fg: #A8501E; }
.vh-tone--violet { --tone-bg: rgba(107,63,168,.14); --tone-fg: #6B3FA8; }
.vh-tone--danger { --tone-bg: rgba(190,50,39,.14);  --tone-fg: #BE3227; }
.vh-tone--muted  { --tone-bg: rgba(76,85,96,.16);   --tone-fg: #4C5560; }

/* On charcoal the deep tones vanish, so each lifts to its light counterpart and
   the plate carries a little more alpha to stay visible. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .vh-tone--brand  { --tone-bg: rgba(147,200,242,.18); --tone-fg: #93C8F2; }
  :root:not([data-theme="light"]) .vh-tone--teal   { --tone-bg: rgba(82,198,212,.18);  --tone-fg: #52C6D4; }
  :root:not([data-theme="light"]) .vh-tone--green  { --tone-bg: rgba(97,200,132,.18);  --tone-fg: #61C884; }
  :root:not([data-theme="light"]) .vh-tone--amber  { --tone-bg: rgba(220,180,90,.18);  --tone-fg: #DCB45A; }
  :root:not([data-theme="light"]) .vh-tone--clay   { --tone-bg: rgba(236,164,112,.18); --tone-fg: #ECA470; }
  :root:not([data-theme="light"]) .vh-tone--violet { --tone-bg: rgba(193,163,238,.18); --tone-fg: #C1A3EE; }
  :root:not([data-theme="light"]) .vh-tone--danger { --tone-bg: rgba(247,141,131,.18); --tone-fg: #F78D83; }
  :root:not([data-theme="light"]) .vh-tone--muted  { --tone-bg: rgba(175,182,190,.16); --tone-fg: #AFB6BE; }
}
:root[data-theme="dark"] .vh-tone--brand  { --tone-bg: rgba(147,200,242,.18); --tone-fg: #93C8F2; }
:root[data-theme="dark"] .vh-tone--teal   { --tone-bg: rgba(82,198,212,.18);  --tone-fg: #52C6D4; }
:root[data-theme="dark"] .vh-tone--green  { --tone-bg: rgba(97,200,132,.18);  --tone-fg: #61C884; }
:root[data-theme="dark"] .vh-tone--amber  { --tone-bg: rgba(220,180,90,.18);  --tone-fg: #DCB45A; }
:root[data-theme="dark"] .vh-tone--clay   { --tone-bg: rgba(236,164,112,.18); --tone-fg: #ECA470; }
:root[data-theme="dark"] .vh-tone--violet { --tone-bg: rgba(193,163,238,.18); --tone-fg: #C1A3EE; }
:root[data-theme="dark"] .vh-tone--danger { --tone-bg: rgba(247,141,131,.18); --tone-fg: #F78D83; }
:root[data-theme="dark"] .vh-tone--muted  { --tone-bg: rgba(175,182,190,.16); --tone-fg: #AFB6BE; }
.vh-stat__v { font-size: var(--t-h2); font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }
.vh-stat__k { font-size: var(--t-sm); color: var(--text-muted); margin-top: 2px; }

/* ---- Live record search --------------------------------------------------
   Results appear beneath the box as the officer types. Deliberately laid out as
   a grid rather than a dropdown overlay: the desk reads four columns at a glance
   (name, who they are, their numbers, their cover) and an overlay that covers
   the rest of the screen makes that comparison harder, not easier. */
.vh-live { position: relative; }
.vh-live__results {
  margin-top: var(--sp-3); border: 1px solid var(--border);
  border-radius: var(--radius-lg); overflow: hidden; background: var(--layer);
}
.vh-live__row {
  display: grid; grid-template-columns: minmax(9rem, 1.4fr) 1fr auto auto;
  gap: var(--sp-3); align-items: center;
  padding: var(--sp-3) var(--sp-4); color: var(--text);
  border-bottom: 1px solid var(--border); text-decoration: none;
  /* 44px minimum row height: this list is tapped on a tablet at a busy desk. */
  min-height: var(--tap);
}
.vh-live__row:last-child { border-bottom: 0; }
.vh-live__row:hover,
.vh-live__row:focus-visible { background: var(--layer-accent); text-decoration: none; }
.vh-live__name { font-weight: 600; }
.vh-live__meta { color: var(--text-secondary); font-size: var(--t-sm); }
.vh-live__id   { font-size: var(--t-label); color: var(--text-muted); text-align: right; }
.vh-live__tag  { text-align: right; }
.vh-live__empty { padding: var(--sp-4); color: var(--text-secondary); }
/* A record that exists but cannot be opened. Rendered as a div, not a link, so
   it neither highlights on hover nor takes keyboard focus. */
.vh-live__row.is-closed { color: var(--text-muted); cursor: default; }
.vh-live__row.is-closed:hover { background: none; }
.vh-live mark { background: none; color: var(--interactive); font-weight: 700; padding: 0; }
.vh-live__spin { font-size: var(--t-caption); color: var(--text-muted); white-space: nowrap; }

@media (max-width: 40rem) {
  /* Four columns do not fit a phone; stack and drop the right-aligned rails. */
  .vh-live__row { grid-template-columns: 1fr auto; }
  .vh-live__meta { grid-column: 1 / -1; }
  .vh-live__id { text-align: left; grid-column: 1 / -1; }
}

/* ---- The way in ----------------------------------------------------------
   Promoted from a small filter tucked beside a heading to the one prominent
   control on the home screen. With the metric strip removed, nothing else on
   the page competes for attention — which is the point. On a platform the front
   door is a search field, not a report. */
.vh-find {
  display: flex; align-items: center; gap: var(--sp-3);
  background: var(--layer); border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  padding: var(--sp-4) var(--sp-5);
  margin-bottom: var(--sp-6);
  transition: border-color .18s ease, box-shadow .18s ease;
}
/* A literal rgba rather than color-mix(): that function needs Chrome 111+, and
   this product runs on whatever tablet the facility already owns. */
.vh-find:focus-within {
  border-color: var(--interactive);
  box-shadow: 0 0 0 3px rgba(30, 111, 184, .18);
}
.vh-find .vh-icon { color: var(--text-muted); flex: none; }
.vh-find input {
  flex: 1; min-width: 0; border: 0; background: none; padding: 0;
  font-family: inherit; font-size: var(--t-lg); color: var(--text);
  /* The container owns the focus ring; a second one inside it reads as a bug. */
  outline: none;
}
.vh-find input::placeholder { color: var(--text-muted); }
.vh-find input::-webkit-search-cancel-button { cursor: pointer; }
.vh-find kbd {
  flex: none; font-family: var(--mono); font-size: var(--t-caption);
  color: var(--text-muted); background: var(--layer-hover);
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 3px 8px; line-height: 1;
}
/* A keyboard shortcut hint is useless on a device with no keyboard. */
@media (pointer: coarse) { .vh-find kbd { display: none; } }

/* ---- Module hue resolution ----------------------------------------------
   Each module carries TWO hues as inline custom properties: --app-accent for
   light surfaces and --app-accent-dark for dark ones. The rules below pick one
   of them, plus the ink colour that will sit on top of it.

   These MUST be declared on the elements that carry those inline properties,
   not on :root. A custom property whose value contains var() is resolved
   against the element it is DECLARED on — put `--app-hue: var(--app-accent)`
   on :root and it resolves against :root, where --app-accent does not exist,
   so every tile inherits an empty value and the chips render blank.

   The fallback in the dark rule is load-bearing too: a settings row supplies
   only the light hue, and a var() that resolves to nothing would leave the chip
   with no background at all. */
.vh-app,
.vh-settingrow__icon {
  --app-hue: var(--app-accent);
  --app-ink: #ffffff;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .vh-app,
  :root:not([data-theme="light"]) .vh-settingrow__icon {
    --app-hue: var(--app-accent-dark, var(--app-accent));
    --app-ink: #161616;
  }
}
:root[data-theme="dark"] .vh-app,
:root[data-theme="dark"] .vh-settingrow__icon {
  --app-hue: var(--app-accent-dark, var(--app-accent));
  --app-ink: #161616;
}
:root[data-theme="light"] .vh-app,
:root[data-theme="light"] .vh-settingrow__icon {
  --app-hue: var(--app-accent);
  --app-ink: #ffffff;
}

/* ---- App grid ------------------------------------------------------------ */
.vh-appgroup { margin-bottom: var(--sp-6); }
.vh-appgroup__label {
  font-size: var(--t-label); text-transform: uppercase; letter-spacing: .1em;
  color: var(--text-muted); font-weight: 600; margin-bottom: var(--sp-3);
  display:flex; align-items:center; gap: var(--sp-3);
}
.vh-appgroup__label::after { content:''; flex:1; height:1px; background: var(--border); }

.vh-apps { display:grid; grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr)); gap: var(--sp-4); }

/*
 * A tile is a surface, not a colour field.
 *
 * Every tile used to carry a wash of its own hue across the whole card. With
 * twenty-six apps on screen that is twenty-six competing colour fields, and the
 * grid read as busy however carefully each individual hue was chosen. The
 * colour now lives only in the chip, which is enough to identify an app at a
 * glance and leaves the page calm. The wash returns on hover — at that point
 * there is exactly one of them, so it reads as feedback rather than decoration.
 */
.vh-app {
  position: relative; display:flex; flex-direction: column; gap: var(--sp-3);
  background: var(--layer); border:1px solid var(--border); border-radius: var(--radius-lg);
  /* Shorter than it was: the "Works offline" line that used to fill the bottom
     of every tile is gone, and keeping the old height just left a hole. */
  padding: var(--sp-5); color: var(--text); min-height: 8.25rem;
  transition: transform .18s cubic-bezier(.2,.7,.3,1), box-shadow .18s ease, border-color .18s ease;
}
.vh-app:hover, .vh-app:focus-visible {
  text-decoration: none; color: var(--text);
  border-color: var(--app-hue);
  box-shadow: var(--shadow-md);
  transform: translateY(-3px);
}
.vh-app:active { transform: translateY(-1px); }
.vh-app::before {
  content:''; position:absolute; inset:0; border-radius: inherit; pointer-events:none;
  background: var(--app-hue); opacity: 0; transition: opacity .18s ease;
}
.vh-app:hover::before, .vh-app:focus-visible::before { opacity: var(--tile-tint); }
.vh-app > * { position: relative; }
/* Pushes the arrow to the bottom edge whether or not the app has a description,
   so a grid of mixed-length tiles still lines up. */
.vh-app__text { display: block; }

/* The one saturated surface on the tile. The glyph on it is --app-ink rather
   than a hard-coded white, because the dark theme flips to a light chip and
   white-on-light would drop the glyph to roughly 2:1. */
.vh-app__chip {
  width: 3.5rem; height: 3.5rem; border-radius: var(--radius-lg); flex:none;
  display:flex; align-items:center; justify-content:center;
  background: var(--app-hue); color: var(--app-ink);
  box-shadow: 0 2px 8px rgba(0,0,0,.16);
}
/* Slightly heavier strokes on the chip: the glyph is reversed out of a
   saturated field there, and reversed marks always read lighter than they
   measure. */
.vh-app__chip .vh-icon { stroke-width: calc(var(--icon-weight) + .15); }
/* Both are <span>s inside the tile's <a>, so they must be made block-level
   explicitly — left inline they reflow into a single run of text. */
.vh-app__name { display: block; font-size: var(--t-lg); font-weight: 600; line-height: 1.3; }
.vh-app__desc { display: block; font-size: var(--t-sm); color: var(--text-secondary); line-height: 1.45; margin-top: .25rem; }
/* Sits on the bottom edge of the tile, pushed there rather than following the
   text, so tiles with and without a description still align. */
.vh-app__go {
  margin-top: auto; margin-left: auto; color: var(--app-hue); display: flex;
  opacity: 0; transform: translateX(-4px); transition: opacity .18s, transform .18s;
}
.vh-app:hover .vh-app__go, .vh-app:focus-visible .vh-app__go { opacity:1; transform:none; }

/* ---- Home footer ---------------------------------------------------------
   The offline promise, stated once. It used to be printed on every tile, where
   being true of nearly every app made it carry no information at all. */
.vh-homefoot {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-7) 0 0; padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
  color: var(--text-muted); font-size: var(--t-sm); line-height: 1.5;
}
.vh-homefoot .vh-icon { flex: none; }

/* Count badge: only rendered when there is actually work waiting. */
.vh-app__badge {
  position:absolute; top: var(--sp-4); right: var(--sp-4);
  min-width: 1.75rem; height: 1.75rem; padding: 0 .5rem; border-radius: 1rem;
  background: var(--app-hue); color: var(--app-ink); font-size: var(--t-label); font-weight:700;
  display:flex; align-items:center; justify-content:center; font-variant-numeric: tabular-nums;
  box-shadow: 0 2px 6px rgba(0,0,0,.2);
}

.vh-apps__empty { grid-column: 1/-1; }

/* =============================================================================
   SETTINGS
   ========================================================================== */

.vh-section { margin-bottom: var(--sp-5); }
.vh-section__head { display:flex; align-items:baseline; justify-content:space-between; gap: var(--sp-4); flex-wrap:wrap; margin-bottom: var(--sp-4); }
.vh-section__head h2 { margin:0; font-size: var(--t-h3); display:flex; align-items:center; gap: var(--sp-2); }
.vh-tile h3 { display:flex; align-items:center; gap: var(--sp-2); }

/* ---- Health traffic lights ---------------------------------------------- */
.vh-checks { display:grid; grid-template-columns: repeat(auto-fill, minmax(16rem,1fr)); gap: var(--sp-3); }
.vh-check {
  display:flex; gap: var(--sp-3); align-items:flex-start;
  background: var(--layer); border:1px solid var(--border);
  border-left: 4px solid var(--border-strong);
  border-radius: var(--radius-lg); padding: var(--sp-4);
}
.vh-check__icon { flex:none; display:flex; }
.vh-check__label { font-weight:600; font-size: var(--t-sm); }
.vh-check__detail { color: var(--text-secondary); font-size: var(--t-label); margin-top:2px; }
/* State is carried by an icon and the wording as well as the colour, so the
   strip is still readable in greyscale or with colour-vision deficiency. */
.vh-check.is-ok   { border-left-color: var(--support-success); }
.vh-check.is-ok   .vh-check__icon { color: var(--support-success); }
.vh-check.is-warn { border-left-color: var(--support-warning); }
.vh-check.is-warn .vh-check__icon { color: var(--support-warning); }
.vh-check.is-fail { border-left-color: var(--support-error); }
.vh-check.is-fail .vh-check__icon { color: var(--support-error); }

/* ---- Key/value blocks ---------------------------------------------------- */
.vh-kv { display:grid; grid-template-columns: auto 1fr; gap: .4rem var(--sp-4); margin:0; font-size: var(--t-sm); }
.vh-kv dt { color: var(--text-muted); white-space: nowrap; }
.vh-kv dd { margin:0; text-align:right; }

/* ---- Meter --------------------------------------------------------------- */
.vh-meter { height:.5rem; background: var(--layer-hover); border-radius:.25rem; overflow:hidden; }
.vh-meter span { display:block; height:100%; border-radius:.25rem; transition: width .3s ease; }

/* ---- Extension chips ----------------------------------------------------- */
.vh-chips { display:flex; flex-wrap:wrap; gap:5px; }
.vh-chip {
  font-family: var(--mono); font-size: var(--t-caption); padding: 2px 8px;
  border-radius: 1rem; border:1px solid var(--border-strong); color: var(--text-muted);
}
.vh-chip.is-on { background: rgba(25,128,56,.12); border-color: transparent; color: var(--support-success); }
.vh-chip.is-off { opacity:.65; text-decoration: line-through; }
.vh-chip.is-missing { background: rgba(218,30,40,.12); border-color: transparent; color: var(--support-error); }

/* ---- Setting rows -------------------------------------------------------- */
.vh-settinglist { background: var(--layer); border:1px solid var(--border); border-radius: var(--radius-lg); overflow:hidden; }
.vh-settingrow {
  display:flex; align-items:center; gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5); border-bottom:1px solid var(--border);
}
.vh-settingrow:last-child { border-bottom:0; }
.vh-settingrow.is-compact { padding: var(--sp-3) var(--sp-4); }
.vh-settingrow.is-off .vh-settingrow__name,
.vh-settingrow.is-off .vh-settingrow__icon { opacity:.55; }
.vh-settingrow__icon {
  width:2.5rem; height:2.5rem; border-radius: var(--radius-lg); flex:none;
  display:flex; align-items:center; justify-content:center;
  background: var(--app-hue); color: var(--app-ink);
}
.vh-settingrow__icon .vh-icon { stroke-width: calc(var(--icon-weight) + .15); }
.vh-settingrow__text { flex:1; min-width:0; }
.vh-settingrow__name { font-weight:600; font-size: var(--t-body); display:flex; align-items:center; gap: var(--sp-2); flex-wrap:wrap; }
.vh-settingrow__desc { color: var(--text-secondary); font-size: var(--t-label); margin-top:2px; }
.vh-settingrow__actions { display:flex; align-items:center; gap: var(--sp-3); flex:none; }

/* ---- Switch --------------------------------------------------------------
   A real <button role="switch"> inside a POST form, so it works with no
   JavaScript and announces its state to a screen reader. The 44px tap target
   is the padded button; the visible track is smaller.                        */
.vh-switch {
  position:relative; display:inline-flex; align-items:center;
  width:3.25rem; height:1.75rem; padding:0; flex:none;
  background: var(--border-strong); border:0; border-radius:1rem; cursor:pointer;
  transition: background .18s ease;
}
.vh-switch .knob {
  position:absolute; top:.1875rem; left:.1875rem;
  width:1.375rem; height:1.375rem; border-radius:50%;
  background:#fff; box-shadow:0 1px 3px rgba(0,0,0,.3);
  transition: transform .18s cubic-bezier(.2,.7,.3,1);
}
.vh-switch.is-on { background: var(--support-success); }
.vh-switch.is-on .knob { transform: translateX(1.5rem); }
.vh-switch.is-static { cursor:default; opacity:.75; }
.vh-switch:focus-visible { outline:3px solid var(--focus); outline-offset:2px; }

@media (max-width: 768px) {
  .vh-settingrow { flex-wrap:wrap; }
  .vh-settingrow__text { flex-basis:100%; order:2; }
  .vh-settingrow__actions { order:3; margin-left:auto; }
  .vh-kv { grid-template-columns: 1fr auto; }
}

/* =============================================================================
   TRIAGE / NURSING STATION
   ========================================================================== */

/* SATS colours. Each is paired with its label everywhere it appears — colour
   alone must never be the only carrier of clinical priority (WCAG 1.4.1), and
   in a triage context that is a patient-safety rule, not just an a11y one.

   DELIBERATELY EXCLUDED from the brand palette retune. These five have to match
   the printed SATS/TEWS charts on the wall beside the nurse; a nurse comparing
   the screen to the chart must see the same red. Do not "harmonise" them. */
:root {
  --sats-red:    #da1e28;
  --sats-orange: #ba4e00;
  --sats-yellow: #b28600;
  --sats-green:  #198038;
  --sats-blue:   #4d5358;
}

.vh-triage-board { display:grid; grid-template-columns: repeat(auto-fit, minmax(10rem,1fr)); gap: var(--sp-3); margin-bottom: var(--sp-4); }
.vh-triage-card {
  display:flex; flex-direction:column; gap:2px; padding: var(--sp-4);
  border-radius: var(--radius-lg); border:1px solid var(--border);
  background: var(--layer); color: var(--text); border-left-width:5px;
}
.vh-triage-card:hover { text-decoration:none; box-shadow: var(--shadow-md); }
.vh-triage-card.is-active { box-shadow: inset 0 0 0 2px var(--interactive); }
.vh-triage-card__n { font-size: var(--t-h1); font-weight:600; line-height:1; font-variant-numeric: tabular-nums; }
.vh-triage-card__l { font-weight:600; font-size: var(--t-sm); }
.vh-triage-card__t { font-size: var(--t-caption); color: var(--text-muted); }
.vh-triage-card.is-red    { border-left-color: var(--sats-red); }
.vh-triage-card.is-red    .vh-triage-card__n { color: var(--sats-red); }
.vh-triage-card.is-orange { border-left-color: var(--sats-orange); }
.vh-triage-card.is-orange .vh-triage-card__n { color: var(--sats-orange); }
.vh-triage-card.is-yellow { border-left-color: var(--sats-yellow); }
.vh-triage-card.is-yellow .vh-triage-card__n { color: var(--sats-yellow); }
.vh-triage-card.is-green  { border-left-color: var(--sats-green); }
.vh-triage-card.is-green  .vh-triage-card__n { color: var(--sats-green); }
.vh-triage-card.is-none   { border-left-color: var(--border-strong); }

/* Priority pill — colour + word together. */
.vh-triage-pill {
  display:inline-flex; align-items:center; gap:6px; font-weight:700;
  font-size: var(--t-caption); letter-spacing:.04em; text-transform:uppercase;
  padding: .2rem .6rem; border-radius: 1rem; color:#fff; white-space:nowrap;
}
.vh-triage-pill.is-red    { background: var(--sats-red); }
.vh-triage-pill.is-orange { background: var(--sats-orange); }
.vh-triage-pill.is-yellow { background: var(--sats-yellow); }
.vh-triage-pill.is-green  { background: var(--sats-green); }
.vh-triage-pill.is-blue   { background: var(--sats-blue); }

/* ---- Shortcut tabs ------------------------------------------------------- */
.vh-tabs {
  display:flex; gap:2px; flex-wrap:wrap; margin-bottom: var(--sp-4);
  border-bottom:1px solid var(--border);
}
.vh-tab {
  display:inline-flex; align-items:center; gap:6px; cursor:pointer;
  background:none; border:0; border-bottom:3px solid transparent;
  font-family:inherit; font-size: var(--t-sm); font-weight:500; color: var(--text-secondary);
  padding: var(--sp-3) var(--sp-4); min-height: var(--tap); white-space:nowrap;
}
.vh-tab:hover { color: var(--text); background: var(--layer-hover); text-decoration: none; }
/* Tabs are <button>s where they switch panels in place (the nursing station)
   and <a>s where they are real destinations (Records). Both need to look the
   same, so the link never carries an underline. */
a.vh-tab, a.vh-tab:hover { text-decoration: none; }
.vh-tab.is-active { color: var(--interactive); border-bottom-color: var(--interactive); font-weight:600; }
.vh-tab:focus-visible { outline:3px solid var(--focus); outline-offset:-3px; }

.vh-panel { display:none; }
.vh-panel.is-active { display:block; }

/* ---- Stepper -------------------------------------------------------------
   Used where a screen has a real order to it — the nursing assessment. Steps
   stay clickable in any order, because a nurse who only needs to add one
   procedure should not have to walk through vitals to reach it; the numbering
   carries the intended path without enforcing it. The connecting rule is
   decorative and is hidden from assistive tech by being a ::before on the row,
   not a sibling element. */
.vh-steps {
  display:flex; gap:2px; flex-wrap:wrap; margin-bottom: var(--sp-4);
  border-bottom:1px solid var(--border); position:relative;
}
.vh-step {
  display:inline-flex; align-items:center; gap: var(--sp-3); cursor:pointer;
  background:none; border:0; border-bottom:3px solid transparent;
  font-family:inherit; font-size: var(--t-sm); font-weight:500; color: var(--text-secondary);
  padding: var(--sp-3) var(--sp-4); min-height: var(--tap); white-space:nowrap;
}
.vh-step__n {
  width:1.75rem; height:1.75rem; border-radius:50%; flex:none;
  display:inline-flex; align-items:center; justify-content:center;
  background: var(--layer-hover); color: var(--text-secondary);
  font-size: var(--t-label); font-weight:700; font-variant-numeric: tabular-nums;
}
.vh-step__l { display:inline-flex; align-items:center; gap:6px; }
.vh-step:hover { color: var(--text); background: var(--layer-hover); }
.vh-step.is-active { color: var(--interactive); border-bottom-color: var(--interactive); font-weight:600; }
.vh-step.is-active .vh-step__n { background: var(--interactive); color:#fff; }
.vh-step:focus-visible { outline:3px solid var(--focus); outline-offset:-3px; }

@media (max-width: 48rem) {
  /* On a phone the labels wrap badly; the number plus icon still identifies
     each step, and the active step keeps its words. */
  .vh-step { padding: var(--sp-3); gap: var(--sp-2); }
  .vh-step:not(.is-active) .vh-step__l { font-size:0; }
  .vh-step:not(.is-active) .vh-step__l .vh-icon { font-size: initial; }
}

/* ---- Live TEWS readout --------------------------------------------------- */
/* ---- Disposition cards ---------------------------------------------------
   A radio that reads as a choice rather than a dot. Used where picking one of
   a handful of outcomes is the decision on the screen — where the patient goes
   at the end of a consultation. The native input is kept (and only visually
   hidden) so keyboard and screen-reader behaviour stay exactly as they were. */
.vh-radio-card {
  display: flex; align-items: center; gap: var(--sp-3); cursor: pointer;
  border: 1px solid var(--border); border-radius: var(--radius-lg);
  padding: var(--sp-4); background: var(--layer);
  min-height: var(--tap);
  transition: border-color .15s ease, background .15s ease;
}
.vh-radio-card:hover { border-color: var(--border-strong); background: var(--layer-hover); }
.vh-radio-card input { position: absolute; opacity: 0; width: 0; height: 0; }
.vh-radio-card span { display: flex; align-items: center; gap: var(--sp-3); font-weight: 500; }
.vh-radio-card:has(input:checked) {
  border-color: var(--interactive);
  background: var(--layer-accent);
  box-shadow: inset 0 0 0 1px var(--interactive);
}
.vh-radio-card:has(input:checked) span { color: var(--interactive); font-weight: 600; }
/* :has() is recent; without it the card still works, it just does not highlight.
   This keeps the selected state visible on older browsers via the focus ring. */
.vh-radio-card:focus-within { outline: 3px solid var(--focus); outline-offset: 2px; }

/* A wait long enough to be a problem rather than a number. */
.is-late { color: var(--support-error); font-weight: 700; }

.vh-tews-score { text-align:center; padding: var(--sp-4) 0 var(--sp-3); }
.vh-tews-score__n { display:block; font-size: 3.5rem; font-weight:300; line-height:1; font-variant-numeric: tabular-nums; }
.vh-tews-score__l { display:block; font-size: var(--t-caption); color: var(--text-muted); text-transform:uppercase; letter-spacing:.06em; margin-top:4px; }

@media (max-width: 768px) {
  .vh-tabs { overflow-x:auto; flex-wrap:nowrap; }
}

/* =============================================================================
   APPOINTMENTS
   ========================================================================== */

/* ---- Week grid ----------------------------------------------------------- */
.vh-week { display:grid; grid-template-columns: repeat(7, 1fr); gap: var(--sp-2); }
.vh-week__day {
  background: var(--layer); border:1px solid var(--border); border-radius: var(--radius-lg);
  min-height: 12rem; display:flex; flex-direction:column; overflow:hidden;
}
.vh-week__day.is-today { border-color: var(--interactive); box-shadow: inset 0 3px 0 var(--interactive); }
.vh-week__head {
  display:flex; align-items:baseline; justify-content:space-between; gap: var(--sp-2);
  padding: var(--sp-3); border-bottom:1px solid var(--border);
}
.vh-week__head a { display:flex; align-items:baseline; gap:6px; color: var(--text); }
.vh-week__head a:hover { text-decoration:none; color: var(--interactive); }
.vh-week__head span { font-size: var(--t-caption); text-transform:uppercase; letter-spacing:.06em; color: var(--text-muted); }
.vh-week__head strong { font-size: var(--t-h4); }
.vh-week__body { padding: var(--sp-2); display:flex; flex-direction:column; gap:4px; overflow-y:auto; max-height: 22rem; }
.vh-week__appt {
  display:flex; align-items:center; gap:6px; padding: 5px 7px; border-radius: var(--radius);
  background: var(--layer-accent); color: var(--text); font-size: var(--t-label); line-height:1.2;
}
.vh-week__appt:hover { text-decoration:none; background: var(--layer-hover); }
.vh-week__appt .t { font-family: var(--mono); font-size: var(--t-caption); color: var(--text-muted); flex:none; }
.vh-week__appt .n { flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.vh-week__empty { color: var(--text-muted); text-align:center; padding: var(--sp-3) 0; }

/* Status dot — colour plus the row's own status tag elsewhere, never colour alone. */
.vh-dot { width:7px; height:7px; border-radius:50%; flex:none; background: var(--support-warning); }
.vh-dot.is-arrived, .vh-dot.is-confirmed { background: var(--support-success); }
.vh-dot.is-completed { background: var(--text-muted); }
.vh-dot.is-no_show { background: var(--support-error); }
.vh-dot.is-cancelled, .vh-dot.is-rescheduled { background: var(--border-strong); }

/* ---- Slot picker --------------------------------------------------------- */
.vh-slots { display:grid; grid-template-columns: repeat(auto-fill, minmax(8.5rem,1fr)); gap: var(--sp-2); }
.vh-slot {
  position:relative; display:flex; flex-direction:column; gap:2px; cursor:pointer;
  border:1px solid var(--border-strong); border-radius: var(--radius-lg);
  padding: var(--sp-3); background: var(--layer); transition: border-color .15s, background .15s;
}
.vh-slot input { position:absolute; opacity:0; pointer-events:none; }
.vh-slot__time { font-weight:600; font-size: var(--t-body); }
.vh-slot__cap { font-size: var(--t-caption); color: var(--text-muted); }
.vh-slot:hover:not(.is-full) { border-color: var(--interactive); background: var(--layer-accent); }
.vh-slot:has(input:checked) { border-color: var(--interactive); background: var(--layer-accent); box-shadow: inset 0 0 0 1px var(--interactive); }
.vh-slot:has(input:focus-visible) { outline:3px solid var(--focus); outline-offset:2px; }
.vh-slot.is-full { opacity:.45; cursor:not-allowed; background: var(--layer-hover); }
.vh-slot.is-full .vh-slot__cap { color: var(--support-error); }

/* ---- Row overflow menu --------------------------------------------------- */
.vh-more { position:relative; }
.vh-more > summary { list-style:none; cursor:pointer; }
.vh-more > summary::-webkit-details-marker { display:none; }
.vh-more__menu {
  position:absolute; right:0; top:calc(100% + 4px); z-index:20; width: 17rem; text-align:left;
  background: var(--layer); border:1px solid var(--border-strong); border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg); padding: var(--sp-4);
}
.vh-more__label { display:block; font-size: var(--t-label); color: var(--text-secondary); margin-bottom:4px; font-weight:500; }

@media (max-width: 1024px) {
  .vh-week { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 560px) {
  .vh-week { grid-template-columns: 1fr; }
  .vh-more__menu { right:auto; left:0; width: 15rem; }
}

/* =============================================================================
   ICD-11 TERM PICKER
   ========================================================================== */

.vh-icd { margin-bottom: var(--sp-4); }
.vh-icd__label { display:block; font-size: var(--t-label); color: var(--text-secondary); margin-bottom: 6px; font-weight:500; }
.vh-icd__box { position: relative; }

/* ---- Inline predictive completion ---------------------------------------
   The ghost sits underneath a transparent-background input in the identical
   font, size and padding, so the grey remainder lines up exactly with the
   caret. Getting these two boxes to disagree by even a pixel is what makes
   this effect look broken, hence the shared values rather than eyeballed ones. */
/* Two classes deliberately: .vh-input is declared later in this file and sets
   an opaque background, which would paint straight over the ghost. Matching on
   the wrapper as well wins on specificity regardless of source order. */
.vh-icd .vh-icd__input {
  position: relative; z-index: 1; background: transparent;
}
.vh-icd__ghost {
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  display: flex; align-items: center; overflow: hidden; white-space: pre;
  padding: .625rem .75rem;                 /* == .vh-input padding */
  font-family: inherit; font-size: var(--t-body); line-height: 1.55;
  border: 1px solid transparent;           /* == .vh-input border width */
  border-radius: var(--radius);
  background: var(--layer);
}
.vh-icd__ghost span { color: transparent; }   /* spacer for what is already typed */
.vh-icd__ghost i    { color: var(--text-muted); font-style: normal; }

/* Code mode: someone typed 1F40 rather than "malaria". */
.vh-icd__mode {
  position: absolute; right: .5rem; top: 50%; transform: translateY(-50%); z-index: 2;
  font-family: var(--mono); font-size: var(--t-caption); font-weight: 600;
  color: var(--interactive); background: var(--layer-accent);
  border-radius: 1rem; padding: 2px 9px; pointer-events: none;
}
.vh-icd.is-code .vh-icd__input { font-family: var(--mono); letter-spacing: .04em; }
.vh-icd.is-code .vh-icd__ghost { font-family: var(--mono); letter-spacing: .04em; }

.vh-icd__hint {
  margin-top: 4px; font-size: var(--t-caption); color: var(--text-muted);
  display: flex; align-items: center; gap: 4px; flex-wrap: wrap;
}
.vh-icd__hint kbd {
  font-family: var(--mono); font-size: var(--t-caption);
  border: 1px solid var(--border-strong); border-bottom-width: 2px;
  border-radius: 3px; padding: 0 5px; color: var(--text-secondary);
}

/* ---- Chosen terms -------------------------------------------------------- */
.vh-icd__chips { display:flex; flex-wrap:wrap; gap: var(--sp-2); }
.vh-icd__chips:not(:empty) { margin-bottom: var(--sp-2); }
.vh-icd__chip {
  display:inline-flex; align-items:center; gap: var(--sp-2); max-width:100%;
  background: var(--layer-accent); border:1px solid var(--interactive);
  border-radius: var(--radius-lg); padding: .3rem .5rem .3rem .35rem;
  font-size: var(--t-label);
}
/* A term with no code is not an error — it is work for the coding worklist —
   so it is marked, not alarmed. */
.vh-icd__chip.is-uncoded { background: var(--layer-hover); border-color: var(--support-warning); border-style: dashed; }
.vh-icd__chip-code {
  font-family: var(--mono); font-size: var(--t-caption); font-weight:600;
  background: var(--interactive); color:#fff; border-radius: 3px; padding: 1px 6px; flex:none;
}
.vh-icd__chip.is-uncoded .vh-icd__chip-code { background: var(--support-warning); }
.vh-icd__chip-text { font-weight:500; }
.vh-icd__chip-title { color: var(--text-muted); font-size: var(--t-caption); }
.vh-icd__remove {
  background:none; border:0; cursor:pointer; color: var(--text-muted);
  font-size: 1.1rem; line-height:1; padding:0 2px; flex:none;
}
.vh-icd__remove:hover { color: var(--support-error); }

/* ---- Suggestions --------------------------------------------------------- */
.vh-icd__menu {
  position:absolute; left:0; right:0; top:calc(100% + 4px); z-index:30;
  background: var(--layer); border:1px solid var(--border-strong);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  max-height: 19rem; overflow-y:auto; padding: 4px;
}
.vh-icd__opt {
  display:flex; align-items:baseline; gap: var(--sp-3); width:100%; text-align:left;
  background:none; border:0; cursor:pointer; font-family:inherit; font-size: var(--t-sm);
  padding: .5rem .625rem; border-radius: var(--radius); color: var(--text);
}
.vh-icd__opt:hover, .vh-icd__opt.is-active { background: var(--layer-accent); }
.vh-icd__opt-code {
  font-family: var(--mono); font-size: var(--t-caption); font-weight:600;
  color: var(--interactive); flex:none; min-width: 4.5rem;
}
.vh-icd__opt-title { flex:1; }
/* The characters the clinician actually typed, so it is obvious why a row
   matched. <mark> is styled explicitly — the browser default is a yellow
   highlight that is unreadable on the dark theme. */
.vh-icd__opt mark {
  background: none; color: var(--interactive); font-weight: 700; padding: 0;
}
.vh-icd__opt.is-active mark { color: var(--interactive); }
.vh-icd__opt-tag {
  font-size: var(--t-caption); color: var(--support-success);
  background: rgba(25,128,56,.12); border-radius: 1rem; padding: 1px 8px; flex:none;
}
.vh-icd__empty { padding: var(--sp-3); font-size: var(--t-sm); color: var(--text-muted); display:flex; align-items:center; gap: var(--sp-2); flex-wrap:wrap; }

/* ---- Loading ------------------------------------------------------------- */
/* A type-ahead that goes blank while it waits reads as broken, so the menu
   always says what it is doing. */
.vh-icd__loading {
  display:flex; align-items:center; gap: var(--sp-2);
  padding: var(--sp-3); font-size: var(--t-sm); color: var(--text-muted);
}
.vh-icd__spinner {
  width: 14px; height: 14px; flex:none; border-radius: 50%;
  border: 2px solid var(--border-strong); border-top-color: var(--interactive);
  animation: vh-spin .6s linear infinite;
}
@keyframes vh-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .vh-icd__spinner { animation: none; border-top-color: var(--border-strong); }
}

/* ---- Tiles / cards ------------------------------------------------------- */
.vh-tile { background: var(--layer); border: 1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-5); }
.vh-tile h3 { font-size: var(--t-h4); }
.vh-grid { display:grid; gap: var(--sp-4); }
.vh-grid.cols-4 { grid-template-columns: repeat(4, 1fr); }
.vh-grid.cols-3 { grid-template-columns: repeat(3, 1fr); }
.vh-grid.cols-2 { grid-template-columns: repeat(2, 1fr); }

.vh-kpi { background: var(--layer); border:1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-4) var(--sp-5); border-left: 4px solid var(--interactive); }
.vh-kpi .k { font-size: var(--t-label); text-transform: uppercase; letter-spacing:.06em; color: var(--text-muted); }
.vh-kpi .v { font-size: var(--t-h1); font-weight: 600; line-height: 1.1; margin-top: var(--sp-2); font-variant-numeric: tabular-nums; }
.vh-kpi .d { font-size: var(--t-label); color: var(--text-muted); margin-top: 4px; }

/* ---- Buttons ------------------------------------------------------------- */
.vh-btn { display:inline-flex; align-items:center; justify-content:center; gap: var(--sp-2);
  font-family:inherit; font-size: var(--t-body); font-weight:500; min-height: var(--tap);
  padding: .625rem 1.25rem; border-radius: var(--radius); border:1px solid transparent; cursor:pointer;
  transition: background .12s, border-color .12s; }
.vh-btn--primary { background: var(--interactive); color:#fff; }
.vh-btn--primary:hover { background: var(--interactive-hover); text-decoration:none; color:#fff; }
.vh-btn--secondary { background: var(--layer); color: var(--text); border-color: var(--border-strong); }
.vh-btn--secondary:hover { background: var(--layer-hover); text-decoration:none; }
.vh-btn--ghost { background:none; color: var(--interactive); }
.vh-btn--ghost:hover { background: var(--layer-hover); text-decoration:none; }
.vh-btn--danger { background: var(--support-error); color:#fff; }
.vh-btn[disabled] { opacity:.5; cursor:not-allowed; }

/* ---- Tags / badges ------------------------------------------------------- */
.vh-tag { display:inline-flex; align-items:center; gap:4px; font-size: var(--t-label); font-weight:500; padding: .25rem .75rem; border-radius: 1rem; background: var(--layer-accent); color: var(--interactive); }
.vh-tag--grey { background: var(--layer-hover); color: var(--text-secondary); }
.vh-tag--success { background: rgba(25,128,56,.14); color: var(--support-success); }
.vh-tag--warning { background: rgba(138,97,0,.16); color: var(--support-warning); }
.vh-tag--error { background: rgba(218,30,40,.12); color: var(--support-error); }
:root[data-theme="dark"] .vh-tag--warning,
:root:not([data-theme="light"]) .vh-tag--warning { color: #f1c21b; }

/* ---- Tables -------------------------------------------------------------- */
.vh-table-wrap { background: var(--layer); border:1px solid var(--border); border-radius: var(--radius-lg); overflow-x: auto; }
.vh-table { width:100%; border-collapse: collapse; font-size: var(--t-sm); }
.vh-table th { text-align:left; font-size: var(--t-label); text-transform:uppercase; letter-spacing:.04em; color: var(--text-secondary); font-weight:600; padding: var(--sp-3) var(--sp-4); border-bottom:1px solid var(--border-strong); background: var(--layer); position: sticky; top:0; }
.vh-table td { padding: var(--sp-3) var(--sp-4); border-bottom:1px solid var(--border); }
.vh-table tr:hover td { background: var(--layer-hover); }
.vh-table tr:last-child td { border-bottom:0; }
.vh-table .num { text-align:right; font-variant-numeric: tabular-nums; }

/* ---- Forms --------------------------------------------------------------- */
.vh-field { margin-bottom: var(--sp-4); }
.vh-field > label { display:block; font-size: var(--t-label); color: var(--text-secondary); margin-bottom: 6px; font-weight:500; }
.vh-field .req { color: var(--support-error); }
.vh-input, .vh-select, .vh-textarea {
  width:100%; font-family:inherit; font-size: var(--t-body); color: var(--text);
  background: var(--layer); border:1px solid var(--border-strong); min-height: var(--tap);
  border-bottom-color: var(--text-muted); border-radius: var(--radius); padding: .625rem .75rem;
}
.vh-input:focus, .vh-select:focus, .vh-textarea:focus { outline: 3px solid var(--focus); outline-offset:-3px; border-color: transparent; }
.vh-help { font-size: var(--t-label); color: var(--text-muted); margin-top:4px; }
.vh-fieldset { border:1px solid var(--border); border-radius: var(--radius-lg); padding: var(--sp-4) var(--sp-5); margin-bottom: var(--sp-5); }
.vh-fieldset > legend { font-size: var(--t-label); text-transform:uppercase; letter-spacing:.06em; color: var(--text-muted); font-weight:600; padding: 0 var(--sp-2); }

/* ---- Notifications ------------------------------------------------------- */
.vh-note { display:flex; gap: var(--sp-3); align-items:flex-start; padding: var(--sp-4); border-radius: var(--radius-lg); border-left:4px solid var(--support-info); background: var(--layer); border:1px solid var(--border); margin-bottom: var(--sp-4); font-size: var(--t-sm); }
.vh-note--success { border-left-color: var(--support-success); }
.vh-note--error { border-left-color: var(--support-error); }
.vh-note--warning { border-left-color: var(--support-warning); }
.vh-note .t { font-weight:600; font-size: var(--t-body); margin-bottom: 2px; }

/* ---- Sync / offline indicator ------------------------------------------- */
.vh-sync { display:flex; align-items:center; gap:6px; font-size: var(--t-label); padding: .375rem .75rem; border-radius: 1rem; background: rgba(255,255,255,.12); }
.vh-sync.is-online  { color:#7fd88a; }
.vh-sync.is-offline { color:#ffb784; }
.vh-offline-banner { display:none; background: var(--support-warning); color:#fff; text-align:center; font-size: var(--t-sm); padding: var(--sp-2); position:fixed; top: var(--header-h); left:0; right:0; z-index:35; }
body.is-offline .vh-offline-banner { display:block; }

/* ---- Avatar / meta ------------------------------------------------------- */
.vh-avatar { width:2.25rem;height:2.25rem;border-radius:50%; background: var(--accent); color:#fff; display:inline-flex; align-items:center; justify-content:center; font-weight:600; font-size: var(--t-sm); flex:none; }
.vh-meta-grid { display:grid; grid-template-columns: repeat(auto-fill, minmax(13rem,1fr)); gap: var(--sp-4); }
.vh-meta-grid .k { font-size: var(--t-caption); text-transform:uppercase; letter-spacing:.05em; color: var(--text-muted); }
.vh-meta-grid .v { font-size: var(--t-lg); margin-top:2px; }

/* ---- Search bar ---------------------------------------------------------- */
.vh-search { display:flex; align-items:center; gap: var(--sp-2); background: var(--layer); border:1px solid var(--border-strong); border-radius: var(--radius); padding: 0 var(--sp-3); min-height: var(--tap); }
.vh-search:focus-within { outline: 3px solid var(--focus); outline-offset: -3px; border-color: transparent; }
.vh-search input { flex:1; border:0; background:none; padding: .625rem 0; font-size: var(--t-body); color: var(--text); font-family:inherit; min-width: 0; }
.vh-search input:focus { outline:none; }
.vh-search kbd {
  font-family: var(--mono); font-size: var(--t-caption); color: var(--text-muted);
  border:1px solid var(--border-strong); border-bottom-width:2px; border-radius:3px; padding: 1px 6px; flex:none;
}

/* ---- Simple bar chart (offline, no chart lib) --------------------------- */
.vh-bars { display:flex; align-items:flex-end; gap: 6px; height: 8.75rem; padding-top: var(--sp-3); }
.vh-bars .bar { flex:1; background: linear-gradient(var(--accent), var(--blue)); border-radius: 2px 2px 0 0; min-height:2px; position:relative; }
.vh-bars .bar:hover::after { content: attr(data-v); position:absolute; top:-1.25rem; left:50%; transform:translateX(-50%); font-size: var(--t-caption); color: var(--text-secondary); }
.vh-bars-x { display:flex; gap:6px; margin-top:6px; }
.vh-bars-x span { flex:1; text-align:center; font-size: var(--t-caption); color: var(--text-muted); }

/* ---- Login --------------------------------------------------------------- */
.vh-login { min-height:100vh; display:grid; grid-template-columns: 1fr 1fr; }
.vh-login__brand { background: linear-gradient(160deg, var(--navy), #123a63); color:#fff; padding: var(--sp-8); display:flex; flex-direction:column; justify-content:center; }
.vh-login__brand h1 { font-size: 2.75rem; color:#fff; } .vh-login__brand h1 span { color: var(--accent); }
.vh-login__brand p { color: rgba(255,255,255,.75); max-width: 30rem; font-size: var(--t-lg); }
.vh-login__brand .feats { margin-top: var(--sp-6); display:grid; gap: var(--sp-3); }
.vh-login__brand .feats div { display:flex; gap:var(--sp-3); align-items:center; color: rgba(255,255,255,.88); }
.vh-login__form { display:flex; align-items:center; justify-content:center; padding: var(--sp-6); background: var(--bg); }
.vh-login__form .card { width:100%; max-width: 25rem; }

/* ---- Utilities ----------------------------------------------------------- */
.row { display:flex; gap: var(--sp-4); flex-wrap:wrap; }
.row.between { justify-content:space-between; align-items:center; }
.row.tight { gap: var(--sp-2); }
.mt-4 { margin-top: var(--sp-4);} .mt-5 { margin-top: var(--sp-5);} .mb-0{margin-bottom:0;}
.grow { flex:1; }
.f2 { grid-column: span 2; }
.mono { font-family: var(--mono); font-size: var(--t-sm); }
.empty { text-align:center; padding: var(--sp-7); color: var(--text-muted); font-size: var(--t-body); }
.vh-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;
}

/* ---- Motion -------------------------------------------------------------- */
/* Tiles fade up in sequence on the launcher. Purely a cue that the grid is a
   list you can scan, so it is the first thing dropped under reduced-motion. */
@keyframes vh-rise { from { opacity:0; transform: translateY(10px); } to { opacity:1; transform:none; } }
.vh-app { animation: vh-rise .32s cubic-bezier(.2,.7,.3,1) backwards; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-iteration-count: 1 !important;
    transition-duration: .001ms !important; scroll-behavior: auto !important;
  }
  .vh-app:hover, .vh-app:focus-visible { transform: none; }
  .vh-app__go { opacity: 1; transform: none; }
}

/* ---- Responsive ---------------------------------------------------------- */
@media (max-width: 1200px) {
  .vh-grid.cols-4 { grid-template-columns: repeat(2,1fr); }
}
@media (max-width: 1024px) {
  .vh-grid.cols-3 { grid-template-columns: repeat(2,1fr); }
  .vh-login { grid-template-columns: 1fr; } .vh-login__brand { display:none; }
  .vh-hero { padding: var(--sp-5); }
  .vh-clock { text-align: left; }
  :root { --t-display: 2.5rem; }
}
@media (max-width: 768px) {
  #navToggle { display: flex; }              /* off-canvas nav: the button works here */
  .vh-sidenav { transform: translateX(-100%); transition: transform .2s; }
  body.nav-open .vh-sidenav { transform: none; box-shadow: var(--shadow-lg); }
  .vh-main { margin-left: 0; padding: var(--sp-4); }
  .vh-grid.cols-4, .vh-grid.cols-3, .vh-grid.cols-2 { grid-template-columns: 1fr; }
  .f2 { grid-column: auto; }
  .vh-apps { grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr)); gap: var(--sp-3); }
  .vh-app { min-height: 8.5rem; padding: var(--sp-4); }
  .vh-app__chip { width: 3rem; height: 3rem; }
  /* The header is a single non-wrapping flex row, so on a narrow screen its
     items must be dropped deliberately or they run off the right edge (the
     avatar and Sign out did exactly that at 485px). Order of sacrifice: the
     things repeated elsewhere go first. The text-size control is kept at every
     width — a small screen is where it matters most. */
  .vh-header { gap: var(--sp-2); padding: 0 var(--sp-2); }
  .vh-header .brand { min-width: 0; }
  .vh-header .brand small { display:none; }      /* strapline: decorative */
  .vh-user { display:none; }                     /* name/role: also in the account menu */
  .vh-avatar { display:none; }
  #syncText { display:none; }                    /* the wifi glyph already says it */
  .vh-sync { padding: .375rem .5rem; }
  /* The search stays full width on a phone; only its padding tightens. */
  .vh-find { padding: var(--sp-3) var(--sp-4); }
  .vh-find input { font-size: var(--t-body); }
  .vh-hero { padding: var(--sp-5) var(--sp-4); }
  :root { --t-h1: 1.875rem; }
}
@media (max-width: 560px) {
  /* Wordmark goes; the health-cross logo still links home. */
  .vh-header .brand__text { display:none; }
  .vh-textsize button { min-width: 1.75rem; padding: 0 .25rem; }
}
@media (max-width: 420px) {
  .vh-apps { grid-template-columns: repeat(2, 1fr); }
  .vh-app__desc { display: none; }        /* label + icon carry it at this width */
  .vh-hero__meta { font-size: var(--t-sm); }
}
