/* ============================================================================
   LabApp design system — Data-Dense Dashboard
   ----------------------------------------------------------------------------
   Cascade layers, in priority order (later layer always wins, regardless of
   where a rule sits in this file):

       tokens < base < layout < components < screens < responsive < utilities

   This replaces the old "the mobile block MUST stay last" convention: the
   phone rules live in `responsive`, so a component rule can no longer silently
   undo them no matter where it is written. Add new rules INSIDE a layer.

   Type:    Figtree (UI) + Noto Sans (data) — vendored in /static/fonts,
            no network request (offline invariant).
   Colour:  clinical blue #1E40AF, amber #D97706 for "needs attention".
   Density: 8/10 — 4-32px spacing scale, compact rows, tabular numerals.
   ========================================================================== */
@layer tokens, base, layout, components, screens, responsive, utilities;

/* ── vendored typefaces (variable, one file per family) ────────────────── */
@font-face {
  font-family: "Figtree";
  src: url("/static/fonts/figtree-var.woff2") format("woff2");
  font-weight: 300 900; font-style: normal; font-display: swap;
}
@font-face {
  font-family: "Noto Sans LabApp";
  src: url("/static/fonts/notosans-var.woff2") format("woff2");
  font-weight: 100 900; font-style: normal; font-display: swap;
}
/* A real italic, so placeholders and notes are not a slanted upright. The
   browser only downloads it once something on the page is italic. Rebuilt
   with tools/build_fonts.py; licences are the OFL-*.txt files beside them. */
@font-face {
  font-family: "Noto Sans LabApp";
  src: url("/static/fonts/notosans-italic-var.woff2") format("woff2");
  font-weight: 100 900; font-style: italic; font-display: swap;
}

@layer tokens {
  :root {
    /* brand ramp — anchored on #1E40AF */
    --b-50:  #eff4ff;  --b-100: #dbe6fe;  --b-200: #bfd3fd;  --b-300: #93b4fc;
    --b-400: #6090f8;  --b-500: #3b82f6;  --b-600: #2563eb;  --b-700: #1d4ed8;
    --b-800: #1e40af;  --b-900: #1e3a8a;

    /* neutral ramp */
    --n-0:   #ffffff;  --n-25:  #fbfcfe;  --n-50:  #f8fafc;  --n-100: #f1f5f9;
    --n-200: #e2e8f0;  --n-300: #cbd5e1;  --n-400: #94a3b8;  --n-500: #64748b;
    --n-600: #475569;  --n-700: #334155;  --n-800: #1e293b;  --n-900: #0f172a;

    /* semantic */
    --accent:      #d97706;  --accent-soft: #fef3c7;  --accent-ink: #92400e;
    --accent-bd:   #fde68a;
    --success:     #15803d;  --success-soft:#dcfce7;  --success-bd: #bbf7d0;
    --danger:      #dc2626;  --danger-soft: #fee2e2;  --danger-bd:  #fecaca;
    --info:        #1d4ed8;  --info-soft:   #dbeafe;
    /* work-list status "To verify" (2026-09-15) — violet, because pending
       already owns amber and complete green. Ink on soft ≈ 6.0:1. */
    --status-verify:      #6d28d9;  --status-verify-soft: #e3dafd;
    --status-verify-faint:#eee9fd;  --status-verify-bd:   #d9cdfb;
    /* work-list "Pending" is blue and "Complete" green (1.20.5, matching the
       soft-3D reference). Amber stays the "needs attention" accent. Ink on
       soft: pending ≈ 5.9:1, complete ≈ 6.6:1. */
    --status-pending:      #1d4ed8;  --status-pending-soft:  #d4e2fd;
    --status-pending-faint:#e6eefe;  --status-pending-bd:    #bfd3fd;
    --status-complete:     #166534;  --status-complete-soft: #cdeed8;
    --status-complete-faint:#dff4e6; --status-complete-bd:   #b4ebc6;

    /* clinical result flags */
    --flag-low:  #1d4ed8;  --flag-high: #dc2626;
    /* panic values sit outside the ordinary high/low pair on purpose */
    --flag-crit: #b00020;

    /* surfaces */
    --bg:        var(--n-50);
    --panel:     var(--n-0);
    --panel-alt: var(--n-25);
    --header-bg: var(--n-100);
    /* sign-in only, and the only place these three are allowed: the page
       wash and the mark/button green of the LIS login screen this one was
       ported from (user request 2026-09-14). Deliberately not folded into
       the brand ramp — the app is blue, this screen is not the app. */
    --auth-bg:       #f6f7f9;
    --auth-accent:   #157f3d;
    --auth-accent-dk:#116632;

    /* provider console lab page (user-supplied design, 2026-09-15): a deep
       green accent and an amber notice, used by admin/tenant_detail.html */
    --pv-accent:      #0f5c4b;
    --pv-accent-dk:   #0b4a3c;
    --pv-accent-soft: #e4efea;
    --pv-warn-bg:     #fbf3e3;
    --pv-warn-bd:     #ead9b4;
    --pv-warn-ink:    #8a5a00;

    /* text */
    --ink:       var(--n-800);
    --ink-soft:  var(--n-600);
    /* not --n-500: that is 4.0:1 on the soft-3D page wash (#e7ecf2) and
       4.3:1 on a card's lower edge; this is ≥ 4.7:1 on both and on white. */
    --muted:     #5b6778;
    --ink-faint: var(--n-400);

    /* lines */
    --line:      var(--n-200);
    --line-soft: var(--n-100);
    --line-strong: var(--n-300);

    /* legacy aliases — every pre-existing rule and template keeps working */
    --brand:      var(--b-700);
    --brand-dk:   var(--b-800);
    --brand-soft: var(--b-50);
    --ok:  var(--success);
    --err: var(--danger);
    --low:  var(--flag-low);
    --high: var(--flag-high);

    /* spacing — density 8/10 */
    --s-1: 4px;  --s-2: 8px;  --s-3: 12px; --s-4: 16px;
    --s-5: 20px; --s-6: 24px; --s-8: 32px; --s-10: 40px;

    /* radius */
    --r-xs: 4px; --r-sm: 6px; --r: 8px; --r-md: 10px;
    --r-lg: 12px; --r-xl: 16px; --r-full: 999px;

    /* elevation */
    --sh-1: 0 1px 2px rgba(15,23,42,.05);
    --sh-2: 0 1px 3px rgba(15,23,42,.07), 0 1px 2px rgba(15,23,42,.04);
    --sh-3: 0 4px 12px rgba(15,23,42,.07);
    --sh-4: 0 12px 32px rgba(15,23,42,.13);
    --sh-pop: 0 20px 50px rgba(15,23,42,.22);

    /* focus */
    --ring: 0 0 0 3px rgba(37,99,235,.30);
    --ring-err: 0 0 0 3px rgba(220,38,38,.25);

    /* motion */
    --dur-1: 120ms; --dur-2: 180ms; --dur-3: 260ms;
    --ease: cubic-bezier(.2,0,0,1);
    --ease-out: cubic-bezier(.16,1,.3,1);

    /* type */
    /* Each vendored face falls back to the OTHER before the system: Figtree
       has no Greek (β-hCG), Noto Sans has no ≤ ≥ or arrows. Both are loaded
       on every page, so a missing glyph never reaches a device font. */
    --font-ui:   "Figtree", "Noto Sans LabApp", "Segoe UI Variable Text", "Segoe UI", system-ui, sans-serif;
    --font-data: "Noto Sans LabApp", "Figtree", "Segoe UI", system-ui, sans-serif;
    --font-mono: "Cascadia Mono", Consolas, ui-monospace, monospace;

    /* control sizing */
    --ctl-h: 38px;
    --ctl-h-sm: 32px;

    /* sidebar mark — the lab's chosen colour overrides all three on <body>
       (base.html, Settings → Sidebar, 2026-10-25) */
    --brand-ink: var(--b-800);
    --brand-icon: var(--b-700);
    --brand-ring: var(--b-200);
    /* theme swatches (2026-10-25): each tile sets its own pair inline */
    --sw-a: var(--b-800);
    --sw-b: var(--accent);

    /* z-index scale */
    --z-nav: 60; --z-drawer: 80; --z-modal: 100; --z-toast: 200;

    /* ── soft-3D surface language (1.20.5, user reference mockup) ─────────
       Every lab screen reads these instead of flat colours: a cool-grey page
       wash, gradient cards with an inner highlight, raised gradient buttons
       and inset (sunken) fields. The provider console and the sign-in card
       reset them to the flat values below, so they are unchanged. */
    --page-wash:  linear-gradient(165deg, #f2f5f9 0%, #e7ecf2 55%, #eceff5 100%);
    /* The stuck section-tab bar: frosted, so the gradient wash shows through. */
    --bar-wash:   rgba(236, 240, 245, .84);
    --sidebar-bg: linear-gradient(180deg, #ffffff 0%, #f6f8fb 100%);
    --nav-active: linear-gradient(180deg, #edf3ff 0%, #e1eafc 100%);
    --nav-active-ring: inset 0 0 0 1px rgba(191,211,253,.55), 0 1px 2px rgba(30,64,175,.06);

    --surface:    linear-gradient(180deg, #ffffff 0%, #f1f4f8 100%);
    --surface-bd: #e1e6ed;
    --sh-card:    inset 0 1px 0 rgba(255,255,255,.95), 0 1px 3px rgba(15,23,42,.07), 0 10px 28px rgba(15,23,42,.09);
    --empty-bg:   linear-gradient(180deg, rgba(255,255,255,.72) 0%, rgba(255,255,255,.38) 100%);
    --th-bg:      linear-gradient(180deg, #f8fafc 0%, #edf0f5 100%);

    --btn-bg:        linear-gradient(180deg, #3a62d6 0%, #1e40af 58%, #1b3a9e 100%);
    --btn-bg-hover:  linear-gradient(180deg, #2f56c6 0%, #1b3a9e 58%, #17328b 100%);
    --btn-bd:        #1b3a9e;
    --btn-bd-hover:  #17328b;
    --sh-btn:        inset 0 1px 0 rgba(255,255,255,.22), 0 2px 3px rgba(30,64,175,.22), 0 6px 14px rgba(30,64,175,.24);
    --sh-btn-press:  inset 0 1px 2px rgba(15,23,42,.25), 0 1px 2px rgba(30,64,175,.2);
    --btn-kbd-bg:    #e8eefb;
    --btn-kbd-ink:   var(--b-900);
    --btn-soft-bg:       linear-gradient(180deg, #edf3ff 0%, #d7e5fd 100%);
    --btn-soft-bg-hover: linear-gradient(180deg, #e3edff 0%, #cadcfc 100%);
    --btn-soft-bd:       #c3d5fb;
    --btn-soft-ink:      var(--b-800);
    --btn-neutral-bg:       linear-gradient(180deg, #ffffff 0%, #e6e9ef 100%);
    --btn-neutral-bg-hover: linear-gradient(180deg, #ffffff 0%, #e3e7ee 100%);
    --btn-neutral-bd:       #d6dce5;
    --sh-raise:   inset 0 1px 0 rgba(255,255,255,.95), 0 1px 2px rgba(15,23,42,.08), 0 4px 10px rgba(15,23,42,.08);
    --sh-btn-danger: inset 0 1px 0 rgba(255,255,255,.2), 0 2px 3px rgba(220,38,38,.22), 0 6px 14px rgba(220,38,38,.18);
    --danger-hover: #b91c1c;

    --field-bg:    #e6eaf0;
    --field-bd:    #cfd6e0;
    --sh-field:    inset 0 2px 4px rgba(15,23,42,.11), inset 0 1px 1px rgba(15,23,42,.06), 0 1px 0 rgba(255,255,255,.95);
    --placeholder: #5b6778;
    --well:        #e8ecf1;
    --sh-well:     inset 0 1px 3px rgba(15,23,42,.10), 0 1px 0 rgba(255,255,255,.9);
    --tab-bg:      linear-gradient(180deg, #fbfcfd 0%, #e8ebf0 100%);
    --tab-divider: #d3d9e1;
    --sh-count:    inset 0 1px 2px rgba(15,23,42,.08);
  }

  /* Flat look kept for the provider console and the sign-in card. */
  .provider, .auth-body {
    --page-wash: var(--bg);  --sidebar-bg: var(--panel); --bar-wash: var(--bg);
    --nav-active: var(--b-50); --nav-active-ring: none;
    --surface: var(--n-0);   --surface-bd: var(--line);  --sh-card: var(--sh-1);
    --empty-bg: var(--panel); --th-bg: var(--header-bg);
    --btn-bg: var(--b-700);  --btn-bg-hover: var(--b-800);
    --btn-bd: var(--b-700);  --btn-bd-hover: var(--b-800);
    --sh-btn: var(--sh-1);   --sh-btn-press: none;  --btn-kbd-bg: rgba(255,255,255,.2); --btn-kbd-ink: #fff;
    --btn-soft-bg: var(--n-0); --btn-soft-bg-hover: var(--b-50);
    --btn-soft-bd: var(--line-strong); --btn-soft-ink: var(--b-700);
    --btn-neutral-bg: var(--n-0); --btn-neutral-bg-hover: var(--n-100);
    --btn-neutral-bd: var(--line-strong);
    --sh-raise: var(--sh-1); --sh-btn-danger: var(--sh-1);
    --field-bg: var(--n-0);  --field-bd: var(--line-strong); --sh-field: var(--sh-1);
    --placeholder: var(--ink-faint);
    --well: var(--n-100);    --sh-well: none;
  }
}

@layer base {
  *, *::before, *::after { box-sizing: border-box; }
  /* body grows with the page (min-height, not height): a fixed 100% body is
     the containing block of the phone's sticky .topbar, which then let go
     after one screen of scrolling and took the menu button with it. */
  html, body { margin: 0; }
  html { height: 100%; }
  body { min-height: 100%; }
  html { -webkit-text-size-adjust: 100%; }

  body {
    font-family: var(--font-ui);
    font-size: 14px;
    line-height: 1.5;
    color: var(--ink);
    background: var(--bg);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
  }

  h1, h2, h3, h4 { margin: 0 0 var(--s-3); font-weight: 650; letter-spacing: -.011em; }
  h1 { font-size: 20px; line-height: 1.25; }
  h2 { font-size: 16px; line-height: 1.3; }
  h3 { font-size: 13.5px; line-height: 1.35; }

  a { color: var(--brand); }

  /* Numbers are data: never let them shift width between renders. */
  .num, td.num, th.num, .big, .tabnum,
  input.num, .recent-no, .ac-price, .count { font-variant-numeric: tabular-nums; }

  kbd {
    font: 11px var(--font-mono);
    background: var(--line-soft); border: 1px solid var(--line);
    border-radius: var(--r-xs); padding: 1px 5px; color: var(--muted);
  }

  code { font-family: var(--font-mono); }

  /* One transition contract for every interactive surface. */
  a, button, .btn, input, select, textarea, summary, .chip, .tile,
  .recent-row, .opt, .toggle, .seg-opt {
    transition: background-color var(--dur-1) var(--ease),
                border-color var(--dur-1) var(--ease),
                color var(--dur-1) var(--ease),
                box-shadow var(--dur-1) var(--ease),
                transform var(--dur-1) var(--ease);
  }

  /* Focus is never removed — only restyled. */
  :focus-visible { outline: 2px solid var(--b-600); outline-offset: 2px; }
  button:focus-visible, .btn:focus-visible, a:focus-visible, summary:focus-visible {
    outline: 2px solid var(--b-600); outline-offset: 2px; border-radius: var(--r-sm);
  }

  ::selection { background: var(--b-100); color: var(--b-900); }

  @media print {
    .sidebar, .topbar, .toolbar, .flash, #toasts, #progress, .nav-backdrop { display: none !important; }
    .main { padding: 0; max-width: none; }
    body { background: #fff; }
    /* Ctrl+P on an app screen: paper gets the flat look, not the soft-3D
       gradients, shadows and sunken fields. */
    :root {
      --surface: var(--n-0); --sh-card: none; --th-bg: var(--header-bg);
      --empty-bg: var(--n-0); --field-bg: var(--n-0); --sh-field: none;
      --sh-raise: none; --sh-btn: none; --sh-well: none; --nav-active-ring: none;
    }
  }
}

@layer layout {
  .layout { display: flex; min-height: 100vh; }
  @media screen { .layout { background: var(--page-wash); } }
  .main { flex: 1; min-width: 0; padding: var(--s-6) var(--s-8) var(--s-10); max-width: 1560px; }

  /* ── sidebar ──────────────────────────────────────────────────────────── */
  /* The shell stays put while the page scrolls (2026-09-16, from a screen
     recording: a long register carried the brand, the menu and Sign out off
     screen). Sticky rather than an inner scroll box on purpose — the document
     keeps scrolling, so Ctrl+F, PageUp/Down, print and mobile address-bar
     collapse behave normally. A menu taller than the screen scrolls inside
     `nav`; the responsive layer turns this into the fixed phone drawer. */
  .sidebar {
    width: 240px; flex-shrink: 0;
    background: var(--sidebar-bg); border-right: 1px solid var(--line);
    display: flex; flex-direction: column;
    align-self: flex-start;
  }
  @media screen {
    .sidebar { position: sticky; top: 0; height: 100vh; height: 100dvh; }
  }
  /* The brand tokens follow the lab's own colour when one is chosen
     (Settings → Sidebar, 2026-10-25); otherwise the theme's blues. */
  .brand {
    display: flex; align-items: center; gap: var(--s-3);
    padding: var(--s-5) var(--s-4) var(--s-4);
    font-size: 15px; font-weight: 700; letter-spacing: -.015em; color: var(--brand-ink);
  }
  .brand svg { width: 24px; height: 24px; flex-shrink: 0; color: var(--brand-icon); }
  /* The lab's logo, cropped to a circle with a ring in the brand colour. */
  .brand-logo {
    width: 40px; height: 40px; flex-shrink: 0; border-radius: 50%;
    object-fit: cover; background: var(--n-0);
    border: 2px solid var(--brand-ring);
  }
  /* Long lab names wrap to a second line instead of truncating to "MY PATHOLOGY L…". */
  .brand span {
    min-width: 0; line-height: 1.25;
    font-size: 14px; font-weight: 650; letter-spacing: .02em; text-transform: uppercase;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .sidebar nav { flex: 1; padding: var(--s-1) var(--s-3); overflow-y: auto; }
  .sidebar nav a, .sidebar nav summary {
    display: flex; align-items: center; gap: var(--s-3);
    padding: 9px 11px; margin-bottom: 2px; border-radius: var(--r-md);
    font-size: 13.5px; font-weight: 500; color: var(--ink-soft); text-decoration: none;
    position: relative;
  }
  .sidebar nav a svg, .sidebar nav summary svg {
    width: 18px; height: 18px; flex-shrink: 0; color: var(--n-400);
    transition: color var(--dur-1) var(--ease);
  }
  .sidebar nav a:hover { background: var(--n-100); color: var(--ink); }
  .sidebar nav a:hover svg { color: var(--n-500); }
  /* Active item is a soft blue pill (1.20.5). Weight and icon colour change
     with it, so the current section is never marked by background alone. */
  .sidebar nav a.active {
    background: var(--nav-active); box-shadow: var(--nav-active-ring);
    color: var(--b-800); font-weight: 650;
  }
  .sidebar nav a.active svg { color: var(--b-700); }
  .sidebar nav details { margin-top: var(--s-2); }
  .sidebar nav summary {
    list-style: none; cursor: pointer; user-select: none;
    color: var(--muted); font-weight: 600;
    font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
  }
  .sidebar nav summary::-webkit-details-marker { display: none; }
  .sidebar nav summary::after {
    content: ""; margin-left: auto; width: 6px; height: 6px;
    border-right: 1.5px solid var(--n-300); border-bottom: 1.5px solid var(--n-300);
    transform: rotate(-45deg); transition: transform var(--dur-2) var(--ease);
  }
  .sidebar nav summary:hover { background: var(--n-100); color: var(--ink-soft); }
  .sidebar nav details[open] summary::after { transform: rotate(45deg); }
  .sidebar nav details a { padding-left: 40px; font-size: 13px; }

  .sidebar-foot { padding: var(--s-2) var(--s-3) var(--s-3); border-top: 1px solid var(--line-soft); }
  .sidebar-foot a, .sidebar-foot .logout-btn {
    display: flex; align-items: center; gap: var(--s-3);
    padding: 8px 11px; border-radius: var(--r-md);
    font-size: 13px; color: var(--muted); text-decoration: none;
  }
  .sidebar-foot a svg, .sidebar-foot .logout-btn svg { width: 18px; height: 18px; }
  .sidebar-foot a:hover { color: var(--danger); background: var(--danger-soft); }
  .logout-form { margin: 0; }
  /* NOTE: .logout-btn is a <button>, so its reset lives in the `components`
     layer — a rule here would lose to the base button styling. */

  /* Account card (2026-10-25): initials avatar · person + role · lab. No date
     line (removed at the user's request). Every screen is bold (utilities
     layer), so the hierarchy is carried by size and colour. */
  .session-info {
    display: flex; align-items: center; gap: var(--s-3);
    padding: var(--s-3); margin-bottom: var(--s-2);
    background: var(--n-0); border: 1px solid var(--line-soft);
    border-radius: var(--r-lg); box-shadow: var(--sh-raise);
  }
  .session-text { min-width: 0; flex: 1; }
  .session-avatar {
    flex: none; width: 36px; height: 36px; border-radius: 50%;
    display: grid; place-items: center;
    background: var(--brand-icon); color: var(--n-0);
    font-size: 13px; letter-spacing: .02em;
  }
  .session-lab {
    font-size: 11px; letter-spacing: .04em; text-transform: uppercase; color: var(--muted);
    margin-top: 2px;
    line-height: 1.3; overflow: hidden;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  }
  /* The full name, wrapped (2026-10-24): "Dr. Priya Sh…" told nobody who was
     signed in. The role chip wraps under a long name instead of squeezing it. */
  .session-user { display: flex; flex-wrap: wrap; align-items: center; gap: 2px var(--s-2); min-width: 0; }
  .session-name {
    font-size: 13.5px; color: var(--ink);
    min-width: 0; overflow-wrap: anywhere; line-height: 1.3;
  }
  .session-role {
    flex: none; font-size: 9.5px; letter-spacing: .06em; text-transform: uppercase;
    color: var(--b-800); background: var(--b-50);
    padding: 2px 7px; border-radius: var(--r-full);
  }

  /* ── mobile top bar (shown by the responsive layer) ────────────────────── */
  .topbar { display: none; }
  .nav-backdrop { display: none; }

  /* ── page furniture ───────────────────────────────────────────────────── */
  .page-head {
    display: flex; justify-content: space-between; align-items: flex-start;
    gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-5);
  }
  .page-head h1 { margin-bottom: var(--s-1); }
  .page-sub { margin: 0; color: var(--muted); font-size: 13.5px; max-width: 70ch; }
  .page-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }

  /* One raised container of joined segments split by thin dividers; the
     current section is a solid blue segment (1.20.5). Still scrolls sideways
     when the tabs outgrow the screen. */
  .subnav {
    display: flex; width: fit-content; max-width: 100%;
    overflow-x: auto; scrollbar-width: thin; -webkit-overflow-scrolling: touch;
    margin: 0; padding: 3px;
    background: var(--tab-bg); border: 1px solid var(--surface-bd); border-radius: var(--r-lg);
    box-shadow: var(--sh-raise);
  }
  /* Section tabs stay reachable on a long page (see .sidebar). The strip that
     sticks is the full-width bar in the page colour, not the pill itself: a
     floating pill let the page scroll visibly past and under it on both
     sides (2026-10-24). Screen only: paper has no viewport to stick to. */
  .subnav-bar {
    margin: calc(var(--s-6) * -1) calc(var(--s-8) * -1) var(--s-3);
    padding: var(--s-4) var(--s-8) var(--s-3);
    background: var(--bar-wash);
    -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px);
  }
  @media screen {
    .subnav-bar { position: sticky; top: 0; z-index: calc(var(--z-nav) - 1); }
    /* Keyboard focus and #anchors land below the stuck tab strip, not under it. */
    html { scroll-padding-top: 80px; }
  }
  .subnav a {
    display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; flex-shrink: 0;
    padding: 7px 14px; min-height: 36px; position: relative;
    font-size: 13.5px; font-weight: 500; color: var(--ink); text-decoration: none;
    border-radius: var(--r-sm);
  }
  .subnav a + a { box-shadow: -1px 0 0 var(--tab-divider); }
  .subnav a svg { width: 16px; height: 16px; color: var(--n-400); }
  .subnav a:hover { background: var(--n-0); color: var(--ink); }
  .subnav a.active, .subnav a.active:hover {
    background: var(--btn-bg); color: var(--n-0); font-weight: 600;
    box-shadow: var(--sh-btn);
  }
  .subnav a.active + a { box-shadow: none; }
  .subnav a.active svg { color: var(--n-0); }
  .subnav a:focus-visible { outline-offset: -2px; }

  .card {
    background: var(--surface); border: 1px solid var(--surface-bd); border-radius: var(--r-lg);
    padding: var(--s-5); margin-bottom: var(--s-4); box-shadow: var(--sh-card);
  }
  .row { display: flex; gap: var(--s-4); flex-wrap: wrap; align-items: flex-end; }
  .row > * { flex-shrink: 0; }
  .grow { flex: 1; }
  .toolbar { display: flex; gap: var(--s-2); flex-wrap: wrap; margin: var(--s-3) 0; }
  .toolbar .push-end { margin-left: auto; }
  .head-actions { margin: 0; }

  /* Responsive form grid used across settings/designer. */
  .ds-grid {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--s-3) var(--s-4); align-items: start;
  }
  .ds-grid .field { min-width: 0; }
  .ds-grid .span2 { grid-column: span 2; }
  .ds-grid .span3 { grid-column: 1 / -1; }
  /* Side-by-side cards that each need a real width (2026-10-24): a wrapping
     flex `.row` of `.card.grow` gave the third card whatever was left, and
     Masters → Centres printed case-type names one letter wide. */
  .card-grid {
    display: grid; gap: var(--s-4); align-items: start;
    grid-template-columns: repeat(auto-fit, minmax(min(440px, 100%), 1fr));
  }
  .card-grid > .card { margin-bottom: 0; min-width: 0; }
}

@layer components {
  /* ── forms ────────────────────────────────────────────────────────────── */
  label {
    display: block; margin-bottom: 5px;
    font-size: 11.5px; font-weight: 600; letter-spacing: .01em; color: var(--ink-soft);
  }
  .req { color: var(--danger); font-weight: 700; }

  input, select, textarea {
    font: inherit; font-size: 14px; width: 100%;
    min-height: var(--ctl-h); padding: 8px 11px;
    color: var(--ink); background: var(--field-bg);
    border: 1px solid var(--field-bd); border-radius: var(--r); box-shadow: var(--sh-field);
  }
  textarea { min-height: 84px; line-height: 1.5; }
  input::placeholder, textarea::placeholder { color: var(--placeholder); }
  input:hover, select:hover, textarea:hover { border-color: var(--n-400); }
  input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--b-600); background-color: var(--n-0); box-shadow: var(--ring);
  }
  input[aria-invalid="true"], input.invalid {
    border-color: var(--danger); box-shadow: var(--ring-err);
  }
  input:disabled, select:disabled, textarea:disabled {
    background: var(--n-100); color: var(--muted); cursor: not-allowed; box-shadow: none;
  }
  /* A field shown for information only (Settings → Lab name, set by the
     provider): flat and grey like a disabled box, still selectable. */
  input[aria-readonly="true"] { background: var(--n-100); color: var(--muted); box-shadow: none; cursor: default; }
  input[type="checkbox"], input[type="radio"] {
    width: 18px; height: 18px; min-height: 0; accent-color: var(--b-700);
    box-shadow: none; cursor: pointer;
  }
  input[type="color"] { padding: 2px; width: 44px; min-height: var(--ctl-h); cursor: pointer; }
  input[type="file"] {
    min-height: 0; padding: 7px 9px; background: var(--n-50); cursor: pointer; box-shadow: none;
  }
  input[type="date"] { min-width: 150px; }
  select {
    cursor: pointer; padding-right: 32px; appearance: none;
    padding-right: 40px;
    /* chevron + a thin divider, so the arrow reads as its own segment */
    background-image: linear-gradient(45deg, transparent 50%, var(--n-600) 50%),
                      linear-gradient(135deg, var(--n-600) 50%, transparent 50%),
                      linear-gradient(var(--field-bd), var(--field-bd));
    background-position: calc(100% - 16px) calc(50% + 1px), calc(100% - 11px) calc(50% + 1px),
                         calc(100% - 30px) 50%;
    background-size: 5px 5px, 5px 5px, 1px 55%;
    background-repeat: no-repeat;
  }
  input.num { text-align: right; }

  .field { min-width: 110px; }
  .field.sm { min-width: 70px; max-width: 90px; }
  .field.md { min-width: 150px; }
  .field.lg { min-width: 260px; flex: 1; }
  .field-actions { display: flex; gap: var(--s-2); align-self: flex-end; }
  .hint { margin: var(--s-1) 0 0; font-size: 12px; color: var(--muted); line-height: 1.45; }
  .hint code {
    font: 11.5px var(--font-mono); background: var(--line-soft);
    padding: 1px 4px; border-radius: var(--r-xs);
  }
  /* A hint that says something is wrong must not read as ordinary help text.
     Templates were already writing `class="hint warn"` with no rule behind it,
     so every one of those warnings rendered as the same muted grey as the rest
     — a warning nobody sees (found 2026-09-10). */
  .hint.warn { color: var(--accent-ink); font-weight: 500; }
  .check-row { align-self: center; padding-top: var(--s-2); }
  .check-inline {
    display: inline-flex; align-items: center; gap: var(--s-2); min-height: var(--ctl-h-sm);
    font-size: 13.5px; font-weight: 500; color: var(--ink); text-transform: none; cursor: pointer;
  }
  .check-list { display: flex; flex-direction: column; gap: var(--s-1); }
  .radio-row {
    display: flex; gap: var(--s-4); align-items: center; min-height: var(--ctl-h); flex-wrap: wrap;
  }
  .radio-row label {
    display: inline-flex; gap: 6px; align-items: center; margin: 0;
    font-weight: 400; font-size: 13.5px; color: var(--ink); text-transform: none;
  }
  .with-add { display: flex; gap: var(--s-2); align-items: center; }
  .with-add select { flex: 1; min-width: 0; }
  /* The "+" beside the select is as tall as it (it was a 32 px .sm button
     next to a 38 px select — UI audit 2026-10-24). */
  .with-add > .btn { min-height: var(--ctl-h); min-width: var(--ctl-h); padding: 0 var(--s-3); font-size: 16px; }
  /* A computed figure shown in a form grid: plain text, not a dead input. */
  .ro-value { display: flex; align-items: center; min-height: var(--ctl-h); font-size: 15px; font-weight: 700; color: var(--ink); }
  .sep-line { border: none; border-top: 1px solid var(--line); margin: var(--s-4) 0; }
  .form-sect {
    margin: var(--s-6) 0 var(--s-3);
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
    color: var(--muted);
  }
  .card-sub { margin: calc(var(--s-2) * -1) 0 var(--s-4); color: var(--muted); font-size: 13px; max-width: 78ch; }

  /* ── buttons ──────────────────────────────────────────────────────────── */
  button, .btn {
    font: inherit; font-size: 13.5px; font-weight: 600; line-height: 1;
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    min-height: var(--ctl-h); padding: 9px 16px;
    border: 1px solid var(--btn-bd); border-radius: var(--r);
    background: var(--btn-bg); color: #fff;
    text-decoration: none; cursor: pointer; box-shadow: var(--sh-btn);
    white-space: nowrap;
  }
  button:hover, .btn:hover { background: var(--btn-bg-hover); border-color: var(--btn-bd-hover); }
  button:active, .btn:active { transform: translateY(1px); box-shadow: var(--sh-btn-press); }
  button[disabled], .btn[disabled] { opacity: .55; cursor: not-allowed; transform: none; }

  /* Raised light-blue pill ("Show complete"). */
  .btn.secondary, button.secondary {
    background: var(--btn-soft-bg); color: var(--btn-soft-ink); border-color: var(--btn-soft-bd);
    box-shadow: var(--sh-raise);
  }
  .btn.secondary:hover, button.secondary:hover { background: var(--btn-soft-bg-hover); border-color: var(--b-300); }

  /* Raised white/light-grey ("Reset", "Last 7 days"). */
  .btn.ghost, button.ghost {
    background: var(--btn-neutral-bg); color: var(--ink-soft); border-color: var(--btn-neutral-bd);
    box-shadow: var(--sh-raise);
  }
  .btn.ghost:hover, button.ghost:hover { background: var(--btn-neutral-bg-hover); border-color: var(--n-400); color: var(--ink); }
  .btn.secondary:active, button.secondary:active, .btn.ghost:active, button.ghost:active,
  .btn.danger-text:active, button.danger-text:active { box-shadow: var(--sh-field); }

  .btn.danger, button.danger {
    background: var(--danger); border-color: var(--danger); color: #fff; box-shadow: var(--sh-btn-danger);
  }
  .btn.danger:hover, button.danger:hover { background: var(--danger-hover); border-color: var(--danger-hover); }

  .btn.danger-text, button.danger-text {
    background: var(--btn-neutral-bg); color: var(--danger); border-color: var(--btn-neutral-bd);
    box-shadow: var(--sh-raise);
  }
  .btn.danger-text:hover, button.danger-text:hover { background: var(--danger-soft); border-color: var(--danger-bd); }

  .btn.sm, button.sm { min-height: var(--ctl-h-sm); padding: 5px 11px; font-size: 12.5px; border-radius: var(--r-sm); }
  .btn-ico { width: 16px; height: 16px; flex-shrink: 0; }
  .btn kbd {
    margin-left: var(--s-2); background: var(--btn-kbd-bg); border-color: transparent;
    color: var(--btn-kbd-ink); border-radius: var(--r-sm); padding: 2px 6px;
  }
  .btn.secondary kbd, .btn.ghost kbd { background: var(--line-soft); border-color: var(--line); color: var(--muted); }
  .icon-btn { padding: 4px 7px; min-width: var(--ctl-h-sm); }
  .icon-btn svg { width: 16px; height: 16px; }
  .inline-form { display: inline; }
  /* A select and its Save on one line inside a table cell (2026-10-23: the
     receipt payment-mode correction) — a bare select fills the cell and
     pushes the button onto a line of its own. */
  .inline-pick { display: inline-flex; align-items: center; gap: var(--s-2); }
  .inline-pick select { width: auto; min-width: 7.5rem; }
  /* Payments → Void (2026-10-25): the reason box opens under the button;
     a voided row stays on the list, muted, its amount struck through. */
  .void-pick { display: inline-block; }
  .void-pick > summary { list-style: none; }
  .void-pick > summary::-webkit-details-marker { display: none; }
  .void-pick[open] > form {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
    margin-top: var(--s-2); padding: var(--s-2) var(--s-3);
    border: 1px solid var(--danger-bd); border-radius: var(--r-md); background: var(--danger-soft);
  }
  .void-pick input { min-width: 12rem; flex: 1; }
  tr.voided td { color: var(--muted); }

  /* Sidebar "Sign out" is a <button> that must read as a quiet nav row, not a
     primary action — it has to sit in this layer to beat the button defaults. */
  .sidebar-foot .logout-btn {
    width: 100%; justify-content: flex-start; gap: var(--s-3);
    padding: 8px 11px; min-height: 0;
    background: none; border: 0; box-shadow: none;
    font-size: 13px; font-weight: 500; color: var(--muted); text-align: left;
  }
  .sidebar-foot .logout-btn:hover { color: var(--danger); background: var(--danger-soft); }
  .sidebar-foot .logout-btn:active { transform: none; }

  /* Submitting state: label swaps for a spinner at a frozen width (base.html). */
  button[data-guarding] { position: relative; color: transparent !important; opacity: 1; }
  button[data-guarding]::after {
    content: ""; position: absolute; top: 50%; left: 50%;
    width: 15px; height: 15px; margin: -8px 0 0 -8px; border-radius: 50%;
    border: 2px solid rgba(255,255,255,.45); border-top-color: #fff;
    animation: spin .7s linear infinite;
  }
  button.ghost[data-guarding]::after, button.secondary[data-guarding]::after,
  button.danger-text[data-guarding]::after { border-color: var(--line); border-top-color: var(--b-700); }
  .busy-note { display: none; }
  button[disabled] + .busy-note { display: inline; margin-left: var(--s-2); font-size: 13px; color: var(--muted); }

  /* ── tables ───────────────────────────────────────────────────────────── */
  table.grid { width: 100%; border-collapse: collapse; background: var(--panel); }
  table.grid th, table.grid td {
    padding: 9px 12px; text-align: left; font-size: 13px;
    border-bottom: 1px solid var(--line-soft);
  }
  table.grid th {
    background: var(--th-bg);
    font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--muted); border-bottom: 1px solid var(--line); white-space: nowrap;
  }
  table.grid td.num, table.grid th.num { text-align: right; }
  table.grid tbody tr:hover td { background: var(--b-50); }
  table.grid tr.heading td { font-weight: 700; background: var(--line-soft); }
  table.grid tr.posted td { opacity: .55; background: var(--line-soft) !important; }
  table.grid tr.row-off .cell-main { color: var(--muted); }
  /* An editable list (name boxes + Save/Delete per row): fixed columns from
     the <colgroup>, so the name box takes what is left, never 30 px. */
  table.grid.list-edit { table-layout: fixed; }
  table.grid.list-edit td { padding: 6px 8px; }
  table.grid.list-edit tbody tr:hover td { background: none; }
  td.list-acts { white-space: nowrap; text-align: right; }
  td.list-acts .inline-form { display: inline-block; }
  .cell-main { font-weight: 600; }
  .cell-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
  td.due { color: var(--danger); font-weight: 700; }
  th.actions-col { width: 250px; }
  td.actions { white-space: nowrap; }
  td.actions .btn, td.actions form { vertical-align: middle; }
  /* Patients list: the text button is one width on every row and a pending
     row keeps an empty slot for Amend results, so the icons form columns
     (2026-10-24). */
  table.visit-rows td.actions > .btn.secondary:first-child { min-width: 142px; justify-content: center; }
  td.actions .icon-slot { display: inline-block; visibility: hidden; height: 1px; }
  .empty-row { padding: var(--s-8); text-align: center; color: var(--muted); font-size: 13.5px; }
  /* Phone-only detail lines; the responsive layer shows them. This rule sat
     in `utilities`, which outranks `responsive`, so they never showed
     anywhere (2026-10-24). */
  .m-only { display: none; }

  .table-wrap {
    overflow-x: auto; -webkit-overflow-scrolling: touch;
    background: var(--surface); border: 1px solid var(--surface-bd); border-radius: var(--r-lg);
    box-shadow: var(--sh-card);
  }
  /* A section heading after a table box sat flush against it (Day Summary,
     Day Book; 2026-10-24). */
  .table-wrap + h2, .table-wrap + h3 { margin-top: var(--s-5); }
  table.vlist th {
    position: sticky; top: 0; z-index: 1;
    background: var(--th-bg); padding: 9px 12px;
  }
  table.vlist td { padding: 9px 12px; vertical-align: middle; }
  table.vlist tbody tr:last-child td { border-bottom: 0; }
  /* Tests list: the name is what is read, the method only a reminder. */
  table.vlist th.col-test { min-width: 190px; }
  table.vlist td.col-method { font-size: 12px; }
  .bp-wrap { max-height: 70vh; overflow: auto; }
  .list-meta {
    display: flex; justify-content: space-between; gap: var(--s-3); flex-wrap: wrap;
    font-size: 13px; color: var(--ink-soft); margin: 0 2px var(--s-2);
  }

  /* Abnormal results carry a glyph as well as colour (never colour alone). */
  td.flag-low, td.flag-high { font-weight: 700; }
  td.flag-low { color: var(--flag-low); }
  td.flag-high { color: var(--flag-high); }
  td.flag-low::after { content: " \2193"; font-weight: 700; }
  td.flag-high::after { content: " \2191"; font-weight: 700; }
  input.flag-low { color: var(--flag-low); font-weight: 700; border-color: var(--b-300); }
  input.flag-high { color: var(--flag-high); font-weight: 700; border-color: var(--danger-bd); }
  /* Panic values (04 §3): the doctor is called about these, so they are louder
     than an ordinary abnormal — filled, not just coloured — and they keep a
     glyph of their own for anyone who cannot separate the two reds. */
  td.flag-critical_low, td.flag-critical_high {
    color: var(--flag-crit); font-weight: 700;
  }
  td.flag-critical_low::after, td.flag-critical_high::after {
    content: " !!"; font-weight: 700;
  }
  input.flag-critical_low, input.flag-critical_high {
    color: var(--flag-crit); font-weight: 700;
    border-color: var(--flag-crit); background: var(--danger-soft);
  }

  /* ── status pills ─────────────────────────────────────────────────────── */
  .pill {
    display: inline-block; padding: 2px 9px; border-radius: var(--r-full);
    font-size: 11px; font-weight: 700; letter-spacing: .01em; white-space: nowrap;
  }
  /* A hairline ring in the pill's own colour gives the soft-3D edge. */
  .pill.pending   { background: var(--status-pending-soft); color: var(--status-pending); box-shadow: inset 0 0 0 1px var(--status-pending-bd); }
  .pill.complete  { background: var(--status-complete-soft); color: var(--status-complete); box-shadow: inset 0 0 0 1px var(--status-complete-bd); }
  .pill.to_verify { background: var(--status-verify-soft); color: var(--status-verify); box-shadow: inset 0 0 0 1px var(--status-verify-bd); }
  /* "needs attention" (inactive, needs review, incomplete) — amber, never the
     blue of the Pending status, so a warning still reads as one. */
  .pill.warn      { background: var(--accent-soft); color: var(--accent-ink); box-shadow: inset 0 0 0 1px var(--accent-bd); }
  .pill.collected { background: var(--info-soft); color: var(--info); }
  .pill.failed    { background: var(--danger-soft); color: var(--danger); }
  .pill.running   { background: var(--b-100); color: var(--b-800); }
  .pill.role      { background: var(--b-50); color: var(--b-800); }
  .pill.off       { background: var(--line-soft); color: var(--muted); }

  /* ── reference-interval coverage (Masters → Test) ──────────────────────
     A missing interval is stated in words as well as colour: nobody signs
     an interval they cannot see is absent, and colour alone is not a
     message. `a.pill` is the list's link into the editor. */
  .list-meta .meta-note { color: var(--muted); }
  a.pill { text-decoration: none; }
  a.pill:hover { filter: brightness(.96); }
  .cov { display: flex; flex-wrap: wrap; gap: 3px; margin-bottom: 3px; }
  .cov-chip {
    display: inline-block; padding: 1px 6px; border-radius: var(--r-xs);
    font-size: 10.5px; font-weight: 700; white-space: nowrap;
  }
  .cov-chip.ok  { background: var(--success-soft); color: var(--success); }
  .cov-chip.gap { background: var(--danger-soft); color: var(--danger); }
  .cov-sexed {
    display: inline-block; padding: 1px 6px; border-radius: var(--r-xs);
    font-size: 10.5px; font-weight: 600; color: var(--muted);
    background: var(--line-soft); white-space: nowrap;
  }
  .band-adds { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; margin-top: var(--s-3); }
  .band-adds-sep {
    font-size: 10.5px; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; color: var(--muted); margin-left: var(--s-2);
  }

  /* Small circle with a ring; a segmented tab recolours the ring. */
  .count {
    display: inline-block; min-width: 20px; padding: 0 5px; border-radius: var(--r-full);
    text-align: center; font-size: 11px; font-weight: 700; line-height: 20px;
    background: var(--n-0); color: var(--ink-soft);
    box-shadow: 0 0 0 1.5px var(--line-strong), var(--sh-count);
  }

  /* ── chips + segmented control ────────────────────────────────────────── */
  .chip {
    display: inline-flex; align-items: center; gap: var(--s-2);
    padding: 7px 13px; min-height: var(--ctl-h); border-radius: var(--r-full);
    border: 1px solid var(--btn-neutral-bd); background: var(--btn-neutral-bg); box-shadow: var(--sh-raise);
    color: var(--ink-soft); font-weight: 600; font-size: 13.5px; text-decoration: none;
  }
  .chip:hover { border-color: var(--n-400); background: var(--btn-neutral-bg-hover); }
  .chip.active { background: var(--btn-bg); border-color: var(--btn-bd); color: #fff; box-shadow: var(--sh-btn); }
  .chip.sm { padding: 4px 11px; min-height: var(--ctl-h-sm); font-size: 12.5px; }
  .chip.active.sm { background: var(--btn-soft-bg); border-color: var(--btn-soft-bd); color: var(--b-800); box-shadow: var(--sh-raise); }
  .chip-badge {
    font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    padding: 2px 7px; border-radius: var(--r-full);
    background: var(--success-soft); color: var(--success);
  }
  .chip.active .chip-badge { background: rgba(255,255,255,.22); color: #fff; }
  .chip.removable span { margin-left: var(--s-1); font-size: 15px; line-height: 1; color: var(--muted); }
  .chip.removable:hover { border-color: var(--danger); color: var(--danger); }
  .chip.removable:hover span { color: var(--danger); }

  /* max-width + overflow: a third tab scrolls inside the control on a narrow
     card rather than wrapping or widening the page. */
  .segmented {
    display: inline-flex; gap: 3px; padding: 3px; border-radius: var(--r-lg);
    background: var(--well); box-shadow: var(--sh-well);
    max-width: 100%; overflow-x: auto; scrollbar-width: none;
  }
  /* The Logs switcher (Activity / Report links / Outbox) sits above the
     page heading; give the heading room instead of touching it. */
  .segmented + .page-head { margin-top: var(--s-4); }
  .segmented a {
    display: inline-flex; align-items: center; gap: var(--s-2); white-space: nowrap; flex-shrink: 0;
    padding: 6px 10px 6px 14px; min-height: var(--ctl-h-sm); border-radius: var(--r);
    border: 1px solid transparent;
    font-weight: 500; font-size: 13.5px; color: var(--ink-soft); text-decoration: none;
  }
  .segmented a:hover { color: var(--ink); background: var(--n-0); }
  .segmented a:focus-visible { outline: 2px solid var(--b-600); outline-offset: -2px; }
  .segmented a.active {
    background: var(--btn-neutral-bg); color: var(--b-800); font-weight: 650;
    border-color: var(--btn-neutral-bd); box-shadow: var(--sh-raise);
  }
  .segmented a.active .count { color: var(--b-800); box-shadow: 0 0 0 1.5px var(--b-300), var(--sh-count); }
  /* Work-list status tabs: each segment carries its own tint (pending blue,
     to verify violet, complete green); the selected one deepens the tint,
     gains a border and a stronger ring and is raised, so "selected" never
     rests on hue alone. */
  .segmented a[data-status="pending"]   { --seg-ink: var(--status-pending);  --seg-faint: var(--status-pending-faint);  --seg-soft: var(--status-pending-soft);  --seg-bd: var(--status-pending-bd); }
  .segmented a[data-status="to_verify"] { --seg-ink: var(--status-verify);   --seg-faint: var(--status-verify-faint);   --seg-soft: var(--status-verify-soft);   --seg-bd: var(--status-verify-bd); }
  .segmented a[data-status="complete"]  { --seg-ink: var(--status-complete); --seg-faint: var(--status-complete-faint); --seg-soft: var(--status-complete-soft); --seg-bd: var(--status-complete-bd); }
  .segmented a[data-status] { background: var(--seg-faint); color: var(--seg-ink); }
  .segmented a[data-status] .count { color: var(--seg-ink); box-shadow: 0 0 0 1.5px var(--seg-bd), var(--sh-count); }
  .segmented a[data-status]:hover { background: var(--seg-soft); color: var(--seg-ink); }
  .segmented a.active[data-status] {
    background: var(--seg-soft); border-color: var(--seg-bd); color: var(--seg-ink);
    font-weight: 700; box-shadow: var(--sh-raise);
  }
  .segmented a.active[data-status] .count { box-shadow: 0 0 0 2px var(--seg-ink), var(--sh-count); }
  /* A strip wider than its box scrolls sideways; its clipped end fades out so
     the hidden tabs are visibly there (base.html sets .more-left/.more-right
     from the scroll position, 2026-10-24). */
  :is(.segmented, .quick-ranges, .subnav).more-right {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 32px), transparent);
  }
  :is(.segmented, .quick-ranges, .subnav).more-left {
    -webkit-mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent);
            mask-image: linear-gradient(to left, #000 calc(100% - 32px), transparent);
  }
  :is(.segmented, .quick-ranges, .subnav).more-left.more-right {
    -webkit-mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent);
            mask-image: linear-gradient(to right, transparent, #000 32px, #000 calc(100% - 32px), transparent);
  }

  /* ── choice cards (.seg + .seg-opt) ───────────────────────────────────────
     Used by Settings -> Workspace. The radio stays VISIBLE: the previous
     stylesheet had a later ".seg label input { display:none }" rule that hid
     it and fused the two cards together. */
  .seg {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: var(--s-3); margin-bottom: var(--s-4);
  }
  .seg-opt {
    display: flex; gap: var(--s-3); align-items: flex-start;
    padding: var(--s-3) var(--s-4);
    border: 1.5px solid var(--line); border-radius: var(--r-lg); background: var(--n-0);
    font-size: 13.5px; font-weight: 500; color: var(--ink);
    text-transform: none; cursor: pointer; margin: 0;
  }
  .seg-opt:hover { border-color: var(--b-300); background: var(--n-25); }
  .seg-opt input { flex-shrink: 0; margin-top: 2px; }
  .seg-opt b { display: block; font-weight: 650; margin-bottom: 2px; }
  .seg-opt small { display: block; color: var(--muted); font-size: 12px; line-height: 1.45; }
  .seg-opt:has(input:checked) { border-color: var(--b-600); background: var(--b-50); box-shadow: var(--ring); }
  .seg-opt:has(input:focus-visible) { outline: 2px solid var(--b-600); outline-offset: 2px; }

  /* ── Patient Entry header grid (.prow) ────────────────────────────────────
     `.row` is a wrapping flex line with `align-items: flex-end` and
     `flex-shrink: 0`, which is right for a filter bar and wrong for a form
     of ten controls: a field carrying a `.hint` is taller, so bottom-aligning
     it lifted its label and input 22 px above every other control, and the
     line broke wherever it overflowed — between Age and Age Type at ~1330 px,
     i.e. one value split across two lines. Explicit tracks, `align-items:
     start` so every control shares a baseline and a hint hangs below instead
     of shoving its own field upward. Track widths are the content's: "Years",
     "Female" and a 10-digit number all fit without wrapping. */
  .prow {
    display: grid;
    gap: var(--s-2) var(--s-4);
    align-items: start;
  }
  .prow + .prow { margin-top: var(--s-4); }
  /* The tracks decide the width here, so the `.sm`/`.md`/`.lg` flex
     modifiers are not used inside a `.prow` — and would fight it if they were. */
  .prow .field { min-width: 0; max-width: none; }
  /* Named areas, not auto-placement: the field order is stated once per
     breakpoint and a field can never drift away from the field it belongs
     with. Every `f-*` area is claimed at every tier — an unplaced item would
     land in an implicit row of its own. */
  .f-date { grid-area: date; }
  .f-labno { grid-area: labno; }
  .f-title { grid-area: title; }
  .f-name { grid-area: name; }
  .f-mobile { grid-area: mobile; }
  .f-age { grid-area: age; }
  .f-agetype { grid-area: agetype; }
  .f-gender { grid-area: gender; }
  .f-dob { grid-area: dob; }
  .f-preg { grid-area: preg; }
  .prow-id {
    /* Title is 112 px: "Master", "Baby of", "Kumari", "Selvi" plus the select
       arrow — at 88 px they read "Maste" (2026-10-24). The name track stops at
       440 px: as `1fr` it ran to ~880 px on a wide screen (user report,
       2026-10-24), and the spare width now stays empty at the row's end. */
    grid-template-columns: 150px 92px 112px minmax(220px, 440px) 160px;
    grid-template-areas: "date labno title name mobile";
  }
  /* Same cap where the name has a line of its own (≤1140, phones). */
  .prow-id .f-name { max-width: 440px; }
  /* Date of birth: a DD-MM-YYYY text box with a calendar button inside its
     right edge; the native picker it opens sits invisibly under the button
     so the calendar drops from the right place. */
  .dob-box { position: relative; }
  .dob-box input[type="text"] { width: 100%; padding-right: 40px; }
  .dob-cal, .dob-cal:hover {
    position: absolute; top: 0; right: 0; bottom: 0; width: 38px;
    display: inline-flex; align-items: center; justify-content: center;
    background: none; border: 0; box-shadow: none; padding: 0;
    color: var(--muted); cursor: pointer;
  }
  .dob-cal:hover, .dob-cal:focus-visible { color: var(--ink); }
  .dob-pick {
    position: absolute; right: 0; bottom: 0; width: 38px; height: 100%;
    opacity: 0; pointer-events: none; border: 0; padding: 0;
  }
  .prow-demo {
    grid-template-columns: 96px 108px 112px 170px 180px 1fr;
    grid-template-areas: "age agetype gender dob preg .";
  }
  /* A select with its "New" button beside it (the Doctor field). */
  .field-with-btn { display: flex; gap: var(--s-2); align-items: stretch; }
  .field-with-btn > select { flex: 1 1 auto; min-width: 0; }
  /* No field in a `.prow` carries a `.hint` any more — the missing-mobile
     warning was removed at the user's request (2026-09-14), and it was the
     reason this row needed a reserved strip at all. If one ever returns, it
     must not be an `x-show`: as one it appeared and vanished on the first and
     last keystroke and the whole card jumped 22 px each time. Reserve its
     line and toggle visibility. */
  /* Lab No. is assigned by the FY sequence at save, never typed. */
  .labno-auto {
    background: var(--n-100); font-weight: 700; text-align: center;
    color: var(--ink-soft); cursor: default;
  }

  /* ── case-type strip (.case-strip + .case-tile) ───────────────────────────
     Patient Entry's modality row, ported from the LIS spec's billing tiles.
     Radios, not JS, so it posts with scripting off; the native control is
     hidden here (unlike `.seg-opt`) because the whole tile is the target and
     a 13-tile row cannot carry 13 dots. Focus is therefore drawn on the
     tile — never remove that rule, or the strip becomes keyboard-invisible. */
  .case-strip {
    display: flex; flex-wrap: wrap; gap: var(--s-2);
    margin: var(--s-4) 0 0; padding: 0; border: 0;
  }
  .case-strip legend {
    padding: 0; margin-bottom: var(--s-2);
    font-size: 11.5px; font-weight: 600; letter-spacing: .04em;
    text-transform: uppercase; color: var(--muted);
  }
  .case-tile {
    display: flex; flex-direction: column; gap: 1px; margin: 0;
    min-width: 92px; padding: 6px var(--s-3);
    border: 1px solid var(--line-strong); border-radius: var(--r-sm);
    background: var(--n-0); cursor: pointer; text-transform: none;
  }
  .case-tile:hover { border-color: var(--b-300); background: var(--b-50); }
  .case-tile input { position: absolute; opacity: 0; width: 0; height: 0; }
  /* A long name ("MAMMOGRAPHY") wraps inside its tile instead of running
     past the border on a phone (2026-10-24). */
  .case-tile .ct-name { font-size: 12.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
  .case-tile .ct-code { font-size: 10.5px; font-weight: 400; color: var(--ink-faint); }
  .case-tile:has(input:checked) {
    border-color: var(--b-700); background: var(--b-700);
  }
  .case-tile:has(input:checked) .ct-name { color: var(--n-0); }
  .case-tile:has(input:checked) .ct-code { color: var(--b-200); }
  .case-tile:has(input:focus-visible) { outline: 2px solid var(--b-600); outline-offset: 2px; }

  /* ── feedback ─────────────────────────────────────────────────────────── */
  /* An input holding a value the form will refuse (today: a package split
     that does not total its package price). Marks the field while the user
     is still typing, so the error is seen before Save is pressed. */
  input.field-err, input.field-err:focus {
    border-color: var(--danger-bd);
    background: var(--danger-soft);
    color: var(--danger);
  }
  .flash {
    padding: 10px 14px; border-radius: var(--r-md); margin-bottom: var(--s-4);
    font-weight: 600; font-size: 13.5px;
  }
  .flash.ok { background: var(--success-soft); color: var(--success); }
  .flash.err { background: var(--danger-soft); color: var(--danger); }
  /* A blocker with something to do about it (2026-10-25, result entry): the
     message and its icon on the left, the action as a real button on the
     right — an underlined link or a bare checkbox inside the sentence looked
     unfinished. Wraps under the message on a narrow screen. */
  .flash.with-action {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--s-3); flex-wrap: wrap; border: 1px solid var(--danger-bd);
  }
  .flash .flash-msg { display: flex; align-items: center; gap: var(--s-2); min-width: 0; }
  .flash .flash-msg > svg { flex-shrink: 0; width: 18px; height: 18px; }
  .flash .flash-actions { display: flex; gap: var(--s-2); flex-shrink: 0; }
  /* A confirmation the form posts (a checkbox), dressed as a toggle button:
     outlined until ticked, filled once it is, so its state reads at a glance. */
  .flash-confirm {
    display: inline-flex; align-items: center; gap: 8px; cursor: pointer;
    min-height: var(--ctl-h-sm); padding: 0 12px; border-radius: var(--r-md);
    border: 1px solid var(--danger); background: var(--n-0); color: var(--danger);
    font-weight: 700; font-size: 13px; user-select: none;
  }
  .flash-confirm input { margin: 0; accent-color: var(--danger); }
  .flash-confirm:has(input:checked) { background: var(--danger); color: var(--n-0); }
  .flash-confirm:has(input:focus-visible) { outline: 2px solid var(--danger); outline-offset: 2px; }

  /* Bottom-right, clear of every page head (2026-10-24): top-right, a toast
     sat on "New Patient", the receipt's WhatsApp button and the Paid box. */
  #toasts {
    position: fixed; bottom: var(--s-4); right: var(--s-4); z-index: var(--z-toast);
    display: flex; flex-direction: column; align-items: flex-end; gap: var(--s-2);
    width: min(380px, calc(100vw - 32px)); pointer-events: none;
  }
  .toast {
    display: flex; align-items: flex-start; gap: var(--s-3); width: 100%;
    padding: 12px 13px; background: var(--panel);
    border: 1px solid var(--line); border-radius: var(--r-md);
    box-shadow: var(--sh-4); font-weight: 600; font-size: 13.5px;
    pointer-events: auto; animation: toast-in var(--dur-2) var(--ease-out);
  }
  .toast svg { width: 18px; height: 18px; flex-shrink: 0; margin-top: 1px; }
  .toast span { flex: 1; min-width: 0; overflow-wrap: break-word; }
  .toast.ok { border-left: 3px solid var(--success); }
  .toast.ok svg { color: var(--success); }
  .toast.err { border-left: 3px solid var(--danger); }
  .toast.err svg { color: var(--danger); }
  .toast.warn { border-left: 3px solid var(--accent-ink); }
  .toast.warn svg { color: var(--accent-ink); }
  .toast .toast-x {
    border: 0; background: none; color: var(--muted); font-size: 17px; line-height: 1;
    padding: 0 2px; cursor: pointer; flex-shrink: 0; min-height: 0; box-shadow: none;
  }
  .toast .toast-x:hover { color: var(--ink); background: none; }
  .toast.out { opacity: 0; transform: translateY(4px); transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease); }
  @keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }

  /* ── modal + confirm ──────────────────────────────────────────────────── */
  .modal {
    display: none; position: fixed; inset: 0; z-index: var(--z-modal);
    background: rgba(15,23,42,.45); align-items: center; justify-content: center;
    padding: var(--s-4);
  }
  .modal.open { display: flex; }
  .modal-box {
    background: var(--n-0); border-radius: var(--r-xl); padding: var(--s-5) var(--s-6);
    min-width: 320px; max-width: 90vw; max-height: 85vh; overflow: auto;
    box-shadow: var(--sh-pop);
  }
  .modal-box.wide { min-width: min(460px, 92vw); }
  .modal.open .modal-box { animation: modal-pop var(--dur-2) var(--ease-out); }
  @keyframes modal-pop { from { opacity: 0; transform: translateY(8px) scale(.97); } }

  .confirm-box { max-width: 400px; min-width: min(340px, 92vw); text-align: center; }
  .confirm-ico {
    width: 44px; height: 44px; margin: 2px auto var(--s-3); border-radius: 50%;
    background: var(--danger-soft); color: var(--danger);
    display: flex; align-items: center; justify-content: center;
  }
  .confirm-ico svg { width: 22px; height: 22px; }
  /* The warning mark is for destructive confirms only (base.html toggles
     `hidden`); `display: flex` above would otherwise beat the attribute. */
  .confirm-ico[hidden] { display: none; }
  #confirm-text { margin: 0 0 var(--s-5); white-space: pre-line; font-size: 14.5px; }
  .confirm-actions { display: flex; gap: var(--s-3); justify-content: center; }
  .confirm-actions .btn { min-width: 104px; }

  /* Calculator (F11). */
  .calc-box { min-width: min(340px, 92vw); }
  .calc-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); margin-bottom: var(--s-3); }
  .calc-head h3 { margin: 0; }
  .calc-row { display: flex; gap: var(--s-2); }
  .calc-row input { flex: 1; min-width: 0; font-size: 16px; font-variant-numeric: tabular-nums; }
  .calc-row button { min-width: 52px; }
  .calc-err { margin: var(--s-2) 0 0; color: var(--danger); font-size: 13px; }

  /* ── empty states ─────────────────────────────────────────────────────── */
  .empty-state {
    text-align: center; padding: var(--s-10) var(--s-5);
    background: var(--empty-bg); border: 1px dashed var(--line-strong); border-radius: var(--r-lg);
  }
  .empty-ico {
    width: 56px; height: 56px; margin: 0 auto var(--s-4); border-radius: var(--r-lg);
    display: grid; place-items: center; background: var(--nav-active); color: var(--b-700);
    box-shadow: var(--sh-raise);
  }
  .empty-ico svg { width: 28px; height: 28px; }
  .empty-state h2 { font-size: 17px; font-weight: 700; margin-bottom: var(--s-2); color: var(--n-900); }
  .empty-state p { margin: 0 auto; max-width: 46ch; color: var(--muted); }
  .empty-actions { display: flex; gap: var(--s-2); justify-content: center; flex-wrap: wrap; margin-top: var(--s-5); }

  /* ── loading ──────────────────────────────────────────────────────────── */
  #progress {
    position: fixed; top: 0; left: 0; height: 3px; width: 0; z-index: var(--z-toast);
    background: linear-gradient(90deg, var(--b-700), var(--b-400));
    border-radius: 0 3px 3px 0; opacity: 0;
    transition: width var(--dur-2) var(--ease); box-shadow: 0 0 8px rgba(37,99,235,.5);
  }
  #progress.run { opacity: 1; }
  #progress.done { transition: width .2s var(--ease), opacity .32s var(--ease) .12s; opacity: 0; }

  .skeleton {
    position: relative; overflow: hidden; background: var(--n-200);
    border-radius: var(--r); min-height: 14px; color: transparent !important;
  }
  .skeleton::after {
    content: ""; position: absolute; inset: 0; transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.7), transparent);
    animation: skeleton-shimmer 1.2s infinite;
  }
  @keyframes skeleton-shimmer { 100% { transform: translateX(100%); } }

  /* Page skeleton (2026-10-25): over the content area only — the sidebar
     stays usable-looking — while the next page loads. Shown by base.html's
     loading script after 150 ms; mirrors .main's padding so the blocks sit
     where a page head, a card and a table will appear. */
  .page-skel {
    position: fixed; top: 0; right: 0; bottom: 0; left: 240px; z-index: 90;
    padding: var(--s-6) var(--s-8); overflow: hidden;
    background: var(--page-wash);
  }
  .page-skel[hidden] { display: none; }
  .page-skel .ps-title { width: 240px; height: 26px; }
  .page-skel .ps-sub { width: 180px; height: 14px; margin: var(--s-2) 0 var(--s-6); }
  .page-skel .ps-card {
    background: var(--n-0); border: 1px solid var(--line-soft); border-radius: var(--r-lg);
    padding: var(--s-5); margin-bottom: var(--s-4); max-width: 1400px;
  }
  .page-skel .ps-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-4); }
  .page-skel .ps-field { height: 38px; }
  .page-skel .ps-row { height: 18px; margin-bottom: var(--s-3); }
  .page-skel .ps-head { height: 22px; margin-bottom: var(--s-4); }

  .is-loading { position: relative; opacity: .55; pointer-events: none; }
  .is-loading::after {
    content: ""; position: absolute; top: 50%; left: 50%; width: 22px; height: 22px;
    margin: -11px 0 0 -11px; border: 3px solid var(--line); border-top-color: var(--b-700);
    border-radius: 50%; animation: spin .7s linear infinite;
  }
  @keyframes spin { to { transform: rotate(360deg); } }

  /* ── autocomplete ─────────────────────────────────────────────────────── */
  .autocomplete { position: relative; }
  .autocomplete .ac-list {
    position: absolute; top: 100%; left: 0; right: 0; z-index: 30;
    background: var(--n-0); border: 1px solid var(--line); border-radius: 0 0 var(--r-md) var(--r-md);
    max-height: 240px; overflow: auto; box-shadow: var(--sh-4);
  }
  .autocomplete .ac-list div { padding: 8px 12px; cursor: pointer; }
  .autocomplete .ac-list div:hover { background: var(--b-50); }
  .autocomplete .ac-list:empty { display: none; }
  .autocomplete .ac-list.is-loading:empty { display: block; min-height: 46px; }
  /* A list hanging off a narrow box (the mobile lookup, 2026-10-23): as wide
     as a name line needs, anchored to the box's right edge because that box
     ends the row — at the box's own width every name wrapped to 3 lines. */
  .autocomplete .ac-list.ac-wide { left: auto; width: max(100%, min(26rem, 90vw)); }
  .autocomplete .ac-list .ac-new { color: var(--b-700); border-top: 1px solid var(--line); }
  .autocomplete .ac-list .ac-price { float: right; font-weight: 700; white-space: nowrap; }
  .autocomplete .ac-list .empty-row { padding: var(--s-3); cursor: default; }
  .autocomplete .ac-list .empty-row:hover { background: none; }

  /* Patient Entry: "Name changed — this saves as a new patient. Keep LAB/10"
     under the identity row (2026-10-24). */
  .link-note {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3);
    margin: var(--s-2) 0 0; padding: 6px 10px; border-radius: var(--r-md);
    background: var(--accent-soft); border: 1px solid var(--accent-bd); color: var(--accent-ink); font-size: 13px;
  }
  /* Search, package and the count: the count sits at the end of the row. */
  .tests-pick .test-count { align-self: center; white-space: nowrap; }
  .tests-pick .test-count b { font-size: 16px; }

  .ac-menu {
    background: var(--n-0); border: 1px solid var(--line); border-radius: var(--r-md);
    max-height: 300px; overflow-y: auto; box-shadow: var(--sh-4);
  }
  .ac-menu .ac-row {
    display: flex; align-items: center; gap: var(--s-3); margin: 0;
    padding: 9px 12px; border-bottom: 1px solid var(--line-soft);
  }
  .ac-menu .ac-row:last-child { border-bottom: 0; }
  .ac-menu .ac-row:hover { background: var(--b-50); }
  .ac-menu .ac-price { margin-left: auto; text-align: right; font-weight: 700; white-space: nowrap; }
  .ac-results .ac-skel { display: none; }
  .ac-results .ac-skel .skeleton { height: 38px; margin-bottom: var(--s-2); }
  .ac-results.is-loading .ac-hint { display: none; }
  .ac-results.is-loading .ac-skel { display: block; }
  .ac-results.is-loading:has(.ac-skel) { opacity: 1; pointer-events: auto; }
  .ac-results.is-loading:has(.ac-skel)::after { content: none; }

  /* ── tiles ────────────────────────────────────────────────────────────── */
  .tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--s-3); }
  .tile {
    background: var(--surface); border: 1px solid var(--surface-bd); border-radius: var(--r-lg);
    padding: var(--s-4); text-decoration: none; color: var(--ink); box-shadow: var(--sh-card);
  }
  .tile:hover { border-color: var(--b-300); box-shadow: var(--sh-card), 0 0 0 1px var(--b-200); }
  .tile .big { font-size: 26px; font-weight: 700; letter-spacing: -.02em; line-height: 1.1; }
  .tile .lbl { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
}

@layer screens {
  /* Settings → General: the account row carries today's date at its end. */
  .gen-account { margin: var(--s-2) 0 0; max-width: 560px; }
  .gen-date { flex: none; font-size: 12.5px; color: var(--muted); font-variant-numeric: tabular-nums; }

  /* ── themes (2026-10-25) ──────────────────────────────────────────────── */
  /* Sidebar footer: a disclosure listing every theme, picked with a click. */
  .theme-menu { margin-bottom: var(--s-1); }
  .theme-menu > summary {
    display: flex; align-items: center; gap: var(--s-3); cursor: pointer;
    padding: 8px var(--s-3); border-radius: var(--r-md); color: var(--ink-soft);
    font-size: 13.5px; list-style: none;
  }
  .theme-menu > summary::-webkit-details-marker { display: none; }
  .theme-menu > summary:hover { background: var(--n-100); color: var(--ink); }
  .theme-menu > summary svg { width: 18px; height: 18px; flex-shrink: 0; }
  .theme-list {
    display: grid; gap: 2px; margin: var(--s-1) 0 var(--s-2); padding: var(--s-2);
    background: var(--n-0); border: 1px solid var(--line-soft); border-radius: var(--r-lg);
    box-shadow: var(--sh-raise); max-height: 300px; overflow-y: auto;
  }
  .theme-opt {
    display: flex; align-items: center; gap: var(--s-2); cursor: pointer;
    padding: 6px var(--s-2); border-radius: var(--r); font-size: 13px; color: var(--ink);
  }
  .theme-opt:hover { background: var(--n-100); }
  .theme-opt input { position: absolute; opacity: 0; pointer-events: none; }
  .theme-opt:has(input:checked) { background: var(--b-50); color: var(--b-800); }
  .theme-opt:has(input:focus-visible) { box-shadow: var(--ring); }
  /* Two-tone dot: main colour with the accent as its lower-right quarter. */
  .theme-sw {
    display: inline-block;
    width: 18px; height: 18px; flex-shrink: 0; border-radius: 50%;
    background: conic-gradient(var(--sw-b) 90deg, var(--sw-a) 90deg);
    box-shadow: inset 0 0 0 1px rgba(15,23,42,.12);
  }
  .theme-sw-lab { background: var(--n-0); box-shadow: inset 0 0 0 2px var(--line-strong); }

  /* Settings → General: one tile per theme, a tiny preview of the shell. */
  .theme-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: var(--s-3); margin-top: var(--s-2);
  }
  .theme-tile {
    display: grid; gap: var(--s-2); cursor: pointer; padding: var(--s-2);
    border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--n-0);
  }
  .theme-tile input { position: absolute; opacity: 0; pointer-events: none; }
  .theme-tile:has(input:checked) { border-color: var(--b-700); box-shadow: 0 0 0 2px var(--b-200); }
  .theme-tile:has(input:focus-visible) { box-shadow: var(--ring); }
  .theme-prev {
    position: relative; display: block; height: 56px; overflow: hidden;
    border-radius: var(--r); background: var(--n-100);
  }
  .theme-prev .tp-bar { position: absolute; inset: 0 auto 0 0; width: 30%; background: var(--n-0); border-right: 1px solid var(--line); }
  .theme-prev .tp-btn { position: absolute; left: 40%; top: 14px; width: 44%; height: 14px; border-radius: 4px; background: var(--sw-a); }
  .theme-prev .tp-dot { position: absolute; left: 40%; top: 34px; width: 22%; height: 8px; border-radius: 4px; background: var(--sw-b); }
  .theme-name { font-size: 13px; color: var(--ink); }
  .theme-chip { display: inline-block; width: 14px; height: 14px; border-radius: 4px; vertical-align: -2px; background: var(--sw-a); box-shadow: inset 0 0 0 1px rgba(15,23,42,.12); }
  .theme-opt, .theme-tile { position: relative; }
  /* ══ sign-in ═══════════════════════════════════════════════════════════
     One narrow white card centred on a cool grey page: icon-labelled rows
     and one green button, nothing else (user request 2026-10-25 removed the
     mark, the lab name and the visible heading). Ported from the LIS spec's
     `(auth)/login` screen (user request 2026-09-14).

     The port keeps the source's geometry (360 px card, 36 px controls, 15 px
     label icons) and takes its colour from OUR token ramp rather than the
     source's `ink-*` scale, so the one screen a lab sees before the app does
     not drift away from the app. The green is the exception: it is the
     source's own colour and is declared in the tokens layer beside
     `--auth-bg`, sign-in only. `.auth-lab` / `.auth-title` below still dress
     the provider console's sign-in and the patient report pages. */
  .auth-body { min-height: 100%; background: var(--auth-bg); }
  .auth {
    min-height: 100vh; display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    width: 100%; padding: var(--s-10) var(--s-4);
  }
  .auth-card {
    width: 100%; max-width: 360px;
    background: var(--n-0); border: 1px solid var(--line);
    border-radius: var(--r-sm); padding: var(--s-8) 28px;
    box-shadow: 0 1px 2px rgb(15 23 42 / 6%);
  }
  .auth-lab {
    margin: 0 0 var(--s-1); text-align: center; font-size: 12px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase; color: var(--muted);
  }
  .auth-title {
    margin: 0 0 var(--s-6); text-align: center;
    font-family: var(--font-ui); font-weight: 700; font-size: 19px; line-height: 1.25;
    letter-spacing: -.01em; color: var(--n-900);
  }
  .auth-card .flash { margin-bottom: var(--s-4); }
  /* One column: label, field, error — the source's stacked rows. */
  .auth-form { display: block; }
  .auth-form label {
    display: flex; align-items: center; gap: 6px; margin: 0 0 var(--s-1);
    font-size: 13.5px; font-weight: 500; color: var(--n-900);
    text-transform: none; letter-spacing: 0;
  }
  .auth-form label + input, .auth-form label + .auth-pass { margin-bottom: 0; }
  .auth-ico { flex: none; color: var(--n-900); }
  .auth-form input {
    width: 100%; height: 36px; min-height: 36px; padding: 0 var(--s-2); font-size: 14px;
    border-radius: var(--r-xs); border: 1px solid var(--line-strong); background: var(--n-0);
    color: var(--ink);
  }
  .auth-form input:focus { border-color: var(--b-600); outline: none; }
  .auth-form input.bad { border-color: var(--danger); background: var(--danger-soft); }
  .auth-form .auth-pass + p + label, .auth-form input + p + label { margin-top: var(--s-4); }
  .auth-pass { position: relative; }
  .auth-pass input { padding-right: 40px; }
  /* Edge/IE draw their own reveal eye inside a password box, so a box that
     carries .auth-eye showed two. Hide the browser's only where ours exists. */
  .auth-pass input::-ms-reveal, .auth-pass input::-ms-clear { display: none; }
  .auth-eye, .auth-eye:hover {
    position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
    background: none; border: 0; box-shadow: none; padding: 4px; min-height: 0;
    color: var(--muted); cursor: pointer; display: inline-flex;
  }
  .auth-eye:hover { color: var(--ink); }
  .auth-eye .off { display: none; }
  .auth-eye.on .off { display: inline; }
  /* Field-level message: 12px, under its own box, never colour alone. */
  .auth-err { margin: var(--s-1) 0 0; font-size: 12px; color: var(--danger); }
  .auth-err[hidden] { display: none; }
  /* Form-level message: the generic "wrong credentials" sentence, which has
     no field to sit under. */
  .auth-formerr {
    margin: var(--s-4) 0 0; padding: var(--s-2) var(--s-3);
    border-radius: var(--r-xs); background: var(--danger-soft);
    font-size: 12.5px; color: var(--danger);
  }
  .auth-formerr[hidden] { display: none; }
  .auth-submit, .auth-submit:hover {
    display: inline-flex; align-items: center; justify-content: center;
    /* Full width on every screen (2026-10-24): it already was on a phone, and
       a short button under full-width fields read as secondary. */
    margin-top: var(--s-4); width: 100%; min-width: 96px;
    height: 36px; min-height: 36px; padding: 0 var(--s-5);
    border-radius: var(--r-xs); font-size: 14px; font-weight: 600;
    background: var(--auth-accent); border-color: var(--auth-accent); color: var(--n-0);
  }
  .auth-submit:hover { background: var(--auth-accent-dk); border-color: var(--auth-accent-dk); }
  .auth-submit:disabled { cursor: not-allowed; opacity: .6; }
  .auth-help { margin: var(--s-6) 0 0; font-size: 13px; color: var(--muted); text-align: center; }

  /* ══ patient report page (/r/<code>) ═══════════════════════════════════
     The one screen a patient sees. Wider than the sign-in card because it
     holds the report itself; the PIN page above it keeps the 360 px card. */
  /* The PIN, locked, not-ready and dead-link pages: their lines sit centred
     under the centred heading (2026-10-24 — they were flush left). */
  .auth-hero { text-align: center; }
  .auth-hero .auth-sub { margin: 0 0 var(--s-3); line-height: 1.5; color: var(--ink-soft); }
  .report-ready { width: 100%; max-width: 860px; box-sizing: border-box; }
  .report-ready .auth-sub {
    margin: -8px 0 var(--s-4); text-align: center;
    font-size: 13.5px; font-weight: 600; color: var(--muted);
  }
  /* Shown only by a browser that will not render the report inline — an
     empty grey box with no way out is the failure this prevents. */
  .report-fallback {
    margin: 0; padding: var(--s-6); text-align: center;
    font-size: 14px; color: var(--muted);
  }
  .report-frame {
    display: block; width: 100%; height: min(72vh, 900px);
    margin-bottom: var(--s-4); box-sizing: border-box;
    border: 1px solid var(--line); border-radius: var(--r-sm);
    background: var(--n-0);
  }
  .report-actions {
    display: flex; flex-wrap: wrap; gap: var(--s-3); justify-content: center;
  }
  @media (max-width: 640px) {
    /* A phone frames a PDF badly — shorter preview, and the two actions
       stack full width so Download is never below the fold. Grid, not a
       100% flex-basis: that overflowed the padded column by its own gap. */
    .report-frame { height: 48vh; }
    .report-actions { display: grid; grid-template-columns: 1fr; }
    .report-actions .btn { justify-content: center; }
  }

  /* ══ dashboard ═════════════════════════════════════════════════════════ */
  /* One KPI strip: two hero counters + (in Full mode) the two money figures.
     auto-fit means 4-up on a desktop, 2-up on a laptop, 1-up on a phone —
     the old fixed 2-column grid left half of a 1440px row empty. */
  .dash-tiles {
    grid-template-columns: repeat(auto-fit, minmax(232px, 1fr));
    align-items: stretch; margin-bottom: var(--s-4);
  }
  .tile-kpi { display: flex; flex-direction: column; justify-content: flex-start; }
  .tile-kpi .big { font-size: 24px; }
  .tile-kpi .lbl { margin-top: var(--s-1); }
  /* Sits where the hero tiles carry their button, so the strip has one baseline. */
  .tile-go {
    display: inline-flex; align-items: center; gap: var(--s-1); margin-top: auto; padding-top: var(--s-3);
    font-size: 12.5px; font-weight: 600; color: var(--b-700);
  }
  .tile-go svg { width: 14px; height: 14px; transition: transform var(--dur-1) var(--ease); }
  .tile-kpi:hover .tile-go svg { transform: translateX(2px); }
  .tile-hero {
    position: relative; display: flex; flex-direction: column; align-items: flex-start;
    gap: var(--s-1); padding: var(--s-5);
  }
  .tile-ico {
    position: absolute; top: var(--s-4); right: var(--s-4);
    width: 40px; height: 40px; border-radius: var(--r-lg);
    display: grid; place-items: center; background: var(--b-50); color: var(--b-700);
  }
  .tile-ico svg { width: 20px; height: 20px; }
  .tile-ico.amber { background: var(--accent-soft); color: var(--accent-ink); }
  .tile-link { display: block; text-decoration: none; color: inherit; border-radius: var(--r); }
  .tile-link .big { display: block; font-size: 38px; line-height: 1.05; letter-spacing: -.03em; }
  .tile-link .lbl { display: block; font-size: 13px; margin-top: 2px; }
  .tile-note { font-size: 12px; color: var(--muted); }
  .tile-act { margin-top: auto; padding: 7px 14px; font-size: 13px; }
  .tile-hero .tile-link { margin-bottom: var(--s-2); }
  .dash-money { max-width: 520px; margin-bottom: var(--s-4); }
  .dash-empty { margin-bottom: var(--s-4); }
  .dash-empty kbd { font-size: 12px; padding: 2px 6px; }
  .dash-hint { grid-column: 1 / -1; }

  .recent { padding: 0; overflow: hidden; }
  .recent-head {
    display: flex; justify-content: space-between; align-items: center; gap: var(--s-3);
    padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line-soft);
  }
  .recent-head h3 { margin: 0; }
  .recent-row {
    display: flex; align-items: center; gap: var(--s-3);
    padding: 9px var(--s-4); min-height: 46px;
    border-bottom: 1px solid var(--line-soft); text-decoration: none; color: inherit;
  }
  .recent-row:last-child { border-bottom: 0; }
  .recent-row:hover { background: var(--b-50); }
  .recent-no { min-width: 34px; font-weight: 700; color: var(--b-800); }
  .recent-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
  .recent-main .cell-main { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .recent-go { width: 16px; height: 16px; color: var(--n-300); flex-shrink: 0; }

  .charts {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr));
    gap: var(--s-3); margin-bottom: var(--s-4);
  }
  .chart-card { margin-bottom: 0; }
  .chart-card h3 { margin-bottom: var(--s-3); }
  .chart-card svg { width: 100%; height: auto; display: block; }
  /* Chart palette lives here, not in fill="#..." attributes scattered through
     the template: the validated blue/amber pair (CVD ΔE 32) plus recessive
     axis/label inks, all from the same tokens as the rest of the app. */
  .ch-a { fill: var(--b-600); }
  .ch-b { fill: var(--accent); }
  .ch-axis { stroke: var(--line); }
  .ch-tick { fill: var(--ink-faint); }
  .ch-legend { fill: var(--muted); }
  .ch-label { fill: var(--ink-soft); }

  .sys-card {
    display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
    padding: var(--s-3) var(--s-4); margin-bottom: var(--s-4);
    background: var(--surface); border: 1px solid var(--surface-bd); border-radius: var(--r-lg);
    font-size: 13px; color: var(--muted); box-shadow: var(--sh-card);
  }
  .sys-card svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--success); }
  .sys-card.warn { background: #fffbeb; border-color: #fde68a; color: var(--accent-ink); }
  .sys-card.warn svg { color: var(--accent); }
  .sys-card .grow { flex: 1 1 200px; }
  .sys-card .sys-link { color: inherit; font-weight: 600; }
  .sys-card .warn-text { color: var(--danger); }
  .sys-line { display: flex; gap: var(--s-5); flex-wrap: wrap; font-size: 12.5px; color: var(--muted); margin-top: var(--s-1); }
  .sys-line a { color: inherit; }
  .sys-line .sys-warn { color: var(--danger); font-weight: 600; }

  /* ══ work list / filters ═══════════════════════════════════════════════ */
  .filters { padding: var(--s-4); }
  .filters .row { gap: var(--s-3) var(--s-4); align-items: flex-end; }
  .filters-top { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
  /* Commission filters (both screens): Doctor · Share % · From · To · Basis ·
     Load on one grid. The wrapping flex row clipped the doctor's name,
     stacked the Basis radios and dropped Load to a line of its own (UI
     audit 2026-10-24); the responsive layer regroups it for tablet/phone. */
  .filters .row.comm-filter {
    display: grid; flex: 1 1 100%; align-items: end;
    grid-template-columns: minmax(220px, 2fr) 100px minmax(150px, 1fr) minmax(150px, 1fr) auto auto;
  }
  .comm-filter > .field { min-width: 0; max-width: none; }
  .comm-filter .radio-row { flex-wrap: nowrap; white-space: nowrap; }
  /* One sunken pill holding plain-text ranges; the active one is raised white. */
  .quick-ranges {
    display: flex; gap: 2px; flex-wrap: wrap; max-width: 100%;
    padding: 3px; border-radius: var(--r-full); background: var(--well); box-shadow: var(--sh-well);
  }
  .quick-ranges .chip {
    background: none; border-color: transparent; box-shadow: none; color: var(--ink-soft); font-weight: 500;
  }
  .quick-ranges .chip:hover { background: var(--n-0); color: var(--ink); }
  .quick-ranges .chip.active {
    background: var(--btn-neutral-bg); border-color: var(--btn-neutral-bd); color: var(--b-800);
    font-weight: 650; box-shadow: var(--sh-raise);
  }
  .filters-more { margin-top: var(--s-3); }
  .filters-more > summary { list-style: none; display: inline-flex; align-items: center; gap: 7px; cursor: pointer; }
  .filters-more > summary::-webkit-details-marker { display: none; }
  .filters-more > summary svg { width: 15px; height: 15px; }
  .filters-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--s-3); align-items: end; margin-top: var(--s-3);
  }
  .filters-actions { display: flex; gap: var(--s-2); align-items: center; }
  .active-filters {
    display: flex; gap: 6px; flex-wrap: wrap; margin-top: var(--s-3);
    padding-top: var(--s-3); border-top: 1px solid var(--line-soft);
  }

  /* ══ result entry ══════════════════════════════════════════════════════ */
  .res-head { margin-bottom: var(--s-3); }
  .res-id { padding: 0; overflow: hidden; }
  .res-id .pt-top {
    display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3) var(--s-4);
    padding: var(--s-4) var(--s-5);
    background: linear-gradient(90deg, var(--brand-soft), var(--panel));
    border-bottom: 1px solid var(--line-soft);
  }
  .res-id .pt-avatar {
    flex: none; width: 48px; height: 48px; border-radius: var(--r-full);
    display: grid; place-items: center;
    background: var(--brand); color: var(--n-0); font-size: 17px; letter-spacing: .02em;
    box-shadow: 0 0 0 3px var(--n-0), 0 0 0 4px var(--b-200);
  }
  .res-id .pt-who { flex: 1; min-width: 0; }
  .res-id .pt-name { font-size: 18px; color: var(--ink); overflow-wrap: anywhere; }
  .res-id .pt-meta { font-size: 13px; color: var(--muted); margin-top: 2px; }
  .res-id .pt-lab {
    flex: none; display: flex; flex-direction: column; align-items: flex-end;
    padding: var(--s-2) var(--s-3); border-radius: var(--r-md);
    background: var(--surface); border: 1px solid var(--b-200);
  }
  .res-id .pt-lab strong { font-family: var(--font-data); font-size: 18px; color: var(--brand-dk); letter-spacing: .04em; }
  .res-id .pt-lbl,
  .res-id .pt-facts dt {
    font-size: 10.5px; color: var(--muted); text-transform: uppercase; letter-spacing: .06em;
  }
  .res-id .pt-facts {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(160px, 100%), 1fr));
    gap: var(--s-3) var(--s-5); margin: 0; padding: var(--s-4) var(--s-5);
  }
  .res-id .pt-facts > div { min-width: 0; padding-left: var(--s-3); border-left: 3px solid var(--line); }
  .res-id .pt-facts dd { margin: 2px 0 0; font-size: 14px; color: var(--ink); overflow-wrap: anywhere; }
  .res-visit .row {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--s-3) var(--s-4); align-items: start;
  }
  .res-visit .field { min-width: 0; max-width: none; }
  .res-visit .lbl { font-size: 10.5px; color: var(--muted); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
  .res-visit .val { font-size: 13.5px; font-weight: 700; }
  .res-dept { padding: 0; overflow: hidden; }
  .res-dept-head {
    display: flex; align-items: center; gap: var(--s-3);
    padding: 10px var(--s-4); background: var(--header-bg); border-bottom: 1px solid var(--line);
  }
  .res-dept-head h2 {
    margin: 0; font-size: 12px; font-weight: 800; text-transform: uppercase;
    letter-spacing: .08em; color: var(--ink-soft);
  }
  .res-scroll { overflow-x: auto; }
  table.grid.results td, table.grid.results th { padding: 5px 8px; }
  table.grid.results th { background: none; }
  table.grid.results tbody tr:hover td { background: var(--n-25); }
  table.grid.results input, table.grid.results select {
    width: 100%; min-height: 34px; padding: 4px 9px; font-size: 13.5px;
    border: 1px solid var(--n-300); border-radius: var(--r-sm); background: var(--n-0);
    box-shadow: inset 0 1px 1px rgba(15,23,42,.04);
  }
  table.grid.results input::placeholder { color: var(--ink-faint); font-style: italic; }
  table.grid.results input:hover, table.grid.results select:hover { border-color: var(--n-400); }
  table.grid.results input:focus, table.grid.results select:focus {
    border-color: var(--b-600); background: var(--n-0); outline: none; box-shadow: var(--ring);
  }
  table.grid.results input.res-input:not(:placeholder-shown) { background: var(--b-50); border-color: var(--b-200); }
  .results td.result-col { width: 190px; }
  .results td.remarks-col input { border-style: dashed; background: var(--n-25); }
  .results td.unit-col { color: var(--muted); white-space: nowrap; }
  .results td.ref-col { font-size: 12px; color: var(--muted); white-space: pre-line; }
  .results td.method-col { font-size: 11px; color: var(--muted); }
  .results tr.testrow td { font-weight: 700; background: var(--header-bg); }
  .res-auto { position: relative; display: block; }
  .res-auto .auto-tag {
    position: absolute; right: 7px; top: 50%; transform: translateY(-50%); pointer-events: none;
    font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    padding: 1px 6px; border-radius: var(--r-full); background: var(--n-200); color: var(--n-600);
  }
  input.formula-ro {
    background: var(--n-100) !important; border-color: var(--n-200) !important;
    color: var(--n-600); padding-right: 48px;
  }
  /* A signed result this user may not amend: shown, not editable. */
  table.grid.results input.res-input[readonly] {
    background: var(--n-100); border-color: var(--n-200); color: var(--n-600); cursor: default;
  }
  /* Calculated (AUTO) and locked values keep their flag colour (2026-10-24):
     the grey read-only ink above hid an abnormal MCH the report bolds. */
  table.grid.results input[readonly].flag-low { color: var(--flag-low); font-weight: 700; }
  table.grid.results input[readonly].flag-high { color: var(--flag-high); font-weight: 700; }
  table.grid.results input[readonly].flag-critical_low,
  table.grid.results input[readonly].flag-critical_high { color: var(--flag-crit); font-weight: 700; }
  /* The tube's barcode sits with its test, not under the Remarks header. */
  .results .res-tube {
    margin-left: var(--s-2); font-family: var(--font-data); font-size: 11.5px;
    color: var(--muted); white-space: nowrap;
  }
  .results .res-ref { margin-top: 3px; font-size: 11.5px; color: var(--muted); white-space: pre-line; }

  /* ── delta check (verification screen) ────────────────────────────────
     The last signed value rides under the input instead of taking a column
     of its own: a seventh column pushed Reference and Remarks off a laptop
     screen, and a dash-filled column for a first-time patient reads as data.
     Nothing is emitted at all when there is no previous result. */
  .results .delta {
    display: block; margin-top: 3px; font-size: 11px; line-height: 1.35;
    color: var(--muted); white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis;
  }
  .results .delta b { color: var(--ink-soft); }
  .results .delta b.flag-low { color: var(--flag-low); }
  .results .delta b.flag-high { color: var(--flag-high); }
  .results .delta b.flag-critical_low,
  .results .delta b.flag-critical_high { color: var(--flag-crit); }
  .results .delta-lbl { font-size: 9.5px; text-transform: uppercase; letter-spacing: .05em; }
  .results .delta-date { color: var(--ink-faint); }
  .results .delta-pct {
    display: inline-block; margin-left: 3px; padding: 0 5px; border-radius: var(--r-full);
    font-weight: 800; background: var(--danger-soft); color: var(--danger);
  }
  /* A big move is a scanning aid, so it marks the whole row and not just the
     chip — the eye finds the rail before it reads the percentage. Painting a
     border here would be undone by the row-hover background rule; an inset
     shadow is a different property, so both survive. */
  table.grid.results tr.delta-big td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
  .results tr.delta-big .delta,
  .results tr.delta-big .delta b { color: var(--danger); }

  /* ── critical values list (results/criticals.html) ──────────────────────
     The call log grew the Informed column until dates, values and flags
     wrapped mid-word; it gets a fixed share and the short cells stay on one
     line. The Inform summary carries the patient's name, so it wraps. */
  table.crit-list th.crit-informed { width: 20%; }
  table.crit-list td[data-label="Patient"] .cell-main { white-space: nowrap; }
  .crit-inform > summary.btn {
    white-space: normal; text-align: left; height: auto; max-width: 100%;
    line-height: 1.35; padding-block: 7px;
  }
  .bill-strip .big-num { font-size: 17px; font-weight: 800; }
  .draft-bar {
    display: none; align-items: center; gap: var(--s-3);
    background: #fffbeb; border: 1px solid #fde68a; color: var(--accent-ink);
    padding: 10px var(--s-4); border-radius: var(--r-md);
    margin-bottom: var(--s-4); font-size: 13.5px;
  }
  .draft-bar.show { display: flex; }
  .draft-bar svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--accent); }
  .draft-bar #draft-restore { margin-left: auto; }

  /* ══ masters / test editor ═════════════════════════════════════════════ */
  /* One card per parameter (2026-10-24). It was a 14-column table, and the
     Patient Entry grid's `.prow { display: grid }` landed on its
     `tbody.prow`, so no row lined up with its header and every box was
     squeezed to a few characters. Fields are labelled in place now, on an
     8-track grid that drops to 4 and then 2 tracks. */
  .tests-tbl td.num input { text-align: right; }
  .plist { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-3); counter-reset: param; }
  .pblock {
    counter-increment: param;
    border: 1px solid var(--line); border-radius: var(--r-md);
    background: var(--panel); padding: var(--s-3) var(--s-4) var(--s-4);
  }
  /* A heading prints a title row and no value: marked by a brand rule and
     tint, with the value boxes faded (they are `inert`, still posted). */
  .pblock.is-heading { background: var(--brand-soft); border-left: 4px solid var(--brand); }
  .pblock.is-heading .pb-no::after { content: " · Heading — prints no value"; color: var(--brand); }
  .pblock.is-heading .pb-val { opacity: .4; }
  .pblock.has-error { border-color: var(--danger); box-shadow: 0 0 0 2px var(--danger-soft); }
  .pblock input.is-bad { border-color: var(--danger); }
  .pb-bands-btn[aria-expanded="true"] { background: var(--brand-soft); border-color: var(--brand); color: var(--brand); }
  .pb-head {
    display: flex; align-items: center; gap: var(--s-2) var(--s-3); flex-wrap: wrap;
    padding-bottom: var(--s-3); margin-bottom: var(--s-3); border-bottom: 1px solid var(--line-soft);
  }
  .pb-order { display: flex; gap: 2px; }
  .pb-order .btn { min-height: 28px; min-width: 28px; padding: 0 6px; font-size: 11px; }
  .pb-no { font-size: 12px; font-weight: 700; color: var(--muted); min-width: 2.5em; white-space: nowrap; }
  .pb-no::before { content: "#" counter(param); }
  .pb-head .cov { margin: 0; }
  .pb-acts { display: flex; gap: var(--s-2); align-items: center; margin-left: auto; }
  .pb-grid {
    display: grid; grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: var(--s-3); align-items: start;
  }
  .pb-grid + .pb-grid, .pb-more > .pb-grid { margin-top: var(--s-3); }
  .pb-grid .field { min-width: 0; }
  .pb-grid .c2 { grid-column: span 2; }
  .pb-grid .c3 { grid-column: span 3; }
  .pb-grid .c4 { grid-column: span 4; }
  .pb-grid textarea { min-height: var(--ctl-h); height: calc(var(--ctl-h) * 1.6); resize: vertical; }
  .pb-style { display: flex; flex-wrap: wrap; gap: 0 var(--s-4); min-height: var(--ctl-h); align-items: center; }
  .pb-more { margin-top: var(--s-3); }
  .pb-more > summary { font-size: 12.5px; font-weight: 600; color: var(--ink-soft); cursor: pointer; }
  .pb-bands {
    margin-top: var(--s-4); padding: var(--s-3) var(--s-4) var(--s-4);
    background: var(--n-25); border: 1px solid var(--line-soft); border-radius: var(--r-md);
  }
  .pb-bands-head { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-2); }
  .pb-bands-head strong { font-size: 13px; }
  .pb-bands-head .hint { margin: 0; }
  .bandwrap { overflow-x: auto; border: 1px solid var(--line-soft); border-radius: var(--r-sm); background: var(--panel); }
  .bandgrid { table-layout: fixed; width: 100%; min-width: 980px; margin: 0; }
  .bandgrid .bc-sex, .bandgrid .bc-tri, .bandgrid .bc-crit { width: 88px; }
  .bandgrid .bc-age { width: 78px; }
  .bandgrid .bc-unit { width: 124px; }
  .bandgrid .bc-val { width: 84px; }
  .bandgrid .bc-x { width: 52px; }
  .bandgrid th, .bandgrid td { padding: 6px; vertical-align: top; }
  .bandgrid th:first-child, .bandgrid td:first-child { padding-left: var(--s-3); }
  .bandgrid tbody tr:hover td { background: none; }
  .bandgrid .band-days { font-size: 11px; color: var(--muted); margin-top: 3px; white-space: nowrap; }
  .bandgrid .band-days.bad { color: var(--danger); font-weight: 700; white-space: normal; }
  @media (max-width: 1180px) {
    .pb-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .pb-grid .c3 { grid-column: span 3; }
    .pb-grid .c4 { grid-column: 1 / -1; }
  }
  /* Under 901 px the band boxes keep the 16 px phone size (no zoom on
     focus), so the columns widen and the table scrolls inside .bandwrap. */
  @media (max-width: 900px) {
    .bandgrid { min-width: 1300px; }
    .bandgrid .bc-sex { width: 128px; }
    .bandgrid .bc-age { width: 96px; }
    .bandgrid .bc-unit { width: 150px; }
    .bandgrid .bc-tri { width: 116px; }
    .bandgrid .bc-val, .bandgrid .bc-crit { width: 110px; }
    .bandgrid .bc-x { width: 60px; }
  }
  /* Phone: fields stay in reading order (Code, Name, Unit, Type…) with
     no half-width orphan; the header keeps arrows, number and actions on
     one line and moves the coverage chips to their own line. */
  @media (max-width: 600px) {
    .pb-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .pb-grid .c2, .pb-grid .c3, .pb-grid > .c1:first-child { grid-column: 1 / -1; }
    .pb-head .cov { order: 3; flex-basis: 100%; }
    /* No room for the words beside four 44 px buttons; the brand rule and
       tint already mark the row, and the faded boxes say why. */
    .pblock.is-heading .pb-no::after { content: none; }
  }

  /* ══ report designer ═══════════════════════════════════════════════════ */
  .tpl-bar { display: flex; gap: var(--s-3); align-items: flex-start; justify-content: space-between; flex-wrap: wrap; margin-bottom: var(--s-4); }
  .tpl-chips { display: flex; gap: var(--s-2); flex-wrap: wrap; }
  .tpl-chips-lbl {
    align-self: center; font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: .07em; color: var(--muted); margin-right: var(--s-1);
  }
  .tpl-new summary { list-style: none; }
  /* Open, the fold takes the bar's full width below the chips with its
     button still at the right, instead of the button jumping left. */
  .tpl-new[open] { flex-basis: 100%; display: flex; flex-direction: column; align-items: flex-end; }
  .tpl-new[open] .tpl-new-form { align-self: stretch; }
  .tpl-new summary::-webkit-details-marker { display: none; }
  .tpl-new-form {
    display: flex; gap: var(--s-3); align-items: flex-end; flex-wrap: wrap; margin-top: var(--s-3);
    padding: var(--s-4); background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
  }
  .tpl-new-form .field { flex: 1 1 220px; min-width: 0; max-width: none; }
  .designer { display: grid; grid-template-columns: minmax(0, 1fr) minmax(360px, 52%); gap: var(--s-5); align-items: start; }
  .ds-form { min-width: 0; }
  .ds-sec { background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s-5); margin-bottom: var(--s-3); }
  .ds-sec h2 { font-size: 15px; margin-bottom: var(--s-3); }
  .ds-sec h2 .muted { font-size: 12px; font-weight: 500; margin-left: 6px; }
  .ds-sub { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); margin: 0 0 var(--s-3); }
  .ds-jump { display: flex; gap: var(--s-1); overflow-x: auto; scrollbar-width: thin; margin: 0 0 var(--s-3); padding-bottom: var(--s-1); }
  .ds-jump a {
    white-space: nowrap; padding: 7px var(--s-3); min-height: var(--ctl-h-sm);
    display: inline-flex; align-items: center; border-radius: var(--r);
    font-size: 13px; font-weight: 600; color: var(--ink-soft); text-decoration: none;
  }
  .ds-jump a:hover { background: var(--n-100); }
  .ds-jump a.active { background: var(--b-50); color: var(--b-800); }
  .ds-card {
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
    margin-bottom: var(--s-3); scroll-margin-top: 70px; overflow: hidden; box-shadow: var(--sh-1);
  }
  .ds-card-head {
    display: flex; gap: var(--s-3); align-items: flex-start;
    padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--line-soft); background: var(--panel-alt);
  }
  .ds-card-head h2 { margin: 2px 0; font-size: 15px; }
  .ds-card-head p { margin: 0; font-size: 13px; color: var(--muted); line-height: 1.5; }
  .ds-card-head .tag {
    font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    color: var(--b-800); background: var(--b-50); padding: 2px 8px;
    border-radius: var(--r-full); margin-left: 6px; vertical-align: 2px;
  }
  .ds-card-body { padding: var(--s-5); }
  .ds-adv summary { display: flex; gap: var(--s-3); align-items: flex-start; padding: var(--s-4) var(--s-5); cursor: pointer; list-style: none; }
  .ds-adv summary::-webkit-details-marker { display: none; }
  .ds-adv summary h2 { margin: 2px 0; font-size: 15px; display: block; }
  .ds-adv summary p { margin: 0; font-size: 13px; color: var(--muted); }
  /* .howto (the designer's 3-step intro) went with the v1.20.16 text cut. */
  .step {
    flex-shrink: 0; width: 26px; height: 26px; border-radius: 50%;
    display: grid; place-items: center; font-weight: 700; font-size: 12.5px;
    background: var(--b-700); color: #fff;
  }
  .step-adv { background: var(--n-200); color: var(--ink-soft); }
  .step-adv::before { content: "+"; }
  .ds-adv[open] .step-adv::before { content: "−"; }

  .opts { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: var(--s-3); }
  .opts.two { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
  .opt {
    position: relative; display: flex; flex-direction: column; gap: var(--s-3);
    padding: var(--s-3); border: 1.5px solid var(--line); border-radius: var(--r-lg);
    background: var(--n-0); cursor: pointer; text-transform: none;
    color: var(--ink); font-size: 13.5px; font-weight: 500; min-height: 44px; margin: 0;
  }
  .opt input { position: absolute; opacity: 0; width: 1px; height: 1px; }
  .opt-sk { display: block; border-radius: var(--r); background: var(--n-100); padding: 6px; }
  .opt-sk svg { width: 100%; height: 56px; display: block; }
  .opt-txt b { display: block; font-weight: 650; }
  .opt-txt small { display: block; color: var(--muted); font-size: 12px; line-height: 1.4; margin-top: 3px; }
  .opt-check {
    position: absolute; top: 10px; right: 10px; width: 22px; height: 22px; border-radius: 50%;
    background: var(--b-700); color: #fff; display: none; place-items: center;
  }
  .opt-check svg { width: 12px; height: 12px; }
  .opt:hover { border-color: var(--b-300); }
  .opt:has(input:checked) { border-color: var(--b-600); background: var(--b-50); }
  .opt:has(input:checked) .opt-check { display: grid; }
  .opt:has(input:focus-visible) { outline: 2px solid var(--b-600); outline-offset: 2px; }

  .toggles { display: flex; flex-direction: column; gap: var(--s-1); }
  .toggle {
    display: flex; gap: var(--s-3); align-items: flex-start; padding: var(--s-2) 10px;
    border-radius: var(--r-md); cursor: pointer; text-transform: none;
    color: var(--ink); font-size: 13.5px; font-weight: 500; min-height: 44px; margin: 0;
  }
  .toggle:hover { background: var(--n-100); }
  .toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; }
  .toggle-track {
    flex-shrink: 0; width: 40px; height: 22px; border-radius: var(--r-full);
    background: var(--n-300); position: relative; margin-top: 1px;
    transition: background var(--dur-2) var(--ease);
  }
  .toggle-knob {
    position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
    background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.25);
    transition: transform var(--dur-2) var(--ease-out);
  }
  .toggle:has(input:checked) .toggle-track { background: var(--b-700); }
  .toggle:has(input:checked) .toggle-knob { transform: translateX(18px); }
  .toggle:has(input:focus-visible) .toggle-track { outline: 2px solid var(--b-600); outline-offset: 2px; }
  .toggle-txt b { display: block; font-weight: 650; }
  .toggle-txt small { display: block; color: var(--muted); font-size: 12px; line-height: 1.4; margin-top: 2px; }

  .color-field { display: flex; gap: var(--s-2); align-items: center; }
  .color-field input[type="color"] { flex-shrink: 0; height: var(--ctl-h); border-radius: var(--r-sm); }
  /* Settings → Sidebar: a sample of the sidebar's top row, framed like the
     sidebar panel, so the logo and colour are judged where they will sit. */
  .sb-preview.brand {
    display: inline-flex; max-width: 100%; padding: var(--s-3) var(--s-4);
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-md);
  }
  /* The preview swaps the flask icon for the chosen logo; an inline <svg>
     ignores the UA's [hidden] rule, so it is stated here. */
  .brand [hidden] { display: none; }
  .upload-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: var(--s-3); }
  /* Three-part letterhead editor. The designer's form column is ~600 px, so
     three real columns would be 145 px each — the text part takes a full row
     and the two image parts share the next one, labelled Left/Middle/Right so
     the grouping still reads like the band it prints (v1.13.0). */
  .lh-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-3); }
  .lh-grid > .upload-card { height: 100%; }
  .lh-card { grid-column: 1 / -1; }
  .lh-card .upload-thumb { height: 40px; }
  .lh-card .upload-thumb svg { width: 18px; height: 18px; }
  .lh-text { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-2) var(--s-3); margin-top: var(--s-3); }
  .lh-text .field { min-width: 0; }
  .lh-text .span2 { grid-column: 1 / -1; }
  .upload-tile { display: flex; gap: var(--s-3); padding: var(--s-3); border: 1px solid var(--line); border-radius: var(--r-lg); background: var(--panel-alt); }
  .upload-thumb {
    width: 96px; height: 72px; flex-shrink: 0; border-radius: var(--r); border: 1px solid var(--line);
    background: repeating-conic-gradient(var(--n-100) 0 25%, #fff 0 50%) 0 0 / 16px 16px;
    display: grid; place-items: center; overflow: hidden;
  }
  .upload-thumb img { max-width: 100%; max-height: 100%; object-fit: contain; }
  .upload-thumb.empty { background: var(--n-100); color: var(--n-300); }
  .upload-thumb svg { width: 28px; height: 28px; }
  .upload-meta { min-width: 0; flex: 1; }
  .upload-meta > label { font-size: 13px; color: var(--ink); text-transform: none; }
  .upload-actions { display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap; margin-top: var(--s-2); }
  .upload-card { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--r-lg); overflow: hidden; background: var(--n-0); }
  .upload-card.has-img { border-color: var(--success-bd); }
  .upload-card .upload-thumb { position: relative; width: 100%; height: 96px; border: 0; border-radius: 0; border-bottom: 1px solid var(--line-soft); }
  .upload-card .upload-thumb img { max-height: 80px; }
  .upload-state {
    position: absolute; top: var(--s-2); right: var(--s-2);
    font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    padding: 2px 8px; border-radius: var(--r-full);
    background: var(--n-0); color: var(--muted); border: 1px solid var(--line);
  }
  .has-img .upload-state { background: var(--success-soft); color: var(--success); border-color: var(--success-bd); }
  .upload-card .upload-meta { padding: 10px var(--s-3) var(--s-3); }
  .file-btn {
    position: relative; overflow: hidden; cursor: pointer; text-transform: none;
    font-size: 12.5px; color: var(--ink-soft); margin: 0;
  }
  .file-btn input { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
  .file-btn:has(input:focus-visible) { outline: 2px solid var(--b-600); outline-offset: 2px; }
  .file-btn svg { width: 14px; height: 14px; vertical-align: -2px; margin-right: 5px; }
  /* In a page's action row (Tests → Import Excel) it is a sibling of real
     buttons, so it takes their size and colour, not the upload card's. */
  .page-actions .file-btn { font-size: revert-layer; color: revert-layer; }
  .page-actions .file-btn svg { width: 16px; height: 16px; vertical-align: middle; margin-right: 0; }
  .file-name { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; max-width: 160px; white-space: nowrap; }

  /* ── branding image cropper (report_designer.html) ─────────────────── */
  .crop-box { width: min(720px, 94vw); }
  .crop-stage {
    position: relative; width: 100%; overflow: hidden; border-radius: var(--r);
    border: 1px solid var(--line); background: repeating-conic-gradient(var(--n-100) 0 25%, #fff 0 50%) 0 0 / 16px 16px;
    touch-action: none; cursor: grab;
  }
  .crop-stage:active { cursor: grabbing; }
  .crop-stage canvas { display: block; width: 100%; height: auto; }
  .crop-controls { display: flex; gap: var(--s-3); align-items: center; margin-top: var(--s-3); }
  .crop-controls input[type=range] { flex: 1; min-height: 0; }
  .crop-controls svg { width: 16px; height: 16px; color: var(--muted); flex-shrink: 0; }
  /* Always reachable: a tall picture pushed the buttons below a 900 px
     window, inside the box's own scroll. They stick to its bottom edge. */
  .crop-actions {
    display: flex; gap: var(--s-3); justify-content: flex-end; margin-top: var(--s-4); flex-wrap: wrap;
    position: sticky; bottom: calc(var(--s-5) * -1); padding: var(--s-3) 0 var(--s-5);
    background: var(--n-0); border-top: 1px solid var(--line-soft);
  }
  .crop-hint { font-size: 12px; color: var(--muted); margin: var(--s-2) 0 0; }
  .crop-pct { font-size: 12px; font-weight: 600; color: var(--ink-soft); min-width: 42px; text-align: right; }
  .crop-controls .ds-zoom { flex-shrink: 0; }

  .ds-actions {
    position: sticky; bottom: var(--s-3); z-index: 5;
    display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap;
    padding: var(--s-3) var(--s-4); background: rgba(255,255,255,.92); backdrop-filter: blur(8px);
    border: 1px solid var(--line); border-radius: var(--r-lg); box-shadow: var(--sh-3);
  }
  /* The password that unlocks Verify & Sign rides in the action bar beside
     the button it unlocks — a separate confirmation screen would just be one
     more thing to click through on a busy bench. */
  .ds-status { font-size: 12.5px; }
  /* Sticks below the sticky .subnav-bar (72 px tall on a desktop), not
     under it, so Fit / Actual size stay clickable after scrolling. */
  .ds-preview { position: sticky; top: calc(72px + var(--s-3)); }
  .ds-preview-head { display: flex; justify-content: space-between; align-items: center; gap: var(--s-3); margin-bottom: var(--s-3); flex-wrap: wrap; }
  .ds-preview-head h2 { margin: 0; font-size: 15px; }
  .ds-preview-head .muted { font-size: 12px; font-weight: 500; margin-left: 6px; }
  .ds-preview-head small { display: block; font-size: 12px; }
  .ds-testprint { display: flex; justify-content: center; margin-top: var(--s-3); }
  /* The preview is a page, not a web view: exact paper proportions, a white
     sheet with a hard edge on a grey backdrop, so an admin can see where the
     printer will cut. --sheet-pw/--sheet-ph are that page in CSS px, rendered
     from the template's own paper size; --sheet-scale is the zoom. */
  .sheet-wrap {
    position: relative; overflow: auto; background: var(--n-300);
    border-radius: var(--r-lg); border: 1px solid var(--line);
    display: flex; padding: var(--s-4); max-height: calc(100vh - 190px);
    align-items: flex-start;        /* never stretch the sheet: the page keeps its own proportions */
    justify-content: center;
    justify-content: safe center;   /* centred, but a full-size page stays scrollable to its left edge */
    /* A4 fallback: the template overrides these inline from its own paper, so
       a var() typo can never leave the sheet with no size at all. */
    --sheet-pw: 794; --sheet-ph: 1123; --sheet-scale: 1;
  }
  /* Reserves the footprint the transform-scaled iframe no longer takes in
     layout — this box is what the scrollbars measure, so it is the page times
     the zoom, to the same subpixel as the iframe's transform. */
  .sheet-spacer {
    position: relative; flex: 0 0 auto;
    width: calc(var(--sheet-pw) * var(--sheet-scale) * 1px);
    height: calc(var(--sheet-ph) * var(--sheet-scale) * 1px);
  }
  .sheet {
    position: absolute; inset: 0; overflow: hidden;
    background: var(--n-0); box-shadow: var(--sh-4);
    outline: 1px solid var(--n-400);   /* unscaled, so the page edge stays crisp at every zoom */
  }
  .sheet iframe {
    position: absolute; top: 0; left: 0; border: 0; background: var(--n-0);
    width: calc(var(--sheet-pw) * 1px); height: calc(var(--sheet-ph) * 1px);
    transform: scale(var(--sheet-scale)); transform-origin: top left;
  }
  .ds-zoom { display: inline-flex; border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden; }
  .ds-zoom button {
    border: 0; border-radius: 0; box-shadow: none; background: var(--n-0);
    color: var(--ink-soft); font-size: 12.5px; font-weight: 600; min-height: 30px;
    padding: 0 10px; text-transform: none; letter-spacing: 0;
  }
  .ds-zoom button + button { border-left: 1px solid var(--line); }
  .ds-zoom button:hover { background: var(--n-100); }
  .ds-zoom button[aria-pressed="true"] { background: var(--b-700); color: #fff; }
  .ds-preview-jump { display: none; }

  /* ══ viewer ════════════════════════════════════════════════════════════ */
  .viewer-pane { display: flex; flex-direction: column; height: calc(100vh - 76px); min-height: 420px; }
  .viewer-bar { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-3); }
  .viewer-bar h1 { margin: 0; font-size: 17px; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .viewer-ico { width: 20px; height: 20px; color: var(--muted); flex-shrink: 0; }
  .viewer-actions { display: flex; gap: var(--s-2); flex-wrap: wrap; margin-left: auto; }
  .viewer-pane .page-head { margin-bottom: var(--s-3); }
  .viewer-toolbar { margin: 0; align-items: center; }
  .viewer-toolbar .is-off { opacity: .5; cursor: default; }
  .viewer-body {
    position: relative; flex: 1; min-height: 0; background: var(--n-300);
    border: 1px solid var(--line); border-radius: var(--r-lg); padding: var(--s-4);
    display: flex; justify-content: center; overflow: auto;
  }
  .viewer-body iframe {
    width: 100%; max-width: 960px; height: 100%; border: 0; background: #fff;
    border-radius: var(--r-xs); box-shadow: var(--sh-4);
  }
  .viewer-sheet {
    position: absolute; inset: var(--s-4); margin: 0 auto;
    width: min(960px, calc(100% - 32px)); min-height: 60vh; border-radius: var(--r-xs);
  }

  /* ══ Print & Share (reports/print_options.html) ═══════════════════════ */
  .po-crumbs { display: flex; gap: 6px; margin: 0 0 var(--s-2); font-size: 13px; color: var(--muted); }
  .po-crumbs a { color: var(--muted); text-decoration: none; }
  .po-crumbs a:hover { color: var(--ink); text-decoration: underline; }
  .po-head { display: flex; justify-content: space-between; align-items: flex-start;
    gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-5); }
  .po-head h1 { margin: 0 0 var(--s-2); }
  .po-meta { display: flex; flex-wrap: wrap; gap: 6px; }
  .po-tag { display: inline-flex; align-items: center; min-height: 26px; padding: 0 10px;
    border: 1px solid var(--line); border-radius: var(--r-full); background: var(--panel);
    font-size: 12.5px; color: var(--ink-soft); white-space: nowrap; }
  .po-tag.strong { font-weight: 600; color: var(--ink); }
  .po-tag.ok { background: var(--success-soft); border-color: var(--success-bd); color: var(--success); font-weight: 500; }
  .po-tag.warn { background: var(--accent-soft); border-color: transparent; color: var(--accent-ink); font-weight: 500; }

  .po-grid { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: var(--s-5); align-items: start; }
  .po-main { min-width: 0; }
  .po-main form { margin: 0; }
  .po-card { padding: 0; overflow: hidden; }
  .po-card-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
    padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--line-soft); }
  .po-card-head h2 { margin: 0; font-size: 15px; display: flex; align-items: baseline; gap: 8px; }
  .po-muted { font-size: 12.5px; font-weight: 400; color: var(--muted); }
  .po-link { font-size: 13px; color: var(--b-700); text-decoration: none; }
  .po-link:hover { text-decoration: underline; }
  .po-small { margin: 2px 0 0; font-size: 12.5px; color: var(--muted); line-height: 1.45; }
  .po-dim { color: var(--muted); font-variant-numeric: tabular-nums; }
  .po-sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }

  .po-tpls { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: var(--s-3); padding: var(--s-5); }
  .po-tpl { position: relative; display: block; margin: 0; cursor: pointer; }
  .po-tpl input { position: absolute; opacity: 0; pointer-events: none; }
  .po-tpl-body { display: flex; flex-direction: column; gap: 3px; height: 100%;
    padding: var(--s-3) var(--s-4); border: 1px solid var(--line-strong); border-radius: var(--r-md);
    background: var(--panel); transition: border-color var(--dur-1, .12s), background var(--dur-1, .12s); }
  .po-tpl:hover .po-tpl-body { border-color: var(--b-300); }
  .po-tpl-name { font-weight: 600; color: var(--ink); font-size: 14px; }
  .po-tpl-sub { font-size: 12px; color: var(--muted); }
  .po-tpl input:checked + .po-tpl-body { border-color: var(--b-700); background: var(--b-50);
    box-shadow: inset 0 0 0 1px var(--b-700); }
  .po-tpl input:focus-visible + .po-tpl-body { outline: 2px solid var(--b-700); outline-offset: 2px; }

  .po-tests { border-top: 1px solid var(--line-soft); }
  .po-tests > summary { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
    padding: var(--s-3) var(--s-5); cursor: pointer; font-weight: 600; font-size: 14px; list-style: none; }
  .po-tests > summary::-webkit-details-marker { display: none; }
  .po-tests > summary::after { content: "▾"; color: var(--muted); margin-left: var(--s-2); transition: transform var(--dur-1, .12s); }
  .po-tests[open] > summary::after { transform: rotate(180deg); }
  .po-tests > summary > span:first-child { margin-right: auto; }
  .po-count { font-size: 12.5px; font-weight: 500; color: var(--success); }
  .po-count.none { color: var(--danger); }
  .po-depts { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--s-4); padding: 0 var(--s-5) var(--s-5); }
  .po-dept { margin: 0; padding: 0; border: 0; min-width: 0; }
  .po-dept legend { padding: 0; margin-bottom: var(--s-2); font-size: 12px; font-weight: 600;
    text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
  .po-check { display: flex; align-items: center; gap: var(--s-2); padding: 5px 0;
    font-weight: 400; font-size: 13.5px; color: var(--ink); cursor: pointer; }

  .po-actions { display: flex; flex-direction: column; }
  .po-act-group { padding: var(--s-4) var(--s-5); }
  .po-act-group + .po-act-group { border-top: 1px solid var(--line-soft); }
  .po-act-label { display: block; margin-bottom: var(--s-2); font-size: 12px; font-weight: 600;
    text-transform: uppercase; letter-spacing: .04em; color: var(--muted); }
  .po-btns { display: flex; flex-wrap: wrap; gap: var(--s-2); }
  .po-save { display: flex; align-items: center; justify-content: space-between; gap: var(--s-4);
    flex-wrap: wrap; background: var(--panel-alt); }
  .po-save .po-act-label { margin-bottom: 0; }
  .po-inline { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
  .po-inline .po-act-label { margin: 0; }

  .po-flush { margin-top: var(--s-4); }
  table.po-files { width: 100%; border-collapse: collapse; }
  table.po-files th { text-align: left; padding: 9px var(--s-5); font-size: 12px; font-weight: 500;
    color: var(--muted); background: var(--panel-alt); border-bottom: 1px solid var(--line); }
  table.po-files td { padding: 10px var(--s-5); border-bottom: 1px solid var(--line-soft);
    font-size: 13.5px; vertical-align: middle; }
  table.po-files tr:last-child td { border-bottom: 0; }
  table.po-files tr.current td { background: var(--b-50); }
  table.po-files th.num, table.po-files td.num { text-align: right; }
  .po-file { display: flex; align-items: center; gap: var(--s-3); min-width: 0; }
  .po-kind { flex: none; display: grid; place-items: center; width: 38px; height: 38px;
    border-radius: var(--r); font-size: 10.5px; font-weight: 700; letter-spacing: .03em;
    background: var(--danger-soft); color: var(--danger); }
  .po-kind.html { background: var(--n-100); color: var(--ink-soft); }
  .po-kind.receipt { background: var(--success-soft); color: var(--success); }
  .po-fname { display: block; font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
  .po-fsub { display: block; font-size: 12px; color: var(--muted); }
  .po-cur { color: var(--b-700); font-weight: 600; }
  .po-open { text-align: right; white-space: nowrap; }
  .po-foot { padding: var(--s-3) var(--s-5); border-top: 1px solid var(--line-soft); text-align: center; }

  .po-rail { position: sticky; top: var(--s-4); }
  .po-share .po-card-head { border-left: 3px solid var(--b-600); }
  .po-state { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 500; }
  .po-state::before { content: ""; width: 7px; height: 7px; border-radius: 50%; }
  .po-state.on { color: var(--success); } .po-state.on::before { background: var(--success); }
  .po-state.off { color: var(--danger); } .po-state.off::before { background: var(--danger); }
  .po-qr { width: 168px; margin: var(--s-5) auto var(--s-4); padding: 8px; background: var(--n-0);
    border: 1px solid var(--line); border-radius: var(--r-md); }
  .po-qr svg { display: block; width: 100%; height: auto; }
  /* A switched-off link's QR reaches nobody: shown faded, never scannable-looking. */
  .po-qr.is-off { opacity: .25; filter: grayscale(1); }
  .po-linkrow { display: flex; gap: var(--s-2); padding: 0 var(--s-5); align-items: flex-start; }
  .po-linkrow textarea {
    flex: 1; min-width: 0; min-height: 0; resize: none; font-size: 12.5px; line-height: 1.45;
    padding: 6px 9px; word-break: break-all; overflow: hidden;
  }
  .po-facts { list-style: none; margin: var(--s-3) 0 0; padding: 0 var(--s-5); font-size: 12.5px; color: var(--muted); }
  .po-facts li { padding: 3px 0 3px 16px; position: relative; line-height: 1.45; }
  .po-facts li::before { content: ""; position: absolute; left: 2px; top: 10px; width: 5px; height: 5px;
    border-radius: 50%; background: var(--line-strong); }
  .po-facts li.ok { color: var(--success); } .po-facts li.ok::before { background: var(--success); }
  .po-facts li.bad { color: var(--danger); } .po-facts li.bad::before { background: var(--danger); }
  .po-send { margin: var(--s-4) var(--s-5) 0; padding-top: var(--s-4); border-top: 1px solid var(--line-soft);
    display: flex; flex-direction: column; gap: 6px; }
  .po-sendbtn { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2);
    min-height: 40px; padding: 0 var(--s-3); border: 1px solid var(--line); border-radius: var(--r);
    color: var(--ink); text-decoration: none; font-size: 13.5px; font-weight: 500; background: var(--panel); }
  a.po-sendbtn:hover { border-color: var(--b-300); background: var(--b-50); color: var(--b-700); }
  .po-sendbtn.is-off { color: var(--ink-faint); background: var(--panel-alt); cursor: not-allowed; }
  .po-sendbtn small { font-size: 11.5px; font-weight: 400; }
  .po-linkctl { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2);
    margin-top: var(--s-4); padding: var(--s-3) var(--s-5); border-top: 1px solid var(--line-soft);
    background: var(--panel-alt); }
  .po-linkctl form { display: contents; }
  .po-linkctl .po-link { margin-left: auto; }
  .po-empty { padding: var(--s-6) var(--s-5); text-align: center; }
  .po-empty svg { width: 36px; height: 36px; color: var(--ink-faint); }
  .po-empty p { margin: var(--s-2) 0 0; }
  .po-empty .btn { margin-top: var(--s-4); }

  /* ══ QR / share ════════════════════════════════════════════════════════ */
  .share-card { border-left: 3px solid var(--b-600); }
  .share-body { display: flex; gap: var(--s-5); align-items: flex-start; flex-wrap: wrap; }
  .share-qr { flex: 0 0 auto; width: 120px; padding: 6px; background: #fff; border: 1px solid var(--line); border-radius: var(--r); }
  .share-qr svg { display: block; width: 100%; height: auto; }
  .share-main { flex: 1 1 260px; min-width: 0; }
  .share-link-row { display: flex; gap: var(--s-2); }
  .share-link-row input { flex: 1 1 auto; min-width: 0; font-size: 13px; color: var(--muted); font-family: var(--font-mono); }
  .share-link-row .btn { flex-shrink: 0; }
  .share-status { margin-top: var(--s-2); font-size: 13px; color: var(--muted); }
  .share-status.warn {
    display: inline-block; padding: 6px 10px; border-radius: var(--r);
    color: var(--accent-ink); background: #fffbeb; border: 1px solid #fde68a;
  }
  .share-actions { margin-top: var(--s-3); display: flex; gap: var(--s-2); flex-wrap: wrap; }
  .share-empty { padding: var(--s-6) var(--s-4) var(--s-5); }
  .share-empty .empty-ico { width: 44px; height: 44px; margin-bottom: var(--s-3); border-radius: var(--r-lg); }
  .share-empty .empty-ico svg { width: 22px; height: 22px; }

  /* ══ ops: staff / backup / outbox / audit ══════════════════════════════ */
  .msg-tpl textarea { font-family: inherit; }
  .card .auth-pass input { padding-right: 38px; }
  .card .auth-eye { right: var(--s-2); }
  .pass-reset { display: flex; gap: 6px; align-items: center; margin: 0; }
  .pass-reset .auth-pass input { width: 170px; }
  .backup-tiles { margin-bottom: var(--s-4); }
  .tile.bk-ok { border-left: 3px solid var(--success); }
  .tile.bk-bad { border-left: 3px solid var(--danger); }
  .tile.bk-bad .big { color: var(--danger); }
  .bk-path { font-size: 12px; word-break: break-all; margin: var(--s-1) 0 2px; font-family: var(--font-mono); }
  table.grid tr.row-failed td { background: #fef7f7; }
  table.grid tbody tr.row-failed:hover td { background: var(--danger-soft); }
  .audit-json {
    margin: 0; font-size: 11.5px; line-height: 1.4; max-width: 320px; max-height: 130px;
    overflow: auto; white-space: pre-wrap; word-break: break-word;
    background: var(--n-50); border: 1px solid var(--line); border-radius: var(--r-xs);
    padding: var(--s-1) 6px; font-family: var(--font-mono);
  }

  /* ══ money / vouchers ══════════════════════════════════════════════════ */
  .vch-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-3); }
  .vch-head h3 { margin: 0; }

  /* ══ errors ════════════════════════════════════════════════════════════ */
  .error-page {
    max-width: 480px; margin: 80px auto; text-align: center;
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-xl);
    padding: var(--s-10) var(--s-8) var(--s-8); box-shadow: var(--sh-3);
  }
  .error-page .err-ico { width: 52px; height: 52px; color: var(--n-400); }
  .error-page .err-code { margin-top: var(--s-1); font-size: 12.5px; font-weight: 700; letter-spacing: .1em; color: var(--muted); }
  .error-page h1, .error-page h2 { margin: 6px 0 var(--s-1); font-size: 20px; color: var(--ink); }
  .error-page p { margin: 0 0 var(--s-6); color: var(--muted); }
  .error-page .err-actions { display: flex; gap: var(--s-3); justify-content: center; flex-wrap: wrap; }

  /* Floating warning (patient entry). */
  .warn-float { position: fixed; bottom: 44px; right: var(--s-5); z-index: var(--z-toast); }

  /* ── Impersonation banner ──────────────────────────────────────────────
     A provider viewing a lab. Sticky rather than fixed so it pushes the
     page down instead of covering the top bar, and loud on purpose: the one
     mistake to design against is an operator forgetting which side of the
     product they are on. */
  .imp-bar {
    position: sticky; top: 0; z-index: calc(var(--z-toast) + 1);
    display: flex; align-items: center; justify-content: center;
    gap: var(--s-3);
    /* One line of a known height, so the sticky sidebar (and the phone top
       bar and drawer) can sit below it instead of being pushed off-screen —
       Sign out fell below a 900 px viewport (UI audit 2026-10-24). */
    height: var(--imp-h); padding: 0 var(--s-4); white-space: nowrap;
    background: var(--danger); color: #fff;
    font-size: 13px; font-weight: 600;
    box-shadow: var(--sh-1);
  }
  body:has(.imp-bar) { --imp-h: 38px; }
  .imp-bar > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
  @media screen {
    body:has(.imp-bar) .sidebar {
      top: var(--imp-h);
      height: calc(100vh - var(--imp-h)); height: calc(100dvh - var(--imp-h));
    }
    body:has(.imp-bar) .layout { min-height: calc(100vh - var(--imp-h)); }
  }
  .imp-bar strong { font-weight: 800; }
  .imp-bar form { margin: 0; }
  .imp-bar button {
    padding: 3px 12px; border-radius: var(--r-sm);
    border: 1px solid rgb(255 255 255 / .55);
    background: rgb(255 255 255 / .14); color: #fff;
    font: inherit; cursor: pointer;
  }
  .imp-bar button:hover { background: rgb(255 255 255 / .26); }

  /* ── Provider console ──────────────────────────────────────────────────
     Its own chrome, not the lab's: `admin/provider_base.html` deliberately
     does not extend `base.html`, so nothing here can inherit a lab's
     branding or navigation. Dark bar so the two interfaces are never
     confused at a glance. */
  body.provider { background: var(--n-100); }
  .pv-bar {
    display: flex; align-items: center; gap: var(--s-5);
    padding: 0 var(--s-5); height: 52px;
    background: var(--n-900); color: #fff;
  }
  .pv-brand {
    display: flex; align-items: center; gap: 8px;
    font-weight: 700; color: #fff; text-decoration: none;
  }
  .pv-dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--accent); flex: none;
  }
  .pv-nav { display: flex; gap: var(--s-1); margin-right: auto; }
  .pv-nav a {
    padding: 6px 12px; border-radius: var(--r-sm);
    color: rgb(255 255 255 / .74); text-decoration: none; font-size: 14px;
  }
  .pv-nav a:hover { background: rgb(255 255 255 / .1); color: #fff; }
  .pv-nav a.active { background: rgb(255 255 255 / .16); color: #fff; }
  .pv-out { display: flex; align-items: center; gap: var(--s-3); margin: 0; }
  .pv-who { font-size: 13px; color: rgb(255 255 255 / .7); }
  .pv-out .btn.ghost {
    border-color: rgb(255 255 255 / .3); color: #fff; background: transparent;
  }
  .pv-out .btn.ghost:hover { background: rgb(255 255 255 / .12); }
  /* A phone must never scroll the console sideways: the bar used to be wider
     than 390 px and dragged every console page with it. */
  .pv-brand, .pv-nav a { white-space: nowrap; }
  @media (max-width: 640px) {
    .pv-bar { gap: var(--s-2); padding: 0 var(--s-4); }
    .pv-brand { font-size: 0; gap: 0; }          /* the dot stays as the mark */
    .pv-nav a { padding: 6px 10px; font-size: 13.5px; }
    .pv-who { display: none; }
    .pv-out .btn.ghost { min-height: 34px; padding: 0 10px; font-size: 13px; }
  }
  .pv-main { max-width: 1180px; margin: 0 auto; padding: var(--s-6) var(--s-5) 64px; }
  @media (max-width: 640px) { .pv-main { padding-inline: var(--s-4); } }
  .pv-main .card + .card { margin-top: var(--s-5); }
  .card.danger { border-color: var(--danger-bd); background: var(--danger-soft); }
  .card.danger h2 { color: var(--danger); }

  /* Console → lab detail. */
  .crumbs { margin: 0 0 var(--s-1); font-size: 13px; color: var(--muted); }
  .crumbs a { color: var(--muted); text-decoration: none; }
  .crumbs a:hover { color: var(--ink); text-decoration: underline; }
  .lab-title { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
  .lab-title .pill { font-size: 12px; }
  .card-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: var(--s-3); margin-bottom: var(--s-3);
  }
  .card-head h2 { margin: 0; }
  .card-head a.muted { font-size: 13px; }
  .nowrap { white-space: nowrap; }

  .stat-strip {
    display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
    margin-bottom: var(--s-4); box-shadow: var(--sh-1);
  }
  .stat { display: flex; flex-direction: column; gap: 2px; padding: var(--s-4) var(--s-5); }
  .stat + .stat { border-left: 1px solid var(--line-soft); }
  .stat-n { font-size: 20px; font-weight: 600; color: var(--ink); font-variant-numeric: tabular-nums; }
  .stat-n small { font-size: 13px; font-weight: 500; color: var(--muted); }
  .stat-l { font-size: 12px; color: var(--muted); }

  .card.access { border-left-width: 3px; }
  .card.access.is-on  { border-left-color: var(--success); }
  .card.access.is-off { border-left-color: var(--danger); }
  .access-body {
    display: flex; justify-content: space-between; align-items: flex-end;
    gap: var(--s-5); flex-wrap: wrap;
  }
  .access-body h2 { margin: 0 0 var(--s-2); }
  .access-state {
    display: flex; align-items: center; gap: var(--s-2);
    margin: 0; font-weight: 600; color: var(--ink);
  }
  .access-state .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
  .is-on  .access-state .dot { background: var(--success); }
  .is-off .access-state .dot { background: var(--danger); }
  .access-form { display: flex; align-items: flex-end; gap: var(--s-3); margin: 0; flex-wrap: wrap; }
  .access-form .field { min-width: 220px; }

  /* Console → Add a lab. */
  .nl-layout {
    display: grid; grid-template-columns: minmax(0, 1fr) 300px;
    gap: var(--s-5); align-items: start;
  }
  .nl-main .card { margin-bottom: var(--s-4); }
  .pv-main .nl-main .card + .card { margin-top: 0; }
  .nl-head { display: flex; gap: var(--s-3); align-items: flex-start; margin-bottom: var(--s-5); }
  .nl-head h2 { margin: 0; font-size: 16px; }
  .nl-head p { margin: 2px 0 0; font-size: 13px; color: var(--muted); }
  .nl-num {
    flex: none; width: 28px; height: 28px; border-radius: 50%;
    display: grid; place-items: center; font-size: 13px; font-weight: 600;
    background: var(--b-50); color: var(--b-700); border: 1px solid var(--b-300);
  }
  .nl-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s-4); }
  .nl-grid .field { margin: 0; min-width: 0; }
  .nl-grid .field input { width: 100%; }
  .nl-grid [hidden] { display: none !important; }
  .nl-span2 { grid-column: 1 / -1; }
  .req { color: var(--danger); }
  .hint.ok { color: var(--success); }

  .plan-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-3); }
  .plan-card { position: relative; display: block; cursor: pointer; margin: 0; }
  .plan-card input { position: absolute; opacity: 0; pointer-events: none; }
  .plan-body {
    display: flex; flex-direction: column; gap: 2px; height: 100%;
    padding: var(--s-3) var(--s-4); border: 1px solid var(--line-strong);
    border-radius: var(--r-md); background: var(--panel);
    transition: border-color var(--dur-1, .12s), background var(--dur-1, .12s);
  }
  .plan-card:hover .plan-body { border-color: var(--b-300); }
  .plan-name { font-size: 15px; font-weight: 600; color: var(--ink); }
  .plan-sub { font-size: 12px; color: var(--muted); }
  .plan-card input:checked + .plan-body {
    border-color: var(--b-700); background: var(--b-50);
    box-shadow: inset 0 0 0 1px var(--b-700);
  }
  .plan-card input:focus-visible + .plan-body { outline: 2px solid var(--b-700); outline-offset: 2px; }

  .pw-wrap { position: relative; }
  .pw-wrap input { padding-right: 64px; }
  .pw-toggle {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    border: 0; background: transparent; color: var(--b-700);
    font-size: 13px; font-weight: 500; padding: 4px 8px; cursor: pointer; min-height: 0;
  }

  .nl-side { position: sticky; top: var(--s-5); }
  .nl-summary h2 { margin: 0 0 var(--s-3); font-size: 16px; }
  .nl-summary dl { margin: 0 0 var(--s-3); }
  .nl-summary dl > div {
    display: flex; justify-content: space-between; gap: var(--s-3);
    padding: var(--s-2) 0; border-bottom: 1px solid var(--line-soft); font-size: 13px;
  }
  .nl-summary dt { color: var(--muted); }
  .nl-summary dd { margin: 0; color: var(--ink); font-weight: 500; text-align: right; overflow-wrap: anywhere; }
  .nl-submit, .nl-cancel { width: 100%; justify-content: center; margin-top: var(--s-2); }

  .pv-dialog {
    width: min(440px, calc(100vw - 32px)); padding: 0; border: 1px solid var(--line);
    border-radius: var(--r-lg); background: var(--panel); color: var(--ink);
    box-shadow: 0 20px 48px rgb(15 23 42 / .22);
  }
  .pv-dialog::backdrop { background: rgb(15 23 42 / .45); }
  .pv-dialog[open] { animation: modal-pop var(--dur-2) var(--ease-out); }
  .pv-dialog form { margin: 0; }
  .pv-dialog section { padding: var(--s-6); }
  .pv-dialog h2 { margin: 0 0 var(--s-3); font-size: 17px; }
  .pv-dialog .field { margin-top: var(--s-4); }
  .pv-dialog .field input { width: 100%; }
  .dd-facts {
    margin: 0; padding: var(--s-3) var(--s-4); list-style: none;
    background: var(--panel-alt); border: 1px solid var(--line-soft); border-radius: var(--r);
    font-size: 13px; color: var(--ink-soft);
  }
  .dd-facts li + li { margin-top: var(--s-1); }
  .dd-phrase { user-select: all; color: var(--ink); }
  .dd-lead { margin: 0; color: var(--ink-soft); line-height: 1.5; }
  .dd-actions {
    display: flex; justify-content: flex-end; gap: var(--s-2); margin-top: var(--s-6);
  }
  .dd-actions .btn[disabled] { opacity: .45; cursor: not-allowed; }
}

@layer responsive {
  /* Everything viewport-conditional lives here. Because `responsive` sits
     above `components` and `screens` in the layer order, these rules win
     without needing to be last in the file or carry !important. */

  @media (min-width: 721px) {
    .filters-more > summary { display: none; }
  }

  /* ── ≤1024: report designer stacks, preview moves to a FAB ───────────── */
  @media (max-width: 1024px) {
    /* minmax(0, …), not 1fr: a bare 1fr track keeps its content's min width,
       and on a 320 px phone the form pushed the page 20 px sideways. */
    .designer { grid-template-columns: minmax(0, 1fr); }
    .designer > * { min-width: 0; }
    .ds-preview { position: static; }
    /* One column: the preview sits under the form, so the save bar carries a
       "Preview" jump. It used to float over the bar's right third. */
    .ds-actions .ds-preview-jump { display: inline-flex; }
    .ds-preview-jump svg { width: 18px; height: 18px; }
  }

  /* ── ≤1140: Patient Entry gives the name its own line ──────────────────
     The sidebar is still docked here, so the card is ~150 px narrower than
     the viewport: below this the desktop tracks (774 px of minimums) push the
     page into a horizontal scroll. Measured, not guessed — at a 960 px
     viewport the card is 656 px. */
  @media (max-width: 1140px) {
    .plan-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .prow-id {
      grid-template-columns: 150px 92px 112px minmax(150px, 1fr);
      grid-template-areas: "date labno title mobile"
                           "name name  name  name";
    }
    .prow-demo {
      grid-template-columns: 96px 108px 112px minmax(150px, 1fr);
      grid-template-areas: "age  agetype gender dob"
                           "preg preg    preg   .";
    }
  }

  /* ── ≤900: off-canvas nav + touch sizing ────────────────────────────── */
  @media (max-width: 900px) {
    /* Tables inside cards scroll sideways in their own box up to tablet
       width (base.html wraps them); a stacked .vlist simply has nothing to
       scroll at ≤720 px. */
    .card-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }
    .topbar {
      display: flex; align-items: center; gap: var(--s-3);
      height: 56px; padding: 0 var(--s-2) 0 var(--s-1);
      background: var(--panel); border-bottom: 1px solid var(--line);
      position: sticky; top: 0; z-index: var(--z-nav);
    }
    .topbar-title {
      flex: 1; font-weight: 700; color: var(--brand-ink); font-size: 15px;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .nav-toggle, .topbar-action {
      display: grid; place-items: center; width: 44px; height: 44px; min-height: 44px;
      border-radius: var(--r-md); background: none; border: 0; box-shadow: none;
      color: var(--ink-soft); padding: 0;
    }
    .nav-toggle:hover, .topbar-action:hover { background: var(--n-100); }
    .nav-toggle svg, .topbar-action svg { width: 22px; height: 22px; }
    .topbar-action { color: var(--b-700); }

    .layout { min-height: calc(100vh - 56px); }
    .sidebar {
      position: fixed; top: 0; bottom: 0; left: 0; width: min(84vw, 300px);
      z-index: var(--z-drawer); transform: translateX(-100%);
      transition: transform var(--dur-3) var(--ease-out); overflow-y: auto;
    }
    body.nav-open .sidebar { transform: none; box-shadow: var(--sh-pop); }
    /* Below the impersonation bar, not under it. */
    body:has(.imp-bar) .topbar { top: var(--imp-h); }
    body:has(.imp-bar) .sidebar { top: var(--imp-h); height: auto; }
    body:has(.imp-bar) .layout { min-height: calc(100vh - 56px - var(--imp-h)); }
    .nav-backdrop {
      display: block; position: fixed; inset: 0;
      background: rgba(15,23,42,.45); z-index: 70;
    }
    .nav-backdrop[hidden] { display: none; }
    .sidebar nav a, .sidebar nav summary { min-height: 44px; }
    .theme-menu > summary, .theme-opt { min-height: 44px; }

    .main { padding: var(--s-4) var(--s-3) 60px; max-width: none; }
    .page-skel { left: 0; top: 56px; padding: var(--s-4) var(--s-3); }
    .page-skel .ps-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    h1 { font-size: 18px; }
    .card { padding: var(--s-4); }
    .row { gap: var(--s-3); }

    /* 16px inputs = no iOS focus zoom; 44px targets everywhere. */
    input, select, textarea { min-height: 44px; font-size: 16px; }
    textarea { min-height: 84px; }
    input[type="checkbox"], input[type="radio"] { min-height: 0; }
    /* A bare file picker and every colour swatch are tap targets like any
       button (2026-10-25: Settings' theme pickers were 27 px tall, the
       accreditation file pickers 38). A picker inside a .file-btn fills
       that button, which is the target. */
    input[type="file"] { min-height: 44px; }
    input[type="color"] { height: 44px; width: 52px; min-height: 0; }
    button, .btn { min-height: 44px; padding: 10px 16px; }
    .btn.sm, button.sm { min-height: 40px; }
    /* The report viewer's bar is the one place a small button is the
       primary action on a phone (Print / Download) — full 44px there. */
    .viewer-actions .btn.sm { min-height: 44px; padding-inline: 14px; }
    .icon-btn { min-width: 40px; }
    .toast .toast-x { min-height: 0; }
    /* The password eye sits inside a 44 px box: 40 px keeps it inside. */
    .auth-eye { min-height: 40px; min-width: 40px; align-items: center; justify-content: center; }

    .field.sm, .field.md, .field.lg { min-width: 0; max-width: none; flex: 1 1 140px; }
    .row > .field { flex: 1 1 140px; min-width: 0; max-width: none; }
    .row > .field.sm { flex-basis: 100px; }
    /* Patient Entry on a phone: two columns, the name full width, Age still
       next to its unit. */
    .prow-id {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      grid-template-areas: "date  labno"
                           "title mobile"
                           "name  name";
    }
    .prow-demo {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      grid-template-areas: "age    agetype"
                           "gender dob"
                           "preg   preg";
    }
    .toolbar { gap: var(--s-2); }
    .toolbar .btn, .toolbar button { flex: 1 1 auto; text-align: center; }
    .modal-box { min-width: 0; width: 92vw; }
    .ds-actions { bottom: var(--s-2); }
    .page-head { gap: var(--s-3); }
    .page-head h1 { overflow-wrap: anywhere; }
    /* One column: sharing comes straight after print & save, above the archive. */
    /* stretch, not the desktop `start`: as a column each card is full width
       (2026-10-24 — the Share card shrank to 175 px on a phone). */
    .po-grid { display: flex; flex-direction: column; gap: var(--s-4); align-items: stretch; }
    .po-main { display: contents; }
    .po-main > form { order: 1; }
    .po-rail { position: static; order: 2; }
    .po-main > .po-flush { order: 3; margin-top: 0; }
    .card.po-card { padding: 0; }
    .po-grid, .po-grid > *, .po-main > * { min-width: 0; max-width: 100%; }
    .po-card-head, .po-act-group, .po-tpls, .po-foot { padding-inline: var(--s-4); }
    .po-tests > summary, .po-depts { padding-inline: var(--s-4); }
    table.po-files th, table.po-files td { padding-inline: var(--s-4); }
    .po-hide { display: none; }
    .nl-layout { grid-template-columns: minmax(0, 1fr); }
    .nl-side { position: static; }
    .nl-grid, .plan-grid { grid-template-columns: minmax(0, 1fr); }
    .stat-strip { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .stat:nth-child(3) { border-left: 0; border-top: 1px solid var(--line-soft); }
    .stat:nth-child(4) { border-top: 1px solid var(--line-soft); }
    .access-form, .access-form .field { width: 100%; min-width: 0; }

    /* Phone touch targets (audited at 320–768 px, 2026-09-17): quick-range
       chips, disclosure rows and tick boxes were 17–32 px tall. */
    .chip.sm { min-height: 40px; padding-inline: 14px; }
    .chip { min-height: 40px; }
    /* Height via padding, not `display: flex` — flex drops the native
       disclosure triangle (it only paints on list-item boxes) and overrode
       every self-styled summary (.po-tests, .ds-adv, .filters-more). */
    .main summary { min-height: 44px; padding-block: 10px; box-sizing: border-box; }
    input[type="checkbox"], input[type="radio"] { width: 22px; height: 22px; }
    label:has(> input[type="checkbox"]), label:has(> input[type="radio"]),
    .check-inline { min-height: 44px; }
    /* A long test name must wrap inside its tick-box label, not widen the page. */
    .row > .check-inline { flex-shrink: 1; min-width: 0; max-width: 100%; }
    .check-inline > input { flex-shrink: 0; }
    /* Result Entry's balance form: its inline margin-left:auto kept the
       Collect row on one 750 px line on a tablet. */
    .bill-strip .row > form { margin-left: 0 !important; flex: 1 1 100%; }
    .bill-strip .row > form > .field { flex: 1 1 140px; }
    /* Sticks just under the 56px sticky top bar instead of behind it; the
       bar's bleed follows .main's phone padding. */
    .subnav-bar {
      margin: calc(var(--s-4) * -1) calc(var(--s-3) * -1) var(--s-3);
      padding: var(--s-2) var(--s-3); top: 56px;
    }
    /* #anchors and keyboard focus land below the 56 px top bar and, where a
       page has one, the stuck tab strip under it (2026-10-24: the desktop
       80 px left a focused field hidden under both). */
    html { scroll-padding-top: 68px; }
    html:has(.subnav-bar) { scroll-padding-top: 124px; }
  }

  /* ── ≤720: stacked cards, dropped columns, phone tables ─────────────── */
  @media (max-width: 720px) {
    .card { padding: var(--s-4); }
    table.grid th, table.grid td { padding: 8px 10px; }

    /* Wrapped tables scroll sideways; only .vlist becomes stacked cards. */
    .table-wrap { overflow-x: auto; background: var(--panel); border: 1px solid var(--line); }
    .table-wrap:has(> table.vlist) { overflow: visible; background: none; border: 0; box-shadow: none; }
    /* Registers: every money table wider than three columns is a .vlist
       (stacked cards, Balance/Amount in plain view); the narrow ones left
       here still scroll sideways if a long name needs it. */
    .table-wrap.reg { overflow-x: auto; background: var(--panel); border: 1px solid var(--line); }
    /* Editable name lists (Centres page): each row is a small block — the
       name box full width, then code / order (labelled from data-label) and
       Save / Delete on one line. A name-only list keeps name and buttons on
       one line. Nothing scrolls sideways and no name is cut. */
    table.grid.list-edit, table.grid.list-edit tbody { display: block; }
    table.grid.list-edit colgroup, table.grid.list-edit thead { display: none; }
    table.grid.list-edit tr {
      display: flex; flex-wrap: wrap; align-items: flex-end; gap: 6px var(--s-2);
      padding: var(--s-2) 0; border-bottom: 1px solid var(--line-soft);
    }
    table.grid.list-edit td { display: block; padding: 0; border: 0; min-width: 0; }
    table.grid.list-edit td:first-child { flex: 1 1 100%; }
    table.grid.list-edit tr:not(:has(td[data-label])) td:first-child { flex: 1 1 0; }
    table.grid.list-edit td[data-label] { flex: 1 1 0; }
    table.grid.list-edit td[data-label]::before {
      content: attr(data-label); display: block; margin-bottom: 2px;
      font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--muted);
    }
    table.grid.list-edit td.list-acts { flex: 0 0 auto; margin-left: auto; }
    table.grid.list-edit td.empty-row { flex: 1 1 100%; }

    table.vlist, table.vlist tbody, table.vlist tr, table.vlist td, table.vlist tfoot { display: block; }
    table.vlist thead { display: none; }
    table.vlist tr {
      background: var(--panel); border: 1px solid var(--line); border-radius: var(--r-lg);
      padding: 6px var(--s-3) 10px; margin-bottom: 10px; box-shadow: var(--sh-1);
    }
    table.vlist td {
      display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-3);
      padding: 5px 0; border-bottom: 0; text-align: right;
      /* A long value wraps inside the card (2026-10-25: a critical's
         reference text — "Normal : 70-99 Prediabetes (IFG): 100-125 …" —
         pushed the Criticals page 74 px sideways). `nowrap` is a desktop
         table choice; a phone card has no columns to keep aligned. */
      white-space: normal; overflow-wrap: anywhere; min-width: 0;
    }
    table.vlist td::before {
      content: attr(data-label);
      font-size: 11px; text-transform: uppercase; letter-spacing: .05em;
      color: var(--muted); font-weight: 600; text-align: left; flex-shrink: 0;
    }
    table.vlist td.actions {
      display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-start;
      padding-top: 10px; margin-top: var(--s-1);
      border-top: 1px solid var(--line-soft); white-space: normal;
    }
    table.vlist td.actions::before { content: none; }
    table.vlist td.actions .btn:first-child { flex: 1 1 100%; text-align: center; }
    td.actions .icon-slot { display: none; }
    /* A cell with a main line and sub-lines (a person and their age, mobile,
       doctor) stacks under its label instead of laying the lines side by side
       (2026-10-24). Lab No. and Patient keep their own layout below. */
    table.vlist td:has(> .cell-sub) { display: block; text-align: left; }
    table.vlist td:has(> .cell-sub)::before { display: block; margin-bottom: 2px; }
    table.vlist td[data-label="Lab No."], table.vlist td[data-label="Patient"] { display: block; text-align: left; }
    table.vlist td[data-label="Lab No."]::before { content: "Lab No. "; display: inline; margin-right: 6px; }
    table.vlist td[data-label="Lab No."] .cell-main, table.vlist td[data-label="Lab No."] .cell-sub { display: inline; }
    table.vlist td[data-label="Lab No."] .cell-sub { margin-left: var(--s-2); }
    table.vlist td[data-label="Patient"]::before { content: none; }
    table.vlist td[data-label="Patient"] .cell-main { font-size: 15px; }
    table.vlist tr:hover td { background: none; }
    table.vlist tfoot tr { background: var(--line-soft); }
    table.vlist tfoot td[colspan="2"] { display: none; }
    /* The Patients list's "12 visits" beside the Balance total (2026-10-24:
       the card read "BALANCE 0.00" with nothing to say what it totalled). */
    table.vlist tfoot td.vl-count { display: flex; }
    table.vlist tfoot td.vl-count::before { content: none; }
    table.vlist tr:has(> th) { display: none; }
    table.vlist td:empty { display: none; }
    table.vlist td:not(.actions):has(> form) { flex-direction: column; align-items: stretch; gap: 6px; text-align: left; }
    table.vlist td:has(> table) { display: block; overflow-x: auto; text-align: left; padding: 6px 0; }
    table.vlist td:has(> table)::before { content: none; }
    table.vlist table { display: table; width: 100%; min-width: 480px; }
    table.vlist table thead { display: table-header-group; }
    table.vlist table tbody { display: table-row-group; }
    table.vlist table tr { display: table-row; background: none; border: 0; padding: 0; margin: 0; border-radius: 0; box-shadow: none; }
    table.vlist table td, table.vlist table th { display: table-cell; text-align: left; padding: 6px var(--s-2); border-bottom: 1px solid var(--line-soft); }
    table.vlist table td::before { content: none; }
    table.vlist table td.num, table.vlist table th.num { text-align: right; }
    table.vlist .m-hide { display: none !important; }
    table.vlist td.m-title { display: block; text-align: left; }
    table.vlist td.m-title::before { content: none; }
    table.vlist td.m-title .cell-main { font-size: 15px; }
    table.vlist td.actions.inline { flex-wrap: nowrap; }
    table.vlist td.actions.inline > .btn { flex: 1 1 0; }
    table.vlist td.actions.inline form { flex: 1 1 0; display: flex; }
    table.vlist td.actions.inline form .btn { width: 100%; }
    /* Tests → Show parameters: eight columns cannot share a phone card, so
       each parameter stacks — name, then labelled unit/min/max/type on one
       wrapping line, the range text on its own line. */
    table.vlist table.params-mini { min-width: 0; }
    table.vlist table.params-mini thead { display: none; }
    table.vlist table.params-mini tbody, table.vlist table.params-mini tr { display: block; }
    table.vlist table.params-mini tr { padding: 8px 0; border-bottom: 1px solid var(--line-soft); }
    table.vlist table.params-mini td { display: inline-block; border: 0; padding: 2px 14px 2px 0; text-align: left; }
    table.vlist table.params-mini td::before {
      content: attr(data-label) " "; font-size: 11px; color: var(--muted);
      text-transform: uppercase; letter-spacing: .05em;
    }
    table.vlist table.params-mini td:first-child,
    table.vlist table.params-mini td[data-label="Normal range"] { display: block; }
    table.vlist table.params-mini td:first-child::before { content: none; }
    table.vlist table.params-mini td[data-label="Normal range"]::before { display: block; }
    table.vlist table.params-mini td:empty { display: none; }
    /* Critical values: the Inform row is the foot of its result's card, not
       a card of its own; the value keeps its "!!" glyph beside it. */
    table.vlist.crit-list tr.crit-main:has(+ tr.crit-form) {
      margin-bottom: 0; border-bottom: 0; border-radius: var(--r-lg) var(--r-lg) 0 0;
      padding-bottom: 4px;
    }
    table.vlist.crit-list tr.crit-form {
      border-top: 1px dashed var(--line); border-radius: 0 0 var(--r-lg) var(--r-lg);
      padding-top: 8px;
    }
    table.vlist.crit-list tr.crit-form summary.btn { width: 100%; justify-content: flex-start; }
    table.vlist.crit-list td[data-label="Value"] { gap: 4px; }
    table.vlist.crit-list td[data-label="Value"]::before { margin-right: auto; }

    .page-head .btn kbd { display: none; }
    .page-head > .btn, .page-head .page-actions .btn { flex: 1 1 auto; }
    .page-head .page-actions { width: 100%; }
    .filters { padding: var(--s-3); }
    .filters-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .filters-actions { grid-column: 1 / -1; }
    .filters-actions button, .filters-actions .btn { flex: 1; text-align: center; }
    .quick-ranges { overflow-x: auto; flex-wrap: nowrap; padding-bottom: var(--s-1); }
    .quick-ranges .chip { flex-shrink: 0; }
    .list-meta { margin-bottom: 6px; }

    /* Result grid on a phone (2026-10-24): one block per parameter — name
       and remarks on the left, the value with its unit · reference under it
       on the right. The seven-column table scrolled sideways inside the
       card, so Unit, Reference and Remarks sat off-screen while typing. */
    .res-scroll { overflow: visible; }
    table.grid.results, table.grid.results tbody { display: block; }
    table.grid.results thead { display: none; }
    table.grid.results tr {
      display: grid; grid-template-columns: minmax(0, 1fr);
      gap: 4px var(--s-2); padding: 8px 10px; border-bottom: 1px solid var(--line-soft);
    }
    table.grid.results td { display: block; padding: 0; border: 0; }
    table.grid.results td:empty, table.grid.results td.num, table.grid.results td.unit-col,
    table.grid.results td.ref-col, table.grid.results td.method-col { display: none; }
    table.grid.results tr.testrow { background: var(--header-bg); }
    table.grid.results tr:has(> td.result-col) {
      grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
      grid-template-areas: "name value" "rem value"; align-items: start;
    }
    table.grid.results tr:has(> td.result-col) > td:nth-child(2) { grid-area: name; align-self: center; }
    .results td.result-col { grid-area: value; width: auto; }
    .results td.remarks-col { grid-area: rem; }
    /* 16 px or iOS zooms the page on focus; 40 px is the smallest target. */
    .results td.remarks-col input { min-height: 40px; font-size: 16px; }
    .results .delta { white-space: normal; }
    table.grid.results tr.delta-big { box-shadow: inset 3px 0 0 var(--danger); }
    .res-visit .field { flex: 1 1 45%; }
    .res-visit .row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .bill-strip .row > form > button { flex: 1 1 100%; }
    /* Case-type tiles fill each row evenly instead of leaving ragged gaps;
       wide enough that "MAMMOGRAPHY" is never broken mid-word. */
    .case-tile { flex: 1 1 128px; min-width: 0; }
    .case-tile .ct-name { overflow-wrap: break-word; }

    /* Tests row: search and package each take the full width, the count sits
       under them on the right (2026-10-24: "Name or short nam", "Choose a pa"). */
    .tests-pick > .field { flex: 1 1 100%; }
    .tests-pick .test-count { flex: 1 1 100%; text-align: right; }

    .tests-tbl th:nth-child(3), .tests-tbl td:nth-child(3) { display: none; }
    .tests-tbl td.num input { width: 90px !important; }
    .pass-reset { flex-wrap: wrap; }
    .pass-reset .auth-pass { flex: 1 1 150px; }
    .pass-reset .auth-pass input { width: 100%; }

    .ds-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ds-grid .span2 { grid-column: 1 / -1; }
    .ds-card-head, .ds-card-body { padding-left: var(--s-3); padding-right: var(--s-3); }
    .opts, .opts.two { grid-template-columns: 1fr; }
    .opt { flex-direction: row; align-items: center; }
    .opt-sk { width: 84px; flex-shrink: 0; }
    .opt-sk svg { height: 48px; }
    .ds-actions { padding: 10px var(--s-3); }
    .ds-actions .btn, .ds-actions button { flex: 1 1 auto; }
    .ds-actions .btn kbd { display: none; }
    /* A 150 px password box wedged between two full-width buttons is a
       target nobody can hit, so it takes a row of its own. */

    .tiles { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; }
    .tile { padding: var(--s-3); }
    .tile .big { font-size: 22px; }
    .recent-row { padding: 10px var(--s-3); }
    .recent-head { padding: 10px var(--s-3); }
    .sys-card .btn { flex: 1 1 100%; }
    .radio-row label { min-height: 44px; }
    .modal-box { padding: var(--s-4); }
    .page-sub { max-width: none; }
    .m-only { display: block; }

    .viewer-pane { height: auto; min-height: 0; }
    .viewer-body { padding: var(--s-2); }
    .viewer-body iframe { height: 70vh; }
    .viewer-sheet { inset: var(--s-2); width: calc(100% - 16px); min-height: 0; }

    #toasts { right: auto; left: 50%; transform: translateX(-50%); align-items: stretch; }
    .warn-float { left: var(--s-3); right: var(--s-3); bottom: var(--s-3); }
  }

  @media (max-width: 560px) {
    .dash-tiles { grid-template-columns: 1fr; }
    .tile-link .big { font-size: 32px; }
    .lh-grid { grid-template-columns: 1fr; }
    .lh-text { grid-template-columns: 1fr; }
  }

  @media (max-width: 520px) {
    /* The card is already one column and 360 px wide, so nothing needs
       restacking — it only needs to stop being a card with margins on a
       360 px screen, and the button to be thumb-width. */
    .auth { padding: var(--s-6) var(--s-3); }
    .auth-card { padding: var(--s-6) var(--s-4); }
    .auth-submit, .auth-submit:hover { width: 100%; }
  }

  @media (max-width: 460px) {
    .ds-grid { grid-template-columns: 1fr; }
  }

  /* Commission filters: Doctor + Share %, then the dates, then Basis + Load
     on a tablet; one field a row (dates paired) on a phone. */
  @media (max-width: 1180px) {
    .filters .row.comm-filter { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    .comm-filter > .cf-doctor, .comm-filter > .cf-basis { grid-column: span 3; }
    .comm-filter > .cf-date { grid-column: span 2; }
    .comm-filter > .field-actions .btn { width: 100%; }
  }
  @media (max-width: 560px) {
    .filters .row.comm-filter { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .comm-filter > .cf-doctor, .comm-filter > .cf-pct, .comm-filter > .cf-basis, .comm-filter > .field-actions { grid-column: 1 / -1; }
    .comm-filter > .cf-date { grid-column: span 1; }
  }

  /* Keyboard shortcuts mean nothing on a phone or a touch screen: hide
     every <kbd> chip and "Press Enter…" hint (.kbd-hint) there. */
  @media (max-width: 900px), (hover: none) {
    kbd, .kbd-hint { display: none; }
  }

  /* ── motion preference ──────────────────────────────────────────────── */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      transition-duration: .01ms !important;
      animation-duration: .01ms !important;
      animation-iteration-count: 1 !important;
      scroll-behavior: auto !important;
    }
    #progress, .skeleton::after, .is-loading::after,
    .toast, .modal.open .modal-box, button[data-guarding]::after { animation: none; }
    button:active, .btn:active { transform: none; }
  }
}

@layer utilities {
  .muted { color: var(--muted); }
  .right { text-align: right; }
  .mt { margin-top: var(--s-4); }
  .mono { font-family: var(--font-mono); font-size: 12.5px; }
  /* Under 16 px an iPhone zooms the page on focus. */
  @media (max-width: 900px) {
    input.mono, textarea.mono, select.mono { font-size: 16px; }
  }
  /* left: 0 — without an offset the box keeps its static position, and inside
     a scrolled table (an actions-column header) that escaped the scroller and
     widened every phone page by the table's full width. */
  .sr-only {
    position: absolute; left: 0; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); white-space: nowrap;
  }
  /* Links whose address moved to data-href (hover-preview hidden). A bare
     link in running text reads as a link; a segment or tab of a switcher
     (`.segmented`, any role="tablist") keeps its own segment style. */
  a[data-href] { cursor: pointer; }
  .main a[data-href]:not([class]):not(.segmented a, [role="tablist"] a) { color: var(--brand); text-decoration: underline; }
  /* All screen text is bold (user request 2026-09-16). !important because the
     console templates carry unlayered <style> weights, which beat any layer.
     Print documents never load app.css, so reports keep their own weights
     and an abnormal row still stands out. */
  body, body *, body *::before, body *::after { font-weight: 700 !important; }
}
