/* Hooperslist — "/" marketing landing page (anonymous visitors only).
   Full homepage per docs/ui_content_recommendations.md §1 (2026-08-04
   decision), matching reference/preview/hooperslist-homepage.html.
   Tokens match docs/color-palette.md ("Blacktop+") - redeclared under
   .landing-page (harmless, identical to static/css/base.css's :root
   tokens) so this file stays self-contained. */

.landing-page {
  /* Every token below except --live comes straight from base.css's :root
     and cascades here unchanged - do NOT redeclare them. A palette swap
     should mean editing base.css's :root once, not hunting through every
     page's own CSS for a second copy of the same values. */
  --live: #34D399; /* Diverged from docs/color-palette.md's teal/live alias
    by request - win streaks and the "latest" badge read as green, not
    teal. --teal itself is untouched (still used for the per-league
    "no logo" identity accent, see .landing-featured-avatar below). */

  background: var(--bg);
  color: var(--text);
  font-family: "Inter", Arial, sans-serif;
  line-height: 1.6;
}

.landing-page .mono { font-family: "JetBrains Mono", monospace; }
.landing-page a { color: inherit; text-decoration: none; }
.landing-wrap { max-width: 1120px; margin: 0 auto; padding: 0 24px; }

/* ─── Header / nav ────────────────────────────────────────────────── */
.landing-header {
  border-bottom: 1px solid var(--border);
  padding: 20px 0;
}

.landing-nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
}

.landing-brand {
  display: flex;
  align-items: center;
  gap: 2px;
}

.landing-brand-mark {
  transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.landing-brand:hover .landing-brand-mark,
.landing-brand:focus-visible .landing-brand-mark {
  transform: rotate(-12deg) scale(1.08);
}
.landing-brand:active .landing-brand-mark {
  transform: rotate(-4deg) scale(0.9);
}

.landing-brand-name {
  font-family: "Saira Condensed", sans-serif;
  font-weight: 700;
  font-size: 22px;
  text-transform: uppercase;
  letter-spacing: 1.5px;
}

.landing-brand-name .accent,
.landing-headline .accent {
  color: var(--primary);
}

.landing-nav-links {
  display: flex;
  gap: 28px;
  font-size: 14px;
  color: var(--text-secondary);
}

.landing-nav-links a:hover { color: var(--text); }

/* "Log in" is a button (opens the coming-soon modal), not a real link, but
   sits among plain <a> nav links above - reset the browser's default
   button chrome so it reads identically (mirrors .landing-footer-link-btn
   below). */
.landing-nav-link-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  cursor: pointer;
  color: inherit;
}
.landing-nav-link-btn:hover { color: var(--text); }

/* Hamburger trigger for .landing-mobile-nav-dialog - hidden until the
   560px breakpoint (below) hides .landing-nav-links and shrinks the CTA,
   leaving no other way to reach Features/Roles/Mobile/Log in on mobile. */
.landing-nav-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  flex-shrink: 0;
  overflow: hidden;
  line-height: 0;
  -webkit-appearance: none;
  appearance: none;
}
.landing-nav-toggle:hover { border-color: var(--text-secondary); }
.landing-nav-toggle:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Match the {% icon %} call's own size=20 rather than shrinking a larger
   intrinsic SVG via CSS - one source of truth for the rendered size
   instead of relying on CSS width/height to always out-specificity the
   tag's own width/height attributes. */
.landing-nav-toggle svg {
  width: 20px;
  height: 20px;
  display: block;
  flex-shrink: 0;
}

.landing-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 11px 22px;
  border-radius: 6px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.3px;
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  line-height: 1.3;
}

.landing-btn-primary { background: var(--primary); color: var(--on-primary); }
.landing-btn-primary:hover { background: var(--primary-hover); }
.landing-btn-ghost { border-color: var(--border-strong); color: var(--text); background: transparent; }
.landing-btn-ghost:hover { border-color: var(--text-secondary); }
.landing-btn:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* ─── Hero ────────────────────────────────────────────────────────── */
.landing-hero { padding: 76px 0 64px; position: relative; overflow: hidden; }
.landing-hero-watermark { position: absolute; top: -40px; right: -60px; width: 520px; height: 520px; opacity: 0.12; pointer-events: none; z-index: 0; }
.landing-hero-grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 56px; align-items: center; position: relative; z-index: 1; }

.landing-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 12px;
  letter-spacing: 1.5px;
  color: var(--primary);
  text-transform: uppercase;
  font-weight: 600;
  margin-bottom: 18px;
}

.landing-eyebrow::before {
  content: "";
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--primary);
  display: inline-block;
  margin-right: 2px;
}

.landing-headline {
  font-family: "Saira Condensed", sans-serif;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  margin: 0;
  font-size: 52px;
  line-height: 1.05;
}

.landing-lede {
  font-size: 17px;
  color: var(--text-secondary);
  max-width: 46ch;
  margin: 22px 0 32px;
}

.landing-ctas { display: flex; gap: 14px; margin-bottom: 34px; flex-wrap: wrap; }
.landing-hero-note { font-size: 13px; color: var(--text-muted); margin: 0; }
.landing-hero-proof { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-secondary); margin: 14px 0 0; }
.landing-hero-note a { color: var(--text-secondary); text-decoration: underline; }
.landing-hero-note a:hover { color: var(--text); }

/* "Log in here" is a button (opens the coming-soon modal), not a real
   link - reset the browser's default button chrome so it reads
   identically to .landing-hero-note a above (mirrors
   .landing-footer-link-btn/.landing-nav-link-btn). */
.landing-hero-note-link-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  cursor: pointer;
  color: var(--text-secondary);
  text-decoration: underline;
}
.landing-hero-note-link-btn:hover { color: var(--text); }

/* ─── Scoreboard signature card ──────────────────────────────────── */
.landing-scoreboard {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 14px;
  padding: 20px 22px 24px;
  position: relative;
}

.landing-scoreboard-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding-bottom: 14px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--border);
}

.landing-scoreboard-title { font-size: 13px; letter-spacing: 1.5px; color: var(--text-secondary); }

.landing-live-dot {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  font-weight: 600;
  color: var(--live);
  letter-spacing: 0.5px;
}

.landing-live-dot::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--live);
}

table.landing-standings { width: 100%; border-collapse: collapse; font-size: 13px; }
table.landing-standings th {
  text-align: left;
  font-weight: 500;
  color: var(--text-muted);
  font-size: 11px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  padding: 0 8px 8px;
  border-bottom: 1px solid var(--border);
}
table.landing-standings th.num, table.landing-standings td.num { text-align: right; }
table.landing-standings td { padding: 9px 8px; border-bottom: 1px solid var(--border); color: var(--text); }
table.landing-standings tr:last-child td { border-bottom: none; }
table.landing-standings td.team { display: flex; align-items: center; gap: 9px; font-weight: 500; }

table.landing-standings tbody tr { transition: background 0.15s ease; }
table.landing-standings tbody tr:hover { background: rgba(245, 241, 236, 0.035); }

table.landing-standings tr.leader {
  background: linear-gradient(90deg, rgba(242, 106, 16, 0.10), transparent 65%);
  box-shadow: inset 3px 0 0 var(--primary);
}
table.landing-standings tr.leader:hover { background: linear-gradient(90deg, rgba(242, 106, 16, 0.14), transparent 65%); }
table.landing-standings tr.leader .team { font-weight: 700; }

.landing-standings .rank {
  width: 20px;
  height: 20px;
  border-radius: 5px;
  background: var(--bg-card);
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  flex: none;
}

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

.landing-standings td.pct {
  position: relative;
  background-image: linear-gradient(90deg, rgba(242, 106, 16, 0.16) var(--pct, 0%), transparent var(--pct, 0%));
}

.landing-standings td.streak { color: var(--live); font-weight: 600; }
.landing-standings td.streak::before { content: "\25b2"; font-size: 8px; margin-right: 3px; position: relative; top: -1px; }
.landing-standings td.streak.cold { color: var(--alert); }
.landing-standings td.streak.cold::before { content: "\25bc"; }

.team-dot {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: none;
  display: inline-block;
  border: 1px solid rgba(245, 241, 236, 0.15);
}
.team-dot.dot-white { background: #F5F1EC; }
.team-dot.dot-green { background: #22C55E; border-color: transparent; }
.team-dot.dot-blue { background: #4E8CFF; border-color: transparent; }
.team-dot.dot-red { background: #E5484D; border-color: transparent; }
.team-dot.dot-yellow { background: var(--gold); border-color: transparent; }
.team-dot.dot-black { background: #121113; border-color: rgba(245, 241, 236, 0.35); }

.landing-scoreboard-foot {
  display: flex;
  justify-content: space-between;
  margin-top: 14px;
  font-size: 12px;
  color: var(--text-muted);
}

/* ─── Sections ────────────────────────────────────────────────────── */
.landing-page section { padding: 64px 0; border-top: 1px solid var(--border); }
.landing-section-head { max-width: 560px; margin-bottom: 40px; }
.landing-section-head .landing-eyebrow { margin-bottom: 14px; }
.landing-section-head h2 { font-size: 30px; margin-bottom: 12px; }
.landing-section-head p { color: var(--text-secondary); font-size: 15px; margin: 0; }

/* ─── Stat tiles (inside the Active Leagues header, see the section
   below - merged into one block so the proof numbers sit right where
   the leagues they describe are about to be shown) ──────────────────
   Card treatment matches .landing-feature exactly (bg-raised, border,
   radius, icon chip) rather than a one-off style, so this reads as the
   same design system as the rest of the page instead of a bolted-on
   strip. The corner dot keeps the "Live Pulse Stat" signal from before -
   same pulse keyframe/timing as templates/core/_live_updated_badge.html's
   .hl-live-dot (static/css/base.css), which means "this is current, not
   a stale export" on Standings/Leaders and is equally true here since
   these numbers come from refresh_platform_stats, re-run on a schedule -
   just demoted to a detail on the icon now that the card itself carries
   the section's visual weight. */
.landing-stats-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
  margin: 24px 0 36px;
}
.landing-stat {
  position: relative;
  display: flex;
  align-items: center;
  gap: 18px;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 24px;
  min-width: 0;
  overflow: hidden;
}
/* Ambient glow behind the icon, tinted to this tile's own accent (below) -
   sits under the icon/body so it reads as a soft wash, not a fight for
   attention; --stat-glow defaults to the orange used by tile 1. */
.landing-stat::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120px 80px at 15% 0%, var(--stat-glow, rgba(242, 106, 16, 0.16)), transparent 70%);
  pointer-events: none;
}
.landing-stat-icon {
  position: relative;
  z-index: 1;
  flex: none;
  width: 56px;
  height: 56px;
  border-radius: 14px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.landing-stat-icon svg { width: 26px; height: 26px; }
.landing-stat-dot {
  position: absolute;
  top: -2px;
  right: -2px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent-stat, var(--primary));
  border: 2px solid var(--bg-raised);
}
@media (prefers-reduced-motion: no-preference) {
  .landing-stat-dot::after {
    content: "";
    position: absolute;
    inset: -4px;
    border-radius: 50%;
    border: 1px solid var(--accent-stat, var(--primary));
    animation: landing-stat-pulse 2s ease-out infinite;
  }
}
@keyframes landing-stat-pulse {
  0% { opacity: 0.7; transform: scale(0.6); }
  100% { opacity: 0; transform: scale(2); }
}
.landing-stat-body { position: relative; z-index: 1; display: flex; flex-direction: column; min-width: 0; }
.landing-stat-value {
  font-size: clamp(28px, 3.4vw, 36px);
  font-weight: 700;
  line-height: 1.05;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.landing-stat-label {
  margin-top: 4px;
  font-size: 13px;
  letter-spacing: 0.4px;
  color: var(--text-muted);
  white-space: nowrap;
}
/* Per-metric accent (2026-08 revision, explicit product decision -
   supersedes the earlier "one accent across all three tiles" call from
   docs/public_pages_polish_checklist.md's carnival audit). These three
   are being treated as distinct named categories - leagues/players/games -
   not an arbitrary list getting a color per position, which is what that
   audit was actually flagging elsewhere on this page. */
.landing-stat:nth-child(1) { --accent-stat: var(--primary); --stat-glow: rgba(242, 106, 16, 0.16); }
.landing-stat:nth-child(1) .landing-stat-icon { background: rgba(242, 106, 16, 0.16); color: var(--primary-hover); }
.landing-stat:nth-child(2) { --accent-stat: var(--teal); --stat-glow: rgba(47, 211, 196, 0.14); }
.landing-stat:nth-child(2) .landing-stat-icon { background: rgba(47, 211, 196, 0.16); color: var(--teal); }
.landing-stat:nth-child(3) { --accent-stat: var(--gold); --stat-glow: rgba(245, 195, 75, 0.14); }
.landing-stat:nth-child(3) .landing-stat-icon { background: rgba(245, 195, 75, 0.16); color: var(--gold); }

/* Shared by every [data-count-target] number (the three stat tiles plus
   the "N leagues" badge below) - display:inline-block so the transform
   in .is-counted actually applies (plain inline spans don't reliably
   transform across browsers), transform-origin:center so the pop scales
   from the number's own middle instead of its inline box's baseline
   corner. static/js/landing.js's startCounts() adds .is-counted the
   frame the count-up finishes, independent of the [data-reveal] fade
   it rides in on - a scroll-triggered flourish, not a hover one, so it
   plays identically on mobile. */
.landing-count-value { display: inline-block; transform-origin: center; }
@media (prefers-reduced-motion: no-preference) {
  .landing-count-value.is-counted { animation: landing-count-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }
}
@keyframes landing-count-pop {
  0% { transform: scale(1); }
  50% { transform: scale(1.16); }
  100% { transform: scale(1); }
}

/* ─── Active leagues: featured carousel + "view all" directory ──────
   2026-08 revision: replaced the always-open poster-card grid with a
   circular-avatar carousel spotlighting the first 8 leagues (each card
   glowing in its own accent-rotate identity color at rest, not just on
   hover) plus a "View all" toggle that reveals a compact search +
   "Show more" row list for the rest. Product call - see the featured
   card partial's own docstring for what "Featured" on the first card
   means (nothing more than "sorts first", there's no real signal yet). */
.landing-league-banner { padding: 44px 0 56px; }
.landing-league-banner .landing-eyebrow { margin-bottom: 8px; }
.landing-league-banner-head { font-size: 20px; margin: 0; }
.landing-league-banner-head-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}
.landing-league-count {
  flex: none;
  font-size: 12px;
  color: var(--text-secondary);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 6px 14px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* -- featured carousel -- */
.landing-featured-shell { position: relative; margin: 0 0 8px; }
.landing-featured-wrap {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
}
.landing-featured-track {
  display: flex;
  gap: 20px;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  padding: 10px 8px 20px;
  scrollbar-width: none;
}
.landing-featured-track::-webkit-scrollbar { display: none; }

a.landing-featured-card {
  /* Element-qualified for the same reason a.landing-league-card used to
     be (.landing-page a { color: inherit } in base.css otherwise wins
     the specificity tiebreak) - see git history for the fuller note. */
  --league-accent: var(--primary);
  --league-glow: rgba(242, 106, 16, 0.16);
  flex: 0 0 220px;
  scroll-snap-align: center;
  position: relative;
  background: linear-gradient(180deg, rgba(245, 241, 236, 0.05), transparent 60%);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 28px 18px 22px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 14px;
  text-decoration: none;
  color: var(--text-secondary);
  overflow: hidden;
  transition: transform 0.25s ease, border-color 0.25s ease;
}
/* Ambient glow, always on (not just :hover) - the whole point of this
   revision was a carousel that reads as alive at rest, not just on
   interaction. */
.landing-featured-card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(160px 120px at 50% 0%, var(--league-glow), transparent 72%);
  pointer-events: none;
}
.landing-featured-card:hover { transform: translateY(-5px); border-color: var(--league-accent); }

.landing-featured-badge {
  position: relative;
  z-index: 1;
  align-self: flex-start;
  margin: -8px 0 -8px -4px;
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--on-primary);
  background: var(--lime);
  padding: 3px 8px;
  border-radius: 999px;
}

/* Circular avatar - a deliberate exception to the square logo-stage
   used elsewhere (league_home, player rosters): this is a compact
   "featured" spotlight, not the primary place a logo needs to render
   pixel-perfect uncropped, so object-fit:cover's occasional crop on a
   non-square upload is an accepted tradeoff here. */
.landing-featured-avatar {
  position: relative;
  z-index: 1;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(245, 241, 236, 0.06);
  border: 1px solid var(--border-strong);
}
.landing-featured-avatar img { width: 100%; height: 100%; object-fit: cover; }
.landing-featured-mark {
  font-family: "Saira Condensed", sans-serif;
  font-weight: 800;
  font-size: 22px;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}
.landing-featured-name {
  position: relative;
  z-index: 1;
  font-family: "Saira Condensed", sans-serif;
  font-weight: 800;
  font-size: 16px;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  color: var(--text);
  line-height: 1.2;
}
.landing-featured-meta {
  position: relative;
  z-index: 1;
  font-family: "JetBrains Mono", monospace;
  font-size: 11px;
  color: var(--text-muted);
}

/* Same accent-rotate identity/values as the rest of the product
   (apps.core.templatetags.badges.accent_rotate + static/css/base.css's
   .hl-avatar-placeholder[data-accent]) - a fallback-mark card glows in
   its own accent, a real-logo card (no data-accent) keeps the brand
   orange glow above. */
.landing-featured-card[data-accent="teal"] { --league-accent: var(--teal); --league-glow: rgba(47, 211, 196, 0.16); }
.landing-featured-card[data-accent="teal"] .landing-featured-avatar { background: rgba(47, 211, 196, 0.16); }
.landing-featured-card[data-accent="teal"] .landing-featured-mark { color: var(--teal); }
.landing-featured-card[data-accent="violet"] { --league-accent: var(--violet); --league-glow: rgba(155, 123, 255, 0.16); }
.landing-featured-card[data-accent="violet"] .landing-featured-avatar { background: rgba(155, 123, 255, 0.16); }
.landing-featured-card[data-accent="violet"] .landing-featured-mark { color: var(--violet); }
.landing-featured-card[data-accent="lime"] { --league-accent: var(--lime); --league-glow: rgba(182, 226, 74, 0.16); }
.landing-featured-card[data-accent="lime"] .landing-featured-avatar { background: rgba(182, 226, 74, 0.16); }
.landing-featured-card[data-accent="lime"] .landing-featured-mark { color: var(--lime); }
.landing-featured-card[data-accent="gold"] { --league-accent: var(--gold); --league-glow: rgba(245, 195, 75, 0.16); }
.landing-featured-card[data-accent="gold"] .landing-featured-avatar { background: rgba(245, 195, 75, 0.16); }
.landing-featured-card[data-accent="gold"] .landing-featured-mark { color: var(--gold); }
.landing-featured-card[data-accent="coral"] { --league-accent: var(--alert); --league-glow: rgba(255, 90, 77, 0.16); }
.landing-featured-card[data-accent="coral"] .landing-featured-avatar { background: rgba(255, 90, 77, 0.16); }
.landing-featured-card[data-accent="coral"] .landing-featured-mark { color: var(--alert); }

/* Per-card staggered entrance, same treatment the old poster grid used. */
.landing-featured-card[data-reveal] {
  transform: translateY(10px) scale(0.97);
  transition-property: opacity, transform, border-color;
  transition-duration: 0.45s, 0.45s, 0.2s;
  transition-delay: min(var(--reveal-delay, 0ms), 480ms), min(var(--reveal-delay, 0ms), 480ms), 0s;
}
.landing-featured-card[data-reveal].is-visible { transform: translateY(0) scale(1); }
.landing-featured-card[data-reveal].is-visible:hover { transform: translateY(-5px) scale(1); }

.landing-featured-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 6;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  color: var(--text);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.landing-featured-arrow:hover { background: var(--bg-card); border-color: var(--primary); }
.landing-featured-arrow-prev { left: 0; }
.landing-featured-arrow-next { right: 0; }
.landing-featured-arrow-icon-prev { transform: rotate(-90deg); }
.landing-featured-arrow-icon-next { transform: rotate(90deg); }
.no-js .landing-featured-arrow { display: none; }

.landing-featured-dots { display: flex; justify-content: center; gap: 7px; margin-top: 4px; }
.landing-featured-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--border-strong);
  border: none;
  padding: 0;
  cursor: pointer;
  transition: background 0.15s ease, width 0.15s ease;
}
.landing-featured-dot.is-active { background: var(--primary-hover); width: 18px; border-radius: 4px; }
.no-js .landing-featured-dots { display: none; }

/* -- "View all" toggle + collapsed directory -- */
.landing-featured-viewall-row { display: flex; justify-content: center; margin: 6px 0 28px; }
.landing-featured-viewall-btn { gap: 8px; }
.landing-featured-viewall-chev { transition: transform 0.2s ease; transform: rotate(90deg); }
.landing-featured-viewall-btn[aria-expanded="true"] .landing-featured-viewall-chev { transform: rotate(-90deg); }
.no-js .landing-featured-viewall-row { display: none; }
.no-js .landing-featured-directory { display: block !important; }

.landing-featured-dir-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 14px;
  margin-bottom: 18px;
}
.landing-featured-dir-search-field { position: relative; flex: 1 1 280px; min-width: 0; max-width: 360px; }
.landing-featured-dir-search-icon {
  position: absolute;
  left: 13px;
  top: 50%;
  transform: translateY(-50%);
  color: var(--text-muted);
  pointer-events: none;
}
.landing-featured-dir-search {
  width: 100%;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 11px 36px 11px 38px;
  font-size: 14px;
  font-family: inherit;
  color: var(--text);
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.landing-featured-dir-search::placeholder { color: var(--text-muted); }
.landing-featured-dir-search:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 3px rgba(242, 106, 16, 0.18);
}
.landing-featured-dir-search::-webkit-search-cancel-button { display: none; }
.landing-featured-dir-search-clear {
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--text-muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.landing-featured-dir-search-clear:hover { background: var(--bg-card); color: var(--text); }
.landing-featured-dir-result-count { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.no-js .landing-featured-dir-controls { display: none; }

.landing-featured-dir-list { display: flex; flex-direction: column; gap: 8px; }
a.landing-featured-dir-row {
  --league-accent: var(--primary);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 10px 14px;
  border-radius: 12px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  text-decoration: none;
  color: var(--text-secondary);
  transition: border-color 0.2s ease, background 0.2s ease;
}
.landing-featured-dir-row:hover { border-color: var(--league-accent); background: var(--bg-raised); }
.landing-featured-dir-row[hidden] { display: none; }
.landing-featured-dir-avatar {
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(245, 241, 236, 0.06);
  border: 1px solid var(--border-strong);
}
.landing-featured-dir-avatar img { width: 100%; height: 100%; object-fit: cover; }
.landing-featured-dir-mark { font-family: "Saira Condensed", sans-serif; font-weight: 800; font-size: 14px; color: var(--text-muted); }
.landing-featured-dir-row[data-accent="teal"] { --league-accent: var(--teal); }
.landing-featured-dir-row[data-accent="teal"] .landing-featured-dir-avatar { background: rgba(47, 211, 196, 0.16); }
.landing-featured-dir-row[data-accent="teal"] .landing-featured-dir-mark { color: var(--teal); }
.landing-featured-dir-row[data-accent="violet"] { --league-accent: var(--violet); }
.landing-featured-dir-row[data-accent="violet"] .landing-featured-dir-avatar { background: rgba(155, 123, 255, 0.16); }
.landing-featured-dir-row[data-accent="violet"] .landing-featured-dir-mark { color: var(--violet); }
.landing-featured-dir-row[data-accent="lime"] { --league-accent: var(--lime); }
.landing-featured-dir-row[data-accent="lime"] .landing-featured-dir-avatar { background: rgba(182, 226, 74, 0.16); }
.landing-featured-dir-row[data-accent="lime"] .landing-featured-dir-mark { color: var(--lime); }
.landing-featured-dir-row[data-accent="gold"] { --league-accent: var(--gold); }
.landing-featured-dir-row[data-accent="gold"] .landing-featured-dir-avatar { background: rgba(245, 195, 75, 0.16); }
.landing-featured-dir-row[data-accent="gold"] .landing-featured-dir-mark { color: var(--gold); }
.landing-featured-dir-row[data-accent="coral"] { --league-accent: var(--alert); }
.landing-featured-dir-row[data-accent="coral"] .landing-featured-dir-avatar { background: rgba(255, 90, 77, 0.16); }
.landing-featured-dir-row[data-accent="coral"] .landing-featured-dir-mark { color: var(--alert); }

.landing-featured-dir-body { min-width: 0; }
.landing-featured-dir-name {
  display: block;
  color: var(--text);
  font-size: 14px;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.landing-featured-dir-name mark { background: none; color: var(--primary-hover); font-weight: 800; }
.landing-featured-dir-meta { font-size: 12px; color: var(--text-muted); }

.landing-featured-dir-empty {
  display: none;
  padding: 20px 0 4px;
  font-size: 14px;
  color: var(--text-muted);
  text-align: center;
}
.landing-featured-dir-empty:not([hidden]) { display: block; }

.landing-featured-dir-more {
  display: block;
  margin: 18px auto 0;
  font-family: "JetBrains Mono", monospace;
  font-size: 12px;
  color: var(--text-secondary);
  background: transparent;
  border: 1px dashed var(--border-strong);
  border-radius: 999px;
  padding: 9px 20px;
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.landing-featured-dir-more:hover { border-color: var(--primary-hover); color: var(--text); }
.landing-featured-dir-more[hidden] { display: none; }

@media (max-width: 640px) {
  .landing-stats-grid { grid-template-columns: 1fr; }
  .landing-featured-arrow { display: none; }
  .landing-featured-card { flex-basis: 190px; }
}

@media (max-width: 860px) {
  .landing-league-banner-head-row { flex-direction: column; align-items: flex-start; gap: 10px; }
}

/* ─── Real-product showcase: live share-card images ───────────────
   Replaces the former illustrated action banner. Cards are plain <a>
   wrappers around a real, server-rendered share-card PNG (1200x630,
   apps.core.share_cards) - width/height attrs on the <img> match that
   exact ratio so the layout doesn't shift while it loads. Same card
   shell (bg-raised/border/radius) as .landing-feature/.landing-story-card
   rather than a one-off treatment. */
.landing-showcase-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; margin-top: 8px; }
a.landing-showcase-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 12px;
  overflow: hidden;
  min-width: 0;
  transition: transform 0.3s cubic-bezier(0.16, 0.84, 0.44, 1), border-color 0.3s ease, box-shadow 0.3s ease;
}
.landing-showcase-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-strong);
  box-shadow: 0 16px 32px -20px rgba(0, 0, 0, 0.65);
}
.landing-showcase-card:active { transform: translateY(-1px) scale(0.99); }
.landing-showcase-media { position: relative; display: block; overflow: hidden; }
.landing-showcase-media img { display: block; width: 100%; height: auto; aspect-ratio: 1200 / 630; background: var(--bg); transition: transform 0.4s cubic-bezier(0.16, 0.84, 0.44, 1); }
.landing-showcase-card:hover .landing-showcase-media img { transform: scale(1.05); }
.landing-showcase-live {
  position: absolute;
  top: 10px;
  left: 10px;
  padding: 4px 10px;
  border-radius: 999px;
  background: rgba(8, 8, 10, 0.72);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.landing-showcase-live .landing-live-dot { font-size: 10px; letter-spacing: 0.6px; }
.landing-showcase-caption {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 14px 16px 16px;
  border-top: 1px solid var(--border);
}
.landing-showcase-caption-title { font-size: 14px; font-weight: 700; color: var(--text); }
.landing-showcase-caption-sub { font-size: 12.5px; color: var(--text-muted); }
.landing-showcase-foot { text-align: center; margin-top: 28px; }

/* ─── Court map illustration ─────────────────────────────────────── */
.landing-court-map-wrap { margin-bottom: 36px; border-radius: 16px; overflow: hidden; border: 1px solid var(--border); background: #0E0D0E; }
.landing-court-map-wrap svg { display: block; width: 100%; height: auto; }
.landing-court-legend { display: flex; flex-wrap: wrap; padding: 16px 20px 20px; font-size: 13px; color: var(--text-secondary); }
/* Legend items are <button>s (clickable/focusable, see below) sitting
   among plain divider styling - same button-chrome reset as
   .landing-footer-link-btn/.landing-nav-link-btn, with border-right
   re-applied after for the divider look. */
.landing-court-legend-item {
  display: flex;
  align-items: center;
  padding: 0 20px 0 0;
  margin: 4px 20px 4px 0;
  border: none;
  border-right: 1px solid var(--border);
  background: none;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.landing-court-legend-item:last-child { border-right: none; margin-right: 0; padding-right: 0; }
.landing-court-legend-item:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 4px; }
.landing-court-legend-item em { font-style: normal; color: var(--text-muted); margin-left: 4px; }
.landing-court-legend .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; margin-right: 8px; display: inline-block; }

/* ─── Court map / legend / role card cross-highlight ───────────────
   Hovering, focusing, or clicking a court dot, a legend button, or a
   role card highlights the other two (static/js/landing.js groups them
   by data-role and toggles .is-role-active on the whole group) - makes
   the diagram and the cards below read as one linked explanation
   instead of two things that merely share colors. */
.landing-court-map-wrap [data-role] { cursor: pointer; transition: transform 0.2s ease; transform-box: fill-box; transform-origin: center; }
/* Real focus ring instead of relying on the same subtle hover-scale as
   the only signal - a keyboard user tabbing through needs a clearer cue
   than mouse-hover visitors do. */
.landing-court-map-wrap [data-role]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 3px;
  border-radius: 50%;
}
.landing-court-map-wrap [data-role].is-role-active { transform: scale(1.22); }
.landing-court-legend-item.is-role-active { color: var(--text); }
.landing-court-legend-item.is-role-active .dot { transform: scale(1.3); }
.landing-role[data-role] { cursor: default; }
.landing-role[data-role].is-role-active { transform: translateY(-3px); background: var(--bg-card); }

/* ─── Mobile showcase ─────────────────────────────────────────────── */
.landing-mobile-showcase { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: 48px; align-items: center; }
.landing-phone-frame { display: flex; justify-content: center; }
.landing-phone-frame svg { width: 100%; max-width: 280px; height: auto; }
.landing-mobile-copy h2 { font-size: 30px; margin-bottom: 14px; }
.landing-mobile-copy p { color: var(--text-secondary); font-size: 15px; margin-bottom: 22px; max-width: 46ch; }
.landing-mobile-points { display: flex; flex-direction: column; gap: 14px; }
.landing-mobile-point { display: flex; align-items: flex-start; gap: 12px; font-size: 14px; color: var(--text-secondary); }
.landing-mobile-point svg { flex: none; width: 18px; height: 18px; color: var(--primary-hover); margin-top: 2px; }

/* ─── Features grid ───────────────────────────────────────────────── */
.landing-features { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 1fr; gap: 20px; }
/* First card takes a 2x2 "hero" cell (standard sparse auto-placement
   fills the remaining 5 single cells around it, no explicit placement
   needed for items 2-6) instead of six identical tiles - gives the grid
   a focal point without touching the icon-color decision below. */
.landing-features .landing-feature:first-child {
  grid-column: span 2;
  grid-row: span 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 32px;
  position: relative;
  overflow: hidden;
}
/* Same corner-glow language as .landing-hero::before/.landing-cta-band::before
   (radial orange fade) - fills the empty space a 2x2 cell leaves below
   the centered content instead of introducing a new decorative motif. */
.landing-features .landing-feature:first-child::before {
  content: "";
  position: absolute;
  right: -70px;
  bottom: -70px;
  width: 240px;
  height: 240px;
  background: radial-gradient(circle, rgba(242, 106, 16, 0.14) 0%, rgba(242, 106, 16, 0) 72%);
  pointer-events: none;
}
.landing-features .landing-feature:first-child > * { position: relative; z-index: 1; }
.landing-features .landing-feature:first-child .landing-feature-icon { width: 52px; height: 52px; margin-bottom: 20px; }
.landing-features .landing-feature:first-child .landing-feature-icon svg { width: 26px; height: 26px; }
.landing-features .landing-feature:first-child h3 { font-size: 21px; }
.landing-features .landing-feature:first-child p { font-size: 15px; max-width: 40ch; }
@media (max-width: 860px) {
  .landing-features .landing-feature:first-child { grid-row: auto; }
}
@media (max-width: 560px) {
  .landing-features .landing-feature:first-child { grid-column: auto; }
}
.landing-feature { background: var(--bg-raised); border: 1px solid var(--border); border-radius: 12px; padding: 24px; }
.landing-feature-icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
}
.landing-feature-icon svg { width: 21px; height: 21px; }
/* One accent for every feature icon (was a teal/violet/lime/gold rotation
   per card) - the features aren't different categories that need
   color-coding, so a single consistent chip reads as one brand instead
   of a rainbow assigned by grid position. */
.landing-feature-icon { background: rgba(242, 106, 16, 0.16); color: var(--primary-hover); }
.landing-feature h3 { font-size: 16px; text-transform: none; letter-spacing: 0; margin-bottom: 8px; }
.landing-feature p { font-size: 14px; color: var(--text-secondary); margin: 0; }

/* ─── Our story (vision / mission / why) ──────────────────────────── */
.landing-story { padding-top: 44px; }
/* z-index:0 (not just position:relative) so the connecting-line pseudo
   below establishes its stacking context right here - without it, a
   negative z-index escapes to the nearest ancestor that has one (likely
   all the way up to <body>), which can leave the line painted behind
   unrelated opaque backgrounds instead of just behind these three cards. */
.landing-story-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; position: relative; z-index: 0; }
/* Dashed thread running behind the three cards at icon height, only
   visible in the gaps between them (cards are opaque) - ties Vision ->
   Mission -> Why now together as one line of reasoning instead of three
   unrelated boxes. Desktop-only: at the 860px/560px breakpoints below
   the grid drops to 2 then 1 columns, where a cross-card line stops
   making sense. */
@media (min-width: 861px) {
  .landing-story-grid::before {
    content: "";
    position: absolute;
    left: 16%;
    right: 16%;
    top: 44px;
    height: 1px;
    background-image: repeating-linear-gradient(90deg, var(--border-strong) 0 8px, transparent 8px 16px);
    z-index: -1;
  }
}
.landing-story-card {
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 24px;
  transition: transform 0.3s cubic-bezier(0.16, 0.84, 0.44, 1), border-color 0.3s ease, box-shadow 0.3s ease;
}
.landing-story-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-strong);
  box-shadow: 0 16px 32px -20px rgba(242, 106, 16, 0.3);
}
.landing-story-icon {
  width: 40px;
  height: 40px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 16px;
}
.landing-story-icon svg { width: 21px; height: 21px; }
/* Same single-accent treatment as .landing-feature-icon above, for the
   same reason - three story cards, no semantic category to color-code. */
.landing-story-icon { background: rgba(242, 106, 16, 0.16); color: var(--primary-hover); }
.landing-story-card h3 { font-size: 16px; text-transform: none; letter-spacing: 0; margin-bottom: 8px; }
.landing-story-card p { font-size: 14px; color: var(--text-secondary); margin: 0; }

/* ─── Roles / hierarchy strip ─────────────────────────────────────── */
.landing-roles { display: flex; gap: 0; border: 1px solid var(--border); border-radius: 12px; overflow: hidden; }
.landing-role {
  flex: 1;
  padding: 26px 22px;
  border-right: 1px solid var(--border);
  background: var(--bg-raised);
  border-top: 2px solid transparent;
}
.landing-role:last-child { border-right: none; }
/* Kept as a 5-color rotation, unlike the other decorative rotations
   flattened elsewhere on this page - these five colors are the same
   role→color mapping used as the actual map legend for the court
   diagram right above this strip (templates/core/landing.html's
   .landing-court-legend dots + SVG marker fills), so this is a
   legitimate categorical key, not color assigned by grid position. */
.landing-role.r-orange { border-top-color: var(--primary); }
.landing-role.r-teal { border-top-color: var(--teal); }
.landing-role.r-violet { border-top-color: var(--violet); }
.landing-role.r-gold { border-top-color: var(--gold); }
.landing-role.r-lime { border-top-color: var(--lime); }
.landing-role .tag { font-size: 11px; letter-spacing: 1px; text-transform: uppercase; margin-bottom: 10px; display: block; font-weight: 600; }
.landing-role.r-orange .tag { color: var(--primary-hover); }
.landing-role.r-teal .tag { color: var(--teal); }
.landing-role.r-violet .tag { color: var(--violet); }
.landing-role.r-gold .tag { color: var(--gold); }
.landing-role.r-lime .tag { color: var(--lime); }
.landing-role h3 { font-size: 16px; text-transform: none; letter-spacing: 0; margin-bottom: 6px; }
.landing-role p { font-size: 13px; color: var(--text-secondary); margin: 0; }

/* ─── CTA band ─────────────────────────────────────────────────────── */
.landing-cta-band { border-top: 1px solid var(--border); text-align: center; padding: 70px 0; }
.landing-cta-band h2 { font-size: 30px; margin-bottom: 14px; }
.landing-cta-band p { color: var(--text-secondary); margin-bottom: 28px; }
.landing-cta-price {
  margin: 18px 0 0;
  font-size: 13px;
  color: var(--text-muted);
}
.landing-cta-price strong { color: var(--text); font-weight: 600; }
.landing-cta-price-was { text-decoration: line-through; opacity: 0.7; }
.landing-cta-price a { color: var(--text-secondary); text-decoration: underline; }
.landing-cta-price a:hover { color: var(--text); }

/* ─── Footer ──────────────────────────────────────────────────────── */
.landing-footer {
  border-top: 1px solid var(--border);
  padding: 56px 0 0;
  background: linear-gradient(180deg, transparent, rgba(242, 106, 16, 0.05));
}
.landing-footer-grid {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr;
  gap: 32px;
  padding-bottom: 40px;
}
.landing-footer-col { display: flex; flex-direction: column; gap: 12px; font-size: 14px; }
.landing-footer-col a { color: var(--text-secondary); transition: color 0.2s ease; }
.landing-footer-col a:hover { color: var(--text); }

/* "Start a league" is a button (opens the modal), not a real link, but
   sits in the same column as plain <a> footer links above - reset the
   browser's default button chrome so it reads identically. */
.landing-footer-link-btn {
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  color: var(--text-secondary);
  transition: color 0.2s ease;
}
.landing-footer-link-btn:hover { color: var(--text); }
.landing-footer-col-head {
  font-size: 11px;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 4px;
}
.landing-footer-brand-col .landing-brand { pointer-events: none; margin-bottom: 2px; }
.landing-footer-brand-col .landing-brand-name { font-size: 19px; }
.landing-footer-tagline { color: var(--text-secondary); font-size: 14px; margin: 0; max-width: 32ch; }
.landing-footer-domain { color: var(--text-muted); font-size: 12px; letter-spacing: 0.3px; }
.landing-footer-bottom {
  border-top: 1px solid var(--border);
  /* Longhand top/bottom only - a `padding: 20px 0` shorthand here would
     zero out the 24px left/right inset .landing-wrap already gives this
     element (same specificity, defined later wins), pulling the
     copyright text flush against the viewport edge on mobile and out of
     alignment with the footer columns above it on desktop. */
  padding-top: 20px;
  padding-bottom: 20px;
  font-size: 13px;
  color: var(--text-muted);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px 20px;
}
.landing-footer-legal { display: flex; gap: 20px; }
.landing-footer-legal a { color: var(--text-muted); transition: color 0.2s ease; }
.landing-footer-legal a:hover { color: var(--text); }

@media (max-width: 860px) {
  .landing-hero-grid { grid-template-columns: 1fr; }
  .landing-features { grid-template-columns: 1fr 1fr; }
  .landing-story-grid { grid-template-columns: 1fr 1fr; }
  .landing-roles { flex-direction: column; }
  .landing-role { border-right: none; border-bottom: 1px solid var(--border); }
  .landing-role:last-child { border-bottom: none; }
  .landing-headline { font-size: 38px; }
  .landing-mobile-showcase { grid-template-columns: 1fr; }
  .landing-phone-frame { order: -1; }
  .landing-showcase-grid { grid-template-columns: 1fr 1fr; }
  .landing-footer-grid { grid-template-columns: 1fr 1fr; }
  .landing-footer-brand-col { grid-column: 1 / -1; }
}

/* Below 1168px (.landing-wrap's max-width plus its own 24px*2 padding),
   the wrap stops centering with margin and pins its content 24px from
   the viewport edge instead - close enough to the fixed .back-to-top
   button (right: 16-20px, see back-to-top.css) that the two overlap at
   the very bottom of the page: this row right-aligns its legal links
   (justify-content: space-between) to the same edge the button anchors
   to. Confirmed via a real hit-test across a width sweep, not just
   visually - "Privacy" was unclickable (the button intercepted the tap)
   from 900px down through phone widths. A dedicated breakpoint rather
   than reusing the 860px layout one above - that one's tuned for the
   hero/features/roles reflow, not for this. Padding-right shifts the
   whole right-aligned group left, clear of the button's footprint (max
   66px: 46px wide + 20px offset), instead of repositioning/hiding a
   component shared with pages that don't have this problem.
   base.css's .site-footer-bottom (every other public page's footer,
   including every @public_league_view page which also renders
   core/_back_to_top.html) has the same shape and needs the identical
   fix - see its own `body:has(.back-to-top)`-guarded copy of this rule
   in base.css, guarded rather than unconditional like this one since
   not every page using that shared footer has a back-to-top button. */
@media (max-width: 1180px) {
  .landing-footer-bottom { padding-right: 88px; }
}

@media (max-width: 560px) {
  .landing-features { grid-template-columns: 1fr; }
  .landing-story-grid { grid-template-columns: 1fr; }
  .landing-nav-links { display: none; }
  .landing-ctas { flex-direction: column; }
  /* Keep Product/Get started side-by-side (inherits the 2-col layout from
     the 860px breakpoint below) instead of stacking all 3 footer columns
     full-width, which made the mobile footer unnecessarily tall. */
  .landing-footer-grid { gap: 24px 20px; }
  .landing-hero-watermark { width: 340px; height: 340px; top: -20px; right: -90px; }
  .landing-showcase-grid { grid-template-columns: 1fr; }
  .landing-brand-name { font-size: 17px; letter-spacing: 1px; }
  /* Header keeps only brand + hamburger below 560px - "Start a league" isn't
     lost, it's still the first thing in .landing-mobile-nav-footer, so the
     header row doesn't have to carry two competing CTAs (button + burger). */
  .landing-nav .landing-btn-primary { display: none; }
  .landing-nav-toggle { display: inline-flex; }
}

/* ═══════════════════════════════════════════════════════════════════
   Polish pass: page loader, scroll-triggered reveals, ambient motion,
   micro-interactions. Every animated rule below is scoped inside
   `prefers-reduced-motion: no-preference` (or explicitly zeroed under
   `reduce`) so motion-sensitive visitors get an instant, static page.
   ═══════════════════════════════════════════════════════════════════ */

/* Page loader: moved to static/css/page-loader.css (shared across
   public pages) - landing.html now includes it directly. */

/* ─── Sticky header ───────────────────────────────────────────────── */
.landing-header {
  position: sticky;
  top: 0;
  z-index: 40;
  background: rgba(8, 8, 10, 0.7);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  transition: background 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
}
.landing-header.is-scrolled {
  background: rgba(8, 8, 10, 0.92);
  border-bottom-color: var(--border-strong);
  box-shadow: 0 8px 24px -12px rgba(0, 0, 0, 0.6);
}

/* ─── Mobile nav drawer ───────────────────────────────────────────────
   Native <dialog> (same top-layer/free-focus-trap/Escape reasoning as
   modal.css's .hl-modal-dialog) styled as a right-edge sheet instead of a
   centered card. Only reachable via .landing-nav-toggle, which only
   exists below the 560px breakpoint, so no separate "hide on desktop"
   rule is needed here - it's simply never opened above that width. */
dialog.landing-mobile-nav-dialog,
dialog.landing-mobile-nav-dialog *,
dialog.landing-mobile-nav-dialog *::before,
dialog.landing-mobile-nav-dialog *::after {
  box-sizing: border-box;
}

dialog.landing-mobile-nav-dialog {
  position: fixed;
  inset: 0 0 0 auto;
  margin: 0;
  padding: 0;
  border: none;
  border-left: 1px solid var(--border);
  background: var(--bg-raised);
  color: var(--text);
  width: min(80vw, 320px);
  height: 100dvh;
  max-height: none;
  box-shadow: -16px 0 40px -16px rgba(0, 0, 0, 0.6);
}

dialog.landing-mobile-nav-dialog::backdrop {
  background: rgba(6, 5, 4, 0.72);
  backdrop-filter: blur(3px);
}

.landing-mobile-nav-panel {
  display: flex;
  flex-direction: column;
  height: 100%;
  /* Faint radial glow anchored top-right, same ballgrad-adjacent orange as
     the hero watermark - ties the drawer back to the rest of the page
     instead of reading as a bare generic sheet. */
  background: radial-gradient(220px 160px at 100% 0%, rgba(242, 106, 16, 0.12), transparent 70%);
}

.landing-mobile-nav-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 18px 16px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}

.landing-mobile-nav-brand { gap: 2px; }
.landing-mobile-nav-brand .landing-brand-name { font-size: 15px; letter-spacing: 1px; }
.landing-mobile-nav-brand .landing-brand-mark { width: 26px; height: 26px; }

.landing-mobile-nav-close {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: none;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.landing-mobile-nav-close:hover { background: var(--bg-card); color: var(--text); transform: rotate(90deg); }
.landing-mobile-nav-close:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

.landing-mobile-nav-eyebrow {
  padding: 16px 16px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  color: var(--text-muted);
  flex-shrink: 0;
}

.landing-mobile-nav-links {
  display: flex;
  flex-direction: column;
  padding: 4px 10px 8px;
  overflow-y: auto;
}
.landing-mobile-nav-links a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 10px;
  border-radius: 8px;
  font-size: 15px;
  font-weight: 600;
  color: var(--text-secondary);
  transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease;
}
.landing-mobile-nav-links a:hover,
.landing-mobile-nav-links a:focus-visible {
  background: var(--bg-card);
  color: var(--text);
  transform: translateX(3px);
}
.landing-mobile-nav-links a.is-active {
  background: var(--bg-card);
  color: var(--text);
}

.landing-mobile-nav-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--border-strong);
  transition: background 0.15s ease, box-shadow 0.15s ease;
}
/* Dots stay at their neutral default (--border-strong, set on
   .landing-mobile-nav-dot above) instead of cycling through five accent
   colors per link - the links aren't a category that needs
   color-coding, and .is-active below already gives the current section
   a clear signal without borrowing a color per position. */
.landing-mobile-nav-links a.is-active .landing-mobile-nav-dot {
  box-shadow: 0 0 0 3px rgba(242, 106, 16, 0.18);
}

.landing-mobile-nav-footer {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  margin-top: auto;
  border-top: 1px solid var(--border);
  flex-shrink: 0;
}
.landing-mobile-nav-footer .landing-btn { width: 100%; }

/* Same body-scroll-lock convention as modal.css (:has() over a JS helper). */
body:has(dialog.landing-mobile-nav-dialog[open]) {
  overflow: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  dialog.landing-mobile-nav-dialog {
    transform: translateX(100%);
    transition: transform 0.28s cubic-bezier(0.16, 1, 0.3, 1),
                overlay 0.28s ease allow-discrete,
                display 0.28s ease allow-discrete;
  }
  dialog.landing-mobile-nav-dialog[open] {
    transform: translateX(0);
  }
  @starting-style {
    dialog.landing-mobile-nav-dialog[open] {
      transform: translateX(100%);
    }
  }

  dialog.landing-mobile-nav-dialog::backdrop {
    opacity: 0;
    transition: opacity 0.28s ease, overlay 0.28s ease allow-discrete, display 0.28s ease allow-discrete;
  }
  dialog.landing-mobile-nav-dialog[open]::backdrop {
    opacity: 1;
  }
  @starting-style {
    dialog.landing-mobile-nav-dialog[open]::backdrop {
      opacity: 0;
    }
  }
}

/* ─── Scroll reveal ───────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  [data-reveal] {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.7s cubic-bezier(0.16, 0.84, 0.44, 1), transform 0.7s cubic-bezier(0.16, 0.84, 0.44, 1);
    transition-delay: var(--reveal-delay, 0s);
  }
  [data-reveal].is-visible { opacity: 1; transform: translateY(0); }

  .landing-features[data-reveal-group] .landing-feature:nth-child(1) { --reveal-delay: 0s; }
  .landing-features[data-reveal-group] .landing-feature:nth-child(2) { --reveal-delay: 0.08s; }
  .landing-features[data-reveal-group] .landing-feature:nth-child(3) { --reveal-delay: 0.16s; }
  .landing-features[data-reveal-group] .landing-feature:nth-child(4) { --reveal-delay: 0.24s; }
  .landing-features[data-reveal-group] .landing-feature:nth-child(5) { --reveal-delay: 0.32s; }
  .landing-features[data-reveal-group] .landing-feature:nth-child(6) { --reveal-delay: 0.4s; }

  .landing-story-grid[data-reveal-group] .landing-story-card:nth-child(1) { --reveal-delay: 0s; }
  .landing-story-grid[data-reveal-group] .landing-story-card:nth-child(2) { --reveal-delay: 0.08s; }
  .landing-story-grid[data-reveal-group] .landing-story-card:nth-child(3) { --reveal-delay: 0.16s; }

  .landing-roles[data-reveal-group] .landing-role:nth-child(1) { --reveal-delay: 0s; }
  .landing-roles[data-reveal-group] .landing-role:nth-child(2) { --reveal-delay: 0.08s; }
  .landing-roles[data-reveal-group] .landing-role:nth-child(3) { --reveal-delay: 0.16s; }
  .landing-roles[data-reveal-group] .landing-role:nth-child(4) { --reveal-delay: 0.24s; }
  .landing-roles[data-reveal-group] .landing-role:nth-child(5) { --reveal-delay: 0.32s; }

  .landing-stats-grid[data-reveal-group] .landing-stat:nth-child(1) { --reveal-delay: 0s; }
  .landing-stats-grid[data-reveal-group] .landing-stat:nth-child(2) { --reveal-delay: 0.08s; }
  .landing-stats-grid[data-reveal-group] .landing-stat:nth-child(3) { --reveal-delay: 0.16s; }

  .landing-showcase-grid[data-reveal-group] .landing-showcase-card:nth-child(1) { --reveal-delay: 0s; }
  .landing-showcase-grid[data-reveal-group] .landing-showcase-card:nth-child(2) { --reveal-delay: 0.08s; }
  .landing-showcase-grid[data-reveal-group] .landing-showcase-card:nth-child(3) { --reveal-delay: 0.16s; }
}
/* JS-disabled fallback: content must never stay hidden without an observer to reveal it. */
.no-js [data-reveal] { opacity: 1 !important; transform: none !important; }

/* Second-layer "pop" nested inside the stat tiles and league cards -
   their icon/logo arrives a beat after the card itself has already
   faded up (0.15s into the card's own reveal), landing with a slight
   overshoot rather than just riding the parent's fade. Driven off the
   same .is-visible class as the outer [data-reveal] card, so it needs
   no JS of its own and, like the count-up pop above, fires on scroll -
   not hover - so mobile visitors see it too. */
@media (prefers-reduced-motion: no-preference) {
  .landing-stat[data-reveal] .landing-stat-icon { opacity: 0; }
  .landing-stat[data-reveal].is-visible .landing-stat-icon {
    animation: landing-pop-in 0.55s cubic-bezier(0.34, 1.56, 0.64, 1) 0.15s both;
  }

  .landing-featured-card[data-reveal] .landing-featured-mark,
  .landing-featured-card[data-reveal] .landing-featured-avatar img {
    opacity: 0;
  }
  .landing-featured-card[data-reveal].is-visible .landing-featured-mark,
  .landing-featured-card[data-reveal].is-visible .landing-featured-avatar img {
    animation: landing-pop-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) 0.15s both;
  }
}
@keyframes landing-pop-in {
  0% { opacity: 0; transform: scale(0.4) rotate(-6deg); }
  60% { opacity: 1; transform: scale(1.1) rotate(2deg); }
  100% { opacity: 1; transform: scale(1) rotate(0deg); }
}
/* No-JS fallback: .is-visible is only ever added by landing.js's
   IntersectionObserver, so without this the icon/logo opacity:0 above
   would never be cleared (same reasoning as .no-js [data-reveal]). */
.no-js .landing-stat-icon,
.no-js .landing-featured-mark,
.no-js .landing-featured-avatar img,
.no-js .landing-phone-row {
  opacity: 1 !important;
}

/* ─── Hero entrance + spotlight ───────────────────────────────────── */
/* Orange stage-light glow sitting behind the headline column, echoing
   the CTA band's own ::before glow further down the page rather than
   introducing a one-off effect. Purely decorative - z-index:0 keeps it
   under .landing-hero-grid (z-index:1). */
.landing-hero::before {
  content: "";
  position: absolute;
  left: -8%;
  top: 8%;
  width: 560px;
  height: 380px;
  background: radial-gradient(ellipse at center, rgba(242, 106, 16, 0.18) 0%, rgba(242, 106, 16, 0) 70%);
  pointer-events: none;
  z-index: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .landing-hero::before { animation: landing-glow-pulse 5s ease-in-out infinite; }

  /* Held invisible until static/js/landing.js flips .landing-hero-ready
     (once [data-page-loader] actually clears) - same reasoning as
     .landing-brand-replay below: animating from first paint would burn
     the entrance behind the loader's z-index:9999 overlay. */
  .landing-hero-grid > div:first-child,
  .landing-hero-grid > .landing-scoreboard {
    opacity: 0;
  }
  .landing-hero-ready .landing-hero-grid > div:first-child {
    animation: landing-hero-rise 0.8s cubic-bezier(0.16, 0.84, 0.44, 1) both;
  }
  /* Rise-in, then hand off to the ambient float below (delay matches the
     rise's own finish) - one continuous motion instead of a hard cut. */
  .landing-hero-ready .landing-hero-grid > .landing-scoreboard {
    animation: landing-hero-rise-card 0.8s cubic-bezier(0.16, 0.84, 0.44, 1) 0.15s both,
               landing-float 6s ease-in-out 0.95s infinite;
  }
}
@keyframes landing-hero-rise {
  0% { opacity: 0; transform: translateY(22px); }
  100% { opacity: 1; transform: translateY(0); }
}
@keyframes landing-hero-rise-card {
  0% { opacity: 0; transform: translateY(22px) scale(0.98); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
/* No-JS fallback: .landing-hero-ready is only ever added by landing.js,
   so without this the hero would stay opacity:0 forever (same reasoning
   as .no-js [data-reveal] above). */
.no-js .landing-hero-grid > div:first-child,
.no-js .landing-hero-grid > .landing-scoreboard {
  opacity: 1 !important;
}

/* ─── Ambient motion ──────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .landing-hero-watermark { animation: landing-rotate 18s linear infinite; }
  .landing-phone-frame svg { animation: landing-float 7s ease-in-out infinite; }

  /* Standings rows slide in one at a time once the phone mockup itself
     scrolls into view (same .is-visible signal as the rest of the
     [data-reveal] system) instead of appearing all at once - reads more
     like a live list populating than a static drawing. */
  .landing-phone-row { opacity: 0; transform: translateX(14px); transition: opacity 0.5s ease, transform 0.5s cubic-bezier(0.16, 0.84, 0.44, 1); }
  .landing-phone-frame[data-reveal].is-visible .landing-phone-row { opacity: 1; transform: translateX(0); }
  .landing-phone-frame[data-reveal].is-visible .landing-phone-row:nth-of-type(1) { transition-delay: 0.25s; }
  .landing-phone-frame[data-reveal].is-visible .landing-phone-row:nth-of-type(2) { transition-delay: 0.35s; }
  .landing-phone-frame[data-reveal].is-visible .landing-phone-row:nth-of-type(3) { transition-delay: 0.45s; }
  .landing-phone-frame[data-reveal].is-visible .landing-phone-row:nth-of-type(4) { transition-delay: 0.55s; }

  /* Footer + drawer only here - CSS animations start the instant an
     element is styled, and the footer/drawer brand copies are either
     below the fold or inside a <dialog> that starts closed, so by the
     time either is actually seen the bounce has either not happened yet
     (drawer opens later, restarting it - display:none -> block always
     replays a CSS animation, no JS needed) or already finished off-
     screen (footer). The header copy is deliberately NOT included here -
     see .landing-brand-replay below - because it sits in the one spot
     that's on-screen from first paint, which on this page means "hidden
     behind .page-loader" (z-index 9999) for its entire 0.6s/0.5s run. */
  .landing-footer-brand-col .landing-brand-mark,
  .landing-mobile-nav-brand .landing-brand-mark {
    animation: landing-brand-drop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  }
  .landing-footer-brand-col .landing-brand-name,
  .landing-mobile-nav-brand .landing-brand-name {
    animation: landing-brand-in 0.5s ease-out 0.15s both;
  }

  /* Header copy: static/js/landing.js adds .landing-brand-replay once
     [data-page-loader] actually clears, so this plays where it can be
     seen instead of racing (and losing to) the loader overlay. */
  .landing-brand-replay .landing-brand-mark { animation: landing-brand-drop 0.6s cubic-bezier(0.34, 1.56, 0.64, 1) both; }
  .landing-brand-replay .landing-brand-name { animation: landing-brand-in 0.5s ease-out 0.15s both; }
}
@keyframes landing-rotate { to { transform: rotate(360deg); } }
@keyframes landing-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-8px); }
}
@keyframes landing-brand-drop {
  0% { opacity: 0; transform: translateY(-16px) rotate(-20deg) scale(0.7); }
  65% { opacity: 1; transform: translateY(2px) rotate(4deg) scale(1.06); }
  100% { opacity: 1; transform: translateY(0) rotate(0deg) scale(1); }
}
@keyframes landing-brand-in {
  0% { opacity: 0; transform: translateX(-6px); }
  100% { opacity: 1; transform: translateX(0); }
}
/* ─── Live dot pulse ──────────────────────────────────────────────── */
.landing-live-dot { position: relative; }
.landing-live-dot::before { position: relative; z-index: 1; }
@media (prefers-reduced-motion: no-preference) {
  .landing-live-dot::after {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--live);
    transform: translateY(-50%);
    animation: landing-pulse-ring 1.8s cubic-bezier(0.2, 0.7, 0.4, 1) infinite;
  }
}
@keyframes landing-pulse-ring {
  0% { opacity: 0.6; transform: translateY(-50%) scale(1); }
  100% { opacity: 0; transform: translateY(-50%) scale(3.2); }
}

/* ─── Micro-interactions ─────────────────────────────────────────── */
.landing-btn {
  transition: background 0.2s ease, border-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
}
.landing-btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 24px -10px rgba(242, 106, 16, 0.55); }
.landing-btn-primary:active { transform: translateY(0); box-shadow: none; }
.landing-btn-ghost:hover { transform: translateY(-2px); }
.landing-btn-ghost:active { transform: translateY(0); }

.landing-nav-links a { transition: color 0.2s ease; }

.landing-feature {
  transition: transform 0.3s cubic-bezier(0.16, 0.84, 0.44, 1), border-color 0.3s ease, box-shadow 0.3s ease;
}
.landing-feature:hover {
  transform: translateY(-4px);
  border-color: var(--border-strong);
  box-shadow: 0 16px 32px -20px rgba(0, 0, 0, 0.65);
}
.landing-feature-icon { transition: transform 0.3s ease; }
.landing-feature:hover .landing-feature-icon { transform: scale(1.08); }

.landing-stat {
  transition: transform 0.3s cubic-bezier(0.16, 0.84, 0.44, 1), border-color 0.3s ease, box-shadow 0.3s ease;
}
.landing-stat:hover {
  transform: translateY(-4px);
  border-color: var(--border-strong);
  box-shadow: 0 16px 32px -20px rgba(0, 0, 0, 0.65);
}
.landing-stat-icon { transition: transform 0.3s ease; }
.landing-stat:hover .landing-stat-icon { transform: scale(1.08); }

.landing-role { transition: transform 0.3s ease, background 0.3s ease; }
.landing-role:hover { transform: translateY(-3px); background: var(--bg-card); }

.landing-court-legend-item .dot { transition: transform 0.2s ease; }
.landing-court-legend-item:hover .dot { transform: scale(1.3); }

.landing-mobile-point svg { transition: transform 0.2s ease; }
.landing-mobile-point:hover svg { transform: scale(1.15); }

/* ─── CTA band ambient glow ───────────────────────────────────────── */
.landing-cta-band { position: relative; overflow: hidden; }
.landing-cta-band::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 640px;
  height: 320px;
  background: radial-gradient(ellipse at center, rgba(242, 106, 16, 0.16) 0%, rgba(242, 106, 16, 0) 70%);
  transform: translate(-50%, -50%);
  pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
  .landing-cta-band::before { animation: landing-glow-pulse 5s ease-in-out infinite; }
}
@keyframes landing-glow-pulse {
  0%, 100% { opacity: 0.7; }
  50% { opacity: 1; }
}
.landing-cta-band > .landing-wrap { position: relative; z-index: 1; }

/* Back-to-top button: moved to static/css/back-to-top.css (shared across
   public pages) - landing.html now includes it directly. */

/* ─── Smooth in-page scrolling (offset for the sticky header) ────── */
/* scroll-behavior itself lives in back-to-top.css (html:has(.back-to-top)) */
#features, #roles, #mobile { scroll-margin-top: 84px; }

/* ─── Mobile sticky CTA bar ──────────────────────────────────────────
   Only below 560px, where .landing-nav's own "Create your league"
   button is already hidden in favor of the hamburger menu (see that
   rule further up) - above that width the header always carries the
   CTA, so this would just be a redundant second one. Hidden by default
   and off-screen (not just transparent) so it can never intercept taps
   meant for page content while dismissed; static/js/landing.js toggles
   [data-visible] once the hero scrolls out of view, off again once the
   bottom CTA band scrolls into view. */
.landing-mobile-cta-bar {
  display: none;
}
@media (max-width: 560px) {
  .landing-mobile-cta-bar {
    display: block;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 36;
    padding: 12px 16px calc(12px + env(safe-area-inset-bottom, 0px));
    background: rgba(8, 8, 10, 0.92);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-top: 1px solid var(--border);
    transform: translateY(100%);
    transition: transform 0.25s cubic-bezier(0.16, 0.84, 0.44, 1);
    pointer-events: none;
  }
  .landing-mobile-cta-bar[data-visible] {
    transform: translateY(0);
    pointer-events: auto;
  }
  .landing-mobile-cta-bar .landing-btn { width: 100%; }

  /* Clear the bar's ~78px footprint so the circular back-to-top button
     (back-to-top.css, bottom-right) doesn't sit underneath/overlapped by
     it - same "move the other fixed element up" pattern that file
     already uses for the league public shell's bottom tab bar. */
  body:has(.landing-mobile-cta-bar[data-visible]) .back-to-top {
    bottom: calc(78px + env(safe-area-inset-bottom, 0px));
  }
}
@media (prefers-reduced-motion: reduce) {
  .landing-mobile-cta-bar { transition: none; }
}
.no-js .landing-mobile-cta-bar { display: none; }
