/* ============================================================
   DanceYourLife — Public Calendar Styles
   Self-contained. No dependency on admin.css.
   Uses CSS custom properties for studio theming.
   ============================================================ */

/* --- Custom Properties --- */
:root {
    --pcal-primary: #6f2a81;
    --pcal-primary-hover: #5a2269;
    --pcal-primary-light: #f3e8f9;
    --pcal-success: #059669;
    --pcal-success-light: #ecfdf5;
    --pcal-warning: #d97706;
    --pcal-warning-light: #fffbeb;
    --pcal-danger: #dc2626;
    --pcal-danger-light: #fef2f2;
    --pcal-info: #0284c7;
    --pcal-text: #1e293b;
    --pcal-text-muted: #64748b;
    --pcal-border: #e2e8f0;
    --pcal-bg: #f8fafc;
    --pcal-surface: #ffffff;
    --pcal-radius: 8px;
    --pcal-radius-lg: 12px;
    --pcal-shadow: 0 1px 3px rgba(0,0,0,.08), 0 1px 2px rgba(0,0,0,.06);
    --pcal-shadow-lg: 0 10px 25px rgba(0,0,0,.12);
    --pcal-font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    --pcal-transition: 150ms ease;

    /* Weekly-grid surfaces and status pills.
       On a DARK studio theme the defaults below collapse: --pcal-surface is both the
       panel and the card, so a slot sits on its own colour (measured 1.00:1 on the noir
       template), and the *-light status fills are near-white blobs on a near-black card.
       PublicLayout overrides every token in this group from the same luminance test that
       already picks the hero mode — see "dark calendar surfaces" there. The values here
       are the light-theme behaviour the grid has always had, so light themes are
       byte-identical to before. */
    --pcal-grid-panel: var(--pcal-surface);
    --pcal-slot-bg: var(--pcal-surface);
    --pcal-slot-border: var(--pcal-border);
    --pcal-slot-shadow: none;
    --pcal-accent-lift: 0%;
    --pcal-badge-ok-bg: var(--pcal-success-light);
    --pcal-badge-ok-fg: var(--pcal-success);
    --pcal-badge-warn-bg: var(--pcal-warning-light);
    --pcal-badge-warn-fg: var(--pcal-warning);
    --pcal-badge-danger-bg: var(--pcal-danger-light);
    --pcal-badge-danger-fg: var(--pcal-danger);
    --pcal-today-bg: var(--pcal-primary);
    --pcal-today-border: var(--pcal-primary);
    --pcal-today-shadow: inset 0 -3px 0 rgba(0,0,0,.12);
    --pcal-today-name: rgba(255,255,255,.85);
    --pcal-today-date: #fff;
    --pcal-evt-badge-mix: 100%;
    --pcal-evt-badge-fg: #fff;

    /* Form controls, inset panels and callouts.
       Same contract as the weekly-grid group above: the values here ARE the light-theme
       behaviour these surfaces have always had (white fields, slate text, the pastel
       *-50 callout fills), so light studios render byte-identical. PublicLayout replaces
       every one of them from its existing luminance test, expressed as color-mix against
       the studio's own --pcal-text / --pcal-surface, so a dark studio gets fields and
       callouts in its OWN palette instead of white boxes punched into a dark page. */
    --pcal-field-bg: #ffffff;
    --pcal-field-text: #1e293b;
    --pcal-field-border: var(--pcal-border);
    --pcal-field-placeholder: #94a3b8;
    --pcal-field-ring: rgba(111,42,129,.12);
    --pcal-label-color: #374151;
    --pcal-inset: #f8fafc;
    --pcal-row-bg: #ffffff;
    --pcal-note-bg: #eef2ff;
    --pcal-ok-bg: #ecfdf5;
    --pcal-ok-border: #a7f3d0;
    --pcal-ok-fg: #065f46;
    --pcal-info-bg: #eff6ff;
    --pcal-info-border: #bfdbfe;
    --pcal-info-fg: #1e40af;
    --pcal-warn-chip-bg: #fef3c7;
    --pcal-warn-chip-fg: #b45309;
    --pcal-err-bg: #fef2f2;
    --pcal-err-fg: #dc2626;
}

/* --- Page Wrappers --- */
.pcal-page-wrapper {
    font-family: var(--pcal-font);
    color: var(--pcal-text);
    background: var(--pcal-bg);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* --- Admin-configured page header --- */
.pcal-page-header {
    text-align: center;
    margin: 0 auto 20px;
    max-width: 720px;
    padding: 0 16px;
}
.pcal-page-title {
    font-size: clamp(1.6rem, 3vw, 2.2rem);
    font-weight: 800;
    margin: 0 0 6px;
    color: var(--pcal-text);
}
.pcal-page-subtitle {
    font-size: .95rem;
    color: var(--pcal-text-muted);
    margin: 0;
}

/* --- Day view --- */
.pcal-day-single {
    background: var(--pcal-surface);
    border: 1px solid var(--pcal-border);
    border-radius: var(--pcal-radius-lg);
    padding: 16px;
}
.pcal-day-stack { display: flex; flex-direction: column; gap: 10px; }
.pcal-no-slots-day { padding: 32px 16px; }

/* --- List view (horizontal rows) --- */
.pcal-list-body {
    display: flex;
    flex-direction: column;
    gap: 24px;
    padding: 20px 24px 24px;
}
@media (max-width: 640px) {
    .pcal-list-body { padding: 16px; }
}

.pcal-list-section { display: flex; flex-direction: column; gap: 10px; }
.pcal-list-section-title {
    margin: 0;
    font-size: 12px;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: var(--pcal-text-muted);
    display: flex;
    align-items: center;
    gap: 8px;
}
.pcal-list-section.pcal-today .pcal-list-section-title { color: var(--pcal-primary); }
.pcal-list-today-tag { color: var(--pcal-primary); }
.pcal-list-section-sep { opacity: .6; }

.pcal-list-rows { display: flex; flex-direction: column; gap: 10px; }

.pcal-list-row {
    display: grid;
    grid-template-columns: 88px 1fr auto;
    align-items: center;
    gap: 16px;
    padding: 14px 18px;
    background: var(--pcal-surface);
    border: 1px solid var(--pcal-border);
    border-radius: 14px;
    cursor: pointer;
    transition: border-color var(--pcal-transition), box-shadow var(--pcal-transition), transform var(--pcal-transition);
}
.pcal-list-row:hover {
    border-color: var(--pcal-primary);
    box-shadow: 0 6px 18px rgba(0,0,0,.06);
}
.pcal-list-row-full { opacity: .65; cursor: default; }
.pcal-list-row-full:hover { border-color: var(--pcal-border); box-shadow: none; }
.pcal-list-row-booked { border-color: var(--pcal-success); }

.pcal-list-row-time {
    font-size: 15px;
    font-weight: 700;
    color: var(--pcal-text);
    letter-spacing: .01em;
}
.pcal-list-row-main { min-width: 0; }
/* List view rows can grow vertically — wrap long titles / meta instead of truncating
   with ellipsis. overflow-wrap:anywhere is the safety belt: a single very long word
   (URL pasted in a class name, long no-space string) still gets broken so the row
   never blows out the card width. Studios with long class names like "Pole Dance –
   All Levels (Bring a Friend Night)" now get 2 lines instead of an unreadable
   "Pole Dance – All Le…". */
.pcal-list-row-name {
    font-size: 15px;
    font-weight: 700;
    color: var(--pcal-text);
    margin-bottom: 2px;
    overflow-wrap: anywhere;
    word-break: break-word;
}
.pcal-list-row-meta {
    font-size: 13px;
    color: var(--pcal-text-muted);
    overflow-wrap: anywhere;
    word-break: break-word;
}

.pcal-list-row-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-shrink: 0;
}
.pcal-list-status {
    display: inline-block;
    padding: 6px 14px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
    background: var(--pcal-primary-light);
    color: var(--pcal-primary);
}
.pcal-list-status-full { background: var(--pcal-danger-light); color: var(--pcal-danger); }
/* Last seat — urgent, but still bookable (solid danger to read as scarcity, not sold-out). */
.pcal-list-status-last { background: var(--pcal-danger); color: #fff; font-weight: 700; }

.pcal-list-book {
    appearance: none;
    border: 0;
    background: var(--pcal-primary);
    color: #fff;
    padding: 9px 22px;
    border-radius: 999px;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
    transition: background var(--pcal-transition), transform var(--pcal-transition);
}
.pcal-list-book:hover:not(:disabled) { background: var(--pcal-primary-hover); }
.pcal-list-book:disabled { background: var(--pcal-border); color: var(--pcal-text-muted); cursor: default; }

.pcal-list-book-checked,
.pcal-list-book-checked:disabled {
    background: var(--pcal-success, #059669);
    color: #fff;
    cursor: default;
}
.pcal-list-book-checked:hover:not(:disabled) { background: var(--pcal-success, #059669); }

@media (max-width: 640px) {
    .pcal-list-row {
        grid-template-columns: 1fr;
        gap: 8px;
    }
    .pcal-list-row-actions { justify-content: flex-end; }
}

/* --- Category badge on slot card --- */
.pcal-slot-category {
    display: inline-block;
    align-self: flex-start;
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    padding: 2px 8px;
    margin-bottom: 6px;
    border-radius: 999px;
    color: var(--cat-accent, var(--pcal-primary));
    background: color-mix(in srgb, var(--cat-accent, var(--pcal-primary)) 12%, transparent);
}

.pcal-content {
    flex: 1;
    max-width: 1200px;
    margin: 0 auto;
    padding: 24px 16px;
    width: 100%;
    box-sizing: border-box;
}

.pcal-embed-wrapper {
    font-family: var(--pcal-font);
    color: var(--pcal-text);
    background: var(--pcal-surface);
    padding: 12px;
}

.pcal-footer {
    text-align: center;
    padding: 16px;
    color: var(--pcal-text-muted);
    font-size: 13px;
    border-top: 1px solid var(--pcal-border);
}

/* --- Calendar Container --- */
.pcal-container {
    background: var(--pcal-grid-panel);
    border-radius: var(--pcal-radius-lg);
    box-shadow: var(--pcal-shadow);
    overflow-x: auto;
    overflow-y: visible;
    /* Keep the calendar tall enough for embedded iframes to host a full modal without
       needing inner scroll just to show the login / booking dialog. */
    min-height: 720px;
}

.pcal-header {
    padding: 20px 24px;
    border-bottom: 1px solid var(--pcal-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.pcal-header h2 {
    margin: 0;
    font-size: 20px;
    font-weight: 700;
    color: var(--pcal-text);
}

/* --- Navigation --- */
.pcal-nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 24px;
    border-bottom: 1px solid var(--pcal-border);
    gap: 8px;
    flex-wrap: wrap;
}

.pcal-nav-center {
    display: flex;
    align-items: center;
    gap: 12px;
}

.pcal-nav-dates {
    font-size: 14px;
    color: var(--pcal-text-muted);
    font-weight: 500;
}

/* --- Buttons --- */
.pcal-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    border-radius: var(--pcal-radius);
    font-size: 14px;
    font-weight: 500;
    font-family: var(--pcal-font);
    cursor: pointer;
    border: none;
    transition: all var(--pcal-transition);
    text-decoration: none;
    line-height: 1.4;
}

.pcal-btn:disabled {
    opacity: .55;
    cursor: not-allowed;
}

.pcal-btn-primary {
    background: var(--pcal-primary);
    color: #fff;
}
.pcal-btn-primary:hover:not(:disabled) {
    background: var(--pcal-primary-hover);
}

.pcal-btn-outline {
    background: transparent;
    color: var(--pcal-text);
    border: 1.5px solid var(--pcal-border);
}
.pcal-btn-outline:hover:not(:disabled) {
    background: var(--pcal-bg);
    border-color: #cbd5e1;
}

.pcal-btn-ghost {
    background: transparent;
    color: var(--pcal-primary);
    padding: 8px 12px;
}
.pcal-btn-ghost:hover:not(:disabled) {
    background: var(--pcal-primary-light);
}

.pcal-btn-danger {
    background: var(--pcal-danger);
    color: #fff;
}
.pcal-btn-danger:hover:not(:disabled) {
    background: #b91c1c;
}

.pcal-btn-danger-ghost {
    background: transparent;
    color: var(--pcal-danger);
    padding: 4px;
    border: none;
}
.pcal-btn-danger-ghost:hover {
    background: var(--pcal-danger-light);
}

.pcal-btn-link {
    background: none;
    border: none;
    color: var(--pcal-primary);
    padding: 4px 0;
    font-size: 13px;
    cursor: pointer;
    text-decoration: underline;
}
.pcal-btn-link:hover {
    color: var(--pcal-primary-hover);
}

.pcal-btn-full {
    width: 100%;
    justify-content: center;
}

.pcal-btn-sm {
    padding: 6px 12px;
    font-size: 13px;
}

.pcal-btn-icon {
    padding: 4px;
    border-radius: 6px;
}

.pcal-btn-spinner {
    display: inline-block;
    width: 16px;
    height: 16px;
    border: 2px solid rgba(255,255,255,.3);
    border-top-color: #fff;
    border-radius: 50%;
    animation: pcal-spin .6s linear infinite;
}

@keyframes pcal-spin {
    to { transform: rotate(360deg); }
}

/* --- Weekly Grid --- */
.pcal-weekly {
    padding: 0;
}

/* Mobile-only day selector + its "Today/Tomorrow" label (see the @media block below). Hidden on
   desktop — the 7-column grid already shows every day. */
.pcal-day-tabs, .pcal-selected-day-label { display: none; }

.pcal-grid {
    display: grid;
    /* Each day column gets a comfortable minimum so class cards stay readable
       (titles/times don't wrap to 3-4 lines). Columns still stretch to fill a
       wide embed via the 1fr max; on a narrow embed the grid exceeds the
       container and .pcal-container's overflow-x:auto scrolls horizontally —
       same week-view behaviour as Google Calendar. */
    grid-template-columns: repeat(7, minmax(160px, 1fr));
    gap: 0;
    border-top: 1px solid var(--pcal-border);
    min-width: 1120px;
}

.pcal-day-col {
    border-right: 1px solid var(--pcal-border);
    min-height: 180px;
}

.pcal-day-col:last-child {
    border-right: none;
}

.pcal-day-header {
    padding: 12px 10px;
    text-align: center;
    border-bottom: 1px solid var(--pcal-border);
    background: var(--pcal-bg);
}

.pcal-day-name {
    display: block;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--pcal-text-muted);
}

.pcal-day-date {
    display: block;
    font-size: 13px;
    font-weight: 500;
    color: var(--pcal-text);
    margin-top: 2px;
}

/* On a dark theme a solid --pcal-primary fill made the selected day the brightest block
   on the page while its own text sat at 2.66:1 (fails AA). The dark override swaps the
   fill for a 2px underline; light themes keep the filled header exactly as before. */
.pcal-today .pcal-day-header {
    background: var(--pcal-today-bg);
    border-bottom-color: var(--pcal-today-border);
    box-shadow: var(--pcal-today-shadow);
}
.pcal-today .pcal-day-name {
    color: var(--pcal-today-name);
}
.pcal-today .pcal-day-date {
    color: var(--pcal-today-date);
    font-weight: 800;
}

.pcal-day-slots {
    padding: 8px 6px;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.pcal-no-slots {
    text-align: center;
    color: var(--pcal-text-muted);
    font-size: 13px;
    padding: 24px 8px;
}

/* --- Slot Card --- */
.pcal-slot {
    border-radius: var(--pcal-radius);
    background: var(--pcal-slot-bg);
    border: 1px solid var(--pcal-slot-border);
    box-shadow: var(--pcal-slot-shadow);
    cursor: pointer;
    transition: all var(--pcal-transition);
    overflow: hidden;
    display: flex;
}

.pcal-slot:hover {
    box-shadow: var(--pcal-shadow);
    /* Derive the hover border from the theme: lightens on dark surfaces, darkens on
       light ones (a fixed #cbd5e1 glows out of palette on dark templates). */
    border-color: color-mix(in srgb, var(--pcal-text) 25%, var(--pcal-border));
    transform: translateY(-1px);
}

.pcal-slot-accent {
    width: 4px;
    flex-shrink: 0;
    /* The accent is the studio's own service colour, so it can land anywhere — two of the
       six on the noir demo sat at 1.67:1 against the card, i.e. invisible. --pcal-accent-lift
       is 0% on light themes (first declaration wins where color-mix is unsupported) and
       lifts the colour toward the text on dark ones, keeping the studio's hue. */
    background: var(--slot-accent, var(--pcal-primary));
    background: color-mix(in srgb,
        var(--slot-accent, var(--pcal-primary)) calc(100% - var(--pcal-accent-lift)),
        var(--pcal-text) var(--pcal-accent-lift));
}

.pcal-slot-body {
    padding: 8px 10px;
    flex: 1;
    min-width: 0;
}

/* Grid view card title — wraps to a max of 2 lines so cards stay roughly the same
   height (a 6-line card next to a 1-line card breaks the grid rhythm). Single
   very long un-breakable words still get broken via overflow-wrap. -webkit-line-clamp
   is widely supported in evergreen browsers; the fallback (no clamp) just shows the
   full wrapped title, which is still better than the previous ellipsis truncation. */
.pcal-slot-name {
    font-size: 13px;
    font-weight: 600;
    color: var(--pcal-text);
    overflow-wrap: anywhere;
    word-break: break-word;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.pcal-slot-time {
    font-size: 12px;
    color: var(--pcal-text-muted);
    margin-top: 2px;
}

.pcal-slot-instructor {
    font-size: 12px;
    color: var(--pcal-text-muted);
    margin-top: 2px;
}

.pcal-slot-location {
    font-size: 11px;
    color: var(--pcal-text-muted);
    margin-top: 3px;
    display: flex;
    align-items: center;
    gap: 4px;
}

.pcal-slot-footer {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    flex-wrap: wrap;
}

/* ──────────────────────────────────────────────────────────────────────────────
   Density variants — applied to the .pcal-container wrapper via the
   ?card-size= URL param from embed.js (sourced from the Integration page's
   "Card density" dropdown). "comfortable" is the default and uses the base
   styles above; this section provides "compact" and "spacious" overrides
   that proportionally adjust padding + font sizes on the slot card.
   Selectors are scoped to .pcal-density-* so the studio's own /pages/calendar
   render (which never gets the density class) stays untouched.
   ────────────────────────────────────────────────────────────────────────────── */

/* COMPACT — tighter rows, smaller text. Good for sidebar / narrow-iframe embeds. */
.pcal-density-compact .pcal-slot-body       { padding: 5px 7px; }
.pcal-density-compact .pcal-slot-name       { font-size: 11px; }
.pcal-density-compact .pcal-slot-time       { font-size: 10px; margin-top: 1px; }
.pcal-density-compact .pcal-slot-instructor { font-size: 10px; margin-top: 1px; }
.pcal-density-compact .pcal-slot-location   { font-size: 9px;  margin-top: 2px; }
.pcal-density-compact .pcal-slot-category   { font-size: 8.5px; padding: 1px 4px; }
.pcal-density-compact .pcal-slot-footer     { gap: 4px; margin-top: 4px; }

/* SPACIOUS — roomier cards with larger text. Good for prominent main-content embeds. */
.pcal-density-spacious .pcal-slot-body       { padding: 12px 14px; }
.pcal-density-spacious .pcal-slot-name       { font-size: 15px; }
.pcal-density-spacious .pcal-slot-time       { font-size: 13px; margin-top: 4px; }
.pcal-density-spacious .pcal-slot-instructor { font-size: 13px; margin-top: 4px; }
.pcal-density-spacious .pcal-slot-location   { font-size: 12px; margin-top: 5px; }
.pcal-density-spacious .pcal-slot-category   { font-size: 11px; padding: 3px 8px; }
.pcal-density-spacious .pcal-slot-footer     { gap: 8px; margin-top: 9px; }

/* --- Capacity Badges --- */
.pcal-capacity-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 10px;
    line-height: 1.4;
}

.pcal-status-available .pcal-capacity-badge,
.pcal-capacity-badge.pcal-status-available {
    background: var(--pcal-badge-ok-bg);
    color: var(--pcal-badge-ok-fg);
}

.pcal-status-warning .pcal-capacity-badge,
.pcal-capacity-badge.pcal-status-warning {
    background: var(--pcal-badge-warn-bg);
    color: var(--pcal-badge-warn-fg);
}

.pcal-status-full .pcal-capacity-badge,
.pcal-capacity-badge.pcal-status-full {
    background: var(--pcal-badge-danger-bg);
    color: var(--pcal-badge-danger-fg);
}

/* Last seat — stronger than the amber "Few spots left" warning to signal real
   scarcity, but solid danger (not danger-light) so it's distinct from "Full". */
.pcal-status-last .pcal-capacity-badge,
.pcal-capacity-badge.pcal-status-last {
    background: var(--pcal-danger);
    color: #fff;
    font-weight: 700;
}

.pcal-booked-badge {
    display: inline-block;
    font-size: 11px;
    font-weight: 600;
    padding: 2px 8px;
    border-radius: 10px;
    background: var(--pcal-primary-light);
    color: var(--pcal-primary);
}
.pcal-checked-in-badge {
    background: #d1fae5;
    color: #059669;
}

/* Booked cards tint toward the brand accent instead of a hardcoded light lavender —
   a fixed #faf5ff turns glaring white on dark templates (Luxe/Noir) while the text
   stays theme-light, making every booked card unreadable. color-mix keeps the tint
   subtle on light themes and dark-correct on dark ones. */
.pcal-slot-booked {
    border-color: var(--pcal-primary);
    background: color-mix(in srgb, var(--pcal-primary) 10%, var(--pcal-surface));
}

/* --- Filters --- */
.pcal-toolbar {
    padding: 12px 24px;
    border-bottom: 1px solid var(--pcal-border);
}

.pcal-filters {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: center;
}

.pcal-select {
    padding: 6px 28px 6px 10px;
    border: 1.5px solid var(--pcal-border);
    border-radius: var(--pcal-radius);
    font-size: 13px;
    font-family: var(--pcal-font);
    color: var(--pcal-text);
    background: var(--pcal-surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2364748b' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E") no-repeat right 10px center;
    appearance: none;
    cursor: pointer;
    min-width: 160px;
}

.pcal-select:focus {
    outline: none;
    border-color: var(--pcal-primary);
    box-shadow: 0 0 0 3px rgba(111,42,129,.12);
    box-shadow: 0 0 0 3px var(--pcal-field-ring, rgba(111,42,129,.12));
}

/* --- Popup / Modal Overlay --- */
.pcal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(15,23,42,.55);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 10000;
    padding: 16px;
}

.pcal-popup {
    background: var(--pcal-surface);
    border-radius: 16px;
    box-shadow: 0 24px 48px -12px rgba(15,23,42,.22), 0 0 0 1px rgba(15,23,42,.06);
    max-width: 480px;
    width: 100%;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    animation: pcal-popup-in 200ms cubic-bezier(0.34,1.56,0.64,1);
}

@keyframes pcal-popup-in {
    from {
        opacity: 0;
        transform: scale(.95) translateY(8px);
    }
    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

.pcal-popup-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 24px;
    border-bottom: 1px solid var(--pcal-border);
    flex-shrink: 0;
}

.pcal-popup-header h3 {
    margin: 0;
    font-size: 18px;
    font-weight: 700;
    color: var(--pcal-text);
}

.pcal-popup-close {
    background: transparent;
    border: none;
    cursor: pointer;
    padding: 6px;
    border-radius: var(--pcal-radius);
    color: var(--pcal-text-muted);
    transition: all var(--pcal-transition);
}
.pcal-popup-close:hover {
    background: var(--pcal-bg);
    color: var(--pcal-text);
}

.pcal-popup-body {
    padding: 24px;
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
}

/* Rich text the studio authored in the admin's Quill editor.
   Quill styles its own paragraphs with `margin: 0; padding: 0` (quill.snow.css), so the
   studio composes against a zero-margin rhythm and types an empty paragraph
   (`<p><br></p>`) when it wants a blank line. Re-rendering that same HTML out here under
   the browser default `p { margin: 1em 0 }` inflates every paragraph gap and turns one
   authored blank line into a ~3-line hole — the visitor sees whitespace that simply is
   not in the description field. Mirror the editor's metrics so what the studio typed is
   what the visitor reads. */
.pcal-rich-text {
    line-height: 1.5;
}
.pcal-rich-text p,
.pcal-rich-text ol,
.pcal-rich-text ul,
.pcal-rich-text h1,
.pcal-rich-text h2,
.pcal-rich-text h3,
.pcal-rich-text h4,
.pcal-rich-text h5,
.pcal-rich-text h6 {
    margin: 0;
    padding: 0;
}
.pcal-rich-text ol,
.pcal-rich-text ul {
    padding-left: 1.5em;
}
/* Quill 2 emits every list as <ol> and marks the kind on the item, so without these the
   studio's bullet list renders as "1. 2. 3." out here. `.ql-ui` is the editor's own
   marker span and has no business in the popup. */
.pcal-rich-text li[data-list="bullet"] { list-style-type: disc; }
.pcal-rich-text li[data-list="ordered"] { list-style-type: decimal; }
.pcal-rich-text .ql-ui { display: none; }
.pcal-rich-text img { max-width: 100%; }
.pcal-rich-text > *:last-child { margin-bottom: 0; }

.pcal-popup-footer {
    padding: 16px 24px 20px;
    border-top: 1px solid var(--pcal-border);
    background: var(--pcal-surface);
    flex-shrink: 0;
    max-height: 55vh;
    overflow-y: auto;
}

.pcal-popup-divider {
    height: 1px;
    background: var(--pcal-border);
    margin: 20px 0;
}

/* --- Slot Details in Popup --- */
.pcal-slot-details {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.pcal-detail-row {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 14px;
    color: var(--pcal-text);
}

.pcal-detail-row svg {
    flex-shrink: 0;
    color: var(--pcal-text-muted);
}

/* --- Login Form --- */
.pcal-login-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.pcal-login-title {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
    color: var(--pcal-text);
}

.pcal-form-group {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.pcal-label {
    font-size: 13px;
    font-weight: 600;
    color: #374151;
    color: var(--pcal-label-color, #374151);
}

.pcal-input {
    padding: 10px 14px;
    border: 1.5px solid var(--pcal-border);
    border: 1.5px solid var(--pcal-field-border, var(--pcal-border));
    border-radius: var(--pcal-radius);
    font-size: 14px;
    font-family: var(--pcal-font);
    color: #1e293b;
    color: var(--pcal-field-text, #1e293b);
    background: #ffffff;
    background: var(--pcal-field-bg, #ffffff);
    transition: border-color var(--pcal-transition), box-shadow var(--pcal-transition);
    width: 100%;
    box-sizing: border-box;
}

.pcal-input::placeholder {
    color: #94a3b8;
    color: var(--pcal-field-placeholder, #94a3b8);
    opacity: 1;
}

.pcal-input:focus {
    outline: none;
    border-color: var(--pcal-primary);
    box-shadow: 0 0 0 3px rgba(111,42,129,.12);
}

.pcal-input-sm {
    padding: 7px 10px;
    font-size: 13px;
}

.pcal-input-group {
    display: flex;
    position: relative;
}

.pcal-input-group .pcal-input {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    flex: 1;
}

.pcal-input-addon {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0 12px;
    background: var(--pcal-bg);
    border: 1.5px solid var(--pcal-border);
    border-left: none;
    border-radius: 0 var(--pcal-radius) var(--pcal-radius) 0;
    cursor: pointer;
    color: var(--pcal-text-muted);
    transition: color var(--pcal-transition);
}
.pcal-input-addon:hover {
    color: var(--pcal-text);
}

/* --- Booking Form --- */
.pcal-booking-form {
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.pcal-clickable {
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    user-select: none;
}

.pcal-guest-row {
    display: flex;
    gap: 8px;
    align-items: center;
}

.pcal-guest-row .pcal-input {
    flex: 1;
}

.pcal-booked-info {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

/* --- Alerts --- */
.pcal-alert {
    padding: 12px 16px;
    border-radius: var(--pcal-radius);
    font-size: 14px;
    line-height: 1.5;
}

.pcal-alert-info {
    background: #eff6ff;
    color: #1e40af;
    border: 1px solid #bfdbfe;
}

.pcal-alert-success {
    background: var(--pcal-success-light);
    color: #065f46;
    border: 1px solid #a7f3d0;
}

.pcal-alert-error {
    background: var(--pcal-danger-light);
    color: #991b1b;
    border: 1px solid #fecaca;
}

.pcal-alert-warning {
    background: var(--pcal-warning-light);
    color: #92400e;
    border: 1px solid #fde68a;
}

/* --- Misc --- */
.pcal-text-muted {
    color: var(--pcal-text-muted);
    font-style: italic;
}

.pcal-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 60px 20px;
    flex-direction: column;
    gap: 12px;
}

.pcal-loading-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--pcal-border);
    border-top-color: var(--pcal-primary);
    border-radius: 50%;
    animation: pcal-spin .7s linear infinite;
}

.pcal-error {
    text-align: center;
    padding: 40px 20px;
    color: var(--pcal-danger);
}

.pcal-maintenance {
    text-align: center;
    padding: 60px 20px;
}

.pcal-maintenance h3 {
    margin: 0 0 8px;
    font-size: 18px;
    color: var(--pcal-text);
}

.pcal-maintenance p {
    margin: 0;
    color: var(--pcal-text-muted);
    font-size: 14px;
}

/* --- Toast Notifications --- */
.pcal-toast {
    position: fixed;
    bottom: 24px;
    right: 24px;
    z-index: 99999;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 14px 18px;
    border-radius: var(--pcal-radius);
    box-shadow: 0 4px 16px rgba(0,0,0,.25);
    max-width: 380px;
    min-width: 220px;
    font-size: 14px;
    line-height: 1.4;
    font-family: var(--pcal-font);
    color: #fff;
    opacity: 0;
    transform: translateY(12px);
    transition: opacity .25s ease, transform .25s ease;
}

.pcal-toast.pcal-toast-visible {
    opacity: 1;
    transform: translateY(0);
}

.pcal-toast-success { background: #059669; }
.pcal-toast-error { background: #dc2626; }
.pcal-toast-info { background: #0284c7; }

.pcal-toast-close {
    background: none;
    border: none;
    color: #fff;
    cursor: pointer;
    font-size: 18px;
    line-height: 1;
    padding: 0 0 0 10px;
    opacity: .8;
}
.pcal-toast-close:hover { opacity: 1; }

/* --- Responsive: Mobile (< 768px) --- */
@media (max-width: 767px) {
    .pcal-grid {
        grid-template-columns: 1fr;
        /* The desktop rule forces min-width:1120px so the 7-column week can scroll inside
           .pcal-container on narrow embeds. Mobile shows ONE day column, so that floor must
           be lifted — leaving it makes the single column 1120px wide and the whole calendar
           card scroll far to the right into empty space. */
        min-width: 0;
    }

    .pcal-day-col {
        border-right: none;
        border-bottom: 1px solid var(--pcal-border);
        min-height: auto;
    }

    .pcal-day-col:last-child {
        border-bottom: none;
    }

    .pcal-day-header {
        display: flex;
        align-items: center;
        gap: 8px;
        text-align: left;
        padding: 10px 12px;
    }

    .pcal-day-name {
        display: inline;
    }

    .pcal-day-date {
        display: inline;
        margin-top: 0;
    }

    .pcal-day-slots {
        padding: 8px 12px;
    }

    /* Mobile week view = mobile-app layout: a horizontal day selector on top, then only the
       selected day's slots. The non-selected day columns collapse; the selected day's own header
       is hidden because the active tab already shows the day. */
    .pcal-day-tabs {
        display: flex;
        gap: 6px;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        padding: 4px 10px 12px;
    }
    .pcal-day-tabs::-webkit-scrollbar { display: none; }
    .pcal-day-tab {
        flex: 1 1 0;
        min-width: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 2px;
        padding: 7px 6px;
        border: 1.5px solid var(--pcal-border, #e2e8f0);
        border-radius: 10px;
        background: var(--pcal-surface, #fff);
        color: var(--pcal-text, #0f172a);
        cursor: pointer;
        line-height: 1.1;
    }
    .pcal-day-tab.is-selected {
        background: var(--pcal-primary, #6f2a81);
        border-color: var(--pcal-primary, #6f2a81);
        color: #fff;
    }
    .pcal-day-tab-name { font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.03em; opacity: 0.8; }
    .pcal-day-tab-date { font-size: 1.05rem; font-weight: 600; }
    .pcal-day-tab-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--pcal-primary, #6f2a81); }
    .pcal-day-tab.is-selected .pcal-day-tab-dot { background: rgba(255,255,255,.9); }

    .pcal-selected-day-label {
        display: block;
        font-size: 0.95rem;
        font-weight: 600;
        color: var(--pcal-text, #0f172a);
        padding: 0 12px 8px;
    }

    .pcal-grid .pcal-day-col:not(.is-selected) { display: none; }
    .pcal-grid .pcal-day-col.is-selected { border-bottom: none; }
    .pcal-grid .pcal-day-col.is-selected .pcal-day-header { display: none; }

    .pcal-slot {
        flex-direction: row;
    }

    /* Week nav on one tidy row: icon-only prev/next squares at the edges, "This Week" + the
       date range centered between them. font-size:0 collapses the buttons' text label while
       the inline SVG chevrons keep their attribute-set 16px size. */
    .pcal-nav {
        padding: 12px 16px;
        flex-wrap: nowrap;
    }
    .pcal-nav > .pcal-btn {
        font-size: 0;
        gap: 0;
        padding: 9px 11px;
        flex-shrink: 0;
    }
    .pcal-nav-center {
        flex: 1;
        min-width: 0;
        justify-content: center;
        flex-wrap: wrap;
        gap: 2px 10px;
        text-align: center;
    }
    .pcal-nav-dates {
        font-size: 13px;
        white-space: nowrap;
    }

    .pcal-header {
        padding: 16px;
    }

    .pcal-content {
        padding: 16px 8px;
    }

    /* Filters share one row (styles | instructors), each half-width — see FilterBar.razor for
       the matching flex-basis on .fb-cat-wrap and the instructor select. width:100% makes the
       50% child bases resolve (the filters div is itself a flex item of the toolbar) and pushes
       the toolbar's Log out button onto its own right-aligned row below. */
    .pcal-filters {
        flex-wrap: wrap;
        gap: 10px;
        width: 100%;
    }

    .pcal-select {
        min-width: 0;
        width: 100%;
    }

    .pcal-popup {
        max-width: 100%;
        border-radius: 12px;
        margin: 8px;
    }

    .pcal-popup-body {
        padding: 16px;
    }

    .pcal-guest-row {
        flex-wrap: wrap;
    }
}

/* --- Small phone overrides --- */
@media (max-width: 480px) {
    .pcal-page-wrapper { padding: 0; }
    .pcal-content { padding: 8px 4px; }

    /* Buttons */
    .pcal-btn { font-size: 0.82rem; padding: 8px 14px; }
    .pcal-btn-sm { font-size: 0.75rem; padding: 5px 10px; }

    /* Modals */
    .acct-modal { max-width: calc(100vw - 24px) !important; margin: 12px; }
    .acct-modal-body { padding: 16px; }
    .acct-modal-footer { padding: 12px 16px; flex-wrap: wrap; }

    /* Account page */
    .acct-wrap { padding: 16px 8px; }
    .acct-tabs { gap: 0; overflow-x: auto; }
    .acct-tab { padding: 8px 12px; font-size: 0.82rem; white-space: nowrap; }
    .acct-card { padding: 16px; }
    .acct-form-row { grid-template-columns: 1fr; }
    .acct-table th, .acct-table td { padding: 8px 6px; font-size: 0.78rem; }

    /* Billing cards */
    .billing-card-row { flex-direction: column; align-items: flex-start; gap: 8px; }
    .billing-card-actions { width: 100%; justify-content: flex-end; }

    /* Upgrade modal options */
    .upgrade-option { flex-direction: column; align-items: flex-start; gap: 4px; }
}

/* --- Embed Mode Overrides --- */
/* The ?only=<block> embed renders a StudioHome section (.ds-sec) inside the iframe. That
   layout's generous 80px/120px page-section padding (and the 32px top on the first section)
   wastes space in an embed, where the host page controls outer spacing. Collapse it so the
   widget fills the iframe — its own .pcal-content padding (4px below) is the only gutter. */
.pcal-embed-wrapper .ds-sec,
.pcal-embed-wrapper .ds.no-section-headers > section:first-of-type .ds-sec {
    padding: 0 !important;
}

.pcal-embed-wrapper .pcal-content {
    padding: 4px;
}

.pcal-embed-wrapper .pcal-container {
    box-shadow: none;
    border-radius: 0;
    overflow-x: auto;
}

.pcal-embed-wrapper .pcal-toolbar {
    padding: 8px 12px;
}

.pcal-embed-wrapper .pcal-nav {
    padding: 8px 12px;
}

.pcal-embed-wrapper .pcal-select {
    font-size: 12px;
    padding: 5px 24px 5px 8px;
}

/* ── Embedded modal positioning ───────────────────────────────────────────────────────
   Inside an auto-resized inline embed the iframe has no scroll of its own (it's sized to its
   content, scrolling="no"), so a position:fixed modal centres in the middle of the TALL
   iframe — far below what the visitor is actually looking at, forcing them to scroll the host
   page to find it. Two layers fix this:

   1. FALLBACK (always on, host-independent): pin the overlay to the TOP of the iframe content
      and top-align the card. When the visitor opens a modal while looking at the top of the
      embed (the common case), it appears right there with no scrolling. This is delivered by
      the iframe's own CSS (our origin), so it works even when the host page is still serving a
      stale, cached copy of embed.js that knows nothing about the band protocol.

   2. BANDED (when the host embed.js cooperates): on modal open, embed.js locks the host scroll
      and posts the visible viewport band as --dyl-embed-vtop / --dyl-embed-vheight and adds
      html.dyl-embed-banded (see PublicLayout's message handler). We then lay the overlay over
      exactly that band and centre the card in it, so the modal is dead-centre on screen at ANY
      scroll position — not just when the visitor is at the top.

   BOTH layers are gated on html.dyl-embed-iframed — set by PublicLayout ONLY when the page is
   actually rendered inside a host iframe (window.self !== window.top). When the embed URL is
   instead opened as a TOP-LEVEL document (directly, or in an in-app browser / Custom Tab) the
   page IS the viewport, so the base .pcal-overlay { position:fixed; inset:0 } already centres
   the modal perfectly — applying the absolute override there is what pushed the modal down the
   page. So at top level we leave the fixed overlay untouched. */
html.dyl-embed-iframed .pcal-embed-wrapper .pcal-overlay,
html.dyl-embed-iframed .pcal-embed-wrapper .apf-overlay,
html.dyl-embed-iframed .pcal-embed-wrapper .wsm-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: auto;
    min-height: 100%;
    height: auto;
    align-items: flex-start;
}
html.dyl-embed-iframed .pcal-embed-wrapper .pcal-popup { margin: 16px auto; }

html.dyl-embed-banded .pcal-embed-wrapper .pcal-overlay,
html.dyl-embed-banded .pcal-embed-wrapper .apf-overlay,
html.dyl-embed-banded .pcal-embed-wrapper .wsm-overlay {
    top: var(--dyl-embed-vtop, 0);
    min-height: 0;
    height: var(--dyl-embed-vheight, 100%);
    align-items: center;
}
html.dyl-embed-banded .pcal-embed-wrapper .pcal-popup {
    margin: 0 auto;
    /* Cap the card to the visible band so it never exceeds the device viewport (its default
       max-height:90vh is 90% of the TALL iframe inside an embed). Internal scroll keeps long
       content reachable. */
    max-height: calc(var(--dyl-embed-vheight, 90vh) - 32px);
}
/* The appointment booking popup's card is .apf-shell (not .pcal-popup) — give it the same band
   cap so its body scrolls internally and the footer/Continue stays inside the visible band. */
html.dyl-embed-banded .pcal-embed-wrapper .apf-shell {
    min-height: 0;
    max-height: calc(var(--dyl-embed-vheight, 90vh) - 32px);
}

/* ── Mobile embed height cap ───────────────────────────────────────────────────────────
   On a PHONE, letting the iframe grow to the full (very tall) calendar means a centred modal
   lands far down the content and the visitor must scroll to find it. Instead we cap the
   embedded content to ~one device screen — --dyl-embed-cap, set by PublicLayout from
   screen.availHeight — and let the calendar scroll INTERNALLY. The iframe then stays about
   viewport-height (every embed.js version's long-standing height-sync just mirrors the smaller
   posted height), so the modal can go back to a plain viewport-centred position:fixed and land
   on screen. This needs NO cooperation from the host page's embed.js — the recurring delivery
   problem — because it relies only on our own iframe content + the basic height-sync.

   These rules are LAST so they win (equal specificity) over the iframed top-pin and the banded
   rules above whenever .dyl-embed-capped is present (mobile only). overflow-y:auto does NOT
   create a containing block for position:fixed, so the modal still escapes the scroll box and
   centres on the iframe viewport rather than being clipped. */
/* Gate this cap on .dyl-embed-modal-open (set by PublicLayout's checkModal only while a modal is
   open). The cap exists solely so a fixed booking/calendar modal centres on ~one screen — but
   applied to EVERY mobile embed it makes plain inline content (gift cards, packages, a browsable
   calendar) scroll INSIDE the clipped wrapper instead of letting the iframe grow + the host page
   scroll. No modal open → no cap → the iframe auto-resizes to its content and the host page scrolls. */
html.dyl-embed-capped.dyl-embed-modal-open .pcal-embed-wrapper {
    max-height: var(--dyl-embed-cap, 90vh);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}
html.dyl-embed-capped .pcal-embed-wrapper .pcal-overlay,
html.dyl-embed-capped .pcal-embed-wrapper .apf-overlay,
html.dyl-embed-capped .pcal-embed-wrapper .wsm-overlay {
    position: fixed;
    inset: 0;
    top: 0;
    min-height: 0;
    height: auto;
    align-items: center;
}
html.dyl-embed-capped .pcal-embed-wrapper .pcal-popup {
    margin: 0 auto;
    max-height: 85vh;
}
/* Cap the appointment popup's card (.apf-shell) so the calendar/slots scroll INSIDE it and the
   footer/Continue stays on screen. Prefer --dyl-embed-vheight — the REAL visible height the host
   page measures and posts (the only value that accounts for an in-app browser's toolbars; a plain
   vh from inside the iframe over-reports and pushes Continue off the bottom). Fall back to a
   conservative 70vh only when the host hasn't posted a band. */
html.dyl-embed-capped .pcal-embed-wrapper .apf-shell {
    min-height: 0;
    max-height: calc(var(--dyl-embed-vheight, 70vh) - 16px);
}

/* ── Embed-on-mobile: un-cap the appointment shell ─────────────────────────────────────
   At <=720px inside an embed, AppointmentBookingPopup.razor switches to the proven
   overlay-scroll + sticky-footer model (the fixed .apf-overlay becomes the single scroll
   container). The banded/capped .apf-shell max-height rules above would re-clip the footer
   to an over-reported vh/--dyl-embed-vheight, so disable them here. Desktop embed keeps the
   caps. This block is LAST so it wins by source order over the banded+capped caps. */
@media (max-width: 720px) {
    html.dyl-embed-iframed .pcal-embed-wrapper .apf-shell {
        max-height: none;
        min-height: 0;
    }
}

/* ── Mobile: single-scroll model for the class/event popup (.pcal-popup) ───────────────
   Mirror of the .apf-shell overlay-scroll model above (see AppointmentBookingPopup.razor's
   <=720px block). The capped .pcal-popup traps touch scrolling in TWO nested regions — the
   body (image/description) and the footer (details + booking form, max-height:55vh) — so a
   gesture that lands on the header, padding or the "wrong" region moves nothing and the
   modal feels frozen (worst inside an embed, where embed.js also locks the host scroll).
   Instead make the fixed overlay the ONE scroll container: un-cap the card, let body and
   footer flow at natural height, and the whole popup scrolls with a swipe anywhere on it.
   Unlike .apf-footer, .pcal-popup-footer is tall (it holds the booking form) so it cannot
   be sticky — it simply joins the flow. Desktop keeps the capped card + internal scrolls. */
@media (max-width: 720px) {
    /* Top level (no iframe): the fixed overlay is the device viewport — just scroll it. */
    html:not(.dyl-embed-iframed) .pcal-overlay {
        padding: 0;
        align-items: flex-start;
        overflow-y: auto;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior: contain;
    }
    html:not(.dyl-embed-iframed) .pcal-popup {
        margin: 0 auto;
        max-height: none;
        border-radius: 0;
        overflow: visible;
    }
    html:not(.dyl-embed-iframed) .pcal-popup-body,
    html:not(.dyl-embed-iframed) .pcal-popup-footer {
        max-height: none;
        overflow: visible;
    }

    /* Embed: the capped wrapper keeps the iframe ~one screen tall, so the fixed overlay
       matches the visible area and can be the scroller. !important wins over the
       banded/capped overlay + card rules above (same trick as the .apf-* block). */
    html.dyl-embed-iframed .pcal-overlay {
        padding: 0 !important;
        align-items: flex-start !important;
        overflow-y: auto !important;
        -webkit-overflow-scrolling: touch;
        overscroll-behavior: contain;
    }
    html.dyl-embed-iframed .pcal-popup {
        margin: 0 auto !important;
        min-height: 0;
        max-height: none !important;
        border-radius: 0 !important;
        overflow: visible !important;
    }
    html.dyl-embed-iframed .pcal-popup-body,
    html.dyl-embed-iframed .pcal-popup-footer {
        max-height: none !important;
        overflow: visible !important;
    }

    /* With the whole card in one scroll, a long description would carry the header (and its
       X) off the top. Pin the header so the body scrolls UNDER it and the close button is
       always reachable. Sticky works because the popup is overflow:visible and the overlay
       is the scroll container; the opaque surface bg stops text bleeding through. Applies in
       both the top-level and embed single-scroll cases (all ≤720px popups scroll this way). */
    .pcal-popup-header {
        position: sticky;
        top: 0;
        z-index: 3;
        background: var(--pcal-surface);
    }
}

/* Account action modals (Renew, Upgrade, Cancel booking, Order detail, …) use
   position:fixed; inset:0; align-items:center. Inside the auto-resized embed iframe that
   breaks the same way .pcal-overlay does above: a fixed overlay with bottom:0 anchors to
   the document, the iframe height-sync loop (PublicLayout's dyl-embed-height postMessage)
   inflates the frame, and the vertically-centred card is left stranded far below the
   visible content — the "modal way down / empty space" embed bug. Pin the overlay to the
   top of the content (absolute, bottom:auto so it can't stretch to a runaway document
   bottom) and top-align the card so it always sits next to what the visitor is looking at.
   Scoped to embeds only — the standalone /portal/{slug}/account page keeps the fixed,
   viewport-centred modal. */
.pcal-embed-wrapper .acct-modal-overlay {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    bottom: auto;
    min-height: 100%;
    align-items: flex-start;
}
.pcal-embed-wrapper .acct-modal {
    margin: 24px auto;
}

/* ── iOS Safari/WebKit: neutralise position:fixed inside the auto-sized embed iframe ──────
   The mobile "capped" model above pins booking/calendar overlays with position:fixed and
   relies on the fixed element centring on the (capped, ~one-screen) iframe viewport. That
   works in Blink (Android Chrome) and on desktop, but iOS Safari/WebKit does NOT position
   fixed elements reliably inside an auto-sized, scrolling="no" iframe — the overlay collapses
   toward the top with ~zero usable height, so only the sticky header (the ✕) paints and the
   modal body (e.g. the appointment day/time calendar) has nowhere to render. Symptom: the
   booking form "doesn't open" on iPhone Safari/Chrome while it works on Android + Windows.

   @supports (-webkit-touch-callout: none) is true ONLY on iOS WebKit (all iOS browsers —
   Safari AND iOS Chrome — share the engine) and false everywhere else, so this override is
   invisible to Android/desktop. We fall back to the SAME absolute top-pin model already used
   as the host-independent fallback for the un-capped iframed case (which iOS renders fine —
   only `fixed` is broken). The wrapper is already capped to ~one screen and is the scroll
   container, and PublicLayout scrolls the modal into view, so top:0 lands it on screen.
   Higher-than-media-query specificity (3 classes) + placed after the capped rule, so it wins
   for `position` without needing !important. See PublicLayout.razor + [[project_embed_modal_centering]]. */
@supports (-webkit-touch-callout: none) {
    html.dyl-embed-capped .pcal-embed-wrapper .pcal-overlay,
    html.dyl-embed-capped .pcal-embed-wrapper .apf-overlay,
    html.dyl-embed-capped .pcal-embed-wrapper .wsm-overlay {
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        bottom: auto;
        min-height: 100%;
        height: auto;
        align-items: flex-start;
    }
}

/* --- Print --- */
@media print {
    .pcal-nav,
    .pcal-filters,
    .pcal-overlay,
    .pcal-footer,
    .pcal-btn {
        display: none !important;
    }

    .pcal-slot {
        break-inside: avoid;
        box-shadow: none;
        border: 1px solid #ccc;
    }

    .pcal-grid {
        grid-template-columns: repeat(7, 1fr);
    }
}
