/* League home page (templates/leagues/league_home.html) - page-specific
   classes layered on top of static/css/base.css's shared .hl-* components
   and tokens, same pattern as landing.css/demo.css (docs/color-palette.md).
   Visual language adapted from reference/preview/dashboard.html (season
   hero card, stat strip, upcoming games, announcement feed) onto this
   project's own component system rather than copied wholesale - the
   mockup's generic "Core Features"/"Create your league" marketing blocks
   are left to the landing page, since they don't fit a specific league's
   own public page. Phase 10: the original quick-action tiles and mini
   standings widget were dropped for a League-at-a-Glance stat strip plus
   a dedicated Upcoming Games widget - Standings/Schedule/Players are
   already one click away via leagues/_public_nav.html's sidebar/bottom
   nav on every public league page. Phase 11: a standings snapshot is
   back as its own new section (not a revival of the old Phase 10
   widget), alongside three genuinely new sections - Top Performers
   (stat leaders), a Last Result card in the Upcoming Games column, and
   Recent Awards - confirmed with the user before adding. */

/* ─── Masthead (league identity hero) ─────────────────────────────────
   Replaces the plain .hl-header-band (still used as-is by
   teams/team_detail.html - not touched here) with a dedicated, more
   "official page" treatment: a bigger avatar/monogram, the league name at
   display size, and its location/founded-year/track-record facts as
   pills instead of a single muted caption line. `.hl-avatar`/
   `.hl-avatar-placeholder[data-accent]` (base.css) are reused for the
   logo/monogram itself - `.lh-masthead-avatar` only overrides their size,
   same layering `.hl-avatar--sm` already uses. */
.lh-masthead {
  position: relative;
  overflow: hidden;
  background: linear-gradient(135deg, var(--orange-deep) 0%, var(--bg-card) 66%);
  border-radius: 16px;
  padding: var(--space-5) var(--space-6);
  margin-bottom: var(--space-5);
  box-shadow: 0 24px 48px -28px rgba(0, 0, 0, 0.6);
}

/* Faint dot-grid, masked to a diagonal fade so it only shows through the
   gradient's brighter orange corner (matches the Playbook/mockup design's
   masthead texture) - purely decorative, sits behind
   .lh-masthead-glow/-row/-body in paint order since none of them set a
   z-index and this is the earliest-painted content (a ::before). */
.lh-masthead::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: radial-gradient(rgba(245, 241, 236, 0.18) 1px, transparent 1px);
  background-size: 15px 15px;
  opacity: 0.28;
  mask-image: linear-gradient(135deg, #000 0%, transparent 58%);
  -webkit-mask-image: linear-gradient(135deg, #000 0%, transparent 58%);
  pointer-events: none;
}

.lh-masthead-glow {
  position: absolute;
  top: -70px;
  right: -70px;
  width: 240px;
  height: 240px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--primary-rgb), 0.18), transparent 70%);
  pointer-events: none;
}

.lh-masthead-row {
  position: relative;
  display: flex;
  /* flex-start, not center: .lh-masthead-col's own actions-row sits
     above its body text (see that rule's comment) - centering this row
     would leave the logo floating lower than the column's own content
     starts, undoing the point of keeping the logo flush with the top. */
  align-items: flex-start;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* Everything beside the logo (templates/leagues/league_home.html) - the
   share buttons' own row plus the eyebrow/title/meta below it, both
   inside one flex column so the logo (a sibling in .lh-masthead-row,
   above) stays flush with the top of the row regardless of how much
   vertical space the buttons take. */
.lh-masthead-col {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* Right-aligned, sits above .lh-masthead-body inside the same column
   rather than its own full-width row above the whole masthead (that
   pushed the logo down too, leaving a big gap between the card's top
   edge and the logo that the tight left-edge gap didn't have) and rather
   than an absolutely-positioned corner overlay (that overlapped the
   eyebrow text - see the removed padding-right rules' own git history). */
.lh-masthead-actions-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-2);
}

/* Phase 19 (user feedback: "make the logo much more visible") - up from
   92px to 128px, and from a faint outer shadow to a real two-layer ring
   (a thin cutout matching the card background, then a solid brand-orange
   ring outside it) so the logo reads as the clear focal point of the
   card rather than one item among several of similar visual weight. Ring
   shadow moved to .lh-masthead-avatar-wrap in Phase 23 (see that rule's
   own comment) - this element itself is just the image/placeholder
   filling the wrapper now. Bumped again, 128px -> 152px, once the share
   buttons moved into their own top row (above) and stopped needing
   .lh-masthead-eyebrow's padding-right reservation - that freed-up width
   goes to the logo instead, per the user's own request. */
.lh-masthead-avatar-wrap {
  position: relative;
  flex-shrink: 0;
  width: 152px;
  height: 152px;
  border-radius: 28px;
  overflow: hidden;
  box-shadow:
    0 0 0 4px var(--bg-card),
    0 0 0 6px rgba(var(--primary-rgb), 0.45),
    0 22px 44px -22px rgba(0, 0, 0, 0.85);
}

/* Diagonal shine, clipped to the wrapper's rounded corners - loops every
   6s once revealed (user request: "make the shining logo occur every 5
   or 10 seconds", split the difference) rather than the original
   one-and-done sweep. The keyframes do the actual sweep in the first
   ~1.1s of each cycle, then hold off-screen for the rest of it, so it
   reads as "a shine passes every few seconds," not a constant animation. */
.lh-masthead-avatar-wrap::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, transparent 35%, rgba(255, 255, 255, 0.4) 50%, transparent 65%);
  transform: translateX(-130%);
  pointer-events: none;
}

html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-masthead-avatar-wrap::after {
  animation: lh-avatar-shine 6s ease-in-out 0.2s infinite;
}

@keyframes lh-avatar-shine {
  0% { transform: translateX(-130%); }
  18% { transform: translateX(130%); }
  100% { transform: translateX(130%); }
}

.lh-masthead-avatar {
  /* No `display` override - the <img> case wants its default inline
     (percentage width/height still applies fine, it's a replaced
     element) and the no-logo placeholder <div> needs to keep
     .hl-avatar-placeholder's own `display: inline-flex` for its initials
     to stay centered; overriding to `block` here would break that. */
  width: 100%;
  height: 100%;
  border-radius: inherit;
  font-size: 44px;
}

.lh-masthead-body {
  position: relative;
}

.lh-masthead-eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 6px;
  margin: 0 0 4px;
}

/* Share button (core/_share_button.html) - ghost icon buttons (no filled
   pill behind them) so they read as quiet corner controls against the
   masthead's own gradient card rather than a second "tab" competing with
   it. Lives in .lh-masthead-top-row's normal flex flow (right-aligned via
   that row's justify-content), not absolutely positioned over the
   eyebrow/title - that row sits above .lh-masthead-row, so this never
   competes for width with the eyebrow or the title on any line, at any
   viewport. */
/* User request: the link-share and image-share actions (core/
   _share_button.html renders them as two adjacent .hl-share wrappers
   inside one .hl-share-group) used to sit as two visually distinct
   circular buttons with a bold divider between them - now merged into one
   seamless transparent pill (single outer border, not two individually-
   bordered buttons touching each other). Each icon is still its own
   click target/popover (that logic lives in the shared partial and isn't
   touched here). A faint hairline divider (below) marks the midpoint
   without breaking the merged-pill look.

   Bug fix: this originally also had `overflow: hidden` on this container
   to keep each button's hover-fill from squaring off past the pill's
   rounded corners - but .hl-share-popover (core/_share_button.html) is
   `position: absolute` and a DESCENDANT of this element, so overflow:
   hidden was silently clipping the popover to this ~70px pill's own tiny
   box any time a visitor's browser fell back to it (no navigator.share
   support, or a canShare({files}) miss) - the popover computed a real
   size/position but painted nowhere, reading as "the button does
   nothing". Fixed by rounding the first/last button's own OUTER corners
   to match the pill instead of clipping the whole container. */
.lh-masthead-actions {
  display: inline-flex;
  align-items: center;
  gap: 0;
  background: transparent;
  border: 1px solid rgba(245, 241, 236, 0.4);
  border-radius: 999px;
  padding: 3px;
}

.lh-masthead-actions .hl-share:first-child .hl-share-btn {
  border-radius: 999px 0 0 999px;
}

.lh-masthead-actions .hl-share:last-child .hl-share-btn {
  border-radius: 0 999px 999px 0;
}

.lh-masthead-actions .hl-share-group {
  gap: 0;
}

/* Hairline separator between the two actions - sits exactly on the
   boundary between the two adjacent .hl-share wrappers (gap: 0 above, so
   `right: 0` on the first one lands right at that seam). Faint on
   purpose (0.25 alpha vs. the outer pill border's 0.4) so it reads as a
   quiet mid-point mark, not a second border splitting the pill in two. */
.lh-masthead-actions .hl-share:first-child::after {
  content: "";
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  width: 1px;
  height: 16px;
  background: rgba(245, 241, 236, 0.25);
}

/* The two buttons/popovers inside are core/_share_button.html's own
   shared markup (used across every public page) - restyled here, scoped
   under `.lh-masthead-actions`, so only this instance loses its own
   circular dark background/border (each button is transparent with no
   border of its own now - the outer container above carries the one
   shared border/pill shape) in favor of sitting as bare icons on the
   masthead's own background. Deliberately no `transform` on hover
   (Phase 24 fix: a `scale()`/`translateY()` hover lift here was found to
   break the share popover on narrow screens - static/css/share.css's own
   @media (max-width: 560px) rule makes .hl-share-popover
   `position: fixed` specifically so it centers on the VIEWPORT, but per
   spec ANY ancestor with a non-none `transform` becomes the containing
   block for a `position: fixed` descendant INSTEAD of the viewport -
   since a mouse user has to be hovering this button to click it, that
   transform was reliably active at the exact moment the popover opened,
   centering it against this small icon instead of the screen. Background/
   color transitions carry no such risk - never add `transform` back to
   this element or its ancestors without checking this). */
.lh-masthead-actions .hl-share-btn {
  background: transparent;
  border: none;
  border-radius: 0;
  color: var(--text);
  padding: 7px 9px;
  /* A `drop-shadow` (not `box-shadow` - this needs to hug the icon glyph
     itself, not the button's square/circular box) so the icon stays
     readable against BOTH ends of the masthead's gradient - the bright
     orange corner and the near-black corner - without needing an actual
     background fill behind it. */
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7));
  transition: background 0.15s ease, color 0.15s ease;
}

.lh-masthead-actions .hl-share-btn:hover {
  background: rgba(245, 241, 236, 0.14);
  color: var(--primary);
}

.lh-masthead-actions .hl-share-btn:active {
  transform: scale(0.92);
}

.lh-masthead-actions .hl-share-btn svg {
  width: 16px;
  height: 16px;
}

@media (max-width: 560px) {
  .lh-masthead-actions .hl-share-btn {
    padding: 6px 8px;
  }

  .lh-masthead-actions .hl-share-btn svg {
    width: 14px;
    height: 14px;
  }
}

/* Phase 19: Seasons Hosted/Awards Given folded into the eyebrow line as
   quiet metadata (see league_home.html's own comment for why - "make the
   logo much more visible" meant removing the Phase 18 stat panel, not
   just re-skinning it) - muted, not the eyebrow's own bold brand-orange,
   so "OFFICIAL LEAGUE PAGE" still reads as the one thing this line is
   actually saying and these are a quieter aside. */
.lh-masthead-eyebrow-fact {
  color: var(--text-muted);
  font-weight: 600;
}

.lh-masthead-eyebrow-dot {
  margin-right: 6px;
  opacity: 0.6;
}

.lh-masthead-name {
  margin: 0 0 var(--space-2);
  font-size: clamp(26px, 4vw, 38px);
}

.lh-masthead-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.lh-masthead-pill {
  display: inline-flex;
  align-items: center;
  padding: 4px 12px;
  border-radius: 999px;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--text-secondary);
  white-space: nowrap;
}

.lh-masthead-pill strong {
  color: var(--text);
  font-weight: 700;
  margin-right: 3px;
}

.lh-masthead-pill-icon {
  margin-right: 5px;
  opacity: 0.75;
  flex-shrink: 0;
}

.lh-masthead-desc {
  position: relative;
  margin: var(--space-4) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--border);
  max-width: 720px;
  color: var(--text-secondary);
  font-size: 15px;
  line-height: 1.6;
}

/* ─── Season section (Phase 22: merged into the masthead) ──────────────
   Was its own separate card (.lh-hero - a full second background/border/
   border-radius/margin, "Season hero" below is its own history) sitting
   under the masthead; user asked to combine the two into one component.
   Now a section INSIDE .lh-masthead, closed off only by a hairline
   divider (padding-top + border-top), same lightweight pattern
   .lh-masthead-desc already used for the description above it - one
   card, one border, reads as a single continuous "this league, right
   now" block instead of two stacked panels. The divider itself "draws
   in" left-to-right (::before, animated width) once the whole card
   reveals, and this section's own content fades in a beat after the
   identity area above it - see the `html.lh-anim-ready` rules near the
   bottom of this file for both. */
.lh-masthead-season {
  position: relative;
  margin-top: var(--space-4);
  padding-top: var(--space-4);
}

.lh-masthead-season::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  height: 1px;
  width: 100%;
  background: var(--border);
}

/* ─── Season hero (Phase 16 compact rewrite) - history kept for context
   ─────────────────────────────────────────────────────────────────────
   Was a taller card with a giant faint ball-motif watermark, a separate
   date sub-line, and a pulsating "LIVE" treatment on its own row - user
   feedback asked for compact + the LIVE label gone + more useful info in
   its place. Now two tight rows: identity+picker, then a single
   dot-separated facts line (dates, team count, current leader) - about
   half the old vertical footprint for strictly more information. Phase
   22 above then dropped the card shell entirely in favor of
   .lh-masthead-season's divider. */

.lh-hero-top {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.lh-hero-identity {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  min-width: 0;
}

.lh-hero-title {
  margin: 0;
}

/* ─── Season picker (Phase 13) ────────────────────────────────────────
   Pinned to the end of .lh-hero-top (same margin-left: auto push
   .lh-masthead-actions above uses) so it reads as a control on the
   season heading, not another fact about it. */
.lh-season-picker {
  margin-left: auto;
}

.lh-season-select {
  appearance: none;
  cursor: pointer;
  background: var(--bg-raised) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%237C7266' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") no-repeat right 12px center;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  color: var(--text);
  font-family: var(--font-body);
  font-size: 13px;
  font-weight: 600;
  padding: 8px 32px 8px 14px;
  max-width: 220px;
}

.lh-season-select:hover {
  border-color: var(--primary);
}

.lh-hero-facts {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-secondary);
}

.lh-hero-fact {
  color: var(--text-secondary);
  text-decoration: none;
}

.lh-hero-dot {
  color: var(--text-muted);
  opacity: 0.6;
}

.lh-hero-leader {
  color: var(--text-secondary);
  transition: color 0.15s ease;
}

.lh-hero-leader strong {
  color: var(--text);
  font-weight: 700;
}

.lh-hero-leader:hover {
  color: var(--primary);
}

.lh-hero-leader:hover strong {
  color: var(--primary);
}

/* Below ~480px, "2026 SUMMER LEAGUE" + its status badge rarely both fit
   .lh-hero-identity's one line, and the season picker (already
   margin-left: auto) was left to wrap wherever flex-wrap happened to
   break it - sometimes trailing half off its own line. Force the picker
   onto its own guaranteed full-width row instead of leaving that to
   chance (Phase 22: the wordmark mark this comment used to also drop
   here is gone from the template entirely now - see
   .lh-masthead-season's own comment for why). */
@media (max-width: 480px) {
  .lh-season-picker {
    flex-basis: 100%;
    margin-left: 0;
  }

  .lh-season-select {
    width: 100%;
    max-width: none;
  }

  .lh-hero-facts {
    font-size: 11.5px;
  }
}

/* ─── League at a Glance stats (Phase 19 redesign) ─────────────────────
   User feedback, three rounds now: "looks AI generated." Phase 17
   already moved from six separate bordered cards to one hairline-divided
   panel, but left-aligned icon-then-number-then-label stacks read as a
   left-justified form, not a stats display. Centered per the user's own
   request, plus restrained "actually designed" details instead of the
   generic filled-icon-chip look. Phase 20 (user feedback: "having a
   circle border is not good") dropped the outlined-ring icon container
   from the Phase 19 pass too - the icon is bare now (just colored,
   slightly larger to still carry presence without a container), with a
   gentle scale-up on hover for the linked cells so it doesn't go
   completely inert. The number's short accent underline is the one
   remaining per-stat accent mark, doing double duty as what used to be
   the icon ring's job too. */
.lh-glance-panel {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
  /* Faint top-left highlight - the one bit of depth this panel gets,
     instead of the old per-tile radial glows. Subtle enough to read as
     "this card catches light" rather than as its own visible shape. */
  background-image: radial-gradient(480px 160px at 0% 0%, rgba(245, 241, 236, 0.035), transparent 65%);
  overflow: hidden;
}

.lh-glance-cell {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--space-4) var(--space-3);
  text-align: center;
  text-decoration: none;
  color: inherit;
  border-right: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  transition: background 0.15s ease;
}

/* Last column of each row (every 3rd cell) and the final row (the last
   3 cells, since 6 stats / 3 columns = exactly 2 rows) drop their
   trailing dividers - a hairline grid, not a hairline-bordered box
   floating inside a second outer border. */
.lh-glance-cell:nth-child(3n) {
  border-right: none;
}

.lh-glance-cell:nth-last-child(-n+3) {
  border-bottom: none;
}

.lh-glance-cell-link:hover {
  background: var(--bg-raised);
}

.lh-glance-cell-icon {
  display: inline-flex;
  color: var(--tile-accent, var(--text-muted));
  margin-bottom: 4px;
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.lh-glance-cell-link:hover .lh-glance-cell-icon {
  transform: scale(1.15) translateY(-1px);
}

.lh-glance-cell-value {
  position: relative;
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.15;
  padding-bottom: 8px;
}

/* The short accent underline - centered, thin, half-opacity so it reads
   as a mark under the number rather than a bar competing with it. Widens
   slightly and brightens on hover (linked cells only) as the one small
   piece of interactive feedback tying the icon's own hover animation
   together with the number it sits under. */
.lh-glance-cell-value::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 18px;
  height: 2px;
  border-radius: 999px;
  background: var(--tile-accent, var(--primary));
  opacity: 0.55;
  transition: width 0.2s ease, opacity 0.2s ease;
}

.lh-glance-cell-link:hover .lh-glance-cell-value::after {
  width: 28px;
  opacity: 0.85;
}

/* ─── Count-up "landing" flourish ───────────────────────────────────────
   static/js/league-home.js already counts every [data-lh-countup]
   numeral up from 0 on reveal (League at a Glance, Top Performers, the
   season facts' team count) - .lh-count-settle (base rule further down,
   near .lh-leader-row) adds a small settle pop right as each one lands
   on its real value instead of just stopping cold, the class added by
   that same script at the exact moment counting finishes (not a CSS
   animation-delay guess - cells count up on independently staggered
   starts, so there's no one fixed delay that would land on "done" for
   all of them). Glance cells specifically also have an accent underline
   to brighten in sync, tying the two together as one small moment
   instead of two coincidentally timed ones. */
.lh-glance-cell-value.lh-count-settle::after {
  animation: lh-count-settle-underline 0.5s ease;
}

@keyframes lh-count-settle-pop {
  0% { transform: scale(1); }
  45% { transform: scale(1.16); }
  100% { transform: scale(1); }
}

@keyframes lh-count-settle-underline {
  0%, 100% { width: 18px; opacity: 0.55; }
  45% { width: 30px; opacity: 1; }
}

.lh-glance-cell-label {
  font-size: 10.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* Was a 6-color rotation (violet/teal/orange/gold/alert/lime) assigned
   by nth-child position across six unrelated metrics (Teams/Active
   Players/Games Played/Upcoming Games/Avg Pts/Highest Score) - no
   category linked those metrics to a particular color, so it read as
   decoration rather than meaning. --tile-accent already falls back to
   var(--primary) (see the rule using it below), so removing the
   per-cell overrides here is enough to make every tile the one brand
   accent instead of six. */

@media (max-width: 640px) {
  .lh-glance-panel {
    grid-template-columns: repeat(2, 1fr);
  }

  /* Re-open the 3rd/6th-cell right border the 3-column rule above
     closed, then re-close it for the actual last column of a 2-column
     grid (every even cell) - and the last ROW is now the last 2 cells,
     not 3. */
  .lh-glance-cell:nth-child(3n) {
    border-right: 1px solid var(--border);
  }

  .lh-glance-cell:nth-child(2n) {
    border-right: none;
  }

  .lh-glance-cell:nth-last-child(-n+3) {
    border-bottom: 1px solid var(--border);
  }

  .lh-glance-cell:nth-last-child(-n+2) {
    border-bottom: none;
  }
}

/* ─── Season progress bar ─────────────────────────────────────────── */
.lh-progress {
  margin-top: var(--space-4);
}

.lh-progress-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
  font-size: 12px;
}

.lh-progress-head span:first-child {
  color: var(--text-secondary);
  font-weight: 600;
}

.lh-progress-count {
  font-family: var(--font-mono);
  color: var(--text-muted);
  white-space: nowrap;
}

.lh-progress-track {
  height: 6px;
  border-radius: 999px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  overflow: hidden;
}

.lh-progress-fill {
  height: 100%;
  min-width: 2px;
  border-radius: 999px;
  background: linear-gradient(90deg, var(--orange-deep), var(--primary));
  transition: width 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}

/* Real end_date/status context (see league_home.html's comment above this
   block's markup for why this is date-based rather than the Playbook
   mock's fabricated Playoffs/Finals ticks) - same muted/mono treatment as
   .lh-progress-count, just on its own line under the track instead of
   competing with the games-played count for the head row's width. */
.lh-progress-eta {
  display: block;
  margin-top: var(--space-2);
  font-family: var(--font-mono);
  font-size: 11px;
  color: var(--text-muted);
}

/* ─── Standings snapshot table (Phase 16 rewrite) ──────────────────────
   User request: "reverting to a table with the same layout in the
   landing page of the system" - matches templates/core/landing.html's
   hero scoreboard mockup (.landing-standings/.landing-scoreboard,
   static/css/landing.css) structurally: rank chip + team, W, L, PCT
   (its own cell doubles as a win% fill-bar via the `--pct` custom
   property set inline per row), Streak (colored, arrow-prefixed, cold
   below .500 this game). This page's own `.lh-standings-*` classes, not
   a reuse of `.landing-*` (those are redeclared/scoped under
   `.landing-page`, and a real team has a real logo/color to show - not
   the landing mockup's flat fake "team-dot" swatch) - same read, this
   page's own component. */
/* Phase 20 compact/polish pass (user request: "compact", "headers text
   align center", "professional and readable"). The four numeric
   columns' headers AND body cells are centered (both, so they actually
   line up with each other - was right-aligned body under a left-aligned
   header before), padding/row height trimmed down throughout for a
   denser feel. Phase 26 (user request: "text align to center it" for
   ALL headers, "fit the table 100% in the web view") reverted Phase 20's
   own Team-header-stays-left-aligned exception - see that rule's own
   history below - and dropped the 480px cap in favor of the table
   filling its section's actual width. */
/* ─── Standings leader row emphasis ─────────────────────────────────
   User request: give Standings a bigger visual anchor than the rest of
   the page - every other section here already converged on the same
   bordered-panel/hairline-rows shape, so a plain 4th copy of that
   treatment wouldn't have stood out. First attempt was a separate
   "leader spotlight" card above the table, but that just repeated the
   exact same name/record/PCT/streak the table's own row 1 shows two
   lines down - redundant, not "bigger". Replaced with baking the
   emphasis directly INTO row 1 (`tr.lh-standings-leader`, already the
   rank===1 row): a noticeably bigger crest, bigger name, and a
   "League Leader" tag - one representation of the leader, not two. */
.lh-standings-name-col {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.lh-standings-leader-tag {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  font-size: 9.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--primary);
}

.lh-standings-table tr.lh-standings-leader td {
  padding-top: 12px;
  padding-bottom: 12px;
}

.lh-standings-table tr.lh-standings-leader .hl-avatar,
.lh-standings-table tr.lh-standings-leader .hl-avatar-placeholder {
  width: 44px;
  height: 44px;
  border-radius: 10px;
}

/* User request: one consistent color for every no-logo team placeholder
   in this table, not base.css's `[data-accent]` teal/violet/lime/gold/
   coral rotation (apps.core.templatetags.badges.accent_rotate) and not a
   team's own `primary_color` override (both still apply everywhere else -
   team_detail/game_detail/season_calendar/box score/full standings all
   keep the per-team look; templates/leagues/league_home.html's markup for
   this table drops both the `data-accent` attribute and the
   `primary_color` inline style so nothing here can win over this rule by
   source order or specificity).

   Follow-up user request: dark/near-black instead of the league's
   --primary tint - deliberately built from the fixed neutral tokens
   (--bg/--border-strong/--text), never --primary/--primary-rgb, so it
   reads as a plain dark chip that pairs with any theme_palette rather
   than needing to individually look good against all 10 - the same
   reasoning theme.py's own docstring gives for why backgrounds/borders/
   text stay fixed while only 4 tokens vary per league. */
.lh-standings-table .hl-avatar-placeholder {
  background: var(--bg);
  color: var(--text);
  border-color: var(--border-strong);
}

.lh-standings-table tr.lh-standings-leader .lh-standings-name {
  font-size: 15px;
}

.lh-standings-table {
  width: 100%;
  /* Fixed layout + explicit (px, not %) widths on the four short numeric
     columns below, so the Team column (left with no explicit width)
     always absorbs whatever's left - without this, table auto-layout
     sizes every column to its own content's natural width, and on a
     narrow phone that squeezed Team down below "Demo Ballers"'s own
     width and wrapped it to two lines mid-word instead of leaving it the
     room it needs (or ellipsis-truncating a genuinely long team name,
     see .lh-standings-name below). Percentages here (tried first)
     compounded badly with each cell's own padding - box-sizing: border-
     box below makes a column's declared width its full on-screen
     footprint, padding included, so this math stays true at any table
     width instead of silently eating into Team's share. */
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 12.5px;
}

.lh-standings-table th,
.lh-standings-table td {
  box-sizing: border-box;
}

.lh-standings-table th:nth-child(2),
.lh-standings-table td:nth-child(2) {
  width: 46px;
}

.lh-standings-table th:nth-child(3),
.lh-standings-table td:nth-child(3) {
  width: 46px;
}

/* DIFF (Phase 27) - "+17"/"-17", occasionally 3 digits, still short
   enough for the same rough width as PCT/W-L. */
.lh-standings-table th:nth-child(4),
.lh-standings-table td:nth-child(4) {
  width: 46px;
}

.lh-standings-table th:last-child,
.lh-standings-table td:last-child {
  width: 58px;
}

/* Phase 25/27: PCT and DIFF are the two columns that give way first on a
   narrow phone - PCT is pure arithmetic on W-L (not new information),
   and while DIFF IS new information, at this width it's still the more
   dispensable of the four numeric columns compared to the record (W-L)
   and the "hot/cold right now" read (Streak). Team gets the freed-up
   width automatically either way (table-layout: fixed drops a
   `display: none` column from its own track calculation entirely, it
   doesn't just go blank and waste the space). Full standings (linked
   above) always has both. */
@media (max-width: 400px) {
  .lh-standings-pct-col,
  .lh-standings-diff-col {
    display: none;
  }

  /* A few more px reclaimed for Team specifically - the rank chip/avatar/
     name gap and the table's own left inset were tuned for the roomier
     5-and-6-column versions this table used to be. */
  .lh-standings-team {
    gap: 6px;
  }

  .lh-standings-table th:first-child,
  .lh-standings-table td:first-child {
    padding-left: 6px;
  }
}

/* Every header centered (Team included - Phase 26, see this block's own
   history above) with real breathing room on all four sides, not just
   bottom - `10px 12px 14px` reads as an actual header ROW now rather
   than text crammed against the divider it sits above. Team's body data
   (name + logo) stays left-aligned underneath its own centered label -
   normal for a compact table where the header names the column and the
   data reads left-to-right beneath it, same convention this table's own
   W-L/PCT/Streak columns already used before this change (centered
   header, centered data - Team is centered header, left data, which is
   just as common a pairing). */
.lh-standings-table th {
  text-align: center;
  font-weight: 600;
  color: var(--text-muted);
  font-size: 10.5px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 10px 12px 14px;
  border-bottom: 1px solid var(--border);
}

.lh-standings-table td {
  padding: 8px;
  border-bottom: 1px solid var(--border);
  color: var(--text);
}

.lh-standings-table th.num,
.lh-standings-table td.num {
  text-align: center;
  padding-left: 4px;
  padding-right: 4px;
}

.lh-standings-table tbody tr:last-child td {
  border-bottom: none;
}

.lh-standings-table tbody tr {
  transition: background 0.15s ease;
}

.lh-standings-table tbody tr:hover {
  background: rgba(245, 241, 236, 0.035);
}

.lh-standings-table tr.lh-standings-leader {
  background: linear-gradient(90deg, rgba(var(--primary-rgb), 0.1), transparent 65%);
  box-shadow: inset 3px 0 0 var(--primary);
}

.lh-standings-table tr.lh-standings-leader:hover {
  background: linear-gradient(90deg, rgba(var(--primary-rgb), 0.14), transparent 65%);
}

.lh-standings-team {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  text-decoration: none;
  color: inherit;
  /* Lets .lh-standings-name's ellipsis actually take effect inside a
     table-layout: fixed cell - flex children default to min-width: auto
     (their content's natural width), which overrides a descendant's own
     overflow/text-overflow entirely until the flex container itself is
     told it's allowed to shrink below that. */
  min-width: 0;
}

.lh-standings-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lh-standings-table tr.lh-standings-leader .lh-standings-team {
  font-weight: 700;
}

.lh-standings-rank {
  flex: none;
  width: 18px;
  height: 18px;
  border-radius: 5px;
  background: var(--bg-raised);
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-mono);
  font-size: 10px;
}

.lh-standings-table tr.lh-standings-leader .lh-standings-rank {
  background: var(--primary);
  color: var(--on-primary);
}

/* Was a static `background-image` gradient with a hard color-stop at
   `--pct` - correct, but a gradient stop position isn't something CSS
   transitions can animate, so the fill always just appeared instantly.
   Switched to a `::before` sized to `--pct` via `width` (still not
   itself animatable) with a `transform: scaleX()` reveal on top (scaling
   an already-correctly-sized box between 0 and 1 sidesteps needing the
   percentage inside the animated property at all) - same technique
   `.lh-progress-fill`/`.lh-standings-pct::before` siblings elsewhere on
   this page already use for their own draw-in. `z-index: -1` (not the
   default stacking order a plain `position: absolute` child would get)
   is load-bearing: this cell's real content is a bare text node with no
   wrapping span of its own to lift above the fill via a positive
   z-index, and an absolutely-positioned pseudo-element with z-index:auto
   paints AFTER a table cell's own in-flow text in stacking order, which
   would sit the fill on top of and partly obscure the ".778" text. */
.lh-standings-pct {
  position: relative;
  overflow: hidden;
}

.lh-standings-pct::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  width: var(--pct, 0%);
  background: rgba(var(--primary-rgb), 0.14);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 1s cubic-bezier(0.16, 1, 0.3, 1);
}

html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-standings-pct::before {
  transform: scaleX(1);
}

.lh-standings-streak {
  display: inline-flex;
  align-items: center;
  color: var(--teal);
  font-weight: 700;
  font-family: var(--font-mono);
}

.lh-standings-streak::before {
  content: "\25b2";
  font-size: 8px;
  margin-right: 3px;
  position: relative;
  top: -1px;
}

.lh-standings-streak.cold {
  color: var(--alert);
}

.lh-standings-streak.cold::before {
  content: "\25bc";
}

/* ─── Announcements section-head count badge ─────────────────────────
   Lives INSIDE the <h2> now (right after the "Announcements" text node),
   not as a sibling flex-item of .hl-section-head - it used to be the
   latter, which base.css's @media (max-width: 480px) .hl-section-head
   { flex-direction: column; align-items: stretch } rule stretched to the
   full row width (a lone wrapped flex child with align-items: stretch
   fills the cross axis), rendering as a wide, mostly-empty pill on its
   own line under the heading instead of a small badge beside it. Nesting
   it in the heading's own inline content sidesteps that breakpoint
   entirely - it just flows right after "Announcements" like inline text,
   at every viewport width, immune to any current or future
   .hl-section-head layout change. */
.lh-announcement-count {
  display: inline-block;
  margin-left: 6px;
  vertical-align: 2px;
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 2px 8px;
}

/* ─── Upcoming games panel ──────────────────────────────────────────── */
/* User request ("redesign the seasons card and other cards in the UI"):
   moved off N individually-bordered game cards onto the same single-
   panel/hairline-divider move Top Performers/League at a Glance already
   made (see .lh-leaders-panel's own comment) - one .lh-games-panel, each
   game a .lh-game-row. The soonest game's "tips off in..." countdown
   used to be an absolutely-positioned ribbon straddling the card's own
   top edge (it needed room above the card that a continuous panel of
   rows doesn't have) - now an .hl-live-badge/.hl-live-dot line inside
   the row instead, reusing the exact same "this is live/current"
   language as the Active season tile above, rather than a bespoke
   pulsing-ribbon animation. */
.lh-games-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.lh-game-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  color: inherit;
  border-top: 1px solid var(--border);
  transition: background 0.15s ease;
}

.lh-game-row:first-child {
  border-top: none;
}

.lh-game-row:hover {
  background: var(--bg-raised);
}

.lh-game-date-chip {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  border-radius: 10px;
  background: rgba(245, 241, 236, 0.06);
  color: var(--text-secondary);
}

.lh-game-row--next .lh-game-date-chip {
  background: rgba(var(--primary-rgb), 0.16);
  color: var(--primary);
}

.lh-game-date-day {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  line-height: 1.3;
}

.lh-game-date-num {
  font-family: var(--font-mono);
  font-size: 15px;
  font-weight: 700;
  line-height: 1.3;
}

.lh-game-info {
  min-width: 0;
  flex: 1;
}

.lh-game-next-badge {
  margin-bottom: 2px;
}

.lh-game-matchup {
  display: block;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
}

.lh-game-meta {
  display: block;
  margin-top: 1px;
  font-size: 12px;
  color: var(--text-muted);
}

.lh-game-row-arrow {
  flex-shrink: 0;
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1;
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.lh-game-row:hover .lh-game-row-arrow {
  opacity: 1;
  transform: translateX(0);
}

/* ─── Top Performers (stat leaders) ────────────────────────────────────
   Was three separate bordered cards, one per category, each with a
   colored top bar - direct user feedback was that this read as a generic
   "AI stat card" grid. Rebuilt as one hairline-divided ledger, the same
   "single panel, internal dividers, not N separate boxed cards" move
   League at a Glance itself already made in Phase 17 - supersedes this
   section's own prior comment arguing cards were fine here (categories
   still get their own accent, just as a chip instead of a top bar). */
.lh-leaders-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.lh-leader-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  color: inherit;
  border-top: 1px solid var(--border);
  transition: background 0.15s ease;
}

.lh-leader-row:first-child {
  border-top: none;
}

.lh-leader-row:hover {
  background: var(--bg-raised);
}

/* Was a per-category color rotation (points=alert/rebounds=violet/
   assists=teal) - unified to the single brand accent (--tile-accent
   falls back to var(--primary), same as the rule using it below) to
   match League at a Glance's own single-accent panel just above this
   one on the page (see that section's "Was a 6-color rotation..."
   comment in this file). */

.lh-leader-chip {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 10px;
  font-family: var(--font-mono);
  font-weight: 700;
  font-size: 10px;
  letter-spacing: 0.3px;
  background: color-mix(in srgb, var(--tile-accent, var(--primary)) 16%, transparent);
  color: var(--tile-accent, var(--primary));
  transition: transform 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.lh-leader-row:hover .lh-leader-chip {
  transform: scale(1.08) rotate(-4deg);
}

.lh-leader-info {
  flex: 1;
  min-width: 0;
}

.lh-leader-player-name {
  display: block;
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lh-leader-value-wrap {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
}

.lh-leader-value {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: 22px;
  font-weight: 700;
  color: var(--text);
  line-height: 1.15;
}

/* Per-game average (PPG/RPG/APG/SPG/BPG - user request) under the raw
   season total - same "second, smaller line, ranking stays total-based"
   treatment as .stl-step-avg/.stl-avg on the full Stat Leaders page. */
.lh-leader-avg {
  font-family: var(--font-mono);
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  margin-top: 1px;
}

/* Same count-up landing pop League at a Glance's numbers get (see that
   rule's own comment) - a bare fallback here since Top Performers/the
   season facts' team count don't have an accent underline of their own
   to sync a second flourish with. */
.lh-count-settle {
  animation: lh-count-settle-pop 0.32s ease;
}

.lh-leader-team {
  display: block;
  margin-top: 2px;
  font-size: 11.5px;
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ─── Last Result card (Upcoming Games column) ────────────────────── */
.lh-last-result {
  display: flex;
  flex-direction: column;
  gap: 4px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-2);
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s ease;
}

.lh-last-result:hover {
  border-color: var(--primary);
}

.lh-last-result-label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-muted);
}

.lh-last-result-matchup {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-size: 13px;
  font-weight: 600;
  color: var(--text-secondary);
}

.lh-last-result-team {
  font-family: var(--font-mono);
}

.lh-last-result-winner {
  color: var(--text);
  font-weight: 700;
}

/* ─── Recent Awards panel ────────────────────────────────────────────── */
/* User request ("redesign the seasons card and other cards in the UI"):
   same single-panel/hairline-divider move as Upcoming Games above,
   instead of a grid of individually-bordered, corner-glowing trophy
   cards. Each row is now also a real link to who won it (player or team
   profile) - the old card version had nowhere to click at all. */
.lh-awards-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.lh-award-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  color: inherit;
  border-top: 1px solid var(--border);
  transition: background 0.15s ease;
}

.lh-award-row:first-child {
  border-top: none;
}

.lh-award-row:hover {
  background: var(--bg-raised);
}

/* Every row gets the same gold-tinted icon container regardless of
   whether the award has a custom icon image - a bare trophy glyph
   (base.css's {% icon %} tag) fills in when it doesn't, so the list
   never has one row's layout hop left without an icon while its
   neighbors have one. */
.lh-award-icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  overflow: hidden;
  flex-shrink: 0;
  background: rgba(245, 195, 75, 0.14);
  border: 1px solid rgba(245, 195, 75, 0.3);
  color: var(--gold);
}

.lh-award-icon img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.lh-award-info {
  min-width: 0;
  flex: 1;
}

.lh-award-title {
  display: block;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 15px;
  text-transform: uppercase;
}

.lh-award-meta {
  display: block;
  margin-top: 2px;
  font-size: 12px;
  color: var(--text-muted);
}

.lh-award-row-arrow {
  flex-shrink: 0;
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1;
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.lh-award-row:hover .lh-award-row-arrow {
  opacity: 1;
  transform: translateX(0);
}

/* ─── Seasons: Active tile ───────────────────────────────────────────── */
/* .hl-card-link (base.css) is shared across the whole site (team cards,
   etc.) and stays untouched - these overrides are scoped under .lh-seasons
   so only this page's season cards pick them up. As of the "compact row
   list" pass below, ONLY the Active tile (.lh-season-featured) still uses
   .hl-card-link at all - Upcoming/Completed moved to .lh-season-row, a
   plain <a>, not a card. History: a colored left accent bar per status
   (active = teal, upcoming = violet, completed = lime) read as a generic
   dashboard-template tell, so status color was dropped entirely - every
   card got the same plain border, status living only in the group's own
   eyebrow label above ("ACTIVE"/"UPCOMING"/"COMPLETED"). Then "more
   attractive... impact... professional touch" added the same quiet
   brand-orange corner glow every other card-like element on this page
   uses (.lh-award-row/.lh-game-row's panels don't have one - it's
   specifically a "boxed card" flourish, kept here for the one season
   card still boxed). */
.lh-seasons .hl-card-link {
  position: relative;
  overflow: hidden;
  transition: border-color 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}

.lh-seasons .hl-card-link::before {
  content: "";
  position: absolute;
  top: -30px;
  right: -30px;
  width: 90px;
  height: 90px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(var(--primary-rgb), 0.14), transparent 70%);
  pointer-events: none;
}

.lh-seasons .hl-card-link:hover {
  border-color: var(--primary);
  transform: translateY(-2px);
  box-shadow: 0 14px 24px -18px rgba(var(--primary-rgb), 0.45);
}

.lh-seasons .hl-card-link h3 {
  font-size: 22px;
}

/* Bento treatment for the Active group (user request: "top 20 designs"
   -> bento grid) - a bigger tile with more padding, a bigger headline,
   and a bigger version of the same corner glow every season card already
   has, rather than a different visual language just for this one. Grid
   (not a single block) because a league can technically run more than
   one active season at once - still featured-sized, just wraps. */
.lh-season-featured-group {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--space-4);
}

.lh-seasons .lh-season-featured {
  padding: var(--space-6);
}

.lh-seasons .lh-season-featured::before {
  top: -50px;
  right: -50px;
  width: 160px;
  height: 160px;
}

.lh-seasons .lh-season-featured .hl-live-badge {
  margin-bottom: var(--space-2);
}

.lh-seasons .lh-season-featured h3 {
  margin-top: 0;
  font-size: 30px;
}

/* ─── Seasons: Upcoming/Completed row list ───────────────────────────── */
/* User request ("redesign the seasons card and other cards in the UI",
   "compact row list" picked for this group specifically): the same
   single-panel/hairline-divider move as Upcoming Games/Recent Awards
   above - a deliberate step DOWN in visual weight from the Active tile
   above it, and one that scales to a league with many completed seasons
   without sprawling into more and more grid rows the way boxed cards
   would. */
.lh-seasons-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 12px;
}

.lh-season-row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  text-decoration: none;
  color: inherit;
  border-top: 1px solid var(--border);
  transition: background 0.15s ease;
}

.lh-season-row:first-child {
  border-top: none;
}

.lh-season-row:hover {
  background: var(--bg-raised);
}

.lh-season-row-date {
  flex-shrink: 0;
  width: 108px;
  font-family: var(--font-mono);
  font-size: 12px;
  color: var(--text-muted);
}

.lh-season-row-name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-heading);
  font-weight: 700;
  font-size: 15px;
  text-transform: uppercase;
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lh-season-row-arrow {
  flex-shrink: 0;
  color: var(--text-muted);
  font-size: 16px;
  line-height: 1;
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.lh-season-row:hover .lh-season-row-arrow {
  opacity: 1;
  transform: translateX(0);
}

@media (max-width: 480px) {
  .lh-season-row-date {
    width: 72px;
    font-size: 11px;
  }
}

/* ─── Contact footer ─────────────────────────────────────────────── */
.lh-contact-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Icons added to each contact method (email/phone/website) - a small
   wayfinding aid so the row reads at a glance instead of as three
   identically-styled text buttons a visitor has to actually read to
   tell apart. */
.lh-contact-row .hl-btn-secondary svg {
  width: 15px;
  height: 15px;
  opacity: 0.75;
  flex-shrink: 0;
}

.lh-contact-row .hl-btn-secondary:hover svg {
  opacity: 1;
}

/* ─── Upcoming games + announcements columns ─────────────────────── */
.lh-columns {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-5);
  margin-bottom: var(--space-2);
}

/* ─── Announcements: timeline rail ────────────────────────────────── */
/* Design history: a busier layout (emoji + bold colored label + a bright
   "New" pill) was quieted into one plain bordered card, then the colored
   left border was dropped for reading as a generic dashboard-template
   tell, then a "top 20 designs" pass picked this section moving off
   cards entirely - GitHub-commit-history style, a thin rail connecting
   every entry's own icon marker top to bottom, content sitting loosely
   beside it instead of boxed. No card background/border left anywhere;
   the marker + message typography carry the whole thing. */
.lh-announcement-list {
  display: flex;
  flex-direction: column;
}

.lh-announcement-row {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding-bottom: var(--space-5);
  cursor: pointer;
}

.lh-announcement-row:last-child {
  padding-bottom: 0;
}

/* Stretched link - .lh-announcement-body (the message) is the only <a>,
   so this makes the whole row clickable rather than just that one line. */
.lh-announcement-body::after {
  content: "";
  position: absolute;
  inset: 0;
}

/* The rail: a fixed-width column holding the icon marker, with its own
   connecting line drawn below the marker through this row's own
   padding-bottom - since every row's line covers exactly its own height
   plus the gap beneath it, the rail reads as one continuous line down
   the whole list regardless of how many lines any given message wraps
   to, with no separate/absolutely-positioned "master line" element to
   keep in sync. Suppressed on the last row (nothing left to connect to). */
.lh-announcement-rail {
  position: relative;
  flex-shrink: 0;
  width: 34px;
  display: flex;
  justify-content: center;
}

.lh-announcement-rail::before {
  content: "";
  position: absolute;
  top: 34px;
  bottom: calc(-1 * var(--space-5));
  left: 50%;
  width: 2px;
  transform: translateX(-50%);
  background: var(--border);
  transition: background 0.15s ease;
}

.lh-announcement-row:last-child .lh-announcement-rail::before {
  content: none;
}

/* Hovering/focusing a row brightens its own line segment along with the
   icon's existing scale-up - one hover moment tying marker and rail
   together, the same "small piece of interactive feedback" the Glance
   underline and leader chip already do elsewhere on this page. */
.lh-announcement-row:hover .lh-announcement-rail::before,
.lh-announcement-row:focus-within .lh-announcement-rail::before {
  background: var(--text-muted);
}

/* Icon marker - fully circular (a timeline dot with an icon in it,
   rather than the square chip the card version used) sitting ON the
   rail. Category color lives here and only here now - no card border,
   no corner glow, one clear signal. A thin matching ring gives it a
   touch more presence than a flat color fill alone. */
.lh-announcement-icon-chip {
  position: relative;
  z-index: 1;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: var(--bg-card);
  border: 1px solid rgba(245, 241, 236, 0.16);
  color: var(--text-secondary);
  transition: transform 0.15s ease, border-color 0.15s ease;
}

.lh-announcement-row[data-category="award_given"] .lh-announcement-icon-chip { background: rgba(245, 195, 75, 0.16); border-color: rgba(245, 195, 75, 0.4); color: var(--gold); }
.lh-announcement-row[data-category="award_created"] .lh-announcement-icon-chip { background: rgba(155, 123, 255, 0.16); border-color: rgba(155, 123, 255, 0.4); color: var(--violet); }
.lh-announcement-row[data-category="game_completed"] .lh-announcement-icon-chip { background: rgba(47, 211, 196, 0.16); border-color: rgba(47, 211, 196, 0.4); color: var(--teal); }

.lh-announcement-row:hover .lh-announcement-icon-chip,
.lh-announcement-row:focus-within .lh-announcement-icon-chip {
  transform: scale(1.08);
}

/* Recency marker - a small solid dot on the marker's corner, same
   "unread" convention as a notification badge, instead of an inline
   "New" pill competing with the meta text for reading order. Pulses
   with the same rgba(--primary)-ring animation .hl-live-dot (base.css)
   already uses for "this is current" elsewhere on the site - opt-in via
   prefers-reduced-motion: no-preference, same as that rule, so a
   reduced-motion visitor still sees the dot, just steady. */
.lh-announcement-icon-chip--new::after {
  content: "";
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--primary);
  border: 2px solid var(--bg-card);
  transform: translate(11px, -11px);
}

@media (prefers-reduced-motion: no-preference) {
  .lh-announcement-icon-chip--new::after {
    animation: lh-announcement-new-pulse 2s ease-in-out infinite;
  }
}

@keyframes lh-announcement-new-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(var(--primary-rgb), 0.45); }
  50% { box-shadow: 0 0 0 4px rgba(var(--primary-rgb), 0); }
}

.lh-announcement-main {
  min-width: 0;
  flex: 1;
  padding-top: 6px;
}

.lh-announcement-body {
  display: block;
  margin: 0 0 2px;
  font-size: 13px;
  font-weight: 500;
  color: var(--text);
  line-height: 1.4;
  text-decoration: none;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
  transition: color 0.15s ease;
}

.lh-announcement-row:hover .lh-announcement-body,
.lh-announcement-row:focus-within .lh-announcement-body {
  color: var(--primary);
}

.lh-announcement-meta {
  display: flex;
  align-items: center;
  gap: 5px;
  margin: 0;
  font-size: 11px;
  color: var(--text-muted);
}

.lh-announcement-type {
  white-space: nowrap;
}

.lh-announcement-meta-dot {
  opacity: 0.6;
}

.lh-announcement-time {
  white-space: nowrap;
}

/* Playbook spec #76 - a quiet "there's more" affordance that only shows
   once you're actually hovering the row, rather than sitting there
   permanently competing with the timestamp for attention. */
.lh-announcement-arrow {
  flex-shrink: 0;
  align-self: center;
  margin-top: 6px;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  opacity: 0;
  transform: translateX(-3px);
  transition: opacity 0.2s ease, transform 0.2s ease;
}

.lh-announcement-row:hover .lh-announcement-arrow,
.lh-announcement-row:focus-within .lh-announcement-arrow {
  opacity: 1;
  transform: translateX(0);
}

@media (min-width: 700px) {
  .lh-columns {
    grid-template-columns: 1.6fr 1fr;
    align-items: start;
  }
}

/* ─── Small-screen tightening (phones) ────────────────────────────── */
@media (max-width: 480px) {
  .lh-masthead {
    padding: var(--space-4);
  }

  /* The bigger logo (below) leaves less room beside it for the eyebrow
     line at this width - "OFFICIAL LEAGUE PAGE" alone already nearly
     fills it, so appending "· 4 Seasons · 2 Awards" wrapped into an
     awkward 3-line stack with a lone "·" starting a line. Dropped here
     specifically (still shown from ~481px up) rather than shrunk further
     - it's a nice-to-have trust signal, not information a visitor
     actually needs, and League at a Glance below already covers the
     season's own numbers. */
  .lh-masthead-eyebrow-fact {
    display: none;
  }

  /* User request: location/Est pills (.lh-masthead-meta) wrapped onto
     their own separate lines at phone width instead of sitting side by
     side - the column beside the 104px logo below is only ~183px here,
     and two pills at the default padding/gap (24px + 24px + 8px gap =
     181-190px depending on the location string) clear that by only a
     handful of px. Tightened padding/gap/font-size close that gap for
     the common "City, PH" + "Est. YYYY" case without shrinking the pills
     into illegible chips - a genuinely long `league.location` can still
     wrap to its own line, same as any other flex-wrap content, that's
     not a bug, just no longer the default outcome for a short one. */
  .lh-masthead-pill {
    padding: 4px 9px;
    font-size: 10.5px;
  }

  .lh-masthead-meta {
    gap: 6px;
  }

  /* User request (3rd pass): put the share buttons back level with the
     logo instead of stacking them above the eyebrow (the margin-chasing
     fix from the previous pass avoided an eyebrow/share overlap, but
     left the share pill floating alone above an empty gap where the
     logo hadn't started yet - not actually what was asked for).

     .lh-masthead-col is already `position: relative` (see that rule
     above) and .lh-masthead-actions-row is its first child - pulling it
     out of flow with `position: absolute; top: 0; right: 0` anchors it
     to that same box, i.e. exactly level with the logo beside it
     (.lh-masthead-avatar-wrap is flex-start in .lh-masthead-row, so its
     own top is that same y). .lh-masthead-body (eyebrow/name/pills) then
     becomes the column's only in-flow child and starts at the same top
     too - the "same level" fix from the previous pass for free, no
     margin-chasing required.

     That still leaves the overlap this file's own history already
     warned about (see .lh-masthead-actions' comment above) - "OFFICIAL
     LEAGUE PAGE" alone (the only eyebrow content left once
     .lh-masthead-eyebrow-fact hides above) measured ~167px inside this
     ~183px column. Two changes make it safe specifically at this
     breakpoint: shrinking the share pill further (icon/padding below, on
     top of the existing 560px pass) to ~64px for the full two-button
     pill (link + image-share, templates/leagues/league_home.html keeps
     both - see core/_share_button.html's image_icon param for how the
     second one gets a different glyph without dropping the button), and
     reserving that plus a small gap (72px) on the eyebrow's own
     padding-right so the text wraps before it, rather than running under
     the button. Unlike the desktop case in that same history note, THIS
     eyebrow can never grow past "OFFICIAL LEAGUE PAGE" at this width
     (facts are already hidden above), so there's no risk of the
     reservation falling short against longer real data the way it did
     before.

     Bug fix (user report: "share/QR buttons don't work on mobile"): the
     72px reservation above only keeps the eyebrow's own TEXT from
     visually running under this row - it does nothing about which one
     actually catches a tap where the two overlap. .lh-masthead-body
     (eyebrow/name/pills) has its own `position: relative` (that rule,
     above) with no z-index, same as this row; two `position`ed siblings
     at the same (auto) stacking level paint in DOM order, and
     .lh-masthead-body comes AFTER this row in the markup - so it was
     painting (and catching taps) ON TOP of these buttons the entire
     time this row has been `position: absolute`, real "OFFICIAL LEAGUE
     PAGE" text or not: an element's padding/content box still counts
     for hit-testing wherever it sits, whether or not there's visible
     ink there. Invisible on a mouse-driven desktop check (nothing here
     is absolutely positioned above 480px, so there's no overlap to
     lose a stacking fight over in the first place) - only reachable by
     actually tapping a real touch layout, which is exactly the report.
     An explicit z-index settles it regardless of source order. */
  .lh-masthead-actions-row {
    position: absolute;
    top: 0;
    right: 0;
    margin-bottom: 0;
    z-index: 1;
  }

  /* The pill's base style (above) is a transparent fill with a faint
     light border - fine on desktop where .lh-masthead-actions-row sits
     in its own flow row above the eyebrow, always over roughly the same
     mid-tone part of the gradient. Anchored to the card's literal
     top-right corner instead (above), it lands somewhere different on
     .lh-masthead's diagonal gradient depending on how narrow the card
     is - at ~320px wide it sat almost exactly on the gradient's near-
     white end (the 66%-to-var(--bg-card) stop lands much closer to that
     corner on a narrower box), and the transparent pill's own near-white
     border all but disappeared against it. A real background fixes that
     regardless of card width instead of chasing a border color that
     works on every gradient stop. */
  .lh-masthead-actions {
    background: rgba(20, 12, 5, 0.4);
  }

  .lh-masthead-actions .hl-share-btn {
    padding: 4px 6px;
  }

  .lh-masthead-actions .hl-share-btn svg {
    width: 12px;
    height: 12px;
  }

  .lh-masthead-eyebrow {
    padding-right: 72px;
  }

  /* Still meaningfully bigger than the pre-Phase-19 desktop size (92px)
     used to be, not just "less small than before" - the logo stays the
     clear focal point at phone width too. Sizing/shadow live on the
     wrapper since Phase 23 (see that rule's own comment); the inner
     .lh-masthead-avatar just fills whatever size the wrapper is. Bumped
     alongside the desktop size (88px -> 104px, same ~1.19x the desktop
     128px -> 152px bump used) now that the share buttons' own top row
     means nothing here needs to reserve width for them anymore. */
  .lh-masthead-avatar-wrap {
    width: 104px;
    height: 104px;
    border-radius: 20px;
    box-shadow:
      0 0 0 3px var(--bg-card),
      0 0 0 5px rgba(var(--primary-rgb), 0.4),
      0 14px 28px -16px rgba(0, 0, 0, 0.85);
  }

  .lh-masthead-avatar {
    font-size: 34px;
  }

  .lh-masthead-name {
    font-size: 24px;
  }

  .lh-glance-cell-value,
  .lh-leader-value {
    font-size: 19px;
  }

  .lh-glance-cell {
    padding: var(--space-3) var(--space-2);
  }

  .lh-announcement-meta {
    font-size: 10px;
  }

}

/* ─── Scroll-reveal / count-up animation hooks (static/js/league-home.js)
   ─────────────────────────────────────────────────────────────────────
   `html.lh-anim-ready` is only added by the script once it's confirmed
   IntersectionObserver support and no `prefers-reduced-motion: reduce` -
   every rule below is scoped under it, so a JS failure or a
   reduced-motion visitor always gets the plain, already-correct static
   page rather than content stuck at opacity: 0. Count-up numerals
   (`data-lh-countup`) and the season-progress fill (`data-lh-progress`)
   already render their real, final value server-side - the script only
   re-plays that value as a brief animation once it scrolls into view.
   Phase 16 polish pass: base reveal trimmed from 0.7s/20px to 0.55s/14px
   (snappier, less "float up from the deep" - reads as quicker/more
   confident without becoming abrupt) and extended to the new
   .lh-standings-table rows, which previously had no stagger of their
   own (the table just appeared as part of its section's one flat
   reveal). */
html.lh-anim-ready [data-lh-reveal] {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.55s cubic-bezier(0.16, 1, 0.3, 1), transform 0.55s cubic-bezier(0.16, 1, 0.3, 1);
}

html.lh-anim-ready [data-lh-reveal].lh-is-visible {
  opacity: 1;
  transform: none;
}

/* Glance cells/leader tiles/standings rows/game rows/announcement rows/
   award rows/season cards+rows each get their own shorter, staggered
   fade+rise nested inside their already-revealed section, so a panel/
   grid/table/list settles in piece by piece instead of snapping in as
   one flat block - every per-item list on the page shares this same
   cascade language. */
html.lh-anim-ready [data-lh-reveal] .lh-glance-cell,
html.lh-anim-ready [data-lh-reveal] .lh-leader-row,
html.lh-anim-ready [data-lh-reveal] .lh-standings-table tbody tr,
html.lh-anim-ready [data-lh-reveal] .lh-game-row,
html.lh-anim-ready [data-lh-reveal] .lh-announcement-row,
html.lh-anim-ready [data-lh-reveal] .lh-award-row,
html.lh-anim-ready [data-lh-reveal] .lh-season-row,
html.lh-anim-ready [data-lh-reveal] .lh-seasons .hl-card-link {
  opacity: 0;
  transform: translateY(8px);
  /* Restates each item's own border-color/box-shadow/background/transform
     transitions alongside opacity/transform - this selector's higher
     specificity would otherwise replace (not merge with) their base
     `transition` shorthand and leave hover feedback abrupt once
     revealed. */
  transition: opacity 0.4s ease, transform 0.4s ease, border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
}

html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-glance-cell,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-leader-row,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-standings-table tbody tr,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-game-row,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-announcement-row,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-award-row,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-season-row,
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-seasons .hl-card-link {
  opacity: 1;
  transform: none;
}

html.lh-anim-ready [data-lh-reveal] .lh-glance-cell:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-leader-row:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-standings-table tbody tr:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-announcement-row:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-award-row:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-season-row:nth-child(2),
html.lh-anim-ready [data-lh-reveal] .lh-seasons .hl-card-link:nth-child(2) { transition-delay: 0.05s; }
html.lh-anim-ready [data-lh-reveal] .lh-glance-cell:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-leader-row:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-standings-table tbody tr:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-announcement-row:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-award-row:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-season-row:nth-child(3),
html.lh-anim-ready [data-lh-reveal] .lh-seasons .hl-card-link:nth-child(3) { transition-delay: 0.1s; }
html.lh-anim-ready [data-lh-reveal] .lh-glance-cell:nth-child(4),
html.lh-anim-ready [data-lh-reveal] .lh-standings-table tbody tr:nth-child(4),
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(4),
html.lh-anim-ready [data-lh-reveal] .lh-announcement-row:nth-child(4),
html.lh-anim-ready [data-lh-reveal] .lh-award-row:nth-child(4),
html.lh-anim-ready [data-lh-reveal] .lh-season-row:nth-child(4),
html.lh-anim-ready [data-lh-reveal] .lh-seasons .hl-card-link:nth-child(4) { transition-delay: 0.15s; }
html.lh-anim-ready [data-lh-reveal] .lh-glance-cell:nth-child(5),
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(5),
html.lh-anim-ready [data-lh-reveal] .lh-announcement-row:nth-child(5),
html.lh-anim-ready [data-lh-reveal] .lh-season-row:nth-child(5),
html.lh-anim-ready [data-lh-reveal] .lh-seasons .hl-card-link:nth-child(5) { transition-delay: 0.2s; }
html.lh-anim-ready [data-lh-reveal] .lh-glance-cell:nth-child(6),
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(6),
html.lh-anim-ready [data-lh-reveal] .lh-announcement-row:nth-child(6),
html.lh-anim-ready [data-lh-reveal] .lh-season-row:nth-child(6) { transition-delay: 0.25s; }
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(7) { transition-delay: 0.3s; }
html.lh-anim-ready [data-lh-reveal] .lh-game-row:nth-child(8) { transition-delay: 0.35s; }

/* ─── Masthead season section cascade (Phase 22) ───────────────────────
   Now that the season status lives inside .lh-masthead rather than as
   its own separately-revealed card, it gets its own small choreographed
   sequence instead of just popping in flush with the identity area
   above it in one flat motion: the section itself fades/rises in a beat
   after the card starts revealing, then its own divider "draws in" left
   to right a beat after that, then the status badge pops in last. Every
   delay is relative to the SAME .lh-is-visible trigger the outer card
   uses (not chained to each other), so this stays one coordinated
   sequence regardless of how long any single step takes. */
html.lh-anim-ready [data-lh-reveal] .lh-masthead-season {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.5s ease 0.1s, transform 0.5s ease 0.1s;
}

html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-masthead-season {
  opacity: 1;
  transform: none;
}

html.lh-anim-ready [data-lh-reveal] .lh-masthead-season::before {
  width: 0%;
  transition: width 0.6s ease 0.25s;
}

html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-masthead-season::before {
  width: 100%;
}

/* The status badge (ACTIVE/UPCOMING/COMPLETED) gets a single one-time
   "pop" on reveal - deliberately NOT a loop or pulse (Phase 16 already
   retired this card's old pulsating LIVE treatment on explicit user
   feedback; this is a different thing, a one-shot entrance flourish that
   runs once and stops, same species as the count-up numerals elsewhere
   on this page rather than an ongoing animation). `backwards` fill-mode
   holds the 0%-keyframe state (scaled down/transparent) during its own
   delay instead of flashing the badge at full size for that instant. */
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-hero-badge-pop {
  animation: lh-badge-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) 0.35s backwards;
}

/* Phase 23: the share corner tab gets its own one-time entrance, fired a
   beat after the status badge's so the whole card's entrance reads as
   one choreographed sequence (identity -> season -> divider draws in ->
   badge pops -> share tab appears) rather than several independently-
   timed fades. Fade + shadow-grow, deliberately NOT `lh-badge-pop`'s
   `transform: scale()` (Phase 24 fix - see .lh-masthead-actions' own
   comment: a `transform` on this element, even briefly during an
   animation, becomes the containing block for its `position: fixed`
   share popover descendant on narrow screens, centering it against this
   ~60px pill instead of the viewport). */
html.lh-anim-ready [data-lh-reveal].lh-is-visible .lh-masthead-actions {
  animation: lh-tab-pop 0.4s ease 0.45s backwards;
}

@keyframes lh-tab-pop {
  from {
    opacity: 0;
    box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
  }
  to {
    opacity: 1;
    box-shadow: 0 10px 20px -10px rgba(0, 0, 0, 0.75);
  }
}

@keyframes lh-badge-pop {
  from {
    opacity: 0;
    transform: scale(0.75);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}
