/* _content/ChoirMusic/Components/Layout/MainLayout.razor.rz.scp.css */
#blazor-error-ui[b-b83kw5wptc] {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss[b-b83kw5wptc] {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

/* Wraps @Body whenever the top bar is showing, so fixed-position bar content
   (below) never overlaps whatever the current page renders — Choir Music's
   group list, Admin's or About's placeholder text, etc. Not needed in the
   signed-out branch, since @Body there is just the full-screen login card.
   The offset is sized to the .logout-bar's own box (10px top position + its
   own height) plus a small 8px breathing gap, rather than a rounder number
   that left a noticeably larger gap above the page content. */
.app-content[b-b83kw5wptc] {
    padding-top: calc(74px + env(safe-area-inset-top, 0px));
    box-sizing: border-box;
}

/* Brief state while the Village membership check (see
   NetworkNodesMembershipService and MainLayout's OnInitializedAsync) is
   still in flight — deliberately plain/minimal since it's only ever on
   screen for as long as one HTTP call takes. */
.membership-check[b-b83kw5wptc] {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    inset: 0;
    color: rgba(255, 255, 255, 0.6);
    font-size: 14px;
}

/* Shown instead of the app entirely when the membership check comes back
   negative — same full-screen centered-card treatment as Home.razor's own
   .login-page/.login-card, since this is effectively another "you can't get
   into the app yet" screen. */
.not-a-member-page[b-b83kw5wptc] {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    inset: 0;
}

.not-a-member-card[b-b83kw5wptc] {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 16px;
    padding: 48px 40px;
    text-align: center;
    max-width: 420px;
    width: 100%;
}

.not-a-member-card h2[b-b83kw5wptc] {
    margin: 0 0 8px;
    font-size: 24px;
    font-weight: 600;
}

.not-a-member-card p[b-b83kw5wptc] {
    margin: 0 0 16px;
    color: rgba(255, 255, 255, 0.5);
    font-size: 14px;
}

.not-a-member-card p:last-of-type[b-b83kw5wptc] {
    margin-bottom: 32px;
}

/* .not-a-member-signout-btn's own rule removed here -- that button is gone
   from MainLayout.razor now that .logout-bar (with its own signout-btn)
   renders for this state too, rather than duplicating a second Sign out
   control inside the card. */

@media (max-width: 640px) {
    .app-content[b-b83kw5wptc] {
        /* Matches the icon buttons' own mobile breakpoint below, which grow
           from 40px to 48px and need a bit more clearance underneath — same
           10px top position + taller bar height + 8px gap as the desktop
           rule above. */
        padding-top: calc(82px + env(safe-area-inset-top, 0px));
    }
}

/* Moved here from Home.razor.css — this bar (and the buttons/help dialog
   below) now lives in the shared layout instead of a single page, so its
   scoped styles have to live here too: Blazor's CSS isolation scopes each
   .razor.css file to the elements its own .razor file renders. */
.logout-bar[b-b83kw5wptc] {
    position: fixed;
    /* env(safe-area-inset-top) keeps this clear of the notch / status bar
       on phones like the iPhone instead of sitting underneath it. */
    top: calc(10px + env(safe-area-inset-top, 0px));
    /* Spans the full row edge-to-edge, matching NetworkNodesApp's
       logout-bar, rather than floating as a centered pill. Content stays
       visually centered via justify-content below. */
    left: 0;
    right: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    background: #4f8cff;
    padding: 8px 20px;
    border-radius: 20px;
    font-size: 13px;
    box-sizing: border-box;
    /* iOS home-screen (Add to Home Screen / "standalone" display mode,
       see wwwroot/manifest.json) WebKit compositing fix. Blazor Server
       never does a real page navigation for in-app moves -- opening the
       Music Event modal (.event-modal-overlay), the Help modal, the PDF
       viewer overlay, or the offline-copy modal all just patch the DOM
       over SignalR, the same way React/Vue SPA routing does. In standalone
       display mode specifically (not in an ordinary mobile Safari tab),
       WebKit has a long-standing bug where a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed -- it keeps existing in the
       DOM (dev tools still shows it, and un-installing/reinstalling the
       PWA "fixes" it, which is what made this look like a one-off) but
       stops being repainted at its fixed viewport position, so it reads to
       the user as the whole top bar -- Choir Music / Song Library / Admin
       / Help / Sign out -- having vanished after they open (and, more
       often, after they close) any of those full-screen overlays. Forcing
       this element onto its OWN GPU compositing layer up front, rather
       than letting WebKit decide whether it deserves one, is the standard
       workaround for this exact class of bug: translateZ(0) (plus the
       -webkit- prefix for older iOS Safari/WebKit) promotes it, and
       backface-visibility/perspective keep that layer from being merged
       back into the document layer on the next repaint. This is harmless
       on every other platform -- it doesn't change layout or visible
       appearance anywhere, only how the browser paints this element. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;
}

/* Round icon buttons — Choir Music / Admin / Help / Sign out all share this
   look, matching NetworkNodesApp's logout-bar icon buttons (same size,
   background, and hover treatment) rather than being tucked behind a
   hamburger menu. */
.logout-bar .choir-music-btn[b-b83kw5wptc],
.logout-bar .song-library-btn[b-b83kw5wptc],
.logout-bar .admin-btn[b-b83kw5wptc],
.logout-bar .help-btn[b-b83kw5wptc],
.logout-bar .signout-btn[b-b83kw5wptc] {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    flex: none;
    border: none;
    outline: none;
    box-shadow: none;
    appearance: none;
    -webkit-appearance: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.12);
    color: #1a1a1a;
    cursor: pointer;
}

.logout-bar .choir-music-btn:hover[b-b83kw5wptc],
.logout-bar .choir-music-btn:focus[b-b83kw5wptc],
.logout-bar .choir-music-btn:focus-visible[b-b83kw5wptc],
.logout-bar .song-library-btn:hover[b-b83kw5wptc],
.logout-bar .song-library-btn:focus[b-b83kw5wptc],
.logout-bar .song-library-btn:focus-visible[b-b83kw5wptc],
.logout-bar .admin-btn:hover[b-b83kw5wptc],
.logout-bar .admin-btn:focus[b-b83kw5wptc],
.logout-bar .admin-btn:focus-visible[b-b83kw5wptc],
.logout-bar .help-btn:hover[b-b83kw5wptc],
.logout-bar .help-btn:focus[b-b83kw5wptc],
.logout-bar .help-btn:focus-visible[b-b83kw5wptc],
.logout-bar .signout-btn:hover[b-b83kw5wptc],
.logout-bar .signout-btn:focus[b-b83kw5wptc],
.logout-bar .signout-btn:focus-visible[b-b83kw5wptc] {
    background: rgba(0, 0, 0, 0.22);
    outline: none;
    box-shadow: none;
}

@media (max-width: 640px) {
    .logout-bar .choir-music-btn[b-b83kw5wptc],
    .logout-bar .song-library-btn[b-b83kw5wptc],
    .logout-bar .admin-btn[b-b83kw5wptc],
    .logout-bar .help-btn[b-b83kw5wptc],
    .logout-bar .signout-btn[b-b83kw5wptc] {
        width: 48px;
        height: 48px;
    }
}

.logout-bar .user-name[b-b83kw5wptc] {
    color: #000000;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

@media (max-width: 640px) {
    .logout-bar .user-name[b-b83kw5wptc] {
        max-width: 40vw;
    }
}

/* Help dialog — same dark-panel treatment as NetworkNodesApp's help modal. */
.help-modal-overlay[b-b83kw5wptc] {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2000;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}

/* Full-screen by design -- every modal in this app occupies the entire
   viewport once opened, rather than floating as a smaller centered card. */
.help-modal-panel[b-b83kw5wptc] {
    background: rgba(20, 25, 40, 0.98);
    border: none;
    border-radius: 0;
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    color: #e6ecff;
    display: flex;
    flex-direction: column;
}

.help-modal-header[b-b83kw5wptc] {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 14px 18px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
    font-size: 15px;
    flex-shrink: 0;
}

.help-modal-close[b-b83kw5wptc] {
    background: transparent;
    border: none;
    color: rgba(255, 255, 255, 0.5);
    font-size: 14px;
    cursor: pointer;
    padding: 4px 8px;
    border-radius: 4px;
}

.help-modal-close:hover[b-b83kw5wptc] {
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
}

.help-modal-body[b-b83kw5wptc] {
    padding: 8px 22px 22px;
    overflow-y: auto;
    font-size: 14px;
    line-height: 1.5;
    flex: 1;
    min-height: 0;
}

.help-modal-body h3[b-b83kw5wptc] {
    font-size: 14px;
    color: #9cc4ff;
    margin: 18px 0 6px;
}

.help-modal-body h3:first-child[b-b83kw5wptc] {
    margin-top: 14px;
}

.help-modal-body p[b-b83kw5wptc] {
    margin: 6px 0;
    color: rgba(230, 236, 255, 0.85);
}

.help-modal-body ul[b-b83kw5wptc] {
    margin: 6px 0;
    padding-left: 20px;
}

.help-modal-body li[b-b83kw5wptc] {
    margin: 6px 0;
    color: rgba(230, 236, 255, 0.85);
}

.help-modal-footnote[b-b83kw5wptc] {
    margin-top: 18px;
    padding-top: 12px;
    border-top: 1px solid rgba(255, 255, 255, 0.1);
    font-size: 12px;
    color: rgba(255, 255, 255, 0.5);
}
/* _content/ChoirMusic/Components/Pages/Admin.razor.rz.scp.css */
.admin-page[b-ffbqstm79g] {
    max-width: 760px;
}

/* Scoped, independently-scrollable wrapper -- same reasoning as
   ChoirMusicPage's own .choir-table-scroll (duplicated here per this app's
   established CSS-isolation convention): without this, a long choir name
   used to force the ENTIRE page wider than the viewport instead of just
   this table. The @media rule further down already shrinks the
   fixed-width columns enough that a typical phone never actually needs
   that scroll -- this is the fallback for a longer name or a narrower
   device, not the primary fix. */
.choir-table-scroll[b-ffbqstm79g] {
    margin-top: 24px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.choir-table[b-ffbqstm79g] {
    width: 100%;
    min-width: 360px;
    border-collapse: collapse;
}

.choir-table thead th[b-ffbqstm79g] {
    text-align: left;
    padding: 0 8px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.choir-table-icon-col[b-ffbqstm79g] {
    width: 60px;
}

/* Mobile: shrink the icon and cell padding so a typical phone fits all
   three columns without needing .choir-table-scroll's horizontal scroll at
   all. Matches ChoirMusicPage.razor.css's own icon-button breakpoint
   convention (also 640px). */
@media (max-width: 640px) {
    .choir-table[b-ffbqstm79g] {
        min-width: 300px;
    }

    .choir-table-icon-col[b-ffbqstm79g] {
        width: 40px;
    }

    .choir-icon[b-ffbqstm79g] {
        width: 32px;
        height: 32px;
    }

    .choir-table-actions-col[b-ffbqstm79g] {
        width: 70px;
    }

    .choir-row td[b-ffbqstm79g],
    .choir-table thead th[b-ffbqstm79g] {
        padding: 8px 4px;
    }
}

.choir-table-sort-btn[b-ffbqstm79g] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: none;
    padding: 0;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
    cursor: pointer;
}

.choir-table-sort-btn:hover[b-ffbqstm79g],
.choir-table-sort-btn:focus-visible[b-ffbqstm79g] {
    color: rgba(0, 0, 0, 0.9);
}

.choir-table-sort-indicator[b-ffbqstm79g] {
    font-size: 10px;
}

.choir-row[b-ffbqstm79g] {
    cursor: pointer;
}

.choir-row:hover[b-ffbqstm79g],
.choir-row:focus-visible[b-ffbqstm79g] {
    background: rgba(0, 0, 0, 0.04);
}

.choir-row:focus-visible[b-ffbqstm79g] {
    outline: 2px solid #4f8cff;
    outline-offset: -2px;
}

.choir-row td[b-ffbqstm79g] {
    padding: 10px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.choir-icon[b-ffbqstm79g] {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: block;
    object-fit: cover;
}

.choir-table-name-col[b-ffbqstm79g] {
    font-size: 15px;
    font-weight: 500;
}

.choir-table-actions-col[b-ffbqstm79g] {
    width: 90px;
    white-space: nowrap;
    text-align: right;
}

/* ---------- Page header / "Link Choir" toolbar ----------
   Same grey rounded-rectangle treatment as AdminChoirManagePage's Music
   Library/Music Events toolbars (.admin-grid-toolbar there) -- itself
   matching MainLayout's top "sign off" bar (.logout-bar)'s shape, but grey
   rather than that bar's brand blue since this is a page header, not a
   primary action bar. Duplicated here rather than shared, matching this
   app's existing convention of duplicating scoped "look" classes per
   component (see AdminChoirManagePage.razor.css's own comment on
   .choir-access-denied for the same convention). */
.admin-grid-toolbar[b-ffbqstm79g] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 12px;
    padding: 10px 16px;
    margin-bottom: 8px;
}

.admin-grid-toolbar h3[b-ffbqstm79g] {
    margin: 0;
}

.admin-grid-add-btn[b-ffbqstm79g] {
    padding: 6px 14px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.admin-grid-add-btn:hover[b-ffbqstm79g] {
    background: rgba(0, 0, 0, 0.04);
}

.admin-grid-empty[b-ffbqstm79g] {
    color: rgba(0, 0, 0, 0.5);
}

.admin-grid-row-btn[b-ffbqstm79g] {
    padding: 4px 10px;
    margin-left: 6px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 12px;
    cursor: pointer;
}

.admin-grid-row-btn:hover[b-ffbqstm79g] {
    background: rgba(0, 0, 0, 0.04);
}

.admin-grid-row-btn-danger[b-ffbqstm79g] {
    border-color: rgba(229, 0, 0, 0.35);
    color: #c62828;
}

.admin-grid-row-btn-danger:hover[b-ffbqstm79g] {
    background: rgba(229, 0, 0, 0.06);
}

.admin-grid-error[b-ffbqstm79g] {
    margin-top: 12px;
    color: #c62828;
    font-size: 13px;
}

/* ---------- Link Choir / Delete Choir modals ----------
   Same overlay/panel/form treatment as AdminChoirManagePage's add/edit and
   delete-confirm modals -- duplicated here for the same CSS-isolation
   reason as that page's own comment on this pattern (each component's
   scoped .razor.css only matches elements THAT component renders). */

.admin-modal-overlay[b-ffbqstm79g] {
    position: fixed;
    inset: 0;
    /* Was 1000 -- tied with MainLayout's .logout-bar (also position:
       fixed, also z-index: 1000). See ChoirMusicPage.razor.css's
       .event-modal-overlay for the full reasoning: every other full-screen
       overlay in this app already sits clearly above the top bar's 1000,
       and this one is bumped to match that same convention rather than
       relying on paint order to keep it above the bar while open. */
    z-index: 1010;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.35);
    padding: 0;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}

/* Full-screen by design -- every modal in this app, including small
   confirmation popups (see .admin-modal-panel-small below), occupies the
   entire viewport once opened, rather than floating as a smaller centered
   card. */
.admin-modal-panel[b-ffbqstm79g] {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    overflow-y: auto;
    background: #fff;
    border-radius: 0;
    box-shadow: none;
}

.admin-modal-panel-small[b-ffbqstm79g] {
    max-width: none;
}

.admin-modal-header[b-ffbqstm79g] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.admin-modal-close[b-ffbqstm79g] {
    border: none;
    background: none;
    font-size: 14px;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.5);
}

.admin-modal-close:hover[b-ffbqstm79g] {
    color: rgba(0, 0, 0, 0.9);
}

.admin-modal-body[b-ffbqstm79g] {
    padding: 20px;
}

.admin-form-field[b-ffbqstm79g] {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 14px;
}

.admin-form-field label[b-ffbqstm79g] {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

.admin-form-field input[type="text"][b-ffbqstm79g],
.admin-form-field select[b-ffbqstm79g] {
    padding: 8px 10px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    font-size: 14px;
    font-family: inherit;
}

.admin-modal-footer[b-ffbqstm79g] {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 8px;
}

.admin-modal-cancel-btn[b-ffbqstm79g],
.admin-modal-save-btn[b-ffbqstm79g],
.admin-modal-danger-btn[b-ffbqstm79g] {
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
}

.admin-modal-cancel-btn:hover[b-ffbqstm79g] {
    background: rgba(0, 0, 0, 0.04);
}

.admin-modal-save-btn[b-ffbqstm79g] {
    border-color: #4f8cff;
    background: #4f8cff;
    color: #fff;
}

.admin-modal-save-btn:hover[b-ffbqstm79g] {
    background: #3d7ae6;
}

.admin-modal-danger-btn[b-ffbqstm79g] {
    border-color: #c62828;
    background: #c62828;
    color: #fff;
}

.admin-modal-danger-btn:hover[b-ffbqstm79g] {
    background: #a91f1f;
}

/* Same card treatment as MainLayout's whole-app "not-a-member" screen and
   ChoirMusicViewPage's per-choir "access denied" screen — this is the same
   kind of message, just scoped to the Admin page. */
.choir-access-denied[b-ffbqstm79g] {
    max-width: 480px;
    background: rgba(0, 0, 0, 0.03);
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 16px;
    padding: 32px 28px;
}

.choir-access-denied h4[b-ffbqstm79g] {
    margin: 0 0 8px;
    font-size: 18px;
}

.choir-access-denied p[b-ffbqstm79g] {
    margin: 0;
    color: rgba(0, 0, 0, 0.6);
    font-size: 14px;
    line-height: 1.5;
}
/* _content/ChoirMusic/Components/Pages/AdminChoirManagePage.razor.rz.scp.css */
.admin-choir-manage-page[b-g3vy6n1m7h] {
    max-width: none;
}

.admin-choir-manage-header[b-g3vy6n1m7h] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 16px;
}

.admin-choir-manage-header h3[b-g3vy6n1m7h] {
    margin: 0;
}

/* Same card treatment as MainLayout's whole-app "not-a-member" screen and
   ChoirMusicViewPage's per-choir "access denied" screen. */
.choir-access-denied[b-g3vy6n1m7h] {
    max-width: 480px;
    background: rgba(0, 0, 0, 0.03);
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 16px;
    padding: 32px 28px;
}

.choir-access-denied h4[b-g3vy6n1m7h] {
    margin: 0 0 8px;
    font-size: 18px;
}

.choir-access-denied p[b-g3vy6n1m7h] {
    margin: 0;
    color: rgba(0, 0, 0, 0.6);
    font-size: 14px;
    line-height: 1.5;
}

/* ---------- Choir icon upload ---------- */

.choir-icon-panel[b-g3vy6n1m7h] {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: 16px;
}

.choir-icon-preview[b-g3vy6n1m7h] {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: block;
    object-fit: cover;
    flex: none;
}

.choir-icon-upload[b-g3vy6n1m7h] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
}

.choir-icon-buttons[b-g3vy6n1m7h] {
    display: flex;
    align-items: center;
    gap: 8px;
}

/* The visible control is this <button>, styled the same as
   .admin-grid-add-btn below (see the markup's combined class list) so it
   reads as an ordinary button. The actual <InputFile> it opens is hidden
   via an inline style in the markup (not a CSS class here), same shape as
   NetworkNodesApp's VillageManagerModal "Change Logo" control -- see
   TriggerIconUpload in the code block below. */
.choir-icon-upload-btn[b-g3vy6n1m7h] {
    display: inline-block;
}

/* ---------- Music Library / Music Events grids ---------- */

.admin-grid-panel[b-g3vy6n1m7h] {
    max-width: 900px;
    margin-top: 32px;
}

/* Grey pill treatment matching MainLayout's top "sign off" bar
   (.logout-bar) -- same rounded-rectangle shape and horizontal padding, but
   grey rather than that bar's brand blue, since this is a section header
   rather than a primary action bar. */
.admin-grid-toolbar[b-g3vy6n1m7h] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    background: rgba(0, 0, 0, 0.06);
    border-radius: 12px;
    padding: 10px 16px;
}

.admin-grid-toolbar h4[b-g3vy6n1m7h] {
    margin: 0;
}

.admin-grid-add-btn[b-g3vy6n1m7h] {
    padding: 6px 14px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.admin-grid-add-btn:hover[b-g3vy6n1m7h] {
    background: rgba(0, 0, 0, 0.04);
}

.admin-grid-empty[b-g3vy6n1m7h] {
    color: rgba(0, 0, 0, 0.5);
}

/* Caps each grid at 40% of the viewport's height and scrolls its own rows
   independently, rather than letting the table grow with however many
   entries a choir happens to have -- otherwise a long Music Library could
   push the Music Events panel far down the page, or off it entirely.
   overflow-x: auto is a separate, second fix: the Music Library grid
   (Title/Composer/actions) and Music Events grid (Title/Date/Location/
   actions) below both have real column widths to show, and neither had
   any floor on them or any horizontal scroll of their own -- on a phone
   that either squeezed every column down to an unreadable, wrapped mess,
   or forced this whole page wider than the viewport, exactly the same
   "have to pinch-zoom out to see it" bug already fixed on the choir list
   tables elsewhere in this app (see ChoirMusicPage.razor.css's own
   .choir-table-scroll for the identical reasoning). Scoping the scroll to
   this box, with .admin-grid-table's own min-width below giving it
   something to scroll AT, keeps the rest of the page at a normal scale. */
.admin-grid-scroll[b-g3vy6n1m7h] {
    max-height: 40vh;
    overflow-y: auto;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Search box shown above a grid once it has entries -- see
   FilteredLibraries()/FilteredEvents() in AdminChoirManagePage.razor. */
.admin-grid-search[b-g3vy6n1m7h] {
    margin-top: 12px;
}

.admin-grid-search-input[b-g3vy6n1m7h] {
    width: 100%;
    max-width: 320px;
    padding: 8px 10px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    font-size: 16px;
    font-family: inherit;
}

.admin-grid-table[b-g3vy6n1m7h] {
    width: 100%;
    /* Shared by both the 2-data-column Music Library grid (Title/Composer)
       and the 3-data-column Music Events grid (Title/Date/Location) --
       see .admin-grid-scroll's own comment just above for why this floor
       exists at all. Generous enough that even the denser Music Events
       grid gets a reasonable width per column rather than being squeezed
       to fit whatever space happens to be available. */
    min-width: 480px;
    margin-top: 12px;
    border-collapse: collapse;
}

.admin-grid-table thead th[b-g3vy6n1m7h] {
    text-align: left;
    padding: 0 8px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

/* Keeps the header row visible while a capped grid's body scrolls, so the
   column labels don't disappear the moment you scroll past row one. #fff
   matches this page's other card/panel backgrounds (e.g. the modals below)
   so scrolled-past rows don't show through the header. */
.admin-grid-scroll .admin-grid-table thead th[b-g3vy6n1m7h] {
    position: sticky;
    top: 0;
    background: #fff;
    z-index: 1;
}

.admin-grid-table td[b-g3vy6n1m7h] {
    padding: 10px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    font-size: 14px;
}

.admin-grid-actions-col[b-g3vy6n1m7h] {
    width: 140px;
    white-space: nowrap;
    text-align: right;
}

.admin-grid-row-btn[b-g3vy6n1m7h] {
    padding: 4px 10px;
    margin-left: 6px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 12px;
    cursor: pointer;
}

.admin-grid-row-btn:hover[b-g3vy6n1m7h] {
    background: rgba(0, 0, 0, 0.04);
}

.admin-grid-row-btn-danger[b-g3vy6n1m7h] {
    border-color: rgba(229, 0, 0, 0.35);
    color: #c62828;
}

.admin-grid-row-btn-danger:hover[b-g3vy6n1m7h] {
    background: rgba(229, 0, 0, 0.06);
}

.admin-grid-error[b-g3vy6n1m7h] {
    margin-top: 12px;
    color: #c62828;
    font-size: 13px;
}

/* ---------- Music Library / Music Events: mobile card view ----------
   Same "two mutually-exclusive layouts, swapped purely by media query"
   pattern as ChoirMusicPage's .choir-events-picker-desktop/-mobile and
   SongLibraryPage's .song-library-desktop/-mobile. .admin-grid-scroll's
   own overflow-x already keeps .admin-grid-table's 480px floor from
   resizing the page, but that still means panning sideways to read every
   row on a phone -- below this stacked one-card-per-entry layout takes
   over instead, styled after ChoirMusicPage's own "non-singing"
   event-modal-section cards, so every field (and the Edit/Delete actions)
   reads top to bottom with no horizontal scrolling at all. Shared by both
   the Music Library grid (Title/Composer) and the Music Events grid
   (Title/Date/Location) -- see the markup's own comment on each. */
.admin-grid-desktop[b-g3vy6n1m7h] {
    display: block;
}

.admin-grid-mobile[b-g3vy6n1m7h] {
    display: none;
    flex-direction: column;
    gap: 10px;
    margin-top: 12px;
}

.admin-grid-card[b-g3vy6n1m7h] {
    padding: 12px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.02);
    font-size: 14px;
}

.admin-grid-card-title[b-g3vy6n1m7h] {
    font-size: 14px;
    font-weight: 600;
}

.admin-grid-card-line[b-g3vy6n1m7h] {
    margin: 6px 0 0;
    font-size: 13px;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: rgba(0, 0, 0, 0.75);
}

.admin-grid-card-actions[b-g3vy6n1m7h] {
    display: flex;
    justify-content: flex-end;
    gap: 6px;
    margin-top: 10px;
}

/* Buttons already carry their own left margin for the desktop table's
   tightly packed action column -- not wanted here, where .admin-grid-card-
   actions' own gap already spaces them. */
.admin-grid-card-actions .admin-grid-row-btn[b-g3vy6n1m7h] {
    margin-left: 0;
}

@media (max-width: 640px) {
    .admin-grid-desktop[b-g3vy6n1m7h] {
        display: none;
    }

    .admin-grid-mobile[b-g3vy6n1m7h] {
        display: flex;
    }
}

/* ---------- Add/edit and delete-confirm modals ----------
   Same overlay/panel treatment as MainLayout's existing help-modal-overlay
   / help-modal-panel, scoped to this component instead of shared, matching
   this app's existing convention of duplicating the "modal card" look per
   component rather than a shared global class (see ChoirMusicViewPage's
   choir-access-denied for the same convention applied to that card). */

.admin-modal-overlay[b-g3vy6n1m7h] {
    position: fixed;
    inset: 0;
    /* Was 1000 -- tied with MainLayout's .logout-bar (also position:
       fixed, also z-index: 1000). See ChoirMusicPage.razor.css's
       .event-modal-overlay for the full reasoning: every other full-screen
       overlay in this app already sits clearly above the top bar's 1000,
       and this one is bumped to match that same convention rather than
       relying on paint order to keep it above the bar while open. */
    z-index: 1010;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.35);
    padding: 0;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}

/* Full-screen by design -- every modal in this app, including small
   confirmation popups (see .admin-modal-panel-small below), occupies the
   entire viewport once opened, rather than floating as a smaller centered
   card. */
.admin-modal-panel[b-g3vy6n1m7h] {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    overflow-y: auto;
    background: #fff;
    border-radius: 0;
    box-shadow: none;
}

.admin-modal-panel-small[b-g3vy6n1m7h] {
    max-width: none;
}

.admin-modal-header[b-g3vy6n1m7h] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 16px 20px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
}

.admin-modal-close[b-g3vy6n1m7h] {
    border: none;
    background: none;
    font-size: 14px;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.5);
}

.admin-modal-close:hover[b-g3vy6n1m7h] {
    color: rgba(0, 0, 0, 0.9);
}

.admin-modal-body[b-g3vy6n1m7h] {
    padding: 20px;
}

/* Modifier classes -- added ONLY on the Edit/Add Music Event modal's own
   panel/body/form elements (alongside their existing .admin-modal-panel/
   .admin-modal-body classes), not changes to those shared base rules
   themselves -- so the Music Library modal and both delete-confirmation
   modals, which reuse those same base classes but have no similarly tall
   grid to expand, are completely unaffected.

   Turns the whole modal into a flex column: the header keeps its natural
   height, this form becomes a flex column filling whatever's left below
   it, every field in it (Title/Date/Location/Notes) keeps its own natural
   height same as before, and .event-sections-field is the one child set
   to flex: 1 1 auto below -- so IT absorbs whatever vertical space is
   actually left in the browser window instead of a fixed guess at how
   much room "should" be enough, and its own .event-sections-scroll (also
   flex: 1 1 auto) is what actually scrolls once the program has more
   sections than fit in that space. .admin-modal-panel's own overflow-y:
   auto and this rule's own overflow-y: auto on the body are both left in
   place as fallbacks -- normally neither ever needs to trigger, since the
   flex layout already sizes everything to fit, but a viewport too short
   even for the fixed fields above the grid (an unusual case, but not an
   impossible one) still gets a working scrollbar instead of clipped,
   unreachable content. */
.admin-modal-panel-flex[b-g3vy6n1m7h] {
    display: flex;
    flex-direction: column;
}

.admin-modal-body-flex[b-g3vy6n1m7h] {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.event-modal-form-flex[b-g3vy6n1m7h] {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.admin-form-field[b-g3vy6n1m7h] {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 14px;
}

.admin-form-field label[b-g3vy6n1m7h] {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

.admin-form-field input[type="text"][b-g3vy6n1m7h],
.admin-form-field input[type="date"][b-g3vy6n1m7h],
.admin-form-field textarea[b-g3vy6n1m7h] {
    padding: 8px 10px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    font-size: 14px;
    font-family: inherit;
}

/* Anchors the Music Event modal's Title/Location suggestions dropdown (see
   those fields' own markup comment) to this field wrapper, same reasoning
   as .event-sections-title-cell above for the per-row section Title
   column -- so the list drops down directly under the field regardless of
   where else it sits in the modal's own scroll. */
.admin-form-field-suggest[b-g3vy6n1m7h] {
    position: relative;
}

.admin-form-grid[b-g3vy6n1m7h] {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 0 16px;
}

/* Two fields side by side already shrinks to fit (minmax(0, 1fr) never
   forces the modal wider than the viewport), but on a phone that meant two
   genuinely cramped, half-width fields instead of the comfortable
   full-width ones every other field in this form already gets. Stacking
   to a single column below this width is a readability fix, not an
   overflow one -- matches the 640px breakpoint used throughout this app. */
@media (max-width: 640px) {
    .admin-form-grid[b-g3vy6n1m7h] {
        grid-template-columns: 1fr;
    }
}

.admin-modal-footer[b-g3vy6n1m7h] {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    margin-top: 8px;
}

.admin-modal-cancel-btn[b-g3vy6n1m7h],
.admin-modal-save-btn[b-g3vy6n1m7h],
.admin-modal-danger-btn[b-g3vy6n1m7h] {
    padding: 8px 16px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
}

.admin-modal-cancel-btn:hover[b-g3vy6n1m7h] {
    background: rgba(0, 0, 0, 0.04);
}

.admin-modal-save-btn[b-g3vy6n1m7h] {
    border-color: #4f8cff;
    background: #4f8cff;
    color: #fff;
}

.admin-modal-save-btn:hover[b-g3vy6n1m7h] {
    background: #3d7ae6;
}

.admin-modal-danger-btn[b-g3vy6n1m7h] {
    border-color: #c62828;
    background: #c62828;
    color: #fff;
}

.admin-modal-danger-btn:hover[b-g3vy6n1m7h] {
    background: #a91f1f;
}

/* ---------- Music Event modal: Music Event Sections (program) grid ---------- */

/* flex: 1 1 auto -- alongside .event-modal-form-flex above, this is the
   one child of that flex column that actually grows to fill whatever
   vertical space is left below Title/Date/Location/Notes; min-height: 0
   overrides flexbox's own default (a flex item won't normally shrink
   below its content's natural size) so this can still shrink down to
   whatever's actually available rather than forcing the page taller.
   display: flex column of its own so its two children -- the header row
   (fixed height) and .event-sections-scroll (flex: 1 1 auto again) -- can
   split THIS element's own now-variable height the same way. */
.event-sections-field[b-g3vy6n1m7h] {
    margin-top: 4px;
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.event-sections-header[b-g3vy6n1m7h] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 8px;
}

.event-sections-header label[b-g3vy6n1m7h] {
    margin: 0;
}

/* Grows to fill whatever vertical space the flex chain above
   (.event-modal-form-flex -> .event-sections-field) actually has left in
   the browser window, rather than a fixed height guessed in advance --
   see .event-modal-form-flex's own comment for the full chain. flex: 1 1
   auto plus min-height: 0 is what lets this shrink/grow with the rest of
   that chain instead of just sitting at its own content's natural size;
   overflow-y: auto is still what actually scrolls once a program has more
   sections than fit in whatever space that ends up being, so the
   Save/Cancel footer below it stays reachable without pushing the whole
   modal taller than the viewport, same intent this rule always had, just
   no longer capped at one fixed number regardless of how much room the
   window actually has. */
.event-sections-scroll[b-g3vy6n1m7h] {
    flex: 1 1 auto;
    min-height: 120px;
    overflow-y: auto;
    /* Safety net for narrow phones -- .event-sections-table's own min-width
       below means Seq/Title/Song/Remove no longer get squeezed down to an
       unreadable, near-unusable width on a small screen; this is what lets
       that min-width actually be reached (via a sideways scroll/swipe
       inside this box) instead of being clipped or forcing the whole modal
       wider. */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
}

/* table-layout: fixed, not the default auto -- under auto layout, a column
   with modest-length content (Song's dropdown showing e.g. "Take Up Your
   Cross") only ever gets as wide as that content needs, and any left-over
   width toward the table's own 100% doesn't reliably get redistributed to
   it (browsers disagree on exactly how auto layout divides up slack space)
   -- which is what left Song narrow with a dead gap before Remove. Fixed
   layout instead takes its column widths from THIS row's own cells: Seq and
   the actions column keep their explicit pixel widths (event-sections-seq-
   col/event-sections-actions-col below), and Title/Song, left with no
   explicit width of their own, split whatever's left over EQUALLY and
   predictably -- which is also why Song's <select> (already width: 100% of
   its cell) now actually reaches all the way to Remove. */
.event-sections-table[b-g3vy6n1m7h] {
    width: 100%;
    /* table-layout: fixed (see this rule's own comment above) means Title
       and Song split whatever space is left after Seq/actions equally and
       predictably -- but on a narrow phone that "whatever's left" used to
       shrink down to a near-unusable ~40-50px per column. min-width gives
       fixed layout an actual floor to divide up instead, with
       .event-sections-scroll's own overflow-x: auto (added alongside this)
       providing the horizontal scroll needed to reach it below that width. */
    min-width: 420px;
    table-layout: fixed;
    border-collapse: collapse;
}

/* .admin-grid-actions-col's shared 140px width was sized for the Music
   Library/Music Events grids above, which each carry TWO buttons (Edit +
   Delete) side by side in that column. This table's actions column only
   ever holds the single Remove button, so reusing that 140px left ~60px of
   permanently blank cell space between Song's dropdown and the button
   itself -- exactly the "dead gap before Remove" this table kept getting
   reported for, even after table-layout: fixed made Song's own column
   width correct. Overriding just the width here (still right-aligned,
   still nowrap, from the shared class) tightens the column to what one
   button actually needs, so the freed width goes to Title/Song instead. */
.event-sections-table .admin-grid-actions-col[b-g3vy6n1m7h] {
    width: 90px;
}

.event-sections-table thead th[b-g3vy6n1m7h] {
    position: sticky;
    top: 0;
    background: #fff;
    z-index: 1;
    text-align: left;
    padding: 6px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

/* Top-aligned, not middle -- the Title cell is taller than its neighbors
   now that Notes stacks underneath the title input (see that cell's own
   markup comment), and centering every cell against that taller combined
   height left Seq's number box and Song's dropdown floating in the gap
   between the title and notes lines instead of level with the title itself.
   Top-aligning puts Seq/Title/Song back on one visual line, with Notes and
   Remove simply following below/beside wherever they land. */
.event-sections-table td[b-g3vy6n1m7h] {
    padding: 6px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    vertical-align: top;
}

/* The data row (Seq/Title/Song/Remove) no longer carries its own bottom
   border -- see .event-sections-notes-row below, which does instead -- so
   a section's two rows read as one visually grouped block, with a single
   divider after Notes rather than a second, redundant line between the
   data row and its own Notes row. */
.event-sections-row td[b-g3vy6n1m7h] {
    border-bottom: none;
}

/* Tightens the gap to the data row directly above -- that row's own
   bottom padding already provides some space, so this row doesn't need
   its own full top padding on top of that as well. */
.event-sections-notes-row td[b-g3vy6n1m7h] {
    padding-top: 0;
}

.event-sections-seq-col[b-g3vy6n1m7h] {
    width: 64px;
}

.event-sections-seq-input[b-g3vy6n1m7h] {
    width: 56px;
}

/* box-sizing: border-box is the fix for the Notes row overflowing past
   Remove and forcing the whole grid to scroll horizontally -- without it,
   "width: 100%" below sizes only the CONTENT box, so this rule's own
   padding (6px + 6px) and border (1px + 1px) -- 16px total -- get added ON
   TOP of that 100%, and every one of these inputs quietly overflows its
   own cell by 16px. That's invisible for Seq/Title/Song, which just bleed
   into their neighboring column's padding, but the Notes row's input spans
   the table's full width (colspan="4"), so ITS 16px of overflow lands
   past the table's own right edge -- exactly where Remove sits -- which is
   what was pushing it wider than Remove and triggering the scrollbar
   (.event-sections-scroll's overflow-y: auto forces overflow-x: auto too,
   per the CSS spec's mixed-overflow rule, so any horizontal overflow here
   shows up as a scrollbar rather than just being clipped). border-box
   makes "width: 100%" include the padding/border instead, so every one of
   these inputs -- Notes included -- now actually ends exactly at its
   cell's edge.
*/
.event-sections-seq-input[b-g3vy6n1m7h],
.event-sections-text-input[b-g3vy6n1m7h],
.event-sections-select[b-g3vy6n1m7h] {
    padding: 6px 8px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    font-size: 16px;
    font-family: inherit;
    width: 100%;
    box-sizing: border-box;
}

/* Anchors the custom Title suggestions dropdown (see the row's own markup
   comment on why this replaced a native <input list="..."> + <datalist>)
   to this cell rather than the whole table, so it drops down directly
   under that row's own input regardless of scroll position within
   .event-sections-scroll. */
.event-sections-title-cell[b-g3vy6n1m7h] {
    position: relative;
}

/* Its own full-width row now (see the markup's own comment on why), not
   stacked under the title input within the Title cell anymore -- kept
   visually secondary (smaller, muted text) all the same, since it's still
   a note ON the section rather than a peer field of equal weight to
   Title/Song. */
.event-sections-notes-input[b-g3vy6n1m7h] {
    display: block;
    font-size: 16px;
    color: rgba(0, 0, 0, 0.6);
}

/* ---------- Program (Music Event Sections): mobile stacked layout ----------
   Below ~640px, Seq/Title/Song/Remove side by side (even with
   .event-sections-scroll's own horizontal scroll) meant reading a single
   section required scrolling sideways to see Song, then again to reach
   Remove -- exactly the "has a scrollbar, right side outside view" pattern
   already fixed elsewhere in this app (see ChoirMusicPage.razor.css's own
   .choir-table-expand-cell), but this table's problem isn't overflow past
   its own box -- .event-sections-scroll already contains that correctly --
   it's that a data table simply isn't a usable shape on a phone once it
   has more columns than comfortably fit. Rather than duplicating every
   input in a second, parallel "mobile card" markup block (this app's usual
   pattern elsewhere -- see SongLibraryPage's own song-library-desktop/
   -mobile split), this table's rows/cells switch to display: block
   instead, turning each <tr> into its own stacked block and each <td> into
   a full-width one beneath it -- the exact same <input>/<select> elements
   stay bound to the same section.Xxx properties either way, so there's
   only ever one copy of this row's state, never two copies that could
   drift out of sync. .event-sections-table thead is hidden since column
   headers don't mean anything once cells stack -- each td's own
   data-label attribute (see the markup) prints an inline label via ::before
   instead, so Title/Song/Notes are each still labeled without needing a
   second render of this row just for mobile. */
@media (max-width: 640px) {
    /* No longer wide enough to need a fixed 420px floor to divide up
       between columns -- there's only one column now. Removing this (and
       .event-sections-scroll's own overflow-x, effectively a no-op once
       nothing here is wider than the modal) is what stops this table from
       still forcing a sideways scroll purely to reach its own former
       min-width, even after every column has already stacked. */
    .event-sections-table[b-g3vy6n1m7h] {
        min-width: 0;
        width: 100%;
    }

    .event-sections-table[b-g3vy6n1m7h],
    .event-sections-table tbody[b-g3vy6n1m7h],
    .event-sections-row[b-g3vy6n1m7h],
    .event-sections-notes-row[b-g3vy6n1m7h],
    .event-sections-table td[b-g3vy6n1m7h] {
        display: block;
        width: 100%;
    }

    .event-sections-table thead[b-g3vy6n1m7h] {
        display: none;
    }

    /* The data row becomes its own card -- top corners rounded, no bottom
       border -- with .event-sections-notes-row directly below closing it
       off (bottom corners, no top border) so the two <tr>s still read as
       one visually grouped section, the same pairing .event-sections-row
       td's own base rule (border-bottom: none) already keeps on desktop. */
    .event-sections-row[b-g3vy6n1m7h] {
        margin-top: 10px;
        padding: 10px 10px 6px;
        border: 1px solid rgba(0, 0, 0, 0.08);
        border-bottom: none;
        border-radius: 8px 8px 0 0;
        background: rgba(0, 0, 0, 0.02);
    }

    .event-sections-row:first-child[b-g3vy6n1m7h] {
        margin-top: 0;
    }

    .event-sections-notes-row[b-g3vy6n1m7h] {
        padding: 0 10px 10px;
        border: 1px solid rgba(0, 0, 0, 0.08);
        border-top: none;
        border-radius: 0 0 8px 8px;
        background: rgba(0, 0, 0, 0.02);
    }

    .event-sections-table td[b-g3vy6n1m7h] {
        padding: 6px 0;
        border-bottom: none;
    }

    /* Prints "Seq"/"Title"/"Song"/"Notes" above each stacked field, reading
       the exact text straight from the markup's own data-label attribute
       rather than needing four separate CSS rules with hardcoded content
       strings -- one for whichever td actually carries the attribute (the
       Remove button's own actions cell deliberately doesn't, since a
       button needs no such label). */
    .event-sections-table td[data-label][b-g3vy6n1m7h]::before {
        content: attr(data-label);
        display: block;
        font-size: 11px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: 0.02em;
        color: rgba(0, 0, 0, 0.5);
        margin-bottom: 3px;
    }

    /* Both had fixed pixel widths sized for sitting beside Title/Song in a
       table row (see their own base rules above) -- meaningless, and
       actively wrong, once their td is a full-width block instead. Seq's
       own number input stays deliberately narrow (it's a 1-2 digit number,
       not a paragraph), unlike every other stacked field here. */
    .event-sections-seq-col[b-g3vy6n1m7h] {
        width: 100%;
    }

    .event-sections-seq-input[b-g3vy6n1m7h] {
        width: 70px;
    }

    .event-sections-table .admin-grid-actions-col[b-g3vy6n1m7h] {
        width: 100%;
        text-align: right;
        padding-top: 8px;
    }
}

.event-section-suggestions[b-g3vy6n1m7h] {
    position: absolute;
    z-index: 20;
    top: 100%;
    left: 0;
    right: 0;
    margin: 2px 0 0;
    padding: 4px 0;
    list-style: none;
    max-height: 180px;
    overflow-y: auto;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}

.event-section-suggestions li[b-g3vy6n1m7h] {
    padding: 6px 10px;
    font-size: 13px;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.event-section-suggestions li:hover[b-g3vy6n1m7h] {
    background: rgba(79, 140, 255, 0.12);
}

/* ---------- PDF drop zone (Music Library modal's PdfFile field) ---------- */

.pdf-drop-zone[b-g3vy6n1m7h] {
    position: relative;
    border: 1.5px dashed rgba(0, 0, 0, 0.25);
    border-radius: 8px;
    padding: 14px 12px;
    text-align: center;
    background: rgba(0, 0, 0, 0.02);
    transition: border-color 0.15s ease, background 0.15s ease;
}

/* Toggled by drag-drop-upload.js's wirePdfDropZone while a file is
   dragged over this zone. */
.pdf-drop-zone.drop-target-active[b-g3vy6n1m7h] {
    border-color: #4f8cff;
    background: rgba(79, 140, 255, 0.06);
}

/* The real <input type="file"> InputFile renders, stretched invisibly
   over the whole zone so clicking anywhere opens the file picker — the
   dashed box itself is just a label; drag/drop is handled separately by
   the JS listeners wired to this zone's own id, not by this input. */
.pdf-drop-input[b-g3vy6n1m7h] {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    cursor: pointer;
}

.pdf-drop-zone-content[b-g3vy6n1m7h] {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    font-size: 13px;
    /* Lets clicks fall through to the file input underneath, except for
       the Clear button, which opts back in below. */
    pointer-events: none;
}

.pdf-drop-placeholder[b-g3vy6n1m7h] {
    color: rgba(0, 0, 0, 0.5);
}

.pdf-drop-filename[b-g3vy6n1m7h] {
    font-weight: 600;
    word-break: break-all;
}

.pdf-clear-btn[b-g3vy6n1m7h] {
    pointer-events: auto;
    padding: 3px 10px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 12px;
    cursor: pointer;
}

.pdf-clear-btn:hover[b-g3vy6n1m7h] {
    background: rgba(0, 0, 0, 0.04);
}
/* _content/ChoirMusic/Components/Pages/ChoirMusicPage.razor.rz.scp.css */
.choir-music-list-page[b-h5qftfbcbx] {
    max-width: 760px;
}

/* PDF annotation save/load failure banner -- see ChoirMusicPage.razor's own
   _pdfViewerAnnotationWarning comment for why this exists. Amber, not the
   app's error red (see offline/event.html's .error), since this is a
   "retry when you get a chance" notice, not a hard failure blocking
   anything -- the PDF itself still opened/closed fine either way. */
.pdf-annotation-warning[b-h5qftfbcbx] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 12px 0 0;
    padding: 10px 14px;
    border-radius: 8px;
    background: rgba(255, 179, 0, 0.14);
    border: 1px solid rgba(158, 106, 0, 0.35);
    color: #7a5200;
    font-size: 13px;
}

.pdf-annotation-warning button[b-h5qftfbcbx] {
    flex: none;
    border: none;
    background: none;
    color: inherit;
    font-weight: 700;
    cursor: pointer;
    padding: 0 2px;
}

/* Scoped, independently-scrollable wrapper -- same reasoning as
   SongLibraryPage's own .song-library-scroll: without this, a long choir
   name (or, on a narrow phone, even the plain icon+name+membership
   combination at its normal desktop widths) used to force the ENTIRE page
   wider than the viewport instead of just this table, which is what
   showed up as "everything on Choir Music is too big, I have to
   pinch-zoom out just to see it." Scoping the scroll to this box instead
   keeps the rest of the page at a normal, unshrunk scale, with only this
   table's own overflow reachable by a sideways scroll/swipe inside its own
   box. The @media rule further down already shrinks the fixed-width
   columns enough that a typical phone never actually needs that scroll --
   this is the fallback for a longer name or a narrower device, not the
   primary fix. */
.choir-table-scroll[b-h5qftfbcbx] {
    margin-top: 24px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.choir-table[b-h5qftfbcbx] {
    width: 100%;
    /* Keeps the three columns from being squeezed down to unreadable
       widths inside the scroll wrapper above -- without a floor here, the
       browser was free to shrink every column (icon/name/membership alike)
       to fit whatever width was available, which is what produced
       single-word-wrapped, hard-to-read rows instead of a clean sideways
       scroll. */
    min-width: 420px;
    border-collapse: collapse;
    /* table-layout: fixed -- without it, the browser's default "auto"
       table layout picks column widths by looking at the CONTENT of every
       row, including the expanded choir's own row (colspan="3", holding
       the Music Event picker or Song Library grid). That expanded content
       is already wrapped in its own overflow-x: auto box, but an "auto"
       box's INTRINSIC content width still feeds into the outer table's
       column-width math unless the table ignores it outright -- which is
       exactly what was pushing this table (and so this scroll wrapper,
       and on some content, the page itself) wider the moment a choir was
       expanded, instead of staying at a fixed width with only the
       expanded content scrolling sideways inside its own box. Fixed
       layout sizes columns ONLY from the header row's own widths (see
       .choir-table-icon-col/.choir-table-membership-col above, with the
       name column taking whatever's left) and never revisits that once a
       row expands, so expanding a choir can no longer resize this table,
       this page, or the columns already on screen. */
    table-layout: fixed;
}

/* table-layout: fixed above locks this table's own column widths, but it
   does NOT clip content -- a table cell never clips its children just
   because they're wider than the cell's own (now-fixed) box, the same way
   .song-library-table's/the Music Event picker's own dedicated overflow-x:
   auto wrappers already have to handle their OWN wide content instead of
   relying on the cell to contain it for them. This colspan="3" cell is
   that same gap for anything rendered directly inside it that ISN'T
   already in one of those dedicated wrappers -- the search box above the
   grid, and the stacked mobile card list on a narrow phone, chief among
   them (neither has, or needs, its own horizontal scroll the way the wide
   desktop grid does) -- so without this, either one growing even slightly
   wider than this fixed-width cell (a long unbroken word, a rounding
   difference in a percentage width) spills out to the right of the whole
   table, and so the whole page, exactly like the bug table-layout: fixed
   above already fixed for the table's own column sizing. overflow-x:
   hidden here is the same backstop app.css's html/body rule already
   applies at the page level -- clip rather than let it drag anything
   sideways -- just scoped to this one cell instead. */
.choir-table-expand-cell[b-h5qftfbcbx] {
    overflow-x: hidden;
}

.choir-table thead th[b-h5qftfbcbx] {
    text-align: left;
    padding: 0 8px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.choir-table-icon-col[b-h5qftfbcbx] {
    width: 60px;
}

/* Mobile: shrink the icon, membership badge, and cell padding so a typical
   phone fits all three columns without needing .choir-table-scroll's
   horizontal scroll at all -- that scroll is still there as a fallback
   (see .choir-table-scroll's own comment) for a longer choir name or a
   narrower device, but shouldn't be the normal experience for the common
   case. Matches ChoirMusicPage.razor.css's own icon-button breakpoint
   convention (also 640px) used elsewhere in this app, e.g.
   MainLayout.razor.css's .logout-bar buttons. */
@media (max-width: 640px) {
    /* Was min-width: 340px -- meant as a floor so table-layout: fixed's
       three columns (icon/name/membership) never got squeezed down to an
       unreadable width, but 340px does not actually fit inside a typical
       375px phone once .stub-page's own 24px+24px padding (see app.css)
       is subtracted -- only 327px is ever actually available there, so
       that "floor" was 13px wider than the room it had to stand on, and
       .choir-table-scroll's overflow-x: auto (a fallback for a genuinely
       long name or an unusually narrow device -- see that rule's own
       comment) ended up triggering on every ordinary phone instead,
       exactly the "still has a scrollbar" report this was supposed to
       prevent. table-layout: fixed already protects the icon and
       membership columns from shrinking below their own explicit widths
       below regardless of the table's overall width -- min-width: 0 here
       (overriding the base rule's 420px, meant for desktop) just lets the
       table actually settle at 100% (its own base rule) of whatever
       .choir-table-scroll has to offer instead of forcing a floor wider
       than that on every phone. */
    .choir-table[b-h5qftfbcbx] {
        min-width: 0;
    }

    /* Widened from 40px -- flush against .choir-table-name-col's own left
       padding at that width, which read as the icon crowding/touching the
       choir name text rather than sitting a comfortable distance from it. */
    .choir-table-icon-col[b-h5qftfbcbx] {
        width: 48px;
    }

    .choir-icon[b-h5qftfbcbx] {
        width: 32px;
        height: 32px;
    }

    .choir-table-membership-col[b-h5qftfbcbx] {
        width: 84px;
        /* Overrides the desktop nowrap below -- "Not a member" has no room
           to stay on one line at this width, and letting it wrap to two
           short lines reads better than forcing the column (and so the
           whole table) wider just to keep it flat. */
        white-space: normal;
    }

    .membership-badge[b-h5qftfbcbx] {
        padding: 3px 6px;
        font-size: 11px;
        white-space: normal;
        text-align: center;
    }

    .choir-row td[b-h5qftfbcbx],
    .choir-table thead th[b-h5qftfbcbx] {
        padding: 8px 4px;
    }
}

.choir-table-sort-btn[b-h5qftfbcbx] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: none;
    padding: 0;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
    cursor: pointer;
}

.choir-table-sort-btn:hover[b-h5qftfbcbx],
.choir-table-sort-btn:focus-visible[b-h5qftfbcbx] {
    color: rgba(0, 0, 0, 0.9);
}

.choir-table-sort-indicator[b-h5qftfbcbx] {
    font-size: 10px;
}

.choir-row[b-h5qftfbcbx] {
    cursor: pointer;
}

.choir-row:hover[b-h5qftfbcbx],
.choir-row:focus-visible[b-h5qftfbcbx] {
    background: rgba(0, 0, 0, 0.04);
}

.choir-row:focus-visible[b-h5qftfbcbx] {
    outline: 2px solid #4f8cff;
    outline-offset: -2px;
}

.choir-row td[b-h5qftfbcbx] {
    padding: 10px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

/* A choir the signed-in person isn't a member of — still shown in the
   list (so they know it exists) but not clickable: no pointer cursor, no
   hover highlight, and the name/icon are dimmed to read as unavailable
   rather than merely unstyled. */
.choir-row-locked[b-h5qftfbcbx] {
    cursor: default;
}

.choir-row-locked:hover[b-h5qftfbcbx],
.choir-row-locked:focus-visible[b-h5qftfbcbx] {
    background: none;
}

.choir-row-locked .choir-icon[b-h5qftfbcbx] {
    opacity: 0.45;
}

.choir-row-locked .choir-table-name-col[b-h5qftfbcbx] {
    color: rgba(0, 0, 0, 0.45);
}

.choir-icon[b-h5qftfbcbx] {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: block;
    object-fit: cover;
}

.choir-table-name-col[b-h5qftfbcbx] {
    font-size: 15px;
    font-weight: 500;
}

.choir-table-membership-col[b-h5qftfbcbx] {
    width: 150px;
    white-space: nowrap;
}

.membership-badge[b-h5qftfbcbx] {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
}

.membership-badge-member[b-h5qftfbcbx] {
    background: rgba(38, 176, 80, 0.12);
    color: #1c7a3e;
}

.membership-badge-non-member[b-h5qftfbcbx] {
    background: rgba(0, 0, 0, 0.06);
    color: rgba(0, 0, 0, 0.5);
}

/* ---------- Music Events dropdown (expanded row) ---------- */

.choir-events-row td[b-h5qftfbcbx] {
    padding: 4px 8px 16px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.choir-events-loading[b-h5qftfbcbx],
.choir-events-empty[b-h5qftfbcbx] {
    margin: 8px 0 0;
    color: rgba(0, 0, 0, 0.5);
    font-size: 13px;
}

.choir-events-picker[b-h5qftfbcbx] {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-top: 8px;
    max-width: 440px;
}

.choir-events-picker label[b-h5qftfbcbx] {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

.choir-events-picker-row[b-h5qftfbcbx] {
    display: flex;
    align-items: stretch;
    gap: 8px;
}

.choir-events-picker select[b-h5qftfbcbx] {
    flex: 1;
    min-width: 0;
    padding: 8px 10px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    font-size: 16px;
    font-family: inherit;
    background: #fff;
}

.choir-events-open-btn[b-h5qftfbcbx] {
    flex: none;
    padding: 8px 16px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.choir-events-open-btn:hover:not(:disabled)[b-h5qftfbcbx] {
    background: rgba(0, 0, 0, 0.04);
}

.choir-events-open-btn:disabled[b-h5qftfbcbx] {
    opacity: 0.5;
    cursor: default;
}

/* ---------- Music Events picker: mobile row list ----------
   Two mutually-exclusive layouts for the same picker, swapped purely by
   this @media rule (both stay in the DOM at all times, see the markup's
   own comment on .choir-events-picker-desktop/-mobile) -- the native
   <select> above for anything wider than a phone, and this tappable row
   list below for anything narrower, since a plain <option> can't put a
   date above a title, wrap a long title, or alternate row colors the way
   this list needs to. */
.choir-events-picker-mobile[b-h5qftfbcbx] {
    display: none;
    flex-direction: column;
    gap: 8px;
}

.choir-events-picker-list[b-h5qftfbcbx] {
    display: flex;
    flex-direction: column;
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-radius: 8px;
    overflow: hidden;
}

.choir-events-picker-item[b-h5qftfbcbx] {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    width: 100%;
    padding: 10px 12px;
    border: none;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    background: #fff;
    text-align: left;
    font: inherit;
    cursor: pointer;
}

.choir-events-picker-item:last-child[b-h5qftfbcbx] {
    border-bottom: none;
}

/* Zebra striping -- every other row gets a faint grey background so
   adjacent rows stay visually distinct once a long title wraps onto a
   second or third line and the rows are no longer a uniform height. */
.choir-events-picker-item-alt[b-h5qftfbcbx] {
    background: rgba(0, 0, 0, 0.035);
}

.choir-events-picker-item:hover[b-h5qftfbcbx],
.choir-events-picker-item:focus-visible[b-h5qftfbcbx] {
    background: rgba(79, 140, 255, 0.1);
}

.choir-events-picker-item-selected[b-h5qftfbcbx],
.choir-events-picker-item-selected.choir-events-picker-item-alt[b-h5qftfbcbx] {
    background: rgba(79, 140, 255, 0.18);
}

.choir-events-picker-item-date[b-h5qftfbcbx] {
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.55);
}

.choir-events-picker-item-title[b-h5qftfbcbx] {
    font-size: 14px;
    font-weight: 500;
    color: rgba(0, 0, 0, 0.9);
    /* The whole point of this row layout over the native <select> it
       replaces on mobile -- a long title wraps onto another line instead
       of being clipped or forcing the row (and so the page) wider. */
    overflow-wrap: anywhere;
    white-space: normal;
}

/* Its own full-width row rather than sharing one with the list above (the
   way the desktop Open button shares .choir-events-picker-row with the
   select) -- see the markup's own comment on why that sharing was what
   left the Open button squeezed down (or effectively pushed off-screen)
   on a narrow phone. */
.choir-events-open-btn-mobile[b-h5qftfbcbx] {
    width: 100%;
}

@media (max-width: 640px) {
    .choir-events-picker-desktop[b-h5qftfbcbx] {
        display: none;
    }

    .choir-events-picker-mobile[b-h5qftfbcbx] {
        display: flex;
    }
}

/* ---------- Music Event record modal ----------
   Same overlay/panel treatment as Admin.razor's modals, duplicated here per
   this app's established CSS-isolation convention (each component's scoped
   .razor.css only matches elements THAT component renders). */

.event-modal-overlay[b-h5qftfbcbx] {
    position: fixed;
    inset: 0;
    /* Was 1000 -- exactly tied with MainLayout's .logout-bar (also z-index:
       1000, also position: fixed). It happened to still paint on top,
       since it's later in DOM/paint order, but that made "this modal
       covers the top bar while open" an accident of markup order rather
       than a declared stacking rule -- fragile, and easy to invert by
       simply reordering markup later. Every other full-screen overlay in
       this app (offline modal 1500, its confirm dialog 1550, the PDF
       viewer 2000, MainLayout's own Help modal 2000) is already given an
       explicit z-index clearly above the top bar's 1000 for exactly this
       reason; this one is bumped to match that same convention. */
    z-index: 1010;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.35);
    padding: 0;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}

/* Full-screen by design -- every modal in this app occupies the entire
   viewport once opened, rather than floating as a smaller centered card. */
.event-modal-panel[b-h5qftfbcbx] {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    overflow-y: auto;
    background: #fff;
    border-radius: 0;
    box-shadow: none;
}

/* No border-bottom here (there used to be one) -- with nothing but blank
   padding between it and .event-modal-offline-link's own border below, it
   only ever read as a stray, floating divider line sitting in an otherwise
   empty gap, not an intentional separator. */
.event-modal-header[b-h5qftfbcbx] {
    display: flex;
    align-items: center;
    /* No justify-content here anymore -- .event-modal-title-group's own
       flex: 1 1 auto below is what pushes the offline-copy button and
       Close to this row's right edge now, by consuming whatever space
       they don't need, the same way justify-content: space-between used
       to when Close was still nested inside .event-modal-header-actions
       (its only sibling back then). That single "actions" group no
       longer holds both buttons -- see .event-modal-close's own comment
       in the markup for why -- so space-between would otherwise spread
       three separate items apart instead of keeping the right-hand ones
       tight against each other. */
    flex-wrap: wrap;
    row-gap: 8px;
    gap: 12px;
    padding: 16px 20px;
}

/* Groups EventModeToggle's badge with the title so both stay together on
   the header's left side -- this box's own flex: 1 1 auto below is what
   pins the offline-copy button and Close to the row's right edge (see
   .event-modal-header's own comment on why). */
.event-modal-title-group[b-h5qftfbcbx] {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
    /* Grows to fill whatever room .event-modal-header-actions/.event-modal-
       close (both flex: none) don't need, pushing them to the row's right
       edge -- see .event-modal-header's own comment on why this replaced
       justify-content: space-between there. */
    flex: 1 1 auto;
}

.event-modal-title-group strong[b-h5qftfbcbx] {
    min-width: 0;
    overflow-wrap: anywhere;
}

/* Below ~640px there usually isn't room for the title, the offline-copy
   button's full "View offline copy" label, and Close all on one unwrapped
   row -- .event-modal-header-actions used to have flex: none and the
   offline button used to have white-space: nowrap, so that row simply grew
   wider than the screen instead of wrapping, pushing the whole modal (and
   so the whole page, since this modal is full-viewport-width) into
   horizontal overflow. Letting the header wrap (row-gap above) fixes that
   on its own; the order/margin rules below go a step further and also
   keep Close up on the title's own row (top-right, per the reference
   screenshot) with only "View offline copy" dropping to its own row
   underneath, indented to start under the title's own first letter rather
   than sitting flush left under .event-mode-badge's icon. */
@media (max-width: 640px) {
    /* Close now sits on the SAME row as the title (see the markup's own
       comment on why it's a separate flex item, not nested inside
       .event-modal-header-actions). .event-modal-title-group is left at
       its default order (0, unchanged) so it still anchors row one; Close
       is pulled forward to order: 1 so it joins the title on that same
       row -- title-group's own flex: 1 1 auto already pushes it to the
       row's right edge. .event-modal-header-actions is pushed to
       order: 2 *and* given flex: 1 1 100%, which is what actually forces
       it onto its own row underneath -- there's no room left on row one
       for a flex-basis: 100% item once title-group + Close already fit
       within this width, so it wraps. Desktop, with no override here,
       keeps the default DOM/order sequence instead -- title-group, then
       .event-modal-header-actions (the offline button), then Close --
       exactly the original single-row layout. */
    .event-modal-close[b-h5qftfbcbx] {
        order: 1;
    }

    .event-modal-header-actions[b-h5qftfbcbx] {
        order: 2;
        flex: 1 1 100%;
        /* Left-aligned now, not flex-end -- see .event-modal-offline-btn's
           own margin-left just below for why. */
        justify-content: flex-start;
    }

    /* Indents "View offline copy" on its own row to start under the
       title's own first letter, not under .event-mode-badge's icon --
       24px (.event-mode-badge's width) + 8px (.event-modal-title-group's
       own gap) is exactly how far that first letter sits from this row's
       shared left edge on the title's own row above. */
    .event-modal-offline-btn[b-h5qftfbcbx] {
        margin-left: 32px;
    }
}

/* An actual clickable toggle (see this markup's own comment) styled as a
   round badge rather than a normal button -- the border/background resets
   here strip the browser's default <button> chrome so it still reads as
   the same badge it was before it became interactive. Grey/off by default
   (Practice), and a distinct color per active state (Singing/
   Accompaniment) below, same "on" treatment as this app's other
   toggle-styled controls (e.g. AdminChoirManagePage's
   admin-modal-save-btn). */
.event-mode-badge[b-h5qftfbcbx] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    border: none;
    border-radius: 50%;
    flex: none;
    background: rgba(0, 0, 0, 0.06);
    color: rgba(0, 0, 0, 0.4);
    padding: 0;
    cursor: pointer;
}

.event-mode-badge:hover[b-h5qftfbcbx] {
    background: rgba(0, 0, 0, 0.1);
}

.event-mode-badge:focus-visible[b-h5qftfbcbx] {
    outline: 2px solid #4f8cff;
    outline-offset: 2px;
}

.event-mode-badge-singing[b-h5qftfbcbx] {
    background: rgba(79, 140, 255, 0.15);
    color: #4f8cff;
}

.event-mode-badge-singing:hover[b-h5qftfbcbx] {
    background: rgba(79, 140, 255, 0.25);
}

/* Accompaniment's own color -- distinct from Singing's brand blue so the
   two "hidden detail" states (see this button's own top comment) are
   still told apart at a glance, not just from Practice's plain grey. A
   violet in the same saturation/lightness family as Singing's blue,
   rather than reaching for green/amber, which this page already uses
   elsewhere for success/warning semantics (.membership-badge-member,
   .pdf-annotation-warning) that don't apply here. */
.event-mode-badge-accompaniment[b-h5qftfbcbx] {
    background: rgba(138, 92, 246, 0.15);
    color: #8a5cf6;
}

.event-mode-badge-accompaniment:hover[b-h5qftfbcbx] {
    background: rgba(138, 92, 246, 0.25);
}

.event-mode-badge-icon[b-h5qftfbcbx] {
    width: 14px;
    height: 14px;
}

/* Stacks EventModeToggle above its own text label (see the markup's own
   comment on why the label was added) -- flex: none keeps this from being
   stretched/squeezed by .event-modal-title-group's own flex: 1 1 auto
   layout the way a bare button + label pair might be. */
.event-mode-toggle-group[b-h5qftfbcbx] {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    flex: none;
}

/* Small, uppercase, tight line-height -- reads as a caption under the
   badge rather than competing with the event title's own <strong> right
   next to it. Grey/off by default (Practice), matching .event-mode-badge's
   own plain default; Singing/Accompaniment get the same two colors as
   their badge counterparts below, so the label and the badge always agree
   at a glance. */
.event-mode-badge-text[b-h5qftfbcbx] {
    font-size: 0.6rem;
    font-weight: 700;
    line-height: 1;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.4);
    white-space: nowrap;
}

.event-mode-badge-text-singing[b-h5qftfbcbx] {
    color: #4f8cff;
}

.event-mode-badge-text-accompaniment[b-h5qftfbcbx] {
    color: #8a5cf6;
}

/* Groups the offline-copy button with the Close button so both sit
   together on the header's right side, pinned there by .event-modal-
   header's own justify-content: space-between above -- same pairing
   pattern .event-modal-title-group above uses for the left side. */
.event-modal-header-actions[b-h5qftfbcbx] {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
}

.event-modal-close[b-h5qftfbcbx] {
    border: none;
    background: none;
    font-size: 14px;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.5);
    flex: none;
}

.event-modal-close:hover[b-h5qftfbcbx] {
    color: rgba(0, 0, 0, 0.9);
}

/* The button over to wwwroot/offline/event.html -- filled and brand-blue
   (same accent as EventModeToggle's "on" state) rather than a plain text
   link, so it reads as a real, prominent action worth noticing before a
   connection is ever lost, not a minor footnote to discover after. */
.event-modal-offline-btn[b-h5qftfbcbx] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: #4f8cff;
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    text-decoration: none;
    padding: 7px 14px;
    border-radius: 999px;
    flex: none;
}

.event-modal-offline-btn:hover[b-h5qftfbcbx] {
    background: #3a76e6;
}

.event-modal-offline-btn:focus-visible[b-h5qftfbcbx] {
    outline: 2px solid #4f8cff;
    outline-offset: 2px;
}

.event-modal-offline-btn svg[b-h5qftfbcbx] {
    width: 14px;
    height: 14px;
    flex: none;
}

.event-modal-body[b-h5qftfbcbx] {
    padding: 20px;
}

.event-modal-fields[b-h5qftfbcbx] {
    margin: 0;
}

.event-modal-fields dt[b-h5qftfbcbx] {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
    margin: 14px 0 2px;
}

.event-modal-fields dt:first-child[b-h5qftfbcbx] {
    margin-top: 0;
}

.event-modal-fields dd[b-h5qftfbcbx] {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    white-space: pre-wrap;
    /* pre-wrap wraps at normal word/space boundaries, but a single long
       unbroken token typed into Location/Notes (a URL with no spaces, say)
       has none to wrap at -- overflow-wrap lets THAT specific word break
       instead of stretching this modal (and so the page) wider than the
       screen on mobile. */
    overflow-wrap: anywhere;
}

/* ---------- Music Event Sections (program) ---------- */

.event-modal-sections[b-h5qftfbcbx] {
    margin-top: 20px;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
    padding-top: 16px;
}

/* When EventModeToggle is ON ("Singing"), ChoirMusicPage.razor now skips
   rendering the performance-detail <dl class="event-modal-fields"> above
   entirely (rather than rendering it empty), which makes this the very
   first element in .event-modal-body. In that case its own margin-top/
   border-top/padding-top -- meant to separate it from the fields above --
   have nothing above them to separate from, and previously only showed as
   a floating divider line sitting in an otherwise blank gap. Collapsing
   all three to 0 here lets the section list sit directly under
   .event-modal-body's own top padding instead, with no stray line. */
.event-modal-sections:first-child[b-h5qftfbcbx] {
    margin-top: 0;
    border-top: none;
    padding-top: 0;
}

.event-modal-section[b-h5qftfbcbx] {
    padding: 12px;
    margin-bottom: 10px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.02);
    font-size: 14px;
}

.event-modal-section:last-child[b-h5qftfbcbx] {
    margin-bottom: 0;
}

.event-modal-section-notes[b-h5qftfbcbx] {
    margin: 6px 0 0;
    font-size: 13px;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: rgba(0, 0, 0, 0.7);
}

.event-modal-library[b-h5qftfbcbx] {
    margin: 10px 0 0;
    padding-top: 8px;
    border-top: 1px dashed rgba(0, 0, 0, 0.08);
}

.event-modal-library-title[b-h5qftfbcbx] {
    font-size: 13px;
    font-weight: 600;
}

.event-modal-library-line[b-h5qftfbcbx] {
    margin: 4px 0 0;
    font-size: 13px;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: rgba(0, 0, 0, 0.75);
}

.event-modal-library-links[b-h5qftfbcbx] {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin: 6px 0 0;
}

/* The separating line below the voice-part links (Melody through Bass),
   above the Accompaniment 1/2 links -- same border-top/margin-top/
   padding-top divider convention as .event-modal-sections elsewhere on
   this page, rather than a literal <hr>, so it matches this app's existing
   look. Only ever applied when there's a voice-part group above it to
   separate from -- see this class's own call site in the markup. */
.event-modal-library-links-accompaniment[b-h5qftfbcbx] {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid rgba(0, 0, 0, 0.08);
}

/* Plain-button reset so a part link reads the same as the old <a> it
   replaced (see this section's own markup comment on why every link --
   audio included -- now opens LinkPreviewModal instead of a new tab or an
   always-on inline player). */
.event-modal-library-links button[b-h5qftfbcbx] {
    border: none;
    background: none;
    padding: 0;
    font-family: inherit;
    color: #4f8cff;
    font-size: 13px;
    text-decoration: none;
    cursor: pointer;
}

.event-modal-library-links button:hover[b-h5qftfbcbx],
.event-modal-library-links button:focus-visible[b-h5qftfbcbx] {
    text-decoration: underline;
}

/* Icon-only and compact -- the "PDF" text label that used to sit beside
   this icon is gone; title/aria-label above still carry the accessible
   name. Now a colorful "file attached"-style glyph (see the SVG in
   ChoirMusicPage.razor's markup) rather than a single-color stroke outline,
   so no border/background chrome around it -- that would compete with the
   icon's own red/yellow coloring instead of just framing a neutral outline
   the way the old icon needed. */
.event-modal-pdf-btn[b-h5qftfbcbx] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
}

.event-modal-pdf-btn:hover[b-h5qftfbcbx] {
    opacity: 0.8;
}

.event-modal-pdf-btn svg[b-h5qftfbcbx] {
    width: 24px;
    height: 24px;
}

/* ---------- Offline copy modal ----------
   Hidden by default (no "open" class -- see choirMusicOfflineModal.js) so
   this markup can sit permanently in the DOM without being visible until
   its trigger link actually opens it. z-index sits above the event modal
   (1000) but below the full-window PDF viewer overlay (2000) below, in the
   unlikely case both are ever open at once.

   Full-screen by design, same as every other modal in this app -- the
   panel and iframe below are both sized to exactly 100% of this overlay
   (which is itself inset: 0) rather than a fixed vh figure, so there is no
   leftover gap of blank panel background below the iframe. */
.offline-modal-overlay[b-h5qftfbcbx] {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1500;
    background: rgba(0, 0, 0, 0.5);
    padding: 0;
    box-sizing: border-box;
}

.offline-modal-overlay.open[b-h5qftfbcbx] {
    display: flex;
}

.offline-modal-panel[b-h5qftfbcbx] {
    width: 100%;
    height: 100%;
    margin: 0 auto;
    background: #fff;
    border-radius: 0;
    overflow: hidden;
    box-shadow: none;
}

/* No .offline-modal-close here anymore -- offline/event.html now renders
   its own close ("X") button in its own header, so the same control isn't
   floating twice near the same corner. See ChoirMusicPage.razor's own
   #offline-modal-overlay markup comment for the full reasoning. */

.offline-modal-iframe[b-h5qftfbcbx] {
    display: block;
    width: 100%;
    height: 100%;
    border: none;
}

/* ---------- Offline copy modal: "exit" confirmation ----------
   Shown on top of the offline-modal-panel (same overlay, higher z-index)
   when the X close button is clicked, before the offline copy is actually
   torn down -- see offline-modal.js's confirmClose()/cancelConfirmClose(). */
.offline-modal-confirm-overlay[b-h5qftfbcbx] {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 1550;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.6);
    padding: 0;
    box-sizing: border-box;
}

.offline-modal-confirm-overlay.open[b-h5qftfbcbx] {
    display: flex;
}

/* Full-screen, same as this app's other confirmation dialogs (e.g. the
   admin delete-confirm popups) -- its content is centered within the full
   viewport rather than shrunk to a small floating card. */
.offline-modal-confirm-panel[b-h5qftfbcbx] {
    width: 100%;
    height: 100%;
    max-width: none;
    background: #fff;
    border-radius: 0;
    box-shadow: none;
    padding: 20px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
}

.offline-modal-confirm-panel p[b-h5qftfbcbx] {
    margin: 0 0 22px;
    max-width: 480px;
    font-size: 17px;
    line-height: 1.5;
    color: rgba(0, 0, 0, 0.85);
}

.offline-modal-confirm-actions[b-h5qftfbcbx] {
    display: flex;
    justify-content: center;
    gap: 10px;
}

.offline-modal-confirm-actions button[b-h5qftfbcbx] {
    padding: 10px 24px;
    border-radius: 6px;
    font-size: 14px;
    cursor: pointer;
}

.offline-modal-confirm-cancel[b-h5qftfbcbx] {
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    color: rgba(0, 0, 0, 0.75);
}

.offline-modal-confirm-cancel:hover[b-h5qftfbcbx] {
    background: rgba(0, 0, 0, 0.04);
}

.offline-modal-confirm-continue[b-h5qftfbcbx] {
    border: none;
    background: #4f8cff;
    color: #fff;
    font-weight: 600;
}

.offline-modal-confirm-continue:hover[b-h5qftfbcbx] {
    background: #3d7ae6;
}

/* ---------- Full-window PDF viewer/editor overlay ---------- */

.pdf-viewer-overlay[b-h5qftfbcbx] {
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: #fff;
    padding: 16px 20px;
    box-sizing: border-box;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}
/* _content/ChoirMusic/Components/Pages/ChoirMusicViewPage.razor.rz.scp.css */
.choir-music-view-page[b-391recb22x] {
    max-width: none;
}

/* Same card treatment as MainLayout's whole-app "not-a-member" screen —
   this is the same kind of message, just scoped to one choir instead of
   the whole app. */
.choir-access-denied[b-391recb22x] {
    max-width: 480px;
    background: rgba(0, 0, 0, 0.03);
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 16px;
    padding: 32px 28px;
}

.choir-access-denied h4[b-391recb22x] {
    margin: 0 0 8px;
    font-size: 18px;
}

.choir-access-denied p[b-391recb22x] {
    margin: 0;
    color: rgba(0, 0, 0, 0.6);
    font-size: 14px;
    line-height: 1.5;
}
/* _content/ChoirMusic/Components/Pages/Home.razor.rz.scp.css */
.login-page[b-ywogfffvhi] {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    inset: 0;
}

.login-card[b-ywogfffvhi] {
    background: rgba(255, 255, 255, 0.05);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 16px;
    padding: 48px 40px;
    text-align: center;
    max-width: 380px;
    width: 100%;
}

.login-card h2[b-ywogfffvhi] {
    margin: 0 0 8px;
    font-size: 24px;
    font-weight: 600;
}

.login-card p[b-ywogfffvhi] {
    margin: 0 0 32px;
    color: rgba(255, 255, 255, 0.5);
    font-size: 14px;
}

.login-unconfigured[b-ywogfffvhi] {
    margin-bottom: 0;
}

.login-unconfigured code[b-ywogfffvhi] {
    background: rgba(255, 255, 255, 0.1);
    border-radius: 4px;
    padding: 1px 5px;
    font-size: 13px;
}

.login-buttons[b-ywogfffvhi] {
    display: flex;
    flex-direction: column;
    gap: 12px;
}

.login-btn[b-ywogfffvhi] {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 12px 20px;
    border-radius: 8px;
    font-size: 14px;
    font-weight: 500;
    text-decoration: none;
    transition: opacity 0.15s;
}

.login-btn:hover[b-ywogfffvhi] {
    opacity: 0.9;
}

.google-btn[b-ywogfffvhi] {
    background: #fff;
    color: #333;
}

.microsoft-btn[b-ywogfffvhi] {
    background: #2f2f2f;
    color: #fff;
    border: 1px solid rgba(255, 255, 255, 0.15);
}
/* _content/ChoirMusic/Components/Pages/SongLibraryPage.razor.rz.scp.css */
.song-library-page[b-12baqaw9k0] {
    max-width: 1100px;
}

/* PDF-open failure banner -- same look and reasoning as ChoirMusicPage's
   own .pdf-annotation-warning (CSS isolation means that rule doesn't reach
   here -- see this file's own top comment on that established
   convention). Amber, not the app's error red, since the rest of this
   page still works fine either way. */
.pdf-annotation-warning[b-12baqaw9k0] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin: 12px 0 0;
    padding: 10px 14px;
    border-radius: 8px;
    background: rgba(255, 179, 0, 0.14);
    border: 1px solid rgba(158, 106, 0, 0.35);
    color: #7a5200;
    font-size: 13px;
}

.pdf-annotation-warning button[b-12baqaw9k0] {
    flex: none;
    border: none;
    background: none;
    color: inherit;
    font-weight: 700;
    cursor: pointer;
    padding: 0 2px;
}

/* ---------- Choir list ----------
   Same look as ChoirMusicPage's own choir-table/choir-row/choir-icon/
   choir-table-*-col/membership-badge* classes -- duplicated here per this
   app's established CSS-isolation convention (each component's scoped
   .razor.css only matches elements THAT component renders). */

/* Scoped, independently-scrollable wrapper -- same reasoning as this
   file's own .song-library-scroll further down (and ChoirMusicPage's
   identical .choir-table-scroll, duplicated here per this app's established
   CSS-isolation convention): without this, a long choir name -- or, on a
   narrow phone, even the plain icon+name+membership combination at its
   normal desktop widths -- used to force the ENTIRE page wider than the
   viewport instead of just this table. Scoping the scroll to this box
   instead keeps the rest of the page at a normal, unshrunk scale. The
   @media rule further down already shrinks the fixed-width columns enough
   that a typical phone never actually needs that scroll -- this is the
   fallback for a longer name or a narrower device, not the primary fix. */
.choir-table-scroll[b-12baqaw9k0] {
    margin-top: 24px;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.choir-table[b-12baqaw9k0] {
    width: 100%;
    /* Keeps the three columns from being squeezed down to unreadable
       widths inside the scroll wrapper above. */
    min-width: 420px;
    border-collapse: collapse;
    /* table-layout: fixed -- without it, the browser's default "auto"
       table layout picks column widths by looking at the CONTENT of every
       row, including the expanded choir's own row (colspan="3", holding
       this page's own Song Library grid -- see .song-library-table's
       min-width: 660px below). That grid is already wrapped in its own
       overflow-x: auto box (.song-library-scroll), but an "auto" box's
       INTRINSIC content width still feeds into the outer table's
       column-width math unless the table ignores it outright -- which is
       exactly what was pushing this table (and so this scroll wrapper,
       and the page itself) wider the moment a choir's library was
       expanded, instead of staying at a fixed width with only the
       expanded grid scrolling sideways inside its own box. Fixed layout
       sizes columns ONLY from the header row's own widths (see
       .choir-table-icon-col/.choir-table-membership-col above, with the
       name column taking whatever's left) and never revisits that once a
       row expands, so expanding a choir can no longer resize this table,
       this page, or the columns already on screen. */
    table-layout: fixed;
}

/* table-layout: fixed above locks this table's own column widths, but it
   does NOT clip content -- a table cell never clips its children just
   because they're wider than the cell's own (now-fixed) box, the same way
   .song-library-table's/the Music Event picker's own dedicated overflow-x:
   auto wrappers already have to handle their OWN wide content instead of
   relying on the cell to contain it for them. This colspan="3" cell is
   that same gap for anything rendered directly inside it that ISN'T
   already in one of those dedicated wrappers -- the search box above the
   grid, and the stacked mobile card list on a narrow phone, chief among
   them (neither has, or needs, its own horizontal scroll the way the wide
   desktop grid does) -- so without this, either one growing even slightly
   wider than this fixed-width cell (a long unbroken word, a rounding
   difference in a percentage width) spills out to the right of the whole
   table, and so the whole page, exactly like the bug table-layout: fixed
   above already fixed for the table's own column sizing. overflow-x:
   hidden here is the same backstop app.css's html/body rule already
   applies at the page level -- clip rather than let it drag anything
   sideways -- just scoped to this one cell instead. */
.choir-table-expand-cell[b-12baqaw9k0] {
    overflow-x: hidden;
}

.choir-table thead th[b-12baqaw9k0] {
    text-align: left;
    padding: 0 8px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.choir-table-icon-col[b-12baqaw9k0] {
    width: 60px;
}

/* Mobile: shrink the icon, membership badge, and cell padding so a typical
   phone fits all three columns without needing .choir-table-scroll's
   horizontal scroll at all. Duplicated from ChoirMusicPage.razor.css's
   identical rule per this app's CSS-isolation convention. */
@media (max-width: 640px) {
    /* Was min-width: 340px -- meant as a floor so table-layout: fixed's
       three columns (icon/name/membership) never got squeezed down to an
       unreadable width, but 340px does not actually fit inside a typical
       375px phone once .stub-page's own 24px+24px padding (see app.css)
       is subtracted -- only 327px is ever actually available there, so
       that "floor" was 13px wider than the room it had to stand on, and
       .choir-table-scroll's overflow-x: auto (a fallback for a genuinely
       long name or an unusually narrow device -- see that rule's own
       comment) ended up triggering on every ordinary phone instead,
       exactly the "still has a scrollbar" report this was supposed to
       prevent. table-layout: fixed already protects the icon and
       membership columns from shrinking below their own explicit widths
       below regardless of the table's overall width -- min-width: 0 here
       (overriding the base rule's 420px, meant for desktop) just lets the
       table actually settle at 100% (its own base rule) of whatever
       .choir-table-scroll has to offer instead of forcing a floor wider
       than that on every phone. */
    .choir-table[b-12baqaw9k0] {
        min-width: 0;
    }

    /* Widened from 40px -- flush against .choir-table-name-col's own left
       padding at that width, which read as the icon crowding/touching the
       choir name text rather than sitting a comfortable distance from it. */
    .choir-table-icon-col[b-12baqaw9k0] {
        width: 48px;
    }

    .choir-icon[b-12baqaw9k0] {
        width: 32px;
        height: 32px;
    }

    .choir-table-membership-col[b-12baqaw9k0] {
        width: 84px;
        white-space: normal;
    }

    .membership-badge[b-12baqaw9k0] {
        padding: 3px 6px;
        font-size: 11px;
        white-space: normal;
        text-align: center;
    }

    .choir-row td[b-12baqaw9k0],
    .choir-table thead th[b-12baqaw9k0] {
        padding: 8px 4px;
    }
}

.choir-table-sort-btn[b-12baqaw9k0] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    border: none;
    background: none;
    padding: 0;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
    cursor: pointer;
}

.choir-table-sort-btn:hover[b-12baqaw9k0],
.choir-table-sort-btn:focus-visible[b-12baqaw9k0] {
    color: rgba(0, 0, 0, 0.9);
}

.choir-table-sort-indicator[b-12baqaw9k0] {
    font-size: 10px;
}

.choir-row[b-12baqaw9k0] {
    cursor: pointer;
}

.choir-row:hover[b-12baqaw9k0],
.choir-row:focus-visible[b-12baqaw9k0] {
    background: rgba(0, 0, 0, 0.04);
}

.choir-row:focus-visible[b-12baqaw9k0] {
    outline: 2px solid #4f8cff;
    outline-offset: -2px;
}

.choir-row td[b-12baqaw9k0] {
    padding: 10px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.choir-row-locked[b-12baqaw9k0] {
    cursor: default;
}

.choir-row-locked:hover[b-12baqaw9k0],
.choir-row-locked:focus-visible[b-12baqaw9k0] {
    background: none;
}

.choir-row-locked .choir-icon[b-12baqaw9k0] {
    opacity: 0.45;
}

.choir-row-locked .choir-table-name-col[b-12baqaw9k0] {
    color: rgba(0, 0, 0, 0.45);
}

.choir-icon[b-12baqaw9k0] {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    display: block;
    object-fit: cover;
}

.choir-table-name-col[b-12baqaw9k0] {
    font-size: 15px;
    font-weight: 500;
}

.choir-table-membership-col[b-12baqaw9k0] {
    width: 150px;
    white-space: nowrap;
}

.membership-badge[b-12baqaw9k0] {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 12px;
    font-weight: 600;
}

.membership-badge-member[b-12baqaw9k0] {
    background: rgba(38, 176, 80, 0.12);
    color: #1c7a3e;
}

.membership-badge-non-member[b-12baqaw9k0] {
    background: rgba(0, 0, 0, 0.06);
    color: rgba(0, 0, 0, 0.5);
}

/* ---------- Song library grid (expanded row) ---------- */

.song-library-row td[b-12baqaw9k0] {
    padding: 4px 8px 16px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
}

.song-library-loading[b-12baqaw9k0],
.song-library-empty[b-12baqaw9k0] {
    margin: 8px 0 0;
    color: rgba(0, 0, 0, 0.5);
    font-size: 13px;
}

.song-library-search[b-12baqaw9k0] {
    margin-top: 8px;
}

.song-library-search-input[b-12baqaw9k0] {
    width: 100%;
    max-width: 320px;
    padding: 8px 10px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    font-size: 16px;
    font-family: inherit;
}

/* Capped and independently scrollable, same reasoning as
   AdminChoirManagePage's .admin-grid-scroll -- a choir with a long library
   shouldn't push the rest of this page (or other choirs' rows below it)
   far down/off screen. */
.song-library-scroll[b-12baqaw9k0] {
    max-height: 50vh;
    overflow-y: auto;
    /* Six columns (Title/Composer/Text alternative/Notes/Links/PDF) is
       comfortably wider than a phone screen once real data is in most of
       them -- without its own horizontal scroll, this table used to force
       the SongLibraryPage itself wider than the viewport on mobile the
       moment a choir's library was expanded, which is exactly the "must be
       visible without shrinking, whole page must fit" bug: iOS Safari in
       particular shrinks the ENTIRE page down to whatever scale fits the
       widest thing on it, not just this table. Scoping the scroll to this
       box instead keeps the rest of the page (and the choir list above it)
       at a normal, unshrunk scale, with only this table's own overflow
       reachable by a sideways scroll/swipe inside its own box. */
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    margin-top: 12px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
}

/* Six columns (Title/Composer/Text alternative/Notes/Links/PDF) had no
   per-column width floor at all, so inside .song-library-scroll's
   overflow-x: auto wrapper the browser was free to squeeze every one of
   them down as far as it could before the scrollbar ever kicked in --
   which on a phone meant each column narrower than a single word, so
   ordinary titles like "Gospel Acclamation: cf. Ephesians 1:17-18" wrapped
   into eight-plus stacked lines instead of two or three. min-width here
   (the sum of the per-column min-widths below, plus the PDF column) gives
   the scroll wrapper an actual floor to scroll AT: below this width the
   table now scrolls sideways with each column still readable, rather than
   squeezing everything down to fit whatever width happened to be
   available. 190px for the PDF column (see .song-library-pdf-col below)
   -- wide enough for the icon-only main button plus the labeled
   Accompaniment button on the same line. */
.song-library-table[b-12baqaw9k0] {
    width: 100%;
    min-width: 820px;
    border-collapse: collapse;
}

.song-library-title-col[b-12baqaw9k0] {
    width: 22%;
    min-width: 150px;
}

.song-library-composer-col[b-12baqaw9k0] {
    width: 16%;
    min-width: 110px;
}

.song-library-textalt-col[b-12baqaw9k0] {
    width: 16%;
    min-width: 110px;
}

.song-library-notes-col[b-12baqaw9k0] {
    width: 20%;
    min-width: 130px;
}

.song-library-links-col[b-12baqaw9k0] {
    width: 20%;
    min-width: 130px;
}

.song-library-table thead th[b-12baqaw9k0] {
    position: sticky;
    top: 0;
    background: #fff;
    z-index: 1;
    text-align: left;
    padding: 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

.song-library-table td[b-12baqaw9k0] {
    padding: 10px 8px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.06);
    font-size: 14px;
    vertical-align: top;
    /* A long unbroken word (a URL pasted into Notes/Text alternative, say)
       has no space to wrap at on its own -- this lets it break instead of
       stretching this column, and so this table's own .song-library-scroll
       wrapper, wider than it needs to be. */
    overflow-wrap: anywhere;
}

/* No longer a fixed 50px -- a piece with an accompaniment PDF now shows a
   second, labeled button beside the icon-only main one (see
   .song-library-pdf-btn-accompaniment below), which needs more room than a
   single 30x30 icon. 190px is enough for both buttons on one line; a piece
   with just the main PDF (the common case) doesn't stretch the column
   itself since this is a floor, not a fixed width -- table-layout stays
   the browser default (auto), so a column with only the 30px icon button
   still shrinks to fit it. */
.song-library-pdf-col[b-12baqaw9k0] {
    width: auto;
    min-width: 190px;
    text-align: center;
    white-space: nowrap;
}

.song-library-links[b-12baqaw9k0] {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 12px;
}

/* Plain-button reset, same shape as ChoirMusicPage's own
   .event-modal-library-links button -- a part link opens LinkPreviewModal
   below instead of navigating away. */
.song-library-link-btn[b-12baqaw9k0] {
    border: none;
    background: none;
    padding: 0;
    font-family: inherit;
    color: #4f8cff;
    font-size: 13px;
    cursor: pointer;
}

.song-library-link-btn:hover[b-12baqaw9k0],
.song-library-link-btn:focus-visible[b-12baqaw9k0] {
    text-decoration: underline;
}

/* Icon-only PDF button, same treatment as ChoirMusicPage's own
   .event-modal-pdf-btn -- see that class's own comment on why no border/
   background chrome (it would compete with the icon's own red/yellow
   coloring). */
.song-library-pdf-btn[b-12baqaw9k0] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: none;
    background: none;
    cursor: pointer;
    white-space: nowrap;
}

.song-library-pdf-btn:hover[b-12baqaw9k0] {
    opacity: 0.8;
}

.song-library-pdf-btn svg[b-12baqaw9k0] {
    width: 24px;
    height: 24px;
}

/* Widens the icon-only base button to fit its own "Accompaniment" label --
   see the markup's own comment on why this piece's two PDFs are both shown
   here (unlike ChoirMusicPage's event modal, which picks one automatically
   from its Singing/Accompaniment/Practice toggle instead of showing two). */
.song-library-pdf-btn-accompaniment[b-12baqaw9k0] {
    width: auto;
    gap: 4px;
    padding: 2px 8px 2px 2px;
    margin-left: 4px;
    font-family: inherit;
    font-size: 12px;
    font-weight: 600;
    color: rgba(0, 0, 0, 0.75);
    white-space: nowrap;
}

/* ---------- Music Library: mobile card view ----------
   Same "two mutually-exclusive layouts, swapped purely by media query"
   pattern as ChoirMusicPage's .choir-events-picker-desktop/-mobile: the six-
   column .song-library-table above is fine on anything wide enough for its
   660px min-width, but on a phone that means panning to read Composer/Text
   alternative/Notes/Links for every single entry. Below that width this
   stacked one-card-per-entry layout takes over instead -- styled after
   ChoirMusicPage's own Practice-mode event-modal-section/event-modal-library
   cards -- so Title, Composer, Text alternative, Notes and Links are all
   readable top to bottom with no horizontal scrolling at all. */
.song-library-desktop[b-12baqaw9k0] {
    display: block;
}

.song-library-mobile[b-12baqaw9k0] {
    display: none;
    flex-direction: column;
    gap: 10px;
    margin-top: 12px;
    max-height: 50vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

.song-library-card[b-12baqaw9k0] {
    padding: 12px;
    border: 1px solid rgba(0, 0, 0, 0.08);
    border-radius: 8px;
    background: rgba(0, 0, 0, 0.02);
    font-size: 14px;
}

.song-library-card-title[b-12baqaw9k0] {
    font-size: 14px;
    font-weight: 600;
}

.song-library-card-line[b-12baqaw9k0] {
    margin: 6px 0 0;
    font-size: 13px;
    line-height: 1.5;
    white-space: pre-wrap;
    overflow-wrap: anywhere;
    color: rgba(0, 0, 0, 0.75);
}

.song-library-card-links[b-12baqaw9k0] {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 14px;
    margin: 8px 0 0;
}

/* Same icon, but with its "Open PDF" label restored (unlike the icon-only
   desktop .song-library-pdf-btn) -- there's no dedicated PDF column left to
   anchor an icon-only button to in this stacked layout, so the label keeps
   its purpose clear on its own line. */
.song-library-card-pdf-btn[b-12baqaw9k0] {
    /* display: flex (not inline-flex) so this button is block-level and
       always starts its own line -- see .song-library-card-pdf-btn-accompaniment
       below, whose margin-top only creates vertical separation between the
       two buttons if each one is already on its own line. width: max-content
       keeps it shrink-wrapped to its label instead of stretching to fill
       the card's full width now that it's block-level. */
    display: flex;
    width: max-content;
    align-items: center;
    gap: 6px;
    margin: 8px 0 0;
    padding: 0;
    border: none;
    background: none;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    color: rgba(0, 0, 0, 0.75);
    cursor: pointer;
}

.song-library-card-pdf-btn:hover[b-12baqaw9k0] {
    opacity: 0.8;
}

.song-library-card-pdf-btn svg[b-12baqaw9k0] {
    width: 22px;
    height: 22px;
    flex: none;
}

/* Sits directly under the main PDF button above it (see the markup) --
   a tighter top margin than .song-library-card-pdf-btn's own 8px keeps the
   two read as a related pair rather than two unrelated actions. */
.song-library-card-pdf-btn-accompaniment[b-12baqaw9k0] {
    margin-top: 4px;
}

@media (max-width: 640px) {
    .song-library-desktop[b-12baqaw9k0] {
        display: none;
    }

    .song-library-mobile[b-12baqaw9k0] {
        display: flex;
    }
}

/* ---------- Full-window PDF viewer/editor overlay ----------
   Same as ChoirMusicPage's own .pdf-viewer-overlay. */
.pdf-viewer-overlay[b-12baqaw9k0] {
    position: fixed;
    inset: 0;
    z-index: 2000;
    background: #fff;
    padding: 16px 20px;
    box-sizing: border-box;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}
/* _content/ChoirMusic/Components/Shared/ChoirSheetMusicViewer.razor.rz.scp.css */
.sheet-music-panel[b-8w3bhy7p95] {
    display: flex;
    flex-direction: column;
    height: calc(100vh - 180px);
    min-height: 480px;
}

/* Used when this component fills a dedicated full-window overlay (see
   ChoirMusicPage.razor's PDF modal) instead of sitting inline on a normal
   page with its own surrounding chrome -- overrides the calc() above so
   the panel fills its container exactly rather than leaving room for
   chrome that isn't there. Declared after .sheet-music-panel so it wins
   the height cascade despite equal selector specificity. */
.sheet-music-panel-fullscreen[b-8w3bhy7p95] {
    height: 100%;
    min-height: 0;
}

.sheet-music-header[b-8w3bhy7p95] {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: none;
    padding: 0 0 14px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.sheet-music-header h4[b-8w3bhy7p95] {
    margin: 0;
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sheet-music-exit-btn[b-8w3bhy7p95] {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: none;
    padding: 8px 14px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.sheet-music-exit-btn:hover[b-8w3bhy7p95] {
    background: rgba(0, 0, 0, 0.04);
}

.sheet-music-exit-btn svg[b-8w3bhy7p95] {
    width: 14px;
    height: 14px;
}

/* overflow-x: auto is the mobile-fit fix for SfPdfViewer2's own toolbar --
   see this component's own top-of-markup comment on EnableDesktopMode="true":
   that flag deliberately keeps the SAME desktop toolbar (with every
   annotation tool) on every device, at the documented cost of switching off
   ALL of Syncfusion's own mobile-specific layout logic, toolbar included.
   A desktop-width toolbar of icons doesn't reflow on a narrow phone, so
   without this it used to push the whole overlay -- and so the whole page,
   since this viewer fills a full-viewport overlay (.pdf-viewer-overlay in
   ChoirMusicPage.razor.css/SongLibraryPage.razor.css) -- wider than the
   screen. Scoping the overflow to this box means Syncfusion's toolbar (or
   anything else it renders) can only ever grow THIS container's own scroll
   area sideways, never the page underneath it, matching the read-only
   overflow-containment pattern used everywhere else in this app (e.g.
   SongLibraryPage's own .song-library-scroll). max-width: 100% keeps this
   box itself from ever exceeding its parent no matter what Syncfusion's
   root element reports its own intrinsic width as. */
.sheet-music-viewer[b-8w3bhy7p95] {
    flex: 1;
    min-height: 0;
    max-width: 100%;
    overflow-x: auto;
}
/* _content/ChoirMusic/Components/Shared/LinkPreviewModal.razor.rz.scp.css */
/* Same overlay/panel treatment as this app's other modals (see
   AdminChoirManagePage's admin-modal-overlay/panel), duplicated here per
   this app's established CSS-isolation convention -- scoped to this
   component instead of shared, since each component's scoped .razor.css
   only matches elements THAT component renders.

   z-index sits above ChoirMusicPage's own event-modal-overlay (1000) and
   its offline-modal-overlay (1500) -- this opens FROM inside that event
   modal and needs to sit visually on top of it -- but below the full-window
   PDF viewer overlay (2000), in the unlikely case both are ever open at
   once. */
.link-preview-modal-overlay[b-m769s58apj] {
    position: fixed;
    inset: 0;
    z-index: 1600;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.5);
    padding: 0;
    /* WebKit/iOS Safari compositing-layer bug: a `position: fixed` element
       that shares the page with another large/full-viewport `position:
       fixed` element added or removed via a DOM patch (rather than a full
       page load) can drop out of the fixed-position compositing layer
       entirely once that sibling is removed or re-added -- most visible in
       installed/home-screen ("standalone display mode") PWAs, which is why
       this reads to users as the top bar (Choir Music / Song Library /
       Admin / Help / Sign out) vanishing after opening or closing this
       overlay. See MainLayout.razor.css's .logout-bar for the original
       occurrence of this fix and the full write-up; this overlay is one of
       the DOM-patched full-viewport elements that can trigger it, so it
       gets the same treatment: forcing it onto its own GPU compositing
       layer up front (rather than letting WebKit decide whether it
       deserves one) keeps the *other* fixed elements on the page from
       losing their layer when this one is toggled. Harmless on every other
       platform -- doesn't change layout or visible appearance, only how
       the browser paints. */
    transform: translateZ(0);
    -webkit-transform: translateZ(0);
    -webkit-backface-visibility: hidden;
    backface-visibility: hidden;
    -webkit-perspective: 1000;
    perspective: 1000;

}

/* Full-screen by design -- every modal in this app occupies the entire
   viewport once opened, rather than floating as a smaller centered card. */
.link-preview-modal-panel[b-m769s58apj] {
    width: 100%;
    height: 100%;
    max-width: none;
    max-height: none;
    display: flex;
    flex-direction: column;
    background: #fff;
    border-radius: 0;
    box-shadow: none;
    overflow: hidden;
}

.link-preview-modal-header[b-m769s58apj] {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 14px 18px;
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    flex: none;
}

.link-preview-modal-header strong[b-m769s58apj] {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    min-width: 0;
}

.link-preview-modal-close[b-m769s58apj] {
    border: none;
    background: none;
    font-size: 14px;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.5);
    flex: none;
}

.link-preview-modal-close:hover[b-m769s58apj] {
    color: rgba(0, 0, 0, 0.9);
}

.link-preview-modal-body[b-m769s58apj] {
    padding: 20px;
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Belt-and-suspenders alongside the panel's own overflow: hidden --
       whatever a foreign embed (YouTube, Vimeo, SoundCloud, ...) renders
       inside the iframe below is that site's own page, entirely outside
       this app's control, so this keeps IT from ever forcing this box (and
       so the panel, and so the page) wider than the screen on mobile. */
    max-width: 100%;
    overflow: hidden;
    box-sizing: border-box;
}

/* Smaller on narrow phones so more of the actual video/embed fits without
   needing to shrink/pinch-zoom to see the player's own controls in full. */
@media (max-width: 640px) {
    .link-preview-modal-body[b-m769s58apj] {
        padding: 10px;
    }
}

.link-preview-modal-audio[b-m769s58apj] {
    width: 100%;
    max-width: 100%;
}

.link-preview-modal-iframe[b-m769s58apj] {
    display: block;
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    border: none;
}
/* _content/ChoirMusic/Components/Shared/LinkUrlField.razor.rz.scp.css */
.link-url-field[b-di239ejwkr] {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin-bottom: 14px;
}

.link-url-field label[b-di239ejwkr] {
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: rgba(0, 0, 0, 0.6);
}

.link-url-input-row[b-di239ejwkr] {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 8px;
    border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, 0.15);
    background: #fff;
    transition: border-color 0.15s ease, background 0.15s ease;
}

/* Toggled by drag-drop-upload.js's wireLinkDropZone while a drag is over
   this field — see that file's onDragOver/onDragLeave handlers. */
.link-url-input-row.drop-target-active[b-di239ejwkr] {
    border-color: #4f8cff;
    background: rgba(79, 140, 255, 0.06);
}

.link-url-icon[b-di239ejwkr] {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    flex: none;
    color: rgba(0, 0, 0, 0.55);
}

.link-url-icon svg[b-di239ejwkr] {
    width: 100%;
    height: 100%;
}

.link-url-input[b-di239ejwkr] {
    flex: 1 1 auto;
    min-width: 0;
    border: none;
    outline: none;
    padding: 4px 0;
    font-size: 16px;
    font-family: inherit;
    background: transparent;
}

.link-url-clear-btn[b-di239ejwkr] {
    flex: none;
    border: 1px solid rgba(0, 0, 0, 0.15);
    border-radius: 4px;
    background: #fff;
    cursor: pointer;
    font-size: 12px;
    font-weight: 600;
    line-height: 1;
    color: rgba(0, 0, 0, 0.6);
    padding: 5px 10px;
    white-space: nowrap;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}

.link-url-clear-btn:hover[b-di239ejwkr] {
    color: rgba(0, 0, 0, 0.85);
    border-color: rgba(0, 0, 0, 0.3);
    background: rgba(0, 0, 0, 0.04);
}

.link-url-error[b-di239ejwkr] {
    font-size: 12px;
    color: #c62828;
}

/* Never shown directly -- see LinkUrlField.razor's own comment on why this
   exists at all (drag-drop-upload.js hands a dropped audio file to this
   exact element). */
.link-url-audio-input[b-di239ejwkr] {
    display: none;
}
