/* bracket.css — public page: tabs, bracket layout, round view, team view */

/* ── Update notification bar ── */
.update-bar {
  background: var(--blue);
  color: #fff;
  text-align: center;
  padding: 0.35em 1em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-sm);
  font-weight: 500;
  letter-spacing: 0.04em;
  position: sticky;
  top: var(--hdr-h, 0px);
  z-index: 95;
  flex-shrink: 0;
}

/* ── Offline notice banner (fetch failed; last-good data shown) ── */
.offline-notice {
  background: color-mix(in srgb, var(--lose-color) 14%, transparent);
  border-bottom: 0.08em solid color-mix(in srgb, var(--lose-color) 45%, transparent);
  color: var(--lose-color);
  text-align: center;
  padding: 0.55em 1em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-sm);
  font-weight: 600;
  flex-shrink: 0;
}

/* ── Weather / delay notice banner ── */
/* Deliberately the loudest thing on the page (owner, 2026-08-02: "needs to be
   more prominent. It shouldn't be incidentally overlooked. This is for TDs to
   communicate to everyone that a significant schedule change has happened").
   It was a 14%-opacity tint at --text-sm, which read as decoration next to the
   real content. Now a SOLID fill with dark text — the one element on the page
   that inverts the usual tinted-banner treatment, on purpose, because it is
   the only one that means "what you were about to do has changed". A TD clears
   it when the delay is over, so it costs nothing when nothing is wrong. */
.delay-notice {
  /* FIXED colour, deliberately outside the theme palette — do not swap this
     for --gold or any other token (owner, 2026-08-02: "I would possibly make
     it not the tournament style yellow either"). Two reasons, and the second
     is a correctness one: (1) --gold IS the tournament's brand colour, so an
     alert wearing it reads as more branding rather than as an exception;
     (2) a TD's accent colour is mapped straight onto --gold/--gold-lt by
     theme-css.js, so a brand-coloured alert restyles itself per tournament —
     and a TD who picks a dark accent would get this banner's dark text on a
     dark fill, i.e. an invisible emergency notice. Orange is the warning
     convention, and is distinct from both the brand gold and the --lose-color
     red used for eliminations and the offline notice. 6.47:1 against the text
     colour below, comfortably past AA. */
  --tt-alert: #f4761a;
  background: var(--tt-alert);
  color: #14160f;
  border-bottom: 0.14em solid color-mix(in srgb, var(--tt-alert) 60%, #000);
  text-align: left;
  padding: 0.8em 1.1em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-lg);
  font-weight: 700;
  letter-spacing: 0.01em;
  line-height: 1.35;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.6em;
  box-shadow: 0 0.15em 0.5em rgba(0, 0, 0, 0.28);
}
.delay-notice-icon { width: 1.3em; height: 1.3em; flex-shrink: 0; }
/* Fires when the notice APPEARS or its wording changes — never on a re-poll of
   a standing notice, which would strobe at the fetch interval (see
   updateDelayNotice). Three pulses, then it sits still and stays loud. */
@keyframes tt-delay-attn {
  0%, 100% { box-shadow: 0 0.15em 0.5em rgba(0, 0, 0, 0.28); }
  50%      { box-shadow: 0 0 0 0.4em color-mix(in srgb, var(--tt-alert) 55%, transparent); }
}
.delay-notice--new { animation: tt-delay-attn 1.1s ease-in-out 3; }
@media (prefers-reduced-motion: reduce) { .delay-notice--new { animation: none; } }

/* ── Tabs ── */
.view-tabs {
  display: flex;
  background: var(--surface);
  border-bottom: 0.07em solid var(--border);
  position: sticky;
  top: calc(var(--hdr-h, 0px) + var(--bar-h, 0px));
  z-index: 90;
}
.view-tab {
  flex: 1;
  text-align: center;
  padding: 0.85em 0.3em;
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  cursor: pointer;
  border-bottom: 0.18em solid transparent;
  transition: all 0.15s;
  min-height: 3em;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3em;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.tab-icon {
  width: 1.4em; height: 1.4em;
  flex-shrink: 0;
  display: none; /* shown only on mobile via media query */
}
.tab-label { line-height: 1; }
.view-tab.active { color: var(--blue-lt); border-bottom-color: var(--blue-lt); }
.view-tab:active { background: color-mix(in srgb, var(--text) 5%, transparent); }

/* ── Panels ── */
.view-panel { display: none; }
.view-panel.active { display: block; }

/* ── Selectors ── */
.selector-row {
  position: sticky;
  top: calc(var(--hdr-h, 0px) + var(--bar-h, 0px) + var(--tabs-h, 0px));
  z-index: 80;
  background: var(--surface);
  border-bottom: 0.15em solid var(--blue);
  box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.selector-inner {
  max-width: 65em;
  margin: 0 auto;
  padding: 0.8em 1em;
  display: flex;
  gap: 0.6em;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
}

/* ── Bucket Challenge leaderboard ── */
.bucket-status-wrap {
  flex-shrink: 0;
  padding: 0.9em 1em 0;
}
.bucket-status-bar {
  background: var(--surface2);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  text-align: center;
  padding: 0.55em 1.5em;
  font-family: 'Barlow', sans-serif;
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.03em;
  color: var(--text-dim);
}
.bucket-panel {
  max-width: 42em;
  margin: 0 auto;
  padding: 0.35em 1em 3em;
}
.bucket-leaderboard {
  display: flex;
  flex-direction: column;
  gap: 0.35em;
}
.bucket-row {
  display: flex;
  align-items: center;
  gap: 0.75em;
  background: var(--surface);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.75em 1em;
}
.bucket-rank {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--text-mute);
  min-width: 1.8em;
  text-align: center;
}
/* Medal backgrounds for top-3 rows */
.bucket-row--1 {
  background: var(--medal-gold-bg);
  border-color: var(--medal-gold-border);
}
.bucket-row--1 .bucket-rank,
.bucket-row--1 .bucket-team-name,
.bucket-row--1 .bucket-count { color: var(--medal-gold-text); }

.bucket-row--2 {
  background: var(--medal-silver-bg);
  border-color: var(--medal-silver-border);
}
.bucket-row--2 .bucket-rank,
.bucket-row--2 .bucket-team-name,
.bucket-row--2 .bucket-count { color: var(--medal-silver-text); }

.bucket-row--3 {
  background: var(--medal-bronze-bg);
  border-color: var(--medal-bronze-border);
}
.bucket-row--3 .bucket-rank,
.bucket-row--3 .bucket-team-name,
.bucket-row--3 .bucket-count { color: var(--medal-bronze-text); }

/* Frozen top-3 block — desktop: sticky below tabs/bar/header */
.bucket-frozen {
  position: sticky;
  top: calc(var(--hdr-h, 0px) + var(--bar-h, 0px) + var(--tabs-h, 0px));
  z-index: 70;
  background: var(--bg);
  max-width: 42em;
  margin: 0 auto;
  padding: 1em 1em 0.35em;
  border-bottom: 0.07em solid var(--border);
}
.bucket-leaderboard--top3 { gap: 0.35em; }
.bucket-team-name {
  flex: 1;
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.bucket-count {
  font-family: 'Oswald', sans-serif;
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--blue-lt);
  min-width: 2.5em;
  text-align: right;
}
/* ── Standings board ──
   Deliberately built from the Bucket Challenge row card above, not from the
   pool tables: the two share the fourth tab slot, so a visitor switching
   between them should not feel like they changed apps. Same surface, border,
   radius, rhythm and Oswald team name; no medal colouring (owner call — a
   bucket leader is a prize, a standings leader is just today's top of the
   list). The stats live inside the row instead of needing header cells, so
   there is no table chrome to reintroduce. The pool tables embedded in the
   Schedule tab keep .standings-table — a compact grid is right above a list
   of games. */
.std-board {
  max-width: 42em;
  margin: 0 auto;
  padding: 0.75em 0 0.35em;
  display: flex;
  flex-direction: column;
  gap: 0.35em;
}
.std-row {
  display: flex;
  align-items: center;
  gap: 0.75em;
  background: var(--surface);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.6em 1em;
  /* Explicit, not just content-driven — every row the same height regardless
     of name length or whether it has a logo (owner, 2026-08-02: "all divs
     should be the same height. Logos should not increase the height of the
     div"). Matches .team-chip/.team-brand-banner's own height for the same
     reason. overflow:hidden is the actual enforcement — without it a taller
     child would still push the row open despite the explicit height. */
  height: 3.85rem;
  overflow: hidden;
}
/* De-emphasize rank and record for a team that hasn't played, NOT the name
   itself — this used to be opacity on the whole row, which dimmed the team
   name along with everything else (owner, 2026-08-02: "standings team
   names should be the same color white as an active game card" — it was
   reading closer to a TBD placeholder's muted color instead, purely from
   this opacity, since .std-team's own color was already correct). The "—"
   in place of a rank number already says "not ranked yet" on its own. */
.std-row--unplayed .std-rank,
.std-row--unplayed .std-meta { opacity: 0.55; }
/* Colored left edge for a team that's set its own colors — squared off
   rather than following the card's rounded corners, which is what a flush
   accent bar looks like everywhere else this pattern shows up. Color itself
   comes from data-css (border-left-color), set per-row in bracket.js. */
.std-row--branded { border-left-width: 0.35em; border-left-style: solid; border-top-left-radius: 0; border-bottom-left-radius: 0; }
/* A dedicated slot between the name and the record, logo centered inside it
   — NOT sized to hug the name (owner, 2026-08-02: "Undo moving the team
   logo in standings. The record should always be right justified. Find a
   reasonable width constraint to the left of the record and center logos
   within that space"). Only rendered when a row actually has a logo, so an
   unbranded row doesn't reserve blank space — .std-main stays flex:1 either
   way, so .std-figures lands at the same right-justified position
   regardless. Right 3/8 of the row, same fraction as .team-brand-logo-slot
   for consistency (owner, 2026-08-02: "center the logo in the right 3/8 of
   the div instead of 1/3" — mobile may need its own number given how much
   more of a narrow row the team name itself needs, not yet split out). */
.std-logo-slot { flex: 0 0 37.5%; min-width: 0; display: flex; align-items: center; justify-content: center; }
/* Height is the sizing dimension (owner, 2026-08-02: "logos shouldn't expand
   the height of a card... should expand/shrink based on the card height") —
   comfortably under the row's own text-driven height (.std-main's two-line
   name+meta stack plus padding). max-width:100% additionally constrains it to
   the slot above, so a wide logo shrinks to fit rather than overflowing it. */
.std-team-logo { height: 3.25rem; width: auto; max-width: 100%; object-fit: contain; border-radius: var(--radius-xs); }
/* Mobile/PWA Standings needs a narrower slot than desktop — the team name
   column is tighter here than on Team Path's selector chips or the
   team-brand-banner, which share this same 37.5% fraction on desktop (owner,
   2026-08-02: "The logo position for standings on mobile/pwa should be
   closer to centered on right 1/3 instead of 3/8"). Same breakpoint the
   bracket view uses to switch to its mobile carousel (window.innerWidth <
   1280), so "mobile" means the same thing here as everywhere else in the app. */
@media (max-width: 1279px) {
  .std-logo-slot { flex: 0 0 33.33%; }
}
.std-rank {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--text-mute);
  min-width: 1.8em;
  text-align: center;
  flex-shrink: 0;
}
.std-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.15em; }
/* Flex ROW (seed chip + name), same shape as .bc-team on bracket cards —
   .std-main's own column flex only stretches ONE box per line, so the chip
   and name need their own row here to sit side by side instead of stacking
   (owner, 2026-08-02: short name should take over when the full one is
   actually truncated, same mechanism bracket cards already use). */
.std-name-line { display: flex; align-items: center; gap: 0.3em; min-width: 0; }
.std-team {
  flex: 1;
  min-width: 0;
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Runs for / against — secondary, so they read as detail under the name
   rather than competing with the record. */
.std-meta {
  display: flex;
  gap: 0.9em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  letter-spacing: 0.06em;
  color: var(--text-mute);
}
.std-stat b {
  font-weight: 600;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.std-figures {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.05em;
  min-width: 3.4em;
}
/* The row's headline figure, in the same slot and weight as .bucket-count */
.std-record {
  font-family: 'Oswald', sans-serif;
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--blue-lt);
  font-variant-numeric: tabular-nums;
}
.std-record-sep { opacity: 0.5; margin: 0 0.1em; }
.std-diff {
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--text-mute);
  font-variant-numeric: tabular-nums;
}
.std-diff--pos { color: var(--win-color); }
.std-diff--neg { color: var(--lose-color); }
/* The tiebreak order is printed under the board: a TD gets asked "why is that
   team above us" and needs the answer on the same screen. */
.std-tiebreaks {
  /* rem, not em: this block sets its own small font-size, so 42em here would
     measure against THAT and the rule would come out narrower than the board
     it sits under. */
  max-width: 42rem;
  box-sizing: border-box;
  margin: 0 auto;
  padding: 0.9em 0 1em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-mute);
  border-top: 0.07em solid var(--border);
}
/* Projection notice — same block, tinted like every other pick affordance. */
.std-tiebreaks--proj { color: var(--blue-lt); border-top-color: var(--blue); }
.bucket-icon-sm {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
  opacity: 0.5;
  color: var(--text-dim);
}
.bucket-empty {
  text-align: center;
  color: var(--text-mute);
  padding: 3em 1em;
  font-style: italic;
}

/* ── Bucket edit mode (bucket-role users) ── */
.bucket-edit-wrap {
  max-width: 42em;
  margin: 0 auto;
  padding: 0.75em 1em 0.5em;
  display: flex;
  flex-direction: column;
  gap: 0.4em;
}
.bucket-edit-row {
  display: flex;
  align-items: center;
  gap: 0.75em;
  background: var(--surface);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0.6em 0.85em;
}
.bucket-edit-name {
  flex: 1;
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.bucket-edit-inp {
  width: 4.5em;
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-lg); /* 1rem = 16px, also prevents iOS zoom */
  font-weight: 700;
  text-align: right;
  background: var(--bg);
  border: 0.07em solid var(--border);
  border-radius: 0.25em;
  color: var(--blue-lt);
  padding: 0.2em 0.4em;
}
.bucket-edit-actions {
  max-width: 42em;
  margin: 0 auto;
  padding: 0.75em 1em 3em;
  display: flex;
  align-items: center;
  gap: 0.65em;
}
.bucket-edit-cancel-btn {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 0.55em 1.1em;
  border-radius: var(--radius-xs);
  border: 0.07em solid var(--border);
  cursor: pointer;
  background: transparent;
  color: var(--text-dim);
}
.bucket-edit-cancel-btn:hover,
.bucket-edit-cancel-btn:active { color: var(--text); border-color: var(--text-dim); }
/* bucket-edit-save-btn: base styles from base.css primary-button rule */
.bucket-edit-status {
  font-size: 0.8rem;
  color: var(--text-dim);
  margin-left: 0.25em;
}

/* ── Round sections (By Round all-rounds layout) ── */
.round-section { max-width: 65em; margin: 0 auto; padding: 0 1em; }
.round-section-hdr {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-lg);
  font-weight: 600;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--gold-lt);
  padding: 1.2em 0 0.6em;
  border-bottom: 0.07em solid var(--border);
  margin-bottom: 1em;
}
.selector-row label {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-sm);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--blue-lt);
  flex-shrink: 0;
  font-weight: 600;
}

/* ── Schedule two-column selector ── */
.sched-selector-inner {
  align-items: stretch;
  justify-content: flex-start;
  flex-wrap: nowrap;
  gap: 0.75em;
}
.sched-select-wrap {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.sched-select-wrap--group { flex: 3; }
.sched-select-wrap--view  { flex: 5; }
.sched-select { width: 100%; }
select.styled {
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-lg);
  font-weight: 600;
  padding: 0.75em 0.9em;
  border-radius: var(--radius-sm);
  border: 0.1em solid var(--blue);
  background: var(--surface2);
  color: var(--text);
  cursor: pointer;
  flex: 1;
  min-height: 3.2em;
}
select.styled:focus { outline: 0.13em solid var(--blue-lt); outline-offset: 0.07em; }

.team-share-btn {
  font-family: 'Oswald', sans-serif;
  font-size: var(--btn-font-size);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: var(--btn-pad-v) var(--btn-pad-h);
  border-radius: var(--radius-xs);
  border: 0.07em solid var(--border);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
  touch-action: manipulation;
  min-height: 2.75rem;
}
.team-share-btn:hover,
.team-share-btn:active { color: var(--text); border-color: var(--text-dim); }

/* ════════════════════════════════════════════════════════════
   BRACKET LAYOUT
   ════════════════════════════════════════════════════════════ */

.bracket-outer {
  display: flex;
  align-items: flex-start;
  width: 100%;
  overflow-x: auto;
  padding: calc(var(--col-hdr-h) + 0.5em) 0 1em 0;
}

.bracket-side {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: flex-start;
}
.bracket-side.left-side  { justify-content: flex-end; }
.bracket-side.right-side { justify-content: flex-start; }

.rounds-wrap {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  position: relative;
  max-width: 65em;
  margin: 0 auto;
}

/* ── Open/A/B flight switcher (tiered format) ── */
.bracket-flights {
  display: flex;
  gap: 0.4em;
  justify-content: center;
  /* Top margin (space OUTSIDE the row) rather than padding: this row paints
     its own --bg panel, so padding-top would grow that panel instead of
     moving the pills down off the header rule above them. */
  margin: 1em auto 0.8em;
  max-width: 30em;
  /* The bracket canvas below is absolutely positioned and can extend up over
     this control row; keep the pills on top so they stay clickable. */
  position: relative;
  z-index: 5;
  background: var(--bg);
  padding: 0.15em 0.4em;
  border-radius: var(--radius-sm);
}
.sched-flights { margin: 1em auto 0.4em; }
.flight-pill {
  flex: 1;
  font-family: 'Oswald', sans-serif;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.5em 0.6em;
  border-radius: var(--radius-xs);
  border: 0.07em solid var(--border);
  background: transparent;
  color: var(--text-dim);
  cursor: pointer;
  white-space: nowrap;
  min-height: 2.4em;
}
.flight-pill.active { background: var(--blue); border-color: var(--blue); color: #fff; }
.flight-pill:hover:not(.active) { color: var(--text); border-color: var(--text-dim); }
@media (pointer: coarse) { .flight-pill { min-height: 44px; } }

/* A/B flights: simple left-to-right round columns (their own single-elim
   brackets, rendered outside the Open tree). Horizontal-scroll on overflow. */
.flight-bracket {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 1.4em;
  overflow-x: auto;
  padding: 0.5em 0.75em 1em;
  max-width: 100%;
  /* Containing block for the measured connector overlay below. */
  position: relative;
}
/* Connector overlay: sized to the flight's full scroll area and drawn in the
   same coordinate space as the cards, so it scrolls with them. Painted under
   the cards (they carry z-index 1) and never intercepts taps. */
.flight-conn-svg {
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  overflow: visible;
}
.flight-col {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  gap: 0.9em;
  flex: 0 0 auto;
  min-width: 11em;
}
/* Flight championship column: trophy header above the final's card,
   centered as a group (no space-around spread), gold-tinted like the
   main championship column. */
.flight-col--champ {
  justify-content: center;
  gap: 0.6em;
  padding: 0 1em;
  background: color-mix(in srgb, var(--gold) 4%, transparent);
  border-left: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
  border-right: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
}
.flight-col--champ .champ-winner { text-align: center; }
.flight-col-hdr {
  text-align: center;
  font-family: 'Oswald', sans-serif;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 0.2em;
}
/* Cards in a flight are statically laid out. The double class out-ranks the
   later `.bc { position: absolute }` rule — with equal specificity the
   cascade would let .bc win and stack every flight card at the same spot. */
.bc.flight-bc {
  position: relative;
  left: auto;
  right: auto;
  min-height: 0;
  z-index: 1;   /* above .flight-conn-svg */
}
.flight-empty {
  text-align: center;
  color: var(--text-mute);
  padding: 2em 1em;
  font-family: 'Barlow', sans-serif;
}
/* "View Schedule" action inside the bracket-tab empty state */
.bracket-empty-goto {
  display: inline-block;
  flex: none;
  margin-top: 1em;
}

.round-col {
  position: relative;
  flex-shrink: 0;
  width: var(--col-w);
  height: calc(var(--slot-h) * 8);
}

.round-col-hdr {
  position: absolute;
  top: calc(var(--col-hdr-h) * -1.1);
  left: 0;
  right: 0;
  text-align: center;
  font-family: 'Oswald', sans-serif;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  white-space: nowrap;
}

.round-gap {
  position: relative;
  flex-shrink: 0;
  width: var(--col-gap);
  height: calc(var(--slot-h) * 8);
}
.round-gap svg {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  overflow: visible;
}

/* ── Game card (bracket) ── */
.bc {
  position: absolute;
  left: 0;
  right: 0;
  min-height: var(--card-h);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  overflow: visible;
  background: var(--surface);
  display: flex;
  flex-direction: column;
}
.bc-hdr {
  flex: 1;
  display: flex;
  justify-content: flex-start;
  align-items: center;
  padding: 0 0.45em;
  background: var(--surface2);
  border-bottom: 0.07em solid var(--border);
  min-height: 0;
}
.bc-id {
  font-family: 'Oswald', sans-serif;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  color: var(--blue-lt);
  white-space: nowrap;
  line-height: 1;
}
.bc-time {
  font-size: 0.68rem;
  color: var(--gold-lt);
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* No width cap: with show-date on, "7/15 Wed 9:00 PM" overran the old
     60% and ellipsized. Let it use the header's leftover space instead —
     min-width:0 keeps ellipsis as the true last resort, and
     shrinkFieldTags() already frees room by abbreviating field tags. */
  flex: 0 1 auto;
  min-width: 0;
  text-align: right;
  margin-left: auto;
  line-height: 1;
}
/* Desktop bracket team names: single line with ellipsis to prevent crowding */
.bc-team {
  flex: 1;
  /* A flex item's min-width defaults to auto (its content's natural size),
     not 0 — without this, a long unbreakable name pushed the whole CARD
     wider instead of ever reaching .bc-name's own overflow:hidden, which
     also silently defeated fitTeamCardNames()'s overflow check (nothing
     to detect if nothing was actually clipped). */
  min-width: 0;
  padding: 0.2em 0.45em;
  font-size: 0.64rem;
  font-weight: 600;
  color: var(--text);
  border-bottom: 0.07em solid var(--border);
  overflow: hidden;
  line-height: 1.25;
  display: flex;
  align-items: center;
  gap: 0.3em;
  min-height: var(--row-h);
}
.bc-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tt-seed {
  flex: none;
  font-family: 'Barlow', sans-serif;
  font-size: 0.72em;
  font-weight: 400;
  color: var(--text-mute);
  margin-right: 0.25em;
  white-space: nowrap;
}
.game-score {
  font-family: 'Oswald', sans-serif;
  font-weight: 700;
  font-size: 0.9em;
  color: var(--text-dim);
  flex-shrink: 0;
  min-width: 2ch;
  text-align: right;
  letter-spacing: 0.02em;
}
.game-score.live-score { color: var(--blue-lt); }
/* Final game: score matches team name color */
.bc-team.won .game-score,
.mbc-champ-team.won .game-score,
.mbc-game-team.won .game-score { color: var(--win-color); }
.bc-team.lost .game-score,
.mbc-champ-team.lost .game-score,
.mbc-game-team.lost .game-score { color: var(--lose-color); }
.round-team-row:has(.round-team-name.won) .game-score { color: var(--win-color); }
.round-team-row:has(.round-team-name.lost) .game-score { color: var(--lose-color); }
.live-badge {
  display: inline-block;
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--live-color);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.live-period {
  display: inline-block;
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--gold-lt);
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.live-updated {
  display: inline-block;
  font-family: 'Barlow', sans-serif;
  font-size: 0.78em;
  font-weight: 400;
  color: var(--text-mute);
  white-space: nowrap;
}
/* Live indicator occupies the date/time slot — keep it on one row. */
.bc-time-live, .round-game-live, .mbc-game-live, .mbc-champ-live {
  display: inline-flex;
  flex-direction: row;
  align-items: baseline;
  gap: 0.65em;
  flex-wrap: nowrap;
}
.bc-time-live { max-width: none; overflow: visible; }
.bc-time-live .live-badge,
.bc-time-live .live-period { font-size: 1em; }
.bc-team:last-child { border-bottom: none; }
.bc-team.tbd  { color: var(--text-mute); font-style: italic; font-weight: 400; font-size: 0.7rem; }
.bc-team.bye  { }
.bc-bye-label {
  display: block;
  width: 100%;
  text-align: center;
  color: color-mix(in srgb, var(--text) 45%, transparent);
  font-style: italic;
  font-weight: 300;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
}
.bc-team.won  { color: var(--win-color); }
.bc-team.lost { color: var(--lose-color); }

/* ── Admin drag-and-drop ── */
.bc-dragging    { opacity: 0.3; pointer-events: none; }
.bc-drop-target { box-shadow: 0 0 0 2px var(--blue), 0 0 12px color-mix(in srgb, var(--blue-lt) 35%, transparent); }
/* Drop rows are thin BARS, not card-sized outlines. The grid is half a
   bracket row apart — fine enough to express a game centred between two games
   that are themselves off-grid — and card-sized ghosts at that pitch would
   overlap each other and bury the cards underneath. `top` addresses the row's
   centre line, hence the translate. */
.bc-drop-row {
  position: absolute;
  left: 6%;
  right: 6%;
  height: 0.5em;
  transform: translateY(-50%);
  border-top: 2px dashed var(--line-color);
  opacity: 0.35;
  pointer-events: all;
  cursor: default;
}
/* Rows the bracket cascade actually lands on — the midpoints of the feeding
   column's cards — read stronger, so the natural shape stays visible while a
   TD is free to ignore it. */
.bc-drop-row--cascade {
  left: 2%;
  right: 2%;
  border-top-style: solid;
  border-top-color: var(--blue);
  opacity: 0.28;
}
/* Level with a winner feeder. The default hang is a quarter-gap, which is off
   the half-step grid, so this is the only way to put a card exactly on its
   feeder's line — worth calling out as its own kind of row. */
.bc-drop-row--align {
  left: 0;
  right: 0;
  border-top-style: solid;
  border-top-color: var(--gold);
  opacity: 0.45;
}
.bc-drop-row.bc-drop-target {
  opacity: 1;
  border-top-color: var(--blue);
  border-top-width: 3px;
  box-shadow: 0 0 10px color-mix(in srgb, var(--blue-lt) 55%, transparent);
}
/* While a card is in the air the rows stop being background furniture — they
   are the thing being aimed at, so bring them up out of their resting wash.
   The class is on <body> so it reaches every column in one toggle. */
body.bc-drag-active .bc-drop-row          { opacity: 0.75; }
body.bc-drag-active .bc-drop-row--cascade { opacity: 0.85; border-top-width: 3px; }
body.bc-drag-active .bc-drop-row--align   { opacity: 0.95; border-top-width: 3px; }
body.bc-drag-active .bc-drop-row.bc-drop-target { opacity: 1; }
/* Rows inside a card's own footprint. Inert and invisible until a drag frees
   them (see freeCoveredRows) — they paint above the cards, so a live one
   would make the card underneath impossible to pick up. These must come AFTER
   the drag-active rules above: same specificity, so order decides. */
.bc-drop-row--covered,
body.bc-drag-active .bc-drop-row--covered { pointer-events: none; opacity: 0; }
body.bc-drag-active .bc-drop-row--covered.bc-drop-row--live { pointer-events: all; opacity: 0.8; }
body.bc-drag-active .bc-drop-row--covered.bc-drop-row--live.bc-drop-target { opacity: 1; }
/* The footprint the dragged card would occupy, centred on the hovered row.
   Sized in px by initBracketDrag from the card being dragged. */
.bc-slot-preview {
  position: absolute;
  left: 0;
  right: 0;
  border: 2px dashed var(--blue);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--blue) 14%, transparent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--blue-lt) 40%, transparent);
  pointer-events: none;
  z-index: 3;
}
/* Placed by hand — this card renders at its slot row instead of wherever the
   automatic layout would put it. The class is only emitted while editing
   (see sideHTML), so the published bracket carries no extra ink. */
.bc-pinned::after {
  content: "";
  position: absolute;
  top: 3px; right: 3px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--blue);
  opacity: 0.75;
  pointer-events: none;
}
.bc-drag-ghost  {
  opacity: 0.88;
  transform: rotate(1.5deg);
  box-shadow: 0 8px 28px rgba(0,0,0,0.55);
  cursor: grabbing !important;
}
/* Active state for the bracket Edit button */
.admin-btn-active { color: var(--blue-lt) !important; border-color: var(--blue) !important; }
/* Hide bracket edit button on mobile — drag-and-drop is desktop only */
@media (max-width: 1279px) {
  #bracketEditBtn { display: none !important; }
}

/* ── Championship center ── */
.champ-center {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0 0.5em;
  background: color-mix(in srgb, var(--gold) 4%, transparent);
  border-left: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
  border-right: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
  width: var(--col-w);
  height: calc(var(--slot-h) * 8);
}
.champ-inner { width: 100%; text-align: center; }
.champ-trophy { font-size: 1.8em; margin-bottom: 0.3em; }
/* Trophy renders from the icon file, not the 🏆 emoji — desktop systems
   without a color-emoji font showed an empty box. Sized in em so it
   inherits each wrapper's font-size. */
.trophy-img { width: 1em; height: 1em; vertical-align: -0.12em; }
.champ-title {
  font-family: 'Oswald', sans-serif;
  font-size: 0.68rem;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--gold);
  margin-bottom: 0.3em;
}
.champ-gametime {
  font-size: 0.74rem;
  color: var(--gold-lt);
  font-weight: 600;
  margin-bottom: 0.5em;
}
.champ-bc { position: relative; height: var(--card-h); }
.champ-winner { font-family: 'Oswald', sans-serif; font-size: 0.95rem; color: var(--gold-lt); letter-spacing: 0.07em; margin-top: 0.5em; }

/* ── Desktop championship column ── */
.champ-center--desktop {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  /* stretch (not center): .champ-bc must span the column like the
     original width:100% wrapper, or the card collapses to content width */
  align-items: stretch;
  justify-content: flex-start;
  padding: 0 0.5vw;
  /* Horizontal breathing room between the tinted column and the SF cards.
     The 0.8vw per side is subtracted from the inline width in
     renderDesktopBracket so the total stays champVw. */
  margin: 0 0.8vw;
  background: color-mix(in srgb, var(--gold) 4%, transparent);
  border-left: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
  border-right: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
}
.champ-center--desktop .champ-winner { text-align: center; }
/* Championship card anchored to the feeder row: the wrapper's top is the
   row both feeder games enter at; the trophy/title block floats ABOVE the
   card so the card itself lines up with its feeders. */
.champ-anchor { position: absolute; left: 0.5vw; right: 0.5vw; }
.champ-inner--above {
  position: absolute;
  bottom: 100%;
  left: 0;
  right: 0;
  margin-bottom: 1.2vh;
}
.champ-center--ifnec {
  width: 100%;
  margin-top: 1.5vh;
  text-align: center;
}
.champ-ifnec-label {
  font-family: 'Oswald', sans-serif;
  font-size: 0.55em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 0.15em;
}
.champ-center--ifnec.champ-ifnec--pending {
  opacity: 0.5;
}
.champ-ifnec-note {
  font-size: 0.52em;
  color: var(--text-mute);
  font-style: italic;
  margin-bottom: 0.4em;
  line-height: 1.3;
}

/* Bracket side label */
.bracket-side-label {
  display: block;
  width: fit-content;
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  padding: 0.15em 0.4em 0.15em 0;
  background: transparent;
  color: var(--text-dim);
  border-bottom: 0.1em solid var(--border);
  position: absolute;
  top: calc(var(--col-hdr-h) * -2);
  cursor: default;
  user-select: none;
}
.left-side  .bracket-side-label { left: 0; }
.right-side .bracket-side-label { right: 0; }

.bracket-side-wrap {
  flex: 1;
  min-width: 0;
  position: relative;
}

/* ── Carousel round column header (tablet + mobile) ── */
.carousel-round-hdr {
  font-family: 'Oswald', sans-serif;
  font-size: 0.70rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--gold-lt);
  text-align: center;
  padding: 0.3em 0 0.5em;
}

/* ── Mobile bracket (legacy classes, kept for compatibility) ── */
.mobile-bracket-wrap {
  width: 85vw;
  margin: 0 auto;
  overflow-x: auto;
  display: flex;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 0;
  scroll-padding-right: 15vw;
  padding-right: 15vw;
}
.mobile-bracket-section { flex: 0 0 85vw; width: 85vw; }
.mobile-bracket-label {
  padding: 0.5em 0 0.25em;
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
.mobile-bracket-divider { margin: 0.5em 0; height: 0.07em; background: var(--border); }
.mobile-bracket-svg-wrap { overflow: visible; }

/* ── Round view ── */
.round-panel { padding: 0 0 1.5em; }
/* ── W/L legend ── */

/* ── Round games 2-column grid on desktop ── */
.round-games-grid { display: contents; }
@media (min-width: 1024px) {
  .round-games-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 1.5em;
  }
}

.round-game-card {
  border: 0.07em solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
  margin-bottom: 1.4em;
}
.round-game-card.champ2-pending {
  opacity: 0.55;
  border-style: dashed;
}
.round-game-card.champ2-not-needed {
  opacity: 0.35;
  border-style: dashed;
  text-decoration-line: line-through;
}
.champ2-rv-note {
  font-size: var(--text-xs);
  color: var(--text-mute);
  font-style: italic;
  padding: 0.3em 0.8em 0;
}
.champ2-rv-note--nn {
  color: var(--lose-color);
  font-style: normal;
  font-weight: 600;
}
.round-game-card.upcoming {
  opacity: 0.75;
  border-style: dashed;
}
.round-game-header {
  display: flex;
  align-items: center;
  gap: 0.6em;
  padding: 0.6em 1em;
  background: var(--surface2);
  border-bottom: 0.07em solid var(--border);
}
.round-game-id {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.07em;
  color: var(--blue-lt);
  flex-shrink: 0;
}
.round-game-day-time { display: flex; align-items: baseline; gap: 0.45em; flex: 1; justify-content: flex-end; }
.round-game-day  { font-family: 'Oswald', sans-serif; font-size: 0.82rem; font-weight: 600; color: var(--text-dim); line-height: 1; flex-shrink: 0; }
.round-game-dow  { font-family: 'Oswald', sans-serif; font-size: 0.95rem; font-weight: 700; color: var(--gold-lt); line-height: 1; }
.round-game-time { font-family: 'Oswald', sans-serif; font-size: 0.95rem; font-weight: 700; color: var(--gold-lt); line-height: 1; }
.round-game-offset { font-family: 'Oswald', sans-serif; font-size: 0.78rem; font-weight: 700; line-height: 1; margin-left: 0.35em; }
.round-game-offset--late  { color: var(--lose-color); }
.round-game-offset--early { color: var(--win-color); }
.round-team-row {
  display: flex;
  align-items: center;
  padding: 0.75em 1em;
  border-bottom: 0.07em solid var(--border);
  min-height: 3.2em;
  gap: 0.6em;
}
.round-team-row:last-child { border-bottom: none; }
.round-team-name { font-size: clamp(1rem, 0.08vw + 0.98rem, 1.15rem); font-weight: 700; flex: 1; }
.round-team-name.tbd  { color: var(--text-mute); font-style: italic; font-weight: 400; }
.round-team-name.bye  { color: var(--text-mute); font-style: italic; font-weight: 400; opacity: 0.55; letter-spacing: 0.08em; }
.round-team-name.won  { color: var(--win-color); }
.round-team-name.lost { color: var(--lose-color); }
/* ── Team view ── */
.team-panel { max-width: 65em; margin: 0 auto; padding: 0 1em 1.5em; }
/* Team Path only (owner, 2026-08-02: "padding above the team header below
   the site header") — the branded banner sat flush against the selector row
   above it with no breathing room; Standings' own #standingsOut keeps the
   shared rule's 0 top padding since its rows don't need the same gap. */
#view-teampath .team-panel { padding-top: 1em; }
.team-no-games { padding: 1em; color: var(--text-mute); font-style: italic; }

/* Cards in the team panel show a lose-destination sidebar on the right */
.team-panel .round-game-card { display: flex; flex-direction: row; align-items: stretch; }
.round-game-main { flex: 1; min-width: 0; }
.round-lose-side {
  flex-shrink: 0;
  max-width: 25%;
  border-left: 0.07em solid var(--border);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3em;
  padding: 0.4em 0.25em;
  background: color-mix(in srgb, var(--lose-color) 6%, transparent);
}
.round-lose-tag {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--lose-color);
}
.round-lose-arrow {
  font-size: var(--text-md);
  color: var(--lose-color);
  line-height: 1;
  opacity: 0.75;
}
.round-lose-dest {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--text-dim);
  letter-spacing: 0.04em;
  text-align: center;
}
.round-lose-dest.out { color: var(--lose-color); }
.round-lose-arrow--cont { color: var(--win-color); opacity: 1; }

/* ── Victory sidebar (shown when team won the game) ── */
.round-win-side {
  flex-shrink: 0;
  max-width: 25%;
  border-left: 0.07em solid var(--win-color);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25em;
  padding: 0.4em 0.2em;
  background: color-mix(in srgb, var(--win-color) 10%, transparent);
}
.round-win-img {
  width: 100%;
  height: auto;
  max-height: 60%;
  object-fit: contain;
  display: block;
}
.round-win-tag {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--win-color);
  line-height: 1;
}

/* ── Team chip grid (empty state) ── */
.team-chips-prompt {
  max-width: 42em;
  margin: 1.2em auto 0.3em;
  padding: 0 1em;
  color: var(--text-dim);
  font-size: var(--text-md);
}
.team-chips-grid {
  display: flex;
  flex-direction: column;
  gap: 0.35em;
  /* No horizontal padding of its own — matches .std-board, which relies
     entirely on the shared .team-panel wrapper's padding. This used to
     double up (1em here + 1em from .team-panel), which is why chips came
     out narrower than a Standings row despite the same max-width (owner,
     2026-08-02: "same... width as the boxes for standings"). */
  padding: 1em 0 2em;
  max-width: 42em;
  margin: 0 auto;
}
.team-chip {
  display: flex;
  align-items: center;
  width: 100%;
  box-sizing: border-box;
  background: var(--surface);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  /* Same padding and explicit height as .std-row — not min-height: every
     chip the same height regardless of a logo or a wrapped/long name, same
     as every Standings row (owner, 2026-08-02: "same height... as the boxes
     for standings" / "all divs should be the same height. Logos should not
     increase the height of the div"). overflow:hidden is the enforcement. */
  padding: 0.6em 1em;
  height: 3.85rem;
  overflow: hidden;
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  cursor: pointer;
  text-align: left;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
  -webkit-tap-highlight-color: transparent;
}
.team-chip-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.team-chip:hover, .team-chip:active { background: var(--surface2); border-color: var(--blue-lt); color: var(--blue-lt); }
/* Background/text color come from data-css (arbitrary hex, per team) — same
   pattern as .team-brand-banner. White + shadow is the default text
   treatment (legible against any gradient/solid/stripes); data-css overrides
   it inline when a coach picked their own name color, which always wins
   over these rules regardless of :hover. */
.team-chip--branded { color: #fff; text-shadow: 0 1px 4px rgba(0, 0, 0, 0.65); border-color: transparent; }
.team-chip--branded:hover, .team-chip--branded:active { color: #fff; }
.team-chip.elim { opacity: 0.45; }
.team-elim-inline {
  max-width: 42em;
  margin: 0.5em auto 0.5em;
  padding: 0 1em;
  text-align: center;
}
.team-elim-inline-img {
  width: 65%;
  height: auto;
  display: block;
  margin: 0 auto;
  border-radius: var(--radius-md);
  object-fit: contain;
}
.team-path-annotation {
  padding: 0.18em 0 0 0.5em;
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: 0.05em;
  display: flex;
  gap: 0.25em;
  align-items: center;
}
.team-path-annotation--terminal { padding-bottom: 0.5em; }
.team-path-connector {
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding: 0 0 0 0.5em;
  margin: 0.1em 0 0.6em;
}
.team-path-connector-track {
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 1em;
  padding-left: 0.4em;
}
.team-path-connector-line {
  font-size: var(--text-md);
  line-height: 1;
  color: var(--win-color);
  width: auto;
  height: auto;
  background: none;
}
.team-path-connector-line::before {
  content: '↓';
}
.team-path-connector-line.lose {
  background: none;
  border-left: none;
  color: var(--lose-color);
}
.team-path-win-label {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: 0.05em;
  color: var(--win-color);
}
.color-win  { color: var(--win-color); }
.color-lose { color: var(--lose-color); }
.color-gold { color: var(--gold-lt); }
.team-path-label {
  padding: 0.6em 0.75em;
  font-size: 1.15rem;
  font-family: 'Oswald', sans-serif;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  background: var(--surface2);
  border-radius: var(--radius-sm);
  border-left: 0.2em solid currentColor;
  margin: 0.5em 0 1.4em;
  text-align: center;
}
.team-path-label.win-label  { color: var(--win-color); }
.team-path-label.lose-label { color: var(--lose-color); }
.team-path-divider { margin: 1.4em 0; height: 0.07em; background: var(--border); }

/* ── Championship banner (team path terminal) ── */
.team-path-champion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  margin: 0.9em 0 0.5em;
  padding: 0.7em 1.4em;
  background: linear-gradient(135deg, color-mix(in srgb, var(--gold) 16%, transparent), color-mix(in srgb, var(--gold) 4%, transparent));
  border: 0.1em solid var(--gold);
  border-radius: var(--radius-md);
  box-shadow: 0 0 16px color-mix(in srgb, var(--gold) 22%, transparent);
}
.team-path-champion .tpc-pre {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  font-weight: 600;
  letter-spacing: 0.1em;
  color: var(--win-color);
}
.team-path-champion .tpc-trophy { font-size: 1.7rem; line-height: 1; }
.team-path-champion .tpc-word {
  font-family: 'Oswald', sans-serif;
  font-size: 1.35rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold-lt);
}
.team-path-champion.is-crowned {
  box-shadow: 0 0 24px color-mix(in srgb, var(--gold) 40%, transparent);
}
.team-path-champion.is-crowned .tpc-word { font-size: 1.55rem; }

/* ── Clickable loss-destination boxes (team path navigation) ── */
.round-lose-side[data-lose-anchor] { cursor: pointer; }
/* The destination game name reads as a tappable pill (matches .tp-opp-chip)
   so the jump affordance is visible at rest — not just on hover, which
   touch devices never trigger. */
.round-lose-side[data-lose-anchor] .round-lose-dest {
  border: 1px solid var(--border);
  border-radius: 1.2em;
  padding: 0.2em 0.55em;
  color: var(--blue-lt);
  white-space: nowrap;
  max-width: 100%;
  -webkit-tap-highlight-color: transparent;
}
.round-lose-side[data-lose-anchor]:hover .round-lose-dest,
.round-lose-side[data-lose-anchor]:active .round-lose-dest {
  background: var(--surface2);
  border-color: var(--blue-lt);
}
.tp-target-flash { animation: tpTargetFlash 1.4s ease-out; }
@keyframes tpTargetFlash {
  0%   { box-shadow: 0 0 0 0.25em var(--gold); }
  100% { box-shadow: 0 0 0 0.25em transparent; }
}

/* ── Championship bar (mobile/rounds) ── */
.champ-bar { margin: 0.4em 0 0.75em; padding: 0.65em 0.9em; background: var(--surface2); border-radius: var(--radius-sm); border: 0.07em solid var(--border); }
.champ-bar-label { font-family: 'Oswald', sans-serif; font-size: 0.68rem; letter-spacing: 0.13em; text-transform: uppercase; color: var(--gold); margin-bottom: 0.5em; }
.champ-bar-teams { display: flex; gap: 0.5em; align-items: center; }
.champ-team-slot { flex: 1; border: 0.07em solid var(--border); border-radius: 0.25em; padding: 0.5em 0.75em; font-size: 0.96rem; font-weight: 600; color: var(--text); background: var(--surface); }
.champ-team-slot.tbd { color: var(--text-mute); font-style: italic; font-weight: 400; }
.champ-winner-display { margin-top: 0.6em; text-align: center; font-family: 'Oswald', sans-serif; font-size: 0.95rem; color: var(--gold-lt); letter-spacing: 0.07em; }

/* ════════════════════════════════════════════════════════════
   MOBILE LAYOUT (phones + tablets under 1280px)
   Snap-scroll carousel: one round visible at a time.
   ════════════════════════════════════════════════════════════ */

/* Breakpoint contract: public bracket goes mobile under 1280px (desktop bracket needs the width); admin/lineup switch at 1023px. Intentional two-tier split. */
@media (max-width: 1279px) {
  .update-bar, .delay-notice { flex-shrink: 0; position: static; }

  .bracket-outer { display: none; }

  .bracket-side {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: stretch;
  }
  .bracket-side.left-side,
  .bracket-side.right-side { justify-content: unset; }

  .bracket-side-label { position: static; margin-bottom: 0.5em; display: inline-block; font-size: var(--text-xs); }

  .rounds-wrap {
    width: 100%;
    max-width: 100%;
    margin: 0;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    padding: 0.5em 0.5em 0.5em 0;
  }

  .round-col {
    flex-shrink: 0;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    width: var(--col-w);
    height: auto;
    min-height: auto;
    position: relative;
    padding-right: 0.5em;
  }
  .round-col:last-child { padding-right: 1.5em; }

  .round-col-hdr {
    position: static;
    text-align: center;
    font-size: 0.70rem;
    color: var(--gold-lt);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    padding: 0.4em 0.2em;
    margin-bottom: 0.4em;
  }

  .round-gap { display: none; }

  .bc {
    position: relative;
    margin-bottom: 0.5em;
    border-radius: var(--radius-sm);
    min-height: auto;
  }
  .bc-hdr { padding: 0.33em 0.33em; font-size: 0.62rem; }
  .bc-id  { font-size: 0.65rem; }
  .bc-time { font-size: 0.65rem; }
  .bc-team {
    padding: 0.18em 0.33em;
    font-size: 0.65rem;
    min-height: 1.9em;
    line-height: 1.15;
    white-space: normal;
    word-break: break-word;
  }

  .champ-center {
    position: relative;
    width: auto;
    flex-shrink: 0;
    padding: 0.6em 0.5em;
    border-left: 0.07em solid color-mix(in srgb, var(--gold) 15%, transparent);
    border-right: none;
    border-top: none;
    border-bottom: none;
    margin: 0;
  }
  .champ-inner   { width: 100%; text-align: center; }
  .champ-title   { font-size: 0.62rem; margin-bottom: 0.2em; }
  .champ-gametime { font-size: 0.65rem; margin-bottom: 0.35em; }
  .champ-trophy  { font-size: 1.2em; margin-bottom: 0.15em; }
  .champ-bc      { position: relative; height: auto; margin-bottom: 0; }
  .champ-winner  { font-size: 0.80rem; margin-top: 0.3em; }
  .bracket-side-wrap { position: static; flex: 1; min-width: 0; }
}

/* ════════════════════════════════════════════════════════════
   MOBILE BRACKET — two-column peek layout
   Current round (80vw) fully visible + next round peeks 20vw.
   Cards match By Round card style. Convergence is animated via
   JS-driven translateY blend as the user swipes between rounds.
   ════════════════════════════════════════════════════════════ */

/* Horizontal column container — JS-driven snap via scrollLeft */
.mbc-outer {
  display: flex;
  flex-direction: row;
  overflow-x: scroll;
  overflow-y: hidden;
  overscroll-behavior: none;
  width: 100%;
  scrollbar-width: none;
}
.mbc-outer::-webkit-scrollbar { display: none; }

/* One column per round — no CSS snap (JS handles it) */
.mbc-col {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Full-width vertical scroll overlay — the ONLY scroll container.
   Positioned below .mbc-hdr so the dot-navigation bar stays tappable.
   touch-action:pan-y lets iOS handle vertical with native momentum;
   horizontal swipes are detected in JS and forwarded to .mbc-outer. */
.mbc-scroll-cap {
  overflow-y: scroll;
  overflow-x: hidden;
  overscroll-behavior-y: contain;
  overscroll-behavior-x: none;
  touch-action: pan-y;
  scrollbar-width: none;
}
.mbc-scroll-cap::-webkit-scrollbar { display: none; }

/* Round name + progress dots — above the scroll area */
.mbc-hdr {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5em 1em 0.45em;
  background: var(--surface);
  border-bottom: 0.07em solid var(--border);
}
.mbc-hdr-label {
  font-family: 'Oswald', sans-serif;
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--gold-lt);
}
.mbc-dots { display: flex; gap: 5px; align-items: center; }
.mbc-dot {
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--border);
  transition: background 0.2s, transform 0.2s;
  cursor: pointer;
  flex-shrink: 0;
}
.mbc-dot.active { background: var(--blue-lt); transform: scale(1.35); }

/* ── Mobile bracket carousel: prev/next arrow buttons ───────────────────
   Only shown on true pointer-device (mouse) screens via hover:hover +
   pointer:fine. They are position:fixed so they float above all carousel
   columns and above captureEl (which has no explicit z-index). z-index 205
   ensures they sit above the scroll overlay. Top is set by JS to vertically
   centre within the scroll area; transform: translateY(-50%) handles
   the offset. Arrows are hidden by default and overridden to display:flex
   inside the media query, so non-mouse browsers never see them. */
/* Default: arrows hidden on touch/non-hover devices.
   Must precede the media-query override — same specificity, cascade order wins. */
.mbc-arrow { display: none; }

@media (hover: hover) and (pointer: fine) {
  .mbc-arrow {
    display: flex;
    align-items: center;
    justify-content: center;
    position: fixed;
    width: 2.5rem;
    height: 2.5rem;
    transform: translateY(-50%);
    border-radius: 50%;
    background: color-mix(in srgb, var(--surface) 80%, transparent);
    border: 0.1em solid var(--border);
    color: var(--text-dim);
    font-family: 'Oswald', sans-serif;
    font-size: 1.7rem;
    line-height: 1;
    cursor: pointer;
    z-index: 205;
    opacity: 0.55;
    transition: opacity 0.15s, background 0.15s, color 0.15s;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
  }
  .mbc-arrow:hover {
    opacity: 1;
    background: color-mix(in srgb, var(--surface2) 92%, transparent);
    color: var(--gold-lt);
    border-color: var(--gold);
  }
  .mbc-arrow--prev { left: 0.5rem; }
  .mbc-arrow--next { right: 0.5rem; }
}

/* Column scroll area — overflow:hidden; .mbc-scroll-cap overlay drives scroll */
.mbc-scroll {
  flex: 1;
  overflow: hidden;
}

/* Absolutely-positioned card container — width + height set by JS, centred horizontally */
.mbc-inner {
  position: relative;
  flex-shrink: 0;
  margin: 0 auto;
}

/* ── Championship card ── */
.mbc-champ-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  /* Gold-tinted surface gradient — stays dark-on-dark or light-on-light per
     theme (was a hardcoded dark-green endpoint, illegible with light-mode
     text once the gradient crossed into it). */
  background: linear-gradient(170deg, var(--surface2) 0%, color-mix(in srgb, var(--gold) 12%, var(--bg)) 100%);
  border: 0.15em solid var(--gold);
  border-radius: var(--radius-md);
  box-shadow: 0 0 32px color-mix(in srgb, var(--gold) 30%, transparent), inset 0 1px 0 color-mix(in srgb, var(--gold-lt) 12%, transparent);
  overflow: hidden;
}
/* Top info block */
.mbc-champ-top {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.9em 1em 0.7em;
  gap: 0.2em;
  flex-shrink: 0;
}
.mbc-champ-trophy {
  font-size: calc(var(--mbc-card-h, 120px) * 0.22);
  line-height: 1;
  filter: drop-shadow(0 2px 8px color-mix(in srgb, var(--gold-lt) 60%, transparent));
}
.mbc-champ-label {
  font-family: 'Oswald', sans-serif;
  font-size: calc(var(--mbc-card-h, 120px) * 0.115);
  font-weight: 600;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--gold);
}
.mbc-champ-time {
  font-size: calc(var(--mbc-card-h, 120px) * 0.085);
  color: var(--text-dim);
  letter-spacing: 0.04em;
}
/* Separator */
.mbc-champ-sep {
  height: 0.07em;
  background: var(--gold);
  opacity: 0.35;
  flex-shrink: 0;
  margin: 0 1.2em;
}
/* Teams block fills remaining height */
.mbc-champ-teams {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 0.5em 1.2em;
  gap: 0.15em;
  min-height: 0;
}
.mbc-champ-team {
  font-family: 'Barlow', sans-serif;
  font-size: calc(var(--mbc-card-h, 120px) * 0.155);
  font-weight: 700;
  color: var(--text);
  display: flex;
  align-items: center;
  gap: 0.4em;
  max-width: 100%;
  /* Same reasoning as .bc-team/.mbc-game-team: a flex item's min-width
     defaults to its content's size, not 0, which would let a long name
     grow this past max-width instead of ever reaching .mbc-champ-name's
     own overflow:hidden. */
  min-width: 0;
}
.mbc-champ-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.mbc-champ-team.tbd  { color: var(--text-mute); font-style: italic; font-weight: 400; font-size: calc(var(--mbc-card-h, 120px) * 0.115); }
.mbc-champ-team.won  { color: var(--win-color); }
.mbc-champ-team.lost { color: var(--lose-color); }
.mbc-champ-vs {
  font-size: calc(var(--mbc-card-h, 120px) * 0.075);
  color: var(--text-mute);
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.mbc-champ-winner {
  flex-shrink: 0;
  padding: 0.5em 1.2em 0.6em;
  font-family: 'Oswald', sans-serif;
  font-size: calc(var(--mbc-card-h, 120px) * 0.10);
  font-weight: 700;
  color: var(--gold-lt);
  letter-spacing: 0.1em;
  text-align: center;
  border-top: 0.07em solid color-mix(in srgb, var(--gold) 30%, transparent);
}

/* champ2 state classes on .mbc-card */
.mbc-card.mbc-champ2-pending .mbc-champ-card {
  opacity: 0.55;
}
.mbc-card.mbc-champ2-not-needed {
  opacity: 0.35;
}
.mbc-champ2-not-needed {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  border: 0.07em dashed var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-sizing: border-box;
}
.mbc-champ2-nn-label {
  font-family: 'Oswald', sans-serif;
  font-size: 0.7em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.mbc-champ2-nn-tag {
  font-family: 'Oswald', sans-serif;
  font-size: 0.9em;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--lose-color);
  text-transform: uppercase;
}
/* Suppress champ winner banner when CHAMP2 is pending/needed */
.mbc-card.mbc-champ-winner-pending .mbc-champ-winner {
  display: none;
}

/* Each card: absolute position, translateY driven by JS scroll blend */
.mbc-card {
  position: absolute;
  left: 0;
  right: 0;
}
.mbc-card .round-game-card { margin-bottom: 0; height: 100%; box-sizing: border-box; }

/* ── Bracket-side divider: subtle label between L and R side games ── */
.mbc-playin-divider[data-div-type="side"] .mbc-playin-line {
  opacity: 0.45;
}
.mbc-playin-divider[data-div-type="side"] .mbc-playin-label {
  font-weight: 400;
  letter-spacing: 0.07em;
  opacity: 0.6;
}

/* ── Play-in divider: sits inside .mbc-card, anchored above it ── */
.mbc-playin-divider {
  position: absolute;
  left: 0;
  right: 0;
  /* top is set inline as -(DIV_H + GAP) so it floats above the card */
  display: flex;
  align-items: center;
  gap: 0.6em;
  pointer-events: none;
}
.mbc-playin-line {
  flex: 1;
  height: 1px;
  background: var(--border);
}
.mbc-playin-label {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-dim, var(--border));
  white-space: nowrap;
}

/* ── Mobile bracket: game card — top 25% header, bottom 75% teams ── */
.mbc-game-card {
  height: 100%;
  display: flex;
  flex-direction: column;
  border: 0.07em solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface);
  box-sizing: border-box;
  transition: opacity 0.15s;
}
.mbc-game-card.upcoming {
  border-style: dashed;
}
/* Each team row gets 3 flex-units = 37.5% of card (×2 = 75%) */
.mbc-game-team {
  flex: 3;
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding: 0 0.7em;
  font-family: 'Barlow', sans-serif;
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--text);
  min-height: 0;
  min-width: 0;
}
.mbc-game-team.tbd  {
  color: var(--text-mute);
  font-style: italic;
  font-weight: 400;
  font-size: 0.88rem;
}
.mbc-game-team.bye  { color: var(--text-mute); font-style: italic; font-weight: 400; opacity: 0.55; letter-spacing: 0.08em; }
.mbc-game-team.won  { color: var(--win-color); }
.mbc-game-team.lost { color: var(--lose-color); }
/* Name span: takes all space, truncates with ellipsis */
.mbc-team-name {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Header row gets 2 flex-units = 25% of card */
.mbc-game-mid {
  flex: 2;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  padding: 0 0.9em;
  background: var(--surface2);
  border-bottom: 0.07em solid var(--border);
  min-height: 0;
}
.mbc-game-lbl {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.07em;
  color: var(--blue-lt);
  flex-shrink: 0;
}
.mbc-game-datetime { display: flex; align-items: baseline; gap: 0.3em; margin-left: auto; }
.mbc-game-day {
  font-family: 'Oswald', sans-serif;
  font-size: 0.82rem;
  font-weight: 600;
  color: var(--text-dim);
  margin-right: 0.2em;
}
.mbc-game-dow {
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--gold-lt);
}
.mbc-game-time {
  font-family: 'Oswald', sans-serif;
  font-size: 0.95rem;
  font-weight: 700;
  color: var(--gold-lt);
}

/* ════════════════════════════════════════════════════════════
   MOBILE — flex-body layout: body is a locked column,
   tabs sit naturally at the bottom (no position:fixed needed),
   selector is fixed above tabs with no body-scroll jitter.
   ════════════════════════════════════════════════════════════ */
@media (max-width: 1279px) {
  /* Compact the header to reclaim vertical space */
  .header-timestamp { display: none !important; }
  .site-subtitle { font-size: 0.58em; margin-top: 0.05em; letter-spacing: 0.08em; }

  /* Viewport-locked flex column — body never scrolls, so position:fixed is rock-solid.
     Use height:100% (initial containing block) rather than 100dvh — on iOS, dvh can
     start from behind the notch/address bar and break safe-area layout. --app-h is set
     synchronously from window.innerHeight before first paint as a JS correction. */
  html { height: 100%; height: 100svh; height: var(--app-h, 100svh); }
  body { height: 100%; height: 100svh; height: var(--app-h, 100svh); overflow: hidden; display: flex; flex-direction: column; }
  /* position:sticky inside overflow:hidden body is a no-op and can break WebKit flex
     layout (content starts at y=0 behind the floating header). Use relative instead. */
  .site-header { flex-shrink: 0; position: relative; top: auto; }

  /* Tab bar: last flex item → naturally sits at the bottom, no position:fixed needed */
  .view-tabs {
    order: 10;
    flex-shrink: 0;
    position: relative; top: auto; bottom: auto;
    background: var(--bg);
    border-top: 0.22em solid var(--gold);
    border-bottom: none;
    box-shadow: 0 -6px 24px rgba(0,0,0,0.55);
    padding-bottom: max(1em, env(safe-area-inset-bottom, 0px));
    padding-left: max(env(safe-area-inset-left, 0px), 1.2em);
    padding-right: max(env(safe-area-inset-right, 0px), 1.2em);
    z-index: 90;
  }
  .view-tab {
    min-height: 0;
    padding: 0.75em 0.5em 0.9em;
    border-bottom: none;
    border-top: 0.25em solid transparent;
    font-size: var(--text-sm);
    letter-spacing: 0.08em;
    gap: 0.3em;
  }
  .tab-icon {
    display: block;
    width: 1.8em;
    height: 1.8em;
  }
  .view-tab.active {
    color: var(--gold-lt);
    border-top-color: var(--gold);
  }
  .view-tab.active .tab-icon {
    color: var(--gold-lt);
  }

  /* Active panels fill remaining space between header and tab bar */
  .view-panel.active { flex: 1; min-height: 0; overflow: hidden; }
  /* Schedule and teampath: flex column so selector row can sit naturally above tab bar */
  #view-schedule.active, #view-teampath.active {
    display: flex;
    flex-direction: column;
  }
  /* Content scrolls within the flex column; selector row is a static sibling below */
  #view-schedule.active .round-panel,
  #view-standings.active .team-panel,
  #view-teampath.active .team-panel {
    flex: 1;
    min-height: 0;
    overflow-y: auto;

    overscroll-behavior-y: contain;
    scrollbar-width: none;
  }
  /* Same treatment as .bucket-panel: without box-sizing the 1em side padding
     is ADDED to the full-width panel and every row overflows the screen. */
  #view-standings.active { display: flex; flex-direction: column; overflow: hidden; }
  #view-standings.active .team-panel {
    width: 100%;
    max-width: none;
    margin: 0;
    padding-left: 0.75em;
    padding-right: 0.75em;
    box-sizing: border-box;
  }
  /* Team Path's .team-panel needs the SAME explicit width as Standings'
     above, for a reason that only shows up with a long, unbranded-into-
     wrapping team name: it is a block child of a column flex container
     (#view-teampath.active), and `align-items: stretch` does not reliably
     clamp its width to the viewport once `.team-brand-banner` inside it
     holds `white-space: nowrap` text — the panel's own box grows to fit
     that text's full unwrapped width instead, and the banner's ellipsis
     CSS (already correct: min-width:0, overflow:hidden, text-overflow)
     never gets a chance to fire because its container was never actually
     narrow (owner, 2026-08-02, screenshot: a long team name running off
     the right edge of the screen on Team Path, uncropped, on mobile).
     max-width:none overrides the desktop 65em cap, which would otherwise
     win against a 100% that is itself wider than 65em on a big phone. */
  #view-teampath.active .team-panel {
    width: 100%;
    max-width: none;
    box-sizing: border-box;
  }
  /* A/B flights now use the same mobile column carousel as the tree (its
     own fixed scroll overlay) — the panel just needs the flex column so the
     bracket root fills the space between pills and tab bar. */
  #view-bracket.active { display: flex; flex-direction: column; }
  #view-bracket.active #bracketRoot { flex: 1; min-height: 0; }
  #view-schedule.active .round-panel::-webkit-scrollbar,
  #view-standings.active .team-panel::-webkit-scrollbar,
  #view-teampath.active .team-panel::-webkit-scrollbar { display: none; }

  #view-buckets.active {
    display: flex;
    flex-direction: column;
    overflow: hidden;
  }
  /* On mobile the frozen top-3 is a non-scrolling flex sibling */
  #view-buckets.active .bucket-frozen {
    position: static;
    flex-shrink: 0;
    width: 100%;
    max-width: none;
    margin: 0;
    padding: 0.75em 1em 0.35em;
    box-sizing: border-box;
    background: var(--bg);
    border-bottom: 0.07em solid var(--border);
  }
  #view-buckets.active .bucket-panel {
    flex: 1;
    width: 100%;
    max-width: none;
    margin: 0;
    box-sizing: border-box;
    overflow-y: auto;

    overscroll-behavior-y: contain;
    scrollbar-width: none;
  }
  #view-buckets.active .bucket-panel::-webkit-scrollbar { display: none; }

  /* Team Path controls stay on ONE line. The select is the only elastic thing
     here, so it gives up the width: with "What if?" wrapping to a second row
     this bar was 131px tall on a 390px phone, and it sits at the bottom of the
     viewport where every pixel it takes comes straight off the bracket. The
     buttons tighten a little too, but keep the 2.75rem touch target. */
  #view-teampath .selector-inner {
    flex-wrap: nowrap;
    gap: 0.4em;
    padding: 0.6em 0.8em;
  }
  #view-teampath .selector-inner select.styled {
    /* min-width:0 is what actually lets a flex item shrink below its content
       width — without it the select refuses to give ground and the button
       wraps anyway. */
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--text-md);
    padding: 0.5em 0.6em;
    min-height: 2.75rem;
  }
  #view-teampath .selector-inner .team-share-btn {
    flex: 0 0 auto;
    padding: var(--btn-pad-v) 0.7em;
    min-height: 2.75rem;
  }

  /* Selector row: flex item at the bottom of its view panel — no position:fixed needed.
     Sits naturally above the tab bar via the outer body flex layout.
     Avoids iOS Safari safe-area timing issues that caused gaps with position:fixed. */
  #view-schedule.active .selector-row,
  #view-teampath.active .selector-row {
    position: static;
    top: auto;
    bottom: auto;
    background: var(--surface);
    border-top: 0.3em solid var(--gold);
    border-bottom: none;
    box-shadow: 0 -2px 12px rgba(0,0,0,0.4);
    z-index: 85;
    order: 10;
    flex-shrink: 0;
  }

  /* ── Team view / schedule mobile overrides ── */
  .team-path-label {
    font-size: 1em;
    padding: 0.55em 0.9em;
  }
  .round-game-card {
    margin-bottom: 0.65em;
  }
  .round-game-header {
    padding: 0.4em 0.75em;
  }
  .round-game-dow,
  .round-game-time {
    font-size: 0.85rem;
  }
  .round-team-row {
    padding: 0.55em 0.75em;
    min-height: 2.8em;
  }
  .round-team-name {
    font-size: 0.95rem;
  }
}

/* ── Field badge: which field a game is played on (admin "Field" input) ── */
.game-field-tag {
  font-family: 'Oswald', sans-serif;
  font-size: 0.63rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-dim);
  /* Soft tint keyed to the field color (currentColor = the color set by the
     numbered palette class or the user-picked inline color). No border, slight
     radius — reads as a highlight, not a tappable button. */
  background: color-mix(in srgb, currentColor 14%, transparent);
  border: none;
  border-radius: 0.3em;
  padding: 0.12em 0.4em;
  margin-left: 0.4em;
  white-space: nowrap;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  line-height: 1;
}
.mbc-champ-label .game-field-tag { font-size: 0.7rem; }
/* Per-field colors: Teal / Orange / Purple / Pink / Blue (user-overridable in Settings > Fields) */
.game-field-tag--1 { color: var(--field-1); border-color: var(--field-1); }
.game-field-tag--2 { color: var(--field-2); border-color: var(--field-2); }
.game-field-tag--3 { color: var(--field-3); border-color: var(--field-3); }
.game-field-tag--4 { color: var(--field-4); border-color: var(--field-4); }
.game-field-tag--5 { color: var(--field-5); border-color: var(--field-5); }
/* Field tag sits as first item inside the time block — no extra left margin needed */
.round-game-day-time .game-field-tag { margin-left: 0; }

/* Bracket card headers: keep the game label hard-left and push the field tag
   + time block together to the right, so the field reads as part of the
   when/where cluster rather than hugging the game name. */
.bc-hdr .game-field-tag,
.mbc-game-mid .game-field-tag { margin-left: auto; }
/* When a game has no field tag, push the time block right on its own. */
.bc-id + .bc-time,
.mbc-game-lbl + .mbc-game-datetime { margin-left: auto; }

/* ── Team path: tappable opponent chip ("who might I play?") ──────────── */
/* Distinct pill inside the game card; tapping it jumps to the opponent's
   source game, while tapping the rest of the card jumps to this game. */
.tp-opp-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  border: 1px solid var(--border);
  border-radius: 1.2em;
  padding: 0.35em 0.75em;
  margin: -0.2em 0;
  min-height: 2.2em;        /* comfortable thumb target */
  color: var(--blue-lt);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.tp-opp-chip:active { background: var(--surface2); }
.tp-opp-chevron {
  font-size: 1.15em;
  line-height: 1;
  color: var(--text-dim);
}

/* Persistent jump highlight: stays until scrolled off screen or tab change */
.tp-target-hold { box-shadow: 0 0 0 0.25em var(--gold); }

/* ── Touch target sizing (coarse pointer = touchscreen) ── */
@media (pointer: coarse) {
  .tp-opp-chip        { min-height: 2.75rem; }
  /* .team-chip's own min-height (3.85rem, matching a Standings row) already
     exceeds this touch-target minimum — no override needed, and one here
     would only fight that match on a touch device. */
  .bucket-edit-save-btn, .bucket-edit-cancel-btn { min-height: 2.75rem; }
}

/* ── Standings table (round robin / pool play — Schedule tab) ── */
.standings-block {
  max-width: 42em;
  margin: 0 auto;
  padding: 0.9em 1em 0.1em;
}
.standings-group { margin-bottom: 1.2em; }
.standings-hdr {
  font-family: 'Oswald', sans-serif;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--gold-lt);
  margin-bottom: 0.4em;
}
.standings-card {
  background: var(--surface);
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.standings-table {
  width: 100%;
  border-collapse: collapse;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-md);
}
.standings-empty {
  text-align: center;
  color: var(--text-mute);
  padding: 2em 1em;
  font-family: 'Barlow', sans-serif;
}

/* The tiebreak order is printed under every table: a TD gets asked "why is
   that team above us" and needs the answer on the same screen. */
.standings-tiebreaks {
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--text-mute);
  padding: 0.55em 0.8em 0.7em;
  border-top: 0.07em solid var(--border);
}
.standings-table th {
  font-family: 'Oswald', sans-serif;
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-dim);
  text-align: right;
  padding: 0.5em 0.65em;
  border-bottom: 0.12em solid var(--gold);
}
.standings-table th.standings-team-th { text-align: left; }
.standings-table td {
  text-align: right;
  padding: 0.45em 0.65em;
  border-bottom: 0.07em solid var(--border);
  color: var(--text);
  font-variant-numeric: tabular-nums;
}
.standings-table tr:last-child td { border-bottom: none; }
.standings-table td.standings-team {
  text-align: left;
  font-weight: 600;
  max-width: 11em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.standings-diff-pos { color: var(--win-color); }
.standings-diff-neg { color: var(--lose-color); }

/* ── Team Path record summary ── */
.team-record-bar {
  font-family: 'Oswald', sans-serif;
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--gold-lt);
  text-align: center;
  padding: 0.2em 0 0.7em;
  letter-spacing: 0.04em;
}
.team-record-detail {
  font-size: 0.72em;
  font-weight: 500;
  color: var(--text-dim);
  letter-spacing: 0.06em;
}

/* ── Print: paper bracket sheet ──────────────────────────────────────────
   TDs tape paper brackets to the fence. Print whichever tab is active with
   the interactive chrome stripped; the palette flips to paper via the token
   override in base.css. Landscape suits the bracket tree. */
@page { size: landscape; margin: 10mm; }
@media print {
  .header-controls,
  .view-tabs,
  .selector-row,
  .update-bar,
  .offline-notice,
  .bracket-flights,
  .toast,
  .mbc-arrow,
  .team-share-btn,
  .admin-quick-bar { display: none !important; }
  .site-header,
  .bucket-frozen { position: static !important; }
  .site-header { border-bottom: 0.15em solid #000; }
  .bracket-outer,
  .bracket-scroller,
  .mbc-scroll { overflow: visible !important; }
  .round-panel,
  .team-panel { overflow: visible !important; max-height: none !important; }
}

/* ── Project-winners mode ── */
/* The on/off toggle is an admin-public-btn in the header (next to Edit);
   this bar only carries the hint + clear button while projecting. */
.project-bar {
  display: flex;
  gap: 0.6em;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  padding: 0.55em 1em 0;
}
.project-clear {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.45em 0.9em;
  border-radius: var(--radius-xs);
  border: 0.07em solid var(--lose-color);
  background: transparent;
  color: var(--lose-color);
  cursor: pointer;
}
.project-clear:hover { color: var(--text); border-color: var(--text-dim); }
/* Save in the Edit bar: the one affirmative action among discards, so it
   borrows the accent rather than the destructive red the base class uses. */
.project-clear--primary {
  border-color: var(--blue);
  color: var(--blue-lt);
}
.project-clear--primary:hover { color: var(--text); border-color: var(--blue-lt); }
.project-hint {
  flex-basis: 100%;
  text-align: center;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  color: var(--text-mute);
  padding-bottom: 0.2em;
}
@media (pointer: coarse) { .project-clear { min-height: 44px; } }
@media print { .project-bar { display: none !important; } }

/* .share-* generic building blocks (overlay/box/menu/actions/etc.) moved to
   base.css (owner, 2026-08-02) — the coach panel's own Share needs them too,
   and coach.html doesn't load this file. .share-themes/.share-scope/
   .share-align/.share-opt-note stay here — bracket-specific option rows. */

/* ── Team Path "What if?" stepper ── */
.pathproj-box { max-width: 30rem; }
.pathproj-step {
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  color: var(--text-mute);
  text-align: center;
  margin-bottom: 0.9em;
}
.pathproj-q {
  font-family: 'Oswald', sans-serif;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: var(--text-xs);
  color: var(--text-mute);
  text-align: center;
  margin-bottom: 0.6em;
}
.pathproj-opts {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: 0.6em;
  flex-wrap: wrap;
}
.pathproj-opt {
  flex: 1 1 9em;
  min-height: 3.4em;
  padding: 0.7em 0.8em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-md);
  font-weight: 700;
  color: var(--text);
  background: var(--surface);
  border: 0.11em solid var(--border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.pathproj-opt:hover { border-color: var(--blue); background: color-mix(in srgb, var(--blue) 12%, transparent); }
/* The viewed team reads as the "us" option so a fan can pick their own side
   without reading both labels every round. */
.pathproj-opt--own { border-color: var(--gold); color: var(--gold-lt); }
.pathproj-opt--own:hover { border-color: var(--gold); background: color-mix(in srgb, var(--gold) 14%, transparent); }
/* An opponent that isn't known yet: dashed, because it isn't a pick — it
   opens the games that would decide it. */
.pathproj-opt--unknown {
  border-style: dashed;
  color: var(--text-mute);
  font-weight: 400;
  font-style: italic;
  display: flex;
  flex-direction: column;
  gap: 0.25em;
  align-items: center;
  justify-content: center;
}
.pathproj-opt-sub {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-xs);
  font-style: normal;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--blue-lt);
}
/* Nothing upstream is settled enough to ask about — not a button. */
.pathproj-opt--dead {
  border-style: dashed;
  color: var(--text-mute);
  font-weight: 400;
  font-style: italic;
  cursor: default;
  display: flex;
  align-items: center;
  justify-content: center;
}
.pathproj-vs {
  align-self: center;
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--text-mute);
}
.pathproj-note, .pathproj-done-sub {
  margin-top: 0.8em;
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  color: var(--text-mute);
  text-align: center;
}
.pathproj-done { padding: 0.8em 0 0.2em; text-align: center; }
.pathproj-done-line {
  font-family: 'Oswald', sans-serif;
  font-size: var(--text-md);
  color: var(--gold-lt);
}
.pathproj-actions { justify-content: space-between; }

/* Projected picks: tinted blue + italic, distinct from a real (green) win */
.bc-team.won.projected { color: var(--blue-lt); }
.bc-team.won.projected .bc-name { font-style: italic; }
/* Same on the mobile carousel and its championship card — green there read as
   a settled win. */
.mbc-game-team.won.projected,
.mbc-champ-team.won.projected { color: var(--blue-lt); font-style: italic; }
/* Same convention on the Team Path chain, which renders projections too. */
.round-team-name.won.projected,
.round-team-name.lost.projected { color: var(--blue-lt); font-style: italic; }
.round-team-row:has(.round-team-name.projected) .game-score { color: var(--blue-lt); }
.round-win-side--proj {
  border-left-color: var(--blue-lt);
  background: color-mix(in srgb, var(--blue-lt) 10%, transparent);
}
.round-win-side--proj .round-win-tag { color: var(--blue-lt); }
/* ── "Potential Outcome" marking ──────────────────────────────────────────
   A screenshot is the one channel nothing can prevent, so the marking has to
   live in the pixels and survive being cropped to a single card. Hence: on the
   CARD (a banner is one crop away from gone), a fill rather than only a border
   (a tight crop can miss an edge), and the words spelled out rather than a
   glyph or a colour convention nobody outside this app knows.
   Deliberately not subtle. This is the one place where being unmissable beats
   being handsome.

   Drawn as a ::before overlay rather than a background-image, because the
   cards it has to mark do not share a background: the championship card is a
   gold gradient and the carousel card paints its own header bar, so anything
   set on the card itself is either replaced or painted over. An overlay sits
   above all of that, and above the text — which is the point of a watermark.
   pointer-events stay off so picking still works through it. */
.bc--proj,
.round-game--proj,
.mbc--proj { position: relative; border-color: color-mix(in srgb, var(--blue-lt) 60%, transparent); }
.bc--proj::before,
.round-game--proj::before,
.mbc--proj::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  border-radius: inherit;
  background-image: repeating-linear-gradient(
    -45deg,
    var(--proj-hatch) 0 0.55em,
    transparent 0.55em 1.5em
  );
}
/* .bc is absolutely positioned already; re-declaring position would drop it
   back into the flow and pile every card at the top of its column. */
.bc--proj { position: absolute; }
.bc-proj-chip {
  font-family: 'Oswald', sans-serif;
  font-size: 0.52rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--blue-lt);
  background: color-mix(in srgb, var(--blue-lt) 16%, var(--surface));
  border: 0.09em solid color-mix(in srgb, var(--blue-lt) 65%, transparent);
  border-radius: 0.25em;
  padding: 0.05em 0.4em;
  margin-left: 0.35em;
}
/* Carousel card: the chip rides the bottom-right corner instead of the header
   row, which on a phone is already label + field + date + time. That corner is
   the score column, and a pick carries no score, so it is always free. */
/* The champion banner. A projected champion in gold, alone under a trophy, is
   the single most convincing thing this page can render — so it loses the gold
   and says what it is. */
.champ-winner--proj {
  color: var(--blue-lt) !important;
  font-style: italic;
}
.champ-winner--proj .trophy-img { opacity: 0.5; }
/* On a game card the chip sits at the end of the bottom team row, where the
   score would go — and a pick never has a score. It must never be the thing
   that shrinks: the team name beside it already ellipsises, so the chip holds
   its size and the name gives way. */
.bc-team > .bc-proj-chip,
.mbc-game-team > .bc-proj-chip {
  flex: 0 0 auto;
  margin-left: auto;
}
/* Print/export palette: --proj-hatch already switches to grey there (see
   base.css), so only the border and the chip need saying. */
body.print-palette .bc--proj,
body.print-palette .round-game--proj,
body.print-palette .mbc--proj { border-color: #666 !important; }
body.print-palette .bc-proj-chip {
  color: #000 !important;
  background: #fff !important;
  border-color: #000 !important;
}

/* ── CSP refactor: classes replacing generated inline styles ── */
.u-invisible { visibility: hidden; }
.u-relative { position: relative; }
.u-dim30 { opacity: 0.3; }
.u-push-right { margin-left: auto; flex-shrink: 0; }
.conn-svg { position: absolute; top: 0; left: 0; width: 100%; height: 100%; overflow: visible; }
.bcc-outerflex {
  display: flex;
  align-items: flex-start;
  /* min-content keeps the columns at their computed width instead of letting
     flex squeeze them when the bracket is wider than the viewport; the
     wrapper below then scrolls horizontally. Centered on screen — left-by-
     default is a SHARE/print setting only (shareOpts() in bracket.js), applied
     to this same class name but inside paintFlights()'s own isolated render
     frame, never to the live page (owner, 2026-08-02: "left justified... was
     supposed to be for share only... not the actual live desktop view"). */
  justify-content: center;
  width: 97vw;
  min-width: min-content;
  margin: 0 auto;
  box-sizing: border-box;
  padding: calc(var(--col-hdr-h)*1.7) 0 1em;
}
/* Desktop: a large field (22+ team double elim) needs more room than the
   window has once the readability floors in getDims() kick in. Scroll rather
   than shrink — the fit-to-viewport sizing was built for 8-16 team brackets. */
@media (min-width: 1280px) {
  #view-bracket.active { overflow-x: auto; }
}
/* ── Stacked desktop layout (large winners/losers brackets) ──────────────
   Side by side, a functionally-32-team double elim puts 12 round columns plus
   the championship across one screen: every column lands under the
   readability floor and the sheet has to scroll anyway. Stacking winners over
   losers halves the columns across, so the cards stay legible; the cost is
   height, which the page simply scrolls. Both rows read LEFT TO RIGHT (the
   losers side is not mirrored here) and share one column width, so the rounds
   line up vertically. See getDims / stackedDims in bracket.js. */
/* Column wrapper holding the flight pills and the bracket itself. The pills
   are MOVED in here at render time (mountFlightPills) so they sit with the
   bracket instead of in their own page-centred row with dead space beneath —
   and so they travel with it into the exported image. */
.bracket-shell {
  display: flex;
  flex-direction: column;
  align-items: center;
  min-width: min-content;
}
.bracket-shell > .bracket-flights { margin: 0.9em auto 0.15em; }

.bcc-stack {
  display: flex;
  align-items: center;      /* championship centred across both rows */
  /* Centered on screen — same reasoning as .bcc-outerflex above: the
     left-by-default alignment is a share/print-only setting, applied inside
     paintFlights()'s isolated render frame, not to this live-page class. */
  justify-content: center;
  width: 97vw;
  min-width: min-content;   /* scroll instead of squeezing the columns */
  margin: 0 auto;
  box-sizing: border-box;
  /* Top padding only has to clear the absolutely-positioned side label and
     round headers; the pills above supply the visual separation. */
  padding: calc(var(--col-hdr-h)*1.7) 0 1em;
  gap: 0.5vw;
}
.bcc-stack-rows {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  /* Room for the second row's absolutely-positioned side label + headers. */
  gap: calc(var(--col-hdr-h)*3.2);
}
.bcc-stack-row { display: flex; flex: 0 0 auto; }
/* The championship sits INSIDE the winners row (its natural home — the
   losers row reads as the lower half of the sheet), centred vertically in
   that row and carrying the whole finals story. */
/* Stacked: the championship column behaves like the side-by-side one — the
   card is ANCHORED to the row its two feeders enter at (winners final above,
   losers final below) with the trophy block floating over it, so the
   connector lines land on the card instead of on a floating panel. */
.champ-center--stacked { align-items: stretch; }
/* Flights keep the Open tree's tinted championship COLUMN — same 4% gold, same
   left/right rules, spanning the full bracket height. It used to be a small
   bordered box floating beside the tree, which read as a different kind of
   thing from the main bracket's championship when the two sit a pill-click
   apart. Everything else comes from .champ-center--desktop, including the
   .champ-anchor treatment that sits the title card on its feeders' row.
   justify-content only governs the FALLBACK, used when the final has no
   on-screen winner feeder to be level with — then the block just centres. */
.champ-center--flight { justify-content: center; }
.champ-center--flight .champ-inner,
.champ-center--flight .champ-bc,
.champ-center--flight .champ-winner { width: 100%; }

/* Captions around the losers final, which the stacked layout draws in the
   winners row beside the winners final so both visibly feed the title game.
   It used to be drawn in BOTH rows — a dashed copy here and the real game at
   the end of the losers row — which cost the losers side a whole column to
   say the same thing twice. Now it lives here only, named above and with its
   destination below. */
.bc-final-cap {
  position: absolute;
  left: 0;
  right: 0;
  text-align: center;
  font-family: 'Oswald', sans-serif;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* Gold, matching the "winner to ..." note in the losers row. These captions
     are the thread a reader follows between the two stacked rows, so they
     read as one set rather than as a label and an afterthought. */
  color: var(--gold);
  pointer-events: none;
}
/* Under the card rather than above it: says where the winner goes, since this
   game is drawn in the winners row and no longer sits at the end of the
   losers row. Same colour as the caption above — position is the difference. */
/* Bigger than a round column: this is the thing a visitor looks for first. */
.champ-center--stacked .champ-title { font-size: 0.92rem; letter-spacing: 0.2em; }
.champ-center--stacked .champ-trophy { font-size: 2.6em; }
.champ-center--stacked .champ-gametime { font-size: 0.86rem; }
.champ-center--stacked .champ-winner { font-size: 1.05rem; }

/* Who is playing for it, spelled out — "Winner of G.39" on its own tells a
   visitor nothing. */
.champ-feeders {
  display: flex;
  align-items: stretch;
  gap: 0.5em;
  width: 100%;
  margin-top: 0.9em;
  font-family: 'Barlow', sans-serif;
}
.champ-feed { flex: 1 1 0; min-width: 0; text-align: center; }
.champ-feed-lbl {
  font-family: 'Oswald', sans-serif;
  font-size: 0.56rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
}
.champ-feed-team { font-size: var(--text-sm); color: var(--text-dim); line-height: 1.3; }
.champ-feed-vs {
  align-self: center;
  font-family: 'Oswald', sans-serif;
  font-size: 0.6rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-mute);
}

/* The losers-bracket final, repeated here so the championship's second
   feeder is legible without scrolling to the other bracket. */
.champ-lbfinal {
  width: 100%;
  margin-top: 1.1em;
  padding-top: 0.9em;
  border-top: 0.07em solid var(--border);
  text-align: center;
}
.champ-lbfinal-lbl {
  font-family: 'Oswald', sans-serif;
  font-size: 0.6rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin-bottom: 0.4em;
}
.champ-lbfinal .bc { position: relative; left: auto; right: auto; }
.champ-lbfinal-note {
  font-family: 'Barlow', sans-serif;
  font-size: var(--text-xs);
  color: var(--text-mute);
  margin-top: 0.4em;
  line-height: 1.35;
}

.bcc-sideflex { position: relative; flex: 0 0 auto; display: flex; }
.bcc-rowflex { display: flex; flex-direction: row; }
.bracket-side-label--abs { position: absolute; top: calc(var(--col-hdr-h)*-2.2); }
.bc.bc--static { position: relative; left: auto; right: auto; }
.team-path-win-label--loss { color: var(--lose-color); }

/* ── Share bracket as an image ──────────────────────────────────────── */
/* Offscreen render frame for the image exporter. It must actually LAY OUT
   (the bracket is sized in vw/vh and measured with getBoundingClientRect),
   so it can't be display:none or zero-sized — it is parked off-canvas
   instead. Fixed size = the virtual desktop viewport the image is rendered
   at, so a phone still exports the full desktop bracket. Bracket-specific
   (the coach panel's Share renders a plain table, no offscreen frame needed),
   so this one stays here rather than moving to base.css with the rest. */
.share-frame {
  position: fixed;
  left: -20000px;
  top: 0;
  width: 1600px;
  height: 1000px;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
.share-themes { gap: 1.1em; }
/* Two of the three scope labels start with "All brackets", so they need room
   to sit on their own lines rather than wrapping mid-label. */
.share-scope { gap: 0.9em 1.1em; flex-wrap: wrap; }
.share-align { gap: 0.9em 1.1em; flex-wrap: wrap; }
.share-opt-note { color: var(--text-mute); }

/* ── Spectator notification panel ──────────────────────────────────────
   Reuses .share-overlay/.share-box/.share-opt wholesale — it is the same kind
   of thing (a small settings sheet over the bracket) and inventing a second
   set of box styles is how the two drift apart. Only the team list needs
   anything of its own: it can run to 30+ teams, so it scrolls and columns. */
.notify-teams {
  max-height: 34vh;
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9em, 1fr));
  gap: 0.15em 0.8em;
  margin: 0.2em 0 0.6em;
  padding: 0.5em 0.6em;
  border: 0.07em solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
}
.notify-team { margin: 0; }
.notify-teams-row { margin-top: 0.5em; }

/* Notification settings panel. The BODY scrolls; the title and the actions do
   not. Without this the box hits .share-box's 88vh cap and simply overflows —
   a 20-team picker put "Turn on" past the bottom of a phone screen with no way
   to scroll to it, so the panel looked like it had done nothing. */
/* The settings PAGE. Ordinary document flow — it scrolls with the page like
   any other view, which is why none of the fixed-position traps that made the
   modal unreachable on a phone can apply here. */
.notify-page { max-width: 34em; margin: 0 auto; padding: 1em 1em 2.5em; }
.notify-page-hdr { display: flex; align-items: center; gap: 0.8em; margin-bottom: 0.4em; }
.notify-page-hdr .share-title { margin: 0; }
.notify-page-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em;
  margin-top: 1.2em;
}
.notify-page-actions > * { flex: 1 1 12em; }
.notify-section-hdr {
  font-family: 'Oswald', sans-serif;
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-mute);
  margin: 0.9em 0 0.3em;
}
.notify-page-body > #notifyForm > .notify-section-hdr:first-child { margin-top: 0; }
.notify-note {
  margin-top: 1em;
  padding: 0.7em 0.8em;
  font-size: 0.72rem;
  line-height: 1.45;
  color: var(--text-dim);
  background: var(--surface2);
  border-left: 0.2em solid var(--gold);
  border-radius: var(--radius-xs);
}
.notify-note strong { color: var(--text); }

/* On mobile the active view panel is a fixed-height flex child with
   overflow:hidden — right for the bracket carousel, wrong for a settings page,
   which would simply be clipped with no way to reach the bottom. This is the
   same class of mistake as the modal it replaced, so it is spelled out: the
   notification page scrolls itself. */
#view-notify.view-panel.active { overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* ── Quick results (embedded admin Results tab) ───────────────────────────
   Same containing rule as #view-notify above, same reason. Wider than
   .notify-page (65em, matching .team-panel — the width Schedule/Team Path
   already use) since this holds the admin panel's game-card grid, not a
   settings form. */
.results-embed-page { max-width: 65em; margin: 0 auto; padding: 1em; }
.results-embed-frame {
  display: block;
  width: 100%;
  border: 0;
  border-radius: var(--radius-sm);
  background: var(--bg);
}
#view-results.view-panel.active { overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* Pull-to-refresh / #refreshBtn CSS moved to base.css (owner, 2026-08-02:
   "the refresh button should be available in coach and admin panel also") —
   those pages don't load bracket.css, and the indicator/spinner are shared
   UI now, not bracket-specific. */

/* ── Install instructions page ────────────────────────────────────────────
   Same containing rule as #view-notify directly above, and for the same
   reason: on mobile the active view panel is a fixed-height flex child with
   overflow:hidden, so a page of steps would be clipped with no way to scroll
   to the last one. */
#view-install.view-panel.active { overflow-y: auto; -webkit-overflow-scrolling: touch; }

/* Sized up from --text-sm (owner, 2026-08-02: "a bit small on mobile"). This
   page is read once, standing up, by someone following steps on the device
   they are reading it on — it is instructions, not chrome, so it gets body
   copy sizing rather than the panel's default small text. */
.install-intro {
  font-size: var(--text-md);
  line-height: 1.6;
  color: var(--text-dim);
  margin-bottom: 0.8em;
}

.install-steps {
  margin: 0.5em 0 1.3em;
  padding-left: 1.5em;
  font-size: var(--text-md);
  line-height: 1.7;
  color: var(--text);
}
.install-steps li { margin-bottom: 0.6em; }
.install-steps li::marker { color: var(--text-dim); }
.install-steps strong { color: var(--text); }

/* The iOS/Safari constraint. Given the gold treatment because someone
   following these steps in Chrome on an iPhone will not find the menu item at
   all, and will reasonably conclude the app is broken rather than that their
   browser cannot do it. */
.install-note {
  margin: 0.2em 0 1em;
  padding: 0.75em 0.9em;
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--text-dim);
  background: var(--surface2);
  border-left: 0.2em solid var(--gold);
  border-radius: var(--radius-xs);
}
.install-note strong { color: var(--text); }

/* The install page borrows .notify-section-hdr / .notify-note from the
   notifications page, which sizes them for a dense settings form. Here they
   are headings and a closing note in a page of instructions, so they scale up
   with the rest of it — scoped to #view-install so the settings page is
   untouched (owner asked about this page only). */
#view-install .notify-section-hdr { font-size: 0.82rem; margin-top: 1.2em; }
#view-install .notify-note { font-size: var(--text-sm); line-height: 1.6; padding: 0.75em 0.9em; }
#view-install .share-title { font-size: 1.05rem; }
