/* =============================================================================
   Sunshine Intake — design system
   -----------------------------------------------------------------------------
   Hand-written, dependency-free CSS. No framework and no CDN, deliberately:
     - A strict Content-Security-Policy with no third-party origins is only
       possible if nothing is fetched from one.
     - PHI-handling software should not load styling from an external host that
       could be compromised or that logs referrers.
     - A licensed on-premise install must work with no build step and no network.

   Theming: the two brand colors are injected per tenant as --brand-* custom
   properties by the layout (inside a nonce'd <style>), so re-skinning for a new
   clinic is a settings change, not a stylesheet edit.

   Dark mode is a selected set of values, not an inversion: each token has its own
   dark step chosen against the dark surface.
   ========================================================================== */

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

:root {
    color-scheme: light;

    /* Brand defaults. The layout overrides --brand-primary / --brand-accent from
       tenant settings; everything else derives from them. */
    --brand-primary: #2563eb;
    --brand-accent: #0d9488;

    /* Surfaces */
    --page: #f7f8fa;
    --surface: #ffffff;
    --surface-2: #f2f4f7;
    --surface-3: #e8ebef;
    --surface-inverse: #12151b;

    /* Ink */
    --ink: #10141b;
    --ink-secondary: #4a5261;
    --ink-muted: #6f7887;
    --ink-inverse: #ffffff;

    /* Lines */
    --line: #dfe3e9;
    --line-strong: #c6ccd6;
    --focus-ring: color-mix(in oklab, var(--brand-primary) 55%, white);

    /* Status — fixed, never themed. ALWAYS paired with an icon and a text label:
       red/green are indistinguishable under deuteranopia (measured ΔE 4.1), so
       color alone can never be the channel. */
    --status-good: #0ca30c;
    --status-good-bg: #e8f7e8;
    --status-warning: #b06f00;
    --status-warning-mark: #fab219;
    --status-warning-bg: #fdf4e3;
    --status-critical: #d03b3b;
    --status-critical-bg: #fdecec;
    --status-info: #2563eb;
    --status-info-bg: #eaf1fe;

    /* Chart data marks. A fixed, validated blue — deliberately NOT the tenant's
       brand color: an arbitrary admin-chosen hex cannot be checked for
       lightness band, chroma and CVD separation, and data encoding is the one
       place where "on brand" must lose to "readable". Branding drives chrome. */
    --viz-series: #2a78d6;
    --viz-ordinal-1: #86b6ef;
    --viz-ordinal-2: #3987e5;
    --viz-ordinal-3: #256abf;
    --viz-ordinal-4: #0d366b;
    --viz-grid: #e6e9ee;
    --viz-axis: #c6ccd6;
    --viz-track: #eef1f5;

    /* Shape & depth */
    --radius-sm: 6px;
    --radius: 10px;
    --radius-lg: 14px;
    --shadow-sm: 0 1px 2px rgba(16, 20, 27, 0.06);
    --shadow: 0 1px 3px rgba(16, 20, 27, 0.08), 0 4px 12px rgba(16, 20, 27, 0.05);
    --shadow-lg: 0 8px 32px rgba(16, 20, 27, 0.14);

    /* Type */
    --font: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    --sidebar-w: 244px;
    --topbar-h: 56px;
}

/* Dark mode: OS preference, unless the user has explicitly chosen light. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        color-scheme: dark;

        --page: #0d0f13;
        --surface: #171a1f;
        --surface-2: #1f232a;
        --surface-3: #272c34;
        --surface-inverse: #f2f4f7;

        --ink: #f4f6f8;
        --ink-secondary: #b3bcc9;
        --ink-muted: #8b95a3;
        --ink-inverse: #10141b;

        --line: #2b3038;
        --line-strong: #3a4149;

        --status-good: #4ec44e;
        --status-good-bg: #14261a;
        --status-warning: #f0b429;
        --status-warning-mark: #fab219;
        --status-warning-bg: #2a2113;
        --status-critical: #f07171;
        --status-critical-bg: #2c1719;
        --status-info: #6da7ec;
        --status-info-bg: #16202f;

        --viz-series: #3987e5;
        --viz-ordinal-1: #184f95;
        --viz-ordinal-2: #2a78d6;
        --viz-ordinal-3: #5598e7;
        --viz-ordinal-4: #9ec5f4;
        --viz-grid: #262b33;
        --viz-axis: #3a4149;
        --viz-track: #232830;

        --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
        --shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.35);
        --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.6);
    }
}

/* Explicit dark choice must win over an OS light preference. */
:root[data-theme="dark"] {
    color-scheme: dark;

    --page: #0d0f13;
    --surface: #171a1f;
    --surface-2: #1f232a;
    --surface-3: #272c34;
    --surface-inverse: #f2f4f7;

    --ink: #f4f6f8;
    --ink-secondary: #b3bcc9;
    --ink-muted: #8b95a3;
    --ink-inverse: #10141b;

    --line: #2b3038;
    --line-strong: #3a4149;

    --status-good: #4ec44e;
    --status-good-bg: #14261a;
    --status-warning: #f0b429;
    --status-warning-mark: #fab219;
    --status-warning-bg: #2a2113;
    --status-critical: #f07171;
    --status-critical-bg: #2c1719;
    --status-info: #6da7ec;
    --status-info-bg: #16202f;

    --viz-series: #3987e5;
    --viz-ordinal-1: #184f95;
    --viz-ordinal-2: #2a78d6;
    --viz-ordinal-3: #5598e7;
    --viz-ordinal-4: #9ec5f4;
    --viz-grid: #262b33;
    --viz-axis: #3a4149;
    --viz-track: #232830;

    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow: 0 1px 3px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.35);
    --shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.6);
}

/* ----------------------------------------------------------------- reset ---- */

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

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    font-family: var(--font);
    font-size: 15px;
    line-height: 1.55;
    color: var(--ink);
    background: var(--page);
    -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 { margin: 0; font-weight: 600; line-height: 1.3; letter-spacing: -0.011em; }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.125rem; }
h3 { font-size: 1rem; }
h4 { font-size: 0.875rem; }
p { margin: 0 0 0.75rem; }
p:last-child { margin-bottom: 0; }

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

hr { border: 0; border-top: 1px solid var(--line); margin: 1.25rem 0; }

code, pre, .mono { font-family: var(--font-mono); font-size: 0.85em; }

/* Visible focus for keyboard users, suppressed for mouse clicks. */
:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

.skip-link {
    position: absolute; left: 0.5rem; top: -3rem; z-index: 100;
    background: var(--surface); color: var(--ink);
    padding: 0.5rem 0.875rem; border-radius: var(--radius);
    box-shadow: var(--shadow); transition: top 0.15s;
}
.skip-link:focus { top: 0.5rem; }

/* ----------------------------------------------------------------- shell ---- */

.shell { display: flex; min-height: 100vh; }

.sidebar {
    width: var(--sidebar-w);
    flex: 0 0 var(--sidebar-w);
    background: var(--surface-inverse);
    color: rgba(255, 255, 255, 0.72);
    display: flex;
    flex-direction: column;
    position: sticky;
    top: 0;
    height: 100vh;
    overflow-y: auto;
}

/* A clinic that prefers a light chrome sets brand.sidebar_style = light. */
.sidebar[data-style="light"] {
    background: var(--surface);
    color: var(--ink-secondary);
    border-right: 1px solid var(--line);
}

.sidebar__brand {
    display: flex; align-items: center; gap: 0.625rem;
    padding: 1rem 1rem 0.875rem;
    font-weight: 600; font-size: 0.95rem;
    color: #fff;
    min-height: var(--topbar-h);
}
.sidebar[data-style="light"] .sidebar__brand { color: var(--ink); }

.sidebar__mark {
    width: 30px; height: 30px; flex: 0 0 30px;
    border-radius: 8px;
    background: var(--brand-primary);
    color: #fff;
    display: grid; place-items: center;
    font-size: 0.8rem; font-weight: 700; letter-spacing: -0.02em;
}
.sidebar__mark img { width: 100%; height: 100%; object-fit: contain; border-radius: 8px; }

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

.nav { padding: 0.5rem 0.5rem 1rem; display: flex; flex-direction: column; gap: 1px; }

.nav__heading {
    font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.07em;
    color: rgba(255, 255, 255, 0.38);
    padding: 1rem 0.625rem 0.375rem;
    font-weight: 600;
}
.sidebar[data-style="light"] .nav__heading { color: var(--ink-muted); }

.nav__link {
    display: flex; align-items: center; gap: 0.625rem;
    padding: 0.5rem 0.625rem;
    border-radius: var(--radius-sm);
    color: inherit;
    font-size: 0.875rem;
    font-weight: 500;
}
.nav__link:hover { background: rgba(255, 255, 255, 0.07); text-decoration: none; color: #fff; }
.sidebar[data-style="light"] .nav__link:hover { background: var(--surface-2); color: var(--ink); }

.nav__link[aria-current="page"] {
    background: rgba(255, 255, 255, 0.11);
    color: #fff;
    font-weight: 600;
}
.sidebar[data-style="light"] .nav__link[aria-current="page"] {
    background: color-mix(in oklab, var(--brand-primary) 12%, transparent);
    color: var(--brand-primary);
}

.nav__link svg { flex: 0 0 17px; width: 17px; height: 17px; opacity: 0.85; }

.nav__badge {
    margin-left: auto;
    background: var(--brand-primary); color: #fff;
    font-size: 0.6875rem; font-weight: 700;
    min-width: 19px; height: 19px; padding: 0 5px;
    border-radius: 999px;
    display: grid; place-items: center;
}

.sidebar__footer {
    margin-top: auto;
    padding: 0.75rem;
    border-top: 1px solid rgba(255, 255, 255, 0.09);
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.45);
}
.sidebar[data-style="light"] .sidebar__footer { border-color: var(--line); color: var(--ink-muted); }

.main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.topbar {
    height: var(--topbar-h);
    display: flex; align-items: center; gap: 0.75rem;
    padding: 0 1rem;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    position: sticky; top: 0; z-index: 20;
}

.topbar__title { font-size: 0.95rem; font-weight: 600; }
.topbar__spacer { flex: 1 1 auto; }

/* The quick patient lookup: the field and its magnifier as one control.
   `nowrap` is load-bearing, not tidiness. Every input in this stylesheet is
   width:100% so it fills a form row, and in a wrapping flex container that
   claims the whole line and pushes the submit button onto a second one — two
   rows inside a 56px topbar, which centred to a clipped field with a magnifier
   hanging below the header. Bonding them also means there is no gap for the
   two halves to drift apart in.

   Wide enough for a name and an MRN, never wide enough to crowd the account
   controls beside it. */
.topbar__search {
    display: flex; align-items: stretch; flex-wrap: nowrap;
    min-width: 150px; max-width: 240px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    /* Clips the input's and button's own corners to the wrapper's radius, so the
       seam between them cannot show. */
    overflow: hidden;
}

/* The focus ring belongs to the whole control, since the input is the only thing
   in it that takes focus but no longer carries a border of its own. Matches the
   outline the base field rule uses rather than inventing a second focus style. */
.topbar__search:focus-within {
    outline: 2px solid var(--focus-ring);
    border-color: var(--brand-primary);
}

.topbar__search input[type="search"] {
    /* min-width:0 so the field can shrink inside the flex row instead of forcing
       the control past its max-width. */
    width: 100%; min-width: 0;
    border: 0; border-radius: 0;
    background: transparent;
    min-height: 34px;
    padding: 0.3rem 0 0.3rem 0.55rem;
    font-size: 0.8125rem;
}
/* The wrapper shows the focus, so the field must not draw a second ring inside it. */
.topbar__search input[type="search"]:focus,
.topbar__search input[type="search"]:focus-visible {
    outline: 0;
    border-color: transparent;
}

.topbar__search button {
    display: inline-flex; align-items: center; justify-content: center;
    flex: 0 0 auto;
    padding: 0 0.5rem;
    border: 0;
    background: transparent;
    color: var(--ink-muted);
    cursor: pointer;
}
.topbar__search button:hover { background: var(--surface-2); color: var(--ink); }
.topbar__search button svg { width: 15px; height: 15px; }

.menu-toggle {
    display: none;
    background: none; border: 0; padding: 0.375rem;
    color: var(--ink); cursor: pointer; border-radius: var(--radius-sm);
}
.menu-toggle:hover { background: var(--surface-2); }

.content { padding: 1.25rem; max-width: 1500px; width: 100%; }

.page-head {
    display: flex; flex-wrap: wrap; align-items: flex-start; gap: 0.75rem;
    margin-bottom: 1.25rem;
}
.page-head__text { flex: 1 1 320px; min-width: 0; }
.page-head__sub { color: var(--ink-secondary); font-size: 0.875rem; margin-top: 0.25rem; }
.page-head__actions { display: flex; flex-wrap: wrap; gap: 0.5rem; }

/* A band label between groups of cards on one page — the app launcher's
   "Installed" and "Available" bands, and Settings' per-app section. Quieter than
   an <h2> inside a card, because it labels a region rather than titling a panel. */
.section-heading {
    font-size: 0.8125rem; font-weight: 600; letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink-muted);
    margin: 0 0 0.625rem;
}
.section-heading:not(:first-child) { margin-top: 1.75rem; }

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

.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
    padding: 0.5rem 0.875rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink);
    font: inherit; font-size: 0.875rem; font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
    transition: background 0.12s, border-color 0.12s;
}
.btn:hover { background: var(--surface-2); text-decoration: none; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }
.btn svg { width: 15px; height: 15px; }

.btn--primary {
    background: var(--brand-primary);
    border-color: var(--brand-primary);
    color: #fff;
}
.btn--primary:hover { background: color-mix(in oklab, var(--brand-primary) 88%, black); }

.btn--danger { background: var(--status-critical); border-color: var(--status-critical); color: #fff; }
.btn--danger:hover { background: color-mix(in oklab, var(--status-critical) 88%, black); }

.btn--ghost { background: transparent; border-color: transparent; }
.btn--ghost:hover { background: var(--surface-2); }

.btn--sm { padding: 0.3rem 0.55rem; font-size: 0.8125rem; }
.btn--block { width: 100%; }

.btn-group { display: inline-flex; gap: 0.375rem; flex-wrap: wrap; }

/* ----------------------------------------------------------------- cards ---- */

.card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow-sm);
}
.card + .card { margin-top: 1rem; }

.card__head {
    display: flex; align-items: center; gap: 0.625rem; flex-wrap: wrap;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--line);
}
.card__head h2, .card__head h3 { font-size: 0.9375rem; }
/* Direct child only: a button nested in a form or a span is not the thing being
   aligned — that wrapper takes .push instead, and two elements both claiming the
   free space is how a head ends up looking crooked. */
.card__head > .btn { margin-left: auto; }
.card__body { padding: 1rem; }
.card__body--flush { padding: 0; }
/* Two bodies in one card — a summary above, a table or a form below. */
.card__body--divided { border-bottom: 1px solid var(--line); }
.card__foot {
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--line);
    background: var(--surface-2);
    border-radius: 0 0 var(--radius) var(--radius);
    font-size: 0.875rem;
}

/* A card whose whole subject is destructive or resolved. The border is a second
   signal only — the heading and its status chip still say which it is. */
.card--danger { border-color: var(--status-critical); }
.card--good { border-color: var(--status-good); }

/* A bordered block *inside* a card body: one coverage record, one template. Not a
   nested card — no shadow, so the hierarchy stays one level deep. */
.panel {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 0.875rem;
}
.panel + .panel { margin-top: 0.75rem; }

/* A reading-width card for a single message (credentials handover, confirmation). */
.narrow { max-width: 640px; }

.grid { display: grid; gap: 1rem; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.grid--sidebar { grid-template-columns: minmax(0, 2fr) minmax(280px, 1fr); align-items: start; }

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

/* ------------------------------------------------------------ stat tiles ---- */

.stat {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: 0.875rem 1rem;
    box-shadow: var(--shadow-sm);
    display: flex; flex-direction: column; gap: 0.125rem;
    min-width: 0;
}
.stat--link:hover { border-color: var(--line-strong); text-decoration: none; }

.stat__label {
    font-size: 0.75rem; font-weight: 600;
    color: var(--ink-secondary);
    text-transform: uppercase; letter-spacing: 0.045em;
    display: flex; align-items: center; gap: 0.3rem;
}
/* Proportional figures: this is a standalone number, not a column. */
.stat__value { font-size: 1.75rem; font-weight: 600; letter-spacing: -0.02em; line-height: 1.15; }
/* For a value that is a pair or a fraction ("4/7") rather than one number. */
.stat__value--sm { font-size: 1.25rem; }
.stat__meta { font-size: 0.8125rem; color: var(--ink-muted); }
/* A chip standing in for the value, on its own line and optically centered. */
.stat__mark { margin: 0.3rem 0; }

.stat--good .stat__value { color: var(--status-good); }
.stat--warning .stat__value { color: var(--status-warning); }
.stat--critical .stat__value { color: var(--status-critical); }

/* Hero figure — exactly one per view. */
.hero-figure { font-size: 3rem; font-weight: 600; letter-spacing: -0.03em; line-height: 1; }

/* A single value that is the point of the block it sits in — a start date, a
   temporary password. Larger than body text, well short of a stat tile. */
.figure { font-size: 1.125rem; line-height: 1.35; }

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

.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }

table.data {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.875rem;
}
table.data th, table.data td {
    padding: 0.6rem 0.75rem;
    text-align: left;
    border-bottom: 1px solid var(--line);
    vertical-align: middle;
}
table.data thead th {
    font-size: 0.6875rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--ink-secondary);
    background: var(--surface-2);
    white-space: nowrap;
    position: sticky; top: 0; z-index: 1;
}
table.data thead th a { color: inherit; }
table.data tbody tr:hover { background: var(--surface-2); }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }
table.data td.nowrap { white-space: nowrap; }
table.data .row-link { color: var(--ink); font-weight: 600; }

/* Left edge carries the patient's attention state. Never the only signal —
   the row also shows a status chip with an icon and a word. */
table.data tr[data-flag="red"] td:first-child { box-shadow: inset 3px 0 0 var(--status-critical); }
table.data tr[data-flag="amber"] td:first-child { box-shadow: inset 3px 0 0 var(--status-warning-mark); }
table.data tr[data-flag="green"] td:first-child { box-shadow: inset 3px 0 0 transparent; }

.empty {
    padding: 2.5rem 1.25rem;
    text-align: center;
    color: var(--ink-muted);
}
.empty h3 { color: var(--ink); margin-bottom: 0.375rem; }
.empty svg { width: 34px; height: 34px; opacity: 0.35; margin-bottom: 0.625rem; }
/* For an empty state inside a card that already has a heading and a body padding
   of its own — the full 2.5rem would read as a gap, not as emptiness. */
.empty--tight { padding: 1.5rem 1rem; }

/* --------------------------------------------------- badges, chips, status --- */

.badge {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    font-size: 0.75rem; font-weight: 600;
    background: var(--surface-2);
    color: var(--ink-secondary);
    border: 1px solid var(--line);
    white-space: nowrap;
    max-width: 100%;
}
.badge__dot { width: 7px; height: 7px; border-radius: 999px; flex: 0 0 7px; background: currentColor; }

/* Status chip. The glyph is load-bearing accessibility, not decoration:
   red and green measure ΔE 4.1 apart under deuteranopia, so the shape and the
   word are what actually distinguish them. Never render one of these
   icon-less or label-less. */
.status {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.15rem 0.5rem;
    border-radius: 999px;
    font-size: 0.75rem; font-weight: 600;
    white-space: nowrap;
}
.status__glyph { font-size: 0.8em; line-height: 1; }
.status--green { background: var(--status-good-bg); color: var(--status-good); }
.status--amber { background: var(--status-warning-bg); color: var(--status-warning); }
.status--red { background: var(--status-critical-bg); color: var(--status-critical); }
.status--info { background: var(--status-info-bg); color: var(--status-info); }
.status--neutral { background: var(--surface-2); color: var(--ink-secondary); }

.chips { display: flex; flex-wrap: wrap; gap: 0.3rem; }

.chip {
    display: inline-flex; align-items: center; gap: 0.3rem;
    padding: 0.1rem 0.45rem;
    border-radius: var(--radius-sm);
    font-size: 0.7188rem; font-weight: 600;
    border: 1px solid var(--line);
    background: var(--surface-2);
    color: var(--ink-secondary);
    white-space: nowrap;
}

.phase-pill {
    display: inline-flex; align-items: center; gap: 0.375rem;
    font-size: 0.75rem; font-weight: 600;
    padding: 0.15rem 0.5rem 0.15rem 0.35rem;
    border-radius: 999px;
    background: var(--surface-2);
    border: 1px solid var(--line);
    color: var(--ink-secondary);
    white-space: nowrap;
}
.phase-pill__dot { width: 8px; height: 8px; border-radius: 999px; flex: 0 0 8px; }
.phase-pill__num {
    font-variant-numeric: tabular-nums;
    opacity: 0.65;
}

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

.form { display: grid; gap: 1rem; }
.form__grid { display: grid; gap: 0.875rem 1rem; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.form__row { display: grid; gap: 0.3rem; min-width: 0; }
.form__row--wide { grid-column: 1 / -1; }

.form__section {
    border-top: 1px solid var(--line);
    padding-top: 1rem;
    margin-top: 0.25rem;
}
.form__section:first-child { border-top: 0; padding-top: 0; margin-top: 0; }
.form__section-title {
    font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.06em; color: var(--ink-muted);
    margin-bottom: 0.625rem;
}

label { font-size: 0.8125rem; font-weight: 600; color: var(--ink-secondary); }
.label-hint { font-weight: 400; color: var(--ink-muted); }

/* Heads a group of controls rather than one control. Deliberately not a <label>:
   a label may only point at a single field, and a group of checkboxes needs
   role="group" + aria-labelledby to be announced as one thing. */
.field-label { font-size: 0.8125rem; font-weight: 600; color: var(--ink-secondary); }

input[type="text"], input[type="email"], input[type="password"], input[type="number"],
input[type="date"], input[type="datetime-local"], input[type="tel"], input[type="url"],
input[type="search"], input[type="color"], select, textarea {
    width: 100%;
    padding: 0.5rem 0.625rem;
    font: inherit; font-size: 0.875rem;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    min-height: 38px;
}
input[type="color"] { padding: 0.2rem; height: 38px; cursor: pointer; }
textarea { min-height: 92px; resize: vertical; line-height: 1.5; }

input:focus, select:focus, textarea:focus {
    outline: 2px solid var(--focus-ring);
    outline-offset: 0;
    border-color: var(--brand-primary);
}

input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] {
    border-color: var(--status-critical);
}

input::placeholder, textarea::placeholder { color: var(--ink-muted); }
input:disabled, select:disabled, textarea:disabled { background: var(--surface-2); color: var(--ink-muted); }

select[multiple] { min-height: 130px; padding: 0.25rem; }

/* Width by content, not by grid cell: a color swatch, a two-digit day count and
   a date all look wrong stretched to a 220px column. Three sizes, so these stay
   consistent between screens. */
.input--xs { max-width: 68px; }
.input--sm { max-width: 120px; }
.input--md { max-width: 200px; }

.field-error {
    font-size: 0.8125rem; font-weight: 500;
    color: var(--status-critical);
    display: flex; align-items: flex-start; gap: 0.25rem;
}
/* Keeps a leading icon at full size and on the first line of a wrapped message. */
.field-error svg { flex: 0 0 13px; width: 13px; height: 13px; margin-top: 3px; }
.field-help { font-size: 0.8125rem; color: var(--ink-muted); }

.check {
    display: flex; align-items: flex-start; gap: 0.5rem;
    font-size: 0.875rem; font-weight: 400; color: var(--ink);
    cursor: pointer;
}
.check input[type="checkbox"], .check input[type="radio"] {
    width: 16px; height: 16px; margin: 0.2rem 0 0; flex: 0 0 16px;
    accent-color: var(--brand-primary); cursor: pointer;
}
.check__text { min-width: 0; }
.check__title { font-weight: 500; }
.check__desc { font-size: 0.8125rem; color: var(--ink-muted); }

.check-grid { display: grid; gap: 0.5rem; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }

/* A checkbox occupying a .form__grid cell beside fields that have a label above
   them: drop it to sit on their baseline instead of floating at the cell top. */
.check--aligned { margin-top: 1.4rem; }
@media (max-width: 560px) {
    /* One column — there is nothing left to align with. */
    .check--aligned { margin-top: 0; }
}

.form__actions {
    display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center;
    border-top: 1px solid var(--line);
    padding-top: 1rem;
}
.form__actions .spacer { flex: 1 1 auto; }

/* --------------------------------------------------------------- filters ---- */

/* One row above the content, per the interaction spec. */
.filters {
    display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: flex-end;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
    border-radius: var(--radius) var(--radius) 0 0;
}
.filters__field { display: grid; gap: 0.2rem; min-width: 0; }
.filters__field label { font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.05em; }
.filters__field--grow { flex: 1 1 200px; }
.filters input, .filters select { min-height: 34px; padding: 0.3rem 0.5rem; font-size: 0.8125rem; }
.filters__actions { display: flex; gap: 0.375rem; }

.active-filters { display: flex; flex-wrap: wrap; gap: 0.375rem; padding: 0.625rem 1rem 0; }

/* ----------------------------------------------------------------- alerts --- */

.alert {
    display: flex; gap: 0.625rem; align-items: flex-start;
    padding: 0.75rem 0.875rem;
    border-radius: var(--radius);
    border: 1px solid;
    font-size: 0.875rem;
    margin-bottom: 1rem;
}
.alert svg { width: 17px; height: 17px; flex: 0 0 17px; margin-top: 1px; }
.alert__body { min-width: 0; }
.alert__title { font-weight: 600; }
.alert--success { background: var(--status-good-bg); border-color: color-mix(in oklab, var(--status-good) 32%, transparent); color: var(--status-good); }
.alert--error { background: var(--status-critical-bg); border-color: color-mix(in oklab, var(--status-critical) 32%, transparent); color: var(--status-critical); }
.alert--warning { background: var(--status-warning-bg); border-color: color-mix(in oklab, var(--status-warning-mark) 42%, transparent); color: var(--status-warning); }
.alert--info { background: var(--status-info-bg); border-color: color-mix(in oklab, var(--status-info) 32%, transparent); color: var(--status-info); }
.alert a { color: inherit; text-decoration: underline; }

.flashes { position: sticky; top: calc(var(--topbar-h) + 0.5rem); z-index: 15; }

/* ------------------------------------------------------------------ tabs ---- */

.tabs {
    display: flex; gap: 0.125rem; overflow-x: auto;
    border-bottom: 1px solid var(--line);
    margin-bottom: 1rem;
}
.tabs__link {
    padding: 0.5rem 0.75rem;
    font-size: 0.875rem; font-weight: 500;
    color: var(--ink-secondary);
    border-bottom: 2px solid transparent;
    white-space: nowrap;
}
.tabs__link:hover { color: var(--ink); text-decoration: none; }
.tabs__link[aria-current="page"] {
    color: var(--brand-primary);
    border-bottom-color: var(--brand-primary);
    font-weight: 600;
}

/* ----------------------------------------------- disclosures & popovers ----- */

/* <details> is the whole mechanism for "reveal the form that records this".
   Native, so it needs no JavaScript and no CSP exemption. */
.disclosure {
    cursor: pointer;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--ink-secondary);
}
.disclosure:hover { color: var(--ink); }
.disclosure::marker { color: var(--ink-muted); }
details[open] > .disclosure { margin-bottom: 0.5rem; }

/* When the <details> *is* the card, its summary is the card head and carries the
   head's padding — the margin below would double the border gap, so drop it. */
.disclosure--padded { padding: 0.875rem 1rem; }
details[open] > .disclosure--padded { margin-bottom: 0; }
.card__body--topline { border-top: 1px solid var(--line); }

/* A <summary> styled as a button opens a small panel of controls — the row-level
   "Cancel with a reason" and "Reset password" affordances.

   The panel stays in normal flow rather than being absolutely positioned: these
   live in the last cell of a table inside .table-wrap, whose overflow-x would
   clip a positioned panel and add a scrollbar instead of showing it. */
.popover { display: inline-block; }
.popover > summary { list-style: none; }
.popover > summary::-webkit-details-marker { display: none; }

.popover__panel {
    margin-top: 0.375rem;
    padding: 0.75rem;
    min-width: 240px;
    text-align: left;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
}

/* ------------------------------------------------- definition/detail lists --- */

.dl { display: grid; gap: 0.625rem 1rem; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.dl__item { min-width: 0; }
.dl__key {
    font-size: 0.6875rem; font-weight: 600; text-transform: uppercase;
    letter-spacing: 0.05em; color: var(--ink-muted);
}
.dl__val { font-size: 0.875rem; overflow-wrap: break-word; }
.dl__val--strong { font-weight: 600; }

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

.timeline { display: grid; gap: 0; }
.timeline__item {
    display: grid; grid-template-columns: 22px 1fr; gap: 0.625rem;
    padding-bottom: 1rem;
    position: relative;
}
.timeline__item:not(:last-child)::before {
    content: ""; position: absolute;
    left: 10px; top: 20px; bottom: 0;
    width: 1px; background: var(--line);
}
.timeline__dot {
    width: 9px; height: 9px; border-radius: 999px;
    background: var(--viz-series);
    margin: 6px 0 0 6px;
    /* 2px surface ring so the dot stays legible over the connector line. */
    box-shadow: 0 0 0 2px var(--surface);
}
/* The dot states an outcome where the entry has one — a pinned note, a message
   that failed. The row text always says the same thing in words. */
.timeline__dot--good { background: var(--status-good); }
.timeline__dot--warning, .timeline__dot--pinned { background: var(--status-warning-mark); }
.timeline__dot--critical { background: var(--status-critical); }

.timeline__body { min-width: 0; font-size: 0.875rem; padding-top: 1px; }
.timeline__meta { font-size: 0.75rem; color: var(--ink-muted); }

/* -------------------------------------------------------------- schedule ---- */

/* The Schedule page: the connected Google calendars, drawn as a calendar.
   Day and Week are one 24-hour time grid with a different number of columns;
   Month is a grid of cells holding chips. Three rules hold the whole thing
   together:

   1. Every position is a percentage of the day, never a pixel offset. The strict
      CSP forbids a style attribute, so per-event geometry arrives as a generated
      class from Ui::dyn(), whose declaration allowlist has no `*` — so
      `calc(var(--cal-hour-h) * 8.5)` is impossible, and `top: 35.4167%` of a
      column that is exactly 24 hours tall is the same place.
   2. The calendar's color is an edge, never a fill. Google fills its blocks
      because it owns its palette; here the color is a hex an administrator typed
      and text cannot be guaranteed readable on it, in either theme.
   3. The pinned rows and the scrolling body share one grid template, so a day
      heading stays over its own column. */

.sched-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem;
    padding: 0.625rem 0.875rem;
}
/* Pushes the view tabs away from the date arrows and lets the calendar chips
   wrap underneath on a narrow screen instead of squeezing the controls. */
.sched-bar__views { margin-left: auto; border-bottom: 0; }
.sched-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; flex-basis: 100%; }

/* The selected calendar filter. Border and weight as well as background: a chip
   distinguished only by a tint disappears in forced-colors mode. */
.chip--on {
    border-color: var(--brand-primary);
    background: var(--status-info-bg);
    color: var(--ink);
}

/* The per-calendar color. Set by a nonce'd class from Ui::bg(), and decorative:
   every entry names its calendar in its tooltip and its accessible text, so the
   color is a shortcut and never the only channel. */
.sched-dot {
    width: 9px; height: 9px; border-radius: 999px;
    flex: 0 0 auto;
    background: var(--ink-muted);
    box-shadow: 0 0 0 1px var(--surface);
}

/* ---- the calendar frame ---- */

.cal-card { padding: 0; overflow: hidden; }

.cal {
    /* One hour of grid. Everything vertical derives from it: the body is 24 of
       these, so a percentage of the body is a share of the day. */
    --cal-hour-h: 48px;
    --cal-gutter-w: 3.75rem;
    /* Below this a week column stops being readable, so the columns stop
       shrinking and the type does instead (see the narrow-screen block). */
    --cal-day-min: 5.5rem;
    font-size: 0.8125rem;
}

/* The pinned rows reserve the same scrollbar gutter as the scrolling body, which
   is what keeps a day heading over its column. `overflow: hidden` is still a
   scroll container, so `scrollbar-gutter: stable` applies to it; where a browser
   ignores the property, or draws overlay scrollbars that take no space, the two
   sides agree anyway. */
.cal__pinned {
    overflow: hidden;
    scrollbar-gutter: stable;
    border-bottom: 1px solid var(--line);
    background: var(--surface);
}

.cal__head, .cal__allday, .cal__body { display: grid; }

.cal--day .cal__head,
.cal--day .cal__allday,
.cal--day .cal__body { grid-template-columns: var(--cal-gutter-w) 1fr; }

.cal--week .cal__head,
.cal--week .cal__allday,
.cal--week .cal__body {
    grid-template-columns: var(--cal-gutter-w) repeat(7, minmax(var(--cal-day-min), 1fr));
}

/* ---- day headings ---- */

.cal__corner {
    display: flex; align-items: flex-end; justify-content: center;
    padding: 0.35rem 0.3rem;
    border-right: 1px solid var(--line);
    text-align: center;
}
.cal__corner--allday { align-items: center; }

.cal__dayhead {
    display: flex; flex-direction: column; align-items: center; gap: 0.15rem;
    padding: 0.45rem 0.25rem 0.5rem;
    border-left: 1px solid var(--line);
    text-decoration: none;
    min-width: 0;
}
.cal__dayhead:hover { background: var(--surface-2); }

.cal__dow {
    font-size: 0.6875rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--ink-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.cal__daynum { font-size: 1.0625rem; font-weight: 600; color: var(--ink); line-height: 1.15; }

/* Today's date in a filled disc — the same brand fill and white ink the primary
   button uses. The word "today" is in the heading's screen-reader text too. */
.cal__dayhead--today .cal__dow { color: var(--brand-primary); }
.cal__dayhead--today .cal__daynum {
    display: grid; place-items: center;
    width: 1.75rem; height: 1.75rem;
    border-radius: 999px;
    background: var(--brand-primary);
    color: #fff;
}

/* ---- the all-day band ---- */

/* Outside the scroller on purpose. Inside it, the grid opens scrolled to the
   clinic's morning and this band would already be above the fold — an all-day
   closure nobody can see is worse than one that costs a row of height. */
.cal__allday { border-top: 1px solid var(--line); }
.cal__alldaycell {
    display: grid; gap: 2px; align-content: start;
    padding: 0.25rem;
    border-left: 1px solid var(--line);
    min-width: 0;
    /* Three chips of headroom, then the cell scrolls on its own rather than
       pushing the hour grid off the screen. */
    max-height: 5.25rem; overflow-y: auto;
}

/* ---- the hour grid ---- */

.cal__scroll {
    overflow-y: auto;
    scrollbar-gutter: stable;
    /* Tall enough to hold a clinic day, short enough that the page's own footer
       and controls stay reachable without scrolling past the calendar. */
    max-height: min(65vh, 40rem);
}

.cal__gutter { border-right: 1px solid var(--line); }
.cal__hour { height: var(--cal-hour-h); position: relative; }
/* The label sits astride its hour line rather than under it, which is what makes
   it read as marking the line and not the space below. */
.cal__hourlabel {
    position: absolute; top: 0; right: 0.4rem;
    transform: translateY(-50%);
    font-size: 0.6875rem;
    color: var(--ink-muted);
    white-space: nowrap;
    background: var(--surface);
    padding: 0 0.15rem;
}

.cal__col {
    position: relative;
    height: calc(var(--cal-hour-h) * 24);
    border-left: 1px solid var(--line);
    /* Hour rules as a background rather than 24 elements per column: a week of
       them is 168 divs that exist only to draw a line. */
    background-image: repeating-linear-gradient(
        to bottom,
        var(--line) 0 1px,
        transparent 1px var(--cal-hour-h)
    );
}
.cal__col--weekend { background-color: var(--surface-2); }
/* Week only. The tint is how one column of seven is picked out; in the day view
   there is nothing to pick it out from, and tinting the whole grid buries the
   out-of-hours shading that is the useful mark there. The heading's filled disc
   still says which day this is. */
.cal--week .cal__col--today { background-color: var(--status-info-bg); }

/* Outside the clinic's hours. Shaded rather than clipped — the events there are
   still fetched and still positioned; this only says the clinic is not open. */
.cal__closed {
    position: absolute; left: 0; right: 0; top: 0;
    background: var(--surface-3);
    opacity: 0.55;
    pointer-events: none;
}

/* Now: a line with a dot at its left end, on today's column only. */
.cal__now {
    position: absolute; left: 0; right: 0;
    height: 0;
    border-top: 2px solid var(--status-critical);
    z-index: 3;
    pointer-events: none;
}
.cal__now::before {
    content: "";
    position: absolute; left: 0; top: -4px;
    width: 7px; height: 7px;
    border-radius: 999px;
    background: var(--status-critical);
}

/* ---- an event on the grid ---- */

.cal-block {
    position: absolute;
    display: flex;
    overflow: hidden;
    /* The height is the event's true length, which for a 15-minute slot is 12px.
       This grows the box enough to read without moving the next one. */
    min-height: 1.25rem;
    border-radius: var(--radius-sm);
    background: var(--surface);
    border: 1px solid var(--line-strong);
    box-shadow: var(--shadow-sm);
    text-decoration: none;
    color: var(--ink);
    z-index: 2;
}
.cal-block:hover { border-color: var(--brand-primary); z-index: 4; }
.cal-block:focus-visible { z-index: 4; }

/* The calendar's color, and the one thing in the box allowed to be an arbitrary
   admin-chosen hex: nothing has to stay legible on top of it. */
.cal-block__edge { flex: 0 0 3px; background: var(--ink-muted); }

.cal-block__inner {
    display: grid; gap: 0;
    padding: 0.1rem 0.3rem;
    min-width: 0;
    align-content: start;
}
.cal-block__time { font-size: 0.6875rem; font-weight: 600; color: var(--ink-secondary); }
.cal-block__title {
    font-weight: 600;
    font-size: 0.75rem;
    line-height: 1.25;
    overflow: hidden; text-overflow: ellipsis;
    /* Two lines, then clipped. The full title is in the tooltip. */
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
.cal-block__meta {
    font-size: 0.6875rem; color: var(--ink-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Too short for two lines: the time and title share one, and the padding goes. */
.cal-block--compact .cal-block__inner {
    display: flex; gap: 0.3rem; align-items: baseline;
    padding: 0 0.3rem;
}
.cal-block--compact .cal-block__title {
    display: block; -webkit-line-clamp: none;
    white-space: nowrap;
    font-size: 0.6875rem;
}

/* ---- a chip: the all-day band, and month cells ---- */

.cal-chip {
    display: flex; align-items: center; gap: 0.3rem;
    padding: 0.1rem 0.3rem;
    border-radius: var(--radius-sm);
    text-decoration: none;
    color: var(--ink);
    min-width: 0;
    font-size: 0.75rem;
}
.cal-chip:hover { background: var(--surface-2); }
/* In the all-day band a chip is a filled bar rather than a line of text, so the
   band reads as blocks of time claimed. */
.cal-chip--band { background: var(--surface-2); border: 1px solid var(--line); }
.cal-chip__time { color: var(--ink-secondary); flex: 0 0 auto; font-size: 0.6875rem; }
.cal-chip__title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- month ---- */

.cal--month .cal__dow,
.cal--month .cal__week { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }

.cal__dowcell {
    padding: 0.4rem 0.5rem;
    font-size: 0.6875rem; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--ink-muted);
    text-align: center;
    border-bottom: 1px solid var(--line);
    border-left: 1px solid var(--line);
}
.cal__dowcell:first-child { border-left: 0; }

.cal__week { border-bottom: 1px solid var(--line); }
.cal__week:last-child { border-bottom: 0; }

.cal__cell {
    display: flex; flex-direction: column; gap: 0.15rem;
    min-height: 6.5rem;
    padding: 0.25rem;
    border-left: 1px solid var(--line);
    min-width: 0;
}
.cal__cell:first-child { border-left: 0; }
/* Days from the months either side. Dimmed rather than blank: a booking on the
   1st of next month is still a booking, and an empty cell under a real date
   reads as "nothing on". */
.cal__cell--other { background: var(--surface-2); }
.cal__cell--other .cal__celldate { color: var(--ink-muted); }
.cal__cell--today { background: var(--status-info-bg); }

.cal__cellhead { display: block; text-decoration: none; text-align: right; }
.cal__celldate {
    display: inline-grid; place-items: center;
    min-width: 1.5rem; height: 1.5rem;
    font-size: 0.8125rem; font-weight: 600;
    color: var(--ink-secondary);
    border-radius: 999px;
}
.cal__cellhead:hover .cal__celldate { background: var(--surface-3); color: var(--ink); }
.cal__celldate--today { background: var(--brand-primary); color: #fff; }
.cal__cellhead:hover .cal__celldate--today { background: var(--brand-primary); color: #fff; }

.cal__cellbody { display: grid; gap: 1px; align-content: start; min-width: 0; }

.cal__more {
    font-size: 0.6875rem; font-weight: 600;
    color: var(--ink-secondary);
    padding: 0.05rem 0.3rem;
    text-decoration: none;
}
.cal__more:hover { color: var(--brand-primary); text-decoration: underline; }

/* ---- narrow screens ---- */

@media (max-width: 900px) {
    .cal { --cal-gutter-w: 3rem; --cal-day-min: 4.5rem; font-size: 0.75rem; }
    /* A week column this narrow holds a time or a title, not both, and the time
       is the part that locates the entry. The rest stays in the tooltip and in
       the day view, which every day heading links to. */
    .cal--week .cal-block__meta { display: none; }
    .cal__cell { min-height: 5rem; }

    /* Seven columns cannot shrink below legibility, so a narrow screen scrolls
       the week sideways rather than clipping Saturday off the end — the one
       outcome a schedule must never have, because the day is still there and the
       reader has no way to know it was cut. Both the pinned headings and the hour
       body sit inside this scroller, so they move together and stay aligned. */
    .cal-card { overflow-x: auto; }
    .cal--week { min-width: 36rem; }
}

@media (max-width: 600px) {
    .cal { --cal-hour-h: 42px; }
    /* A month cell loses its chip titles before it loses the date: a cell too
       small for a line of text still has to say which day it is, and the "+N
       more" link still leads to the day view. */
    .cal--month .cal-chip__title { display: none; }
    .cal__cell { min-height: 4rem; }
}

/* ------------------------------------------------------------- checklist ---- */

.checklist { display: grid; gap: 0.125rem; }
.checklist__item {
    display: flex; align-items: flex-start; gap: 0.5rem;
    padding: 0.5rem;
    border-radius: var(--radius-sm);
    font-size: 0.875rem;
}
.checklist__item:hover { background: var(--surface-2); }

/* Each item is its own POST form, so the tick is a submit button rather than a
   checkbox — a checkbox would imply a change that only takes effect on save. */
.checklist__toggle { padding: 0.1rem; min-width: 22px; }
.checklist__box {
    display: inline-grid; place-items: center;
    width: 17px; height: 17px;
    border-radius: 4px;
    border: 1.5px solid var(--line-strong);
    /* Flips with the theme so the tick holds contrast on both greens. */
    color: var(--ink-inverse);
}
.checklist__box--done { border-color: var(--status-good); background: var(--status-good); }
/* Only the step text is struck through. The line used to be set on the whole
   label, and text-decoration propagates to in-flow descendants — so it ran through
   the help text and through the name of whoever completed it, which reads as if
   the person had been crossed out too. */
.checklist__item--done .checklist__label { color: var(--ink-muted); }
.checklist__item--done .checklist__text { text-decoration: line-through; }
.checklist__meta { font-size: 0.75rem; color: var(--ink-muted); }
.checklist__req { color: var(--status-critical); font-weight: 700; }

/* ----------------------------------------------------- document requirements - */

/* "What does this record still need?" — one row per required document, stating
   plainly whether it is on file and what it is for. Three channels again: the
   left border, the marker glyph, and the words "On file"/"Not on file yet". */

.req-list { display: grid; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }

.req {
    display: flex;
    align-items: flex-start;
    gap: 0.625rem;
    padding: 0.625rem 0.75rem;
    border: 1px solid var(--line);
    border-left-width: 3px;
    border-radius: var(--radius-sm);
    background: var(--surface);
}
.req--met { border-left-color: var(--status-good); }
.req--missing { border-left-color: var(--status-warning-mark); }
/* Skipped: resolved, but not by a document. Muted rather than green — nothing about
   this row should read as "we have it". */
.req--skipped { border-left-color: var(--ink-muted); }

.req__mark {
    flex: 0 0 20px;
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    margin-top: 1px;
    border-radius: 999px;
}
.req__mark svg { width: 13px; height: 13px; }
.req--met .req__mark { background: var(--status-good-bg); color: var(--status-good); }
.req--missing .req__mark { background: var(--status-warning-bg); color: var(--status-warning); }
.req--skipped .req__mark { background: var(--surface-2); color: var(--ink-secondary); }

.req__text { flex: 1 1 auto; min-width: 0; }
.req__name {
    display: flex; align-items: center; flex-wrap: wrap; gap: 0.375rem;
    font-size: 0.875rem; font-weight: 600;
}
.req__why { font-size: 0.8125rem; color: var(--ink-secondary); }
.req__state { font-size: 0.75rem; color: var(--ink-muted); }
/* The reason someone gave for skipping the document. Their words, so it is set
   apart from the surrounding sentence rather than run into it. */
.req__reason { font-style: italic; overflow-wrap: break-word; }
.req__action { flex: 0 0 auto; }
/* "Add" and "Skip" side by side, and the skip panel below them rather than
   squeezed into the button row. Capped so an open panel does not crowd out the
   requirement text it belongs to. */
.req__buttons { align-items: flex-start; }
.req__action .popover__panel { max-width: min(320px, 70vw); }

.req-list__foot {
    margin-top: 0.625rem;
    font-size: 0.75rem;
    color: var(--ink-muted);
}

@media (max-width: 560px) {
    .req { flex-wrap: wrap; }
    .req__action { width: 100%; }
    .req__action .btn { width: 100%; }
}

/* ------------------------------------------------------------------ meter --- */

.meter {
    height: 7px; border-radius: 999px;
    background: var(--viz-track);
    overflow: hidden;
}
.meter__fill { height: 100%; border-radius: 999px; background: var(--viz-series); }
.meter__fill--good { background: var(--status-good); }
.meter__fill--warning { background: var(--status-warning-mark); }
.meter__fill--critical { background: var(--status-critical); }

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

.chart { width: 100%; }
.chart svg { display: block; width: 100%; height: auto; overflow: visible; }

.chart__grid line { stroke: var(--viz-grid); stroke-width: 1; }
.chart__axis line { stroke: var(--viz-axis); stroke-width: 1; }

/* Text on a chart wears text tokens, never the series color. */
.chart__label { fill: var(--ink-secondary); font-size: 11.5px; }
.chart__tick { fill: var(--ink-muted); font-size: 10.5px; font-variant-numeric: tabular-nums; }
.chart__value { fill: var(--ink); font-size: 11.5px; font-weight: 600; font-variant-numeric: tabular-nums; }

.chart__bar { fill: var(--viz-series); }
.chart__bar-track { fill: var(--viz-track); }
.chart__bar:hover { fill: color-mix(in oklab, var(--viz-series) 82%, black); }

.chart-legend {
    display: flex; flex-wrap: wrap; gap: 0.75rem;
    font-size: 0.8125rem; color: var(--ink-secondary);
    margin-top: 0.5rem;
}
.chart-legend__item { display: inline-flex; align-items: center; gap: 0.35rem; }
.chart-legend__swatch { width: 10px; height: 10px; border-radius: 3px; flex: 0 0 10px; }

/* Raw audit metadata: wraps, but never widens the row it sits in. */
.audit-meta { max-width: 280px; margin: 0.25rem 0 0; }

/* A table view of every chart, so nothing is gated behind color perception. */
.chart-table summary {
    cursor: pointer; font-size: 0.8125rem; color: var(--ink-secondary);
    padding: 0.375rem 0; font-weight: 500;
}
.chart-table summary:hover { color: var(--ink); }
.chart-table[open] summary { margin-bottom: 0.5rem; }

/* Horizontal bar rows, built in HTML rather than SVG where a row also needs
   links and badges — same visual spec: capped thickness, rounded data-end. */
.barlist { display: grid; gap: 0.5rem; }
.barlist__row { display: grid; grid-template-columns: minmax(110px, 1.4fr) minmax(0, 3fr) auto; gap: 0.625rem; align-items: center; font-size: 0.875rem; }
.barlist__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.barlist__track { background: var(--viz-track); border-radius: 4px; height: 18px; position: relative; overflow: hidden; }
.barlist__fill { position: absolute; inset: 0 auto 0 0; border-radius: 0 4px 4px 0; background: var(--viz-series); min-width: 2px; }
.barlist__value { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 2.5rem; text-align: right; }

@media (max-width: 560px) {
    .barlist__row { grid-template-columns: 1fr auto; }
    .barlist__track { grid-column: 1 / -1; }
}

/* --------------------------------------------------------- pipeline board --- */

.board { display: flex; gap: 0.75rem; overflow-x: auto; padding-bottom: 0.75rem; }
.board__col {
    flex: 0 0 268px;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    display: flex; flex-direction: column;
    max-height: 74vh;
}
.board__head { padding: 0.625rem 0.75rem; border-bottom: 1px solid var(--line); }
.board__title { display: flex; align-items: center; gap: 0.4rem; font-size: 0.8125rem; font-weight: 600; }
.board__count { margin-left: auto; font-size: 0.75rem; color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.board__body { overflow-y: auto; padding: 0.5rem; display: grid; gap: 0.5rem; align-content: start; }
.board__none { padding: 1rem 0.5rem; }

.board__card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    padding: 0.55rem 0.625rem;
    display: grid; gap: 0.3rem;
    font-size: 0.8125rem;
    box-shadow: var(--shadow-sm);
}
.board__card:hover { border-color: var(--line-strong); text-decoration: none; }
.board__card[data-flag="red"] { border-left: 3px solid var(--status-critical); }
.board__card[data-flag="amber"] { border-left: 3px solid var(--status-warning-mark); }
.board__card-name { font-weight: 600; color: var(--ink); }
.board__card-meta { font-size: 0.75rem; color: var(--ink-muted); display: flex; flex-wrap: wrap; gap: 0.375rem; }

/* --------------------------------------------------- phase progress track --- */

/* The chart header's answer to "where is this family in the process?".
   An ordered list, because that is what it is: the phases in order, each either
   behind, current, or ahead.

   Three channels per step, never color alone:
     - the marker glyph differs by state (tick / ring / hollow number),
     - the label's weight and ink differ,
     - a visually hidden word states the state for a screen reader, and
       aria-current="step" marks the one the record is in. */

.track {
    display: flex;
    align-items: flex-start;
    list-style: none;
    margin: 0;
    padding: 0.25rem 0 0;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* The base step is the "ahead" state (.track__step--ahead carries no rules of its
   own); --done and --current style themselves on top of it. */
.track__step {
    position: relative;
    flex: 1 1 0;
    min-width: 96px;
    padding: 0 0.25rem;
    text-align: center;
}

/* The connector runs from this marker's center to the next one's, at marker
   height, and is drawn *behind* both (the marker is z-indexed above it). A step
   fills the segment leaving it once the record has passed through, so the filled
   run always ends at the current phase. */
.track__step:not(:last-child)::after {
    content: "";
    position: absolute;
    z-index: 0;
    top: 10px;
    left: 50%;
    right: -50%;
    height: 2px;
    background: var(--line-strong);
}
.track__step--done::after { background: var(--status-good); }

.track__marker {
    position: relative;
    z-index: 1;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    margin: 0 auto 0.4rem;
    border-radius: 999px;
    border: 2px solid var(--line-strong);
    background: var(--surface);
    color: var(--ink-muted);
    font-size: 0.6875rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}
.track__marker svg { width: 12px; height: 12px; stroke-width: 3; }

.track__step--done .track__marker {
    border-color: var(--status-good);
    background: var(--status-good);
    /* Not #fff: the dark-mode green is light, and a white tick on it measures
       2.3:1 — under the 3:1 a graphical object needs. --ink-inverse flips with the
       theme, so the tick stays legible in both. */
    color: var(--ink-inverse);
}
.track__step--current .track__marker {
    border-color: var(--brand-primary);
    color: var(--brand-primary);
    /* Halo rather than a size change, so the row of markers stays on one line. */
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--brand-primary) 20%, transparent);
}

.track__label {
    display: block;
    font-size: 0.75rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--ink-muted);
    overflow-wrap: break-word;
}
.track__step--done .track__label { color: var(--ink-secondary); }
.track__step--current .track__label { color: var(--ink); }

.track__meta {
    display: block;
    margin-top: 0.15rem;
    font-size: 0.6875rem;
    color: var(--ink-muted);
}

/* The summary line above the track: how far along, and how long here. */
.track-head {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.5rem;
    font-size: 0.8125rem;
    color: var(--ink-secondary);
}
.track-head__count { font-weight: 600; color: var(--ink); }

/* ------------------------------------------------------------- pagination --- */

.pagination {
    display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--line);
    font-size: 0.875rem;
    color: var(--ink-secondary);
}
.pagination__spacer { flex: 1 1 auto; }

/* ---------------------------------------------------------------- avatars --- */

.avatar {
    width: 30px; height: 30px; flex: 0 0 30px;
    border-radius: 999px;
    background: color-mix(in oklab, var(--brand-primary) 16%, transparent);
    color: var(--brand-primary);
    display: grid; place-items: center;
    font-size: 0.6875rem; font-weight: 700;
}
.avatar--lg { width: 42px; height: 42px; flex-basis: 42px; font-size: 0.875rem; }

.person { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.person__name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person__meta { font-size: 0.75rem; color: var(--ink-muted); }

/* ------------------------------------------------------------------ misc ---- */

/* Utilities exist so a template never needs a style attribute: the CSP has no
   'unsafe-inline' in style-src and a nonce cannot cover an attribute, so a
   style="…" is dropped by the browser and the rule silently does nothing.
   Anything genuinely dynamic goes through Ui::dyn() instead. */

.muted { color: var(--ink-muted); }
.small { font-size: 0.8125rem; }
.tiny { font-size: 0.75rem; }
.strong { font-weight: 600; }
.right { text-align: right; }
.center { text-align: center; }
.nowrap { white-space: nowrap; }
.pre-wrap { white-space: pre-wrap; }
.pre { white-space: pre; }
.break-any { overflow-wrap: anywhere; }
.tabular { font-variant-numeric: tabular-nums; }

.text-secondary { color: var(--ink-secondary); }
.text-brand { color: var(--brand-primary); }
.text-good { color: var(--status-good); }
.text-warning { color: var(--status-warning); }
.text-critical { color: var(--status-critical); }

.stack { display: grid; gap: 1rem; }
.stack--sm { display: grid; gap: 0.5rem; }
.row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.row--tight { gap: 0.3rem; }
.row--wide { gap: 1rem; }
.row--between { justify-content: space-between; }
.row--top { align-items: flex-start; }

/* Pushes what follows to the far end of a flex row — a card action, a meta note. */
.push { margin-left: auto; }
.grow { flex: 1 1 auto; }
.min-0 { min-width: 0; }

.m-0 { margin: 0; }
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: 0.5rem; }
.mb-2 { margin-bottom: 1rem; }
.my-1 { margin: 0.5rem 0; }

.divider { border-top: 1px solid var(--line); margin: 1rem 0; }
.divider--sm { margin: 0.5rem 0; }
.divider--xs { margin: 0.25rem 0; }

/* A form wrapping a single button, sitting in a row of other buttons. */
.inline-form { display: inline-block; }

/* A whole block that is one link — a settings tile. The child heading carries
   the link color instead, so the tile does not read as a wall of blue. */
.plain-link { color: inherit; text-decoration: none; }
.plain-link:hover { text-decoration: none; }

/* Prose bullets inside a card: the browser default indent is far too deep. */
.bullets { margin: 0.4rem 0 0; padding-left: 1.15rem; }
.bullets li + li { margin-top: 0.2rem; }

/* A long log that must not push the page height — the panel scrolls instead. */
.scroll-y { max-height: 320px; overflow-y: auto; }

.ml-xs { margin-left: 0.25rem; }
/* A flex child that fills the row but wraps to its own line before going narrow. */
.grow-260 { flex: 1 1 260px; min-width: 0; }
/* A table cell that should be exactly as wide as its buttons. */
table.data td.shrink, table.data th.shrink { width: 1%; }
/* A key column in a two-column detail table — keeps the values aligned. */
table.data th.col-200 { width: 200px; }

.phi-note {
    font-size: 0.75rem; color: var(--ink-muted);
    display: inline-flex; align-items: center; gap: 0.3rem;
}

.kbd {
    font-family: var(--font-mono); font-size: 0.75rem;
    padding: 0.1rem 0.3rem;
    border: 1px solid var(--line-strong);
    border-bottom-width: 2px;
    border-radius: 4px;
    background: var(--surface-2);
}

.copy-box {
    display: flex; align-items: center; gap: 0.5rem;
    padding: 0.625rem 0.75rem;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono); font-size: 0.8125rem;
    overflow-x: auto;
}
/* A value someone has to read aloud or type by hand — a temporary password. */
.copy-box--lg { font-size: 1rem; font-weight: 600; letter-spacing: 0.02em; }

/* ------------------------------------------------------------------ auth ---- */

.auth {
    min-height: 100vh;
    display: grid; place-items: center;
    padding: 1.5rem;
    background: var(--page);
}
.auth__card {
    width: 100%; max-width: 400px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: 1.75rem;
}
.auth__brand { display: flex; align-items: center; gap: 0.625rem; margin-bottom: 1.25rem; }
.auth__brand .sidebar__mark { width: 34px; height: 34px; flex-basis: 34px; }
.auth__title { font-size: 1.125rem; }
.auth__divider {
    display: flex; align-items: center; gap: 0.625rem;
    margin: 1.25rem 0;
    font-size: 0.75rem; color: var(--ink-muted);
    text-transform: uppercase; letter-spacing: 0.05em;
}
.auth__divider::before, .auth__divider::after {
    content: ""; flex: 1 1 auto; border-top: 1px solid var(--line);
}
.auth__foot { margin-top: 1.25rem; font-size: 0.75rem; color: var(--ink-muted); text-align: center; }

.google-btn {
    display: flex; align-items: center; justify-content: center; gap: 0.5rem;
    width: 100%;
    padding: 0.625rem;
    background: var(--surface);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius-sm);
    font: inherit; font-size: 0.9375rem; font-weight: 500;
    color: var(--ink);
    cursor: pointer;
}
.google-btn:hover { background: var(--surface-2); text-decoration: none; }
.google-btn svg { width: 18px; height: 18px; }

/* ---------------------------------------------------------- error / minimal - */

/* The explicit minmax(0, 1fr) column is what makes .error-detail's overflow-x
   actually scroll. Without it the single implicit track is auto-sized, so a long
   stack-trace line grew the track past the viewport and pushed the whole page
   into a horizontal scroll on narrow screens instead of scrolling inside the
   trace block. */
/* =============================================================================
   Public legal notices (/privacy, /terms)
   -----------------------------------------------------------------------------
   The only pages served to anonymous visitors from the open internet, so this
   block assumes no sidebar, no session and no app chrome. Readable prose column
   with the same tokens as everything else, and it inherits the dark scheme.
   ========================================================================== */

.legal {
    max-width: 46rem;
    margin: 0 auto;
    padding: 2.5rem 1.25rem 3rem;
}

.legal__head {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding-bottom: 1.25rem;
    border-bottom: 1px solid var(--line);
}

.legal__mark {
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: var(--radius-sm);
    background: var(--brand-primary);
    color: #fff;
    font-size: 0.8125rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    overflow: hidden;
}

.legal__mark img { width: 100%; height: 100%; object-fit: contain; }

.legal__eyebrow {
    margin: 0;
    font-size: 0.75rem;
    color: var(--ink-muted);
}

.legal__title {
    margin: 0.125rem 0 0;
    font-size: 1.375rem;
    line-height: 1.2;
}

.legal__body { color: var(--ink-secondary); line-height: 1.65; }

.legal__body h2 {
    margin: 2rem 0 0.5rem;
    font-size: 1.0625rem;
    color: var(--ink);
}

.legal__body h3 {
    margin: 1.5rem 0 0.375rem;
    font-size: 0.9375rem;
    color: var(--ink);
}

.legal__body p { margin: 0.625rem 0; }
.legal__body ul { margin: 0.625rem 0; padding-left: 1.25rem; }
.legal__body li { margin: 0.3125rem 0; }

.legal__body code {
    padding: 0.0625rem 0.3125rem;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    font-size: 0.875em;
    word-break: break-all;
}

.legal__lede {
    font-size: 1.0625rem;
    color: var(--ink);
    margin-top: 1.5rem;
}

.legal__meta {
    padding: 0.75rem;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    font-size: 0.8125rem;
}

.legal__contact {
    padding: 0.75rem;
    border-left: 3px solid var(--brand-accent);
    background: var(--surface-2);
}

/* A value the administrator has not filled in. Deliberately conspicuous: better
   an obvious gap than a confident-looking wrong legal entity. */
.legal__unset {
    padding: 0.0625rem 0.25rem;
    border-radius: var(--radius-sm);
    background: #fef3c7;
    color: #92400e;
    font-size: 0.875em;
    font-style: italic;
}

.legal__foot {
    margin-top: 2.5rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line);
}

.legal__links {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    font-size: 0.8125rem;
}

.legal__note {
    margin: 0.75rem 0 0;
    font-size: 0.75rem;
    color: var(--ink-muted);
}

@media (prefers-color-scheme: dark) {
    .legal__unset { background: #4a3410; color: #fcd34d; }
}

/* Narrow viewport — a phone, or the frame the agreement gate reads these in. The
   generous desktop padding costs a fifth of the visible height there, before a
   word of the notice. */
@media (max-width: 640px) {
    .legal { padding: 1.25rem 1rem 1.75rem; }
    .legal__head { padding-bottom: 0.875rem; }
}

.minimal {
    min-height: 100vh;
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    place-items: center;
    padding: 1.5rem;
}
.minimal__card { width: 100%; max-width: 560px; }
.error-code { font-size: 3rem; font-weight: 600; line-height: 1; color: var(--ink-muted); }
.error-detail {
    margin-top: 1rem; padding: 0.75rem;
    background: var(--surface-2);
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    font-family: var(--font-mono); font-size: 0.6875rem;
    white-space: pre-wrap; overflow-x: auto;
    color: var(--status-critical);
    max-height: 340px;
}

/* ------------------------------------------------------------- responsive --- */

@media (max-width: 900px) {
    .menu-toggle { display: inline-flex; }

    .sidebar {
        position: fixed; z-index: 40;
        inset: 0 auto 0 0;
        transform: translateX(-100%);
        transition: transform 0.18s ease-out;
        box-shadow: var(--shadow-lg);
    }
    /* Toggled with :target so the mobile menu needs no JavaScript — which keeps
       the CSP free of any script exemption for navigation. */
    .sidebar:target { transform: translateX(0); }

    .sidebar__close { display: inline-flex !important; }

    .content { padding: 1rem; }
    .grid--4, .grid--3 { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
    .stat__value { font-size: 1.5rem; }
}

@media (min-width: 901px) {
    .sidebar__close { display: none !important; }
}

@media (max-width: 560px) {
    body { font-size: 14.5px; }
    .page-head__actions { width: 100%; }
    .page-head__actions .btn { flex: 1 1 auto; }
    .grid--4, .grid--3, .grid--2 { grid-template-columns: 1fr; }
    table.data th, table.data td { padding: 0.5rem; }
    .hide-sm { display: none !important; }
}

@media (max-width: 400px) {
    .hide-xs { display: none !important; }
}

/* ------------------------------------------------------------ file upload --- */

/* Toggled from app.js. Presentation only ever changes by class, because the CSP
   has no 'unsafe-inline' for styles. */
.is-hidden { display: none !important; }

.dropzone__target {
    border: 1.5px dashed var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface-2);
    padding: 1.25rem 1rem;
    text-align: center;
    cursor: pointer;
    display: block;
    transition: border-color 0.15s, background 0.15s;
}

.dropzone__target:hover { border-color: var(--brand-primary); }

.dropzone__target:focus-within {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
    border-color: var(--brand-primary);
}

.dropzone__target.is-dragging {
    border-color: var(--brand-primary);
    border-style: solid;
    background: var(--status-info-bg);
}

.dropzone__icon { color: var(--ink-muted); display: block; margin: 0 auto 0.4rem; }
.dropzone__lead { font-weight: 600; font-size: 0.875rem; }
.dropzone__hint { font-size: 0.75rem; color: var(--ink-muted); margin-top: 0.2rem; }

/* The real input stays in the DOM and keyboard-reachable — it is what actually
   submits — but the label above is the visible affordance. */
.dropzone__input {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Without JS the input must be visible, or there is no way to choose a file. */
.dropzone:not(.is-enhanced) .dropzone__input {
    position: static;
    width: auto; height: auto;
    margin: 0.5rem 0 0;
    clip: auto;
    overflow: visible;
}

.dropzone:not(.is-enhanced) .dropzone__lead,
.dropzone:not(.is-enhanced) .dropzone__icon { display: none; }

.upload-list { display: grid; gap: 0.5rem; margin-top: 0.75rem; }

.upload-item {
    display: flex;
    align-items: flex-start;
    gap: 0.75rem;
    padding: 0.625rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}

.upload-item__thumb {
    flex: 0 0 auto;
    width: 56px; height: 56px;
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    border: 1px solid var(--line);
    overflow: hidden;
    display: flex; align-items: center; justify-content: center;
}

.upload-item__thumb img { width: 100%; height: 100%; object-fit: cover; }

.upload-item__ext {
    font-size: 0.625rem; font-weight: 700; letter-spacing: 0.03em;
    color: var(--ink-muted);
}

.upload-item__fields { flex: 1 1 auto; min-width: 0; display: grid; gap: 0.375rem; }
.upload-item__field { display: grid; gap: 0.15rem; }
.upload-item__field input, .upload-item__field select { width: 100%; }
.upload-item__meta { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.upload-item__remove { flex: 0 0 auto; align-self: center; }

.upload-errors {
    margin-top: 0.5rem;
    font-size: 0.75rem;
    color: var(--status-critical);
    display: grid; gap: 0.15rem;
}

.upload-summary { font-size: 0.75rem; color: var(--ink-muted); margin-top: 0.5rem; }

/* Thumbnails in the stored-document table. */
.doc-thumb {
    width: 40px; height: 40px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--surface-2);
    object-fit: cover;
    display: block;
}

.doc-thumb--icon {
    display: flex; align-items: center; justify-content: center;
    color: var(--ink-muted);
}

.doc-cell { display: flex; align-items: flex-start; gap: 0.5rem; min-width: 0; }
.doc-cell__text { min-width: 0; }
.doc-actions { flex-wrap: nowrap; justify-content: flex-end; }
.doc-row { display: flex; justify-content: space-between; align-items: center; gap: 0.5rem; }

/* ---------------------------------------------------- document preview modal --- */

.doc-modal {
    /* Big enough to actually read a scanned page, but never edge-to-edge. */
    width: min(1100px, 94vw);
    height: min(88vh, 1000px);
    max-width: none;
    max-height: none;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    color: var(--ink);
    box-shadow: var(--shadow-lg);
    overflow: hidden;
}

/* Only laid out as a column once open — a closed dialog must stay display:none. */
.doc-modal[open] { display: flex; flex-direction: column; }

.doc-modal::backdrop {
    background: rgba(16, 20, 27, 0.55);
    backdrop-filter: blur(2px);
}

.doc-modal__head {
    display: flex; align-items: flex-start; gap: 0.75rem;
    padding: 0.875rem 1rem;
    border-bottom: 1px solid var(--line);
    background: var(--surface-2);
    flex: 0 0 auto;
}

.doc-modal__heading { min-width: 0; flex: 1 1 auto; }

.doc-modal__title {
    font-size: 0.9375rem; font-weight: 600; margin: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.doc-modal__close {
    flex: 0 0 auto;
    width: 30px; height: 30px;
    display: flex; align-items: center; justify-content: center;
    font-size: 1.25rem; line-height: 1;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
    color: var(--ink-secondary);
    cursor: pointer;
}

.doc-modal__close:hover { background: var(--surface-3); color: var(--ink); }

.doc-modal__close:focus-visible {
    outline: 2px solid var(--focus-ring);
    outline-offset: 2px;
}

.doc-modal__body {
    flex: 1 1 auto;
    min-height: 0;              /* lets the iframe shrink instead of overflowing */
    overflow: auto;
    background: var(--surface-3);
    display: flex; align-items: center; justify-content: center;
    padding: 0.75rem;
}

.doc-modal__status { margin: 0; }

.doc-modal__image {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
    border-radius: var(--radius-sm);
    background: #fff;
    box-shadow: var(--shadow-sm);
}

.doc-modal__frame {
    width: 100%; height: 100%;
    border: 0;
    border-radius: var(--radius-sm);
    background: #fff;
}

.doc-modal__foot {
    flex: 0 0 auto;
    display: flex; align-items: center; justify-content: space-between;
    gap: 0.75rem; flex-wrap: wrap;
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--line);
    background: var(--surface-2);
}

.doc-modal__nav,
.doc-modal__actions { display: flex; align-items: center; gap: 0.5rem; }
.doc-modal__position { min-width: 5.5rem; text-align: center; }
.doc-modal__nav .btn[disabled] { opacity: 0.45; pointer-events: none; }

@media (max-width: 640px) {
    .doc-modal { width: 100vw; height: 100vh; border-radius: 0; border: 0; }
    .doc-modal__foot { justify-content: center; }
    .doc-modal__nav, .doc-modal__actions { flex: 1 1 100%; justify-content: center; }
    .doc-actions { flex-wrap: wrap; }
}

@media (max-width: 640px) {
    .upload-item { flex-wrap: wrap; }
    .upload-item__remove { align-self: flex-start; }
}

/* -------------------------------------------------- legal agreement gate ---- */

/* One block per outstanding notice: the tick, and the notice itself in a frame
   directly beneath it. The document sits with the checkbox rather than behind a
   link to somewhere else, so there is no way to agree without having been offered
   the thing — and no way to read it that costs you the page. */

.agree {
    display: grid;
    gap: 0.625rem;
    padding: 0.75rem 0.875rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}

/* Summary styled as a button, so the marker triangle has to go — same treatment
   as .popover. */
.agree__doc > summary { list-style: none; width: fit-content; }
.agree__doc > summary::-webkit-details-marker { display: none; }
.agree__doc[open] > summary { margin-bottom: 0.5rem; }

/* Fixed height, so the notice scrolls inside its own box instead of turning the
   gate into a page nobody reaches the bottom of. Capped against the viewport as
   well as in rem: on a laptop the Agree button must stay in view, or the frame
   becomes the page it was meant to avoid. The wrapper carries the border and the
   overflow because iOS Safari expands an iframe to its content height and scrolls
   the ancestor instead. */
.agree__frame {
    height: min(60vh, 26rem);
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--surface);
}
.agree__frame iframe {
    display: block;
    width: 100%;
    height: 100%;
    min-height: 100%;
    border: 0;
}

.agree__hint { margin: 0.5rem 0 0; }

/* ------------------------------------------------- full-record report ------- */

/* One continuous read-only document rather than a grid of cards: it is meant to be
   read top to bottom on screen and then printed or saved as a PDF, so it is laid
   out as a document on both. Deliberately narrow — a record read at full monitor
   width is a record nobody reads. */

.report {
    max-width: 52rem;
    margin: 0 auto;
    padding: 1.75rem 2rem 2.5rem;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
}

.report__bar { margin-bottom: 1rem; }

.report__head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
    padding-bottom: 0.875rem;
    border-bottom: 2px solid var(--ink);
}
.report__brand { display: flex; align-items: center; gap: 0.625rem; }
.report__logo { height: 34px; width: auto; }
.report__clinic { font-size: 1rem; font-weight: 700; }
.report__kind { font-size: 0.75rem; color: var(--ink-secondary); }
.report__stamp { font-size: 0.75rem; color: var(--ink-secondary); text-align: right; }

.report__title { margin: 1.25rem 0 0.125rem; font-size: 1.5rem; }
.report__subtitle { margin: 0; font-size: 0.875rem; color: var(--ink-secondary); }

.report__flag {
    margin: 0.875rem 0 0;
    padding: 0.5rem 0.75rem;
    border-left: 3px solid var(--status-warning-mark);
    background: var(--status-warning-bg);
    font-size: 0.8125rem;
}

/* States what the page is and what it is missing. Not an .alert: those are things
   to act on, and these two are part of the document. */
.report__notice {
    display: flex; align-items: flex-start; gap: 0.375rem;
    margin: 0.875rem 0 0;
    font-size: 0.75rem; color: var(--ink-secondary);
}
.report__notice--gap { margin-top: 0.375rem; }
.report__notice svg { flex: 0 0 auto; margin-top: 1px; }

.report__section {
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--line);
}
.report__section > h2 {
    margin: 0 0 0.75rem;
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}
.report__section h3 {
    margin: 1rem 0 0.5rem;
    font-size: 0.8125rem; font-weight: 700;
    color: var(--ink-secondary);
}
/* Only where the h3 IS the section's first content — "Required for the payer
   submission" under a bare h2. An h3 that follows a block of values needs its full
   margin, or it reads as a caption for the values above it. */
.report__section > h2 + h3 { margin-top: 0.25rem; }

.report__block {
    margin-top: 0.75rem;
    padding: 0.75rem 0.875rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
}
.report__block h3 { margin-top: 0; color: var(--ink); font-size: 0.875rem; }

.report__dl--gap { margin-top: 0.75rem; }
.report__labels { margin-top: 0.875rem; display: grid; gap: 0.25rem; }
.report__address { margin: 0; font-size: 0.875rem; }
.report__none { margin: 0; font-size: 0.8125rem; color: var(--ink-muted); }
.report__meta { font-size: 0.6875rem; color: var(--ink-muted); }
.report__note {
    margin: 0.625rem 0 0;
    font-size: 0.8125rem;
    color: var(--ink-secondary);
}

.report__table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
.report__table th {
    text-align: left;
    padding: 0.375rem 0.5rem;
    border-bottom: 1px solid var(--ink-muted);
    font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.04em;
    color: var(--ink-secondary);
}
.report__table td {
    padding: 0.375rem 0.5rem;
    border-bottom: 1px solid var(--line);
    vertical-align: top;
}

.report__checks { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.375rem; }
.report__checks li { display: flex; align-items: flex-start; gap: 0.5rem; font-size: 0.8125rem; }
/* A drawn box with a text mark, so a tick survives greyscale, a fax, and a
   photocopy — none of which keep a background color. */
.report__box {
    flex: 0 0 auto;
    display: grid; place-items: center;
    width: 13px; height: 13px; margin-top: 2px;
    border: 1px solid var(--ink-secondary);
    border-radius: 2px;
    font-size: 11px; font-weight: 700; line-height: 1;
}

.report__entry { margin-top: 0.875rem; font-size: 0.8125rem; }
.report__entry-head {
    font-size: 0.6875rem; text-transform: uppercase; letter-spacing: 0.03em;
    color: var(--ink-muted);
    margin-bottom: 0.25rem;
}

.report__foot {
    margin-top: 1.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--line);
    font-size: 0.6875rem;
    color: var(--ink-muted);
}

/* Only ever a printed artifact. */
.report__running { display: none; }

@media (max-width: 720px) {
    .report { padding: 1rem 1rem 1.5rem; border-radius: 0; }
    .report__stamp { text-align: left; }
}

/* --------------------------------------------------------------- printing --- */

@media print {
    .sidebar, .topbar, .page-head__actions, .filters, .pagination, .btn { display: none !important; }
    body { background: #fff; font-size: 11pt; }
    .card { border: 1px solid #999; box-shadow: none; break-inside: avoid; }
    .panel, .popover__panel { box-shadow: none; }
    /* A printed chart is a record: the whole phase track belongs on the page, not
       scrolled out of it. */
    .track { overflow-x: visible; }
    .scroll-y { max-height: none; overflow: visible; }
    /* Same reason for the schedule: a printed day is a record of the whole day,
       not of the twelve hours that happened to be scrolled into view. */
    .cal__scroll, .cal__alldaycell { max-height: none; overflow: visible; }
    .cal__week, .cal__cell { break-inside: avoid; }
    /* Forced-colors/print fallback: the status word carries the meaning, since
       the background tint will not survive. */
    .status { border: 1px solid #666; }
    .content { padding: 0; }

    /* The full-record report is the one page designed to end up on paper, so it
       drops its on-screen frame and becomes the sheet itself. */
    @page { margin: 14mm 12mm 16mm; }

    .report {
        max-width: none;
        margin: 0;
        padding: 0;
        border: 0;
        background: transparent;
        font-size: 10pt;
    }
    /* The screen-only action bar above the document, and its heading. */
    .report__bar { display: none !important; }

    /* Never split a heading from what it introduces, or a record from itself. */
    .report__section { break-inside: auto; page-break-inside: auto; }
    .report__section > h2 { break-after: avoid; page-break-after: avoid; }
    .report__section h3 { break-after: avoid; page-break-after: avoid; }
    .report__block, .report__entry, .report__checks li { break-inside: avoid; page-break-inside: avoid; }
    .report__table tr { break-inside: avoid; page-break-inside: avoid; }
    /* A grid row is free to break between items, which put "Email consent / SMS
       consent" on one sheet and both answers on the next. Each label keeps its
       value. */
    .dl__item { break-inside: avoid; page-break-inside: avoid; }
    /* Long tables repeat their header on each sheet — the one thing print does
       give us for free. */
    .report__table thead { display: table-header-group; }

    .report__head { border-bottom: 2px solid #000; }
    .report__notice { color: #333; }
    .report__foot { color: #333; }

    /* Identity on every sheet. Chrome and Edge repeat a fixed element per page,
       which is what the Save-as-PDF path here goes through; elsewhere it lands
       once and the footer covers the rest. */
    .report__running {
        display: block;
        position: fixed;
        bottom: 0; left: 0; right: 0;
        font-size: 8pt;
        color: #555;
        border-top: 1px solid #bbb;
        padding-top: 2mm;
    }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* High-contrast / forced-colors mode: let the OS palette through, and lean on
   the glyph + label pairing that status chips already carry. */
@media (forced-colors: active) {
    .status, .badge, .chip, .card, .btn, .panel, .req { border: 1px solid CanvasText; }
    /* --line-strong does not survive, and without it the bonded search control
       loses the one edge that marks it as a field. */
    .topbar__search { border: 1px solid CanvasText; }
    .chart__bar { fill: CanvasText; }
    .meter__fill { background: Highlight; }
    /* The tick and the number survive; the fill colors do not. */
    .track__marker { border: 2px solid CanvasText; }
    .track__step--current .track__marker { border-color: Highlight; }
    /* The selected calendar filter, and everything on the calendar marked by a
       tint alone, need a border here or the state is simply lost. */
    .chip--on { border: 2px solid Highlight; }
    .cal__col--today, .cal__cell--today { border: 2px solid Highlight; }
    .cal__dayhead--today .cal__daynum,
    .cal__celldate--today { border: 2px solid Highlight; }
    /* The out-of-hours shading and the per-calendar colors do not survive, so the
       grid keeps its rules and every entry keeps its box. */
    .cal__closed { background: transparent; border-top: 1px solid CanvasText; opacity: 1; }
    .cal-block, .cal-chip--band { border: 1px solid CanvasText; }
    .cal-block__edge { background: CanvasText; }
    .cal__now { border-top-color: LinkText; }
}
