/* ============================================================
   FOOS APP — WEB APP SHELL
   The design system for the logged-in product at foos-app.com.
   Canonical source: docs/web-app-mockups/foos.css, with the §3a
   design-QA layer applied (it overrides the mockups where they
   conflict: contrast floor, motion tokens, focus rings, state
   components).
   Dark-first. Fonts: Outfit (display) + DM Sans (body).
   ============================================================ */

:root {
  --background: #020202;
  --card: #0f0f0f;
  --card-2: #141414;
  --secondary: #1a1a1a;
  --border: #222222;
  --border-strong: #2e2e2e;
  --foreground: #f8f8f8;
  --muted: #a0a0a0;
  /* §3a contrast floor: the mockups' #6b6b6b is 3.60:1 on --card and fails
     AA at the 10–12.5px sizes it is used at. #858585 is 5.19:1. */
  --muted-2: #858585;
  --primary: #f94144;
  --primary-press: #d40924;
  /* §3a: flat red on white is 3.58:1 and fails. The brand gradient's dark
     half carries AA, so every primary button uses the gradient. */
  --primary-grad: linear-gradient(145deg, #f94144, #d40924);
  /* Brand red as INK (pill text, "liked", the wordmark's APP). --primary is the
     right red on black at 5.8:1; light mode swaps in the deeper half of the
     gradient below. Fills, dots and borders keep --primary in both modes. */
  --primary-ink: #f94144;
  --success: #22c55e;
  --warning: #fbbf24;
  --gold: #f5c518;
  --silver: #c8ccd4;
  --bronze: #cd8a45;
  --live: #f94144;
  --radius: 16px;
  --radius-sm: 12px;
  --font-display: 'Outfit', system-ui, sans-serif;
  --font-body: 'DM Sans', system-ui, sans-serif;
  --shell-max: 680px;
  --tabbar-h: 64px;
  --header-h: 62px;

  /* §3a motion system — these four are the ONLY durations/easings. */
  --motion-fast: 120ms;
  --motion-normal: 200ms;
  --motion-slow: 350ms;
  --motion-ease: cubic-bezier(0.22, 1, 0.36, 1);

  /* Play System surfaces (§5.2/§5.3). Declared as tokens so the Play Bar and
     the Match Takeover — the two most emotionally loaded surfaces in the
     product — read the same in light mode without a single hex in component
     CSS. The reds and ambers are the brand's, dimmed into backgrounds. */
  --play-now-bg: linear-gradient(120deg, #1c0507, #12090a 55%);
  --play-now-border: rgba(249, 65, 68, .55);
  --play-deck-bg: linear-gradient(120deg, #1a1305, #100d05 55%);
  --play-deck-border: rgba(251, 191, 36, .5);
  --play-wait-bg: var(--card-2);
  --play-shadow: 0 8px 28px rgba(0, 0, 0, .55);
  --takeover-bg: radial-gradient(120% 90% at 50% 0%, #3a0a0f 0%, #160406 45%, #050203 100%);
  --takeover-deck-bg: radial-gradient(120% 90% at 50% 0%, #3a2a06 0%, #171004 45%, #050302 100%);
  --takeover-card-bg: rgba(15, 15, 15, .82);

  /* Toasts (§3a). Three variants, each a tinted field + matching ink, so the
     component CSS below stays hex-free and light mode is a token swap. */
  --toast-ok-bg: #102616;
  --toast-ok-border: rgba(34, 197, 94, .5);
  --toast-ok-ink: #d9ffe6;
  --toast-err-bg: #2a0e10;
  --toast-err-border: rgba(249, 65, 68, .55);
  --toast-err-ink: #ffdcdd;
  --toast-info-bg: #0a1420;
  --toast-info-border: #1d3a5f;
  --toast-info-ink: #cfe4fa;

  /* === BRACKET & SCORE SHEET (§5.5/§5.6) === */
  --match-w: 225px;                        /* match card width, mockup-canonical */
  --conn-w: 34px;                          /* connector column width */
  --conn-line: var(--border-strong);       /* connector ink */
  --conn-thick: 2px;
  --success-subtle: rgba(34, 197, 94, .12);   /* winner-row flash wash */
  --gold-line: rgba(245, 197, 24, .5);        /* §5.5 champion border */
  --gold-panel-line: rgba(245, 197, 24, .35);
  --gold-panel-bg: linear-gradient(140deg, #171204, #0f0f0f 70%);
  --champion-bg: radial-gradient(120% 90% at 50% 0%, #3a2f06, #171204 45%, #0f0f0f);
}

/* Light mode. Dark is the brand; light must be equally legible. */
@media (prefers-color-scheme: light) {
  :root {
    --background: #f6f6f7;
    --card: #ffffff;
    --card-2: #fbfbfc;
    --secondary: #eeeff1;
    --border: #e0e2e6;
    --border-strong: #cbcfd6;
    --foreground: #0b0b0c;
    --muted: #5c6169;
    --muted-2: #676d76; /* #6c727b measured 4.49:1 on --background — under the floor */
    /* The dark-mode semantic colors are bright by design and land at 1.6–2.3:1
       on white — unreadable. Darkened here so status text clears 4.5:1 in light
       mode too (§3a floor). The pill BACKGROUNDS stay the same translucent
       tints; only the ink changes. */
    --success: #166534;
    --warning: #92590a;
    --gold: #7d6200;
    /* Deeper than the gradient's dark half because red ink usually sits on the
       translucent red pill tint, not on plain white: #d40924 measured 4.22:1
       against .pill.live's background. #c1121f clears the floor there. */
    --primary-ink: #c1121f;

    /* The Play surfaces stay unmistakably red/amber in light mode, but as tints
       on paper rather than glows in a dark room. The takeover keeps a deep red
       field in BOTH modes — it is a full-screen moment with white display type
       on it, and a pale version would fail contrast and lose all its weight. */
    --play-now-bg: linear-gradient(120deg, #fff1f1, #fff 60%);
    --play-now-border: rgba(193, 18, 31, .45);
    --play-deck-bg: linear-gradient(120deg, #fff8e6, #fff 60%);
    --play-deck-border: rgba(146, 89, 10, .4);
    --play-shadow: 0 6px 20px rgba(16, 20, 28, .12);
    --takeover-card-bg: rgba(8, 8, 8, .58);

    /* Toasts invert: pale field, dark ink. The dark-mode inks are 1.2–1.4:1 on
       paper. These pair with the darkened --success/--warning family above. */
    --toast-ok-bg: #e8f9ee;
    --toast-ok-border: rgba(22, 101, 52, .35);
    --toast-ok-ink: #0d4023;
    --toast-err-bg: #fdeced;
    --toast-err-border: rgba(193, 18, 31, .35);
    --toast-err-ink: #7a0b14;
    --toast-info-bg: #eaf2fd;
    --toast-info-border: rgba(29, 58, 95, .28);
    --toast-info-ink: #123055;

    /* The only bracket token that lands on a light surface. The gold/champion
       values above sit on surfaces that stay dark in both schemes (§1.3). */
    --success-subtle: rgba(22, 101, 52, .08);
  }
  .card { box-shadow: 0 1px 2px rgba(16, 20, 28, .05); }
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; }

/* The UA hides [hidden] with `display: none`, which ANY author `display` wins
   against — so a `.pill { display: flex }` that a Stimulus controller sets
   hidden still paints as an empty nub. Components use the hidden attribute as
   their off switch throughout, so enforce it once here. */
[hidden] { display: none !important; }

body.foos-app {
  background: var(--background);
  color: var(--foreground);
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.5;
  padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 20px);
}
body.foos-app.has-playbar { padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 84px); }

.foos-app a { color: inherit; text-decoration: none; }
.foos-app button { font-family: var(--font-body); cursor: pointer; border: none; background: none; color: inherit; }
.foos-app img { max-width: 100%; display: block; }

/* §3a: one global focus ring, always visible for keyboard users. */
.foos-app :focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
  border-radius: 4px;
}

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---------- App header ---------- */
.app-header {
  position: sticky; top: 0; z-index: 50;
  background: color-mix(in srgb, var(--background) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--border);
}
.app-header .bar {
  max-width: var(--shell-max); margin: 0 auto;
  display: flex; align-items: center; gap: 12px;
  padding: 12px 16px;
}
.wordmark {
  font-family: var(--font-display); font-weight: 800; font-style: italic;
  font-size: 19px; letter-spacing: .5px;
}
.wordmark .red { color: var(--primary-ink); }
.hdr-spacer { flex: 1; }
.hdr-icon {
  width: 38px; height: 38px; border-radius: 50%;
  background: var(--secondary); border: 1px solid var(--border);
  display: flex; align-items: center; justify-content: center;
  font-size: 16px; position: relative; flex-shrink: 0;
}
.hdr-icon .badge {
  position: absolute; top: -3px; right: -3px;
  /* --primary-press, not --primary: white on #f94144 is 3.58:1 and these digits
     are 10px. The deeper red carries 5.43:1 in both modes. */
  background: var(--primary-press); color: #fff;
  font-size: 10px; font-weight: 700;
  min-width: 17px; height: 17px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  border: 2px solid var(--background);
  font-variant-numeric: tabular-nums;
}
.hdr-icon .badge:empty { display: none; }

/* ---------- Shell / content column ---------- */
.shell { max-width: var(--shell-max); margin: 0 auto; padding: 16px; }
.shell.wide { max-width: 1160px; }

/* ---------- Bottom tab bar (left rail on desktop) ---------- */
.tabbar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 60;
  background: color-mix(in srgb, var(--background) 92%, transparent);
  backdrop-filter: blur(16px);
  border-top: 1px solid var(--border);
  display: flex; justify-content: space-around; align-items: center;
  height: calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom: env(safe-area-inset-bottom);
}
.tab {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  font-size: 10.5px; color: var(--muted); font-weight: 500;
  min-width: 56px; min-height: 44px; padding: 6px 0;
}
.tab .ico { font-size: 20px; line-height: 1; }
/* `.foos-app a { color: inherit }` would otherwise make every link-tab read as
   full --foreground, flattening the active state. State beats markup here. */
.foos-app .tab, .foos-app .tab-play-wrap { color: var(--muted); }
.foos-app .tab[aria-current="page"],
.foos-app .tab-play-wrap[aria-current="page"] { color: var(--foreground); }
.tab[aria-current="page"] .ico { color: var(--primary-ink); }
.tab-play {
  width: 54px; height: 54px; border-radius: 50%;
  background: var(--primary-grad);
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; color: #fff;
  margin-top: -26px;
  border: 4px solid var(--background);
  box-shadow: 0 6px 18px rgba(249, 65, 68, .45);
  font-weight: 800; font-family: var(--font-display);
}
.tab-play-wrap {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  font-size: 10.5px; color: var(--muted); font-weight: 600;
}
.tab-play-wrap[aria-current="page"] { color: var(--foreground); }

/* More sheet trigger sits in the tab bar on mobile only. */
.tab-more { display: flex; }

/* A destination the shell knows about but whose page hasn't shipped yet. Shown,
   never linked — the owner can see what's coming without hitting a dead route.
   The "Soon" pill carries the message; dimming is confined to the decorative
   icon so no label drops below the 4.5:1 floor (§3a). */
.is-soon { cursor: default; }
.is-soon .ico, .is-soon .tab-play { opacity: .5; }
.is-soon .tab-play { box-shadow: none; }
/* Rail rows are horizontal, so the pill trails the label. Hidden on the mobile
   tab bar, where the only unready slot is Play and there's no room for it. */
.tab .pill.soon { display: none; margin-left: auto; }

/* ---------- Cards & type ---------- */
.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; margin-bottom: 14px;
}
.card-tight { padding: 12px; }
h1.page-title { font-family: var(--font-display); font-weight: 800; font-size: 24px; margin: 4px 0 14px; }
.section-label {
  font-family: var(--font-display);
  font-size: 12px; font-weight: 700; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--muted); margin: 22px 0 10px;
  display: flex; align-items: center; gap: 8px;
}
.section-label .rule { flex: 1; height: 1px; background: var(--border); }
/* A "see all" link riding the section rule: same row, sentence case, not shouty. */
.section-label .section-link {
  color: var(--muted); font-weight: 600; letter-spacing: 0; text-transform: none;
}
/* §3a numeral hierarchy: stats do the celebrating. */
.num, .mc-score, .stat .num, .stepper .val {
  font-family: var(--font-display); font-weight: 800; font-variant-numeric: tabular-nums;
}

/* ---------- Avatars ---------- */
.avatar {
  width: 40px; height: 40px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-weight: 700; font-size: 14px; color: #fff; flex-shrink: 0;
  font-family: var(--font-display); overflow: hidden;
}
.avatar.sm { width: 30px; height: 30px; font-size: 11px; }
.avatar.lg { width: 76px; height: 76px; font-size: 26px; }
.avatar img { width: 100%; height: 100%; object-fit: cover; }
.av-red { background: linear-gradient(135deg, #f94144, #8f1d2c); }
.av-blue { background: linear-gradient(135deg, #3b82f6, #1e3a8a); }
.av-green { background: linear-gradient(135deg, #22c55e, #14532d); }
.av-purple { background: linear-gradient(135deg, #a855f7, #581c87); }
.av-orange { background: linear-gradient(135deg, #fb923c, #7c2d12); }
.av-teal { background: linear-gradient(135deg, #2dd4bf, #134e4a); }

/* ---------- Pills & chips ---------- */
.pill {
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--secondary); border: 1px solid var(--border);
  color: var(--muted);
  border-radius: 999px; padding: 4px 11px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
.pill.live { background: rgba(249, 65, 68, .14); border-color: rgba(249, 65, 68, .45); color: var(--primary-ink); }
.pill.live .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--primary); animation: foos-pulse 1.6s infinite; }
.pill.open { background: rgba(34, 197, 94, .12); border-color: rgba(34, 197, 94, .4); color: var(--success); }
.pill.soon { background: rgba(251, 191, 36, .1); border-color: rgba(251, 191, 36, .38); color: var(--warning); }
.pill.done { opacity: .75; }
.pill.gold { background: rgba(245, 197, 24, .12); border-color: rgba(245, 197, 24, .4); color: var(--gold); }
.chip-row { display: flex; gap: 8px; overflow-x: auto; padding: 2px 0 10px; scrollbar-width: none; }
.chip-row::-webkit-scrollbar { display: none; }
.chip {
  padding: 8px 16px; border-radius: 999px; white-space: nowrap;
  background: var(--secondary); border: 1px solid var(--border);
  font-size: 13px; font-weight: 600; color: var(--muted);
}
.chip.active { background: var(--foreground); color: var(--background); border-color: var(--foreground); }

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  border-radius: 12px; font-weight: 700; font-size: 14.5px;
  padding: 13px 18px; min-height: 44px;
  transition: transform var(--motion-fast) var(--motion-ease);
}
.btn-primary { background: var(--primary-grad); color: #fff; font-size: 15px; font-weight: 800; }
.btn-primary:active { background: var(--primary-press); }
.btn-secondary { background: var(--secondary); color: var(--foreground); border: 1px solid var(--border-strong); }
.btn-ghost { color: var(--muted); font-weight: 600; }
.btn-danger { background: transparent; color: var(--primary-ink); border: 1px solid var(--primary); }
.btn-block { display: flex; width: 100%; }
.btn-sm { padding: 8px 14px; font-size: 13px; border-radius: 10px; min-height: 36px; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ---------- §3a state components ---------- */

/* Skeletons. Three recipes live in the phases that need them; the shimmer
   itself is defined once here. */
.skel {
  background: linear-gradient(100deg, var(--card-2) 40%, var(--secondary) 50%, var(--card-2) 60%);
  background-size: 200% 100%;
  animation: foos-shimmer 1.4s linear infinite;
  border-radius: var(--radius-sm);
}
.skel-line { height: 12px; margin-bottom: 8px; }
.skel-line.short { width: 45%; }
.skel-line.mid { width: 70%; }
.skel-block { height: 160px; }
.skel-circle { border-radius: 50%; width: 40px; height: 40px; }

/* The one empty-state anatomy. All list screens use this and only this. */
.empty { text-align: center; padding: 48px 24px; }
.empty .glyph {
  width: 72px; height: 72px; border-radius: 50%;
  background: var(--secondary); margin: 0 auto 18px;
  display: flex; align-items: center; justify-content: center; font-size: 32px;
}
.empty h2 { font-family: var(--font-display); font-weight: 800; font-size: 20px; margin-bottom: 8px; }
.empty p { color: var(--muted); font-size: 14px; max-width: 320px; margin: 0 auto 20px; }
.empty .btn { margin: 0 4px; }

/* Toasts. Never a browser alert(), anywhere. */
.toast {
  position: fixed; top: 18px; left: 50%;
  transform: translateX(-50%) translateY(-90px);
  background: var(--toast-ok-bg); border: 1px solid var(--toast-ok-border); color: var(--toast-ok-ink);
  border-radius: 12px; padding: 12px 20px; font-weight: 700; z-index: 120;
  transition: transform var(--motion-slow) var(--motion-ease);
  max-width: calc(100vw - 32px);
  box-shadow: var(--play-shadow);
  display: flex; align-items: center; gap: 4px;
}
.toast.show { transform: translateX(-50%) translateY(0); }
.toast.error { background: var(--toast-err-bg); border-color: var(--toast-err-border); color: var(--toast-err-ink); }
.toast.info { background: var(--toast-info-bg); border-color: var(--toast-info-border); color: var(--toast-info-ink); }

/* An error toast persists until dismissed (§3a), so it needs an affordance. */
.toast .toast-x {
  margin-left: 14px; font-weight: 800; opacity: .65; color: inherit;
  min-width: 24px; min-height: 24px;
}
.toast .toast-x:hover { opacity: 1; }

/* Degraded-live pill: cable down > 5s. Never silently stale. */
.live-degraded {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  background: rgba(251, 191, 36, .12); border: 1px solid rgba(251, 191, 36, .45);
  color: var(--warning); border-radius: 999px;
  padding: 7px 15px; font-size: 12.5px; font-weight: 700;
  z-index: 118; opacity: 0; pointer-events: none;
  transition: opacity var(--motion-normal) var(--motion-ease);
}
.live-degraded.show { opacity: 1; }

/* Live-update trust cue: a one-time flash on a node the server just patched. */
.live-patched { animation: foos-patch-flash 600ms var(--motion-ease) 1; }

@keyframes foos-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(249, 65, 68, .6); }
  55% { box-shadow: 0 0 0 9px rgba(249, 65, 68, 0); }
}
@keyframes foos-shimmer {
  from { background-position: 200% 0; }
  to { background-position: -200% 0; }
}
@keyframes foos-patch-flash {
  from { background-color: rgba(249, 65, 68, .08); }
  to { background-color: transparent; }
}

/* Score sheet (§1.2). Four shapes, each used at exactly the duration §5.6
   names at its use site — that is why the durations are literals there and not
   baked in here. */
@keyframes foos-pop-scale {
  0% { transform: scale(1); }
  40% { transform: scale(1.18); }
  100% { transform: scale(1); }
}
@keyframes foos-shake {
  0% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  45% { transform: translateX(6px); }
  70% { transform: translateX(-4px); }
  100% { transform: translateX(0); }
}
@keyframes foos-row-win {
  from { background-color: var(--success-subtle); box-shadow: inset 0 0 0 1px var(--success); }
  to { background-color: transparent; box-shadow: inset 0 0 0 0 transparent; }
}
/* A <dialog> goes display:none → block in one frame, so its entrance has to be
   an animation; a transition has nothing to transition from. */
@keyframes sheet-in {
  from { transform: translateY(105%); }
  to { transform: translateY(0); }
}
@keyframes sheet-in-modal {
  from { transform: scale(.97); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* §3a global reduced-motion kill switch with explicit static fallbacks. */
@media (prefers-reduced-motion: reduce) {
  .foos-app *, .foos-app *::before, .foos-app *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }
  .skel { animation: none; background: var(--card-2); }
  .pill.live .dot { animation: none; }
  .live-patched { animation: none; }
  /* §5.2 explicit fallbacks: the takeover renders instantly in its final
     position, and every pulse becomes a solid dot. */
  .takeover[open] .kicker,
  .takeover[open] h1,
  .takeover[open] .vs-card,
  .takeover[open] .to-actions { animation: none; opacity: 1; transform: none; }
  .takeover .glow { animation: none; }
  .takeover .kicker .dot,
  .playbar.is-now .pulse,
  .play-attention .pulse { animation: none; }
  /* §F.5 bracket page: every pulse becomes a solid dot, the find-my-match ring
     appears without a transition, and hover lifts don't move. */
  .mc-live .dot,
  .your-line.is-now .yl-dot { animation: none; }
  .match-card:hover { transform: none; }
  /* §C.10: the sheet opens and closes where it lands, the value just changes,
     the error panel simply appears, and the final win styling renders without
     the flash. The toast is deliberately NOT suppressed (🔒 §5.6). */
  .score-sheet[open], .score-sheet.is-closing { animation: none; transform: none; opacity: 1; }
  .score-sheet.is-shaking { animation: none; }
  .is-popping,
  .match-card.is-just-saved .mc-team.win,
  .match-card.is-just-saved .mc-score { animation: none; }
  .st-btn:active { transform: none; }
}

/* ============================================================
   THE PLAY SYSTEM (plan §5) — Play Bar, Match Takeover, /play hub
   ============================================================ */

/* ---------- Play Bar (§5.3) ----------
   Docked above the mobile tab bar; bottom-right on desktop. Rendered ONLY when
   the player has active involvement — zero clutter for everyone else, which is
   why there is no "empty" variant of this component. */
.playbar {
  position: fixed; left: 12px; right: 12px;
  bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 10px);
  z-index: 55;
  background: var(--play-wait-bg);
  border: 1px solid var(--border-strong);
  border-radius: 14px;
  display: flex; align-items: center; gap: 12px;
  padding: 10px 12px; width: auto;
  box-shadow: var(--play-shadow);
  max-width: calc(var(--shell-max) - 24px);
  margin: 0 auto;
  text-align: left;
  /* §3a sanctioned motion moment: the Play Bar state flip. */
  transition: background var(--motion-slow) var(--motion-ease),
              border-color var(--motion-slow) var(--motion-ease);
}
.playbar[hidden] { display: none; }
.playbar .pulse {
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--border-strong); flex-shrink: 0;
}
.playbar .pb-text { flex: 1; min-width: 0; }
.playbar .pb-kicker {
  font-size: 10.5px; font-weight: 700; letter-spacing: 1.2px;
  color: var(--muted); text-transform: uppercase;
}
.playbar .pb-kicker:empty { display: none; }
.playbar .pb-line {
  font-size: 13.5px; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.playbar .pb-btn {
  background: var(--primary-grad); color: #fff; font-weight: 800; font-size: 13px;
  border-radius: 10px; padding: 9px 14px; flex-shrink: 0; min-height: 40px;
  display: inline-flex; align-items: center;
}
.playbar .pb-btn[hidden] { display: none; }
/* The chime toggle. Muted by default and deliberately quiet in the layout: it
   is a preference, not a call to action (§3a celebration rationing). */
.playbar .pb-chime {
  flex-shrink: 0; width: 34px; height: 34px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; color: var(--muted); background: transparent;
}

/* Now: the one pulse on the one truly-live object (§3a). */
.playbar.is-now { background: var(--play-now-bg); border-color: var(--play-now-border); }
.playbar.is-now .pulse { background: var(--primary); animation: foos-pulse 1.6s ease-in-out infinite; }
.playbar.is-now .pb-kicker { color: var(--primary-ink); }

.playbar.is-on_deck { background: var(--play-deck-bg); border-color: var(--play-deck-border); }
.playbar.is-on_deck .pulse { background: var(--warning); }
.playbar.is-on_deck .pb-kicker { color: var(--warning); }

/* ---------- Match Takeover (§5.2) ----------
   A real <dialog>: focus trap, Esc, and backdrop come from the platform. */
.takeover {
  position: fixed; inset: 0; z-index: 100;
  width: 100%; max-width: none; height: 100%; max-height: none;
  border: none; padding: 28px; margin: 0;
  background: var(--takeover-bg);
  color: var(--foreground);
  flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  overflow-y: auto;

  /* The takeover keeps its deep red field in BOTH modes (see the light-mode
     block above), so it has to carry the dark palette's ink with it. Without
     this, light mode paints #0b0b0c display type on a near-black stage and the
     headline, player names and secondary link disappear. Tokens inherit, so
     redeclaring them here fixes every descendant — card, pills, buttons. */
  --foreground: #f8f8f8;
  --muted: #a0a0a0;
  --muted-2: #858585;
  --primary-ink: #f94144;
  --card: #0f0f0f;
  --card-2: #141414;
  --secondary: #1a1a1a;
  --border: #222222;
  --border-strong: #2e2e2e;
  --success: #22c55e;
  --warning: #fbbf24;
}
.takeover[open] { display: flex; }
.takeover::backdrop { background: rgba(0, 0, 0, .92); }
.takeover .kicker {
  font-size: 13px; font-weight: 800; letter-spacing: 3px; color: var(--primary-ink);
  /* flex-start, not center: a long tournament name wraps to two lines on a
     390px phone, and centering floats the dot into the gap between them. */
  text-transform: uppercase; display: flex; align-items: flex-start; gap: 10px;
}
.takeover .kicker .dot {
  width: 9px; height: 9px; border-radius: 50%; background: var(--primary);
  /* Rides the first line's optical center whether the label wraps or not. */
  flex: 0 0 auto; margin-top: .38em;
  animation: foos-pulse 1.6s ease-in-out infinite;
}
.takeover h1 {
  font-family: var(--font-display); font-weight: 800; font-size: clamp(34px, 9vw, 64px);
  line-height: 1.02; letter-spacing: -1px; margin: 14px 0 6px;
}
.takeover .sub { color: var(--muted); font-size: 15px; max-width: 420px; }
.takeover .to-actions { width: 100%; max-width: 420px; display: flex; flex-direction: column; align-items: center; }
.takeover .to-more { margin-top: 14px; }
/* The breathing glow: calm, 6s, never a strobe. Its own element so the dialog's
   own background stays a flat token. */
.takeover .glow {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(60% 45% at 50% 0%, rgba(249, 65, 68, .22), transparent 70%);
  animation: foos-breathe 6s ease-in-out infinite alternate;
}
.takeover.is-on-deck { background: var(--takeover-deck-bg); }
.takeover.is-on-deck .kicker { color: var(--warning); }
.takeover.is-on-deck .kicker .dot { background: var(--warning); }
.takeover.is-on-deck .glow { background: radial-gradient(60% 45% at 50% 0%, rgba(251, 191, 36, .18), transparent 70%); }

.vs-card {
  width: 100%; max-width: 420px; margin: 26px 0;
  background: var(--takeover-card-bg); border: 1px solid var(--border-strong);
  border-radius: 20px; padding: 18px; backdrop-filter: blur(8px);
}
.vs-card .vs-label {
  font-size: 11px; font-weight: 800; letter-spacing: 2px; color: var(--muted-2);
  text-transform: uppercase; text-align: left; padding: 0 4px 6px;
}
.vs-team { display: flex; align-items: center; gap: 12px; padding: 10px 4px; }
.vs-team .nm { font-family: var(--font-display); font-weight: 800; font-size: 19px; flex: 1; text-align: left; }
.vs-mid { display: flex; align-items: center; gap: 12px; color: var(--muted-2); font-weight: 800; font-size: 12px; letter-spacing: 2px; }
.vs-mid .rule { flex: 1; height: 1px; background: var(--border); }

/* Entrance choreography (§5.2), staged with the §3a duration tokens. The stage
   OFFSETS are the ones §5.2 specifies (60/120/200/300ms); the durations come
   from the token set, because §3a allows no others. */
.takeover[open] .kicker { animation: foos-rise var(--motion-slow) var(--motion-ease) 60ms both; }
.takeover[open] h1 { animation: foos-pop var(--motion-slow) var(--motion-ease) 120ms both; }
.takeover[open] .vs-card { animation: foos-rise var(--motion-slow) var(--motion-ease) 200ms both; }
.takeover[open] .to-actions { animation: foos-fade var(--motion-normal) var(--motion-ease) 300ms both; }

@keyframes foos-rise {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes foos-pop {
  from { opacity: 0; transform: scale(.94); }
  to { opacity: 1; transform: scale(1); }
}
@keyframes foos-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes foos-breathe {
  from { transform: scale(1); }
  to { transform: scale(1.06); }
}

/* ---------- /play hub (§5.4, mockup 03) ---------- */
.play-block {
  border: 1px solid var(--border); border-radius: 14px; padding: 14px;
  background: var(--card-2); margin-top: 12px;
}
.play-block.is-now { border-color: var(--play-now-border); }
.play-block.is-deck { border-color: var(--play-deck-border); }
.play-block .pb-title {
  font-size: 11px; font-weight: 800; letter-spacing: 1.6px; text-transform: uppercase;
  color: var(--muted-2); margin-bottom: 8px;
}
.play-block.is-now .pb-title { color: var(--primary-ink); }
.play-block.is-deck .pb-title { color: var(--warning); }
.opp-line { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.opp-line .nm { font-family: var(--font-display); font-weight: 800; font-size: 17px; flex: 1; min-width: 0; }
.play-actions { display: flex; gap: 10px; margin-top: 12px; }
.play-actions .btn { flex: 1; }
.play-foot {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  margin-top: 12px; color: var(--muted); font-size: 13px; flex-wrap: wrap;
}
.play-foot .link { color: var(--primary-ink); font-weight: 700; }
.play-foot b { color: var(--foreground); }
.play-attention { border-color: var(--play-now-border); background: var(--play-now-bg); }
.play-attention .pulse {
  width: 10px; height: 10px; border-radius: 50%; background: var(--primary); flex-shrink: 0;
  animation: foos-pulse 1.6s ease-in-out infinite;
}
.day-cell { text-align: center; min-width: 52px; }
.day-cell .day { font-size: 10.5px; font-weight: 700; color: var(--muted); text-transform: uppercase; }
.day-cell .day.today { color: var(--primary-ink); }
.day-cell .at { font-family: var(--font-display); font-weight: 800; font-size: 16px; font-variant-numeric: tabular-nums; }
.medal { font-size: 18px; }

/* Skeleton recipe: the play hub (§3a asks for three; this is the Play one). */
.skel-play .skel-line { height: 12px; }
.skel-play .skel-hero { height: 74px; margin-bottom: 12px; }
.skel-play .skel-row { height: 52px; margin-bottom: 10px; }

/* ---------- Feed cards ---------- */
.feed-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.feed-head .who { flex: 1; min-width: 0; }
.feed-head .name { font-weight: 700; font-size: 14.5px; }
.feed-head .ctx { font-size: 12.5px; color: var(--muted); }
.feed-media {
  border-radius: var(--radius-sm); overflow: hidden; margin: 10px 0;
  aspect-ratio: 16 / 10; position: relative; background: var(--card-2);
}
.feed-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.feed-title { font-family: var(--font-display); font-weight: 800; font-size: 17px; line-height: 1.25; margin-bottom: 4px; }
.feed-body { color: var(--muted); font-size: 14px; }
.feed-footnote { text-align: center; color: var(--muted-2); font-size: 12.5px; padding: 6px 0 10px; }
.feed-footnote a { color: var(--muted); font-weight: 600; }
.feed-actions {
  display: flex; gap: 20px; color: var(--muted);
  font-size: 13.5px; font-weight: 600; padding-top: 8px;
}
.feed-actions span { display: flex; align-items: center; gap: 6px; }
.feed-actions .liked { color: var(--primary-ink); }

/* ---------- Live Now rail ---------- */
.rail { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 6px; scrollbar-width: none; }
.rail::-webkit-scrollbar { display: none; }
.live-tile {
  min-width: 240px; border-radius: var(--radius-sm); overflow: hidden;
  border: 1px solid var(--border); position: relative;
  aspect-ratio: 16 / 9; display: flex; align-items: flex-end; padding: 10px;
}
.live-tile .pill { position: absolute; top: 10px; left: 10px; }
.thumb-label {
  font-family: var(--font-display); font-weight: 700; font-size: 13px;
  color: rgba(248, 248, 248, .94); text-shadow: 0 2px 8px rgba(0, 0, 0, .6);
}
.thumb-sub { font-weight: 500; opacity: .85; }
/* Stand-in imagery until real thumbnails exist for a surface. */
.g-table { background: linear-gradient(135deg, #20344f, #0b1526 60%, #050a12); }
.g-crowd { background: linear-gradient(135deg, #3d1b1c, #160809 65%); }
.g-club { background: linear-gradient(135deg, #173428, #07120c 65%); }
.g-studio { background: linear-gradient(135deg, #33214d, #120a1e 65%); }
.g-warm { background: linear-gradient(135deg, #4d2d14, #1c0f05 65%); }

/* ---------- Stat band ---------- */
.stat-band {
  display: flex; border: 1px solid var(--border);
  border-radius: var(--radius); overflow: hidden; background: var(--card);
}
.stat { flex: 1; text-align: center; padding: 14px 6px; }
.stat + .stat { border-left: 1px solid var(--border); }
.stat .num { font-size: 24px; }
.stat .lbl {
  font-size: 11px; color: var(--muted); font-weight: 600;
  letter-spacing: .4px; text-transform: uppercase;
}

/* ---------- Lists ---------- */
.row-item { display: flex; align-items: center; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.row-item:last-child { border-bottom: none; }
.row-main { flex: 1; min-width: 0; }
/* Both are spans in the markup (a row is often an <a>, which can't take block
   children) so they have to be told to be lines. Without this the sub-line runs
   on after the title — visible today on /play. */
.row-title { display: block; font-weight: 700; font-size: 14.5px; }
.row-sub { display: block; font-size: 12.5px; color: var(--muted); }

/* ---------- More sheet (mobile nav overflow) ---------- */
.sheet-backdrop {
  position: fixed; inset: 0; background: rgba(0, 0, 0, .68); z-index: 90;
  opacity: 0; pointer-events: none; transition: opacity var(--motion-normal) var(--motion-ease);
}
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 95;
  background: var(--card-2); border: 1px solid var(--border-strong); border-bottom: none;
  border-radius: 20px 20px 0 0;
  max-width: 520px; margin: 0 auto;
  padding: 12px 18px calc(22px + env(safe-area-inset-bottom));
  transform: translateY(105%);
  transition: transform var(--motion-slow) var(--motion-ease);
}
.sheet[open], .sheet.open { transform: translateY(0); }
.sheet .handle { width: 40px; height: 4px; border-radius: 2px; background: var(--border-strong); margin: 0 auto 14px; }
.sheet h3 { font-family: var(--font-display); font-weight: 800; font-size: 18px; margin-bottom: 6px; }
.more-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; padding: 12px 0 4px; }
.more-item {
  background: var(--card); border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 14px 8px; text-align: center; font-size: 12px; font-weight: 600; color: var(--muted);
}
.more-item .ico { display: block; font-size: 22px; margin-bottom: 6px; color: var(--foreground); }
.more-item .pill.soon { margin-top: 6px; }

/* ---------- Desktop: tab bar becomes the left rail ---------- */
@media (min-width: 900px) {
  body.foos-app, body.foos-app.has-playbar { padding-bottom: 40px; padding-left: 224px; }
  .tabbar {
    top: 0; bottom: 0; left: 0; right: auto; width: 210px; height: auto;
    flex-direction: column; justify-content: flex-start; gap: 4px;
    border-top: none; border-right: 1px solid var(--border);
    padding: 84px 12px 20px;
  }
  .tab {
    flex-direction: row; gap: 12px; font-size: 14px; width: 100%;
    padding: 11px 14px; border-radius: 12px; min-width: 0; justify-content: flex-start;
  }
  .tab[aria-current="page"] { background: var(--secondary); }
  .tab .ico { font-size: 19px; }
  .tab-play-wrap { width: 100%; margin: 8px 0; }
  .tab-play {
    width: 100%; height: 46px; border-radius: 12px; margin-top: 0; border: none;
    font-size: 15px; display: flex; gap: 8px;
  }
  /* Desktop rail shows every destination, so the More sheet is mobile-only. */
  .tab-more { display: none; }
  .rail-only { display: flex; }
  .tab .pill.soon { display: inline-flex; }
  .app-header .bar, .shell { max-width: 720px; }
  .shell.wide { max-width: 1160px; }
  /* §5.3 puts the bar at the top of the desktop right column. That column
     (live now / trending / my clubs) arrives with the feed in Phase 2; until
     then it docks bottom-right as the mockups show, which is where the eye
     already goes for a live-status object. */
  .playbar { left: auto; right: 24px; bottom: 24px; width: 380px; margin: 0; }
}
@media (max-width: 899px) {
  .rail-only { display: none; }
}

/* ============================================================
   THE BRACKET PAGE (plan §5.5) — /tournaments/:id
   One page for every format and every role: header card, the
   participant's "your line", the trees (or qualifying), and the
   sections under them. The score sheet (§5.6) and the champion
   moment (§5.5) layer onto these same atoms.
   ============================================================ */

/* ---------- §1.3 Dark-in-both-schemes surfaces carry their own ink ----------
   The Grand Finals panel and the crowned header keep a dark gold field in light
   mode, exactly like the shipped .takeover. Without redeclaring the dark
   palette here, light mode paints #0b0b0c type onto near-black and the panel
   goes blank — a real shipped bug, fixed live, not to be regressed. */
.gf-panel, .tourney-header.is-champion {
  --foreground: #f8f8f8;
  --muted: #a0a0a0;
  --muted-2: #858585;
  --primary-ink: #f94144;
  --card: #0f0f0f;
  --card-2: #141414;
  --secondary: #1a1a1a;
  --border: #222222;
  --border-strong: #2e2e2e;
  --success: #22c55e;
  --warning: #fbbf24;
  /* Bright gold, not light mode's #7d6200: on this dark gold field the bright
     value is the correct ink (11.7:1 on #171204). */
  --gold: #f5c518;
}

/* ---------- Header card (§A.2) ---------- */
.tourney-header {
  position: relative;
  display: grid;
  transition: border-color var(--motion-slow) var(--motion-ease);
}
.tourney-header__main, .tourney-header__champ { grid-area: 1 / 1; }
.th-title { display: flex; align-items: flex-start; gap: 10px; }
.th-title h1 {
  font-family: var(--font-display); font-weight: 800; font-size: 24px;
  line-height: 1.12; letter-spacing: -0.5px;
  flex: 1; min-width: 0; overflow-wrap: anywhere;
}
.th-title .pill { flex: 0 0 auto; margin-top: 3px; }
.th-meta {
  font-size: 13.5px; color: var(--muted); margin-top: 5px;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 6px;
}
.th-meta a { color: var(--primary-ink); font-weight: 600; }
.th-meta .sep { color: var(--muted-2); }
.tourney-header .chip-row { flex-wrap: wrap; overflow: visible; margin-top: 10px; }
/* Header chips are labels, not controls — smaller than the filter-row chip. */
.tourney-header .chip { padding: 5px 11px; font-size: 12px; }
.th-organizers {
  display: flex; align-items: center; gap: 9px;
  font-size: 13px; color: var(--muted); margin-top: 10px;
}
.avatar-stack { display: flex; flex-shrink: 0; }
.avatar-stack .avatar { border: 2px solid var(--card); }
.avatar-stack .avatar + .avatar { margin-left: -10px; }
.th-actions { display: flex; gap: 8px; margin-top: 14px; min-height: 44px; align-items: center; }
.th-actions .btn { flex: 1; min-height: 40px; }

@media (min-width: 1280px) {
  .tourney-header__main { display: flex; gap: 24px; align-items: center; }
  .th-block { flex: 1; min-width: 0; }
  .th-actions { margin-top: 0; flex: 0 0 auto; }
  .th-actions .btn { flex: 0 0 auto; }
}

/* ---------- The participant's "your line" (§A.3) ---------- */
.your-line {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  border: 1px solid var(--border-strong);
}
.yl-dot {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--border-strong); flex: 0 0 auto;
}
.yl-text { flex: 1; min-width: 180px; font-size: 13.5px; font-weight: 600; }
.yl-sub { display: block; font-weight: 500; color: var(--muted); font-size: 12.5px; }
.yl-actions { display: flex; gap: 8px; flex-shrink: 0; }
/* Border + dot carry the state; the card background stays flat --card, which is
   deliberately quieter than the Play Bar's gradient (§A.3). */
.your-line.is-now { border-color: var(--play-now-border); }
.your-line.is-now .yl-dot { background: var(--primary); animation: foos-pulse 1.6s ease-in-out infinite; }
.your-line.is-deck { border-color: var(--play-deck-border); }
.your-line.is-deck .yl-dot { background: var(--warning); }
.your-line.is-waiting .yl-dot { background: var(--muted-2); }

/* ---------- Bracket tools row (§A.5) ---------- */
.bracket-tools {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 10px;
}
.bracket-tools .chip { padding: 7px 14px; font-size: 12.5px; min-height: 36px; display: inline-flex; align-items: center; }
.bracket-hint { font-size: 12px; color: var(--muted-2); flex: 1; min-width: 140px; }

/* ---------- The trees (§A.4) ----------
   Structure REUSED from the shipped bracket page: a flex row of round columns,
   each match wrapped with CSS connector elbows. It handles losers-bracket
   dropdown rounds, which an ::after-elbow approach cannot. Only the paint is new. */
.bracket-viewport {
  overflow-x: auto; overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  padding: 2px 0 10px;
  position: relative; width: 100%;
}
.bracket-grid {
  display: flex; align-items: stretch; min-width: max-content;
  transform: scale(var(--bracket-scale, 1));
  transform-origin: top left;
}
.bracket-round { display: flex; flex-direction: column; flex: 0 0 auto; }
.bracket-round__header {
  position: sticky; left: 0;
  font-family: var(--font-display);
  font-size: 11px; font-weight: 700; letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--muted-2); text-align: center; padding: 10px 0;
  white-space: nowrap;
}
.bracket-round__matches {
  display: flex; flex-direction: column; flex: 1; justify-content: space-around;
}
.bracket-match-wrapper {
  display: flex; align-items: center; justify-content: flex-start;
  position: relative; flex: 1; min-height: 76px;
}
.bracket-round:not(.bracket-round--first) .bracket-match-wrapper { min-height: 0; }
.bracket-match-wrapper .match-card { flex-shrink: 0; position: relative; z-index: 2; }

.connector--in {
  width: var(--conn-w); height: var(--conn-thick);
  background: var(--conn-line); flex-shrink: 0;
}
.connector--out { position: relative; width: var(--conn-w); height: 100%; flex-shrink: 0; }
.connector__h {
  position: absolute; left: 0; top: 50%; width: 100%;
  height: var(--conn-thick); background: var(--conn-line);
  transform: translateY(-50%);
}
.bracket-match--odd .connector__v {
  position: absolute; right: 0; top: 50%;
  width: var(--conn-thick); height: calc(50% + 1px); background: var(--conn-line);
}
.bracket-match--even .connector__v {
  position: absolute; right: 0; bottom: 50%;
  width: var(--conn-thick); height: calc(50% + 1px); background: var(--conn-line);
}
.bracket-match-wrapper:only-child .connector__v { display: none; }

/* ---------- The match card (§B) ----------
   A real <button> with span internals: buttons take phrasing content, so every
   internal block is a span with a display class (§B.1). */
.match-card {
  display: block; width: var(--match-w); padding: 0; text-align: left;
  background: var(--card-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  font-size: 13px; overflow: hidden;
  transition: transform var(--motion-fast) var(--motion-ease),
              border-color var(--motion-fast) var(--motion-ease),
              box-shadow var(--motion-normal) var(--motion-ease);
}
.mc-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 5px 10px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase;
  color: var(--muted-2);
  /* color-mix, not white-alpha: the mockup's tint is invisible in light mode. */
  background: color-mix(in srgb, var(--foreground) 3%, transparent);
  border-bottom: 1px solid var(--border);
}
.mc-label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mc-status { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 4px; }
.mc-status .dot { width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.mc-live { color: var(--primary-ink); }
.mc-live .dot { background: var(--primary); animation: foos-pulse 1.6s ease-in-out infinite; }
.mc-void { color: var(--warning); }
.mc-teams { display: block; }
.mc-team { display: flex; align-items: center; gap: 8px; padding: 8px 10px; }
.mc-team + .mc-team { border-top: 1px solid var(--border); }
.mc-seed {
  flex: 0 0 14px; width: 14px; text-align: center;
  font-size: 10px; font-weight: 700; color: var(--muted-2);
  font-variant-numeric: tabular-nums;
}
.mc-name {
  flex: 1; min-width: 0; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mc-name.is-tbd { color: var(--muted-2); font-style: italic; font-weight: 500; }
.mc-score { flex: 0 0 auto; font-size: 15px; text-align: right; min-width: 16px; }
.mc-forfeit { flex: 0 0 auto; font-size: 11px; color: var(--muted-2); font-weight: 600; }
.mc-games {
  display: block; padding: 0 10px 8px;
  font-size: 11px; color: var(--muted-2); font-variant-numeric: tabular-nums;
}
.mc-you {
  font-size: 10px; font-weight: 800; letter-spacing: .8px;
  color: var(--primary-ink); margin-left: 5px;
}
.mc-iron {
  font-size: 10px; font-weight: 800; letter-spacing: .5px; margin-left: 5px;
  color: var(--warning); background: rgba(251, 191, 36, .1);
  border: 1px solid rgba(251, 191, 36, .38);
  border-radius: 4px; padding: 0 4px; white-space: nowrap;
}

/* §B.3: the loser recedes by token, not by opacity. `opacity: .45` measures
   4.36:1 dark / ~3.6:1 light and fails the §3a floor; --muted passes both
   (6.9:1 / 6.3:1). No green on routine winners — green is rationed. */
.mc-team.loss .mc-name, .mc-team.loss .mc-score { color: var(--muted); }
.mc-team.win .mc-name, .mc-team.win .mc-score { color: var(--foreground); }

/* §B.5: dimming is confined to the teams area so the head label never drops
   below the floor (whole-card opacity takes "IF NEEDED" to ~2.3:1). */
.match-card.is-ghost .mc-teams { opacity: .5; }
.match-card.is-bye .mc-teams { opacity: .65; }
.match-card.is-void .mc-teams { opacity: .5; }

@media (hover: hover) {
  .match-card:hover { transform: translateY(-2px); border-color: var(--foreground); }
}
.match-card:active { transform: scale(.99); }

/* The viewer's own match — applied client-side from play_status (§1.4/§G.4), so
   broadcast HTML stays neutral and shared. */
.match-card.your-match {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px var(--primary), 0 4px 24px rgba(249, 65, 68, .35);
}
/* Find-my-match (§B.6): same family, stronger, held 1400ms. */
.match-card.is-found {
  box-shadow: 0 0 0 3px var(--primary), 0 4px 34px rgba(249, 65, 68, .65);
}

/* ---------- Grand Finals panel (§A.4) ---------- */
.gf-panel {
  background: var(--gold-panel-bg);
  border: 1px solid var(--gold-panel-line);
  border-radius: var(--radius);
  padding: 16px;
  color: var(--foreground);
}
.gf-legs { display: flex; flex-wrap: wrap; gap: 12px; }
.gf-leg { min-width: 0; }
.gf-reset-note {
  background: rgba(251, 191, 36, .1); border: 1px solid rgba(251, 191, 36, .38);
  color: var(--warning); border-radius: var(--radius-sm);
  padding: 9px 12px; font-size: 13px; font-weight: 700; margin-bottom: 12px;
}
.gf-foot { font-size: 12px; color: var(--muted); margin-top: 14px; }
@media (min-width: 900px) {
  .gf-panel { max-width: 800px; margin-left: auto; margin-right: auto; }
}

/* ---------- Match grids (round robin + qualifying rounds, §A.6/§A.7) ---------- */
.round-block { margin-bottom: 6px; }
.match-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 10px;
}
.match-grid .match-card { width: 100%; }
@media (max-width: 520px) { .match-grid { grid-template-columns: 1fr; } }
.byes-strip {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 10px;
}
.byes-strip .byes-label { font-size: 13px; color: var(--muted); }
.byes-strip .chip { padding: 5px 11px; font-size: 12px; }
.rounds-collapse > summary, .roster-more > summary {
  cursor: pointer; list-style: none; min-height: 44px;
  display: flex; align-items: center;
  font-size: 13.5px; font-weight: 700; color: var(--primary-ink);
}
.rounds-collapse > summary::-webkit-details-marker,
.roster-more > summary::-webkit-details-marker { display: none; }
.rounds-collapse > summary::before, .roster-more > summary::before { content: '▸'; margin-right: 8px; }
.rounds-collapse[open] > summary::before, .roster-more[open] > summary::before { content: '▾'; }

/* ---------- The unified standings table (§A.6/§A.7) ---------- */
.stand-wrap { overflow-x: auto; max-height: 520px; overflow-y: auto; }
.stand-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.stand-table thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--card);
  font-size: 12px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--muted-2); text-align: left; padding: 8px 10px;
  border-bottom: 1px solid var(--border);
}
.stand-table td { padding: 9px 10px; border-bottom: 1px solid var(--border); }
.stand-table tbody tr:last-child td { border-bottom: none; }
.stand-table th.num, .stand-table td.num { text-align: center; font-variant-numeric: tabular-nums; }
.stand-table td.rank {
  width: 42px; text-align: center;
  font-family: var(--font-display); font-weight: 800; color: var(--primary-ink);
  font-variant-numeric: tabular-nums;
}
.stand-table td.who { font-weight: 600; }
.stand-table td.num.strong { font-weight: 800; }
/* A left-edge marker, never a background wash: rank colour has to survive the
   viewer's own row sitting on top of an advancing row. */
.stand-table tr.is-advancing td:first-child { box-shadow: inset 3px 0 0 var(--success); }
.stand-table tr.is-you td:first-child { box-shadow: inset 3px 0 0 var(--primary); }
.stand-table tr.is-out td { color: var(--muted-2); }
.tag {
  font-size: 10px; font-weight: 700; letter-spacing: .6px;
  border-radius: 4px; padding: 1px 5px; margin-left: 6px; white-space: nowrap;
}
.tag-adv { color: var(--success); background: rgba(34, 197, 94, .12); border: 1px solid rgba(34, 197, 94, .4); }
.tag-out { color: var(--muted-2); background: var(--secondary); border: 1px solid var(--border); }
.tag-you { color: var(--primary-ink); background: rgba(249, 65, 68, .14); border: 1px solid rgba(249, 65, 68, .45); }

/* ---------- Below-tree sections (§A.8) ---------- */
.rank-cell {
  flex: 0 0 34px; text-align: center;
  font-family: var(--font-display); font-weight: 800; font-size: 14px;
  color: var(--muted); font-variant-numeric: tabular-nums;
}
.row-item .pill { flex: 0 0 auto; }
.podium-row { display: flex; gap: 10px; align-items: flex-end; margin-bottom: 14px; }
.podium-cell {
  flex: 1; min-width: 0; text-align: center;
  background: var(--card-2); border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 14px 8px;
}
.podium-cell.place-1 {
  order: 2; padding: 20px 10px;
  border-color: var(--gold-line); box-shadow: 0 6px 18px rgba(212, 175, 55, .25);
}
.podium-cell.place-2 { order: 1; }
.podium-cell.place-3 { order: 3; }
.podium-cell .medal-label {
  font-size: 11px; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted-2);
}
.podium-cell.place-1 .medal-label { color: var(--gold); }
.podium-cell.place-2 .medal-label { color: var(--silver); }
.podium-cell.place-3 .medal-label { color: var(--bronze); }
.podium-cell .who { font-weight: 700; font-size: 14px; margin-top: 5px; overflow-wrap: anywhere; }
.podium-cell .pts { font-family: var(--font-display); font-weight: 800; font-size: 18px; margin-top: 3px; }
.facts-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; }
.fact {
  display: flex; flex-direction: column; gap: 3px;
  padding: 11px 13px; background: var(--card-2);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.fact .lbl {
  font-size: 11px; font-weight: 800; letter-spacing: .08em; text-transform: uppercase;
  color: var(--muted-2);
}
.fact .val { font-family: var(--font-display); font-weight: 800; font-size: 16px; line-height: 1.2; }

@media (min-width: 1280px) {
  .below-tree-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
}

/* ---------- Skeleton recipe: bracket columns (§1.6) ---------- */
.skel-bracket { display: flex; gap: var(--conn-w); overflow: hidden; }
.skel-bracket .skel-col { flex: 0 0 auto; }
.skel-bracket .skel-line { width: var(--match-w); }
.skel-bracket .skel-match { width: var(--match-w); height: 76px; margin-bottom: 14px; }

/* ---------- The score sheet (§C) ----------
   A real <dialog> on the shipped `.sheet` shell, so the focus trap, Esc and the
   inert backdrop come from the platform (⚑ F-21). Bottom sheet on a phone,
   centred modal from 768px — same element, media-query placement (⚑ F-22). */
.score-sheet {
  z-index: 130; /* above the toast layer: the sheet owns the screen while open */
  max-height: 88vh; overflow-y: auto; overscroll-behavior: contain;
  color: var(--foreground);
  transform: none; transition: none;
}
.score-sheet::backdrop { background: rgba(0, 0, 0, .68); }
.score-sheet[open] { transform: none; animation: sheet-in var(--motion-slow) var(--motion-ease) both; }
.score-sheet.is-closing {
  animation: none;
  transform: translateY(105%);
  transition: transform 220ms var(--motion-ease); /* §5.6 explicit */
}
.score-sheet.is-shaking { animation: foos-shake var(--motion-normal) var(--motion-ease) 1; }

@media (min-width: 768px) {
  /* inset:0 + margin:auto + fit-content height is the platform's own way to
     centre a dialog; it needs all four offsets, which is why `.sheet`'s
     bottom-anchored trio is restated rather than unset. */
  .score-sheet {
    top: 0; right: 0; bottom: 0; left: 0;
    margin: auto; height: fit-content;
    max-width: 480px; max-height: 88vh;
    border-radius: 20px; border-bottom: 1px solid var(--border-strong);
    padding: 16px 20px 20px;
  }
  .score-sheet[open] { animation: sheet-in-modal var(--motion-normal) var(--motion-ease) both; }
  .score-sheet.is-closing { transform: scale(.97); opacity: 0; transition: all var(--motion-normal) var(--motion-ease); }
  .score-sheet .handle { display: none; }
}

.ss-head { display: flex; align-items: center; gap: 10px; }
.ss-head h3 { flex: 1; min-width: 0; margin: 0; font-size: 17px; }
.ss-format { font-size: 12.5px; color: var(--muted); margin: 2px 0 6px; }
.ss-status { font-size: 13px; color: var(--muted); margin: 2px 0 10px; }
.ss-note, .ss-by { font-size: 12.5px; color: var(--muted-2); margin-top: 10px; }
.ss-games { font-size: 12.5px; color: var(--muted); margin-top: 10px; font-variant-numeric: tabular-nums; }
.ss-skeleton { display: grid; gap: 10px; padding: 6px 0 4px; }

/* §C.7: `--destructive` on --card-2 measures 3.87:1 and fails the §3a floor, so
   inline form errors reuse the toast's vetted error tokens as a panel (15.6:1
   dark / 10.5:1 light). This is the product-wide inline-error convention now. */
.ss-error {
  display: block;
  background: var(--toast-err-bg); border: 1px solid var(--toast-err-border);
  color: var(--toast-err-ink); border-radius: var(--radius-sm);
  padding: 9px 12px; font-size: 13px; font-weight: 600; margin: 0 0 10px;
}

/* ---- team rows (§C.3/§C.4) ---- */
.score-teams { display: grid; gap: 8px; }
.score-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  background: var(--card); border: 1px solid var(--border);
  border-radius: 14px; padding: 12px; min-height: 64px;
  text-align: left;
  transition: border-color var(--motion-fast) var(--motion-ease);
}
.score-row .team { flex: 1; min-width: 0; font-size: 15px; font-weight: 700; overflow: hidden; text-overflow: ellipsis; }
.score-row.winner { border-color: var(--success); }
.score-row.is-loss .team { color: var(--muted); }
.score-row .tally {
  font-family: var(--font-display); font-weight: 800; font-size: 26px;
  font-variant-numeric: tabular-nums; min-width: 30px; text-align: right;
}
.pick-mark {
  width: 28px; height: 28px; border-radius: 50%; flex: 0 0 auto;
  border: 2px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
}
.score-pick.winner .pick-mark { background: var(--success); border-color: var(--success); }
.score-pick.winner .pick-mark::after { content: "✓"; color: #fff; font-size: 15px; font-weight: 800; }

/* ---- per-game steppers (§C.4) ---- */
.score-games { display: grid; grid-template-columns: 30px 1fr 1fr; gap: 8px 12px; align-items: center; margin-top: 14px; }
/* A single-game match (a points race, or best-of-1) has no G-label gutter and
   no tally column — two steppers, side by side. */
.score-games.is-single { grid-template-columns: 1fr 1fr; }
.score-games .sg-head {
  font-size: 11px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
  color: var(--muted-2); text-align: center;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sg-row { display: contents; }
.sg-label { font-family: var(--font-display); font-weight: 800; font-size: 11px; letter-spacing: 1px; color: var(--muted-2); }
.stepper { display: flex; align-items: center; justify-content: center; gap: 10px; }
/* 44×44, not the mockup's 36px — the workspace touch law outranks it (⚑ F-26). */
.st-btn {
  width: 44px; height: 44px; flex: 0 0 44px; border-radius: 10px;
  background: var(--secondary); border: 1px solid var(--border-strong);
  font-size: 20px; font-weight: 700; color: var(--foreground);
  display: flex; align-items: center; justify-content: center;
  transition: transform var(--motion-fast) var(--motion-ease);
}
.st-btn:active { transform: scale(.94); }
.st-val {
  width: 46px; min-width: 30px; text-align: center;
  font-family: var(--font-display); font-weight: 800; font-size: 26px;
  font-variant-numeric: tabular-nums;
  background: transparent; border: none; color: var(--foreground);
  -moz-appearance: textfield;
}
.st-val::-webkit-outer-spin-button, .st-val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.is-popping { animation: foos-pop-scale var(--motion-fast) var(--motion-ease) 1; }

.ss-actions { margin-top: 16px; }
.ss-body .btn-block + .btn-block { margin-top: 8px; }

/* ---- 409, already reported (§C.8) ----
   Amber, not red (⚑ F-32): nothing failed — the result simply already exists. */
.score-conflict {
  background: var(--play-deck-bg); border: 1px solid var(--play-deck-border);
  border-radius: 14px; padding: 14px;
}
.score-conflict .sc-copy { font-size: 13.5px; font-weight: 600; margin: 12px 0; }
.ss-mini { display: flex; justify-content: center; }
.ss-mini .match-card { width: 100%; max-width: var(--match-w); }

/* ---- admin section (§C.5) ---- */
.score-admin { margin-top: 16px; border-top: 1px dashed var(--border-strong); }
.score-admin > summary {
  min-height: 44px; display: flex; align-items: center;
  font-size: 13px; font-weight: 700; color: var(--muted);
  cursor: pointer; list-style: none;
}
.score-admin > summary::-webkit-details-marker { display: none; }
.score-admin > summary::before { content: "⚙"; margin-right: 8px; }
.admin-grid, .admin-picker, .admin-confirm-actions { display: flex; flex-wrap: wrap; gap: 8px; }
.admin-confirm { padding: 10px 0 4px; }
.admin-confirm-copy { font-size: 13.5px; font-weight: 600; margin-bottom: 10px; }
.admin-note { font-size: 12px; color: var(--muted-2); margin-top: 10px; }

/* ---- the submitter's own result sequence (§C.6) ----
   Richer than the generic trust flash, and it replaces it: live-patch skips the
   echo for this card (§G.2) so the two never stack. */
.match-card.is-just-saved .mc-team.win { animation: foos-row-win 600ms var(--motion-ease) 1; }
.match-card.is-just-saved .mc-score { animation: foos-pop-scale var(--motion-normal) var(--motion-ease) 1; }

/* ---------- Anonymous spectator shell footer (§A.1, F-5) ---------- */
.spectator-foot {
  max-width: 1160px; margin: 32px auto 0; padding: 24px 16px 40px;
  border-top: 1px solid var(--border); text-align: center;
}
.spectator-foot .sf-line { font-size: 14px; color: var(--muted); margin: 0 0 14px; }
.spectator-foot .sf-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
