/* Entity sticky hero — wrapper geometry for team / league / player /
   match detail pages.

   **B.2 sticky-only (2026-05-16, all entity pages):**
     - `.app__sticky-header` is opted out of `position: sticky` for every
       entity page (see body-class override at the bottom of this file —
       the `display: contents` trick).
     - `.hero-nav` is `position: sticky; top: 0` and stays pinned with
       its slim title / actions. Transparent at scroll=0 (the body
       gradient reads through it); opaque + backdrop-blurred once
       `.is-stuck` lands.
     - `.entity-hero-expanded` scrolls away with the page naturally —
       no transition, no `.compact` collapse, no JS-driven height.
     - `.entity-tabs-wrapper` (team / league / player) /
       `.match-tabs-wrapper` (match) is `position: sticky;
       top: var(--hero-nav-height)` and pins just below the slim
       hero-nav once the expanded leaves the viewport.
     - Match-specific twist: the slim nav has a `.hero-nav__match-title`
       (crests + score + mini status) hidden by default and faded in
       when `.is-stuck` activates, so the user keeps the home/score/
       away context even after the big hero scrolls away.
     - Result: native CSS sticky cascade, zero JS during scroll, no
       mobile reflow jank. Mirrors industry-standard sticky header pattern.

   The legacy match-detail `.compact` collapse (`grid-template-rows: 1fr
   ↔ 0fr` on `.entity-sticky.compact`, driven by
   `entity_hero_sticky_controller`) was removed in the same pass — the
   match nav now uses the same `hero-nav-stuck` controller as the other
   entity pages. */

@layer components {
  .entity-sticky {
    background: var(--color-bg);
  }

  /* `.entity-hero` wraps the hero nav + expanded block (tabs are a
     sibling OUTSIDE this wrapper). It's the gradient host on the match
     hero (where the tint must read through the transparent nav). For
     team / league / player the gradient is scoped INSIDE the expanded
     so it scrolls away with the big hero. */
  .entity-hero {
    position: relative;
    isolation: isolate;
  }

  /* Direct children sit above the gradient. `isolation: isolate` on
     `.entity-hero` establishes a local stacking context so any ::before
     layer doesn't compete with anything else on the page. */
  .entity-hero > * {
    position: relative;
    z-index: 1;
  }

  .entity-hero-expanded {
    /* B.2: a normal block. No collapse, no transition. */
    display: block;
  }

  .entity-hero-expanded__inner {
    min-height: 0;
  }
}

/* B.2 sticky cascade for team / league / player / match. Lives in
   `@layer pages` so it wins against `pages/team-detail.css` /
   `pages/match-detail.css` (same specificity, later layer rule wins —
   but `body.<page>` selectors have higher specificity so we'd win
   regardless).

   Containing-block trick: `position: sticky` sticks within its closest
   ancestor's bounding box, not the viewport. If `.hero-nav` is sticky
   inside `.entity-hero` (~313 px tall), it unsticks once the user
   scrolls past `.entity-hero`. To make hero-nav stick through the
   entire page, its containing block must extend the full page height.

   Solution: `display: contents` on the wrappers (`.app__sticky-header`
   in layout.css, plus `.entity-sticky` and `.entity-hero` here). The
   wrappers stay in the DOM (templates unchanged) but vanish from the
   layout tree, so their children behave as direct children of `.app`
   (`min-height: 100dvh`). The sticky `.hero-nav` and tabs-wrapper now
   stick to the viewport through the entire scrollable page.

   Zero JS during scroll. Pure CSS. */
@layer pages {
  /* `display: contents` flattens these wrappers so their children
     stick to the viewport through the full page. `.entity-hero` is
     kept as a REAL box for player AND match because both host
     positioned decoration that needs a real containing block:
       - player: `.player-hero__crest` (absolute) + `data-shirt-number`
         `::after` watermark.
       - match: `.entity-hero.match-hero::before` (home/away split
         radial) + `.match-bg-shield` images.
     For those two the hero-nav is rendered OUTSIDE `.entity-hero` as
     a sibling so it still sticks full-page through the flattened
     `.entity-sticky`. Team and league have no such decoration, so
     keeping their `.entity-hero` flat keeps the original e55e28db
     look untouched. */
  body.team-page .entity-sticky,
  body.league-page .entity-sticky,
  body.player-page .entity-sticky,
  body.match-page .entity-sticky,
  body.team-page .entity-hero,
  body.league-page .entity-hero {
    display: contents;
  }

  /* Single page-level gradient that covers BOTH the hero-nav area and
     the big hero. Painted via `body::before` so the transparent sticky
     hero-nav reads the team tint through it; once the user scrolls
     past ~400 px the gradient scrolls out of view naturally and the
     `is-stuck` toggle (see below) gives the nav an opaque chrome so
     scrolling content doesn't bleed through.

     Match uses its own per-team split gradient on `.entity-hero.match-hero::before`
     (see match-detail.css) — the page-level gradient here is the
     generic entity backdrop (team-page / league-page / player-page). */
  body.team-page,
  body.league-page,
  body.player-page,
  body.match-page {
    position: relative;
  }

  /* PAGE-WIDE TEAM WASH (body::before): single 420px radial + linear
     that covers the hero-nav AND the big hero behind it. The team-
     color vars come pre-tinted with low alpha + low lightness so the
     wash has to be wide to read on the dark surface. One layer keeps
     the look simple — adding a second layer on `.entity-hero-expanded`
     ended up biasing the colour pattern away from the e55e28db look
     the user signed off on. */
  body.team-page::before,
  body.league-page::before,
  body.player-page::before {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 420px;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(
        ellipse 90% 70% at 50% 15%,
        var(--team-color-1, rgb(0 255 135 / 8%)) 0%,
        var(--team-color-2, rgb(0 255 135 / 3%)) 50%,
        transparent 80%
      ),
      linear-gradient(180deg, var(--team-color-2, rgb(0 255 135 / 3%)) 0%, transparent 60%);
  }


  /* Sentinel hidden in normal flow — observed by `hero-nav-stuck`
     controller to flip the `.is-stuck` class on the nav. 1px tall,
     negative margin nets to zero so layout is unaffected. */
  body.team-page .hero-nav-stuck-sentinel,
  body.league-page .hero-nav-stuck-sentinel,
  body.player-page .hero-nav-stuck-sentinel,
  body.match-page .hero-nav-stuck-sentinel {
    height: 1px;
    margin-bottom: -1px;
    pointer-events: none;
  }

  /* Hero-nav pins at the very top. Transparent at scroll=0 so the
     body gradient reads through it; `.is-stuck` lands once the page
     scrolls past the sentinel (see `hero_nav_stuck_controller.js`). */
  body.team-page .hero-nav,
  body.league-page .hero-nav,
  body.player-page .hero-nav,
  body.match-page .hero-nav {
    position: sticky;
    top: 0;
    z-index: 20;
    background: transparent;
    transition:
      background-color 200ms ease,
      backdrop-filter 200ms ease;
  }

  body.team-page .hero-nav.is-stuck,
  body.league-page .hero-nav.is-stuck,
  body.player-page .hero-nav.is-stuck,
  body.match-page .hero-nav.is-stuck {
    background: color-mix(in oklab, var(--color-bg) 88%, transparent);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
  }

  /* Player / match override — nav becomes a fixed transparent
     overlay over the top of the page so the entity-hero's own
     gradient (player's 4-radial / match's home-away split, both
     painted INSIDE the hero box) can extend up into the nav strip.
     The hero box compensates with `padding-top: var(--hero-nav-
     height)` so visible content (crest watermark, score section,
     photo, name) stays where it was visually. Team and league
     don't need this — their gradient lives on body::before so the
     sticky nav already reads it through. */
  body.player-page .hero-nav,
  body.match-page .hero-nav {
    position: fixed;
    left: 0;
    right: 0;
  }

  body.player-page .player-hero.entity-hero,
  body.match-page .entity-hero.match-hero {
    padding-top: var(--hero-nav-height, 56px);
  }

  /* Tabs pin directly below the slim hero-nav. Team / league / player
     use `.entity-tabs-wrapper`; match uses `.match-tabs-wrapper`. */
  body.team-page .entity-tabs-wrapper,
  body.league-page .entity-tabs-wrapper,
  body.player-page .entity-tabs-wrapper,
  body.match-page .match-tabs-wrapper {
    position: sticky;
    top: var(--hero-nav-height, 56px);
    z-index: 10;
    background: var(--color-bg);
  }

  /* Modal/bottom-sheet open → drop the sticky chrome BELOW the page
     content so any open sheet renders on top of it. The sheets are
     `position: fixed; z-index: 9998/9999` (select-modal.css), but a
     sheet authored deep inside a content column that carries its own
     stacking context (e.g. a `z-index: 1` wrapper) can't out-paint the
     sticky tabs (`z-index: 10`) / hero-nav (`z-index: 20`) with raw
     z-index alone — the high value is confined to the trapped context.
     Lowering the chrome to `z-index: 0` while a sheet is open lets the
     content column win the sibling comparison so the sheet paints over
     the nav on every viewport. `body.dismissable-scroll-locked` is the
     refcounted "a sheet is open" signal toggled by `lib/dismissable.js`
     (set on first open, cleared on last close), so this only applies
     while a sheet is up — normal scroll keeps the sticky chrome intact.
     `pointer-events: none` guards the rare sliver from intercepting
     taps meant for the sheet. */
  body.dismissable-scroll-locked.team-page .entity-tabs-wrapper,
  body.dismissable-scroll-locked.league-page .entity-tabs-wrapper,
  body.dismissable-scroll-locked.player-page .entity-tabs-wrapper,
  body.dismissable-scroll-locked.match-page .match-tabs-wrapper,
  body.dismissable-scroll-locked.team-page .hero-nav,
  body.dismissable-scroll-locked.league-page .hero-nav,
  body.dismissable-scroll-locked.player-page .hero-nav,
  body.dismissable-scroll-locked.match-page .hero-nav {
    z-index: 0;
    pointer-events: none;
  }
}
