/* ── Brand Tokens ──────────────────────────────────────── */
:root {
    --color-primary: #018c91;
    --color-primary-dark: #026e73;
    --color-primary-contrast: #fff;
    --sidebar-width: 220px;
    --topbar-height: 56px;
    --font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --color-bg: #f4f7f8;
    --color-surface: #fff;
    --color-text: #1a1d1e;
    --color-text-muted: #6c757d;
    --color-border: #e4e8ea;
    /* Radius scale: controls default 10px, cards/dropdowns 16px, buttons/chips full pill. */
    --radius: 10px;
    --radius-sm: 7px;
    --radius-lg: 16px;
    --radius-pill: 999px;
    --shadow-sm: 0 1px 3px rgba(16,42,45,.05);
    --shadow: 0 4px 16px rgba(16,42,45,.07);
    --shadow-lg: 0 10px 32px rgba(16,42,45,.14);
    /* Translucent primary tints — used for hovers/selection so they layer on any surface. */
    --primary-tint: rgba(1,140,145,.08);
    --primary-tint-weak: rgba(1,140,145,.04);
    --primary-tint-strong: rgba(1,140,145,.14);
    --focus-ring: rgba(1,140,145,.18);
    /* Extended palette — every color in the app goes through these tokens so the
       dark theme below can restyle everything in one place. */
    --color-surface-alt: #f9fafb;
    --color-hover: #f3f4f6;
    --color-border-2: #e5e7eb;
    --color-border-strong: #c0c8cc;
    --color-text-faint: #9ca3af;
    --danger: #dc2626;
    --danger-fg: #b91c1c;
    --danger-bg: #fee2e2;
    --danger-border: #fca5a5;
    --success: #059669;
    --success-fg: #166534;
    --success-bg: #d1fae5;
    --success-border: #86efac;
    --warn-bg: #fef3c7;
    --warn-fg: #b45309;
    --warn-border: #fdba74;
    --accent-bg: #e6f7f7;
    --info-bg: #f0f4ff;
    --info-fg: #3b5bdb;
}

/* ── Dark Theme ────────────────────────────────────────── */
[data-theme="dark"] {
    color-scheme: dark;
    --color-primary: #2bb3b8;
    --color-primary-dark: #22999e;
    --color-bg: #101415;
    --color-surface: #191f21;
    --color-surface-alt: #1f2629;
    --color-hover: #242c2f;
    --color-text: #e7ebec;
    --color-text-muted: #9aa5a8;
    --color-text-faint: #79848a;
    --color-border: #2a3336;
    --color-border-2: #2f393d;
    --color-border-strong: #46545a;
    --shadow-sm: 0 1px 3px rgba(0,0,0,.35);
    --shadow: 0 4px 16px rgba(0,0,0,.45);
    --shadow-lg: 0 10px 32px rgba(0,0,0,.55);
    --primary-tint: rgba(43,179,184,.12);
    --primary-tint-weak: rgba(43,179,184,.06);
    --primary-tint-strong: rgba(43,179,184,.2);
    --focus-ring: rgba(43,179,184,.28);
    --danger: #f87171;
    --danger-fg: #fca5a5;
    --danger-bg: #3d1f1f;
    --danger-border: #7f1d1d;
    --success: #34d399;
    --success-fg: #86efac;
    --success-bg: #14352a;
    --success-border: #14532d;
    --warn-bg: #3a2c12;
    --warn-fg: #fbbf24;
    --warn-border: #92400e;
    --accent-bg: #123539;
    --info-bg: #1c2540;
    --info-fg: #93b0f5;
}

/* ── Reset ─────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

body {
    font-family: var(--font);
    background: var(--color-bg);
    color: var(--color-text);
    font-size: 14px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

a { color: var(--color-primary); text-decoration: none; }
a:hover { text-decoration: underline; }

h1 { font-size: 1.55rem; font-weight: 700; letter-spacing: -0.015em; }
h2 { font-size: 1.25rem; font-weight: 700; letter-spacing: -0.01em; }
/* FocusOnNavigate lands on the page h1; the UA focus rectangle is just noise there. */
h1:focus, .auth-title:focus { outline: none; }

/* ── App Layout ────────────────────────────────────────── */
.app-layout {
    display: grid;
    /* minmax(0, 1fr), not 1fr: a grid track's automatic minimum is its content's intrinsic width,
       so one unshrinkable child — the topbar's user block, a wide table — pushed the whole column
       past the viewport and gave every page a sideways scroll on a phone. Letting the track go
       below its content hands the overflow back to the children, which already have their own
       scroll contexts (.admin-card is overflow-x:auto under 900px). */
    grid-template-columns: var(--sidebar-width) minmax(0, 1fr);
    grid-template-rows: var(--topbar-height) 1fr;
    grid-template-areas:
        "sidebar topbar"
        "sidebar content";
    min-height: 100vh;
}

/* ── Collapsed sidebar (rail) ──────────────────────────── */
/* Icons only, with the label shown on hover. The pin is remembered per browser. */
.app-layout.rail { --sidebar-width: 60px; }
.app-layout.rail .nav-label,
.app-layout.rail .nav-section { display: none; }
/* The "Setup" flyout has its own room and shows on hover regardless of rail/docked state — the
   blanket .nav-label hiding above is only meant for the 60px icon strip itself, not the popup
   its items open into, so restore labels for anything inside it. */
.app-layout.rail .nav-submenu .nav-label { display: inline; }
/* At 60px there isn't room for the wordmark, but the brand must not vanish with it: the
   square "D" mark takes over, stacked above the pin so expanding again stays obvious. */
.app-layout.rail .sidebar-logo { display: none; }
.app-layout.rail .sidebar-mark { display: block; height: 26px; }
.app-layout.rail .sidebar-pin {
    position: static; transform: none; margin: 0 auto; width: 26px; height: 26px; font-size: 0.9rem;
}
.app-layout.rail .sidebar-header {
    flex-direction: column;
    justify-content: center;
    gap: 6px;
    height: auto;
    padding: 12px 8px;
}
[dir="rtl"] .app-layout.rail .sidebar-pin { transform: scaleX(-1); }
.app-layout.rail .nav-links li a { justify-content: center; gap: 0; padding: 10px 0; margin: 2px 8px; }
/* Collapsing the menu is a request for room to work in, so hand the reclaimed width to the
   page instead of leaving it as margin — otherwise the 1200px cap swallows the whole gain
   and docking the menu looks like it did nothing. */
.app-layout.rail .content-body { max-width: none; }
.app-layout.rail .nav-links { overflow: visible; }
.app-layout.rail .nav-links li { position: relative; }
.app-layout.rail .nav-links li a::after {
    content: attr(data-label);
    position: absolute; inset-inline-start: calc(100% + 8px); top: 50%; transform: translateY(-50%);
    background: var(--color-text); color: var(--color-surface);
    padding: 4px 10px; border-radius: var(--radius-sm); font-size: 0.8125rem; white-space: nowrap;
    opacity: 0; pointer-events: none; transition: opacity .12s; z-index: 50; box-shadow: var(--shadow);
}
.app-layout.rail .nav-links li a:hover::after,
.app-layout.rail .nav-links li a:focus-visible::after { opacity: 1; }

.sidebar-header { position: relative; }
.sidebar-pin {
    position: absolute; inset-inline-end: 6px; top: 50%; transform: translateY(-50%);
    background: rgba(255,255,255,.12); border: none; color: #fff; cursor: pointer;
    width: 20px; height: 20px; border-radius: 50%; font-size: 0.75rem; line-height: 1;
    display: flex; align-items: center; justify-content: center;
}
.sidebar-pin:hover { background: rgba(255,255,255,.28); }
/* The arrow points the way it will move, which flips with direction. */
[dir="rtl"] .sidebar-pin { transform: translateY(-50%) scaleX(-1); }

/* ── Sidebar ───────────────────────────────────────────── */
.sidebar {
    grid-area: sidebar;
    background: linear-gradient(180deg, var(--color-primary), var(--color-primary-dark));
    color: var(--color-primary-contrast);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    /* Must stay fully visible, not auto: an auto/scroll value on either axis forces the other
       axis to auto too (CSS overflow spec), which would clip the "Setup" flyout submenu — it
       pops out past the sidebar's right edge via position:absolute. Rail mode relied on this
       same fix before it was unconditional; nav item count is short enough that giving up
       independent sidebar scrolling here is a non-issue in practice. */
    overflow: visible;
    /* position:sticky makes the sidebar its own stacking context, so the "Setup" flyout's
       z-index only ranks it against other sidebar children — against the page it sat at the
       sidebar's own level (auto = 0), and any positioned element in the content area (a sticky
       table header, a search box) painted over the popup. Above the topbar (10), below drawers
       (40+), modals (50+) and toasts. */
    z-index: 20;
}

.sidebar-header {
    padding: 14px 16px;
    border-bottom: 1px solid rgba(255,255,255,.15);
    display: flex;
    align-items: center;
    height: var(--topbar-height);
}

.sidebar-logo {
    height: 32px;
    width: auto;
    max-width: 100%;
    object-fit: contain;
}

/* The compact mark exists only for the rail; the expanded sidebar shows the wordmark. */
.sidebar-mark { display: none; }

.nav-links {
    list-style: none;
    padding: 10px 0;
    flex: 1;
}

/* Nav items are inset rounded pills; margin-inline keeps RTL mirroring automatic. */
.nav-links li a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    margin: 2px 10px;
    color: rgba(255,255,255,.82);
    font-size: 0.875rem;
    transition: background .18s, color .18s;
    border-radius: var(--radius);
}

.nav-links li a:hover {
    background: rgba(255,255,255,.1);
    color: #fff;
    text-decoration: none;
}

.nav-links li a.active {
    background: rgba(255,255,255,.18);
    color: #fff;
    font-weight: 600;
    text-decoration: none;
    box-shadow: inset 0 0 0 1px rgba(255,255,255,.08);
}

.nav-section {
    padding: 14px 22px 4px;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: rgba(255,255,255,.55);
}

.nav-icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
    opacity: .85;
}

/* ── "Setup" hover flyout ──────────────────────────────── */
/* A single grouped item revealed on hover/focus instead of a permanent flat block — these are
   pages visited to configure the clinic, not day-to-day work. The trigger reuses the same look
   as a plain nav link; the flyout itself borrows the themed surface/shadow tokens (not the
   sidebar's own dark rgba tones) since it pops out over the light/dark content area, not the
   sidebar background. */
.nav-group { position: relative; }
.nav-group-toggle {
    display: flex;
    align-items: center;
    width: 100%;
    gap: 10px;
    padding: 9px 12px;
    margin: 2px 10px;
    color: rgba(255,255,255,.82);
    font: inherit;
    font-size: 0.875rem;
    text-align: start;
    background: none;
    border: none;
    border-radius: var(--radius);
    cursor: pointer;
    transition: background .18s, color .18s;
}
.nav-group-toggle:hover,
.nav-group-toggle:focus-visible,
.nav-group:focus-within .nav-group-toggle {
    background: rgba(255,255,255,.1);
    color: #fff;
}
.nav-caret {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    opacity: .7;
    margin-inline-start: auto;
    transition: transform .15s;
}
.nav-group.open .nav-caret { transform: rotate(90deg); }
.nav-submenu {
    display: none;
    list-style: none;
    margin: 0;
    padding: 6px;
    position: absolute;
    inset-inline-start: calc(100% + 6px);
    /* "Setup" is always the last item in the nav list (admin panels stack several sections
       above it), so anchoring from the bottom and growing upward keeps the menu on-screen —
       anchoring from the top instead cut off its lower items below the viewport. The max-height
       + scroll is a safety net for a short viewport even a bottom anchor can't fully avoid. */
    bottom: 0;
    max-height: calc(100vh - 16px);
    overflow-y: auto;
    min-width: 13rem;
    z-index: 40;
    background: var(--color-surface);
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}
.nav-group:hover .nav-submenu,
.nav-group:focus-within .nav-submenu,
.nav-group.open .nav-submenu {
    display: block;
}
.nav-submenu li a {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    margin: 0;
    border-radius: var(--radius-sm);
    color: var(--color-text);
    font-size: 0.875rem;
}
.nav-submenu li a:hover {
    background: var(--primary-tint-weak);
    /* Without this, the only matching :hover color rule left is the dark sidebar's own
       ".nav-links li a:hover" (this popup's <li><a> still lives inside .nav-links in the DOM) —
       white text on this light popup surface, invisible in light mode. */
    color: var(--color-text);
    text-decoration: none;
}
.nav-submenu li a.active {
    background: var(--primary-tint);
    color: var(--color-primary-dark);
    font-weight: 600;
}
.nav-submenu .nav-icon { opacity: .7; }
/* Collapsed rail: the flyout still opens (same absolute anchor), the toggle just loses its
   label/caret and centers its icon like every other rail link. */
.app-layout.rail .nav-group-toggle { justify-content: center; gap: 0; padding: 10px 0; margin: 2px 8px; }
.app-layout.rail .nav-caret { display: none; }

/* ── Top Bar ───────────────────────────────────────────── */
.topbar {
    grid-area: topbar;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    padding: 0 20px;
    box-shadow: var(--shadow-sm);
    position: sticky;
    top: 0;
    z-index: 10;
}

.topbar-user {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* Bare buttons in the topbar (theme toggle, sign out) render as ghost pills. */
.topbar-user button {
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    color: var(--color-text);
    font-size: 0.8125rem;
    cursor: pointer;
    transition: background .15s, border-color .15s;
}
.topbar-user button:hover { background: var(--color-hover); border-color: var(--color-border-strong); }

.topbar-username {
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* The topbar is one fixed-height row, so a two-word label that wraps ("Sign out", a two-part
   name) doesn't grow the bar — it just gets clipped mid-word. Nothing in here wraps; the search
   box gives up width instead, which it can afford and they can't. */
.topbar-user button, .topbar-username { white-space: nowrap; }

/* Icon + label, so the label alone can be dropped on a phone without the control disappearing. */
.topbar-signout { display: inline-flex; align-items: center; gap: 5px; }

/* ── Content ───────────────────────────────────────────── */
.content {
    grid-area: content;
    padding: 28px 32px;
    overflow-y: auto;
}

.content-body {
    max-width: 1200px;
}

.page-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 24px;
}

/* The text block (title + description) is what should give way to a long
   description wrapping onto more lines — the action button/link(s) on the
   right must never squeeze down to the point their own label wraps.
   :not(:first-child), because a header with no actions has the text block as its last child
   too, and pinning that at max-content width is the opposite of the intent: Messages' one-line
   description is 1018px of unwrappable text, which put that whole page into a sideways scroll
   at every width below a desktop. */
.page-header > *:last-child:not(:first-child) { flex-shrink: 0; }

.page-header h1 {
    color: var(--color-text);
}

.page-header p {
    color: var(--color-text-muted);
    margin-top: 4px;
}

/* ── Search ────────────────────────────────────────────── */
.search-wrapper {
    position: relative;
    width: 280px;
}

.search-input {
    width: 100%;
    padding: 8px 14px 8px 34px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    font-size: 0.875rem;
    background: var(--color-bg);
    color: var(--color-text);
    outline: none;
    transition: border-color .15s, box-shadow .15s, background .15s;
}

.search-input:focus {
    border-color: var(--color-primary);
    background: var(--color-surface);
    box-shadow: 0 0 0 3px var(--focus-ring);
}

.search-input::placeholder {
    color: var(--color-text-muted);
}

.search-icon {
    position: absolute;
    left: 9px;
    top: 50%;
    transform: translateY(-50%);
    width: 16px;
    height: 16px;
    color: var(--color-text-muted);
    pointer-events: none;
}

.search-dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    right: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    z-index: 100;
    /* The dropdown is left/right-anchored to the input, so on a tablet — where the topbar has
       squeezed the field to ~120px but is not yet narrow enough for the phone rules below — it
       inherited that width and shredded every result over five lines. A floor of its own lets it
       grow past the field to the right; the field can stay as narrow as the row demands. */
    min-width: min(320px, calc(100vw - 24px));
    /* Scrolls instead of growing without limit when a query matches a lot. */
    max-height: min(60vh, 30rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: 4px 0;
}

/* Category filter badges, pinned above the scrolling results. */
.search-filters {
    display: flex; flex-wrap: wrap; gap: 0.25rem;
    padding: 6px 10px 8px;
    border-bottom: 1px solid var(--color-border-2);
    position: sticky; top: 0; background: var(--color-surface); z-index: 1;
}
.search-filter {
    border: 1px solid var(--color-border); background: var(--color-surface);
    color: var(--color-text-muted); border-radius: var(--radius-pill);
    padding: 2px 10px; font-size: 0.75rem; font-weight: 500; cursor: pointer;
    transition: background .15s, color .15s, border-color .15s;
}
.search-filter:hover { border-color: var(--color-primary); color: var(--color-primary); }
.search-filter.active {
    background: var(--color-primary); border-color: var(--color-primary);
    color: var(--color-primary-contrast);
}
.search-more {
    display: block; width: 100%; text-align: center; cursor: pointer;
    background: none; border: none; border-top: 1px solid var(--color-border-2);
    padding: 8px; font-size: 0.8125rem; font-weight: 600; color: var(--color-primary);
}
.search-more:hover { background: var(--primary-tint-weak); }

.search-empty { padding: 12px; text-align: center; font-size: 0.82rem; color: var(--color-text-muted); }

/* Patient picker inside the appointment panel — same visual language as the top search
   dropdown, but a plain custom list rather than a native <datalist> (see AppointmentPanel.razor
   for why: a datalist re-populated on every keystroke closes its own popup in most browsers). */
/* A search-as-you-type dropdown. Named for its first use, but the rules are generic, so the
   combo-* aliases let another field reuse it without a second copy of the same CSS. A native
   <datalist> is not an option here: browsers close its popup whenever the options re-render,
   which is every keystroke when the list comes from the server. */
.patient-search-wrapper, .combo-wrapper { position: relative; }
.patient-search-dropdown, .combo-dropdown {
    position: absolute; top: calc(100% + 4px); left: 0; right: 0;
    background: var(--color-surface); border: 1px solid var(--color-border);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); z-index: 20;
    max-height: 14rem; overflow-y: auto; padding: 4px 0;
}
.patient-search-result, .combo-option {
    display: block; width: 100%; text-align: left; background: none; border: none;
    padding: 8px 12px; font-size: 0.875rem; color: var(--color-text); cursor: pointer;
}
.patient-search-result:hover, .combo-option:hover { background: var(--color-hover); }

/* A suggestion row in the medication picker: one medication, name only. Registered names run to
   a hundred characters, so the row gives all its width to the name and ellipsises the remainder —
   the title attribute carries the full text on hover. Packs, makers and prices are in the
   "more info" table, where a pack size gives a price meaning. */
.drug-option-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ── Multi-select: a summary line that opens a checkbox panel ──────────────
   For a list too long to sit open in a narrow panel. Closed it costs one row and says what is
   chosen; open it is an ordinary combo dropdown. */
.multiselect-toggle {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    text-align: start; cursor: pointer;
}
.multiselect-summary { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.multiselect-caret { font-size: 0.7rem; opacity: .6; flex: none; }
.multiselect-panel { padding: 6px; }
/* Catches a click anywhere outside so the panel closes. Under .combo-dropdown's z-index 20, and
   invisible — it exists only to receive the click. */
.dropdown-backdrop { position: fixed; inset: 0; z-index: 19; }
/* Sits on the field's own label, so it is only reachable once a name is in the box. */
.drug-more-info { font-size: 0.72rem; font-weight: 400; margin-inline-start: 0.4rem; }
.drug-detail-dialog { width: min(680px, 94vw); }

.search-result {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 12px;
    font-size: 0.875rem;
    color: var(--color-text);
    transition: background .1s;
    cursor: pointer;
}

.search-result:hover {
    background: var(--color-hover);
    text-decoration: none;
}

.search-result-icon {
    width: 14px;
    height: 14px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}

/* ── Dashboard ─────────────────────────────────────────── */
.dashboard-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 300px;
    text-align: center;
    color: var(--color-text-muted);
}

.dashboard-empty svg {
    width: 72px;
    height: 72px;
    margin-bottom: 18px;
    padding: 16px;
    color: var(--color-primary);
    background: var(--accent-bg);
    border-radius: 50%;
    opacity: .9;
}

.dashboard-empty h2 {
    font-size: 1.1rem;
    font-weight: 500;
    color: var(--color-text);
    margin-bottom: 6px;
}

/* ── Blazor Built-ins ──────────────────────────────────── */
#blazor-error-ui {
    background: #b32121;
    color: white;
    padding: 10px 16px;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 9999;
    font-size: 0.875rem;
    display: none;
}

#blazor-error-ui a { color: #fff; text-decoration: underline; }

.valid.modified:not([type=checkbox]) { outline: 1px solid var(--success); }
.invalid { outline: 1px solid var(--danger); }
.validation-message { color: var(--danger); font-size: 0.8125rem; margin-top: 2px; }

/* ── Utility ───────────────────────────────────────────── */
.text-muted   { color: var(--color-text-muted); }
.text-success { color: var(--success-fg); }
.text-danger  { color: var(--danger); }
.btn-primary {
    background: var(--color-primary);
    color: var(--color-primary-contrast);
    border: none;
    padding: 8px 18px;
    border-radius: var(--radius-pill);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background .18s, box-shadow .18s, transform .18s;
}
.btn-primary:hover:not(:disabled) {
    background: var(--color-primary-dark);
    box-shadow: var(--shadow);
    transform: translateY(-1px);
}
.btn-primary:active:not(:disabled) { transform: translateY(0); box-shadow: none; }
.btn-primary:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }
.btn-primary.btn-sm { padding: 5px 12px; font-size: 0.8rem; }
.btn-primary:disabled { opacity: .5; cursor: default; }

/* ── Patient Grid ──────────────────────────────────────── */
.patients-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
}

.search-wrapper.inline { position: relative; }
.search-wrapper.inline .search-input { padding-left: 32px; width: 260px; }
.search-wrapper.inline .search-icon {
    position: absolute;
    left: 8px; top: 50%;
    transform: translateY(-50%);
    width: 16px; height: 16px;
    color: var(--color-text-muted);
    pointer-events: none;
}

.btn-columns {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    padding: 6px 14px;
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text);
    cursor: pointer;
    transition: border-color .15s, color .15s;
}
.btn-columns:hover { border-color: var(--color-primary); color: var(--color-primary); }

.column-toggle-wrap { position: relative; }

.backdrop {
    position: fixed;
    inset: 0;
    z-index: 10;
    background: transparent;
}

.column-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 6px 0;
    min-width: 160px;
    max-height: 55vh;
    overflow-y: auto;
    z-index: 11;
    box-shadow: var(--shadow-lg);
}

.column-menu-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 14px;
    font-size: 0.875rem;
    cursor: pointer;
    user-select: none;
}
.column-menu-item:hover { background: var(--color-bg); }

/* ── Localized date picker ─────────────────────────────── */
.ldp-wrap { position: relative; display: inline-block; width: 100%; }
.ldp-input-group { position: relative; }
/* .ldp-input-group input.ldp-input, not .ldp-input: both ".field-input" and the more specific
   ".field input" (line ~923) also set this element's padding — same "class + element type"
   specificity as a bare "input.ldp-input", so whichever of those was declared LAST in this file
   silently won and collapsed this back to 12px on both sides. That stayed invisible wherever
   AllowClear was off (nothing on the right to collide with) or no icon existed yet (nothing on
   the left) — exactly why it went unnoticed until the calendar icon added a second element
   competing for the space. Scoping to the wrapper class makes this strictly more specific than
   either competitor, so it wins regardless of source order. */
.ldp-input-group input.ldp-input { cursor: pointer; background: var(--color-surface); padding-left: 30px; padding-right: 26px; }
.ldp-input[disabled] { cursor: not-allowed; }
.ldp-icon {
    position: absolute;
    left: 9px;
    top: 50%;
    transform: translateY(-50%);
    width: 15px;
    height: 15px;
    color: var(--color-text-muted);
    pointer-events: none;
}
.ldp-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    border: none;
    background: none;
    color: var(--color-text-muted);
    cursor: pointer;
    font-size: 0.72rem;
    line-height: 1;
    padding: 4px;
}
.ldp-clear:hover { color: var(--color-text); }
.ldp-popup {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 8px;
    z-index: 11;
    box-shadow: var(--shadow-lg);
    width: 17rem;
}
.ldp-popup-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
    font-size: 0.8125rem;
    font-weight: 500;
    margin-bottom: 6px;
}
.ldp-mini-select {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 0.8125rem;
    font-weight: 500;
    padding: 2px 4px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-surface);
    color: var(--color-text);
}
.ldp-popup-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.ldp-dow { text-align: center; font-size: 0.7rem; color: var(--color-text-muted); padding: 2px 0; }
.ldp-day {
    text-align: center;
    font-size: 0.8125rem;
    padding: 6px 0;
    border-radius: var(--radius);
    cursor: pointer;
}
.ldp-day:hover { background: var(--color-bg); }
.ldp-day.outside { color: var(--color-text-muted); opacity: .5; }
.ldp-day.today { font-weight: 700; }
.ldp-day.selected { background: var(--color-primary); color: #fff; }

/* Was flex:1 1 auto — on a half-width field the date box stretched to fill the whole row while
   showing an 8-character date, leaving a wide dead gap next to the compact time box. Capping it
   keeps both fields close to their actual content width, so the pair reads as one balanced
   control instead of one oversized box next to a small one. */
/* The two halves of one control, so they have to read as one control. A native <input type="time">
   carries intrinsic height from its browser-drawn clock and inner spinner fields — it rendered
   ~4px taller than the plain text date input beside it, which made the time half look like the
   bigger, more important field. Stretching both to the row's height keeps them identical without
   pinning a magic pixel value that a font or padding change would silently break. */
/* Date and time side by side while there is room, stacked when there is not.
   The time half used to be flex: 0 0 7rem — no grow, NO SHRINK — so every pixel of a squeeze
   landed on the date, which had min-width: 0 and collapsed. In the appointment panel
   (width: min(420px, 92vw), two of these to a row) that left the date around 50px against 56px
   of its own padding for the calendar icon and clear button: an input showing no date at all.
   Both halves now carry a real minimum and the row wraps rather than crushing either. */
.ldtp-wrap { display: flex; flex-wrap: wrap; gap: 6px; align-items: stretch; }
/* min(…, 100%) rather than a flat minimum: on a phone the panel is 92vw and a half-width column
   can be narrower than 9rem, where a hard min-width would push the input out of its own field
   instead of wrapping. Bounded to the container, the halves stack and each takes the full width. */
.ldtp-wrap .ldp-wrap { flex: 1 1 9rem; min-width: min(9rem, 100%); }
.ldtp-wrap .ldp-input-group { height: 100%; }
.ldtp-wrap input.ldp-input { height: 100%; }
/* Grows to share a line, capped so a lone time field on a wide form is not absurdly long.
   The floor was 6rem, which is 96px, and box-sizing is border-box — so after 24px of horizontal
   padding and 2px of border the clock had ~70px to draw in. That is enough in Safari/WKWebView,
   which renders a bare "HH:MM", and NOT enough anywhere else: Chrome and Firefox add a
   calendar-picker indicator, and in a 12-hour regional format (en-US, the default) a third
   "AM/PM" segment as well. The value clipped or the control overflowed its field, on the New
   Visit page and both halves of the appointment panel. 9.5rem leaves ~126px, which fits
   "12:30 PM" plus the indicator with room to spare. */
.ldtp-time { flex: 1 1 9.5rem; min-width: min(9.5rem, 100%); max-width: 12rem; }
/* Centres the time text against the taller box so it doesn't sit high next to the date. */
.ldtp-time::-webkit-datetime-edit { display: flex; align-items: center; }
/* The indicator is a fixed-size button; letting it shrink is what keeps the segments legible
   when the field is at its narrowest rather than the icon pushing them out of view. */
.ldtp-time::-webkit-calendar-picker-indicator { flex: 0 0 auto; margin-inline-start: 2px; }

.grid-wrap {
    overflow-x: auto;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
    transition: opacity .15s;
}
.grid-loading-overlay { opacity: .5; pointer-events: none; }

.patient-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

.patient-table thead th {
    background: var(--color-surface-alt);
    color: var(--color-text-muted);
    font-weight: 600;
    font-size: 0.8125rem;
    padding: 11px 14px;
    text-align: start;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.patient-table th.sortable { cursor: pointer; user-select: none; }
.patient-table th.sortable:hover { color: var(--color-primary); }
.patient-table th.sorted { color: var(--color-primary); }
.sort-arrow { margin-left: 4px; font-size: 0.7rem; }

.patient-table tbody td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text);
    vertical-align: middle;
}
.patient-table tbody tr:last-child td { border-bottom: none; }
.patient-table tbody tr:hover td { background: var(--primary-tint-weak); }

.grid-loading {
    padding: 48px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 0.875rem;
}
.grid-empty {
    padding: 48px;
    text-align: center;
    color: var(--color-text-muted);
}

.pagination {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-top: 16px;
}

.page-btn {
    min-width: 32px;
    height: 32px;
    padding: 0 8px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 0.875rem;
    cursor: pointer;
    transition: border-color .15s, background .15s;
}
.page-btn:hover:not(:disabled) {
    border-color: var(--color-primary);
    color: var(--color-primary);
}
.page-btn.active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-primary-contrast);
    font-weight: 600;
}
.page-btn:disabled { opacity: .4; cursor: default; }
.page-ellipsis { color: var(--color-text-muted); padding: 0 4px; }

.pagination-info {
    margin-top: 8px;
    font-size: 0.8125rem;
    color: var(--color-text-muted);
}

/* ── Auth / Login ──────────────────────────────────────── */
.auth-layout {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    background: var(--color-bg);
}

.auth-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 44px 40px;
    width: 100%;
    max-width: 400px;
}

/* Registration: too many fields for the narrow login-sized card — a wide card plus the
   .form-grid two-column layout keeps it readable at half the scroll length. */
.auth-card-wide { max-width: 880px; }

.auth-title {
    font-size: 1.6rem;
    font-weight: 700;
    color: var(--color-primary);
    margin-bottom: 4px;
}

.auth-subtitle {
    color: var(--color-text-muted);
    font-size: 0.875rem;
    margin-bottom: 24px;
}

.field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 16px;
}

.field label {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text);
}

.field input {
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    font-size: 0.875rem;
    outline: none;
    transition: border-color .15s, box-shadow .15s;
    background: var(--color-surface);
    color: var(--color-text);
    width: 100%;
}

.field input:focus {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--focus-ring);
}

/* Checkboxes and radios must not inherit the text-input box above — `width: 100%` plus
   8px/12px padding stretched them across their whole grid cell and shoved the label away.
   The attribute selector outranks `.field input`, so source order doesn't matter. */
.field input[type="checkbox"],
.field input[type="radio"] {
    width: auto;
    padding: 0;
    margin: 0;
    inline-size: 15px;
    block-size: 15px;
    flex: 0 0 auto;
    border-radius: 3px;
    accent-color: var(--color-primary);
}
.field input[type="checkbox"]:focus,
.field input[type="radio"]:focus { box-shadow: 0 0 0 3px var(--focus-ring); }

.field-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 20px;
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

.field-row label {
    display: flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
}

/* "Remember me" left, "Forgot password?" right. */
.auth-row-split { justify-content: space-between; }
.auth-row-split .btn-link { font-size: 0.8125rem; }

.auth-card .btn-primary {
    width: 100%;
    padding: 10px;
    font-size: 0.9375rem;
    font-weight: 500;
}

.error-message {
    color: var(--danger-fg);
    font-size: 0.8125rem;
    margin-bottom: 14px;
    padding: 9px 14px;
    background: var(--danger-bg);
    border-radius: var(--radius);
    border: 1px solid var(--danger-border);
}

/* ── Buttons (shared) ──────────────────────────────────── */
.btn-secondary {
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border);
    padding: 8px 18px;
    border-radius: var(--radius-pill);
    font-size: 0.875rem;
    font-weight: 500;
    cursor: pointer;
    transition: background .18s, border-color .18s, box-shadow .18s;
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.btn-secondary:hover { background: var(--color-hover); border-color: var(--color-border-strong); text-decoration: none; }
.btn-secondary:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }

.btn-outline {
    background: transparent;
    color: var(--color-text-muted);
    border: 1px solid var(--color-border);
    padding: 6px 14px;
    border-radius: var(--radius-pill);
    font-size: 0.8125rem;
    font-weight: 500;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    transition: background .18s, border-color .18s, color .18s;
}
.btn-outline:hover, .btn-outline.active {
    background: var(--primary-tint-weak);
    border-color: var(--color-primary);
    color: var(--color-primary);
}
.btn-outline:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--focus-ring); }

.btn-sm { padding: 5px 14px; font-size: 0.8125rem; }

.btn-link {
    background: none;
    border: none;
    padding: 0;
    color: var(--color-primary);
    font-size: 0.8125rem;
    cursor: pointer;
    text-decoration: underline;
}
.btn-link:hover { color: var(--color-primary-dark); }

.btn-icon-xs {
    background: none;
    border: 1px solid var(--color-border);
    border-radius: 4px;
    width: 22px;
    height: 22px;
    padding: 0;
    font-size: 0.75rem;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--color-text-muted);
    transition: background .12s;
}
.btn-icon-xs:hover:not(:disabled) { background: var(--color-bg); color: var(--color-text); }
.btn-icon-xs:disabled { opacity: .35; cursor: default; }

/* ── Patient Form ──────────────────────────────────────── */
.patient-form-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 28px;
    max-width: 1100px;
}

.patient-form-toolbar {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 12px;
    margin-bottom: 20px;
}

.field-label {
    font-size: 0.8125rem;
    font-weight: 500;
    color: var(--color-text);
    margin-bottom: 4px;
    display: block;
}

.field-input {
    padding: 8px 12px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    font-size: 0.875rem;
    outline: none;
    transition: border-color .15s, box-shadow .15s;
    background: var(--color-surface);
    color: var(--color-text);
    width: 100%;
    font-family: inherit;
}
.field-input:focus {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px var(--focus-ring);
}
.field-input[disabled] { background: var(--color-bg); opacity: .6; cursor: not-allowed; }
textarea.field-input { resize: vertical; min-height: 80px; }
select.field-input { appearance: auto; cursor: pointer; }

.form-grid {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: 16px 28px;
    margin-bottom: 24px;
}

@media (max-width: 600px) {
    .form-grid { grid-template-columns: 1fr; }
    .form-grid > * { grid-column: 1 / -1 !important; }
}

/* The grid's row-gap already spaces rows; .field's own margin double-counted it. */
.form-grid > .field { margin-bottom: 0; }

.field-w-full    { grid-column: span 12; }
.field-w-half    { grid-column: span 6; }
.field-w-third   { grid-column: span 4; }
.field-w-quarter { grid-column: span 3; }
/* Checkbox/yes-no fields are narrow: four to a row rather than two half-empty ones. */
.field-w-check   { grid-column: span 3; }
@media (max-width: 1100px) {
    .field-w-check { grid-column: span 4; }
}
/* kept for any legacy usage */
.field-full { grid-column: span 12; }

/* A checkbox row is a single line, so it aligns to the height of a text input beside it. */
.field.field-check { justify-content: center; min-height: 34px; }
.field.field-check .toggle-label { color: var(--color-text); white-space: normal; }

.form-actions {
    display: flex;
    /* Action rows grow: Booking's is Save / Preview page / Copy link / QR code, which is well
       past a phone's width on one line, and with nowrap the last button simply hung off the
       edge. Buttons keep their own width and the row takes another line instead. */
    flex-wrap: wrap;
    gap: 12px;
    padding-top: 4px;
}

/* ── Field Config Panel ────────────────────────────────── */
.field-config-panel {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 16px;
    margin-bottom: 20px;
}

.field-config-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 12px;
    font-size: 0.875rem;
}

.field-config-list { display: flex; flex-direction: column; gap: 6px; }

.field-config-item {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 8px 12px;
    font-size: 0.8125rem;
}

.field-config-reorder { display: flex; gap: 3px; }

.field-config-name {
    flex: 1;
    font-weight: 500;
    color: var(--color-text);
}

.toggle-label {
    display: flex;
    align-items: center;
    gap: 5px;
    cursor: pointer;
    color: var(--color-text-muted);
    white-space: nowrap;
}

/* ── Admin Pages ───────────────────────────────────────── */

/* Toolbar */
.admin-toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 16px;
}

/* Card */
.admin-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: 22px 26px;
    box-shadow: var(--shadow-sm);
}

.admin-card-title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--color-text);
    margin-bottom: 16px;
    padding-bottom: 10px;
    border-bottom: 1px solid var(--color-border);
}

/* Table */
.admin-table-wrap {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
}

.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}

.admin-table thead th {
    background: var(--color-surface-alt);
    color: var(--color-text-muted);
    font-weight: 600;
    font-size: 0.8125rem;
    padding: 11px 16px;
    text-align: start;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.admin-table tbody td {
    padding: 12px 16px;
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
}

.admin-table tbody tr:last-child td { border-bottom: none; }
.admin-table tbody tr:hover td { background: var(--primary-tint-weak); }
.admin-table tbody tr.row-expanded td { background: var(--primary-tint-weak); }
.admin-table tbody tr.row-selected td { background: var(--primary-tint); }

/* Detail panel (expanded row) */
.detail-row > td { padding: 0 !important; }

.detail-panel {
    background: var(--color-bg);
    border-top: 2px solid var(--color-primary);
    padding: 20px 24px;
}

.detail-panel-title {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--color-primary);
    margin-bottom: 14px;
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* User cell */
.user-cell {
    display: flex;
    align-items: center;
    gap: 10px;
}

.user-avatar {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: var(--color-primary);
    color: #fff;
    font-size: 0.8125rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    letter-spacing: .02em;
}

.user-info {
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.user-name {
    font-weight: 500;
    color: var(--color-text);
}

.user-email {
    font-size: 0.8rem;
    color: var(--color-text-muted);
}

/* Badges */
.badge {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 600;
    padding: 3px 10px;
    border-radius: var(--radius-pill);
    white-space: nowrap;
}

.badge-active   { background: var(--success-bg); color: var(--success-fg); }
.badge-inactive { background: var(--danger-bg); color: var(--danger-fg); }
.badge-role     { background: var(--primary-tint); color: var(--color-primary-dark); margin-right: 4px; }
.badge-system   { background: var(--info-bg); color: var(--info-fg); }
.badge-org-name { font-size: 0.75rem; color: var(--color-text-muted); }
/* Pinned to the far side of its toolbar via auto-margin rather than a container-level
   justify-content, so the layout doesn't jump when the badge is absent (a brand-new
   patient has no MRN yet — it's assigned by the server on first save). */
.badge-mrn {
    margin-inline-end: auto;
    background: var(--warn-bg);
    color: var(--warn-fg);
    font-size: 0.8125rem;
    font-weight: 700;
    letter-spacing: .02em;
    padding: 4px 12px;
}

/* Row actions */
.row-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex-wrap: wrap;
}

.action-btn {
    background: none;
    border: none;
    padding: 3px 2px;
    font-size: 0.8125rem;
    color: var(--color-primary);
    cursor: pointer;
    transition: color .12s;
}

.action-btn:hover { color: var(--color-primary-dark); text-decoration: underline; }
.action-btn.active { font-weight: 600; color: var(--color-primary-dark); }
.action-btn.danger, .action-btn-danger { color: var(--danger); }
.action-btn.danger:hover, .action-btn-danger:hover { color: var(--danger-fg); }

.action-sep { color: var(--color-border); font-size: 0.75rem; user-select: none; }

.col-actions { width: 130px; white-space: nowrap; }
.col-select { width: 34px; text-align: center; }

/* ── Confirm dialog ────────────────────────────────────── */
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.35); z-index: 50; }
.modal-dialog {
    position: fixed;
    top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    z-index: 51;
    width: min(440px, 92vw);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 1.25rem 1.4rem 1.1rem;
}
.modal-title { margin: 0 0 0.6rem; font-size: 1.05rem; font-weight: 600; }
.modal-text  { margin: 0 0 0.7rem; font-size: 0.875rem; line-height: 1.45; }
.modal-note  { margin: 0.7rem 0 0; font-size: 0.8rem; color: var(--color-text-muted); }
.modal-actions { display: flex; justify-content: flex-end; gap: 0.5rem; margin-top: 1.1rem; }

.delete-impact {
    margin: 0;
    padding: 0.6rem 0.9rem;
    list-style: none;
    background: var(--danger-bg);
    border: 1px solid var(--danger-border);
    border-radius: var(--radius);
    color: var(--danger-fg);
    font-size: 0.85rem;
}
.delete-impact li { padding: 1px 0; }

.alert-error {
    background: var(--danger-bg);
    border: 1px solid var(--danger-border);
    color: var(--danger-fg);
    border-radius: var(--radius);
    padding: 10px 14px;
    margin-bottom: 12px;
    font-size: 0.875rem;
}

.alert-success {
    background: var(--success-bg);
    border: 1px solid var(--success-border);
    color: var(--success-fg);
    border-radius: var(--radius);
    padding: 10px 14px;
    margin-bottom: 12px;
    font-size: 0.875rem;
}

/* "Heads up" rather than "you broke something" — e.g. editing a template that live automated
   sends draw from. Amber like every other --warn token in the app. */
.alert-warn {
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    color: var(--warn-fg);
    border-radius: var(--radius);
    padding: 10px 14px;
    margin-bottom: 12px;
    font-size: 0.875rem;
}

.tools-section { display: flex; flex-direction: column; gap: 16px; max-width: 640px; }

.tool-card {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--color-surface);
    box-shadow: var(--shadow-sm);
}

.tool-card-body { padding: 20px 24px; }

.tool-card-body h3 { margin: 0 0 6px; font-size: 1rem; font-weight: 600; }

.tool-card-body p { margin: 0 0 14px; }

/* Confirm inline */
.confirm-inline {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--warn-bg);
    border: 1px solid var(--warn-border);
    border-radius: var(--radius-pill);
    padding: 4px 12px;
    font-size: 0.8125rem;
}

.confirm-inline span { color: var(--warn-fg); }

/* Danger button */
.btn-danger {
    background: var(--danger);
    color: #fff;
    border: none;
    padding: 8px 16px;
    border-radius: var(--radius-pill);
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    transition: background .18s, box-shadow .18s;
}

.btn-danger:hover { background: var(--danger-fg); box-shadow: var(--shadow); }
.btn-danger.btn-sm { padding: 5px 12px; font-size: 0.8rem; }

/* Toggle switch inline */
.toggle-switch {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    font-size: 0.875rem;
    color: var(--color-text);
    padding-top: 8px;
}

.toggle-switch input { accent-color: var(--color-primary); width: 16px; height: 16px; cursor: pointer; }

/* Loading / empty */
.admin-loading {
    padding: 48px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 0.875rem;
}

.admin-empty {
    padding: 64px 24px;
    text-align: center;
    color: var(--color-text-muted);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.admin-empty svg {
    width: 56px;
    height: 56px;
    padding: 13px;
    color: var(--color-primary);
    background: var(--accent-bg);
    border-radius: 50%;
    margin-bottom: 12px;
    display: block;
    margin-inline: auto;
}

.admin-empty p { font-size: 0.875rem; }

/* Toast */
.toast {
    position: fixed;
    bottom: 20px;
    right: 20px;
    background: var(--color-text);
    color: var(--color-bg);
    padding: 11px 20px;
    border-radius: var(--radius-lg);
    font-size: 0.875rem;
    font-weight: 500;
    box-shadow: var(--shadow-lg);
    z-index: 1000;
}

.toast.toast-error { background: var(--danger); }

/* Pagination */
.admin-pagination {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 14px;
}

/* Fallback data-table (used by orgs/groups pages) */
.data-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
    background: var(--color-surface);
}

.data-table thead th {
    background: var(--color-surface-alt);
    color: var(--color-text-muted);
    font-weight: 600;
    font-size: 0.8125rem;
    padding: 11px 14px;
    text-align: start;
    border-bottom: 1px solid var(--color-border);
    white-space: nowrap;
}

.data-table tbody td {
    padding: 10px 14px;
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
}

.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover td { background: var(--primary-tint-weak); }

/* Wide tables (the configurable visits grid) scroll inside their card rather than
   pushing the page sideways. */
.table-scroll { overflow-x: auto; }
.row-clickable { cursor: pointer; }
/* Free-text clinical columns get room but stay one line — the full value is in the tooltip. */
.data-table th.col-wide { min-width: 12rem; }
.data-table td.cell-clip { max-width: 20rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

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

/* Inline form (orgs/groups pages) */
.inline-form {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    margin-top: 8px;
    padding: 10px 12px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
}

.inline-form select,
.inline-form input {
    padding: 5px 9px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    font-size: 0.8125rem;
    background: var(--color-surface);
    color: var(--color-text);
    outline: none;
    transition: border-color .15s;
}

.inline-form select:focus,
.inline-form input:focus {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 2px var(--focus-ring);
}

.toolbar {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 16px;
}

.share-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.share-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    padding: 6px 10px;
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    font-size: 0.85rem;
}
.share-row > span:first-child { font-weight: 600; min-width: 0; }
.share-row .btn-link { margin-inline-start: auto; }

.sub-form {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 14px 16px;
    margin: 6px 0;
}

.claims-panel {
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
    padding: 12px 16px;
}

/* The Profile page's tab strip had no rule of its own at all — six inline-block buttons in a
   bare div, which on a phone reflowed into a ragged two- or three-line block with no baseline
   for the active-tab underline to sit on. Same treatment as .hub-tabs: one scrolling row. */
.tab-bar {
    display: flex;
    flex-wrap: nowrap;
    border-bottom: 1px solid var(--color-border-2);
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.tab-bar::-webkit-scrollbar { display: none; }

.tab-btn {
    background: none;
    border: none;
    padding: 6px 14px;
    font-size: 0.875rem;
    color: var(--color-text-muted);
    cursor: pointer;
    border-bottom: 2px solid transparent;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0;
    margin-right: 4px;
    transition: color .15s, border-color .15s, background .15s;
    flex: 0 0 auto;
    white-space: nowrap;
}
@media (max-width: 600px) {
    .tab-btn { padding: 6px 10px; font-size: 0.82rem; }
}

.tab-btn:hover { color: var(--color-text); background: var(--primary-tint-weak); }
.tab-btn.tab-active {
    color: var(--color-primary);
    border-bottom-color: var(--color-primary);
    font-weight: 500;
}

.claim-chip {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: var(--primary-tint);
    color: var(--color-primary-dark);
    border-radius: var(--radius-pill);
    padding: 2px 10px;
    font-size: 0.775rem;
    font-weight: 500;
    margin: 2px;
}

.claim-chip button {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--color-primary-dark);
    font-size: 0.75rem;
    padding: 0 2px;
    line-height: 1;
    opacity: .7;
}

.claim-chip button:hover { opacity: 1; }

.loading-spinner {
    padding: 32px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 0.875rem;
}

/* ── Filter select (admin toolbars) ──────────────────── */
.filter-select {
    height: 34px;
    padding: 0 28px 0 14px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-pill);
    background: var(--color-surface);
    color: var(--color-text);
    font-size: 0.8125rem;
    cursor: pointer;
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 20 20' fill='%23999'%3E%3Cpath fill-rule='evenodd' d='M5.23 7.21a.75.75 0 011.06.02L10 11.17l3.71-3.94a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 8px center;
    max-width: 200px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.filter-select:focus {
    outline: none;
    border-color: var(--color-primary);
}

/* ── Checkbox column ──────────────────────────────────── */
.col-check {
    width: 36px;
    padding-left: 12px !important;
    padding-right: 4px !important;
}

.col-check input[type="checkbox"] {
    accent-color: var(--color-primary);
    width: 15px;
    height: 15px;
    cursor: pointer;
    vertical-align: middle;
}

/* ── Bulk action bar ──────────────────────────────────── */
.bulk-action-bar {
    position: sticky;
    bottom: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 10px 18px;
    background: var(--color-surface);
    border: 1px solid var(--color-primary);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    margin-top: 12px;
    font-size: 0.875rem;
}

.bulk-action-bar .bulk-count {
    font-weight: 600;
    color: var(--color-primary-dark);
    white-space: nowrap;
}

.bulk-action-bar .bulk-divider {
    width: 1px;
    height: 22px;
    background: var(--color-border);
    flex-shrink: 0;
}

.bulk-action-bar label {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    white-space: nowrap;
}

.bulk-action-bar .filter-select {
    max-width: 180px;
}

/* ── Patient count badge (Users page) ─────────────────── */
.patient-count-badge {
    display: inline-block;
    min-width: 28px;
    text-align: center;
    background: var(--primary-tint);
    color: var(--color-primary-dark);
    border-radius: var(--radius-pill);
    padding: 2px 8px;
    font-size: 0.8125rem;
    font-weight: 600;
}

/* Patient avatar variant (muted teal) */
.user-avatar.patient-avatar {
    background: #5ba6a9;
}

/* Access table nested inside detail panel */
.access-table {
    background: var(--color-surface);
    border-radius: var(--radius);
    overflow: hidden;
    border: 1px solid var(--color-border);
}

.access-table thead th {
    background: var(--accent-bg);
    font-size: 0.7rem;
}

.access-table tbody td {
    padding: 9px 12px;
    font-size: 0.8125rem;
}

/* ─── Clinical (visits, sections, charge lines) ─────────────────────────── */
.section-group { margin-bottom: 1rem; }
.section-group-title {
    font-size: 0.78rem; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.05em; color: var(--color-text-muted); margin: 0.75rem 0 0.5rem;
    border-bottom: 1px solid var(--color-border-2); padding-bottom: 0.25rem;
}
/* Yes / No / Not asked — a segmented control built on real radios so keyboard and
   screen-reader behaviour comes for free. Unanswered is visually distinct from "No". */
.yesno-group {
    display: flex; align-items: center; gap: 0.3rem;
    border: 1px solid var(--color-border); border-radius: var(--radius-pill);
    padding: 2px; width: fit-content; background: var(--color-surface);
}
.yesno-group.unanswered { border-style: dashed; border-color: var(--warn-border); }
.yesno-option {
    display: flex; align-items: center; gap: 4px; cursor: pointer;
    padding: 2px 10px; border-radius: var(--radius-pill); font-size: 0.8125rem;
    color: var(--color-text-muted); transition: background .15s, color .15s;
}
.yesno-option:hover { background: var(--color-hover); }
.yesno-option input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.yesno-option.selected { font-weight: 600; }
.yesno-option.selected.yes { background: var(--danger-bg); color: var(--danger-fg); }
.yesno-option.selected.no  { background: var(--success-bg); color: var(--success-fg); }
.yesno-option:focus-within { box-shadow: 0 0 0 3px var(--focus-ring); }
.yesno-hint { font-size: 0.72rem; color: var(--warn-fg); padding-right: 0.4rem; }
.yesno-clear { font-size: 0.72rem; padding-right: 0.4rem; }

.field-readonly {
    padding: 0.45rem 0.6rem; background: var(--color-surface-alt); border: 1px solid var(--color-border-2);
    border-radius: 6px; min-height: 2.1rem; white-space: pre-wrap; font-size: 0.9rem;
}
.panel-header-row {
    display: flex; justify-content: space-between; align-items: center;
    margin-bottom: 0.75rem; gap: 0.5rem;
}
.patient-alert-badge {
    display: inline-flex; align-items: center; justify-content: center;
    width: 17px; height: 17px; border-radius: 50%; font-size: 0.7rem; font-weight: 700;
    margin-inline-end: 5px; cursor: help; vertical-align: -2px;
}
.patient-alert-badge.has-alert  { background: var(--danger-bg); color: var(--danger-fg); border: 1px solid var(--danger-border); }
.patient-alert-badge.unanswered { background: var(--warn-bg);   color: var(--warn-fg);   border: 1px dashed var(--warn-border); }

/* Clinical alerts for the visit — deliberately loud, and above the fold. */
/* Who this is, kept on screen for the whole visit. Scrolling into the fourth section and no
   longer being able to see whose chart you are in is how notes end up on the wrong patient. */
.visit-header-sticky {
    position: sticky;
    top: var(--topbar-height);            /* clears the topbar, which is sticky at 0 */
    z-index: 9;                           /* under the topbar (10) and the nav panel (30) */
    background: var(--color-bg);
    padding-block: 0.5rem;
    margin-block-start: -0.5rem;
    border-bottom: 1px solid var(--color-border);
}
/* .content carries overflow-y:auto, which makes it a scroll container — and since .app-layout is
   min-height (not height) it grows with its content and never actually scrolls, so the document
   scrolls instead. A sticky child sticks to its nearest scroll container, so the header was
   pinned to something that never moves and scrolled away regardless. Opting this one page out
   hands it back to the document. Scoped with :has() rather than dropping the overflow globally,
   the same way .content-body:has(.visit-nav) already reserves the navigator's column.

   Only above the width where the header is actually sticky. Setting one axis to visible forces
   the other to compute visible too, so below 700px — where the header is static anyway — this
   would buy nothing and cost .content its horizontal containment, pushing the visit's wide form
   rows out into a page-wide sideways scroll. */
@media (min-width: 701px) {
    .content:has(.visit-header-sticky) { overflow-y: visible; }
}
/* The mark beside the name: for when the strip below has been scanned past. */
.visit-header-warnicon {
    font-size: 0.85em; color: var(--warn-fg); margin-inline-start: 0.4rem;
    vertical-align: middle; cursor: default;
}
/* Answered allergies and flags, directly under the date of birth. Reference material rather than
   a warning to be dismissed — hence yellow but flat, with no border demanding a decision. */
.visit-header-alerts {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem;
    margin-block: 0.15rem; color: var(--warn-fg); font-size: 0.85rem;
}
.visit-header-alert {
    background: var(--warn-bg); border-radius: var(--radius-pill);
    padding: 1px 9px; font-weight: 600;
    max-width: 22rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Answered on this visit, so the chip opens the field it came from. */
.visit-header-alert-link {
    border: none; font: inherit; font-weight: 600; color: inherit; cursor: pointer;
    text-decoration: underline; text-underline-offset: 2px; text-decoration-thickness: 1px;
}
.visit-header-alert-link:hover { background: var(--warn-border); }
/* On a phone the header must not eat the screen — the name and the alerts are what matter. */
@media (max-width: 700px) {
    .visit-header-sticky { position: static; }
    .visit-header-alert { max-width: 100%; }
}

/* A reminder, not an emergency. These flags are worth seeing before a visit is signed off, but
   they're read on every single visit that has them — and red-on-red at the top of the page, every
   time, is the alarm nobody hears any more. Softened to the warning palette: still the first thing
   noticed, no longer something that looks like it went wrong. */
.visit-alerts {
    display: flex; align-items: flex-start; gap: 0.55rem; margin-bottom: 0.9rem;
    padding: 0.5rem 0.75rem; border-radius: var(--radius);
    background: var(--warn-bg); border: 1px solid var(--warn-border); color: var(--warn-fg);
    font-size: 0.875rem;
}
.visit-alerts-icon { font-size: 0.95rem; line-height: 1.3; flex-shrink: 0; opacity: 0.85; }
.visit-alerts-body { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; min-width: 0; }
.visit-alert-chip {
    background: var(--color-surface); border: 1px solid var(--warn-border);
    border-radius: var(--radius-pill); padding: 1px 9px; font-size: 0.8rem; font-weight: 600;
}


/* Section chips: adding and removing live in one control, one click each. */
.section-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-bottom: 0.9rem; }
.section-chips-label { font-size: 0.78rem; font-weight: 600; color: var(--color-text-muted); margin-right: 0.2rem; }
.section-chip {
    display: inline-flex; align-items: center; gap: 0.35rem; cursor: pointer;
    border: 1px dashed var(--color-border-strong); background: var(--color-surface);
    color: var(--color-text-muted); border-radius: var(--radius-pill);
    padding: 4px 12px; font-size: 0.8125rem; font-weight: 500;
    transition: background .15s, color .15s, border-color .15s;
}
.section-chip:hover:not(:disabled) { border-color: var(--color-primary); color: var(--color-primary); }
.section-chip:disabled { opacity: .5; cursor: default; }
.section-chip.active {
    border-style: solid; border-color: transparent;
    background: var(--primary-tint-strong); color: var(--color-primary-dark);
}
.section-chip-mark { font-size: 0.7rem; opacity: .7; }
/* Whose form and which version. Two colleagues sharing a key render the same name, so without
   this a chip cannot say which one holds the answers on screen. Quiet enough to read past when
   there is only one. */
.section-chip-owner { font-size: 0.68rem; opacity: .6; font-weight: 400; white-space: nowrap; }

/* The overflow drawer for other professions' templates — reachable, not in the way. */
.section-chip-more { border-style: solid; font-weight: 600; }
.section-more-panel {
    margin: -0.4rem 0 0.9rem;
    padding: 0.6rem 0.7rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
.section-more-panel .field-input { max-width: 18rem; margin-bottom: 0.5rem; }
.section-more-list { display: flex; flex-wrap: wrap; gap: 0.4rem; }

/* Collapsible section card — the whole title row is the toggle. */
.section-toggle {
    display: flex; align-items: center; gap: 0.45rem; flex: 1; min-width: 0;
    background: none; border: none; padding: 0; cursor: pointer; color: var(--color-text);
    font: inherit; text-align: start;
}
.section-caret { font-size: 0.7rem; color: var(--color-text-muted); width: 0.8rem; flex-shrink: 0; }
.section-filled { font-size: 0.75rem; }
.section-dirty { color: var(--warn-fg); font-size: 0.7rem; }

.charge-total { font-weight: 600; color: var(--color-primary); }
.add-line-form { margin-top: 0.9rem; padding-top: 0.9rem; border-top: 1px dashed var(--color-border-2); }
/* Wraps: the admin migration card puts nine buttons in this row, and the card clips. */
.visit-status-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.admin-card { background: var(--color-surface); border: 1px solid var(--color-border-2); border-radius: var(--radius-lg); padding: 1.35rem 1.5rem; margin-bottom: 1rem; box-shadow: var(--shadow-sm); }

/* ─── Patient hub + timeline ────────────────────────────────────────────── */
.patient-hub-grid { display: grid; grid-template-columns: 280px 1fr; gap: 1rem; align-items: start; }
@media (max-width: 900px) { .patient-hub-grid { grid-template-columns: 1fr; } }
.summary-list { display: grid; grid-template-columns: auto 1fr; gap: 0.35rem 0.9rem; font-size: 0.88rem; }
.summary-list dt { color: var(--color-text-muted); }
.summary-list dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
/* A flex row that could neither wrap nor scroll: on a phone the patient hub's tabs (Visits,
   Attachments, Billing, Timeline…) were squeezed until their labels were unreadable, and past
   that they pushed the whole page into a sideways scroll. The strip now scrolls on its own —
   the standard mobile tab behaviour, and better than wrapping here because a wrapped row breaks
   the underline that shows which tab is active. The border-bottom stays on the container, so it
   spans the full width no matter how far the strip is scrolled. */
.hub-tabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.25rem;
    border-bottom: 1px solid var(--color-border-2);
    margin-bottom: 1rem;
    overflow-x: auto;
    /* Momentum scrolling on iOS, and no permanent scrollbar eating into the tab height. */
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
.hub-tabs::-webkit-scrollbar { display: none; }
.hub-tab {
    background: none; border: none; padding: 0.5rem 0.9rem; cursor: pointer;
    font-size: 0.9rem; color: var(--color-text-muted); border-bottom: 2px solid transparent;
    border-radius: var(--radius-sm) var(--radius-sm) 0 0; transition: background .15s, color .15s;
    /* No shrinking and no mid-label wrapping: a tab is as wide as its word, and the row scrolls. */
    flex: 0 0 auto;
    white-space: nowrap;
}
/* Tighter on phones so more tabs are reachable before any scrolling is needed at all. */
@media (max-width: 600px) {
    .hub-tab { padding: 0.5rem 0.65rem; font-size: 0.85rem; }

    /* Wrap rather than scroll on a phone. The strips scroll with the scrollbar hidden, which on
       a touch screen gives no hint at all that the row continues: the patient hub shows three of
       its tabs and the rest simply do not appear to exist. Nobody swipes a row they cannot tell
       is swipeable.

       The reason the original chose one scrolling row — that wrapping breaks the baseline the
       active-tab underline sits on — only applies to the container's border, not the indicator:
       each .tab-btn/.hub-tab draws its own 2px bottom border, so the active tab stays marked
       whichever row it lands on. These strips top out around six or eight tabs, so this costs
       two short rows and shows every tab.

       Both selectors live here, below .hub-tabs' own definition, and not up beside .tab-bar's:
       at equal specificity the later rule wins, so a copy sitting above line 2100 silently lost
       to `.hub-tabs { flex-wrap: nowrap }` and left the patient hub scrolling while every
       .tab-bar page wrapped correctly. */
    .tab-bar, .hub-tabs {
        flex-wrap: wrap;
        overflow-x: visible;
        row-gap: 2px;
    }
}
.hub-tab:hover { background: var(--primary-tint-weak); color: var(--color-text); }
.hub-tab.active { color: var(--color-primary); border-bottom-color: var(--color-primary); font-weight: 600; }
.timeline { display: flex; flex-direction: column; gap: 0.6rem; }
.timeline-entry {
    display: flex; gap: 0.9rem; padding: 0.85rem 1rem; background: var(--color-surface);
    border: 1px solid var(--color-border-2); border-radius: var(--radius-lg); cursor: pointer;
    transition: border-color .18s, box-shadow .18s, transform .18s;
}
.timeline-entry:hover { border-color: var(--color-primary); box-shadow: var(--shadow); transform: translateY(-1px); }
.timeline-date { display: flex; flex-direction: column; align-items: center; min-width: 3.4rem; }
.timeline-day { font-size: 1.3rem; font-weight: 700; color: var(--color-primary); line-height: 1.1; }
.timeline-month { font-size: 0.7rem; color: var(--color-text-muted); text-transform: uppercase; }
.timeline-body { flex: 1; min-width: 0; }
.timeline-title-row { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.15rem; }
.timeline-meta { font-size: 0.78rem; color: var(--color-text-faint); margin-top: 0.15rem; }
.timeline-field-label { font-weight: 700; }

/* ─── Calendar (TimeGrid) ───────────────────────────────────────────────── */
.timegrid-wrap { overflow-x: auto; background: var(--color-surface); border: 1px solid var(--color-border-2); border-radius: var(--radius-lg); box-shadow: var(--shadow-sm); }
/* Columns and row height are set inline by TimeGrid (day view = 1 column, 30-min rows). */
.timegrid { display: grid; min-width: 860px; --slot-h: 3rem; }
.timegrid.timegrid-single { min-width: 0; }
.timegrid-gutter { padding-top: 2rem; }
.timegrid-hour-label { height: var(--slot-h); font-size: 0.68rem; color: var(--color-text-faint); text-align: right; padding-right: 0.4rem; transform: translateY(-0.5em); }
.timegrid-day { border-left: 1px solid var(--color-hover); }
.timegrid-day.today .timegrid-day-header { color: var(--color-primary); font-weight: 700; }
.timegrid-day-header { height: 2rem; display: flex; align-items: center; justify-content: center; font-size: 0.8rem; color: var(--color-text-muted); border-bottom: 1px solid var(--color-border-2); position: sticky; top: 0; background: var(--color-surface); }
.timegrid-day-body { position: relative; }
.timegrid-slot { height: var(--slot-h); border-bottom: 1px solid var(--color-hover); cursor: pointer; transition: background .12s; }
.timegrid-slot.half { border-bottom-style: dashed; }
.timegrid-slot:hover { background: var(--primary-tint-weak); }
.timegrid-event {
    position: absolute; left: 2px; right: 2px; background: var(--accent-bg); border-left: 3px solid var(--color-primary);
    border-radius: 4px; padding: 2px 5px; font-size: 0.72rem; overflow: hidden; cursor: pointer;
    display: flex; flex-direction: column; min-height: 1.1rem; line-height: 1.25;
}
.timegrid-event:hover { filter: brightness(1.04); box-shadow: var(--shadow-sm); }
.timegrid-event-row { display: flex; align-items: center; gap: 4px; min-width: 0; }
.timegrid-event-time { font-weight: 600; color: var(--color-primary-dark); flex-shrink: 0; }
/* min-width:0 so the name ellipsises inside the chip instead of the chip clipping it
   mid-glyph — a 15-minute appointment in a narrow column has very little room. */
.timegrid-event-title { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
/* A one-line chip puts the name next to the time instead of stacking it out of sight. */
.timegrid-event.chip-compact { padding: 1px 4px; container-type: inline-size; }
.timegrid-event.chip-compact .timegrid-event-row { flex: 1; min-width: 0; gap: 3px; }
.timegrid-event.chip-compact .timegrid-event-title { font-weight: 500; }
/* In a narrow column the time plus the department dot alone exceed the chip, and the chip
   clips rather than ellipsising. Below that width the time wins: the department is still in
   the tooltip, and the appointment stays readable instead of being sheared off. */
@container (max-width: 58px) {
    .timegrid-event.chip-compact .dept-dot { display: none; }
}
/* Status colour-coding, shared by the week/day chips, the month chips and the legend.
   Each status gets its own fill AND its own left bar, so they stay distinguishable in dark
   mode and for the red/green colour-blind (fill lightness differs, not just hue). */
/* Selectors are paired with the element class so they beat `.month-event`'s own background
   regardless of where they sit in the file. */
.timegrid-event.status-booked,    .month-event.status-booked,    .legend-swatch.status-booked
    { background: var(--info-bg);    border-left-color: var(--info-fg); }
.timegrid-event.status-confirmed, .month-event.status-confirmed, .legend-swatch.status-confirmed
    { background: var(--success-bg); border-left-color: var(--success); }
.timegrid-event.status-arrived,   .month-event.status-arrived,   .legend-swatch.status-arrived
    { background: var(--warn-bg);    border-left-color: var(--warn-fg); }
.timegrid-event.status-completed, .month-event.status-completed, .legend-swatch.status-completed
    { background: var(--color-surface-alt); border-left-color: var(--color-border-strong); }
.timegrid-event.status-completed, .month-event.status-completed { opacity: .75; }
.timegrid-event.status-noshow,    .month-event.status-noshow,    .legend-swatch.status-noshow
    { background: var(--danger-bg);  border-left-color: var(--danger); }

/* Ring the chip a search result linked to. */
.timegrid-event-linked {
    outline: 3px solid var(--color-primary); outline-offset: 1px;
    animation: linked-pulse 1.3s ease-in-out 2;
}
@keyframes linked-pulse { 50% { outline-color: transparent; } }

/* ─── Calendar toolbar ──────────────────────────────────────────────────── */
/* .page-header > *:last-child pins the actions column at its max-content width, which is right
   for a lone button and wrong for a toolbar meant to wrap: from 640px up to roughly 1100px these
   ~840px of controls stayed on one unwrapped line and put the whole calendar into a sideways
   scroll. Letting it shrink is what lets its own flex-wrap do anything at all.
   (:last-child only to outrank that rule's selector — this is always the last child anyway.) */
.page-header > .calendar-toolbar:last-child { flex-shrink: 1; min-width: 0; }

/* Once it does wrap, each group wraps as a whole, so a set of buttons that only makes sense
   together — Day/Week/Month/Agenda, ← Prev/Today/Next → — never gets split across two lines. */
.calendar-toolbar-group { display: inline-flex; gap: 0.15rem; flex: 0 0 auto; }
.calendar-fs-toggle { display: inline-flex; align-items: center; gap: 5px; }

/* ─── Calendar full screen ──────────────────────────────────────────────────
   z-index 35: over the sidebar (20) and the nav flyout (30), under the drawer backdrop (40) and
   the drawer itself (41), so clicking an appointment still opens its panel on top rather than
   behind. A flex column, because the point of the mode is the reclaimed *height* — the header
   and legend keep their natural size and the view takes everything left, scrolling inside itself
   instead of scrolling the whole page and taking the toolbar off-screen with it. */
.calendar-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 35;
    background: var(--color-bg);
    padding: 14px 18px;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.calendar-fullscreen .page-header { margin-bottom: 12px; flex: 0 0 auto; }
.calendar-fullscreen .calendar-legend { flex: 0 0 auto; }

/* On a phone the toolbar wraps to four rows and the legend to two, which between them took more
   than half of the screen the mode exists to free up. Drop what is redundant here and keep what
   is not: the "Calendar" heading (you came from the Calendar nav item) and the status key (the
   chips are colour-coded on every view, full screen or not) go; the date range stays, since it
   is the one label that changes as you page through. */
@media (max-width: 640px) {
    .calendar-fullscreen { padding: 8px 10px; }
    .calendar-fullscreen .page-header h1,
    .calendar-fullscreen .calendar-legend { display: none; }
    .calendar-fullscreen .page-header { margin-bottom: 8px; }
}
/* The view is the only part that reacts to the reclaimed height. min-height: 0 throughout,
   because a flex item's default min-height: auto refuses to shrink below its content and would
   push the view straight back off the bottom of the screen. */
.calendar-fullscreen > .timegrid-wrap,
.calendar-fullscreen > .month-grid,
.calendar-fullscreen > .admin-card { min-height: 0; overflow: auto; }

/* Day/week and agenda take their natural height and scroll once they exceed the screen — they
   are lists of fixed-height rows, so stretching them to fill would just pad a card with blank
   space below the last hour. */
.calendar-fullscreen > .timegrid-wrap,
.calendar-fullscreen > .admin-card { flex: 0 1 auto; max-height: 100%; }

/* Month is the opposite: it is a fixed 5–6 rows whatever the screen, so it should divide the
   height rather than leave it empty. The Mon–Sun labels are the grid's first row and need an
   explicit `auto` track — left to grid-auto-rows they stretch to a full 1fr like a week does
   and open a chasm between the headers and the first week. */
.calendar-fullscreen > .month-grid {
    flex: 1 1 auto;
    grid-template-rows: auto;
    grid-auto-rows: minmax(96px, 1fr);
}
.calendar-toolbar-date { width: 9rem; flex: 0 0 auto; }
@media (max-width: 640px) {
    /* Two groups plus the date field don't fit 360px three-across. Let the date field take the
       rest of whichever line it lands on rather than holding a rigid 9rem and forcing a wrap. */
    .calendar-toolbar-date { width: auto; flex: 1 1 9rem; min-width: 9rem; }
}

.calendar-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin-bottom: 0.7rem; }
.legend-item { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.72rem; color: var(--color-text-muted); }
.legend-swatch {
    width: 14px; height: 11px; border-radius: 3px; display: inline-block;
    border-left: 3px solid var(--color-primary); background: var(--accent-bg);
}
.legend-sep { width: 1px; height: 0.9rem; background: var(--color-border); }

/* ─── Side panel ────────────────────────────────────────────────────────── */
.side-panel-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.25); z-index: 40; }
.side-panel {
    position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 92vw); background: var(--color-surface);
    z-index: 41; padding: 1.4rem; overflow-y: auto; box-shadow: var(--shadow-lg);
    border-start-start-radius: var(--radius-lg); border-end-start-radius: var(--radius-lg);
}
.status-button-row { display: flex; gap: 0.3rem; flex-wrap: wrap; margin-bottom: 0.9rem; }

/* VisitPage section card → the template editor, where per-field carry-forward is decided. */
.section-carry-link { font-size: 0.78rem; white-space: nowrap; margin-inline-start: 0.6rem; text-decoration: none; }

/* ─── Past-visits drawer (VisitPage → PastVisitsPanel) ─────────────────── */
/* Wider than the default drawer: it shows whole visits, label + value + "now" side by side. */
.past-visits-panel { width: min(560px, 94vw); }
.past-visits-toolbar { display: flex; gap: 0.5rem; align-items: center; margin: 0.8rem 0 0.9rem; }
.past-visits-toolbar .field-input { flex: 1; }
.past-visit-entry { cursor: default; }
.past-visit-entry:hover { transform: none; box-shadow: none; border-color: var(--color-border); }
.past-visit-fields { display: flex; flex-direction: column; gap: 0.2rem; margin-top: 0.4rem; }
.past-visit-field {
    display: grid; grid-template-columns: minmax(7rem, 38%) 1fr; gap: 0.2rem 0.6rem; align-items: baseline;
    padding: 0.2rem 0.4rem; border-radius: var(--radius-sm); font-size: 0.82rem;
    border-inline-start: 3px solid transparent;
}
.past-visit-label { color: var(--color-text-muted); }
.past-visit-value { color: var(--color-text); white-space: pre-wrap; word-break: break-word; }
/* The three verdicts: identical stays quiet, missing-here is a hint, changed is the point. */
.past-visit-field.same { opacity: 0.7; }
.past-visit-field.absent { border-inline-start-color: var(--color-border-2); }
.past-visit-field.changed { border-inline-start-color: var(--color-primary); background: var(--primary-tint-weak); }
.past-visit-now {
    grid-column: 2; font-size: 0.75rem; color: var(--color-primary-dark); white-space: pre-wrap; word-break: break-word;
}
.timegrid-event-dept { display: block; font-size: 0.62rem; opacity: 0.75; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The appointment's own text under the patient's name; the chip clips it when space runs out. */
.timegrid-event-text { display: block; font-size: 0.66rem; opacity: 0.85; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.month-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.month-day {
    min-height: 96px; border: 1px solid var(--color-border); border-radius: var(--radius);
    background: var(--color-surface); padding: 4px 6px; cursor: pointer; overflow: hidden;
    transition: border-color .15s, box-shadow .15s;
}
.month-day:hover { border-color: var(--color-primary); box-shadow: var(--shadow-sm); }
.month-day.other-month { opacity: 0.45; }
.month-day.today { border-color: var(--color-primary); border-width: 2px; }
.month-day-num { font-size: 0.75rem; font-weight: 600; color: var(--color-text-muted); }
.month-event {
    font-size: 0.68rem; border-left: 3px solid var(--color-primary); border-radius: 3px;
    padding: 1px 4px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    background: var(--accent-bg);
}
.month-more { font-size: 0.65rem; color: var(--color-text-muted); margin-top: 2px; }
.dept-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; margin-right: 4px; flex-shrink: 0; }

/* Agenda (list) view — everything on one line per appointment, nothing clipped. */
.agenda-list { display: flex; flex-direction: column; gap: 1.1rem; }
.agenda-day-header {
    display: flex; align-items: baseline; gap: .5rem; font-size: .8rem; font-weight: 600;
    padding: .3rem 0 .4rem; border-bottom: 1px solid var(--color-border); margin-bottom: .2rem;
}
.agenda-day-header.today { color: var(--color-primary); }
.agenda-day-count { font-size: .72rem; font-weight: 500; color: var(--color-text-muted); }
.agenda-row {
    display: grid; grid-template-columns: 8.5rem minmax(0, 1fr) auto; gap: .75rem; align-items: center;
    padding: .5rem .7rem; border-bottom: 1px solid var(--color-hover); cursor: pointer;
    border-left: 3px solid transparent; border-radius: var(--radius-sm); transition: background .12s;
}
.agenda-row:hover { background: var(--primary-tint-weak); }
.agenda-row.status-completed { opacity: .6; }
.agenda-row.status-noshow { border-left-color: var(--danger); }
.agenda-row.status-arrived { border-left-color: var(--warn-fg); }
.agenda-row.status-confirmed { border-left-color: var(--success); }
.agenda-time { font-size: .8rem; font-weight: 600; color: var(--color-primary-dark); font-variant-numeric: tabular-nums; }
.agenda-who { display: flex; align-items: center; gap: .4rem; min-width: 0; }
.agenda-name { font-size: .875rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agenda-meta { font-size: .75rem; color: var(--color-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.agenda-empty { padding: 1.5rem; text-align: center; color: var(--color-text-muted); font-size: .875rem; }
@media (max-width: 700px) {
    .agenda-row { grid-template-columns: 5.5rem minmax(0, 1fr); row-gap: .2rem; }
    .agenda-row > .badge { grid-column: 2; justify-self: start; }
}

/* ─── Dashboard ─────────────────────────────────────────────────────────── */
/* minmax(300px, 1fr) can exceed the container when a child has wider min-content (long
   currency labels in the report KPIs), which pushed the whole grid past its parent.
   min(300px, 100%) lets a single column shrink instead of overflowing. */
.dashboard-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); gap: 1rem; align-items: start; min-width: 0; }
.dashboard-grid > * { min-width: 0; }
.dash-list { display: flex; flex-direction: column; gap: 0.4rem; }
/* flex-wrap so a narrow dashboard card wraps the status dropdown to its own line rather than
   crushing the title — with min-width:0 and a fixed-width badge (Overdue/Waiting) + select both
   competing for the same row, the title used to shrink to a couple of characters before an
   ellipsis, on any card holding several badged appointments. */
.dash-list-item { display: flex; align-items: center; gap: 0.6rem; padding: 0.35rem 0; border-bottom: 1px solid var(--color-hover); flex-wrap: wrap; }
.dash-time { font-weight: 600; color: var(--color-primary); min-width: 3rem; font-size: 0.85rem; }
.dash-title { flex: 1 1 8rem; min-width: 8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Three currency figures at 1.8rem overflow a two-column report card, and the card clips.
   Wrap, and scale the figure to the space the row actually has. */
.dash-stats { display: flex; flex-wrap: wrap; gap: 0.85rem 1.5rem; container-type: inline-size; }
.dash-stat { display: flex; flex-direction: column; min-width: 0; flex: 1 1 7.5rem; }
.dash-stat-value {
    font-size: clamp(1.05rem, 6cqi, 1.8rem); font-weight: 700; color: var(--color-primary);
    font-variant-numeric: tabular-nums; overflow-wrap: anywhere;
}
.dash-stat-label { font-size: 0.78rem; color: var(--color-text-muted); }
.badge-low { background: var(--danger-bg); color: var(--danger-fg); }
.dash-status-select {
    font-size: 0.72rem; font-weight: 600; border: 1px solid var(--color-border-2); border-radius: 999px;
    padding: 0.15rem 0.5rem; background: var(--color-surface-alt); cursor: pointer;
}
.dash-status-select.badge-active { background: var(--success-bg); color: var(--success-fg); }
.dash-status-select.badge-inactive { background: var(--danger-bg); color: var(--danger-fg); }

/* ─── Global search dropdown ────────────────────────────────────────────── */
.search-group-title {
    font-size: 0.68rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
    color: var(--color-text-faint); padding: 0.5rem 0.8rem 0.2rem;
}
/* align-items: stretch, not the align-items: center inherited from the .search-result rule up at
   ~560: that rule is a single centred row, this one stacks title over subtitle, and centring a
   column centres both lines horizontally. Invisible while the dropdown was 280px of mostly-full
   rows; obvious the moment it goes full-width on a phone. stretch rather than flex-start so the
   lines start on the correct side in Arabic too. */
.search-result { display: flex; flex-direction: column; align-items: stretch; gap: 1px; }
.search-result.highlighted { background: var(--accent-bg); }
.search-result-main { font-weight: 500; }
.search-result-sub { font-size: 0.75rem; color: var(--color-text-muted); }

/* ─── Attachments ───────────────────────────────────────────────────────── */
/* Our own file picker: the native control's text is browser-language and untranslatable. */
.file-picker { display: inline-flex; align-items: center; gap: 0.5rem; cursor: pointer; }
.file-input-hidden { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }
.file-picker-status { font-size: 0.8rem; }

.attachment-upload-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap; }
.attachment-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 0.9rem; }
.attachment-card {
    border: 1px solid var(--color-border-2); border-radius: var(--radius-lg); overflow: hidden;
    background: var(--color-surface); display: flex; flex-direction: column;
    transition: border-color .18s, box-shadow .18s, transform .18s;
}
.attachment-card:hover { border-color: var(--color-primary); box-shadow: var(--shadow); transform: translateY(-1px); }
.attachment-preview { height: 120px; background: var(--color-surface-alt); display: flex; align-items: center; justify-content: center; cursor: pointer; overflow: hidden; }
.attachment-preview img { width: 100%; height: 100%; object-fit: cover; }
.attachment-file-icon { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; color: var(--color-text-faint); font-size: 0.7rem; font-weight: 700; }
.attachment-meta { padding: 0.45rem 0.6rem 0.15rem; display: flex; flex-direction: column; min-width: 0; }
.attachment-name { font-size: 0.82rem; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attachment-sub { font-size: 0.72rem; color: var(--color-text-faint); }
/* Wraps rather than overflowing: the card clips its content, so an unwrapped row silently
   cut off the last action ("Delete") in narrow columns. */
.attachment-actions {
    display: flex; flex-wrap: wrap; gap: 0.35rem 0.6rem; padding: 0.25rem 0.6rem 0.5rem;
    align-items: center; min-width: 0;
}
.attachment-actions > * { flex: 0 0 auto; }
.btn-danger-link { color: var(--danger); }
.lightbox-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.75); z-index: 60; display: flex; align-items: center; justify-content: center; cursor: zoom-out; }

/* ─── Image viewer / before-after ────────────────────────────────────────── */
/* Each pane's viewport is a fixed box that clips its image, so zooming one pane can never
   resize the pane next to it — that reflow was what made zooming look like it hit both. */
.viewer {
    display: flex; flex-direction: column; gap: 0.5rem; cursor: default;
    background: var(--color-surface); border-radius: var(--radius-lg); padding: 0.85rem;
    max-width: 96vw; max-height: 94vh; box-shadow: var(--shadow-lg);
}
.viewer-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 0.3rem; }
.viewer-hint { margin-left: auto; font-size: 0.72rem; color: var(--color-text-faint); }
.viewer-panes { display: flex; gap: 0.75rem; min-height: 0; }
.viewer-pane { margin: 0; display: flex; flex-direction: column; min-width: 0; flex: 1; }
.viewer-pane-label {
    font-size: 0.7rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--color-text-muted); padding-bottom: 0.25rem;
}
.viewer-viewport {
    position: relative; overflow: hidden; touch-action: none; background: var(--color-surface-alt);
    border: 1px solid var(--color-border); border-radius: var(--radius);
    width: 100%; height: 72vh; display: flex; align-items: center; justify-content: center;
}
.viewer-panes-2 .viewer-viewport { height: 66vh; }
.viewer-rotor { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; transition: transform .18s; }
.viewer-viewport img { max-width: 100%; max-height: 100%; object-fit: contain; user-select: none; -webkit-user-drag: none; }
.viewer-pane-meta { display: flex; align-items: center; gap: 0.5rem; font-size: 0.78rem; padding-top: 0.35rem; }
.viewer-pane-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
.viewer-pane-actions { margin-left: auto; display: flex; gap: 0.5rem; flex-shrink: 0; }
@media (max-width: 800px) {
    .viewer-panes { flex-direction: column; }
    .viewer-panes-2 .viewer-viewport { height: 34vh; }
}
/* ── RTL (Arabic) ──────────────────────────────────────── */
[dir="rtl"] body { direction: rtl; }
/* No .app-layout override for RTL on purpose: dir="rtl" already reverses the grid's inline
   axis, so the base "sidebar topbar" rule puts the sidebar on the right by itself. Restating
   the columns here flipped it a second time and pushed it back to the left. */
[dir="rtl"] .topbar-user { margin-left: 0; margin-right: auto; }
[dir="rtl"] .password-eye { right: auto; left: 6px; }
[dir="rtl"] .password-field .field-input { padding-right: 12px; padding-left: 34px; }
[dir="rtl"] .search-input { padding: 7px 34px 7px 12px; }
[dir="rtl"] .search-icon { left: auto; right: 9px; }
[dir="rtl"] .search-dropdown { left: 0; right: 0; }
[dir="rtl"] .timegrid-event, [dir="rtl"] .month-event { border-left: none; border-right-style: solid; border-right-width: 3px; }
[dir="rtl"] .side-panel { right: auto; left: 0; box-shadow: var(--shadow-lg); }
[dir="rtl"] .column-menu { right: 0; left: auto; }
[dir="rtl"] .ldp-popup { left: auto; right: 0; }
[dir="rtl"] .ldp-clear { right: auto; left: 6px; }
[dir="rtl"] .ldp-input { padding-right: 30px; padding-left: 26px; }
[dir="rtl"] .ldp-icon { left: auto; right: 9px; }
[dir="rtl"] .filter-select { padding: 0 14px 0 28px; background-position: left 8px center; }
[dir="rtl"] .toast { right: auto; left: 20px; }

/* ── Responsive tables ─────────────────────────────────── */
/* Wide tables scroll inside their container instead of blowing out the page. */
.admin-table-wrap, .grid-wrap { overflow-x: auto; }
.admin-table, .patient-table, .data-table { min-width: 560px; }
/* A nested table sits inside a card that may be far narrower than 560px (the report
   breakdowns are ~360px). Let it shrink and scroll inside its own card instead of
   spilling out of it. */
.data-table.nested { min-width: 0; }
.admin-card > .data-table.nested,
.admin-card > table.data-table.nested { display: block; overflow-x: auto; }
@media (max-width: 900px) {
    /* Cards become their own horizontal scroll context on small screens. */
    .admin-card { overflow-x: auto; }
    .content { padding: 12px; }
    .page-header { flex-wrap: wrap; gap: 8px; }
    .search-wrapper { width: 100%; max-width: 280px; }
}
/* The topbar has to survive 360px. Everything in it is one row that cannot wrap, so past a point
   the only honest move is to drop what can be reached elsewhere: the profile name is a link to a
   page the sidebar already lists, and its icon keeps the target. Search shrinks rather than
   holding a fixed width, and the row is allowed to compress before anything overflows. */
@media (max-width: 640px) {
    /* The status buttons wrap internally, but as a flex ITEM the row still took its max-content
       width (four buttons side by side) and hung 90px off the right of a 360px screen, dragging
       the whole document into a sideways scroll. Giving it the full line and letting it shrink
       below that width is what actually lets its own flex-wrap do its job. */
    .page-header { align-items: flex-start; }
    .visit-status-actions { width: 100%; min-width: 0; }

    /* A section's title, its template name, its filled count and Save Section were one unwrapped
       row: the title wrapped to two lines and the button sat on top of it. The heading takes the
       line, the action takes the next, and the secondary labels clip instead of pushing. */
    .panel-header-row { flex-wrap: wrap; }
    .panel-header-row > .section-toggle { flex: 1 1 100%; }
    /* Left, not pushed right: the floating navigator button sits in the bottom-right corner on
       these widths, and a right-aligned Save Section ends up underneath it on whichever section
       happens to be at the foot of the viewport. */
    .panel-header-row > .section-toggle + * { margin-inline-start: 0; }
    .section-filled { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

    .topbar { padding: 0 10px; gap: 6px; min-width: 0; }
    .topbar-user { gap: 6px; min-width: 0; flex-shrink: 0; }
    .topbar-username { font-size: 0; }          /* icon stays, name goes */
    .topbar-username svg { margin-right: 0; }
    /* Every button in the row is a label the sidebar or a tooltip already carries, so they give
       up their horizontal padding before the one control that needs real width — the search box. */
    .topbar-user button { padding-left: 8px; padding-right: 8px; }
    .topbar-signout-label { display: none; }    /* icon stays, "Sign out" goes */
    /* .search-wrapper is a fixed 280px, which alone is most of a 360px screen — the row then
       needed 448px and took the whole document sideways with it. Here it gives up the fixed
       width and takes whatever the buttons leave, but never less than a field you can read
       what you typed in: at flex 1 1 0 with no floor it collapsed to 58px on a 390px phone. */
    .search-wrapper { width: auto; max-width: none; flex: 1 1 auto; min-width: 116px; }
    .search-input { min-width: 0; padding-right: 10px; }
    /* The dropdown is left/right-anchored to the wrapper, so at 58px it rendered a 58px column
       of vertically-shredded results — the single reason global search read as "broken" on a
       phone. Pin it to the viewport instead: the input can stay narrow, the results cannot.
       Same story, same fix for the bell: 300px of notifications anchored right:0 to a 45px
       button hung off the left edge of the screen and vanished under the sidebar. Both take the
       content column — left of it is the nav rail, and an overlay floating over the nav reads
       as a rendering glitch. */
    .search-dropdown,
    .notif-menu {
        position: fixed;
        top: calc(var(--topbar-height) + 4px);
        left: calc(var(--sidebar-width) + 8px);
        right: 8px;
        min-width: 0;
        max-width: none;
        max-height: calc(100vh - var(--topbar-height) - 16px);
    }
    /* The sidebar is on the other side in Arabic, and the physical left/right above would put
       the panel underneath it. Restated rather than made logical: [dir="rtl"] .search-dropdown
       above already sets left/right physically and outranks any inset-inline-* here. */
    [dir="rtl"] .search-dropdown,
    [dir="rtl"] .notif-menu {
        left: 8px;
        right: calc(var(--sidebar-width) + 8px);
    }
    /* Drawers start below the topbar rather than over it. Their close ✕ is top-right, which on a
       phone is the exact spot "Sign out" occupies — one mistimed tap on a closing drawer signed
       the user out mid-booking. The backdrop still covers the topbar, so a tap that lands high
       dismisses the drawer instead of reaching the button underneath. */
    .side-panel {
        top: var(--topbar-height);
        border-start-start-radius: 0;
    }
}

@media (max-width: 700px) {
    /* Collapse the sidebar to an icon rail so content gets the width. */
    :root { --sidebar-width: 56px; }
    .nav-links li a span, .nav-links li a { font-size: 0; gap: 0; justify-content: center; }
    /* The "Setup" flyout's own items live inside .nav-links in the DOM, so the blanket rule
       above collapses their text too — same fix as the pinned rail, restored here. */
    .nav-submenu li a span, .nav-submenu li a { font-size: 0.875rem; gap: 8px; justify-content: flex-start; }
    /* Shrink the pill inset so icons fit the 56px rail. */
    .nav-links li a { margin: 2px 6px; padding: 9px 0; }
    .nav-links li a .nav-icon { width: 20px; height: 20px; }
    .nav-section { display: none; }
    .nav-group-toggle { font-size: 0; gap: 0; justify-content: center; margin: 2px 6px; padding: 9px 0; }
    .nav-group-toggle .nav-icon { width: 20px; height: 20px; }
    .nav-caret { display: none; }
    /* Same story as the pinned rail: the wordmark can't fit 56px, the mark can. */
    .sidebar-logo { display: none; }
    .sidebar-mark { display: block; height: 24px; }
    .sidebar-header { flex-direction: column; justify-content: center; gap: 6px; height: auto; padding: 10px 6px; }
    .sidebar-pin { position: static; transform: none; margin: 0 auto; }
    [dir="rtl"] .sidebar-pin { transform: scaleX(-1); }
}

.image-editor {
    background: var(--color-surface); border-radius: 10px; padding: 0.8rem; cursor: default;
    max-width: 94vw; max-height: 94vh; display: flex; flex-direction: column;
}
.image-editor-toolbar { display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
.image-editor-canvas-wrap {
    overflow: auto; margin-top: 0.5rem; max-height: 74vh; max-width: 92vw;
    background: var(--color-surface-alt); border: 1px solid var(--color-border); border-radius: 6px;
}
.image-editor-canvas-wrap canvas { display: block; }

.password-field { position: relative; }
.password-field .field-input { padding-right: 34px; width: 100%; }
.password-eye {
    position: absolute; right: 6px; top: 50%; transform: translateY(-50%);
    background: none; border: none; cursor: pointer; color: var(--color-text-muted);
    display: flex; align-items: center; padding: 4px;
}
.password-eye:hover { color: var(--color-text); }
.attachment-tags { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-top: 0.2rem; }
.attachment-card.compare-selected { outline: 3px solid var(--color-primary); }

/* ── Obsolete answers on a visit section ───────────────── */
/* A template version can stop rendering a field, but the answer stays on the visit. Amber rather
   than red, and using the existing --warn tokens so it follows both themes: nothing is broken and
   nothing is lost — this is a "look at this", not a failure. border-inline-start, not -left, so
   it lands on the correct side in Arabic. */
.obsolete-answers {
    border-inline-start: 4px solid var(--warn-fg);
    background: var(--warn-bg);
}
.obsolete-answers .panel-header-row strong { color: var(--warn-fg); }

/* ── Autosave state, per section ───────────────────────── */
/* Beside that section's own Save button, never as a page-level toast: with several sections
   open, "Saved" on its own cannot say *what* saved. */
.autosave-state {
    font-size: 0.75rem; margin-inline-end: 0.5rem; color: var(--color-text-muted);
}
.autosave-state.saved  { color: var(--success-fg); }
.autosave-state.failed { color: var(--danger-fg); font-weight: 600; }

/* ── Saved text snippets ───────────────────────────────── */
/* Anchored to the field it writes into, so with several textareas open it is never ambiguous
   which one an insert will land in. */
.snippet-picker { position: relative; display: inline-block; float: inline-end; }
.snippet-trigger { font-size: 0.72rem; padding: 1px 8px; }
.snippet-dropdown {
    position: absolute; inset-inline-end: 0; top: 100%; z-index: 40; width: 22rem; max-width: 80vw;
    background: var(--color-surface); border: 1px solid var(--color-border-2);
    border-radius: var(--radius-lg); box-shadow: var(--shadow-lg); padding: 0.5rem;
}
/* The picker floats to the field's inline-end, so the dropdown hangs leftward from there. At
   80vw that is wider than the content column on a phone: the panel's left edge crossed under the
   nav rail and every entry lost its first characters ("scharge — same-day procedure"). Capping
   the width alone can't fix it — the anchor itself sits too close to the screen edge. Letting the
   picker span the field instead moves the anchor to the field's own right edge, so `width: 100%`
   lines the panel up with the field exactly and it cannot reach the rail at any width. */
@media (max-width: 640px) {
    .snippet-picker { float: none; display: block; }
    .snippet-trigger { float: inline-end; }
    .snippet-dropdown { width: 100%; max-width: none; }
}

.snippet-list { list-style: none; margin: 0.4rem 0 0; padding: 0; max-height: 16rem; overflow-y: auto; }
.snippet-item {
    display: block; width: 100%; text-align: start; background: none; border: none; cursor: pointer;
    padding: 0.35rem 0.4rem; border-radius: var(--radius-sm);
}
.snippet-item:hover { background: var(--primary-tint-weak); }
.snippet-name { display: block; font-weight: 600; font-size: 0.82rem; }
.snippet-preview { display: block; font-size: 0.74rem; color: var(--color-text-muted); }
.snippet-save { display: flex; gap: 0.35rem; margin-top: 0.5rem; border-top: 1px solid var(--color-border-2); padding-top: 0.5rem; }

/* Fill stage: the snippet's blanks as inline inputs inside the flowing text. */
.snippet-dropdown.filling { width: 30rem; }

.snippet-fill-title { font-weight: 600; font-size: 0.85rem; margin: 0 0 0.4rem; }

.snippet-fill-body {
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    font-size: 0.84rem;
    line-height: 1.9;
    max-height: 18rem;
    overflow-y: auto;
    padding: 0.5rem 0.55rem;
    background: var(--color-bg);
    border-radius: var(--radius);
}

.snippet-blank {
    display: inline-block;
    border: none;
    border-bottom: 2px solid var(--color-primary);
    border-radius: 0;
    background: var(--primary-tint-weak);
    color: var(--color-text);
    font: inherit;
    padding: 0 4px;
    margin: 0 2px;
    outline: none;
    vertical-align: baseline;
}

.snippet-blank:focus { background: var(--primary-tint); }

.snippet-fill-actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    margin-top: 0.55rem;
}

.snippet-fill-actions .field-hint { flex: 1; }

/* Small helper text under an input. Sits with .field-label / .field-input as the standard
   form vocabulary — pages used to invent their own hint markup with inline styles. */
.field-hint {
    font-size: 0.75rem;
    color: var(--color-text-muted);
}
.field-hint a { color: var(--color-primary); }

/* SMS length/segment hint under an SMS body field — neutral for a single segment, warm once it
   splits so the extra cost (carriers bill per segment) is obvious before Send, not after. */
.sms-length-hint {
    display: inline-block;
    font-size: 0.75rem;
    color: var(--color-text-muted);
    margin-top: 0.25rem;
}
.sms-length-hint-multi { color: var(--warn-fg); }

/* Star-per-row picker for "default department" — click a row's star to make it the default,
   instead of a plain <select> that hides which one is currently active. */
.default-org-list { display: flex; flex-direction: column; gap: 2px; }
.default-org-row {
    display: flex; align-items: center; gap: 0.5rem;
    background: none; border: 1px solid transparent; border-radius: var(--radius-sm);
    padding: 4px 6px; font: inherit; font-size: 0.85rem; text-align: start;
    color: var(--color-text-muted); cursor: pointer; transition: background .12s;
}
.default-org-row:hover:not(:disabled) { background: var(--color-hover); }
.default-org-row:disabled { cursor: default; opacity: .6; }
.default-org-row svg { flex: none; color: var(--color-border-strong); }
.default-org-row.is-default { color: var(--color-text); font-weight: 600; }
.default-org-row.is-default svg { color: var(--warn-fg); }

/* Amber state badge — "waiting", "expired": needs attention but is not an error. */
.badge-warn { background: var(--warn-bg); color: var(--warn-fg); }
/* Green state badge — "linked", "done": settled, nothing to do. */
.badge-ok { background: var(--success-bg); color: var(--success-fg); }

/* ---- Waiting room ---------------------------------------------------- */

.waiting-room-inbox { margin-bottom: 1rem; }

.waiting-room-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.55rem 0;
    border-top: 1px solid var(--color-border-2);
}

.waiting-room-item:first-of-type { border-top: none; }
.waiting-room-item.likely-match { background: var(--success-bg); border-radius: var(--radius-sm); padding-left: 0.5rem; padding-right: 0.5rem; margin: 0 -0.5rem; }

/* Collapsed by default and visually recessive: nothing in this inbox blocks the visit, so it sits
   at the bottom looking like a footnote until it has a likely match, at which point the whole card
   picks up the same light green the matching row uses. */
/* No accent stripe when there's nothing to say. The amber left border this used to carry was from
   when the inbox lived above the clinical sections and had to announce itself; down here it was
   just a warning colour on a card that warns about nothing. */
.waiting-room-inbox { background: var(--color-surface-2, var(--color-surface)); }
.waiting-room-inbox.has-match {
    background: var(--success-bg);
    border-color: var(--success-border);
    border-left: 3px solid var(--success);
}
.wr-inbox-head {
    display: flex; align-items: center; gap: 0.45rem; width: 100%;
    background: none; border: none; padding: 0; cursor: pointer;
    font: inherit; color: inherit; text-align: start;
}
.wr-inbox-title { font-size: 0.875rem; font-weight: 600; }
.waiting-room-inbox:not(.has-match) .wr-inbox-title { font-weight: 500; color: var(--color-text-muted); }
.wr-inbox-caret { display: inline-block; transition: transform .15s; opacity: 0.6; }
.wr-inbox-caret.open { transform: rotate(90deg); }

/* Dashboard pager: only rendered when a section has more than one page, so most cards never
   show it at all. */
.dash-pager { display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem;
              padding-top: 0.5rem; font-size: 0.78rem; }

/* Share picker: workspaces and people side by side, each a plain tick list. Two columns on a
   wide screen, stacked below — a <select multiple> hides the current choice behind a scroll and
   is close to unusable on a phone, which is where a lot of this gets configured. */
.share-picker { display: flex; flex-wrap: wrap; gap: 1rem 2rem; margin-top: 0.25rem; }
.share-picker-group { min-width: 12rem; flex: 1 1 12rem; }
.share-picker-label {
    display: block; font-size: 0.72rem; font-weight: 700; letter-spacing: 0.04em;
    text-transform: uppercase; color: var(--color-text-muted); margin-bottom: 0.2rem;
}

/* Template family cards. Reuses .wr-cards for the grid so the two "things you configure" pages
   read the same way; the only additions are the version control and the upstream tint. */
.tpl-card { display: flex; flex-direction: column; gap: 0.5rem; }
/* Someone else's: recessive, because the useful action on it is "make my own copy". */
.tpl-card.tpl-upstream { background: var(--color-surface-2, var(--color-surface)); }
.tpl-card-version { display: flex; align-items: center; gap: 0.5rem; }
.tpl-card-version .field-label { margin: 0; white-space: nowrap; font-size: 0.78rem; }
.tpl-card-version .field-input { flex: 1 1 auto; min-width: 0; }

/* One card per form: name and status at a glance, the link and its QR right on the card. */
.wr-cards {
    display: grid;
    /* min(330px, 100%), not a bare 330px: minmax()'s minimum is a hard floor, so on a phone the
       content column is ~310px and the track stayed 330px — every card hung 20px past the edge
       and the page scrolled sideways. Wrapping the floor in min() lets the single column shrink
       to the container when the container is the smaller of the two, and changes nothing on any
       width that could actually fit 330px. */
    grid-template-columns: repeat(auto-fill, minmax(min(330px, 100%), 1fr));
    gap: 1rem;
}

.wr-card {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 1.1rem 1.2rem;
    min-width: 0;
}

.wr-card-head {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    flex-wrap: wrap;
}

.wr-card-title {
    font-weight: 600;
    font-size: 0.95rem;
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
}

.wr-card-meta { font-size: 0.8rem; }

.wr-card-desc {
    font-size: 0.82rem;
    color: var(--color-text-muted);
    margin: 0;
}

.wr-linkrow {
    display: flex;
    gap: 0.4rem;
    align-items: center;
}

.wr-linkrow .field-input {
    flex: 1;
    min-width: 0;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 0.75rem;
    color: var(--color-text-muted);
}

.wr-linkrow .btn-sm { flex: 0 0 auto; white-space: nowrap; }

.wr-card-actions {
    display: flex;
    gap: 0.3rem;
    flex-wrap: wrap;
    margin-top: auto;
    border-top: 1px solid var(--color-border-2);
    padding-top: 0.6rem;
}

/* Always on a white tile: a QR on a dark-theme surface can fail to scan. */
.wr-qr { text-align: center; padding: 0.3rem 0; }
.wr-qr img {
    width: 200px;
    max-width: 100%;
    background: #fff;
    padding: 10px;
    border-radius: var(--radius);
    border: 1px solid var(--color-border-2);
}

.wr-page-note { margin-top: 1rem; }

/* ---- Waiting room: the patient-facing page --------------------------- */

/* Reached on a phone as often as a desk, by someone who has never seen the app: a wide
   readable card, big touch targets, questions grouped under their section headings. */
.wr-public { max-width: 46rem; }

/* The login shell centers vertically, which clips the top of anything taller than the
   viewport — a 50-question intake sheet very much is. */
.auth-layout:has(.wr-public) {
    align-items: flex-start;
    padding: 1.5rem 1rem 4rem;
}

.wr-section-title {
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--color-primary);
    border-bottom: 1px solid var(--color-border-2);
    padding-bottom: 0.35rem;
    margin: 1.4rem 0 0.9rem;
}

.wr-fields {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.9rem 1rem;
}

.wr-fields .field { margin-bottom: 0; grid-column: 1 / -1; }
.wr-fields .wr-field-half { grid-column: auto / span 1; }

@media (max-width: 560px) {
    .wr-fields { grid-template-columns: 1fr; }
    .wr-fields .wr-field-half { grid-column: 1 / -1; }
}

/* Finger-sized inputs: this page is filled standing up, on a tablet at reception. */
.wr-public .field-input {
    padding: 11px 14px;
    font-size: 1rem;
}

.wr-public .field-label { font-size: 0.875rem; }

.wr-submit {
    width: 100%;
    padding: 13px;
    font-size: 1.05rem;
    margin-top: 1.4rem;
}

.wr-note {
    margin-top: 0.9rem;
    font-size: 0.8rem;
    text-align: center;
}

/* Stacked, not side-by-side: each stays a full-width, thumb-sized target on a phone. */
.wr-choice-buttons { display: flex; flex-direction: column; gap: 0.6rem; margin-top: 1rem; }
.wr-choice-buttons .wr-submit { margin-top: 0; }

.wr-done { text-align: center; padding: 1.5rem 0; }
.wr-done-mark {
    width: 64px;
    height: 64px;
    margin: 0 auto 1rem;
    border-radius: 50%;
    background: var(--success-bg);
    color: var(--success-fg);
    font-size: 2rem;
    line-height: 64px;
    text-align: center;
}

/* ---- Public booking page ----------------------------------------------- */

.bp-public { max-width: 46rem; }

.auth-layout:has(.bp-public) {
    align-items: flex-start;
    padding: 1.5rem 1rem 4rem;
}

.bp-header { display: flex; gap: 1rem; align-items: center; margin-bottom: 1rem; }
.bp-avatar {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    object-fit: cover;
    flex: 0 0 auto;
    background: var(--color-surface-2);
}

/* Days across the top, buttons underneath — a public "pick a free half-hour" picker,
   deliberately not TimeGrid: no department chips, no click-to-edit, no auth. */
.sp-days {
    display: flex;
    gap: 0.4rem;
    overflow-x: auto;
    padding-bottom: 0.3rem;
    margin: 1rem 0;
}

.sp-day {
    flex: 0 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.15rem;
    min-width: 52px;
    padding: 0.5rem 0.3rem;
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius);
    background: var(--color-surface);
    cursor: pointer;
}

.sp-day-active {
    border-color: var(--color-primary);
    background: var(--primary-tint);
}

.sp-day-name { font-size: 0.72rem; text-transform: uppercase; color: var(--color-text-muted); }
.sp-day-num { font-size: 1.05rem; font-weight: 600; }

.sp-slots {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(88px, 1fr));
    gap: 0.5rem;
}

.sp-slot {
    padding: 0.55rem 0.4rem;
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius);
    background: var(--color-surface);
    font-size: 0.9rem;
    cursor: pointer;
}

.sp-slot-active {
    border-color: var(--color-primary);
    background: var(--color-primary);
    color: var(--color-primary-contrast);
}

/* ---- Saved text page -------------------------------------------------- */

.snippet-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.snippet-card {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: var(--color-surface);
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: 1rem 1.1rem;
    min-width: 0;
}

.snippet-card-head {
    display: flex;
    align-items: center;
    gap: 0.45rem;
    flex-wrap: wrap;
}

/* The prose block itself, kept short — the card is a picker entry, not a reading pane. */
.snippet-card-body {
    font-size: 0.82rem;
    color: var(--color-text-muted);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.snippet-card-actions {
    display: flex;
    gap: 0.3rem;
    margin-top: auto;
    border-top: 1px solid var(--color-border-2);
    padding-top: 0.5rem;
}

/* ---- Guided tour ------------------------------------------------------ */

/* The shade is one element whose box-shadow floods everything around the spotlight hole,
   so the highlighted control stays fully visible and clickable-looking without cloning it. */
.tour-spotlight {
    position: fixed;
    z-index: 300;
    border-radius: 10px;
    box-shadow: 0 0 0 9999px color-mix(in srgb, #000 55%, transparent);
    pointer-events: none;
    transition: top .25s ease, left .25s ease, width .25s ease, height .25s ease;
}

/* Full-page shade for steps with no target (welcome / closing). */
.tour-shade {
    position: fixed;
    inset: 0;
    z-index: 300;
    background: color-mix(in srgb, #000 55%, transparent);
}

.tour-tip {
    position: fixed;
    z-index: 301;
    width: 320px;
    max-width: calc(100vw - 2rem);
    background: var(--color-surface);
    color: var(--color-text);
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 1rem 1.1rem;
}

.tour-tip h3 {
    font-size: 0.95rem;
    font-weight: 700;
    margin: 0 0 0.4rem;
}

.tour-tip p {
    font-size: 0.84rem;
    color: var(--color-text-muted);
    margin: 0 0 0.8rem;
}

.tour-tip-footer {
    display: flex;
    align-items: center;
    gap: 0.4rem;
}

/* The ?-button menu: every subject the tour can teach, grouped. Wider than a step card because
   it is a list of choices rather than one paragraph, and capped in height so a long menu scrolls
   inside the dialog instead of running off the bottom of a laptop screen. */
.tour-tip-menu {
    width: 420px;
    max-height: min(78vh, 620px);
    display: flex;
    flex-direction: column;
}

.tour-menu {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    overflow-y: auto;
    margin-bottom: 0.8rem;
    /* Room for the scrollbar so it never sits on top of a choice's focus ring. */
    padding-inline-end: 0.25rem;
}

.tour-menu-group {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    margin-top: 0.6rem;
}

.tour-menu-group:first-child { margin-top: 0; }

/* Ticked once the user has walked that subject to the end — the menu doubles as a syllabus. */
.tour-learned {
    color: var(--success-fg, var(--color-primary));
    margin-inline-end: 4px;
    font-weight: 700;
}

.tour-choices { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.8rem; }
.tour-choice {
    text-align: start;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    background: var(--color-surface);
    padding: 0.6rem 0.8rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.tour-choice strong { font-size: 0.9rem; color: var(--color-text); }
.tour-choice span { font-size: 0.78rem; color: var(--color-text-muted); }
.tour-choice:hover { border-color: var(--color-primary); background: var(--primary-tint-weak); }

.tour-dots {
    display: flex;
    gap: 4px;
    flex: 1;
}

.tour-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--color-border-strong);
}

.tour-dot.active { background: var(--color-primary); }

/* Round help button in the topbar, styled like its btn-sm neighbours but unmistakably "help". */
.topbar-help {
    font-weight: 700;
}

/* ---- Get-started checklist -------------------------------------------- */

.getstarted-card { margin-bottom: 1rem; }

/* The card's own title sits in a panel-header-row, so it must not also carry the underline and
   spacing .admin-card-title normally supplies on its own. */
.getstarted-title { margin: 0; border: 0; padding: 0; }

.getstarted-progress { flex: 0 0 auto; margin-bottom: 0.5rem; }
.getstarted-count { font-size: 0.8rem; margin: 0 0 0.5rem; }
.getstarted-blurb { font-size: 0.78rem; margin-inline-start: 0.4rem; }

.getstarted-item { gap: 0.5rem; }
.getstarted-tick { font-weight: 700; color: var(--color-text-muted); }
.getstarted-item.done .getstarted-tick { color: var(--success-fg, var(--color-primary)); }

/* Finished lines stay visible rather than disappearing — the list is also a record of what you
   have already done, which is reassuring on day one. */
.getstarted-item.done .dash-title { text-decoration: line-through; color: var(--color-text-muted); }

.getstarted-actions { display: flex; gap: 0.5rem; white-space: nowrap; }

/* Honeypot: physically off-screen (display:none would tip off smarter bots). */
.hp-field {
    position: absolute;
    inset-inline-start: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Minimal-vs-detailed choice on the register page: two selectable cards, radio hidden. */
.style-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 0.5rem; }
.style-card {
    position: relative;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: 0.6rem 0.7rem;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 0.78rem;
    color: var(--color-text-muted);
}
.style-card strong { color: var(--color-text); font-size: 0.85rem; }
.style-card input { position: absolute; opacity: 0; pointer-events: none; }
.style-card.selected {
    border-color: var(--color-primary);
    background: var(--primary-tint-weak);
    box-shadow: 0 0 0 1px var(--color-primary);
}

/* Subscription plan picker on the register page — same selectable-card mechanics as
   .style-cards, but 2-3 plans need more room than a fixed two-column grid. */
.plan-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 0.5rem; }
.plan-card .plan-price { color: var(--color-primary); font-weight: 600; font-size: 0.82rem; }
.plan-card .plan-features { font-size: 0.72rem; line-height: 1.4; display: flex; flex-direction: column; gap: 1px; margin-top: 0.25rem; }
/* One gated feature per row, ✓ when the plan includes it, — (dimmed) when it doesn't — the
   whole point is that what a plan lacks is as visible as what it has. */
.plan-feat { display: flex; align-items: baseline; gap: 0.35rem; }
.plan-feat::before { content: "✓"; color: var(--color-primary); font-weight: 700; flex-shrink: 0; }
.plan-feat.no { opacity: 0.55; }
.plan-feat.no::before { content: "—"; color: var(--color-text-muted); font-weight: 400; }
.plan-feat em { font-style: normal; color: var(--color-primary); white-space: nowrap; }
/* A caveat on the row above (e.g. "reminders" meaning email only) — too long to fit as a
   same-line <em> hint like the add-on prices above, so it gets its own wrapped line instead. */
.plan-subnote { display: block; margin: -1px 0 0 1.1rem; }
.plan-flag {
    display: inline-block;
    margin-inline-start: 0.35rem;
    padding: 0 0.35rem;
    border-radius: 999px;
    background: var(--color-primary);
    color: #fff;
    font-size: 0.62rem;
    font-weight: 600;
    vertical-align: middle;
}

/* Plain checkbox rows (contact preferences, trial toggle) — unlike .style-card, the box stays
   visible since these are independent toggles, not a mutually-exclusive choice. */
.checkbox-group { display: flex; flex-wrap: wrap; gap: 0.9rem; }
.checkbox-label {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    cursor: pointer;
}
.checkbox-label input[type="checkbox"] { width: auto; }

/* "New here? Create an account" under the login form. */
.auth-register-hint {
    margin-top: 1rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--color-border-2);
    text-align: center;
    font-size: 0.85rem;
    color: var(--color-text-muted);
}

/* ---- Odontogram -------------------------------------------------------- */

/* Chart fill palette. Service.ChartColor stores the bare key; the SVG fills with
   var(--chart-<key>, <key>), so unknown keys still work as plain CSS colors and these
   tokens exist to tune the known ones per theme. */
:root {
    --chart-grey: #9ca3af;
    --chart-white: #f3f4f6;
    --chart-black: #374151;
    --chart-red: #dc2626;
    --chart-yellow: #f59e0b;
    --chart-blue: #3b82f6;
    --chart-green: #16a34a;
    --chart-orange: #ea580c;
    --chart-purple: #9333ea;
    --chart-brown: #92400e;
    --chart-pink: #ec4899;
}

[data-theme="dark"] {
    --chart-white: #e5e7eb;
    --chart-black: #6b7280;
}

.og-panel .panel-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.og-controls { display: flex; gap: 0.3rem; }

.og-procedure-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
    margin: 0.6rem 0 0.2rem;
}

.og-procedure-row .field-label { margin: 0; }
.og-procedure-row select { max-width: 22rem; }
.og-procedure-row .field-hint { flex-basis: 100%; }

.og-swatch {
    display: inline-block;
    width: 18px;
    height: 18px;
    border-radius: 4px;
    border: 1px solid var(--color-border-strong);
    flex: 0 0 auto;
}

.og-color-row { display: flex; align-items: center; gap: 0.4rem; }

.og-filter-row { display: flex; gap: 0.3rem; flex-wrap: wrap; margin: 0.5rem 0; }

.og-chip {
    border: 1px solid var(--color-border-strong);
    background: var(--color-surface);
    color: var(--color-text-muted);
    border-radius: var(--radius-pill);
    padding: 3px 12px;
    font-size: 0.78rem;
    cursor: pointer;
}

.og-chip.active {
    background: var(--primary-tint);
    border-color: var(--color-primary);
    color: var(--color-primary-dark);
}

/* The chart is fixed-proportion; on a narrow screen it scrolls in its own box rather than
   squeezing the teeth into unclickable slivers. */
.og-chart-scroll { overflow-x: auto; padding: 0.4rem 0; }

.og-chart {
    display: block;
    min-width: 640px;
    max-width: 900px;
    width: 100%;
    margin: 0 auto;
}

.og-midline { stroke: var(--color-border-2); stroke-width: 1; stroke-dasharray: 4 4; }

.og-surface-region {
    fill: var(--color-bg);
    stroke: var(--color-border-strong);
    stroke-width: 1;
    cursor: pointer;
    transition: opacity .1s;
}

.og-surface-region:hover { opacity: 0.7; stroke: var(--color-primary); stroke-width: 1.5; }

/* Planned work is an outline of intent, not a fact — dashed and translucent until done. */
.og-surface-region.planned { opacity: 0.65; stroke-dasharray: 3 2; }

.og-surface-region.selected { stroke: var(--color-primary); stroke-width: 2; }

.og-whole { stroke: none; opacity: 0.4; }
.og-whole.planned { opacity: 0.25; }

.og-label {
    font-size: 12px;
    font-weight: 600;
    fill: var(--color-text);
    cursor: pointer;
}

.og-label:hover, .og-label.selected { fill: var(--color-primary); }

.og-glyphs {
    font-size: 12px;
    font-weight: 700;
    fill: var(--danger-fg);
    pointer-events: none;
}

.og-table { margin-top: 0.6rem; }
.og-table .og-status { max-width: 9.5rem; }
.og-table .og-priority { max-width: 4.5rem; }
.og-table .og-remarks .field-input { min-width: 10rem; }

/* ---- Inline field customization (the iPhone model) --------------------- */

.field-edit-banner {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    border-left: 4px solid var(--color-primary);
}
.field-edit-banner .text-muted { font-size: 0.82rem; }

/* A field being customized: dashed outline, room for its chrome, inert content. */
.field.field-editing {
    position: relative;
    border: 1px dashed var(--color-border-strong);
    border-radius: var(--radius);
    padding: 20px 8px 8px;
    cursor: grab;
    background: var(--color-surface);
}
.field.field-editing:active { cursor: grabbing; }

/* The form itself is frozen while editing — clicks drag and remove, they don't type. */
.field-editing .field-frozen { pointer-events: none; opacity: 0.75; }

.field-remove {
    position: absolute;
    top: -9px;
    inset-inline-start: -9px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: none;
    background: var(--danger-fg);
    color: #fff;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
    box-shadow: var(--shadow);
    z-index: 5;
}
.field-remove:hover { transform: scale(1.12); }

.field-edit-tools {
    position: absolute;
    top: 2px;
    inset-inline-end: 4px;
    display: flex;
    align-items: center;
    gap: 4px;
    z-index: 5;
}
.field-width-picker { display: inline-flex; border: 1px solid var(--color-border); border-radius: var(--radius-sm); overflow: hidden; }
.field-width-picker button {
    border: none;
    background: var(--color-surface);
    color: var(--color-text-muted);
    font-size: 0.68rem;
    padding: 1px 6px;
    cursor: pointer;
}
.field-width-picker button.active { background: var(--primary-tint); color: var(--color-primary-dark); font-weight: 700; }
.field-move {
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text-muted);
    border-radius: var(--radius-sm);
    font-size: 0.7rem;
    padding: 1px 5px;
    cursor: pointer;
}

/* The "add a field" tile at the end of the grid, and the buffer it opens. */
.field-add-tile {
    grid-column: span 3;
    min-height: 52px;
    border: 1px dashed var(--color-primary);
    border-radius: var(--radius);
    background: var(--primary-tint-weak);
    color: var(--color-primary-dark);
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
}
.field-buffer {
    margin-top: 0.6rem;
    padding: 0.6rem 0.7rem;
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius);
    background: var(--color-bg);
}
.field-buffer > .field-input { max-width: 16rem; margin-bottom: 0.5rem; }
.field-buffer-list { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.field-buffer-item {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    border: 1px dashed var(--color-border-strong);
    background: var(--color-surface);
    border-radius: var(--radius-pill);
    padding: 4px 12px;
    font-size: 0.8rem;
    cursor: pointer;
}
.field-buffer-item:hover { border-color: var(--color-primary); color: var(--color-primary); }
.field-buffer-name { font-weight: 500; }
.field-buffer-section { font-size: 0.7rem; color: var(--color-text-muted); }
.field-buffer-plus { color: var(--color-primary); font-weight: 700; }

/* ---- Growth chart ------------------------------------------------------ */

.growth-panel .panel-header-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    flex-wrap: wrap;
}

.growth-controls { display: flex; gap: 0.4rem; }
.growth-controls .field-input { width: auto; }

.growth-latest { font-size: 0.85rem; color: var(--color-text-muted); margin: 0.4rem 0 0.2rem; }

.growth-chart-scroll { overflow-x: auto; }
.growth-chart { display: block; min-width: 640px; width: 100%; max-width: 980px; margin: 0 auto; }

/* Reference curves are context, not data: recessive strokes, quiet grid, muted labels.
   The patient's series is the only thing in the primary color. */
.growth-grid { stroke: var(--color-border-2); stroke-width: 0.7; }
.growth-tick { font-size: 10px; fill: var(--color-text-muted); }
.growth-axis-label { font-size: 11px; fill: var(--color-text-muted); }

.growth-ref { fill: none; stroke: var(--color-border-strong); stroke-width: 1.2; }
.growth-ref.median { stroke-width: 1.8; }
.growth-ref-label { font-size: 10px; font-weight: 600; fill: var(--color-text-muted); }

.growth-series { fill: none; stroke: var(--color-primary); stroke-width: 2; }
.growth-point { fill: var(--color-primary); stroke: var(--color-surface); stroke-width: 1.5; }

/* Trends: series "a" is the primary; "b" is the paired series (diastolic, left eye). */
.trend-series { fill: none; stroke-width: 2; }
.trend-point { stroke: var(--color-surface); stroke-width: 1.5; }
.trend-series.a { stroke: var(--color-primary); }
.trend-point.a { fill: var(--color-primary); }
.trend-series.b { stroke: var(--chart-orange); }
.trend-point.b { fill: var(--chart-orange); }

/* Reference geometry: the healthy band sits behind everything; guides are quiet dashes. */
.trend-band { fill: var(--success-bg); opacity: 0.55; }
.trend-guide { stroke: var(--color-border-strong); stroke-width: 1; stroke-dasharray: 5 4; }

.trend-legend { display: flex; gap: 1rem; margin: 0.4rem 0 0; font-size: 0.8rem; color: var(--color-text-muted); }
.trend-legend-item { display: inline-flex; align-items: center; gap: 0.35rem; }
.trend-chip { width: 10px; height: 10px; border-radius: 3px; display: inline-block; }
.trend-chip.a { background: var(--color-primary); }
.trend-chip.b { background: var(--chart-orange); }

/* ---- On-visit calculations -------------------------------------------- */

.calc-card .panel-header-row { display: flex; align-items: baseline; justify-content: space-between; gap: 0.6rem; flex-wrap: wrap; }

.calc-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: 0.8rem 1.2rem;
    margin-top: 0.6rem;
}

.calc-item { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.calc-value { font-size: 1.15rem; font-weight: 700; color: var(--color-text); }
.calc-label { font-size: 0.75rem; color: var(--color-text-muted); }
.calc-activity {
    font-size: 0.75rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text-muted);
    padding: 1px 4px;
    max-width: 100%;
}

/* ── Template builder: one card per field ───────────────────────────────────
   A field carried up to thirteen table cells once alerts and tri-state arrived,
   which no amount of column-hiding made readable. Same shape as .timeline-entry:
   a narrow gutter, a body, and a muted summary line — except the summary is the
   button that opens the editor, so a twenty-field template reads as a list of
   labels and only the field being worked on is expanded. */
.field-cards { display: flex; flex-direction: column; gap: 0.45rem; }
.field-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border-2);
    border-radius: var(--radius-lg);
    transition: border-color .18s, box-shadow .18s;
}
.field-card:hover { border-color: var(--color-border); }
.field-card.open { border-color: var(--color-primary); box-shadow: var(--shadow-sm); }

.field-card-head { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem 0.65rem; }
/* Position and reorder, mirroring .timeline-date's fixed gutter so the label
   inputs line up down the list however wide the numbers get. */
.field-card-pos { display: flex; align-items: center; gap: 0.15rem; min-width: 3.6rem; }
.field-card-num {
    font-size: 0.78rem; font-weight: 700; color: var(--color-text-faint);
    min-width: 1.3rem; text-align: right;
}
/* Label takes the room; type stays just wide enough to read. */
.field-card-head .field-card-label { flex: 1 1 auto; min-width: 6rem; }
.field-card-head .field-card-type { flex: 0 0 8.5rem; }

/* The summary doubles as the open control, so the label input above stays
   clickable — making the whole card a button would steal focus mid-rename. */
.field-card-summary {
    display: block; width: 100%; text-align: start;
    background: none; border: none; cursor: pointer;
    padding: 0 0.65rem 0.5rem 4.75rem;
    font-size: 0.78rem; color: var(--color-text-faint);
}
.field-card-summary:hover { color: var(--color-primary); }
.field-card-summary .field-card-none { font-style: italic; }

.field-card-body {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.9rem;
    padding: 0.1rem 0.65rem 0.65rem 4.75rem;
    border-top: 1px solid var(--color-border-2); margin-top: 0.1rem; padding-top: 0.6rem;
}
.field-card-body label { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.8rem; }
.field-card-body .field-input { width: auto; min-width: 7rem; }
.field-card-hint { font-size: 0.78rem; color: var(--color-text-faint); }

@media (max-width: 640px) {
    /* The gutter indent costs more than it gives once the card is this narrow. */
    .field-card-summary, .field-card-body { padding-left: 0.65rem; }
    .field-card-head { flex-wrap: wrap; }
    .field-card-head .field-card-type { flex: 1 1 100%; }
}

/* ── Template builder list toolbar ──────────────────────────────────────── */
.template-list-toolbar {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
    flex-wrap: wrap;
}
.seg-control {
    display: inline-flex;
    border: 1px solid var(--color-border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--color-surface);
}
.seg-option {
    border: none;
    background: transparent;
    color: var(--color-text-muted);
    font-size: 0.82rem;
    padding: 0.4rem 0.85rem;
    cursor: pointer;
}
.seg-option + .seg-option { border-left: 1px solid var(--color-border); }
.seg-option.active {
    background: var(--primary-tint-weak);
    color: var(--color-primary);
    font-weight: 600;
}
.template-search { max-width: 260px; }

/* Section heading inside a column/details dropdown menu */
.column-menu-heading {
    padding: 0.45rem 0.8rem 0.15rem;
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--color-text-muted);
    border-top: 1px solid var(--color-border);
    margin-top: 0.25rem;
}

/* Customize-mode bar above each togglable visit panel */
.panel-visibility-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.35rem 0.75rem;
    margin-bottom: 0.4rem;
    border: 1px dashed var(--color-border);
    border-radius: 8px;
    background: var(--primary-tint-weak);
    font-size: 0.82rem;
    font-weight: 600;
}
.rx-field-picker {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
    margin-bottom: 0.5rem;
    padding: 0.3rem 0.5rem;
    border: 1px dashed var(--color-border);
    border-radius: 8px;
}

/* ── Arrivals bell ──────────────────────────────────────────────────────── */
.arrivals-wrap { position: relative; display: inline-flex; }
.arrivals-bell { position: relative; display: inline-flex; align-items: center; }
.arrivals-count {
    position: absolute;
    top: -6px;
    inset-inline-end: -6px;
    background: var(--warn, #d97706);
    color: #fff;
    border-radius: 999px;
    font-size: 0.62rem;
    font-weight: 700;
    min-width: 15px;
    height: 15px;
    line-height: 15px;
    text-align: center;
    padding: 0 3px;
}
.arrivals-menu { min-width: 260px; }
.arrivals-row {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    padding: 0.3rem 0.8rem;
    font-size: 0.85rem;
}

/* ── Notification bell ─────────────────────────────────────────────────── */
.notif-wrap { position: relative; display: inline-flex; }
.notif-bell { position: relative; display: inline-flex; align-items: center; }
.notif-count {
    position: absolute;
    top: -6px;
    inset-inline-end: -6px;
    background: var(--danger, #dc2626);
    color: #fff;
    border-radius: 999px;
    font-size: 0.62rem;
    font-weight: 700;
    min-width: 15px;
    height: 15px;
    line-height: 15px;
    text-align: center;
    padding: 0 3px;
}
.notif-menu { min-width: 300px; max-width: 340px; max-height: 70vh; overflow-y: auto; }
.notif-row {
    display: block;
    padding: 0.5rem 0.8rem;
    font-size: 0.85rem;
    border-bottom: 1px solid var(--color-border);
    text-decoration: none;
    color: inherit;
}
.notif-row:last-child { border-bottom: none; }
.notif-row:hover { background: var(--color-bg); cursor: pointer; }
.notif-row.unread { background: var(--primary-tint-weak); }
.notif-row-title { font-weight: 600; display: flex; align-items: center; gap: 0.4rem; }
.notif-row-dot { width: 7px; height: 7px; border-radius: 999px; background: var(--danger, #dc2626); flex: none; }
.notif-row-message { color: var(--color-text-muted); margin-top: 0.15rem; }
.notif-row-time { color: var(--color-text-muted); font-size: 0.72rem; margin-top: 0.2rem; }

/* An arrived patient's row on the dashboard — someone is physically waiting */
.dash-item-waiting {
    background: var(--primary-tint-weak);
    border-radius: 8px;
}

/* ── Visit page: floating "On this page" navigator + panel tab bar ─────────────
   Fixed at the inline-end edge so it stays in view however far the visit scrolls;
   hidden on narrow screens where it would sit on top of the content. */
.visit-nav {
    position: fixed;
    inset-inline-end: 1rem;
    top: 50%;
    transform: translateY(-50%);
    width: 200px;
    max-height: 72vh;
    overflow-y: auto;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 0.55rem 0.55rem 0.65rem;
    z-index: 30; /* under drawers (41) and the toast */
    display: flex;
    flex-direction: column;
    gap: 1px;
}
.visit-nav-head {
    display: flex; align-items: center; justify-content: space-between;
    font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--color-text-muted);
    padding: 0.15rem 0.35rem 0.4rem;
}
.visit-nav-close {
    border: none; background: none; cursor: pointer; color: var(--color-text-muted);
    font-size: 0.75rem; line-height: 1; padding: 0.15rem 0.3rem; border-radius: 6px;
}
.visit-nav-close:hover { color: var(--color-text); background: var(--primary-tint-weak); }
.visit-nav-item {
    border: none; background: none; cursor: pointer; text-align: start;
    font-size: 0.82rem; color: var(--color-text);
    padding: 0.28rem 0.35rem; border-radius: 6px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.visit-nav-item:hover { background: var(--primary-tint-weak); color: var(--color-primary); }
.visit-nav-sub { padding-inline-start: 1rem; font-size: 0.78rem; }
.visit-nav-group {
    font-size: 0.7rem; font-weight: 600; color: var(--color-text-muted);
    padding: 0.35rem 0.35rem 0.1rem; text-transform: uppercase; letter-spacing: 0.03em;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.visit-nav-sep { border-top: 1px solid var(--color-border); margin: 0.35rem 0.2rem; }

/* What the visit still needs, parked at the foot of the navigator. Sticky rather than merely last:
   .visit-nav is capped at 72vh and scrolls, so on a visit with many sections a plain bottom group
   would sit below the fold — which is exactly what moving it here was meant to stop. */
.visit-nav-warngroup {
    position: sticky;
    bottom: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin: 0.35rem -0.55rem -0.65rem;   /* bleed to the panel's edges */
    padding: 0.35rem 0.55rem 0.5rem;
    border-top: 1px solid var(--color-border);
    background: var(--color-surface);     /* opaque: rows scroll underneath it */
}
/* Both variants keep .visit-nav-item's ellipsis clipping — a long field name costs no width. */
.visit-nav-warn { background: var(--warn-bg); color: var(--warn-fg); font-weight: 600; }
.visit-nav-warn:hover:not(:disabled) { background: var(--warn-border); color: var(--warn-fg); }
.visit-nav-match { background: var(--success-bg); color: var(--success-fg); font-weight: 600; }
.visit-nav-match:hover { background: var(--success-border); color: var(--success-fg); }
/* Facts on file, not fields to fill: nothing to jump to, so nothing that looks clickable. */
.visit-nav-static { cursor: default; }
.visit-nav-warn:disabled { opacity: 0.6; cursor: not-allowed; }
.visit-nav-fab {
    position: fixed;
    inset-inline-end: 1rem;
    top: 50%;
    transform: translateY(-50%);
    width: 2.3rem; height: 2.3rem; border-radius: 50%;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    box-shadow: var(--shadow-lg);
    cursor: pointer; color: var(--color-text);
    z-index: 30;
}
/* The only trace of an outstanding answer once the panel is closed. Deliberately a dot and not a
   count: the number is in the panel, this just has to say "there is something". */
.visit-nav-fab-dot {
    position: absolute; top: 3px; inset-inline-end: 3px;
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--warn-fg); border: 1px solid var(--color-surface);
}
.visit-nav-fab:hover { background: var(--primary-tint-weak); color: var(--color-primary); }
/* Below this width there is no room for a reserved column, so the navigator stops being a
   persistent panel and becomes the floating button — tapped open, and closed again by picking a
   section (see ScrollToAsync). Hiding it outright, as this used to, left a phone with no way to
   move between sections at all, and no sight of what the visit still needs. */
@media (max-width: 1280px) {
    .visit-nav {
        top: auto;
        bottom: 4.5rem;                  /* clear of the button that opened it */
        transform: none;
        max-height: 60vh;
        width: min(17rem, calc(100vw - 2rem));
    }
    /* Out of the vertical middle. Centred, it floats over whatever happens to be halfway down the
       page — on a phone that was a section's own Save button, covered by a button that only opens
       a menu. The corner is where a floating action belongs and collides with nothing. */
    .visit-nav-fab { top: auto; bottom: 1rem; transform: none; }
}
/* Reserve the nav's column so it never sits on top of a card's own buttons — the fixed
   element is out of flow, so the content body cedes the space instead. */
@media (min-width: 1281px) {
    .content-body:has(.visit-nav) { padding-inline-end: 230px; }
}
.visit-panel-tabs { margin: 1rem 0 0.6rem; scroll-margin-top: 5rem; }
