/* Bowl4U public site theme — Premium Dark scheme.
   Light values in :root (default); dark values under [data-theme="dark"].
   Source: docs/design/full-project/design-tokens.jsx */

:root {
  --b4u-bg: #F5F7FA;
  --b4u-bg-card: #FFFFFF;
  --b4u-bg-card-hover: #F8FAFC;
  --b4u-bg-elevated: #FFFFFF;
  --b4u-bg-surface: #EFF2F7;
  --b4u-bg-input: #FFFFFF;
  --b4u-border: rgba(0,0,0,0.08);
  --b4u-border-hover: rgba(0,0,0,0.15);
  --b4u-text: #0F172A;
  --b4u-text-secondary: #475569;
  --b4u-text-muted: #94A3B8;
  --b4u-primary: #E03440;
  --b4u-primary-hover: #C62D38;
  --b4u-primary-light: rgba(224,52,64,0.15);
  --b4u-accent: #F97316;
  --b4u-success: #22C55E;
  --b4u-warning: #EAB308;
  --b4u-danger: #EF4444;
  /* Informational blue (notification "move"/change requests). Flipped under
     [data-theme="dark"] below — the light-mode blue is far too dark on a dark surface. */
  --b4u-info: #2563EB;
  /* "Scheduled" blue for registration-opens-soon UI. Intentionally NOT a theme token —
     it echoes the legacy site's opening box and is the same in both themes. */
  --b4u-soon: #3B82F6;

  /* Capacity-meter ramp (PinMeter): green while spots are open → amber → red when nearly
     full. The bars take the vivid site green, but the % label needs a deeper green to stay
     legible at 12px on a white card; the label flips brighter under [data-theme="dark"]. */
  --b4u-meter-open: var(--b4u-success);
  --b4u-meter-open-text: #15803D;
  --b4u-nav-bg: rgba(255,255,255,0.90);
  --b4u-hero-gradient: linear-gradient(135deg, #0B1120 0%, #1A1030 50%, #0B1120 100%);

  /* Archives CTA (events page) — these flip under [data-theme="dark"] below.
     Light: the dark hero gradient with a soft, subtle icon so it reads calm.
     The card is dark in both themes, so its text stays white. */
  --b4u-archive-bg: var(--b4u-hero-gradient);
  --b4u-archive-border: var(--b4u-border);
  --b4u-archive-shadow: 0 14px 34px -16px rgba(0,0,0,0.5);
  --b4u-archive-icon-bg: var(--b4u-primary-light);
  --b4u-archive-icon-fg: var(--b4u-primary);

  --b4u-radius-sm: 8px;
  --b4u-radius-md: 12px;
  --b4u-radius-lg: 14px;
  --b4u-radius-xl: 20px;
  --b4u-maxw: 1280px;

  --b4u-font-body: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;
  --b4u-font-display: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, sans-serif;
}

[data-theme="dark"] {
  --b4u-bg: #0B1120;
  --b4u-bg-card: #141B2D;
  --b4u-bg-card-hover: #1A2340;
  --b4u-bg-elevated: #1E2A45;
  --b4u-bg-surface: #0F1729;
  --b4u-bg-input: #141B2D;
  --b4u-border: rgba(255,255,255,0.08);
  --b4u-border-hover: rgba(255,255,255,0.15);
  --b4u-text: #F1F5F9;
  --b4u-text-secondary: #94A3B8;
  --b4u-text-muted: #64748B;
  --b4u-nav-bg: rgba(11,17,32,0.85);
  --b4u-info: #60A5FA;
  /* Same value as :root — deliberate, so a future theme edit can't orphan it. */
  --b4u-soon: #3B82F6;
  /* The deep light-mode green goes muddy on navy; lift the label to the 400 tint. */
  --b4u-meter-open-text: #4ADE80;

  /* Archives CTA — dark theme makes it pop: a red-tinted gradient, red border + glow,
     and a bold solid-red icon tile. */
  --b4u-archive-bg: linear-gradient(135deg, color-mix(in srgb, var(--b4u-primary) 15%, var(--b4u-bg-elevated)), var(--b4u-bg-elevated) 62%);
  --b4u-archive-border: color-mix(in srgb, var(--b4u-primary) 33%, transparent);
  --b4u-archive-shadow: 0 0 0 1px color-mix(in srgb, var(--b4u-primary) 13%, transparent), 0 16px 40px -18px color-mix(in srgb, var(--b4u-primary) 50%, transparent);
  --b4u-archive-icon-bg: var(--b4u-primary);
  --b4u-archive-icon-fg: #FFFFFF;
}

/* theme.css is only linked from the Blazor host (App.razor), so styling the
   document here affects public Blazor pages only, never the Razor/admin pages.
   Zero the body margin + paint the body so dark mode has no white border. */
html, body {
  margin: 0;
  padding: 0;
}
body {
  background: var(--b4u-bg);
}

/* Scope base styling to the public shell so we never affect Razor/admin pages. */
.b4u-app {
  background: var(--b4u-bg);
  color: var(--b4u-text);
  font-family: var(--b4u-font-body);
  min-height: 100vh;
  transition: background 0.3s, color 0.3s;
}

.b4u-app *,
.b4u-app *::before,
.b4u-app *::after {
  box-sizing: border-box;
}

/* Country flags (CountryFlag → flag-icon span) — round the corners + a subtle ring,
   matching the design's CountryFlag (the flag-icon package renders square flags). */
.b4u-flag {
  border-radius: 3px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08);
}

/* <FocusOnNavigate> focuses the page <h1> on navigation (a11y) — it gets tabindex="-1"
   and (in Chromium) matches :focus-visible, so target that programmatic focus target
   directly. Real interactive controls keep their keyboard focus ring. */
.b4u-app [tabindex="-1"]:focus {
  outline: none;
}
.b4u-app :focus:not(:focus-visible) {
  outline: none;
}

.b4u-container {
  max-width: var(--b4u-maxw);
  margin: 0 auto;
  padding: 0 24px;
}

/* Section sub-heading / muted helper text. Global (non-scoped) so any component
   can use it for sub-titles, empty states, and inline notes. */
.b4u-section-sub { font-size: 15px; color: var(--b4u-text-muted); margin: 0; }

/* Public-page section spacing — GLOBAL so every page (incl. new ones and the shared
   EventsListing) gets vertical rhythm + breathing room before the footer. Pages that
   carry a scoped .b4u-section keep their own values (scoped CSS = higher specificity). */
.b4u-section { padding-top: 16px; padding-bottom: 56px; }
.b4u-section--last { padding-bottom: 80px; }


/* ThemeToggle icon visibility — ALL rules here (global, non-scoped) so there is no
   scoped-vs-global specificity race. Light mode shows moon (→ go dark); dark shows sun. */
.b4u-theme-toggle-light { display: none; }
[data-theme="dark"] .b4u-theme-toggle-dark { display: none; }
[data-theme="dark"] .b4u-theme-toggle-light { display: inline-flex; }

/* Navbar scroll transition — the colour flips that need a [data-theme] ancestor
   live here (global); scoped .razor.css can't host [data-theme] ancestors in this
   project. The base transparent→solid transition + the at-top white logo are in
   Navbar.razor.css. The logo needs no recolouring — its SVG carries its own white
   splash backdrop, so it stays legible on both the dark hero and the light bar. */
/* Light mode, before scroll (nav transparent over the dark hero): light links/icons. */
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-link:not(.is-active) { color: rgba(255, 255, 255, 0.80); }
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-link:not(.is-active):hover { color: #fff; background: rgba(255, 255, 255, 0.12); }
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-burger { color: rgba(255, 255, 255, 0.85); }
/* An OPEN dropdown trigger (lang/profile/notification bell) shows its own light
   background, so it is excluded here (:not(.is-open)) — otherwise its icon/chevron stays
   white → invisible on the light open pill. The theme toggle has no open state.
   EVERY control in .b4u-nav-right must be listed here: its scoped base colour is
   --b4u-text-secondary (#475569 in light mode), which is unreadable on the dark hero.
   The bell was missing and rendered at 2.5:1 against the hero. */
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-right .b4u-theme-toggle,
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-right .b4u-lang:not(.is-open) .b4u-lang-btn,
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-right .b4u-notif:not(.is-open) .b4u-notif-trigger,
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-right .b4u-pm:not(.is-open) .b4u-pm-trigger { color: rgba(255, 255, 255, 0.85); }
/* The theme toggle keeps its white icon over the hero, but its scoped :hover uses the
   light --b4u-bg-card-hover → white icon on a near-white box (invisible). Override the
   hover background with a translucent-white wash, matching the nav-link hover above. */
:root:not([data-theme="dark"]) .b4u-nav:not(.b4u-scrolled):not(.b4u-nav-open) .b4u-nav-right .b4u-theme-toggle:hover { background: rgba(255, 255, 255, 0.12); }

/* Buttons (global so interactive components' plain <button class="b4u-btn"> get styled) */
.b4u-btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    border-radius: 10px;
    font-weight: 600;
    font-family: inherit;
    cursor: pointer;
    text-decoration: none;
    border: 1.5px solid transparent;
    transition: all 0.2s ease;
}
.b4u-btn:hover { transform: translateY(-1px); }
.b4u-btn:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }

.b4u-btn--sm { padding: 8px 16px; font-size: 13px; }
.b4u-btn--md { padding: 11px 24px; font-size: 15px; }
.b4u-btn--lg { padding: 14px 32px; font-size: 16px; }

.b4u-btn--primary {
    background: var(--b4u-primary);
    color: #fff;
    border-color: transparent;
    box-shadow: 0 4px 12px rgba(224,52,64,0.2);
}
.b4u-btn--primary:hover {
    background: var(--b4u-primary-hover);
    box-shadow: 0 8px 24px rgba(224,52,64,0.27);
}
.b4u-btn--outline {
    background: transparent;
    color: var(--b4u-primary);
    border-color: var(--b4u-primary);
}
.b4u-btn--outline:hover { background: var(--b4u-primary-light); }
.b4u-btn--ghost {
    background: transparent;
    color: var(--b4u-text-secondary);
}
.b4u-btn--ghost:hover { background: var(--b4u-bg-card-hover); }
.b4u-btn--secondary {
    background: var(--b4u-bg-card);
    color: var(--b4u-text);
    border-color: var(--b4u-border);
}
.b4u-btn--secondary:hover { background: var(--b4u-bg-card-hover); }

/* Quick "Reset filters" affordance — subtle primary-coloured text+icon button used in
   the events/results filter toolbars. Global so every listing toolbar shares one look. */
.b4u-reset-btn {
    display: inline-flex; align-items: center; gap: 6px;
    background: none; border: none; border-radius: 8px;
    padding: 6px 10px;
    color: var(--b4u-primary); font-size: 13px; font-weight: 600;
    font-family: inherit; cursor: pointer; white-space: nowrap;
    transition: background 0.15s ease;
}
.b4u-reset-btn:hover { background: var(--b4u-primary-light); }

/* Toasts (embedded; matches notifications.jsx) — global so any interactive feature's ToastHost is styled */
.b4u-toast-host {
    position: fixed; top: 80px; right: 20px; z-index: 9999;
    display: flex; flex-direction: column; gap: 10px;
    max-width: min(400px, calc(100vw - 40px));
    pointer-events: none;
}
.b4u-toast {
    display: flex; align-items: flex-start; gap: 12px;
    background: var(--b4u-bg-card);
    border: 1px solid var(--b4u-border);
    border-left: 4px solid var(--b4u-primary);
    border-radius: 14px; padding: 14px 16px;
    box-shadow: 0 8px 32px rgba(0,0,0,.25);
    backdrop-filter: blur(12px);
    pointer-events: auto;
    animation: b4u-toast-in .3s cubic-bezier(.4,0,.2,1);
}
@keyframes b4u-toast-in { from { transform: translateX(120%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
.b4u-toast--success { border-left-color: var(--b4u-success); }
.b4u-toast--error   { border-left-color: #EF4444; }
.b4u-toast--warning { border-left-color: var(--b4u-warning); }
.b4u-toast--info    { border-left-color: #3B82F6; }
.b4u-toast-icon {
    width: 32px; height: 32px; border-radius: 8px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--b4u-primary) 15%, transparent);
    color: var(--b4u-primary);
}
.b4u-toast--success .b4u-toast-icon { background: color-mix(in srgb, var(--b4u-success) 15%, transparent); color: var(--b4u-success); }
.b4u-toast--error   .b4u-toast-icon { background: rgba(239,68,68,.12); color: #EF4444; }
.b4u-toast--warning .b4u-toast-icon { background: color-mix(in srgb, var(--b4u-warning) 15%, transparent); color: var(--b4u-warning); }
.b4u-toast--info    .b4u-toast-icon { background: rgba(59,130,246,.12); color: #3B82F6; }
.b4u-toast-body { flex: 1; min-width: 0; }
.b4u-toast-title { font-weight: 600; font-size: 14px; color: var(--b4u-text); margin-bottom: 2px; }
.b4u-toast-msg { font-size: 14px; font-weight: 500; color: var(--b4u-text); line-height: 1.5; word-break: break-word; }
.b4u-toast-dismiss { background: transparent; border: none; cursor: pointer; padding: 4px; flex-shrink: 0; color: var(--b4u-text-muted); display: flex; }
@media (max-width: 600px) {
    .b4u-toast-host { top: 12px; right: 12px; left: 12px; max-width: none; }
}

/* ── Reconnection UI ──────────────────────────────────────────────────────────
   See docs/superpowers/specs/2026-08-04-blazor-reconnect-ui-design.md.
   Mirrors the toast idiom above on purpose: same card treatment, same shadow, same mobile
   behaviour. Placed top-CENTRE on desktop rather than in the toast slot so a live toast and a
   reconnect pill can never overlap, and stacked above toasts because connection state outranks
   action feedback.

   Visibility is driven ONLY by data-b4u-rc, which /app/reconnect.js owns. Nothing here keys on the
   framework's components-reconnect-* classes: those churn between `show` and `retrying` on every
   attempt, and any animation-delay keyed on them would restart each time and could suppress the
   pill indefinitely.

   The class prefix is b4u-reconnect, NOT b4u-rc — Registrations/RegistrationCard.razor already owns
   b4u-rc-*, and a global .b4u-rc here would turn every card on /my-registrations into a fixed,
   z-index: 10000 pile. The data-b4u-rc attribute and the #b4u-rc-reload id are not classes and do
   not collide, so they keep the short form. */
.b4u-reconnect {
    position: fixed; top: 80px; left: 50%; transform: translateX(-50%);
    z-index: 10000;
    max-width: min(420px, calc(100vw - 40px));
    /* Rendered from App.razor as a direct child of <body>, OUTSIDE .b4u-app — and both the site
       font (.b4u-app) and the box-sizing reset (.b4u-app *) are scoped there on purpose, so the
       public shell never touches Razor/admin pages. Restate both here, as the cookie bar below
       does; without them the pill renders in the UA serif and the card overflows its own slot. */
    font-family: var(--b4u-font-body);
    box-sizing: border-box;
    /* Below 600px this strip sits over the fixed navbar. Let taps on the logo/hamburger through,
       exactly as .b4u-toast-host does; the card re-enables them for its Reload button. */
    pointer-events: none;
}
.b4u-reconnect *,
.b4u-reconnect *::before,
.b4u-reconnect *::after { box-sizing: border-box; }

/* data-b4u-rc is never rendered by the component (see ReconnectOverlay.razor) — it is purely
   JS-owned, so the pre-first-event state is the attribute being ABSENT, not "hidden". Both must
   hide the overlay identically. */
.b4u-reconnect:not([data-b4u-rc]),
.b4u-reconnect[data-b4u-rc="hidden"],
.b4u-reconnect[data-b4u-rc="pending"] { display: none; }

/* Leaving the page: the circuit is only auto-disconnected on `unload`, so the socket close races
   the navigation and the overlay would otherwise flash over a page already on its way out. */
html[data-b4u-leaving] .b4u-reconnect { display: none !important; }

.b4u-reconnect-pill, .b4u-reconnect-card { display: none; }
.b4u-reconnect[data-b4u-rc="pill"] .b4u-reconnect-pill { display: flex; }
.b4u-reconnect[data-b4u-rc="card-offline"] .b4u-reconnect-card,
.b4u-reconnect[data-b4u-rc="card-expired"] .b4u-reconnect-card { display: block; }
.b4u-reconnect[data-b4u-rc="card-offline"] .b4u-reconnect-expired,
.b4u-reconnect[data-b4u-rc="card-expired"] .b4u-reconnect-offline { display: none; }

.b4u-reconnect-pill {
    align-items: center; gap: 10px;
    background: var(--b4u-bg-card);
    border: 1px solid var(--b4u-border);
    border-radius: 999px;
    padding: 10px 18px 10px 14px;
    box-shadow: 0 8px 32px rgba(0,0,0,.25);
    backdrop-filter: blur(12px);
    font-size: 14px; font-weight: 500; color: var(--b4u-text);
    animation: b4u-reconnect-in .25s cubic-bezier(.4,0,.2,1);
}
.b4u-reconnect-spinner {
    width: 14px; height: 14px; flex-shrink: 0; border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--b4u-primary) 25%, transparent);
    border-top-color: var(--b4u-primary);
    animation: b4u-reconnect-spin .7s linear infinite;
}
.b4u-reconnect-card {
    background: var(--b4u-bg-card);
    border: 1px solid var(--b4u-border);
    border-left: 4px solid var(--b4u-primary);
    border-radius: 14px; padding: 16px 18px;
    box-shadow: 0 8px 32px rgba(0,0,0,.25);
    backdrop-filter: blur(12px);
    animation: b4u-reconnect-in .25s cubic-bezier(.4,0,.2,1);
    pointer-events: auto;
}
.b4u-reconnect-card-title { font-weight: 600; font-size: 14px; color: var(--b4u-text); margin-bottom: 4px; }
.b4u-reconnect-card-body { font-size: 14px; font-weight: 500; color: var(--b4u-text-secondary); line-height: 1.5; margin: 0 0 12px; }

@keyframes b4u-reconnect-in { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: none; } }
@keyframes b4u-reconnect-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes b4u-reconnect-spin { to { transform: rotate(360deg); } }

/* Reduced motion drops the MOVEMENT, not the transition: a plain opacity fade still tells the
   visitor something arrived. Only the spinner goes fully static (it is pure motion). */
@media (prefers-reduced-motion: reduce) {
    .b4u-reconnect-pill, .b4u-reconnect-card { animation: b4u-reconnect-fade-in .25s ease; }
    .b4u-reconnect-spinner { animation: none; }
}
@media (max-width: 600px) {
    .b4u-reconnect { top: 12px; right: 12px; left: 12px; transform: none; max-width: none; }
}

/* ── B4uSelect: Syncfusion SfDropDownList themed to the b4u design system ──────
   Global (the popup renders to <body>) + token-based (auto-adapts to dark mode —
   the --b4u-* values flip under [data-theme="dark"]). The classes below are the
   standard Syncfusion dropdown (.e-ddl) classes shared by SfDropDownList/SfComboBox;
   adjust if the rendered DOM in 33.2.8 differs (verify with Playwright). */
.b4u-sf-select.e-input-group,
.b4u-sf-select .e-input-group {
    background: var(--b4u-bg-input);
    border: 1px solid var(--b4u-border);
    border-radius: 10px;
}
/* Base border: beat Syncfusion's bootstrap5 border (its .e-input-group:not(.e-success)…
   selector is 0,5,0) by matching the :not() specificity — theme.css loads after the SF
   theme, so equal specificity resolves in our favour. Otherwise SF's #CED4DA shows through. */
.b4u-sf-select.e-input-group:not(.e-success):not(.e-warning):not(.e-error) { border-color: var(--b4u-border); }
/* Closed control: match the b4u text-input height (40px) + 14px content inset and
   centre the value (Syncfusion's default is ~31px tall with no padding). */
.b4u-sf-select.e-input-group { min-height: 40px; align-items: center; padding: 0 8px 0 14px; }
.b4u-sf-select.e-input-group:not(.e-disabled):hover { border-color: var(--b4u-border-hover); }
.b4u-sf-select.e-input-group.e-input-focus { border-color: var(--b4u-primary); box-shadow: none; }
.b4u-sf-select .e-input,
.b4u-sf-select .e-input-value { color: var(--b4u-text); font-size: 14px; }
.b4u-sf-select.e-input-group .e-input { padding: 0; }
/* Selected value = flag + label: flex-centred, single line with ellipsis (the full
   name still shows in the open dropdown). */
.b4u-sf-select .e-input-value { display: flex; align-items: center; gap: 8px; min-width: 0; }
.b4u-sf-select.e-input-group .b4u-sf-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.b4u-sf-select .e-ddl-icon,
.b4u-sf-select .e-clear-icon { color: var(--b4u-text-muted); }
/* Disabled = read-only gray fill + muted value (matches the disabled .b4u-pf-input). */
.b4u-sf-select.e-disabled { opacity: 1; }
.b4u-sf-select.e-disabled.e-input-group { background: var(--b4u-bg-surface); }
.b4u-sf-select.e-disabled .b4u-sf-label { color: var(--b4u-text-muted); }

/* popup (rendered to <body>, carries the same CssClass) */
.e-popup.b4u-sf-select {
    background: var(--b4u-bg-card);
    border: 1px solid var(--b4u-border);
    border-radius: 12px;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
}
.e-popup.b4u-sf-select .e-list-item {
    color: var(--b4u-text);
    background: transparent;
    display: flex; align-items: center; gap: 8px; padding-left: 10px;
    border-radius: 8px;     /* rounded highlight pill */
    margin: 2px 0;          /* vertical gap only — horizontal margin overflows the list */
}
/* Inset the items horizontally via the list container's padding (no item margin = no scrollbar). */
.e-popup.b4u-sf-select .e-dropdownbase { padding-left: 6px; padding-right: 6px; box-sizing: border-box; }
.e-popup.b4u-sf-select .e-list-item.e-hover,
.e-popup.b4u-sf-select .e-list-item.e-focus { background: var(--b4u-bg-card-hover); }
.e-popup.b4u-sf-select .e-list-item.e-active,
.e-popup.b4u-sf-select .e-list-item.e-active.e-hover {
    background: var(--b4u-primary-light); color: var(--b4u-primary);
}
.e-popup.b4u-sf-select .e-filter-parent { background: transparent; }
.e-popup.b4u-sf-select .e-filter-parent .e-input-group,
.e-popup.b4u-sf-select .e-filter-parent .e-input-group.e-input-focus {
    background: var(--b4u-bg-surface); border: 1px solid var(--b4u-border); border-radius: 8px; box-shadow: none;
}
.e-popup.b4u-sf-select .e-filter-parent .e-input-filter { background: transparent; color: var(--b4u-text); padding: 8px 12px; }
.e-popup.b4u-sf-select .e-nodata { color: var(--b4u-text-muted); }

/* flag + label inside option rows (also rendered in the body popup) */
.b4u-sf-flag {
    width: 22px; height: 16px; border-radius: 3px; background-size: cover;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08); flex-shrink: 0; display: inline-block;
}
.b4u-sf-label { color: inherit; }
/* optional muted secondary text after the label (e.g. a federation name) */
.b4u-sf-sub { color: var(--b4u-text-muted); font-size: 12px; margin-left: 6px; white-space: nowrap; }
.b4u-sf-select.e-input-group .b4u-sf-sub { overflow: hidden; text-overflow: ellipsis; min-width: 0; }

/* Native <input type="date"> calendar icon — the browser default is near-black, which
   clashes with our muted chevrons (and is near-invisible in dark mode). Mute it to match;
   flip it light under dark mode. Global because the dark variant needs a [data-theme]
   ancestor (scoped .razor.css can't host one). */
.b4u-pf-input::-webkit-calendar-picker-indicator { opacity: 0.5; cursor: pointer; }
[data-theme="dark"] .b4u-pf-input::-webkit-calendar-picker-indicator { filter: invert(1); opacity: 0.6; }

/* ── Loading skeletons (b4u-skel) — global so scoped variants share it ── */
:root { --b4u-skel-base: rgba(0, 0, 0, 0.085); --b4u-skel-hi: rgba(255, 255, 255, 0.85); }
[data-theme="dark"] { --b4u-skel-base: rgba(255, 255, 255, 0.10); --b4u-skel-hi: rgba(255, 255, 255, 0.16); }
.b4u-skel { position: relative; overflow: hidden; display: block; flex-shrink: 0; background: var(--b4u-skel-base); }
.b4u-skel::after {
    content: ''; position: absolute; inset: 0;
    background-image: linear-gradient(90deg, transparent 0%, var(--b4u-skel-hi) 50%, transparent 100%);
    background-size: 200% 100%; background-repeat: no-repeat;
    animation: b4uShimmer 1.4s ease-in-out infinite;
}
@keyframes b4uShimmer { 0% { background-position: 150% 0; } 100% { background-position: -150% 0; } }
@keyframes b4uSkelPulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
/* StateBlock illustration animations (7-10 split error). */
@keyframes b4uPulse { 0%, 100% { opacity: 0.55; } 50% { opacity: 1; } }
@keyframes b4uRollBob { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-2px); } }
@keyframes b4uWobble { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(3deg); } }
@keyframes b4uFloatIn { 0% { opacity: 0; transform: translateY(8px); } 100% { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
    .b4u-skel::after { animation: b4uSkelPulse 1.6s ease-in-out infinite; background-image: none; background: var(--b4u-skel-hi); }
}

/* ── Boot shell (b4u-boot) — first-paint placeholder for prerender:false pages ──
   Those pages render nothing server-side, so <main> would ship empty; with .b4u-nav
   position:fixed (no flow height) the footer floats up under the navbar until the
   circuit connects. The shell (Components/Layout/BootSkeleton.razor) holds the height.

   It removes ITSELF: the render-mode boundary emits HTML *comments*, which :has()
   ignores, so the rule below only matches once the island has painted a real ELEMENT
   into <main>. No JS, no interop, no timers.

   The 0.15s delay means a warm-circuit (in-site) navigation swaps content before the
   shimmer is ever visible — only a genuinely slow cold load shows it. Fading opacity
   rather than toggling display keeps the height reserved from the very first frame. */
.b4u-boot { min-height: 100vh; animation: b4uBootIn 0.25s ease 0.15s both; }
@keyframes b4uBootIn { from { opacity: 0; } to { opacity: 1; } }

main:has(> *:not(.b4u-boot)) > .b4u-boot { display: none; }

/* Without :has() the shell could never be removed, so never show it there — those
   engines get the pre-existing behaviour instead of a permanently stuck skeleton. */
@supports not selector(:has(*)) {
    .b4u-boot { display: none; }
}

/* Light-on-dark shimmer while over the hero gradient (.b4u-skel reads these tokens). */
.b4u-boot-hero {
    background: var(--b4u-hero-gradient);
    padding: 120px 24px 60px;
    --b4u-skel-base: rgba(255, 255, 255, 0.13);
    --b4u-skel-hi: rgba(255, 255, 255, 0.26);
}
.b4u-boot-hero-inner { max-width: 540px; margin: 0 auto; }
.b4u-boot-body { padding-top: 40px; padding-bottom: 56px; }
.b4u-boot-bar { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-bottom: 24px; }
.b4u-boot-form { max-width: 720px; margin: 0 auto; display: grid; gap: 18px; }

/* ===========================================================================
   b4u-grid — reusable styled Syncfusion SfGrid (CssClass="b4u-grid").
   NOTE: Syncfusion Blazor SfGrid renders CssClass as a `cssclass` ATTRIBUTE on
   the root .e-grid (NOT into className), so we match it with [cssclass~="b4u-grid"].
   Grid structure renders inline (not a body popup), so these override the
   bootstrap5 theme on source order (App.razor loads bootstrap5 BEFORE theme.css).
   All values are --b4u-* tokens → automatic dark mode.
   Reuse: any SfGrid with CssClass="b4u-grid" inherits this look.
   =========================================================================== */
.e-grid[cssclass~="b4u-grid"] {
    border: 1px solid var(--b4u-border);
    border-radius: 14px;
    overflow: hidden;
    background: var(--b4u-bg-card);
    color: var(--b4u-text);
    font-family: inherit;
}
.e-grid[cssclass~="b4u-grid"] .e-gridheader {
    border-top: 0;
    border-bottom: 1px solid var(--b4u-border);
    background: var(--b4u-bg-surface);
}
.e-grid[cssclass~="b4u-grid"] .e-headercell,
.e-grid[cssclass~="b4u-grid"] .e-detailheadercell {
    background: var(--b4u-bg-surface);
    border-color: var(--b4u-border);
}
.e-grid[cssclass~="b4u-grid"] .e-headercelldiv {
    color: var(--b4u-text-muted);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
}
.e-grid[cssclass~="b4u-grid"] .e-row,
.e-grid[cssclass~="b4u-grid"] .e-row .e-rowcell {
    background: var(--b4u-bg-card);
    color: var(--b4u-text);
    border-color: var(--b4u-border);
    font-size: 14px;
}
/* Row hover: !important + td (not just .e-rowcell) to beat bootstrap5's high-specificity
   hover rules — incl. the selected-row hover (td.e-active.e-rowcell:hover) that otherwise
   turns a hovered row light-grey in dark mode. The `color` keeps plain-text cells (the #
   and Gender columns) readable — bootstrap5's hover forces a dark text colour that would
   vanish on the dark hover bg. */
.e-grid[cssclass~="b4u-grid"] .e-row:hover td,
.e-grid[cssclass~="b4u-grid"] .e-row.e-hover td {
    background: var(--b4u-bg-card-hover) !important;
    color: var(--b4u-text) !important;
}
/* Syncfusion's grid table is border-collapse:separate with a 0.25px border-spacing; those
   sub-pixel inter-cell gaps render as a flickering grid of lines in Firefox (Chromium rounds
   them away). Collapse the spacing to 0 so the cells touch — no gaps, no flicker. */
.e-grid[cssclass~="b4u-grid"] .e-table {
    border-spacing: 0 !important;
}
/* Clean horizontal-only cell borders: a single crisp 1px bottom border on every header + body
   cell, nothing on the other sides. Syncfusion otherwise sets sub-pixel borders on all sides
   that Firefox draws as stray lines (Chromium rounds them to 0). Forcing an integer 1px on one
   side only — like the plain .b4u-sd-table, which Firefox renders fine — kills the artifacts. */
.e-grid[cssclass~="b4u-grid"] .e-gridheader th,
.e-grid[cssclass~="b4u-grid"] .e-gridheader td,
.e-grid[cssclass~="b4u-grid"] .e-content td {
    border-width: 0 0 1px 0 !important;
    border-style: solid !important;
    border-color: var(--b4u-border) !important;
}
.e-grid[cssclass~="b4u-grid"] .e-altrow .e-rowcell {
    background: var(--b4u-bg-card);
}
.e-grid[cssclass~="b4u-grid"] .e-gridheader .e-sortfilter .e-headercelldiv,
.e-grid[cssclass~="b4u-grid"] .e-sortnumber {
    color: var(--b4u-text-muted);
}
.e-grid[cssclass~="b4u-grid"] .e-icon-ascending::before,
.e-grid[cssclass~="b4u-grid"] .e-icon-descending::before {
    color: var(--b4u-primary);
}
.e-grid[cssclass~="b4u-grid"] .e-pager {
    background: var(--b4u-bg-card);
    border-top: 1px solid var(--b4u-border);
    color: var(--b4u-text-muted);
    padding: 10px 14px;
}
/* default-pager fallback (a b4u-grid without a custom pager Template) */
.e-grid[cssclass~="b4u-grid"] .e-pager .e-currentitem,
.e-grid[cssclass~="b4u-grid"] .e-pager .e-numericitem:hover {
    background: var(--b4u-primary);
    color: #fff;
    border-radius: 8px;
}
.e-grid[cssclass~="b4u-grid"] .e-pager .e-numericitem {
    color: var(--b4u-text-muted);
}
/* custom b4u pager ("Showing X–Y of Z" + page buttons), shared by the players and teams
   lists. It has TWO hosts: inside a Syncfusion GridPageSettings <Template>, and standalone
   under the players' mobile card list — so the layout must be defined unscoped. Scoping it
   to `.e-grid .e-pager` left the mobile copy as a bare block: info line stacked above the
   buttons, and `gap` inert so the buttons touched. */
.b4u-pl-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    width: 100%;
}
/* Standalone (mobile cards): the grid host gets its spacing from .e-pager's padding +
   border-top; outside it the pager would sit 8px under the last card. */
.b4u-pl-pager.is-standalone { margin-top: 8px; }
.b4u-pl-pager-info { color: var(--b4u-text-muted); font-size: 13px; }
.b4u-pl-pager-info b { color: var(--b4u-text-secondary); font-weight: 600; }
.b4u-pl-pager-btns { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; }
/* Phones: stack and centre, echoing the centred <Pagination> on the other pages. The
   side-by-side layout has to wrap below ~430px anyway, and space-between then left-aligns
   both rows — ragged, and dependent on how long the translated info line happens to be.
   Stacking makes the two rows deliberate at every phone width and in every locale.
   Not gated on .is-standalone: both the players and teams tabs render the standalone pager
   under their mobile card list, and both should read the same on the same phone.
   MUST follow the .b4u-pl-pager base rule above — equal specificity, so source order wins. */
@media (max-width: 640px) {
    .b4u-pl-pager { flex-direction: column; align-items: center; gap: 10px; }
    .b4u-pl-pager-info { text-align: center; }
}
/* Syncfusion wraps the Template in an unstyled <div> and forces every div inside the pager
   inline (`.e-pager div,.e-pager a { display: inline }`), which collapses both flex containers
   and makes `gap` inert. `display` is the ONLY property it touches, so re-asserting that alone
   at higher specificity is enough — the rest comes from the base rules above. */
.e-grid[cssclass~="b4u-grid"] .e-pager > div:has(> .b4u-pl-pager) {
    display: block;
    width: 100%;
}
.e-grid[cssclass~="b4u-grid"] .e-pager .b4u-pl-pager { display: flex; }
.e-grid[cssclass~="b4u-grid"] .e-pager .b4u-pl-pager-btns { display: inline-flex; }
.b4u-pl-pgbtn {
    min-width: 36px;
    height: 36px;
    padding: 0 8px;
    border-radius: 10px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
    font-weight: 600;
    font-family: inherit;
    line-height: 1;
    cursor: pointer;
    color: var(--b4u-text-secondary);
    border: 1px solid var(--b4u-border);
    background: var(--b4u-bg-card);
    transition: background .15s, color .15s, border-color .15s;
}
.b4u-pl-pgbtn:hover:not(:disabled):not(.is-active) { background: var(--b4u-bg-surface); }
.b4u-pl-pgbtn.is-active { background: var(--b4u-primary); color: #fff; border-color: var(--b4u-primary); }
.b4u-pl-pgbtn:disabled { opacity: .45; cursor: default; }
.b4u-pl-pgflip { display: inline-flex; transform: rotate(180deg); }
.b4u-pl-pgellipsis { color: var(--b4u-text-muted); min-width: 20px; text-align: center; font-size: 14px; align-self: center; }

/* ===========================================================================
   Shared <Pagination> component (events + archives grids). Its page items are
   emitted via RenderTreeBuilder, which never receives the scoped-CSS attribute,
   so the styling must live globally — otherwise the <a> links render as raw
   browser hyperlinks. Mirrors the .b4u-pl-pgbtn pager above + the design
   prototype (36px square, radius 10, gap 6).
   =========================================================================== */
.b4u-pagination { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 32px; flex-wrap: wrap; }
.b4u-page {
    min-width: 36px; height: 36px; padding: 0 8px; border-radius: 10px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 14px; font-weight: 600; font-family: inherit; line-height: 1;
    text-decoration: none; cursor: pointer;
    color: var(--b4u-text-secondary); border: 1px solid var(--b4u-border); background: var(--b4u-bg-card);
    transition: background .15s, color .15s, border-color .15s;
}
.b4u-page:hover { background: var(--b4u-bg-surface); color: var(--b4u-text-secondary); text-decoration: none; }
.b4u-page.is-active { background: var(--b4u-primary); color: #fff; border-color: var(--b4u-primary); }
.b4u-page-arrow { font-size: 18px; }
.b4u-page-ellipsis { color: var(--b4u-text-muted); min-width: 20px; text-align: center; font-size: 14px; align-self: center; }
.e-grid[cssclass~="b4u-grid"] .e-emptyrow .e-rowcell {
    color: var(--b4u-text-muted);
}
/* players grid: centre the Country (col 3) + Gender (col 4) columns
   (the # column, col 1, is already centred via its TextAlign attribute). */
.e-grid[cssclass~="b4u-pl-grid"] .e-rowcell:nth-child(3),
.e-grid[cssclass~="b4u-pl-grid"] .e-rowcell:nth-child(4) {
    text-align: center;
}
.e-grid[cssclass~="b4u-pl-grid"] .e-headercell:nth-child(3) .e-headercelldiv,
.e-grid[cssclass~="b4u-pl-grid"] .e-headercell:nth-child(4) .e-headercelldiv {
    text-align: center;
}

/* ===========================================================================
   ApexCharts tooltip (EventStatsSection registration-activity chart). ApexCharts
   renders the tooltip with its default light theme (white bg) but the text
   inherits the page's --b4u-text, which is light in dark mode → white-on-white
   and unreadable. Re-theme with b4u tokens so it reads in BOTH light and dark.
   Global (not scoped) because the tooltip DOM lives outside the component subtree
   — but it IS inside the themed [data-theme] tree, so var() resolves correctly.
   =========================================================================== */
.apexcharts-tooltip.apexcharts-theme-light {
    background: var(--b4u-bg-card) !important;
    border: 1px solid var(--b4u-border) !important;
    color: var(--b4u-text) !important;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.22) !important;
}
.apexcharts-tooltip.apexcharts-theme-light .apexcharts-tooltip-title {
    background: var(--b4u-bg-card-hover) !important;
    border-bottom: 1px solid var(--b4u-border) !important;
    color: var(--b4u-text) !important;
    font-weight: 600 !important;
}
.apexcharts-tooltip-text,
.apexcharts-tooltip-text-y-label,
.apexcharts-tooltip-text-y-value {
    color: var(--b4u-text) !important;
}
/* x-axis crosshair label (the date pill under the cursor) */
.apexcharts-xaxistooltip.apexcharts-theme-light {
    background: var(--b4u-bg-card) !important;
    border: 1px solid var(--b4u-border) !important;
    color: var(--b4u-text) !important;
}
.apexcharts-xaxistooltip.apexcharts-theme-light::after { border-bottom-color: var(--b4u-bg-card) !important; }
.apexcharts-xaxistooltip.apexcharts-theme-light::before { border-bottom-color: var(--b4u-border) !important; }

/* ===========================================================================
   teams grid (b4u-tm-grid) — DetailTemplate row-expand styling + centering.
   A DetailTemplate inserts a detail-expand column FIRST, so data columns shift
   one right: Country becomes col 4. The expand chevron + detail row are
   Syncfusion DOM (not in the component's scoped subtree) → themed here.
   =========================================================================== */
.e-grid[cssclass~="b4u-tm-grid"] .e-rowcell:nth-child(4) {
    text-align: center;
}
.e-grid[cssclass~="b4u-tm-grid"] .e-headercell:nth-child(4) .e-headercelldiv {
    text-align: center;
}
/* detail-expand column: the chevron toggle + indent cells */
.e-grid[cssclass~="b4u-grid"] .e-detailrowcollapse,
.e-grid[cssclass~="b4u-grid"] .e-detailrowexpand,
.e-grid[cssclass~="b4u-grid"] .e-detailindentcell {
    background: var(--b4u-bg-card);
    border-color: var(--b4u-border);
}
.e-grid[cssclass~="b4u-grid"] .e-row:hover .e-detailrowcollapse,
.e-grid[cssclass~="b4u-grid"] .e-row:hover .e-detailrowexpand,
.e-grid[cssclass~="b4u-grid"] .e-row.e-hover .e-detailrowcollapse,
.e-grid[cssclass~="b4u-grid"] .e-row.e-hover .e-detailrowexpand {
    background: var(--b4u-bg-card-hover);
}
.e-grid[cssclass~="b4u-grid"] .e-icon-grightarrow::before,
.e-grid[cssclass~="b4u-grid"] .e-icon-gdownarrow::before {
    color: var(--b4u-text-muted);
}
/* the expanded detail row holds the roster (component renders inside .e-detailcell) */
.e-grid[cssclass~="b4u-grid"] .e-detailrow .e-detailcell,
.e-grid[cssclass~="b4u-grid"] .e-detailrow .e-detailindentcell {
    background: var(--b4u-bg-surface);
    border-color: var(--b4u-border);
}
.e-grid[cssclass~="b4u-grid"] .e-detailrow .e-detailcell {
    padding: 0;
}
/* Selected / active row cells: the bootstrap5 default selection background is light
   and looks wrong in dark mode — keep them on the dark hover token instead. The
   [cssclass~] attribute selector outranks Syncfusion's own .e-selectionbackground rule. */
.e-grid[cssclass~="b4u-grid"] .e-row .e-rowcell.e-selectionbackground,
.e-grid[cssclass~="b4u-grid"] .e-row .e-rowcell.e-active,
.e-grid[cssclass~="b4u-grid"] td.e-rowcell.e-selectionbackground,
.e-grid[cssclass~="b4u-grid"] td.e-active,
.e-grid[cssclass~="b4u-grid"] .e-detailrowcollapse.e-selectionbackground,
.e-grid[cssclass~="b4u-grid"] .e-detailrowexpand.e-selectionbackground {
    background: var(--b4u-bg-card-hover);
    color: var(--b4u-text);
}
/* Remove the cell-focus overlay (Syncfusion's bootstrap-primary blue inset box-shadow
   shown when a grid cell is clicked/focused). bootstrap5 applies it via a very
   high-specificity `.e-grid.sf-grid :is(… td.e-rowcell …):is(:focus, :focus-visible)`
   rule, so !important is required to override it. */
.e-grid[cssclass~="b4u-grid"] .e-rowcell:focus,
.e-grid[cssclass~="b4u-grid"] .e-rowcell:focus-visible,
.e-grid[cssclass~="b4u-grid"] .e-rowcell.e-focus,
.e-grid[cssclass~="b4u-grid"] .e-rowcell.e-focused,
.e-grid[cssclass~="b4u-grid"] .e-detailrowcollapse:focus,
.e-grid[cssclass~="b4u-grid"] .e-detailrowcollapse:focus-visible,
.e-grid[cssclass~="b4u-grid"] .e-detailrowexpand:focus,
.e-grid[cssclass~="b4u-grid"] .e-detailrowexpand:focus-visible,
.e-grid[cssclass~="b4u-grid"] .e-detailrowcollapse.e-focused,
.e-grid[cssclass~="b4u-grid"] .e-detailrowexpand.e-focused {
    box-shadow: none !important;
    outline: none !important;
}

/* ===== Cookie consent bar (slide-up; built by cookie-consent.js) ===== */
.b4u-cookie { position: fixed; left: 0; right: 0; bottom: 0; z-index: 1000;
    display: flex; justify-content: center; padding: 0 16px 16px; pointer-events: none;
    /* Appended to <body> (outside .b4u-app), so set the font explicitly. */
    font-family: var(--b4u-font-body); }
.b4u-cookie-card { pointer-events: auto; width: 100%; max-width: 760px;
    background: var(--b4u-bg-card); border: 1px solid var(--b4u-border); border-radius: 18px;
    box-shadow: 0 24px 60px rgba(0,0,0,.35); overflow: hidden;
    transform: translateY(130%); opacity: 0;
    transition: transform .34s cubic-bezier(.22,1,.36,1), opacity .34s ease; }
.b4u-cookie.is-shown .b4u-cookie-card { transform: none; opacity: 1; }
.b4u-cookie-accent { height: 4px; background: var(--b4u-primary); }
.b4u-cookie-body { display: flex; align-items: flex-start; gap: 14px; padding: 20px 24px 22px; flex-wrap: wrap; }
.b4u-cookie-icon { flex-shrink: 0; width: 44px; height: 44px; border-radius: 12px;
    display: inline-flex; align-items: center; justify-content: center;
    background: color-mix(in srgb, var(--b4u-primary) 14%, transparent); color: var(--b4u-primary); }
.b4u-cookie-text { flex: 1; min-width: 240px; }
.b4u-cookie-text h3 { margin: 0; font-size: 17px; font-weight: 700; color: var(--b4u-text); letter-spacing: -0.01em; }
.b4u-cookie-text p { margin: 6px 0 0; font-size: 13.5px; line-height: 1.6; color: var(--b4u-text-muted); }
.b4u-cookie-text p a { color: var(--b4u-primary); font-weight: 600; text-decoration: none; white-space: nowrap; }
.b4u-cookie-text p a:hover { text-decoration: underline; }
.b4u-cookie-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-left: auto; }
.b4u-cookie-btn { padding: 10px 18px; font-size: 14px; font-weight: 600; font-family: inherit;
    border-radius: 10px; cursor: pointer; border: 1px solid transparent; white-space: nowrap; transition: filter .15s, background .15s; }
.b4u-cookie-btn-primary { background: var(--b4u-primary); color: #fff; }
.b4u-cookie-btn-primary:hover { filter: brightness(1.07); }
.b4u-cookie-btn-ghost { background: transparent; border-color: var(--b4u-border); color: var(--b4u-text); }
.b4u-cookie-btn-ghost:hover { background: color-mix(in srgb, var(--b4u-text) 8%, transparent); }
@media (max-width: 640px) {
    .b4u-cookie { padding: 0; }
    .b4u-cookie-card { border-radius: 16px 16px 0 0; max-width: none; }
    .b4u-cookie-actions { width: 100%; }
    .b4u-cookie-btn { flex: 1; }
}

/* Federations grid */
.b4u-fedgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(320px, 100%), 1fr)); gap: 20px; }

/* ── Scroll reveal ─────────────────────────────────────────────────────────
   Gated on b4u-reveal-ready (set by scroll-reveal.js) so content is never
   hidden without JS, and on :not(.b4u-reveal-reduced) so reduced-motion users
   get the final state instantly. [data-reveal] fades + slides; [data-inview]
   is a class-only trigger for component-internal animations. */
html.b4u-reveal-ready:not(.b4u-reveal-reduced) [data-reveal] {
  opacity: 0;
  transform: translateY(22px);
  transition: opacity .6s ease, transform .7s cubic-bezier(.22, 1, .36, 1);
  transition-delay: var(--b4u-reveal-delay, 0ms);
  will-change: opacity, transform;
}
html.b4u-reveal-ready:not(.b4u-reveal-reduced) [data-reveal].is-in {
  opacity: 1;
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1 !important; transform: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------------------
   Image fallbacks — see /app/image-fallback.js.
   A broken <img> is hidden so the fallback element beside it can show through.
   !important because per-component rules set `display` on these images.
   --------------------------------------------------------------------------- */
img[data-b4u-fallback][hidden] { display: none !important; }

/* The event-detail page mounts a FIXED bottom tab bar at phone width, which would otherwise sit
   on top of the footer. Key the clearance off the bar's own presence (`:has()`) so it applies on
   exactly the page that has one, with no layout parameter to thread through.

   box-sizing is explicit because the `.b4u-app *` reset covers descendants, not `.b4u-app` itself
   — without it this padding stacks on min-height: 100vh and produces a phantom scrollbar. */
@media (max-width: 640px) {
    .b4u-app:has(.b4u-ed-bottomnav) {
        box-sizing: border-box;
        padding-bottom: calc(62px + env(safe-area-inset-bottom));
    }
}
