/* ─── Design tokens (Customer Communications) ─────────────────────────── */
/*
   Breakpoints — use these three unless a component has a hard physical
   constraint (Client_UI_Design_Review_2026-07-04 P2-4):
     • 600px    — phone → tablet (bottom nav, FAB, stacked layouts)
     • 767.98px — data-list table → cards (Bootstrap md boundary)
     • 900px    — dashboard grids collapse to one column
*/
:root {
    /* BizGo brand neutrals — cool Ink #0F1629 / Paper #EEF1F5 (Claude Design
       handoff "BizGo · Logo & Icon Set"). Lightness relationships mirror the
       previous warm ramp; only the hue shifts warm → cool. */
    --bg: #eef1f5;
    --surface: #ffffff;
    --surface-2: #e4e8ef;
    --surface-3: #d7dde8;
    --ink: #0f1629;
    --ink-2: #414a60;
    --muted: #737d92;
    --line: #dde2ea;
    --line-strong: #c7cedb;
    /* --accent* aliases the brand primary (defined in brand.css). Existing
       code that references --accent keeps working; new code should prefer
       --primary / --primary-soft / --primary-ink directly. */
    --accent: var(--primary);
    --accent-soft: var(--primary-soft);
    --accent-ink: var(--primary-ink);
    --info: oklch(0.58 0.10 240);
    --info-soft: oklch(0.95 0.03 240);
    --info-ink: oklch(0.42 0.10 240);
    /* Channel identity — WhatsApp green / Email blue (Email reuses --info*). */
    --wa: #1fab54;
    --wa-soft: #e7f8ef;
    --wa-ink: #0c7a3f;
    --warn: oklch(0.65 0.13 50);
    --warn-soft: oklch(0.95 0.04 50);
    --warn-ink: oklch(0.45 0.13 50);
    --danger: oklch(0.55 0.15 25);
    --danger-soft: oklch(0.95 0.04 25);
    --danger-ink: oklch(0.45 0.15 25);
    --shadow-sm: 0 1px 2px rgba(15, 22, 41, 0.05), 0 0 0 0.5px rgba(15, 22, 41, 0.04);
    --shadow: 0 1px 2px rgba(15, 22, 41, 0.05), 0 6px 24px -8px rgba(15, 22, 41, 0.12);
    --shadow-pop: 0 8px 32px -4px rgba(15, 22, 41, 0.20), 0 2px 4px rgba(15, 22, 41, 0.07);
    --backdrop: rgba(15, 22, 41, 0.5);
    --radius: 10px;
    --radius-sm: 6px;
    --radius-lg: 16px;

    --pad-1: 6px;
    --pad-2: 10px;
    --pad-3: 14px;
    --pad-4: 20px;
    --pad-5: 28px;

    --row-h: 64px;
    --header-h: 56px;
    --rail-w: 64px;

    /* Bootstrap overrides */
    --bs-body-bg: var(--bg);
    --bs-body-color: var(--ink);
    --bs-body-font-family: 'Geist', -apple-system, system-ui, sans-serif;
    --bs-body-font-size: 14px;
    --bs-body-line-height: 1.45;
    --bs-border-color: var(--line);
    --bs-primary: var(--primary-solid);
    --bs-link-color: var(--accent-ink);
    --bs-link-hover-color: var(--ink);
    --bs-secondary-color: var(--muted);

    /* Bootstrap 5.3 "subtle" utilities (bg-*-subtle / text-*-emphasis) resolve
       these vars at use time, so mapping them onto the semantic tokens once
       here themes both light and dark automatically. */
    --bs-primary-bg-subtle: var(--accent-soft);
    --bs-primary-text-emphasis: var(--accent-ink);
    --bs-success-bg-subtle: var(--accent-soft);
    --bs-success-text-emphasis: var(--accent-ink);
    --bs-info-bg-subtle: var(--info-soft);
    --bs-info-text-emphasis: var(--info-ink);
    --bs-warning-bg-subtle: var(--warn-soft);
    --bs-warning-text-emphasis: var(--warn-ink);
    --bs-danger-bg-subtle: var(--danger-soft);
    --bs-danger-text-emphasis: var(--danger-ink);
    --bs-secondary-bg-subtle: var(--surface-2);
    --bs-secondary-text-emphasis: var(--ink-2);
    --bs-light-bg-subtle: var(--surface-2);
    --bs-light-text-emphasis: var(--ink-2);
    --bs-dark-bg-subtle: var(--surface-3);
    --bs-dark-text-emphasis: var(--ink);
}

[data-theme="dark"] {
    /* Cool dark ramp anchored on BizGo Ink #0F1629; surface-2 = the brand
       sheet's --ink-2 (#1b2540). Paper #EEF1F5 carries the text. */
    --bg: #0b0f1c;
    --surface: #141a2c;
    --surface-2: #1b2540;
    --surface-3: #26314f;
    --ink: #eef1f5;
    --ink-2: #c2c8d6;
    --muted: #808aa0;
    --line: #232c44;
    --line-strong: #36405c;
    --accent: var(--primary);
    --accent-soft: var(--primary-soft);
    --accent-ink: var(--primary-ink);
    --info: oklch(0.72 0.10 240);
    --info-soft: oklch(0.30 0.05 240);
    --info-ink: oklch(0.88 0.08 240);
    /* Channel identity — WhatsApp green / Email blue (Email reuses --info*). */
    --wa: #25d366;
    --wa-soft: #16321f;
    --wa-ink: #4ade80;
    --warn: oklch(0.75 0.13 70);
    --warn-soft: oklch(0.30 0.06 70);
    --warn-ink: oklch(0.88 0.10 70);
    --danger: oklch(0.70 0.15 25);
    --danger-soft: oklch(0.30 0.06 25);
    --danger-ink: oklch(0.88 0.10 25);
    --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.45), 0 0 0 0.5px rgba(0, 0, 0, 0.5);
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.45), 0 6px 24px -8px rgba(0, 0, 0, 0.55);
    --shadow-pop: 0 8px 32px -4px rgba(0, 0, 0, 0.65), 0 2px 4px rgba(0, 0, 0, 0.45);
    /* Slightly lighter veil than light mode — the dark bg already recedes. */
    --backdrop: rgba(0, 0, 0, 0.55);

    /* Bootstrap overrides for dark mode */
    --bs-body-bg: var(--bg);
    --bs-body-color: var(--ink);
    --bs-border-color: var(--line);
    --bs-primary: var(--primary-solid);
    --bs-link-color: var(--accent-ink);
    --bs-link-hover-color: var(--ink);
    --bs-secondary-color: var(--muted);
}

[data-theme="dark"] body {
    color-scheme: dark;
}

/* ─── Base typography ─────────────────────────────────────────────────── */
* { box-sizing: border-box; }

html, body {
    font-family: 'Geist', -apple-system, system-ui, sans-serif;
    font-feature-settings: "ss01", "cv11";
    background: var(--bg);
    color: var(--ink);
    font-size: 14px;
    line-height: 1.45;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

/* Backstop against sideways page scroll.
   A document wider than the phone widens the *layout viewport*, and the fixed
   chrome is measured against that: .bottom-nav's left:0/right:0 stretches to
   the document width and its five items spread off-screen, and panning right
   slides the sticky .top-row away — which is what "the bars scroll halfway"
   actually is. Individual offenders still get fixed at source (wide tables
   belong in .table-responsive / .table-scroll); this only guarantees a new one
   can never take the whole shell with it.

   `clip`, not `hidden`: `hidden` would make the root a scroll container and
   break position:sticky on .top-row. `clip` just clips. */
html {
    overflow-x: clip;
}

h1, h2, h3, h4, h5, h6 { color: var(--ink); }
h1 { font-size: 26px; font-weight: 600; letter-spacing: -0.025em; line-height: 1.15; }
h2 { font-size: 19px; font-weight: 600; letter-spacing: -0.015em; line-height: 1.2; }
h3 { font-size: 15px; font-weight: 600; letter-spacing: -0.005em; }

h1:focus { outline: none; }

a, .btn-link {
    color: var(--accent-ink);
    text-decoration: none;
}
a:hover { color: var(--ink); text-decoration: underline; }

.mono { font-family: 'Geist Mono', ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.tnum { font-variant-numeric: tabular-nums; }
.muted, .text-muted { color: var(--muted) !important; }

.label-eyebrow {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--primary-ink);
}

/* ─── Buttons (Bootstrap-overrides) ───────────────────────────────────── */
.btn {
    font-family: inherit;
    border-radius: 8px;
    font-size: 13px;
    font-weight: 500;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink);
    padding: 6px 12px;
    transition: background 60ms, border-color 60ms;
    box-shadow: none;
}
.btn:hover { background: var(--surface-2); color: var(--ink); }
.btn:focus, .btn:active:focus,
.btn-link.nav-link:focus,
.form-control:focus,
.form-check-input:focus,
.form-select:focus {
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--accent) 25%, transparent);
    border-color: var(--accent);
    outline: none;
}

/* Primary action — the BizGo brand emerald (deep, theme-stable --primary-solid
   so white text stays legible in dark mode). This is the app's default CTA. */
.btn-primary {
    background: var(--primary-solid);
    color: var(--primary-on);
    border-color: var(--primary-solid);
}
.btn-primary:hover, .btn-primary:focus, .btn-primary:active {
    background: var(--primary-solid-hover);
    color: var(--primary-on);
    border-color: var(--primary-solid-hover);
}

/* Success reads as the brand emerald too (primary≈success is intentional —
   the only colour-divergent action is .btn-danger). */
.btn-success {
    background: var(--primary-solid);
    color: var(--primary-on);
    border-color: var(--primary-solid);
}
.btn-success:hover { background: var(--primary-solid-hover); border-color: var(--primary-solid-hover); color: var(--primary-on); }

.btn-outline-primary {
    background: var(--surface);
    color: var(--ink);
    border-color: var(--line-strong);
}
.btn-outline-primary:hover { background: var(--surface-2); color: var(--ink); border-color: var(--line-strong); }

.btn-outline-secondary {
    background: var(--surface);
    color: var(--ink-2);
    border-color: var(--line);
}
.btn-outline-secondary:hover { background: var(--surface-2); color: var(--ink); border-color: var(--line-strong); }

.btn-danger {
    background: var(--danger);
    border-color: var(--danger);
    color: #fff;
}

/* Filled secondary reads like the outline variant — every ConfirmModal footer
   uses .btn-secondary, which Bootstrap otherwise paints native gray. */
.btn-secondary {
    background: var(--surface);
    color: var(--ink-2);
    border-color: var(--line);
}
.btn-secondary:hover, .btn-secondary:focus, .btn-secondary:active {
    background: var(--surface-2);
    color: var(--ink);
    border-color: var(--line-strong);
}

/* Bootstrap's .btn-close ✕ is a black SVG data-URI; invert it on dark surfaces. */
[data-theme="dark"] .btn-close {
    filter: invert(1) grayscale(100%) brightness(180%);
}

.btn-sm { padding: 4px 10px; font-size: 12px; border-radius: 7px; }
.btn-lg { padding: 8px 16px; font-size: 14px; border-radius: 10px; }

/* ─── Forms ──────────────────────────────────────────────────────────── */
.form-control, .form-select {
    font-family: inherit;
    border-radius: 8px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink);
    font-size: 14px;
    padding: 8px 12px;
    transition: border-color 60ms;
}
.form-control:focus, .form-select:focus {
    background: var(--surface);
    color: var(--ink);
    border-color: var(--accent);
}
.form-control::placeholder { color: var(--muted); }
/* File inputs — Bootstrap paints the native picker button its own light gray,
   which glares on dark surfaces; restyle it like a quiet secondary button. */
.form-control[type="file"] { overflow: hidden; }
.form-control::file-selector-button {
    padding: 8px 12px;
    margin: -8px 12px -8px -12px;
    background: var(--surface-2);
    color: var(--ink);
    border: 0;
    border-right: 1px solid var(--line);
    border-radius: 0;
    font-size: 13px;
    font-weight: 500;
    transition: background 60ms;
}
.form-control:hover:not(:disabled):not([readonly])::file-selector-button {
    background: color-mix(in oklab, var(--surface-2) 85%, var(--ink) 8%);
}
.form-control-sm, .form-select-sm { font-size: 13px; padding: 6px 10px; border-radius: 7px; }
.form-control-sm::file-selector-button { padding: 6px 10px; margin: -6px 10px -6px -10px; font-size: 12px; }
.form-label, .form-label.small {
    font-size: 11px;
    font-weight: 500;
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    margin-bottom: 4px;
}
.input-group-text {
    background: var(--surface-2);
    border-color: var(--line);
    color: var(--muted);
    font-size: 13px;
}

/* ─── Cards ──────────────────────────────────────────────────────────── */
.card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    box-shadow: var(--shadow-sm);
}
.card-body { padding: 18px 20px; }
.card-header {
    background: transparent;
    border-bottom: 1px solid var(--line);
    padding: 14px 20px;
    font-weight: 600;
    font-size: 13px;
}

/* ─── Tables ─────────────────────────────────────────────────────────── */
.table {
    /* Bootstrap re-applies `color: var(--bs-table-color)` on every cell, so
       overriding the variable is what makes dark mode actually pick up --ink.
       Setting `color` on .table alone gets clobbered by the cell rule. */
    --bs-table-color: var(--ink);
    --bs-table-color-state: var(--ink);
    --bs-table-color-type: var(--ink);
    --bs-table-bg: var(--surface);
    --bs-table-striped-color: var(--ink);
    --bs-table-striped-bg: var(--surface-2);
    --bs-table-hover-color: var(--ink);
    --bs-table-hover-bg: var(--surface-2);
    color: var(--ink);
    border-color: var(--line);
    margin-bottom: 0;
}
.table > :not(caption) > * > * {
    background: var(--surface);
    color: var(--ink);
    border-bottom-color: var(--line);
}
.table thead th {
    background: var(--bg);
    border-bottom: 1px solid var(--line);
    font-size: 11px;
    font-weight: 500;
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 10px 14px;
}
.table tbody td { padding: 12px 14px; font-size: 13px; vertical-align: middle; border-bottom: 1px solid var(--line); }
.table-hover > tbody > tr:hover > * { background: var(--surface-2); color: var(--ink); }
/* Clickable rows: link the primary cell for keyboard/a11y, keep the row
   @onclick as pointer convenience, and mark it with this class. */
.tr-link { cursor: pointer; }
/* Card chrome around a table, PLUS the horizontal scroll its Bootstrap name
   promises. The shorthand `overflow: hidden` that used to live here loaded
   after bootstrap.min.css and so beat its `overflow-x: auto`: a table wider
   than the phone was silently cropped, with no way to reach the far columns
   (Inventory lost 473px of them at 360px wide). overflow-y stays hidden so the
   12px radius still clips the first and last rows. */
.table-responsive {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    overflow-x: auto;
    overflow-y: hidden;
}

/* Same scroll, no chrome — for a table that already sits inside a .card or
   .panel, where .table-responsive would draw a second border inside the first. */
.table-scroll { overflow-x: auto; }

/* ─── Badges / Pills ─────────────────────────────────────────────────── */
.badge {
    font-weight: 500;
    font-size: 11px;
    padding: 3px 9px;
    border-radius: 999px;
    letter-spacing: 0;
}
.badge.bg-success { background: var(--accent-soft) !important; color: var(--accent-ink) !important; }
.badge.bg-info    { background: var(--info-soft)   !important; color: var(--info-ink)   !important; }
.badge.bg-warning { background: var(--warn-soft)   !important; color: var(--warn-ink)   !important; }
.badge.bg-danger  { background: var(--danger-soft) !important; color: var(--danger-ink) !important; }
.badge.bg-secondary { background: var(--surface-2) !important; color: var(--ink-2)      !important; }
.badge.bg-primary { background: var(--surface-2) !important; color: var(--ink)          !important; }
.badge.bg-light   { background: var(--surface-2)   !important; color: var(--ink-2)      !important; }
.badge.bg-dark    { background: var(--surface-3)   !important; color: var(--ink)        !important; }
/* Legacy `text-dark` riding on themed badges must not force near-black text
   onto dark surfaces — the badge background rules above pick the ink. */
.badge.bg-light.text-dark, .badge.bg-info.text-dark, .badge.bg-warning.text-dark { color: var(--ink-2) !important; }

.pill {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: 11px;
    font-weight: 500;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--surface-2);
    color: var(--ink-2);
    border: 1px solid var(--line);
    white-space: nowrap;
}
.pill.success { background: var(--accent-soft); color: var(--accent-ink); border-color: transparent; }
.pill.info { background: var(--info-soft); color: var(--info-ink); border-color: transparent; }
.pill.warn { background: var(--warn-soft); color: var(--warn-ink); border-color: transparent; }

/* ─── Metric / stat ──────────────────────────────────────────────────── */
.metric { display: flex; flex-direction: column; gap: 2px; }
.metric-label {
    font-size: 11px;
    font-weight: 500;
    color: var(--muted);
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.metric-value {
    font-size: 28px;
    font-weight: 600;
    letter-spacing: -0.025em;
    line-height: 1.1;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}
.metric-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.metric-bar { margin-top: 8px; height: 4px; border-radius: 2px; background: var(--surface-2); overflow: hidden; }
.metric-bar > i { display: block; height: 100%; background: var(--accent); border-radius: 2px; }

/* ─── App shell helpers ──────────────────────────────────────────────── */
.divider { height: 1px; background: var(--line); margin: 16px 0; }

/* ─── Branded header signature ───────────────────────────────────────────
   Emerald→teal gradient underline on the app top-row and the Staff-portal
   top bar, so the brand is present on the always-visible chrome (not just on
   buttons). Layered as ::after over the existing 1px line; token-driven, so
   it re-tints with the brand inputs + BrandTuner. Both bars are position:
   sticky (a positioned context), so the absolute ::after anchors to them. */
.top-row::after,
.portal-top::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--primary-solid), var(--secondary-solid));
    pointer-events: none;
}

/* ─── Misc / framework hooks ─────────────────────────────────────────── */
.content { padding-top: 1.25rem; }

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

#blazor-error-ui {
    color-scheme: light only;
    background: var(--warn-soft);
    color: var(--ink);
    border-top: 1px solid var(--line);
    bottom: 0;
    box-shadow: var(--shadow-pop);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
    font-size: 13px;
}

#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.5rem; }

.blazor-error-boundary {
    background: var(--danger-soft);
    color: var(--danger);
    border: 1px solid var(--danger);
    border-radius: var(--radius);
    padding: 1rem 1.25rem;
    font-size: 13px;
}
.blazor-error-boundary::after { content: "An error has occurred."; }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto;
}
.loading-progress circle { fill: none; stroke: var(--surface-3); stroke-width: 0.6rem; transform-origin: 50% 50%; transform: rotate(-90deg); }
.loading-progress circle:last-child {
    stroke: var(--accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}
.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: 600;
    color: var(--ink);
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}
.loading-progress-text:after { content: var(--blazor-load-percentage-text, "Loading"); }

code { color: var(--accent-ink); font-family: 'Geist Mono', ui-monospace, monospace; }

/* Pagination */
.page-link {
    color: var(--ink-2);
    background: var(--surface);
    border: 1px solid var(--line);
    font-size: 13px;
}
.page-link:hover { background: var(--surface-2); color: var(--ink); border-color: var(--line); }
.page-item.active .page-link { background: var(--primary-solid); border-color: var(--primary-solid); color: var(--primary-on); }
.page-item.disabled .page-link { color: var(--muted); background: var(--surface); }

/* Modal polish */
/* Backdrop for the shared ModalShell — token-driven veil. */
.modal.app-modal { background: var(--backdrop); }
.modal-content { border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--surface); }
.modal-header { border-bottom: 1px solid var(--line); padding: 16px 20px; }
.modal-body { padding: 16px 20px; }
.modal-footer { border-top: 1px solid var(--line); padding: 12px 20px; gap: 8px; }
.modal-title { font-size: 16px; font-weight: 600; }

/* Alerts */
.alert {
    border-radius: var(--radius);
    border: 1px solid var(--line);
    background: var(--surface-2);
    color: var(--ink);
    font-size: 13px;
    padding: 12px 14px;
}
.alert-danger { background: var(--danger-soft); color: var(--danger-ink); border-color: transparent; }
.alert-success { background: var(--accent-soft); color: var(--accent-ink); border-color: transparent; }
.alert-info { background: var(--info-soft); color: var(--info-ink); border-color: transparent; }
.alert-warning { background: var(--warn-soft); color: var(--warn-ink); border-color: transparent; }

/* ─── Staff portal (Dev_Docs/Staff_Portal_Desktop_Alignment_Plan.md) ────────
   Mobile-first staff app shell + utilities. ALL portal styles live here
   (PortalLayout has no scoped css) so the portal reads as one system:
   frosted sticky top bar, floating pill "dock" bottom nav, and card/list/
   form utilities in the same token language as the main app.

   This block is the <768px shell. The desktop shell — sidebar, wide content
   column, profile dropdown — lives in "§ Staff portal — desktop" further down
   and is purely additive. */

/* Shell */
.portal-page {
    min-height: 100vh;
    min-height: 100dvh;
    background: var(--bg);
    color: var(--ink);
    display: flex;
    flex-direction: column;
}
.portal-page-anon {
    /* No tab bar / top bar on the login + change-password screens. */
    justify-content: center;
}

/* Top bar — frosted; the brand gradient underline comes from .portal-top::after. */
.portal-top {
    position: sticky;
    top: 0;
    z-index: 30;
    height: var(--header-h);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 0 max(14px, env(safe-area-inset-right)) 0 max(14px, env(safe-area-inset-left));
    background: color-mix(in srgb, var(--bg) 82%, transparent);
    -webkit-backdrop-filter: blur(14px) saturate(1.5);
    backdrop-filter: blur(14px) saturate(1.5);
    border-bottom: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
}
.portal-brand { display: inline-flex; align-items: center; }
.portal-spacer { flex: 1; }
.portal-user {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    max-width: 52vw;
}
.portal-user-name {
    font-size: 13px;
    font-weight: 500;
    color: var(--ink-2);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
@media (max-width: 380px) { .portal-user-name { display: none; } }
.portal-avatar {
    width: 30px;
    height: 30px;
    border-radius: 999px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    background: linear-gradient(135deg, var(--primary-solid), var(--secondary-solid));
    color: #fff;
    font-size: 11.5px;
    font-weight: 700;
    letter-spacing: 0.02em;
    box-shadow: 0 2px 8px -2px color-mix(in oklab, var(--secondary-solid) 55%, transparent);
}
.portal-iconbtn {
    width: 40px;
    height: 40px;
    border: 0;
    background: transparent;
    color: var(--ink-2);
    border-radius: 12px;
    display: grid;
    place-items: center;
    cursor: pointer;
    flex-shrink: 0;
    -webkit-tap-highlight-color: transparent;
    transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.portal-iconbtn:hover { background: var(--surface-2); color: var(--ink); }
.portal-iconbtn:active { background: var(--surface-3); }
.portal-iconbtn-line { border: 1px solid var(--line); background: var(--surface); box-shadow: var(--shadow-sm); }
.portal-iconbtn-danger { color: var(--danger); }
.portal-iconbtn-danger:hover { background: var(--danger-soft); color: var(--danger-ink); }

/* Content column — bottom padding clears the floating dock. */
.portal-main {
    flex: 1;
    width: 100%;
    max-width: 520px;
    margin: 0 auto;
    min-width: 0;
    padding: 16px 16px calc(96px + env(safe-area-inset-bottom));
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
}
.portal-main-anon {
    padding-bottom: 24px;
    display: flex;
    align-items: center;
}

/* Bottom nav — floating frosted dock. */
.portal-tabs {
    position: fixed;
    left: max(12px, env(safe-area-inset-left));
    right: max(12px, env(safe-area-inset-right));
    bottom: max(12px, env(safe-area-inset-bottom));
    z-index: 40;
    margin: 0 auto;
    max-width: 420px;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 2px;
    padding: 6px;
    background: color-mix(in srgb, var(--surface) 86%, transparent);
    -webkit-backdrop-filter: blur(16px) saturate(1.6);
    backdrop-filter: blur(16px) saturate(1.6);
    border: 1px solid color-mix(in oklab, var(--primary-solid) 16%, var(--line));
    border-radius: 22px;
    box-shadow: 0 12px 32px -8px rgba(15, 22, 41, 0.28), 0 2px 8px -2px rgba(15, 22, 41, 0.12);
}
[data-theme="dark"] .portal-tabs {
    background: color-mix(in srgb, var(--surface-2) 86%, transparent);
    box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.6), 0 2px 8px -2px rgba(0, 0, 0, 0.4);
}
.portal-tab {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: 56px;
    padding: 6px 2px;
    border-radius: 16px;
    color: var(--muted);
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: color var(--dur-med) var(--ease-standard),
                background var(--dur-med) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
}
.portal-tab:hover { color: var(--ink); text-decoration: none; }
.portal-tab:active { transform: scale(0.93); }
.portal-tab-icon { display: inline-flex; align-items: center; justify-content: center; }
.portal-tab-icon svg { width: 21px; height: 21px; transition: transform var(--dur-med) var(--ease-standard); }
.portal-tab-label { font-size: 10.5px; font-weight: 600; letter-spacing: 0.01em; line-height: 1; }
.portal-tab.active {
    color: var(--primary-ink);
    background: linear-gradient(135deg,
        color-mix(in oklab, var(--primary-solid) 16%, transparent),
        color-mix(in oklab, var(--secondary-solid) 12%, transparent));
}
.portal-tab.active .portal-tab-icon svg { transform: translateY(-1px); }

/* Page head + back chip */
.portal-page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 4px 2px 14px;
}
.portal-page-title { margin: 0; font-size: 21px; font-weight: 700; letter-spacing: -0.02em; }
.portal-back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px 6px 8px;
    margin-bottom: 12px;
    border-radius: 999px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink-2);
    font-size: 12.5px;
    font-weight: 600;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
}
.portal-back:hover { color: var(--ink); border-color: var(--line-strong); }
.portal-back svg { width: 15px; height: 15px; }

/* Hero — the portal-scale sibling of .home-hero (token-driven, re-tints). */
.portal-hero {
    position: relative;
    overflow: hidden;
    padding: 20px 18px;
    border: 1px solid color-mix(in oklab, var(--primary-solid) 22%, var(--line));
    border-radius: var(--radius-lg);
    background:
        radial-gradient(130% 180% at 100% 0%, color-mix(in oklab, var(--secondary-solid) 16%, transparent) 0%, transparent 55%),
        radial-gradient(140% 200% at 0% 120%, color-mix(in oklab, var(--primary-solid) 13%, transparent) 0%, transparent 58%),
        var(--surface);
    box-shadow: var(--shadow-sm);
}
.portal-hero::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: linear-gradient(90deg, var(--primary-solid), var(--secondary-solid));
}
.portal-hero-eyebrow {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--primary-ink);
    margin-bottom: 4px;
}
.portal-hero h1 { margin: 0; font-size: 22px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; }
.portal-hero-sub { margin: 4px 0 0; font-size: 13px; color: var(--muted); }

/* Cards */
.portal-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    box-shadow: var(--shadow-sm);
}
.portal-card-title { margin: 0; font-size: 20px; font-weight: 700; letter-spacing: -0.01em; }
.portal-card-tight { gap: 8px; }
.portal-card-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.portal-card-label {
    margin: 0;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    color: var(--muted);
    display: inline-flex;
    align-items: center;
    gap: 6px;
}
.portal-card-label svg { width: 14px; height: 14px; color: var(--primary-ink); }
.portal-link {
    font-size: 12.5px;
    font-weight: 600;
    color: var(--primary-ink);
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    gap: 2px;
    white-space: nowrap;
}
.portal-link:hover { color: var(--ink); }
.portal-link svg { width: 14px; height: 14px; }
.portal-help { margin: 0; color: var(--muted); font-size: 14px; line-height: 1.4; }
.portal-help-small { font-size: 12px; color: var(--muted); margin-top: 4px; }

/* Forms */
.portal-card form { display: flex; flex-direction: column; gap: 14px; }
.portal-field { display: flex; flex-direction: column; gap: 6px; }
.portal-label { font-size: 13px; font-weight: 600; }
.portal-input {
    min-height: 44px;
    width: 100%;
    padding: 0 12px;
    border-radius: 12px;
    border: 1px solid var(--line);
    background: var(--bg);
    color: var(--ink);
    font-size: 16px; /* 16px keeps iOS from zooming on focus */
    transition: border-color var(--dur-fast) var(--ease-standard), box-shadow var(--dur-fast) var(--ease-standard);
}
textarea.portal-input { min-height: 88px; padding: 10px 12px; line-height: 1.4; resize: vertical; }
.portal-input:focus {
    outline: none;
    border-color: var(--primary-solid);
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary-solid) 22%, transparent);
}

/* Buttons */
.portal-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    min-height: 46px;
    width: 100%;
    border: 0;
    border-radius: 12px;
    padding: 0 16px;
    font-weight: 600;
    font-size: 15.5px;
    cursor: pointer;
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.portal-btn:active { transform: scale(0.985); }
.portal-btn svg { width: 18px; height: 18px; }
.portal-btn-primary {
    background: linear-gradient(118deg, var(--primary-solid), var(--secondary-solid));
    color: var(--primary-on);
    box-shadow: 0 8px 20px -8px color-mix(in oklab, var(--secondary-solid) 70%, transparent);
}
.portal-btn-primary:hover, .portal-btn-primary:focus {
    background: linear-gradient(118deg, var(--primary-solid-hover), var(--secondary-solid-hover));
    color: var(--primary-on);
}
.portal-btn-primary[disabled] { opacity: 0.55; cursor: not-allowed; box-shadow: none; }
.portal-btn-sm { min-height: 38px; width: auto; font-size: 13.5px; border-radius: 10px; padding: 0 14px; }
.portal-btn-sm svg { width: 15px; height: 15px; }
.portal-btn-ghost { background: var(--surface); color: var(--ink-2); border: 1px solid var(--line); }
.portal-btn-ghost:hover { background: var(--surface-2); color: var(--ink); }

/* Tappable list rows */
.portal-list {
    list-style: none;
    margin: 0;
    padding: 0;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
}
.portal-list > * + * { border-top: 1px solid var(--line); }
.portal-list-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 13px 14px;
    background: transparent;
    border: 0;
    text-align: left;
    color: var(--ink);
    text-decoration: none;
    font: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: background var(--dur-fast) var(--ease-standard);
}
a.portal-list-item, button.portal-list-item { cursor: pointer; }
a.portal-list-item:hover, button.portal-list-item:hover,
a.portal-list-item:active, button.portal-list-item:active { background: var(--surface-2); color: var(--ink); }
.portal-list-item.is-wrap { flex-wrap: wrap; }
.portal-row-full { flex-basis: 100%; }
.portal-row-icon {
    width: 38px;
    height: 38px;
    border-radius: 12px;
    flex-shrink: 0;
    display: grid;
    place-items: center;
    color: var(--primary-ink);
    background: linear-gradient(135deg,
        color-mix(in oklab, var(--primary-solid) 15%, transparent),
        color-mix(in oklab, var(--secondary-solid) 11%, transparent));
}
.portal-row-icon svg { width: 19px; height: 19px; }
.portal-row-main { flex: 1; min-width: 0; }
.portal-row-title { font-weight: 600; font-size: 14px; line-height: 1.3; }
.portal-row-sub { font-size: 12px; color: var(--muted); margin-top: 1px; }
.portal-row-end { flex-shrink: 0; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; text-align: right; }
.portal-row-amount { font-weight: 700; font-variant-numeric: tabular-nums; }
.portal-chevron { color: var(--muted); flex-shrink: 0; display: inline-flex; }
.portal-chevron svg { width: 16px; height: 16px; }
/* Borderless tappable row for use inside a .portal-card */
.portal-row-flush {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 -8px;
    padding: 8px;
    border: 0;
    border-radius: 10px;
    background: transparent;
    color: var(--ink);
    text-align: left;
    font: inherit;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background var(--dur-fast) var(--ease-standard);
}
.portal-row-flush:hover, .portal-row-flush:active { background: var(--surface-2); }

/* Key-value rows (pay / payslip breakdowns) */
.portal-kv { display: flex; flex-direction: column; }
.portal-kv-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 0;
}
.portal-kv-row + .portal-kv-row { border-top: 1px dashed var(--line); }
.portal-kv-key { font-size: 13px; color: var(--muted); }
.portal-kv-val { font-weight: 600; font-size: 14px; text-align: right; font-variant-numeric: tabular-nums; }
.portal-kv-row.portal-kv-total { border-top: 1px solid var(--line-strong); margin-top: 2px; padding-top: 10px; }
.portal-kv-total .portal-kv-key { color: var(--ink); font-weight: 700; font-size: 14px; }
.portal-kv-total .portal-kv-val { font-size: 17px; font-weight: 800; color: var(--primary-ink); }

/* Leave balance meter */
.portal-meter { height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
.portal-meter > i {
    display: block;
    height: 100%;
    border-radius: 3px;
    background: linear-gradient(90deg, var(--primary-solid), var(--secondary-solid));
}
.portal-bal-row { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.portal-bal-days { font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.portal-bal-unit { font-size: 12px; font-weight: 600; color: var(--muted); }
.portal-bal-sub { font-size: 12px; color: var(--muted); }

/* Quick-link tiles */
.portal-quick { display: grid; grid-template-columns: repeat(2, 1fr); gap: 10px; }
.portal-quick-tile {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 14px;
    color: var(--ink);
    font-weight: 600;
    font-size: 13.5px;
    text-decoration: none;
    box-shadow: var(--shadow-sm);
    -webkit-tap-highlight-color: transparent;
    transition: transform var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
}
.portal-quick-tile:hover { border-color: color-mix(in oklab, var(--primary-solid) 35%, var(--line)); color: var(--ink); }
.portal-quick-tile:active { transform: scale(0.97); }

/* Checklist + attachments (job detail) */
.portal-check.form-check { display: flex; align-items: flex-start; gap: 10px; padding: 9px 0; margin: 0; }
.portal-check + .portal-check { border-top: 1px dashed var(--line); }
.portal-check .form-check-input { width: 20px; height: 20px; margin: 0; flex-shrink: 0; }
.portal-check .form-check-input:checked { background-color: var(--primary-solid); border-color: var(--primary-solid); }
.portal-check .form-check-label { padding-top: 1px; }
.portal-check .form-check-input:checked ~ .form-check-label { color: var(--muted); text-decoration: line-through; }
.portal-attachment {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    max-width: 100%;
    padding: 8px 12px;
    border-radius: 12px;
    border: 1px solid var(--line);
    background: var(--bg);
    color: var(--ink-2);
    font-size: 13px;
    font-weight: 500;
    text-decoration: none;
}
.portal-attachment:hover { border-color: var(--line-strong); background: var(--surface-2); color: var(--ink); }
.portal-attachment svg { width: 15px; height: 15px; color: var(--primary-ink); flex-shrink: 0; }
.portal-attachment span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Inset sub-card (form line items, pickers) */
.portal-subcard {
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 10px;
}

/* Auth (login / change password) */
.portal-auth { width: 100%; max-width: 400px; margin: 0 auto; }
.portal-auth-brand { display: flex; justify-content: center; margin-bottom: 18px; }
.portal-auth .portal-card { box-shadow: var(--shadow); padding: 20px; gap: 14px; }

/* "Keep me signed in" row inside the portal sign-in card. */
.portal-check {
    display: flex;
    align-items: center;
    gap: 9px;
    font-size: 13px;
    color: var(--ink-2, var(--ink));
    cursor: pointer;
    user-select: none;
}
.portal-check input[type="checkbox"] {
    width: 17px;
    height: 17px;
    flex: 0 0 auto;
    accent-color: var(--primary-solid);
    cursor: pointer;
}

/* Identity row on the "already signed in" interstitial (Shared/AuthGate.razor). */
.portal-identity {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface-2);
    min-width: 0;
}
.portal-identity-name {
    font-weight: 600;
    font-size: 14px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ─── Staff messages — bubbles + composer (Docs/Staff_Portal_Plan.md §5.3) ── */
.staff-thread-pane {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 12px 0;
    max-height: 60vh;
    overflow-y: auto;
}
.staff-thread-pane-portal {
    max-height: calc(100vh - 320px);
}
.staff-bubble-row { display: flex; }
.staff-bubble-row.is-outbound { justify-content: flex-end; }
.staff-bubble-row.is-inbound { justify-content: flex-start; }
.staff-bubble {
    max-width: 80%;
    padding: 8px 12px;
    border-radius: 14px;
    line-height: 1.35;
    font-size: 14px;
    white-space: pre-wrap;
    word-wrap: break-word;
}
.staff-bubble-out {
    background: var(--primary-solid);
    color: var(--primary-on);
    border-bottom-right-radius: 4px;
}
.staff-bubble-in {
    background: var(--surface-2);
    color: var(--ink);
    border-bottom-left-radius: 4px;
}
.staff-bubble-body { margin-bottom: 2px; }
.staff-bubble-meta { display: flex; gap: 6px; align-items: center; font-size: 11px; opacity: 0.85; }
.staff-bubble-tick { font-weight: 600; }

.staff-composer {
    display: flex;
    gap: 8px;
    align-items: flex-end;
    padding: 8px 0;
}
.staff-composer-input {
    flex: 1;
    min-height: 44px;
    border-radius: 10px;
    border: 1px solid var(--line);
    background: var(--bg);
    padding: 10px 12px;
    font-size: 16px;
    resize: vertical;
}
.staff-composer-input:focus { outline: 2px solid var(--accent); outline-offset: 0; border-color: transparent; }
.staff-composer-send {
    min-height: 44px;
    padding: 0 20px;
    border-radius: 10px;
    border: 0;
    background: var(--primary-solid);
    color: var(--primary-on);
    font-weight: 600;
    cursor: pointer;
}
.staff-composer-send[disabled] { opacity: 0.5; cursor: not-allowed; }

.staff-thread-list { margin-top: 12px; }
.staff-thread-row { transition: background-color 0.15s; }
.staff-thread-row:hover { background: var(--surface-2); }

/* ─── Portal jobs calendar ───────────────────────────────────────────────── */
.portal-jobs-calendar {
    display: flex;
    flex-direction: column;
    gap: 12px;
}
.portal-jobs-day {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
    padding: 12px;
    box-shadow: var(--shadow-sm);
}
.portal-jobs-day.is-today { border-color: color-mix(in oklab, var(--primary-solid) 45%, var(--line)); }
.portal-jobs-day-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    font-weight: 600;
    font-size: 11.5px;
    color: var(--muted);
    margin-bottom: 8px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.portal-today-chip {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.05em;
    padding: 2px 8px;
    border-radius: 999px;
    color: var(--primary-on);
    background: linear-gradient(118deg, var(--primary-solid), var(--secondary-solid));
}
.portal-jobs-empty { font-size: 12.5px; color: var(--muted); padding: 2px 0; }
.portal-jobs-card {
    position: relative;
    display: block;
    width: 100%;
    text-align: left;
    background: var(--surface-2);
    border: 0;
    border-radius: 12px;
    padding: 10px 12px 10px 16px;
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
    overflow: hidden;
    font: inherit;
    -webkit-tap-highlight-color: transparent;
    transition: background var(--dur-fast) var(--ease-standard), transform var(--dur-fast) var(--ease-standard);
}
.portal-jobs-card + .portal-jobs-card { margin-top: 8px; }
.portal-jobs-card::before {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background: linear-gradient(180deg, var(--primary-solid), var(--secondary-solid));
}
.portal-jobs-card:hover { background: var(--surface-3, var(--surface-2)); color: var(--ink); }
.portal-jobs-card:active { transform: scale(0.985); }
.portal-jobs-card-time {
    font-size: 12px;
    font-weight: 700;
    color: var(--primary-ink);
    font-variant-numeric: tabular-nums;
}
.portal-jobs-card-title { font-weight: 600; font-size: 14px; margin-top: 1px; }
.portal-jobs-card-meta {
    font-size: 12px;
    color: var(--muted);
    margin-top: 3px;
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
}
.portal-range { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.portal-range-label { font-weight: 600; font-size: 13.5px; }
/* Full-width segmented control (portal view switchers) */
.portal-seg { display: flex; width: 100%; }
.portal-seg > button { flex: 1; padding: 8px 12px; font-size: 13px; }
.text-prewrap { white-space: pre-wrap; }

/* ─── Staff portal — pointer + keyboard affordances ─────────────────────────
   The portal was built tap-first: no focus rings, and :active scale transforms
   that feel twitchy under a mouse. These rules apply at ALL widths — a phone
   with a keyboard deserves the ring too. See
   Dev_Docs/Staff_Portal_Desktop_Alignment_Plan.md §7 Phase 1.5. */

.portal-tab:focus-visible,
.portal-quick-tile:focus-visible,
.portal-list-item:focus-visible,
.portal-row-flush:focus-visible,
.portal-jobs-card:focus-visible,
.portal-back:focus-visible,
.portal-btn:focus-visible,
.portal-iconbtn:focus-visible,
.portal-attachment:focus-visible,
.portal-profile-trigger:focus-visible,
.portal-profile-item:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* Press-scale is a touch idiom. Keep it where there is no hover pointer; drop
   it on mouse/trackpad, where a click that shrinks the target reads as a bug. */
@media (hover: hover) and (pointer: fine) {
    .portal-tab:active,
    .portal-quick-tile:active,
    .portal-btn:active,
    .portal-jobs-card:active {
        transform: none;
    }
}

/* ─── Staff portal — profile dropdown ───────────────────────────────────────
   Rendered by PortalProfileMenu, desktop-only (PortalLayout gates it behind
   d-none d-md-flex). Same shape as MainLayout's owner dropdown. */

.portal-profile { position: relative; }

.portal-profile-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    max-width: 240px;
    padding: 4px 10px 4px 4px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--surface);
    color: var(--ink);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard),
                border-color var(--dur-fast) var(--ease-standard);
}
.portal-profile-trigger:hover { background: var(--surface-2); border-color: var(--line-strong); }
.portal-profile-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.portal-profile-caret { flex-shrink: 0; color: var(--muted); }
.portal-profile-trigger[aria-expanded="true"] .portal-profile-caret { transform: rotate(180deg); }

/* Sits above the backdrop (z 60 vs 50) so the menu itself stays clickable. */
.portal-profile-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    z-index: 60;
    min-width: 232px;
    padding: 6px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    box-shadow: var(--shadow-pop);
    animation: portal-profile-in 120ms var(--ease-standard);
}
@keyframes portal-profile-in {
    from { opacity: 0; transform: translateY(-4px); }
    to   { opacity: 1; transform: translateY(0); }
}
.portal-profile-backdrop { position: fixed; inset: 0; z-index: 50; background: transparent; }

.portal-profile-head { padding: 8px 10px 10px; border-bottom: 1px solid var(--line); margin-bottom: 6px; }
.portal-profile-head-name { font-weight: 600; font-size: 14px; }
.portal-profile-head-role { font-size: 12px; color: var(--muted); margin-top: 1px; }

.portal-profile-item {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--ink-2);
    font: inherit;
    font-size: 13px;
    font-weight: 500;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    transition: background 60ms, color 60ms;
}
.portal-profile-item:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.portal-profile-item svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--muted); }
.portal-profile-item:hover svg { color: var(--ink); }
.portal-profile-item.is-danger { color: var(--danger); }
.portal-profile-item.is-danger:hover { background: var(--danger-soft); color: var(--danger-ink); }
.portal-profile-item.is-danger svg { color: var(--danger); }
.portal-profile-sep { height: 1px; background: var(--line); margin: 6px 0; }

/* ─── Staff portal — desktop (>=768px) ──────────────────────────────────────
   Dev_Docs/Staff_Portal_Desktop_Alignment_Plan.md. Everything below is additive:
   under 768px the portal renders exactly as it did before this block existed.

   Measurements are MainLayout's on purpose — 56px bar (--header-h), 240px rail,
   24/28 content padding — so an owner who also uses the staff portal, or a staff
   member who has seen the owner app, reads them as one product. */

/* Body wrapper: a plain column on mobile (so .portal-main keeps its flex:1
   against .portal-page), a sidebar + content row on desktop. Mirrors
   MainLayout.razor.css's .page-body exactly. */
.portal-body {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
    min-height: 0;
}

/* Month-grid offset cells exist only for the desktop 7-column calendar. Declared
   before the media query so the desktop override actually wins. */
.portal-jobs-blank { display: none; }

/* Layout primitives, mobile base. Each is a plain stack under 768px and becomes
   its grid inside the media query below. Declared here (not only in the media
   query) so the pages that use them keep their vertical rhythm on a phone — the
   `mt-3` margins these wrappers replaced were doing that job. */
.portal-grid-2,
.portal-split,
.portal-split-even,
.portal-split-main,
.portal-split-aside,
.portal-tile-row,
.portal-toolbar {
    display: flex;
    flex-direction: column;
    gap: 16px;
}
/* 8px matches the `d-flex flex-column gap-2` these wrappers replaced, so the
   phone layout is unchanged to the pixel. */
.portal-tile-row { gap: 8px; }
.portal-toolbar { gap: 8px; margin-bottom: 16px; }

@media (min-width: 768px) {
    /* — Shell — */

    .portal-body { flex-direction: row; }

    .portal-sidebar {
        width: 240px;
        flex-shrink: 0;
        position: sticky;
        top: var(--header-h);
        height: calc(100vh - var(--header-h));
        background: var(--bg);
        border-right: 1px solid var(--line);
    }

    /* The dock is a thumb-reach device. With a sidebar present it is both
       redundant and, floating over a wide viewport, faintly absurd. */
    .portal-tabs { display: none; }

    .portal-main {
        max-width: 1040px;
        padding: 24px 28px;
        padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 28px);
    }
    /* The auth screens keep their own centred column. */
    .portal-main-anon { max-width: 100%; padding-bottom: 24px; }

    /* Brand column mirrors the sidebar so the lockup centres above it. The
       lockup is scaled here rather than by rendering a second <BizGoLogo/> —
       two instances collide on the SVG gradient ids. Matches MainLayout's
       default 30px mark / 22px wordmark; the inline font-size the component
       emits is why the wordmark needs !important. */
    .portal-top { padding: 0 20px 0 0; gap: 0; }
    .portal-brand { width: 240px; justify-content: center; flex-shrink: 0; }
    .portal-brand .bizgo-mark { width: 30px; height: 30px; }
    .portal-brand .bizgo-wm { font-size: 22px !important; }

    /* /me/change-password is an AUTHENTICATED route, so it renders inside the
       full shell — where its .portal-auth-brand lockup repeats the one already
       in the top bar. Scoped to :not(.portal-page-anon) so /me/login, which has
       no top bar, keeps the only brand it has. */
    .portal-page:not(.portal-page-anon) .portal-auth-brand { display: none; }

    /* — Density — */

    .portal-page-head { margin: 0 0 20px; }
    .portal-page-title { font-size: 26px; }
    .portal-card { padding: 20px; }
    .portal-card-title { font-size: 22px; }
    .portal-hero { padding: 26px 24px; }
    .portal-hero h1 { font-size: 28px; }
    .portal-hero-sub { font-size: 14px; }

    /* Submit buttons stop spanning the column (plan §8 decision 4). Pages that
       genuinely want the stretch opt back in with .portal-btn-block.

       width:auto alone is not enough: .portal-card and its form are flex
       columns, and a flex item's default align-self:stretch overrides the
       width. The align-self rules are scoped to those column parents so the
       .portal-page-head buttons — flex ROW items relying on align-items:center
       — keep their vertical centring. */
    .portal-btn { width: auto; min-width: 168px; }
    .portal-btn-block { width: 100%; }
    .portal-btn-sm { min-width: 0; }
    .portal-card > .portal-btn:not(.portal-btn-block),
    .portal-card form > .portal-btn:not(.portal-btn-block),
    .portal-field > .portal-btn:not(.portal-btn-block) { align-self: flex-start; }

    /* 16px only existed to stop iOS zooming on focus; no such tax on desktop. */
    .portal-input { font-size: 14px; min-height: 40px; }
    .staff-composer-input { font-size: 14px; }

    /* Uniform end column turns a stack of list rows into aligned columns without
       needing a second, table-shaped DOM. Scoped to full-width .portal-list rows:
       applying it to every .portal-row-end squeezed the flush rows inside narrow
       cards until the title and the amount overlapped. */
    .portal-list-item { padding: 14px 18px; }
    .portal-list-item .portal-row-end { min-width: 150px; }
    .portal-row-title { font-size: 14.5px; }

    /* Segmented control + range stepper share one toolbar row. The explicit
       row direction matters: the mobile base sets column, and a media query
       that only re-declares display:flex would inherit it. */
    .portal-toolbar {
        display: flex;
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        gap: 16px;
        flex-wrap: wrap;
        margin-bottom: 20px;
    }
    .portal-toolbar .portal-seg { width: auto; flex: 0 0 auto; }
    .portal-toolbar .portal-seg > button { flex: 0 0 auto; min-width: 92px; }
    .portal-toolbar .portal-range { justify-content: flex-start; gap: 12px; margin-bottom: 0 !important; }
    .portal-range-label { font-size: 15px; }

    /* A column of full-width inputs stretched across 1040px is unusable. Forms
       keep a comfortable measure instead of getting a two-column field grid —
       these are 4-6 field forms, and splitting them would cost more in tab-order
       churn than it buys. */
    .portal-form-card { max-width: 640px; }

    /* — Messages — */

    /* The 320px allowance existed to leave room for the dock and composer on a
       phone. On desktop the pane simply fills what the shell leaves it. */
    .staff-thread-pane-portal { max-height: none; height: calc(100vh - var(--header-h) - 260px); min-height: 320px; }
    .portal-chat { max-width: 820px; }
    .staff-bubble { max-width: 68%; }
    .staff-composer { position: sticky; bottom: 0; background: var(--bg); padding-bottom: 4px; }
}

/* ─── Staff portal — multi-column content (>=1024px) ────────────────────────
   768px buys the SHELL (sidebar, no dock, wider column, desktop density). It
   does NOT buy room for two content columns: with 240px of sidebar gone from a
   768px viewport the content column is ~470px, and splitting that in two made
   leave chips wrap mid-row and payslip amounts overlap their titles. Every
   two-up layout therefore waits for 1024px. */
@media (min-width: 1024px) {
    /* Two-up card grid (home). Children opt out with .portal-span-2. */
    .portal-grid-2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }
    .portal-span-2 { grid-column: 1 / -1; }

    /* Main + side rail. Placement is explicit rather than source-order driven so
       pages keep the DOM in mobile reading order and still put the rail right. */
    .portal-split { display: grid; grid-template-columns: minmax(0, 1fr) 320px; gap: 16px; align-items: start; }
    .portal-split-main { grid-column: 1; grid-row: 1; }
    .portal-split-aside { grid-column: 2; grid-row: 1; }

    /* Even split (salary + recent payslips). */
    .portal-split-even { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; align-items: start; }

    /* Quick links 4-up instead of 2-up. */
    .portal-quick { grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 14px; }
    .portal-quick-tile { padding: 16px; font-size: 14px; }

    /* Balance tiles sit in a row rather than stacking. */
    /* 240px, not 220: at 220 a 1040px column fits four tiles of ~235px and the
       longer leave-type names wrap their "d available" onto a second line. */
    .portal-tile-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); gap: 14px; }

    /* Week and month views become a real 7-column calendar; day view stays a
       single column and simply doesn't get .is-grid. Seven columns need the
       1024px content width — at 768px each day cell is ~60px. */
    .portal-jobs-calendar.is-grid {
        display: grid;
        grid-template-columns: repeat(7, minmax(0, 1fr));
        gap: 10px;
        align-items: start;
    }
    .portal-jobs-calendar.is-grid .portal-jobs-day { min-height: 132px; }
    .portal-jobs-calendar.is-grid .portal-jobs-day-header { flex-direction: column; align-items: flex-start; gap: 4px; }
    .portal-jobs-calendar.is-grid .portal-jobs-card { padding: 8px 8px 8px 12px; }
    .portal-jobs-calendar.is-grid .portal-jobs-card-title { font-size: 13px; }
    /* Leading offset cells so the 1st of the month lands under its weekday.
       Hidden below this width, where the calendar is a plain vertical list. */
    .portal-jobs-blank { display: block; }

    /* /me/more is redundant here (every destination is in the sidebar) but still
       routable, so it gets a tile grid rather than one stretched list. */
    .portal-list.is-grid {
        display: grid;
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 12px;
        background: transparent;
        border: 0;
        box-shadow: none;
        overflow: visible;
    }
    .portal-list.is-grid > * + * { border-top: 0; }
    .portal-list.is-grid > li {
        background: var(--surface);
        border: 1px solid var(--line);
        border-radius: var(--radius-lg);
        box-shadow: var(--shadow-sm);
        overflow: hidden;
    }
}

@media (min-width: 1400px) {
    .portal-main { max-width: 1200px; }
}

/* ─── Motion tokens (UI_Modernization_Plan §6) ──────────────────────────── */
:root {
    --ease-standard: cubic-bezier(0.2, 0, 0, 1);
    --dur-fast: 120ms;
    --dur-med: 180ms;
    --dur-slow: 240ms;
}

/* ─── Shared page-shell utilities (UI_Modernization_Plan §4.2) ──────────── */
/* These replace the per-page inline <style> redefinitions of .page-header / .tile / .panel etc. */
.page-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 14px 0;
}
.page-header .page-title {
    margin: 0 0 4px 0;
    font-size: 22px;
    font-weight: 700;
    letter-spacing: -0.015em;
    line-height: 1.15;
    color: var(--ink);
}
.page-header .page-subtitle {
    margin: 0;
    font-size: 13px;
    color: var(--muted);
}
.page-header .page-eyebrow {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--primary-ink);
    margin-bottom: 4px;
}
.page-header .page-actions {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    align-items: center;
}

@media (max-width: 600px) {
    .page-header { gap: 8px; }
    .page-header .page-title { font-size: 19px; }
    .page-header .page-actions { width: 100%; }
}

.tile-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(185px, 100%), 1fr));
    gap: 12px;
    margin-bottom: 20px;
}
.tile-row > * { min-width: 0; }
/* One KPI card for the whole product — the home "pulse" cards (.dsh-stat) and
   the module dashboard tiles (.tile) are the same component. Big values use
   the font's proportional figures (tabular-nums makes 121 look loose at
   display sizes); tabular is reserved for columns that must align. */
.tile, .dsh-stat {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 6px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 14px 16px;
    text-decoration: none;
    color: inherit;
    box-shadow: var(--shadow-sm);
    transition: border-color var(--dur-fast) var(--ease-standard),
                box-shadow var(--dur-fast) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
}
a.tile:hover, a.dsh-stat:hover, .tile.is-link:hover {
    border-color: color-mix(in oklab, var(--accent) 45%, var(--line));
    box-shadow: var(--shadow);
    transform: translateY(-1px);
    color: inherit;
    text-decoration: none;
}
.tile-label, .tile-title, .dsh-stat-label {
    font-size: 11px; font-weight: 600; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--muted);
}
.tile-value, .dsh-stat-value {
    font-size: 26px; font-weight: 650; letter-spacing: -0.02em;
    line-height: 1.1; color: var(--ink);
}
.tile-sub, .dsh-stat-foot {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    font-size: 12px; color: var(--muted); min-height: 18px;
}
.dsh-stat-foot { justify-content: space-between; }
.dsh-stat-spark { display: inline-flex; margin-left: auto; }
/* Value tones — only when the number itself carries state */
.tile.warn .tile-value, .tile.warning .tile-value { color: var(--warn-ink); }
.tile.danger .tile-value  { color: var(--danger-ink); }
.tile.success .tile-value { color: var(--accent-ink); }
.tile.info .tile-value    { color: var(--info-ink); }

/* Period delta — signed, colored by direction × whether up is good */
.delta, .dsh-stat-delta {
    display: inline-flex; align-items: center; gap: 3px;
    font-size: 11.5px; font-weight: 600; color: var(--muted);
}
.delta.up, .delta.ok, .dsh-stat-delta.tone-ok { color: var(--accent-ink); }
.delta.warn, .dsh-stat-delta.tone-warn { color: var(--warn-ink); }
.delta.down, .delta.danger, .dsh-stat-delta.tone-danger { color: var(--danger-ink); }

.panel {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 16px 18px;
    margin-bottom: 16px;
    box-shadow: var(--shadow-sm);
}
.panel-title { margin: 0 0 12px 0; font-size: 14px; font-weight: 600; color: var(--ink); }
/* Dashboard card heading — small caps + optional right-aligned action link.
   Same treatment on home (.dsh-card-head) and every module dashboard. */
.panel-head, .dsh-card-head {
    display: flex; align-items: baseline; justify-content: space-between;
    gap: 10px; margin-bottom: 12px;
}
.panel-head h3, .panel-head .panel-title, .dsh-card-head h3 {
    margin: 0; font-size: 12.5px; font-weight: 600; letter-spacing: 0.05em;
    text-transform: uppercase; color: var(--muted);
}
.panel-action, .dsh-card-action {
    font-size: 12px; font-weight: 600; white-space: nowrap;
    color: var(--accent-ink); text-decoration: none;
}
.panel-action:hover, .dsh-card-action:hover { color: var(--ink); text-decoration: none; }

.empty-hero {
    padding: 32px;
    text-align: center;
    background: var(--surface);
    border: 1px dashed var(--line-strong);
    border-radius: 12px;
    color: var(--muted);
}

/* Narrow page container for focused edit/compose/onboarding surfaces. */
.page-narrow { max-width: 760px; }

/* Template gallery card (invoice + payslip template lists). */
.tpl-card { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 16px; display: flex; gap: 16px; align-items: flex-start; }
.tpl-card .accent { width: 6px; align-self: stretch; border-radius: 3px; }
.tpl-card .body { flex: 1; }
.tpl-card.is-default { box-shadow: 0 0 0 2px color-mix(in oklab, var(--primary) 30%, transparent) inset; }

/* ─── Sticky save/cancel bar for edit & compose forms ─────────────────────
   Promoted from the template editors' .tpl-sticky-actions — the shared
   scaffold is PageHeader → .panel sections → .sticky-actions. Slots:
   .left (context note) and .right (Cancel + primary action). */
.sticky-actions {
    position: sticky;
    bottom: 0;
    z-index: 10;
    margin-top: 16px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 12px 16px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    box-shadow: 0 -4px 16px -8px rgba(15, 22, 41, 0.10);
}
.sticky-actions .left { display: flex; gap: 12px; align-items: center; font-size: 12.5px; color: var(--muted); min-width: 0; }
.sticky-actions .right { display: flex; gap: 8px; margin-left: auto; }

/* ─── Status chips (token-driven, replace hardcoded backgrounds) ──────────
   One system for every status concept — rendered via <StatusPill>. */
.status-pill {
    display: inline-block;
    padding: 2px 8px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 500;
    background: var(--surface-2);
    color: var(--ink-2);
    border: 1px solid var(--line);
    white-space: nowrap;
}
.status-pill.draft,
.status-pill.neutral  { background: var(--surface-2); color: var(--ink-2); }
.status-pill.info     { background: var(--info-soft); color: var(--info-ink); border-color: transparent; }
.status-pill.success  { background: var(--accent-soft); color: var(--accent-ink); border-color: transparent; }
.status-pill.warn     { background: var(--warn-soft); color: var(--warn-ink); border-color: transparent; }
.status-pill.danger   { background: var(--danger-soft); color: var(--danger-ink); border-color: transparent; }
.status-pill.dark     { background: var(--surface-3); color: var(--ink); border-color: transparent; }

/* ─── Entity color chips — tinted-pill treatment for user-picked hex colors
   (customer groups, reply tags). Tint + border carry the hue; text stays
   --ink so any color remains readable in both themes. Set the color via
   style="--chip: #hex". ──────────────────────────────────────────────── */
.color-chip {
    --chip: var(--muted);
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 9px;
    border-radius: 999px;
    font-size: 11.5px;
    font-weight: 500;
    background: color-mix(in srgb, var(--chip) 15%, transparent);
    border: 1px solid color-mix(in srgb, var(--chip) 45%, transparent);
    color: var(--ink);
    text-decoration: none;
    white-space: nowrap;
}
a.color-chip:hover { border-color: var(--chip); color: var(--ink); text-decoration: none; }
.color-chip .chip-dot { width: 8px; height: 8px; border-radius: 999px; background: var(--chip); flex-shrink: 0; }
/* Standalone entity-color dot (e.g. next to a page title). */
.color-dot {
    --chip: var(--muted);
    display: inline-block;
    width: 14px;
    height: 14px;
    border-radius: 999px;
    background: var(--chip);
    flex-shrink: 0;
}

/* ─── Marquee CTA — the emerald→teal brand gradient, for hero/empty-state/
   onboarding moments. Louder than .btn-primary by design. Token-driven
   (re-tints with the brand inputs + BrandTuner); solids keep white text
   legible in both themes. ──────────────────────────────────────────────── */
.btn-cta {
    background: linear-gradient(118deg, var(--primary-solid), var(--secondary-solid));
    border: 1px solid transparent;
    color: var(--primary-on);
    font-weight: 600;
    border-radius: 8px;
    padding: 8px 16px;
    box-shadow: 0 6px 18px -8px color-mix(in oklab, var(--secondary-solid) 65%, transparent);
    transition: background var(--dur-fast) var(--ease-standard);
}
.btn-cta:hover, .btn-cta:focus { background: linear-gradient(118deg, var(--primary-solid-hover), var(--secondary-solid-hover)); border-color: transparent; color: var(--primary-on); }
.btn-cta[disabled] { opacity: 0.55; cursor: not-allowed; }

/* ─── Skeleton screens (UI_Modernization_Plan §4.1) ────────────────────── */
.skeleton {
    background: linear-gradient(90deg, var(--surface-2) 0%, var(--surface-3) 50%, var(--surface-2) 100%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.4s linear infinite;
    border-radius: 8px;
    color: transparent;
}
.skeleton-line { height: 12px; margin: 6px 0; border-radius: 4px; }
.skeleton-block { height: 80px; margin: 8px 0; border-radius: 12px; }
.skeleton-card {
    height: 72px;
    margin: 8px 0;
    border-radius: 12px;
    border: 1px solid var(--line);
}
@keyframes skeleton-shimmer {
    0% { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

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

/* ─── Data-list: table on desktop, cards on mobile (§4.1) ──────────────── */
.data-list { background: transparent; }
.data-list-cards { display: none; flex-direction: column; gap: 8px; }
.data-list-table { display: block; }
@media (max-width: 767.98px) {
    .data-list-table { display: none; }
    .data-list-cards { display: flex; }
}
.data-card {
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    padding: 12px 14px;
    text-decoration: none;
    color: var(--ink);
    display: flex;
    flex-direction: column;
    gap: 6px;
    transition: background var(--dur-fast) var(--ease-standard);
}
.data-card:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.data-card-title { font-weight: 600; font-size: 14px; color: var(--ink); }
.data-card-sub { font-size: 12px; color: var(--muted); }
.data-card-row { display: flex; justify-content: space-between; align-items: center; gap: 10px; }

/* ─── Toasts (UI_Modernization_Plan §4.4) ──────────────────────────────── */
.toast-stack {
    position: fixed;
    z-index: 1150;
    display: flex;
    flex-direction: column;
    gap: 8px;
    pointer-events: none;
}
@media (max-width: 600px) {
    .toast-stack { top: calc(env(safe-area-inset-top, 0px) + 12px); left: 12px; right: 12px; align-items: stretch; }
}
@media (min-width: 601px) {
    .toast-stack { bottom: calc(env(safe-area-inset-bottom, 0px) + 16px); right: 16px; width: min(360px, calc(100vw - 32px)); }
}
.toast-card {
    pointer-events: auto;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    box-shadow: var(--shadow-pop);
    padding: 12px 14px;
    color: var(--ink);
    font-size: 13px;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    overflow: hidden;
    position: relative;
    animation: toast-in var(--dur-slow) var(--ease-standard);
}
.toast-card.is-leaving { animation: toast-out var(--dur-med) var(--ease-standard) forwards; }
.toast-card.is-info { border-left: 3px solid var(--info); }
.toast-card.is-success { border-left: 3px solid var(--accent); }
.toast-card.is-warn { border-left: 3px solid var(--warn); }
.toast-card.is-danger { border-left: 3px solid var(--danger); }
.toast-card .toast-title { font-weight: 600; margin-bottom: 2px; }
.toast-card .toast-body { color: var(--ink-2); }
.toast-card .toast-close { background: transparent; border: 0; color: var(--muted); cursor: pointer; font-size: 18px; line-height: 1; padding: 0 4px; }
.toast-card .toast-progress { position: absolute; left: 0; right: 0; bottom: 0; height: 2px; background: var(--accent); transform-origin: left center; animation: toast-progress 5s linear forwards; }
@keyframes toast-in {
    from { opacity: 0; transform: translateY(-8px); }
    to { opacity: 1; transform: translateY(0); }
}
@keyframes toast-out {
    from { opacity: 1; transform: translateY(0); }
    to { opacity: 0; transform: translateY(-8px); }
}
@keyframes toast-progress {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
}
@media (prefers-reduced-motion: reduce) {
    .toast-card { animation: none; }
    .toast-card .toast-progress { animation: none; }
}

/* ─── Bottom sheet + Drawer (UI_Modernization_Plan §4.1) ────────────────── */
.sheet-backdrop {
    position: fixed;
    inset: 0;
    background: var(--backdrop);
    z-index: 1140;
    opacity: 0;
    animation: sheet-fade-in var(--dur-med) var(--ease-standard) forwards;
}
@keyframes sheet-fade-in { to { opacity: 1; } }

.bottom-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    background: var(--surface);
    border-top: 1px solid var(--line);
    border-radius: 18px 18px 0 0;
    padding: 14px 16px calc(env(safe-area-inset-bottom, 0px) + 16px) 16px;
    box-shadow: var(--shadow-pop);
    z-index: 1141;
    max-height: 85vh;
    overflow-y: auto;
    transform: translateY(100%);
    animation: sheet-up var(--dur-slow) var(--ease-standard) forwards;
}
.bottom-sheet::before {
    content: "";
    display: block;
    width: 36px;
    height: 4px;
    background: var(--line-strong);
    border-radius: 2px;
    margin: 0 auto 12px;
}
@keyframes sheet-up { to { transform: translateY(0); } }
@media (min-width: 601px) {
    .bottom-sheet {
        left: 50%;
        right: auto;
        bottom: 50%;
        transform: translate(-50%, 50%) scale(0.96);
        opacity: 0;
        border-radius: 14px;
        border: 1px solid var(--line);
        width: min(520px, calc(100vw - 32px));
        animation: sheet-pop var(--dur-slow) var(--ease-standard) forwards;
        padding: 18px 20px;
    }
    .bottom-sheet::before { display: none; }
}
@keyframes sheet-pop {
    to { transform: translate(-50%, 50%) scale(1); opacity: 1; }
}

.drawer {
    position: fixed;
    top: 0;
    bottom: 0;
    width: min(86vw, 320px);
    background: var(--surface);
    z-index: 1141;
    box-shadow: var(--shadow-pop);
    overflow-y: auto;
    padding: calc(env(safe-area-inset-top, 0px) + 12px) 12px calc(env(safe-area-inset-bottom, 0px) + 12px);
}
.drawer.is-left  { left: 0;  border-right: 1px solid var(--line); transform: translateX(-100%); animation: drawer-in-left  var(--dur-slow) var(--ease-standard) forwards; }
.drawer.is-right { right: 0; border-left: 1px solid var(--line);  transform: translateX(100%);  animation: drawer-in-right var(--dur-slow) var(--ease-standard) forwards; }
@keyframes drawer-in-left  { to { transform: translateX(0); } }
@keyframes drawer-in-right { to { transform: translateX(0); } }

@media (prefers-reduced-motion: reduce) {
    .sheet-backdrop, .bottom-sheet, .drawer { animation: none; opacity: 1; transform: none; }
    .bottom-sheet { transform: translateY(0); }
}

/* ─── Module menu sheet ────────────────────────────────────────────────────
   The mobile counterpart of the desktop sidebar (MainLayout: the module-name
   pill opens this). NavMenu's rail rows are sized for a mouse; size them for a
   thumb here. The doubled `.bottom-sheet.module-menu-sheet` prefix is load-
   bearing: NavMenu's scoped rules (`.rail-nav[b-x] a.rail-item`) tie on
   specificity with a single class and win on source order, since the scoped
   bundle is linked after app.css. */
/* height:auto also neutralises NavMenu's >=768px `height: calc(100vh - 56px)`,
   which would otherwise stretch the sheet if the viewport crosses the
   breakpoint while the menu is open. */
.bottom-sheet.module-menu-sheet .nav-scrollable { padding: 4px 8px 0; height: auto; }
.bottom-sheet.module-menu-sheet .nav-section-label { padding: 14px 10px 4px; }
.bottom-sheet.module-menu-sheet .rail-nav a.rail-item {
    padding: 12px 10px;
    font-size: 15px;
    border-radius: 10px;
    gap: 12px;
}
.bottom-sheet.module-menu-sheet .rail-nav a.rail-item .rail-icon { width: 20px; height: 20px; }

/* ─── FAB (Floating Action Button) ─────────────────────────────────────── */
.fab {
    position: fixed;
    right: calc(env(safe-area-inset-right, 0px) + 16px);
    bottom: calc(env(safe-area-inset-bottom, 0px) + 16px);
    width: 56px;
    height: 56px;
    border-radius: 999px;
    background: linear-gradient(118deg, var(--primary-solid), var(--secondary-solid));
    /* background: var(--primary-solid); */
    color: var(--primary-on);
    border: 0;
    box-shadow: var(--shadow-pop);
    display: grid;
    place-items: center;
    text-decoration: none;
    z-index: 1100;
    transition: transform var(--dur-med) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.fab:hover, .fab:focus-visible { background: var(--primary-solid-hover); color: var(--primary-on); transform: scale(1.04); text-decoration: none; }
.fab:active { transform: scale(0.96); }
.fab svg { width: 24px; height: 24px; }
/* Emitted by <Fab>; collapses to nothing on desktop where the FAB is hidden. */
.content-bottom-spacer { height: 0; }
@media (min-width: 601px) {
    /* FAB is mobile-only by default; pages can opt-in to desktop visibility */
    .fab:not(.fab-everywhere) { display: none; }
}
@media (max-width: 600px) {
    /* Reserve space at the bottom of the page so the FAB and the always-on bottom
       nav don't cover the last row of content. Emitted by <Fab>, so it only kicks
       in on pages that actually have a FAB, and only at mobile widths. */
    .content-bottom-spacer { height: 88px; }
    /* Lift the FAB above the bottom nav bar so it doesn't overlap it. The bar's
       height lives in --bottom-nav-h (defined with .bottom-nav below). */
    .fab { bottom: calc(env(safe-area-inset-bottom, 0px) + var(--bottom-nav-h, 61px) + 16px); }
}

/* ─── Icon button — standardized 40px hit area (UI_Modernization_Plan §4.1) ── */
.icon-btn {
    width: 40px;
    height: 40px;
    display: inline-grid;
    place-items: center;
    border-radius: 10px;
    border: 1px solid transparent;
    background: transparent;
    color: var(--ink-2);
    cursor: pointer;
    padding: 0;
    transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.icon-btn:hover { background: var(--surface-2); color: var(--ink); }
.icon-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
.icon-btn svg { width: 18px; height: 18px; }
.icon-btn.is-bordered { border-color: var(--line); }
.icon-btn.is-bordered:hover { border-color: var(--line-strong); }

/* ─── Segmented control (iOS-style) ──────────────────────────────────────── */
.segmented {
    display: inline-flex;
    background: var(--surface-2);
    border-radius: 10px;
    padding: 3px;
    gap: 2px;
}
.segmented > button {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--ink-2);
    font-size: 12px;
    font-weight: 600;
    padding: 6px 12px;
    border-radius: 8px;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.segmented > button.is-active { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }
.segmented > button:hover:not(.is-active) { color: var(--ink); }

/* ─── Theme toggle — labelled tri-state (Shared/ThemeToggle, Segmented) ─────
   Sits among the stacked .btn rows of the owner mobile overflow sheet, so it
   borrows their height and padding to stay on the same rhythm. */
.theme-toggle-field {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 6px 10px;
    min-height: 40px;
}
.theme-toggle-label {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    font-weight: 500;
    color: var(--ink-2);
}
.theme-toggle-label svg { flex-shrink: 0; color: var(--muted); }
.theme-toggle-seg { flex-shrink: 0; }

/* ─── Empty state ──────────────────────────────────────────────────────── */
.empty-state {
    text-align: center;
    padding: 32px 20px;
    color: var(--muted);
    border: 1px dashed var(--line-strong);
    border-radius: 12px;
    background: var(--surface);
}
.empty-state .empty-icon { width: 40px; height: 40px; margin: 0 auto 10px; color: var(--muted); }
.empty-state .empty-title { font-size: 15px; font-weight: 600; color: var(--ink); margin-bottom: 4px; }
.empty-state .empty-body { font-size: 13px; margin-bottom: 12px; }

/* ─── App tile grid (Applications sheet on mobile, modal on desktop) ───── */
.app-tile-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 10px;
}
@media (min-width: 520px) {
    .app-tile-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* Shown when the sheet resolved to Home only — see BottomNav.razor. */
.app-tile-hint {
    margin: 14px 0 0;
    color: var(--muted);
    font-size: 12px;
    text-align: center;
}
.app-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 14px 8px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 12px;
    text-decoration: none;
    color: var(--ink);
    text-align: center;
    transition: background var(--dur-fast) var(--ease-standard), border-color var(--dur-fast) var(--ease-standard);
    min-width: 0;
}
.app-tile:hover, .app-tile:focus-visible {
    background: var(--surface-2);
    border-color: var(--line-strong);
    color: var(--ink);
    text-decoration: none;
}
.app-tile.is-active {
    border-color: var(--primary);
    /* Matches the desktop .apps-modal-tile.active fill — the sheet and the
       modal are the same Applications surface. */
    background: linear-gradient(118deg, var(--primary-solid), var(--secondary-solid));
}
.app-tile-icon {
    display: inline-flex;
    width: 32px;
    height: 32px;
    align-items: center;
    justify-content: center;
    color: var(--ink);
    flex-shrink: 0;
}
.app-tile-icon svg,
.app-tile-icon .rail-icon { width: 24px; height: 24px; }
.app-tile-name {
    font-size: 12px;
    font-weight: 600;
    color: var(--ink);
    line-height: 1.2;
    word-break: break-word;
}

/* ─── Bottom nav (mobile) ─────────────────────────────────────────────── */
.bottom-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 1050;
    display: none;
    background: var(--surface);
    border-top: 1px solid var(--line);
    padding: 6px 4px calc(env(safe-area-inset-bottom, 0px) + 6px);
    backdrop-filter: blur(6px);
}
.bottom-nav-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 2px; }
.bottom-nav-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-height: 48px;
    padding: 4px 2px;
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.02em;
    text-decoration: none;
    border-radius: 10px;
    cursor: pointer;
}
.bottom-nav-item svg { width: 20px; height: 20px; }
.bottom-nav-item.is-active { color: var(--primary-ink); }
.bottom-nav-item:hover { color: var(--ink); text-decoration: none; }
/* Height of the fixed bottom nav at the current width, excluding the safe-area
   inset. Anything that has to sit above the bar (the FAB, the module handle,
   .content's bottom gutter) reads this instead of hard-coding a number in its
   own media query. Zero wherever .bottom-nav is display:none.

   The bar's height is SET from the token rather than measured from it, so the
   two cannot drift: 61px = 1px border-top + 6px + the 48px item row + 6px.
   The old hard-coded 80px FAB offset assumed 64px and was 3px out — invisible
   on a floating FAB, but not on the module handle, which is meant to sit flush
   against the bar's top edge. */
:root { --bottom-nav-h: 0px; }
@media (max-width: 600px) {
    :root { --bottom-nav-h: 61px; }
    .bottom-nav {
        display: block;
        box-sizing: border-box;
        height: calc(var(--bottom-nav-h) + env(safe-area-inset-bottom, 0px));
    }
    body.has-bottom-nav { padding-bottom: calc(env(safe-area-inset-bottom, 0px) + var(--bottom-nav-h)); }
}

/* ─── Pull-to-refresh indicator ────────────────────────────────────────── */
.ptr-indicator {
    height: 0;
    overflow: hidden;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    color: var(--muted);
    transition: height var(--dur-fast) var(--ease-standard);
}
.ptr-indicator.is-pulling { height: 48px; }
.ptr-indicator.is-refreshing { height: 48px; }
.ptr-spinner {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 2px solid var(--line-strong);
    border-top-color: var(--primary);
    animation: ptr-spin 0.8s linear infinite;
    margin-bottom: 14px;
}
@keyframes ptr-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .ptr-spinner { animation: none; } }

/* ─── Filter bar ───────────────────────────────────────────────────────── */
.filter-bar {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.filter-bar .chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    border-radius: 999px;
    border: 1px solid var(--line);
    background: var(--surface);
    color: var(--ink-2);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard);
}
.filter-bar .chip:hover { background: var(--surface-2); color: var(--ink); }
.filter-bar .chip.is-active { background: var(--primary-soft); color: var(--primary-ink); border-color: transparent; }
@media (max-width: 600px) {
    .filter-bar { position: sticky; top: 0; z-index: 5; background: var(--bg); padding: 8px 0; }
}

/* ─── Search box (input + debounce + clear — <SearchBox>) ──────────────── */
.search-box { position: relative; max-width: 480px; }
.search-box .form-control { padding-right: 34px; }
.search-box .search-clear {
    position: absolute;
    right: 6px;
    top: 50%;
    transform: translateY(-50%);
    border: 0;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    padding: 4px;
    line-height: 1;
    display: inline-flex;
}
.search-box .search-clear:hover { color: var(--ink); }

/* ─── Page transitions ─────────────────────────────────────────────────── */
.page-fade { animation: page-fade-in var(--dur-fast) var(--ease-standard); }
@keyframes page-fade-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .page-fade { animation: none; } }

/* ─── Auth shell ───────────────────────────────────────────────────────── */
.auth-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: max(env(safe-area-inset-top, 0px), 24px) 20px max(env(safe-area-inset-bottom, 0px), 24px);
    background: var(--bg);
}
.auth-card {
    width: 100%;
    max-width: 420px;
    margin: 0 auto;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 16px;
    padding: 28px 24px;
    box-shadow: var(--shadow);
}
.auth-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 18px;
    font-weight: 700;
    color: var(--ink);
    margin-bottom: 20px;
}
.auth-brand-mark {
    width: 32px;
    height: 32px;
    border-radius: 8px;
    background: var(--primary-solid);
    color: var(--primary-on);
    display: grid;
    place-items: center;
    font-weight: 800;
}
.auth-title { margin: 0 0 4px 0; font-size: 22px; font-weight: 700; letter-spacing: -0.015em; }
.auth-subtitle { margin: 0 0 22px 0; color: var(--muted); font-size: 14px; }

.auth-field { position: relative; margin-bottom: 14px; }
.auth-input {
    width: 100%;
    height: 48px;
    padding: 18px 12px 4px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: var(--ink);
    font-size: 16px;
    transition: border-color var(--dur-fast) var(--ease-standard);
}
.auth-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px color-mix(in oklab, var(--primary) 22%, transparent); }
.auth-input::placeholder { color: transparent; }
.auth-field label {
    position: absolute;
    left: 12px;
    top: 14px;
    color: var(--muted);
    font-size: 14px;
    pointer-events: none;
    transition: transform var(--dur-fast) var(--ease-standard), font-size var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
/* Float the label up while the field is focused or filled. The `:focus-within`
   / `:has()` selectors key off the wrapper rather than the `+ label`
   adjacent-sibling combinator, which proved fragile against the DOM the
   <InputText> component renders (the label stayed pinned over the typed text).
   The `+ label` rules are kept so the <select> static label keeps its existing
   pinned position; `.auth-label-static` is excluded from the wrapper rules,
   which target only true floating labels. */
.auth-field .auth-input:focus + label,
.auth-field .auth-input:not(:placeholder-shown) + label,
.auth-field:focus-within label:not(.auth-label-static),
.auth-field:has(.auth-input:not(:placeholder-shown)) label:not(.auth-label-static) {
    transform: translateY(-10px);
    font-size: 11px;
    color: var(--primary-ink);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

/* Password show/hide toggle (PasswordRevealButton). A compact icon floated
   inside the field on the right — vertically centred and inset from the edge so
   it reads as an inline affordance, not a button column. `.pw-wrap` is the
   positioning context; the input carries `has-reveal` so its text clears the
   icon. Works over both the floating-label `.auth-input` and the stacked
   `.portal-input`. */
.pw-wrap { position: relative; display: block; }
.pw-reveal {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    padding: 0;
    background: transparent;
    border: 0;
    border-radius: 8px;
    color: var(--muted);
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;             /* strip the browser's default button chrome */
    transition: color .12s ease, background-color .12s ease;
}
.pw-reveal:hover { color: var(--ink); background: color-mix(in oklab, var(--ink) 8%, transparent); }
.pw-reveal:active { background: color-mix(in oklab, var(--ink) 14%, transparent); }
.pw-reveal:focus-visible { outline: 2px solid var(--primary); outline-offset: 1px; color: var(--ink); }
.pw-reveal svg { display: block; }
.auth-input.has-reveal,
.portal-input.has-reveal { padding-right: 40px; }
/* Suppress Edge/IE's built-in reveal + clear controls — we render our own eye,
   and both showing at once is a double icon. Kept as separate rules (a grouped
   selector list is dropped whole if a browser doesn't know one pseudo) and
   forced past the UA stylesheet. */
.pw-wrap input::-ms-reveal { display: none !important; }
.pw-wrap input::-ms-clear { display: none !important; }
input[type="password"]::-ms-reveal { display: none !important; }
input[type="password"]::-ms-clear { display: none !important; }

.auth-cta {
    width: 100%;
    min-height: 48px;
    border-radius: 10px;
    border: 0;
    background: linear-gradient(118deg, var(--primary-solid), var(--secondary-solid));
    /* background: var(--primary-solid); */
    color: var(--primary-on);
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    margin-top: 4px;
    transition: background var(--dur-fast) var(--ease-standard);
}
.auth-cta:hover { background: linear-gradient(118deg, var(--primary-solid-hover), var(--secondary-solid-hover)); }
.auth-cta[disabled] { opacity: 0.55; cursor: not-allowed; }
.auth-meta { text-align: center; font-size: 13px; color: var(--muted); margin-top: 16px; }
.auth-meta a { color: var(--primary-ink); font-weight: 600; }

/* Public booking pages — "Powered by BizGo" lockup under the card content. */
.booking-powered {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    margin-top: 22px;
    padding-top: 16px;
    border-top: 1px solid var(--line);
    color: var(--muted);
    font-size: 12px;
}

/* ─── Public booking page (/book/{slug}) ───────────────────────────────
   Calendly-style stepper: service cards → day strip + time grid → details.
   --bkp-accent resolves the tenant brand colour (set as --brand-primary on
   the card via inline style) and falls back to the BizGo primary. */
.bkp-card { max-width: 600px; --bkp-accent: var(--brand-primary, var(--primary-solid)); }

.bkp-step { margin-bottom: 20px; }
.bkp-step + .bkp-step { border-top: 1px solid var(--line); padding-top: 18px; }
.bkp-step-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.bkp-step-num {
    flex: none;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    font-size: 12px;
    font-weight: 700;
    background: var(--surface-2);
    color: var(--ink-2);
}
.bkp-step-num.is-active { background: var(--bkp-accent); color: var(--primary-on); }
.bkp-step-num.is-done { background: var(--accent-soft); color: var(--accent-ink); }
.bkp-step-title { margin: 0; font-size: 14.5px; font-weight: 650; letter-spacing: -0.01em; color: var(--ink); }

/* Service cards */
.bkp-services { display: flex; flex-direction: column; gap: 8px; }
.bkp-service {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    text-align: left;
    padding: 12px 14px;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease-standard),
                box-shadow var(--dur-fast) var(--ease-standard),
                transform var(--dur-fast) var(--ease-standard);
}
.bkp-service:hover, .bkp-service:focus-visible {
    border-color: color-mix(in oklab, var(--bkp-accent) 45%, var(--line));
    box-shadow: var(--shadow-sm);
    transform: translateY(-1px);
}
.bkp-service-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.bkp-service-name { font-weight: 600; font-size: 14px; color: var(--ink); }
.bkp-service-desc { font-size: 12.5px; color: var(--muted); }
.bkp-service-meta { flex: none; display: flex; flex-direction: column; align-items: flex-end; gap: 3px; }
.bkp-service-price { font-weight: 700; font-size: 14px; color: var(--ink); font-variant-numeric: tabular-nums; }
.bkp-duration { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--muted); white-space: nowrap; }
.bkp-service-go { flex: none; color: var(--muted); display: inline-flex; }

/* Collapsed "picked" summary rows (chosen service / chosen time) */
.bkp-picked {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: color-mix(in oklab, var(--bkp-accent) 6%, var(--surface));
    border: 1px solid color-mix(in oklab, var(--bkp-accent) 28%, var(--line));
    border-radius: var(--radius);
}
.bkp-picked-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.bkp-picked-name { font-weight: 600; font-size: 14px; color: var(--ink); }
.bkp-picked-meta { font-size: 12.5px; color: var(--muted); }
.bkp-change {
    flex: none;
    border: 0;
    background: none;
    color: var(--primary-ink);
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: var(--radius-sm);
}
.bkp-change:hover { background: var(--surface-2); }

/* Staff chips — soft pills, no hard outline */
.bkp-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 16px; }
.bkp-chip {
    border: 0;
    background: var(--surface-2);
    color: var(--ink-2);
    padding: 7px 15px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 550;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard),
                color var(--dur-fast) var(--ease-standard),
                box-shadow var(--dur-fast) var(--ease-standard);
}
.bkp-chip:hover { background: var(--surface-3); }
.bkp-chip.is-selected {
    background: var(--bkp-accent);
    color: var(--primary-on);
    font-weight: 650;
    box-shadow: 0 2px 8px -2px color-mix(in oklab, var(--bkp-accent) 55%, transparent);
}

/* Date + time picker: calendar on the left, times on the right (stacks on phones) */
.bkp-picker { display: grid; grid-template-columns: 1fr 0.9fr; gap: 16px; align-items: start; }
@media (max-width: 520px) { .bkp-picker { grid-template-columns: 1fr; gap: 14px; } }

/* Month calendar */
.bkp-cal { user-select: none; }
.bkp-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.bkp-cal-month { font-size: 14px; font-weight: 650; color: var(--ink); letter-spacing: -0.01em; }
.bkp-cal-nav {
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 8px;
    background: var(--surface-2);
    color: var(--ink-2);
    display: grid;
    place-items: center;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard), color var(--dur-fast) var(--ease-standard);
}
.bkp-cal-nav:not(:disabled):hover { background: var(--surface-3); color: var(--ink); }
.bkp-cal-nav:disabled { opacity: 0.35; cursor: default; }
.bkp-cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 3px; }
.bkp-cal-dow {
    text-align: center;
    font-size: 10.5px;
    font-weight: 650;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: var(--muted);
    padding-bottom: 4px;
}
.bkp-cal-pad { aspect-ratio: 1; }
.bkp-cal-day {
    aspect-ratio: 1;
    border: 0;
    background: transparent;
    border-radius: 9px;
    font-size: 13px;
    font-weight: 550;
    color: var(--line-strong);
    font-variant-numeric: tabular-nums;
    cursor: default;
    position: relative;
    transition: background var(--dur-fast) var(--ease-standard),
                color var(--dur-fast) var(--ease-standard),
                box-shadow var(--dur-fast) var(--ease-standard);
}
/* Days with open slots: legible, tappable, with a small availability dot */
.bkp-cal-day.has-slots {
    color: var(--ink);
    background: color-mix(in oklab, var(--bkp-accent) 9%, var(--surface));
    cursor: pointer;
}
.bkp-cal-day.has-slots::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 5px;
    width: 4px;
    height: 4px;
    border-radius: 50%;
    background: var(--bkp-accent);
    transform: translateX(-50%);
}
.bkp-cal-day.has-slots:hover { background: color-mix(in oklab, var(--bkp-accent) 20%, var(--surface)); }
.bkp-cal-day.is-selected,
.bkp-cal-day.is-selected.has-slots {
    background: var(--bkp-accent);
    color: var(--primary-on);
    font-weight: 700;
    box-shadow: 0 3px 10px -3px color-mix(in oklab, var(--bkp-accent) 60%, transparent);
}
.bkp-cal-day.is-selected.has-slots::after { background: var(--primary-on); }
.bkp-cal-day:disabled { cursor: default; }
/* Booked / unavailable nights: greyed out, struck through, not selectable */
.bkp-cal-day.is-booked {
    color: var(--muted);
    background: color-mix(in oklab, var(--muted) 12%, var(--surface));
    text-decoration: line-through;
    text-decoration-color: color-mix(in oklab, var(--muted) 55%, transparent);
    cursor: not-allowed;
}
.bkp-cal-day.is-booked:hover { background: color-mix(in oklab, var(--muted) 12%, var(--surface)); }

/* Times panel for the chosen day */
.bkp-times-panel { min-height: 100%; }
.bkp-times-day { font-size: 12.5px; font-weight: 650; color: var(--ink); margin-bottom: 10px; }
.bkp-times {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(72px, 100%), 1fr));
    gap: 8px;
    max-height: 232px;
    overflow-y: auto;
    scrollbar-width: thin;
    padding-right: 2px;
}
@media (max-width: 520px) { .bkp-times { max-height: none; } }
.bkp-time {
    padding: 10px 4px;
    border: 0;
    border-radius: 9px;
    background: var(--surface-2);
    font-size: 13.5px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard),
                color var(--dur-fast) var(--ease-standard),
                box-shadow var(--dur-fast) var(--ease-standard);
}
.bkp-time:hover, .bkp-time:focus-visible {
    background: var(--bkp-accent);
    color: var(--primary-on);
    box-shadow: 0 3px 10px -3px color-mix(in oklab, var(--bkp-accent) 55%, transparent);
}
/* Already-booked time: greyed, struck through, not selectable */
.bkp-time.is-booked,
.bkp-time.is-booked:hover {
    background: color-mix(in oklab, var(--muted) 12%, var(--surface-2));
    color: var(--muted);
    text-decoration: line-through;
    text-decoration-color: color-mix(in oklab, var(--muted) 55%, transparent);
    cursor: not-allowed;
    box-shadow: none;
}
.bkp-times-hint {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 8px;
    text-align: center;
    min-height: 140px;
    padding: 16px;
    color: var(--muted);
    font-size: 13px;
    background: var(--surface-2);
    border-radius: var(--radius);
}

/* Calendar loading shimmer */
.bkp-cal-skel { display: flex; flex-direction: column; gap: 10px; }
.bkp-cal-skel-head { height: 20px; width: 45%; border-radius: 6px; }
.bkp-cal-skel-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.bkp-cal-skel-grid span { aspect-ratio: 1; border-radius: 8px; }
.bkp-cal-skel-head,
.bkp-cal-skel-grid span {
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
    background-size: 200% 100%;
    animation: bkp-shimmer 1.2s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .bkp-cal-skel-head, .bkp-cal-skel-grid span { animation: none; } }

/* Loading shimmer + empty state */
.bkp-skel {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(76px, 100%), 1fr));
    gap: 8px;
}
.bkp-skel span {
    height: 38px;
    border-radius: var(--radius-sm);
    background: linear-gradient(90deg, var(--surface-2) 25%, var(--surface-3) 50%, var(--surface-2) 75%);
    background-size: 200% 100%;
    animation: bkp-shimmer 1.2s linear infinite;
}
@keyframes bkp-shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .bkp-skel span { animation: none; } }
.bkp-empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    text-align: center;
    padding: 24px 12px;
    border: 1px dashed var(--line-strong);
    border-radius: var(--radius);
    color: var(--muted);
    font-size: 13.5px;
}

/* Details form: phone + email side by side when there's room */
.bkp-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
@media (max-width: 480px) { .bkp-2col { grid-template-columns: 1fr; } }

/* Confirmation state */
.bkp-done { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; }
.bkp-done-icon { color: var(--accent-ink); display: inline-flex; margin-bottom: 4px; }
.bkp-status-link {
    margin-top: 14px;
    width: 100%;
    padding: 12px 14px;
    background: var(--surface-2);
    border-radius: var(--radius);
    font-size: 12.5px;
    color: var(--muted);
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.bkp-status-link a { color: var(--primary-ink); font-weight: 600; word-break: break-all; }

/* Registration country/city + business-name (Registration_Location_And_Business_Name_Plan §11). */
/* A <select> is never :placeholder-shown, so its label is pinned in the raised state. */
.auth-label-static {
    position: absolute;
    left: 12px;
    top: 6px;
    font-size: 11px;
    color: var(--primary-ink);
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    pointer-events: none;
}
select.auth-input { appearance: none; padding-top: 20px; }

.auth-typeahead-field { position: relative; }
.auth-typeahead-list {
    list-style: none;
    margin: 4px 0 0;
    padding: 4px;
    position: absolute;
    z-index: 20;
    left: 0;
    right: 0;
    max-height: 220px;
    overflow-y: auto;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.auth-typeahead-list li {
    padding: 9px 10px;
    border-radius: 7px;
    cursor: pointer;
    font-size: 15px;
    color: var(--ink);
}
.auth-typeahead-list li:hover { background: color-mix(in oklab, var(--primary) 12%, transparent); }
.auth-typeahead-empty { color: var(--muted); cursor: default; }
.auth-typeahead-empty:hover { background: transparent; }

.auth-inline-note { font-size: 13px; color: var(--muted); margin: -6px 0 12px; }
.auth-inline-warn { color: var(--danger, #c0392b); }
.auth-inline-ok { color: var(--success, #2e7d32); }
.auth-linkbtn {
    background: none;
    border: 0;
    padding: 0;
    margin-left: 4px;
    color: var(--primary-ink);
    font-weight: 600;
    font-size: 13px;
    cursor: pointer;
    text-decoration: underline;
}
.auth-attribution { font-size: 11px; margin-top: 10px; opacity: 0.8; }

/* "Keep me signed in" — a checkbox row sitting between the last field and the
   submit button. Full-width label so the whole row is a hit target on touch. */
.auth-check {
    display: flex;
    align-items: center;
    gap: 9px;
    margin: 2px 0 16px;
    font-size: 13.5px;
    color: var(--ink-2, var(--ink));
    cursor: pointer;
    user-select: none;
}
.auth-check input[type="checkbox"] {
    width: 17px;
    height: 17px;
    flex: 0 0 auto;
    accent-color: var(--primary-solid);
    cursor: pointer;
}
.auth-check-hint {
    display: block;
    margin: -10px 0 16px;
    font-size: 12px;
    color: var(--muted);
}

/* "You're already signed in" interstitial (Shared/AuthGate.razor) — the identity
   the visitor is about to continue as, shown so the choice is an informed one. */
.auth-identity {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 12px;
    margin-bottom: 18px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface-2);
    min-width: 0;
}
.auth-identity-avatar { width: 34px; height: 34px; font-size: 13px; flex: 0 0 auto; }
.auth-identity-name {
    font-weight: 600;
    font-size: 14px;
    color: var(--ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.auth-linkbtn[disabled] { opacity: 0.55; cursor: not-allowed; }

/* ─── Tabs (Account / detail pages) ─────────────────────────────────────── */
.tabs {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    border-bottom: 1px solid var(--line);
    margin-bottom: 18px;
    scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tabs > button, .tabs > a {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--muted);
    font-size: 13px;
    font-weight: 600;
    padding: 10px 14px;
    border-bottom: 2px solid transparent;
    cursor: pointer;
    white-space: nowrap;
    text-decoration: none;
}
.tabs > button:hover, .tabs > a:hover { color: var(--ink); text-decoration: none; }
.tabs > button.is-active, .tabs > a.is-active { color: var(--primary-ink); border-bottom-color: var(--primary); }

/* ─── Swipe-action row (Inbox) ─────────────────────────────────────────── */
.swipe-row { position: relative; overflow: hidden; touch-action: pan-y; }
.swipe-row-actions {
    position: absolute;
    top: 0; bottom: 0;
    display: flex;
    align-items: stretch;
}
.swipe-row-actions.is-left  { left: 0; }
.swipe-row-actions.is-right { right: 0; }
.swipe-row-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0 18px;
    color: #fff;
    font-size: 12px;
    font-weight: 600;
    border: 0;
    cursor: pointer;
    min-width: 64px;
}
.swipe-row-action.is-archive { background: var(--info); }
.swipe-row-action.is-snooze  { background: var(--warn); }
.swipe-row-action.is-danger  { background: var(--danger); }
.swipe-row-body {
    position: relative;
    background: var(--surface);
    transition: transform var(--dur-med) var(--ease-standard);
}

/* ═══ Dashboard & data-viz layer ═════════════════════════════════════════
   Shared by the home landing dashboard and every module dashboard.
   Series colors are a CVD-validated categorical palette (dataviz skill,
   validate_palette.js: light on #fff, dark on #141a2c — both pass; the two
   light slots below 3:1 are always paired with legends + value labels).
   Slot order is the safety mechanism — assign s0→s3 in order, never cycle. */
:root {
    --viz-1: #2a78d6;   /* blue    — series 1 (counts, inbound, invoiced) */
    --viz-2: #1baf7a;   /* emerald — series 2 (money in, outbound, paid)  */
    --viz-3: #eda100;   /* amber   — series 3                             */
    --viz-4: #4a3aa7;   /* violet  — series 4                             */
    --viz-grid: var(--line);
    --viz-axis: var(--line-strong);
    /* Aging / severity ramp: amber → danger, monotone darkening. Ordered
       buckets only (never series identity); rows always carry value labels. */
    --age-1: var(--viz-3);
    --age-2: color-mix(in oklab, var(--viz-3) 66%, var(--danger));
    --age-3: color-mix(in oklab, var(--viz-3) 33%, var(--danger));
    --age-4: var(--danger);
}
[data-theme="dark"] {
    --viz-1: #3987e5;
    --viz-2: #199e70;
    --viz-3: #c98500;
    --viz-4: #9085e9;
}

/* Tones shared by stat values, sparklines, chart fills, list values */
.tone-ok      { color: var(--accent-ink); }
.tone-warn    { color: var(--warn-ink); }
.tone-danger  { color: var(--danger-ink); }
.tone-info    { color: var(--info-ink); }
.tone-neutral { color: var(--ink); }

/* ── Layout grids ── */
.dsh-pulse { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(190px, 100%), 1fr)); gap: 12px; }
.dsh-pulse > * { min-width: 0; }
.dsh-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
.dsh-grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 16px; margin-bottom: 16px; }
.dsh-grid-2 > *, .dsh-grid-3 > * { min-width: 0; }
.dsh-grid-2 > .panel, .dsh-grid-3 > .panel { margin-bottom: 0; }
@media (max-width: 900px) { .dsh-grid-2, .dsh-grid-3 { grid-template-columns: 1fr; } }
.dsh-chart-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(340px, 100%), 1fr)); gap: 16px; }
.dsh-chart-grid > * { min-width: 0; }
@media (min-width: 980px) { .dsh-card-wide { grid-column: span 2; } }
.dsh-spotlight-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 16px; }
.dsh-spotlight-grid > * { min-width: 0; }

.dsh-card {
    background: var(--surface); border: 1px solid var(--line);
    border-radius: 12px; padding: 16px; box-shadow: var(--shadow-sm);
}
.dsh-card-empty { color: var(--muted); font-size: 13px; }

.section-heading {
    font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--muted); margin: 0 0 12px; font-weight: 600;
}

/* ── Sparkline (inline SVG trend in stat cards) ── */
.spark { display: block; }
.spark-line { stroke: var(--viz-1); stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; vector-effect: non-scaling-stroke; }
.spark-fill { fill: var(--viz-1); opacity: 0.12; }
.spark.tone-ok .spark-line     { stroke: var(--viz-2); }   .spark.tone-ok .spark-fill     { fill: var(--viz-2); }
.spark.tone-warn .spark-line   { stroke: var(--warn); }    .spark.tone-warn .spark-fill   { fill: var(--warn); }
.spark.tone-danger .spark-line { stroke: var(--danger); }  .spark.tone-danger .spark-fill { fill: var(--danger); }

/* ── Styled hover/focus tooltip (CSS-only charts) ──
   Elements carry data-tip + tabindex=0; the tooltip never gates a value —
   every chart also direct-labels or lists its numbers. */
[data-tip] { position: relative; }
[data-tip]:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
[data-tip]:hover::after, [data-tip]:focus-visible::after {
    content: attr(data-tip);
    position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
    z-index: 40; width: max-content; max-width: 260px;
    background: var(--ink); color: var(--bg);
    font-size: 11.5px; font-weight: 500; line-height: 1.45; text-align: left;
    padding: 6px 9px; border-radius: 8px; box-shadow: var(--shadow-pop);
    pointer-events: none; white-space: pre-line;
}

/* ── Vertical bars (plot row + aligned tick row) ──
   The bar row and tick row share --plot-gap so ticks stay under their
   columns; on small screens the whole plot scrolls as one unit. */
.dsh-plot { padding: 0 2px; --plot-gap: 6px; }
.dsh-bars { display: flex; align-items: flex-end; gap: var(--plot-gap); height: 150px; border-bottom: 1px solid var(--viz-axis); }
.dsh-bars-col {
    flex: 1; min-width: 0; height: 100%;
    display: flex; align-items: flex-end; justify-content: center;
    border-radius: 6px 6px 0 0;
}
.dsh-bars-col:hover { background: color-mix(in oklab, var(--surface-2) 60%, transparent); }
.dsh-bars-stack { display: flex; gap: 2px; align-items: flex-end; height: 100%; width: 100%; justify-content: center; }
.dsh-bar { width: 100%; max-width: 22px; border-radius: 4px 4px 0 0; min-height: 2px; }
.dsh-bar.s0 { background: var(--viz-1); }
.dsh-bar.s1 { background: var(--viz-2); }
.dsh-bar.s2 { background: var(--viz-3); }
.dsh-bar.s3 { background: var(--viz-4); }
.dsh-ticks { display: flex; gap: var(--plot-gap); margin-top: 6px; }
.dsh-ticks > span { flex: 1; min-width: 0; text-align: center; font-size: 10px; color: var(--muted); white-space: nowrap; overflow: hidden; }
.dsh-legend { display: flex; gap: 16px; margin-top: 10px; font-size: 11.5px; color: var(--muted); flex-wrap: wrap; }
.dsh-swatch { display: inline-block; width: 10px; height: 10px; border-radius: 2px; vertical-align: -1px; margin-right: 4px; }
.dsh-swatch.s0 { background: var(--viz-1); } .dsh-swatch.s1 { background: var(--viz-2); }
.dsh-swatch.s2 { background: var(--viz-3); } .dsh-swatch.s3 { background: var(--viz-4); }
@media (max-width: 600px) {
    .dsh-plot { overflow-x: auto; }
    .dsh-bars-col, .dsh-ticks > span { min-width: 24px; }
}

/* ── Horizontal bars (aging, rankings, load) ── */
.dsh-hbar { display: grid; gap: 4px; }
.dsh-hbar-row { display: grid; grid-template-columns: 96px 1fr 110px; align-items: center; gap: 10px; padding: 4px 0; }
.dsh-hbar-row.is-link { cursor: pointer; }
.dsh-hbar-row.is-link:hover .dsh-hbar-label { text-decoration: underline; }
.dsh-hbar-label { font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dsh-hbar-track { background: var(--surface-2); height: 12px; border-radius: 6px; overflow: hidden; }
.dsh-hbar-fill { display: block; height: 100%; min-width: 2px; border-radius: 0 6px 6px 0; background: var(--viz-1); }
.dsh-hbar-fill.tone-ok     { background: var(--viz-2); }
.dsh-hbar-fill.tone-warn   { background: var(--warn); }
.dsh-hbar-fill.tone-danger { background: var(--danger); }
.dsh-hbar-fill.age-0 { background: var(--viz-1); }
.dsh-hbar-fill.age-1 { background: var(--age-1); }
.dsh-hbar-fill.age-2 { background: var(--age-2); }
.dsh-hbar-fill.age-3 { background: var(--age-3); }
.dsh-hbar-fill.age-4 { background: var(--age-4); }
.dsh-hbar-val { text-align: right; font-variant-numeric: tabular-nums; font-size: 13px; color: var(--ink); white-space: nowrap; }
.dsh-hbar-val .count { color: var(--muted); font-size: 11.5px; margin-left: 4px; }

/* ── Funnel / pipeline stages ── */
.dsh-funnel { display: flex; align-items: stretch; gap: 8px; flex-wrap: wrap; }
.dsh-funnel-stage {
    flex: 1; min-width: 120px; display: flex; flex-direction: column; gap: 2px;
    text-decoration: none; color: inherit;
    background: var(--surface-2); border: 1px solid var(--line);
    border-radius: 10px; padding: 12px 14px;
    transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
a.dsh-funnel-stage:hover { border-color: var(--accent); background: var(--surface); color: inherit; }
.dsh-funnel-label { font-size: 12px; color: var(--muted); }
.dsh-funnel-val { font-size: 20px; font-weight: 650; color: var(--ink); }
.dsh-funnel-stage.tone-ok .dsh-funnel-val, .dsh-funnel-stage.success .dsh-funnel-val { color: var(--accent-ink); }
.dsh-funnel-sub { font-size: 11.5px; color: var(--muted); }
.dsh-funnel-arrow { align-self: center; color: var(--muted); font-size: 18px; }
@media (max-width: 560px) { .dsh-funnel-arrow { display: none; } }

/* ── Ranked mini-lists (spotlights, recents) ── */
.dsh-list { list-style: none; padding: 0; margin: 0; }
.dsh-list li {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
    padding: 7px 0; border-bottom: 1px solid var(--line); font-size: 13px;
}
.dsh-list li:last-child { border-bottom: none; }
.dsh-list li.is-link { cursor: pointer; }
.dsh-list li.is-link:hover .dsh-list-primary { text-decoration: underline; }
.dsh-list-main { display: flex; flex-direction: column; min-width: 0; }
.dsh-list-primary { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.dsh-list-secondary { color: var(--muted); font-size: 11.5px; }
.dsh-list-value { font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }

/* ── Heatmap (sequential — one hue, lighter→darker via --heat 0..100) ── */
.dsh-heat { border-collapse: separate; border-spacing: 2px; }
.dsh-heat td.cell {
    width: 22px; height: 18px; border-radius: 3px;
    background: color-mix(in oklab, var(--viz-1) calc(var(--heat, 0) * 1%), var(--surface-2));
}
.dsh-heat th { font-size: 10px; color: var(--muted); font-weight: 500; padding: 0 3px; }

/* ── Quick-action chips (module dashboards) ── */
.qa-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr)); gap: 10px; }
.qa-item {
    display: flex; align-items: center; gap: 10px; padding: 11px 14px;
    background: var(--surface); border: 1px solid var(--line); border-radius: 10px;
    color: var(--ink); text-decoration: none; font-size: 13.5px; font-weight: 500;
    transition: border-color var(--dur-fast) var(--ease-standard), background var(--dur-fast) var(--ease-standard);
}
.qa-item:hover { border-color: var(--accent); color: var(--accent-ink); background: var(--surface-2); text-decoration: none; }
.qa-item .qa-ic { display: inline-flex; flex-shrink: 0; color: var(--primary-ink); }

/* ── Home hero band — the landing "wow" moment. Token-driven (re-tints with
   the brand inputs + BrandTuner) and readable in both themes. ── */
.home-hero {
    position: relative; overflow: hidden;
    display: flex; justify-content: space-between; align-items: flex-end;
    gap: 18px; flex-wrap: wrap;
    padding: 24px 24px 22px;
    margin-bottom: 24px;
    border: 1px solid color-mix(in oklab, var(--primary-solid) 24%, var(--line));
    border-radius: var(--radius-lg);
    background:
        radial-gradient(120% 190% at 100% 0%, color-mix(in oklab, var(--secondary-solid) 14%, transparent) 0%, transparent 55%),
        radial-gradient(130% 200% at 0% 115%, color-mix(in oklab, var(--primary-solid) 12%, transparent) 0%, transparent 58%),
        var(--surface);
    box-shadow: var(--shadow-sm);
}
.home-hero::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
    background: linear-gradient(90deg, var(--primary-solid), var(--secondary-solid));
}
.home-hero-eyebrow {
    font-size: 11px; font-weight: 600; letter-spacing: 0.06em;
    text-transform: uppercase; color: var(--primary-ink); margin-bottom: 6px;
}
.home-hero h1 { margin: 0; font-size: 25px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; }
.home-hero-sub { margin: 6px 0 0; font-size: 13.5px; color: var(--muted); }
.home-hero-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
@media (max-width: 600px) {
    .home-hero { padding: 18px 16px; }
    .home-hero h1 { font-size: 21px; }
    .home-hero-actions { width: 100%; }
}

/* ── Account Usage tab (Dev_Docs/Tenant_Usage_Tracking_Plan.md) ── */
.usage-group { margin-top: 18px; }
.usage-group:first-of-type { margin-top: 8px; }
.usage-group-head {
    margin: 0 0 10px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--muted);
}
.usage-row { margin-bottom: 14px; }
.usage-row-top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    margin-bottom: 5px;
}
.usage-label { font-size: 14px; color: var(--ink); }
.usage-per {
    margin-left: 6px;
    font-size: 11px;
    font-weight: 600;
    color: var(--muted);
}
.usage-count { font-size: 13px; font-weight: 600; color: var(--ink); white-space: nowrap; }
.usage-meter {
    height: 8px;
    border-radius: 999px;
    background: var(--line);
    overflow: hidden;
}
.usage-meter-fill {
    height: 100%;
    border-radius: 999px;
    background: var(--accent);
    transition: width 0.3s ease;
}
.usage-meter.is-warn .usage-meter-fill { background: var(--warn); }
.usage-meter.is-full .usage-meter-fill { background: var(--danger); }
.usage-nudge { margin-top: 18px; color: var(--muted); }

/* ============================================================
   Forms: validation + keyboard
   (Dev_Docs/Forms_Validation_And_Tab_Order_Plan.md §2–3)
   Bootstrap supplies .is-invalid borders for .form-control /
   .form-select; these cover our custom inputs and the shared
   error affordances.
   ============================================================ */

/* Inline field errors (FieldError component). */
.invalid-feedback.d-block {
    color: var(--danger, #dc3545);
    font-size: 12.5px;
    margin-top: 4px;
}

/* Auth pages use .auth-input, not .form-control. */
.auth-input.is-invalid {
    border-color: var(--danger, #dc3545) !important;
}
.auth-input.is-invalid:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger, #dc3545) 22%, transparent);
}

/* Wrapper-level invalid state (Quill shells, typeaheads, pickers). */
.field-shell.is-invalid {
    border: 1px solid var(--danger, #dc3545);
    border-radius: 8px;
}

/* Summary strip AppEditForm shows near the actions after a rejected submit. */
.form-error-summary {
    margin-top: 14px;
    padding: 10px 14px;
    border-radius: 10px;
    border: 1px solid color-mix(in srgb, var(--danger, #dc3545) 35%, transparent);
    background: color-mix(in srgb, var(--danger, #dc3545) 8%, transparent);
    color: var(--danger, #dc3545);
    font-size: 13.5px;
}
.form-error-summary > div { margin-top: 4px; }

/* ============================================================
   Legal re-consent soft notice
   (Dev_Docs/Legal_Policy_Update_Reconsent_Plan.md §5.3 / R7)
   Grace-window banner inside the normal shell — the blocking
   gate is the LegalReconsent full-screen card instead.
   ============================================================ */

.legal-notice-banner {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 16px;
    border-bottom: 1px solid var(--line);
    background: var(--info-soft);
    color: var(--info-ink);
    font-size: 13.5px;
}
.legal-notice-banner > span { flex: 1; min-width: 0; }
.legal-notice-banner a { margin-left: 10px; text-decoration: underline; color: inherit; }

/* ============================================================
   Support centre  (/support)
   (Dev_Docs/Support_FAQ_Seed_And_Tenant_Display_Plan.md §7.1)
   FAQ = a category rail beside grouped accordions. The rail is
   sticky on desktop and collapses to horizontal chips on
   mobile; counts are post-gating, so a tenant only ever sees
   categories their plan and feature flags make real.
   ============================================================ */

.support-tabs { margin-bottom: 16px; }
.support-section { max-width: 820px; }

.support-faq {
    display: grid;
    grid-template-columns: minmax(180px, 220px) minmax(0, 1fr);
    gap: 24px;
    align-items: start;
}
.support-faq .faq-search { margin-bottom: 16px; max-width: 100%; }

.faq-rail {
    position: sticky;
    top: 12px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.faq-rail-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    width: 100%;
    padding: 7px 10px;
    border: 1px solid transparent;
    border-radius: 8px;
    background: none;
    color: var(--ink-2);
    font-size: 13px;
    text-align: left;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard);
}
.faq-rail-item:hover { background: var(--surface-2); color: var(--ink); }
.faq-rail-item.is-active {
    background: var(--primary-soft);
    color: var(--primary-ink);
    font-weight: 600;
}
.faq-rail-count {
    flex-shrink: 0;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}
.faq-rail-item.is-active .faq-rail-count { color: inherit; }

.faq-main { min-width: 0; }

.support-group-label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--muted);
    margin: 20px 0 6px;
}
.support-group-label:first-child { margin-top: 0; }

.faq-item {
    border: 1px solid var(--line);
    border-radius: 10px;
    padding: 2px 14px;
    margin-bottom: 8px;
    background: var(--surface);
}
.faq-item > summary {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    font-weight: 600;
    padding: 10px 0;
    list-style: none;
}
.faq-item > summary::-webkit-details-marker { display: none; }
.faq-item > summary > span { flex: 1; min-width: 0; }
.faq-item[open] > summary { border-bottom: 1px solid var(--line); }

/* Permalink stays out of the way until the row is hovered or focused. */
.faq-permalink {
    flex-shrink: 0;
    display: inline-flex;
    color: var(--muted);
    opacity: 0;
    transition: opacity var(--dur-fast) var(--ease-standard);
}
.faq-item:hover .faq-permalink,
.faq-item > summary:focus-within .faq-permalink { opacity: 1; }
.faq-permalink:hover { color: var(--primary); }

.faq-answer { padding: 10px 0 12px; color: var(--ink); }
.faq-answer p { margin: 0 0 8px; }
.faq-answer p:last-child { margin-bottom: 0; }
.faq-answer ul { margin: 0 0 8px; padding-left: 20px; }

@media (max-width: 820px) {
    .support-faq { grid-template-columns: 1fr; gap: 12px; }
    .faq-rail {
        position: static;
        flex-direction: row;
        flex-wrap: nowrap;
        gap: 6px;
        overflow-x: auto;
        padding-bottom: 4px;
        scrollbar-width: none;
    }
    .faq-rail::-webkit-scrollbar { display: none; }
    .faq-rail-item {
        width: auto;
        flex-shrink: 0;
        border: 1px solid var(--line);
        border-radius: 999px;
        padding: 6px 12px;
        font-size: 12px;
    }
    .faq-rail-item.is-active { border-color: transparent; }
}

/* ─── Help guides ─────────────────────────────────────────── */
.guide-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(220px, 100%), 1fr)); gap: 12px; }
.guide-card {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: 14px;
    border: 1px solid var(--line);
    border-radius: 12px;
    background: var(--surface);
    text-decoration: none;
    color: inherit;
    transition: border-color var(--dur-fast) var(--ease-standard);
}
.guide-card:hover { border-color: var(--primary); }
.guide-card-title { font-weight: 600; }
.guide-card-summary { font-size: 13px; color: var(--muted); }
.guide-detail h2 { margin-bottom: 6px; }
.guide-summary { color: var(--muted); margin-bottom: 16px; }

/* ─── Log an issue / suggestion ───────────────────────────── */
.contact-form { max-width: 640px; }
.attach-list { list-style: none; padding: 0; margin: 8px 0 0; }
.attach-list li {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 5px 0;
    border-bottom: 1px solid var(--line);
}
.btn-link-danger { background: none; border: none; color: var(--danger); cursor: pointer; font-size: 13px; }
.contact-actions { display: flex; align-items: center; gap: 16px; margin-top: 20px; flex-wrap: wrap; }

/* ─── Help guide detail — long-form procedural prose ──────
   (Dev_Docs/Support_Help_Guides_Seed_Plan.md G5 / Phase 4)
   Guides are read start-to-finish, unlike an FAQ accordion,
   so they get a measured column, real heading rhythm and
   numbered steps that are easy to follow while working.
   ───────────────────────────────────────────────────────── */

.guide-detail { max-width: 68ch; }

.guide-body { color: var(--ink); line-height: 1.65; }
.guide-body p { margin: 0 0 12px; }
.guide-body p:last-child { margin-bottom: 0; }

/* Section headings inside a guide (### in the source → h4). */
.guide-body h4 {
    margin: 24px 0 8px;
    font-size: 15px;
    font-weight: 650;
    color: var(--ink);
}
.guide-body h4:first-child { margin-top: 0; }

.guide-body ul,
.guide-body ol { margin: 0 0 12px; padding-left: 24px; }
.guide-body li { margin-bottom: 6px; }
.guide-body li:last-child { margin-bottom: 0; }

/* Steps carry more weight than bullets — they're the thing being followed. */
.guide-body ol { counter-reset: none; }
.guide-body ol > li { padding-left: 2px; }
.guide-body ol > li::marker { font-weight: 650; color: var(--primary); }

.guide-body a { color: var(--primary); text-decoration: underline; }
.guide-body strong { font-weight: 650; }

/* Screenshots. Bordered so a light UI screenshot doesn't bleed into the page. */
.guide-figure { margin: 0 0 20px; }
.guide-figure img {
    display: block;
    width: 100%;
    height: auto;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface-2);
}
.guide-figure figcaption {
    margin-top: 6px;
    font-size: 12.5px;
    color: var(--muted);
}

/* Guides tab: the category rail sits above the grid rather than beside it,
   because cards already form the horizontal rhythm. */
.faq-rail--inline {
    position: static;
    flex-direction: row;
    flex-wrap: wrap;
    gap: 6px;
    margin-bottom: 16px;
}
.faq-rail--inline .faq-rail-item {
    width: auto;
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 6px 12px;
    font-size: 12px;
}
.faq-rail--inline .faq-rail-item.is-active { border-color: transparent; }

/* ─── Quick start path (/support → Quick start) ────────────
   (Dev_Docs/Support_Help_Guides_Seed_Plan.md §7)
   The Tier 1 guides as a numbered path with self-reported
   progress. Reads as a checklist, not a nav menu.
   ───────────────────────────────────────────────────────── */

.qs-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 12px;
}
.qs-head h3 { margin: 0 0 4px; font-size: 17px; }
.qs-head p { margin: 0; font-size: 13.5px; max-width: 60ch; }
.qs-count {
    flex-shrink: 0;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

.qs-progress {
    height: 6px;
    border-radius: 999px;
    background: var(--surface-2);
    overflow: hidden;
    margin-bottom: 20px;
}
.qs-progress-fill {
    height: 100%;
    background: var(--primary);
    border-radius: 999px;
    transition: width var(--dur-normal, .25s) var(--ease-standard, ease);
}

.qs-done {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    margin-bottom: 16px;
    border-radius: 10px;
    background: var(--success-soft, var(--info-soft));
    color: var(--success-ink, var(--info-ink));
    font-size: 13.5px;
}

.qs-steps { list-style: none; margin: 0; padding: 0; }

.qs-step {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    margin-bottom: 8px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    transition: border-color var(--dur-fast) var(--ease-standard);
}
.qs-step.is-next { border-color: var(--primary); }
.qs-step.is-done { opacity: .68; }

/* The tick doubles as the step number until it's done. */
.qs-tick {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    border-radius: 999px;
    border: 1px solid var(--line);
    background: var(--surface-2);
    color: var(--ink-2);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-standard);
}
.qs-tick:hover { border-color: var(--primary); color: var(--primary); }
.qs-step.is-done .qs-tick {
    background: var(--primary);
    border-color: transparent;
    color: #fff;
}
.qs-step-number { font-size: 12px; font-weight: 650; font-variant-numeric: tabular-nums; }

.qs-step-main {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
    text-decoration: none;
    color: inherit;
}
.qs-step-title { font-weight: 600; font-size: 14px; }
.qs-step.is-done .qs-step-title { text-decoration: line-through; }
.qs-step-sub {
    font-size: 12.5px;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.qs-continue { flex-shrink: 0; }

@media (max-width: 600px) {
    .qs-step-sub { white-space: normal; }
    .qs-continue { display: none; }   /* the row itself is the link on mobile */
}

/* ── PWA ambient UI ───────────────────────────────────────────────────────────
   Offline bar, update toast, the "installed" confirmation and the install modal.
   Rendered by Shared/PwaHost in both MainLayout and PortalLayout.
   Dev_Docs/PWA_Apps_Plan.md §7.4/§8.3. The bar and the two floating cards sit
   above the bottom nav/dock and respect the notch insets that the rest of the
   mobile chrome already honours. */

.pwa-offline-bar {
    position: sticky;
    top: 0;
    z-index: 1200;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 7px 14px;
    font-size: .82rem;
    font-weight: 600;
    color: var(--warn-fg, #7a4a00);
    background: var(--warn-bg, #ffe9c2);
    border-bottom: 1px solid rgba(0, 0, 0, .08);
}

[data-theme="dark"] .pwa-offline-bar {
    color: #ffd79a;
    background: #3a2a10;
    border-bottom-color: rgba(255, 255, 255, .08);
}

.pwa-update-toast,
.pwa-install-card {
    position: fixed;
    left: 12px;
    right: 12px;
    /* Clear the bottom nav (main app) / dock (portal) plus the home indicator. */
    bottom: calc(88px + env(safe-area-inset-bottom));
    z-index: 1300;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 14px;
    border-radius: 14px;
    background: var(--surface-1, #fff);
    border: 1px solid var(--border, rgba(0, 0, 0, .1));
    box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
}

.pwa-install-card { flex-direction: column; align-items: stretch; }

.pwa-update-text,
.pwa-install-text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }

.pwa-update-text strong,
.pwa-install-text strong { font-size: .92rem; }

.pwa-update-text span,
.pwa-install-text span { font-size: .8rem; color: var(--text-muted, #667); }

.pwa-install-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* Install prompt — a centred modal (Shared/ModalShell), not another corner
   card. It asks a yes/no question we actually want answered, and it is only
   ever rendered on a phone or tablet (PwaHost.EvaluateInstallAsync). */
.pwa-install-modal .modal-dialog { max-width: 380px; }

.pwa-install-body {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 10px;
    padding: 28px 24px 20px;
}

.pwa-install-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: var(--radius-lg);
    background: var(--primary-soft);
    color: var(--primary-ink);
}

.pwa-install-title { margin: 0; font-size: 17px; font-weight: 700; }
.pwa-install-copy { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-2); }

/* Full-width thumb targets — the two answers carry equal weight. */
.pwa-install-footer .btn { flex: 1 1 0; }

.pwa-dismiss {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border: 0;
    border-radius: 8px;
    background: transparent;
    color: var(--text-muted, #667);
    cursor: pointer;
}

.pwa-dismiss:hover { background: var(--surface-2, rgba(0, 0, 0, .05)); }

.pwa-ios-steps { margin: 0 0 10px; padding-left: 20px; display: grid; gap: 8px; font-size: .9rem; }
.pwa-ios-note { margin: 0; font-size: .8rem; color: var(--text-muted, #667); }

/* Tablet/desktop: anchor bottom-right rather than spanning the viewport. */
@media (min-width: 768px) {
    .pwa-update-toast,
    .pwa-install-card {
        left: auto;
        right: 20px;
        bottom: 20px;
        max-width: 380px;
    }
}

/* ── In-app PDF viewer (js/pdf-viewer.js) ─────────────────────────────────────
   Full-screen overlay used for "View PDF" on mobile. Mobile browsers cannot
   render a PDF in an iframe/embed, and in an installed PWA a new tab or a
   download strands the user in a chrome-less activity with no back button —
   so we render it ourselves and supply the Back button. The DOM is built in
   JS (same approach as js/cookie-notice.js), but the styling lives here so it
   picks up the light/dark tokens like everything else. */
.pdfv-lock, .pdfv-lock body { overflow: hidden; }

.pdfv {
    position: fixed;
    inset: 0;
    z-index: 2000;
    display: flex;
    flex-direction: column;
    background: var(--bg);
    color: var(--ink);
}

.pdfv-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 12px;
    /* Notched phones: the overlay covers the status bar, so pad it out. */
    padding-top: calc(10px + env(safe-area-inset-top));
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-sm);
    flex: 0 0 auto;
}

.pdfv-title {
    flex: 1 1 auto;
    min-width: 0;
    font-weight: 600;
    font-size: 14px;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.pdfv-actions {
    display: flex;
    align-items: center;
    gap: 4px;
    flex: 0 0 auto;
}

.pdfv-count {
    font-size: 12px;
    color: var(--muted);
    padding: 0 4px;
    white-space: nowrap;
}

.pdfv-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    /* 44px is the tap-target floor — this is a thumb-first surface. */
    min-height: 44px;
    min-width: 44px;
    padding: 0 10px;
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
    color: var(--ink);
    background: transparent;
    border: 0;
    border-radius: var(--radius-sm);
    cursor: pointer;
}

.pdfv-btn:hover { background: var(--surface-2); }
.pdfv-btn:disabled { opacity: .4; cursor: default; }
.pdfv-btn:disabled:hover { background: transparent; }
.pdfv-icon { padding: 0; font-size: 20px; }
.pdfv-back { flex: 0 0 auto; }

/* The label is redundant next to the chevron once space is tight. */
@media (max-width: 420px) {
    .pdfv-back span { display: none; }
}

.pdfv-body {
    flex: 1 1 auto;
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
}

.pdfv-status {
    padding: 32px 16px;
    text-align: center;
    color: var(--muted);
    font-size: 14px;
}

.pdfv-status-error { color: var(--danger); }

.pdfv-pages {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    padding: 12px;
}

.pdfv-page {
    /* White regardless of theme: it is paper, not chrome. */
    background: #fff;
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow);
    overflow: hidden;
    max-width: 100%;
}

.pdfv-page canvas { display: block; }
