/* scores/polish.css — entrance + interaction animations for /scores
 * Presentation-only. Scoped to html[data-route="/scores"].
 * /rankings redirects permanently to /scores (nginx.conf).
 * Reduced-motion fallbacks included.
 * G8b fix / fix/iter2-closers 2026-06-10
 * Player-rankings panel + its pr-* entrance/skeleton rules removed 2026-07-16
 * (Pat: "looks like a player's ranking thing, which we don't need" — panel
 * deleted from scores/index.html, this file's dead pr-* selectors with it).
 */

/* ── Button hover / active / focus-visible ────────────────── */
html[data-route="/scores"] .btn,
html[data-route="/scores"] .press-btn {
  transition: transform .14s ease, box-shadow .14s ease;
}
html[data-route="/scores"] .btn:hover,
html[data-route="/scores"] .press-btn:hover {
  transform: translateY(-2px);
  box-shadow: 0 5px 16px rgba(255, 26, 26, .22);
}
html[data-route="/scores"] .btn:active,
html[data-route="/scores"] .press-btn:active {
  transform: translateY(0);
}
html[data-route="/scores"] .btn:focus-visible,
html[data-route="/scores"] .press-btn:focus-visible {
  outline: 2px solid var(--gold-accent, var(--accent,#FF2D2D));
  outline-offset: 3px;
}

/* ── Reduced-motion overrides ─────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html[data-route="/scores"] .btn:hover,
  html[data-route="/scores"] .press-btn:hover {
    transform: none;
  }
}

/* ── AP TOP 25 = mini scores-tiles (Pat 2026-07-01) ─────────────────────────
   Team + logo LEFT on the school-color L→R gradient (like the game tiles); rank ·
   movement · record on the RIGHT. Movement keeps green-up / red-down (sanctioned
   exception per Pat). Lives here (loaded last, stamped) to override the bundled
   core.css AP rules — direct core.css edits are CloudFlare-masked (unstamped). */
html[data-route="/scores"] .ap-row {
  display: flex !important;
  grid-template-columns: none !important;
  align-items: center; gap: 10px;
  padding: 9px 11px; margin: 4px 0; border-radius: 9px;
  border-top: 0 !important; position: relative; overflow: hidden;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--team, #3A3A3A) 78%, #000),
    color-mix(in srgb, var(--team, #3A3A3A) 26%, #0a0a0a)) !important;
}
html[data-route="/scores"] .ap-row::before { display: none !important; }
html[data-route="/scores"] .ap-team { order: 0; flex: 1 1 auto; display: inline-flex; align-items: center; gap: 8px; min-width: 0; }
html[data-route="/scores"] .ap-logo { width: 22px !important; height: 22px !important; }
/* CLS fix (FISHING-LEDGER #4 / LANE 2): the AP Top 25 is ALWAYS exactly 25
   rows once loaded, but the list starts with 3 skeleton rows — reserve the
   full 25-row height up front (row box ~43px incl. margin, computed against
   the .ap-row rule above) so swapping skeleton->real content doesn't balloon
   the rail height after paint. A short degraded ("Rankings unavailable")
   state just leaves blank space below one row rather than shifting layout. */
html[data-route="/scores"] .ap-list { min-height: 1080px; }
html[data-route="/scores"] .ap-name {
  font-family: var(--f-display, "Graduate", sans-serif); font-weight: 700; font-size: 13px;
  color: #F5F5F5 !important; text-shadow: 0 1px 2px rgba(0,0,0,.55);
}
html[data-route="/scores"] .ap-rank { order: 1; color: #F5F5F5 !important; text-shadow: 0 1px 2px rgba(0,0,0,.55); min-width: 20px; text-align: center; }
html[data-route="/scores"] .ap-move { order: 2; text-shadow: 0 1px 2px rgba(0,0,0,.55); }
html[data-route="/scores"] .ap-rec { order: 3; color: rgba(255,255,255,.92) !important; text-shadow: 0 1px 2px rgba(0,0,0,.55); white-space: nowrap; }
html[data-route="/scores"] .ap-pts { display: none !important; }

/* ── My Teams empty-state CTA (DEAD-14) ───────────────────────────────────
   core.css's .sb-empty is plain text-only, dashed-border, no way forward
   when the user has zero favorited teams. Lives here (loaded last, stamped)
   so it actually reaches the CDN — direct core.css edits are CloudFlare-
   masked (unstamped), per the AUD-24 note above. */
html[data-route="/scores"] .sb-empty-msg {
  color: var(--white-bright, #F5F5F5);
  font: 700 14px/1.4 var(--f-body, sans-serif);
}
html[data-route="/scores"] .sb-empty-sub {
  margin: 6px 0 14px;
}
html[data-route="/scores"] .sb-empty-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}

/* ── Mobile SCORES | AP TOP 25 toggle (Pat 2026-07-16) ───────────────────────
   The AP rail used to stack below the whole scoreboard on mobile and get
   buried. The existing top-of-page tab bar is now a MOBILE-ONLY toggle
   between the scoreboard and the AP rail so the rail is one tap away.
   Desktop keeps the current two-column layout with NO tab bar at all — the
   break here matches core.css's own .sb-layout stack point (max-width:960px,
   core.css:1595), not the generic sitewide 880px chrome breakpoint, so there
   is no dead zone between the two where the rail is stacked-below but the
   tab bar to reach it is hidden.
   One .ap-list DOM node — this is a pure CSS show/hide keyed off
   [data-mobile-view] on #tab-scores (see scores/script.js); no duplicate
   list, no second fetch. Lives here (loaded last, stamped) so it beats
   core.css's @layer components rules on cascade order alone. */
@media (min-width: 961px) {
  html[data-route="/scores"] .sc-page-tabs { display: none; }
}
@media (max-width: 960px) {
  /* default view (no attribute, or explicit "scores") = scoreboard */
  html[data-route="/scores"] #tab-scores[data-mobile-view="ap"] .sb-filterbar,
  html[data-route="/scores"] #tab-scores[data-mobile-view="ap"] .sb-main {
    display: none;
  }
  html[data-route="/scores"] #tab-scores:not([data-mobile-view="ap"]) .sb-rail {
    display: none;
  }
}

/* ── Desktop AP rail: independent scroll window (Pat 2026-07-16) ────────────
   core.css makes .sb-rail position:sticky; top:64px above 960px but sets no
   height cap, so the list runs past the viewport and the user has to scroll
   the whole page to reach the bottom of the poll. Cap the rail to the
   viewport and give it its own scroll container so a mouse-wheel over the
   rail moves the list, not the page, while every entry stays reachable. */
@media (min-width: 961px) {
  html[data-route="/scores"] .sb-rail {
    max-height: calc(100vh - 64px - 24px);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
    scrollbar-color: var(--gray-border, #2A2A2A) transparent;
  }
  html[data-route="/scores"] .sb-rail::-webkit-scrollbar { width: 8px; }
  html[data-route="/scores"] .sb-rail::-webkit-scrollbar-track { background: transparent; }
  html[data-route="/scores"] .sb-rail::-webkit-scrollbar-thumb {
    background: var(--gray-border, #2A2A2A);
    border-radius: 4px;
  }
  html[data-route="/scores"] .sb-rail::-webkit-scrollbar-thumb:hover {
    background: var(--gray-light, #C2C2C2);
  }
}

/* ── Mobile CONF selector: own row (SITE-AUDIT-2026-07-02, AUD-24) ──────────
   At <=620px the Conf <select> shared the sticky filter row with the
   scrollable filter-chip strip + week-nav and got squeezed to ~20px wide,
   clipped by the row's overflow-hidden guard (core.css:1552-1564 — rect
   check showed .sb-conf width:20 vs the bar's clip edge). Give it its own
   full-width row below the chip strip so it never competes for space.
   Lives here (loaded last, stamped) — direct core.css edits are
   CloudFlare-masked (unstamped); core.css's .sb-filterbar rules are inside
   @layer components (core.css:1125), so this unlayered override wins on
   cascade order alone (core.css:941), no !important needed for that part. */
@media (max-width: 620px) {
  html[data-route="/scores"] .sb-filterbar {
    flex-wrap: wrap;
    overflow-x: visible;
  }
  html[data-route="/scores"] .sb-filters { order: 1; }
  html[data-route="/scores"] .sb-week { order: 2; }
  html[data-route="/scores"] .sb-conf-wrap {
    order: 3;
    flex: 1 1 100%;
    max-width: none;
    margin-left: 0;
    margin-top: 2px;
  }
  html[data-route="/scores"] .sb-conf {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
    width: 100%;
    min-height: var(--sns-touch-target, 44px);
  }
}

/* ── Q-EXP-5 (D-EXPCARD-0716 §2b): #game-modal → shared quick-look visual
   contract. Restyle-in-place: native <dialog> + live-poll behavior untouched
   (CSS only). Base gm-* rules live in core.css (CF-masked for /scores), so
   contract overrides belong here in the stamped bundle. Tokens mirror
   /assets/exp-card.css: scrim rgba(0,0,0,.6)+blur(4px), ✕ ≥44px pinned,
   border/radius/sizing. */
.game-modal {
  width: min(760px, 92vw);
  max-width: none;
  max-height: 86vh;
  padding: 0;
  background: var(--bg-surface, #0A0A0A);
  border: 1px solid var(--gray-border, #2A2A2A);
  border-radius: 12px;
  box-shadow: 0 16px 56px rgba(0,0,0,.75);
  overflow: hidden;               /* .gm-body is the scroll region */
}
.game-modal::backdrop {
  background: rgba(0,0,0,.6);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.gm-head { flex: 0 0 auto; }      /* ✕ row pinned; never scrolls */
.gm-close {
  width: 44px;
  height: 44px;
  min-width: 44px;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  flex: 0 0 auto;
  background: rgba(255,0,0,.15);
  border: 1px solid rgba(255,0,0,.5);
  border-radius: 8px;
  color: var(--white-bright, #F5F5F5);
  font-size: 18px;
  line-height: 1;
}
.gm-close:hover { background: rgba(255,0,0,.35); color: var(--white-bright, #F5F5F5); }
.gm-body { flex: 1 1 auto; min-height: 0; overscroll-behavior: contain; }
@media (max-width: 880px) {
  .game-modal {
    width: min(92vw, 420px);
    max-height: 78dvh;
    border-radius: 16px;
  }
}

/* ---- Modal CTA: "View full game" -> /game/<id> ------------------------- *
   Rendered only for games that actually have a detail page (see the
   resolvability index in scores/script.js), so this is absent rather than
   dead for the FCS / non-FBS events ESPN lists that we don't carry. */
.gm-foot { flex: 0 0 auto; }
.gm-foot:empty { display: none; }
.gm-foot .gm-cta-wrap { padding: 14px 18px; border-top: 1px solid var(--gray-border, #2A2A2A); }
.gm-cta-wrap:empty { display: none; }
.gm-cta {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  padding: 12px 18px;
  border: 1px solid var(--gray-border, #2A2A2A);
  border-radius: 10px;
  background: var(--black-soft, #111);
  color: var(--white-bright, #F5F5F5);
  font: 700 13px/1 var(--f-body, sans-serif);
  letter-spacing: .08em;
  text-transform: uppercase;
  text-decoration: none;
  transition: border-color .15s, background .15s, transform .1s;
}
.gm-cta:hover,
.gm-cta:focus-visible {
  border-color: var(--red-primary, #FF1A1A);
  background: color-mix(in srgb, var(--red-primary, #FF1A1A) 10%, var(--black-soft, #111));
}
.gm-cta:active { transform: scale(.98); }
.gm-cta:focus-visible { outline: 2px solid var(--red-primary, #FF1A1A); outline-offset: 2px; }
.gm-cta-arrow { font-size: 15px; line-height: 1; transition: transform .15s; }
.gm-cta:hover .gm-cta-arrow { transform: translateX(3px); }
@media (prefers-reduced-motion: reduce) {
  .gm-cta, .gm-cta-arrow { transition: none; }
  .gm-cta:hover .gm-cta-arrow { transform: none; }
}
