/* ============================================================================
   Booking funnel + patient portal.
   Layered ON TOP of magiclink.css, which already owns the page gradient (.mlk-page),
   the wrapper widths, the buttons (.mlk-btn*), the sheet/overlay and the spinner.
   Everything here is prefixed .bk-* so the two never collide.

   Design constraints that are not cosmetic:
     * Tap targets >= 44px — this is a phone-first surface reached from an SMS link.
     * No colour-only state. Timezone labels, policy notes and errors all carry text,
       because a patient misreading a time is the failure mode this whole feature exists
       to prevent.
   ========================================================================== */

/* ---------- progress ---------- */
.bk-progress {
    height: 4px;
    border-radius: 999px;
    background: rgba(255, 255, 255, .28);
    overflow: hidden;
    margin: 4px 0 6px;
}

.bk-progress-fill {
    height: 100%;
    background: #fff;
    border-radius: 999px;
    transition: width .25s ease;
}

.bk-steplabel {
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .02em;
    color: rgba(255, 255, 255, .82);
    margin-bottom: 14px;
}

/* ---------- card ---------- */
.bk-card {
    background: #fff;
    border-radius: 18px;
    padding: 24px 22px 20px;
    box-shadow: 0 18px 44px rgba(15, 32, 63, .16);
}

.bk-center { text-align: center; }

.bk-h1 {
    font-size: 21px;
    font-weight: 800;
    letter-spacing: -.01em;
    color: #0f203f;
    margin: 0 0 6px;
}

.bk-h2 {
    font-size: 18px;
    font-weight: 700;
    color: #0f203f;
    margin: 0 0 6px;
}

/* "Sending your code…" and friends: ring above, label centred beneath it. Block layout on
   purpose — .mlk-spinner centres itself with `margin: 0 auto`, which only works outside a
   flex row. The bottom margin on .bk-body is cleared so the padding here owns the spacing. */
.bk-sending { text-align: center; padding: 34px 0 28px; }
.bk-sending .bk-body { margin: 0; }

.bk-body {
    font-size: 14.5px;
    line-height: 1.5;
    color: #4a5a75;
    margin: 0 0 18px;
}

.bk-hint { font-size: 12.5px; color: #6b7b95; }
.bk-hint-block { display: block; margin-top: 8px; line-height: 1.45; }
.bk-optional { font-weight: 400; color: #8a97ad; }

/* ---------- choice list ---------- */
.bk-choices { display: flex; flex-direction: column; gap: 10px; margin-bottom: 6px; }

.bk-choice {
    display: grid;
    grid-template-columns: 28px 1fr;
    grid-template-areas: "icon main" "icon sub" "icon meta";
    gap: 2px 12px;
    width: 100%;
    min-height: 60px;
    padding: 14px 16px;
    text-align: left;
    background: #f7f9fc;
    border: 1.5px solid #e3e9f2;
    border-radius: 14px;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease, transform .08s ease;
}

.bk-choice:hover:not(:disabled) { border-color: #1d4ed8; background: #f2f6ff; }
.bk-choice:active:not(:disabled) { transform: scale(.995); }
.bk-choice:disabled { opacity: .55; cursor: default; }
.bk-choice:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

.bk-choice-primary { background: #eef4ff; border-color: #bfd3ff; }

.bk-choice-icon { grid-area: icon; align-self: center; color: #1d4ed8; }
.bk-choice-main { grid-area: main; font-size: 15px; font-weight: 700; color: #0f203f; }
.bk-choice-sub  { grid-area: sub;  font-size: 13px; color: #5b6b85; line-height: 1.4; }
.bk-choice-meta { grid-area: meta; display: flex; gap: 8px; align-items: center; margin-top: 4px; }

/* ---------- fields ---------- */
.bk-field { margin-bottom: 14px; }
.bk-inline { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; }

.bk-label {
    display: block;
    font-size: 12.5px;
    font-weight: 700;
    color: #37455f;
    margin-bottom: 5px;
}

.bk-input, .bk-select {
    width: 100%;
    min-height: 46px;
    padding: 11px 13px;
    font-size: 15px;                 /* >=16px avoids iOS zoom-on-focus; 15 is the floor we accept */
    font-family: inherit;
    color: #0f203f;
    background: #fff;
    border: 1.5px solid #d8e0ec;
    border-radius: 11px;
    box-sizing: border-box;
}

.bk-input:focus, .bk-select:focus {
    outline: none;
    border-color: #1d4ed8;
    box-shadow: 0 0 0 3px rgba(29, 78, 216, .14);
}

.bk-input:disabled, .bk-select:disabled { background: #f4f6fa; color: #8a97ad; }

textarea.bk-input { resize: vertical; min-height: 64px; }

.bk-otp {
    letter-spacing: .5em;
    text-align: center;
    font-size: 22px;
    font-weight: 700;
    font-family: 'Spline Sans Mono', ui-monospace, monospace;
}

.bk-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
@media (max-width: 460px) { .bk-grid-2 { grid-template-columns: 1fr; } }

.bk-check {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    font-size: 13.5px;
    line-height: 1.45;
    color: #37455f;
    padding: 10px 0;
    cursor: pointer;
}

.bk-check input { margin-top: 2px; width: 18px; height: 18px; flex: none; }

/* ---------- slots ---------- */
.bk-day { margin-bottom: 18px; }

.bk-day-head {
    font-size: 13px;
    font-weight: 800;
    color: #0f203f;
    padding-bottom: 8px;
    border-bottom: 1px solid #eaeff7;
    margin-bottom: 10px;
}

.bk-slots { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }

.bk-slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1px;
    min-height: 52px;
    padding: 9px 6px;
    background: #f7f9fc;
    border: 1.5px solid #e3e9f2;
    border-radius: 11px;
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
}

.bk-slot:hover:not(:disabled) { border-color: #1d4ed8; background: #f2f6ff; }
.bk-slot:disabled { opacity: .5; cursor: default; }
.bk-slot:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

.bk-slot-time { font-size: 14.5px; font-weight: 700; color: #0f203f; }

/* The timezone rides with every single time, deliberately. */
.bk-slot-tz {
    font-size: 10.5px;
    font-weight: 700;
    letter-spacing: .04em;
    color: #1d4ed8;
    background: #eaf0ff;
    border-radius: 5px;
    padding: 1px 5px;
    margin-left: 4px;
}

.bk-slot-provider { font-size: 11px; color: #6b7b95; text-align: center; line-height: 1.25; }

/* ---------- notes / alerts ---------- */
.bk-note {
    background: #f4f7fc;
    border: 1px solid #e3e9f2;
    border-radius: 12px;
    padding: 14px 16px;
    font-size: 13.5px;
    line-height: 1.5;
    color: #4a5a75;
}

.bk-alert {
    border-radius: 12px;
    padding: 12px 14px;
    font-size: 13.5px;
    line-height: 1.5;
    margin-bottom: 14px;
}

.bk-alert-error { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }
.bk-alert-warn  { background: #fffbeb; border: 1px solid #fde68a; color: #92400e; }
.bk-alert-info  { background: #eff6ff; border: 1px solid #bfdbfe; color: #1e40af; }
.bk-alert a { color: inherit; font-weight: 700; }

/* ---------- actions ---------- */
.bk-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 10px;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid #eef2f8;
}

.bk-actions:only-child, .bk-actions > :only-child { margin-left: auto; }

.bk-secondary-row {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid #eef2f8;
}

.mlk-btn-danger {
    background: #dc2626;
    color: #fff;
    border: none;
}

.mlk-btn-danger:hover:not(:disabled) { background: #b91c1c; }

/* ---------- confirmed ---------- */
.bk-success-badge {
    width: 62px;
    height: 62px;
    margin: 0 auto 14px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: #dcfce7;
    color: #15803d;
}

.bk-success-badge .mud-icon-root { font-size: 34px; }

.bk-summary {
    background: #f7f9fc;
    border: 1px solid #e3e9f2;
    border-radius: 14px;
    padding: 16px;
    margin: 16px 0;
    text-align: left;
}

.bk-summary-when { font-size: 16px; font-weight: 800; color: #0f203f; margin-bottom: 8px; }
.bk-summary-time { display: block; font-size: 20px; font-weight: 800; color: #1d4ed8; margin-top: 2px; }

.bk-summary-row {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13.5px;
    color: #4a5a75;
    padding: 3px 0;
}

.bk-summary-row .mud-icon-root { font-size: 17px; color: #6b7b95; }

.bk-confirm-actions { display: flex; flex-direction: column; gap: 8px; margin-top: 6px; }

.bk-empty-icon { font-size: 46px; color: #b6c2d6; margin-bottom: 10px; }

/* ---------- portal ---------- */
.bk-topbar-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 13px;
    font-weight: 700;
    color: #fff;
    text-decoration: none;
    background: rgba(255, 255, 255, .16);
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 999px;
    padding: 6px 13px;
    cursor: pointer;
    white-space: nowrap;
}

.bk-topbar-link:hover { background: rgba(255, 255, 255, .26); }
.bk-topbar-link .mud-icon-root { font-size: 16px !important; width: 16px; height: 16px; flex: none; }

/* ---------- signed-in account cluster (portal top bar) ---------- */
.bk-account { display: flex; align-items: center; gap: 8px; min-width: 0; }

.bk-account-chip {
    display: flex; align-items: center; gap: 8px; min-width: 0;
    padding: 4px 12px 4px 4px;
    background: rgba(255, 255, 255, .16);
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 999px;
}

.bk-account-avatar {
    width: 28px; height: 28px; flex: none; border-radius: 50%;
    background: #fff; color: #1d4ed8;
    display: grid; place-items: center;
    font-size: 12px; font-weight: 800;
}

.bk-account-id { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.bk-account-name { font-size: 13px; font-weight: 700; color: #fff; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bk-account-sub { font-size: 11px; color: rgba(255, 255, 255, .75); white-space: nowrap; }

.bk-topbar-signout { background: transparent; }

/* Phone: the icons carry the meaning and the labels are dropped, so the brand keeps its room. The
   identity chip stays — knowing WHOSE portal this is matters more on a shared phone, not less. */
@media (max-width: 560px) {
    .bk-topbar-label { display: none; }
    .bk-topbar-link { padding: 7px 9px; }
    .bk-account-sub { display: none; }
    .bk-account-name { max-width: 92px; }
}

/* The top bar is TRANSPARENT over the page gradient on phones and a WHITE bar from 641px up
   (magiclink.css). The styles above are the on-gradient treatment — white text on a translucent
   white fill — which on the white desktop bar rendered white-on-white: the account name, the avatar
   ring and the Sign out button were all invisible. Everything that relies on the dark backdrop is
   restated here for the light one. */
@media (min-width: 641px) {
    .bk-topbar-link {
        color: #1d4ed8;
        background: #fff;
        border-color: #bfd3ff;
    }
    .bk-topbar-link:hover { background: #eef4ff; }

    .bk-account-chip {
        background: #f4f7fc;
        border-color: #e3e9f2;
    }
    .bk-account-avatar { background: #1d4ed8; color: #fff; }
    .bk-account-name { color: #0f203f; }
    .bk-account-sub { color: #6b7b95; }

    /* Sign out is the destructive-ish action of the pair: quieter than Book, and legible. */
    .bk-topbar-signout {
        color: #5b6b85;
        background: #fff;
        border-color: #dbe3ee;
    }
    .bk-topbar-signout:hover { background: #f3f6fa; color: #0f203f; }
}

.bk-portal-head {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.bk-portal-who { display: flex; align-items: center; gap: 12px; min-width: 0; }

/* Initials, matching the provider avatars in the funnel — it confirms WHOSE appointments these are,
   which matters on a shared device where the wrong account silently showing its list is the failure
   mode. */
.bk-portal-avatar {
    width: 44px; height: 44px; flex: none; border-radius: 13px;
    background: #eef4ff; color: #1d4ed8; border: 1px solid #dbe6fb;
    display: grid; place-items: center;
    font-weight: 800; font-size: 15px;
}

.bk-portal-head .bk-h1 { margin: 0; }
.bk-portal-head .bk-body { margin: 2px 0 0; }

.bk-tabs { display: flex; gap: 4px; border-bottom: 1px solid #eaeff7; margin-bottom: 16px; }

.bk-tab {
    background: none;
    border: none;
    border-bottom: 2.5px solid transparent;
    padding: 9px 14px;
    font-size: 14px;
    font-weight: 700;
    color: #6b7b95;
    cursor: pointer;
}

.bk-tab.is-active { color: #1d4ed8; border-bottom-color: #1d4ed8; }

/* ---------- appointment card ----------
   Three columns: WHEN (a calendar chip), WHAT (time + provider + place), and the actions. The date
   is a chip rather than a line of text because the list is scanned for a date, not read — and it
   speaks the same visual language as the date tiles in the booking picker, so the two surfaces
   don't look like different products. */
.bk-appt {
    display: grid;
    grid-template-columns: 76px 1fr auto;
    gap: 16px;
    align-items: start;
    border: 1.5px solid #e3e9f2;
    border-radius: 14px;
    padding: 16px;
    margin-bottom: 12px;
    background: #fff;
    transition: border-color .12s ease, box-shadow .12s ease;
}

.bk-appt:hover { border-color: #cddcf5; box-shadow: 0 4px 16px rgba(15, 32, 63, .06); }

/* Past appointments are history, not choices: muted so the eye lands on what is still ahead. */
.bk-appt.is-past { background: #fbfcfe; }
.bk-appt.is-past .bk-appt-cal { background: #f2f5f9; border-color: #e6ecf4; color: #8a97ad; }
.bk-appt.is-past .bk-appt-time { color: #6b7b95; }

.bk-appt-cal {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    padding: 9px 4px;
    border: 1px solid #dbe6fb; border-radius: 12px;
    background: #eef4ff; color: #1d4ed8;
    line-height: 1.1;
}
.bk-appt-dow { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.bk-appt-day { font-size: 24px; font-weight: 800; margin: 1px 0; }
.bk-appt-mon { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }

/* Fallback when DateText is not in the expected format — never drop the date entirely. */
.bk-appt-datetext { font-size: 12px; font-weight: 700; color: #6b7b95; }

.bk-appt-main { min-width: 0; }
.bk-appt-time { font-size: 19px; font-weight: 800; color: #0f203f; }

.bk-appt-meta {
    display: flex; flex-wrap: wrap; gap: 4px 16px;
    margin-top: 6px; font-size: 13.5px; color: #4a5a75;
}
.bk-appt-meta > div { display: flex; align-items: center; gap: 5px; min-width: 0; }
.bk-appt-meta .mud-icon-root { font-size: 16px !important; width: 16px; height: 16px; color: #8a97ad; flex: none; }

.bk-appt-tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; }

/* Actions sit in their own column on desktop; the border above them is only needed once they wrap
   underneath, so it is added back in the stacked layout below. */
.bk-appt-actions { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.bk-appt-actions .mlk-btn { white-space: nowrap; }

.bk-appt-side { max-width: 240px; }

@media (max-width: 720px) {
    .bk-appt { grid-template-columns: 60px 1fr; }
    .bk-appt-side { grid-column: 1 / -1; max-width: none; }
    .bk-appt-actions {
        flex-direction: row; margin-top: 4px; padding-top: 12px;
        border-top: 1px solid #eef2f8;
    }
    .bk-appt-day { font-size: 20px; }
}

.bk-pill {
    display: inline-block;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .02em;
    color: #1d4ed8;
    background: #eaf0ff;
    border-radius: 6px;
    padding: 2px 7px;
}

.bk-pill-muted { color: #6b7b95; background: #eef1f6; }

.bk-sheet { max-height: 86vh; overflow-y: auto; }

/* Wider than the 410px magiclink sheet: these two dialogs carry a DATE GRID and a time grid, which
   at that width wrap to one or two columns and turn a week into a long scroll. */
@media (min-width: 641px) {
    .bk-sheet { width: min(680px, 100% - 32px); }
}

/* Header row: title left, close right. The close button is the only dismissal these dialogs have —
   the backdrop deliberately does nothing — so it must always be present and reachable. */
.bk-sheet-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 4px;
}

.bk-sheet-close {
    flex: none;
    width: 34px; height: 34px;
    border-radius: 50%;
    border: 1px solid #e3e9f2;
    background: #fff;
    color: #5b6b85;
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
}

.bk-sheet-close:hover:not(:disabled) { background: #f3f6fa; color: #0f203f; }
.bk-sheet-close:disabled { opacity: .45; cursor: default; }
.bk-sheet-close:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

/* Prompt shown before a date is chosen. */
.bk-pickday { font-size: 13px; font-weight: 700; color: #37455f; margin: 14px 0 8px; }

/* ---------- "already booked" list, shown when a cap refuses a booking ---------- */
/* Informational, so it is a neutral panel rather than a second error block: the red alert above
   already carries the refusal, and repeating that tone here would read as four problems. */
.bk-existing {
    background: #f7f9fc;
    border: 1px solid #e3e9f2;
    border-radius: 12px;
    padding: 12px 14px;
    margin-bottom: 14px;
}

.bk-existing-head {
    font-size: 12px; font-weight: 800; letter-spacing: .03em; text-transform: uppercase;
    color: #6b7b95; margin-bottom: 8px;
}

.bk-existing-row {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
    padding: 8px 0; border-top: 1px solid #eef2f8;
}
.bk-existing-row:first-of-type { border-top: none; }

/* The one that is actually blocking THIS booking. */
.bk-existing-row.is-same .bk-existing-name { color: #0f203f; }

.bk-existing-when { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.bk-existing-date { font-size: 12.5px; font-weight: 700; color: #6b7b95; }
.bk-existing-time { font-size: 14px; font-weight: 800; color: #0f203f; }

.bk-existing-who { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; min-width: 0; }
.bk-existing-name { font-size: 13.5px; font-weight: 700; color: #37455f; }
.bk-existing-prov { font-size: 12.5px; color: #6b7b95; }

/* Reduced motion: the progress bar and press states are the only animation here. */
@media (prefers-reduced-motion: reduce) {
    .bk-progress-fill, .bk-choice, .bk-slot { transition: none; }
}

/* ============================================================================
   Single-page picker (.zd-*).

   What is borrowed from Zocdoc is the INFORMATION ARCHITECTURE, not the look: a persistent filter
   bar, one row per provider, and a fixed day-column grid so availability is scannable at a glance.
   Colour, type and radii stay on the SAMS Trust-Blue system already used by app.css/magiclink.css.

   Two rules here are accessibility, not taste:
     * Slot chips are >= 44px tall. This is a phone-first surface and a mis-tap books the wrong time.
     * An empty day renders a visible dash. Collapsing it would shift the columns under the reader
       and make a closed Wednesday look like a loading bug.
   ========================================================================== */

/* The --mlk-* tokens are scoped to .mlk-page in magiclink.css, NOT to :root — so every
   .mlk-btn-* used inside .zd-page resolved var(--mlk-primary) to nothing and rendered WHITE.
   Re-declaring the tokens here is what makes the shared buttons pick up the theme. Same values,
   one source of truth to keep in step if magiclink.css changes. */
.zd-page {
    /* Full viewport, as a column: top bar, filter bar, then .zd-wrap taking the rest. This is what
       lets the results region size itself to the space actually left over instead of subtracting a
       list of hand-measured constants from 100vh.

       height, NOT min-height — and that distinction is the whole thing. min-height:100vh sets a
       FLOOR: the column may still grow past the viewport, so .zd-wrap was never actually forced to
       shrink, its columns never had to scroll internally, and the document scrolled instead —
       taking the results under the sticky bars as it went. A definite height is what makes
       `flex: 1 1 auto; min-height: 0` on the wrap mean "you get exactly what is left".

       dvh first for mobile browsers, whose 100vh includes a URL bar that is not there. */
    display: flex;
    flex-direction: column;
    height: 100vh;
    height: 100dvh;
    overflow: hidden;

    --mlk-primary: #1976D2;
    --mlk-primary-deep: #0D47A1;
    --mlk-danger: #D93025;
    --mlk-danger-hover: #B3261E;
    --mlk-danger-bg: #FDECEA;
    --mlk-danger-border: #F4C7C3;
    --mlk-ctl-border: #D7E1EC;
    --mlk-subtle-border: #C5D3E3;
    --mlk-text: #1C2733;
    --mlk-text-2: #4E5D6C;
    --mlk-text-3: #64748B;
    --mlk-muted-panel-2: #F5F8FC;

    min-height: 100vh;
    background: #f4f7fb;
}

/* The dialog is portalled outside .zd-page, so it needs the tokens too. */
.zd-dialog {
    --mlk-primary: #1976D2;
    --mlk-primary-deep: #0D47A1;
    --mlk-danger: #D93025;
    --mlk-danger-hover: #B3261E;
    --mlk-ctl-border: #D7E1EC;
    --mlk-text: #1C2733;
    --mlk-text-2: #4E5D6C;
    --mlk-text-3: #64748B;
    --mlk-muted-panel-2: #F5F8FC;
}

/* Back arrow above a step. Sits outside .bk-actions so it reads as navigation ("where am I")
   rather than as one of the choices on offer. */
.zd-back {
    display: inline-flex; align-items: center; gap: 5px;
    background: none; border: none; padding: 4px 6px 4px 0; margin-bottom: 10px;
    font: inherit; font-size: 13px; font-weight: 700; color: #1976D2;
    cursor: pointer; border-radius: 8px;
}
.zd-back:hover:not(:disabled) { color: #0D47A1; }
.zd-back:disabled { opacity: .45; cursor: default; }
.zd-back .mud-icon-root { font-size: 17px; }

/* Secondary actions (Back, Cancel, Resend, Keep it) are OUTLINED, not bare text: a text-only
   control next to a solid primary reads as a caption, and patients were missing the way out. */
.zd-page .mlk-btn-outline,
.zd-dialog .mlk-btn-outline {
    background: #fff; color: #1976D2; border: 1.5px solid #bfd3ff; font-weight: 700;
}
.zd-page .mlk-btn-outline:hover:not(:disabled),
.zd-dialog .mlk-btn-outline:hover:not(:disabled) { background: #eef4ff; border-color: #1976D2; }
.zd-page .mlk-btn-outline:disabled,
.zd-dialog .mlk-btn-outline:disabled { opacity: .5; cursor: default; }

/* Primary action: solid brand blue, never a white ghost. Stated explicitly rather than relying on
   the token alone, so a missing variable can never silently produce an invisible button again. */
.zd-page .mlk-btn-primary,
.zd-dialog .mlk-btn-primary {
    background: #1976D2; color: #fff; border: none; font-weight: 700;
}
.zd-page .mlk-btn-primary:hover:not(:disabled),
.zd-dialog .mlk-btn-primary:hover:not(:disabled) { background: #0D47A1; }
.zd-page .mlk-btn-primary:disabled,
.zd-dialog .mlk-btn-primary:disabled { background: #9DB8DA; color: #fff; cursor: default; }

/* ---------- top bar ---------- */
/* BOTH bars are direct flex children of .zd-page, and both are pinned to their natural height.
   flex-shrink defaults to 1, so without this a tall results region would squeeze the two navbars
   instead of scrolling its own columns — the bars would visibly compress as the provider list
   grew. Fixing them at their content height is what leaves .zd-wrap the exact balance of the
   viewport, whatever those two happen to measure. */
.zd-topbar,
.zd-filterbar { flex: 0 0 auto; }

.zd-topbar { background: #fff; border-bottom: 1px solid #e3e9f2; position: sticky; top: 0; z-index: 20; }

.zd-topbar-inner {
    width: min(var(--sams-container), 100% - 2 * var(--sams-gutter));
    margin: 0 auto; padding: 12px 0;
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
}

.zd-brand { display: flex; align-items: center; gap: 10px; min-width: 0; }

.zd-brand-avatar {
    width: 32px; height: 32px; flex: none; border-radius: 9px; background: #1d4ed8; color: #fff;
    display: grid; place-items: center; font-weight: 800; font-size: 15px;
}

.zd-brand-name { font-weight: 800; font-size: 15.5px; color: #0f203f; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.zd-topbar-right { display: flex; align-items: center; gap: 8px; flex: none; }

/* The wide-screen shape of the right-hand side: phone number, then the action pills.
   .zd-topbar-inline exists so the narrow-screen account menu can replace all three as a group —
   but wrapping them cost them .zd-topbar-right's flex gap, which left the number and the Sign in
   pill separated by nothing but a collapsed space. Given back here, with a hairline between the
   two kinds of thing: the number is information, the pills are actions.

   This MUST stay above the max-width:880px block that hides it. A media query adds no specificity,
   so the same selector declared later in the file wins at every width — declared after it, this
   rule re-showed the inline links on a phone and the bar carried both shapes at once. */
.zd-topbar-inline { display: flex; align-items: center; gap: 10px; }

.zd-topbar-inline .zd-topbar-phone { padding-right: 12px; border-right: 1px solid #e3e9f2; }

.zd-topbar-phone { display: inline-flex; align-items: center; gap: 5px; font-size: 13.5px; font-weight: 700; color: #1d4ed8; text-decoration: none; }
.zd-topbar-phone .mud-icon-root { font-size: 16px; }

.zd-topbar-link {
    font-size: 13px; font-weight: 700; color: #1d4ed8; text-decoration: none;
    border: 1.5px solid #bfd3ff; border-radius: 999px; padding: 6px 13px; background: #fff; cursor: pointer;
}

.zd-topbar-link:hover { background: #eef4ff; }

/* Quieter than "My appointments" beside it — an escape hatch, not an invitation. */
.zd-topbar-signout { color: #6b7b95; border-color: #dbe3ee; font-weight: 600; }
.zd-topbar-signout:hover { background: #f3f6fa; }

/* ---------- filter bar ---------- */
/* Sticky UNDER the 57px top bar so both stay visible while the patient scrolls providers. */
.zd-filterbar { background: #fff; border-bottom: 1px solid #e3e9f2; position: sticky; top: 57px; z-index: 15; }

.zd-filterbar-inner {
    width: min(var(--sams-container), 100% - 2 * var(--sams-gutter));
    margin: 0 auto; padding: 12px 0;
    display: flex; gap: 12px; flex-wrap: wrap;
}

.zd-filter { display: flex; flex-direction: column; gap: 3px; min-width: 170px; flex: 1 1 170px; }

.zd-filter-label { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: #6b7b95; }

.zd-filter-select {
    min-height: 44px; padding: 9px 12px; font-size: 14.5px; font-family: inherit; color: #0f203f;
    background: #fff; border: 1.5px solid #d8e0ec; border-radius: 10px; cursor: pointer;
}

.zd-filter-select:focus { outline: none; border-color: #1d4ed8; box-shadow: 0 0 0 3px rgba(29, 78, 216, .14); }
.zd-filter-select:disabled { background: #f4f6fa; color: #8a97ad; }

/* ---------- layout ---------- */
.zd-wrap {
    width: min(var(--sams-container), 100% - 2 * var(--sams-gutter));
    margin: 0 auto; padding: 20px 0;
    /* Fills the page column so .zd-split can flex inside it. min-height:0 is what actually lets a
       flex child shrink below its content and scroll — without it the columns push the wrap taller
       than the viewport and the document scrolls again.
       The 20px padding is the gutter above and BELOW the cards; it is inside the measurement now,
       rather than being forgotten by a calc(). */
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
}
/* Everything except the results region keeps its natural height — only .zd-split flexes. */
.zd-wrap > *:not(.zd-split) { flex: 0 0 auto; }
.zd-narrow { max-width: 620px; }
.zd-center { text-align: center; }

.zd-card { background: #fff; border: 1px solid #e3e9f2; border-radius: 16px; padding: 22px; box-shadow: 0 2px 10px rgba(15, 32, 63, .05); }

.zd-h2 { font-size: 18px; font-weight: 800; color: #0f203f; margin: 0 0 6px; }
.zd-body { font-size: 14.5px; line-height: 1.55; color: #4a5a75; margin: 0; }
.zd-body a { color: #1d4ed8; }
.zd-empty-icon { font-size: 44px; color: #b6c2d6; margin-bottom: 10px; }

.zd-link { background: none; border: none; padding: 0; font: inherit; font-size: 13.5px; font-weight: 700; color: #1d4ed8; cursor: pointer; text-decoration: underline; }

.zd-alert { border-radius: 12px; padding: 12px 14px; font-size: 13.5px; line-height: 1.5; margin-bottom: 14px; }
.zd-alert-error { background: #fef2f2; border: 1px solid #fecaca; color: #991b1b; }
.zd-alert a { color: inherit; font-weight: 700; }

/* ---------- self-pay notice ---------- */
/* Amber, not red: this is a price, not an error. Red would read as "something went wrong" for a
   patient who deliberately chose to pay themselves. */
.zd-selfpay {
    display: flex; gap: 12px; align-items: flex-start;
    background: #fffbeb; border: 1px solid #fde68a; border-radius: 12px;
    padding: 13px 15px; margin-bottom: 14px;
}

.zd-selfpay .mud-icon-root { font-size: 22px; color: #b45309; flex: none; }
.zd-selfpay-amount { font-size: 15.5px; font-weight: 800; color: #92400e; }
.zd-selfpay-note { font-size: 13px; color: #92400e; opacity: .85; line-height: 1.45; margin-top: 2px; }

/* ---------- week nav ---------- */
.zd-weeknav { display: flex; align-items: center; justify-content: center; gap: 14px; margin-bottom: 14px; }

.zd-weekbtn {
    width: 40px; height: 40px; border-radius: 50%; border: 1.5px solid #d8e0ec; background: #fff;
    font-size: 20px; line-height: 1; color: #1d4ed8; cursor: pointer;
}

.zd-weekbtn:hover:not(:disabled) { background: #eef4ff; border-color: #1d4ed8; }
.zd-weekbtn:disabled { opacity: .4; cursor: default; }

.zd-weeklabel { font-size: 14px; font-weight: 800; color: #0f203f; }

.zd-tz {
    font-size: 10.5px; font-weight: 800; letter-spacing: .04em; color: #1d4ed8;
    background: #eaf0ff; border-radius: 5px; padding: 2px 6px; margin-left: 6px;
}

/* ---------- provider list LEFT, one availability card RIGHT ---------- */
/* The doctor is what the patient is choosing; the calendar is the single surface that answers that
   choice. Sharing one border made the provider read as a heading on a calendar widget instead of a
   listing entry, so each gets its own card — and there is exactly ONE calendar for the location,
   not one per doctor. A column of calendars turns a choice of person into a grid comparison. */
.zd-split {
    display: grid;
    grid-template-columns: 288px 1fr;
    gap: 14px;
    /* stretch, not start: both columns are the same height as the region, so the availability side
       fills the screen instead of collapsing to a one-line "Select a provider…" card floating in
       white space. */
    align-items: stretch;

    /* Takes whatever height is left, MEASURED rather than assumed — see the flex chain on .zd-page
       and .zd-wrap below. The previous version subtracted a hand-counted 128px for the two bars,
       which missed .zd-wrap's own 20px top and bottom padding and left the page ~40px too tall:
       hence the scrollbar that would not go away. Nothing here is a guess now, so it cannot drift
       when a bar's height changes.
       Each COLUMN scrolls inside this region; the document does not scroll at all. */
    flex: 1 1 auto;
    min-height: 0;
}

.zd-provider-col {
    display: flex;
    flex-direction: column;
    gap: 10px;
    min-width: 0;
    /* No longer sticky: the parent is now a fixed-height region, so the rail simply fills it and
       scrolls inside. Sticky was how the list used to stay on screen while the DOCUMENT scrolled —
       with the document no longer scrolling there is nothing to stick to.
       overscroll-behavior:contain stops the wheel chaining out once the rail hits its end. */
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;

    /* Keeps the scrollbar off the cards, and gives the last card's shadow and a focus ring room
       inside the scroll box rather than being clipped by it. */
    padding: 2px 6px 4px 2px;
    margin: -2px -6px -4px -2px;
}

.zd-avail-col {
    min-width: 0;
    /* Same treatment as the rail: full height of the region, scrolling inside itself. Flex column
       so the single card it holds can be told to fill that height. */
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
}
/* The card fills the column rather than sitting at the top of it — that is what turns "Select a
   provider to see their available dates." from a thin strip into a full-height panel. flex-grow
   only, so a calendar taller than the screen still overflows into the column's own scroll. */
.zd-avail-col > * { flex: 1 0 auto; }

/* Below this the rail squeezes the day columns under a tappable width, so stack the two columns. */
@media (max-width: 880px) {
    /* Stacked, the whole page goes back to flowing and scrolling normally: one column above the
       other cannot both fit a phone screen, and two internal scroll boxes inside a scrolling page
       is three scrollbars for two lists. The floor stays so short content still fills the screen. */
    .zd-page { height: auto; min-height: 100vh; overflow: visible; }
    .zd-wrap { display: block; }

    .zd-split {
        grid-template-columns: 1fr; gap: 10px;
        flex: 0 0 auto; align-items: start;
    }
    .zd-provider-col,
    .zd-avail-col { height: auto; overflow: visible; }
    .zd-avail-col > * { flex: 0 0 auto; }
}

/* Both cards share the same surface treatment so they read as siblings. */
.zd-provider-card,
.zd-avail-card {
    background: #fff;
    border: 1px solid #e3e9f2;
    border-radius: 16px;
    box-shadow: 0 2px 10px rgba(15, 32, 63, .05);
}

.zd-provider-card {
    display: flex;
    gap: 12px;
    align-items: flex-start;
    padding: 16px;
    min-width: 0;
    /* NOT sticky itself — .zd-provider-col carries that for the whole list. A sticky card inside a
       sticky column pins against its own parent and stops moving. */
}

/* The selected doctor must be unmistakable: this card is the only thing explaining whose times fill
   the panel beside it. A tinted surface plus a heavier border, not just a colour shift, so it still
   reads when the list is scanned rather than studied. */
.zd-provider-card.is-selected {
    border-color: #1976D2;
    background: #f7faff;
    box-shadow: 0 4px 18px rgba(25, 118, 210, .16);
}

.zd-provider-card.is-selected .zd-avatar {
    background: #1976D2;
    border-color: #1976D2;
    color: #fff;
}

/* Initials, not a photo: the directory holds no images, and a generic silhouette reads as a missing
   asset. A soft tinted tile rather than a saturated gradient block — it is an identity marker, not
   the loudest thing on the row. */
.zd-avatar {
    width: 42px; height: 42px; flex: none; border-radius: 12px;
    background: #eef4ff; color: #1d4ed8; border: 1px solid #dbe6fb;
    display: grid; place-items: center;
    font-weight: 800; font-size: 14px;
}

.zd-provider-id { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.zd-provider-name { font-size: 15px; font-weight: 800; color: #0f203f; line-height: 1.3; }
.zd-provider-sub { font-size: 12.5px; color: #6b7b95; line-height: 1.35; }

.zd-provider-loc {
    display: flex; align-items: flex-start; gap: 5px;
    font-size: 12px; color: #7b89a0; line-height: 1.35; margin-top: 1px;
}

/* MudBlazor's own .mud-icon-root sets 1.5rem and was beating a plain class selector — which is why
   these rendered oversized. The components now pass Size="Size.Small"; this pins the size so a
   future MudBlazor default cannot blow the row out again. */
.zd-provider-card .mud-icon-root { font-size: 14px !important; width: 14px; height: 14px; flex: none; }
.zd-provider-loc .mud-icon-root { color: #9fb0c6; margin-top: 1px; }

/* A quiet pill, not a green shout: it qualifies the doctor, it is not a call to action. */
.zd-badge {
    display: inline-flex; align-items: center; gap: 4px;
    align-self: flex-start; margin-top: 5px;
    padding: 3px 8px; border-radius: 999px;
    background: #ecfdf3; border: 1px solid #cdefdc;
    font-size: 11px; font-weight: 700; color: #15803d;
}
.zd-badge .mud-icon-root { color: #15803d; }

/* Awaiting a click: the provider card IS the selector when a location has several doctors. */
.zd-provider-card.is-selectable { cursor: pointer; transition: border-color .12s ease, box-shadow .12s ease; }
.zd-provider-card.is-selectable:hover { border-color: #1976D2; box-shadow: 0 4px 16px rgba(25, 118, 210, .14); }
.zd-provider-card.is-selectable:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

.zd-provider-cta { margin-top: 7px; font-size: 12px; font-weight: 800; color: #1976D2; }


/* Now a whole card in the right-hand column (nothing is selected yet), not a strip inside one. */
/* The empty state for "no doctor chosen yet".
   It was a thin strip of #f7f9fc with a dashed hairline — on a page whose own background is nearly
   the same white, that read as nothing at all, and now that it fills the column it was a large
   area of nothing. It is a real panel: white card surface like its sibling, a soft brand wash
   behind the icon, and the message centred in the space rather than pinned to the top-left corner
   of it. Still dashed, because the border is what says "this will be replaced by content". */
.zd-tapprompt {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    gap: 14px;
    padding: 32px 24px;
    font-size: 14px;
    color: #5b6b85;
    background: #fff;
    border: 1.5px dashed #cfdcee;
    border-radius: 16px;
}
.zd-avail-card.zd-tapprompt { border-style: dashed; box-shadow: none; }

/* The icon gets a disc so the panel has a focal point instead of one grey glyph adrift in it. */
.zd-tapprompt .mud-icon-root {
    font-size: 26px !important; width: 26px; height: 26px;
    color: #1d4ed8; flex: none;
    box-sizing: content-box;
    padding: 15px;
    background: #eef4ff;
    border-radius: 50%;
}
.zd-tapprompt span { max-width: 30ch; line-height: 1.5; }

/* Not bookable online at all: no calendar, no button, no arrows — just the route that works. */
.zd-callonly {
    display: flex; gap: 10px; align-items: flex-start;
    background: #f7f9fc; border: 1px solid #e3e9f2; border-radius: 11px; padding: 14px;
}
.zd-callonly .mud-icon-root { font-size: 18px !important; color: #8a97ad; flex: none; margin-top: 1px; }
.zd-callonly-title { font-size: 13.5px; font-weight: 800; color: #0f203f; }
.zd-callonly-text { font-size: 12.5px; color: #5b6b85; line-height: 1.45; margin-top: 2px; }
.zd-callonly-text a { color: #1d4ed8; }

.zd-avail-card { padding: 14px 18px 18px; min-width: 0; }

/* Availability header inside the right card. */
.zd-avail-head {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding-bottom: 10px; margin-bottom: 12px; border-bottom: 1px solid #eef2f8;
}

.zd-avail-title { font-size: 13px; font-weight: 800; color: #0f203f; }

/* Whose times these are. With one card serving several doctors the header is the only thing tying
   the calendar to a name — without it a patient who scrolled past the list is reading an anonymous
   grid. Takes the slack in the header so the arrows stay pinned right. */
.zd-avail-for {
    flex: 1 1 auto; min-width: 0;
    font-size: 12.5px; font-weight: 700; color: #1976D2;
    text-align: right;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Forward AND backward. Backward is disabled until the patient has actually paged forward, so it
   never offers a trip into dates behind "next available". */
.zd-avail-nav { display: flex; gap: 6px; flex: none; }

.zd-weekbtn {
    width: 32px; height: 32px; border-radius: 50%; border: 1.5px solid #d8e0ec; background: #fff;
    font-size: 17px; line-height: 1; color: #1d4ed8; cursor: pointer;
}
.zd-weekbtn:hover:not(:disabled) { background: #eef4ff; border-color: #1d4ed8; }
.zd-weekbtn:disabled { opacity: .35; cursor: default; }

/* ---------- inline loader ----------
   The app's existing ECG loader (App.razor / app.css), un-fixed so it can sit inside a card.
   Overrides only position and scale — a second, different spinner would make this feel like a
   different product. */
.svai-page-loader.svai-loader-inline {
    position: static; inset: auto; z-index: auto;
    background: transparent;
    -webkit-backdrop-filter: none; backdrop-filter: none;
    flex-direction: column; gap: 10px;
}

.svai-loader-inline .svai-ecg { width: 64px; height: 64px; box-shadow: 0 4px 14px rgba(15, 23, 42, .10); }
.svai-loader-inline .svai-ecg svg { width: 58px; }
.svai-loader-inline .svai-ecg::before { inset: -4px; }

.svai-loader-msg { font-size: 12.5px; font-weight: 600; color: #6b7b95; }

/* Portal list: holds the card open at roughly the height of a couple of rows while the scope
   loads. Without a floor the card collapses to the loader, the pinned footer jumps up to meet it,
   and the whole console shudders on every tab switch. */
.pp-loader-list { min-height: 190px; }

/* Min-heights so a card does not collapse and bounce the page while it loads. */
/* The availability panel now fills its column, so its loader has to fill the panel — otherwise the
   pulse sits in a 132px box at the TOP of a full-height card with empty white below it.
   The card becomes a flex column (its children were already a plain vertical stack, so nothing
   else moves) and the loader takes the remaining space. .svai-page-loader already centres on both
   axes; it simply had no height to centre within. min-height is kept as the floor for the stacked
   mobile layout, where the card is no longer stretched. */
.zd-avail-card { display: flex; flex-direction: column; }
.zd-loader-avail  { flex: 1 1 auto; min-height: 132px; }
.zd-loader-dialog { min-height: 150px; }
.zd-loader-page   { min-height: 180px; }

/* One line, not a wall of empty columns: an empty result should offer a way forward. */
.zd-noavail {
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    font-size: 13.5px; color: #6b7b95;
    background: #f7f9fc; border-radius: 11px; padding: 14px;
}

/* ---------- date chips ---------- */
/* Dates that ACTUALLY have openings, wrapped rather than laid on a calendar axis. A sparse
   schedule shows the two days it has instead of five columns of nothing. */
/* Grouped BY WEEK. A calendar week is the unit patients actually think in ("anything next week?"),
   and a fixed 8-across grid straddled week boundaries so Sunday and Monday sat side by side with
   nothing marking the change. Seven columns = one week per row, aligned Mon..Sun. */
.zd-week { margin-bottom: 14px; }
.zd-week:last-child { margin-bottom: 0; }

.zd-week-label {
    font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
    color: #6b7b95; margin-bottom: 7px;
}
.zd-week-label .zd-week-count { color: #1d4ed8; text-transform: none; letter-spacing: 0; font-weight: 700; }

.zd-dates { display: grid; grid-template-columns: repeat(7, 1fr); gap: 7px; }
@media (max-width: 880px) { .zd-dates { grid-template-columns: repeat(4, 1fr); } }
@media (max-width: 560px) { .zd-dates { grid-template-columns: repeat(3, 1fr); } }

.zd-page-note { font-size: 11.5px; color: #8a97ad; text-align: right; margin-top: 10px; }

/* Prompt shown when a location has several providers and none has been asked for yet. */
.zd-pickprovider {
    display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
    background: #f7f9fc; border: 1px dashed #dfe6f0; border-radius: 11px;
    padding: 16px; font-size: 13.5px; color: #5b6b85;
}
.zd-pickprovider .mud-icon-root { font-size: 22px; color: #8a97ad; }
.zd-pickprovider-btn { align-self: flex-start; }

.zd-date {
    display: flex; flex-direction: column; align-items: center; gap: 0;
    min-width: 0; min-height: 44px; padding: 9px 6px;
    background: #eef4ff; border: 1.5px solid #bfd3ff; border-radius: 11px;
    font-family: inherit; cursor: pointer;
    transition: background .12s ease, border-color .12s ease, transform .08s ease;
}

.zd-date:hover:not(:disabled) { background: #dfe9ff; border-color: #1d4ed8; }

/* The chosen day in the reschedule picker. Solid rather than a border tint: the times below belong
   to exactly one of these tiles, and a subtle outline is not enough to say which. */
.zd-date.is-selected { background: #1d4ed8; border-color: #1d4ed8; }
.zd-date.is-selected .zd-date-dow,
.zd-date.is-selected .zd-date-num,
.zd-date.is-selected .zd-date-mon,
.zd-date.is-selected .zd-date-count { color: #fff; }
.zd-date:active:not(:disabled) { transform: scale(.98); }
.zd-date:disabled { opacity: .5; cursor: default; }
.zd-date:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

.zd-date-dow { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: #5b6b85; }
.zd-date-num { font-size: 19px; font-weight: 800; color: #0f203f; line-height: 1.1; }
.zd-date-mon { font-size: 10.5px; font-weight: 700; text-transform: uppercase; color: #5b6b85; }
/* The count is what makes a chip worth tapping — "1 time" sets expectations before it opens. */
.zd-date-count { font-size: 9.5px; font-weight: 700; color: #1d4ed8; margin-top: 3px; text-align: center; line-height: 1.2; }

/* Fully booked: visibly present, clearly not clickable. Kept in the flow rather than hidden —
   an absent day reads as a bug, a greyed one reads as "booked", which is the truth. Muted enough
   that the eye still lands on the bookable chips first. */
.zd-date.is-disabled {
    background: #f4f6fa; border-color: #e3e9f2; cursor: default;
    box-shadow: none;
}
.zd-date.is-disabled .zd-date-dow,
.zd-date.is-disabled .zd-date-mon { color: #a8b3c5; }
.zd-date.is-disabled .zd-date-num { color: #b6c2d6; }
.zd-date.is-disabled .zd-date-count { color: #a8b3c5; font-weight: 600; }

/* ---------- booking dialog ---------- */
.zd-dialog {
    width: min(560px, calc(100vw - 24px));
    max-height: 88vh;
    display: flex; flex-direction: column;
    background: #fff; border-radius: 18px;
    box-shadow: 0 24px 60px rgba(15, 32, 63, .28);
    overflow: hidden;
}

/* Pinned: the doctor and day must not scroll away while the patient fills the form — that is the
   whole reason this is a dialog rather than another page. */
.zd-dialog-head {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
    padding: 16px 18px; border-bottom: 1px solid #eef2f8; background: #f7f9fc; flex: none;
}

.zd-dialog-who { font-size: 15.5px; font-weight: 800; color: #0f203f; }
.zd-dialog-when { font-size: 13.5px; color: #5b6b85; margin-top: 2px; }
.zd-dialog-time { font-weight: 700; color: #1d4ed8; }

.zd-dialog-close {
    background: none; border: none; font-size: 17px; line-height: 1; color: #6b7b95;
    cursor: pointer; padding: 4px 6px; border-radius: 8px; flex: none;
}
.zd-dialog-close:hover { background: #eef2f8; color: #0f203f; }

.zd-dialog-body { padding: 18px; overflow-y: auto; }

/* Times wrap in a dense grid — this is the one place times are shown, so they get room. */
.zd-dialog-times { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }

.zd-dialog-note { font-size: 12.5px; color: #6b7b95; margin: 12px 0 0; }

.zd-slot {
    min-height: 46px; padding: 10px 6px; width: 100%;
    background: #eef4ff; border: 1.5px solid #bfd3ff; border-radius: 9px;
    font-size: 14px; font-weight: 700; color: #1d4ed8; font-family: inherit;
    white-space: nowrap; cursor: pointer;
    transition: background .12s ease, border-color .12s ease, color .12s ease;
}

.zd-slot:hover:not(:disabled) { background: #1d4ed8; border-color: #1d4ed8; color: #fff; }
.zd-slot:disabled { opacity: .5; cursor: default; }
.zd-slot:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
    .zd-date, .zd-slot { transition: none; }
}

/* ---------- chosen-slot summary ---------- */
/* Pinned above the details form so the patient can see what they are committing to while typing. */
.zd-chosen { background: #fff; border: 1.5px solid #bfd3ff; border-radius: 14px; padding: 15px 17px; margin-bottom: 14px; }
.zd-chosen-label { font-size: 11px; font-weight: 800; letter-spacing: .04em; text-transform: uppercase; color: #6b7b95; }
.zd-chosen-when { font-size: 17px; font-weight: 800; color: #0f203f; margin: 3px 0 2px; }
.zd-chosen-sub { font-size: 13.5px; color: #5b6b85; margin-bottom: 8px; }

@media (prefers-reduced-motion: reduce) {
    .zd-skeleton-card { animation: none; background: #f4f7fb; }
    .zd-slot { transition: none; }
}

/* The same panel, asking rather than reporting: a question the patient answers, not a refusal, so
   it takes the informational blue rather than sitting under the red error block. */
.bk-existing.is-prompt { background: #eff6ff; border-color: #bfdbfe; }
.bk-existing-prompt { font-size: 14px; font-weight: 700; color: #1e40af; margin-bottom: 10px; line-height: 1.45; }
.bk-existing.is-prompt .bk-existing-row { border-top-color: #dbe8fb; }

/* ============================================================================
   Patient portal console (.pp-*)
   ----------------------------------------------------------------------------
   The signed-in half of /practice/{slug}/account. The funnel is a one-question-at-a-time wizard;
   this is the opposite job — a small management surface for someone who already has appointments
   and wants to scan them — so it takes a sidebar + list-of-rows shell rather than a stack of cards.

   The rows are a GRID, not a <table>. Same visual grammar as a table on desktop (aligned columns
   under a header strip), but each row is free to reflow into a stacked block on a phone, which a
   real table cannot do without horizontal scrolling — and this page is reached from an SMS link, so
   the phone is the common case, not the fallback.
   ========================================================================== */

.pp-app {
    --pp-rail: 256px;
    /* Clears the brand bar above. That bar SCROLLS (it is not fixed), so the rail is pinned below it
       rather than over it — anchoring the rail at top:0 would have it covering the clinic's name on
       any window narrow enough for the centred top bar to reach the left edge. */
    --pp-topbar: 60px;

    padding: 18px 24px 56px;
    padding-left: calc(var(--pp-rail) + 24px);
}

/* Long lines are harder to scan than short ones, and the rows are already wide. The panel stops
   growing well before an ultrawide monitor turns each row into a ruler. */
/* Was capped at 1180px, which left a wide window with an empty band down the right while the
   row grid squeezed. The console is a table — it should use the width it is given — so the cap is
   now generous enough to stop lines becoming unreadable on an ultrawide and nothing more. */
.pp-content { max-width: 1600px; }

/* ---------- sidebar ---------- */
/* FIXED to the left edge of the viewport — it stays put while the list scrolls, which is what makes
   this read as a console rather than a page with a column beside it. It scrolls internally if a
   tenant's nav ever outgrows a short window. */
.pp-side {
    position: fixed;
    top: var(--pp-topbar);
    left: 0;
    bottom: 0;
    width: var(--pp-rail);
    /*z-index: 30;*/

    background: #fff;
    /* Square against the screen edge, rounded on the side that faces the content. */
   /* border-radius: 0 18px 18px 0;*/
    box-shadow: 0 18px 44px rgba(15, 32, 63, .16);
    padding: 16px 12px 20px;
    overflow-y: auto;

    /* A column, so the footer can be pushed to the bottom of the rail rather than trailing the last
       nav item up the page. */
    display: flex;
    flex-direction: column;
}

/* Identity sits at the FOOT of the rail, next to Sign out — the two belong together, and the top of
   a console is for where you can go, not for who you are. */
.pp-side-id {
    display: flex; align-items: center; gap: 10px;
    padding: 10px 8px;
    min-width: 0;
}

.pp-side-avatar {
    width: 40px; height: 40px; flex: none; border-radius: 12px;
    background: #eef4ff; color: #1d4ed8; border: 1px solid #dbe6fb;
    display: grid; place-items: center;
    font-weight: 800; font-size: 14px;
}

.pp-side-who { min-width: 0; line-height: 1.25; }
.pp-side-name { font-size: 14px; font-weight: 800; color: #0f203f; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-side-sub { font-size: 12px; color: #6b7b95; white-space: nowrap; }

.pp-nav-label {
    font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
    color: #8a97ad; padding: 10px 10px 5px;
}

.pp-nav { display: flex; flex-direction: column; gap: 2px; }

.pp-nav-item {
    display: flex;
    align-items: center;
    /* Both explicit: a <button> centres its content by default in BOTH axes, and the flex rule is
       what actually governs here — text-align alone leaves the icon and label centred in the rail. */
    justify-content: flex-start;
    text-align: left;
    gap: 10px;
    width: 100%;
    /* >= 44px tap target — the phone-first rule at the top of this file applies to the nav too. */
    min-height: 44px;
    padding: 8px 10px;
    border: none; border-radius: 10px;
    background: none;
    font-size: 14px; font-weight: 600; color: #4a5a75;
    text-decoration: none;
    cursor: pointer;
}

.pp-nav-item:hover { background: #f4f7fc; color: #0f203f; }
.pp-nav-item:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }
.pp-nav-item .mud-icon-root { font-size: 19px !important; width: 19px; height: 19px; color: #8a97ad; flex: none; }

.pp-nav-item.is-active { background: #eef4ff; color: #1d4ed8; font-weight: 800; }
.pp-nav-item.is-active .mud-icon-root { color: #1d4ed8; }

.pp-nav-count {
    margin-left: auto;
    min-width: 22px; padding: 1px 6px;
    border-radius: 999px; background: #eef2f8;
    font-size: 11.5px; font-weight: 800; color: #5b6b85; text-align: center;
}
.pp-nav-item.is-active .pp-nav-count { background: #dbe6fb; color: #1d4ed8; }

/* margin-top:auto is what pins this to the bottom of the rail — everything above it keeps its
   natural height and the gap collects here. */
.pp-side-foot { border-top: 1px solid #eef2f8; margin-top: auto; padding-top: 8px; }
.pp-side-help { padding: 6px 10px 10px; font-size: 12.5px; color: #6b7b95; line-height: 1.5; }
.pp-side-help a { color: #1d4ed8; font-weight: 700; text-decoration: none; }
.pp-signout { color: #b91c1c; }
.pp-signout .mud-icon-root { color: #d17575 !important; }
.pp-signout:hover { background: #fef2f2; color: #991b1b; }

/* ---------- main panel ---------- */
.pp-main {
    background: #fff;
    border-radius: 18px;
    box-shadow: 0 18px 44px rgba(15, 32, 63, .16);
    padding: 20px 20px 16px;
    min-width: 0;
}

/* PINNED below the brand bar while the list scrolls, so the scope being read and the primary
   action stay on screen through a long history — the admin grids pin their header row for the same
   reason. Sticky needs an opaque background or the rows show through it, and the negative-margin +
   matching padding let that background reach the card's edges instead of leaving a 20px gutter the
   rows scroll up through. */
.pp-head {
    position: sticky; top: var(--pp-topbar); z-index: 12;
    display: flex; align-items: flex-start;
    gap: 12px; flex-wrap: wrap;
    margin: -20px -20px 4px; padding: 20px 20px 14px;
    background: #fff;
    border-bottom: 1px solid #eef2f8;
    border-radius: 18px 18px 0 0;
}
.pp-head .bk-h1 { margin: 0; }
.pp-head-text { min-width: 0; }
/* .mlk-btn is `display: block; width: 100%` — it is designed for the stacked full-width actions in
   the Magic Link sheets. Dropped into a flex row that makes its basis the FULL row, so it wrapped
   onto its own line and stretched edge to edge no matter what alignment was set on the container;
   `flex: none` did not help, because none means basis:auto and auto here resolves to that 100%.
   Shrinking it to its content is what actually lets it sit at the end of the row. */
.pp-head-action {
    display: inline-flex; align-items: center; justify-content: center;
    width: auto; flex: 0 0 auto;
    /* Hard right, and it stays there when the heading beside it wraps — unlike
       justify-content:space-between, which distributes the wrapped rows and strands it mid-row. */
    margin-left: auto;
    align-self: center;
    padding: 11px 18px; font-size: 14.5px; white-space: nowrap;
}
.pp-head-sub { font-size: 13.5px; color: #6b7b95; margin: 3px 0 0; }

/* ---------- table footer ---------- */
/* PINNED to the bottom of the viewport while rows scroll under it, matching .svai-sticky-grid's
   pager on the admin tables. Opaque background and a top border, or rows bleed through it.
   bottom:-1px (not 0) closes the hairline the browser can leave between a sticky edge and the
   element below at fractional device-pixel ratios — the same 1px seam .svai-sticky-grid documents. */
.pp-tfoot {
    position: sticky; bottom: -1px; z-index: 11;
    display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
    /* Same negative-margin trick as .pp-head: the card pads 20px, so without this the opaque
       background stops short of the edges and rows scroll up through the two side gutters. */
    margin: 4px -20px -16px; padding: 12px 20px 14px;
    background: #fff;
    border-top: 1px solid #e3e9f2;
    border-radius: 0 0 18px 18px;
    font-size: 12.5px; color: #6b7b95;
}
.pp-tfoot b { color: #1f2d45; }
.pp-tfoot-help a { color: #1d4ed8; font-weight: 600; }

/* ---------- the row list ---------- */
/* One shared column template so the header strip and every row line up. The action column is a
   fixed 48px: it holds a single icon button and must not grow with the content beside it. */
.pp-table { --pp-cols: minmax(150px, 1fr) minmax(120px, 1fr) minmax(140px, 1.2fr) minmax(110px, .8fr) 48px; }

/* PINNED directly under .pp-head, so the column names stay with the rows they label.
   --pp-head-h is measured by js/portalsticky.js because the heading's height is not a constant: it
   wraps on a narrow window and grows with a long practice name. The 76px fallback is only what
   applies for the frame before the first measurement (and if JS is off) — near enough that nothing
   visibly jumps.
   Full-bleed via the same negative margin the head and foot use, or rows scroll up through the
   card's 20px side gutters beside it. */
.pp-thead {
    position: sticky;
    top: calc(var(--pp-topbar) + var(--pp-head-h, 87px));
    z-index: 11;
    display: grid;
    grid-template-columns: var(--pp-cols);
    gap: 12px;
    margin: 0 -20px 8px; padding: 11px 32px;
    /* Solid primary with white labels, matching the admin grids' header row (.svai-sticky-grid).
       It also has to be fully opaque now that it is pinned — rows scroll underneath it. */
    background: var(--mlk-primary, #1976D2);
    font-size: 10.5px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
    color: #fff;
}

.pp-row {
    display: grid;
    grid-template-columns: var(--pp-cols);
    gap: 12px;
    align-items: center;
    padding: 13px 12px;
    border: 1.5px solid #e3e9f2;
    border-radius: 12px;
    margin-bottom: 8px;
    background: #fff;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.pp-row:hover { border-color: #cddcf5; box-shadow: 0 4px 16px rgba(15, 32, 63, .06); }

/* Past and cancelled rows are history, not choices. */
.pp-row.is-muted { background: #fbfcfe; }
.pp-row.is-muted .pp-when-date, .pp-row.is-muted .pp-when-time { color: #6b7b95; }

.pp-cell { min-width: 0; }

/* The column name, carried on the row itself for the stacked phone layout. Hidden on desktop, where
   the header strip already says it. */
.pp-cell-label { display: none; font-size: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: #8a97ad; margin-bottom: 2px; }

.pp-when-date { font-size: 14.5px; font-weight: 800; color: #0f203f; }
.pp-when-time { font-size: 13.5px; font-weight: 700; color: #1d4ed8; margin-top: 1px; }
.pp-when-time .bk-slot-tz { margin-left: 5px; }

.pp-line { display: flex; align-items: center; gap: 6px; font-size: 13.5px; color: #4a5a75; min-width: 0; }
.pp-line + .pp-line { margin-top: 3px; }
.pp-line .mud-icon-root { font-size: 16px !important; width: 16px; height: 16px; color: #8a97ad; flex: none; }
.pp-line span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-muted { color: #8a97ad; }

.pp-tags { display: flex; flex-wrap: wrap; gap: 5px; }

/* The reason an appointment has no actions, stated on the row rather than left to an empty corner —
   a patient who cannot find "cancel" simply no-shows. */
.pp-note { grid-column: 1 / -1; font-size: 12.5px; color: #6b7b95; line-height: 1.45; padding-top: 2px; }

/* The "opening your link" state. It reads as ACTIVE — brand colour and weight — rather than as
   another grey policy note, because those two lines sit in the same slot and the grey one means
   "nothing is happening, here is why". */
.pp-note-busy { display: flex; align-items: center; gap: 8px; color: #1d4ed8; font-weight: 700; }

/* Shared inline spinner, used by every "this is working" state on the patient surfaces: the portal's
   row action, and the sign-in card's buttons. 15px so it drops into the 36px action button without
   resizing it, and it reuses the mlk-spin keyframe already defined in magiclink.css (BookingLayout
   loads both stylesheets). */
.bk-spin {
    width: 15px; height: 15px; flex: none;
    display: inline-block; border-radius: 50%;
    border: 2px solid #cddcf5; border-top-color: #1d4ed8;
    animation: mlk-spin .7s linear infinite;
}

/* On a filled button the default blue-on-pale-blue ring is invisible against the blue fill. */
.mlk-btn-primary .bk-spin,
.mlk-btn-danger .bk-spin { border-color: rgba(255, 255, 255, .4); border-top-color: #fff; }

/* .mlk-btn is display:block, so a spinner and a label inside it would stack. Only the busy state
   becomes a flex row — the resting button keeps its own centring rules untouched. */
.mlk-btn.is-busy { display: flex; align-items: center; justify-content: center; gap: 9px; }

/* A button mid-request is disabled, and .mlk-btn-disabled / [disabled] styling greys it out — but
   the one that is actually working must stay legible, or the spinner becomes the faintest thing on
   the card at the exact moment it is the only thing happening. */
.mlk-btn.is-busy:disabled { opacity: 1; }

/* Motion here is the whole message — a static ring reads as a broken icon — so reduced-motion gets
   a pulse rather than nothing at all. */
@media (prefers-reduced-motion: reduce) {
    .bk-spin { animation: bk-pulse 1.2s ease-in-out infinite; }
}
@keyframes bk-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
.pp-note a { color: #1d4ed8; font-weight: 700; text-decoration: none; }
.pp-note-error { color: #b91c1c; font-weight: 600; }
.pp-note-error a { color: #b91c1c; }

/* ---------- the corner action ---------- */
/* One button, no menu: it opens the appointment's own Magic Link page, which is where Reschedule and
   Cancel actually live. */
.pp-actions { position: relative; display: flex; justify-content: flex-end; }

.pp-menu-btn {
    width: 36px; height: 36px;
    display: grid; place-items: center;
    border: 1.5px solid #dbe3ee; border-radius: 10px;
    background: #fff; color: #4a5a75;
    cursor: pointer;
}
.pp-menu-btn:hover { background: #eef4ff; border-color: #bfd3ff; color: #1d4ed8; }
.pp-menu-btn:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }
.pp-menu-btn:disabled { opacity: .5; cursor: default; }

/* The busy button is disabled too — every Edit on the page is, while one is opening — but it must
   NOT be dimmed with the others: it is the one thing on screen that is doing something, and at 50%
   opacity the spinner was the faintest mark in the row. Dimming the rest is what makes it read. */
.pp-menu-btn.is-busy:disabled { opacity: 1; background: #eef4ff; border-color: #bfd3ff; }
.pp-menu-btn .mud-icon-root { font-size: 18px !important; width: 18px; height: 18px; }

/* ---------- my details ---------- */
.pp-details { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 12px; padding-top: 14px; }

.pp-detail { border: 1.5px solid #e3e9f2; border-radius: 12px; padding: 13px 14px; }
.pp-detail-label { font-size: 10.5px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: #8a97ad; }
.pp-detail-value { font-size: 15px; font-weight: 700; color: #0f203f; margin-top: 3px; word-break: break-word; }
.pp-detail-hint { font-size: 12.5px; color: #6b7b95; margin-top: 4px; line-height: 1.45; }

/* ---------- tablet and phone: the rail becomes a drawer ----------

   Below 900px a 256px rail leaves the appointment rows too narrow to read, so it comes off the
   layout entirely and returns as a drawer behind a hamburger.

   This replaced a horizontal scrolling strip of nav chips. The strip fitted, but it cost a row of
   vertical space on the screen with the least of it, hid its own group labels ("Appointments",
   "Account") because they made no sense inline, and put Sign out on the same swipeable track as the
   scope buttons — where a mis-swipe ends the session. A drawer keeps the rail's real shape: full
   labels, grouped, with Sign out kept at the foot and away from everything else.

   The furniture that only exists at this width — the bar and the scrim — is display:none above it,
   so the desktop console is untouched. */
.pp-mobilebar { display: none; }
.pp-scrim { display: none; }

@media (max-width: 900px) {
    /* No left inset any more: the rail is out of flow, so the panel gets the whole width. */
    .pp-app { padding: 0 14px 48px; }
    .pp-content { max-width: none; }

    /* --- the bar that opens it --- */
    /* Sticky, not fixed: it scrolls with the page on the way down but is always within reach at the
       top, and a fixed bar on a short phone screen costs a permanent strip of the list. */
    .pp-mobilebar {
        display: flex;
        align-items: center;
        gap: 10px;
        position: sticky;
        top: 0;
        z-index: 40;
        margin: 0 -14px 12px;
        padding: 8px 14px;
        background: rgba(255, 255, 255, .92);
        backdrop-filter: saturate(180%) blur(8px);
        border-bottom: 1px solid #eef2f8;
    }

    .pp-mobilebar-title {
        font-size: 15px;
        font-weight: 800;
        color: #0f203f;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* 44px is the accessibility floor for a touch target, and this is the only way into the menu —
       it is the last control on the page that should be tight to hit. */
    .pp-burger {
        flex: none;
        width: 44px;
        height: 44px;
        display: grid;
        place-items: center;
        border: 1.5px solid #e3e9f2;
        border-radius: 12px;
        background: #fff;
        cursor: pointer;
        padding: 0;
    }
    .pp-burger:hover { background: #f4f7fc; }
    .pp-burger:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }

    .pp-burger-bars { display: block; width: 18px; }
    .pp-burger-bars span {
        display: block;
        height: 2px;
        border-radius: 2px;
        background: #0f203f;
        transition: transform .2s ease, opacity .2s ease;
    }
    .pp-burger-bars span + span { margin-top: 4px; }

    /* Open, the bars fold into a cross — the same control closes it, so it has to stop looking like
       the thing that opens it. */
    .nav-open .pp-burger-bars span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
    .nav-open .pp-burger-bars span:nth-child(2) { opacity: 0; }
    .nav-open .pp-burger-bars span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

    /* --- the drawer --- */
    /* Kept in the DOM and slid off-screen rather than display:none'd, so it animates and so its
       buttons keep their identity across opens. Never wider than the screen: min() stops a 320px
       phone getting a drawer with no visible list behind it. */
    .pp-side {
        position: fixed;
        top: 0;
        bottom: 0;
        left: 0;
        width: min(84vw, 320px);
        z-index: 60;
        border-radius: 0 18px 18px 0;
        transform: translateX(-100%);
        transition: transform .24s cubic-bezier(.3, .9, .4, 1);
        overflow-y: auto;
        /* Clears the notch / home indicator on a phone held in either orientation. */
        padding: max(16px, env(safe-area-inset-top)) 12px max(20px, env(safe-area-inset-bottom));
    }
    .nav-open .pp-side { transform: none; }

    /* The rail's desktop shape is the right one here, so nothing about the nav itself changes —
       vertical, labelled, Sign out at the foot. That is the whole point of the drawer. */

    .pp-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 50;
        background: rgba(15, 32, 63, .45);
        opacity: 0;
        pointer-events: none;
        transition: opacity .24s ease;
    }
    .nav-open .pp-scrim { opacity: 1; pointer-events: auto; }
}

@media (max-width: 900px) and (prefers-reduced-motion: reduce) {
    .pp-side, .pp-scrim, .pp-burger-bars span { transition: none; }
}

@media (max-width: 720px) {
    /* Stacked rows: the header strip has nothing left to align, so each cell carries its own label
       and the action button moves to the top-right corner of the block. */
    .pp-thead { display: none; }

    .pp-row {
        grid-template-columns: minmax(0, 1fr) 48px;
        align-items: start;
        row-gap: 10px;
        padding: 14px 13px;
    }
    .pp-cell { grid-column: 1; }
    .pp-cell-label { display: block; }
    .pp-actions { grid-column: 2; grid-row: 1; align-self: start; }
    .pp-main { padding: 16px 14px 14px; }
}

/* ==========================================================================
   SETUP MODAL  (landing questionnaire: patient type -> location -> insurance)

   PRACTICE HEADER over the question, after the directory-listing pattern the
   patient already knows from Zocdoc and Google: a round clinic image on the left,
   then badge / name / specialty / address stacked beside it. The header is rendered
   once above all three steps, so the clinic stays on screen while they answer.

   Colour is used sparingly and only where it means something — the badge, the
   selected state, the primary button. The card itself stays white.

   --zd-brand comes per tenant from BookingSiteConfig.BrandAccentHex, sanitised in
   PracticeBooking.BrandStyle. Static hex fallbacks first, color-mix behind @supports:
   a custom property holding an unsupported function still PARSES and only fails when
   substituted, which would leave these surfaces transparent rather than blue.
   ========================================================================== */

.zd-setup {
    --zd-brand: #1d4ed8;
    --zd-brand-wash: #eef4ff;
    --zd-brand-line: #bfd3ff;
    --zd-brand-deep: #14318f;

    /* Inherits .zd-dialog's `display: flex; flex-direction: column` — now the right shape,
       because this modal has the same anatomy as the booking dialog: a fixed practice
       header, a scrolling middle, and a fixed footer. The regions must each declare their
       own flex behaviour (flex: none / flex: 1 + min-height: 0), which is what was missing
       when this was flat and the fix was `display: block`. */
    width: min(540px, calc(100vw - 24px));
    padding: 0;
    text-align: left;
}

@supports (background: color-mix(in srgb, red 10%, white)) {
    .zd-setup {
        --zd-brand-wash: color-mix(in srgb, var(--zd-brand) 8%, #fff);
        --zd-brand-line: color-mix(in srgb, var(--zd-brand) 26%, #fff);
        --zd-brand-deep: color-mix(in srgb, var(--zd-brand) 76%, #0b1b3a);
    }
}

/* Deeper backdrop than a plain dialog: nothing behind it is worth reading, so it
   blurs rather than merely dimming. */
.zd-setup-veil {
    background: rgba(9, 19, 38, .55);
    -webkit-backdrop-filter: blur(3px);
    backdrop-filter: blur(3px);
}
.mlk-overlay > .zd-setup { margin: auto; }

/* ---- practice identity ---- */
.zd-id {
    flex: none;                       /* frozen: the practice never scrolls away */
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 22px 24px;
    background: #fbfaf6;              /* a warm neutral, so the header reads as a plate */
    border-bottom: 1px solid #eceadf;
}

/* Round, like every practice listing the patient has seen. flex:none so a long clinic
   name can never squeeze it into an oval. */
.zd-id-photo {
    width: 84px; height: 84px;
    flex: none;
    border-radius: 50%;
    overflow: hidden;
    background: #fff;
    border: 1px solid #e6e3d6;
    box-shadow: 0 2px 8px rgba(15, 32, 63, .10);
    display: grid; place-items: center;
}
/* cover, not contain: this slot is a PHOTO of the practice in the reference, and a
   photo letterboxed inside a circle looks like a mistake. A logo that happens to be
   square still fills it correctly. */
.zd-id-photo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.zd-id-photo span { font-size: 30px; font-weight: 800; color: var(--zd-brand); }

.zd-id-text { min-width: 0; display: flex; flex-direction: column; gap: 2px; }

/* Amber, deliberately off-palette: a badge that shares the brand colour stops reading
   as a badge and becomes part of the heading. */
.zd-id-badge {
    align-self: flex-start;
    display: inline-flex; align-items: center; gap: 5px;
    margin-bottom: 4px;
    padding: 3px 10px 3px 8px;
    border-radius: 999px;
    background: #fdf3d4; border: 1px solid #f2dfa4;
    font-size: 11.5px; font-weight: 700; color: #7a5a12;
    white-space: nowrap;
}
.zd-id-badge .mud-icon-root { font-size: 14px !important; width: 14px; height: 14px; color: #b8860b; }

.zd-id-name {
    margin: 0;
    font-size: 25px; font-weight: 800; color: #16233c;
    letter-spacing: -.5px; line-height: 1.15;
    text-wrap: balance;
    overflow-wrap: anywhere;
}

.zd-id-kind { font-size: 15px; font-weight: 600; color: #3f4d66; line-height: 1.3; }

.zd-id-meta {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px;
    margin-top: 2px;
    font-size: 13px; color: #8a92a6; line-height: 1.4;
}
.zd-id-meta a { color: var(--zd-brand); font-weight: 700; text-decoration: none; }
.zd-id-meta a:hover { text-decoration: underline; }
.zd-id-meta a:focus-visible { outline: 2px solid var(--zd-brand); outline-offset: 2px; border-radius: 3px; }

/* ---- question ----
   Three regions: a frozen head (question, search, carrier header), a scrolling body,
   and a frozen foot. min-height: 0 on both the column and the scroller is what actually
   permits the scroll — a flex item's default min-height is auto, so without it the body
   grows to fit its content and pushes the footer out of the modal instead of scrolling. */
.zd-setup-main {
    flex: 1 1 auto;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    position: relative;
    padding: 20px 24px 0;
    min-width: 0;
}

/* The head owns the gap to the body. Putting it on the head rather than on each of the
   things inside it means the spacing is the same whether that step has a sub-line, a
   search box, both, or — as on the patient-type step — only the question. It was 4px
   there, which is why the first popup read as cramped. */
.zd-setup-shead { flex: none; margin-bottom: 16px; }

.zd-setup-sbody {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    /* Room for a focus ring on the last row, which would otherwise be clipped by the
       scroll container. */
    padding: 2px 2px 4px;
    margin: 0 -2px;
}

/* The patient-type step has NO footer, and .zd-setup-main's bottom padding is 0 because
   the footer normally supplies that gutter — so the choices sat flush against the bottom
   edge of the modal. :last-child is the exact test for "this step has no footer", so the
   gutter appears only where it is missing rather than being added everywhere and then
   subtracted again. */
.zd-setup-sbody:last-child { padding-bottom: 24px; }

.zd-setup-steps { position: absolute; top: 24px; right: 24px; display: flex; gap: 5px; }
.zd-setup-pip {
    width: 18px; height: 4px; border-radius: 99px;
    background: #dde5f0;
    transition: background-color .2s ease, width .2s ease;
}
.zd-setup-pip.done { background: var(--zd-brand-line); }
.zd-setup-pip.on { background: var(--zd-brand); width: 26px; }

.zd-setup-q {
    margin: 0 64px 0 0;   /* right margin clears the pips; the head owns the gap below */
    font-size: 17px; font-weight: 800; color: #16233c;
    letter-spacing: -.2px; line-height: 1.3; text-wrap: balance;
}
/* Spacing sits ABOVE each element in the head, so an absent one leaves no orphaned gap —
   the patient-type step has no sub-line and no search box at all. */
.zd-setup-sub { margin: 6px 0 0; font-size: 13.5px; color: #6b7b95; line-height: 1.5; }

.zd-setup-note {
    border: 1.5px dashed #d8e0ec; border-radius: 12px; padding: 14px 16px;
    font-size: 13.5px; color: #5b6b85;
}

/* ---- patient type ----
   Rows, not a side-by-side pair: the labels are sentences, and two across would wrap
   into three-word stacks at this width. */
.zd-setup-choices { display: flex; flex-direction: column; gap: 10px; }

.zd-setup-choice {
    display: grid;
    grid-template-columns: 42px minmax(0, 1fr) 20px;
    grid-template-rows: auto auto;
    align-items: center;
    gap: 0 10px;
    width: 100%;
    padding: 14px 15px;
    text-align: left;
    border: 1.5px solid #e3e9f2; border-radius: 12px;
    background: #fff; font-family: inherit; cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.zd-setup-choice:hover:not(:disabled) { border-color: var(--zd-brand); background: var(--zd-brand-wash); }
.zd-setup-choice:focus-visible { outline: 3px solid var(--zd-brand-line); outline-offset: 2px; }
.zd-setup-choice:disabled { opacity: .55; cursor: default; }

/* EVERY child is placed explicitly. Auto-placement is wrong here and fails silently —
   it once put the sub-line in the 20px chevron track, where it overflowed the button
   and pushed the chevron down a row. Icon spans both rows in column 1, the two text
   rows stack in column 2, chevron spans both in column 3.
   min-width: 0 lets a long label wrap inside its track instead of widening it. */
.zd-choice-plate {
    grid-column: 1;
    grid-row: 1 / span 2;
    display: grid; place-items: center;
    width: 42px; height: 42px; border-radius: 12px;
    background: var(--zd-brand-wash); border: 1px solid var(--zd-brand-line);
}
.zd-choice-plate .mud-icon-root { font-size: 22px !important; width: 22px; height: 22px; color: var(--zd-brand); }

.zd-setup-choice-t {
    grid-column: 2; grid-row: 1;
    min-width: 0;
    font-size: 14.5px; font-weight: 700; color: #16233c;
}
.zd-setup-choice-s {
    grid-column: 2; grid-row: 2;
    min-width: 0;
    font-size: 12.5px; color: #6b7b95; margin-top: 2px;
}

.zd-choice-go {
    grid-column: 3;
    grid-row: 1 / span 2;
    display: grid; place-items: center;
    color: #c2cddc;
    transition: transform .15s ease, color .15s ease;
}
.zd-choice-go .mud-icon-root { font-size: 20px !important; width: 20px; height: 20px; }
.zd-setup-choice:hover:not(:disabled) .zd-choice-go { color: var(--zd-brand); transform: translateX(2px); }

/* ---- insurance: carrier picker ----
   Tiles in two columns, as on every insurance selector a patient has met. */

.zd-ins-search {
    display: flex; align-items: center; gap: 9px;
    padding: 0 14px;
    height: 46px;
    border: 1.5px solid #dde3ec; border-radius: 999px;
    background: #fff;
    margin-top: 14px;   /* above, not below — .zd-setup-shead owns the gap to the list */
}
.zd-ins-search:focus-within { border-color: var(--zd-brand); box-shadow: 0 0 0 3px var(--zd-brand-line); }
.zd-ins-search .mud-icon-root { font-size: 19px !important; width: 19px; height: 19px; color: #97a3b6; flex: none; }
.zd-ins-search input {
    flex: 1; min-width: 0;
    border: 0; outline: none; background: none;
    font-family: inherit; font-size: 14.5px; color: #16233c;
}
.zd-ins-search input::placeholder { color: #97a3b6; }

/* Carrier list: three rows of tiles, then it scrolls — the modal keeps the SAME height whether
   the practice takes six carriers or sixty. Without the cap the dialog grew to its 88vh limit, so
   a well-contracted practice pushed the frozen footer (and "I'm paying for myself" with it) to
   the bottom of the screen, and the step read as a much longer form than it is.

   The cap goes on .zd-setup-sbody, NOT on .zd-ins-grid: the body is deliberately the step's only
   scroll container, and a second one nested inside it produced two scrollbars and trapped the
   wheel in the inner list. max-height, not height, so two carriers still render as a short list
   rather than a short list in a tall empty box.

   The number is derived, not eyeballed: a tile is 46px of mark + 8px padding top and bottom +
   1.5px border top and bottom = 65px, with an 8px grid gap between rows, plus the body's own
   2px/4px focus-ring padding. */
.zd-setup-sbody.zd-sbody-ins {
    --zd-ins-tile-h: 65px;
    --zd-ins-rows: 3;
    max-height: calc(var(--zd-ins-rows) * var(--zd-ins-tile-h)
                     + (var(--zd-ins-rows) - 1) * 8px
                     + 6px);
}

/* No height cap and no scroller of its own — .zd-setup-sbody is the single scroll
   container now. A nested one produced two scrollbars and trapped the wheel inside
   the inner list. */
.zd-ins-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: 8px;
}

.zd-ins-tile {
    display: grid;
    grid-template-columns: 46px minmax(0, 1fr);
    align-items: center;
    gap: 11px;
    padding: 8px 10px;
    text-align: left;
    border: 1.5px solid transparent; border-radius: 12px;
    background: none; font-family: inherit; cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.zd-ins-tile:hover:not(:disabled) { border-color: var(--zd-brand-line); background: var(--zd-brand-wash); }
.zd-ins-tile:focus-visible { outline: 3px solid var(--zd-brand-line); outline-offset: 2px; }
.zd-ins-tile:disabled { opacity: .55; cursor: default; }
.zd-ins-tile.on { border-color: var(--zd-brand); background: var(--zd-brand-wash); }

/* The bordered square that a real carrier logo would sit in. Colour comes from --c-h,
   a hue derived from the carrier's name in PracticeBooking.CarrierMarkStyle — kept at
   low saturation so a row of tiles reads as one set rather than a paint chart. */
.zd-ins-mark {
    width: 46px; height: 46px;
    display: grid; place-items: center;
    border-radius: 10px;
    background: hsl(var(--c-h, 220) 42% 96%);
    border: 1px solid hsl(var(--c-h, 220) 34% 87%);
    color: hsl(var(--c-h, 220) 42% 38%);
    font-size: 14px; font-weight: 800; letter-spacing: .02em;
    flex: none;
}

.zd-ins-name {
    min-width: 0;
    font-size: 14px; font-weight: 600; color: #16233c; line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Underlined text, not a button: it is the way OUT of the list rather than another
   item in it, and the reference design treats it the same way. Lives in the frozen
   footer, so margin-top would push it away from the buttons it sits above. */
.zd-ins-self {
    display: block;
    width: 100%;
    padding: 8px;
    border: 0; border-radius: 10px;
    background: none;
    font-family: inherit; font-size: 14px; font-weight: 700;
    color: #4a5a75; text-decoration: underline; text-underline-offset: 3px;
    cursor: pointer;
}
.zd-ins-self:hover:not(:disabled) { color: var(--zd-brand); }
.zd-ins-self:focus-visible { outline: 3px solid var(--zd-brand-line); outline-offset: 2px; }
.zd-ins-self:disabled { opacity: .55; cursor: default; }
.zd-ins-self.on { color: var(--zd-brand); background: var(--zd-brand-wash); text-decoration: none; }

/* The self-pay quote, inside the step. Green rather than the brand colour: it is a
   confirmed fact about money, not another option to weigh. */
.zd-ins-quote {
    display: flex; align-items: flex-start; gap: 10px;
    margin-bottom: 12px; padding: 12px 14px;
    border: 1.5px solid #bfe3cd; border-radius: 12px;
    background: #f1faf4;
}
.zd-ins-quote .mud-icon-root { font-size: 20px !important; width: 20px; height: 20px; color: #1e7b3c; flex: none; }
.zd-ins-quote-amt { font-size: 15.5px; font-weight: 800; color: #14532d; }
.zd-ins-quote-note { font-size: 12.5px; color: #3f6b50; line-height: 1.45; margin-top: 2px; }

/* ---- insurance: plan list ----
   There is no inline "back to carriers" link any more: the footer Back is context-aware
   and does it, so a second control for the same action was one too many. */

.zd-ins-planhead {
    display: flex; align-items: center; gap: 11px;
    margin-top: 12px;   /* above, not below — .zd-setup-shead owns the gap to the list */
    font-size: 15px; font-weight: 800; color: #16233c;
}

.zd-ins-plans { display: flex; flex-direction: column; gap: 8px; }

.zd-ins-plan {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    width: 100%;
    padding: 12px 14px;
    text-align: left;
    border: 1.5px solid #e3e9f2; border-radius: 11px;
    background: #fff; font-family: inherit;
    font-size: 14px; font-weight: 600; color: #16233c;
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.zd-ins-plan:hover:not(:disabled) { border-color: var(--zd-brand); background: var(--zd-brand-wash); }
.zd-ins-plan:focus-visible { outline: 3px solid var(--zd-brand-line); outline-offset: 2px; }
.zd-ins-plan:disabled { opacity: .55; cursor: default; }
.zd-ins-plan.on { border-color: var(--zd-brand); background: var(--zd-brand-wash); }
.zd-ins-plan .mud-icon-root { font-size: 19px !important; width: 19px; height: 19px; color: var(--zd-brand); flex: none; }

/* ---- location tiles ----
   Square by aspect-ratio rather than a fixed height, so a tile stays square at every
   column width. */
.zd-loc-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 10px; }

.zd-loc-tile {
    position: relative;
    /* min-height, NOT aspect-ratio: 1/1. A 68px pin plus the timezone pill plus a two-line
       name needs ~150px, and a strict square at this column width is ~157px — one long
       clinic name and the content would have been clipped by its own tile. This keeps them
       square-looking and lets a tall one grow instead of cropping. */
    min-height: 150px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 7px; padding: 12px 10px;
    border: 1.5px solid #e3e9f2; border-radius: 14px;
    background: #fff; font-family: inherit; cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.zd-loc-tile:hover:not(:disabled) { border-color: var(--zd-brand); background: var(--zd-brand-wash); }
.zd-loc-tile:focus-visible { outline: 3px solid var(--zd-brand-line); outline-offset: 2px; }
.zd-loc-tile:disabled { opacity: .55; cursor: default; }
.zd-loc-tile.on { border-color: var(--zd-brand); background: var(--zd-brand-wash); }

/* The pin replaces the old 42px icon-in-a-plate. No plate: the artwork has its own colour,
   gold ring and drop shadow, and a tinted rounded square behind it just competed. Bigger
   too — it is the thing being chosen, and at 42px the building inside the pin was mush.
   height is the constraint, not width: the pin is taller than it is wide, so sizing on
   width would let a tall image push the name out of the tile. */
/* The clinic mark. Inline SVG, not an image file, because every part of it is tinted from
   --zd-loc-h — the per-location hue the tile carries as an inline style. A PNG cannot be recoloured
   per tile, and it also cost ~800KB for something drawn at 110px.

   Every fill below reads the SAME hue and differs only in saturation and lightness, so the parts
   stay in a fixed relationship however the hue moves: a pale disc behind, a solid building, lighter
   windows, a darker door, and a white cross that has to hold at any hue. */
.zd-loc-icon { flex: none; display: block; }

.zd-loc-svg {
    height: 110px;
    width: 110px;
    display: block;
}

/* Fallback hue for anything that renders the mark without an index — a single-facility tenant
   rendered outside the grid, or a future caller that forgets the variable. Blue, matching what the
   tile used to be. */

/* Every colour is the SAME hue at different saturation/lightness, so the parts keep a fixed
   relationship however --zd-loc-h moves. Only the cross stays fixed.
   The 210 fallback is inside var(), NOT declared on the element: a declaration here would beat the
   value inherited from the tile and every location would render the same colour. */
/* Filled, not hollow: a pale wash of the same hue behind the building, so the mark reads as a
   badge rather than as a building floating in a ring. Light enough that the WHITE walls still
   separate from it at every hue. */
/* No enclosing ring: the mark is the building alone, so the tile's own surface reads as the
   background instead of being shut inside a circle. That is also what lets a selected tile's brand
   tint show through the whole mark.
   One hue per location, kept light. The ladder is what stops "light" becoming washed out — glass
   lightest, structure mid, badge and ground a step deeper — and the badge stays deepest so the
   white cross holds at every hue. */
/* A pale DISC behind the building — filled, with no outline ring.
   stroke:none is deliberate: the visible ring drew a hard edge that competed with the building
   inside it. The fill alone gives the mark a coloured ground and lets the space between the disc
   edge and the walls carry the location's colour.
   Never remove this rule while the <circle> is in the markup — an SVG shape with no fill declared
   defaults to BLACK, which is how it once rendered as a solid black disc. */
.zd-loc-svg .ring   { fill: hsl(var(--zd-loc-h, 210) 90% 94%); stroke: none; }

.zd-loc-svg .wall   { fill: #FFFFFF; stroke: hsl(var(--zd-loc-h, 210) 82% 64%); stroke-width: 2; }
.zd-loc-svg .roof   { fill: hsl(var(--zd-loc-h, 210) 82% 64%); }
.zd-loc-svg .badge  { fill: hsl(var(--zd-loc-h, 210) 78% 56%); }
.zd-loc-svg .ground { fill: hsl(var(--zd-loc-h, 210) 78% 56%); }
.zd-loc-svg .window { fill: hsl(var(--zd-loc-h, 210) 90% 76%); }
.zd-loc-svg .door   { fill: hsl(var(--zd-loc-h, 210) 90% 76%); }

/* Fixed white, never tinted: it sits on the building and is the one shape that must stay legible at
   every hue. */
.zd-loc-svg .cross     { fill: #FFFFFF; }

.zd-loc-tz {
    font-size: 10px; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
    color: #6b7b95; background: #f1f5fb; border: 1px solid #e6ecf5;
    border-radius: 999px; padding: 1px 8px;
}

/* Two lines before it clips — "SAMS Healthcare - Company 1 Main" is a real name in
   this data, and one line leaves several clinics reading identically. */
.zd-loc-name {
    margin-top: auto;
    font-size: 12.5px; font-weight: 700; color: #16233c; line-height: 1.3;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden; text-align: center;
}

/* ---- fields + footer ---- */
.zd-setup-field { margin-bottom: 12px; display: flex; flex-direction: column; gap: 4px; }
.zd-setup-field .zd-filter-select { width: 100%; }
.zd-setup-field .zd-filter-select:focus {
    border-color: var(--zd-brand); outline: none;
    box-shadow: 0 0 0 3px var(--zd-brand-line);
}

/* Frozen. The divider is what tells the patient the list above it scrolls. */
.zd-setup-foot {
    flex: none;
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px 0 20px;
    margin-top: 4px;
    border-top: 1px solid #eef2f8;
}

/* flex: 1 1 0 on BOTH buttons, so Back and the primary action are exactly equal —
   flex: 1 alone leaves flex-basis at auto and the wider label takes more room. */
.zd-setup-actions { display: flex; gap: 10px; }
.zd-setup-actions .mlk-btn {
    flex: 1 1 0;
    min-width: 0;
    padding: 10px 12px;
    font-size: 14px;
    border-radius: 10px;
}
/* The primary button wears the tenant colour, so the card reads as one identity.
   Specificity note: .zd-dialog .mlk-btn-primary already sets #1976D2, and this is the
   same weight (0,2,0) but declared later — it must stay after that rule in the file. */
.zd-setup .mlk-btn-primary { background: var(--zd-brand); }
.zd-setup .mlk-btn-primary:hover:not(:disabled) { background: var(--zd-brand-deep); }

/* The card behind the modal. Present so the page has height and the filter bar does
   not sit on nothing, but quiet enough not to compete with the question on top. */
.zd-setup-behind { opacity: .45; }

/* ---- phone ---- */
@media (max-width: 640px) {
    .zd-id { gap: 13px; padding: 18px; }
    .zd-id-photo { width: 62px; height: 62px; }
    .zd-id-photo span { font-size: 23px; }
    .zd-id-name { font-size: 20px; letter-spacing: -.3px; }
    .zd-id-kind { font-size: 13.5px; }
    .zd-id-meta { font-size: 12px; }

    .zd-setup-main { padding: 18px 18px 18px; }
    .zd-setup-steps { top: 20px; right: 18px; }
    .zd-setup-q { margin-right: 58px; font-size: 16px; }
}

/* ---- motion ---- */
@keyframes zd-setup-in { from { opacity: 0; transform: translateY(10px) scale(.99); } to { opacity: 1; transform: none; } }
.zd-setup { animation: zd-setup-in .26s cubic-bezier(.3, .9, .4, 1) both; }

@media (prefers-reduced-motion: reduce) {
    .zd-setup { animation: none; }
    .zd-setup-choice, .zd-loc-tile, .zd-choice-go { transition: none; }
}

/* ---- sign-out veils ----
   Centre the loader over the whole page. .mlk-overlay is flex-end below 641px, which
   would drop it to the bottom edge — a page-level "signing you out" belongs in the middle. */
.zd-signout-veil { align-items: center; background: rgba(255, 255, 255, .82); z-index: 1400; }
.pp-signout-veil { align-items: center; background: rgba(255, 255, 255, .82); z-index: 1400; }

/* ==========================================================================
   TOUCH DEVICES AND SMALL SCREENS

   Everything above is laid out for a pointer and a wide window. These rules are the
   corrections for the two things that are genuinely different on a phone or tablet —
   a finger instead of a cursor, and a screen the funnel has to fit inside rather than
   sit in the middle of.
   ========================================================================== */

/* ---- iOS zoom-on-focus ----
   Safari on iOS zooms the whole page in when a focused field's text is under 16px, and it
   does NOT zoom back out on blur — the patient is left on a page wider than the screen,
   scrolling sideways to find the next field. Inside an iframe on a clinic's site that is
   worse still: the zoom applies to the embedding page, so the popup appears to break.

   The base rule sets 15px and its comment calls that "the floor we accept", which is a
   floor for the wrong number — 16px is not a preference, it is the threshold that turns
   the behaviour off. Raised only where it matters, so the desktop form keeps its scale.

   pointer:coarse rather than a width query on purpose: an iPad in landscape is wider than
   many laptops and still zooms, and a narrow desktop window does not. */
@media (pointer: coarse) {
    .bk-input,
    .bk-select,
    textarea.bk-input {
        font-size: 16px;
    }
}

/* ---- location tiles ----
   The pin is sized for a desktop grid of two or three tiles. On a phone the tiles stack
   into one or two columns, so a 110px image turns a four-site practice into a page the
   patient has to scroll through to see its own options. Shrinking the artwork keeps every
   location on one screen, which is what the tile grid exists to do. */
@media (max-width: 560px) {
    .zd-loc-svg { height: 74px; width: 74px; }
    .zd-loc-tile { min-height: 118px; gap: 5px; padding: 10px 8px; }
    .zd-loc-name { font-size: 13.5px; }
}

/* ---- very small phones ----
   360px and below is still a live class of device. The date strip is the only grid that
   cannot simply reflow — it is a week, read left to right — so it drops to three columns
   and lets the row scroll rather than crushing each cell below a readable width. */
@media (max-width: 380px) {
    .zd-setup-main { padding: 14px 14px 16px; }
    .zd-card { padding: 16px; }
    .zd-date { min-height: 46px; padding: 8px 4px; }
    .zd-date-num { font-size: 17px; }
}

/* ---- the appointment list on a phone ----
   Long clinic and provider names are the usual cause of a sideways scroll here: they have
   no spaces to break at and the grid has no slack left to give them. */
@media (max-width: 560px) {
    .pp-cell,
    .pp-detail-value { overflow-wrap: anywhere; }
    .pp-main { padding: 14px 12px 12px; }
}

/* ==========================================================================
   NARROW-SCREEN TOP BAR: the account menu

   Above 880px nothing here applies and the bar keeps its inline links.
   ========================================================================== */

/* The account button is the narrow-screen shape; the inline links are the wide one. */
.zd-acct { display: none; position: relative; }

@media (max-width: 880px) {
    .zd-topbar-inline { display: none; }
    .zd-acct { display: block; }

    /* Now that the right-hand side is one 40px button, the name has the rest of the bar and can
       stop being an ellipsis. */
    .zd-brand { min-width: 0; flex: 1 1 auto; }
    .zd-brand-name { font-size: 15px; }

    .zd-acct-btn {
        width: 40px; height: 40px; flex: none;
        display: grid; place-items: center;
        border: 1.5px solid #bfd3ff; border-radius: 999px;
        background: #fff; color: #1d4ed8; cursor: pointer; padding: 0;
    }
    .zd-acct-btn .mud-icon-root { font-size: 22px !important; width: 22px; height: 22px; }
    .zd-acct-btn:hover { background: #eef4ff; }
    .zd-acct-btn:focus-visible { outline: 3px solid #93b4ff; outline-offset: 2px; }
    .zd-acct-btn.is-open { background: #eef4ff; }

    /* Transparent and full-screen: a tap anywhere outside closes the menu, which is what people try
       before looking for the button again. Under the menu, over everything else. */
    .zd-acct-backdrop { position: fixed; inset: 0; z-index: 60; }

    .zd-acct-menu {
        position: absolute;
        top: calc(100% + 8px);
        right: 0;
        z-index: 61;
        min-width: 216px;
        /* Never wider than the screen it drops onto, however long a phone number is. */
        max-width: calc(100vw - 24px);
        background: #fff;
        border: 1px solid #e3e9f2;
        border-radius: 14px;
        box-shadow: 0 18px 44px rgba(15, 32, 63, .18);
        padding: 6px;
        display: flex;
        flex-direction: column;
        gap: 2px;
    }

    .zd-acct-item {
        display: flex; align-items: center; gap: 10px;
        /* 44px: this menu is the only way to these actions at this width. */
        min-height: 44px;
        width: 100%;
        padding: 8px 10px;
        border: 0; border-radius: 10px;
        background: none; cursor: pointer;
        font-family: inherit; font-size: 14px; font-weight: 700;
        color: #0f203f; text-align: left; text-decoration: none;
    }
    .zd-acct-item .mud-icon-root { font-size: 19px !important; width: 19px; height: 19px; color: #8a97ad; flex: none; }
    .zd-acct-item:hover { background: #f4f7fc; }
    .zd-acct-item:focus-visible { outline: 3px solid #93b4ff; outline-offset: -2px; }

    /* Separated and quieter — the way out should not sit flush against the way in. */
    .zd-acct-signout {
        color: #6b7b95; font-weight: 600;
        border-top: 1px solid #eef2f8; border-radius: 0 0 10px 10px; margin-top: 2px; padding-top: 10px;
    }
}

/* ==========================================================================
   DIALOGS: centred at every width

   .mlk-overlay was built as a bottom sheet below 641px and a centred modal above it. The sheet is a
   deliberate mobile pattern — it sits under the thumb — but it reads as a half-open drawer rather
   than a dialog, and the funnel's dialogs are things you complete, not things you peek at. Centred
   everywhere, so a popup looks the same on a phone as on a desktop.

   Kept as an override at the end rather than an edit to the block above, so the sheet treatment is
   one deletion away if it is ever wanted back.
   ========================================================================== */
@media (max-width: 640px) {
    .mlk-overlay {
        align-items: center;
        padding: 16px;
    }

    .mlk-sheet {
        width: 100%;
        max-width: 440px;
        border-radius: 20px;
        padding: 20px 18px 22px;
        max-height: min(88vh, 720px);
        animation: mlk-fadeUp .26s ease both;
    }

    /* A grab handle on something that no longer slides up from the bottom is furniture that lies
       about how the thing behaves. */
    .mlk-sheet-handle { display: none; }
}

/* ==========================================================================
   PROVIDER + AVAILABILITY ON A NARROW SCREEN

   The desktop layout is a two-column split: doctors on the left, their dates and times on the
   right, both on screen at once. Stacked below 880px that becomes every provider card in a column
   with the slots underneath ALL of them — so a practice with four doctors puts its actual booking
   controls most of a screen below the fold, and the patient has to scroll past the list to find out
   whether picking someone did anything.

   The list becomes a horizontal, snapping row instead. Choosing a doctor stays a side-to-side
   gesture, the availability panel stays directly beneath the row, and the slots are visible without
   scrolling regardless of how many providers the practice has.
   ========================================================================== */
@media (max-width: 880px) {
    .zd-provider-col {
        display: flex;
        flex-direction: row;
        gap: 10px;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        /* Room for the card's own shadow and focus ring, which a flush overflow box would clip
           into looking like a rendering fault rather than a scrollable row. */
        padding: 2px 2px 8px;
        margin-bottom: 4px;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }

    .zd-provider-col > * {
        scroll-snap-align: start;
        /* Wide enough for a name and specialty, narrow enough that the next card is visibly cut off
           — which is what tells the patient the row scrolls at all. */
        flex: 0 0 min(78%, 260px);
    }

    /* A single provider has nothing to scroll between, so it keeps the full width and reads as a
       heading for the panel below rather than as one option among several. */
    .zd-provider-col:has(> *:only-child) { display: block; padding-bottom: 0; }
    .zd-provider-col:has(> *:only-child) > * { width: auto; }
}

/* ==========================================================================
   PATIENT SIGN-IN

   One small form on an otherwise empty page. Two rules make it read as a sign-in rather than as a
   list that failed to load: it sits in the middle of the screen, and it leads with a mark.
   ========================================================================== */

/* Only the HEADER is centred. The fields stay left-aligned: a label centred over its own input
   loses the edge that tells you which control it belongs to, and this form has three of them on the
   registration step. */
.bk-signin { text-align: left; }
.bk-signin .bk-h1,
.bk-signin > .bk-body { text-align: center; }
.bk-signin > .bk-body { margin-left: auto; margin-right: auto; max-width: 34ch; }

/* The mark. A tinted disc rather than a bare glyph — at 22px an outline icon alone reads as a
   stray bullet above the heading instead of the subject of the screen. */
.bk-signin-mark {
    width: 52px; height: 52px;
    margin: 2px auto 14px;
    border-radius: 16px;
    display: grid; place-items: center;
    background: #eef4ff;
    border: 1px solid #dbe6fb;
    color: #1d4ed8;
}
.bk-signin-mark .mud-icon-root { font-size: 26px !important; width: 26px; height: 26px; }

/* ---- sidebar foot after the help line was removed ----
   .pp-side-foot's 8px top padding sat above a help line that had 6px of its own, and .pp-side-id
   adds another 10px. With the help line gone those stack into ~18px of nothing between the divider
   and the patient's name, which reads as a missing element rather than as spacing. */
.pp-side-foot { padding-top: 4px; }
.pp-side-id { padding: 6px 8px; }

/* ---- the rail aligns to the real topbar, not to a guess ----
   --pp-topbar was 60px against a bar that is 52px, which is the white gap between the header and
   the top of the sidebar. It now follows magiclink.css's --sams-topbar-h, so the two cannot drift
   apart again. The fallback matches the declared height and only applies if this stylesheet is ever
   loaded without magiclink.css. */
.pp-app { --pp-topbar: var(--sams-topbar-h, 52px); }

@media (max-width: 900px) {
    /* Sticks BELOW the page header, not over it. At top:0 it shared the same line and, being
       z-index 40 against the header's 30, covered the clinic's name while scrolling. */
    .pp-mobilebar { top: var(--sams-topbar-h, 52px); }
}

/* ==========================================================================
   ALREADY-BOOKED TABLE

   Replaces a stack of two-line rows. Every entry answers the same three questions — who, when, with
   whom — and aligned columns let the patient scan that once instead of re-reading the same shape for
   each booking. It is also what makes a duplicate obvious: two identical times sit directly above
   one another.
   ========================================================================== */

/* Provider names and long patient names are the two things that can outgrow a phone here, and the
   dialog is already narrow. Scrolling the table is right; scrolling the whole dialog sideways is not. */
.bk-existing-tablewrap { overflow-x: auto; margin-top: 8px; -webkit-overflow-scrolling: touch; }

.bk-existing-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 13.5px;
    /* Below this the columns start wrapping into each other and the alignment stops paying for
       itself — at which point the wrapper scrolls instead. */
    min-width: 19rem;
}

.bk-existing-table th {
    text-align: left;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #6b7b95;
    padding: 0 10px 6px 0;
    border-bottom: 1px solid #e6ecf5;
    white-space: nowrap;
}

.bk-existing-table td {
    padding: 9px 10px 9px 0;
    border-bottom: 1px solid #f1f5fb;
    color: #0f203f;
    vertical-align: top;
}
.bk-existing-table tr:last-child td { border-bottom: 0; }

.bk-existing-table .bk-existing-name { font-weight: 700; }
.bk-existing-table .bk-existing-prov { color: #5b6b85; }

/* Times line up digit-for-digit, which is what makes two bookings an hour apart read as an hour
   apart at a glance. */
.bk-existing-table .bk-existing-time,
.bk-existing-table .bk-existing-date {
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}

/* The row for the patient being booked right now. Tinted rather than bordered: a border would read
   as a divider between groups instead of as emphasis on one line. */
.bk-existing-table tr.is-same td { background: #f4f7fc; }

/* "5:30 PM" and "CDT" are separate elements and ran together as "5:30 PMCDT" — the zone label had
   no separation of its own inside a table cell. */
.bk-existing-table .bk-slot-tz { margin-left: 5px; }

/* ---- field-level validation ----
   The form previously reported one message at the bottom for any problem, which on a six-field
   form means the patient has to work out WHICH field it means. Marking the field and putting the
   message under it answers that without them hunting. */
.bk-input.is-invalid,
.bk-select.is-invalid {
    border-color: #d93025;
    /* A ring rather than a thicker border: a border change shifts the box and nudges the layout
       every time a field goes valid or invalid. */
    box-shadow: 0 0 0 3px rgba(217, 48, 37, .12);
}

.bk-field-error {
    display: block;
    margin-top: 5px;
    font-size: 12.5px;
    font-weight: 600;
    color: #b3261e;
    line-height: 1.4;
}
