/* ════════════════════════════════════════════════════════════════
 * MOBILE APP EXPERIENCE — sportscal native-feel layer
 *
 * Loaded automatically on every page via header-auth.js. Provides:
 *
 *   1. Bottom tab bar (mobile only, frosted glass, safe-area aware)
 *   2. Sticky bottom CTA pattern for conversion pages
 *   3. iOS-grade polish foundations: no input zoom, no tap-flash, no
 *      300ms tap delay, momentum scroll, system font stack on mobile,
 *      tap-state animations, safe-area padding
 *
 * Design notes:
 *   - All mobile-only behavior is gated behind `@media (max-width:768px)`
 *     so desktop is untouched.
 *   - Uses `env(safe-area-inset-bottom/top)` so iPhone notches/home-
 *     indicator don't overlap content. Falls back to 0 on browsers
 *     that don't support env().
 *   - Tab bar height is 56px + safe-area-inset-bottom. Pages that need
 *     a sticky CTA stack it ABOVE the tab bar; body padding-bottom is
 *     adjusted automatically by the .has-sticky-cta body modifier.
 * ════════════════════════════════════════════════════════════════ */


/* ─── Foundations: applied always (don't fight desktop UX) ─── */

/* Kill the iOS Safari tap-flash. Critical for native feel —
 * without this every tap shows an unloved blue rectangle. */
* {
    -webkit-tap-highlight-color: transparent;
}

/* No 300ms click delay on iOS for any element treated as tappable. */
button,
a,
[role="button"],
.tappable,
input[type="submit"],
input[type="button"],
.btn,
.btn-continue {
    touch-action: manipulation;
}

/* Subtle press-state animation. Mimics the iOS "press in" feel. */
button:active,
a.btn:active,
.tappable:active,
.btn-continue:active {
    transform: scale(0.97);
    transition: transform 0.08s ease-out;
}

/* iOS auto-zooms inputs whose font-size < 16px on focus. Always use
 * 16px on mobile to prevent the layout-jolt that ruins app feel. */
@media (max-width: 768px) {
    input,
    textarea,
    select {
        font-size: 16px;
    }
}


/* ─── Bottom tab bar ───────────────────────────────────────── */

.mobile-tab-bar {
    /* Hidden on desktop. Shown via @media below. */
    display: none;
}

.mobile-tab-bar-spacer {
    /* Reserves layout space at the bottom of every page so fixed-
     * position content (footer, etc.) isn't overlapped on mobile. */
    display: none;
}

@media (max-width: 768px) {
    .mobile-tab-bar {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 100;

        /* Frosted-glass background — iOS app signature. Falls back to
         * solid white for browsers without backdrop-filter. */
        background: rgba(255, 255, 255, 0.78);
        -webkit-backdrop-filter: saturate(180%) blur(20px);
        backdrop-filter: saturate(180%) blur(20px);

        /* Hard top border — matches the neo-brutalist treatment used
         * elsewhere on the site. The frosted-glass background above gives
         * the tab bar its "native" depth without needing a soft inset. */
        border-top: 1.5px solid #000;

        /* Honor home-indicator safe area on notched iPhones */
        padding-bottom: env(safe-area-inset-bottom, 0);

        /* Prevent text selection on tab labels — iOS apps never
         * highlight UI chrome on long-press. */
        -webkit-user-select: none;
        user-select: none;
    }

    /* Push page content up so the tab bar doesn't cover the bottom. */
    body {
        padding-bottom: calc(56px + env(safe-area-inset-bottom, 0));
    }

    /* When a sticky CTA is present, stack it: tab bar (56px) + CTA
     * (~64px) + safe area. Body opts in via JS (`has-sticky-cta`). */
    body.has-sticky-cta {
        padding-bottom: calc(120px + env(safe-area-inset-bottom, 0));
    }

    /* Show the spacer so other fixed elements honor the tab area. */
    .mobile-tab-bar-spacer {
        display: block;
        height: calc(56px + env(safe-area-inset-bottom, 0));
        pointer-events: none;
    }

    /* Footer stays visible on mobile — it scrolls naturally at the
     * end of the document and carries legal links (Privacy, Terms,
     * Contact) that we shouldn't hide. The body's padding-bottom
     * leaves room for the tab bar so the footer renders cleanly
     * above it at scroll-end. */

    /* Lift the support widget FAB above the mobile tab bar.
     *
     * The tab bar is fixed at bottom: 0 with height ~56px (+ iPhone
     * safe-area). The widget's own stylesheet pins the FAB at
     * `bottom: 14px` on mobile, which puts it INSIDE the tab bar and
     * makes the chat button completely unreachable. This override
     * lifts the FAB and the open-state panel so both clear the bar
     * with a small gap. The FAB's high z-index (2147483000) already
     * keeps it above everything else on the page; this only fixes
     * vertical placement. */
    .sc-support-fab {
        bottom: calc(56px + env(safe-area-inset-bottom, 0) + 14px) !important;
    }

    .sc-support-panel {
        bottom: calc(56px + env(safe-area-inset-bottom, 0) + 78px) !important;
    }

    /* When a sticky CTA bar is present, push the FAB above both the
     * tab bar AND the CTA (~64–90px). Body modifiers:
     *   - `has-sticky-cta`       — homepage / builder sticky install bar
     *   - `has-mobile-page-cta`  — league/team sticky subscribe CTA
     * Without these the bubble sits ON TOP of the free CTA.
     * Include every known fab id (legacy SC + DeskQ host). DeskQ's
     * launcher lives in shadow DOM and is lifted via --dq-chrome-bottom
     * from home-preview.js; these rules cover light-DOM fabs. */
    body.has-sticky-cta .sc-support-fab,
    body.has-sticky-cta #scsc-fab,
    body.has-sticky-cta #sc-fab,
    body.has-sticky-cta #chat-toggle-btn,
    body.has-mobile-page-cta .sc-support-fab,
    body.has-mobile-page-cta #scsc-fab,
    body.has-mobile-page-cta #sc-fab,
    body.has-mobile-page-cta #chat-toggle-btn {
        bottom: calc(56px + 90px + env(safe-area-inset-bottom, 0) + 14px) !important;
    }

    body.has-sticky-cta .sc-support-panel,
    body.has-sticky-cta #sc-panel,
    body.has-mobile-page-cta .sc-support-panel,
    body.has-mobile-page-cta #sc-panel {
        bottom: calc(56px + 90px + env(safe-area-inset-bottom, 0) + 78px) !important;
    }
}

.mobile-tab {
    flex: 1 1 0;
    min-height: 56px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.15rem;
    padding: 0.4rem 0.25rem 0.5rem;
    color: rgba(0, 0, 0, 0.54);
    text-decoration: none;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1;
    transition: color 0.18s ease;
    /* Each tab is its own tappable region — no scale animation on
     * the bar itself, only on individual tabs. */
}

.mobile-tab:active {
    /* Subtle dim on press, no scale (scale on tab bar feels wrong). */
    color: rgba(0, 0, 0, 0.6);
    transform: none;
}

.mobile-tab[aria-current="page"],
.mobile-tab.is-active {
    /* Sets currentColor for the ICON, which is a graphical control and only
     * needs 3:1 (this is 3.14:1 on white). The label overrides below because
     * text needs 4.5:1 and 11px green failed it. */
    color: var(--primary-dark, #00A844);
}

/* The active tab's word stays ink; the green icon above it carries the
 * selected state. Weight, not hue, keeps it distinct from the inactive
 * labels (rgba(0,0,0,.54)) — green text this small can't reach AA, and
 * lightening the brand green to force it is not on the table. */
.mobile-tab[aria-current="page"] .mobile-tab-label,
.mobile-tab.is-active .mobile-tab-label {
    color: #111111;
    font-weight: 800;
}

.mobile-tab-icon {
    width: 24px;
    height: 24px;
    display: block;
    stroke-width: 2;
    vector-effect: non-scaling-stroke;
    shape-rendering: geometricPrecision;
}

.mobile-tab-icon * {
    vector-effect: non-scaling-stroke;
}

.mobile-tab[aria-current="page"] .mobile-tab-icon,
.mobile-tab.is-active .mobile-tab-icon {
    stroke-width: 2;
}

.mobile-tab-label {
    line-height: 1;
    white-space: nowrap;
}

/* Avatar variant: when the user is logged in, the Profile tab shows
 * their avatar circle instead of an icon. */
.mobile-tab-avatar {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    background: var(--primary, #00C853);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 0.76rem;
    font-weight: 800;
    line-height: 1;
}

.mobile-tab[aria-current="page"] .mobile-tab-avatar {
    /* Solid black ring around the avatar on the active tab — matches
     * the neo-brutalist 3px black outline used on other active controls. */
    box-shadow: 0 0 0 3px #000;
}


/* ─── Hide top-nav links on mobile (tab bar is primary nav) ─── */

@media (max-width: 768px) {
    /* The desktop top-nav link group ("Build a Calendar · Chat ·
     * Leagues · Pricing · Blog") is replaced by the bottom tab bar
     * for the 4 most-used destinations. Pricing / Blog / Contact /
     * FAQ remain reachable via the hamburger overflow menu (kept
     * visible) and via the footer (kept visible). */
    .nav-links,
    nav .nav-links,
    header .nav-links {
        display: none !important;
    }

    /* Push notched-iPhone safe-area away from the top of the header. */
    header,
    .site-header,
    nav.top-nav {
        padding-top: env(safe-area-inset-top, 0);
    }

    /* The hamburger STAYS visible — it's the overflow menu for pages
     * not in the 4-tab bar (Pricing, Blog, FAQ, Contact, Privacy,
     * Terms, etc.). Tab bar = primary nav; hamburger = "more". */

    /* The footer STAYS visible too — it sits in normal flow at the
     * bottom of the document, scrolls naturally with content, and
     * carries legal links (Privacy, Terms) that we shouldn't hide.
     * Body's padding-bottom (above) leaves room for the tab bar so
     * the footer renders above it at the end of scroll. */

    /* Hide the AI-chat floating widgets on mobile — the bottom-nav
     * "Chat" tab going to /chat IS the mobile path to AI chat, and a
     * floating bubble would just duplicate it. The IDs below belong
     * to chat-widget.js (#sc-fab/#sc-panel) and chat.js
     * (#chat-toggle-btn) — completely separate from the support
     * widget (.sc-support-fab) which is the email-us-direct form
     * and DOES still need to be reachable on mobile (the "Chat" tab
     * is AI chat, not support). */
    #sc-fab,
    #sc-panel,
    #chat-toggle-btn {
        display: none !important;
    }
}


/* ─── Sticky bottom CTA pattern ─────────────────────────────────────
 *
 * Usage: wrap the primary action of a conversion page in
 *   <div class="mobile-sticky-cta">...</div>
 * and on mobile it pins above the tab bar. On desktop it renders
 * inline as if the wrapper weren't there. JS adds `has-sticky-cta`
 * to <body> when this element is present so body padding adjusts. */

.mobile-sticky-cta {
    /* Desktop: behave like a normal block. */
}

@media (max-width: 768px) {
    .mobile-sticky-cta {
        position: fixed;
        bottom: calc(56px + env(safe-area-inset-bottom, 0));
        left: 0;
        right: 0;
        z-index: 95;

        background: rgba(255, 255, 255, 0.96);
        -webkit-backdrop-filter: saturate(180%) blur(18px);
        backdrop-filter: saturate(180%) blur(18px);
        border-top: 1px solid rgba(0, 0, 0, 0.06);

        padding: 0.75rem 1rem;
        display: flex;
        align-items: center;
        justify-content: stretch;
        gap: 0.5rem;
    }

    /* Buttons / links inside the sticky CTA fill the width. */
    .mobile-sticky-cta > button,
    .mobile-sticky-cta > a,
    .mobile-sticky-cta > .cta-primary,
    .mobile-sticky-cta > .btn-continue,
    .mobile-sticky-cta > .hero-cta-btn {
        flex: 1 1 auto;
        min-height: 48px;
        text-align: center;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    /* Optional helper label that sits above the sticky button —
     * "Step 1 of 3" or a price summary. */
    .mobile-sticky-cta-label {
        flex-shrink: 0;
        font-size: 0.78rem;
        color: rgba(0, 0, 0, 0.6);
        font-weight: 600;
        line-height: 1.2;
    }
}


/* ─── Sticky-action pattern (single button promotion) ──────────────────
 *
 * Lower-ceremony alternative to .mobile-sticky-cta: take an existing
 * button (e.g. Continue, Subscribe) and pin it directly to the bottom
 * of the viewport on mobile, full-width, with a frosted shelf above
 * the tab bar. Use when the page only has one primary action and you
 * don't need a wrapper for additional content.
 *
 * Add the class to the button itself:
 *   <button class="btn-continue mobile-sticky-action">Continue</button>
 *
 * Pages that use this should add `has-sticky-action` to <body> so
 * page-content padding accounts for the pinned CTA + tab bar.
 *
 * Buttons inside `display:none` ancestors (e.g. inactive funnel steps)
 * are not rendered, so the same class can decorate every step's primary
 * button — only the active step's button materializes.
 */

@media (max-width: 768px) {
    .mobile-sticky-action {
        position: fixed;
        left: 0.75rem;
        right: 0.75rem;
        bottom: calc(56px + env(safe-area-inset-bottom, 0) + 0.5rem);
        z-index: 95;
        width: auto;
        margin: 0;
        /* Floating rounded pill above the tab bar — matches primary CTAs. */
        border-radius: 10px;
        border: 2px solid #000;
        box-shadow: 3px 3px 0 #000;
        min-height: 56px;
        padding: 0.95rem 1.25rem;
        font-size: 1.05rem;
        font-weight: 800;
        line-height: 1.2;
        text-align: center;
    }

    .mobile-sticky-action:active {
        transform: translate(2px, 2px);
        box-shadow: 1px 1px 0 #000;
        filter: none;
    }

    .mobile-sticky-action:disabled,
    .mobile-sticky-action[disabled] {
        opacity: 0.55;
    }

    body.has-sticky-action {
        padding-bottom: calc(60px + 56px + env(safe-area-inset-bottom, 0) + 0.75rem);
    }

    /* ─── Sticky stack: secondary link above primary action ────────────
     * "Already have an account? Sign in" above Continue — both rounded CTAs.
     */
    .mobile-sticky-stack {
        position: fixed;
        left: 0;
        right: 0;
        bottom: calc(56px + env(safe-area-inset-bottom, 0));
        z-index: 95;
        background: #ffffff;
        border-top: 2px solid #000;
        padding: 0.75rem 1rem calc(0.85rem + env(safe-area-inset-bottom, 0) * 0.4);
        display: flex;
        flex-direction: column;
        align-items: stretch;
        gap: 0.55rem;
    }

    a.mobile-sticky-secondary {
        display: flex;
        align-items: center;
        justify-content: center;
        text-align: center;
        font-size: 0.88rem;
        font-weight: 600;
        color: #222 !important;
        text-decoration: none !important;
        line-height: 1.25;
        min-height: 44px;
        padding: 0.55rem 0.85rem;
        background: #fff;
        border: 2px solid #000;
        border-radius: 10px;
        box-shadow: 2px 2px 0 #000;
        box-sizing: border-box;
    }
    a.mobile-sticky-secondary span {
        font-weight: 800;
        color: #000;
        text-decoration: none;
        border-bottom: 2px solid #00C853;
        padding-bottom: 1px;
    }
    a.mobile-sticky-secondary:active {
        transform: translate(1px, 1px);
        box-shadow: 1px 1px 0 #000;
    }

    .mobile-sticky-stack-primary {
        width: 100%;
        margin: 0 0 4px;       /* leave room for the offset shadow */
        border-radius: 10px;
        border: 2px solid #000;
        min-height: 56px;
        padding: 0.95rem 1rem;
        font-size: 1.02rem;
        font-weight: 800;
        line-height: 1.2;
        text-align: center;
        box-shadow: 3px 3px 0 #000;
        transition: transform 0.08s ease-out, box-shadow 0.08s ease-out;
        color: #000;
    }
    .mobile-sticky-stack-primary:active:not(:disabled) {
        transform: translate(2px, 2px);
        box-shadow: 2px 2px 0 #000;
    }
    .mobile-sticky-stack-primary:disabled,
    .mobile-sticky-stack-primary[disabled] {
        opacity: 0.55;
        box-shadow: 3px 3px 0 #000;
    }

    body.has-sticky-stack {
        padding-bottom: calc(130px + 56px + env(safe-area-inset-bottom, 0));
    }
}


/* ─── Page transitions (mobile-only) ───────────────────────────────────
 *
 * Two layers:
 *
 *   1. Per-route enter animation. Every page on mobile fades + lifts
 *      slightly into place when the document becomes interactive.
 *      Plays on every full navigation; combined with the leave-fade
 *      below it gives the "soft handoff" feel iOS apps have when
 *      transitioning between view controllers.
 *
 *   2. Link-click leave fade. header-auth.js intercepts same-origin
 *      navigation clicks, adds `.page-leaving` to <body>, waits for
 *      the fade-out, then triggers the navigation. The next page
 *      naturally plays its enter animation.
 *
 *   3. Funnel step slide. The 3-step builder funnel uses an in-page
 *      slide animation (forward = slide-in-from-right, back = slide-
 *      in-from-left) instead of the instant display:none swap. CSS
 *      classes are added/removed by goToStep() in the funnel HTML.
 *
 * Honors `prefers-reduced-motion` for accessibility — falls back to
 * instant swaps with no animations.
 */

@keyframes mobile-page-enter {
    /* Opacity-only on <body> so position:fixed descendants (tab bar,
     * sticky CTA) aren't translated relative to body during the
     * animation. The slide-in lift is preserved on the funnel-step
     * keyframes below where it's safe (those elements aren't
     * containing fixed-position descendants). */
    from { opacity: 0; }
    to { opacity: 1; }
}

@keyframes mobile-funnel-slide-in-right {
    from {
        opacity: 0;
        transform: translateX(28px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes mobile-funnel-slide-in-left {
    from {
        opacity: 0;
        transform: translateX(-28px);
    }
    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@media (max-width: 768px) {
    /* Per-route enter animation. Fast (220ms) so the page feels
     * responsive, not slow. `backwards` keeps the start state until
     * the animation runs. */
    body {
        animation: mobile-page-enter 0.22s cubic-bezier(0.2, 0, 0, 1) both;
    }

    /* When a navigation is intercepted, fade the body out before the
     * actual page change. The matching enter animation on the next
     * page completes the handoff. Opacity-only here too — same reason
     * as the enter keyframe (avoid translating position:fixed kids). */
    body.page-leaving {
        opacity: 0;
        transition: opacity 0.16s ease-out;
        animation: none;
    }

    /* Funnel step slide animations. The active step plays one of two
     * keyframes depending on direction (set by goToStep via classes
     * on the active <div class="funnel-step">). */
    .funnel-step.active.is-entering-from-right {
        animation: mobile-funnel-slide-in-right 0.28s cubic-bezier(0.2, 0, 0, 1) both;
    }
    .funnel-step.active.is-entering-from-left {
        animation: mobile-funnel-slide-in-left 0.28s cubic-bezier(0.2, 0, 0, 1) both;
    }
}

/* Respect users who've asked for reduced motion. Slides become
 * instant swaps; fades become opacity changes only (no transform).
 * This is OS-level (Settings → Accessibility on iOS, etc.). */
@media (prefers-reduced-motion: reduce) {
    body,
    body.page-leaving,
    .funnel-step.active.is-entering-from-right,
    .funnel-step.active.is-entering-from-left {
        animation: none !important;
        transition: opacity 0.1s linear !important;
        transform: none !important;
    }
}


/* ─── Bottom sheet ─────────────────────────────────────────────────────
 *
 * iOS-style modal pattern. A sheet slides up from the bottom edge,
 * darkens the page with a backdrop, dismisses on backdrop tap, swipe-
 * down on the handle, or Escape. Full-width on mobile; centered card
 * on desktop. Replaces alert(), confirm(), and inline modal CSS for
 * any flow that asks the user to confirm or pick from a list.
 *
 * Open/close API lives on window: openBottomSheet(id), closeBottomSheet
 * (id). The HTML structure expected:
 *
 *   <div class="bottom-sheet" id="my-sheet" role="dialog" aria-modal=
 *        "true" aria-hidden="true" hidden>
 *     <div class="bottom-sheet-backdrop" data-bottom-sheet-close></div>
 *     <div class="bottom-sheet-panel">
 *       <div class="bottom-sheet-handle"></div>
 *       <h2 class="bottom-sheet-title">Title</h2>
 *       <div class="bottom-sheet-body">…</div>
 *       <button class="bottom-sheet-action" type="button">Confirm</button>
 *     </div>
 *   </div>
 *
 * Honors safe-area-inset-bottom so the action button doesn't hide
 * under the iPhone home indicator. */

.bottom-sheet {
    position: fixed;
    inset: 0;
    z-index: 10000;
    pointer-events: none;
}

/* Bottom sheets are visible regardless of viewport — they work on
 * desktop too (centered card) and mobile (full-width sheet). The
 * @media split is in the panel rules below. */
.bottom-sheet[aria-hidden="false"] {
    pointer-events: auto;
}

.bottom-sheet[hidden] {
    /* Match the attribute selector even though [hidden] hides at
     * `display: none`. Needed for the open animation: we remove
     * [hidden] first, then on the next frame flip aria-hidden so
     * the transition runs. */
    display: none;
}

.bottom-sheet-backdrop {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0);
    transition: background 0.28s cubic-bezier(0.32, 0.72, 0, 1);
    -webkit-tap-highlight-color: transparent;
}

.bottom-sheet[aria-hidden="false"] .bottom-sheet-backdrop {
    background: rgba(0, 0, 0, 0.45);
}

.bottom-sheet-panel {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    background: #fff;
    border-top: 2px solid #000;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
    padding: 0.4rem 1.25rem calc(1.25rem + env(safe-area-inset-bottom, 0));
    box-shadow: 0 -6px 0 #000;
    transform: translateY(100%);
    transition: transform 0.34s cubic-bezier(0.32, 0.72, 0, 1);
    max-height: 88vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.bottom-sheet[aria-hidden="false"] .bottom-sheet-panel {
    transform: translateY(0);
}

.bottom-sheet-handle {
    width: 36px;
    height: 4px;
    border-radius: 0;
    background: #000;
    margin: 0.5rem auto 0.85rem;
    flex-shrink: 0;
    /* Visual cue that the sheet can be dismissed with a swipe-down. */
}

.bottom-sheet-title {
    font-size: 1.15rem;
    font-weight: 800;
    margin: 0 0 0.5rem;
    line-height: 1.25;
}

.bottom-sheet-body {
    font-size: 0.95rem;
    color: rgba(0, 0, 0, 0.72);
    line-height: 1.5;
}

.bottom-sheet-body p {
    margin: 0 0 0.75rem;
}

.bottom-sheet-body p:last-child {
    margin-bottom: 0;
}

.bottom-sheet-action {
    display: block;
    width: 100%;
    margin: 1.25rem 0 4px;     /* 4px reserves room for the offset shadow */
    padding: 0.95rem 1rem;
    min-height: 52px;
    background: var(--primary, #00C853);
    color: #000;
    font-weight: 800;
    font-size: 1.02rem;
    /* Soft corners, thick black border, hard offset shadow. */
    border: 2px solid #000;
    border-radius: 10px;
    box-shadow: 3px 3px 0 #000;
    cursor: pointer;
    transition: transform 0.08s ease-out, box-shadow 0.08s ease-out;
    -webkit-tap-highlight-color: transparent;
}

.bottom-sheet-action:active {
    transform: translate(2px, 2px);
    box-shadow: 2px 2px 0 #000;
}

.bottom-sheet-action.is-secondary {
    background: #fff;
    color: #000;
    margin-top: 0.6rem;
}

/* Desktop variant: center the panel as a card-style modal. The whole
 * pattern still uses the same DOM and JS — only the CSS shifts. */
@media (min-width: 769px) {
    .bottom-sheet-panel {
        position: relative;
        margin: 8vh auto 0;
        max-width: 440px;
        border: 2px solid #000;
        border-radius: 0;
        padding: 1.25rem 1.5rem 1.5rem;
        box-shadow: 5px 5px 0 #000;
        transform: scale(0.94) translateY(20px);
        transition: transform 0.22s ease-out;
    }
    .bottom-sheet[aria-hidden="false"] .bottom-sheet-panel {
        transform: scale(1) translateY(0);
    }
    .bottom-sheet-handle {
        display: none; /* No swipe-down affordance on desktop. */
    }
    /* Center the whole panel via flex on the wrapper so it doesn't
     * stick to the bottom on desktop. */
    .bottom-sheet[aria-hidden="false"] {
        display: flex;
        align-items: flex-start;
        justify-content: center;
    }
}

/* Reduced-motion: skip the slide. */
@media (prefers-reduced-motion: reduce) {
    .bottom-sheet-backdrop,
    .bottom-sheet-panel {
        transition: opacity 0.12s linear !important;
        transform: none !important;
    }
    .bottom-sheet[aria-hidden="false"] .bottom-sheet-panel {
        transform: none !important;
    }
}


/* ─── Page-level sticky CTA (mobile, opt-in) ───────────────────────────
 *
 * For pages where a single primary action dominates intent — league
 * pages ("Add NBA to my calendar"), team pages ("Add Lakers"), blog
 * posts ending in a CTA, etc. The element pins to the bottom of the
 * viewport, sits above the bottom tab bar, and respects the home-
 * indicator safe area.
 *
 * Usage: anywhere in the page,
 *
 *   <a href="/build-your-sports-team-calendar?team=nba"
 *      class="mobile-page-cta">Add NBA to my calendar →</a>
 *
 * Body must also receive `.has-tab-bar` (already auto-set when the
 * tab bar renders) so the page reserves space for both. The element
 * is hidden on desktop (≥769px) so it only manifests on phones. */

@media (max-width: 768px) {
    .mobile-page-cta {
        position: fixed;
        /* Inset by 14px on both sides so the 4px hard offset shadow always
         * sits inside the viewport. Previously the inset matched the
         * shadow width (12 + 4 = 16) which left no breathing room on
         * very narrow iPhones — the shadow visibly clipped the rounded
         * Safari corners. */
        left: 14px;
        right: 14px;
        /* Stack above the 56px tab bar + safe area bottom inset. */
        bottom: calc(56px + env(safe-area-inset-bottom, 0) + 12px);
        z-index: 95;
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 0.4rem;
        padding: 0.95rem 1.1rem;
        min-height: 52px;
        background: var(--primary, #00C853);
        color: #000;
        font-weight: 800;
        font-size: 1.02rem;
        text-decoration: none;
        /* Soft corners, thick black border, hard offset shadow. */
        border-radius: 10px;
        border: 2px solid #000;
        box-shadow: 3px 3px 0 #000;
        /* Reveal-on-scroll: hidden at the top of the page (where the inline
         * subscribe card already shows) and slides up once the user scrolls
         * into the content. body.cta-revealed is toggled by header-auth.js.
         * This keeps the bottom uncluttered instead of a permanent bar. */
        transform: translateY(calc(100% + 28px));
        opacity: 0;
        pointer-events: none;
        transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.2s ease, box-shadow 0.08s ease-out;
        -webkit-tap-highlight-color: transparent;
        /* Defensive — make sure the element can't push wider than its
         * computed left/right insets even if its inner text is forced
         * onto one line. */
        box-sizing: border-box;
        /* The element is a <button> (it triggers an in-page subscribe
         * action, not navigation). Reset native button chrome and let the
         * fixed left/right insets define its width like the old <a>. */
        -webkit-appearance: none;
        appearance: none;
        width: auto;
        font-family: inherit;
        text-align: center;
        cursor: pointer;
    }
    /* Revealed — slid up into view once the user has scrolled in. */
    body.cta-revealed .mobile-page-cta {
        transform: translateY(0);
        opacity: 1;
        pointer-events: auto;
    }
    body.cta-revealed .mobile-page-cta:active {
        transform: translate(2px, 2px);
        box-shadow: 2px 2px 0 #000;
    }
    .mobile-page-cta:hover {
        color: #000;
    }
    /* Lift the support chat bubble above the CTA only while the CTA is
     * revealed, so they never overlap. The bubble is #scsc-fab (an id) —
     * the old `.sc-support-fab` rules above never matched it, which is why
     * it sat on top of the subscribe button. Its own component CSS pins it
     * at bottom: 56+safe+12; here we push it up by the CTA's height (~64px)
     * while revealed, and let it drop back when the CTA hides. */
    #scsc-fab {
        transition: bottom 0.3s cubic-bezier(0.22, 1, 0.36, 1), transform 0.15s ease;
    }
    body.cta-revealed #scsc-fab {
        bottom: calc(56px + env(safe-area-inset-bottom, 0px) + 12px + 64px) !important;
    }
    /* Pre-hydration state: header-auth.js injects the button disabled and
     * the page script enables it once it knows what action to perform. */
    .mobile-page-cta:disabled {
        background: #d6d6d6;
        color: #6b6b6b;
        cursor: default;
        box-shadow: 3px 3px 0 #9a9a9a;
    }
    .mobile-page-cta:disabled:active {
        transform: none;
        box-shadow: 3px 3px 0 #9a9a9a;
    }

    /* Pages with a sticky CTA need extra bottom padding so trailing
     * content doesn't sit hidden under it. The class is added by
     * header-auth.js when it detects a .mobile-page-cta in the page. */
    body.has-mobile-page-cta {
        padding-bottom: calc(56px + env(safe-area-inset-bottom, 0) + 80px);
    }
}

/* Hide cleanly on desktop. */
@media (min-width: 769px) {
    .mobile-page-cta { display: none; }
}


/* ─── Top-right "…" page-actions button ────────────────────────────────
 *
 * Floats above the page in the top-right corner on mobile. Tapping it
 * opens a bottom sheet (built dynamically by page-actions.js) listing
 * the actions registered for that page — Share is always there; pages
 * register their own context-specific actions via
 * window.registerPageAction({ label, onClick, … }).
 *
 * Hidden on desktop (≥769px) since the same actions are reachable
 * via the regular header / cmd-key shortcuts on a real keyboard.
 *
 * Sits above the fixed header but below the bottom sheet. It is offset
 * left of the hamburger so both affordances remain tappable. */

.mobile-page-actions-btn {
    display: none; /* shown only at ≤768px */
}

@media (max-width: 768px) {
    .mobile-page-actions-btn {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: fixed;
        /* Top-right; lifted by safe-area-inset-top so the iPhone notch
         * / Dynamic Island doesn't crowd it. */
        top: calc(env(safe-area-inset-top, 0) + 10px);
        right: 62px;
        width: 40px;
        height: 40px;
        border: none;
        border-radius: 50%;
        background: #fff;
        color: #111;
        box-shadow: 0 0 0 2px #000, 3px 3px 0 #000;
        cursor: pointer;
        z-index: 1002;
        transition: transform 0.08s, background 0.15s;
        -webkit-tap-highlight-color: transparent;
    }

    .mobile-page-actions-btn:active {
        transform: scale(0.94);
        background: rgba(245, 245, 245, 0.95);
    }

    .mobile-page-actions-btn svg {
        fill: currentColor;
    }
}

/* On dark-themed pages (chat hero is full black), invert the button
 * for legibility. Pages opt in by setting data-page-theme="dark" on
 * <html> or <body>; the chat page does this in its head. */
@media (max-width: 768px) {
    [data-page-theme="dark"] .mobile-page-actions-btn,
    body[data-page-theme="dark"] .mobile-page-actions-btn {
        background: #000;
        color: #fff;
        box-shadow: 0 0 0 2px #fff, 3px 3px 0 #fff;
    }
}


/* ─── Page-action rows (rendered inside the bottom sheet) ──────────────
 *
 * iOS Settings-app style: full-width rows with an icon on the left,
 * label + optional secondary line, and a chevron on the right. The
 * destructive variant turns the label red — for "Sign out", "Delete",
 * etc. */

.page-actions-list {
    display: flex;
    flex-direction: column;
    gap: 0;
    margin: 0.25rem -1.25rem -0.5rem;
    /* Negative side margin pulls rows out to the panel edges so the
     * separator lines run full-width — matches iOS Settings. */
}

.page-action-row {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    width: 100%;
    padding: 0.95rem 1.25rem;
    background: transparent;
    border: none;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    text-align: left;
    cursor: pointer;
    font-family: inherit;
    transition: background 0.12s;
    -webkit-tap-highlight-color: transparent;
}

.page-action-row:last-child {
    border-bottom: none;
}

.page-action-row:active {
    background: rgba(0, 0, 0, 0.04);
}

.page-action-row.is-destructive .page-action-label {
    color: #dc2626;
}

.page-action-icon {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.05rem;
    color: #000;
    background: #fefce8;
    border-radius: 0;
}

.page-action-icon-empty {
    /* Placeholder so action rows without icons still align text. */
    background: transparent;
}

.page-action-text {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}

.page-action-label {
    font-size: 0.96rem;
    font-weight: 600;
    color: rgba(0, 0, 0, 0.88);
    line-height: 1.25;
}

.page-action-desc {
    font-size: 0.78rem;
    color: rgba(0, 0, 0, 0.55);
    line-height: 1.35;
}

.page-action-chevron {
    flex-shrink: 0;
    color: rgba(0, 0, 0, 0.28);
    font-size: 1.4rem;
    line-height: 1;
    margin-left: 0.25rem;
}


/* ─── Toast (used by the Share action's clipboard fallback) ────────────
 *
 * Lightweight in-page status message. Slides down from the top, lives
 * for ~1.8s, fades out. Fixed-position so it stacks above the … button
 * + tab bar. Used by page-actions.js when navigator.share isn't
 * available. */

.mobile-page-toast {
    position: fixed;
    top: calc(env(safe-area-inset-top, 0) + 12px);
    left: 50%;
    transform: translate(-50%, -8px);
    z-index: 10001;
    padding: 0.65rem 1rem;
    background: #000;
    color: #fff;
    font-size: 0.88rem;
    font-weight: 700;
    border-radius: 0;
    border: 1.5px solid #000;
    box-shadow: 3px 3px 0 #00C853;
    opacity: 0;
    transition: opacity 0.22s ease-out, transform 0.22s cubic-bezier(0.2, 0, 0, 1);
    pointer-events: none;
}


/* ─── Native-feel polish: focus rings, overscroll, callouts ────────────
 *
 * The iOS Safari + Chrome behaviors below make a web app feel like a
 * web page when they fire. Suppress them globally so taps are clean.
 *
 * Notes:
 * - `:focus` is replaced with `:focus-visible` so the blue outline only
 *   shows up for keyboard users. Without this every tap leaves a
 *   stuck outline on the last-tapped button.
 * - `overscroll-behavior: none` only inside `(display-mode: standalone)`
 *   so the page still behaves like a web page in browser mode (where
 *   pull-to-refresh is expected).
 * - `-webkit-touch-callout: none` on chrome / interactive elements
 *   stops the long-press preview popover. Re-enabled on `<p>`, `<a>`
 *   inside article bodies so links and quoted text remain selectable.
 */

button,
.btn,
.btn-continue,
[role="button"],
.tappable,
.tab-item,
.lp-team-link,
.blog-card,
.mobile-page-actions-btn,
.hamburger-btn,
.mobile-sticky-action,
.mobile-sticky-stack-primary,
.mobile-sticky-secondary {
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}

/* Replace the always-on focus ring with `:focus-visible`. The negation
 * keeps assistive-tech users on keyboards getting a focus ring, while
 * mouse / touch users don't see the lingering outline. */
button:focus,
.btn:focus,
.btn-continue:focus,
[role="button"]:focus,
.tappable:focus,
input[type="submit"]:focus,
input[type="button"]:focus {
    outline: none;
}

button:focus-visible,
.btn:focus-visible,
.btn-continue:focus-visible,
[role="button"]:focus-visible,
.tappable:focus-visible {
    outline: 1.5px solid #00C853;
    outline-offset: 2px;
}

/* Kill rubber-band scroll only when running as an installed PWA. In
 * a regular browser tab pull-to-refresh is expected; in standalone
 * mode the bounce immediately gives away that it's "just a website". */
@media (display-mode: standalone) {
    html, body {
        overscroll-behavior: none;
    }
    /* Standalone mode: respect the top safe-area inset (notch / status
     * bar) so the header doesn't sit underneath it. Not applied in
     * browser mode because Safari already reserves room for the URL
     * bar. */
    body {
        padding-top: env(safe-area-inset-top, 0);
    }
}

/* Smooth fixed-position scrolling on iOS — without this any
 * .overflow-y:auto element scrolls in lurches. */
.scroll-y,
.bottom-sheet-panel,
.team-list,
.results-list {
    -webkit-overflow-scrolling: touch;
}

body.bottom-sheet-open .sc-support-fab,
body.bottom-sheet-open .sc-support-panel,
body.bottom-sheet-open #scSupportFab,
body.bottom-sheet-open #chat-toggle-btn,
body.bottom-sheet-open #sc-fab,
body.bottom-sheet-open #sc-panel {
    display: none !important;
}

.mobile-page-toast.is-visible {
    opacity: 1;
    transform: translate(-50%, 0);
}

@media (prefers-reduced-motion: reduce) {
    .mobile-page-toast {
        transition: opacity 0.12s linear !important;
        transform: translate(-50%, 0) !important;
    }
}


/* ─── Misc iOS polish ──────────────────────────────────────────────── */

@media (max-width: 768px) {
    /* Smooth iOS-style momentum scroll on common scroll containers. */
    body,
    .scroll-container,
    .modal-body,
    .funnel-step,
    .preview-content {
        -webkit-overflow-scrolling: touch;
    }

    /* Prevent the body from rubber-banding when content is short.
     * Apps don't bounce the whole viewport when there's nothing to
     * scroll — only their inner scroll containers do. */
    html, body {
        overscroll-behavior-y: none;
    }
}

/* Skip-to-content target for the tab bar (a11y). Matches the rest of
 * the focus-style system already in use. */
.mobile-tab:focus-visible {
    outline: 1.5px solid var(--primary, #00C853);
    outline-offset: -4px;
    border-radius: 0;
}

.mobile-sticky-cta > *:focus-visible {
    outline: 1.5px solid var(--primary, #00C853);
    outline-offset: 2px;
}
