.base {
    overflow-x: clip;
}

.base__container {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    margin: 0;
    box-sizing: border-box;
    /* Only the sidebar offset animates. A catch-all here also tweened `height` on the
       viewport-locked pages, so every viewport change slid the card over 250ms. */
    transition: padding-left .25s ease-in-out;
    background-color: var(--color-olympia-canvas);
}

.base__header {
    position: fixed;
    top: 0;
    padding-top: env(safe-area-inset-top);
    z-index: 40;
    width: 100%;
    background-color: var(--color-olympia-canvas);
}


.base__overlay {
    display: none;
    position: fixed;
    width: 100%;
    height: 100%;
    top: 0;
    left: 0;
    opacity: 0;
    transition: .25s ease;
    background-color: var(--color-gray-900);
    z-index: 50;

}

.base__content-container {
    display: flex;
    justify-content: center;
    flex: 1;
    width: 100%;
    margin-top: calc(var(--dim-mobile-header-height) + env(safe-area-inset-top));
}

/* No fixed header on chromeless pages, so the offset above would render as dead space. */
.base__container--chromeless .base__content-container {
    margin-top: env(safe-area-inset-top);
}

/*
 * Member-layout full-height bridge for the events check-in page. The fill-viewport
 * cascade only fires in the management sidebar layout, so on this member-facing page
 * the flex chain stops at <main> (a plain block) and the card hugs its content. Scoped
 * to the unique `.communities-events-checkin` wrapper so no other member page is affected.
 */
.base__content-container:has(.communities-events-checkin) > div,
.base__content-container:has(.communities-events-checkin) main,
.base__content-container:has(.communities-events-checkin) .communities,
.base__content-container:has(.communities-events-checkin) .communities__content {
    display: flex;
    flex-direction: column;
    flex: 1 1 0%;
    min-height: 0;
}

/*
 * Management sidebar — full-viewport-height fixed rail on lg+ that the header
 * and content shift right of via padding on `.base__container--has-sidebar`.
 * Collapsed state is driven by Alpine on `.base__container`; the rail width
 * animates via the modifier class so labels ease out without a layout flash.
 */
.base__sidebar {
    display: none;
}

@media (min-width: 768px) {
    .base__sidebar {
        display: flex;
        flex-direction: column;
        position: fixed;
        top: 0;
        left: 0;
        bottom: 0;
        width: 256px;
        z-index: 30;
        border-right: solid 1px var(--color-olympia-canvas-border);
        background-color: var(--color-olympia-canvas);
        overflow: hidden;
        overscroll-behavior: contain;
        transition: width .2s ease-in-out;
    }

    .base__container--has-sidebar {
        padding-left: 256px;
        /* Content offset animates in lockstep with the rail's width so the content edge follows
         * the swipe rather than snapping to its final width. */
        transition: padding-left .2s ease-in-out;
        /* Admin content fills the available width; member-facing pages keep the 1046px default. */
        --dim-max-width: none;
    }

    /*
     * Fill-viewport layout opts in via a `.fill-viewport` marker on the page: lock the
     * container to the viewport so an inner region owns the scroll (lists, timeline,
     * leaderboards). Pages without the marker fall back to normal page scroll.
     */
    .base__container--has-sidebar:has(.fill-viewport) {
        height: 100vh;
        overflow: hidden;
    }

    /* Sidebar owns the community identity and primary nav, so the top header is redundant on lg+. */
    .base__container--has-sidebar > .base__header {
        display: none;
    }

    /* 56px (not 64) so the icons sit dead-centre at their natural padding (12px container + 8px
     * link + 8px = the 28px rail centre) — no nudge needed, so they never reposition on collapse. */
    .base__container--sidebar-collapsed .base__sidebar {
        width: 56px;
    }

    .base__container--sidebar-collapsed {
        padding-left: 56px;
    }

    /*
     * Iconification (labels hidden, icon-only chrome, centred icons) is driven by a SEPARATE
     * `--rail-iconified` class, not `--sidebar-collapsed`. On collapse, Alpine adds it ~200ms
     * after the geometry class so the rail visibly narrows over the still-present labels (clipped
     * by overflow:hidden) before they swap out — a genuine close rather than an instant icon-swap
     * followed by a shrink. On expand it's removed immediately so labels return as the rail opens.
     */
    /* Uniform row height in BOTH states. The text label's line box (12.75px × 1.4 ≈ 17.85px) is
     * taller than the 16px icon, so a row is ~34px with its label and ~32px without — when the label
     * is hidden (or an icon-only button swaps in) the row shortens and the icon nudges up. Pinning a
     * min-height that both states share keeps the icon vertically put. */
    .base-sidebar a,
    .base-sidebar button {
        min-height: 2.125rem;
    }

    /* Fade only the text labels out the moment collapse starts (geometry class, immediate) — fast,
     * so the text disappears quickly while the rail keeps closing. Scoped to spans on purpose: the
     * whole-button labels (Store/Financial/switcher) must NOT fade, or their icons would dim to 0
     * before their icon-only replacements appear at the swap, reading as a pop. */
    span.base-sidebar__label {
        transition: opacity .1s ease-in-out;
    }

    .base__container--sidebar-collapsed span.base-sidebar__label {
        opacity: 0;
    }

    /* The collapse chevron isn't a text span, so give it the same fast fade — otherwise it lingers
     * until the 200ms swap and reads as disappearing too late after you've clicked it. */
    .base-sidebar__collapse-toggle {
        transition: opacity .1s ease-in-out;
    }

    .base__container--sidebar-collapsed .base-sidebar__collapse-toggle {
        opacity: 0;
    }

    .base__container--rail-iconified .base-sidebar__label {
        display: none !important;
    }

    /* Collapsed-only chrome — shown only once the rail has iconified (lg+). */
    .base-sidebar__collapsed-only {
        display: none;
    }

    .base__container--rail-iconified .base-sidebar__collapsed-only {
        display: inline-flex;
    }

    /*
     * Sidebar Flowbite tooltips — Popper positions them globally, but we only want them
     * to fire once the rail is iconified (labels are visible otherwise). When the rail
     * is expanded, force the tooltip to stay hidden even if Flowbite tries to show it.
     */
    .base__container--has-sidebar:not(.base__container--rail-iconified) .sidebar-tooltip {
        display: none !important;
    }

    /*
     * Admin chain stretches to viewport height so list cards (and any inner flex-1)
     * extend to the bottom. Only the explicit wrappers in the chain are flexed.
     * Gated on :has(.fill-viewport) so non-opted pages keep normal page scrolling.
     */
    .base__container--has-sidebar:has(.fill-viewport) .base__content-container,
    .base__container--has-sidebar:has(.fill-viewport) .base__content-container > div,
    .base__container--has-sidebar:has(.fill-viewport) .base__content-container main,
    .base__container--has-sidebar:has(.fill-viewport) .communities,
    .base__container--has-sidebar:has(.fill-viewport) .communities__content {
        display: flex;
        flex-direction: column;
        flex: 1 1 0%;
        min-height: 0;
    }

    /*
     * Breathing room between the pill-tab section and the outer card on desktop.
     * Scoped to non-empty section-tabs so pages without tabs don't gain extra top space.
     */
    .base__container--has-sidebar .communities__section-tabs:has(*) {
        margin-bottom: 1rem;
    }

    /*
     * Per-page wrappers inside `.communities__content` typically use `mx-auto max-w-[...]`.
     * In a flex-column parent, auto-margins override stretch and collapse the child to its
     * content width (calendar / dashboard look "mobile-style"). Force 100% width so the
     * wrapper fills the available space — the max-width cap is `none` in admin anyway.
     */
    .base__container--has-sidebar:has(.fill-viewport) .communities__content > * {
        width: 100%;
        min-height: 0;
    }

    /*
     * Lists inside the admin card scroll within the card (not the page). The card and
     * any flex-column wrappers between it and the list need min-height: 0 to allow the
     * scroll surface to shrink-fit the available space. Gated on .fill-viewport (not
     * .common-list-card) so cards that opt out of the list-card min-height — e.g. the
     * timeline calendar — still get the cascade.
     */
    .base__container--has-sidebar .fill-viewport,
    .base__container--has-sidebar .fill-viewport .flex.flex-col {
        min-height: 0;
    }
    .base__container--has-sidebar .common-list {
        overflow-y: auto;
        /* overflow-y:auto makes overflow-x compute to auto (CSS overflow quirk); pin it
           so a two-finger back-swipe navigates instead of scrolling the list sideways. */
        overflow-x: clip;
        /* Contain vertical overscroll only — leave the horizontal axis default so the
           back gesture still reaches the browser. */
        overscroll-behavior-y: contain;
    }
}

/*
 * Mobile fill-viewport — the same contract as the lg+ rules above, minus the sidebar, for
 * pages that mark their card with `fill-viewport-mobile`. Without it a mobile card is sized
 * by its content and stops short of the bottom nav; here the page is locked to the *visible*
 * viewport (dvh, so the browser toolbar counts) and every wrapper down to the card flexes,
 * so the card reaches the nav and the inner list owns the scroll.
 */
@media (max-width: 1023px) {
    /* No `overflow: hidden` on purpose — the flex chain already keeps the content inside the
       viewport, and hiding overflow here would clip the toolbar's Flowbite datepicker popup. */
    .base__container:has(.fill-viewport-mobile) {
        height: 100dvh;
        /* Beats the 100vh floor `.base__container` sets, which would otherwise win over height. */
        min-height: 0;
    }

    /* Row flex (centring the content), so it only needs to be allowed to shrink. */
    .base__container:has(.fill-viewport-mobile) .base__content-container {
        min-height: 0;
    }

    .base__container:has(.fill-viewport-mobile) .base__content-container > div,
    .base__container:has(.fill-viewport-mobile) main,
    .base__container:has(.fill-viewport-mobile) .users,
    .base__container:has(.fill-viewport-mobile) .users__content,
    .base__container:has(.fill-viewport-mobile) .communities,
    .base__container:has(.fill-viewport-mobile) .communities__content {
        display: flex;
        flex-direction: column;
        flex: 1 1 0%;
        min-height: 0;
    }

    /* These wrappers reserve 100px for the fixed nav. The container reserves it below
       instead, so keeping the margin here would just push the card back off the screen. */
    .base__container:has(.fill-viewport-mobile) .users,
    .base__container:has(.fill-viewport-mobile) .communities {
        margin-bottom: 0;
    }

    /* Page wrappers use `mx-auto`, which collapses them to content width in a flex column. */
    .base__container:has(.fill-viewport-mobile) .users__content > *,
    .base__container:has(.fill-viewport-mobile) .communities__content > * {
        width: 100%;
    }

    .base__container:has(.fill-viewport-mobile) .fill-viewport-mobile,
    .base__container:has(.fill-viewport-mobile) .fill-viewport-mobile .flex.flex-col {
        min-height: 0;
    }

    /* The card now takes its height from the viewport, so the list-card floor is dead weight. */
    .base__container:has(.fill-viewport-mobile) .common-list-card {
        min-height: 0;
    }
}

/* The bottom nav is fixed below md, so the content has to stop above it. */
@media (max-width: 767px) {
    .base__container:has(.fill-viewport-mobile) .base__content-container {
        padding-bottom: calc(var(--dim-bottom-nav-height) + env(safe-area-inset-bottom) + 12px);
    }
}

/*
 * Standalone (home screen) PWA: iOS 26.0 resolves dvh against a viewport that still subtracts
 * Safari's toolbar (WebKit 158055568, fixed in 26.1), so the dvh lock ended ~60px short and
 * everything fixed to the viewport — bottom nav, drawer, overlay — floated above the screen
 * bottom. There is no toolbar in standalone, so vh is the true height.
 */
@media (max-width: 1023px) and (display-mode: standalone) {
    .base__container:has(.fill-viewport-mobile) {
        height: 100vh;
    }
}

/*
 * Pin the section tabs below the fixed mobile header. Scoped to <768px because that's
 * where `.base__header` is fixed — above it the header scrolls with the page, so the
 * tabs must too. Sticky works here because every ancestor clips with `overflow-x: clip`
 * (never `hidden`/`auto`), which doesn't create a scroll container.
 */
@media (max-width: 767.98px) {
    /* Fixed rather than sticky: the offset depends on env(safe-area-inset-top), which
       iOS Safari animates while its toolbar collapses during scroll. A sticky bar re-pins
       against that on the content timeline and trembles; a fixed bar shares the viewport
       timeline with the fixed header, so the two move in lockstep. */
    .communities__section-tabs:has(*) {
        position: fixed;
        top: calc(var(--dim-mobile-header-height) + env(safe-area-inset-top, 0px));
        left: 0;
        right: 0;
        z-index: 30;
        background-color: var(--color-olympia-canvas);
        padding-bottom: 8px;
    }

    /* Same 16px side margins and 12px header gap as mobile — the ≥480 utilities would
       otherwise push the pills flush against the viewport edge and the header. The gap
       lives inside the fixed bar (padding, not margin) so its background covers it. */
    .communities__section-tabs:has(*) > div {
        margin-inline: 16px;
    }
    .communities__section-tabs:has(*) {
        padding-top: 12px;
    }
    .communities__section-tabs:has(*) nav {
        margin-top: 0;
    }

    /* Reserve exactly the bar's height so content rests flush against it, as on mobile.
       58px = 12px bar padding-top + 38px pill row (text-xs pills, py-[6px]) + 8px bar
       padding-bottom — update alongside any pill sizing change. */
    .communities__section-tabs:has(*) + .communities__content {
        margin-top: 0;
        padding-top: 58px;
    }

    /* Breadcrumbs and the container's top padding would sit hidden underneath the fixed
       bar (they did under the old JS pinning too) — drop them so no dead gap remains. */
    .base__content-container:has(.communities__section-tabs > *) #desktop-header-wrapper {
        display: none;
    }
    .base__content-container:has(.communities__section-tabs > *) > div {
        padding-top: 0;
    }
}

@media (max-width: 479.98px) {
    /* 54px = the 58px above minus 4px: below 480 the nav's mb-1 collapses into the bar's
       bottom padding instead of adding to it. */
    .communities__section-tabs:has(*) + .communities__content {
        padding-top: 54px;
    }
}

.base__content {
    flex: 1;
    max-width: var(--dim-max-width);
    width: 100%; /* Ensure the content stretches fully */
    overflow-x: auto; /* Enable scrolling on smaller devices */
}

/* --break-tablet-large */
@media (min-width: 240px) {
    .base__content {
        padding: 16px;
        display: block; /* Disable flex on mobile */
    }
}

/* --break-screen-small */
@media (min-width: 768px) {
    .base__header {
        position: initial;
        top: initial;
        width: initial;
        padding-top: initial;
        background-color: initial;
    }

    .base__content-container {
        margin-top: initial;
    }
}

/*
 * Header
 */

.base-header {
    display: flex;
    align-items: center;
    height: var(--dim-mobile-header-height);
    border-bottom: solid 1px var(--color-olympia-canvas-border);
    padding: 6px 16px;
    background-color: var(--color-olympia-canvas);
}

.base-header__button {
    display: flex;
    align-items: center;
    cursor: pointer;
    flex-shrink: 0;
}

.base-header__button--disabled {
    cursor: default;
}

.base-header__button--disabled span {
    display: flex;
    width: 24px;
    height: 24px;
}

.base-header__button[data-position="left"] {
    padding-left: 16px;
}

.base-header__logo {
    display: flex;
    align-items: center;
    width: 100%;
    min-width: 0;
    overflow: hidden;
    font-family: var(--font-sans);
    font-size: 22px;
}

.base-header__logo #logo:not(.hidden) {
    display: inline-flex;
}

.base-header__nav {
    display: none;
}

.base-header__user {
    display: none;
    align-items: center;
    justify-content: flex-end;
    width: 100%;
}


.base-header__user-avatar {
    cursor: pointer;
}

.base-header__user-avatar img {
    display: block;
    width: var(--dim-list-item-image-width);
    height: var(--dim-list-item-image-width);
    border-radius: 50%;
    object-fit: cover;
}

/* --break-screen-tablet */
@media (min-width: 480px) {
    .base-header {
        /* Height stays --dim-mobile-header-height while the header is fixed (<768px):
           the sticky section tabs pin right below that value, so a content-driven height
           would leave a see-through strip between header and tabs. */
        padding: 10px 32px 10px 32px;
    }
}

/* --break-screen-tablet */
@media (min-width: 768px) {
    .base-header {
        height: initial;
        padding: 10px 32px 10px 32px;
    }
}

/* --break-screen-small */
@media (min-width: 768px) {
    .base-header {
        height: initial;
        padding: 10px 32px 10px 32px;
    }

    .base-header__button {
        display: none;
    }

    .base-header__logo {
        justify-content: flex-start;
    }

    .base-header__nav {
        display: flex;
        align-items: center;
        padding-left: 24px;
        font-family: var(--font-sans);
        font-size: var(--text-sm);
    }

    .base-header__nav ul {
        display: flex;
        flex-direction: row;
    }

    .base-header__nav li:not(:first-child) {
        margin-left: 12px;
    }

    .base-header__nav li {
        padding: 8px 14px;
        border-radius: 9999px;
        transition: all 0.15s ease;
    }

    .base-header__nav li:hover,
    .base-header__nav-item--active {
        background-color: var(--color-olympia-canvas-selected);
    }

    .base-header__nav li:hover > a,
    .base-header__nav-item--active > a {
        color: var(--color-olympia-ink-900) !important;
    }

    .base-header__nav li a {
        color: var(--color-olympia-ink-500);
    }

    .base-header__user {
        display: flex;
    }
}

/*
 * Drawers
 */

.base__drawer {
    position: fixed;
    padding-top: env(safe-area-inset-top);
    width: var(--dim-drawer-width);
    height: 100%;
    box-sizing: border-box;
    transition: .25s ease-in-out;
    overflow-x: hidden;
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 60;
    background: var(--color-olympia-canvas);
}

.safe-area-pt {
    padding-top: max(1rem, env(safe-area-inset-top));
}

.safe-area-pb {
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
}

.base__drawer[data-position="left"] {
    left: 0;
    transform: translateX(calc(-1 * var(--dim-drawer-width)));
}

.base__drawer[data-position="right"] {
    right: 0;
    transform: translateX(var(--dim-drawer-width));
}

.base__drawer--open {
    transform: translateX(0) !important;
}

/* >>> Right Drawer Content <<< */

.base-drawer-right {
    margin-top: 8px;
}

.base-drawer-right__avatar {
    display: flex;
}

.base-drawer-right__avatar img {
    display: block;
    width: var(--dim-list-item-image-width);
    height: var(--dim-list-item-image-width);
    margin: 0;
    border-radius: 50%;
    object-fit: cover;
}

/*
 * Footer
 */

.base-footer {
    background-color: var(--color-olympia-canvas);
}

.base-footer__bottom-navigation {
    position: fixed;
    bottom: 0;
    width: 100%;
    padding-bottom: env(safe-area-inset-bottom);
    /* 20, not 10: the nav now renders before the content, so a later z-10 element (WhatsApp
       button, dropdown menus) would win the tie and paint over it. Nothing uses 11–19. */
    z-index: 20;
    background-color: var(--color-olympia-canvas);
    border-top: solid 1px var(--color-olympia-canvas-border);
}

@media (min-width: 768px) {
    .base-footer__bottom-navigation {
        display: none;
    }
}

/*
* Animations
*/
@keyframes rotate-horizontal {
  0% {
    transform: rotateY(0deg);
  }
  50% {
    transform: rotateY(180deg);
  }
  100% {
    transform: rotateY(360deg);
  }
}

.animate-rotate-horizontal {
  animation: rotate-horizontal 1s ease-in-out;
}

/* Cross-fade transition between pages */
@view-transition {
    navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 200ms;
}

/* Standalone skips the cross-fade. iOS opens the page with the viewport 47px short and grows it
   ~20ms after the transition has captured its geometry (probe: pagereveal 158ms, resize 175ms,
   finished 427ms), so the root group animates between two viewport sizes and snaps at the end.
   The incoming page is hidden until the viewport settles anyway (reveal in base.html), so the
   fade only ever showed a blank canvas here. */
@media (display-mode: standalone) {
    @view-transition {
        navigation: none;
    }
}
