@layer pages {
/* Online Booking checkout flow (/book/<uri>?v=2) -> .psw-book. The shared
   two-column scaffold (widget/book/base.html) behind the in-flow checkout steps
   (location, register, subaccounts, employee, review, source). Mirrors
   pages/psw-record.css: a single column under a compact workflow_header on
   mobile (submit in the sticky `footer`), and at >=1024px a centered 1128px
   container with a 638px content column + a 442px order-Summary card split by a
   48px gutter (the footer hides itself). The landing (home) and success
   (complete, built from success_shell) screens own their own layout. */
.psw-book {
    /* The footer's own height (psw-footer.css models it term-by-term, including
       the safe-area inset, and tracks the CTA count this page renders) + the
       24px of content-to-footer air every mobile screen keeps. */
    --book-footer-clearance: calc(var(--psw-footer-height) + var(--space-6));
    min-height: 100vh;
    background: var(--color-white);
}

/* Reserve the scrollbar gutter across the in-flow checkout steps so the centered
   layout keeps the same width whether the step scrolls (a long list of service
   cards) or not (a short categories step) — otherwise the content shifts sideways
   by the scrollbar width as you move between steps. Scoped to `.psw-book` only:
   the landing (home) and success (complete) screens own full-bleed layouts (hero
   cover, themed banner) that must reach the viewport edge, and are never compared
   step-to-step, so a reserved gutter there just leaves an empty right strip. */
html:has(.psw-book) {
    scrollbar-gutter: stable;
}

/* Mobile: a 16px page gutter around the header + content; reserve bottom
   clearance for the fixed footer. The workflow_header supplies its own top/bottom
   spacing, so the form needs no top padding. */
.psw-book__form {
    padding: 0 var(--space-4) var(--book-footer-clearance);
}

/* Let the content column shrink within the grid instead of blowing it out. */
.psw-book__main {
    min-width: 0;
}

/* Mobile: single column — the Summary aside is hidden (the header cart action and
   the sticky footer carry the order/affordance and the submit). */
.psw-book__aside {
    display: none;
}

/* Order Summary card (desktop only): the running order (Component("summary"):
   date/time header + priced lines + charges + Total) over the submit. White card,
   1px gray-200 border, 8px radius, 24px padding, 24px gap down to the submit. */
.psw-book__summary {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
    padding: var(--space-6);
    border: var(--border-width-sm) solid var(--color-gray-200);
    border-radius: var(--radius-lg);
}

/* The shared workflow_header + footer hide their mobile chrome at >=769px
   (desktop flows use a modal with an in-card submit). But the booking widget
   keeps its single-column mobile layout through tablet widths — where the header
   cart action is the only way to reach the Summary and the sticky footer carries
   the only submit (the aside rail + its submit appear at >=1024px) — so keep
   both visible across that band. */
@media (min-width: 769px) and (max-width: 1023.98px) {
    .psw-book .psw-workflow-header__trail,
    .psw-book .psw-footer {
        display: flex;
    }
}

/* The two-column scaffold needs ~1128px for its 638px content + 442px Summary
   columns (48px gutter); below that the fixed 442px aside crushes the content
   column and clips the calendar, so hold the single-column mobile layout (with
   the cart-overlay Summary) through tablet widths and only split at >=1024px. */
@media (min-width: 1024px) {
    /* 32px above the heading, 24px side gutter, and 24px below the content so
       every step leaves the same breathing room below its last option card. */
    .psw-book__form {
        padding: var(--space-8) var(--space-6) var(--space-6);
    }

    /* The heading and the grid share a centered 1128px container so their left
       edges align (Figma x=156 on a 1440 artboard). */
    .psw-book__form > .psw-workflow-header,
    .psw-book__layout {
        max-width: 1128px;
        margin-left: auto;
        margin-right: auto;
    }

    /* Two-column grid: 638px content column + 442px Summary card, 48px gutter,
       32px below the heading. The content column shrinks on narrower desktops;
       the card stays a fixed 442px. */
    .psw-book__layout {
        display: grid;
        grid-template-columns: minmax(0, 638px) 442px;
        column-gap: 48px;
        margin-top: var(--space-8);
        align-items: start;
    }

    /* Card + branding stack in the right column; the card sticks while the long
       content scrolls past it. */
    .psw-book__aside {
        display: flex;
        flex-direction: column;
        gap: var(--space-6);
        position: sticky;
        top: var(--space-8);
    }

    /* Component("summary") renders its own bordered card on desktop, but here the
       wrapper above already provides that surface (border + 24px padding). Strip
       the nested card chrome so the receipt + total share the single summary
       border instead of being boxed twice (Figma 3080:214877). */
    .psw-book__summary .psw-summary {
        padding: 0;
        border: 0;
        border-radius: 0;
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Mobile order-Summary screen -> .psw-book-summary-overlay (Figma 3027:193390).
   A full-screen <dialog> the header cart action opens on mobile to reveal the
   order Summary (the desktop rail is hidden below 769px). showModal() promotes
   it to the top layer (over the fixed footer) and dims the page behind it.
   Display-only: a "Summary" title + close X over the cloned Summary body, which
   reuses the bare mobile .psw-summary verbatim. */
.psw-book-summary-overlay {
    width: 100%;
    max-width: 100%;
    height: 100%;
    max-height: 100%;
    margin: 0;
    padding: 0;
    border: 0;
    background: var(--color-white);
    color: var(--color-content-high);
}

/* Native <dialog> is display:none until shown; lay out the column only when
   open so the title row pins and the body scrolls. */
.psw-book-summary-overlay[open] {
    display: flex;
    flex-direction: column;
}

.psw-book-summary-overlay::backdrop {
    background: rgba(15, 15, 15, 0.4);
}

/* Title row is the shared workflow_header (centered title, close X in the
   48px trailing slot) — the same chrome as the flow's nav bar and the
   checkout cart sheet; it carries its own vertical padding. 16px page
   gutter to match the form. */
.psw-book-summary-overlay__head.psw-workflow-header {
    padding-right: var(--space-4);
    padding-left: var(--space-4);
    flex-shrink: 0;
}

/* Scrollable Summary body: 16px gutter, room below for the home-indicator inset.
   The cloned .psw-summary supplies its own 16px row rhythm. */
.psw-book-summary-overlay__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: 0 var(--space-4) calc(var(--space-6) + env(safe-area-inset-bottom));
}
/* Carries autofocus so showModal() lands here instead of the close X (see
   book/base.html). It's a container, not a control — never ring it. */
.psw-book-summary-overlay__body:focus,
.psw-book-summary-overlay__body:focus-visible {
    outline: none;
}

/* The cart trigger is hidden >=769px, so this screen is only reachable on
   mobile; the desktop rail carries the Summary there. A closed <dialog> is
   natively display:none, so no desktop guard is needed (and hiding an *open*
   dialog would leave the page inert behind it). */

/* Lock the page behind the open screen (showModal makes it inert; this also
   stops iOS background scroll). */
html:has(.psw-book-summary-overlay[open]) {
    overflow: hidden;
}

/* ─────────────────────────────────────────────────────────────────────────
   Category selection step -> .psw-book-items. A 12px-gap stack of white cards
   (1px gray-200 border, 8px radius): a text column (title + 3-line-clamped
   memo) beside an 80px circular photo. The whole card links into its item list
   (data-psw-book-nav). The item and team-member steps render choice_card
   components into this same .psw-book-items grid.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-items {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.psw-book-items__card {
    display: flex;
    flex-direction: column;
    padding: var(--space-4);
    border: var(--border-width-sm) solid var(--color-gray-300);
    border-radius: var(--radius-lg);
    background: var(--color-white);
}

/* Whole-card affordance for cards that navigate (categories); inner
   links/buttons keep their own behavior. The dark hover wash matches the
   choice_card option cards on the sibling steps. */
.psw-book-items__card[data-psw-book-nav] {
    cursor: pointer;
    transition: background-color 120ms ease;
}

.psw-book-items__card[data-psw-book-nav]:hover {
    background:
        linear-gradient(rgba(15, 15, 15, 0.04), rgba(15, 15, 15, 0.04)),
        var(--color-white);
}

/* Navigating card mid-request (psw-book-loading.js): dim the content and
   overlay a centred spinner so the click reads as in-flight. */
.psw-book-items__card--loading {
    position: relative;
    pointer-events: none;
    cursor: progress;
}
.psw-book-items__card--loading > :not(.psw-btn__spinner) {
    opacity: 0.5;
}
.psw-book-items__card--loading > .psw-btn__spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -10px 0 0 -10px;                    /* centre the 20px spinner */
}

/* Generic in-flight overlay (window.pswBookLoading.setOverlay): dim the
   element's content and float a centred spinner. Reused for the clicked
   item/product choice card (add/remove) and the calendar/times results region
   (date/slot/delete AJAX). Same treatment as the navigating-card overlay
   above, but scoped to a class the AJAX steps add to any element. */
.psw-book-loading-overlay {
    position: relative;
    pointer-events: none;
    cursor: progress;
}
.psw-book-loading-overlay > :not(.psw-btn__spinner) {
    opacity: 0.5;
}
.psw-book-loading-overlay > .psw-btn__spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    margin: -10px 0 0 -10px;                    /* centre the 20px spinner */
}

.psw-book-items__row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
}

.psw-book-items__text {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    flex: 1;
    min-width: 0;
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
}

/* The title doubles as the card's real control (toggle button on items, link
   on categories) — strip the chrome, keep the text. */
.psw-book-items__title {
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
    font-family: var(--font-sans);
    font-size: var(--text-label-lg);            /* 18px (Figma) */
    line-height: var(--text-label-lg--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-items__image {
    flex: none;
    width: 80px;
    height: 80px;
    border-radius: var(--radius-max);
    object-fit: cover;
}

/* Memo body copy reads at Paragraph/Medium in these cards (sneak_peek
   defaults to small) — keep the inline toggle on the same scale. */
.psw-book-items__memo,
.psw-book-items__memo .psw-sneak-peek__toggle {
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
}

/* Category cards: the memo sits in the text column, 16px under the title
   (4px column gap + 12px margin). */
.psw-book-items__memo {
    margin-top: var(--space-3);
}

@media (min-width: 769px) {
    .psw-book-items__card {
        padding: var(--space-6);
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Payment-option step -> .psw-book-paymeth (Pay vs Buy now pay later). Two
   list_item rich cards in a 12px-gap stack; 16px padding (24px on desktop) per
   the Figma, and the selected card shows only the colored ring (no gray hover
   overlay), matching the sibling shop paymeth.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-paymeth__option-stack {
    --psw-stack-gap: var(--space-3);
}

.psw-book-paymeth__options .psw-list-item {
    padding: var(--space-4);
    min-height: 0;
}

.psw-book-paymeth__options .psw-list-item:not(.psw-list-item--disabled):has(.psw-list-item__input:checked):hover,
.psw-book-paymeth__options .psw-list-item:not(.psw-list-item--disabled):has(.psw-list-item__input:checked):active {
    background: var(--color-white);
}

@media (min-width: 769px) {
    .psw-book-paymeth__options .psw-list-item {
        padding: var(--space-6);
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Location step -> .psw-book-location. The location_picker component carries
   the inputs + map; the page adds the intro sentence above it (the unlocked
   variant shows it on mobile only, per the Figma).
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-location__intro {
    margin: 0 0 var(--space-6);
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-high);
}

/* Failed-submit summary: 24px to the content below it. The 12px above comes
   from the workflow header's built-in bottom padding on mobile. */
.psw-book-location .psw-error-summary {
    margin-bottom: var(--space-6);
}

@media (min-width: 769px) {
    .psw-book-location__intro--mobile {
        display: none;
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Register step -> .psw-book-register. The shared data_entry_form carries
   the fields; the page adds the intro sentence above it.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-register__intro {
    margin: 0 0 var(--space-6);
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-medium);
}

@media (min-width: 769px) {
    .psw-book-register__intro {
        margin-bottom: var(--space-8);
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Gift-transfer step -> .psw-book-transfer. A "send as gift" checkbox card
   above the recipient fields, which psw-book-transfer.js reveals when the box
   is checked. The fields stack mirrors the data_entry_form rhythm (24px gap,
   32px on desktop); __note is the secondary explanatory copy above them.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-transfer {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.psw-book-transfer__fields {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

/* display:flex above would defeat the [hidden] attribute psw-book-transfer.js
   toggles to reveal/collapse the recipient fields. */
.psw-book-transfer__fields[hidden] {
    display: none;
}

.psw-book-transfer__note {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-sm);
    line-height: var(--text-paragraph-sm--line-height);
    color: var(--color-content-medium);
}

@media (min-width: 769px) {
    .psw-book-transfer {
        gap: var(--space-8);
    }

    .psw-book-transfer__fields {
        gap: var(--space-8);
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Date & time step -> .psw-book-results. The month calendar under a
   month-label + [<][>]/Today nav header; on desktop a divider and the
   "Available times" column follow (3-column grids of slot pills per
   Morning/Afternoon/Evening section). Mobile shows the calendar only — times
   live on the standalone "Select time" page (?show_times=true), which leads
   with the selected-day heading instead.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-results__month-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-8);
}

.psw-book-results__month-label {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-heading-xs);
    line-height: var(--text-heading-xs--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-results__month-nav {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* One step title and one month label per breakpoint
   ("Select date & time" + "August 2026" / "Select date" + "Aug 2026").
   The header keeps the component's own flex display when shown. */
.psw-workflow-header.psw-book-results__header--desktop,
.psw-book-results__month-label--desktop {
    display: none;
}

/* Mobile: center the calendar wrapper (harmless once the calendar itself fills
   the width below). */
.psw-book-results [data-psw-book-month-wrapper] {
    text-align: center;
}

/* Mobile: let the booking calendar stretch to the page width instead of the
   component's fixed 7x49px track, so it scales with the viewport (Figma). */
@media (max-width: 768px) {
    .psw-book-results .psw-calendar {
        display: flex;
        width: 100%;
    }

    .psw-book-results .psw-calendar__weekdays,
    .psw-book-results .psw-calendar__grid {
        grid-template-columns: repeat(7, minmax(0, 1fr));
    }
}

.psw-book-results__month-header,
.psw-book-results__date-heading {
    text-align: left;
}

/* Mobile picks times on the standalone page; the in-page column is desktop
   chrome (the --hidden state hides it there until a date is selected). */
.psw-book-results__times {
    display: none;
}

.psw-book-results__divider {
    border: 0;
    border-top: var(--border-width-sm) solid var(--color-gray-200);
    /* 32px above the "Available times" section (Figma). */
    margin: var(--space-8) 0;
}

.psw-book-results__times-heading {
    /* 32px below "Available times" down to the first time-of-day section. */
    margin: 0 0 var(--space-8);
    font-family: var(--font-sans);
    font-size: var(--text-heading-sm);
    line-height: var(--text-heading-sm--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

/* Reservation step prompt (check-in/out date / time) — replaces the static
   "Available times" label in the times column, leading with a back control
   that steps the check-in -> check-out state machine back. */
.psw-book-results__prompt {
    display: flex;
    align-items: center;
    /* 16px between the back button and the step title — matches the workflow
       header's desktop back-arrow/title gap. The times column is desktop only,
       so no viewport guard is needed. */
    gap: var(--space-4);
    margin: 0 0 var(--space-6);
}

/* The prompt heading sits in the flex row beside the back control, so it sheds
   the standalone heading's bottom margin (the wrapper above owns that spacing). */
.psw-book-results__prompt-text {
    margin: 0;
}

/* Give the reservation step-back control the same bordered 36px white square as
   the header back button (the workflow_header applies this to its own icon
   button; the prompt lives outside that scope). The times column is desktop
   only, so no viewport guard is needed. */
.psw-book-results__prompt .psw-btn--icon-only {
    width: 36px;
    height: 36px;
    border-color: var(--color-border-default);
    border-radius: var(--radius-lg);
}
.psw-book-results__prompt .psw-btn--icon-only .psw-icon {
    width: 20px;
    height: 20px;
}

/* Reservation "Clear dates" control: in the month nav row on desktop, centered
   below the calendar on mobile (the calendar paints the check-in -> check-out
   range; this resets it). Scoped by container so the breakpoint swap beats the
   button's own display. */
.psw-book-results__month-nav .psw-book-results__clear {
    display: none;
}
.psw-book-results__clear-row {
    display: flex;
    justify-content: center;
    margin-top: var(--space-4);
}
@media (min-width: 769px) {
    .psw-book-results__month-nav .psw-book-results__clear {
        display: inline-flex;
    }
    .psw-book-results__clear-row {
        display: none;
    }
}

/* Selected-day heading on the standalone "Select time" page. */
.psw-book-results__date-heading {
    margin: 0 0 var(--space-6);
    font-family: var(--font-sans);
    font-size: var(--text-heading-xs);
    line-height: var(--text-heading-xs--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-times {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.psw-book-times__label {
    margin: 0 0 var(--space-4);
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    font-weight: var(--font-weight-regular);
    color: var(--color-content-medium);
}

.psw-book-times__grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-3);
}

/* Each slot is the shared `button` component (secondary when free, primary when
   selected — see _times.html), so its fill / border / hover / active / focus all
   come from psw-btn.css. Only the drop-in list's per-slot width + the waitlist
   hatch are layered on here.

   Drop-in class lessons: a vertical list of rows, each a fixed-width time button
   beside a seat-icon capacity label (multi-select). */
.psw-book-times__list {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    margin: 0;
    padding: 0;
    list-style: none;
}

.psw-book-times__row {
    display: flex;
    align-items: center;
    gap: var(--space-6);
}

.psw-book-times__slot--lesson {
    flex: 0 0 150px;
}

/* A full drop-in lesson still books -- onto the waiting list -- so the button
   stays tappable but takes a diagonal hatch to read as "full" (Figma
   4468:115546 / 4406:213877; the hatch belongs on desktop too). Dropped once
   selected (the button flips to the `primary` variant), where the solid theme
   fill takes over.

   Painted as a tiled inline SVG, NOT a repeating-linear-gradient: a gradient's
   hard 1px stops snap unevenly on fractional / non-retina DPRs (alternating
   1px/2px lines), crisp only at 2x. The SVG stroke antialiases consistently and
   the 8px tile lands on integer device pixels at every common DPR (1, 1.25,
   1.5, 1.75, 2), so the lines stay uniform. Same DPI-robust technique as the
   psw-document dashed border (pages/psw-document.css:134). The main segment
   runs top-left -> bottom-right to match Figma; the two extra corner segments
   make the diagonal continuous across tile seams. Color is gray-300 inline --
   data URIs can't read CSS custom properties. */
.psw-book-times__slot--waitlist:not(.psw-btn--primary) {
    background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' width='8' height='8'%3e%3cpath d='M0 0 L8 8 M6 -2 L10 2 M-2 6 L2 10' fill='none' stroke='%23e2e2e2' stroke-width='1'/%3e%3c/svg%3e");
}

.psw-book-times__capacity {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-high);
}

.psw-book-times__capacity .psw-icon {
    flex-shrink: 0;
    color: var(--color-gray-500);
}

/* The seat-icon capacity stacks an optional secondary note under its label so
   the full/waitlist treatment reads "Join wait list" over "Class is full"; an
   available lesson ("N spots left") renders the label alone as one row. */
.psw-book-times__capacity-text {
    display: flex;
    flex-direction: column;
}

.psw-book-times__capacity-note {
    font-size: var(--text-paragraph-sm);
    line-height: var(--text-paragraph-sm--line-height);
    color: var(--color-content-medium);
}

@media (min-width: 769px) {
    .psw-book-times__slot--lesson {
        flex-basis: 302px;
    }
}

.psw-book-times__empty {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-medium);
}

@media (min-width: 769px) {
    .psw-workflow-header.psw-book-results__header--desktop {
        display: flex;
    }

    .psw-book-results__month-label--desktop {
        display: inline;
    }

    .psw-workflow-header.psw-book-results__header--mobile,
    .psw-book-results__month-label--mobile {
        display: none;
    }

    .psw-book-results__month-label {
        font-size: var(--text-heading-sm);
        line-height: var(--text-heading-sm--line-height);
    }

    .psw-book-results [data-psw-book-month-wrapper] {
        text-align: initial;
    }

    /* Desktop times column (hidden until a date is selected). */
    .psw-book-results__times {
        display: block;
    }

    .psw-book-results__times--hidden {
        display: none;
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Landing page (/book/<uri>?v=2 entry) -> .psw-book-home. Standalone layout
   (home.html does not extend the checkout scaffold): a cover photo (or a
   primary-color band) with the vendor logo overlapping the seam, a centered
   hero (name + rating + bio), then the CTA stack. Mirrors psw-suitelink.css's
   centered column + branding split, plus the cover/avatar header.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-home {
    --book-home-column: 450px;     /* CTA + hero column width on mobile/tablet */
    /* flow-root contains the cover's top margin so the 8px inset stays inside
       the 100vh box instead of escaping and forcing an 8px scroll. */
    display: flow-root;
    min-height: 100vh;
    background: var(--color-white);
}

/* Cover photo: an inset, rounded top band on mobile/tablet (the logo overlaps
   its bottom edge); becomes the full-height right panel on wide desktop. Falls
   back to a primary-color band (set inline) when the vendor has no hero image.
   Width is left auto so the side margins inset it instead of overflowing. */
.psw-book-home__cover {
    height: 190px;
    margin: var(--space-2) var(--space-2) 0;
    overflow: hidden;
    border-radius: var(--radius-lg);
    background: var(--color-gray-100);
}

.psw-book-home__cover-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.psw-book-home__body {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* No top padding: the header sits at the seam so its logo can overlap the
       cover (see __avatar). */
    padding: 0 var(--space-4);
    /* Clear the fixed mobile footer — branding-only here, which --psw-footer-height
       accounts for, + the standard 24px of content-to-footer air. */
    padding-bottom: calc(var(--psw-footer-height) + var(--space-6));
}

.psw-book-home__hero,
.psw-book-home__actions,
.psw-book-home__branding {
    width: 100%;
    max-width: var(--book-home-column);
}

.psw-book-home__hero {
    display: flex;
    flex-direction: column;
    align-items: center;
    /* 16px between the avatar/name header, the rating and the bio (Figma). */
    gap: var(--space-4);
    text-align: center;
    /* 32px below the bio, down to the CTA stack. */
    margin-bottom: var(--space-8);
    /* Mobile: the name + bio inset an extra 16px (32px total) past the 16px
       page gutter the full-width CTAs keep; reset on desktop. */
    padding: 0 var(--space-4);
}

/* Header = logo + vendor name. Mobile/tablet: a centered column whose logo
   straddles the cover seam. Wide desktop: a row (logo left of the name). */
.psw-book-home__header {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-6);
    width: 100%;
}

/* The logo straddles the seam — half over the cover, half over the white body
   (xxl avatar is 116px, so pull up by half). */
.psw-book-home__avatar {
    position: relative;
    z-index: 1;
    display: flex;
    flex: 0 0 auto;
    margin-top: -58px;
}

/* White ring around the logo so it reads cleanly over the cover photo. */
.psw-book-home__avatar .psw-avatar {
    border: 4px solid var(--color-white);
    background: var(--color-white);
}

.psw-book-home__title {
    margin: 0;
    font-family: var(--font-sans);
    font-weight: var(--font-weight-bold);
    font-size: var(--text-heading-lg);                 /* mobile 32/40 */
    line-height: var(--text-heading-lg--line-height);
    color: var(--color-content-high);
}

/* Google rating row: gold star + average + "· N reviews" link. */
.psw-book-home__rating {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-high);
}

.psw-book-home__star {
    flex: 0 0 auto;
}

.psw-book-home__rating-value {
    font-weight: var(--font-weight-semibold);
}

.psw-book-home__rating-dot {
    color: var(--color-content-medium);
}

/* The review-count link is a tertiaryLink button; align it on the row baseline. */
.psw-book-home__rating .psw-btn {
    padding: 0;
    height: auto;
}

.psw-book-home__bio {
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-medium);
    text-align: center;
}

/* The bio "Show more" toggle reads at Paragraph/Medium (16px) like the copy it
   sits in (sneak_peek defaults its toggle to 14px). */
.psw-book-home__bio .psw-sneak-peek__toggle {
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
}

/* The CTA list is the `stack` component; the column width above + the top
   margin are page-specific, and the buttons sit 12px apart (Figma). */
.psw-book-home__actions {
    margin-top: 0;
    --psw-stack-gap: var(--space-3);
}

/* The divider above the page-bottom branding shows only in the wide-desktop
   two-column layout (below that, the sticky footer carries branding). */
.psw-book-home__divider {
    display: none;
    width: 100%;
    max-width: var(--book-home-column);
    height: 0;
    margin: 0;
    border: 0;
    border-top: var(--border-width-sm) solid var(--color-gray-200);
}

/* Mobile/tablet: branding is carried by the sticky footer, so the inline copy hides. */
.psw-book-home__branding {
    display: none;
}

@media (min-width: 769px) {
    /* Constrain the cover so the landing reads as a centered card, not a
       full-width billboard, on wide screens. */
    .psw-book-home__cover {
        height: 240px;
    }

    .psw-book-home__body {
        padding-bottom: var(--space-8);   /* no fixed footer to clear */
    }

    /* The extra mobile side inset on the name/bio is only for the tight phone
       gutter — drop it once the column has room. */
    .psw-book-home__hero {
        padding: 0;
    }

    .psw-book-home__branding {
        display: block;
        margin-top: var(--space-6);
    }
}

/* Wide desktop: the two-column split (Figma 3080:187406) — a vertically
   centered, left-aligned content column beside a full-height rounded hero
   panel. Below this width it collapses to the stacked "cover on top" layout. */
@media (min-width: 1024px) {
    .psw-book-home {
        --book-home-column: 540px;
        display: grid;
        grid-template-columns: minmax(0, 1fr) minmax(0, 44%);
        align-items: stretch;
    }

    /* Right panel: a fixed full-viewport-height image inset 16px. Sticky +
       align-self:start pins it in view so it stays put while a tall left
       column scrolls past (Figma; the panel must not scroll away). */
    .psw-book-home__cover {
        grid-column: 2;
        grid-row: 1;
        align-self: start;
        position: sticky;
        top: var(--space-4);
        height: calc(100vh - 2 * var(--space-4));
        margin: var(--space-4);
    }

    /* Left column: the content column is centered both ways within the panel
       (Figma) — vertically via justify-content, horizontally via align-items;
       the hero keeps its own left-aligned text. */
    .psw-book-home__body {
        grid-column: 1;
        grid-row: 1;
        justify-content: center;
        align-items: center;
        padding: var(--space-10);
    }

    /* Logo sits inline to the left of the name; no cover overlap. */
    .psw-book-home__header {
        flex-direction: row;
        align-items: center;
    }

    .psw-book-home__avatar {
        margin-top: 0;
    }

    .psw-book-home__hero {
        align-items: flex-start;
        text-align: left;
    }

    .psw-book-home__title {
        flex: 1 1 auto;
        min-width: 0;
        font-size: var(--text-heading-2xl);                /* desktop 40/52 */
        line-height: var(--text-heading-2xl--line-height);
    }

    .psw-book-home__rating {
        justify-content: flex-start;
    }

    .psw-book-home__bio {
        text-align: left;
    }

    .psw-book-home__divider {
        display: block;
        margin-top: var(--space-8);
    }

    .psw-book-home__branding {
        margin-top: var(--space-8);
        text-align: center;
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Review step -> .psw-book-review. The gratuity tile grid (heading-less; the
   page title carries it) with the inline custom flat-amount field the
   "Custom" tile reveals, then the gift-card tile grid (its own component
   heading). One loose column; the pickers own their internal layout.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-review {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

/* [hidden] keeps it out of flow; when shown it slots between the pickers. */
.psw-book-review__custom {
    max-width: 343px;
}

/* ─────────────────────────────────────────────────────────────────────────
   Payment step -> .psw-book-source. The payment_method_picker carries the
   layout (segmented control, method rows, policy copy); the page only styles
   the add-new forms (relocated into the picker's groups by
   psw-book-payment.js) and the Stripe error line.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-source__new-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);   /* 24px between the name-on-card and card-number fields */
    /* No margin: the picker's 24px gap sits above (segmented control -> group)
       and the loose stack's 32px gap below (fields -> policy) frame the form. */
    margin: 0;
}

/* display:flex above would defeat the [hidden] attribute the page JS uses. */
.psw-book-source__new-form[hidden] {
    display: none;
}

/* A div standing in for an <input>: psw-field__input brings the border/
   radius; center the Stripe iframe (~20px tall) inside the 48px box. This
   field is hand-authored without the input Component's .psw-field__input-wrap,
   so it lacks the wrap's 8px top margin — add it here so the label -> field gap
   matches the name-on-card field's 12px (4px psw-field flex gap + 8px). */
.psw-book-source__card-element {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 48px;
    margin-top: var(--space-2);
}

.psw-book-source__card-element > * {
    width: 100%;
}

.psw-book-source__errors {
    margin: var(--space-4) 0 0;
    font-size: var(--text-paragraph-sm);
    line-height: var(--text-paragraph-sm--line-height);
    color: var(--color-danger-400);
}

/* ─────────────────────────────────────────────────────────────────────────
   BNPL email / mailing-address steps. The provider step (.psw-book-bnpl +
   [data-psw-book-bnpl]) carries its own picker layout; these two collection
   steps stack input/select components directly, so they own the same
   field-to-field rhythm the data_entry_form uses (24px mobile, 32px desktop).
   __intro is the "apply for <provider> financing" line above the fields;
   __row pairs the State dropdown and Postal code side by side on desktop.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-bnpl__intro {
    margin: 0 0 var(--space-6);
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-high);
}

[data-psw-book-bnpl-email-step],
[data-psw-book-bnpl-address-step] {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

.psw-book-bnpl__row {
    display: flex;
    flex-direction: column;
    gap: var(--space-6);
}

@media (min-width: 769px) {
    [data-psw-book-bnpl-email-step],
    [data-psw-book-bnpl-address-step] {
        gap: var(--space-8);
    }

    /* State + Postal code share a row on desktop (Figma 2234:87731); each fills
       half the column. min-width:0 lets the inputs shrink instead of overflow. */
    .psw-book-bnpl__row {
        flex-direction: row;
        gap: var(--space-4);
    }

    .psw-book-bnpl__row > * {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   Intake step -> .psw-book-capture. The shared data_entry_form carries the
   fields; the page adds the pro-authored form memo above it (gray, like the
   Forms widget's .psw-record__memo).
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-capture__memo {
    margin: 0 0 var(--space-6);
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-medium);
    overflow-wrap: anywhere;
}

/* ─────────────────────────────────────────────────────────────────────────
   Completed screen -> .psw-book-complete. success_shell (header=False) hero
   over the order-recap card. The card's sections reorder per viewport via
   flex order — Figma: mobile = date / items / address / totals (buttons in
   the sticky footer); desktop = date / buttons / address / items / totals.
   The shell's own desktop-actions duplicate is hidden — the page renders its
   own action block (the sets differ: "View summary" is desktop-only).
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-complete .psw-success-shell__content {
    display: flex;
    flex-direction: column;
}

.psw-book-complete .psw-success-shell__desktop-actions {
    display: none;
}

.psw-book-complete__section {
    /* Mobile rhythm: 16px flanks every divider. Desktop widens this to 24px
       (ENG-37067) in the min-width:769px block below. */
    padding: var(--space-4) 0;
}

.psw-book-complete__section:first-child {
    padding-top: 0;
}

.psw-book-complete__section:last-child {
    padding-bottom: 0;
}

/* The :last-child reset above never matches the totals: the shell's own
   desktop-actions div (display:none here) always trails the sections, so on the
   service layout the last VISIBLE section still stacked its padding onto the
   card's. Zero it so the card closes symmetrically at both widths — 16px on
   mobile, 24px on desktop, from the card's own padding (ENG-37483). The product
   layout keeps the padding: there the action block really does follow the
   totals. Higher specificity than the section rule, so the desktop
   `padding: var(--space-6) 0` shorthand below can't put it back. */
.psw-book-complete:not(.psw-book-complete--product) .psw-book-complete__totals {
    padding-bottom: 0;
}

/* Section dividers (the actions block joins the date section undivided). */
.psw-book-complete__items,
.psw-book-complete__video,
.psw-book-complete__address,
.psw-book-complete__totals {
    border-top: var(--border-width-sm) solid var(--color-gray-200);
}

.psw-book-complete__when {
    order: 1;
    display: flex;
    flex-direction: column;
}

/* A single-session booking holds date + time directly, so __when's own gap is
   the date/time gap (4px mobile / 8px desktop). A multi-session booking holds
   __session blocks, so __when's gap becomes the space below each session's time
   (16px mobile / 24px desktop) while __session owns the inner date/time gap. */
.psw-book-complete__when:not(:has(.psw-book-complete__session)) {
    gap: var(--space-1);
}

.psw-book-complete__when:has(.psw-book-complete__session) {
    gap: var(--space-4);
}

.psw-book-complete__session {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

/* Multi-session lists divide each row with the card's section rule (Figma
   3076:29278): the parent __when gap sits above the line, this padding below it,
   so the divider is flanked 16px/16px on mobile (24px/24px desktop, @media
   below) — matching the section rhythm. A collapsed overflow row is
   display:none, so its divider drops out with it; the "Show +N more" toggle
   isn't a __session, so no divider lands above it. */
.psw-book-complete__session + .psw-book-complete__session {
    padding-top: var(--space-4);
    border-top: var(--border-width-sm) solid var(--color-gray-200);
}

/* A reservation stay renders as a two-column Check-in | Check-out block split by
   a vertical divider (Figma 3111:293436) -- the same shape as the in-flow
   summary rail; a service session stays a single stacked date/time. */
.psw-book-complete__session:has(.psw-book-complete__session-col) {
    flex-direction: row;
    align-items: stretch;
    gap: var(--space-4);
}

.psw-book-complete__session-col {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.psw-book-complete__session-col--end {
    align-items: flex-end;
    text-align: right;
}

.psw-book-complete__session-divider {
    flex: 0 0 auto;
    align-self: stretch;
    width: var(--border-width-sm);
    background: var(--color-gray-200);
}

.psw-book-complete__session-label {
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-gray-600);
}

.psw-book-complete__date {
    /* 20px mobile (Figma); the desktop @media below steps it up. */
    font-size: var(--text-heading-xs);
    line-height: var(--text-heading-xs--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-complete__time {
    font-size: var(--text-label-lg);
    line-height: var(--text-label-lg--line-height);
    color: var(--color-content-medium);
}

/* Multi-session "Show more": overflow rows (the 3rd onward) collapse behind a
   toggle on every viewport (Figma 3076:29278); Alpine flips the --shown class to
   reveal them. Visibility is driven by a class (not Alpine's inline x-show), so
   the cascade alone governs it — no !important needed. Mirrors the house
   psw-booking-date toggle. */
.psw-book-complete__session--overflow {
    display: none;
}

.psw-book-complete__session--overflow.psw-book-complete__session--shown {
    display: flex;
}

/* The toggle is two pre-rendered tertiaryLink buttons (chevron-down "Show +N
   more" / chevron-up "Show less"); one is hidden per state so the vendor primary
   color + hover/pressed come from psw-btn (ENG-37041 iter-2). Mirrors the house
   psw-booking-date toggle. */
.psw-book-complete__toggle {
    display: inline-flex;
    align-self: flex-start;
    min-width: 0;
    /* __when's session gap already sets the space above the toggle. */
}

.psw-book-complete__toggle-less,
.psw-book-complete__toggle--expanded .psw-book-complete__toggle-more {
    display: none;
}

.psw-book-complete__toggle--expanded .psw-book-complete__toggle-less {
    display: inline-flex;
}

/* Mobile order: items above the address; buttons live in the sticky footer. */
.psw-book-complete__actions {
    order: 2;
    display: none;
}

.psw-book-complete__items {
    order: 3;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

/* The meeting link stays pinned above the address on both viewports — the
   mobile action block is hidden (the buttons ride the sticky footer), so
   "under the CTAs" is a desktop-only relationship. */
.psw-book-complete__video {
    order: 4;
}

.psw-book-complete__address {
    order: 5;
}

.psw-book-complete__totals {
    order: 6;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.psw-book-complete__item {
    display: flex;
    align-items: center;
    gap: var(--space-4);
}

.psw-book-complete__item-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.psw-book-complete__item-label {
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-complete__item-subtitle {
    font-size: var(--text-paragraph-sm);
    line-height: var(--text-paragraph-sm--line-height);
    color: var(--color-content-medium);
}

/* estimate/complete.html renders this note as a plain block child of the
   shell's content card, so the margin here IS its 16px gap (ENG-36988). */
.psw-book-complete__note {
    margin: var(--space-4) 0 0;
    font-size: var(--text-paragraph-sm);
    line-height: var(--text-paragraph-sm--line-height);
    color: var(--color-content-medium);
}

/* Inside the totals section the note is a flex child, so the section's own 16px
   gap already separates it from the payment row — the margin above would stack
   on top of it for 32px. The estimate success page renders the same note
   directly in the success_shell content block (no flex gap), which is what the
   margin is there for. */
.psw-book-complete__totals > .psw-book-complete__note {
    margin-top: 0;
}

/* Product sales (subscription / package / gift) drop the date + address
   sections, so the item card leads the recap — no top divider above it; the
   divider falls between it and the meta/total rows (totals keeps its border). */
.psw-book-complete--product .psw-book-complete__items {
    border-top: none;
}

@media (min-width: 769px) {
    /* Sections breathe wider on desktop: 24px flanks every divider (ENG-37067) —
       roomier than the 16px rhythm between item / total rows inside a block. */
    .psw-book-complete__section {
        padding: var(--space-6) 0;
    }

    /* Desktop order: the action block follows the date; items drop below the
       address (Figma). */
    .psw-book-complete__actions {
        display: flex;
        flex-direction: column;
        /* Joins the date undivided — the date's 24px bottom is the whole "space
           below date & time"; the buttons don't add a second 24px on top. */
        padding-top: 0;
        /* 12px between the "Add to calendar" button and the Book-another /
           View-summary row (ENG-37067). */
        gap: var(--space-3);
    }

    /* The meeting link seats directly under the action buttons (Figma). */
    .psw-book-complete__video {
        order: 3;
    }

    .psw-book-complete__address {
        order: 4;
    }

    .psw-book-complete__items {
        order: 5;
    }

    .psw-book-complete__actions-row {
        display: flex;
        gap: var(--space-3);
    }

    .psw-book-complete__actions-row > * {
        flex: 1 1 0;
        min-width: 0;
    }

    /* product recap has no date/address: keep the action block below the
       item card + meta/total rows (Figma), unlike the service layout. */
    .psw-book-complete--product .psw-book-complete__actions {
        order: 7;
    }

    .psw-book-complete__date {
        font-size: var(--text-heading-md);
        line-height: var(--text-heading-md--line-height);
    }

    /* Desktop widens the date/time gap to 8px and the space below each session
       to 24px (Figma). */
    .psw-book-complete__when:not(:has(.psw-book-complete__session)) {
        gap: var(--space-2);
    }

    .psw-book-complete__when:has(.psw-book-complete__session) {
        gap: var(--space-6);
    }

    .psw-book-complete__session {
        gap: var(--space-2);
    }

    /* The reservation Check-in/Check-out columns follow the same 4px->8px
       desktop step for their label/date/time rows. */
    .psw-book-complete__session-col {
        gap: var(--space-2);
    }

    /* 24px flanks the inter-session divider on desktop (parent gap already 24px). */
    .psw-book-complete__session + .psw-book-complete__session {
        padding-top: var(--space-6);
    }
}

/* ─────────────────────────────────────────────────────────────────────────
   "Add to calendar" menu. The success card's primary button opens a list of
   calendar destinations — a desktop dropdown anchored under the button
   (Figma 4400:51265) and a mobile bottom sheet (Figma 4404:82804). Both list
   the same options; psw-book-complete.js toggles the right one per viewport.
   The desktop dropdown lives inside .psw-book-complete__actions, which is
   display:none on mobile — so it's hidden there for free, and the footer
   trigger opens the sheet instead.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-calendar {
    position: relative;
}

/* The trigger's trailing chevron flips while the dropdown is open. */
.psw-book-calendar .psw-btn .psw-icon {
    transition: transform 160ms ease;
}

.psw-book-calendar .psw-btn[aria-expanded="true"] .psw-icon {
    transform: rotate(180deg);
}

.psw-book-calendar__menu {
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    right: 0;
    z-index: 10;
    display: flex;
    flex-direction: column;
    padding: var(--space-2);
    border-radius: var(--radius-lg);
    background: var(--color-white);
    box-shadow: 0 5px 10px rgba(0, 0, 0, 0.1);
}

.psw-book-calendar__menu[hidden] {
    display: none;
}

.psw-book-calendar__option {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    width: 100%;
    padding: var(--space-3) var(--space-4);
    border: 0;
    border-radius: var(--radius-lg);
    background: transparent;
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-high);
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

/* Figma --opacity/dark-hover wash; no token for it in tokens.css. */
.psw-book-calendar__option:hover,
.psw-book-calendar__option:focus-visible {
    background: rgba(15, 15, 15, 0.04);
    outline: none;
}

.psw-book-calendar__option .psw-icon {
    flex-shrink: 0;
    /* Gray-500 destination glyphs (ENG-37067) — quieter than the label text. */
    color: var(--color-gray-500);
}

/* Mobile bottom sheet — a native <dialog> pinned to the bottom of the viewport
   over a scrim. Only ever opened below 769px (psw-book-complete.js). The dialog
   is sized to its content and bottom-anchored (inset: auto 0 0 0); a click on
   the scrim above it lands on the dialog element and closes it. */
.psw-book-calendar-sheet {
    margin: 0;
    padding: 0;
    width: 100%;
    max-width: none;
    max-height: 85vh;
    border: 0;
    background: transparent;
}

.psw-book-calendar-sheet:not([open]) {
    display: none;
}

.psw-book-calendar-sheet[open] {
    inset: auto 0 0 0;
}

.psw-book-calendar-sheet::backdrop {
    background: rgba(15, 15, 15, 0.4);
}

.psw-book-calendar-sheet__panel {
    width: 100%;
    padding: var(--space-6) var(--space-4) calc(var(--space-4) + env(safe-area-inset-bottom));
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    background: var(--color-white);
    box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.08);
}

.psw-book-calendar-sheet__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-bottom: var(--space-6);
}

.psw-book-calendar-sheet__title {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-heading-xs);
    line-height: var(--text-heading-xs--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-calendar-sheet__options {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}
/* Carries autofocus so showModal() lands here instead of the close X (see
   book/complete.html). It's a container, not a control — never ring it. */
.psw-book-calendar-sheet__options:focus,
.psw-book-calendar-sheet__options:focus-visible {
    outline: none;
}

/* ─────────────────────────────────────────────────────────────────────────
   Class schedule step -> .psw-book-schedule. Scheduled (open / series) classes
   book as a unit, so the step is a read-only list of the class's lesson days:
   a bold date over a gray time range per row, rows split by a 1px divider. The
   Summary rail (desktop) / header cart (mobile) carry the same lessons as
   stacked session rows. An empty state stands in when no lessons are available.
   ───────────────────────────────────────────────────────────────────────── */
.psw-book-schedule__list {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}

.psw-book-schedule__lesson {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);          /* 4px date -> time */
}

/* Rows are separated by a divider; the last row has no trailing rule. */
.psw-book-schedule__lesson:not(:last-child) {
    margin-bottom: var(--space-4);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--color-gray-200);
}

.psw-book-schedule__lesson-date {
    font-family: var(--font-sans);
    font-size: var(--text-label-lg);        /* bold 18/24 */
    line-height: var(--text-label-lg--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-schedule__lesson-time {
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);    /* regular 16/24, gray */
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-gray-600);
}

/* Empty state (no available lessons): bold lead line + gray explanation. */
.psw-book-schedule__empty {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.psw-book-schedule__empty-title {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-label-lg);
    line-height: var(--text-label-lg--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-schedule__empty-text {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-gray-600);
}


/* Custom-gratuity picker body (/book/<uri>/review) — the panels inside the
   shared modal (Component.wrap("modal", fullscreen_mobile=True)). The dialog
   chrome, the mobile full-screen frame and the branding line all belong to
   psw-modal; only the percent stepper is this page's own. */

/* Clears the segmented control above. Owned here, not by psw-modal__content:
   the gap belongs to this page's body, not to every modal's content region.
   Only the visible panel takes it — the hidden one is display:none. */
.psw-book-gratuity__panel {
    margin-top: var(--space-8);
}

.psw-book-gratuity__panel[hidden] {
    display: none;
}

/* Percent mode: -/+ pinned to the edges with the value centered between. */
.psw-book-gratuity__stepper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
}

.psw-book-gratuity__stepper-value {
    min-width: 0;
    text-align: center;
}

.psw-book-gratuity__stepper-percent {
    font-family: var(--font-sans);
    font-size: var(--text-label-md);
    line-height: var(--text-label-md--line-height);
    font-weight: var(--font-weight-bold);
    color: var(--color-content-high);
}

.psw-book-gratuity__stepper-amount {
    font-family: var(--font-sans);
    font-size: var(--text-paragraph-md);
    line-height: var(--text-paragraph-md--line-height);
    color: var(--color-content-medium);
}

@media (max-width: 768px) {
    .psw-book-gratuity__panel {
        margin-top: var(--space-6);
    }
}
}
