:root {
    --brand-navy: #0b3a63;
    --brand-river: #1f6fa5;
    --brand-sky: #5aa7d6;
    --brand-accent: #d96a1d;
    --brand-gold: #e08a3a;
    --brand-sunset-soft: #ffb347;
    --brand-sunset-deep: #c94b16;
    --brand-sunset-start: #c96a2b;
    --brand-sunset-end: #e08a3a;
    --brand-sunset-shadow: rgba(201, 106, 43, 0.25);
    --brand-ink: #0a2239;
    --surface: #0b1726;
    --surface-soft: #10253d;
    --surface-card: rgba(10, 28, 47, 0.82);
    --copy: #dce9f5;
    --copy-muted: #a8bfd6;
    --line: rgba(255, 255, 255, 0.08);
}

/* ------------------------------
   Motion
   IMPORTANT: the `to` state uses `transform: none` rather than the equivalent
   `translateY(0)` / `scale(1) translateY(0)`. With `animation-fill-mode: both`
   (set on .page-animate etc.), the `to` styles persist forever after the
   animation finishes. Any non-`none` transform on an ancestor creates a new
   containing block — which breaks `position: fixed` for any descendant modal
   (the modal ends up positioned relative to the page-animated container,
   not the viewport, so scrolling moves it out of view). `transform: none`
   has identical visual end-state but doesn't create a stacking context.
------------------------------ */
@keyframes fade-slide-up {
    from {
        opacity: 0;
        transform: translateY(18px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes fade-slide-right {
    from {
        opacity: 0;
        transform: translateX(-18px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes fade-slide-left {
    from {
        opacity: 0;
        transform: translateX(18px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

@keyframes soft-pop-in {
    from {
        opacity: 0;
        transform: scale(.98) translateY(10px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* ------------------------------
   Base
------------------------------ */
html,
body {
    font-family: 'Inter', sans-serif;
    background: radial-gradient(circle at top, rgba(201, 106, 43, 0.08) 0%, rgba(201, 106, 43, 0) 24%), radial-gradient(circle at 80% 20%, rgba(224, 138, 58, 0.06) 0%, rgba(224, 138, 58, 0) 30%), linear-gradient(180deg, #0a1724 0%, #0c1d2d 45%, #091521 100%);
    color: var(--copy);
    /* B.5 — DO NOT add overflow-x:hidden or max-width:100vw here. Android
       WebView (used inside the MAUI BlazorWebView) treats overflow-x:hidden
       on html/body as "no scrolling at all" and breaks vertical scroll on
       the phone. The other defensive rules below (max-width: 100% on
       cards, overflow-wrap on text, min-width: 0 on biz-form-row) are
       enough to prevent horizontal overflow without touching the document
       scroll behaviour. */
}

/* B.5 — ensure all branded cards / panels respect their grid column width
   on narrow viewports (no element should bleed past its container). */
.section-card,
.club-panel,
.aircraft-card,
.contact-card,
.price-card {
    max-width: 100%;
    min-width: 0;
    box-sizing: border-box;
}

/* B.5 — break long words / URLs inside copy blocks instead of letting them
   force horizontal overflow. Uses overflow-wrap: break-word (only breaks at
   word boundaries when truly necessary) NOT "anywhere" which splits even
   individual letters and was making short capitalised words like "AIRCRAFT"
   render one letter per line in narrow table columns. */
.card-copy,
.card-title,
.section-copy,
.section-title,
.page-title,
h1, h2, h3, h4, h5 {
    overflow-wrap: break-word;
    word-break: normal;
}

/* B.5 — biz-form-row (the inline form bar used on every report page) wraps
   to a new line as the viewport narrows. Make sure each field can shrink to
   the available width rather than forcing the row to overflow. */
.biz-form-row {
    max-width: 100%;
    min-width: 0;
}

.biz-form-row > .biz-field {
    min-width: 0;
    max-width: 100%;
}

a,
.btn-link {
    color: var(--brand-sky);
}

    a:hover {
        color: #8ec7ea;
    }

h1,
h2,
h3,
h4,
h5 {
    color: #fff;
    font-weight: 800;
}

.page-animate {
    animation: fade-slide-up .55s ease-out both;
}

.shell-container,
.page-shell {
    width: min(1180px, calc(100% - 2rem));
    margin: 0 auto;
}

/* ------------------------------
   Shared Buttons / Utility
------------------------------ */
.eyebrow {
    text-transform: uppercase;
    letter-spacing: 0.2em;
    font-size: 0.8rem;
    color: var(--brand-gold);
    font-weight: 700;
    margin-bottom: 1rem;
}

.section-spacing {
    padding-top: 2rem;
}

/* Phase B.5 — vertical stack for multiple section-cards on a single page (Privacy
   Policy, About, etc.). Wrap successive <article class="section-card"> children
   in <div class="section-stack"> to space them apart without touching the
   card's own internal padding. */
.section-stack {
    display: grid;
    gap: 1.5rem;
}

.page-header {
    padding-top: 4rem;
}

.page-title {
    font-size: clamp(2rem, 4vw, 3.2rem);
    margin-bottom: 0.75rem;
}

/* Phase B.5 — Blazor's enhanced navigation programmatically focuses headings
   after route changes (for screen-reader users). Browsers differ on whether
   that programmatic focus also matches :focus-visible — Firefox does, Chrome
   doesn't — which is why a blue outline kept reappearing on Firefox.
   Since these headings aren't interactive (you can't tab into them, they're
   decorative landmarks), suppress the visible focus ring on BOTH :focus and
   :focus-visible. Screen-reader behaviour is unaffected — the focus event
   still fires for assistive technology to announce the new heading. */
.page-title:focus,
.page-title:focus-visible,
.section-title:focus,
.section-title:focus-visible,
.card-title:focus,
.card-title:focus-visible {
    outline: none;
    box-shadow: none;
}

.hero-copy,
.section-copy {
    max-width: 70ch;
    color: var(--copy-muted);
    font-size: 1.05rem;
    line-height: 1.8;
    margin-top: 1rem;
}

.hero-actions,
.inline-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    margin-top: 1.5rem;
}

.hero-title,
.hero-title:focus,
.hero-title:focus-visible {
    outline: none !important;
    border: none !important;
    box-shadow: none !important;
    position: relative;
    z-index: 1;
    backface-visibility: hidden;
    transform: translateZ(0);
}

.btn-primary-brand,
.btn-secondary-brand {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    text-decoration: none;
    border-radius: 999px;
    font-weight: 700;
    /* Condensed (UI consistency pass): was .95rem/1.4rem — oversized next
       to the new 44px inputs. */
    padding: 0.6rem 1.15rem;
    font-size: .95rem;
    transition: transform .2s ease, box-shadow .2s ease, opacity .2s ease, background .2s ease;
    border: none;
    cursor: pointer;
    will-change: transform;
}

    .btn-primary-brand:hover,
    .btn-secondary-brand:hover {
        transform: translateY(-2px);
    }

.btn-primary-brand {
    color: #fff;
    background: linear-gradient( 135deg, var(--brand-sunset-start) 0%, var(--brand-sunset-end) 100% );
    box-shadow: 0 10px 25px var(--brand-sunset-shadow), 0 0 0 rgba(0,0,0,0);
}

    .btn-primary-brand:hover {
        box-shadow: 0 16px 34px rgba(201, 106, 43, 0.30);
    }

.btn-secondary-brand {
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.14);
    background: rgba(255, 255, 255, 0.04);
}

    .btn-secondary-brand:hover {
        box-shadow: 0 12px 26px rgba(0,0,0,.16);
    }

/* Phase B.5 — global disabled state for branded buttons so every
   <button disabled> looks visibly inert (faded + not-allowed cursor) and
   doesn't lift on hover. Mirrors the per-page rule used on the booking page;
   promoting it here means Sell & Snapshot, Apply filter, Save record, etc.,
   all behave consistently without each page needing its own override. */
.btn-primary-brand:disabled,
.btn-primary-brand[disabled],
.btn-secondary-brand:disabled,
.btn-secondary-brand[disabled] {
    opacity: .5;
    cursor: not-allowed;
    box-shadow: none;
    filter: grayscale(.4);
    pointer-events: none;     /* belt-and-braces: even if a child intercepts the
                                 click, the disabled flag still blocks it. */
}

.btn-primary-brand:disabled:hover,
.btn-primary-brand[disabled]:hover,
.btn-secondary-brand:disabled:hover,
.btn-secondary-brand[disabled]:hover {
    transform: none;
    box-shadow: none;
}

.brand-nav {
    animation: fade-slide-up .45s ease-out both;
}

.site-footer {
    animation: fade-slide-up .55s ease-out both;
}

/* ============================================================================
   Phase B.5 — Blazor Server reconnect modal: HIDDEN COMPLETELY.
   The default Blazor overlay is a full-screen translucent wash with a
   spinner mounted top-centre, which feels jarring because it fires on
   routine form submits and keep-alive checks. We hide it entirely; if
   Blazor actually cannot reconnect, the inline script in App.razor
   triggers a full location.reload() automatically, so the user lands on
   a fresh page rather than a "disconnected" overlay.
   Override is intentionally aggressive ( !important + all pseudo-elements )
   because Blazor's framework injects inline styles for some of these.
   ============================================================================ */
#components-reconnect-modal,
#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-hide,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-rejected {
    display: none !important;
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
}
#components-reconnect-modal::before,
#components-reconnect-modal::after {
    display: none !important;
    content: none !important;
}

/* Phase B.5 — fine-print bar at the very bottom of the footer for copyright +
   legal links (Privacy, Terms, Cookies). Smaller and dimmer than the rest of
   the footer, sits below the main columns with a thin divider. Standard
   "fine print" pattern across most commercial sites. */
.footer-fineprint {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: center;
    gap: .5rem 1rem;
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid rgba(255, 255, 255, .08);
    color: rgba(203, 213, 225, .55);
    font-size: .78rem;
}

.footer-fineprint-links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.footer-fineprint a {
    color: rgba(203, 213, 225, .65);
    text-decoration: none;
    transition: color .18s ease;
}

.footer-fineprint a:hover {
    color: #fff;
    text-decoration: underline;
}

/* ------------------------------
   Layout Grids
------------------------------ */
.grid-3,
.grid-4,
.stats-grid,
.gallery-grid,
.pricing-grid,
.contact-grid {
    display: grid;
    gap: 1.25rem;
}

.grid-3,
.pricing-grid,
.contact-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
}

.grid-4,
.gallery-grid,
.stats-grid {
    grid-template-columns: repeat(4, minmax(0, 1fr));
}

/* ------------------------------
   Cards
------------------------------ */
.section-card,
.feature-card,
.stat-card,
.aircraft-card,
.gallery-card,
.price-card,
.contact-card {
    border: 1px solid var(--line);
    border-radius: 28px;
    background: var(--surface-card);
    padding: 1.4rem;
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18), 0 24px 60px rgba(0, 0, 0, 0.12), inset 0 1px 0 rgba(255,255,255,0.03);
    transition: transform .24s ease, box-shadow .24s ease, border-color .24s ease, background .24s ease;
    transform: translateY(0);
    will-change: transform;
}

    .section-card:hover,
    .feature-card:hover,
    .stat-card:hover,
    .aircraft-card:hover,
    .gallery-card:hover,
    .price-card:hover,
    .contact-card:hover {
        transform: translateY(-6px);
        box-shadow: 0 16px 30px rgba(0, 0, 0, 0.22), 0 34px 80px rgba(0, 0, 0, 0.18), inset 0 1px 0 rgba(255,255,255,0.04);
        border-color: rgba(255,255,255,0.14);
    }

/* Phase B.5 — when a card is rendered as an <a> (clickable tile pattern, used
   on the prices page → /contact), neutralise default link styling so it looks
   identical to the non-clickable <article> version, while inheriting the
   hover-lift behaviour from the rule above. */
a.price-card,
a.price-card--clickable {
    text-decoration: none;
    color: inherit;
    display: block;
    cursor: pointer;
}
a.price-card:hover,
a.price-card--clickable:hover {
    /* Strengthen the brand orange border on hover so it's clear this is clickable. */
    border-color: rgba(249, 115, 22, .55);
}

.card-kicker {
    color: var(--brand-gold);
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    font-weight: 700;
}

.card-title {
    color: #fff;
    margin: 0.6rem 0;
}

.card-copy,
.price-points li,
.contact-list li,
.auth-helper {
    color: var(--copy-muted);
}

.stats-grid .stat-card h3 {
    font-size: 2rem;
    margin-bottom: 0.25rem;
}

/* ------------------------------
   Hero Section
------------------------------ */
.hero-section {
    /* B.5 — top padding halved (was 5.5rem) to tighten the gap between the
       sticky nav and the start of the page content. Bottom padding kept at
       4rem so the hero still has breathing room before the next section. */
    padding: 2.75rem 0 4rem;
}

.hero-panel {
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 32px;
    padding: 3rem;
    background: linear-gradient( 135deg, rgba(15, 48, 79, 0.96) 0%, rgba(16, 67, 110, 0.92) 48%, rgba(201, 75, 22, 0.14) 100% ), radial-gradient( circle at top right, rgba(224, 138, 58, 0.12) 0%, rgba(224, 138, 58, 0) 34% );
    box-shadow: 0 24px 80px rgba(0, 0, 0, 0.24), inset 0 1px 0 rgba(255,255,255,0.04);
}

.hero-layout {
    display: grid;
    grid-template-columns: 1.2fr 0.8fr;
    gap: 1.25rem;
    align-items: start;
}

.hero-title {
    max-width: 18ch;
    font-size: clamp(2.5rem, 5vw, 3rem);
    line-height: 1.02;
    margin-bottom: 1rem;
}

.hero-side-panel {
    align-self: start;
    border-radius: 28px;
    padding: 1.5rem;
    background: linear-gradient( 180deg, rgba(255,255,255,0.10), rgba(255,255,255,0.05) );
    border: 1px solid rgba(255,255,255,0.12);
    box-shadow: 0 18px 40px rgba(0,0,0,.14), inset 0 1px 0 rgba(255,255,255,.06);
    backdrop-filter: blur(14px);
}

.hero-feature-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.hero-feature-card {
    min-height: 0;
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 24px;
    background: linear-gradient( 180deg, rgba(9, 39, 66, 0.86), rgba(8, 31, 54, 0.78) );
    padding: 1.15rem;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    overflow: hidden;
    word-break: normal;
    overflow-wrap: anywhere;
    box-shadow: 0 10px 24px rgba(0,0,0,.14), 0 18px 40px rgba(0,0,0,.10), inset 0 1px 0 rgba(255,255,255,.04);
    transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease, background .22s ease;
    transform: translateY(0);
    will-change: transform;
    animation: soft-pop-in .55s ease-out both;
}

    .hero-feature-card:hover {
        transform: translateY(-7px) scale(1.01);
        border-color: rgba(255,255,255,.16);
        box-shadow: 0 16px 28px rgba(0,0,0,.20), 0 28px 64px rgba(0,0,0,.16), inset 0 1px 0 rgba(255,255,255,.05);
    }

    .hero-feature-card:nth-child(1) {
        animation-delay: .05s;
    }

    .hero-feature-card:nth-child(2) {
        animation-delay: .12s;
    }

    .hero-feature-card:nth-child(3) {
        animation-delay: .19s;
    }

    .hero-feature-card:nth-child(4) {
        animation-delay: .26s;
    }

.hero-panel > * {
    animation: fade-slide-up .6s ease-out both;
}

    .hero-panel > *:nth-child(1) {
        animation-delay: .04s;
    }

    .hero-panel > *:nth-child(2) {
        animation-delay: .12s;
    }

    .hero-panel > *:nth-child(3) {
        animation-delay: .20s;
    }

.hero-feature-title {
    font-size: clamp(0.98rem, 1.45vw, 1.2rem);
    line-height: 1.08;
    margin: 0 0 0.5rem 0;
    color: #fff;
    word-break: keep-all;
    overflow-wrap: normal;
    hyphens: manual;
}
.hero-feature-copy {
    color: var(--copy-muted);
    line-height: 1.5;
    font-size: clamp(0.88rem, 1.05vw, 0.95rem);
    overflow-wrap: normal;
}

.hero-content-column {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    padding-top: 0.35rem;
}

.hero-brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 2rem;
}

.hero-logo {
    width: 48px;
    height: 48px;
    object-fit: contain;
    flex: 0 0 auto;
    filter: drop-shadow(0 6px 14px rgba(0,0,0,.18));
}

.section-intro {
    margin-bottom: 1.75rem;
    max-width: 760px;
}
.section-intro--compact {
    max-width: 680px;
}

.section-title {
    font-size: clamp(1.8rem, 3vw, 2.7rem);
    line-height: 1.15;
    margin: 3rem 0 1rem;
    color: #fff;
}
.section-title--compact {
    font-size: clamp(1.55rem, 2.6vw, 2.25rem);
    max-width: 18ch;
}

.section-grid-top {
    margin-top: 1.25rem;
}

.quick-panel {
    min-height: 220px;
}

/* ------------------------------
   Lists
------------------------------ */
.check-list,
.price-points,
.contact-list {
    margin: 0;
    padding-left: 1.1rem;
}

.booking-note {
    border-left: 4px solid var(--brand-gold);
}

/* ------------------------------
   Auth Pages
------------------------------ */
.auth-page {
    padding: 4rem 1.25rem 5rem;
}

.auth-shell {
    max-width: 560px;
    margin: 0 auto;
}

.auth-card {
    background: linear-gradient(180deg, rgba(10, 33, 57, 0.92), rgba(8, 26, 45, 0.88));
    border: 1px solid rgba(255, 255, 255, 0.08);
    border-radius: 28px;
    padding: 2rem;
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.28), inset 0 1px 0 rgba(255, 255, 255, 0.04);
    backdrop-filter: blur(12px);
}

.auth-card--small {
    text-align: center;
}

.auth-eyebrow {
    margin: 0 0 0.75rem;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--brand-gold);
}

.auth-title {
    margin: 0 0 0.75rem;
    font-size: clamp(2rem, 4vw, 2.75rem);
    line-height: 1.05;
    color: #fff;
}

.auth-copy {
    margin: 0 0 1.75rem;
    color: rgba(255, 255, 255, 0.78);
    line-height: 1.7;
    font-size: 1rem;
}

.auth-form {
    display: grid;
    gap: 1rem;
}

.auth-field {
    display: grid;
    gap: 0.45rem;
}

/* B.5 — small inline hint shown under a form field (e.g. the password rules
   under the password input on /register). Subdued so it reads as guidance,
   not validation error. */
.auth-hint {
    color: rgba(203, 213, 225, 0.72);
    font-size: 0.82rem;
    line-height: 1.4;
    margin-top: -0.1rem;
}
.auth-hint strong {
    color: rgba(241, 245, 249, 0.92);
    font-weight: 700;
}

    .auth-field label {
        color: #fff;
        font-size: 0.95rem;
        font-weight: 600;
    }

.auth-input {
    width: 100%;
    min-height: 54px;
    padding: 0.9rem 1rem;
    border-radius: 16px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: rgba(255, 255, 255, 0.06);
    color: #fff;
    outline: none;
    transition: border-color 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
    box-sizing: border-box;
    font: inherit;
}

    .auth-input:focus {
        border-color: rgba(217, 106, 29, 0.8);
        box-shadow: 0 0 0 4px rgba(217, 106, 29, 0.12);
        background: rgba(255, 255, 255, 0.08);
    }

.auth-options {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 0.25rem;
}

.auth-checkbox {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    color: rgba(255, 255, 255, 0.82);
    font-size: 0.95rem;
}

.auth-error {
    border: 1px solid rgba(248, 113, 113, 0.3);
    background: rgba(127, 29, 29, 0.22);
    color: #fecaca;
    border-radius: 14px;
    padding: 0.9rem 1rem;
    line-height: 1.5;
}

/* Phase B.8 — post-redirect banner on /login, used by the email confirmation
   flow. Success variant is green so the just-clicked-confirm-link user gets
   positive feedback; error variant matches .auth-error for consistency. */
.auth-banner {
    border-radius: 14px;
    padding: 0.9rem 1rem;
    line-height: 1.5;
    margin: 0 0 1rem;
    font-size: 0.95rem;
}

.auth-banner strong {
    display: block;
    margin-bottom: 0.15rem;
}

.auth-banner--success {
    border: 1px solid rgba(34, 197, 94, 0.32);
    background: rgba(22, 101, 52, 0.18);
    color: #bbf7d0;
}

.auth-banner--error {
    border: 1px solid rgba(248, 113, 113, 0.3);
    background: rgba(127, 29, 29, 0.22);
    color: #fecaca;
}

.auth-submit {
    min-height: 54px;
    border-radius: 16px;
    font-weight: 700;
    font-size: 1rem;
    margin-top: 0.25rem;
}

.auth-footer {
    margin-top: 1.4rem;
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.96rem;
}

    .auth-footer a {
        color: #fff;
        font-weight: 600;
        text-decoration: none;
    }

        .auth-footer a:hover {
            text-decoration: underline;
        }

.validation-message,
.validation-errors {
    color: #fecaca;
    font-size: 0.9rem;
}

/* ------------------------------
   About page
------------------------------ */

.values-list {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-top: 0.75rem;
}

.value-item {
    padding-bottom: 0.9rem;
    border-bottom: 1px solid rgba(255,255,255,0.08);
}

    .value-item:last-child {
        border-bottom: none;
        padding-bottom: 0;
    }

.value-title {
    font-weight: 600;
    color: #fff;
    margin-bottom: 0.2rem;
    font-size: 0.95rem;
    letter-spacing: 0.2px;
}

.value-description {
    color: rgba(255,255,255,0.75);
    font-size: 0.9rem;
    line-height: 1.45;
}

/* ------------------------------
   Fleet page
------------------------------ */

.fleet-grid {
    align-items: stretch;
}

.fleet-card {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-height: 100%;
}

/* Phase B.5 — keep multi-word aircraft names like "Piper PA28-161 Warrior III"
   from breaking awkwardly across lines. `text-wrap: balance` asks the browser
   to even out the line lengths; combined with the non-breaking space between
   "Warrior" and "III" in fleet.json, the model designation stays together. */
.fleet-card .card-title {
    text-wrap: balance;
    word-break: keep-all;
}

.fleet-description {
    margin-bottom: 1.1rem;
}

.fleet-meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: auto;
}

.fleet-meta-item {
    display: inline-flex;
    flex-direction: column;
    gap: 0.2rem;
    padding: 0.75rem 0.9rem;
    min-width: 120px;
    border-radius: 16px;
    background: rgba(255,255,255,0.04);
    border: 1px solid rgba(255,255,255,0.08);
}

.fleet-meta-label {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: rgba(255,255,255,0.6);
    font-weight: 700;
}

.fleet-meta-value {
    color: #fff;
    font-weight: 600;
    font-size: 0.98rem;
}

/* ------------------------------
   Gallery
------------------------------ */

/* ------------------------------
   Gallery
------------------------------ */

/* ------------------------------
   Gallery
------------------------------ */

.gallery-stage-shell {
    display: grid;
    gap: 1.15rem;
}

.gallery-stage-card {
    position: relative;
    border-radius: 30px;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,0.08);
    background: rgba(10, 28, 47, 0.72);
    box-shadow: 0 18px 40px rgba(0,0,0,0.24), 0 38px 90px rgba(0,0,0,0.18), inset 0 1px 0 rgba(255,255,255,0.03);
    outline: none;
}

.gallery-stage-image-wrap {
    position: relative;
    /* Phase B.5 — every gallery image renders at the same visual size
       regardless of the source photo's dimensions; width stretches to fill
       the card / column. object-fit: cover on the <img> below crops portrait
       / landscape sources to fit without distortion.
       Height uses clamp() so it scales smoothly between a phone (240 px) and
       a desktop (560 px) without the awkward "huge image on a phone" effect
       the previous fixed 560 px caused at narrow widths. */
    width: 100%;
    height: clamp(240px, 50vw, 560px);
    max-height: 70vh;
    overflow: hidden;
    background: linear-gradient(135deg, rgba(15, 48, 79, 0.92), rgba(201, 106, 43, 0.18)), radial-gradient(circle at top right, rgba(224, 138, 58, 0.18), rgba(224, 138, 58, 0));
}

.gallery-stage-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    animation: gallery-stage-fade .45s ease;
    transform: scale(1);
    transition: transform .9s ease;
}

.gallery-stage-card:hover .gallery-stage-image {
    transform: scale(1.03);
}

.gallery-stage-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.78), rgba(0,0,0,0.14) 50%, rgba(0,0,0,0.04)), linear-gradient(to right, rgba(201,106,43,0.10), transparent 32%);
    pointer-events: none;
}

.gallery-stage-content {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    /* B.5 — padding scales with viewport so the overlay text doesn't
       crowd the card on small screens. */
    padding: clamp(0.9rem, 3vw, 1.9rem);
    z-index: 2;
    max-width: 760px;
    animation: gallery-stage-copy-in .45s ease;
}

.gallery-stage-kicker {
    margin: 0 0 0.45rem;
    color: var(--brand-gold);
    /* B.5 — scales 0.62rem → 0.8rem from phone to desktop. */
    font-size: clamp(0.62rem, 1.8vw, 0.8rem);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.16em;
}

.gallery-stage-title {
    margin: 0;
    color: #fff;
    /* B.5 — was clamp(2rem, 4vw, 3.2rem) which floored at 2rem (32px) on
       small phones — too big. Lower floor lets the headline fit on a
       narrow card without dominating it. */
    font-size: clamp(1.15rem, 5vw, 3.2rem);
    line-height: 1.1;
}

.gallery-stage-copy {
    margin: 0.7rem 0 0;
    color: rgba(255,255,255,0.84);
    /* B.5 — was fixed 1rem; now scales 0.85rem → 1rem with viewport. */
    font-size: clamp(0.85rem, 2.4vw, 1rem);
    line-height: 1.55;
    max-width: 60ch;
}

.gallery-stage-nav,
.gallery-stage-open {
    position: absolute;
    z-index: 3;
    border: 1px solid rgba(255,255,255,0.16);
    background: rgba(255,255,255,0.10);
    color: #fff;
    backdrop-filter: blur(10px);
    transition: transform 0.22s ease, background 0.22s ease, box-shadow 0.22s ease, opacity 0.22s ease;
}

    .gallery-stage-nav:hover {
        transform: translateY(-50%) scale(1.03);
        background: rgba(255,255,255,0.16);
        box-shadow: 0 10px 24px rgba(0,0,0,0.18);
    }

    .gallery-stage-open:hover {
        transform: translateY(-1px);
        background: rgba(255,255,255,0.16);
        box-shadow: 0 10px 24px rgba(0,0,0,0.18);
    }

.gallery-stage-nav {
    top: 50%;
    transform: translateY(-50%);
    width: 3.4rem;
    height: 3.4rem;
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    cursor: pointer;
    opacity: .88;
}

.gallery-stage-nav--prev {
    left: 1rem;
}

.gallery-stage-nav--next {
    right: 1rem;
}

.gallery-stage-open {
    right: 1rem;
    bottom: 1rem;
    border-radius: 999px;
    padding: 0.74rem 1rem;
    font-size: 0.9rem;
    font-weight: 700;
    cursor: pointer;
}

.gallery-thumbs {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 1rem;
}

.gallery-thumb {
    position: relative;
    border-radius: 20px;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,0.08);
    background: rgba(10, 28, 47, 0.7);
    padding: 0;
    cursor: pointer;
    text-align: left;
    color: inherit;
    font: inherit;
    transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease, opacity .24s ease, filter .24s ease;
    box-shadow: 0 10px 24px rgba(0,0,0,0.18), inset 0 1px 0 rgba(255,255,255,0.03);
}

    .gallery-thumb:hover {
        transform: translateY(-4px);
        border-color: rgba(255,255,255,0.14);
        box-shadow: 0 16px 28px rgba(0,0,0,0.22), inset 0 1px 0 rgba(255,255,255,0.04);
    }

    .gallery-thumb.is-active {
        transform: translateY(-2px) scale(1.02);
        border-color: rgba(224,138,58,0.55);
        box-shadow: 0 18px 32px rgba(0,0,0,0.24), 0 0 0 2px rgba(224,138,58,0.22), inset 0 1px 0 rgba(255,255,255,0.06);
    }

.gallery-thumb-image {
    width: 100%;
    height: 160px;
    object-fit: cover;
    display: block;
    transition: transform .5s ease, opacity .3s ease, filter .3s ease;
}

.gallery-thumb:hover .gallery-thumb-image {
    transform: scale(1.06);
}

.gallery-thumb.is-active .gallery-thumb-image {
    filter: saturate(1.05) contrast(1.02);
}

.gallery-thumb-overlay {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0,0,0,0.76), rgba(0,0,0,0.08) 55%, transparent), linear-gradient(to right, rgba(201,106,43,0.10), transparent);
    pointer-events: none;
}

.gallery-thumb-caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 0.9rem 1rem;
    color: #fff;
    font-size: 0.95rem;
    font-weight: 700;
    z-index: 2;
}

/* Lightbox */

.gallery-lightbox {
    position: fixed;
    inset: 0;
    z-index: 10000;
    background: rgba(3, 10, 18, 0.90);
    backdrop-filter: blur(14px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.2rem;
    animation: gallery-lightbox-in .22s ease;
}

.gallery-lightbox-dialog {
    position: relative;
    width: min(1180px, 100%);
    max-height: 92vh;
    outline: none;
}

.gallery-lightbox-figure {
    margin: 0;
    overflow: hidden;
    border-radius: 28px;
    border: 1px solid rgba(255,255,255,0.10);
    background: rgba(8, 26, 45, 0.96);
    box-shadow: 0 24px 60px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.03);
}

.gallery-lightbox-image {
    width: 100%;
    max-height: 74vh;
    display: block;
    object-fit: contain;
    background: #08121c;
    animation: gallery-stage-fade .28s ease;
}

.gallery-lightbox-caption {
    padding: 1rem 1.15rem 1.2rem;
}

.gallery-lightbox-count {
    color: var(--brand-gold);
    font-size: 0.82rem;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    font-weight: 700;
    margin-bottom: 0.35rem;
}

.gallery-lightbox-title {
    margin: 0 0 0.35rem;
    color: #fff;
    font-size: clamp(1.2rem, 2.2vw, 1.6rem);
    line-height: 1.15;
}

.gallery-lightbox-copy {
    margin: 0;
    color: rgba(255,255,255,0.78);
    line-height: 1.6;
}

.gallery-lightbox-close,
.gallery-lightbox-nav {
    position: absolute;
    border: none;
    cursor: pointer;
    color: #fff;
    background: rgba(255,255,255,0.10);
    border: 1px solid rgba(255,255,255,0.14);
    backdrop-filter: blur(10px);
    transition: transform 0.22s ease, background 0.22s ease;
}

    .gallery-lightbox-close:hover {
        transform: translateY(-1px);
        background: rgba(255,255,255,0.16);
    }

    .gallery-lightbox-nav:hover {
        transform: translateY(-50%) scale(1.03);
        background: rgba(255,255,255,0.16);
    }

.gallery-lightbox-close {
    top: -0.9rem;
    right: -0.9rem;
    width: 3rem;
    height: 3rem;
    border-radius: 999px;
    font-size: 1.5rem;
}

.gallery-lightbox-nav {
    top: 50%;
    transform: translateY(-50%);
    width: 3.2rem;
    height: 3.2rem;
    border-radius: 999px;
    font-size: 2rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.gallery-lightbox-nav--prev {
    left: -1.15rem;
}

.gallery-lightbox-nav--next {
    right: -1.15rem;
}

.gallery-stage-image--animated {
    animation: gallery-stage-fade .35s ease;
}

.gallery-stage-content--animated {
    animation: gallery-stage-copy-in .35s ease;
}

.gallery-stage-nav:focus-visible,
.gallery-stage-open:focus-visible,
.gallery-thumb:focus-visible,
.gallery-lightbox-close:focus-visible,
.gallery-lightbox-nav:focus-visible {
    outline: 3px solid rgba(224, 138, 58, 0.95);
    outline-offset: 3px;
    box-shadow: 0 0 0 6px rgba(224, 138, 58, 0.16);
}

@keyframes gallery-stage-fade {
    from {
        opacity: 0;
        transform: scale(1.015);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

@keyframes gallery-stage-copy-in {
    from {
        opacity: 0;
        transform: translateY(14px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes gallery-lightbox-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@media (max-width: 1024px) {
    .gallery-thumbs {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }

    /* B.5 — min-height removed; the new clamp() in the base rule handles
       responsive scaling from 240 px (phone) up to 560 px (desktop) smoothly,
       without conflicting hard minima that forced oversized cards on phones. */
}

@media (max-width: 900px) {

    .gallery-stage-content {
        padding: 1.15rem 1.15rem 1.3rem;
    }

    .gallery-stage-title {
        font-size: clamp(1.35rem, 7vw, 2rem);
    }

    .gallery-stage-copy {
        font-size: 0.92rem;
    }

    .gallery-thumbs {
        grid-template-columns: 1fr;
    }

    .gallery-thumb-image {
        height: 150px;
    }

    .gallery-stage-nav {
        width: 2.8rem;
        height: 2.8rem;
        font-size: 1.7rem;
    }

    .gallery-stage-nav--prev {
        left: 0.7rem;
    }

    .gallery-stage-nav--next {
        right: 0.7rem;
    }

    .gallery-stage-open {
        right: 0.7rem;
        bottom: 0.7rem;
        padding: 0.65rem 0.9rem;
        font-size: 0.82rem;
    }

    .gallery-lightbox {
        padding: 1rem;
    }

    .gallery-lightbox-nav {
        width: 2.8rem;
        height: 2.8rem;
        font-size: 1.8rem;
    }

    .gallery-lightbox-nav--prev {
        left: 0.3rem;
    }

    .gallery-lightbox-nav--next {
        right: 0.3rem;
    }
}

/* ------------------------------
   Responsive
------------------------------ */
@media (max-width: 1024px) {
    .grid-4,
    .stats-grid,
    .gallery-grid,
    .grid-3,
    .pricing-grid,
    .contact-grid,
    .hero-layout {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 720px) {
    .hero-panel {
        padding: 2rem;
    }

    .grid-4,
    .stats-grid,
    .gallery-grid,
    .grid-3,
    .pricing-grid,
    .contact-grid,
    .hero-layout {
        grid-template-columns: 1fr;
    }

    .hero-title {
        max-width: none;
    }
}

@media (max-width: 1024px) {
    .hero-feature-grid {
        grid-template-columns: 1fr;
    }

    .hero-side-panel {
        padding: 1.2rem;
    }

    .hero-feature-card {
        padding: 1rem;
    }
}

@media (max-width: 640px) {
    .auth-page {
        padding: 2rem 1rem 3rem;
    }

    .auth-card {
        padding: 1.35rem;
        border-radius: 22px;
    }

    .auth-title {
        font-size: 2rem;
    }

    .auth-input,
    .auth-submit {
        min-height: 50px;
    }
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation: none !important;
        transition: none !important;
        scroll-behavior: auto !important;
    }
}

/* =========================
   Shared club controls
   ========================= */

.club-panel {
    border-radius: 28px;
}

.club-panel--danger {
    border: 1px solid rgba(220, 38, 38, .18);
}

.club-panel-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
    flex-wrap: wrap;
}

.club-kicker {
    margin: 0 0 .35rem;
    color: var(--brand-gold);
    font-size: .78rem;
    font-weight: 800;
    letter-spacing: .14em;
    text-transform: uppercase;
}

.club-form,
.booking-form {
    display: grid;
    gap: 1rem;
}

.club-form-grid,
.booking-form-grid {
    display: grid;
    gap: 1rem;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    /* NOTE: an earlier consistency pass capped this at 900px, which left
       form grids floating inside full-width cards (looked broken on
       Aircraft blocks / Inventory / Sales / Profile). Width-capping must
       happen on the CARD, not the grid — use .section-card--narrow below
       on pages whose forms deserve a tighter column. */
}

/* Opt-in card width cap for form-centric pages (UI item 6). Apply to the
   section-card/club-panel WRAPPING a form to give the whole card — title,
   fields and buttons — a comfortable reading width on big screens. */
.section-card--narrow {
    max-width: 980px;
}

.club-field,
.booking-field {
    display: grid;
    gap: .48rem;
    grid-column: span 3;
    min-width: 0;
}

.club-field--wide,
.booking-field--wide {
    grid-column: 1 / -1;
}

/* Half-width helper for forms that want two fields side-by-side
   inside the 12-column .booking-form-grid — used by Sell Block. */
.club-field--half,
.booking-field--half {
    grid-column: span 6;
}

.club-field label,
.booking-field label,
.club-label {
    font-weight: 800;
    color: rgba(241, 245, 249, .98);
    font-size: .95rem;
    line-height: 1.2;
}


.club-textarea,
.booking-textarea {
    min-height: 120px;
    resize: vertical;
}

.booking-action-row,
.club-action-row {
    display: flex;
    flex-wrap: wrap;
    gap: .85rem;
    align-items: center;
    margin-top: .9rem;
}

.booking-secondary-button {
    /* Condensed (UI consistency pass) to match .btn-*-brand + 44px inputs. */
    min-height: 44px;
    padding: 0 1rem;
    border-radius: 999px;
    border: 1px solid rgba(148, 163, 184, .36);
    background: rgba(30, 41, 59, .92);
    color: #f8fafc;
    font-weight: 800;
    font-size: .95rem;
    transition: border-color .18s ease, background .18s ease, box-shadow .18s ease, transform .18s ease;
    white-space: nowrap;
    /* Phase B.10 — when this class is on an <a>, the browser defaults give us
       text-decoration: underline + display: inline (so the 54px min-height
       doesn't actually centre the text). These three rules make anchors
       behave the same as <button>s: no underline, content centred vertically
       and horizontally inside the pill. */
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

    .booking-secondary-button:hover,
    .booking-secondary-button:focus {
        text-decoration: none;
    }

.booking-secondary-button:hover {
    border-color: rgba(96, 165, 250, .55);
    background: rgba(37, 99, 235, .14);
    box-shadow: 0 10px 24px rgba(15, 23, 42, .18);
    transform: translateY(-1px);
}

.booking-secondary-button:active {
    transform: translateY(0);
}

.booking-help-text {
    color: rgba(226, 232, 240, .74);
    font-size: .92rem;
    max-width: 560px;
}

.booking-meta {
    color: rgba(226, 232, 240, .72);
}

.booking-colour-input {
    display: flex;
    align-items: stretch; /* swatch + text input share height */
    gap: 10px;
}

input[type="color"].booking-colour-picker {
    /* Swatch matches the sibling .booking-input dimensions — square the
       default height (56px) so it sits flush with the text input next to it.
       The compact form-grid override below shrinks it for the compact case. */
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    min-height: 56px;
    align-self: stretch;
    border-radius: 14px;
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: transparent;
    padding: 0;
    cursor: pointer;
    overflow: hidden;
}

    input[type="color"].booking-colour-picker::-webkit-color-swatch-wrapper {
        padding: 0;
    }

    input[type="color"].booking-colour-picker::-webkit-color-swatch {
        border: none;
        border-radius: 12px;
    }

/* Compact form-grid shrinks .booking-input to 36px — bring the colour swatch
   along so it doesn't tower over its sibling text input. */
.booking-form-grid--compact input[type="color"].booking-colour-picker,
.booking-form-grid--aircraft-blocks input[type="color"].booking-colour-picker {
    width: 36px !important;
    height: 36px !important;
    min-height: 36px !important;
    border-radius: 10px !important;
}

.club-input,
.club-select,
.club-textarea,
.booking-input {
    width: 100%;
    /* Condensed (UI consistency pass): was 56px tall with .95rem padding —
       comfortable but bulky, especially on phones. 44px keeps the Android
       touch-target minimum while reading far tighter. */
    min-height: 44px;
    border-radius: 14px;
    border: 1px solid rgba(148, 163, 184, .38);
    background: rgba(20, 35, 56, .92);
    color: #f8fafc;
    padding: .55rem .85rem;
    outline: none;
    box-sizing: border-box;
    transition: border-color .18s ease, box-shadow .18s ease, background .18s ease;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, .04), 0 0 0 1px rgba(255, 255, 255, .02);
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    font: inherit;
}

/* Native <select> dropdown items inherit very little from the parent's CSS —
   set explicit padding, colour, background, and stretched width on <option>
   so the popped-open list reads cleanly against the dark theme and each row
   fills the panel rather than collapsing to its label width. Applies to ALL
   native selects (some pages use plain <select>, others <select class="booking-input">
   via <InputSelect>) so styling is consistent everywhere. */
select option,
select optgroup {
    background: #142338;
    color: #f8fafc;
    padding: .55rem .75rem;
    font-size: .95rem;
    min-height: 36px;
    /* Stretch each option so its background / hover highlight spans the
       whole panel width on browsers that respect it (Chromium, WebKit). */
    width: 100%;
    min-width: 100%;
    box-sizing: border-box;
}

    .club-input::placeholder,
    .club-textarea::placeholder,
    .booking-input::placeholder {
        color: rgba(203, 213, 225, .46);
    }

    .club-input:focus,
    .club-select:focus,
    .club-textarea:focus,
    .booking-input:focus {
        border-color: rgba(96, 165, 250, .9);
        box-shadow: 0 0 0 4px rgba(59, 130, 246, .16);
        background: rgba(24, 41, 67, .98);
    }

    .club-input:hover,
    .club-select:hover,
    .club-textarea:hover,
    .booking-input:hover {
        border-color: rgba(96, 165, 250, .55);
    }

/* Date / time inputs — colour-scheme: dark forces the browser's native calendar
   widget to render in dark mode, and the calendar-picker-indicator filter inverts
   the default near-black icon to white so it's visible on the dark input. Applied
   to ALL type=date / time / datetime-local inputs (not just .booking-input) so
   the dashboard's plain `<input type="date">` controls inherit the styling too. */
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
    color-scheme: dark;
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="week"]::-webkit-calendar-picker-indicator {
    filter: invert(1) brightness(1.45);
    opacity: 1;
    cursor: pointer;
}

/* ============================================================
   Phase B.5 — Number input spinner styling
   By default browsers render an ugly white double-arrow spinner on number
   inputs that clashes with the dark theme. Style it as a single dark chevron
   pair that matches the rest of the design system.
============================================================ */
input[type="number"] {
    -moz-appearance: textfield;       /* Firefox: hide entirely so we draw our own */
    appearance: textfield;
}
/* WebKit / Blink: keep the spinner but restyle it.
   The spinner is split across ::-webkit-outer-spin-button (the wrapper) and
   ::-webkit-inner-spin-button (the actual up/down arrows). We can only really
   tweak the inner one; the outer takes layout space we can collapse. */
input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: inner-spin-button;
    opacity: 1;
    background: transparent;
    border-left: 1px solid rgba(255,255,255,.12);
    cursor: pointer;
    /* The system spinner doesn't accept full custom colours, but on Chromium
       we can hint dark by using color-scheme. */
    color-scheme: dark;
    width: 1.1rem;
    margin: 0;
    padding: 0;
    align-self: stretch;
}
input[type="number"]:hover::-webkit-inner-spin-button,
input[type="number"]:focus::-webkit-inner-spin-button {
    background: rgba(255,255,255,.06);
}

/* Select inputs */
select.booking-input,
select.club-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 3rem;
    background-image: linear-gradient(45deg, transparent 50%, rgba(255,255,255,.82) 50%), linear-gradient(135deg, rgba(255,255,255,.82) 50%, transparent 50%);
    background-position: calc(100% - 22px) calc(50% - 3px), calc(100% - 16px) calc(50% - 3px);
    background-size: 6px 6px, 6px 6px;
    background-repeat: no-repeat;
}

/* Phase B.10 — normalise every input + select inside the approvals
   form grid (Pending and Approved booking edit forms) to the same
   42px height. Lives in GLOBAL CSS rather than ClubApprovals.razor.css
   because Blazor's <InputSelect> renders a child-component <select>
   that doesn't carry the page's scoped CSS attribute — so a scoped
   selector matches the raw <select>s (Aircraft, Start time, Duration)
   but skips the InputSelect-backed ones (Member, Booking type,
   Instructor), leaving them stuck at the 56px global default. */
.approvals-form-grid select.booking-input,
.approvals-filter-grid select.booking-input,
.approvals-form-grid input.booking-input,
.approvals-filter-grid input.booking-input {
    min-height: 42px !important;
    height: 42px !important;
    max-height: 42px !important;
    padding: 0 .85rem !important;
    padding-right: 2.5rem !important;
    font-size: .92rem !important;
    box-sizing: border-box !important;
    line-height: 1.2 !important;
    vertical-align: middle;
}

/* Date inputs don't carry the custom dropdown arrow, so no extra right
   padding needed. */
.approvals-form-grid input[type="date"].booking-input {
    padding-right: .85rem !important;
}

/* Textarea inside the form grid keeps its own height (multi-line). */
.approvals-form-grid textarea.booking-input {
    height: auto !important;
    max-height: none !important;
    min-height: 96px !important;
    padding: .55rem .85rem !important;
}

/* Buttons in the inline edit form sit on the same 42px baseline as the
   fields above them. Scoped to .club-edit-form so the standalone
   "New booking" page on /club/booking keeps its full 54px pills. */
.club-edit-form .club-edit-actions .booking-secondary-button,
.club-edit-form .club-edit-actions .btn-primary-brand,
.club-edit-form .club-edit-actions .btn-secondary-brand {
    min-height: 42px !important;
    height: 42px !important;
    padding: 0 1.1rem !important;
    font-size: .88rem !important;
    border-radius: 999px !important;
}

.booking-switch-label {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: .75rem;
    cursor: pointer;
    user-select: none;
    color: rgba(241, 245, 249, .96);
    font-weight: 800;
    margin: 0;
}

.booking-switch-input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    margin: 0;
    pointer-events: none;
}

.booking-switch-slider {
    position: relative;
    width: 44px;
    height: 24px;
    border-radius: 999px;
    background: rgba(148, 163, 184, .28);
    border: 1px solid rgba(255, 255, 255, .08);
    transition: background .2s ease, border-color .2s ease, box-shadow .2s ease;
    flex: 0 0 auto;
}

.booking-switch-slider::after {
    content: "";
    position: absolute;
    top: 2px;
    left: 3px;
    width: 18px;
    height: 18px;
    border-radius: 999px;
    background: #ffffff;
    box-shadow: 0 2px 6px rgba(15, 23, 42, .25);
    transition: transform .2s ease;
}

.booking-switch-input:checked + .booking-switch-slider {
    background: linear-gradient(135deg, #2563eb, #0ea5e9);
    border-color: rgba(96, 165, 250, .45);
    box-shadow: 0 0 0 4px rgba(37, 99, 235, .12);
}

.booking-switch-input:checked + .booking-switch-slider::after {
    transform: translateX(20px);
}

.booking-switch-text {
    line-height: 1.2;
}

@media (max-width: 1024px) {
    .club-field,
    .booking-field {
        grid-column: span 6;
    }
}

@media (max-width: 640px) {
    .club-form-grid,
    .booking-form-grid {
        grid-template-columns: 1fr;
    }

    .club-field,
    .club-field--wide,
    .booking-field,
    .booking-field--wide {
        grid-column: auto;
    }

    .booking-action-row,
    .club-action-row {
        align-items: stretch;
    }
}

.layout-header-row{
  display:flex;
  align-items:center;
  justify-content:space-between;
  gap:1rem;
}
.club-notification-shell{position:relative;display:flex;align-items:center;}
.club-notification-button{position:relative;border:1px solid rgba(255,255,255,.2);background:#0f172a;color:#fff;border-radius:999px;padding:.6rem .9rem;display:inline-flex;align-items:center;gap:.4rem;}
.club-notification-count{position:absolute;top:-.35rem;right:-.35rem;min-width:1.2rem;height:1.2rem;border-radius:999px;background:#dc2626;color:#fff;font-size:.75rem;display:flex;align-items:center;justify-content:center;padding:0 .2rem;}
.club-notification-panel{position:absolute;right:0;top:calc(100% + .5rem);width:min(26rem,90vw);max-height:32rem;overflow:auto;background:#fff;border:1px solid #e5e7eb;border-radius:1rem;box-shadow:0 20px 40px rgba(15,23,42,.18);padding:.75rem;z-index:50;color:#0f172a;}
.club-notification-panel-header{display:flex;justify-content:space-between;gap:.5rem;font-size:.85rem;color:#64748b;margin-bottom:.5rem;}
.club-notification-list{display:flex;flex-direction:column;gap:.5rem;}
.club-notification-item{width:100%;text-align:left;border:1px solid #e5e7eb;background:#fff;border-radius:.85rem;padding:.75rem;display:flex;flex-direction:column;gap:.3rem;}
.club-notification-item.is-unread{border-color:#93c5fd;background:#eff6ff;}
.club-notification-item-top{display:flex;justify-content:space-between;gap:.75rem;font-size:.85rem;}
.club-notification-empty{padding:.75rem;color:#64748b;}
.club-inline-search{min-width:min(18rem,100%);}
.club-list-status-stack{display:flex;align-items:center;gap:.4rem;flex-wrap:wrap;}
.club-member-expiry-dot{width:1.7rem;height:1.7rem;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;font-size:.75rem;font-weight:700;color:#fff;}
.club-member-expiry-dot.is-good{background:#16a34a;}
.club-member-expiry-dot.is-warn{background:#f59e0b;}
.club-member-expiry-dot.is-danger{background:#dc2626;}
.club-member-expiry-dot.is-muted{background:#475569;}
.club-refresh-text{font-size:.85rem;color:#64748b;align-self:center;}


.experience-photo-strip{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:1rem;}
.experience-photo-card{position:relative;overflow:hidden;border-radius:1.25rem;box-shadow:0 18px 40px rgba(15,23,42,.14);min-height:15rem;background:#e2e8f0;}
.experience-photo-image{width:100%;height:100%;object-fit:cover;display:block;transition:transform .45s ease;}
.experience-photo-card:hover .experience-photo-image{transform:scale(1.03);}
@media (max-width: 900px){.experience-photo-strip{grid-template-columns:1fr;}}

/* =========================================================================
   Approvals filter band (Phase B).
   Lives in app.css (not the page's scoped .razor.css) because the form
   element is rendered by <EditForm> — a Blazor child component whose root
   <form> doesn't inherit the parent component's scope attribute, so scoped
   CSS rules can't reach it. Global rules side-step the issue cleanly.

   Layout: flexbox with flex-wrap. Each field is min 180px and grows to fill
   the row, so the browser auto-fits as many fields per row as the viewport
   can comfortably hold. On a wide desktop (~1080px+) all 5 filters + the
   Apply button line up in one band; on tablet they wrap; on mobile they
   stack one-per-row.
   ========================================================================= */
.approvals-filter-grid {
    display: flex !important;
    flex-wrap: wrap;
    gap: .65rem .8rem;
    align-items: end;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .06);
    border-radius: 14px;
    padding: .85rem 1rem;
    margin: 0 0 1rem;
}

.approvals-filter-grid .approvals-field {
    flex: 1 1 180px;        /* min 180px, grow to fill row */
    grid-column: auto !important;   /* override the default span-6 from .approvals-field */
    min-width: 0;
    display: grid;
    gap: .25rem;
}

.approvals-filter-grid .approvals-field--wide {
    flex-basis: 100%;
}

.approvals-filter-grid .approvals-field label {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: rgba(203, 213, 225, .82);
    font-weight: 700;
}

.approvals-filter-grid .booking-input,
.approvals-filter-grid select.booking-input,
.approvals-filter-grid input.booking-input {
    min-height: 36px !important;
    padding: .35rem .55rem !important;
    font-size: .88rem !important;
    border-radius: 12px;
    width: 100%;
}

.approvals-filter-grid .approvals-filter-actions {
    flex: 0 0 auto;
    margin: 0;
    padding: 0;
    display: flex;
    align-items: end;
}
.approvals-filter-grid .approvals-filter-actions .btn-primary-brand {
    min-height: 36px;
    padding: .35rem 1.25rem;
}

@media (max-width: 760px) {
    .approvals-filter-grid .approvals-filter-actions {
        flex: 1 1 100%;
    }
    .approvals-filter-grid .approvals-filter-actions .btn-primary-brand {
        width: 100%;
    }
}

/* =========================================================================
   Aircraft Blocks — Create/Edit form (Phase B).
   The global .booking-field rule has grid-column: span 3 which is correct
   for a 12-column .booking-form-grid, but this page uses a 3-column grid
   variant — span 3 there means "take all 3 columns" = one field per row.
   Override to "auto" so each field takes one column. Plus compact input
   sizing so the form doesn't sprawl on a desktop.
   Lives in app.css (not the page's scoped CSS) because the inputs come
   from <EditForm> child components whose scope attribute the page's CSS
   can't reach.
   ========================================================================= */
/* Tablet-fix (B.5): same auto-fit treatment as .booking-form-grid--compact.
   The original 1024px hard breakpoint left tablet widths with the broken
   12-column auto-place fallback. */
.booking-form-grid--aircraft-blocks {
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)) !important;
}

.booking-form-grid--aircraft-blocks .booking-field {
    grid-column: auto !important;
}

.booking-form-grid--aircraft-blocks .booking-field--wide {
    grid-column: 1 / -1 !important;
}

.booking-form-grid--aircraft-blocks .booking-input,
.booking-form-grid--aircraft-blocks select.booking-input,
.booking-form-grid--aircraft-blocks input.booking-input {
    min-height: 36px !important;
    padding: .35rem .55rem !important;
    font-size: .9rem !important;
    border-radius: 12px;
}

.booking-form-grid--aircraft-blocks textarea.booking-input,
.booking-form-grid--aircraft-blocks .booking-textarea {
    min-height: 64px !important;
    padding: .5rem .65rem !important;
}

.booking-form-grid--aircraft-blocks .booking-field label {
    font-size: .72rem !important;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: rgba(203, 213, 225, .82);
    font-weight: 700;
    white-space: normal;
    line-height: 1.25;
}

.booking-form-grid--aircraft-blocks .booking-switch-label {
    min-height: 36px;
}

@media (max-width: 420px) {
    .booking-form-grid--aircraft-blocks {
        grid-template-columns: 1fr !important;
    }
}

/* =========================================================================
   Members filter band (Phase B). Same flex-wrap pattern as the approvals
   filter band — fits as many fields per row as the viewport allows without
   crushing them.
   ========================================================================= */
.members-filter {
    display: flex;
    flex-wrap: wrap;
    gap: .55rem .75rem;
    align-items: end;
    background: rgba(255, 255, 255, .03);
    border: 1px solid rgba(255, 255, 255, .06);
    border-radius: 14px;
    padding: .85rem 1rem;
    margin: 1rem 0 1.25rem;     /* breathing room above AND below the band */
    width: 100%;
}

.members-filter .members-field {
    flex: 1 1 180px;
    min-width: 0;
    display: grid;
    gap: .25rem;
}

.members-filter .members-field--actions {
    flex: 0 0 auto;
    align-self: end;
}

.members-filter .members-field label {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: rgba(203, 213, 225, .82);
    font-weight: 700;
}

.members-filter .booking-input,
.members-filter select.booking-input,
.members-filter input.booking-input {
    min-height: 36px !important;
    padding: .35rem .55rem !important;
    font-size: .88rem !important;
    border-radius: 12px;
    width: 100%;
}

.members-filter .btn-secondary-brand {
    min-height: 36px;
    padding: .35rem 1rem;
}

/* Tablet: two filters per row max. Mobile: stack. */
@media (max-width: 760px) {
    .members-filter .members-field--actions {
        flex: 1 1 100%;
    }
    .members-filter .members-field--actions .btn-secondary-brand {
        width: 100%;
    }
}

/* Phase B: "Protected (Owner)" chip shown in place of the Edit button when a
   non-Owner views an Owner row. Subtle gold tone so it reads as informational,
   not error. */
.member-protected-chip {
    display: inline-flex;
    align-items: center;
    padding: .35rem .75rem;
    border-radius: 999px;
    font-size: .8rem;
    font-weight: 700;
    letter-spacing: .02em;
    color: rgba(241, 245, 249, .82);
    background: rgba(224, 138, 58, .14);
    border: 1px solid rgba(224, 138, 58, .28);
    cursor: help;
}

/* ============================================================
   Phase B.2: section dividers inside Profile / Members forms.
   Used to visually break the long edit form into "Contact",
   "Next of kin", "Medical heads-up" groups so the user isn't
   staring at a wall of inputs. Lives in app.css (not scoped) so
   it applies inside both EditForm renderings (profile + members).
============================================================ */
.club-form-section {
    margin: 1.5rem 0 .75rem;
    padding-top: 1.25rem;
    border-top: 1px solid rgba(255, 255, 255, .08);
}

.club-form-section:first-of-type {
    margin-top: 1rem;
}

.club-form-section-title {
    margin: 0 0 .25rem;
    font-size: .95rem;
    font-weight: 600;
    color: rgba(241, 245, 249, .92);
    letter-spacing: .01em;
}

.club-form-section-hint {
    margin: 0;
    font-size: .8rem;
    color: rgba(241, 245, 249, .58);
    line-height: 1.4;
}

/* Phase B.2: read-only safety-field block on the admin Members card.
   Used in View mode to show contact + NoK + medical without making
   them editable. Tighter line height than form rows since this is
   pure display. */
.member-safety-block {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    margin-top: 1rem;
}

.member-safety-section-title {
    margin: 0 0 .35rem;
    font-size: .85rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: rgba(224, 138, 58, .85);
}

.member-safety-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .5rem 1.25rem;
}

@media (max-width: 640px) {
    .member-safety-grid {
        grid-template-columns: 1fr;
    }
}

.member-safety-row {
    display: flex;
    flex-direction: column;
    gap: .15rem;
}

.member-safety-row dt {
    font-size: .72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: rgba(241, 245, 249, .55);
}

.member-safety-row dd {
    margin: 0;
    font-size: .92rem;
    color: rgba(241, 245, 249, .92);
    word-break: break-word;
}

.member-safety-empty {
    color: rgba(241, 245, 249, .42);
    font-style: italic;
}

.member-safety-medical-block {
    padding: .85rem 1rem;
    background: rgba(201, 106, 43, .08);
    border: 1px solid rgba(201, 106, 43, .25);
    border-radius: 10px;
}

.member-safety-medical-block dt {
    color: rgba(255, 179, 71, .92);
}

/* ============================================================
   Phase B.4: Generic compact form-grid modifier.
   Same pattern as .booking-form-grid--aircraft-blocks but reusable
   across any form that wants the condensed desktop look (smaller
   inputs, tighter labels, more fields per row). Applied to the
   Phase B.2 safety-field grids on Profile + Members so they don't
   waste vertical space on a wide screen.
============================================================ */
/* Tablet-fix (B.5): the original ruleset only set a sane column count at
   min-width: 1024px, which meant tablet widths (640-1023px) fell back to the
   base 12-column grid with auto-placed fields — i.e. up to 12 fields per row
   with overlapping labels. Switched to auto-fit minmax(180px, 1fr) so the
   grid fluidly chooses 2/3/4/5 columns based on actual container width
   instead of a hard breakpoint. */
.booking-form-grid--compact {
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)) !important;
}

.booking-form-grid--compact .booking-field {
    grid-column: auto !important;
}

.booking-form-grid--compact .booking-field--wide {
    grid-column: 1 / -1 !important;
}

/* --half inside a compact (auto-fit) grid: span TWO of the fluid tracks —
   i.e. "double width" relative to neighbouring fields. Without this rule
   the compact grid's `grid-column: auto !important` flattened --half back
   to single width (Email fields, address pairs). Scoped to widths where
   the compact grid is guaranteed multi-column; below that everything
   stacks full-width anyway. */
@media (min-width: 480px) {
    .booking-form-grid--compact .booking-field--half {
        grid-column: span 2 !important;
    }
}

.booking-form-grid--compact .booking-input,
.booking-form-grid--compact select.booking-input,
.booking-form-grid--compact input.booking-input {
    min-height: 36px !important;
    padding: .35rem .55rem !important;
    font-size: .9rem !important;
    border-radius: 12px;
}

.booking-form-grid--compact textarea.booking-input,
.booking-form-grid--compact .booking-textarea {
    min-height: 64px !important;
    padding: .5rem .65rem !important;
}

.booking-form-grid--compact .booking-field label {
    font-size: .72rem !important;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: rgba(203, 213, 225, .82);
    font-weight: 700;
    /* Labels were running together at narrow widths because the inherited
       rule didn't reset white-space. Allow wrapping so a long label
       (e.g. "Membership renewal") never overflows its column. */
    white-space: normal;
    line-height: 1.25;
}

.booking-form-grid--compact .booking-switch-label {
    min-height: 36px;
}

/* Very narrow phones — force a single column so the auto-fit doesn't try
   to keep 180px-wide fields and overflow the viewport. */
@media (max-width: 420px) {
    .booking-form-grid--compact {
        grid-template-columns: 1fr !important;
    }
}

/* ============================================================
   Phase B.5 — Loading indicator
   Used across the business-area report pages and any page with an Apply /
   Refresh button so the user gets visible feedback when data is loading.
   Sits in a club-panel and shows a rotating spinner + text. Global so every
   page can drop it in without per-page CSS.
============================================================ */
/* B.5 — brand-logo spinner. Replaces the previous circle-with-coloured-border
   spinner with the Adur Aviation logo rotating slowly. Used inline next to
   loading button text, in loading panels on report pages, etc.
   The logo is loaded as a background-image (not <img>) so the surrounding
   element can be sized via width/height and the rotation works smoothly. */
.app-spinner {
    display: inline-block;
    width: 1.4rem;
    height: 1.4rem;
    background-image: url('images/Logo_Transparent.png');
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    animation: app-spin 1.4s linear infinite;
    vertical-align: -.32rem;
    margin-right: .55rem;
    /* Hint to the renderer that this element is going to be transformed
       continuously, so the browser puts it on its own compositor layer
       and the animation stays smooth. */
    will-change: transform;
}
.app-spinner--lg {
    width: 2.2rem;
    height: 2.2rem;
    margin-right: .75rem;
}

/* ============================================================
   btn-stable — keep a button at its idle-state width when it
   switches to a spinner-only loading state.

   Pattern in markup:
     <button class="btn-primary-brand btn-stable @(loading ? "is-loading" : "")">
         <span>Idle label</span>
         @if (loading) { <span class="app-spinner"></span> }
     </button>

   How it works: when .is-loading is set, the idle <span> is made
   invisible (NOT removed — visibility:hidden keeps it laying out)
   so the button width stays exactly what it would be in idle state.
   The spinner is positioned absolutely in the centre, so visually
   the button "swaps" text for spinner with no resize wobble.

   Use only on short buttons where the loading word ("Running…",
   "Applying…") would otherwise change the width noticeably. Long
   buttons (e.g. "Sign in to your account") don't need this.
   ============================================================ */
.btn-stable {
    position: relative;
}

.btn-stable.is-loading > span:not(.app-spinner) {
    visibility: hidden;
}

.btn-stable.is-loading > .app-spinner {
    position: absolute;
    top: 50%;
    left: 50%;
    /* Centre via negative margins instead of transform — the spinner has
       an `animation: app-spin` that continuously sets transform:rotate(),
       which would override any transform:translate we put here and leave
       the spinner anchored at its original baseline-aligned position
       (bottom-right of the button). Negative margins of half the spinner
       size centre it without touching transform. */
    margin: -.7rem 0 0 -.7rem;
}

/* B.5 — inline "spinner + Loading…" combo for use inside tile values
   (.club-stat-value strong) and other places where a bare spinner felt
   uninformative. The wrapper keeps the spinner and text on one line, tones
   the size down so it doesn't dominate the tile when the actual value
   would normally be a big bold number / £-amount, AND scales the spinner
   to exactly match the text height via em units (so the two never look
   mismatched no matter what surrounding CSS does). */
.app-loading-inline {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
    /* Locked to 1rem with !important because the parent tile value
       (.club-stat-value) sets a much larger font-size and same-specificity
       rules can lose the cascade race depending on file ordering. */
    font-size: 1rem !important;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: 0;
    color: rgba(241, 245, 249, 0.78);
}
.app-loading-inline .app-spinner {
    /* em units so the spinner ALWAYS matches the surrounding text height.
       1.15em = very slightly taller than the cap-height of the text,
       which reads as visually equal to most eyes. */
    width: 1.15em;
    height: 1.15em;
    margin-right: 0;             /* flex gap handles the spacing */
    vertical-align: middle;
}
@keyframes app-spin {
    from { transform: rotate(0deg); }
    to   { transform: rotate(360deg); }
}

.app-loading-row {
    display: flex;
    align-items: center;
    gap: .35rem;
    padding: 1rem;
    color: rgba(241, 245, 249, .92);
    font-weight: 600;
}
.app-loading-row .app-loading-text {
    font-size: 1rem;
}
.app-loading-row .app-loading-hint {
    margin-left: .5rem;
    color: rgba(203, 213, 225, .72);
    font-weight: 500;
    font-size: .88rem;
}

/* Dim the in-place results while a refresh is in flight (used by pages that
   keep old data visible during reload so the page doesn't flash). */
.app-results--reloading {
    opacity: .45;
    pointer-events: none;
    transition: opacity .15s ease;
}

/* ============================================================
   Phase B.5 — Biz design system (global)
   These utility classes were previously declared inside per-component
   .razor.css files, which meant pages WITHOUT a scoped file (e.g. all
   five business-area report pages) had no flex filter row, no chip
   pills, and no table styling. Promoting them to global so any page
   that uses the markup picks them up automatically.

   Pages that do have a scoped .razor.css copy of these rules keep
   working unchanged — Blazor's scope attribute gives the scoped
   selector higher specificity, so it wins over the global rule.
============================================================ */
.biz-form-row {
    display: flex;
    flex-wrap: wrap;
    /* row-gap > column-gap so when the button wraps below stacked
       inputs there's clear breathing room above it. */
    gap: 1rem .65rem;
    align-items: flex-end;
    padding: 1rem;
    margin: .85rem 0 1rem;
    background: rgba(255,255,255,.03);
    border: 1px solid rgba(255,255,255,.06);
    border-radius: 18px;
}

.biz-field {
    display: grid;
    gap: .35rem;
    flex: 1 1 140px;
    min-width: 0;
}
.biz-field--wide { flex-basis: 100%; }

.biz-field label {
    font-size: .78rem;
    color: rgba(203, 213, 225, .82);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.biz-field .booking-input,
.biz-field input.booking-input,
.biz-field select.booking-input {
    min-height: 36px;
    padding: .35rem .55rem;
    font-size: .9rem;
    border-radius: 12px;
}

/* Tables — hairline grid that sits comfortably inside a club-panel. */
.biz-table {
    width: 100%;
    border-collapse: collapse;
    font-size: .92rem;
    margin-top: .5rem;
}
.biz-table th, .biz-table td {
    padding: .55rem .7rem;
    text-align: left;
    border-bottom: 1px solid rgba(255,255,255,.06);
    /* #59 — middle-align row content (was top) so action-cell buttons sit
       in line with the row's text rather than towering over short cells. */
    vertical-align: middle;
    color: rgba(241, 245, 249, .92);
}

/* #59 — condensed buttons specifically when they sit inside a table action
   cell. The default .booking-secondary-button is 54px tall (sized for forms)
   which dwarfs a single-line table row; 34px sits comfortably with one row
   of text and matches the padding-light feel of the table itself. */
.biz-action-cell .booking-secondary-button,
.biz-action-cell .btn-primary-brand {
    min-height: 34px;
    padding: 0 .85rem;
    font-size: .85rem;
    font-weight: 700;
}
.biz-table th {
    background: rgba(255,255,255,.03);
    font-weight: 800;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: rgba(203, 213, 225, .72);
    border-bottom: 1px solid rgba(255,255,255,.1);
}
.biz-table tr.biz-row-inactive td { opacity: .55; }
.biz-table tr.biz-total-row td,
tr.biz-total-row td {
    border-top: 2px solid rgba(255,255,255,.16);
    padding-top: .85rem;
}

.biz-muted { color: rgba(203, 213, 225, .72); }
.biz-small { font-size: .82rem; }

.biz-chip {
    display: inline-block;
    padding: .12rem .55rem;
    font-size: .72rem;
    font-weight: 800;
    border-radius: 999px;
    margin-left: .4rem;
}
.biz-chip-good   { background: rgba(22,163,74,.18);  color: #86efac; }
.biz-chip-bad    { background: rgba(220,38,38,.18);  color: #fecaca; }
.biz-chip-future { background: rgba(59,130,246,.18); color: #93c5fd; }

.biz-readings {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 1rem;
    margin: 1.25rem 0 0;
}
.biz-readings > div { display: grid; gap: .15rem; }
.biz-readings dt {
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .08em;
    color: rgba(203, 213, 225, .72);
    font-weight: 700;
}
.biz-readings dd { margin: 0; color: #fff; font-weight: 800; font-size: 1.15rem; padding-left: .5rem; }

.biz-net-good { color: #86efac; }
.biz-net-bad  { color: #fca5a5; }

.biz-action-row { display: flex; gap: .65rem; flex-wrap: wrap; margin-top: .5rem; }

.biz-btn-link {
    background: rgba(255,255,255,.05);
    border: 1px solid rgba(255,255,255,.1);
    color: #fff;
    padding: .35rem .65rem;
    border-radius: 999px;
    font-size: .78rem;
    cursor: pointer;
    margin-right: .35rem;
    font-weight: 700;
    /* Phase B.10 — used as a pill action on <a> and <button> alike, so the
       anchor's default underline + inline display would split the look.
       Force button parity. */
    text-decoration: none;
    display: inline-flex;
    align-items: center;
    /* gap so the icon isn't glued to the label (flex collapses the literal
       whitespace between <i> and the text). */
    gap: .35rem;
    align-items: center;
    justify-content: center;
}
.biz-btn-link:hover,
.biz-btn-link:focus {
    background: rgba(255,255,255,.1);
    border-color: rgba(255,255,255,.18);
    text-decoration: none;
}

.biz-subhead {
    margin: 1rem 0 .5rem;
    font-size: .92rem;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: rgba(203, 213, 225, .82);
    font-weight: 800;
}

@media (max-width: 640px) {
    .biz-form-row { padding: .75rem; }
    .biz-field { flex-basis: 100%; }
    .biz-form-row .btn-primary-brand,
    .biz-form-row .btn-secondary-brand { width: 100%; justify-content: center; }
}

/* ============================================================
   Phase B.5 — Report-page table alignment
   The per-aircraft report had local CSS that right-aligned the value
   column on revenue / cost / breakdown tables. Generalising it: any
   page tagged <article class="… app-report-page"> (or wrapping div)
   gets the same alignment on every .biz-table inside it. So Profit &
   Loss, Instructor Pay, Aircraft Lease, Compliance Forecast and
   Aircraft Revenue all look consistent.
============================================================ */
/* B.5 — centre-align all data cells AND their headers in report tables so
   numbers, durations, and identifiers all sit under the middle of their
   column header. Looks tidier than mixed left/right alignment when columns
   are narrow. */
.app-report-page table.biz-table th,
.app-report-page table.biz-table td {
    text-align: center;
    vertical-align: middle;
}

/* The first column (aircraft / instructor / row label) stays LEFT aligned
   because identifiers read better aligned left. */
.app-report-page table.biz-table tbody tr td:first-child,
.app-report-page table.biz-table thead tr th:first-child {
    text-align: left;
    word-break: normal;
    overflow-wrap: break-word;
}

/* Money columns get tabular numerals so digits line up vertically even when
   the text itself is centred. */
.app-report-page table.biz-table td,
.app-report-page table.biz-table th {
    font-variant-numeric: tabular-nums;
}
.app-report-page .biz-readings dd { padding-left: .5rem; }

/* ============================================================
   Phase B.5 — Responsive table scroll wrapper
   Wide tables (Completed bookings has 17 columns, others have 8-10) blow past
   the viewport on tablets and phones. Wrapping the <table> in
   <div class="app-table-scroll"> gives the table its own horizontal scroll
   so the rest of the page layout doesn't break — and a subtle gradient on
   the right edge hints there's more content to scroll to.

   Usage:
       <div class="app-table-scroll">
           <table class="biz-table">...</table>
       </div>
============================================================ */
.app-table-scroll {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;        /* momentum scroll on iOS */
    position: relative;
    margin: 0 calc(-1 * var(--app-table-scroll-bleed, 0px));
    /* A faint divider so the user sees the scroll area boundary. */
    border-top: 1px solid transparent;
}

/* B.5 — make EVERY biz-table inside a report card automatically scrollable
   without needing each Razor page to wrap its <table> in .app-table-scroll.
   Previous attempt used display:block + min-width:max-content on table
   sections, but that turned each section (thead / tbody / tfoot) into its
   own display:table element with INDEPENDENT column widths — so the header
   row stopped lining up with the body rows.
   This version puts the scroll on the parent .section-card instead via a
   margin-bleed trick: the card itself keeps its rounded corners, but tables
   inside that genuinely exceed the card width will scroll within the card
   bounds without breaking column alignment. */
.app-report-page table.biz-table {
    /* Tables stay as proper <table> elements (NOT display:block) so the
       column widths calculate normally across header + body + footer. */
    max-width: 100%;
}

.app-table-scroll > .biz-table,
.app-table-scroll > table {
    /* Let the table grow beyond the container; otherwise width:100% squashes
       columns into illegible widths. min-content lets the table size to its
       contents and the wrapper handles the overflow. */
    min-width: max-content;
}

/* Edge gradient hinting that more content lies to the right. Pinned to the
   wrapper using a sticky pseudo-element wouldn't survive scroll, so we draw
   it as a background gradient on the wrapper. */
/* B.5 — edge fade gradient removed. It was meant as a scroll affordance but
   read as a stray vertical line in the middle of wide tables on narrow
   viewports because the wrapper is wider than the visible card width when
   scrolled. The horizontal scrollbar at the bottom of the wrapper is hint
   enough that there's more content to the right.
.app-table-scroll::after {
    content: "";
    position: absolute;
    top: 0; right: 0; bottom: 0;
    width: 28px;
    background: linear-gradient(to right, transparent, rgba(13, 24, 41, .9));
    pointer-events: none;
    opacity: 0;
    transition: opacity .15s ease;
}
.app-table-scroll:hover::after,
.app-table-scroll:focus-within::after { opacity: 1; }
@media (max-width: 768px) {
    .app-table-scroll::after { opacity: 1; }
}
*/

/* ============================================================
   Phase B.5 — Responsive table → card stack
   At < 768px, any <table class="biz-table app-responsive-table"> transforms
   into a vertical stack of cards (one card per row). Each <td> needs a
   `data-label="Column name"` attribute — that label is rendered above the
   cell content via CSS ::before. Action cells (<td class="biz-action-cell">)
   render flush-right without a label.

   This eliminates horizontal scrolling on phones AND keeps the desktop table
   layout. No JavaScript, no DOM duplication.

   Markup pattern:
       <table class="biz-table app-responsive-table">
           <thead><tr><th>Date</th>...</tr></thead>
           <tbody>
               @foreach (var x in items) {
                   <tr>
                       <td data-label="Date">@x.Date.ToString("dd/MM/yyyy")</td>
                       ...
                       <td class="biz-action-cell">...</td>
                   </tr>
               }
           </tbody>
       </table>
============================================================ */
@media (max-width: 767px) {
    /* Strip table semantics so each <tr> renders as a block-level card. */
    table.app-responsive-table,
    table.app-responsive-table thead,
    table.app-responsive-table tbody,
    table.app-responsive-table tfoot,
    table.app-responsive-table tr,
    table.app-responsive-table td {
        display: block;
        width: 100%;
    }

    /* Hide the column headers — labels come from data-label per cell. */
    table.app-responsive-table thead {
        position: absolute;
        left: -10000px;   /* visually hidden, still in the AT tree */
    }

    /* Each row becomes a card. */
    table.app-responsive-table tbody tr {
        background: rgba(255, 255, 255, .03);
        border: 1px solid rgba(255, 255, 255, .08);
        border-radius: 14px;
        padding: .75rem .9rem;
        margin-bottom: .75rem;
    }
    table.app-responsive-table tbody tr:last-child { margin-bottom: 0; }

    /* Each cell becomes a label + value row in the card. */
    table.app-responsive-table tbody td {
        border: none !important;
        padding: .3rem 0;
        text-align: left !important;
        white-space: normal;
        min-width: 0 !important;
        font-variant-numeric: normal;
    }

    /* Label above each value. Pulled from data-label="…". */
    table.app-responsive-table tbody td[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: .65rem;
        text-transform: uppercase;
        letter-spacing: .05em;
        font-weight: 800;
        color: rgba(203, 213, 225, .72);
        margin-bottom: .15rem;
    }

    /* Action cells render full-width buttons with a top divider so they sit
       clearly at the bottom of the card. */
    table.app-responsive-table tbody td.biz-action-cell {
        margin-top: .5rem;
        padding-top: .65rem;
        border-top: 1px solid rgba(255, 255, 255, .08) !important;
        text-align: right !important;
        display: flex;
        flex-wrap: wrap;
        gap: .5rem;
        justify-content: flex-end;
    }
    table.app-responsive-table tbody td.biz-action-cell::before { content: none; }

    /* When the cell content is purely numeric (£ figure), pad it so it reads
       like a key-value pair rather than a wall of digits. */
    table.app-responsive-table tbody td.biz-num {
        text-align: left !important;
        font-weight: 700;
    }

    /* Totals footer — stack as its own card with labels too, instead of a
       label-less wall of numbers. */
    table.app-responsive-table tfoot tr {
        /* Distinct tinted card so the totals stand apart from the data rows. */
        background: rgba(96, 165, 250, .12);
        border: 1px solid rgba(96, 165, 250, .28);
        border-radius: 14px;
        padding: .75rem .9rem;
        margin-top: .75rem;
    }

    table.app-responsive-table tfoot td {
        border: none !important;
        padding: .3rem 0;
        text-align: left !important;
        white-space: normal;
        min-width: 0 !important;
    }

    table.app-responsive-table tfoot td[data-label]::before {
        content: attr(data-label);
        display: block;
        font-size: .65rem;
        text-transform: uppercase;
        letter-spacing: .05em;
        font-weight: 800;
        color: rgba(203, 213, 225, .72);
        margin-bottom: .15rem;
    }

    /* Drop empty footer cells (e.g. a blank Expiry total) so they don't leave
       a stray labelled blank in the stacked card. */
    table.app-responsive-table tfoot td:empty { display: none; }

    /* Inside cards we don't need the .biz-table--compact tighter font. */
    table.app-responsive-table.biz-table--compact { font-size: .9rem; }
}

/* ============================================================
   Phase B.7 — Sticky table headers
   <thead> stays pinned to the top of the viewport while the user scrolls
   a long table. Uses position: sticky + top: 0 + an opaque background so
   row content doesn't bleed through underneath.

   Caveat: position: sticky needs a vertically-scrolling ancestor. For
   plain tables (most of them) that's the page itself — works fine. For
   tables wrapped in <div class="app-table-scroll"> (a horizontally-
   scrolling container) sticky never triggers, because the X-only scroll
   container creates a non-vertical scrolling ancestor that swallows the
   sticky behaviour. Those pages (BusinessCompletedBookings,
   ClubInventorySales, ClubInventory) won't gain sticky headers from
   this rule alone — they'd need their wrapper restructured separately.

   Disabled in the responsive-table card stack (<768px) where <thead>
   is visually hidden anyway.
============================================================ */
.biz-table thead th {
    /* Sticky table headers removed — they floated over content when scrolling
       inside panels/modals. Headers now scroll with the rest of the table. */
    position: static;
}

/* Brand the native checkboxes so the tick colour is consistent across
   browsers/OSes (default accent varies — blue on some, grey on others). */
input[type="checkbox"] {
    accent-color: var(--brand-accent);
}

/* Slightly larger, easier-to-hit boxes in the flight-completion panels. */
.block-alloc-row input[type="checkbox"],
.share-split-table input[type="checkbox"] {
    width: 1.05rem;
    height: 1.05rem;
}

@media (max-width: 768px) {
    /* In card-stack mode the thead is already hidden off-screen, so the
       sticky rules don't matter — but reset for tidiness. */
    table.app-responsive-table thead th {
        position: static;
    }
}

/* ============================================================
   Phase B.5 — Filter-row buttons
   The brand buttons default to a tall pill (padding 0.95rem) which towers
   over the compact 36px-tall filter inputs in .biz-form-row. This rule
   shrinks any brand button inside a filter row so the heights match up.
   Applies everywhere — Completed bookings, every report, anywhere we drop
   an Apply / Refresh / Run button next to date / select inputs.
============================================================ */
.biz-form-row .btn-primary-brand,
.biz-form-row .btn-secondary-brand {
    min-height: 36px;
    padding: .35rem 1.1rem;
    font-size: .9rem;
    border-radius: 12px;
    line-height: 1.2;
    align-self: flex-end;
}
.biz-form-row .btn-primary-brand:hover,
.biz-form-row .btn-secondary-brand:hover {
    /* Disable the -2px lift inside filter rows — looks twitchy when the
       button is sitting in line with inputs that don't move. */
    transform: none;
}

/* ============================================================
   Phase B.5 — Custom tooltip
   Native HTML `title` attribute has a ~500ms delay + ugly styling. This is a
   pure-CSS replacement that fires on hover OR keyboard focus, fades in
   quickly, and looks like a proper dark pill with a downward-pointing arrow.

   Usage: just add `data-tooltip="..."` to any element. Pair it with
   `aria-label="..."` for screen-reader accessibility. Do NOT use `title`
   alongside it or the browser will render both.

       <button data-tooltip="Re-open booking"
               aria-label="Re-open booking">...</button>
============================================================ */
[data-tooltip] { position: relative; }

[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
    content: attr(data-tooltip);
    position: absolute;
    z-index: 10001;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    padding: .35rem .6rem;
    background: rgba(15, 23, 42, .96);
    color: #f8fafc;
    border: 1px solid rgba(255,255,255,.15);
    border-radius: 6px;
    font-size: .78rem;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    pointer-events: none;
    box-shadow: 0 6px 14px rgba(0,0,0,.5);
    animation: app-tooltip-fade .14s ease-out;
}

[data-tooltip]:hover::before,
[data-tooltip]:focus-visible::before {
    /* Arrow pointing down to the element. */
    content: "";
    position: absolute;
    z-index: 10001;
    bottom: calc(100% + 2px);
    left: 50%;
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: rgba(15, 23, 42, .96);
    pointer-events: none;
    animation: app-tooltip-fade .14s ease-out;
}

@keyframes app-tooltip-fade {
    from { opacity: 0; transform: translateX(-50%) translateY(4px); }
    to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}

/* Variant: tooltip below the element (use when the element is near the top of
   the viewport — set data-tooltip-pos="bottom"). */
[data-tooltip][data-tooltip-pos="bottom"]:hover::after,
[data-tooltip][data-tooltip-pos="bottom"]:focus-visible::after {
    bottom: auto;
    top: calc(100% + 8px);
}
[data-tooltip][data-tooltip-pos="bottom"]:hover::before,
[data-tooltip][data-tooltip-pos="bottom"]:focus-visible::before {
    bottom: auto;
    top: calc(100% + 2px);
    border-top-color: transparent;
    border-bottom-color: rgba(15, 23, 42, .96);
}

/* ============================================================
   #36/#40 — Booking reset confirmation modal.
   Shared chrome for the "Are you sure?" cascade-reset modal
   used on both /club/booking (create + reschedule) and
   /club/approvals (admin edit). Lives in app.css (not scoped)
   so the same markup works on both pages.
   ============================================================ */
.booking-reset-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.55);
    /* #54 — same pattern as .biz-modal-backdrop: lay the modal at the TOP of
       the backdrop (align-items: flex-start) rather than vertically centred,
       and pair with modals.js which scrolls the user to the modal on open.
       This works even when an ancestor's transform/will-change/filter has
       turned `position: fixed` into "absolute relative to that ancestor",
       which centring would otherwise hide far down the page. Padding-top
       clears the sticky site header (≈4.9rem) plus a comfortable gap. */
    display: flex;
    align-items: flex-start;
    justify-content: center;
    z-index: 100000;
    isolation: isolate;
    padding: 6rem 1rem 2rem;
    overflow-y: auto;
}

.booking-reset-modal {
    /* Explicit positioning + z-index inside the backdrop's context so the box
       reliably paints above the dim layer regardless of what else the page
       has stacked. Without this the modal box was visible to members but
       invisible to admins on /club/booking — the admin-only Searchable
       Dropdown trigger (z-index 9001) was painting over a flex-centered
       child that had no explicit stacking position of its own. */
    position: relative;
    z-index: 1;
    background: #ffffff;
    border-radius: 14px;
    padding: 1.6rem 1.5rem 1.25rem;
    max-width: 460px;
    width: 100%;
    box-shadow: 0 22px 60px rgba(15, 23, 42, 0.28);
    border-top: 4px solid var(--brand-gold, #c96a2b);
}

.booking-reset-modal-title {
    margin: 0 0 .5rem;
    font-size: 1.2rem;
    color: #0f172a;
}

.booking-reset-modal-body {
    margin: 0 0 1.25rem;
    color: #334155;
    font-size: 1rem;
    line-height: 1.45;
}

.booking-reset-modal-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .65rem;
    justify-content: flex-end;
}

.booking-reset-modal-actions .btn-primary-brand,
.booking-reset-modal-actions .booking-secondary-button {
    min-width: 120px;
    padding: .65rem 1.1rem;
    font-size: 1rem;
}

@media (max-width: 540px) {
    .booking-reset-modal {
        padding: 1.25rem 1.1rem 1rem;
    }

    .booking-reset-modal-actions {
        justify-content: stretch;
    }

    .booking-reset-modal-actions .btn-primary-brand,
    .booking-reset-modal-actions .booking-secondary-button {
        flex: 1 1 0;
        min-width: 0;
    }
}

/* ============================================================
   #53 — Standardised corner radius across all site boxes.
   One value (16px) for cards, panels, inputs, textareas,
   selects, modals, list items, leg cards, alerts. Pills
   (999px) and tiny circular badges deliberately keep their
   existing shapes — they're meant to be round, not boxy.
   Defined as a CSS variable so any future tweak is one edit.
   ============================================================ */
:root {
    --radius-box: 16px;
}

/* Cards + panels — was a mix of 18 / 24 / 28 px. */
.section-card,
.feature-card,
.stat-card,
.aircraft-card,
.gallery-card,
.price-card,
.contact-card,
.club-panel,
.biz-card,
.biz-modal-card,
.booking-panel,
.booking-notice,
.booking-list-item,
.booking-alert,
.booking-availability-panel,
.booking-admin-detail-card,
.booking-admin-notes-box,
.booking-inline-editor,
.club-edit-form,
.cfd-stat,
.flight-leg-card {
    border-radius: var(--radius-box);
}

/* Modal box. */
.booking-reset-modal {
    border-radius: var(--radius-box);
}

/* Form controls — was 20 px, now matches the boxes above. */
.booking-input,
.club-input,
.biz-input,
input.booking-input,
select.booking-input,
textarea.booking-input,
.booking-textarea,
.club-textarea,
.biz-textarea,
.app-searchable-dropdown__trigger,
.app-searchable-dropdown__panel {
    border-radius: var(--radius-box);
}

/* Phase B.8 — section-card stacking-context lift for OPEN searchable
   dropdowns. Must be in global app.css (not the dropdown's scoped CSS)
   because Blazor's CSS isolation otherwise only matches the dropdown's
   OWN host card, leaving sibling cards below at default z-index — so the
   panel renders BEHIND them. With this rule the whole host card lifts
   to z-index 9000 the moment its dropdown opens, putting the panel
   above any subsequent .section-card / .club-panel / .biz-card. */
:where(.section-card, .club-panel, .biz-card):has(.app-searchable-dropdown.is-open) {
    position: relative;
    z-index: 9000;
}

/* Counter panels inside leg cards. */
.flight-leg-counter-panel {
    border-radius: var(--radius-box);
}

/* ==========================================================================
   Phase B.11 — LIVE TRACKER component
   ========================================================================== */

/* The tracker root is a native <dialog>. Inline (closed) we reset the UA
   dialog styles so it behaves exactly like the old <div>; expanded, JS
   calls showModal() and the browser hoists it into the TOP LAYER — above
   every stacking context, z-index, transform and overflow on the page,
   which is why this replaced the old position:fixed approach (its controls
   kept losing pointer hit-tests in WebView/desktop in ways computed styles
   never explained). */
.live-tracker {
    position: relative;
    border-radius: 16px;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,.08);
    background: #0f1729;
    outline: none;          /* tabindex="0" focus ring would distract */
    transition: border-radius .2s ease;
    /* UA <dialog> resets for the inline state: */
    display: block;
    width: 100%;
    height: auto;
    margin: 0;
    padding: 0;
    inset: auto;
    max-width: none;
    max-height: none;
    color: inherit;
}

    /* UA sheet hides a closed dialog (display:none) — keep it inline. */
    .live-tracker:not([open]) {
        display: block;
    }

/* Expanded — the dialog is modal in the top layer; just size it to the
   viewport and square the corners. No z-index needed: the top layer wins
   by construction. JS calls invalidateSize after the flip so Leaflet
   re-tiles to the new dimensions. */
.live-tracker--fullscreen[open] {
    position: fixed;
    inset: 0;
    width: 100vw;
    height: 100vh;
    max-width: 100vw;
    max-height: 100vh;
    margin: 0;
    border-radius: 0;
    border: none;
    background: #0f1729;
}

.live-tracker::backdrop {
    background: #0f1729;
}


    .live-tracker--fullscreen .live-tracker-map {
        height: 100vh !important;
    }

.live-tracker-map {
    width: 100%;
    background: #0f1729;
}

/* Fullscreen toggle button — top-right, above the Leaflet panes (z-index 500). */
.lt-fullscreen-toggle {
    position: absolute;
    top: .55rem;
    right: .55rem;
    /* Above Leaflet's control layer (z 1000) — at 500 the button ended
       up underneath the map chrome in true fullscreen, leaving no way
       out of expanded mode on touch devices (no Esc key). */
    z-index: 1100;
    width: 36px;
    height: 36px;
    border-radius: 999px;
    background: rgba(15,23,41,.85);
    border: 1px solid rgba(255,255,255,.12);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    backdrop-filter: blur(2px);
    transition: background .18s ease, transform .18s ease;
}

    .lt-fullscreen-toggle:hover {
        background: rgba(15,23,41,.95);
        transform: scale(1.05);
    }

    .lt-fullscreen-toggle i {
        font-size: 1rem;
    }

/* Phase B.11 — responsive height adjustments.
   On tablet+ the inline `style="height: 280px"` from the Razor prop stays
   in effect (Razor sets it inline, beats stylesheet specificity). On
   phones we override that with a viewport-relative height so the map
   stays usable in landscape (where 280px would eat the whole screen)
   and so it shrinks gracefully in portrait. */
@media (max-width: 720px) {
    .live-tracker:not(.live-tracker--fullscreen) .live-tracker-map {
        height: clamp(200px, 38vh, 360px) !important;
    }
}

@media (max-width: 720px) and (orientation: landscape) {
    .live-tracker:not(.live-tracker--fullscreen) .live-tracker-map {
        height: clamp(160px, 60vh, 320px) !important;
    }

    /* In landscape the overlay pill and the fullscreen button can
       stack on the same row to save the precious vertical space. */
    .live-tracker-overlay {
        top: .35rem;
        left: 3rem;  /* clear of the zoom buttons */
        right: 3rem; /* clear of the fullscreen toggle */
        font-size: .72rem;
    }

    .lt-fullscreen-toggle {
        top: .35rem;
        right: .35rem;
        width: 32px;
        height: 32px;
    }
}

/* Fullscreen mode on phones — drop the overlay padding so pills don't
   waste vertical real estate, and lift the toggle button into the
   safe-area inset (iPhone notch / Android nav bar). */
.live-tracker--fullscreen .live-tracker-overlay {
    top: max(.55rem, env(safe-area-inset-top));
    /* Keep clear of the zoom buttons, the fullscreen toggle AND the
       notch/safe areas. */
    left: calc(max(.55rem, env(safe-area-inset-left)) + 2.65rem);
    right: calc(max(.55rem, env(safe-area-inset-right)) + 2.65rem);
}

.live-tracker--fullscreen .lt-fullscreen-toggle {
    /* Inside the top-layer dialog, plain absolute positioning is enough —
       nothing outside the dialog can compete. 44px = Android's minimum
       comfortable touch target; this is the only exit button on a phone. */
    top: max(.55rem, env(safe-area-inset-top));
    right: max(.55rem, env(safe-area-inset-right));
    width: 44px;
    height: 44px;
    touch-action: manipulation;
}

/* Phones in fullscreen: Android's status bar / camera punch-hole overlays
   the top of the WebView and env(safe-area-inset-top) reports 0 there, so
   push ALL top-row controls (zoom, status pill, exit button) down a fixed
   2.4rem clear of the cutout, and shrink the pill a notch. */
@media (max-width: 768px) {
    /* One visually-aligned row below the phone's status bar / camera
       cutout. (The zoom control's matching offset lives next to the zoom
       styling block above — it needs a heavier selector to out-rank
       leaflet.css.) */
    .live-tracker--fullscreen .live-tracker-overlay {
        top: calc(env(safe-area-inset-top, 0px) + 2.4rem);
        min-height: 44px;          /* matches the toggle, centres the pill */
    }

    .live-tracker--fullscreen .lt-fullscreen-toggle {
        top: calc(env(safe-area-inset-top, 0px) + 2.4rem);
    }

    .live-tracker .lt-pill {
        font-size: .72rem;
        padding: .3rem .55rem;
    }
}

    /* Override Leaflet's default white background on attribution. */
    .live-tracker-map .leaflet-control-attribution {
        background: rgba(15,23,41,.85);
        color: rgba(241,245,249,.65);
        font-size: 10px;
    }

        .live-tracker-map .leaflet-control-attribution a {
            color: rgba(96,165,250,.8);
        }

/* Dark-theme Leaflet's zoom buttons (default is white-on-white) on both
   the per-aircraft tracker and the Live Fleet map, matching the lt-pill
   glassy dark look. */
.live-tracker-map .leaflet-control-zoom,
.live-fleet-map .leaflet-control-zoom {
    border: 1px solid rgba(255,255,255,.12);
    border-radius: 10px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(2,6,23,.45);
}

/* Zoom control vertical alignment. NOTE the deliberately heavier selector
   (3 classes): leaflet.css is injected at runtime AFTER app.css, so at
   equal specificity Leaflet's own `.leaflet-top .leaflet-control
   { margin-top: 10px }` silently won and earlier margin tweaks here did
   nothing. Centres the + button against the pill/toggle row. */
.live-tracker-map .leaflet-top .leaflet-control-zoom,
.live-fleet-map .leaflet-top .leaflet-control-zoom {
    margin-top: 12px;
}

@media (max-width: 768px) {
    .live-tracker--fullscreen .live-tracker-map .leaflet-top .leaflet-control-zoom {
        /* Match the pill/toggle row (env+2.4rem) and optically centre the
           + button (30px) against the 44px toggle: +7px. */
        margin-top: calc(env(safe-area-inset-top, 0px) + 2.4rem + 7px);
    }
}

    .live-tracker-map .leaflet-control-zoom a,
    .live-fleet-map .leaflet-control-zoom a {
        /* No backdrop-filter here: Android WebView repaints blur badly
           while scrolling → visible flicker. Slightly higher opacity
           gives the same glassy look without the compositing cost. */
        background: rgba(15,23,41,.92);
        color: #f1f5f9;
        border-bottom: 1px solid rgba(255,255,255,.08);
    }

        .live-tracker-map .leaflet-control-zoom a:last-child,
        .live-fleet-map .leaflet-control-zoom a:last-child {
            border-bottom: none;
        }

        .live-tracker-map .leaflet-control-zoom a:hover,
        .live-fleet-map .leaflet-control-zoom a:hover {
            background: rgba(30,41,59,.95);
            color: #fff;
        }

        .live-tracker-map .leaflet-control-zoom a.leaflet-disabled,
        .live-fleet-map .leaflet-control-zoom a.leaflet-disabled {
            background: rgba(15,23,41,.6);
            color: rgba(241,245,249,.3);
        }

.live-tracker-overlay {
    position: absolute;
    top: .65rem;
    /* Bounded on BOTH sides (clear of the zoom control and the fullscreen
       toggle) and CENTRED between them, so the status pill reads as a
       proper title row instead of hugging the zoom buttons. */
    left: 3.2rem;
    right: 3.2rem;
    z-index: 500;     /* above Leaflet panes (default zindex 200/400). */
    display: flex;
    gap: .35rem;
    flex-wrap: wrap;
    justify-content: center;
    align-items: center;
    pointer-events: none;
}

.lt-pill {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .35rem .65rem;
    border-radius: 999px;
    font-size: .78rem;
    font-weight: 700;
    color: #fff;
    /* backdrop-filter removed — flickers in Android WebView while
       scrolling; bumped opacity compensates. */
    background: rgba(15,23,41,.92);
    border: 1px solid rgba(255,255,255,.12);
}

    .lt-pill i { font-size: .85rem; }

.lt-pill--airborne {
    background: rgba(224,138,58,.85);
    border-color: rgba(224,138,58,.5);
}

.lt-pill--ground {
    background: rgba(96,165,250,.85);
    border-color: rgba(96,165,250,.5);
}

.lt-pill--stale {
    background: rgba(148,163,184,.85);
    border-color: rgba(148,163,184,.55);
    color: #0f172a;
}

.lt-pill--quiet {
    background: rgba(30,41,59,.85);
    color: rgba(203,213,225,.85);
}

/* Phase B.11 (phase 3) — empty-state panel for ReplayTracker. Same dark
   theme as the live tracker so it sits naturally in the same content
   slot when there's no track to draw. */
.replay-tracker-empty {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding: 1.1rem 1.25rem;
    border-radius: 16px;
    border: 1px dashed rgba(255,255,255,.12);
    background: rgba(15,23,41,.55);
    color: rgba(203,213,225,.78);
    font-size: .9rem;
    line-height: 1.4;
}

    .replay-tracker-empty i {
        font-size: 1.4rem;
        color: rgba(148,163,184,.7);
        flex: 0 0 auto;
    }

/* Phase B.11 (phase 3) — stats grid under the replay map. Auto-fit so
   we squeeze 4 stats on desktop, 2 on tablet, 1 per row on phone. */
.replay-tracker-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: .65rem;
    margin-top: .85rem;
}

.rt-stat {
    padding: .65rem .85rem;
    border-radius: 14px;
    background: rgba(255,255,255,.04);
    border: 1px solid rgba(255,255,255,.07);
    display: grid;
    gap: .1rem;
    min-width: 0;
    overflow-wrap: anywhere;
}

.rt-stat-label {
    color: rgba(203,213,225,.72);
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.rt-stat-value {
    color: #fff;
    font-size: 1.2rem;
    font-weight: 800;
    line-height: 1.15;
}

.rt-stat-hint {
    color: rgba(203,213,225,.62);
    font-size: .78rem;
}

/* Phase B.11 (phase 3 polish) — playback toggle on ReplayTracker. Pill
   in the bottom-right corner of the map so it doesn't collide with the
   pill overlay (top-left) or the fullscreen toggle (top-right). */
.lt-playback-toggle {
    position: absolute;
    bottom: .55rem;
    right: .55rem;
    z-index: 500;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .45rem .95rem;
    border-radius: 999px;
    background: rgba(224, 138, 58, .92);
    color: #fff;
    border: 1px solid rgba(255,255,255,.18);
    font-weight: 800;
    font-size: .85rem;
    cursor: pointer;
    backdrop-filter: blur(2px);
    box-shadow: 0 6px 18px rgba(0,0,0,.25);
    transition: transform .18s ease, filter .18s ease;
}

    .lt-playback-toggle:hover {
        transform: translateY(-1px);
        filter: brightness(1.1);
    }

    .lt-playback-toggle i {
        font-size: 1.05rem;
    }

/* Phase B.11 (phase 3 polish) — flight-replay modal used from listing
   pages (admin booking history, members "my flights", partner history).
   Click the airplane icon button on a row → modal pops with the map,
   stats and Play button. */
.flight-replay-modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 1500;
    background: rgba(2,8,23,.7);
    backdrop-filter: blur(3px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.flight-replay-modal {
    width: min(880px, 100%);
    max-height: calc(100vh - 2rem);
    overflow: auto;
    background: #0f1729;
    border: 1px solid rgba(255,255,255,.1);
    border-radius: 20px;
    padding: 1.25rem 1.5rem;
    color: #f1f5f9;
    box-shadow: 0 30px 80px rgba(0,0,0,.55);
}

    .flight-replay-modal h3 {
        margin-top: 0;
    }

.flight-replay-modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .85rem;
}

.flight-replay-modal-close {
    appearance: none;
    background: rgba(255,255,255,.06);
    border: 1px solid rgba(255,255,255,.12);
    color: #f1f5f9;
    width: 34px;
    height: 34px;
    border-radius: 999px;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1rem;
}

    .flight-replay-modal-close:hover {
        background: rgba(255,255,255,.1);
    }

/* Icon-button used inline on listing rows to open the replay modal. */
.flight-replay-icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: 999px;
    background: rgba(224,138,58,.18);
    color: #fcd34d;
    border: 1px solid rgba(224,138,58,.4);
    cursor: pointer;
    transition: background .15s ease;
    text-decoration: none;
}

    .flight-replay-icon-btn:hover {
        background: rgba(224,138,58,.32);
        color: #fff;
    }

    .flight-replay-icon-btn i {
        font-size: .95rem;
    }

    /* The replay button sits at the right edge of the flight-log table, whose
       wrapper scrolls horizontally. A centred tooltip would spill past that edge
       and pop a horizontal scrollbar on hover — so anchor this button's tooltip
       to its right edge and let it grow leftwards into the table instead. */
    .flight-replay-icon-btn[data-tooltip]:hover::after,
    .flight-replay-icon-btn[data-tooltip]:focus-visible::after {
        left: auto;
        right: 0;
        transform: none;
    }

/* ==========================================================================
   Phase B.11 — LIVE FLEET page (/club/live-fleet)
   ========================================================================== */

.live-fleet {
    display: grid;
    grid-template-columns: 1fr minmax(280px, 360px);
    gap: 1rem;
    min-height: 70vh;
    transition: grid-template-columns .25s ease;
}

    /* Collapsed state — sidebar slides out, map takes the full width.
       JS isn't strictly needed; pure CSS grid transition. */
    .live-fleet.live-fleet--collapsed {
        grid-template-columns: 1fr 0;
    }

        .live-fleet.live-fleet--collapsed .live-fleet-side {
            transform: translateX(20px);
            opacity: 0;
            pointer-events: none;
            width: 0;
            padding: 0;
            overflow: hidden;
        }

.live-fleet-map-wrap {
    position: relative;
    border-radius: 18px;
    overflow: hidden;
    border: 1px solid rgba(255,255,255,.08);
    background: #0f1729;
    min-height: 70vh;
}

.live-fleet-map {
    width: 100%;
    /* Definite height — NOT height:100% of a parent that only has min-height.
       A percentage height against an indefinite parent can resolve to 0 at the
       moment Leaflet measures the container, so it loads no tiles and paints
       blank (the intermittent bug). The per-aircraft LiveTracker uses an explicit
       height and never fails, so match that. */
    height: 70vh;
}


.live-fleet-sidebar-toggle {
    position: absolute;
    top: 50%;
    right: -.6rem;   /* hangs over the right edge */
    transform: translateY(-50%);
    z-index: 500;
    width: 28px;
    height: 56px;
    border-radius: 14px;
    background: rgba(15,23,41,.95);
    border: 1px solid rgba(255,255,255,.14);
    color: #fff;
    cursor: pointer;
    box-shadow: 0 6px 14px rgba(0,0,0,.3);
}

    .live-fleet-sidebar-toggle:hover {
        background: rgba(15,23,41,1);
    }

.live-fleet-side {
    background: rgba(15,23,41,.6);
    border-radius: 18px;
    border: 1px solid rgba(255,255,255,.08);
    padding: 1rem;
    display: flex;
    flex-direction: column;
    gap: .85rem;
    min-height: 70vh;
    max-height: 80vh;
    transition: transform .25s ease, opacity .2s ease, padding .25s ease, width .25s ease;
}

.live-fleet-side-header {
    display: grid;
    gap: .35rem;
}

.live-fleet-filter {
    min-height: 42px !important;
    font-size: .92rem !important;
}

.live-fleet-side-body {
    display: grid;
    gap: .65rem;
    overflow-y: auto;
    padding-right: .15rem;   /* room for the scrollbar without offsetting cards */
}

/* ---- Per-aircraft card ---- */
.lf-card {
    background: rgba(255,255,255,.04);
    border: 1px solid rgba(255,255,255,.07);
    border-left-width: 4px;       /* coloured stripe = aircraft.CalendarColor */
    border-radius: 14px;
    padding: .75rem .85rem;
    display: grid;
    gap: .55rem;
}

.lf-card-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    flex-wrap: wrap;
}

.lf-card-reg {
    font-size: 1.05rem;
    font-weight: 800;
    letter-spacing: .02em;
}

.lf-card-status {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .15rem .55rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 800;
    letter-spacing: .03em;
    border: 1px solid rgba(255,255,255,.1);
    background: rgba(255,255,255,.06);
    color: #e2e8f0;
}

.lf-card-status--airborne {
    background: rgba(224,138,58,.22);
    color: #fcd34d;
    border-color: rgba(224,138,58,.4);
}

.lf-card-status--ground {
    background: rgba(96,165,250,.22);
    color: #bfdbfe;
    border-color: rgba(96,165,250,.4);
}

.lf-card-status--unknown {
    background: rgba(148,163,184,.18);
    color: #cbd5e1;
}

.lf-card-status:has(.bi-exclamation-triangle-fill) {
    background: rgba(239,68,68,.25);
    color: #fee2e2;
    border-color: rgba(239,68,68,.5);
}

.lf-card-callsign {
    margin: 0;
    color: rgba(203,213,225,.78);
    font-size: .82rem;
}

    .lf-card-callsign strong {
        color: #fff;
    }

.lf-card-noposition {
    margin: .15rem 0 0;
    padding: .4rem .55rem;
    border-radius: .5rem;
    background: rgba(148,163,184,.12);
    border: 1px solid rgba(148,163,184,.18);
    color: rgba(203,213,225,.85);
    font-size: .78rem;
    line-height: 1.35;
}

    .lf-card-noposition .bi {
        color: #94a3b8;
        margin-right: .2rem;
    }

/* Per-aircraft maintenance & downtime block (on the aircraft card). */
.aircraft-downtime {
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 1px solid rgba(255, 255, 255, .08);
}

.aircraft-downtime-toggle {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    background: transparent;
    border: none;
    padding: .25rem 0;
    cursor: pointer;
    color: inherit;
}

    .aircraft-downtime-toggle .club-kicker {
        margin: 0;
    }

    .aircraft-downtime-toggle:hover .aircraft-downtime-count {
        color: #fff;
    }

.aircraft-downtime-count {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .82rem;
    color: rgba(203, 213, 225, .8);
}

.aircraft-downtime-body {
    margin-top: .75rem;
}

.aircraft-downtime-form {
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px dashed rgba(255, 255, 255, .12);
}

/* ============================================================
   Compact list row — matches the "Approved bookings" condensed
   style. Two tight info lines on the left, actions inline on the
   right. Reused by Members, Instructors, Partner schools and
   Availability lists.
   ============================================================ */
.crow {
    display: grid;
    grid-template-columns: 1fr auto;
    align-items: center;
    gap: .5rem .85rem;
    padding: .55rem .85rem;
    border-radius: 14px;
    background: rgba(255,255,255,.04);
    border: 1px solid rgba(255,255,255,.07);
    transition: border-color .18s ease, background .18s ease;
}

    .crow:hover {
        border-color: rgba(96,165,250,.22);
        background: rgba(255,255,255,.05);
    }

.crow-summary {
    display: flex;
    align-items: center;
    gap: .55rem;
    min-width: 0;
}

.crow-main {
    display: grid;
    gap: .15rem;
    min-width: 0;
}

.crow-line {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .3rem .45rem;
    font-size: .92rem;
    line-height: 1.25;
    color: #f1f5f9;
}

.crow-line--muted {
    color: rgba(203,213,225,.82);
    font-size: .82rem;
}

.crow-name { color: #fff; font-weight: 800; }
.crow-muted-inline { color: rgba(203,213,225,.82); }
.crow-dot { color: rgba(203,213,225,.5); font-weight: 800; }

.crow-pill {
    display: inline-flex;
    align-items: center;
    padding: .12rem .5rem;
    border-radius: 999px;
    font-size: .72rem;
    font-weight: 800;
    border: 1px solid rgba(255,255,255,.08);
    background: rgba(148,163,184,.16);
    color: #cbd5e1;
}

    .crow-pill.is-good {
        background: rgba(22,163,74,.18);
        color: #86efac;
        border-color: rgba(22,163,74,.32);
    }

.crow-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .4rem;
    justify-content: flex-end;
    align-items: center;
}

/* Condensed action button — same sizing as the Approved-bookings actions.
   Pair with .booking-secondary-button (or .btn-primary-brand) for the look. */
.crow-btn {
    min-height: 36px !important;
    padding: 0 .9rem !important;
    font-size: .82rem !important;
    border-radius: 999px !important;
    white-space: nowrap;
}

/* Shared condensed list-row layout — content on the left, actions inline on
   the right, stacking on tablet/phone. Used by Members, Instructors, Partner
   schools and Availability. */
.club-list-item--compact {
    /* !important so this beats each page's scoped `.club-list-item` base rule
       (which carries the component-attribute specificity bump). */
    display: grid !important;
    grid-template-columns: 1fr auto !important;
    align-items: center !important;
    gap: .6rem 1.75rem !important;
}

    .club-list-item--compact .club-list-status-stack {
        grid-auto-flow: column;
        align-items: center;
    }

    .club-list-item--compact .club-list-actions {
        padding-top: 0;
        justify-content: flex-end;
        flex-wrap: wrap;
    }

    /* Hidden placeholder keeps the actions column a consistent width so the
       right-hand group lines up row to row even when a button is suppressed. */
    .club-list-item--compact .crow-btn--placeholder {
        visibility: hidden;
    }

/* Keep the colour dot a fixed circle everywhere (flex can otherwise squash it). */
.club-colour-dot {
    flex: 0 0 auto;
}

@media (max-width: 900px) {
    .club-list-item--compact {
        grid-template-columns: 1fr !important;
    }
}

/* Joined/Renews date row — hidden in the condensed lists (the dates live in
   View). Global + !important so it applies on every page that uses it. */
.member-date-row { display: none !important; }

/* Expiry chips — the styling previously lived only in the Members scoped CSS,
   so the same markup rendered as plain text on Instructors. Promoted to global
   so every list shows proper pills. */
.member-expiry-chip {
    display: inline-flex;
    align-items: center;
    min-height: 22px;
    padding: .16rem .5rem;
    border-radius: 999px;
    border: 1px solid rgba(255,255,255,.08);
    font-size: .72rem;
    font-weight: 800;
    line-height: 1;
    background: rgba(148,163,184,.14);
    color: #cbd5e1;
}

.member-expiry-chip--good   { background: rgba(22,163,74,.16);  color: #86efac; }
.member-expiry-chip--warn   { background: rgba(245,158,11,.16); color: #fde68a; }
.member-expiry-chip--danger { background: rgba(220,38,38,.16);  color: #fca5a5; }
.member-expiry-chip--muted  { background: rgba(148,163,184,.14); color: #cbd5e1; }

.member-expiry-chip-row {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    margin-top: .2rem;
}

@media (max-width: 640px) {
    .crow { grid-template-columns: 1fr; }
    .crow-actions { justify-content: flex-start; }
}

.lf-card-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: .4rem .85rem;
    margin: 0;
}

.lf-stat {
    display: grid;
    gap: .05rem;
}

    .lf-stat dt {
        color: rgba(203,213,225,.6);
        font-size: .68rem;
        font-weight: 700;
        letter-spacing: .04em;
        text-transform: uppercase;
        margin: 0;
    }

    .lf-stat dd {
        color: #fff;
        font-size: .92rem;
        font-weight: 700;
        margin: 0;
        line-height: 1.15;
    }

.lf-vs {
    display: inline-flex;
    align-items: center;
}

.lf-vs--up    { color: #86efac; }
.lf-vs--down  { color: #fca5a5; }
.lf-vs--level { color: rgba(203,213,225,.85); }

.lf-squawk-bad {
    color: #fca5a5;
    font-weight: 800;
}

.lf-stale {
    color: rgba(252,211,77,.85);
}

.lf-card-zoom {
    align-self: start;
    background: rgba(96,165,250,.18);
    color: #bfdbfe;
    border: 1px solid rgba(96,165,250,.34);
    border-radius: 999px;
    padding: .35rem .8rem;
    font-size: .8rem;
    font-weight: 700;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    transition: background .15s ease;
}

    .lf-card-zoom:hover:not([disabled]) {
        background: rgba(96,165,250,.3);
        color: #fff;
    }

    .lf-card-zoom:disabled {
        opacity: .4;
        cursor: not-allowed;
    }

/* ---- Mobile: stack the panels and let the sidebar push the map down. ---- */
@media (max-width: 900px) {
    .live-fleet {
        grid-template-columns: 1fr;
    }

        .live-fleet.live-fleet--collapsed {
            grid-template-columns: 1fr;
        }

    .live-fleet-map-wrap,
    .live-fleet-map {
        height: 55vh;
    }

    .live-fleet-side {
        min-height: auto;
        max-height: none;
    }

        .live-fleet.live-fleet--collapsed .live-fleet-side {
            display: none;
        }

    .live-fleet-sidebar-toggle {
        top: auto;
        bottom: 1rem;
        right: 1rem;
        width: 44px;
        height: 44px;
        border-radius: 999px;
        transform: none;
    }
}

/* ==========================================================================
   Phase B.10 — PARTNER SCHOOL AREA
   Global because scoped .razor.css doesn't cross page boundaries; the
   partner shell + the partner pages live in separate .razor files so
   shared classes have to be defined here to actually paint.
   ========================================================================== */

.partner-layout {
    display: grid;
    gap: 1.25rem;
}

.partner-chip-nav {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem;
    padding: .25rem 0 1rem 0;
    border-bottom: 1px solid rgba(255,255,255,.06);
    margin-bottom: 1rem;
}

.partner-chip {
    display: inline-flex !important;
    align-items: center;
    gap: .5rem;
    padding: .55rem 1.1rem;
    border-radius: 999px;
    border: 1px solid rgba(255,255,255,.12);
    background: rgba(255,255,255,.04);
    color: rgba(241,245,249,.88) !important;
    text-decoration: none !important;
    font-weight: 700;
    font-size: .95rem;
    transition: background .18s ease, border-color .18s ease, color .18s ease, transform .18s ease;
}

    .partner-chip i {
        font-size: 1.05rem;
    }

    .partner-chip:hover {
        background: rgba(255,255,255,.08);
        border-color: rgba(96,165,250,.4);
        color: #fff !important;
        transform: translateY(-1px);
        text-decoration: none !important;
    }

    .partner-chip.active {
        background: linear-gradient(135deg, rgba(201,106,43,.28), rgba(96,165,250,.18));
        border-color: rgba(224,138,58,.5);
        color: #fff !important;
        box-shadow: 0 8px 22px rgba(0,0,0,.18);
    }

.partner-content {
    display: grid;
    gap: 1rem;
}

/* ---------- Stat / hero grid (dashboard) ---------- */

.partner-stat-grid {
    display: grid;
    gap: .85rem;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    margin-top: .5rem;
}

.partner-stat-card {
    padding: 1rem 1.1rem;
    border-radius: 18px;
    background: rgba(255,255,255,.04);
    border: 1px solid rgba(255,255,255,.07);
}

    .partner-stat-card .label {
        display: block;
        font-size: .78rem;
        font-weight: 800;
        letter-spacing: .04em;
        text-transform: uppercase;
        color: rgba(203,213,225,.72);
        margin-bottom: .35rem;
    }

    .partner-stat-card .value {
        display: block;
        font-size: 1.65rem;
        font-weight: 800;
        color: #fff;
        line-height: 1.1;
    }

    .partner-stat-card .hint {
        display: block;
        margin-top: .2rem;
        font-size: .8rem;
        color: rgba(203,213,225,.65);
    }

    .partner-stat-card.is-warn .value { color: #fcd34d; }
    .partner-stat-card.is-danger .value { color: #fca5a5; }

/* ---------- Compact roster table ---------- */

.partner-roster {
    width: 100%;
    border-collapse: collapse;
    margin-top: .25rem;
}

    .partner-roster thead th {
        text-align: left;
        font-size: .75rem;
        font-weight: 800;
        letter-spacing: .05em;
        text-transform: uppercase;
        color: rgba(203,213,225,.7);
        padding: .65rem .8rem;
        border-bottom: 1px solid rgba(255,255,255,.1);
    }

    .partner-roster tbody td {
        padding: .85rem .8rem;
        border-bottom: 1px solid rgba(255,255,255,.05);
        color: rgba(241,245,249,.92);
        vertical-align: middle;
    }

    .partner-roster tbody tr:hover td {
        background: rgba(255,255,255,.03);
    }

    .partner-roster .student-name {
        font-weight: 700;
        color: #fff;
    }

    .partner-roster .muted {
        color: rgba(203,213,225,.65);
        font-size: .85rem;
    }

    .partner-roster .actions {
        text-align: right;
        white-space: nowrap;
    }

    .partner-roster tfoot td {
        padding: .75rem .8rem;
        border-top: 2px solid rgba(224,138,58,.4);
        background: rgba(224,138,58,.07);
        font-size: .9rem;
    }

    .partner-roster-totals {
        font-weight: 800;
    }

    /* Phase B.10 — collapsible group rows on /partner/history.
       Click anywhere on the row to toggle. Chevron rotates via state. */
    .partner-roster .ph-group-row {
        cursor: pointer;
        background: rgba(96,165,250,.08);
        transition: background .15s ease;
    }

        .partner-roster .ph-group-row:hover {
            background: rgba(96,165,250,.14);
        }

        .partner-roster .ph-group-row td {
            border-bottom: 1px solid rgba(96,165,250,.18);
            padding: .65rem .8rem;
        }

    .ph-group-cell {
        display: inline-flex;
        align-items: center;
        gap: .65rem;
        font-size: .95rem;
        color: #fff;
    }

        .ph-group-cell i {
            font-size: 1.05rem;
            color: #93c5fd;
            transition: transform .15s ease;
        }

        .ph-group-cell strong {
            color: #fff;
        }

        .ph-group-cell .muted {
            color: rgba(203,213,225,.65);
            font-size: .82rem;
            font-weight: 600;
        }

    .partner-roster .ph-detail-row td {
        background: rgba(255,255,255,.015);
    }

/* ==========================================================================
   Phase B.10 — Club / Partner shell chrome + collapsible nav
   Copied out of the scoped ClubShell + ClubNavMenu CSS so PartnerShell
   (and any future shell) gets the same collapsible "[…] navigation"
   widget styling without duplicating files.
   ========================================================================== */

.club-page-shell {
    padding-bottom: 3rem;
}

.club-layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 1rem;
    align-items: start;
    padding-top: 1.15rem;
}

.club-sidebar {
    position: static;
    display: grid;
    gap: .85rem;
    min-width: 0;
}

.club-nav-toggle { display: none; }
.club-nav-shell { display: block; }

.club-sidebar-toggle {
    display: none;
    width: 100%;
    min-height: 56px;
    padding: 0 1rem;
    border-radius: 18px;
    border: 1px solid rgba(255,255,255,.10);
    background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.03));
    color: #fff;
    font-weight: 800;
    align-items: center;
    justify-content: space-between;
    gap: .9rem;
    box-shadow: 0 14px 28px rgba(0,0,0,.16);
    transition: border-color .18s ease, background .18s ease, transform .18s ease, box-shadow .18s ease;
}

    .club-sidebar-toggle:hover {
        border-color: rgba(255,255,255,.16);
        background: linear-gradient(180deg, rgba(255,255,255,.07), rgba(255,255,255,.04));
        transform: translateY(-1px);
    }

.club-sidebar-toggle-icon {
    width: 20px;
    display: grid;
    gap: 4px;
    flex: 0 0 auto;
}

    .club-sidebar-toggle-icon span {
        display: block;
        height: 2px;
        border-radius: 999px;
        background: #fff;
        opacity: .92;
    }

.club-sidebar-toggle-text {
    flex: 1 1 auto;
    text-align: left;
    font-size: .95rem;
    line-height: 1;
}

.club-sidebar-toggle-chevron {
    flex: 0 0 auto;
    font-size: .95rem;
    opacity: .85;
}

.club-sidebar-toggle-chevron--down { display: inline-block; }
.club-sidebar-toggle-chevron--up   { display: none; }

.club-content {
    min-width: 0;
    display: grid;
    gap: 1.25rem;
}

/* ---------- Nav menu (chip strip) ---------- */

.club-nav {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: .55rem;
    padding: 0;
    background: transparent;
    border: none;
    box-shadow: none;
    backdrop-filter: none;
    width: 100%;
}

.club-nav-link {
    text-decoration: none;
    display: block;
    min-width: 0;
    flex: 0 1 150px;
}

.club-nav-item {
    width: 100%;
    min-height: 56px;
    height: 100%;
    border: 1px solid rgba(255,255,255,.08);
    background: rgba(255,255,255,.03);
    color: #fff;
    border-radius: 999px;
    padding: .7rem .95rem;
    display: grid;
    align-content: center;
    gap: .12rem;
    text-align: left;
    transition: transform .2s ease, background .2s ease, border-color .2s ease, box-shadow .2s ease;
    box-sizing: border-box;
}

.club-nav-link:hover .club-nav-item {
    transform: translateY(-3px) scale(1.015);
    background: rgba(255,255,255,.09);
    border-color: rgba(255,255,255,.16);
    box-shadow: 0 14px 30px rgba(0,0,0,.22);
}

.club-nav-link.active .club-nav-item {
    background: linear-gradient(135deg, rgba(201,106,43,.20), rgba(255,255,255,.06));
    border-color: rgba(224,138,58,.28);
    box-shadow: 0 12px 28px rgba(0,0,0,.18);
}

.club-nav-item-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .55rem;
    min-width: 0;
}

.club-nav-title {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    min-width: 0;
    font-weight: 800;
    color: #fff;
    font-size: .92rem;
    line-height: 1.1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

    .club-nav-title i {
        font-size: .95rem;
        line-height: 1;
        flex: 0 0 auto;
    }

.club-nav-copy { display: none; }

/* Phase B.10 — dense chips shrink-wrap to their label width. The old
   `flex: 0 1 120px` rule was chopping any label longer than ~10
   characters ("Aircraft Bloc…", "Partner scho…", "Flight Compl…").
   `flex: 0 0 auto` + content-sized pill + nowrap label = each chip is
   exactly as wide as its text + icon + padding; the strip wraps naturally
   onto as many rows as needed. */
.club-nav--dense .club-nav-link {
    flex: 0 0 auto;
    width: auto;
    min-width: 0;
}

.club-nav--dense .club-nav-item {
    min-height: 42px;
    width: auto;
    min-width: 0;
    padding: .4rem .85rem;
    border-radius: 999px;
}

.club-nav--dense .club-nav-title {
    font-size: .8rem;
    gap: .35rem;
    overflow: visible;
    text-overflow: clip;
}

.club-nav--dense .club-nav-title i { font-size: .82rem; }

.club-nav--dense .club-nav-copy { display: none !important; }

@media (max-width: 1380px) {
    .club-nav-link { flex-basis: 145px; }
}

@media (max-width: 1180px) {
    .club-nav-link { flex: 1 1 calc(25% - .55rem); }
}

@media (max-width: 980px) {
    .club-nav-link { flex: 1 1 calc(50% - .55rem); }

    .club-nav-item {
        min-height: 64px;
        padding: .78rem .9rem;
        border-radius: 28px;
    }

    .club-nav-copy {
        display: block;
        color: rgba(220,233,245,.72);
        font-size: .82rem;
        line-height: 1.2;
    }
}

@media (max-width: 860px) {
    .club-nav {
        display: grid;
        grid-template-columns: 1fr;
        gap: .65rem;
        padding: 0;
    }

    .club-nav-link { flex: initial; }

    .club-nav-item {
        min-height: 72px;
        padding: .88rem 1rem;
        border-radius: 28px;
    }

    .club-nav-copy {
        display: block;
        color: rgba(220,233,245,.72);
        font-size: .88rem;
        line-height: 1.25;
    }

    /* Collapsible activation — show the toggle button, hide the menu
       until the hidden checkbox flips. Same checkbox/label trick as
       ClubShell, but global so PartnerShell gets it too. */
    .club-sidebar-toggle { display: inline-flex; }
    .club-nav-shell .club-nav { display: none; }

    .club-nav-toggle:checked + .club-sidebar-toggle + .club-nav-shell .club-nav {
        display: grid;
        /* No margin-top: .club-sidebar already supplies gap between children,
           so adding more here double-spaces the menu away from the toggle. */
    }

    .club-nav-toggle:checked + .club-sidebar-toggle .club-sidebar-toggle-chevron--down {
        display: none;
    }

    .club-nav-toggle:checked + .club-sidebar-toggle .club-sidebar-toggle-chevron--up {
        display: inline-block;
    }
}

/* Phase B.10 — confirmation modal used by Mark complete. Generic enough
   to reuse for future partner-area confirmations. */
.partner-modal-overlay {
    position: fixed;
    inset: 0;
    z-index: 1500;
    background: rgba(2, 8, 23, .65);
    backdrop-filter: blur(2px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
}

.partner-modal {
    width: 100%;
    max-width: 460px;
    background: #0f1729;
    border: 1px solid rgba(255,255,255,.1);
    border-radius: 20px;
    padding: 1.4rem 1.5rem;
    box-shadow: 0 24px 64px rgba(0,0,0,.5);
    color: #f1f5f9;
}

    .partner-modal .card-title {
        color: #fff;
    }

    .partner-modal .card-copy {
        color: rgba(203,213,225,.85);
        line-height: 1.45;
    }

.partner-action-btn {
    display: inline-flex !important;
    align-items: center;
    gap: .35rem;
    padding: .45rem 1rem;
    border-radius: 999px;
    background: linear-gradient(135deg, #c96a2b, #e08a3a);
    color: #fff !important;
    text-decoration: none !important;
    font-weight: 700;
    font-size: .85rem;
    border: none;
    cursor: pointer;
    transition: filter .18s ease, transform .18s ease;
}

    .partner-action-btn:hover {
        filter: brightness(1.1);
        transform: translateY(-1px);
        color: #fff !important;
        text-decoration: none !important;
    }

.partner-action-link {
    display: inline-flex !important;
    align-items: center;
    gap: .35rem;
    padding: .45rem 1rem;
    border-radius: 999px;
    background: rgba(96,165,250,.18);
    color: #bfdbfe !important;
    text-decoration: none !important;
    font-weight: 700;
    font-size: .85rem;
    border: 1px solid rgba(96,165,250,.34);
    margin-right: .4rem;
    transition: background .18s ease, color .18s ease;
}

    .partner-action-link:hover {
        background: rgba(96,165,250,.28);
        color: #dbeafe !important;
        text-decoration: none !important;
    }

/* ---------- Status pills ---------- */

.partner-pill {
    display: inline-block;
    padding: .25rem .6rem;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 800;
    letter-spacing: .03em;
    border: 1px solid rgba(255,255,255,.1);
    background: rgba(255,255,255,.06);
    color: #e2e8f0;
}

    .partner-pill.is-good {
        background: rgba(22,163,74,.16);
        color: #86efac;
        border-color: rgba(22,163,74,.32);
    }

    .partner-pill.is-warn {
        background: rgba(245,158,11,.16);
        color: #fcd34d;
        border-color: rgba(245,158,11,.32);
    }

    .partner-pill.is-danger {
        background: rgba(239,68,68,.16);
        color: #fca5a5;
        border-color: rgba(239,68,68,.32);
    }

/* ---------- Chart wrapper ---------- */

.partner-chart-wrap {
    position: relative;
    min-height: 260px;
    margin-top: .5rem;
}

/* ---------- Header / search row ---------- */

.partner-header-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin: .75rem 0 .5rem 0;
}

.partner-header-row .partner-search {
    flex: 1 1 18rem;
    min-width: 0;
}

/* Condense EVERY input / select / button living in a partner-header-row,
   not just .partner-search. Date pickers, dropdowns and the Reload button
   all need to match the 40px chip-row height so the strip reads as one. */
.partner-header-row .booking-input,
.partner-header-row input[type=text],
.partner-header-row input[type=date],
.partner-header-row select,
.partner-header-row .btn-primary-brand,
.partner-header-row .btn-secondary-brand {
    width: 100% !important;
    min-height: 40px !important;
    height: 40px !important;
    padding: .35rem .9rem !important;
    border-radius: 12px !important;
    font-size: .9rem !important;
    background: rgba(20,35,56,.92);
    color: #f8fafc;
    border: 1px solid rgba(148,163,184,.38);
}

.partner-header-row .btn-primary-brand,
.partner-header-row .btn-secondary-brand {
    width: auto !important;
    padding: 0 1.1rem !important;
    background: linear-gradient(135deg, #c96a2b, #e08a3a);
    color: #fff !important;
    border: none;
    font-weight: 700;
    cursor: pointer;
}

.partner-header-row label {
    font-size: .72rem !important;
    font-weight: 800;
    letter-spacing: .05em;
    text-transform: uppercase;
    color: rgba(203,213,225,.7);
    margin-bottom: 0 !important;
}

.partner-header-row .partner-toolbar-field {
    flex: 0 0 auto;
}

/* ---------- Responsive ---------- */

@media (max-width: 720px) {
    .partner-chip-nav {
        gap: .45rem;
    }

    .partner-chip span {
        display: none;
    }

    .partner-chip {
        padding: .55rem .8rem;
    }

    .partner-roster .actions {
        text-align: left;
    }

    .partner-action-link {
        margin-right: .25rem;
    }
}
}

/* ============================================================
   Phase B.16 — aircraft access checkbox grid. Shared by the member
   self-hire picker (Members), the school aircraft picker (Partner
   Schools) and the school's per-student picker (student detail).
============================================================ */
.aircraft-access-grid {
    display: flex !important;
    flex-wrap: wrap;
    align-items: center;       /* keep every chip on the same line, centred */
    gap: 0;                    /* spacing comes from per-chip margins (see below) */
    margin: .55rem 0 .25rem;
}

/* NOTE: these chips are <label>s rendered inside class="booking-form", so the
   global `.booking-form label` rules (display/margin/etc.) would otherwise win
   on specificity and break the layout — hence the !important guards below. */
.aircraft-access-chip {
    display: inline-flex !important;
    align-items: center !important;
    vertical-align: middle;
    gap: .6rem;
    /* Fixed height + border-box so selecting (which swaps the ::before glyph)
       can never change the chip's size or nudge it out of line. */
    min-height: 2.5rem;
    box-sizing: border-box;
    /* Spacing lives here (not on the container's flex gap, which the global
       form-label rules were swallowing): right gap between chips on a row,
       bottom gap when they wrap. !important beats `.booking-form label`. */
    margin: 0 .85rem .6rem 0 !important;
    padding: 0 .85rem 0 .6rem !important;
    border-radius: 12px;
    border: 1px solid rgba(255, 255, 255, .1);
    background: rgba(255, 255, 255, .035);
    cursor: pointer;
    transition: border-color .15s ease, background .15s ease;
    user-select: none;
    line-height: 1;
}

    .aircraft-access-chip:hover {
        border-color: rgba(96, 165, 250, .45);
        background: rgba(255, 255, 255, .06);
    }

    /* Hide the native checkbox; the tick box is drawn with ::before so the
       selected state reads consistently across web + WebView regardless of
       the platform's default checkbox styling. The .is-on class (set in markup)
       drives it, so no :has() support is needed. */
    .aircraft-access-chip input {
        position: absolute;
        opacity: 0;
        width: 0;
        height: 0;
        margin: 0;
    }

    .aircraft-access-chip::before {
        content: "";
        width: 1.15rem;
        height: 1.15rem;
        flex: 0 0 auto;
        box-sizing: border-box;
        border-radius: 6px;
        border: 1.5px solid rgba(255, 255, 255, .28);
        background: rgba(255, 255, 255, .04);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        font-size: .82rem;
        font-weight: 900;
        line-height: 1;
        color: transparent;
        transition: background .15s ease, border-color .15s ease, color .15s ease;
    }

    .aircraft-access-chip.is-on {
        border-color: rgba(34, 197, 94, .55);
        background: rgba(22, 163, 74, .14);
    }

        .aircraft-access-chip.is-on::before {
            content: "\2713"; /* ✓ */
            background: #22c55e;
            border-color: #22c55e;
            color: #06240f;
        }

    .aircraft-access-chip.is-inactive { opacity: .5; }

.aircraft-access-reg {
    font-weight: 800;
    color: #f8fafc;
    letter-spacing: .01em;
}

.aircraft-access-type {
    font-size: .68rem;
    font-weight: 700;
    color: rgba(203, 213, 225, .82);
    background: rgba(255, 255, 255, .07);
    padding: .12rem .42rem;
    border-radius: 6px;
    letter-spacing: .03em;
    text-transform: uppercase;
}

.aircraft-access-toolbar {
    display: flex;
    gap: .9rem;
    flex-wrap: wrap;
    margin-bottom: .55rem;
}

/* ============================================================
   Pull-to-refresh (mobile). Native browser PTR is disabled via
   overscroll-behavior so the custom gesture in pull-to-refresh.js is the
   single, consistent one across the app WebView and mobile web. The
   indicator reuses the brand .app-spinner (rotating Adur logo).
============================================================ */
html, body { overscroll-behavior-y: contain; }

.adur-ptr {
    position: fixed;
    top: -56px;
    left: 50%;
    transform: translateX(-50%) translateY(0);
    z-index: 12000;
    width: 44px;
    height: 44px;
    border-radius: 999px;
    background: rgba(15, 23, 42, .96);
    border: 1px solid rgba(255, 255, 255, .12);
    box-shadow: 0 10px 24px rgba(0, 0, 0, .45);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity .15s ease, transform .12s ease, border-color .15s ease;
    pointer-events: none;
}

    /* Centre the logo spinner inside the pill (drop its inline margin). */
    .adur-ptr .app-spinner {
        margin: 0;
        vertical-align: middle;
    }

    /* Armed = pulled far enough; release to refresh. */
    .adur-ptr.is-armed {
        border-color: rgba(34, 197, 94, .6);
        box-shadow: 0 10px 24px rgba(0, 0, 0, .45), 0 0 0 2px rgba(34, 197, 94, .25);
    }
