/* ---------------------------------------------------------------------
   Design tokens — a small, unique-prefixed set of custom properties so
   this never collides with theme/page-builder CSS. Palette takes its cue
   from PT Minder's own site (deep navy + white, minimal decoration,
   card-based layout) without copying it pixel-for-pixel.
   --------------------------------------------------------------------- */
:root {
    --ptb-navy: #101a2e;
    --ptb-blue: #2f6fed;
    /* v9.105.1 — was #f4f6f9, barely different from white; once every card
       lost its border (v9.105.0) that was too subtle to read as "grey page,
       white box" at all — per Jesse: "everything that was grey is now
       white." Genuinely grey now, no border needed to tell it apart from
       --ptb-card. */
    --ptb-bg: #edeef1;
    --ptb-card: #ffffff;
    /* v9.108.0 — was rgba(16, 26, 46, 0.09) (translucent, so it always
       looked right against --ptb-bg without a hardcoded hex); switched to
       a solid colour so it can be exposed as a plain Customizer colour
       control (WP core's colour picker is hex-only, no alpha) — #e9eaec is
       that same rgba composited over white, so nothing visibly changes
       until Jesse actually edits the new "Divider colour" control. */
    --ptb-border: #e9eaec;
    --ptb-divider-width: 1px;
    /* v9.117.0 — form fields (text/tel/email/date/time/number/search
       inputs, select, textarea) used to share --ptb-border with the
       divider lines above, so a "Divider colour" edit silently changed
       field borders too. Per Jesse: "need to be able to set field colour
       in customizer" — separate token, same default, so it's now
       independently controllable. */
    --ptb-field-border: #e9eaec;
    /* v9.118.0 — the focused-field highlight used to read var(--ptb-accent)
       directly; per Jesse: "what used to happen it was highlighted before
       by the purple colour - can we add this to customizer" — own token so
       it's independently settable from Accent, same pattern as the field
       border colour above. */
    --ptb-field-focus: #6d5ef8;
    --ptb-field-focus-bg: color-mix(in srgb, var(--ptb-field-focus) 12%, white);
    --ptb-text: #101828;
    /* v9.153.0 — .ptb-step-heading below used to have no color of its own,
       just inheriting --ptb-text from .ptb-booking; per Jesse: "we need to
       be able to set heading colors and text colors" — own token now, same
       default, so Heading colour and Text colour are independently
       settable in Customizer even though they currently match. */
    --ptb-heading: #101828;
    --ptb-muted: #667085;
    --ptb-success: #1a8a4a;
    /* v9.108.0 — was a separate hardcoded hex; deriving it from --ptb-success
       means the Icon/status colour Customizer control now correctly updates
       the tint background too, instead of only the icon itself. */
    --ptb-success-bg: color-mix(in srgb, var(--ptb-success) 12%, white);
    --ptb-danger: #c22b2b;
    --ptb-danger-bg: color-mix(in srgb, var(--ptb-danger) 12%, white);
    --ptb-accent: #6d5ef8;
    --ptb-accent-bg: color-mix(in srgb, var(--ptb-accent) 12%, white);
    --ptb-warning: #b5690a;
    --ptb-warning-bg: color-mix(in srgb, var(--ptb-warning) 12%, white);
    /* v9.110.0 — Manage Billing, Today's Sessions, Next PT Session, Next
       Class, and the "N spots left" badge all used to share --ptb-accent,
       so changing one changed all five together — per Jesse: "icons still
       missing... need to customize these" once he realized they weren't
       independently controllable. Each gets its own token now, all
       defaulting to the same #6d5ef8 --ptb-accent already used, so nothing
       visibly changes until each is deliberately set differently. */
    --ptb-icon-billing: #6d5ef8;
    --ptb-icon-billing-bg: color-mix(in srgb, var(--ptb-icon-billing) 12%, white);
    --ptb-icon-todays-sessions: #6d5ef8;
    --ptb-icon-todays-sessions-bg: color-mix(in srgb, var(--ptb-icon-todays-sessions) 12%, white);
    --ptb-icon-next-pt: #6d5ef8;
    --ptb-icon-next-pt-bg: color-mix(in srgb, var(--ptb-icon-next-pt) 12%, white);
    --ptb-icon-next-class: #6d5ef8;
    --ptb-icon-next-class-bg: color-mix(in srgb, var(--ptb-icon-next-class) 12%, white);
    --ptb-badge-spots: #6d5ef8;
    --ptb-badge-spots-bg: color-mix(in srgb, var(--ptb-badge-spots) 12%, white);
    /* v9.111.0 — the sales-bump/"go unlimited" banner and the "Cash payment
       client" banner (.ptb-promo-banner / .ptb-promo-banner-static) both
       used to read background: var(--ptb-accent) implicitly, same shared-
       token issue as the stat icons above; split into their own tokens so
       each can be set independently. */
    --ptb-banner-upsell: #6d5ef8;
    --ptb-banner-cash: #6d5ef8;
    /* v9.113.0 — banners were solid-fill/white-text (loud, by original
       design); per Jesse: "the banners need to be like the icons - lighter
       background darker icon text" — switched to the same light-tint-bg +
       full-colour-text pattern .ptb-stat-icon/.ptb-icon-btn-* use. */
    --ptb-banner-upsell-bg: color-mix(in srgb, var(--ptb-banner-upsell) 12%, white);
    --ptb-banner-cash-bg: color-mix(in srgb, var(--ptb-banner-cash) 12%, white);
    /* v9.107.0 — was 0 (Jesse originally asked for every corner square,
       avatars/pills included); now 5px, applied to boxes/buttons/fields/
       banners specifically (v9.106.1-2), not badges/avatars/nav — and
       exposed as a live Customizer control (includes/customizer.php) so
       this can be tuned without a code change. */
    --ptb-radius: 5px;
    /* v9.112.0 — was hardcoded per-element (3-4px, v9.108.0) on just the
       date circle/avatar/count badge; now one shared, Customizer-exposed
       value applied to every fixed-size icon circle/square (date badge,
       client avatar, count badge, stat icons, icon buttons, banner icon)
       so they all breathe consistently and can be tuned in one place, same
       pattern as --ptb-radius above. */
    --ptb-icon-padding: 4px;
    --ptb-shadow: none; /* and every box-shadow gone too — flat cards/rows, no elevation */
}

/* ---------------------------------------------------------------------
   Public booking UI — mobile-first. Tabs/sub-filters/collapsible forms are
   all still the pure-CSS checked-radio/checkbox + label technique (see
   .ptb-tab-radio/.ptb-tab-panel) — the actual JavaScript in this plugin
   (assets/booking.js) swaps in fresh markup after an action instead of a
   full page reload, and every form it touches still works as a plain
   POST if that script doesn't load or run.
   --------------------------------------------------------------------- */
.ptb-booking {
    max-width: 1080px;
    font-size: 16px;
    line-height: 1.55;
    color: var(--ptb-text);
}

/* Full-page app template (templates/ptb-app-template.php) — no theme
   header/footer, so this plugin owns the whole viewport. */
body.ptb-app-page {
    margin: 0;
    padding: 0;
    min-height: 100vh;
    /* v9.106.0 — was background: var(--ptb-bg) (a hardcoded grey); per
       Jesse: "can we just inherit the page background colour set by
       Bricks?" No override here now, so whatever Bricks sets globally on
       body shows through instead of us guessing/hardcoding a shade. */
    display: flex;
    justify-content: center;
    align-items: flex-start;
}
/* Jesse asked for the mobile/compact layout everywhere, on every screen
   size, not just narrow ones — no separate desktop layout at all — so
   what used to be the @media (max-width: 720px) override here (and for
   every other breakpoint in this file) is now just how it always looks:
   edge to edge, no outer padding or rounded corners. */
body.ptb-app-page .ptb-booking {
    width: 100%;
    padding: 0;
}
body.ptb-app-page .ptb-app {
    min-height: 100vh;
    border-radius: 0;
}

/* ---------------------------------------------------------------------
   Buttons + form fields — a fully custom design system as of v8.0.0.
   Earlier versions deliberately inherited the host site's own Bricks
   Builder button/form styling instead of styling these directly; Jesse
   asked for the opposite this time ("not inheriting anything"), so this
   plugin now owns its own look end to end, scoped under .ptb-booking so
   it never leaks into the rest of the page.
   --------------------------------------------------------------------- */
.ptb-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 11px 20px;
    border: none;
    border-radius: var(--ptb-radius);
    background: var(--ptb-navy);
    color: #fff;
    font-family: inherit;
    font-weight: 600;
    font-size: 0.94em;
    line-height: 1.2;
    cursor: pointer;
    text-decoration: none;
    white-space: nowrap;
    transition: filter 0.15s ease, transform 0.08s ease;
}
.ptb-btn:hover {
    filter: brightness(1.15);
    color: #fff;
}
.ptb-btn:active {
    transform: scale(0.97);
}
/* "Install app on phone" test button (v9.119.0, logged-out view only —
   see ptb_booking_shortcode()/includes/pwa.php) — full width, sitting
   above the class list rather than inline with anything else. */
.ptb-install-app-btn {
    width: 100%;
    margin-bottom: 16px;
}

/* Loading state — injected by assets/booking.js while an .ptb-ajax-form
   request is in flight. Works on both text buttons (.ptb-btn) and
   icon-only ones (.ptb-icon-btn) since the spinner just borrows whatever
   color the button already has via currentColor, rather than needing a
   variant per button type. */
.ptb-btn-loading {
    position: relative;
    cursor: default;
}
.ptb-btn-loading > *:not(.ptb-spinner) {
    visibility: hidden;
}
.ptb-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 16px;
    height: 16px;
    margin: -8px 0 0 -8px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%; /* functional, not decorative — a spinning ring reads as "loading", a spinning square with a gap doesn't; kept exempt from the rounded-corners removal */
    opacity: 0.85;
    animation: ptb-spin 0.6s linear infinite;
}
@keyframes ptb-spin {
    to {
        transform: rotate(360deg);
    }
}
/* Same idea for a.ptb-ajax-nav (booking-wizard step links) — a plain <a>
   doesn't have a natural disabled state or room for a spinner glyph like
   a button does, so this just dims it and blocks further clicks/taps
   until the re-fetched .ptb-app replaces it. */
.ptb-ajax-nav-loading {
    opacity: 0.55;
    pointer-events: none;
}
.ptb-booking input[type="text"],
.ptb-booking input[type="tel"],
.ptb-booking input[type="email"],
.ptb-booking input[type="date"],
.ptb-booking input[type="time"],
.ptb-booking input[type="number"],
.ptb-booking input[type="search"],
.ptb-booking select,
.ptb-booking textarea {
    font-family: inherit;
    font-size: 0.95em;
    color: var(--ptb-text);
    background: var(--ptb-card);
    border: 1.5px solid var(--ptb-field-border);
    border-radius: var(--ptb-radius);
    padding: 9px 12px;
    line-height: 1.3;
    /* v9.117.0 — without this, mobile Safari applies its own native
       focused-field chrome (a blue inset highlight) that visually
       replaces this custom border the moment you tap in — per Jesse:
       "when you click in to a field right now the border disappears."
       appearance:none stops the browser from drawing that on top. */
    appearance: none;
    -webkit-appearance: none;
}
.ptb-booking input:focus,
.ptb-booking select:focus,
.ptb-booking textarea:focus {
    outline: none;
    border-color: var(--ptb-field-focus);
    box-shadow: 0 0 0 3px var(--ptb-field-focus-bg);
}
.ptb-booking input[type="checkbox"] {
    width: 15px;
    height: 15px;
}
.ptb-notice {
    position: relative;
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    padding: 13px 16px;
    border-radius: var(--ptb-radius);
    margin-bottom: 18px;
    font-size: 0.92em;
    font-weight: 500;
    transition: opacity 0.3s ease;
}
/* Auto-dismissed (assets/booking.js, after a few seconds) or closed via
   the × — same fade either way, JS removes the element once it finishes. */
.ptb-notice.ptb-notice-dismissing {
    opacity: 0;
}
.ptb-notice-success {
    background: var(--ptb-success-bg);
    color: var(--ptb-success);
}
.ptb-notice-error {
    background: var(--ptb-danger-bg);
    color: var(--ptb-danger);
}
.ptb-notice-close {
    flex: 0 0 auto;
    color: inherit;
    text-decoration: none;
    font-size: 1.3em;
    line-height: 0.8;
    opacity: 0.6;
    padding: 2px 4px;
}
.ptb-notice-close:hover {
    opacity: 1;
}
/* The "Record a pack purchase" link inside session_booked_no_pack's notice
   text (see ptb_booking_notice_html()) — scoped to a link inside the
   notice's own <span> specifically, not .ptb-notice-close, which sits
   outside it and needs its own no-underline look preserved. */
.ptb-notice span a {
    color: inherit;
    font-weight: 700;
    text-decoration: underline;
}
/* The session_booked_no_pack notice's optional "Charge card on file" form
   (ptb_booking_notice_html()) — a real <form> so it can post independently
   of the notice's own dismiss link, styled to read as an inline link like
   .ptb-notice span a above rather than a distinct form control. */
.ptb-notice-inline-form {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
}
.ptb-notice-link-btn {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    color: inherit;
    font: inherit;
    font-weight: 700;
    text-decoration: underline;
    cursor: pointer;
    white-space: nowrap;
}
.ptb-empty {
    color: var(--ptb-muted);
    padding: 8px 0;
}
.ptb-hint {
    color: var(--ptb-muted);
    font-weight: 400;
    font-size: 0.9em;
}
.ptb-icon {
    display: block;
    width: 20px;
    height: 20px;
    flex: 0 0 auto;
}

/* ---------------------------------------------------------------------
   App shell — a branded sidebar + content area (icon nav, rounded frame)
   so this reads as a real app embedded in the page, not a form on a page.
   Tabs are still the pure-CSS checked-radio + label technique (no JS) —
   the radios sit right before .ptb-app-sidebar/.ptb-app-main so the ~
   sibling selectors below can reach into both.
   --------------------------------------------------------------------- */
/* Jesse asked for the compact/mobile layout everywhere, on every screen
   size — no separate wide-viewport layout at all — so this app shell is
   now single-column, not just under a max-width breakpoint. As of this
   version the nav itself moved from a top icon bar to a bottom tab bar,
   always visible over the content regardless of scroll position, rather
   than scrolling away with the page. Went through several iterations on
   the exact look (myGov floating pill, then square/compact, then
   icon-only) before Jesse sent a reference of a docked bank-app-style bar
   (full width, no lift/margin, icon+label together) and asked for that
   shape specifically, in black with white icons and the active tab in
   purple — this is that version. */
.ptb-app {
    position: relative; /* contains the absolutely-positioned radios below */
    display: grid;
    grid-template-columns: 1fr;
    /* v9.105.3 made this var(--ptb-bg) so every row/card (white) had
       actual contrast to sit on instead of rendering white-on-white — see
       that version's note. v9.106.0 drops the hardcoded colour itself:
       transparent lets body.ptb-app-page's own background (now whatever
       Bricks sets, not a hardcoded grey — see that rule) show through
       here too, so the two stay in sync automatically instead of needing
       to be kept in sync by hand. */
    background: transparent;
    border-radius: var(--ptb-radius);
    overflow: hidden;
}
.ptb-tab-radio {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.ptb-app-sidebar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 40;
    /* Black bar, white icons+labels at rest, purple (--ptb-accent, the
       same purple already used for accents/badges elsewhere in this app —
       not a new one-off color) on the active tab — no filled highlight
       box behind the active icon, just the color swap. */
    background: var(--ptb-navy);
    color: #fff;
    padding: 10px 6px calc(10px + env(safe-area-inset-bottom));
}
/* .ptb-app-brand-icon is still used by .ptb-summary-brand in the booking
   wizard's sticky summary card — the sidebar's own copy of it (plus the
   wrapping .ptb-app-brand and .ptb-app-brand-name) was removed outright,
   not just hidden, per Jesse: not needed at the top of the app. */
.ptb-app-brand-icon {
    flex: 0 0 auto;
    width: 36px;
    height: 36px;
    border-radius: 0;
    background: var(--ptb-navy);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 0.85em;
    letter-spacing: 0.02em;
}
.ptb-app-nav {
    display: flex;
    gap: 4px;
}
.ptb-app-nav label {
    display: flex;
    flex: 1 1 0;
    min-width: 0;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 6px 4px;
    border-radius: 0;
    background: transparent;
    cursor: pointer;
    color: #fff;
    font-weight: 600;
    font-size: 0.72em;
    text-align: center;
    white-space: nowrap;
    transition: color 0.15s ease;
}
.ptb-app-nav .ptb-icon {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
}
/* Tap-confirmation spinner — dead-center of the screen (34px, navy ring),
   not the icon itself: Jesse asked for it off the icon and onto the middle
   of the screen. Still `position: fixed`
   so it centers on the viewport regardless of which nav label triggered
   it, and still triggered by :active (fires the instant a finger/pointer
   goes down, before the browser even applies the new :checked state) —
   that part is unchanged from v9.32.1, only where it renders moved. */
.ptb-app-nav label::after {
    content: "";
    position: fixed;
    top: 50%;
    left: 50%;
    width: 34px;
    height: 34px;
    margin: -17px 0 0 -17px;
    border: 3px solid var(--ptb-navy);
    border-right-color: transparent;
    border-radius: 50%; /* same functional exception as .ptb-spinner */
    background: transparent;
    opacity: 0;
    z-index: 9999;
    pointer-events: none;
}
.ptb-app-nav label:active::after {
    opacity: 1;
    animation: ptb-spin 0.6s linear infinite; /* reuses .ptb-spinner's own keyframes, not a duplicate */
}
.ptb-app-main {
    /* Clears the docked bar's own height (no lift/gap now that it's
       flush with the bottom edge again) with room to spare. */
    padding: 18px 16px calc(88px + env(safe-area-inset-bottom));
    min-width: 0;
}
.ptb-tab-panel {
    display: none;
}
#ptb_tab_book:checked ~ .ptb-app-main .ptb-tab-panel-book,
#ptb_tab_sessions:checked ~ .ptb-app-main .ptb-tab-panel-sessions,
#ptb_tab_home:checked ~ .ptb-app-main .ptb-tab-panel-home,
#ptb_tab_schedule:checked ~ .ptb-app-main .ptb-tab-panel-schedule,
#ptb_tab_dashboard:checked ~ .ptb-app-main .ptb-tab-panel-dashboard,
#ptb_tab_clients:checked ~ .ptb-app-main .ptb-tab-panel-clients,
#ptb_tab_payments:checked ~ .ptb-app-main .ptb-tab-panel-payments,
#ptb_tab_admin:checked ~ .ptb-app-main .ptb-tab-panel-admin {
    display: block;
}
#ptb_tab_book:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_book"],
#ptb_tab_sessions:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_sessions"],
#ptb_tab_home:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_home"],
#ptb_tab_schedule:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_schedule"],
#ptb_tab_dashboard:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_dashboard"],
#ptb_tab_clients:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_clients"],
#ptb_tab_payments:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_payments"],
#ptb_tab_admin:checked ~ .ptb-app-sidebar .ptb-app-nav label[for="ptb_tab_admin"] {
    color: var(--ptb-accent);
}

/* ---------------------------------------------------------------------
   Booking wizard (Services > Time > Confirm) — a two-column layout inside
   the "Book a Class" panel: step content on the left, a sticky summary
   card on the right (matching the reference booking-flow screenshot Jesse
   sent). Every step transition is a plain <a href>, still no JavaScript.
   --------------------------------------------------------------------- */
.ptb-book-flow {
    display: grid;
    grid-template-columns: 1fr;
    gap: 28px;
    align-items: start;
}
.ptb-book-flow-main {
    min-width: 0;
}
.ptb-step-heading {
    margin: 0 0 4px;
    font-size: 1.2em;
    color: var(--ptb-heading);
}
.ptb-step-subtext {
    color: var(--ptb-muted);
    margin: 0 0 18px;
}
.ptb-step-back {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 9px 14px;
    border: 1.5px solid var(--ptb-border);
    background: var(--ptb-card);
    color: var(--ptb-text);
    font-size: 0.88em;
    font-weight: 600;
    text-decoration: none;
    margin-bottom: 14px;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
@media (hover: hover) {
    .ptb-step-back:hover {
        background: var(--ptb-bg);
        border-color: var(--ptb-text);
    }
}
.ptb-step-back .ptb-icon {
    width: 15px;
    height: 15px;
}
/* Client Detail's back link sits below its content instead of above (per
   Jesse), so it needs top spacing instead of the default bottom spacing
   every other .ptb-step-back use relies on for the element right after it. */
.ptb-step-back-bottom {
    margin-top: 20px;
    margin-bottom: 0;
}
/* Now lives inside .ptb-book-flow-summary, directly under the CTA — same
   width as the button above it, centered like a button instead of the
   left-aligned inline link it was when it sat at the top of the step. */
.ptb-summary-back {
    width: 100%;
    justify-content: center;
    margin-top: 10px;
    margin-bottom: 0;
}

/* Step 1 — service rows (single-select: clicking a row advances straight
   to the Time step, no separate "Continue" needed since only one class
   can be booked at a time). */
.ptb-service-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.ptb-service-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    background: var(--ptb-card);
    /* Transparent, not none — invisible in the default state (matches every
       other card: light grey page, white box, no visible line) while still
       reserving the space .ptb-service-row-selected below needs to actually
       show its accent-coloured border, without a layout shift on select. */
    border: 1.5px solid transparent;
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 16px 18px;
    /* Matches .ptb-managed-row's min-height — see that rule's comment. */
    min-height: 74px;
    text-decoration: none;
    color: inherit;
    transition: border-color 0.15s ease;
}
.ptb-service-row-selected {
    border-color: var(--ptb-accent);
}
.ptb-service-row-main {
    min-width: 0;
}
/* v9.101.0 — simplified per Jesse's reference screenshot (a plain name +
   one muted icon/text line underneath, no badge wall): name on top,
   .ptb-service-row-meta below it as plain text, same treatment as
   .ptb-managed-row-title/.ptb-managed-row-meta below. */
.ptb-service-row-title {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
}
.ptb-service-row-name {
    font-weight: 700;
}
.ptb-service-row-meta {
    display: flex;
    align-items: center;
    gap: 6px;
    color: var(--ptb-muted);
    font-size: 0.9em;
}
.ptb-service-row-meta .ptb-icon {
    width: 14px;
    height: 14px;
    flex: 0 0 auto;
}
.ptb-service-row-action {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: var(--ptb-radius);
    background: var(--ptb-bg);
    color: var(--ptb-muted);
}
.ptb-service-row-selected .ptb-service-row-action {
    background: var(--ptb-accent);
    color: #fff;
}
.ptb-service-row-action .ptb-icon {
    width: 16px;
    height: 16px;
}
.ptb-row-badge {
    flex: 0 0 auto;
    background: var(--ptb-bg);
    color: var(--ptb-muted);
    font-size: 0.68em;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    padding: 3px 8px;
    border-radius: 0;
}
.ptb-row-badge-accent {
    background: var(--ptb-accent-bg);
    color: var(--ptb-accent);
}
/* "N spots left" badge specifically (v9.110.0) — split out from the
   generic .ptb-row-badge-accent so it can be given its own Customizer
   colour independent of "1-on-1"/"Group" and the Recurring icon badge,
   which stay on the shared Accent token. */
.ptb-row-badge-spots {
    background: var(--ptb-badge-spots-bg);
    color: var(--ptb-badge-spots);
}
.ptb-row-badge-success {
    background: var(--ptb-success-bg);
    color: var(--ptb-success);
}
/* Matches .ptb-icon-btn-warning's amber (the same "deactivate" icon sits
   right next to this badge on Services/Passes rows) — per Jesse, an
   Inactive badge should read as the same color as the icon that caused it. */
.ptb-row-badge-warning {
    background: var(--ptb-warning-bg);
    color: var(--ptb-warning);
}
/* Icon-only variant (v9.63.0) — the "Recurring" badge on a client's next
   PT session (see ptb_render_my_booking_row()'s $is_recurring param).
   Same accent coloring as .ptb-row-badge-accent, just square padding sized
   for a 12px icon instead of uppercase text. */
.ptb-row-badge-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--ptb-accent-bg);
    color: var(--ptb-accent);
    padding: 4px;
}
.ptb-row-badge-icon .ptb-icon {
    width: 12px;
    height: 12px;
}

/* Step 2 — a flat chronological list of the next 10 upcoming sessions of
   the chosen type (ptb_query_next_sessions_for_type()), across all days —
   no date-by-date browsing, so each row carries its own date badge since
   there's no longer a single selected day giving that context. */
.ptb-time-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.ptb-time-row {
    display: flex;
    align-items: center;
    gap: 12px;
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 13px 16px;
    text-decoration: none;
    color: inherit;
}
.ptb-time-row-disabled {
    opacity: 0.55;
}
/* Shared by every date badge in the app (Book a Class time rows, Dashboard
   rows, My Schedule/PT Sessions rows, Client Detail rows) — one class, one
   rule, so they can't drift out of sync with each other again. Deliberate
   exception to the app-wide "no rounded corners" rule — Jesse asked for
   this one specifically as a solid circle. */
.ptb-date-badge {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 42px;
    height: 42px;
    padding: var(--ptb-icon-padding);
    border-radius: 50%;
    background: var(--ptb-navy);
    color: #fff;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}
.ptb-date-badge span {
    display: block;
    font-size: 0.62em;
    text-transform: uppercase;
    color: #fff;
    line-height: 1.2;
}
.ptb-date-badge strong {
    display: block;
    font-size: 1em;
    line-height: 1.2;
}
/* Time on the left, spots/status badge pushed to the far right of the
   same line — instructor name dropped entirely, per Jesse. */
.ptb-time-row-time {
    flex: 1 1 auto;
    font-weight: 700;
}

/* Step 3 — confirm summary + the phone field (the only actual form input
   in this step; the submit button itself lives in the sticky summary
   card via form="ptb-confirm-form", not duplicated here). */
.ptb-confirm-summary {
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 6px 18px;
    margin-bottom: 18px;
}
.ptb-confirm-row {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    padding: 12px 0;
    border-bottom: var(--ptb-divider-width) solid var(--ptb-border);
}
.ptb-confirm-row:last-child {
    border-bottom: 0;
}
.ptb-confirm-row span {
    color: var(--ptb-muted);
}
.ptb-confirm-form {
    margin-bottom: 4px;
}

/* Sticky right-hand summary card — present on all three steps. */
.ptb-book-flow-summary {
    position: static;
    order: -1;
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 18px;
}
.ptb-summary-brand {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-bottom: 14px;
    margin-bottom: 14px;
    border-bottom: var(--ptb-divider-width) solid var(--ptb-border);
}
.ptb-summary-brand .ptb-app-brand-icon {
    background: var(--ptb-navy);
    color: #fff;
    width: 38px;
    height: 38px;
    font-size: 0.8em;
}
.ptb-summary-line {
    padding-bottom: 14px;
    margin-bottom: 14px;
    border-bottom: var(--ptb-divider-width) solid var(--ptb-border);
}
.ptb-summary-line-title {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    font-weight: 700;
}
.ptb-summary-line-meta {
    color: var(--ptb-muted);
    font-size: 0.88em;
    margin-top: 4px;
}
.ptb-summary-total {
    display: flex;
    justify-content: space-between;
    font-weight: 700;
    margin-bottom: 16px;
}
.ptb-summary-cta {
    width: 100%;
    padding: 15px 20px;
}
.ptb-summary-hint {
    margin: 0;
    text-align: center;
}

/* ---------------------------------------------------------------------
   Sales-bump upsell banner (v9.61.0–v9.62.0) — client Home tab only,
   shown when they don't yet have an active Unlimited pass; disappears on
   its own once $subscription is truthy. Deliberately louder than the
   neutral .ptb-stat-card style (solid accent fill, not just an accent
   border) since the whole point is to catch the eye. .ptb-promo-banner is
   the outer flex row (background/padding live here); inside it,
   .ptb-promo-banner-form is display:contents (same reset
   .ptb-stat-card-form uses for the Manage Billing tile) so its child
   button lays out as a direct flex item of the wrapper, not boxed inside
   its own form; .ptb-promo-banner-btn is the actual clickable
   icon+text — submitting straight to Stripe Checkout — with native button
   chrome stripped; .ptb-promo-banner-close (v9.62.0) is a separate
   sibling button, not nested inside the submit button (two <button>s
   can't nest), for the dismiss "X". Sized small and kept to one
   line/nowrap deliberately (Jesse: "the banner is too tall - make
   smaller", was wrapping to 2 lines on a phone) — short single-line copy
   plus compact padding/icon keeps it well under a real phone's width.
   --------------------------------------------------------------------- */
.ptb-promo-banner {
    display: flex;
    align-items: center;
    gap: 6px;
    width: 100%;
    margin: 24px 0 0;
    padding: 10px 10px 10px 14px;
    /* v9.111.0 — was var(--ptb-accent) directly; this is the sales-bump/
       "go unlimited" banner's own colour now (client Home, shown while
       they have no active Unlimited pass), independently customizable from
       the cash-payment banner below. v9.113.0 — light tint bg + full-colour
       text, not solid fill + white (see :root comment above). */
    background: var(--ptb-banner-upsell-bg);
    color: var(--ptb-banner-upsell);
    border-radius: var(--ptb-radius);
}
/* "Cash payment client" banner (trainer's Client Detail view, v9.80.0) —
   reuses .ptb-promo-banner for layout but its own colour. */
.ptb-promo-banner-static {
    background: var(--ptb-banner-cash-bg);
    color: var(--ptb-banner-cash);
}
.ptb-promo-banner-form {
    display: contents;
}
.ptb-promo-banner-btn {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: 1;
    min-width: 0;
    background: none;
    color: inherit;
    border: 0;
    font: inherit;
    text-align: left;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    padding: 0;
}
/* v9.113.0 — was a translucent white circle, which only read correctly
   against the old solid-fill banner; now the banner itself is a light
   tint (see .ptb-promo-banner above), so the icon just sits directly on
   it in the same colour as the banner text — no separate chip needed. */
.ptb-promo-banner-icon {
    flex: none;
    display: flex;
    box-sizing: border-box;
    width: 26px;
    height: 26px;
    padding: var(--ptb-icon-padding);
    border-radius: var(--ptb-radius);
    align-items: center;
    justify-content: center;
    background: transparent;
}
.ptb-promo-banner-icon .ptb-icon {
    width: 15px;
    height: 15px;
}
.ptb-promo-banner-text {
    flex: 1;
    min-width: 0;
    font-weight: 600;
    font-size: 0.9em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ptb-promo-banner-close {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    background: none;
    color: inherit;
    border: 0;
    font-size: 1.1em;
    line-height: 1;
    opacity: 0.75;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}
.ptb-promo-banner-close:hover {
    opacity: 1;
}

/* ---------------------------------------------------------------------
   Dashboard — stat cards + two list cards (Today's Schedule / Upcoming
   Sessions), the instructor's default landing tab. Reused as-is (v9.56.0)
   by the client Home tab (ptb_render_client_home()) for its own stat cards
   — a 2-column grid works fine whether it's showing 2, 3, or 4 cards, no
   changes needed here for that reuse.
   --------------------------------------------------------------------- */
.ptb-stat-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 16px;
    margin: 24px 0;
}
/* v9.59.0, per Jesse: stack the stat cards to one per row on a real phone
   screen — two-up was cramping the number/label on narrow widths. This is
   the one deliberate exception to this file's usual "same layout at every
   width" rule (see body.ptb-app-page .ptb-booking's own comment) — that
   rule was about not needing a *separate desktop layout*, not about this
   specific grid working well all the way down to a phone's width, which it
   didn't. 480px catches actual phones without affecting tablets/desktop. */
@media (max-width: 480px) {
    .ptb-stat-grid {
        grid-template-columns: 1fr;
    }
}
.ptb-stat-card {
    display: block;
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 18px 20px;
}
.ptb-stat-card-link {
    text-decoration: none;
    color: inherit;
}
.ptb-stat-card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}
.ptb-stat-label {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--ptb-muted);
    font-size: 0.88em;
    font-weight: 600;
}
.ptb-stat-icon {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 36px;
    height: 36px;
    padding: var(--ptb-icon-padding);
    border-radius: var(--ptb-radius);
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ptb-accent-bg);
    color: var(--ptb-accent);
}
.ptb-stat-icon .ptb-icon {
    width: 18px;
    height: 18px;
}
.ptb-stat-icon-success {
    background: var(--ptb-success-bg);
    color: var(--ptb-success);
}
.ptb-stat-icon-warning {
    background: var(--ptb-warning-bg);
    color: var(--ptb-warning);
}
.ptb-stat-icon-billing {
    background: var(--ptb-icon-billing-bg);
    color: var(--ptb-icon-billing);
}
.ptb-stat-icon-todays-sessions {
    background: var(--ptb-icon-todays-sessions-bg);
    color: var(--ptb-icon-todays-sessions);
}
.ptb-stat-icon-next-pt {
    background: var(--ptb-icon-next-pt-bg);
    color: var(--ptb-icon-next-pt);
}
.ptb-stat-icon-next-class {
    background: var(--ptb-icon-next-class-bg);
    color: var(--ptb-icon-next-class);
}
.ptb-stat-value {
    font-size: 1.25em; /* was 2em then 1.5em, per Jesse: still too big */
    font-weight: 800;
    margin-top: 12px;
    line-height: 1;
}
.ptb-stat-sub {
    color: var(--ptb-muted);
    font-size: 0.85em;
    margin-top: 4px;
}
/* The Payments tab's "Manage Billing" tile (ptb_render_payments_tab()) —
   same .ptb-stat-card look as every other stat card, but it has to submit
   a real nonce-protected POST (Stripe's Billing Portal action), not just
   navigate to an href, so it's a <button> inside a <form> rather than an
   <a>. This resets the browser's default button chrome so .ptb-stat-card/
   .ptb-stat-card-link's own styling (already shared with the div/a variants)
   is the only thing that shows through. */
.ptb-stat-card-form {
    display: contents;
}
.ptb-stat-card-button {
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}

/* ---------------------------------------------------------------------
   Admin tab (v9.67.0) — Services list + Packs & Passes card. Named
   .ptb-svc-manage-* rather than reusing .ptb-service-row (the "Select a
   service" booking-wizard step's own row, includes/public.php around line
   760) — same-sounding but a structurally different row (a management row
   with edit/toggle/delete actions, not a clickable selectable option), so
   it gets its own classes to avoid inheriting that row's click/hover/
   selected-state styling.

   v9.76.0 — rows became individual grey-filled boxes (per Jesse: "each
   service pack needs to be in a grey filled box - like we do for
   sessions"), replacing the old flat divided-list look (border-bottom
   between rows, no background) — same grey fill treatment as the Payments
   tab's cards below, so the whole Admin/Payments area reads consistently. */
.ptb-svc-manage-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.ptb-svc-manage-row {
    display: flex;
    align-items: center;
    align-content: flex-start;
    flex-wrap: wrap;
    gap: 10px;
    padding: 14px 16px;
    /* v9.105.2 — was var(--ptb-bg) (grey), per an earlier ask; superseded
       now that Jesse wants every box white with the page itself carrying
       the grey ("all boxes have to be white"). */
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
}
.ptb-svc-manage-row-main {
    flex: 1 1 auto;
    min-width: 0;
}
/* Badges-above-name, same on every screen size (v9.99.0, per Jesse: "move
   the badges to the top in 1 line - not stack - follow by name and time
   etc - the delete buttons keep to the right - apply everywhere - apply
   for all views - this layout - desktop - mobile etc") — reuses the same
   .ptb-managed-row-badges component (already order:1, nowrap) the
   Schedule/Sessions rows use, so one rule keeps both in sync. */
.ptb-svc-manage-row-name {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}
.ptb-svc-manage-row-name-text {
    order: 2;
    font-weight: 600;
}
/* v9.99.6 — back to being a sibling of .ptb-svc-manage-row-main (not
   nested inside it) so it sits on the same line as the name/badges,
   vertically centered by .ptb-svc-manage-row's align-items:center — per
   Jesse: "move the icons in line with everything else." v9.99.5's "nest
   inside main" approach guaranteed the icons always dropped to their own
   line below the content, which solved the old stray-gap bug but wasn't
   actually what was wanted — the icons need to sit beside the row, not
   under it. flex: 0 0 auto keeps it at its natural width so it doesn't
   stretch or wrap unless the row genuinely runs out of horizontal room. */
.ptb-svc-manage-row-actions {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
    gap: 4px;
}
.ptb-btn-secondary {
    background: transparent;
    color: var(--ptb-muted);
    border: 1px solid var(--ptb-border);
}
.ptb-btn-secondary:hover {
    background: var(--ptb-bg);
    color: var(--ptb-text);
}
/* ---------------------------------------------------------------------
   Clients — a simple list, not a CRM: avatar, contact info, session
   counts. Reuses the same row-hover treatment as session/booking rows.
   --------------------------------------------------------------------- */
.ptb-client-list {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.ptb-client-row {
    display: flex;
    align-items: center;
    gap: 14px;
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 14px 18px;
    text-decoration: none;
    color: inherit;
}
.ptb-client-avatar {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 40px;
    height: 40px;
    padding: var(--ptb-icon-padding);
    border-radius: var(--ptb-radius);
    background: var(--ptb-navy);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.8em;
    font-weight: 700;
}
.ptb-client-avatar-lg {
    width: 56px;
    height: 56px;
    font-size: 1.1em;
}
.ptb-client-info {
    flex: 1 1 auto;
    min-width: 0;
}
.ptb-client-name {
    font-weight: 700;
}
/* Session count, opposite end from the avatar — same circle treatment,
   just an accent color instead of navy so it doesn't read as a second
   identity badge. Replaced the old "N sessions / 0 attended · last M j"
   text block, which squeezed onto one line badly at mobile widths. */
.ptb-client-count-badge {
    flex: 0 0 auto;
    box-sizing: border-box;
    width: 32px;
    height: 32px;
    padding: var(--ptb-icon-padding);
    border-radius: var(--ptb-radius);
    background: var(--ptb-accent-bg);
    color: var(--ptb-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.85em;
    font-weight: 700;
}
.ptb-client-row-chevron {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    color: var(--ptb-muted);
}
.ptb-client-detail-header {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-bottom: 24px;
}
/* v9.99.0 briefly moved this to the same "actions drop to their own row
   below" treatment as .ptb-managed-row/.ptb-svc-manage-row — wrong call,
   reverted in v9.99.3 per Jesse's screenshot: this header has no badges/
   time content above it, just avatar+name, so the icons belong beside it,
   vertically centered, same as before — not dropped underneath. */
.ptb-client-detail-actions {
    margin-left: auto;
    flex: 0 0 auto;
    display: flex;
    gap: 8px;
}
.ptb-client-bookings {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Shared trainer-facing forms (My Schedule / PT Sessions tabs) — layout
   only, appearance comes from the .ptb-btn/input rules above. */
.ptb-trainer-form {
    display: flex;
    gap: 8px;
    margin-top: 14px;
    flex-wrap: wrap;
    align-items: center;
}

/* My Bookings (client-facing) now reuses .ptb-managed-row (defined below,
   shared with My Schedule/PT Sessions/Client Detail) rather than its own
   card style — per Jesse, one consistent row shape everywhere a booking
   is listed. */
.ptb-attendance-status {
    padding: 4px 10px;
    border-radius: 0;
    font-size: 0.78em;
    font-weight: 700;
    white-space: nowrap;
    background: var(--ptb-bg);
    color: var(--ptb-muted);
}
.ptb-attendance-status-attended {
    background: var(--ptb-success-bg);
    color: var(--ptb-success);
}
.ptb-book-client-form {
    margin-top: 10px;
}

.ptb-schedule-form-card {
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 20px;
    margin-bottom: 24px;
}
/* The <p>/<label> wrappers around each field were shrink-to-fit by
   default (align-items: flex-start below never stretched them), so
   every field in this card rendered at its own narrow intrinsic width
   with the rest of the card sitting empty next to it — genuinely
   cramped and fiddly to tap on mobile, not just a visual quirk. Forcing
   the wrappers and the actual field to both go full width fixes that;
   Date and Time end up as their own full-width rows instead of trying
   (and failing, in a narrow card) to sit side by side — the right call
   for this "mobile view always" design system, not a compromise. */
.ptb-schedule-form-card .ptb-trainer-form p,
.ptb-schedule-form-card .ptb-trainer-form label {
    display: block;
    width: 100%;
}
.ptb-schedule-form-card .ptb-trainer-form input[type="date"],
.ptb-schedule-form-card .ptb-trainer-form input[type="time"],
.ptb-schedule-form-card .ptb-trainer-form input[type="text"],
.ptb-schedule-form-card .ptb-trainer-form input[type="tel"],
.ptb-schedule-form-card .ptb-trainer-form input[type="email"],
.ptb-schedule-form-card .ptb-trainer-form select,
.ptb-schedule-form-card .ptb-trainer-form textarea {
    display: block;
    width: 100%;
    box-sizing: border-box;
    margin-top: 4px;
    padding: 13px 14px;
    font-size: 1.05em;
}
/* The tiny native up/down step arrows on a time input are fiddly to tap
   precisely and aren't how anyone actually sets a time anyway (they tap
   the field and use the real picker) — hidden rather than left as a
   source of mis-taps. Harmless where unsupported (Firefox doesn't show
   these at all to begin with). */
.ptb-schedule-form-card .ptb-trainer-form input[type="time"]::-webkit-inner-spin-button {
    display: none;
}
/* Day/Month/Year + Hour/Minute/AM-PM dropdowns (ptb_render_date_time_
   dropdowns(), includes/public.php) — replaced the native date/time
   inputs entirely, since their actual popup picker is OS/browser-
   rendered with no CSS control at all. Two groups side by side (wrap to
   stacked on narrow screens), each group's own 3 selects sitting in a
   compact row rather than each stacking full-width like a lone select
   normally would in this form. */
.ptb-date-time-dropdowns {
    display: flex;
    flex-direction: column;
    gap: 16px;
    width: 100%;
    margin: 10px 0;
}
/* Always stacked (Date above Time), not just on narrow screens — was
   side-by-side on desktop (two 200px-min groups fit next to each other
   on a wide viewport) until Jesse asked for it to match mobile always,
   consistent with this app's own "mobile view everywhere, no separate
   desktop layout" design system. */
.ptb-dtd-group {
    width: 100%;
}
.ptb-dtd-label {
    display: block;
    font-weight: 600;
    margin-bottom: 4px;
    color: var(--ptb-text);
}
.ptb-dtd-row {
    display: flex;
    gap: 8px;
}
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-row select {
    display: block;
    width: auto;
    flex: 1 1 0;
    min-width: 0;
    box-sizing: border-box;
    margin-top: 0;
    padding: 13px 6px;
    font-size: 1em;
}

/* ---------------------------------------------------------------------
   Custom calendar + time picker (assets/scheduler-picker.js) — a real
   clickable month grid and hour/minute/AM-PM pill buttons, replacing the
   Day/Month/Year + Hour/Minute/AM-PM <select>s above visually, per Jesse:
   "how hard would it be to build our own?" Progressive enhancement: the
   raw <select>s just above are still the actual form fields (JS keeps
   their .value in sync with every click) and are the entire UI if JS
   fails to load or run — .ptb-dtd-js-enhanced/-time (added by JS once it
   confirms it's running) is what hides them and shows this instead, so
   there's no "half old, half new" state possible, only one or the other.
   --------------------------------------------------------------------- */
/* .ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-row select (above)
   is (0,3,1) specificity — higher than a bare .ptb-dtd-js-enhanced
   select[name="..."] rule (0,2,1) would be, so it'd win and keep forcing
   display:block regardless of the JS-added class. Matching the same
   ancestor chain here isn't decoration, it's what actually makes this
   rule win — found by checking real specificity after the raw <select>s
   showed up still visible next to the calendar in Jesse's screenshot. */
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-js-enhanced select[name="ptb_day"],
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-js-enhanced select[name="ptb_month"],
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-js-enhanced select[name="ptb_year"] {
    display: none;
}
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-js-enhanced-time select[name="ptb_hour"],
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-js-enhanced-time select[name="ptb_minute"],
.ptb-schedule-form-card .ptb-trainer-form .ptb-dtd-js-enhanced-time select[name="ptb_ampm"] {
    display: none;
}
/* max-width is the real fix here, not decoration — .ptb-cal-day is
   aspect-ratio:1/1, so its height always matches its (grid-column-
   derived) width. Uncapped, a wide desktop window gives each of the 7
   columns 100+px of width, and the aspect-ratio then makes every cell
   grow just as tall — the whole calendar balloons into giant squares
   instead of staying a normal compact size. Capping the container's own
   width keeps cell size sane at any window width; still 100% (shrinks
   fully) below that cap, so phones are unaffected. */
.ptb-cal {
    background: var(--ptb-card);
    padding: 10px;
    width: 100%;
    max-width: 360px;
    box-sizing: border-box;
    border-radius: var(--ptb-radius);
}
.ptb-cal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 6px;
    margin-bottom: 8px;
}
/* min-width:0 is the actual fix, not decoration — a flex item's default
   min-width is "auto" (its content's natural size), so without this the
   month text refuses to shrink and forces the whole header wider than
   its container on a narrow window, instead of the text just truncating.
   Found by resizing the browser down narrower than any real phone gets,
   per Jesse. */
.ptb-cal-month-label {
    font-weight: 600;
    font-size: 0.95em;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ptb-cal-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--ptb-border);
    background: var(--ptb-bg);
    color: var(--ptb-text);
    cursor: pointer;
}
.ptb-cal-nav:disabled {
    opacity: 0.35;
    cursor: default;
}
.ptb-cal-nav .ptb-icon {
    width: 15px;
    height: 15px;
}
.ptb-cal-weekdays,
.ptb-cal-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 4px;
}
.ptb-cal-weekdays {
    margin-bottom: 4px;
}
.ptb-cal-weekdays span {
    text-align: center;
    font-size: 0.72em;
    font-weight: 600;
    color: var(--ptb-muted);
    padding: 4px 0;
}
.ptb-cal-day,
.ptb-cal-day-blank {
    aspect-ratio: 1 / 1;
}
.ptb-cal-day {
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid transparent;
    background: transparent;
    color: var(--ptb-text);
    font-size: 0.88em;
    font-weight: 500;
    cursor: pointer;
}
.ptb-cal-day:hover {
    border-color: var(--ptb-border);
}
.ptb-cal-day-today {
    font-weight: 700;
    color: var(--ptb-navy);
}
.ptb-cal-day-selected {
    background: var(--ptb-navy);
    border-color: var(--ptb-navy);
    color: #fff;
}
.ptb-time-picker {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.ptb-time-picker-hours {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 6px;
}
/* 4 options (00/15/30/45), not 12 — its own column count, not shared
   with .ptb-time-picker-hours's 6, so 4 items don't leave two empty grid
   slots in the row. */
.ptb-time-picker-minutes {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
    max-width: 260px;
}
.ptb-time-picker-ampm {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 6px;
    max-width: 200px;
}
.ptb-time-pill {
    padding: 8px 4px;
    border: 1px solid var(--ptb-border);
    background: var(--ptb-bg);
    color: var(--ptb-text);
    font-weight: 600;
    font-size: 0.85em;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.ptb-time-pill-active {
    background: var(--ptb-navy);
    border-color: var(--ptb-navy);
    color: #fff;
}
.ptb-time-picker-label {
    font-size: 0.78em;
    font-weight: 600;
    color: var(--ptb-muted);
    margin-bottom: 4px;
    display: block;
}

.ptb-schedule-form-card .ptb-trainer-form {
    flex-direction: column;
    align-items: flex-start;
}
/* Add Client's fields specifically go full width — mobile-friendly single-
   column entry, no side-by-side fields the way the Date/Time pair in the
   Schedule form above still deliberately has. */
.ptb-add-client-form {
    width: 100%;
}
.ptb-add-client-form p {
    width: 100%;
    margin: 0 0 12px;
}
.ptb-add-client-form label {
    display: block;
    width: 100%;
}
.ptb-add-client-form input {
    width: 100%;
    box-sizing: border-box;
}
.ptb-add-client-form button {
    width: 100%;
}
/* Weekday toggle pills (Mon-Sun, "Repeat weekly on") — real checkboxes
   underneath, same "hide the control, style its label" technique as the
   sidebar tab radios (.ptb-tab-radio) elsewhere in this file, just
   scoped locally. Replaced the plain inline checkbox+text row Jesse
   called "archaic." */
/* Full-width row, all 7 pills evenly filling the space (was left-aligned
   chips that could wrap unevenly) — per Jesse asking for this "full
   width." Stays 7 real checkboxes (multi-day selection, e.g. Mon+Wed+Fri
   for one class), not a single <select> — a dropdown can only carry one
   value, and this needs to allow more than one day at once. */
/* .ptb-schedule-form-card .ptb-trainer-form p (above) is (0,2,1) — beats
   a bare .ptb-weekday-checks class rule (0,1,0), forcing display:block
   and stacking every weekday pill as its own full-width row (exactly
   what showed up in Jesse's screenshot as thick horizontal bars). Same
   fix as the select-hiding rule above: match the ancestor chain so this
   rule's specificity actually wins.
   A single-row flex (all 7 in one line, each shrinking to fit) still
   didn't fit on a narrow phone even with single-letter labels — per
   Jesse: "put on 2 lines." Switched to a 4-column grid instead: 7 items
   auto-wrap into 4 + 3 across two rows, and each pill's width comes from
   its grid column (1fr) rather than flex-shrink fighting a min-width, so
   there's no longer a floor width that can force an overflow. */
.ptb-schedule-form-card .ptb-trainer-form .ptb-weekday-checks {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
    width: 100%;
}
.ptb-weekday-pill {
    margin: 0;
}
.ptb-weekday-pill input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.ptb-weekday-pill span {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: 9px 10px;
    border: 1px solid var(--ptb-border);
    background: var(--ptb-bg);
    color: var(--ptb-text);
    font-weight: 600;
    font-size: 0.85em;
    cursor: pointer;
    user-select: none;
    box-sizing: border-box;
    transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.ptb-weekday-pill input:checked + span {
    background: var(--ptb-navy);
    border-color: var(--ptb-navy);
    color: #fff;
}
.ptb-weekday-pill input:focus-visible + span {
    outline: 2px solid var(--ptb-accent);
    outline-offset: 2px;
}

/* ---------------------------------------------------------------------
   Session manager (My Schedule / PT Sessions) — header with a collapsible
   "+ Schedule" toggle, an Upcoming/Past/Cancelled sub-nav, and rows. The
   toggle and sub-nav are both the same pure-CSS checked-input + label
   technique used for the outer sidebar tabs, just scoped locally so two
   instances of this component (group + PT) can exist on the page at once
   without their radios/checkboxes colliding — see the class-based (not
   id-based) :checked selectors below, which work for both instances.
   --------------------------------------------------------------------- */
.ptb-session-manager-header {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-bottom: 20px;
}
.ptb-toggle-checkbox {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.ptb-schedule-toggle-btn {
    flex: 0 0 auto;
}
/* Schedule tab's header now has two buttons ("+ Add Class" and
   "+ Schedule Session") instead of one — its own small flex row so they
   sit side by side and wrap together as a pair on narrow screens. */
.ptb-schedule-header-actions {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
}
.ptb-schedule-form-reveal {
    display: none;
}
.ptb-toggle-checkbox:checked ~ .ptb-schedule-form-reveal {
    display: block;
}
/* Schedule tab's own "+ Schedule Session" reveal — used to be one of two
   independent toggle+reveal pairs sharing this header ("+ Add Service"
   was the other, needing its own ID-scoped rule so checking one didn't
   reveal both) until v9.67.0 moved Add Service to the Admin tab, leaving
   just this one pair — could fall back to the generic .ptb-toggle-
   checkbox/.ptb-schedule-form-reveal rule above now, but keeping its own
   class/ID here avoids touching the working markup for no functional
   gain. */
.ptb-schedule-session-form-reveal {
    display: none;
}
#ptb_toggle_schedule:checked ~ .ptb-schedule-session-form-reveal {
    display: block;
}
/* Client Detail's own "Edit" toggle — kept ID-scoped rather than falling
   back to the generic .ptb-toggle-checkbox/.ptb-schedule-form-reveal pair
   (v9.80.0 removed the "Record payment" toggle this used to sit alongside,
   which is why this class/rule existed on its own in the first place). */
.ptb-edit-client-form-reveal {
    display: none;
}
#ptb_toggle_edit_client:checked ~ .ptb-edit-client-form-reveal {
    display: block;
}
/* Payments tab (v9.50.0) — Buy / Subscribe cards. v9.74.0, per Jesse
   ("2 per row - not 3"): a fixed 2-column grid, same pattern as
   .ptb-stat-grid above — now that Packs & Passes can have any number of
   tiers (not just one pack + one Unlimited), a flex-wrap layout let 3+
   narrow cards crowd onto one row on wide screens; a real 2-column grid
   caps it regardless of how many tiers exist.

   v9.76.0 — grey-filled cards (per Jesse: "they all need to be grey boxes
   like above [.ptb-svc-manage-row] - the same size too"), and every card
   is now display:flex column with its button pushed to margin-top:auto —
   Grid's default align-items:stretch already equalizes height within a
   row, and pinning the button to the bottom keeps it level across cards
   even when one has more hint text than another. */
.ptb-payment-options {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 14px;
    align-items: stretch;
}
@media (max-width: 480px) {
    .ptb-payment-options {
        grid-template-columns: 1fr;
    }
}
.ptb-payment-option {
    display: flex;
    flex-direction: column;
    /* v9.105.2 — was var(--ptb-bg) (grey); every box is white now. */
    background: var(--ptb-card);
    padding: 20px;
    border-radius: var(--ptb-radius);
}
.ptb-payment-option-title {
    font-weight: 700;
    margin-bottom: 4px;
}
.ptb-payment-option-hint {
    color: var(--ptb-muted);
    font-size: 0.88em;
    margin-bottom: 6px;
}
.ptb-payment-option-price {
    font-weight: 700;
    margin-bottom: 14px;
}
.ptb-payment-option form {
    margin: 0;
    margin-top: auto;
}
.ptb-payment-option .ptb-btn {
    width: 100%;
}
.ptb-subnav {
    display: flex;
    gap: 22px;
    border-bottom: var(--ptb-divider-width) solid var(--ptb-border);
    margin-bottom: 18px;
}
.ptb-subnav label {
    padding: 4px 0 12px;
    color: var(--ptb-muted);
    font-weight: 600;
    font-size: 0.88em;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    /* v9.129.0 — was -1px; per Jesse, needed to be -2px to actually
       overlap and cover .ptb-subnav's own border-bottom line. */
    margin-bottom: -2px;
}
/* v9.127.0 — no hover feedback existed at all before this; per Jesse:
   "when hover over a tab it covers the whole part of that line."
   border-bottom already spans a label's full width by definition (it's
   not possible for it to cover only part of one), so this is really
   about there being no hover state to see in the first place — added
   one, same full-width underline treatment the active tab gets. */
.ptb-subnav label:hover {
    color: var(--ptb-text);
    border-bottom-color: var(--ptb-text);
}
.ptb-filter-panel {
    display: none;
}
.ptb-filter-payments:checked ~ .ptb-subnav label[for$="_payments"],
.ptb-filter-classes:checked ~ .ptb-subnav label[for$="_classes"],
.ptb-filter-pt:checked ~ .ptb-subnav label[for$="_pt"],
.ptb-filter-unpaid:checked ~ .ptb-subnav label[for$="_unpaid"],
.ptb-filter-admin-services:checked ~ .ptb-subnav label[for$="_services"],
.ptb-filter-admin-passes:checked ~ .ptb-subnav label[for$="_passes"],
.ptb-filter-admin-currency:checked ~ .ptb-subnav label[for$="_currency"],
.ptb-filter-admin-sms:checked ~ .ptb-subnav label[for$="_sms"] {
    color: var(--ptb-text);
    border-bottom-color: var(--ptb-text);
}
.ptb-filter-payments:checked ~ .ptb-filter-panel-payments,
.ptb-filter-classes:checked ~ .ptb-filter-panel-classes,
.ptb-filter-pt:checked ~ .ptb-filter-panel-pt,
.ptb-filter-unpaid:checked ~ .ptb-filter-panel-unpaid,
/* v9.75.0 — Admin tab's Services/Packs & Passes subnav (per Jesse: "the
   admin page looks messy... maybe we put Services / Packs"), same
   radio+label+panel idiom as every other subnav above. v9.77.0 added
   Currency & GST and SMS Reminders as two more tabs in the same row (per
   Jesse: "GST / Currency needs to sit here... Also add another called SMS
   reminders"). */
.ptb-filter-admin-services:checked ~ .ptb-filter-panel-admin-services,
.ptb-filter-admin-passes:checked ~ .ptb-filter-panel-admin-passes,
.ptb-filter-admin-currency:checked ~ .ptb-filter-panel-admin-currency,
.ptb-filter-admin-sms:checked ~ .ptb-filter-panel-admin-sms {
    display: block;
}

/* align-items: center (was flex-start, v9.63.4) — per Jesse: "the circle
   date and bin need to be centred with the other information." Vertically
   centers the date-badge circle and the cancel/trash button against the
   two-line title+meta content block instead of pinning them to the row's
   top edge. Shared by every list in the app that uses this row shape
   (client Home/Sessions, trainer Dashboard/Schedule/Client Detail). */
.ptb-managed-row {
    display: flex;
    align-items: center;
    align-content: flex-start;
    flex-wrap: wrap;
    gap: 16px;
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: var(--ptb-shadow);
    padding: 16px 18px;
    margin-bottom: 12px;
    /* v9.101.0 — matches .ptb-service-row's min-height (same 42px date
       circle + 16px*2 padding math) so every row across trainer and
       client views lands on the same height, per Jesse: "I want all
       these boxes to be the same height." Now that content is a plain
       2-line name+meta (no badge wall), it's close to this height on its
       own — this is just the guarantee, not doing the heavy lifting. */
    min-height: 74px;
}
.ptb-managed-row-main {
    flex: 1 1 auto;
    min-width: 0;
}
/* v9.101.0 — back to plain name-first-then-meta, per Jesse's reference
   screenshot (bold title, one muted icon+text line underneath, no badge
   wall). .ptb-managed-row-badges (order:1 trick) is no longer used by any
   of this title's own callers — it's kept defined below only because
   .ptb-svc-manage-row-name (Services/Passes rows, a different title
   component entirely) still reuses that exact class. */
.ptb-managed-row-title {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 4px;
    min-width: 0;
}
.ptb-managed-row-name {
    padding: 2px 0;
    font-weight: 700;
    font-size: 1.05em;
}
/* Still used by .ptb-svc-manage-row-name (Services/Passes rows) — see
   comment above. */
.ptb-managed-row-badges {
    order: 1;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 8px;
    max-width: 100%;
    overflow-x: auto;
}
.ptb-managed-row-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    color: var(--ptb-muted);
    font-size: 0.88em;
}
.ptb-managed-row-meta .ptb-icon {
    width: 14px;
    height: 14px;
    flex: 0 0 auto;
}
/* v9.99.6 — a sibling of .ptb-managed-row-main again (not nested inside
   it), same reasoning as .ptb-svc-manage-row-actions above: Jesse wants
   the delete icon in line with the rest of the row, vertically centered,
   not dropped underneath it. flex: 0 0 auto sizes it to its own content
   so it sits at the end of the row without stretching or forcing a wrap.
   This also happens to resolve ptb_render_managed_session_row()'s
   whole-row-clickable rows the same way — that form was already a sibling
   of the click-wrapper (had to be, a <form>/<button> can't nest inside
   another <button>), so it gets the same inline placement for free now
   instead of needing the flex-wrap-onto-its-own-line trick v9.99.4 relied
   on. */
.ptb-managed-row-cancel {
    flex: 0 0 auto;
    display: flex;
}
.ptb-managed-row-cancel + .ptb-managed-row-cancel {
    margin-left: 4px;
}
/* v9.102.0 — no longer used on the trainer's Schedule row (that colour
   moved to the client's own Unpaid tab, see ptb_render_client_detail()/
   ptb_render_client_unpaid_row() below) — light orange whole-row tint,
   left accent border on top of it since a subtle background alone reads
   too faintly at a glance across a list. */
.ptb-managed-row-unpaid {
    background: var(--ptb-warning-bg);
    border-left: 4px solid var(--ptb-warning);
}
/* v9.81.0 — the whole row is the click target now (PT: mark as paid when
   unpaid; class: toggle the roster), replacing v9.79.0/v9.80.0's smaller
   in-row badge/label triggers, per Jesse: "the whole block should be
   clickable... you just click the block. and question is asked." Also
   removed the PT row's separate "Charge card on file" link entirely
   rather than just repositioning it — that link had no manual-billing
   check, so a cash-only client with a stray stripe_customer_id on file
   could still see a card-charge option there (the actual bug reported:
   "cash payment customer is has for card to be charged - not correct").
   .ptb-managed-row-click-form is display:contents (same technique
   .ptb-promo-banner-form already uses elsewhere) so its one child — the
   button — becomes the real flex item of .ptb-managed-row, not the form
   itself; .ptb-managed-row-click is that button (PT) or the roster-toggle
   label (class) — same look either way, replicating exactly what
   .ptb-date-badge + .ptb-managed-row-main looked like as direct flex
   children before this wrapper existed. */
.ptb-managed-row-clickable {
    cursor: pointer;
}
.ptb-managed-row-click-form {
    display: contents;
}
.ptb-managed-row-click {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: 1 1 auto;
    min-width: 0;
    text-align: left;
    text-decoration: none;
    color: inherit;
    /* v9.103.2 — the unpaid-PT row (v9.102.0) is a plain <a> here, not a
       <button>, so it never got button.ptb-managed-row-click's font:
       inherit reset below — left it picking up the theme's default bold
       link styling, which is why an unpaid row's meta line rendered bold
       while every other row (plain <div>, no <a>) didn't. */
    font-weight: normal;
}
button.ptb-managed-row-click {
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    font: inherit;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
}
.ptb-roster-reveal {
    display: none;
    flex: 1 1 100%;
    margin-top: 10px;
    padding-top: 10px;
    border-top: var(--ptb-divider-width) solid var(--ptb-border);
}
.ptb-toggle-checkbox:checked ~ .ptb-roster-reveal {
    display: block;
}
/* Roster attendee boxes (v9.79.0, per Jesse: "when clicked it shows all
   the clients box by box stacked"). Every attendee is a plain link to
   their own client profile (an unpaid one lands on that client's Unpaid
   tab specifically), same treatment the Schedule PT row above gets.
   v9.103.4 dropped the green/orange tint entirely; v9.103.6 brought it
   back per Jesse: "change each client background colour - Green paid -
   Orange not paid - no need to say unpaid or paid - its visual" — colour
   only, no status text this time, and no email either. */
.ptb-roster-box {
    display: block;
    width: 100%;
    box-sizing: border-box;
    text-align: left;
    text-decoration: none;
    background: var(--ptb-bg);
    border: none;
    border-radius: var(--ptb-radius);
    padding: 14px 16px;
    margin-bottom: 8px;
    font: inherit;
    color: inherit;
}
.ptb-roster-box:last-child {
    margin-bottom: 0;
}
.ptb-roster-box-paid {
    background: var(--ptb-success-bg);
}
.ptb-roster-box-unpaid {
    background: var(--ptb-warning-bg);
}
.ptb-roster-box-name {
    display: block;
    font-weight: 700;
}

.ptb-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    width: 36px;
    height: 36px;
    padding: var(--ptb-icon-padding);
    border-radius: var(--ptb-radius);
    border: none;
    background: transparent;
    color: var(--ptb-muted);
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}
.ptb-icon-btn:hover {
    background: var(--ptb-bg);
}
.ptb-icon-btn .ptb-icon {
    width: 16px;
    height: 16px;
}
/* Persistent color-coded boxes (v9.93.0, per Jesse: "for edit we need a
   green box - for pause we need an amber box... consistent across
   everything where there is an icon") — edit/pause/delete always show
   their tint now, not just on hover, so the action reads at a glance.
   v9.112.0 briefly inverted this to solid-bg/white-icon, then reverted in
   v9.113.0 per Jesse: "undo the revert for delete buttons" — back to the
   tint-bg/coloured-icon look. */
.ptb-icon-btn-edit {
    background: var(--ptb-success-bg);
    color: var(--ptb-success);
}
.ptb-icon-btn-warning {
    background: var(--ptb-warning-bg);
    color: var(--ptb-warning);
}
.ptb-icon-btn-danger {
    background: var(--ptb-danger-bg);
    color: var(--ptb-danger);
}
.ptb-icon-btn-edit:hover,
.ptb-icon-btn-warning:hover,
.ptb-icon-btn-danger:hover {
    filter: brightness(0.95);
}

.ptb-cancel-page {
    padding: 40px 0;
}

/* ---------------------------------------------------------------------
   Admin screens — a lighter touch than the client UI, since these should
   still read as native wp-admin, just tidier: consistent cards, clearer
   spacing, a colored left border matching the client-side class cards.
   --------------------------------------------------------------------- */
.ptb-admin-card {
    background: #fff;
    border: 1px solid #dcdcde;
    border-left: 3px solid var(--ptb-blue);
    border-radius: var(--ptb-radius);
    padding: 18px 22px;
    margin-bottom: 20px;
    max-width: 480px;
    box-shadow: none;
}
.ptb-admin-card h2 {
    margin-top: 0;
}
.ptb-dot {
    display: inline-block;
    width: 10px;
    height: 10px;
    border-radius: 0;
    margin-right: 6px;
    vertical-align: middle;
}

/* Account menu (v9.143.0) — cog that reveals a small "Log out" dropdown.
   v9.150.0 moved it from a viewport-fixed top-right corner (shown on
   every tab) into the Home heading row specifically (both trainer
   Dashboard and client Home render this same row) — see
   .ptb-home-heading-row below. */
.ptb-home-heading-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}
.ptb-home-heading-row .ptb-step-heading {
    margin: 0;
}
.ptb-account-menu-toggle {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.ptb-account-menu {
    position: relative;
    z-index: 5;
}
.ptb-account-menu-trigger {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--ptb-card);
    /* v9.153.0 — was var(--ptb-muted) (plain grey); per Jesse: "the icon
       for settings needs to inherit the customizer Primary button
       colour." */
    color: var(--ptb-navy);
    box-shadow: 0 1px 3px rgba(16, 24, 40, 0.12);
    cursor: pointer;
}
.ptb-account-menu-trigger .ptb-icon {
    width: 18px;
    height: 18px;
}
.ptb-account-menu-panel {
    display: none;
    position: absolute;
    top: 44px;
    right: 0;
    min-width: 150px;
    background: var(--ptb-card);
    border-radius: var(--ptb-radius);
    box-shadow: 0 4px 16px rgba(16, 24, 40, 0.16);
    overflow: hidden;
}
.ptb-account-menu-toggle:checked ~ .ptb-account-menu .ptb-account-menu-panel {
    display: block;
}
.ptb-account-menu-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 12px 14px;
    color: var(--ptb-danger);
    text-decoration: none;
    font-size: 0.95em;
    font-weight: 600;
}
.ptb-account-menu-item .ptb-icon {
    width: 18px;
    height: 18px;
}
