/* ==========================================================================
   Wasfa | وصفة — application stylesheet
   Mobile-first. Every value comes from tokens.css; this file only composes.

   1. Reset & base
   2. Bootstrap bridge
   3. Layout shell
   4. Navigation (topbar / sidebar / drawer / bottom nav)
   5. Components
   6. States (loading, empty, error)
   7. Utilities
   8. Motion, print, accessibility
   ========================================================================== */

/* ==========================================================  1. reset & base */

*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* Anchors clear the sticky top bar. */
    scroll-padding-top: calc(var(--topbar-h) + var(--space-4));
}

body {
    margin: 0;
    min-height: 100svh;
    font-family: var(--font-sans);
    font-size: var(--text-base);
    font-weight: var(--weight-normal);
    line-height: var(--leading-normal);
    color: var(--fg-default);
    background: var(--bg-canvas);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
    overscroll-behavior-y: none;
}

h1, h2, h3, h4, h5, h6 {
    margin: 0 0 var(--space-2);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    letter-spacing: var(--tracking-tight);
    color: var(--fg-default);
}

h1 { font-size: var(--text-2xl); }
h2 { font-size: var(--text-xl); }
h3 { font-size: var(--text-lg); }
h4 { font-size: var(--text-md); }

p { margin: 0 0 var(--space-3); }

a {
    color: var(--brand-secondary);
    text-decoration: none;
    transition: color var(--duration-fast) var(--ease-out);
}

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

img, svg, video { max-width: 100%; height: auto; display: block; }

hr {
    border: 0;
    border-top: 1px solid var(--border-subtle);
    margin: var(--space-5) 0;
    opacity: 1;
}

::selection {
    background: var(--brand-200);
    color: var(--brand-900);
}

/* Scrollbars styled to match the surface rather than the OS. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--border-strong) transparent;
}

*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-track { background: transparent; }

*::-webkit-scrollbar-thumb {
    background: var(--border-default);
    border-radius: var(--radius-pill);
    border: 3px solid transparent;
    background-clip: content-box;
}

*::-webkit-scrollbar-thumb:hover { background-clip: content-box; background-color: var(--border-strong); }

/* Two numeric utilities, deliberately different:
 *
 *   .tabular — even digit widths, but the text keeps the page direction.
 *              Use for anything that mixes numbers with words ("16 د 55 ث"),
 *              where forcing LTR would reorder the Arabic around the digits.
 *
 *   .num     — even digit widths *and* isolated LTR. Use only for pure
 *              identifiers: file numbers, order numbers, timestamps.
 */
.tabular {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
}

.num {
    font-variant-numeric: tabular-nums;
    font-feature-settings: "tnum" 1;
    direction: ltr;
    unicode-bidi: isolate;
}

/* The rule above keeps "2026-08-08 14:32" in reading order, but it also makes
   `start` resolve to left — which left-aligned every block-level number sitting
   in an RTL card. Restore the page's own alignment explicitly. Bootstrap's
   .text-center / .text-end utilities are !important, so a deliberately centred
   number still wins. */
[dir="rtl"] .num { text-align: right; }
[dir="ltr"] .num { text-align: left; }

/* ==========================================================  2. bootstrap bridge */
/* Bootstrap ships its own variables; point them at our tokens so utility
   classes and components inherit the design system automatically.          */

:root {
    --bs-body-font-family: var(--font-sans);
    --bs-body-font-size: var(--text-base);
    --bs-body-color: var(--fg-default);
    --bs-body-bg: var(--bg-canvas);
    --bs-emphasis-color: var(--fg-default);
    --bs-secondary-color: var(--fg-muted);
    --bs-border-color: var(--border-default);
    --bs-border-radius: var(--radius-md);
    --bs-border-radius-sm: var(--radius-sm);
    --bs-border-radius-lg: var(--radius-lg);
    --bs-link-color: var(--brand-secondary);
    --bs-link-hover-color: var(--brand-accent);
    --bs-primary: var(--brand-primary);
    --bs-primary-rgb: 21, 80, 138;
    --bs-focus-ring-color: var(--ring);
    --bs-heading-color: var(--fg-default);
}

.text-muted,
.text-muted-2 { color: var(--fg-muted) !important; }

.text-subtle { color: var(--fg-subtle) !important; }

/* ==============================================================  3. layout shell */

.app-shell {
    display: flex;
    flex-direction: column;
    min-height: 100svh;

    /*
     * Room for the fixed top bar, once, on the shell.
     *
     * It was on .app-main, and three later rules reset that padding — so on a
     * desktop the first card sat twenty pixels *under* the bar, and the sidebar
     * started at the top of the window behind it. Carried by the shell, every
     * child begins below the bar exactly as they did when the bar was in flow,
     * and a rule that adjusts .app-main's own spacing cannot undo it.
     */
    padding-block-start: calc(var(--topbar-h) + var(--safe-top));
}

.app-body {
    flex: 1;
    display: flex;
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
}

.app-main {
    flex: 1;
    min-width: 0;                        /* lets flex children shrink & scroll */
    padding-inline: var(--gutter);
    padding-block: var(--space-5)
        calc(var(--bottomnav-h) + var(--safe-bottom) + var(--space-6));
}

.page-container {
    width: 100%;
    max-width: 100%;
    margin-inline: auto;
}

.page-container--narrow { max-width: 720px; }
.page-container--reading { max-width: var(--reading-max); }

/* The patient shell: wide enough for two columns, never wider than is
   comfortable to read across. */
.page-container--patient { max-width: 1100px; }
.topbar__inner--patient { max-width: 1100px; }

/* Standard page header: title, subtitle, actions — wraps gracefully. */
.page-head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-bottom: var(--section-gap);
}

.page-head__title {
    font-size: var(--text-xl);
    margin: 0;
}

.page-head__subtitle {
    color: var(--fg-muted);
    font-size: var(--text-sm);
    margin: var(--space-1) 0 0;
}

.page-head__actions {
    margin-inline-start: auto;
    display: flex;
    gap: var(--space-2);
    flex-wrap: wrap;
}

/* Auto-fitting grids: the count adapts to width, no breakpoint maths. */
.grid { display: grid; gap: var(--space-4); }
.grid--stats     { grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr)); }
.grid--cards     { grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); }
.grid--wide      { grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); }
.grid--choices   { grid-template-columns: repeat(auto-fit, minmax(min(100%, 160px), 1fr)); }

/* A main column that keeps a sidebar of its own without a media query. */

.split--main-first > :first-child { grid-column: span 1; }

@media (min-width: 1024px) {
    .split--7-5 { grid-template-columns: 7fr 5fr; }
    .split--8-4 { grid-template-columns: 8fr 4fr; }
    .split--4-8 { grid-template-columns: 4fr 8fr; }
}

@media (min-width: 768px) {
    .app-main {
        padding-block: var(--space-6) var(--space-8);
    }
}

/* ==============================================================  4. navigation */

/* ---------------------------------------------------------------- top bar */

/*
 * Fixed, not sticky.
 *
 * Sticky held on a desktop but drifted on a phone: during momentum scrolling
 * iOS repaints a sticky element behind the frame it is pinned to, so the bar
 * appears to float loose for a moment on every flick. Fixed is pinned to the
 * viewport and cannot lag. The main column pays for it with padding equal to
 * the bar's height.
 */
.topbar {
    position: fixed;
    inset-inline: 0;
    top: 0;
    z-index: var(--z-topbar);
    height: calc(var(--topbar-h) + var(--safe-top));
    padding-top: var(--safe-top);
    padding-inline: var(--gutter);
    display: flex;
    align-items: center;
    gap: var(--space-3);

    background: var(--bg-glass);
    backdrop-filter: saturate(180%) blur(16px);
    -webkit-backdrop-filter: saturate(180%) blur(16px);
    border-bottom: 1px solid var(--border-subtle);
}

/* Fallback for browsers without backdrop-filter — solid, never transparent. */
@supports not (backdrop-filter: blur(1px)) {
    .topbar { background: var(--bg-surface); }
}

.topbar__inner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: 100%;
    max-width: var(--content-max);
    margin-inline: auto;
}

.topbar__actions {
    margin-inline-start: auto;
    display: flex;
    align-items: center;
    gap: var(--space-1);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: var(--weight-bold);
    font-size: var(--text-md);
    color: var(--fg-default);
    letter-spacing: var(--tracking-tight);
}

.brand:hover { color: var(--fg-default); }

.brand__mark {
    inline-size: 36px;
    block-size: 36px;
    border-radius: var(--radius-sm);
    background: var(--gradient-brand);
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 1.05rem;
    flex-shrink: 0;
    box-shadow: var(--shadow-sm);
}

.brand__text { display: none; }

@media (min-width: 480px) {
    .brand__text { display: inline; }
}

/* Icon button — the workhorse of every bar in the app. */
.icon-btn {
    position: relative;
    inline-size: 40px;
    block-size: 40px;
    display: inline-grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--fg-muted);
    font-size: 1.1rem;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
}

.icon-btn:hover { background: var(--bg-surface-3); color: var(--fg-default); }
.icon-btn:active { transform: scale(0.94); }

.icon-btn__badge {
    position: absolute;
    inset-block-start: 4px;
    inset-inline-end: 4px;
    min-inline-size: 17px;
    block-size: 17px;
    padding-inline: 4px;
    border-radius: var(--radius-pill);
    background: var(--hue-danger);
    color: #fff;
    font-size: 10px;
    font-weight: var(--weight-bold);
    display: grid;
    place-items: center;
    line-height: 1;
}

/* ---------------------------------------------------------------- sidebar */

.sidebar {
    display: none;                       /* mobile uses the drawer instead */
    flex-direction: column;
    inline-size: var(--sidebar-w);
    flex-shrink: 0;
    padding: var(--space-4) var(--space-3);
    gap: var(--space-1);

    position: sticky;
    top: calc(var(--topbar-h) + var(--safe-top));
    block-size: calc(100svh - var(--topbar-h) - var(--safe-top));
    overflow-y: auto;
    overscroll-behavior: contain;

    border-inline-end: 1px solid var(--border-subtle);
    transition: inline-size var(--duration-normal) var(--ease-out);
}

@media (min-width: 1024px) {
    .sidebar { display: flex; }
}

.nav-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    border-radius: var(--radius-sm);
    color: var(--fg-muted);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    white-space: nowrap;
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.nav-item:hover {
    background: var(--bg-surface-3);
    color: var(--fg-default);
}

.nav-item.is-active {
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-weight: var(--weight-semibold);
}

.nav-item__icon {
    inline-size: 20px;
    font-size: 1.05rem;
    flex-shrink: 0;
    text-align: center;
}

.nav-item__badge {
    margin-inline-start: auto;
    background: var(--bg-surface-3);
    color: var(--fg-muted);
    border-radius: var(--radius-pill);
    padding: 1px var(--space-2);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
}

.sidebar__footer {
    margin-top: auto;
    padding: var(--space-5) var(--space-3) var(--space-2);
    font-size: var(--text-2xs);
    color: var(--fg-subtle);
    text-align: center;
    border-top: 1px solid var(--border-subtle);
}

.nav-group__label {
    padding: var(--space-4) var(--space-3) var(--space-1);
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--fg-subtle);
    white-space: nowrap;
}

/* Collapsed rail — icons only, labels revealed on hover as a tooltip. */
.app-shell.is-sidebar-collapsed .sidebar { inline-size: var(--sidebar-w-collapsed); }

.app-shell.is-sidebar-collapsed .sidebar .nav-item {
    justify-content: center;
    padding-inline: var(--space-2);
}

.app-shell.is-sidebar-collapsed .sidebar .nav-item__label,
.app-shell.is-sidebar-collapsed .sidebar .nav-item__badge,
.app-shell.is-sidebar-collapsed .sidebar .nav-group__label {
    display: none;
}

.app-shell.is-sidebar-collapsed .sidebar .nav-item {
    position: relative;
}

.app-shell.is-sidebar-collapsed .sidebar .nav-item::after {
    content: attr(data-label);
    position: absolute;
    inset-inline-start: calc(100% + var(--space-2));
    padding: var(--space-1) var(--space-3);
    background: var(--fg-default);
    color: var(--bg-surface);
    font-size: var(--text-xs);
    border-radius: var(--radius-xs);
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transform: translateX(calc(-4px * var(--dir-sign, 1)));
    transition: opacity var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
    z-index: var(--z-drawer);
}

.app-shell.is-sidebar-collapsed .sidebar .nav-item:hover::after {
    opacity: 1;
    transform: translateX(0);
}

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

.drawer {
    position: fixed;
    inset-block: 0;
    inset-inline-start: 0;
    inline-size: min(84vw, 320px);
    z-index: var(--z-drawer);

    background: var(--bg-surface);
    border-inline-end: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-xl);

    display: flex;
    flex-direction: column;
    padding: calc(var(--safe-top) + var(--space-4)) var(--space-3)
             calc(var(--safe-bottom) + var(--space-4));

    /* Off-canvas in the writing direction, so RTL slides from the right. */
    transform: translateX(calc(-100% * var(--dir-sign, 1)));
    transition: transform var(--duration-normal) var(--ease-out);
    visibility: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
}

.drawer.is-open {
    transform: translateX(0);
    visibility: visible;
}

.drawer__scrim {
    position: fixed;
    inset: 0;
    z-index: calc(var(--z-drawer) - 1);
    background: var(--bg-overlay);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--duration-normal) var(--ease-out),
                visibility var(--duration-normal);
}

.drawer__scrim.is-open { opacity: 1; visibility: visible; }

.drawer__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding-bottom: var(--space-4);
    margin-bottom: var(--space-2);
    border-bottom: 1px solid var(--border-subtle);
}

@media (min-width: 1024px) {
    .drawer, .drawer__scrim { display: none; }
}

/* ----------------------------------------------------------- bottom nav */

.bottomnav {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: var(--z-bottomnav);

    display: flex;
    justify-content: space-around;
    align-items: stretch;

    padding-bottom: var(--safe-bottom);
    background: var(--bg-glass);
    backdrop-filter: saturate(180%) blur(16px);
    -webkit-backdrop-filter: saturate(180%) blur(16px);
    border-top: 1px solid var(--border-subtle);
}

@supports not (backdrop-filter: blur(1px)) {
    .bottomnav { background: var(--bg-surface); }
}

.bottomnav__item {
    flex: 1;
    min-inline-size: 0;
    block-size: var(--bottomnav-h);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;

    color: var(--fg-subtle);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    position: relative;
    /* Comfortable target even for a shaking hand. */
    -webkit-tap-highlight-color: transparent;
}

.bottomnav__item i { font-size: 1.3rem; line-height: 1; }

.bottomnav__item.is-active {
    color: var(--brand-secondary);
    font-weight: var(--weight-bold);
}

.bottomnav__item.is-active::before {
    content: "";
    position: absolute;
    inset-block-start: 0;
    inline-size: 28px;
    block-size: 3px;
    border-radius: 0 0 var(--radius-pill) var(--radius-pill);
    background: var(--gradient-brand);
}

.bottomnav__badge {
    position: absolute;
    inset-block-start: 8px;
    inset-inline-end: calc(50% - 20px);
    min-inline-size: 16px;
    block-size: 16px;
    padding-inline: 4px;
    border-radius: var(--radius-pill);
    background: var(--hue-danger);
    color: #fff;
    font-size: 9px;
    font-weight: var(--weight-bold);
    display: grid;
    place-items: center;
}

@media (min-width: 1024px) {
    .bottomnav { display: none; }
}

/* Screens that keep the bottom bar on mobile need the padding; desktop
   reclaims it. */
@media (min-width: 1024px) {
    .app-main { padding-block-end: var(--space-9); }
}

/* ==============================================================  5. components */

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

.card,
.surface {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    color: var(--fg-default);
}

.card { --bs-card-bg: var(--bg-surface); --bs-card-border-color: var(--border-subtle); }

.card-header {
    background: transparent;
    border-bottom: 1px solid var(--border-subtle);
    padding: var(--space-4) var(--space-5);
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--fg-default);
}

.card-body { padding: var(--space-5); }
.card-footer { background: transparent; border-top: 1px solid var(--border-subtle); padding: var(--space-4) var(--space-5); }

.card--interactive {
    transition: transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
    will-change: transform;
}

.card--interactive:hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md);
    border-color: var(--border-default);
}

.card--glass {
    background: var(--bg-glass);
    border-color: var(--bg-glass-border);
    backdrop-filter: saturate(160%) blur(14px);
    -webkit-backdrop-filter: saturate(160%) blur(14px);
}

/* ------------------------------------------------------------- stat tile */

.stat {
    position: relative;
    padding: var(--space-5);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.stat::after {
    /* A whisper of brand light in the corner — it must never compete with
       the number, so it stays small, soft and behind the content. */
    content: "";
    position: absolute;
    inset-block-start: -55%;
    inset-inline-end: -18%;
    inline-size: 60%;
    aspect-ratio: 1;
    border-radius: 50%;
    background: var(--gradient-brand-soft);
    opacity: 0.55;
    pointer-events: none;
    z-index: 0;
}

.stat > * { position: relative; z-index: 1; }

.stat__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    position: relative;
}

.stat__label {
    font-size: var(--text-xs);
    color: var(--fg-muted);
    font-weight: var(--weight-medium);
}

.stat__value {
    font-size: var(--text-2xl);
    font-weight: var(--weight-black);
    line-height: 1.05;
    letter-spacing: var(--tracking-tight);
    margin-top: var(--space-2);
    position: relative;
}

.stat__icon {
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-sm);
    display: grid;
    place-items: center;
    font-size: 1.1rem;
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    flex-shrink: 0;
}

.stat__trend {
    font-size: var(--text-xs);
    color: var(--fg-muted);
    margin-top: var(--space-1);
}

/* ---------------------------------------------------------------- badges */

.status-badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    white-space: nowrap;
    line-height: 1.6;
}

.status-received  { background: var(--status-received-bg);  color: var(--status-received-fg); }
.status-reviewing { background: var(--status-reviewing-bg); color: var(--status-reviewing-fg); }
.status-preparing { background: var(--status-preparing-bg); color: var(--status-preparing-fg); }
.status-ready     { background: var(--status-ready-bg);     color: var(--status-ready-fg); }
.status-delivered { background: var(--status-delivered-bg); color: var(--status-delivered-fg); }
.status-cancelled { background: var(--status-cancelled-bg); color: var(--status-cancelled-fg); }

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    background: var(--bg-surface-3);
    border: 1px solid var(--border-subtle);
    color: var(--fg-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    white-space: nowrap;
}

.chip--brand { background: var(--accent-soft-bg); color: var(--accent-soft-fg); border-color: transparent; }

/* ---------------------------------------------------------- queue tokens */

.queue-chip {
    display: inline-grid;
    place-items: center;
    min-inline-size: 74px;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    background: var(--gradient-brand);
    color: #fff;
    font-size: var(--text-lg);
    font-weight: var(--weight-black);
    letter-spacing: var(--tracking-wide);
    box-shadow: var(--shadow-sm);
    line-height: 1.15;
}

.queue-chip--ghost {
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    box-shadow: none;
}

.window-chip {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-sm);
    background: var(--fg-default);
    color: var(--bg-surface);
    font-weight: var(--weight-bold);
    font-size: var(--text-sm);
}

/* =========================================================  patient portal */

.patient-hello {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    margin-bottom: var(--section-gap);
}

.patient-hello__eyebrow {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

.patient-hello__name {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
}

.patient-hello__file {
    margin-inline-start: auto;
    flex-shrink: 0;
    text-align: center;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    color: var(--fg-default);
    font-size: var(--text-sm);
    line-height: 1.3;
}

.patient-hello__file:hover { color: var(--fg-default); border-color: var(--border-default); }

/* One column on a phone; request beside history once there is room *and*
   there is actually history to show — an empty aside would otherwise steal
   half the width and leave the main card cramped. */
.patient-grid {
    display: grid;
    gap: var(--section-gap);
    align-items: start;
    grid-template-columns: minmax(0, 1fr);
}

/* The identity card sits outside the grid, so it needs the section rhythm
   spelled out — without this it butts straight against the card below. */
.patient-grid__lead {
    margin-block-end: var(--section-gap);
}

.patient-grid__main {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    min-width: 0;
    inline-size: 100%;
    max-inline-size: 640px;
    margin-inline: auto;
}

.patient-grid__side { min-width: 0; }

.patient-grid__options {
    min-width: 0;
    inline-size: 100%;
    max-inline-size: 640px;
    margin-inline: auto;
}

@media (min-width: 900px) {
    .patient-grid:not(.patient-grid--single) {
        grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr);

        /* The two ways to order run the full width underneath, so a short
           history column does not leave half the page empty. */
        grid-template-areas:
            "main    side"
            "options options";
    }

    .patient-grid:not(.patient-grid--single) .patient-grid__main { grid-area: main; }
    .patient-grid:not(.patient-grid--single) .patient-grid__side { grid-area: side; }

    .patient-grid:not(.patient-grid--single) .patient-grid__options {
        grid-area: options;
        max-inline-size: none;
        margin-inline: 0;
    }

    .patient-grid:not(.patient-grid--single) .patient-grid__main {
        max-inline-size: none;
        margin-inline: 0;
    }
}

.section-title {
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    color: var(--fg-muted);
    margin-bottom: var(--space-3);
}

/* ------------------------------------------------------- the active order */

.active-order {
    position: relative;
    overflow: hidden;
    padding: var(--space-5);
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-md);
}

/* A ready prescription earns a green frame — it is the one state that means
   "get up and walk to the window". */
.active-order.is-ready {
    border-color: var(--status-ready-fg);
    box-shadow: var(--shadow-lg), inset 0 0 0 1px var(--status-ready-fg);
}

.active-order__head {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
}

.active-order__label {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

.active-order__ticket {
    margin: 0;
    font-size: clamp(2.75rem, 2rem + 3.5vw, 4rem);
    font-weight: var(--weight-black);
    line-height: 1;
    letter-spacing: 0.01em;
    background: var(--gradient-brand);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.active-order__window {
    margin-inline-start: auto;
    text-align: center;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--status-ready-fg);
    color: #fff;
    flex-shrink: 0;
}

.active-order__window-label { display: block; font-size: var(--text-2xs); opacity: 0.85; }
.active-order__window-number { display: block; font-size: var(--text-2xl); font-weight: var(--weight-black); line-height: 1.1; }

.active-order__status {
    margin: var(--space-4) 0 0;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
}

.active-order.is-ready .active-order__status { color: var(--status-ready-fg); }

.active-order__facts {
    display: grid;
    /* Two even columns rather than auto-fit: on a wide card auto-fit produced a
       row of narrow tracks and the labels outgrew the values they belong to. */
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3) var(--space-5);
    margin-block: var(--space-5);
    padding-block: var(--space-4);
    border-block: 1px solid var(--border-subtle);
}

.fact { min-width: 0; }
.fact--wide { grid-column: 1 / -1; }
.fact__label { display: block; font-size: var(--text-2xs); color: var(--fg-muted); }
.fact__value { display: block; font-size: var(--text-lg); font-weight: var(--weight-bold); }
.fact__value--text { font-size: var(--text-sm); font-weight: var(--weight-semibold); }

/* --------------------------------------------------------- progress steps */

.steps {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--space-1);
    list-style: none;
    margin: var(--space-5) 0 0;
    padding: 0;
    counter-reset: step;
}

.steps__item {
    position: relative;
    text-align: center;
    min-width: 0;
}

/* The connector sits behind the dots and stops at the last step. */
.steps__item::before {
    content: "";
    position: absolute;
    inset-block-start: 17px;
    inset-inline-start: calc(-50% + 17px);
    inline-size: calc(100% - 34px);
    block-size: 2px;
    background: var(--border-default);
}

.steps__item:first-child::before { content: none; }
.steps__item.is-done::before,
.steps__item.is-now::before { background: var(--brand-500); }

.steps__dot {
    position: relative;
    inline-size: 34px;
    block-size: 34px;
    margin-inline: auto;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--bg-surface-3);
    color: var(--fg-subtle);
    border: 2px solid var(--border-default);
    font-size: 0.85rem;
}

.steps__item.is-done .steps__dot {
    background: var(--brand-500);
    border-color: var(--brand-500);
    color: #fff;
}

.steps__item.is-now .steps__dot {
    background: var(--gradient-brand);
    border-color: transparent;
    color: #fff;
    box-shadow: 0 0 0 4px var(--accent-soft-bg);
}

.steps__label {
    display: block;
    margin-top: var(--space-2);
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    line-height: 1.3;
}

.steps__item.is-now .steps__label {
    color: var(--fg-default);
    font-weight: var(--weight-bold);
}

/* Under ~360 px four labels cannot fit; keep the dots, drop the words. */
@media (max-width: 359px) {
    .steps__label { display: none; }
}

/* ------------------------------------------------------------ action tile */

.action-tile {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);
    color: var(--fg-default);
    transition: transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
    -webkit-tap-highlight-color: transparent;
}

.action-tile:hover {
    color: var(--fg-default);
    border-color: var(--brand-300);
    box-shadow: var(--shadow-md);
}

.action-tile:active { transform: scale(0.995); }

.action-tile__icon {
    inline-size: 46px;
    block-size: 46px;
    flex-shrink: 0;
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    font-size: 1.25rem;
    background: var(--gradient-brand);
    color: #fff;
}

.action-tile__icon--service {
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

.action-tile__title {
    display: block;
    font-weight: var(--weight-bold);
    font-size: var(--text-md);
}

.action-tile__meta {
    display: block;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    margin-top: 2px;
}

.action-tile__chevron { color: var(--fg-subtle); flex-shrink: 0; }

/* ---------------------------------------------------------------- history */

.history { max-block-size: 460px; overflow-y: auto; }

.history__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-5);
    border-bottom: 1px solid var(--border-subtle);
    color: var(--fg-default);
}

.history__row:last-child { border-bottom: 0; }
.history__row:hover { background: var(--bg-surface-3); color: var(--fg-default); }

.history__ticket {
    flex-shrink: 0;
    font-weight: var(--weight-bold);
    font-size: var(--text-sm);
    color: var(--fg-muted);
    min-inline-size: 3.5rem;
}

/* ------------------------------------------------------------ order card */

.order-card {
    position: relative;
    padding: var(--space-4);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    transition: transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
    contain: layout style;
}

@media (hover: hover) {
    .order-card:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
}

.order-card.is-locked {
    border-color: var(--status-preparing-fg);
    background:
        linear-gradient(var(--status-preparing-bg), var(--status-preparing-bg)) padding-box;
}

.order-card--new { animation: pulse-ring var(--duration-slow) var(--ease-out) 3; }

.order-card::before {
    /* Priority rail on the leading edge. */
    content: "";
    position: absolute;
    inset-block: var(--space-4);
    inset-inline-start: 0;
    inline-size: 3px;
    border-radius: var(--radius-pill);
    background: transparent;
}

.order-card.priority-urgent::before  { background: var(--hue-danger); }
.order-card.priority-elderly::before { background: var(--brand-500); }

/* Actions stretch full width on a phone (thumb targets) but stay compact on
   a desktop, where a full-bleed button reads as a banner rather than a control. */
.order-card__actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-4);
}

.order-card__actions > form { flex: 1; }
.order-card__actions .btn { width: 100%; }

@media (min-width: 768px) {
    .order-card__actions { justify-content: flex-end; }
    .order-card__actions > form { flex: 0 0 auto; }
    .order-card__actions .btn { width: auto; min-width: 132px; }
}

@keyframes pulse-ring {
    0%   { box-shadow: 0 0 0 0 rgb(47 169 224 / 55%); }
    100% { box-shadow: 0 0 0 16px rgb(47 169 224 / 0%); }
}

/* --------------------------------------------------------------- buttons */

.btn {
    --bs-btn-border-radius: var(--radius-sm);
    font-family: var(--font-sans);
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    border-radius: var(--radius-sm);
    padding: var(--space-3) var(--space-4);
    transition: transform var(--duration-instant) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                background-color var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
    /* Never smaller than a comfortable touch target. */
    min-block-size: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
}

.btn:active { transform: translateY(1px); }

.btn-lg { font-size: var(--text-base); padding: var(--space-4) var(--space-6); min-block-size: 50px; }
.btn-sm { font-size: var(--text-xs); padding: var(--space-2) var(--space-3); min-block-size: 34px; }

.btn-primary {
    --bs-btn-color: #fff;
    --bs-btn-bg: var(--brand-primary);
    --bs-btn-border-color: transparent;
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--brand-600);
    --bs-btn-hover-border-color: transparent;
    --bs-btn-active-bg: var(--brand-800);
    --bs-btn-active-border-color: transparent;
    --bs-btn-disabled-bg: var(--brand-primary);
    --bs-btn-disabled-border-color: transparent;
    background-image: var(--gradient-sheen);
    box-shadow: var(--shadow-sm);
}

.btn-primary:hover { box-shadow: var(--shadow-md); }

.btn-outline-primary {
    --bs-btn-color: var(--brand-secondary);
    --bs-btn-border-color: var(--border-default);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--brand-primary);
    --bs-btn-hover-border-color: var(--brand-primary);
    --bs-btn-active-bg: var(--brand-800);
}

.btn-light,
.btn-secondary {
    --bs-btn-color: var(--fg-default);
    --bs-btn-bg: var(--bg-surface-3);
    --bs-btn-border-color: var(--border-subtle);
    --bs-btn-hover-color: var(--fg-default);
    --bs-btn-hover-bg: var(--bg-inset);
    --bs-btn-hover-border-color: var(--border-default);
}

/* A dismissal, not a choice — no fill, no border, so it never competes with
   the primary action beside it. Used to close a sheet without acting on it. */
.btn-ghost {
    --bs-btn-color: var(--fg-subtle);
    --bs-btn-bg: transparent;
    --bs-btn-border-color: transparent;
    --bs-btn-hover-color: var(--fg-default);
    --bs-btn-hover-bg: var(--bg-surface-2);
    --bs-btn-hover-border-color: transparent;
    box-shadow: none;
}

.btn-danger  { --bs-btn-bg: var(--hue-danger);  --bs-btn-border-color: transparent; --bs-btn-hover-bg: #991b1b; }
.btn-success { --bs-btn-bg: var(--hue-success); --bs-btn-border-color: transparent; --bs-btn-hover-bg: #166534; }

.btn-outline-danger {
    --bs-btn-color: var(--hue-danger);
    --bs-btn-border-color: var(--border-default);
    --bs-btn-hover-bg: var(--hue-danger);
    --bs-btn-hover-border-color: var(--hue-danger);
    --bs-btn-hover-color: #fff;
}

.btn-outline-secondary {
    --bs-btn-color: var(--fg-muted);
    --bs-btn-border-color: var(--border-default);
    --bs-btn-hover-bg: var(--bg-surface-3);
    --bs-btn-hover-color: var(--fg-default);
    --bs-btn-hover-border-color: var(--border-strong);
}

.btn:disabled, .btn.disabled { opacity: 0.55; box-shadow: none; }

/* ----------------------------------------------------------------- forms */

.form-label {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
    margin-bottom: var(--space-2);
}

.form-control,
.form-select {
    font-family: var(--font-sans);
    font-size: var(--text-base);
    padding: var(--space-3) var(--space-4);
    min-block-size: 46px;
    color: var(--fg-default);
    background-color: var(--bg-surface);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-sm);
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.form-control::placeholder { color: var(--fg-subtle); }

.form-control:focus,
.form-select:focus {
    background-color: var(--bg-surface);
    color: var(--fg-default);
    border-color: var(--brand-500);
    box-shadow: var(--shadow-focus);
    outline: 0;
}

.form-control:disabled,
.form-select:disabled { background-color: var(--bg-inset); opacity: 0.7; }

.form-control.is-invalid,
.form-select.is-invalid { border-color: var(--hue-danger); }

.form-text { color: var(--fg-muted); font-size: var(--text-xs); margin-top: var(--space-1); }
.invalid-feedback { color: var(--hue-danger); font-size: var(--text-xs); }

.input-group > .form-control:focus,
.input-group > .form-select:focus { z-index: 3; }

.form-check-input {
    inline-size: 1.15em;
    block-size: 1.15em;
    margin-top: 0.2em;
    background-color: var(--bg-surface);
    border-color: var(--border-strong);
}

.form-check-input:checked {
    background-color: var(--brand-primary);
    border-color: var(--brand-primary);
}

.form-check-input:focus { box-shadow: var(--shadow-focus); border-color: var(--brand-500); }

.form-switch .form-check-input { inline-size: 2.35em; block-size: 1.3em; }

/* Big, thumb-friendly radio tiles for the patient flow. */
.choice-tile {
    position: relative;
    display: block;
    cursor: pointer;
    padding: var(--space-5) var(--space-4);
    text-align: center;
    background: var(--bg-surface);
    border: 2px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    transition: border-color var(--duration-fast) var(--ease-out),
                background var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-out);
    -webkit-tap-highlight-color: transparent;
}

.choice-tile input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}

.choice-tile:active { transform: scale(0.985); }

.choice-tile:has(input:focus-visible) { box-shadow: var(--shadow-focus); }

.choice-tile:has(input:checked) {
    border-color: var(--brand-500);
    background: var(--accent-soft-bg);
}

.choice-tile:has(input:checked) .choice-tile__check { opacity: 1; transform: scale(1); }

.choice-tile__icon {
    display: block;
    font-size: 1.6rem;
    line-height: 1;
    color: var(--brand-secondary);
}

.choice-tile__title {
    display: block;
    font-weight: var(--weight-bold);
    margin-top: var(--space-2);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
}

.choice-tile__meta {
    display: block;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    margin-top: var(--space-1);
    line-height: var(--leading-snug);
}

.choice-tile__check {
    position: absolute;
    inset-block-start: var(--space-2);
    inset-inline-end: var(--space-2);
    inline-size: 22px;
    block-size: 22px;
    border-radius: 50%;
    background: var(--brand-primary);
    color: #fff;
    display: grid;
    place-items: center;
    font-size: 0.7rem;
    opacity: 0;
    transform: scale(0.6);
    transition: opacity var(--duration-fast) var(--ease-out),
                transform var(--duration-fast) var(--ease-spring);
}

/* PIN entry — separated digits, always LTR. */
.pin-input {
    direction: ltr;
    text-align: center;
    letter-spacing: 0.7em;
    text-indent: 0.7em;                  /* re-centres the tracked text */
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------------- tables */

.table {
    --bs-table-bg: transparent;
    --bs-table-color: var(--fg-default);
    --bs-table-border-color: var(--border-subtle);
    --bs-table-hover-bg: var(--bg-surface-3);
    --bs-table-hover-color: var(--fg-default);
    margin-bottom: 0;
    font-size: var(--text-sm);
}

.table > :not(caption) > * > * {
    padding: var(--space-3) var(--space-4);
    background-color: transparent;
}

.table thead th {
    font-size: var(--text-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    font-weight: var(--weight-bold);
    color: var(--fg-subtle);
    background: var(--bg-surface-2);
    white-space: nowrap;
    border-bottom: 1px solid var(--border-subtle);
}

.table-wrap {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
}

/* Below the tablet breakpoint, tables become stacked cards — each cell
   labelled by its column via data-label. No horizontal scrolling on a phone. */
@media (max-width: 767.98px) {
    .table-stack thead { display: none; }

    .table-stack,
    .table-stack tbody,
    .table-stack tr,
    .table-stack td { display: block; width: 100%; }

    .table-stack tr {
        padding: var(--space-3) var(--space-4);
        border-bottom: 1px solid var(--border-subtle);
    }

    .table-stack tr:last-child { border-bottom: 0; }

    .table-stack td {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
        padding: var(--space-1) 0;
        border: 0;
    }

    .table-stack td::before {
        content: attr(data-label);
        font-size: var(--text-2xs);
        font-weight: var(--weight-bold);
        color: var(--fg-subtle);
        text-transform: uppercase;
        letter-spacing: var(--tracking-wide);
        flex-shrink: 0;
    }

    .table-stack td[data-label=""]::before { content: none; }

    /* A cell forced to LTR (an id, a timestamp) would otherwise flip its
       label to the wrong side of the stacked row. Reverse the flex axis so
       the number keeps its direction and the label keeps its position. */
    [dir="rtl"] .table-stack td.num { flex-direction: row-reverse; }
}

/* Dates and times read left-to-right in every locale. */
input[type="date"],
input[type="time"],
input[type="datetime-local"] {
    direction: ltr;
    text-align: start;
}

/* -------------------------------------------------------------- timeline */

.timeline {
    list-style: none;
    margin: 0;
    padding: 0;
    padding-inline-start: var(--space-6);
    position: relative;
}

.timeline::before {
    content: "";
    position: absolute;
    inset-block: 10px;
    inset-inline-start: 5px;
    inline-size: 2px;
    background: var(--border-subtle);
    border-radius: var(--radius-pill);
}

.timeline > li { position: relative; padding-block: var(--space-2); }

.timeline > li::before {
    content: "";
    position: absolute;
    inset-inline-start: calc(-1 * var(--space-6) + 1px);
    inset-block-start: 14px;
    inline-size: 12px;
    block-size: 12px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 3px solid var(--brand-500);
    box-shadow: 0 0 0 3px var(--bg-surface);
}

.timeline > li.is-muted::before { border-color: var(--border-strong); }

/* --------------------------------------------------------- progress rail */

.progress-steps { display: flex; gap: var(--space-1); }

.progress-steps span {
    flex: 1;
    block-size: 6px;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    transition: background var(--duration-normal) var(--ease-out);
}

.progress-steps span.is-done { background: var(--brand-500); }

.progress-steps span.is-current {
    background: var(--gradient-brand);
    animation: shimmer 1.8s var(--ease-in-out) infinite;
}

@keyframes shimmer { 50% { opacity: 0.45; } }

.progress {
    background: var(--bg-inset);
    border-radius: var(--radius-pill);
    block-size: 8px;
}

.progress-bar { background: var(--gradient-brand); }

/* ---------------------------------------------------------- notifications */

.notification-item {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border-subtle);
    color: var(--fg-default);
    transition: background var(--duration-fast) var(--ease-out);
}

.notification-item:last-child { border-bottom: 0; }
.notification-item:hover { background: var(--bg-surface-3); color: var(--fg-default); }
.notification-item.is-unread { background: var(--accent-soft-bg); }

.notification-item__icon {
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-sm);
    display: grid;
    place-items: center;
    background: var(--bg-surface-3);
    color: var(--brand-secondary);
    flex-shrink: 0;
}

/*
 * The icon carries the tone, not the row.
 *
 * "Your medicine is ready" and "we received your prescription" used to share
 * one grey circle — which meant the one notification a patient must act on
 * now looked exactly like the nine they could read whenever. The colour is
 * on the icon alone, not the row background, so an unread row's own tint
 * (`.is-unread`) still reads clearly against it.
 */
.notification-item--success .notification-item__icon { background: hsl(var(--h-success) 55% 93%); color: hsl(var(--h-success) 40% 32%); }
.notification-item--warning .notification-item__icon { background: hsl(var(--h-warning) 85% 90%); color: hsl(var(--h-warning) 55% 30%); }
.notification-item--danger .notification-item__icon { background: hsl(var(--h-danger) 80% 93%); color: hsl(var(--h-danger) 45% 40%); }

/* The day heading above each group of rows. */
.notif-group__label {
    padding: var(--space-2) var(--space-1);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    color: var(--fg-muted);
}

/* ------------------------------------------------------- confirm dialogue */

/*
 * The platform's own "are you sure", in place of window.confirm.
 *
 * `<dialog>` puts this in the browser's top layer, so it sits above the sticky
 * bar, the drawer and the toasts without entering the z-index argument at all.
 */

.askbox {
    inline-size: min(420px, calc(100vw - var(--space-5)));
    padding: 0;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    color: var(--text-primary);
    box-shadow: var(--shadow-lg);
    /* A dialog is centred by the browser; margin:auto is what tells it to. */
    margin: auto;
}

.askbox::backdrop {
    /* Deliberately dark. The page behind is not available while this is open,
       and a faint wash reads as decoration rather than as a stop. */
    background: rgb(15 23 42 / 55%);
    backdrop-filter: blur(2px);
}

.askbox[open] { animation: askbox-in var(--duration-normal) var(--ease-spring); }

.askbox__form {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-3);
    padding: var(--space-5) var(--space-5) var(--space-4);
}

.askbox__icon {
    inline-size: 52px;
    block-size: 52px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 1.4rem;
    color: var(--brand-500);
    background: color-mix(in srgb, var(--brand-500) 12%, transparent);
}

.askbox[data-tone="danger"] .askbox__icon {
    color: var(--hue-danger);
    background: color-mix(in srgb, var(--hue-danger) 12%, transparent);
}

.askbox[data-tone="warning"] .askbox__icon {
    color: var(--hue-warning);
    background: color-mix(in srgb, var(--hue-warning) 14%, transparent);
}

.askbox__title {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
}

.askbox__text {
    margin: 0;
    font-size: var(--text-sm);
    line-height: 1.7;
    color: var(--text-secondary);
}

.askbox__actions {
    display: flex;
    gap: var(--space-2);
    inline-size: 100%;
    margin-block-start: var(--space-2);
}

/* Equal halves: neither button is the accidental one, and the destructive
   choice is never the wider target. */
.askbox__actions .btn { flex: 1 1 0; min-inline-size: 0; }

@keyframes askbox-in {
    from { opacity: 0; transform: translateY(10px) scale(0.96); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

@media (prefers-reduced-motion: reduce) {
    .askbox[open] { animation: none; }
}

/* --------------------------------------------------------------- toasts */

.toast-stack {
    position: fixed;
    /* Below the sticky bar, never over it — a toast must not hide the way out. */
    inset-block-start: calc(var(--topbar-h) + var(--safe-top) + var(--space-3));
    inset-inline: var(--space-3);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    pointer-events: none;
}

@media (min-width: 640px) {
    .toast-stack { inset-inline-start: auto; inline-size: min(380px, 92vw); align-items: stretch; }
}

.toast-card {
    inline-size: 100%;
    max-inline-size: 420px;
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-lg);
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    animation: toast-in var(--duration-normal) var(--ease-spring);
    /* Positioned so the countdown bar can sit along the bottom edge, and
       clipped so it stays inside the rounded corners. */
    position: relative;
    overflow: hidden;
}

.toast-card.is-leaving { animation: toast-out var(--duration-fast) var(--ease-out) forwards; }

.toast-card__icon {
    inline-size: 26px;
    block-size: 26px;
    border-radius: var(--radius-xs);
    display: grid;
    place-items: center;
    flex-shrink: 0;
    font-size: 0.85rem;
    color: #fff;
}

.toast-card--success .toast-card__icon { background: var(--hue-success); }
.toast-card--error   .toast-card__icon { background: var(--hue-danger); }
.toast-card--warning .toast-card__icon { background: var(--hue-warning); }
.toast-card--info    .toast-card__icon { background: var(--brand-500); }

/* The heading line, the text under it, and the countdown along the edge. */

.toast-card__body {
    flex: 1 1 auto;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.toast-card__label {
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    letter-spacing: .01em;
}

.toast-card--success .toast-card__label { color: var(--hue-success); }
.toast-card--error   .toast-card__label { color: var(--hue-danger); }
.toast-card--warning .toast-card__label { color: var(--hue-warning); }
.toast-card--info    .toast-card__label { color: var(--brand-500); }

.toast-card__life {
    position: absolute;
    inset-block-end: 0;
    inset-inline: 0;
    block-size: 2px;
    transform-origin: inline-start;
    background: currentColor;
    opacity: .35;
    animation: toast-life linear forwards;
}

.toast-card--success .toast-card__life { color: var(--hue-success); }
.toast-card--error   .toast-card__life { color: var(--hue-danger); }
.toast-card--warning .toast-card__life { color: var(--hue-warning); }
.toast-card--info    .toast-card__life { color: var(--brand-500); }

@keyframes toast-life {
    from { transform: scaleX(1); }
    to   { transform: scaleX(0); }
}

@media (prefers-reduced-motion: reduce) {
    .toast-card__life { display: none; }
}

@keyframes toast-in {
    from { opacity: 0; transform: translateY(-14px) scale(0.97); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes toast-out {
    to { opacity: 0; transform: translateY(-8px) scale(0.98); }
}

/* --------------------------------------------------------- live indicator */

.live-dot {
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: var(--hue-success);
    box-shadow: 0 0 0 3px rgb(21 128 61 / 18%);
    display: inline-block;
    transition: background var(--duration-fast) var(--ease-out);
}

.live-dot.is-connecting { background: var(--hue-warning); box-shadow: 0 0 0 3px rgb(180 83 9 / 18%); animation: shimmer 1.4s infinite; }
.live-dot.is-offline    { background: var(--hue-danger);  box-shadow: 0 0 0 3px rgb(185 28 28 / 18%); }

/* --------------------------------------------------------------- modals */

.modal-content {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
}

.modal-header, .modal-footer { border-color: var(--border-subtle); padding: var(--space-4) var(--space-5); }
.modal-body { padding: var(--space-5); }
.modal-backdrop { --bs-backdrop-bg: #06111d; --bs-backdrop-opacity: 0.55; }

/* On phones a dialog reads better as a bottom sheet. */
@media (max-width: 575.98px) {
    .modal-dialog:not(.modal-fullscreen) {
        margin: 0;
        position: fixed;
        inset-inline: 0;
        inset-block-end: 0;
        max-inline-size: 100%;
    }

    .modal-dialog:not(.modal-fullscreen) .modal-content {
        border-radius: var(--radius-xl) var(--radius-xl) 0 0;
        padding-bottom: var(--safe-bottom);
    }
}

.dropdown-menu {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    padding: var(--space-2);
    font-size: var(--text-sm);
    --bs-dropdown-link-color: var(--fg-default);
    --bs-dropdown-link-hover-bg: var(--bg-surface-3);
    --bs-dropdown-link-hover-color: var(--fg-default);
    --bs-dropdown-link-active-bg: var(--accent-soft-bg);
    --bs-dropdown-link-active-color: var(--accent-soft-fg);
    --bs-dropdown-header-color: var(--fg-subtle);
    --bs-dropdown-divider-bg: var(--border-subtle);
}

.dropdown-item { border-radius: var(--radius-xs); padding: var(--space-2) var(--space-3); }

.alert {
    border-radius: var(--radius-md);
    border-width: 1px;
    padding: var(--space-4);
    font-size: var(--text-sm);
}

.alert-warning { background: var(--status-preparing-bg); color: var(--status-preparing-fg); border-color: transparent; }
.alert-success { background: var(--status-ready-bg);     color: var(--status-ready-fg);     border-color: transparent; }
.alert-danger  { background: var(--status-cancelled-bg); color: var(--status-cancelled-fg); border-color: transparent; }
.alert-info    { background: var(--status-reviewing-bg); color: var(--status-reviewing-fg); border-color: transparent; }
.alert-secondary, .alert-light { background: var(--bg-surface-3); color: var(--fg-muted); border-color: var(--border-subtle); }

.list-group {
    --bs-list-group-bg: var(--bg-surface);
    --bs-list-group-color: var(--fg-default);
    --bs-list-group-border-color: var(--border-subtle);
    --bs-list-group-action-hover-bg: var(--bg-surface-3);
    --bs-list-group-action-hover-color: var(--fg-default);
}

.pagination {
    --bs-pagination-bg: var(--bg-surface);
    --bs-pagination-color: var(--fg-muted);
    --bs-pagination-border-color: var(--border-subtle);
    --bs-pagination-hover-bg: var(--bg-surface-3);
    --bs-pagination-hover-color: var(--fg-default);
    --bs-pagination-active-bg: var(--brand-primary);
    --bs-pagination-active-border-color: var(--brand-primary);
    --bs-pagination-disabled-bg: var(--bg-surface);
    --bs-pagination-disabled-color: var(--fg-subtle);
    --bs-pagination-border-radius: var(--radius-sm);
}

/* --------------------------------------------------------------- charts */

.bar-chart {
    display: flex;
    align-items: flex-end;
    gap: 3px;
    block-size: 150px;
}

.bar-chart__bar {
    flex: 1;
    min-inline-size: 3px;
    border-radius: var(--radius-xs) var(--radius-xs) 0 0;
    background: var(--gradient-brand);
    opacity: 0.85;
    transition: opacity var(--duration-fast) var(--ease-out);
    transform-origin: bottom;
    animation: grow-up var(--duration-slow) var(--ease-out) backwards;
}

.bar-chart__bar:hover { opacity: 1; }

@keyframes grow-up { from { transform: scaleY(0); } }

/* ==================================================================  6. states */

.skeleton {
    background: linear-gradient(90deg,
        var(--skeleton-base) 25%,
        var(--skeleton-sheen) 37%,
        var(--skeleton-base) 63%);
    background-size: 400% 100%;
    animation: skeleton-sweep 1.4s ease-in-out infinite;
    border-radius: var(--radius-xs);
    color: transparent !important;
    user-select: none;
}

.skeleton--text { block-size: 0.85em; margin-block: 0.25em; }
.skeleton--title { block-size: 1.4em; inline-size: 45%; }
.skeleton--line { block-size: 0.8em; inline-size: 100%; }
.skeleton--line-short { block-size: 0.8em; inline-size: 60%; }
.skeleton--block { block-size: 88px; border-radius: var(--radius-md); }
.skeleton--circle { border-radius: 50%; }

@keyframes skeleton-sweep {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.empty-state {
    text-align: center;
    padding: var(--space-9) var(--space-5);
    color: var(--fg-muted);
}

.empty-state__icon {
    inline-size: 68px;
    block-size: 68px;
    margin-inline: auto;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-xl);
    background: var(--gradient-brand-soft);
    color: var(--brand-secondary);
    display: grid;
    place-items: center;
    font-size: 1.8rem;
}

.empty-state__title { font-weight: var(--weight-bold); color: var(--fg-default); font-size: var(--text-md); }
.empty-state__body { font-size: var(--text-sm); margin-top: var(--space-1); }

/* Shorthand form: <div class="empty-state"><i class="bi bi-inbox"></i>Nothing here</div> */
.empty-state > i:first-child {
    display: block;
    inline-size: 68px;
    block-size: 68px;
    margin-inline: auto;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-xl);
    background: var(--gradient-brand-soft);
    color: var(--brand-secondary);
    font-size: 1.8rem;
    line-height: 68px;
}

/* Tables wider than their container scroll inside the card, not the page. */
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

/* A table inside an empty-state cell should not inherit the card chrome. */
.table .empty-state { padding-block: var(--space-7); }

.is-loading { position: relative; pointer-events: none; }
.is-loading > * { opacity: 0.5; }

.spinner {
    inline-size: 1em;
    block-size: 1em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    display: inline-block;
    animation: spin 0.7s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* ===============================================================  7. utilities */

.stack     { display: flex; flex-direction: column; gap: var(--space-4); }
.stack-sm  { display: flex; flex-direction: column; gap: var(--space-2); }
.row-flex  { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.push-end  { margin-inline-start: auto; }
.min-w-0   { min-inline-size: 0; }
.truncate  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.clamp-2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.text-gradient {
    background: var(--gradient-brand);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.surface-inset { background: var(--bg-inset); border-radius: var(--radius-md); }

.hide-mobile { display: none; }
@media (min-width: 768px) { .hide-mobile { display: revert; } }

.only-mobile { display: revert; }
@media (min-width: 768px) { .only-mobile { display: none !important; } }

.hide-tablet-down { display: none; }
@media (min-width: 1024px) { .hide-tablet-down { display: revert; } }

/* Horizontal scroller for filter chips on small screens. */
.scroll-x {
    display: flex;
    gap: var(--space-2);
    overflow-x: auto;
    scroll-snap-type: x proximity;
    padding-bottom: var(--space-1);
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}

.scroll-x::-webkit-scrollbar { display: none; }
.scroll-x > * { scroll-snap-align: start; flex-shrink: 0; }

/* ==========================================  8. motion, print, accessibility */

:focus-visible {
    outline: 2px solid var(--brand-500);
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

.skip-link {
    position: absolute;
    inset-inline-start: -9999px;
    z-index: var(--z-toast);
}

.skip-link:focus {
    inset-inline-start: var(--space-4);
    inset-block-start: var(--space-4);
    padding: var(--space-3) var(--space-5);
    background: var(--bg-surface);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-lg);
}

.visually-hidden-focusable:not(:focus):not(:focus-within) {
    position: absolute !important;
    inline-size: 1px;
    block-size: 1px;
    overflow: hidden;
    clip-path: inset(50%);
}

/* Honour the user's motion preference across every animation we ship. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }

    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
    }
}

@media (prefers-contrast: more) {
    :root { --border-subtle: var(--border-strong); }
    .status-badge, .chip { border: 1px solid currentColor; }
}

@media print {
    .topbar, .sidebar, .bottomnav, .drawer, .drawer__scrim,
    .no-print, .btn, .toast-stack { display: none !important; }

    body { background: #fff; color: #000; }
    .card, .table-wrap { box-shadow: none; border: 1px solid #999; }
    .app-main { padding: 0; }
    a[href]::after { content: none; }
}

/* ------------------------------------------------------- secondary request */

.order-brief {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-xs);
    color: var(--fg-default);
    transition: border-color var(--duration-fast) var(--ease-out);
}

.order-brief:hover { color: var(--fg-default); border-color: var(--border-default); }

.order-brief__ticket {
    flex-shrink: 0;
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    color: var(--accent-soft-fg);
    background: var(--accent-soft-bg);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-sm);
}

/* ------------------------------------------------------- form step heading */

.form-step {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    margin: 0 0 var(--space-1);
}

.form-step__num {
    inline-size: 26px;
    block-size: 26px;
    flex-shrink: 0;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-size: var(--text-xs);
    font-weight: var(--weight-black);
}

.form-step__num--plain { background: transparent; color: var(--fg-muted); }

.form-step__optional {
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
    color: var(--fg-subtle);
    background: var(--bg-surface-3);
    padding: 2px var(--space-2);
    border-radius: var(--radius-pill);
}

.form-step__hint {
    font-size: var(--text-xs);
    color: var(--fg-muted);
    margin: 0 0 var(--space-3);
}

/* Sticky submit: on a tall phone the button stays within thumb reach without
   the patient having to scroll back down after picking a clinic. */
/* Room for the floating bar to hover over without hiding anything. */
.stack > .submit-bar { margin-top: var(--space-2); }

.submit-bar {
    position: sticky;
    inset-block-end: calc(var(--bottomnav-h) + var(--safe-bottom) + var(--space-2));
    z-index: var(--z-sticky);
    padding-block: var(--space-2);
}

/*
 * The plate that fades the list out behind the button.
 *
 * It bleeds past the bar to reach the screen edges, so the fade covers the
 * gutter too — but the bleed has to be the gutter, not a guess at it. Hard-coded
 * at 2rem it overhung a phone's 17px gutter by 15px, and 15px of overflow on a
 * right-to-left page does not show up as a stray edge: it widens the scroll area
 * and slides the whole layout sideways. That was this page "leaning right".
 */
.submit-bar::before {
    content: "";
    position: absolute;
    inset-block: -1rem 0;
    inset-inline: calc(var(--gutter) * -1);
    background: linear-gradient(transparent, var(--bg-canvas) 45%);
    pointer-events: none;
    z-index: -1;
}

@media (min-width: 1024px) {
    .submit-bar { position: static; }
    .submit-bar::before { content: none; }
    .submit-bar .btn { inline-size: auto; min-inline-size: 240px; }
}

/* ------------------------------------------------------ the two order paths */

.options {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
}

.option-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-5);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);
    color: var(--fg-default);
    position: relative;
    overflow: hidden;
    min-block-size: 100%;
    transition: transform var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
    -webkit-tap-highlight-color: transparent;
}

.option-card:hover {
    color: var(--fg-default);
    border-color: var(--brand-300);
    box-shadow: var(--shadow-md);
    transform: translateY(-2px);
}

.option-card:active { transform: translateY(0); }

.option-card__icon {
    inline-size: 50px;
    block-size: 50px;
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    font-size: 1.4rem;
    margin-bottom: var(--space-3);
}

.option-card--primary .option-card__icon { background: var(--gradient-brand); color: #fff; }
.option-card--service .option-card__icon { background: var(--accent-soft-bg); color: var(--accent-soft-fg); }

.option-card__title { font-size: var(--text-md); font-weight: var(--weight-bold); }
.option-card__meta { font-size: var(--text-xs); color: var(--fg-muted); line-height: var(--leading-snug); }

.option-card__cta {
    display: flex;
    align-items: center;
    gap: var(--space-1);
    margin-top: var(--space-4);
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    color: var(--brand-secondary);
}

/*
 * A time to book, as a card.
 *
 * The appointment times are a long column of short labels — twenty-nine of them
 * on a seven-hour day — so they take a tighter grid than the two or three
 * option cards this component was built for. Left wide, a phone shows one time
 * per screenful and the patient scrolls past the morning to find eleven.
 */
.options--tight {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
}

/*
 * The same card as a submit button.
 *
 * Booking a time is a POST — it takes a place from a shared pool — so the
 * control has to be a button, and a button carries a browser's own background,
 * border and font. Without this reset the times render as grey system buttons
 * in the middle of a page of cards.
 */
button.option-card {
    inline-size: 100%;
    font: inherit;
    text-align: start;
    cursor: pointer;
    align-items: flex-start;
}

button.option-card[disabled] {
    opacity: .55;
    cursor: default;
    transform: none;
    box-shadow: var(--shadow-sm);
}

.option-card--slot { padding: var(--space-4); }
.option-card--slot .option-card__title { font-size: var(--text-lg); }

/*
 * A ticket that was booked.
 *
 * A rail like the priority one, on the *trailing* edge rather than the leading
 * one. Two facts about the same row — this patient is priority, this patient
 * was expected — and a ticket can carry both, so they cannot share an edge:
 * stacked in the same place, one would simply paint over the other and the
 * pharmacist would never learn that the row had two things to say.
 */
.order-card.is-scheduled::after {
    content: "";
    position: absolute;
    inset-block: var(--space-4);
    inset-inline-end: 0;
    inline-size: 3px;
    border-radius: var(--radius-pill);
    background: var(--hue-info);
}

/*
 * "Next to serve" — one line above the queue.
 *
 * Deliberately not a card like the rows beneath it. It is a statement about
 * the list, not a member of it, and if it looked like a row a pharmacist would
 * work it twice: once here and once further down where the same order appears.
 * A band, a ticket number, a name, and a chevron.
 */
.nextup {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    margin-bottom: var(--space-3);
    border-radius: var(--radius-lg);
    background: var(--gradient-brand);
    color: #fff;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
}

.nextup:hover { color: #fff; box-shadow: var(--shadow-md); }

.nextup__label {
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    letter-spacing: .04em;
    opacity: .85;
    white-space: nowrap;
}

.nextup__ticket {
    font-size: var(--text-lg);
    font-weight: var(--weight-black);
    font-variant-numeric: tabular-nums;
    direction: ltr;
}

.nextup__body { min-inline-size: 0; flex-grow: 1; }
.nextup__name { display: block; font-weight: var(--weight-bold); }

.nextup__meta {
    display: block;
    font-size: var(--text-2xs);
    opacity: .85;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* On a narrow counter screen the label rides above the number rather than
   squeezing the name out of the row. */
@media (max-width: 480px) {
    .nextup { flex-wrap: wrap; }
    .nextup__label { inline-size: 100%; }
}

/* ---------------------------------------------------------------- wizard */

.wizard__progress {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    list-style: none;
    margin: 0 0 var(--space-5);
    padding: 0;
}

.wizard__step {
    flex: 1;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-xs);
    color: var(--fg-subtle);
    min-width: 0;
}

.wizard__step-num {
    inline-size: 28px;
    block-size: 28px;
    flex-shrink: 0;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--bg-surface-3);
    border: 2px solid var(--border-default);
    font-weight: var(--weight-black);
    font-size: var(--text-2xs);
}

.wizard__step.is-active { color: var(--fg-default); font-weight: var(--weight-bold); }

.wizard__step.is-active .wizard__step-num {
    background: var(--gradient-brand);
    border-color: transparent;
    color: #fff;
}

.wizard__step.is-done .wizard__step-num {
    background: var(--brand-500);
    border-color: var(--brand-500);
    color: #fff;
}

.wizard__step-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Big, unmistakable pharmacy choice — the first thing a patient picks. */
.pharmacy-choice {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
}

.pharmacy-choice .choice-tile { padding: var(--space-6) var(--space-4); }
.pharmacy-choice .choice-tile__icon { font-size: 2.25rem; }
.pharmacy-choice .choice-tile__title { font-size: var(--text-md); }

/* ==========================================================  order tracking */

.order-layout { display: grid; gap: var(--section-gap); align-items: start; }
.order-layout__main { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.order-layout__side { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }

@media (min-width: 900px) {
    .order-layout { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
}

/* ------------------------------------------------------------ live status */

.track {
    position: relative;
    overflow: hidden;
    padding: var(--space-5);
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-md);
}

.track.is-ready {
    border-color: var(--status-ready-fg);
    box-shadow: var(--shadow-lg), inset 0 0 0 1px var(--status-ready-fg);
}

.track.is-cancelled { opacity: 0.85; }

.track__ticket-row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
}

.track__label { margin: 0; font-size: var(--text-xs); color: var(--fg-muted); }

.track__ticket {
    margin: 0;
    font-size: clamp(2.75rem, 2rem + 3.5vw, 4rem);
    font-weight: var(--weight-black);
    line-height: 1;
    background: var(--gradient-brand);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.track.is-cancelled .track__ticket {
    background: none;
    color: var(--fg-subtle);
    text-decoration: line-through;
}

.track__window {
    margin-inline-start: auto;
    flex-shrink: 0;
    text-align: center;
    padding: var(--space-2) var(--space-5);
    border-radius: var(--radius-lg);
    background: var(--status-ready-fg);
    color: #fff;
}

.track__window-label { display: block; font-size: var(--text-2xs); opacity: 0.85; }
.track__window-number { display: block; font-size: var(--text-2xl); font-weight: var(--weight-black); line-height: 1.1; }

.track__headline {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-4) 0 0;
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
}

.track.is-ready .track__headline { color: var(--status-ready-fg); }
.track.is-cancelled .track__headline { color: var(--status-cancelled-fg); }

.track__facts {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3) var(--space-5);
    margin-top: var(--space-5);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-subtle);
}

/* --------------------------------------------------------- identity card */

/* Reads like the hospital card in the patient's wallet — it is what they
   show at the window, so it carries the name and file number plainly. */
.id-card {
    border-radius: var(--radius-xl);
    overflow: hidden;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

.id-card__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    background: var(--gradient-brand);
    color: #fff;
    position: relative;
    overflow: hidden;
}

.id-card__head::after {
    content: "";
    position: absolute;
    inset-block-start: -60%;
    inset-inline-end: -10%;
    inline-size: 180px;
    aspect-ratio: 1;
    border-radius: 50%;
    background: rgb(255 255 255 / 10%);
    pointer-events: none;
}

.id-card__avatar {
    inline-size: 46px;
    block-size: 46px;
    flex-shrink: 0;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: rgb(255 255 255 / 22%);
    font-size: var(--text-lg);
    font-weight: var(--weight-black);
    position: relative;
}

.id-card__name {
    margin: 0;
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    position: relative;
}

.id-card__file {
    margin: 0;
    font-size: var(--text-xs);
    color: rgb(255 255 255 / 82%);
    display: flex;
    gap: var(--space-1);
    position: relative;
}

.id-card__brand { font-size: 1.4rem; opacity: 0.5; position: relative; flex-shrink: 0; }

.id-card__facts {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(130px, max-content));
    gap: var(--space-4) var(--space-7);
    margin: 0;
    padding: var(--space-5);
}

.id-card__facts dt { font-size: var(--text-2xs); color: var(--fg-muted); font-weight: var(--weight-medium); }
.id-card__facts dd { margin: var(--space-1) 0 0; font-size: var(--text-sm); font-weight: var(--weight-semibold); }

/* --------------------------------------------------------------- QR card */

.qr-card__frame {
    display: inline-block;
    padding: var(--space-3);
    background: #fff;              /* a QR must stay black-on-white to scan */
    border-radius: var(--radius-md);
    border: 1px solid var(--border-subtle);
    line-height: 0;
}

.qr-card__hint { margin: var(--space-3) 0 0; font-size: var(--text-xs); color: var(--fg-muted); }

/* ------------------------------------------------------ clinic multi-pick */

/* One visit can cover several clinics. Checkboxes styled as chips give a real
   touch target on a phone, which a multi-select never does. */
/*
 * Two columns on a phone, not one.
 *
 * At a 200px minimum a 315px-wide card fits a single column, and fourteen
 * clinics became a thousand pixels of scrolling with the send button sticky
 * over the tail of it. The patient cannot see the shape of the list, only a
 * fragment of it. Halving the rows puts most of a hospital's clinics on one
 * screen, which is the difference between choosing and hunting.
 */
.clinic-picker {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 148px), 1fr));
    gap: var(--space-2);
    margin-block-start: var(--space-3);
}

.clinic-chip {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    /* Narrower cell, so the inline padding gives the name its width back. */
    padding: var(--space-3);
    border: 1.5px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    cursor: pointer;
    transition: border-color var(--duration-fast), background var(--duration-fast), transform var(--duration-fast);
}

.clinic-chip input {
    position: absolute;
    opacity: 0;
    inline-size: 1px;
    block-size: 1px;
}

.clinic-chip__body { min-inline-size: 0; flex-grow: 1; }

/* Wraps rather than truncating: a clinic the patient cannot read in full is a
   clinic they will not confidently choose, and "النساء والولادة" does not fit
   one line in a two-column cell. */
.clinic-chip__name {
    display: block;
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    overflow-wrap: anywhere;
}
.clinic-chip__floor { display: block; font-size: var(--text-2xs); color: var(--fg-muted); }

.clinic-chip__tick {
    flex-shrink: 0;
    inline-size: 22px;
    block-size: 22px;
    border-radius: 50%;
    border: 1.5px solid var(--border-strong);
    display: grid;
    place-items: center;
    color: transparent;
    font-size: var(--text-xs);
    transition: background var(--duration-fast), border-color var(--duration-fast), color var(--duration-fast);
}

.clinic-chip:hover { border-color: var(--brand-secondary); }

.clinic-chip:has(input:focus-visible) {
    outline: 2px solid var(--brand-secondary);
    outline-offset: 2px;
}

.clinic-chip:has(input:checked) {
    border-color: var(--brand-primary);
    background: var(--accent-soft-bg);
}

.clinic-chip:has(input:checked) .clinic-chip__tick {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: #fff;
}

/* At the cap the remaining clinics stay visible but clearly out of reach. */
.clinic-chip:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }

.clinic-picker__count {
    margin: var(--space-3) 0 0;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    font-variant-numeric: tabular-nums;
}

.id-card__eyebrow {
    margin: 0;
    font-size: var(--text-2xs);
    color: rgb(255 255 255 / 78%);
    position: relative;
}

@media (prefers-reduced-motion: no-preference) {
    .clinic-chip:active { transform: scale(0.985); }
}

/* ============================================================  app feel */

/* Cross-document view transitions. The browser fades the outgoing page into the
   incoming one, so following a link reads as one app moving between screens
   rather than a document being replaced. Browsers without support simply
   navigate as before — there is no fallback to maintain. */
@view-transition {
    navigation: auto;
}

/* The shell is the same on every screen, so it should not cross-fade with the
   content. Naming these keeps them put while the page beneath them changes. */
.topbar { view-transition-name: wasfa-topbar; }
.bottomnav { view-transition-name: wasfa-bottomnav; }

::view-transition-old(root) { animation: vt-out 140ms ease both; }
::view-transition-new(root) { animation: vt-in 200ms ease both; }

@keyframes vt-out {
    to { opacity: 0; transform: translateY(-4px); }
}

@keyframes vt-in {
    from { opacity: 0; transform: translateY(6px); }
}

/* A tap should acknowledge itself immediately, before the next page arrives —
   that instant feedback is most of what separates an app from a web page. */
@media (prefers-reduced-motion: no-preference) and (hover: none) {
    .option-card:active,
    .action-tile:active,
    .history__row:active,
    .order-brief:active,
    .bottomnav__item:active,
    .choice-tile:active { transform: scale(0.985); }
}

.option-card,
.action-tile,
.history__row,
.order-brief {
    transition: transform var(--duration-fast), box-shadow var(--duration-fast), border-color var(--duration-fast);
    -webkit-tap-highlight-color: transparent;
}

/* Stop the rubber-band overscroll from revealing the page behind the app. */
body { overscroll-behavior-y: none; }

/* Kill the transition itself rather than nesting @view-transition in a media
   query — the pseudo-elements are what actually animate. */
@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) { animation: none; }
}

/* On a phone the stacked option cards cost most of a screen each. Below the
   two-column breakpoint they become rows — icon, text, chevron — which is the
   shape a native app uses for exactly this choice, and cuts the scroll in half
   without losing anything. */
@media (max-width: 559px) {
    .option-card {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr) auto;
        grid-template-areas:
            "icon title chevron"
            "icon meta  chevron";
        align-items: center;
        column-gap: var(--space-4);
        row-gap: var(--space-1);
        padding: var(--space-4);
        min-block-size: 0;
    }

    .option-card__icon {
        grid-area: icon;
        inline-size: 44px;
        block-size: 44px;
        margin-bottom: 0;
        font-size: 1.2rem;
    }

    .option-card__title { grid-area: title; font-size: var(--text-sm); }
    .option-card__meta { grid-area: meta; }

    /* The call to action collapses to the chevron alone: on a row the whole
       card is obviously tappable, so its wording is redundant. */
    .option-card__cta {
        grid-area: chevron;
        margin-top: 0;
        font-size: 0;
        gap: 0;
    }

    .option-card__cta i {
        font-size: var(--text-md);
        color: var(--fg-muted);
    }
}

/* ------------------------------------------------ identity card statistics */

/* The counts used to be plain label-over-number pairs, which read as metadata
   rather than as something to act on. As tiles they carry an icon, a weighted
   figure, and — where there is somewhere to go — a chevron, so the active count
   becomes the shortest route to the request it is counting. */
.id-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
}

.id-stat {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    color: var(--fg-default);
    text-decoration: none;
    min-width: 0;
    transition: background var(--duration-fast);
}

/* A hairline between the tiles, and none at the card's own edge. */
.id-stat + .id-stat {
    border-inline-start: 1px solid var(--border-subtle);
}

a.id-stat:hover { background: var(--bg-surface-2); color: var(--fg-default); }

.id-stat__icon {
    inline-size: 38px;
    block-size: 38px;
    flex-shrink: 0;
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-size: var(--text-md);
}

.id-stat__body { min-width: 0; display: flex; flex-direction: column; }

.id-stat__value {
    font-size: var(--text-xl);
    font-weight: var(--weight-black);
    line-height: 1.1;
}

.id-stat__label {
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    line-height: var(--leading-snug);
}

.id-stat__go {
    margin-inline-start: auto;
    flex-shrink: 0;
    color: var(--fg-subtle);
    font-size: var(--text-sm);
}

/* Zero is not news: a tile with nothing in it recedes instead of competing
   with the request the patient actually came to see. */
.id-stat.is-muted .id-stat__icon { background: var(--bg-surface-3); color: var(--fg-subtle); }
.id-stat.is-muted .id-stat__value { color: var(--fg-subtle); }

/* Below this the chevron costs more than it says: two tiles side by side leave
   the label too little room, and the whole tile is already the target. */
@media (max-width: 479px) {
    .id-stat { padding: var(--space-3) var(--space-4); }
    .id-stat__go { display: none; }
}

@media (max-width: 359px) {
    .id-stat { padding: var(--space-3); gap: var(--space-2); }
    .id-stat__icon { inline-size: 32px; block-size: 32px; font-size: var(--text-sm); }
}

/* ==========================================================  now serving */

.now-serving {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);
}

.now-serving__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.now-serving__title {
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    color: var(--fg-muted);
    letter-spacing: 0.02em;
}

/* A slow pulse says "live" without a spinner claiming something is loading. */
.now-serving__pulse {
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: var(--brand-secondary);
    flex-shrink: 0;
}

@media (prefers-reduced-motion: no-preference) {
    .now-serving__pulse { animation: now-pulse 2.4s ease-in-out infinite; }
}

@keyframes now-pulse {
    0%, 100% { box-shadow: 0 0 0 0 rgb(22 145 208 / 45%); }
    70%      { box-shadow: 0 0 0 7px rgb(22 145 208 / 0%); }
}

.now-serving__number {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-black);
    line-height: 1;
    color: var(--brand-primary);
}

.now-serving__meta {
    margin: 0;
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

/* When the board is calling this patient, the card stops being a bystander. */
.now-serving.is-mine {
    border-color: var(--brand-primary);
    background: var(--accent-soft-bg);
}

.now-serving.is-mine .now-serving__number { color: var(--brand-primary); }

/* ==============================================================  modals */

.wasfa-modal {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: grid;
    place-items: end center;
    padding: var(--space-4);
    padding-block-end: calc(var(--space-4) + var(--safe-bottom));
}

.wasfa-modal[hidden] { display: none; }

.wasfa-modal__backdrop {
    position: absolute;
    inset: 0;
    background: rgb(8 20 33 / 55%);
    backdrop-filter: blur(2px);
    border: 0;
}

/* Bottom sheet on a phone, centred dialog from tablet up — the shape each
   platform's users already expect. */
.wasfa-modal__panel {
    position: relative;
    inline-size: 100%;
    max-inline-size: 440px;
    max-block-size: calc(100dvh - var(--space-8));
    overflow-y: auto;
    padding: var(--space-6) var(--space-5);
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    box-shadow: var(--shadow-lg);
    text-align: center;
}

@media (prefers-reduced-motion: no-preference) {
    .wasfa-modal__panel { animation: sheet-up 240ms var(--ease-out) both; }
}

@keyframes sheet-up {
    from { opacity: 0; transform: translateY(18px); }
}

@media (min-width: 560px) {
    .wasfa-modal { place-items: center; }
}

/* ------------------------------------------------------- "your turn" */

/* The announcement is an event, not a form: it gets a full-bleed brand header
   rather than an icon floating on white. */
.turn-modal .wasfa-modal__panel {
    padding: 0;
    overflow: hidden;
    text-align: center;
}

.turn__head {
    position: relative;
    padding: var(--space-6) var(--space-5) var(--space-5);
    background: var(--gradient-brand);
    color: #fff;
    overflow: hidden;
}

/* Two soft discs behind the bell — depth without an image to download. */
.turn__head::before,
.turn__head::after {
    content: "";
    position: absolute;
    border-radius: 50%;
    background: rgb(255 255 255 / 10%);
    pointer-events: none;
}

.turn__head::before {
    inline-size: 190px;
    aspect-ratio: 1;
    inset-block-start: -95px;
    inset-inline-end: -55px;
}

.turn__head::after {
    inline-size: 120px;
    aspect-ratio: 1;
    inset-block-end: -70px;
    inset-inline-start: -35px;
}

.turn__bell {
    position: relative;
    inline-size: 58px;
    block-size: 58px;
    margin: 0 auto var(--space-4);
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 1.5rem;
    color: var(--brand-primary);
    background: #fff;
    box-shadow: 0 0 0 8px rgb(255 255 255 / 18%);
}

/* One ring travelling outward: it reads as a call going out, and it stops.
   A loop would keep pulling the eye long after the message landed. */
@media (prefers-reduced-motion: no-preference) {
    .turn__bell::after {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: 50%;
        border: 2px solid rgb(255 255 255 / 70%);
        animation: turn-ring 1.6s var(--ease-out) 3;
    }

    .turn__bell i { animation: turn-swing 1.4s ease-in-out 2; transform-origin: 50% 15%; }
}

@keyframes turn-ring {
    from { transform: scale(1); opacity: 0.9; }
    to   { transform: scale(1.9); opacity: 0; }
}

@keyframes turn-swing {
    0%, 100%   { transform: rotate(0); }
    15%, 45%   { transform: rotate(11deg); }
    30%, 60%   { transform: rotate(-11deg); }
    75%        { transform: rotate(4deg); }
}

.turn__title {
    position: relative;
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-black);
    letter-spacing: -0.01em;
}

.turn__body {
    position: relative;
    margin: var(--space-2) 0 0;
    font-size: var(--text-sm);
    color: rgb(255 255 255 / 85%);
}

/* ---- the stage: where to go, then who it belongs to */

.turn__stage { padding: var(--space-6) var(--space-5) var(--space-2); }

.turn__window {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-4) var(--space-7);
    border-radius: var(--radius-xl);
    background: var(--accent-soft-bg);
    border: 1.5px solid var(--brand-200);
}

.turn__window-label {
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    color: var(--brand-primary);
}

.turn__window-number {
    font-size: clamp(3.2rem, 2.4rem + 6vw, 4.4rem);
    font-weight: var(--weight-black);
    line-height: 0.95;
    color: var(--brand-primary);
}

/* With no window assigned there is nothing to point at, so the box collapses
   and the pharmacy name carries the instruction on its own. */
.turn__window.is-empty { display: none; }

.turn__unit {
    margin: var(--space-4) 0 0;
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
}

.turn__ticket {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-2);
    margin: var(--space-4) 0 0;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--bg-surface-3);
}

.turn__ticket-label { font-size: var(--text-2xs); color: var(--fg-muted); }
.turn__ticket-value { font-size: var(--text-md); font-weight: var(--weight-black); }

.turn__order {
    margin: 0;
    padding: 0 var(--space-5) var(--space-5);
    font-size: var(--text-2xs);
    color: var(--fg-subtle);
}

.turn__actions { padding: var(--space-4) var(--space-5) var(--space-2); }

.turn__actions,
.rating__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.rating__actions { margin-top: var(--space-5); }

/* ------------------------------------------------------- leaving Wasfa */

.redirect { text-align: center; }

.redirect__spinner {
    inline-size: 2rem;
    block-size: 2rem;
    border-width: 3px;
    margin: 0 auto var(--space-4);
    color: var(--brand-primary);
}

.redirect__title {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-black);
}

.redirect__name {
    margin: var(--space-2) 0 0;
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    color: var(--brand-secondary);
}

.redirect__notice {
    margin: var(--space-3) 0 0;
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--bg-surface-3);
    font-size: var(--text-sm);
    color: var(--fg-muted);
}

.redirect__actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin-top: var(--space-5);
}

/* ---------------------------------------------------------- rating */

.rating { text-align: center; }
.rating__title { margin: 0; font-size: var(--text-lg); font-weight: var(--weight-black); }
.rating__subtitle { margin: var(--space-2) 0 0; color: var(--fg-muted); font-size: var(--text-sm); }
.rating__order { margin: var(--space-2) 0 0; font-size: var(--text-xs); color: var(--fg-subtle); }

/* Rendered 5→1 and laid out row-reverse, so "checked ~ label" fills every star
   up to the chosen one. Pinned to ltr: a star row reads the same way in both
   languages, and mirroring it would only invert the fill. */
.stars {
    direction: ltr;
    display: flex;
    flex-direction: row-reverse;
    justify-content: center;
    gap: var(--space-1);
    margin: var(--space-4) 0 0;
    padding: 0;
    border: 0;
}

.stars__star {
    font-size: 2rem;
    line-height: 1;
    color: var(--border-strong);
    cursor: pointer;
    padding: var(--space-1);
    transition: color var(--duration-fast), transform var(--duration-fast);
    -webkit-tap-highlight-color: transparent;
}

.stars__input:checked ~ .stars__star,
.stars:hover .stars__star:hover,
.stars:hover .stars__star:hover ~ .stars__star { color: #f0a500; }

.stars__input:focus-visible + .stars__star {
    outline: 2px solid var(--brand-secondary);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

@media (prefers-reduced-motion: no-preference) {
    .stars__star:active { transform: scale(0.88); }
}

.rating__verdict {
    margin: var(--space-2) 0 var(--space-4);
    min-block-size: 1.2em;
    font-size: var(--text-sm);
    font-weight: var(--weight-bold);
    color: var(--brand-primary);
}

.rating .form-label { display: block; text-align: start; font-size: var(--text-xs); }

/* ------------------------------------------------------- upload dropzone */

.dropzone {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-7) var(--space-5);
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius-xl);
    background: var(--bg-surface-2);
    text-align: center;
    cursor: pointer;
    transition: border-color var(--duration-fast), background var(--duration-fast);
}

.dropzone:hover,
.dropzone.is-over {
    border-color: var(--brand-primary);
    background: var(--accent-soft-bg);
}

.dropzone__icon { font-size: 2.2rem; color: var(--brand-secondary); }
.dropzone__title { font-weight: var(--weight-bold); }
.dropzone__hint { font-size: var(--text-xs); color: var(--fg-muted); max-inline-size: 46ch; }
.dropzone__formats { font-size: var(--text-2xs); color: var(--fg-subtle); }

/* The hidden attribute must win over a display utility.
   Bootstrap's .d-flex sets display on the element itself, which beats the
   user-agent rule for [hidden] — so anything marked hidden while also carrying
   a display class rendered as an empty box. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------ closed page */

/* Not an error page: nothing is broken and nobody did anything wrong, so it
   keeps the ordinary surface rather than the red one. */
.shut {
    min-block-size: 100svh;
    display: grid;
    place-items: center;
    align-content: center;
    gap: var(--space-6);
    padding: var(--space-6);
    background: var(--bg-canvas);
    text-align: center;
}

.shut__card {
    inline-size: 100%;
    max-inline-size: 32rem;
    padding: var(--space-8) var(--space-6);
    border-radius: var(--radius-xl);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-md);
}

/* Says the one word the page exists to say, before anything else is read. */
.shut__badge {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-pill);
    background: var(--bg-surface-3);
    color: var(--fg-muted);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
}

.shut__dot {
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: var(--fg-subtle);
}

.shut__service {
    margin: var(--space-5) 0 0;
    font-size: var(--text-xl);
    font-weight: var(--weight-black);
    /* Stated, not inherited: a bare h1 picks up the light theme's dark ink and
       vanishes against the wall screen's dark card. */
    color: var(--fg-default);
}

.shut__message {
    margin: var(--space-3) 0 0;
    color: var(--fg-muted);
    line-height: var(--leading-relaxed);
}

/* When it opens is the answer people actually came for, so it is the only
   thing on the page given any colour. */
.shut__next {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    margin: var(--space-6) 0 0;
    padding: var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--accent-soft-bg);
}

.shut__next-label { font-size: var(--text-2xs); color: var(--accent-soft-fg); font-weight: var(--weight-bold); }
.shut__next-value { font-size: var(--text-lg); font-weight: var(--weight-black); color: var(--brand-primary); }

/* The staff break wears the same card, and answers a different question with
   it. A closure is answered with a date; a break is answered with a number
   falling to zero, so the countdown — not the sentence — is the largest thing
   on the page. */
.shut__badge--break { background: var(--accent-soft-bg); color: var(--accent-soft-fg); }

.shut__countdown {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    margin: var(--space-5) 0 0;
    padding: var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--accent-soft-bg);
}

.shut__countdown-value {
    font-size: clamp(2.6rem, 1.5rem + 6vw, 4rem);
    font-weight: var(--weight-black);
    line-height: 1;
    color: var(--brand-primary);
    /* Tabular figures, or the digits shuffle sideways once a second. */
    font-variant-numeric: tabular-nums;
    direction: ltr;
}

.shut__countdown-label { font-size: var(--text-2xs); color: var(--accent-soft-fg); font-weight: var(--weight-bold); }

.shut__hours {
    margin: var(--space-4) 0 0;
    font-size: var(--text-xs);
    color: var(--fg-subtle);
}

.shut__hours .shut__range {
    font-weight: var(--weight-bold);
    color: var(--fg-muted);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.shut__brand { margin: 0; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* ---- the hall screen: read from ten metres, in a bright room */

.shut--wall {
    background: #050f1c;
    color: #eaf2fa;
}

.shut--wall .shut__card {
    max-inline-size: 46rem;
    padding: var(--space-10) var(--space-8);
    background: #0d1f33;
    border-color: #1d3a58;
    box-shadow: none;
}

.shut--wall .shut__badge { background: #12283f; color: #7f9ab5; }
.shut--wall .shut__service { color: #eaf2fa; }
.shut--wall .shut__service { font-size: clamp(2rem, 1rem + 3vw, 3.2rem); }
.shut--wall .shut__message { font-size: clamp(1.1rem, 0.9rem + 1vw, 1.6rem); color: #b9cde0; }
.shut--wall .shut__next { background: #12283f; }
.shut--wall .shut__next-label { color: #7f9ab5; }
.shut--wall .shut__next-value { font-size: clamp(1.6rem, 1rem + 2.5vw, 2.6rem); color: #38bdf8; }
.shut--wall .shut__hours { font-size: var(--text-md); color: #7f9ab5; }
.shut--wall .shut__hours .shut__range { color: #eaf2fa; }
.shut--wall .shut__brand { color: #4d6d8c; }

/* Centred explicitly, and left in the page direction: the clock reads
   "8:00 صباحاً", so forcing it left-to-right would reverse the words. */
.shut .shut__clock {
    margin: var(--space-7) 0 0;
    font-size: clamp(2.5rem, 1.5rem + 5vw, 5rem);
    font-weight: var(--weight-black);
    line-height: 1;
    color: #eaf2fa;
    text-align: center;
    font-variant-numeric: tabular-nums;
}


/* ---- geofence preview: a circle to scale, not a map
   A tiled map would mean a request to another host on every pan, which this
   application does not make. What an administrator needs to verify is whether
   the radius they typed covers the building, and a scaled circle answers that
   without leaving the machine.                                              */

.fence-preview {
    width: 100%;
    max-width: 240px;
    height: auto;
    color: var(--fg-subtle);
}

/* ==========================================================  location gate
   Shown above the request form while the patient's position is checked. It
   changes colour with its answer, because a patient scanning the page reads
   the colour before the sentence.                                          */

.gate {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
    padding: var(--space-4);
    margin-bottom: var(--space-4);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}

.gate__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 50%;
    font-size: 1.25rem;
    background: color-mix(in srgb, var(--fg-subtle) 12%, transparent);
    color: var(--fg-muted);
}

/* minmax(0,…) is not needed here, but the floor is: without it a long refusal
   pushes the icon out of the card on a narrow phone. */
.gate__body { flex: 1 1 auto; min-width: 0; }

.gate__status {
    margin: 0;
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

.gate__detail {
    margin: var(--space-1) 0 0;
    font-size: var(--text-sm);
    font-variant-numeric: tabular-nums;
    color: var(--fg-muted);
}

.gate__why {
    margin: var(--space-2) 0 0;
    font-size: var(--text-xs);
    color: var(--fg-subtle);
}

.gate__body .btn { margin-top: var(--space-3); }

.gate[data-state="checking"] .gate__icon {
    background: color-mix(in srgb, var(--bs-primary) 14%, transparent);
    color: var(--bs-primary);
    animation: gate-pulse 1.4s ease-in-out infinite;
}

.gate[data-state="ok"] {
    border-color: color-mix(in srgb, var(--bs-success) 40%, var(--border-default));
    background: color-mix(in srgb, var(--bs-success) 7%, var(--bg-surface));
}

.gate[data-state="ok"] .gate__icon {
    background: color-mix(in srgb, var(--bs-success) 16%, transparent);
    color: var(--bs-success);
}

.gate[data-state="denied"] {
    border-color: color-mix(in srgb, var(--bs-danger) 40%, var(--border-default));
    background: color-mix(in srgb, var(--bs-danger) 7%, var(--bg-surface));
}

.gate[data-state="denied"] .gate__icon {
    background: color-mix(in srgb, var(--bs-danger) 16%, transparent);
    color: var(--bs-danger);
}

/*
 * Blocked is not the same as refused, and must not look the same.
 *
 * A refusal can be undone by pressing the button on the card. A block can only
 * be undone in the browser's own settings, so this state carries instructions
 * instead of a button — and the instructions are the longest thing on the card,
 * which is right: they are the only part that changes anything.
 */
.gate[data-state="blocked"] {
    border-color: color-mix(in srgb, var(--bs-danger) 40%, var(--border-default));
    background: color-mix(in srgb, var(--bs-danger) 7%, var(--bg-surface));
}

.gate[data-state="blocked"] .gate__icon {
    background: color-mix(in srgb, var(--bs-danger) 16%, transparent);
    color: var(--bs-danger);
}

.gate[data-state="blocked"] .gate__detail {
    margin-block-start: var(--space-2);
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    font-size: var(--text-2xs);
    line-height: 1.7;
    color: var(--fg-default);
}

@keyframes gate-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.45; }
}

@media (prefers-reduced-motion: reduce) {
    .gate[data-state="checking"] .gate__icon { animation: none; }
}

/* ==========================================================  hospital mark
   The artwork is dark blue on transparency. On a light surface it reads as
   drawn; on a dark one it vanishes, so it sits on a white plate rather than
   being recoloured — tinting another organisation's logo is not ours to do. */

.hlogo {
    display: inline-block;
    line-height: 0;
    /* The padding and radius are carried in every state, so gaining a plate in
       dark mode does not nudge the layout by a few pixels. */
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
}

.hlogo img {
    width: var(--hlogo-width, 13rem);
    max-width: 100%;
    height: auto;
}

.hlogo--plate {
    background: #fff;
    box-shadow: 0 1px 3px rgb(0 0 0 / 12%);
}

/* The plain variant earns a plate of its own in the dark theme. The mark is
   dark blue on transparency: on a near-black surface its smallest line — the
   health-cluster strapline — falls below any readable contrast. Recolouring
   another organisation's logo is not ours to do, so the ground changes
   instead. Both explicit themes and the system default are covered, since a
   page can arrive in dark mode by either route. */
:root[data-theme="dark"] .hlogo--plain,
:root[data-theme="auto"] .hlogo--plain {
    background: #fff;
    box-shadow: 0 1px 3px rgb(0 0 0 / 35%);
}

@media (prefers-color-scheme: light) {
    :root[data-theme="auto"] .hlogo--plain { background: none; box-shadow: none; }
}

/* ==========================================================  public board
   /kpi — open to anyone, so it carries only aggregates. It is read on a phone
   in a corridor as often as on a manager's screen, which is why the figures
   come first and the explanation of them comes last.                       */

.board {
    max-width: var(--content-max);
    margin-inline: auto;
    padding: var(--space-6) var(--gutter) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.board__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
    padding-bottom: var(--space-5);
    border-bottom: 1px solid var(--border-subtle);
}

/* The floor matters: without it a long department name shoves the logo off
   the edge of a narrow screen instead of wrapping under it. */
.board__titles { flex: 1 1 12rem; min-width: 0; }

.board__title {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    color: var(--fg-default);
}

.board__dept { margin: var(--space-1) 0 0; color: var(--fg-muted); font-size: var(--text-sm); }

.board__filter {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
}

.board__filter-label { font-size: var(--text-sm); color: var(--fg-muted); margin: 0; }
.board__filter .form-select { inline-size: auto; min-inline-size: 11rem; }
.board__dates { margin-inline-start: auto; font-size: var(--text-sm); color: var(--fg-subtle); }

/* ---- the headline figures */

.board__kpis {
    display: grid;
    gap: var(--space-3);
    /* minmax(0,…) so a wide number cannot stretch its column past the grid. */
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
}

.kpi {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
}

.kpi__top { display: flex; align-items: center; gap: var(--space-2); }
.kpi__label { flex: 1 1 auto; min-width: 0; font-size: var(--text-xs); color: var(--fg-muted); }
.kpi__icon { color: var(--fg-subtle); font-size: var(--text-md); }

.kpi__value {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    line-height: var(--leading-tight);
    color: var(--fg-default);
}

.kpi__delta {
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--space-1);
    font-size: var(--text-2xs);
    color: var(--fg-subtle);
}

.kpi__delta-note { color: var(--fg-subtle); }
.kpi__delta--none { font-style: italic; }

/* Good and bad, not up and down: a rise in cancellations and a rise in
   completions are both "up", and colouring them alike is how a dashboard
   tells a comfortable lie. */
.kpi[data-tone="good"] .kpi__delta { color: hsl(var(--h-success) 45% 38%); }
.kpi[data-tone="bad"] .kpi__delta { color: hsl(var(--h-danger) 60% 45%); }

/* ---- panels */

.board__grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
}

.panel {
    padding: var(--space-5);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
}

.panel__title {
    margin: 0 0 var(--space-4);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

.panel__empty { margin: 0; color: var(--fg-subtle); font-size: var(--text-sm); }
.panel__note { margin: var(--space-3) 0 0; font-size: var(--text-xs); color: var(--fg-muted); }

/* ---- hours of the day */

.bars {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    block-size: 9rem;
}

.bars__col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: var(--space-1);
    block-size: 100%;
}

.bars__bar {
    inline-size: 100%;
    /* A floor of 2px so an hour with one order is visibly not zero — but not
       for an hour with none, or the quiet half of the day reads as an axis. */
    block-size: max(2px, var(--h));
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--brand-300);
}

.bars__bar[data-empty="true"] { block-size: 0; }

.bars__bar[data-peak="true"] { background: var(--brand-primary); }
.bars__tick { font-size: var(--text-2xs); color: var(--fg-subtle); line-height: 1; }

/* ---- ratings */

.rating-summary { text-align: center; margin-bottom: var(--space-4); }

.rating-summary__score {
    margin: 0;
    font-size: var(--text-3xl);
    font-weight: var(--weight-black);
    line-height: 1;
    color: var(--fg-default);
}

.rating-summary__stars { margin: var(--space-2) 0 0; color: #f0a500; font-size: var(--text-lg); }
.rating-summary__count { margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--fg-subtle); }

/* The rating breakdown on the public board — five bars, one per star.
   Named apart from `.stars`, which is the picker a patient taps: they were
   both called `.stars`, and the later rule turned the picker into a column. */
.starbars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }

.starbars__row { display: flex; align-items: center; gap: var(--space-3); }

.starbars__label {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

.starbars__label .bi { color: #f0a500; }

.starbars__track {
    flex: 1 1 auto;
    min-width: 0;
    block-size: 0.5rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.starbars__fill { display: block; block-size: 100%; background: #f0a500; border-radius: var(--radius-pill); }
.starbars__count { flex: 0 0 3rem; text-align: end; font-size: var(--text-xs); color: var(--fg-muted); }

/* ---- ranked list */

.ranked { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); }
.ranked__row { display: flex; align-items: center; gap: var(--space-3); }

.ranked__rank {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 1.5rem;
    block-size: 1.5rem;
    border-radius: 50%;
    font-size: var(--text-2xs);
    font-weight: var(--weight-bold);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

.ranked__name { flex: 0 1 9rem; min-width: 0; font-size: var(--text-sm); color: var(--fg-default);
                overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.ranked__track {
    flex: 1 1 auto;
    min-width: 0;
    block-size: 0.5rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.ranked__fill { display: block; block-size: 100%; background: var(--brand-primary); border-radius: var(--radius-pill); }
.ranked__value { flex: 0 0 3.5rem; text-align: end; font-size: var(--text-sm); color: var(--fg-muted); }

/* ---- the department */

.dept__lead { margin: 0 0 var(--space-4); color: var(--fg-muted); font-size: var(--text-sm); }

.dept__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: var(--space-4);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

.dept__item { display: flex; gap: var(--space-3); align-items: flex-start; }

.dept__item > .bi {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.25rem;
    block-size: 2.25rem;
    border-radius: var(--radius-md);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

.dept__item > div { min-width: 0; }
.dept__item-title { margin: 0; font-weight: var(--weight-semibold); font-size: var(--text-sm); color: var(--fg-default); }
.dept__item-text { margin: var(--space-1) 0 0; font-size: var(--text-xs); color: var(--fg-muted); line-height: var(--leading-relaxed); }

/* ---- provenance */

.board__foot {
    padding-top: var(--space-5);
    border-top: 1px solid var(--border-subtle);
    text-align: center;
}

.board__source { margin: 0; font-size: var(--text-xs); color: var(--fg-muted); }
.board__stamp { margin: var(--space-1) 0 0; font-size: var(--text-2xs); color: var(--fg-subtle); }
.board__brand { margin: var(--space-2) 0 0; font-size: var(--text-xs); color: var(--fg-subtle); }

/* ==========================================================  insights
   Each card carries its own workings. An insight whose basis is hidden is one
   people learn to argue with rather than act on, so the evidence line is part
   of the component, not an optional extra.                                  */

.insight {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-inline-start: 3px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

.insight__icon { flex: 0 0 auto; font-size: var(--text-md); color: var(--fg-subtle); line-height: 1.4; }
.insight__body { flex: 1 1 auto; min-width: 0; }

.insight__text {
    margin: 0;
    font-weight: var(--weight-medium);
    color: var(--fg-default);
    line-height: var(--leading-snug);
}

.insight__advice,
.insight__evidence {
    margin: var(--space-2) 0 0;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    line-height: var(--leading-relaxed);
}

.insight__evidence { color: var(--fg-subtle); }

.insight__tag {
    display: inline-block;
    margin-inline-end: var(--space-2);
    padding: 1px var(--space-2);
    border-radius: var(--radius-pill);
    font-size: var(--text-2xs);
    background: var(--bg-inset);
    color: var(--fg-muted);
}

.insight[data-tone="good"] { border-inline-start-color: hsl(var(--h-success) 45% 45%); }
.insight[data-tone="bad"],
.insight[data-severity="high"] { border-inline-start-color: hsl(var(--h-danger) 60% 50%); }
.insight[data-severity="medium"] { border-inline-start-color: hsl(var(--h-warning) 70% 50%); }

.insight[data-severity="high"] .insight__icon { color: hsl(var(--h-danger) 60% 50%); }
.insight[data-severity="medium"] .insight__icon { color: hsl(var(--h-warning) 65% 45%); }

/* ---- the leaderboard */

.score {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

.score__head { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.score__rank { font-size: var(--text-md); }
.score__name { font-weight: var(--weight-semibold); color: var(--fg-default); min-width: 0; }

.score__value {
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    color: var(--brand-primary);
}

.score__parts {
    list-style: none;
    margin: var(--space-4) 0 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.score__part { display: flex; align-items: center; gap: var(--space-3); }

.score__part-label {
    flex: 0 0 9rem;
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

/* The weight is shown beside every component, because a bar at 100% that is
   worth a tenth of the score is not the same news as one worth a third. */
.score__weight { font-size: var(--text-2xs); color: var(--fg-subtle); }

.score__track {
    flex: 1 1 auto;
    min-width: 0;
    block-size: 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.score__fill { display: block; block-size: 100%; background: var(--brand-primary); border-radius: var(--radius-pill); }
.score__part-value { flex: 0 0 2.75rem; text-align: end; font-size: var(--text-xs); color: var(--fg-muted); }

@media (max-width: 30rem) {
    .score__part { flex-wrap: wrap; }
    .score__part-label { flex: 1 1 100%; }
}

/* ==========================================================  printable report
   This is the PDF. The browser writes it, because the browser already has a
   correct Arabic text stack — shaping, joining, bidi — and a PDF writer built
   here would get those wrong in ways nobody notices until a report is
   forwarded with the words backwards.                                       */

.sheet {
    max-width: 60rem;
    margin-inline: auto;
    padding: var(--space-6) var(--gutter) var(--space-8);
    color: var(--fg-default);
}

.sheet__head {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding-bottom: var(--space-4);
    border-bottom: 2px solid var(--border-default);
}

.sheet__id { flex: 1 1 12rem; min-width: 0; }
.sheet__title { margin: 0; font-size: var(--text-xl); font-weight: var(--weight-bold); }
.sheet__dept { margin: var(--space-1) 0 0; color: var(--fg-muted); font-size: var(--text-sm); }

.sheet__meta {
    display: grid;
    gap: var(--space-2) var(--space-5);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
    margin: var(--space-4) 0;
    padding: var(--space-4);
    border-radius: var(--radius-md);
    background: var(--bg-inset);
    font-size: var(--text-sm);
}

.sheet__meta dt { color: var(--fg-subtle); font-size: var(--text-2xs); margin: 0; }
.sheet__meta dd { margin: 0; font-weight: var(--weight-medium); }

.sheet__actions { margin: var(--space-5) 0; }
.sheet__hint { margin: var(--space-2) 0 0; font-size: var(--text-xs); color: var(--fg-muted); }

.sheet__section { margin-top: var(--space-6); }

.sheet__section-title {
    margin: 0 0 var(--space-3);
    font-size: var(--text-md);
    font-weight: var(--weight-semibold);
}

.sheet__table { width: 100%; border-collapse: collapse; font-size: var(--text-sm); }

.sheet__table th,
.sheet__table td {
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--border-subtle);
    text-align: start;
}

.sheet__table th {
    font-weight: var(--weight-semibold);
    background: var(--bg-inset);
    font-size: var(--text-xs);
}

.sheet__foot {
    margin-top: var(--space-7);
    padding-top: var(--space-4);
    border-top: 1px solid var(--border-subtle);
    text-align: center;
    font-size: var(--text-xs);
    color: var(--fg-subtle);
}

@media print {
    /*
     * The right-to-left page, printed.
     *
     * Chrome lays a paged document out correctly and then paints it wrongly
     * when the *root* element is the one carrying `direction: rtl`: the content
     * ends up shifted towards the middle of the sheet with its left-hand side —
     * on an RTL page, the last columns of every table — falling off the edge.
     * A month of daily figures printed as three of its five columns, with
     * nothing on the page to say the other two ever existed.
     *
     * Moving the base direction one level down, onto <body>, is enough: the
     * root prints as a plain block and everything inside it is still
     * right-to-left, so no rule below here had to change. It is scoped to the
     * documents that are actually RTL, because an English page must keep the
     * direction it was written in.
     *
     * Screen rendering is untouched — this is a paint bug that only exists on
     * paper, and the page on screen was never wrong.
     */
    html[dir="rtl"] { direction: ltr; }
    html[dir="rtl"] body { direction: rtl; }

    /* Paper is white whatever the reader's theme is, and a dark background
       would be printed. Colours are stated rather than inherited. */
    .sheet { padding: 0; max-width: none; width: 100%; color: #000; }
    .no-print { display: none !important; }

    /* The report must occupy the page, not a column down the middle of it, and
       nothing may hang off the edge — on a right-to-left page an overflowing
       table loses its last columns off the left margin, which is how a printed
       report arrives with the numbers missing and only the labels showing.
       A fixed layout makes the table honour the width it was given whatever is
       inside it, and long clinic names wrap instead of pushing. */
    html, body { width: auto; margin: 0; }

    .sheet__table { table-layout: fixed; width: 100%; font-size: 9pt; }

    .sheet__table th,
    .sheet__table td {
        padding: 4px 6px;
        overflow-wrap: anywhere;
        word-break: break-word;
    }

    /* The first column carries the name — the date, the clinic, the measure —
       and the rest carry figures, which need far less room. */
    .sheet__table td.num, .sheet__table th { white-space: normal; }

    /* auto-fit needs a definite width to collapse its tracks against. Paged
       media sizes this box intrinsically first, and with nothing to collapse
       against the five fields each claim their 12rem minimum — a 60rem strip
       on a page barely half that, which then hangs off the edge and takes the
       tables with it. Stated columns cannot do that. */
    .sheet__meta {
        background: none;
        border: 1px solid #999;
        grid-template-columns: repeat(3, 1fr);
    }
    .sheet__table th { background: none; border-bottom: 2px solid #000; }
    .sheet__table th, .sheet__table td { border-bottom: 1px solid #ccc; }

    /* A table split across pages must carry its headings onto the next one,
       or the second page is a grid of unlabelled numbers. */
    .sheet__table thead { display: table-header-group; }
    .sheet__table tr { break-inside: avoid; }
    .sheet__section { break-inside: auto; }
    .sheet__section-title { break-after: avoid; }

    /* The mark is dark blue on transparency; on paper it needs no plate. */
    .hlogo, .hlogo--plain { background: none !important; box-shadow: none !important; padding: 0 !important; }

    /* A4 is what the hospital's printers are loaded with. Left to itself the
       browser picks the locale default — Letter on a machine set to US — and
       the page comes out narrower than the report was laid out for. */
    @page { size: A4; margin: 12mm; }
}

/* ==========================================================  KPI wall screen
   /kpi/screen — a television in a corridor, read from several metres by
   someone walking past. Same rules as the hall boards: dark, oversized, no
   navigation, and nothing on it that describes a person.                    */

.wall {
    min-block-size: 100svh;
    padding: clamp(1rem, 2vw, 2rem);
    display: flex;
    flex-direction: column;
    gap: clamp(0.75rem, 1.5vw, 1.5rem);
    background: #050f1c;
    color: #eaf2fa;
}

.wall__head { display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap; }
.wall__titles { flex: 1 1 12rem; min-width: 0; }

.wall__title {
    margin: 0;
    font-size: clamp(1.25rem, 0.9rem + 1.6vw, 2.25rem);
    font-weight: var(--weight-bold);
    color: #eaf2fa;
}

.wall__dept { margin: 0; color: #7f9ab5; font-size: clamp(0.8rem, 0.7rem + 0.4vw, 1.1rem); }

.wall__clock { text-align: center; }

.wall__live {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-2xs);
    color: #7f9ab5;
}

.wall__dot {
    inline-size: 0.5rem;
    block-size: 0.5rem;
    border-radius: 50%;
    background: #34d399;
    animation: wall-pulse 2s ease-in-out infinite;
}

.wall__time {
    margin: var(--space-1) 0 0;
    font-size: clamp(1.5rem, 1rem + 2vw, 2.75rem);
    font-weight: var(--weight-black);
    line-height: 1;
    color: #eaf2fa;
}

.wall__date { margin: 0; font-size: var(--text-2xs); color: #4d6d8c; }

/* ---- the six tiles */

.wall__kpis {
    display: grid;
    gap: clamp(0.5rem, 1vw, 1rem);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
}

/* Scoped to the wall. These class names are shared with the board's tile
   grid, and the absolutely-positioned delta here was landing on the
   board's title — one stylesheet, two components, same names. */
.wall .tile {
    position: relative;
    padding: clamp(0.75rem, 1.5vw, 1.5rem);
    border: 1px solid #16283d;
    border-radius: var(--radius-lg);
    background: #0b1a2b;
    text-align: center;
}

.wall .tile__icon { font-size: clamp(0.9rem, 0.8rem + 0.4vw, 1.3rem); color: #38bdf8; }

.wall .tile__value {
    margin: var(--space-1) 0 0;
    font-size: clamp(1.75rem, 1rem + 3.2vw, 4rem);
    font-weight: var(--weight-black);
    line-height: 1;
    color: #eaf2fa;
}

.wall .tile__label {
    margin: var(--space-2) 0 0;
    font-size: clamp(0.7rem, 0.65rem + 0.25vw, 0.95rem);
    color: #7f9ab5;
}

/* Good and bad, not up and down — the same rule the rest of the system uses. */
.wall .tile__delta {
    position: absolute;
    inset-block-start: clamp(0.5rem, 1vw, 1rem);
    inset-inline-start: clamp(0.5rem, 1vw, 1rem);
    margin: 0;
    display: flex;
    align-items: center;
    gap: 2px;
    font-size: var(--text-2xs);
    color: #4d6d8c;
}

.wall .tile[data-tone="good"] .tile__delta { color: #34d399; }
.wall .tile[data-tone="bad"] .tile__delta { color: #f87171; }

/* ---- the two panels */

.wall__grid {
    display: grid;
    gap: clamp(0.5rem, 1vw, 1rem);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    /* Takes the height the tiles and the alert strip leave behind, so the
       board fills the screen it hangs on instead of floating at the top. */
    flex: 1 1 auto;
    min-block-size: 0;
}

.panel--wall {
    background: #0b1a2b;
    border-color: #16283d;
    box-shadow: none;
    display: flex;
    flex-direction: column;
}

.panel--wall .panel__title { color: #7f9ab5; font-size: clamp(0.8rem, 0.7rem + 0.35vw, 1.05rem); }
.panel--wall .panel__empty,
.panel--wall .panel__note { color: #4d6d8c; }

/* An explicit height, not a share of whatever is left over: the chart is the
   one element here whose meaning depends on how tall it is drawn. */
.wallbars {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
    flex: 1 1 auto;
    min-block-size: clamp(7rem, 20vh, 18rem);
}

.wallbars__col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: var(--space-1);
    block-size: 100%;
}

.wallbars__value { font-size: var(--text-2xs); color: #7f9ab5; line-height: 1; }

.wallbars__bar {
    inline-size: 100%;
    block-size: max(3px, var(--h));
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: #1e5f8f;
}

.wallbars__bar[data-empty="true"] { block-size: 3px; background: #16283d; }
.wallbars__bar[data-today="true"] { background: #38bdf8; }
.wallbars__tick { font-size: var(--text-2xs); color: #4d6d8c; line-height: 1; white-space: nowrap; }

.wallunits { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-3); flex: 1 1 auto; }

.wallunits__row {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    padding-block-end: var(--space-2);
    border-block-end: 1px solid #16283d;
}

.wallunits__name {
    flex: 1 1 auto;
    min-width: 0;
    font-size: clamp(0.75rem, 0.7rem + 0.3vw, 1rem);
    color: #eaf2fa;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.wallunits__stat { font-size: clamp(1rem, 0.85rem + 0.7vw, 1.6rem); font-weight: var(--weight-bold); color: #38bdf8; }
.wallunits__rate { font-size: var(--text-sm); color: #7f9ab5; flex: 0 0 3rem; text-align: end; }

/* ---- the strip along the bottom */

.wall__foot {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    flex-wrap: wrap;
    padding-block-start: var(--space-3);
    border-block-start: 1px solid #16283d;
}

.wall__clear {
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: clamp(0.75rem, 0.7rem + 0.3vw, 1rem);
    color: #34d399;
}

.wall__alerts { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--space-3); flex: 1 1 auto; }

.wall__alert {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-pill);
    font-size: clamp(0.7rem, 0.65rem + 0.25vw, 0.95rem);
    background: rgb(248 113 113 / 12%);
    color: #fca5a5;
}

.wall__alert[data-severity="medium"] { background: rgb(251 191 36 / 12%); color: #fcd34d; }
.wall__brand { margin: 0; margin-inline-start: auto; font-size: var(--text-2xs); color: #4d6d8c; }

@keyframes wall-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

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

/* ---- the wall's ratings panel */

.wallrate { text-align: center; margin-block-end: var(--space-3); }

.wallrate__score {
    margin: 0;
    font-size: clamp(1.75rem, 1.2rem + 2vw, 3rem);
    font-weight: var(--weight-black);
    line-height: 1;
    color: #eaf2fa;
}

.wallrate__stars { margin: var(--space-1) 0 0; color: #f0a500; font-size: clamp(0.9rem, 0.8rem + 0.5vw, 1.4rem); }
.wallrate__count { margin: var(--space-1) 0 0; font-size: var(--text-2xs); color: #4d6d8c; }

.wallstars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.wallstars__row { display: flex; align-items: center; gap: var(--space-2); }
.wallstars__label { flex: 0 0 1rem; font-size: var(--text-2xs); color: #7f9ab5; text-align: center; }

.wallstars__track {
    flex: 1 1 auto;
    min-width: 0;
    block-size: 0.4rem;
    border-radius: var(--radius-pill);
    background: #16283d;
    overflow: hidden;
}

.wallstars__fill { display: block; block-size: 100%; background: #f0a500; border-radius: var(--radius-pill); }
.wallstars__count { flex: 0 0 2.5rem; text-align: end; font-size: var(--text-2xs); color: #7f9ab5; }

/* The panels share one height so the row reads as a row, and each spreads its
   own content down that height rather than piling it at the top and leaving a
   dead zone — which from ten metres looks like a screen that failed to load. */
.panel--wall { justify-content: space-between; }

/* ---- the full board: additions for the comprehensive layout */

.board__platform {
    margin: 0;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    color: var(--brand-primary);
    text-transform: none;
}

.board__screen { align-self: center; }

.board__pick { display: inline-flex; align-items: center; gap: var(--space-2); }
.board__pick .form-control { inline-size: auto; }

.panel__badge {
    display: inline-block;
    margin-inline-start: var(--space-2);
    padding: 1px var(--space-2);
    border-radius: var(--radius-pill);
    font-size: var(--text-2xs);
    font-weight: var(--weight-normal);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

.panel__subtitle {
    margin: var(--space-5) 0 var(--space-3);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--fg-muted);
}

.panel__legend {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
    font-size: var(--text-2xs);
    color: var(--fg-muted);
}

.swatch { inline-size: 0.75rem; block-size: 0.75rem; border-radius: 2px; }
.swatch--total { background: var(--brand-300); }
.swatch--done { background: var(--brand-primary); margin-inline-start: var(--space-3); }

/* ---- grouped bars: the total and the finished count side by side

   Nesting one inside the other showed the proportion but hid the second
   quantity — you could see that half was finished without being able to read
   how many. Two bars state both, and the share is printed under the label
   because it is the one figure neither bar carries on its own.              */

.bars--grouped { block-size: 15rem; align-items: stretch; }
.bars--grouped .bars__col { justify-content: flex-end; gap: 2px; }

.bars__pair {
    flex: 1 1 auto;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: 2px;
    inline-size: 100%;
    min-block-size: 0;
}

.bars__one {
    flex: 1 1 0;
    min-width: 0;
    max-inline-size: 2.25rem;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: center;
    gap: 2px;
    block-size: 100%;
}

.bars--grouped .bars__bar { inline-size: 100%; block-size: max(2px, var(--h)); }
.bars--grouped .bars__bar[data-series="total"] { background: var(--brand-300); }
.bars--grouped .bars__bar[data-series="done"] { background: var(--brand-primary); }
.bars--grouped .bars__bar[data-empty="true"] { block-size: 2px; background: var(--bg-inset); }

.bars--grouped .bars__tick { margin-block-start: 2px; }

.bars__rate {
    margin-block-start: 1px;
    font-size: var(--text-2xs);
    color: var(--brand-primary);
    font-weight: var(--weight-semibold);
    line-height: 1.3;
}

/* Past a dozen periods the numbers above the bars collide. They are dropped
   rather than shrunk to illegibility — the tooltip and the strip below still
   carry them, and a chart of overlapping digits states nothing at all. */
.bars--grouped[data-dense="true"] .bars__number,
.bars--grouped[data-dense="true"] .bars__rate { display: none; }

@media (max-width: 48rem) {
    .bars--grouped .bars__number,
    .bars--grouped .bars__rate { display: none; }
}

/* The trend chart is taller than the hour strip: it carries a number above
   every bar, and cramming those into a 9rem band makes them unreadable. */
.bars--tall {
    block-size: 13rem;
    /* Centred, because with a cap on column width a short series would
       otherwise cling to one edge of an empty chart. */
    justify-content: center;
    gap: var(--space-2);
}

/* A cap on width, so a period containing one bucket draws a bar and not a
   wall: a single column stretched across the panel reads as a fill, not as a
   measurement. */
.bars--tall .bars__col { gap: var(--space-1); max-inline-size: 5rem; }

.bars__number {
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    line-height: 1;
    white-space: nowrap;
}

/* The delivered share sits inside the total bar rather than beside it, so the
   gap between raised and finished is read as a proportion, not as two
   quantities the eye has to divide. */
.bars__bar { position: relative; display: block; overflow: hidden; }

.bars__done {
    position: absolute;
    inset-inline: 0;
    inset-block-end: 0;
    block-size: var(--d, 0%);
    background: var(--brand-primary);
}

/* ---- proportional splits (status, type) */

.split { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.split__row { display: flex; align-items: center; gap: var(--space-2); }

.split__label {
    flex: 0 0 6.5rem;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.split__track {
    flex: 1 1 auto;
    min-width: 0;
    block-size: 0.5rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.split__fill { display: block; block-size: 100%; background: var(--brand-primary); border-radius: var(--radius-pill); }

/* Each status keeps the colour it wears everywhere else in the system, so a
   reader does not have to learn a second palette for this page. */
.split__fill[data-status="delivered"] { background: hsl(var(--h-success) 45% 45%); }
.split__fill[data-status="cancelled"] { background: hsl(var(--h-danger) 60% 55%); }
.split__fill[data-status="ready"] { background: hsl(var(--h-info) 65% 50%); }
.split__fill[data-status="preparing"] { background: hsl(var(--h-warning) 70% 50%); }

.split__value { flex: 0 0 3rem; text-align: end; font-size: var(--text-xs); color: var(--fg-default); }
.split__share { flex: 0 0 3rem; text-align: end; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* ────────────────────────────────────────  survey distribution  ───── */
/* One card per question, each holding a small bar chart — a manager scanning
   several questions reads them as separate findings, not rows of one long
   list where the third and fourth blur together. */

.surveychart-grid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    align-items: start;
}

.surveychart__text-total {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

/* The chart itself: one column per option, bar height driven by --h exactly
   like the daily-orders chart on the reports page, coloured per option via
   --clr rather than by meaning — there is no "good" or "bad" answer here,
   only five colours to tell the columns apart. */
.surveychart__bars {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
    block-size: 7rem;
    margin-block-end: var(--space-3);
}

.surveychart__col {
    flex: 1 1 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-1);
    block-size: 100%;
}

.surveychart__count { font-size: var(--text-2xs); font-weight: var(--weight-semibold); color: var(--fg-muted); }

.surveychart__bar {
    inline-size: 100%;
    block-size: max(3px, var(--h));
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    background: var(--clr, var(--brand-300));
}

.surveychart__legend {
    display: grid;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.surveychart__legend-row { display: flex; align-items: center; gap: var(--space-2); }

.surveychart__dot {
    flex: 0 0 auto;
    inline-size: 0.65rem;
    block-size: 0.65rem;
    border-radius: 50%;
}

.surveychart__legend-label { flex: 1 1 auto; font-size: var(--text-xs); color: var(--fg-default); }
.surveychart__legend-value { flex: 0 0 auto; font-size: var(--text-2xs); color: var(--fg-muted); }

.ranked__sub { font-size: var(--text-2xs); color: var(--fg-subtle); }

/* Two lines per row rather than one: the name, the count and the share sit
   together where they are read, and the bar runs the full width underneath —
   which is what makes a short bar still legible next to a long name. */
.ranked--rich { gap: var(--space-3); }
.ranked__item { display: flex; flex-direction: column; gap: 4px; }
.ranked__head { display: flex; align-items: baseline; gap: var(--space-2); }
.ranked--rich .ranked__name { flex: 1 1 auto; }
.ranked--rich .ranked__value { flex: 0 0 auto; font-weight: var(--weight-semibold); color: var(--fg-default); }
.ranked__share { flex: 0 0 3rem; text-align: end; font-size: var(--text-2xs); color: var(--fg-subtle); }
.ranked--rich .ranked__track { block-size: 0.45rem; }

/* ---- the forecast */

.forecast {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
}

.forecast__card {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-inset);
    text-align: center;
}

.forecast__label { margin: 0; font-size: var(--text-xs); color: var(--fg-muted); }

.forecast__value {
    margin: var(--space-1) 0 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    line-height: 1;
    color: var(--fg-default);
}

.forecast__range { margin: var(--space-1) 0 0; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* ---- the privacy note */

.board__privacy {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0;
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    background: var(--bg-inset);
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

/* ==========================================================  board, rebuilt
   A dashboard read by someone scanning is a set of equal cards, not a column
   of paragraphs — so every tile is the same shape: a title, one figure, a
   small chart, one line of context. A panel holding a single row of text
   between two dense ones breaks the rhythm the eye is following, which is why
   nothing here is allowed to be a lone line in a box of its own.            */

.board { padding: 0; max-width: none; display: block; }

.board__body {
    max-width: var(--content-max);
    margin-inline: auto;
    padding: var(--space-6) var(--gutter) var(--space-8);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* ---- the brand band */

.masthead {
    background: var(--gradient-brand);
    color: #fff;
    padding: var(--space-5) var(--gutter) var(--space-4);
}

.masthead__inner {
    max-width: var(--content-max);
    margin-inline: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
}

.masthead__titles { flex: 1 1 14rem; min-width: 0; }

.masthead__platform {
    margin: 0;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    color: rgb(255 255 255 / 72%);
}

.masthead__title {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    color: #fff;
}

.masthead__dept { margin: var(--space-1) 0 0; font-size: var(--text-sm); color: rgb(255 255 255 / 78%); }

.masthead__filter { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.masthead__filter .form-select { inline-size: auto; min-inline-size: 10rem; }
.masthead__pick { display: inline-flex; gap: var(--space-2); }
.masthead__pick .form-control { inline-size: auto; }

.masthead__range {
    max-width: var(--content-max);
    margin: var(--space-3) auto 0;
    font-size: var(--text-xs);
    color: rgb(255 255 255 / 70%);
}

/* ---- section rules */

.section {
    margin: var(--space-4) 0 0;
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    color: var(--fg-subtle);
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

/* A hairline that runs to the edge, so a heading reads as a divider rather
   than as another piece of content competing with the tiles. */
.section::after {
    content: "";
    flex: 1 1 auto;
    block-size: 1px;
    background: var(--border-subtle);
}

/* ---- the uniform tile grid */

.tiles {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}

/*
 * Wide tiles fit as many across as the screen allows, down to a readable
 * minimum.
 *
 * There used to be a fixed `repeat(3, …)` for these inside the media query and
 * an auto-fit rule after it. Same specificity, later in the file — so the fixed
 * rule never applied at any width, and the section that gained a fourth tile
 * would have gone on being laid out by the rule that was actually in force
 * anyway. Removing the dead one leaves one rule to reason about, and it is the
 * one that adapts: three tiles fill the row, four fill it too.
 */
@media (min-width: 64rem) {
    .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

.tiles--wide { grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); }

.tile {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    /* Equal heights across a row, so the grid reads as a grid. */
    min-block-size: 11rem;
}

.tile--tall { min-block-size: 16rem; }

.tile__title {
    margin: 0;
    font-size: var(--text-xs);
    font-weight: var(--weight-medium);
    color: var(--fg-muted);
    text-align: center;
}

.tile__subtitle {
    margin: var(--space-3) 0 var(--space-2);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    color: var(--fg-subtle);
}

.tile__figure {
    margin: 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-bold);
    line-height: 1;
    text-align: center;
    color: var(--fg-default);
}

.tile__stars { margin: 0; text-align: center; color: #f0a500; font-size: var(--text-md); }
.tile__empty { margin: auto 0; text-align: center; font-size: var(--text-xs); color: var(--fg-subtle); }

/* The chart takes whatever height the tile has spare, so tiles with a long
   figure and tiles with a short one still line up. */
.tile__chart { flex: 1 1 auto; min-block-size: 2.5rem; display: flex; align-items: center; }
.tile__chart--gauge { justify-content: center; }
.tile__chart--stack { align-items: center; }

.tile__delta {
    margin: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    font-size: var(--text-2xs);
    color: var(--fg-subtle);
    text-align: center;
}

.tile__since { color: var(--fg-subtle); }
.tile__flat { font-style: italic; }

.tile[data-tone="good"] .tile__delta { color: hsl(var(--h-success) 45% 38%); }
.tile[data-tone="bad"] .tile__delta { color: hsl(var(--h-danger) 60% 45%); }

/* ---- the day's slots
 *
 * One card per appointment slot, coloured by how full it is rather than a flat
 * list of same-coloured badges — the three states (open, filling, full) are
 * the three things a member of staff is scanning for, and the count alone did
 * not carry that at a glance across sixty-odd slots.
 */

.slot-grid {
    display: grid;
    gap: var(--space-2);
    grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
}

.slot {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-2) var(--space-3);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

.slot__time {
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

.slot__count { font-size: var(--text-2xs); color: var(--fg-muted); }

.slot__meter {
    block-size: 4px;
    border-radius: var(--radius-pill);
    background: var(--bg-surface-3);
    overflow: hidden;
}

.slot__meter-fill { block-size: 100%; border-radius: inherit; }

.slot[data-state="open"] .slot__meter-fill { background: var(--border-subtle); }
.slot[data-state="filling"] { border-color: hsl(var(--h-info) 60% 70%); }
.slot[data-state="filling"] .slot__meter-fill { background: hsl(var(--h-info) 65% 50%); }
.slot[data-state="filling"] .slot__count { color: hsl(var(--h-info) 55% 38%); }
.slot[data-state="full"] { border-color: hsl(var(--h-danger) 60% 70%); background: hsl(var(--h-danger) 80% 97%); }
.slot[data-state="full"] .slot__meter-fill { background: hsl(var(--h-danger) 60% 50%); }
.slot[data-state="full"] .slot__count { color: hsl(var(--h-danger) 55% 42%); font-weight: var(--weight-medium); }

.slot-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
    font-size: var(--text-2xs);
    color: var(--fg-muted);
}

.slot-legend__dot {
    display: inline-block;
    inline-size: 0.6rem;
    block-size: 0.6rem;
    border-radius: 50%;
    margin-inline-end: var(--space-1);
    vertical-align: middle;
}

.slot-legend__dot[data-state="open"] { background: var(--border-subtle); }
.slot-legend__dot[data-state="filling"] { background: hsl(var(--h-info) 65% 50%); }
.slot-legend__dot[data-state="full"] { background: hsl(var(--h-danger) 60% 50%); }

/* ---- charts */

.chart { display: block; inline-size: 100%; }
.chart--spark { block-size: 2.75rem; }
.chart--gauge { max-inline-size: 9rem; margin-inline: auto; color: var(--fg-muted); }
.chart--donut { max-inline-size: 100%; block-size: 100%; }
.chart--pie { max-inline-size: 7rem; }

/* A ring with its figure in the hole: the number is the reading, the ring is
   the context, and putting them apart makes the reader do the joining. */
.tile__ring {
    position: relative;
    flex: 1 1 auto;
    display: grid;
    place-items: center;
    min-block-size: 5.5rem;
    color: var(--fg-subtle);
}

.tile__ring .chart--donut { max-block-size: 6rem; }

.tile__ring-value {
    position: absolute;
    font-size: var(--text-md);
    font-weight: var(--weight-bold);
    color: var(--fg-default);
}

/* ---- the pictogram */

.pictogram { flex: 1 1 auto; display: flex; align-items: center; justify-content: space-evenly; }
.pictogram__side { text-align: center; }

.pictogram__share {
    margin: var(--space-1) 0 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    line-height: 1;
    color: var(--fg-default);
}

.pictogram__label { margin: 0; font-size: var(--text-2xs); color: var(--fg-muted); }
.pictogram__count { margin: 0; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* ---- compact strips inside tiles */

.minisplit { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 3px; flex: 1 1 auto; }
.minisplit__row { display: flex; align-items: center; gap: var(--space-2); }

.minisplit__label {
    flex: 0 0 4.5rem;
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.minisplit__track {
    flex: 1 1 auto;
    min-width: 0;
    block-size: 0.35rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.minisplit__fill { display: block; block-size: 100%; background: var(--brand-primary); }
.minisplit__fill[data-status="ready"] { background: hsl(var(--h-info) 65% 50%); }
.minisplit__fill[data-status="preparing"] { background: hsl(var(--h-warning) 70% 50%); }
.minisplit__value { flex: 0 0 2rem; text-align: end; font-size: var(--text-2xs); color: var(--fg-muted); }

.minibars { flex: 1 1 auto; display: flex; align-items: flex-end; gap: 1px; min-block-size: 2.5rem; }

.minibars__bar {
    flex: 1 1 0;
    min-width: 0;
    block-size: max(2px, var(--h));
    border-radius: 1px 1px 0 0;
    background: var(--brand-300);
}

.minibars__bar[data-empty="true"] { block-size: 2px; background: var(--bg-inset); }
.minibars__bar[data-peak="true"] { background: var(--brand-primary); }

.stackbar {
    display: block;
    inline-size: 100%;
    block-size: 0.6rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.stackbar__fill { display: block; block-size: 100%; background: var(--brand-primary); }

/* ---- pie with its legend */

.pieblock { flex: 1 1 auto; display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.legend { list-style: none; margin: 0; padding: 0; flex: 1 1 8rem; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.legend__row { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-2xs); }
.legend__dot { inline-size: 0.6rem; block-size: 0.6rem; border-radius: 2px; flex: 0 0 auto; }

.legend__name {
    flex: 1 1 auto;
    min-width: 0;
    color: var(--fg-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.legend__value { color: var(--fg-default); }

/* ---- wide panels */

.panel--wide { padding: var(--space-5); }
.panel__head { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; margin-block-end: var(--space-4); }
.panel__head .panel__title { margin: 0; }
.panel__head .panel__legend { margin: 0; margin-inline-start: auto; }

.board__split {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
}

/* ---- the closing band */

.colophon {
    margin-block-start: var(--space-6);
    background: var(--gradient-brand);
    color: #fff;
    padding: var(--space-5) var(--gutter);
}

.colophon__inner { max-width: var(--content-max); margin-inline: auto; text-align: center; }
.colophon__brand { margin: 0; font-size: var(--text-md); font-weight: var(--weight-bold); }
.colophon__dept { margin: var(--space-1) 0 0; font-size: var(--text-sm); color: rgb(255 255 255 / 78%); }
.colophon__source { margin: var(--space-3) 0 0; font-size: var(--text-2xs); color: rgb(255 255 255 / 62%); }
.colophon__stamp { margin: var(--space-1) 0 0; font-size: var(--text-2xs); color: rgb(255 255 255 / 50%); }

/* ---- the trend panel: a chart with its own figures beside it */

/* The chart takes the whole width — bars need it more than any other shape
   here — and the figures run beneath it as a strip inside the same container.
   Full width and not a lone row are only in conflict while the figures sit
   beside the chart rather than under it. */
.panel--trend { padding: var(--space-5); }
.panel__main { min-width: 0; }

.strip {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 8rem), 1fr));
    margin-block-start: var(--space-4);
    padding-block-start: var(--space-4);
    border-block-start: 1px solid var(--border-subtle);
}

.figure {
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--bg-inset);
}

.figure__label { margin: 0; font-size: var(--text-2xs); color: var(--fg-subtle); }

.figure__value {
    margin: var(--space-1) 0 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    line-height: 1;
    color: var(--fg-default);
}

.figure__note { margin: 2px 0 0; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* ==========================================================  the analysis
   Set apart from the measured figures above it, because it is a different
   kind of statement: those are counted, these are inferred. The panel says so
   in its own footer rather than leaving the reader to assume.               */

.ai {
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    overflow: hidden;
}

.ai__head {
    display: flex;
    gap: var(--space-4);
    align-items: flex-start;
    padding: var(--space-5);
    background: var(--gradient-brand);
    color: #fff;
}

.ai__mark {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 3rem;
    block-size: 3rem;
    border-radius: var(--radius-md);
    font-size: 1.5rem;
    background: rgb(255 255 255 / 16%);
}

.ai__titles { min-width: 0; }

.ai__eyebrow {
    margin: 0;
    display: inline-block;
    padding: 2px var(--space-2);
    border-radius: var(--radius-pill);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
    background: rgb(255 255 255 / 20%);
}

.ai__title { margin: var(--space-2) 0 0; font-size: var(--text-lg); font-weight: var(--weight-bold); }
.ai__lead { margin: var(--space-1) 0 0; font-size: var(--text-sm); color: rgb(255 255 255 / 80%); }

.ai__body {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
    padding: var(--space-5);
}

.ai__column { min-width: 0; }

.ai__subtitle {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin: 0 0 var(--space-3);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

.ai__count {
    margin-inline-start: auto;
    padding: 1px var(--space-2);
    border-radius: var(--radius-pill);
    font-size: var(--text-2xs);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

.ai__empty { margin: 0; font-size: var(--text-sm); color: var(--fg-subtle); }

/* A wait with an end, rather than a bare refusal: "1 of 14" shown as progress
   stops the panel reading as broken. */
.ai__thin {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-5) var(--space-4);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-md);
    text-align: center;
    color: var(--fg-muted);
}

.ai__thin > .bi { font-size: 1.5rem; color: var(--fg-subtle); }
.ai__thin-text { margin: 0; font-size: var(--text-sm); }

.ai__progress {
    inline-size: min(100%, 14rem);
    block-size: 0.4rem;
    border-radius: var(--radius-pill);
    background: var(--bg-inset);
    overflow: hidden;
}

.ai__progress-fill { display: block; block-size: 100%; background: var(--brand-primary); }

.ai__chart { margin-block-end: var(--space-4); }
.chart--forecast { block-size: 8rem; color: var(--fg-subtle); }

.ai__legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin: var(--space-2) 0 0;
    font-size: var(--text-2xs);
    color: var(--fg-muted);
}

.swatch--solid { background: var(--brand-primary); }
.swatch--dashed { background: repeating-linear-gradient(90deg, var(--brand-primary) 0 3px, transparent 3px 6px); margin-inline-start: var(--space-3); }
.swatch--band { background: color-mix(in srgb, var(--brand-primary) 20%, transparent); margin-inline-start: var(--space-3); }

.ai__note {
    margin: var(--space-3) 0 0;
    font-size: var(--text-2xs);
    font-style: italic;
    color: var(--fg-subtle);
    line-height: var(--leading-relaxed);
}

.ai__foot {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5);
    border-block-start: 1px solid var(--border-subtle);
    background: var(--bg-inset);
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    line-height: var(--leading-relaxed);
}

/* ---- the trend and its funnel, side by side */

.board__pair {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 68rem) {
    /* The chart gets two thirds: bars need the width more than a funnel of
       five stacked rows does. */
    .board__pair { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
}

.panel--funnel { padding: var(--space-5); display: flex; flex-direction: column; }
.funnel { flex: 1 1 auto; display: flex; gap: var(--space-4); align-items: stretch; }
.chart--funnel { flex: 0 0 5.5rem; block-size: auto; min-block-size: 11rem; }

.funnel__labels {
    list-style: none;
    margin: 0;
    padding: 0;
    flex: 1 1 auto;
    min-width: 0;
    display: grid;
    align-content: stretch;
}

.funnel__row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto auto;
    align-items: center;
    gap: var(--space-2);
    padding-block: var(--space-1);
    border-block-end: 1px solid var(--border-subtle);
    font-size: var(--text-xs);
}

.funnel__row:last-child { border-block-end: 0; }
.funnel__name { color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.funnel__count { font-weight: var(--weight-semibold); color: var(--fg-default); }
.funnel__share { font-size: var(--text-2xs); color: var(--fg-subtle); min-inline-size: 3rem; text-align: end; }

/* The fall from the stage above is the number a manager is looking for, so it
   gets its own column rather than being left to subtraction. */
.funnel__drop { font-size: var(--text-2xs); color: var(--fg-subtle); min-inline-size: 3rem; text-align: end; }
.funnel__drop[data-sign="down"] { color: hsl(var(--h-danger) 55% 50%); }

/* ---- the drawn figures */

.pictogram__figure {
    display: block;
    /* The figure grows with its share, floored so the smaller side stays
       legible rather than shrinking to a smudge. */
    inline-size: calc(2.25rem + var(--fill, 50%) * 0.06);
    max-inline-size: 4.5rem;
    margin-inline: auto;
}

.figurine { display: block; inline-size: 100%; block-size: auto; }

/* ==========================================================  page heading
   One component for every admin screen. Each page used to compose its own —
   some with a flex row, some with a subtitle, some with different spacing —
   so the title sat at a slightly different height on every screen and the eye
   had to re-find it after each click.                                       */

/* A card, not a rule.
   A heading floating on the canvas with a hairline under it reads as the first
   row of whatever follows; in a surface of its own it reads as the name of the
   page, which is what it is. It also gives the top of every screen the same
   silhouette, so the eye stops re-finding the title after each click. */
.pagecard {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-block: var(--space-4) var(--space-4);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    /* A brand edge on the leading side: enough to tie the page to the rail
       without a second block of colour competing with it. */
    border-inline-start: 3px solid var(--brand-primary);
}

.pagecard__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border-radius: var(--radius-md);
    font-size: 1.2rem;
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

/* The floor matters: a long subtitle would otherwise push the actions off the
   edge instead of wrapping under the title. */
.pagecard__titles { flex: 1 1 14rem; min-width: 0; }

.pagecard__title {
    margin: 0;
    font-size: var(--text-xl);
    font-weight: var(--weight-bold);
    letter-spacing: var(--tracking-tight);
    color: var(--fg-default);
}

.pagecard__subtitle {
    margin: var(--space-1) 0 0;
    font-size: var(--text-sm);
    color: var(--fg-muted);
}

.pagecard__actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* ---- the sidebar wears the hospital's colour, at full depth

   Dark, because it is a rail and not a page: a light sidebar beside a light
   canvas gives the eye no edge to rest against, and every scan of the screen
   has to re-find where navigation ends and content begins. Dark states that
   boundary once and stops competing.

   The text is deliberately not white. Pure white on a deep blue vibrates at
   small sizes; a very pale tint of the same hue sits still.                 */

.sidebar {
    background: linear-gradient(180deg, var(--brand-900) 0%, var(--brand-800) 100%);
    border-inline-end-color: rgb(0 0 0 / 25%);
}

.sidebar .nav-item {
    color: rgb(226 238 250 / 82%);
    font-weight: var(--weight-medium);
}

/* Hover lifts the row rather than tinting the text: a colour change alone is
   easy to miss on a dark ground, and a lifted surface reads as "this one". */
.sidebar .nav-item:hover {
    background: rgb(255 255 255 / 10%);
    color: #fff;
}

.sidebar .nav-item:focus-visible {
    outline: 2px solid rgb(255 255 255 / 70%);
    outline-offset: -2px;
}

/* The active row is the one thing on the rail that must be found instantly,
   so it gets a light surface — the only inversion on the whole panel. */
.sidebar .nav-item.is-active {
    background: #fff;
    color: var(--brand-800);
    font-weight: var(--weight-bold);
    box-shadow: 0 1px 3px rgb(0 0 0 / 25%);
}

.sidebar .nav-item.is-active .nav-item__icon { color: var(--brand-primary); }

.sidebar .nav-item__badge {
    background: rgb(255 255 255 / 16%);
    color: #fff;
}

.sidebar .nav-item.is-active .nav-item__badge {
    background: var(--brand-100);
    color: var(--brand-800);
}

.sidebar .nav-group__label {
    color: rgb(226 238 250 / 55%);
    font-weight: var(--weight-semibold);
}

.sidebar .sidebar__footer {
    border-block-start-color: rgb(255 255 255 / 12%);
    color: rgb(226 238 250 / 55%);
}

/* Dark mode keeps the same rail: it was already dark, and swapping it for the
   page's own surface would remove the very edge this exists to draw. */
:root[data-theme="dark"] .sidebar,
:root:not([data-theme="light"]) .sidebar {
    background: linear-gradient(180deg, #0a1626 0%, #0f2035 100%);
    border-inline-end-color: rgb(255 255 255 / 8%);
}

:root[data-theme="dark"] .sidebar .nav-item,
:root:not([data-theme="light"]) .sidebar .nav-item { color: rgb(226 238 250 / 78%); }

:root[data-theme="dark"] .sidebar .nav-item:hover,
:root:not([data-theme="light"]) .sidebar .nav-item:hover {
    background: rgb(255 255 255 / 8%);
    color: #fff;
}

:root[data-theme="dark"] .sidebar .nav-item.is-active,
:root:not([data-theme="light"]) .sidebar .nav-item.is-active {
    background: var(--brand-primary);
    color: #fff;
}

:root[data-theme="dark"] .sidebar .nav-item.is-active .nav-item__icon,
:root:not([data-theme="light"]) .sidebar .nav-item.is-active .nav-item__icon { color: #fff; }

@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .sidebar {
        background: linear-gradient(180deg, var(--brand-900) 0%, var(--brand-800) 100%);
    }

    :root:not([data-theme="dark"]) .sidebar .nav-item.is-active {
        background: #fff;
        color: var(--brand-800);
    }

    :root:not([data-theme="dark"]) .sidebar .nav-item.is-active .nav-item__icon { color: var(--brand-primary); }
}

/* ---- the two maintenance lists

   Shown side by side, because the question an administrator actually has is
   "does this delete my pharmacies too", and the answer belongs on the page
   rather than in a manual.                                                  */

.wipe__label {
    margin: 0 0 var(--space-2);
    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    letter-spacing: var(--tracking-wide);
}

.wipe { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }

.wipe__row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-2);
    border-radius: var(--radius-sm);
    background: var(--bg-inset);
    font-size: var(--text-xs);
}

.wipe__name { flex: 1 1 auto; min-width: 0; color: var(--fg-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wipe__count { color: hsl(var(--h-danger) 60% 45%); font-weight: var(--weight-semibold); }

/* The return arrow takes the icon's place on a detail screen: both are the
   leading element, and having both would put two round badges side by side
   with nothing to distinguish their roles. */
.pagecard__back {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border-radius: var(--radius-md);
    font-size: 1.1rem;
    background: var(--bg-inset);
    color: var(--fg-muted);
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.pagecard__back:hover { background: var(--accent-soft-bg); color: var(--accent-soft-fg); }

/* ==========================================================  tabbed panels
   Fifty-eight settings in one column meant scrolling past the ten groups you
   did not come for to reach the one you did — and it put the save button at
   the very bottom, where it read as belonging to the last card rather than to
   the whole form.

   The list runs down the side rather than across the top: eleven Arabic group
   names laid in a row overflowed the page, and a tab strip that scrolls
   sideways hides half its own options behind a gesture nobody thinks to make.
   A column has room for every label at full length.

   The panels stay in the DOM when hidden. They are one form, and a detached
   field is a field the browser does not submit.                             */

.tabs {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

@media (min-width: 60rem) {
    .tabs { grid-template-columns: 15rem minmax(0, 1fr); }
}

.tabs__strip {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--space-2);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: calc(var(--topbar-h) + var(--space-4));
}

.tabs__tab {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    inline-size: 100%;
    padding: var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    font-size: var(--text-sm);
    font-weight: var(--weight-medium);
    color: var(--fg-muted);
    text-align: start;
    transition: background var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}

.tabs__tab .bi { inline-size: 1.25rem; flex-shrink: 0; text-align: center; }
.tabs__tab:hover { background: var(--bg-surface-3); color: var(--fg-default); }

.tabs__tab[aria-selected="true"] {
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-weight: var(--weight-semibold);
}

.tabs__tab:focus-visible { outline: 2px solid var(--brand-primary); outline-offset: -2px; }

/* On a phone the column becomes a scrolling row: a full-height list of eleven
   items above the panel would push the settings themselves off the screen. */
@media (max-width: 60rem) {
    .tabs__strip { flex-direction: row; overflow-x: auto; position: static; scrollbar-width: thin; }
    .tabs__tab { inline-size: auto; flex: 0 0 auto; white-space: nowrap; border-radius: var(--radius-pill); }
}

/* Without JavaScript every panel stays visible, and the page is exactly what
   it was before: a readable column of groups. */
.tabs__panel { margin: 0; }

/* ==========================================================  the map
   Tiles from OpenStreetMap. No key, no billing, no account, no tracking
   script — and no mapping library, which would be a second dependency fetched
   from a third host to draw a grid, a circle and a click handler.           */

.geomap {
    position: relative;
    block-size: 20rem;
    overflow: hidden;
    background: var(--bg-inset);
    cursor: crosshair;
    /* Tiles arrive one at a time; a border keeps the frame defined while they
       do, instead of a ragged edge assembling itself. */
    border-block: 1px solid var(--border-subtle);
}

.geomap__canvas { position: absolute; inset: 0; }

.geomap__tile {
    position: absolute;
    inset-block-start: 0;
    inset-inline-start: 0;
    inline-size: 256px;
    block-size: 256px;
    /* The tiles are a west-to-east grid whatever the page direction is:
       positioned from the left edge, never mirrored by RTL. */
    left: 0;
    right: auto;
    user-select: none;
    pointer-events: none;
}

/* The circle is sized in metres by the script. Everything here is appearance
   only — nothing that would change what it claims. */
.geomap__circle {
    position: absolute;
    inset-block-start: 0;
    left: 0;
    border-radius: 50%;
    border: 2px solid var(--brand-primary);
    background: color-mix(in srgb, var(--brand-primary) 14%, transparent);
    pointer-events: none;
}

.geomap__pin {
    position: absolute;
    inset-block-start: 0;
    left: 0;
    inline-size: 14px;
    block-size: 14px;
    margin: -7px 0 0 -7px;
    border-radius: 50%;
    background: var(--brand-primary);
    border: 2px solid #fff;
    box-shadow: 0 1px 4px rgb(0 0 0 / 35%);
    pointer-events: none;
}

.geomap__credit {
    margin: var(--space-2) 0 0;
    font-size: var(--text-2xs);
    color: var(--fg-subtle);
}

.geomap__credit a { color: inherit; text-decoration: underline; }

/* ---- the queue's filter tiles and toolbar

   The four counters were already on the page and are the obvious thing to
   press when you want only that status, so they became buttons rather than
   gaining a second control beside them that means the same thing.          */

.stat--filter {
    inline-size: 100%;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    text-align: start;
    border: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    transition: border-color var(--duration-fast) var(--ease-out),
                box-shadow var(--duration-fast) var(--ease-out);
}

.stat--filter:hover { border-color: var(--border-strong); }

.stat--filter.is-on {
    border-color: var(--brand-primary);
    box-shadow: 0 0 0 1px var(--brand-primary);
}

.stat--filter .stat__label,
.stat--filter .stat__value { display: block; }

.queuebar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-block-end: var(--space-4);
}

.queuebar__search { position: relative; flex: 1 1 16rem; min-width: 0; }

/* The two pickers sit between the search box and the sort buttons, and give up
   their width before the search does: a pharmacist types a ticket number far
   more often than they change what kind of work they are looking at. */
.queuebar__pick { flex: 0 1 11rem; min-width: 0; margin: 0; }
.queuebar__pick .form-select { min-width: 0; }

.queuebar__search .bi {
    position: absolute;
    inset-block-start: 50%;
    inset-inline-start: var(--space-3);
    transform: translateY(-50%);
    color: var(--fg-subtle);
    pointer-events: none;
}

.queuebar__search .form-control { padding-inline-start: var(--space-7); }
.queuebar__sort { display: flex; gap: var(--space-1); }

.queuebar__sort .btn.is-on {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: var(--fg-on-brand);
}

.queuebar__count { font-size: var(--text-xs); color: var(--fg-muted); }

/* ---- rating: the mark, the note, and the card on the order page

   The dialog interrupts a patient who has just collected their medicine, so
   it reads as a thank-you before it reads as a task — hence a mark above the
   heading rather than a bare title and a form.                              */

.rating__mark {
    display: grid;
    place-items: center;
    inline-size: 3rem;
    block-size: 3rem;
    margin-inline: auto;
    margin-block-end: var(--space-3);
    border-radius: 50%;
    font-size: 1.5rem;
    background: color-mix(in srgb, var(--bs-success) 14%, transparent);
    color: hsl(var(--h-success) 45% 40%);
}

/* Said plainly, because a patient asked to comment on a pharmacy wants to know
   who reads it before they write anything candid. */
.rating__privacy {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
    font-size: var(--text-2xs);
    color: var(--fg-subtle);
    line-height: var(--leading-relaxed);
}

.rating-card .stars { justify-content: center; }

/* ── the rating card ───────────────────────────────────────────────────────
   This card asks a favour of somebody who has what they came for and is on
   their way out. It has one job: to look worth five seconds. Hence a coloured
   crown with a face on it rather than a grey header bar, a question set as a
   heading rather than a label, and the note folded away until wanted. */

.rating-card { overflow: hidden; }

.rating-card__crown {
    position: relative;
    display: grid;
    place-items: center;
    padding: var(--space-5) 0 0;
    background: var(--gradient-brand);
    block-size: 5.5rem;
}

/* A soft shoulder where the colour meets the card, so the face sits in a
   dip rather than on a hard edge. */
.rating-card__crown::after {
    content: "";
    position: absolute;
    inset-inline: -10%;
    inset-block-end: -1.75rem;
    block-size: 3.5rem;
    background: var(--bg-surface);
    border-start-start-radius: 50%;
    border-start-end-radius: 50%;
}

.rating-card__crown--done { background: linear-gradient(135deg,
    hsl(var(--h-success) 55% 42%), hsl(var(--h-success) 60% 32%)); }

.rating-card__face {
    position: relative;
    z-index: 1;
    display: grid;
    place-items: center;
    inline-size: 4.25rem;
    block-size: 4.25rem;
    border-radius: 50%;
    background: var(--bg-surface);
    color: #f0a500;
    font-size: 2.4rem;
    line-height: 1;
    box-shadow: 0 6px 18px rgb(0 0 0 / 18%);
    transition: transform var(--duration-normal), color var(--duration-fast);
}

/* A small kick when the score changes, so the face reads as a response to
   the tap rather than as a picture that happens to be there. */
.rating-card__face.is-picked { transform: scale(1.12); }

.rating-card--done .rating-card__face { color: hsl(var(--h-success) 50% 40%); }

/* Positioned, so it paints above the crown's shoulder. An absolutely
   positioned pseudo-element outranks static content however late that content
   comes in the markup — which is how the white curve ended up drawn straight
   across the question. */
.rating--page {
    position: relative;
    z-index: 1;
    padding-top: var(--space-4);
    text-align: center;
}
.rating--page .rating__title { font-size: var(--text-lg); }
.rating--page .form-control { text-align: start; }

/* The note, folded. Most patients tap a star and send; an empty box in front
   of all of them turns five seconds into a form. */
.rating__note { margin: var(--space-4) 0 0; text-align: start; }

.rating__note-toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    font-size: var(--text-sm);
    color: var(--fg-muted);
    list-style: none;
}

.rating__note-toggle::-webkit-details-marker { display: none; }
.rating__note-toggle:hover { color: var(--brand-primary); }
.rating__note[open] .rating__note-toggle { margin-block-end: var(--space-2); }
.rating__textarea { font-size: var(--text-sm); }

/* Their own rating, shown back. Feedback that vanishes on submission leaves
   the patient unsure it was received at all. */
.rating-card--done { border-color: color-mix(in srgb, var(--bs-success) 35%, var(--border-subtle)); }

.rating-card__stars {
    /* Pinned to ltr like the picker above it. A star row reads the same way in
       both languages, and mirroring only this one made the rating that was
       given fill from the opposite side to the rating being chosen. */
    direction: ltr;
    margin: var(--space-3) 0 0;
    font-size: var(--text-xl);
    color: #f0a500;
    letter-spacing: 2px;
}

.rating-card__verdict {
    margin: var(--space-2) 0 0;
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

/* Set as a quotation, because it is one: their own words, given back. */
.rating-card__comment {
    margin: var(--space-4) 0 0;
    padding-inline-start: var(--space-3);
    border-inline-start: 3px solid color-mix(in srgb, var(--brand-primary) 45%, transparent);
    font-size: var(--text-sm);
    color: var(--fg-muted);
    font-style: italic;
    text-align: start;
}

.rating-card__thanks {
    margin: 0;
    font-size: var(--text-base);
    font-weight: var(--weight-bold);
    color: hsl(var(--h-success) 45% 40%);
}

.rating-card__survey {
    margin: var(--space-4) 0 0;
    padding: 0;
    list-style: none;
    text-align: start;
    font-size: var(--text-xs);
    color: var(--fg-muted);
    display: grid;
    gap: var(--space-1);
}

/* A table row that stands in for a link to its record.
   The focus ring is not decoration: the row is tabbable so it can be reached
   without a mouse, and a focusable thing with no visible focus is a trap. */
.row-link {
    cursor: pointer;
}

.row-link:hover {
    background: var(--bg-surface-2);
}

.row-link:focus-visible {
    outline: 2px solid var(--brand-primary);
    outline-offset: -2px;
}

/* ── the family list ───────────────────────────────────────────────────────
   People, not rows of data. The initial does the work an icon cannot: four
   identical silhouettes are harder to tell apart at a glance than four
   different letters. */
.familylist {
    list-style: none;
    margin: 0;
    padding: 0;
}

.familylist__row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-block-start: 1px solid var(--border-subtle);
}

.familylist__row:first-child { border-block-start: 0; }
.familylist--plain .familylist__row { padding-inline: 0; }

.familylist__avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border-radius: 50%;
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-weight: var(--weight-bold);
}

/* min-inline-size: 0 so a long name truncates instead of pushing the remove
   button off the edge of a phone. */
.familylist__who {
    flex: 1 1 auto;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.familylist__name {
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.familylist__meta { font-size: var(--text-xs); color: var(--fg-muted); }
.familylist__remove { color: var(--fg-subtle); }
.familylist__remove:hover { color: var(--bs-danger, #b91c1c); }

/* ── who the order is for ──────────────────────────────────────────────────
   A row of people, not a dropdown. On a phone a select hides everyone but the
   chosen one behind a tap, and the whole point of this control is that the
   patient sees at a glance whose file they are about to raise an order
   against — getting that wrong sends someone else's prescription to a
   counter. */
.whofor {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
    gap: var(--space-2);
    margin-block-start: var(--space-3);
}

.whofor__option { cursor: pointer; }
.whofor__option input { position: absolute; opacity: 0; pointer-events: none; }

.whofor__body {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 2px solid var(--border-default);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
    transition: border-color var(--duration-fast), background var(--duration-fast);
}

.whofor__option input:checked + .whofor__body {
    border-color: var(--brand-primary);
    background: var(--accent-soft-bg);
}

/* The picker is reachable by keyboard, and a focusable control with no
   visible focus is a trap. */
.whofor__option input:focus-visible + .whofor__body {
    outline: 2px solid var(--brand-primary);
    outline-offset: 2px;
}

.whofor__avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.25rem;
    block-size: 2.25rem;
    border-radius: 50%;
    background: var(--bg-surface-2);
    color: var(--fg-muted);
    font-weight: var(--weight-bold);
}

.whofor__option input:checked + .whofor__body .whofor__avatar {
    background: var(--brand-primary);
    color: var(--fg-on-brand);
}

.whofor__who {
    flex: 1 1 auto;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
}

.whofor__name {
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.whofor__meta { font-size: var(--text-xs); color: var(--fg-muted); }

.whofor__check { flex: 0 0 auto; color: var(--brand-primary); opacity: 0; }
.whofor__option input:checked + .whofor__body .whofor__check { opacity: 1; }

.form-step--plain { display: flex; align-items: center; gap: var(--space-2); }

/* Whose ticket this is, when it is not the account holder's. Quiet on
   purpose: it is a qualifier on a card, not the card's subject. */
.forwhom {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    padding: 1px var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--bg-surface-2);
    color: var(--fg-muted);
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
}

/* The link to the household from the account page. */
.familylink {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
    color: inherit;
    text-decoration: none;
    transition: border-color var(--duration-fast), transform var(--duration-fast);
}

.familylink:hover { border-color: var(--brand-primary); transform: translateY(-1px); }

.familylink__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.75rem;
    block-size: 2.75rem;
    border-radius: var(--radius-md);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-size: 1.25rem;
}

.familylink__text { flex: 1 1 auto; min-inline-size: 0; display: flex; flex-direction: column; }
.familylink__title { font-weight: var(--weight-semibold); }
.familylink__hint { font-size: var(--text-xs); color: var(--fg-muted); }

/* ── what the hall is calling, on the counter's screen ─────────────────────
   The pharmacist stands with their back to the board. This strip is the only
   place they can see the number the room has just been told, so it is sized
   to be read from a glance up rather than a lean in. */
.nowcalling {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-3) var(--space-4);
    margin-block-end: var(--space-3);
    border: 1px solid var(--border-default);
    border-inline-start: 4px solid hsl(var(--h-success) 45% 45%);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}

.nowcalling__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
    color: var(--fg-muted);
}

.nowcalling__dot {
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: hsl(var(--h-success) 45% 45%);
}

.nowcalling__ticket {
    font-size: var(--text-2xl);
    font-weight: var(--weight-black);
    line-height: 1;
    color: var(--fg-default);
    direction: ltr;
}

.nowcalling__window {
    display: inline-flex;
    align-items: baseline;
    gap: var(--space-1);
    padding: 2px var(--space-2);
    border-radius: var(--radius-pill);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-size: var(--text-xs);
    font-weight: var(--weight-semibold);
}

/* Nobody being called is a fact, not a failure: the strip stays, greyed. */
.nowcalling.is-idle { border-inline-start-color: var(--border-default); }
.nowcalling.is-idle .nowcalling__dot { background: var(--border-strong); }
.nowcalling.is-idle .nowcalling__ticket { color: var(--fg-subtle); }

@media (prefers-reduced-motion: no-preference) {
    .nowcalling.is-new { animation: nowcalling-flash 900ms var(--ease-out); }
}

@keyframes nowcalling-flash {
    from { background: color-mix(in srgb, hsl(var(--h-success) 45% 45%) 18%, var(--bg-surface)); }
    to   { background: var(--bg-surface); }
}

/* ── clinic tags ───────────────────────────────────────────────────────────
   A visit can span several clinics and still be one order. Rendered as one
   comma-separated string those read as a single clinic with a long name, and
   the pharmacist checking they have all three prescriptions has to parse a
   sentence instead of counting chips.

   Named `clinictag`, not `clinic-chip`: that one is already the picker on
   the new-order form, and reusing it would have silently restyled every
   clinic a patient chooses from.

   The hue arrives as --clinic-hue, computed from the clinic id so the same
   clinic is the same colour on every screen. Saturation and lightness are
   fixed here rather than in the template so no clinic can come out
   unreadable — and the name is always written, because colour on its own is
   not a label. */
.clinictags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.clinictag {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-1) var(--space-3);
    border-radius: var(--radius-pill);
    border: 1px solid hsl(var(--clinic-hue) 55% 82%);
    background: hsl(var(--clinic-hue) 70% 96%);
    color: hsl(var(--clinic-hue) 55% 28%);
    font-size: var(--text-xs);
    line-height: 1.25;
}

.clinictag__dot {
    flex: 0 0 auto;
    inline-size: 8px;
    block-size: 8px;
    border-radius: 50%;
    background: hsl(var(--clinic-hue) 60% 45%);
}

.clinictag__text { display: flex; flex-direction: column; }
.clinictag__name { font-weight: var(--weight-semibold); }

/* The floor is why a patient walked somewhere and where a pharmacist would
   send them back to — worth carrying, not worth competing with the name. */
.clinictag__floor {
    font-size: var(--text-2xs);
    opacity: 0.75;
    font-weight: var(--weight-normal);
}

/* Dark mode gets the same hue at a workable lightness rather than a washed
   pastel on a dark ground, which is unreadable at every hue. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .clinictag {
        border-color: hsl(var(--clinic-hue) 40% 34%);
        background: hsl(var(--clinic-hue) 45% 16%);
        color: hsl(var(--clinic-hue) 65% 82%);
    }

    :root:not([data-theme="light"]) .clinictag__dot {
        background: hsl(var(--clinic-hue) 60% 62%);
    }
}

:root[data-theme="dark"] .clinictag {
    border-color: hsl(var(--clinic-hue) 40% 34%);
    background: hsl(var(--clinic-hue) 45% 16%);
    color: hsl(var(--clinic-hue) 65% 82%);
}

:root[data-theme="dark"] .clinictag__dot {
    background: hsl(var(--clinic-hue) 60% 62%);
}

/* Search results on the geofence page. A list of places, each one a button —
   picking the right building is the whole task, so the full address is shown
   rather than truncated to a tidy width. */
.placelist {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-block-start: var(--space-2);
    max-block-size: 14rem;
    overflow-y: auto;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    padding: var(--space-1);
}

.placelist__item {
    text-align: start;
    padding: var(--space-2) var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--fg-default);
    font-size: var(--text-sm);
    line-height: var(--leading-snug);
    cursor: pointer;
}

.placelist__item:hover,
.placelist__item:focus-visible {
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

/* ── choosing a location ───────────────────────────────────────────────────
   The point, said back. The coordinate boxes are folded away because nobody
   types them, so this is the only confirmation that a search, a tap on the
   map or the "use my location" button did anything at all. */
.pointcard {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    border: 1px dashed var(--border-default);
    border-radius: var(--radius-md);
    color: var(--fg-muted);
}

.pointcard.is-set {
    border-style: solid;
    border-color: color-mix(in srgb, var(--brand-primary) 45%, transparent);
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
}

.pointcard .bi { font-size: 1.25rem; }
.pointcard__text { display: flex; flex-direction: column; }
.pointcard__label { font-size: var(--text-2xs); opacity: 0.8; }
.pointcard__value { font-weight: var(--weight-semibold); direction: ltr; }

.manualpoint__toggle {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
    font-size: var(--text-sm);
    color: var(--fg-muted);
    list-style: none;
}

.manualpoint__toggle::-webkit-details-marker { display: none; }
.manualpoint__toggle:hover { color: var(--brand-primary); }

/* ── the filter bar ────────────────────────────────────────────────────────
   Above the figures it governs, not below them. A control placed after the
   numbers it changes reads as an afterthought, so people scroll past it and
   then wonder why the page is showing them last month. */
.filterbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--bg-surface);
}

.filterbar__label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--fg-muted);
}

/* Pushed to the far end: the dates confirm what the selects chose, and a
   confirmation belongs after the thing it confirms. */
.filterbar__meta {
    margin-inline-start: auto;
    font-size: var(--text-xs);
    color: var(--fg-subtle);
}

/* A sparkline under a KPI. Sized small on purpose — it is the shape of the
   number above it, not a chart in its own right. */
.kpi__spark {
    margin-block-start: var(--space-2);
    block-size: 2.25rem;
}

.kpi__spark svg { inline-size: 100%; block-size: 100%; }

/* ── the outlook panel ──────────────────────────────────────────────────────────
   Named `outlook`, not `forecast`: that one is already the card grid on the
   public board, and `.forecast__label` is styled there — reusing the name
   would have restyled a page nobody was looking at.

   The line first, the figures beside it. Two numbers say what the model
   expects; the widening band says how confident it is entitled to be — which
   is the honest part of a forecast and the part a table hides. */
.outlook {
    display: grid;
    gap: var(--space-4);
    grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
    align-items: center;
}

@media (max-width: 55rem) {
    .outlook { grid-template-columns: minmax(0, 1fr); }
}

.outlook__chart svg { inline-size: 100%; block-size: auto; }

.outlook__figures {
    display: grid;
    gap: var(--space-3);
}

.outlook__figure {
    padding: var(--space-3);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-md);
    background: var(--bg-surface-2);
}

.outlook__label { margin: 0; font-size: var(--text-xs); color: var(--fg-muted); }

.outlook__value {
    margin: var(--space-1) 0 0;
    font-size: var(--text-2xl);
    font-weight: var(--weight-black);
    line-height: 1;
    color: var(--fg-default);
}

.outlook__range { margin: var(--space-1) 0 0; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* ── the insight grid ──────────────────────────────────────────────────────
   One grid, not two columns.

   Side by side, alerts and observations came out 122 px and 543 px tall — a
   420 px hole under the shorter one, which is the empty space that made this
   page look unfinished. Two columns only work when both sides are roughly the
   same length, and there is no reason these two ever would be: how many things
   crossed a line has nothing to do with how many patterns the numbers show.

   `align-items: start` matters. Without it every card in a row stretches to
   the tallest, so a one-line alert beside a four-line one grows three lines of
   nothing — the same hole, one row down. */
.insightgrid {
    display: grid;
    gap: var(--space-3);
    /* auto-fill, not auto-fit. auto-fit collapses the empty tracks, so a
       single alert stretched the whole width of the page and read as a banner
       rather than as one item among several. auto-fill keeps the column width
       whatever the count. */
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr));
    align-items: start;
}

/* The tag and the sentence on one line, so a two-word recommendation does not
   occupy two. */
.insight__advice,
.insight__evidence {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--space-2);
}

/* The forecast beside the leaderboard. 5:4 rather than even — the forecast
   carries a chart and needs the width more than a list of names does. Stacks
   below the breakpoint, where two columns of either would be unreadable. */
.lowerpair {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

@media (min-width: 68rem) {
    .lowerpair { grid-template-columns: minmax(0, 5fr) minmax(0, 4fr); }
}

/* ── the insights page layout ──────────────────────────────────────────────
   Alerts beside the forecast. A manager reads them together: one says
   something crossed a line today, the other says whether tomorrow is likely to
   be heavier, and what to do about the first depends on the second.

   3:2 because the alerts are sentences and the forecast is a chart with three
   figures beside it. Stacks below the breakpoint, where neither would be
   readable in half a screen. */
.toppair {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}

@media (min-width: 68rem) {
    .toppair { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
}

/* The pharmacies side by side. There are two of them and the page is comparing
   them; stacked, the reader holds the first score in their head while
   scrolling to the second — the one thing this section exists to avoid. */
.scoregrid {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
    align-items: start;
}

/* ── patient feedback ──────────────────────────────────────────────────────
   A list of what people said, not a table of scores. Each card is one visit,
   and the sentence is the largest thing in it — the stars are the index, the
   words are the content. */
.ratinglist {
    display: grid;
    gap: var(--space-3);
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 24rem), 1fr));
    align-items: start;
}

.ratingcard {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-inline-start: 3px solid var(--border-default);
    border-radius: var(--radius-md);
    background: var(--bg-surface);
}

/* Low scores are findings, not decoration: they are the reason to open this
   page and they are coloured so the eye reaches them first. */
.ratingcard[data-tone="bad"]  { border-inline-start-color: hsl(var(--h-danger) 60% 50%); }
.ratingcard[data-tone="warn"] { border-inline-start-color: hsl(var(--h-warning) 70% 50%); }
.ratingcard[data-tone="good"] { border-inline-start-color: hsl(var(--h-success) 45% 45%); }

.ratingcard__head {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.ratingcard__stars { color: #f0a500; direction: ltr; letter-spacing: 1px; }
.ratingcard__verdict { font-size: var(--text-sm); font-weight: var(--weight-semibold); color: var(--fg-default); }
.ratingcard__when { margin-inline-start: auto; font-size: var(--text-2xs); color: var(--fg-subtle); }

/* Set as a quotation, because it is one. */
.ratingcard__comment {
    margin: var(--space-3) 0 0;
    padding-inline-start: var(--space-3);
    border-inline-start: 2px solid var(--border-subtle);
    color: var(--fg-default);
    line-height: var(--leading-relaxed);
}

.ratingcard__silent { margin: var(--space-3) 0 0; font-size: var(--text-sm); color: var(--fg-subtle); font-style: italic; }

.ratingcard__foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex-wrap: wrap;
    margin-block-start: var(--space-3);
    padding-block-start: var(--space-3);
    border-block-start: 1px solid var(--border-subtle);
    font-size: var(--text-xs);
}

.ratingcard__ticket {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    font-weight: var(--weight-bold);
    color: var(--brand-primary);
    text-decoration: none;
}

.ratingcard__ticket:hover { text-decoration: underline; }
.ratingcard__meta { color: var(--fg-muted); }

/* The survey's answers, read beside the stars they produced — a compact
   wrapped row of chips rather than another blockquote, since these are
   fixed answers rather than the patient's own words. */
.ratingcard__survey {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-3) 0 0;
    padding: 0;
    list-style: none;
}

.ratingcard__survey li {
    padding: var(--space-1) var(--space-2);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-pill);
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    background: var(--bg-surface-2);
}

/* ───────────────────────────────────────────  the service survey  ───── */
/* Its own page, its own pace — one clear question at a time rather than a
   form squeezed into a popup or a sidebar card. */

.ratingsurvey__intro {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-block-end: var(--space-4);
    padding-block-end: var(--space-3);
    border-block-end: 1px solid var(--border-subtle);
}

.ratingsurvey__order { margin: 0; color: var(--fg-muted); font-size: var(--text-sm); }
.ratingsurvey__count { color: var(--fg-subtle); font-size: var(--text-xs); white-space: nowrap; }

.ratingsurvey__question {
    border: 0;
    padding: var(--space-3) var(--space-3) var(--space-4);
    margin: 0 0 var(--space-3);
    border-radius: var(--radius-md);
    background: var(--bg-surface-2);
}

.ratingsurvey__label {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: 0;
    font-weight: var(--weight-semibold);
    font-size: var(--text-sm);
    color: var(--fg-default);
    margin-block-end: var(--space-3);
}

.ratingsurvey__number {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    inline-size: 1.5rem;
    block-size: 1.5rem;
    border-radius: 50%;
    background: var(--accent-soft-bg);
    color: var(--accent-soft-fg);
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
}

.ratingsurvey__type-icon { flex: 0 0 auto; color: var(--fg-subtle); margin-block-start: 0.2em; }
.ratingsurvey__text { flex: 1 1 auto; }

.ratingsurvey__options { display: flex; flex-wrap: wrap; gap: var(--space-2); }

/* A little taller and a touch heavier than the default btn-outline-primary —
   this is the control the whole page is for, and it should read as one. */
.ratingsurvey__option { padding-block: var(--space-2); font-weight: var(--weight-medium); }

.ratingsurvey__privacy { margin-block-end: 0; }

.ratingsurvey__optional {
    display: block;
    font-weight: var(--weight-regular);
    color: var(--fg-subtle);
    font-size: var(--text-xs);
    margin-block-start: 0.15rem;
}

.pager { display: flex; align-items: center; justify-content: center; gap: var(--space-3); }
.pager__count { font-size: var(--text-sm); color: var(--fg-muted); }

/* ─────────────────────────────────────────  install invitation  ───── */
/* Sits above the bottom bar, never over it: the tab bar is how a patient
   leaves the page, and a banner that covers it turns a suggestion into a
   trap. Below the bottom bar in the stack for the same reason. */
.installbar {
    position: fixed;
    inset-inline: var(--space-3);
    inset-block-end: calc(var(--bottomnav-h) + var(--safe-bottom) + var(--space-3));
    z-index: calc(var(--z-bottomnav) - 1);

    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);

    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);

    transform: translateY(140%);
    opacity: 0;
    transition: transform 220ms ease, opacity 220ms ease;
}

.installbar.is-visible {
    transform: translateY(0);
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    .installbar { transition: none; }
}

.installbar__icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 2.5rem;
    block-size: 2.5rem;
    border-radius: var(--radius-md);
    background: var(--brand-50);
    color: var(--brand-primary);
    font-size: 1.15rem;
}

.installbar__body { flex: 1 1 auto; min-inline-size: 0; }

.installbar__title {
    margin: 0 0 2px;
    font-size: var(--text-sm);
    font-weight: var(--weight-semibold);
    color: var(--fg-default);
}

.installbar__text {
    margin: 0;
    font-size: var(--text-2xs);
    line-height: 1.55;
    color: var(--fg-muted);
}

/* The two taps, in order, as they appear in Safari's share sheet. */
.installbar__steps {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-2) 0 0;
    font-size: var(--text-2xs);
    font-weight: var(--weight-medium);
}

.installbar__step {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px var(--space-2);
    border-radius: var(--radius-sm);
    background: var(--brand-50);
    color: var(--brand-800);
}

.installbar__arrow { color: var(--fg-subtle); font-size: 0.7em; }

.installbar__actions {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.installbar__close {
    position: absolute;
    inset-block-start: var(--space-2);
    inset-inline-end: var(--space-2);
    padding: 2px 5px;
    border: 0;
    background: none;
    color: var(--fg-subtle);
    font-size: 0.7rem;
    line-height: 1;
}

/* The iOS card instructs rather than acts, so it reads as a block with the
   acknowledgement beneath it instead of a button parked on the end. */
.installbar--ios { flex-wrap: wrap; }
.installbar--ios .installbar__actions { flex-direction: row; margin-inline-start: auto; }

@media print {
    .installbar { display: none !important; }
}

/* ────────────────────────────────────  the patient page heading  ───── */

/*
 * A brand-coloured band at the top of every patient screen.
 *
 * The staff shell uses `.pagecard`, a white card on a white app. The patient
 * shell is a phone application, and there the heading is what tells you which
 * screen you are on the instant it appears — so it carries the platform's own
 * colour rather than dissolving into the canvas the way six hand-written
 * headings previously did.
 *
 * Rounded and inside the gutter, deliberately not bled to the screen edges: a
 * full-bleed band has to be pulled out with a negative margin, and a negative
 * margin that does not match the gutter to the pixel widens the scroll area and
 * slides the whole right-to-left layout sideways. That bug has been fixed on
 * this page once already.
 */
.pagehero {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-block: var(--space-4);
    padding: var(--space-4);
    border-radius: var(--radius-lg);
    background: var(--gradient-brand);
    box-shadow: var(--shadow-md);
    color: #fff;
    position: relative;
    overflow: hidden;
}

/* A soft bloom so the flat gradient gains a little depth, clipped by the
   band itself so it can never reach the edge of the screen. */
.pagehero::after {
    content: "";
    position: absolute;
    inset-block-start: -70px;
    inset-inline-end: -50px;
    inline-size: 190px;
    block-size: 190px;
    border-radius: 50%;
    background: rgb(255 255 255 / 12%);
    pointer-events: none;
}

.pagehero__back,
.pagehero__icon {
    position: relative;
    z-index: 1;
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    inline-size: 40px;
    block-size: 40px;
    border-radius: var(--radius-md);
    background: rgb(255 255 255 / 16%);
    color: #fff;
    font-size: 1.15rem;
    text-decoration: none;
    transition: background var(--duration-fast);
}

.pagehero__back:hover,
.pagehero__back:focus-visible { background: rgb(255 255 255 / 28%); color: #fff; }

.pagehero__titles {
    position: relative;
    z-index: 1;
    flex: 1 1 auto;
    min-inline-size: 0;
}

.pagehero__title {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--weight-bold);
    line-height: var(--leading-snug);
    color: #fff;
}

.pagehero__subtitle {
    margin: 2px 0 0;
    font-size: var(--text-2xs);
    line-height: var(--leading-snug);
    color: rgb(255 255 255 / 82%);
}

.pagehero__actions {
    position: relative;
    z-index: 1;
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Badges inherit the band rather than punching a light hole in it. */
.pagehero__actions .badge {
    background: rgb(255 255 255 / 18%) !important;
    color: #fff !important;
    border-color: rgb(255 255 255 / 28%) !important;
}

@media print {
    .pagehero {
        background: none;
        box-shadow: none;
        color: #000;
    }

    .pagehero::after { content: none; }
    .pagehero__title, .pagehero__subtitle { color: #000; }
}

/* ─────────────────────────────────────  the purge depth chooser  ───── */
/* Two options with very different consequences. Each is a full row with its
   own count, because the number is the thing that tells an administrator
   which one they are actually about to run. */
.scope {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3);
    margin-block-end: var(--space-2);
    border: 1.5px solid var(--border-subtle);
    border-radius: var(--radius-md);
    cursor: pointer;
    transition: border-color var(--duration-fast), background var(--duration-fast);
}

.scope input { flex: 0 0 auto; }
.scope__body { flex: 1 1 auto; min-inline-size: 0; }
.scope__title { display: block; font-weight: var(--weight-semibold); font-size: var(--text-sm); }

.scope__hint {
    display: block;
    font-size: var(--text-2xs);
    color: var(--fg-muted);
    line-height: var(--leading-snug);
}

.scope__count {
    flex: 0 0 auto;
    font-weight: var(--weight-bold);
    font-variant-numeric: tabular-nums;
    color: var(--fg-muted);
}

.scope:has(input:checked) { border-color: var(--brand-primary); background: var(--accent-soft-bg); }

/* The deeper option is marked before it is chosen, not after. */
.scope--danger:has(input:checked) {
    border-color: var(--hue-danger);
    background: color-mix(in srgb, var(--hue-danger) 8%, transparent);
}

.scope--danger:has(input:checked) .scope__count { color: var(--hue-danger); }

/* ──────────────────────────────────────────  pull to refresh  ───── */
/* Directly under the fixed top bar, not over the content: it is chrome, and a
   refresh control that covers the first card hides the thing being refreshed. */
.pullrefresh {
    position: fixed;
    inset-inline: 0;
    top: calc(var(--topbar-h) + var(--safe-top));
    z-index: calc(var(--z-topbar) - 1);

    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);

    padding-block: var(--space-2);
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-subtle);
    box-shadow: var(--shadow-sm);

    font-size: var(--text-2xs);
    font-weight: var(--weight-semibold);
    color: var(--fg-muted);

    transform: translateY(-100%);
    opacity: 0;
    pointer-events: none;
    transition: opacity 140ms ease;
}

.pullrefresh.is-visible { opacity: 1; }

/* Past the threshold the control says so, before the finger is lifted —
   otherwise the only way to learn whether the pull was far enough is to let go
   and find out. */
.pullrefresh.is-ready { color: var(--brand-primary); }

.pullrefresh__spinner {
    inline-size: 0.9rem;
    block-size: 0.9rem;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
}

@media (prefers-reduced-motion: no-preference) {
    .pullrefresh.is-ready .pullrefresh__spinner,
    .pullrefresh.is-loading .pullrefresh__spinner { animation: pullspin 700ms linear infinite; }
}

@keyframes pullspin { to { transform: rotate(360deg); } }

/* ==========================================================  the day outlook
   Today's arrivals drawn on top of the band this pharmacy usually runs in.
   Shared by the manager's dashboard and the public board, so it carries no
   per-patient figure — hourly counts and nothing else.                     */

.dayband {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.dayband__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
}

.dayband__title { font-weight: var(--weight-bold); }
.dayband__unit { font-size: var(--text-xs); color: var(--fg-muted); }

.dayband__state {
    border-radius: var(--radius-pill);
    padding: 0.15rem 0.7rem;
    font-size: var(--text-xs);
    font-weight: var(--weight-bold);
    white-space: nowrap;
}

.dayband__state--success { background: hsl(var(--h-success) 60% 92%); color: hsl(var(--h-success) 45% 28%); }
.dayband__state--warning { background: hsl(var(--h-warning) 80% 90%); color: hsl(var(--h-warning) 60% 26%); }
.dayband__state--info { background: hsl(var(--h-info) 70% 92%); color: hsl(var(--h-info) 50% 30%); }

.dayband__lead { margin: 0; font-size: var(--text-sm); }

.dayband__chart {
    display: flex;
    align-items: flex-end;
    gap: 2px;
    block-size: 132px;
}

.dayband__col {
    flex: 1 1 0;
    min-inline-size: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    block-size: 100%;
}

/* Hours not yet reached are dimmed rather than hidden: the shape of the
   afternoon still to come is half of what a staffing decision needs. */
.dayband__col.is-ahead { opacity: 0.6; }

.dayband__stack {
    position: relative;
    flex: 1 1 auto;
    background: var(--bg-surface-3);
    border-radius: 3px;
    overflow: hidden;
}

/* The band: where this hour normally lands, from the quiet fifth to the busy
   fifth of the days behind it. */
.dayband__band {
    position: absolute;
    inset-inline: 0;
    background: color-mix(in srgb, var(--brand-primary) 32%, transparent);
}

.dayband__typical {
    position: absolute;
    inset-inline: 0;
    block-size: 2px;
    background: color-mix(in srgb, var(--brand-primary) 60%, transparent);
}

.dayband__bar {
    position: absolute;
    inset-block-end: 0;
    inset-inline-start: 22%;
    inline-size: 56%;
    background: var(--brand-primary);
    border-radius: 2px 2px 0 0;
}

/* An hour outside the band is the one thing on this chart worth reacting to,
   and it is marked by shape as well as colour — a printed page and a reader
   who does not separate hues both still see it. */
.dayband__bar.is-out {
    background: hsl(var(--h-warning) 75% 45%);
    outline: 2px solid hsl(var(--h-warning) 70% 32%);
    outline-offset: -2px;
}

.dayband__hour {
    font-size: 0.62rem;
    color: var(--fg-subtle);
    text-align: center;
}

.dayband__legend {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    font-size: var(--text-xs);
    color: var(--fg-muted);
}

.dayband__key {
    display: inline-block;
    inline-size: 0.8rem;
    block-size: 0.55rem;
    margin-inline-end: 0.3rem;
    border-radius: 2px;
    vertical-align: middle;
}

.dayband__key--band { background: color-mix(in srgb, var(--brand-primary) 32%, transparent); }
.dayband__key--bar { background: var(--brand-primary); }

.dayband__peak { margin-inline-start: auto; }
.dayband__basis { margin: 0; font-size: var(--text-xs); color: var(--fg-subtle); }

/* On the wall the same panel is read from across a hall, so the sentence
   carries the meaning and the chart is the evidence behind it. */
.panel--wall .dayband__title { font-size: 1.1rem; }
.panel--wall .dayband__lead { font-size: 1.05rem; line-height: 1.6; }
.panel--wall .dayband__state { font-size: 0.95rem; padding: 0.2rem 0.9rem; }
.panel--wall .dayband__chart { block-size: 96px; }
.panel--wall .dayband__legend,
.panel--wall .dayband__basis { font-size: 0.8rem; }

/* ==========================================================  الصيدلي الذكي
   The patient-app assistant: its own centred dialog, not a reuse of the
   bottom-sheet `.wasfa-modal`. A "your turn" sheet is one decision, answered
   once and dismissed from the bottom edge; a conversation is read as a whole
   and wants to sit in the middle of the screen, under the reader's thumb,
   the way any chat panel does.                                            */

/* A ring rather than a solid disc behind the drawing: the character is
   already busy with colour of its own, and a flat brand-coloured fill behind
   it competed with the coat instead of framing it. */
.smart-launcher {
    position: fixed;
    inset-inline-end: var(--space-4);
    inset-block-end: calc(var(--bottomnav-h) + var(--safe-bottom) + var(--space-3));
    z-index: calc(var(--z-bottomnav) - 1);

    inline-size: 52px;
    block-size: 52px;
    border: 2px solid var(--brand-primary);
    border-radius: 50%;
    background: var(--bg-surface);
    box-shadow: var(--shadow-xl);
    display: grid;
    place-items: center;
    padding: 0;
}

.smart-launcher .pharm { display: block; filter: drop-shadow(0 1px 1px rgb(0 0 0 / 15%)); }

@media (prefers-reduced-motion: no-preference) {
    .smart-launcher { animation: smart-launcher-in 320ms var(--ease-out) both; }
}

@keyframes smart-launcher-in {
    from { opacity: 0; transform: scale(.6); }
}

/* Desktop: the app is a centred column, and the button follows it rather
   than drifting to the browser window's own edge. */
@media (min-width: 900px) {
    .smart-launcher { inset-inline-end: calc(50vw - var(--content-max) / 2 + var(--space-4)); }
}

/* ── the dialog itself ──────────────────────────────────────────────────── */

.smart-dialog {
    position: fixed;
    inset: 0;
    z-index: 1200;
    display: grid;
    place-items: center;
    padding: var(--space-4);
}

.smart-dialog[hidden] { display: none; }

.smart-dialog__backdrop {
    position: absolute;
    inset: 0;
    border: 0;
    background: rgb(8 20 33 / 55%);
    backdrop-filter: blur(3px);
}

.smart-dialog__card {
    position: relative;
    inline-size: min(440px, 100%);
    max-block-size: min(680px, calc(100dvh - var(--space-8)));
    display: flex;
    flex-direction: column;
    background: var(--bg-surface);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-xl);
    overflow: hidden;
    text-align: start;
}

@media (prefers-reduced-motion: no-preference) {
    .smart-dialog__card { animation: smart-dialog-in 220ms var(--ease-out) both; }
}

@keyframes smart-dialog-in {
    from { opacity: 0; transform: translateY(10px) scale(.97); }
}

/* ── the header: brand-tinted, so the panel reads as staffed rather than as
   a plain form ────────────────────────────────────────────────────────── */

.smartchat__head {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    background: var(--gradient-brand, var(--brand-primary));
    color: #fff;
    flex-shrink: 0;
}

.smartchat__avatar {
    inline-size: 44px;
    block-size: 44px;
    border-radius: 50%;
    background: #fff;
    display: grid;
    place-items: center;
    flex-shrink: 0;
    box-shadow: var(--shadow-md);
}

.smartchat__who { flex: 1; min-inline-size: 0; }
.smartchat__who strong { display: block; font-size: var(--text-md); }
.smartchat__who span { font-size: var(--text-xs); opacity: .85; }

.smartchat__head .icon-btn { color: #fff; }
.smartchat__head .icon-btn:hover { background: rgb(255 255 255 / 15%); }

/* ── the transcript ────────────────────────────────────────────────────── */

.smartchat__log {
    flex: 1 1 auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-block-size: 8rem;
    overflow-y: auto;
    padding: var(--space-4) var(--space-5);
    background: var(--bg-surface-2);
}

/* Each message is a row: the assistant's carries a mini avatar beside it,
   the patient's own does not — their side of the conversation is already
   marked by colour and alignment. */
.smartchat__row {
    display: flex;
    align-items: flex-end;
    gap: var(--space-2);
}

.smartchat__row--me { justify-content: flex-end; }

.smartchat__mini-avatar {
    inline-size: 22px;
    block-size: 22px;
    border-radius: 50%;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    flex-shrink: 0;
    background-image: radial-gradient(circle at 50% 35%, hsl(var(--h-info) 55% 88%), var(--bg-surface) 70%);
}

/* Consecutive replies from the same speaker are grouped: the avatar only
   marks where a turn begins, not every line inside it — the way an actual
   conversation reads, rather than a column of identical icons. */
.smartchat__row.is-grouped .smartchat__mini-avatar { visibility: hidden; }
.smartchat__row--me.is-grouped { margin-block-start: -2px; }

.smartchat__line {
    max-inline-size: 78%;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-lg);
    font-size: var(--text-sm);
    line-height: 1.55;
    box-shadow: var(--shadow-xs, none);
}

.smartchat__line--them {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-start-start-radius: 4px;
}

.smartchat__line--me {
    background: var(--gradient-brand, var(--brand-primary));
    color: #fff;
    border-end-end-radius: 4px;
}

/* The one line that asks the patient to act right now, and the one line that
   admits there is nothing to report — each gets its own colour, so the reply
   that matters is not read at the same weight as small talk. */
.smartchat__line--ready {
    background: hsl(var(--h-success) 55% 94%);
    border-color: hsl(var(--h-success) 45% 55%);
    color: hsl(var(--h-success) 30% 24%);
    font-weight: var(--weight-bold);
}

.smartchat__line--none {
    background: hsl(var(--h-warning) 85% 94%);
    border-color: hsl(var(--h-warning) 60% 55%);
    color: hsl(var(--h-warning) 45% 26%);
}

.smartchat__ready-mark {
    margin-inline-end: var(--space-1);
    color: hsl(var(--h-success) 45% 38%);
}

.smartchat__wait {
    display: flex;
    gap: 5px;
    align-items: center;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-lg);
    border-start-start-radius: 4px;
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
}

.smartchat__wait i { inline-size: 6px; block-size: 6px; border-radius: 50%; background: var(--fg-subtle); }

@media (prefers-reduced-motion: no-preference) {
    .smartchat__wait i { animation: smartdot 1s infinite ease-in-out; }
    .smartchat__wait i:nth-child(2) { animation-delay: .15s; }
    .smartchat__wait i:nth-child(3) { animation-delay: .3s; }
}

@keyframes smartdot { 0%, 60%, 100% { opacity: .35; } 30% { opacity: 1; } }

/* ── chips, the input line, and ending the conversation ───────────────── */

.smartchat__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5) 0;
    background: var(--bg-surface-2);
}

.smartchat__chip {
    border: 1px solid var(--border-subtle);
    background: var(--bg-surface);
    border-radius: var(--radius-pill);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
    color: var(--fg-default);
    transition: background var(--duration-fast) var(--ease-out), border-color var(--duration-fast) var(--ease-out);
}

.smartchat__chip:hover { background: var(--accent-soft-bg); border-color: var(--brand-primary); }

.smartchat__foot {
    flex-shrink: 0;
    padding: var(--space-3) var(--space-5) var(--space-4);
    background: var(--bg-surface-2);
    border-top: 1px solid var(--border-subtle);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.smartchat__say { display: flex; gap: var(--space-2); }

.smartchat__say input {
    flex: 1;
    min-inline-size: 0;
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-pill);
    padding: var(--space-2) var(--space-4);
    font: inherit;
    font-size: var(--text-sm);
    background: var(--bg-surface);
    color: var(--fg-default);
}

.smartchat__send {
    inline-size: 40px;
    block-size: 40px;
    border-radius: 50%;
    border: none;
    background: var(--brand-primary);
    color: #fff;
    display: grid;
    place-items: center;
    flex-shrink: 0;
}

/*
 * Ending the conversation is its own line, not only the × in the corner.
 *
 * A patient who has just had their question answered is at the exact point
 * of being done, and naming that action in words — rather than leaving only
 * a small icon to close the panel — is what makes closing read as a
 * deliberate "I'm finished" instead of an accidental dismissal.
 */
.smartchat__end {
    align-self: center;
    display: flex;
    align-items: center;
    gap: var(--space-2);
    border: none;
    background: none;
    color: var(--fg-muted);
    font-size: var(--text-xs);
    padding: var(--space-1) var(--space-2);
}

.smartchat__end:hover { color: var(--fg-default); }

/* ==========================================================  patient messages
   /admin/messages — the wording a hospital sends its own patients, edited in
   two languages with a live preview beside each field.                     */

.msgcard__preview {
    margin-block-start: var(--space-2);
    padding: var(--space-3);
    border-radius: var(--radius-md);
    background: var(--bg-surface-3);
    border: 1px dashed var(--border-subtle);
}

.msgcard__preview-title { font-weight: var(--weight-bold); font-size: var(--text-sm); }
.msgcard__preview-body { font-size: var(--text-sm); color: var(--fg-muted); margin-block-start: 2px; }
