/* ------------------------------------------------------------------------
 * Shared chrome for the exchange pages (/board/ and /spread-edge/):
 * palette, topbar grammar (wordmark · conn lamp · clock), the one-pane
 * glass sheet, league bands, the status + matchup voices, and the
 * boot-to-data handoff. Pairs with css/fonts.css (faces) and
 * css/exchange-ui.css (shared renderers' styles).
 *
 * Page stylesheets keep only what is genuinely theirs: listing grids,
 * modals, breakpoints — and each declares its own --top-h default
 * (the chrome height before JS measures the real one).
 * ------------------------------------------------------------------------ */

:root {
  --black:  #010101;
  --dark:   #121212;
  --gray:   #1d1e20;
  --shade:  #272727;
  --slate:  #424242;
  --silver: #b2b2b2;
  --white:  #ffffff;
  --green:  #54d738;
  --red:    #ff453a;
  --gold:   #c5b358;
  --mono: 'SF Mono', 'Menlo', 'Consolas', monospace;
  /* THE page edge for both exchange pages: the title, the league band labels
     and the listing rows all inset by this, so they line up by construction
     rather than by three hardcoded copies of the same number agreeing.
     Narrow breakpoints tighten it (see each page's media queries) — phones
     cannot spare 28px a side. */
  --edge-x: 28px;
  /* The inset INSIDE the wide table: cell padding, and the band's column
     labels, which must always use the SAME number or the labels stop
     sitting over their columns. This is internal rhythm, NOT the page edge
     — do not raise it to add breathing room at the window. Spread Edge in
     particular sizes its label boxes off this value (width: 220px + inset,
     justify-self: end), so widening it walks the TOTAL label out of its
     column. Measured: 14 -> 28 put that label 24px off. The board's
     1241-1440px squeeze lowers it to 8. */
  --cell-x: 14px;
}
* { box-sizing: border-box; }
/* No scrollbar chrome. html carries NO overflow rule: overflow-x:hidden
   there broke mobile sticky (root becomes a scroll container), and
   overflow-x:clip silently computed overflow-y to clip — the page could
   not scroll AT ALL. Horizontal clipping lives on body, with clip. */
html { scrollbar-width: none; }
::-webkit-scrollbar { width: 0; height: 0; }
body {
  margin: 0; background: var(--black); color: var(--silver);
  font-family: 'metropolis', -apple-system, 'Helvetica Neue', sans-serif;
  overflow-x: clip;   /* clip: real clipping, no scroll container —
     sticky stays viewport-relative and body's auto height keeps y unclipped */
  /* A short slate must not leave the footer floating mid-screen: the page
     is a column at least one viewport tall, the listing absorbs the slack,
     and the footer rests on the bottom edge. */
  display: flex; flex-direction: column;
  min-height: 100vh; min-height: 100dvh;
}
main { flex: 1 0 auto; }

/* ---- top bar (sticky: the title rides above the scrolling listing) ----
   The chrome's glass lives on #chrome-glass, ONE fixed sheet behind the
   whole chrome (tape + title + band slot). Stacked backdrop-filter
   elements each blur independently and iOS draws a visible boundary
   between them — a single sheet has no interior seams. The topbar itself
   is transparent; its text just rides above the sheet. */
.topbar { position: sticky; top: 0; z-index: 30; }
#chrome-glass { position: fixed; top: 0; left: 0; right: 0; z-index: 6;
                height: calc(var(--top-h) + var(--band-h, 0px));
                background: rgba(0,0,0,.55);
                -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
header { display: flex; align-items: baseline; gap: 22px; padding: 20px var(--edge-x) 12px;
         user-select: none; }
/* Same face and color treatment as the landing page title: metropolis bold,
   all white (no gold split); the whole mark links back to the site. */
.wordmark { font-weight: 700; font-size: 25px; letter-spacing: .5px;
            color: var(--white); text-decoration: none; cursor: pointer;
            display: inline-block; }
.wordmark:hover { color: var(--white); }
.tower { height: 44px; vertical-align: middle; margin: 0 14px 5px 0; }
.spacer { flex: 1; }
.conn { display: flex; align-items: center; gap: 8px; font-size: 12px; letter-spacing: 2px;
        text-transform: uppercase; color: var(--silver); }
/* connecting = amber lamp (an exchange never shows a dead gray light) */
.dot { width: 9px; height: 9px; border-radius: 5px; background: #e8a33d;
       box-shadow: 0 0 8px rgba(232,163,61,.65); animation: pulse 2.2s ease-in-out infinite; }
.dot.live { background: var(--green); box-shadow: 0 0 8px rgba(84,215,56,.7); }
/* pull the animated dots flush against "Connecting" (cancel the flex gap) */
#conndots { margin-left: -8px; }
@keyframes pulse { 50% { opacity: .45; } }
.clock { font-family: var(--mono); font-weight: 600; font-size: 24px; color: var(--white); }
/* The iOS WFTEllipsisText, in CSS: dots step 1 → 2 → 3 → 1 every .4s, and
   all three ALWAYS occupy layout (invisible, not removed) so the line is
   centered once in its full width and never shifts as dots come and go. */
.edots span { animation: 1.2s steps(1, end) infinite; }
.edots .d2 { animation-name: edot2; }
.edots .d3 { animation-name: edot3; }
@keyframes edot2 { 0%, 32% { opacity: 0; } 33%, 100% { opacity: 1; } }
@keyframes edot3 { 0%, 65% { opacity: 0; } 66%, 100% { opacity: 1; } }
/* zone abbreviation rides the clock in the listing's market-label voice */
.clock .tz { font-size: 12.5px; font-weight: 500; color: var(--silver); margin-left: 8px; }

/* ---- league bands: iOS-style sticky section headers ----
   Each band sticks under the topbar while its own league is on screen; the
   next league's band pushes it off. Transparent: the pinned band sits
   inside #chrome-glass's sheet. A band being PUSHED OFF by the next league
   fades out instead of sliding crisply across the title (the sheet can't
   blur it — it renders above the glass). Pages set the grid columns. */
.band { position: sticky; top: var(--top-h); z-index: 10;
        display: grid; align-items: baseline; transition: opacity .15s linear;
        padding: 14px 0 10px; border-bottom: 1px solid rgba(39,39,39,.55);
        font-size: 12.5px; font-weight: 600; letter-spacing: 4px;
        color: var(--gold); text-transform: uppercase; user-select: none; }
.band.escaping { opacity: 0; }
.band .bn { grid-column: 1 / 3; padding: 0 var(--cell-x); white-space: nowrap; }
.band .cl { color: var(--slate); font-size: 11px; letter-spacing: 3px; padding: 0 var(--cell-x); }
.llogo { height: 18px; max-width: 26px; object-fit: contain;
         vertical-align: middle; margin: -2px 9px 0 0; }

/* ---- the status cell + matchup voices (markup from js/exchange-ui.js) ---- */
.matchup { font-weight: 600; font-size: 15px; color: var(--white);
           letter-spacing: .2px; white-space: nowrap; }
/* matchup-as-gamecast-link: quiet until hovered, then the names go gold */
a.espn { color: inherit; text-decoration: none; }
a.espn:hover .matchup { color: var(--gold); }
.matchup .dot { display: inline-block; vertical-align: middle; margin-left: 12px;
                margin-bottom: 2px; }
.matchup .at { color: var(--slate); font-weight: 400; padding: 0 6px; }
.matchup .sc { font-family: var(--mono); font-weight: 600; padding-left: 7px; }

.st { font-family: var(--mono); font-size: 13px; white-space: nowrap; }
.st .score { color: var(--white); font-weight: 600; }
.st .detail { display: inline-block; vertical-align: middle; color: var(--white); font-weight: 600; }
.st .sub { display: block; font-size: 11px; font-weight: 500; color: var(--silver); margin-top: 4px; }
.st .sub.rz { color: var(--red); }              /* red zone */
/* mini base diamond: 2nd top-center, 3rd left, 1st right; lit = occupied */
.st .bases { display: inline-block; position: relative; width: 21px; height: 13px;
             margin-left: 8px; vertical-align: -2px; }
.st .bases i { position: absolute; width: 6px; height: 6px; transform: rotate(45deg);
               border: 1px solid var(--slate); }
.st .bases i.on { background: var(--gold); border-color: var(--gold); }
.st .bases .b2 { top: 0; left: 7px; }
.st .bases .b3 { top: 6px; left: 0; }
.st .bases .b1 { top: 6px; left: 14px; }
.st.pre { color: var(--slate); }

/* ---- boot -> exchange handoff ----
   The boot skeleton dims out (150ms), then the real data blooms in.
   Sequenced in normal flow — no overlap, no layout jump. */
main { padding: 0 0 26px; }
/* ---- the page edge ----
   The listing insets as ONE unit rather than by widening cell padding, so
   the band's column labels keep sitting exactly over their columns. The
   subtraction is what makes row text land on --edge-x itself: the cells
   already contribute --cell-x of their own. Pages that restack into blocks
   zero this out and apply --edge-x directly to the rows. */
section.league { padding-inline: calc(var(--edge-x) - var(--cell-x)); }
.empty { transition: opacity .14s linear; }
.empty.out { opacity: 0; }
.arrive { animation: xg-arrive .35s ease-out; }
@keyframes xg-arrive { from { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .arrive { animation: none; }
  .empty { transition: none; }
}
