/* o!bwc base stylesheet. Theme colors come from per-contest CSS variables set
   inline in <head> (--primary --accent --accent2 --bg --ink). Mobile-first;
   no framework. The signature light-grey blueprint grid is pure CSS. */

/* Non-theme design tokens (the per-contest colour vars stay in the inline
   <head> theme block and are never redefined here). --mono drives the technical
   "blueprint label" treatment on small caps chips; --line is the thin ink rule. */
:root {
  --mono: ui-monospace, 'SF Mono', 'Cascadia Mono', 'Segoe UI Mono', Menlo, Consolas, monospace;
  --line: rgba(20,22,31,.14);
  /* Surface lift. Two layers on purpose: a 1px CONTACT shadow that pins the box
     to the paper (without it the box floats and the edge looks soft), plus a
     wide, very low-alpha AMBIENT one that gives the depth. Both are the ink
     colour at low alpha, never pure black — black turns grey on this warm-grey
     grid and reads as dirt. Kept light enough that the blueprint grid still
     shows through around every card. */
  --shadow: 0 1px 1px rgba(20,22,31,.07), 0 4px 14px rgba(20,22,31,.07);
  /* Hover: same shape, pushed one step. Only ever transitioned as box-shadow —
     no transform, so nothing reflows and no text resamples. */
  --shadow-lift: 0 1px 2px rgba(20,22,31,.10), 0 8px 22px rgba(20,22,31,.13);
  /* For a listed box that sits INSIDE an already-shadowed card (entry cards,
     song cards): contact layer only, so two shadows never stack into a smudge. */
  --shadow-nested: 0 1px 2px rgba(20,22,31,.07);
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  font: 16px/1.55 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
  color: var(--ink, #14161F);
  background-color: var(--bg, #E9EAED);
  background-image:
    repeating-linear-gradient(0deg, transparent 0 23px, rgba(20,22,31,.05) 23px 24px),
    repeating-linear-gradient(90deg, transparent 0 23px, rgba(20,22,31,.05) 23px 24px);
}
.wrap { width: 100%; max-width: 72rem; margin: 0 auto; padding: 0 1rem; }
a { color: var(--primary, #2B3FE0); text-decoration: none; }
a:hover { text-decoration: underline; }
h1, h2, h3 { line-height: 1.2; }
img { max-width: 100%; height: auto; }
code { background: rgba(20,22,31,.08); padding: .1em .35em; border-radius: 3px; font-size: .92em; }

.skip { position: absolute; left: -999px; }
.skip:focus { left: 1rem; top: .5rem; background: #fff; padding: .4rem .7rem; z-index: 10; }

/* Header — tight blueprint bar: the logo, the site name, nav, and the user chip. */
.site-header { background: var(--primary); color: #fff; border-bottom: 3px solid var(--ink); }
.header-row { display: flex; align-items: center; gap: .5rem 1rem; min-height: 52px; flex-wrap: wrap; padding: .35rem 0; }
.brand { color: #fff; display: flex; align-items: center; gap: .55rem; font-weight: 800; flex: 0 0 auto; }
.brand:hover { text-decoration: none; }
.brand-logo { display: block; height: 36px; width: 36px; border-radius: 3px; background: #fff; }
.brand-sub { font-size: .82rem; font-weight: 700; letter-spacing: .01em; opacity: .92; display: none; }
/* Contest-year chip — compact lime/cyan mono tag next to the brand; renders on
   every page (visible even on mobile where .brand-sub is hidden). */
.brand-year { font-family: var(--mono); font-size: .66rem; font-weight: 800; letter-spacing: .06em; color: var(--ink); background: var(--accent); padding: .06rem .38rem; border-radius: 3px; line-height: 1.3; box-shadow: inset 0 -2px 0 rgba(63,216,240,.6); }
/* Desktop layout: brand (left) · nav + sign-in (right), all on one centered row.
   .mainnav flexes to fill and right-aligns its links; .header-actions never shrinks.
   On mobile the whole row wraps cleanly (flex-wrap on .header-row). */
.mainnav { display: flex; flex: 1 1 auto; min-width: 0; gap: .2rem .9rem; flex-wrap: wrap; justify-content: flex-end; }
.mainnav a { color: #fff; font-weight: 700; font-size: .95rem; text-transform: uppercase; letter-spacing: .04em; padding: .1rem 0; opacity: .9; font-family: var(--mono); }
.mainnav a:hover { opacity: 1; text-decoration: none; box-shadow: inset 0 -2px 0 var(--accent); }
.header-actions { flex: 0 0 auto; }
.userbox { color: var(--ink); background: var(--accent); padding: .3rem .7rem; border-radius: 3px; font-weight: 800; font-size: .82rem; font-family: var(--mono); letter-spacing: .02em; }
.userbox:hover { text-decoration: none; filter: brightness(.96); }

/* Announcements */
.annwrap { margin-top: .75rem; }
.ann { border-left: 4px solid var(--primary); background: #fff; padding: .6rem .9rem; margin-bottom: .5rem; border-radius: 0 4px 4px 0; box-shadow: 0 1px 2px rgba(0,0,0,.06); }
.ann-warning { border-left-color: #d98a00; }
.ann-event { border-left-color: var(--accent2); }

/* Main */
main.wrap { padding-top: 1.5rem; padding-bottom: 3rem; min-height: 50vh; }
.card { background: #fff; border: 1px solid var(--line); border-radius: 3px; padding: 1.1rem 1.2rem; margin-bottom: 1rem; }
/* Blueprint section heading: a thin blue rule under the title echoes the banner's
   solid blue bar without the heavy fill. Direct-child only, so nested heads and
   flex header rows (admin, bracket manager) are untouched. */
.card > h2 { margin: 0 0 .7rem; padding-bottom: .35rem; border-bottom: 2px solid var(--primary); }
.grid { display: grid; gap: 1rem; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); }
.btn { display: inline-block; background: var(--primary); color: #fff; border: 0; border-radius: 3px; padding: .55rem 1rem; font-weight: 700; cursor: pointer; }
.btn:hover { text-decoration: none; filter: brightness(1.05); }
.btn-accent { background: var(--accent); color: var(--ink); }
.pill { display: inline-block; background: var(--accent2); color: var(--ink); border-radius: 3px; padding: .1rem .55rem; font-size: .76rem; font-weight: 800; font-family: var(--mono); letter-spacing: .02em; }
.empty { color: #666; font-style: italic; }
.scroll-x { overflow-x: auto; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: .45rem .6rem; border-bottom: 1px solid rgba(20,22,31,.1); }
th { font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; color: #555; font-family: var(--mono); }

.hero { background: var(--primary); color: #fff; border: 2px solid var(--ink); border-radius: 3px; padding: 1.6rem 1.4rem; margin-bottom: 1.5rem; }
.hero h1 { margin: 0 0 .3rem; font-size: 1.8rem; }
.hero .sub { opacity: .9; }

/* Footer */
.site-footer { border-top: 3px solid var(--ink); background: #fff; margin-top: 2rem; }
.footer-row { display: flex; justify-content: space-between; align-items: center; gap: 1rem; padding: 1rem 0; flex-wrap: wrap; font-size: .9rem; color: #555; }
.footlinks { display: flex; gap: 1rem; }

@media (min-width: 720px) {
  .brand-sub { display: inline; }
}

/* ==========================================================================
   Public pages (design 03 §3.1) + shared partial styles (flag, team card,
   matchup card, score table, countdown). Driven by the per-contest theme CSS
   variables; works without JS.
   ========================================================================== */

/* -- shared bits ---------------------------------------------------------- */
.muted { color: #666; }
.small { font-size: .85rem; }
.page-head { margin: 0 0 1rem; }
.page-head h1 { margin: 0; }
.page-sub { color: #555; margin: .3rem 0 0; }
.prose > :first-child { margin-top: 0; }
.prose > :last-child { margin-bottom: 0; }
.prose h2, .prose h3 { margin-top: 1.2rem; }
.card.gated { text-align: center; }
.formula { background: rgba(20,22,31,.05); border-left: 3px solid var(--primary); padding: .8rem 1rem; margin: .8rem 0; font-family: var(--mono); }
.formula div { margin: .25rem 0; }

/* Blueprint labels: small caps, seed/flag/status chips, phase + state pills,
   metric numbers, countdowns — the "technical annotation" layer of the design.
   Font only (each class keeps its own colours/box), so this can't break layout. */
.seed-badge, .q-badge, .team-status, .flag, .round-phase, .mc-seed, .mc-state,
.mc-vs, .mh-seed, .mh-won, .j-chip, .j-progress-num, .tag, .lft-badge, .ql-head,
.ql-num, .adm-tile-label, .adm-ribbon, .anon-dot, .ann-sev, .bp-atom, .sub-note,
.countdown, .big-count, .adm-tile-num, .tally, .pk-num, .pk-seg-num {
  font-family: var(--mono);
}

/* -- flag ------------------------------------------------------------------ */
/* Real country flags (osu! flag PNGs — Windows can't render flag emoji). */
.flag-img { display: inline-block; width: 24px; height: 16px; object-fit: cover; border-radius: 2px; vertical-align: middle; box-shadow: 0 0 0 1px rgba(20,22,31,.18); background: rgba(20,22,31,.06); }
/* Neutral text placeholder for unknown/empty codes (no image to load). */
.flag { display: inline-block; font-size: .68rem; font-weight: 800; letter-spacing: .04em; background: var(--ink); color: #fff; padding: .1rem .3rem; border-radius: 3px; vertical-align: middle; }
.flag-unknown { background: #999; }

/* -- avatar ---------------------------------------------------------------- */
/* The osu! profile picture shown beside a REAL user's name (rosters, free
   agents, contribution tables, the pick'em leaderboard, screening, and the
   qualifier's judge rows once judge names are public). Built by
   app/views/partials/avatar.js — never rendered next to an anonymised entry.

   inline-block + vertical-align keeps it inside existing <li>/<td> rows without
   turning them into flex containers, so no row layout changes; `flex: none`
   stops it being squashed in the rows that ARE flex (.fa-head, .adm-member).
   Sizes are fixed px squares (never a %), so a long roster cannot widen the
   page — at 375px the rows still wrap on the text, not the picture. */
.avatar { display: inline-block; flex: none; vertical-align: middle; object-fit: cover;
  border-radius: 6px; background: rgba(20,22,31,.06); box-shadow: 0 0 0 1px rgba(20,22,31,.18); }
.avatar-sm { width: 20px; height: 20px; border-radius: 4px; }
.avatar-md { width: 28px; height: 28px; }
.avatar-lg { width: 48px; height: 48px; }
.avatar-xl { width: 56px; height: 56px; }
/* No avatar_url (a user who has never signed in): a neutral initial, never a
   broken image. line-height centres the letter without changing the box. */
.avatar-none { background: rgba(20,22,31,.1); color: #5f626f; font-family: var(--mono);
  font-weight: 800; text-align: center; text-transform: uppercase; overflow: hidden; }
.avatar-none.avatar-sm { font-size: .62rem; line-height: 20px; }
.avatar-none.avatar-md { font-size: .82rem; line-height: 28px; }
.avatar-none.avatar-lg { font-size: 1.3rem; line-height: 48px; }
/* The account chip adds a 1px BORDER, and box-sizing is border-box everywhere,
   so its content box is 2px shorter than the square — a line-height equal to the
   full size would push the initial past `overflow: hidden` and clip it. Centre
   this one with flex instead, which is border-width agnostic. */
.avatar-none.avatar-xl { font-size: 1.5rem; display: inline-flex; align-items: center;
  justify-content: center; line-height: 1; }

/* -- team card ------------------------------------------------------------ */
.team-card { background: #fff; border: 1px solid rgba(20,22,31,.14); border-radius: 6px; padding: .8rem .9rem; }
.team-card.is-dq { opacity: .6; }
.team-card-head { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; font-weight: 700; }
.team-country { font-weight: 800; }
.team-card-name { margin-top: .25rem; font-size: .92rem; color: #555; }
.team-name { color: var(--ink); font-weight: 600; }
.team-name:hover { color: var(--primary); }
.team-roster, .roster { list-style: none; margin: .5rem 0 0; padding: 0; font-size: .9rem; font-weight: 400; color: #444; }
.team-roster li, .roster li { padding: .1rem 0; }
.cap-star { color: var(--primary); }
.seed-badge { background: var(--accent); color: var(--ink); font-size: .72rem; font-weight: 800; padding: .05rem .4rem; border-radius: 3px; }
.team-status { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; background: rgba(20,22,31,.1); padding: .05rem .35rem; border-radius: 3px; }
.q-badge { background: var(--primary); color: #fff; font-size: .72rem; font-weight: 800; padding: .05rem .35rem; border-radius: 3px; }

/* -- matchup card --------------------------------------------------------- */
/* The full composition lives in the CLASH block near the end of this file; this
   is only the box itself. `overflow: hidden` is what makes the pick'em bar in
   the bottom band inherit the card's corner radius instead of squaring it off. */
.matchup-card { background: #fff; border: 1px solid var(--line); border-radius: 6px; overflow: hidden;
  display: flex; flex-direction: column; }
.mc-side.is-tbd { color: #6b6e7a; }      /* 5.1:1 on the card white */
.mc-vs { text-align: center; }
.mc-state { font-size: .7rem; text-transform: uppercase; letter-spacing: .04em; }

/* -- score table / countdown --------------------------------------------- */
.score-table caption { text-align: left; font-weight: 700; padding: 0 0 .4rem; }
.sticky-first th:first-child, .sticky-first td:first-child { position: sticky; left: 0; background: #fff; }
.countdown { font-weight: 700; color: var(--primary); font-variant-numeric: tabular-nums; }
.countdown-label { font-weight: 400; color: #555; }

/* -- home ----------------------------------------------------------------- */
/* Banner-as-hero: full content-width, sharp corners, thin ink frame to sit on
   the blueprint grid. aspect-ratio holds its box before the image loads so the
   page doesn't jump. */
.home-banner { margin: -.25rem 0 1rem; }
.home-banner img { width: 100%; aspect-ratio: 1872 / 477; border: 2px solid var(--ink); border-radius: 3px; display: block; }
.home-tagline { margin: -.25rem 0 1.25rem; color: #555; font-family: var(--mono); font-size: .82rem; letter-spacing: .02em; border-left: 3px solid var(--accent2); padding-left: .7rem; }
/* Running-contest CTA band: replaces the thin tagline with a bold blue call-out
   (lime keyword + action buttons) pointing at the three live surfaces. Wraps to
   a stacked layout on narrow screens. */
.home-cta { margin: -.1rem 0 1.3rem; background: var(--primary); border: 2px solid var(--ink); border-radius: 3px; padding: .9rem 1.1rem; color: #fff; display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .7rem 1.1rem; }
.home-cta-text { margin: 0; font-size: 1.05rem; font-weight: 600; line-height: 1.35; flex: 1 1 16rem; }
.home-cta-text strong { color: var(--accent); font-weight: 800; }
.home-cta-actions { display: flex; flex-wrap: wrap; gap: .5rem; }
.home-cta-btn { display: inline-block; background: var(--accent); color: var(--ink); border-radius: 3px; padding: .55rem 1rem; font-family: var(--mono); font-weight: 800; font-size: .82rem; text-transform: uppercase; letter-spacing: .03em; }
.home-cta-btn:hover { text-decoration: none; filter: brightness(1.05); }
.home-cta-ghost { background: transparent; color: #fff; border: 2px solid var(--accent2); padding: calc(.55rem - 2px) calc(1rem - 2px); }
.home-cta-ghost:hover { background: rgba(255,255,255,.12); filter: none; }
/* Song titles are shown IN FULL — no clamp, no ellipsis, no tooltip. They sit on
   one line wherever there is room and wrap at a SPACE when there isn't; a word is
   never split (overflow-wrap/word-break/hyphens all stay off). Used by the home
   "now happening" card and the home schedule. */
.song-line { display: block; max-width: 100%; white-space: normal;
  overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none; }
.home-grid { display: grid; gap: 1rem; grid-template-columns: 1fr; }
/* Grid tracks default to min-width:auto, so a long unbreakable run of text would
   push a column out to its min-content width and blow the layout wider than the
   viewport. min-width:0 lets the columns hold their fractional widths and the
   text wrap inside them instead. */
.home-main, .home-side { min-width: 0; }
.now-card { border-left: 4px solid var(--accent); }
.now-flag { display: inline-block; font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .06em; color: var(--ink); background: var(--accent); padding: .1rem .5rem; border-radius: 3px; font-family: var(--mono); }
.now-card h2 { margin: .5rem 0 .3rem; }
.now-deadline { font-size: 1.05rem; margin: .3rem 0; }
.now-songs { margin: .5rem 0 0; padding-left: 1.1rem; }
.quick-links { display: grid; gap: .5rem; }
.quick { display: block; background: #fff; border: 1px solid rgba(20,22,31,.14); border-radius: 6px; padding: .6rem .8rem; color: var(--ink); }
.quick:hover { text-decoration: none; border-color: var(--primary); }
.quick strong { display: block; }
.quick span { font-size: .82rem; color: #666; }
.round-phase { font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; padding: .1rem .4rem; border-radius: 3px; background: rgba(20,22,31,.08); white-space: nowrap; }
.phase-judging, .phase-mapping, .phase-ban_pick { background: var(--accent); color: var(--ink); }
.phase-results, .phase-closed { background: var(--accent2); color: var(--ink); }
.counts-card { text-align: center; }
.big-count { font-size: 1.2rem; }
@media (min-width: 860px) { .home-grid { grid-template-columns: 2fr 1fr; } }

/* -- schedule ------------------------------------------------------------- */
.sched-round-head { display: flex; align-items: center; gap: .7rem; }
.sched-round-head h2 { margin: 0; }
.sched-cols { display: grid; gap: 1rem; grid-template-columns: 1fr; margin-top: .6rem; }
.sched-cols h4 { margin: 0 0 .3rem; }
.sched-songs { margin: 0; padding-left: 1.1rem; }
.sched-dates th { text-transform: none; letter-spacing: 0; color: #555; font-weight: 600; width: 12rem; }
.sched-banpick { margin-top: 1rem; }
@media (min-width: 720px) { .sched-cols { grid-template-columns: 1fr 1fr; } }

/* -- teams / filter ------------------------------------------------------- */
.filter-bar { display: flex; gap: .8rem; align-items: end; flex-wrap: wrap; background: #fff; border: 1px solid rgba(20,22,31,.14); border-radius: 6px; padding: .7rem .9rem; margin-bottom: 1rem; }
.filter-bar label { display: flex; flex-direction: column; font-size: .8rem; font-weight: 700; gap: .2rem; }
.filter-bar input, .filter-bar select { font: inherit; padding: .35rem .5rem; border: 1px solid rgba(20,22,31,.25); border-radius: 4px; }
.team-grid { align-items: start; }

/* -- team detail ---------------------------------------------------------- */
.team-head h1 { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.kv th { text-transform: none; letter-spacing: 0; color: #555; font-weight: 600; width: 12rem; }
.about-block { margin-top: 1rem; border-top: 1px solid rgba(20,22,31,.1); padding-top: .8rem; }
.about-block h4 { margin: .8rem 0 .3rem; }

/* -- free agents ---------------------------------------------------------- */
.fa-group h2 { display: flex; align-items: center; gap: .5rem; }
.fa-card { background: #fff; border: 1px solid rgba(20,22,31,.14); border-radius: 6px; padding: .7rem .8rem; }
/* The card's picture is the shared .avatar-lg (see the avatar block above) —
   the old bespoke .fa-avatar / .fa-avatar-none rules are gone with it. */
.fa-head { display: flex; gap: .6rem; align-items: center; }
.lft-badge { font-size: .72rem; font-weight: 700; color: var(--ink); background: var(--accent2); padding: .05rem .4rem; border-radius: 3px; }
.fa-blurb { margin: .5rem 0 0; font-size: .9rem; color: #444; }

/* -- qualifier standings -------------------------------------------------- */
.ql-card { padding-top: .8rem; }
.ql-table { min-width: 100%; }
.ql-head, .ql-row { display: grid; align-items: center; gap: .3rem; padding: .35rem .5rem; }
.ql-head { font-size: .72rem; text-transform: uppercase; letter-spacing: .03em; color: #555; border-bottom: 2px solid rgba(20,22,31,.2); }
.ql-details { border-bottom: 1px solid rgba(20,22,31,.1); }
.ql-row { cursor: pointer; list-style: none; }
.ql-row::-webkit-details-marker { display: none; }
.ql-details[open] .ql-row { background: rgba(43,63,224,.05); }
.ql-row:hover { background: rgba(43,63,224,.04); }
.ql-c { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ql-num { text-align: right; font-variant-numeric: tabular-nums; }
.ql-team { font-weight: 700; }
.ql-anon { color: #5f626f; font-size: .85rem; }
.ql-total { font-weight: 800; }
.ql-detail-body { padding: .6rem 1rem 1rem; background: rgba(20,22,31,.02); }
.ql-raw th { text-transform: none; letter-spacing: 0; }
.ql-comment td { color: #444; font-size: .9rem; }
.passed { color: var(--primary); font-weight: 800; }
.passdown-note { color: var(--primary); font-size: .85rem; margin: 0 0 .5rem; }
.ql-legend { margin-top: .6rem; }

/* -- formula -------------------------------------------------------------- */
.params-table th { text-transform: none; letter-spacing: 0; }
.params-table tr.mismatch { background: rgba(217,138,0,.12); }

/* -- bracket -------------------------------------------------------------- */
/* The SVG tree is a collapsed overview: the round cards below are the primary
   (and, closed, the only) rendering of each matchup. Pure <details>, no JS. */
.bracket-tree-card { padding: .7rem 1.2rem; }
.bracket-tree summary { cursor: pointer; font-weight: 700; font-family: var(--mono); font-size: .8rem; text-transform: uppercase; letter-spacing: .04em; }
.bracket-tree[open] summary { margin-bottom: .4rem; }
.bracket-wrap { padding: .5rem 0; }
.bracket-lists { margin-top: 1rem; }
.bracket-round { margin-bottom: 1.6rem; }
/* min(100%, 15rem) — the floor collapses to the container on a narrow screen, so
   a 240px track can never be wider than the viewport (the admin bracket manager
   uses this base grid; the public clash page overrides it further down). */
.matchup-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 15rem), 1fr)); }

/* Round header — the page's rhythm device. A solid blue bar in an ink frame,
   quoting the site header and the home CTA band, so each round opens with the
   same strong horizontal beat instead of a bare <h3>. The name is set in the
   mono blueprint face (same treatment as the header nav), the matchup count is
   a quiet white annotation, and the round's phase is a lime chip pushed to the
   right edge. Wraps to two lines on narrow screens rather than overflowing. */
.round-bar { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .7rem;
  background: var(--primary); color: #fff; border: 2px solid var(--ink); border-radius: 3px;
  padding: .45rem .8rem; margin: 0 0 .8rem; box-shadow: var(--shadow); }
.round-bar-t { margin: 0; font-family: var(--mono); font-size: .95rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .06em; }
.round-bar-n { font-family: var(--mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .05em; color: rgba(255,255,255,.82); }
/* Pushed right by auto margin, so it stays on the far edge at every width and
   drops to the start of the second line only when the bar actually wraps. */
.round-bar-p { margin-left: auto; font-family: var(--mono); font-size: .68rem; font-weight: 800;
  text-transform: uppercase; letter-spacing: .05em; background: var(--accent); color: var(--ink);
  border-radius: 3px; padding: .1rem .45rem; }

/* -- matchup -------------------------------------------------------------- */
/* minmax(0, 1fr), not 1fr: a grid track defaults to a min-content floor, so one
   long unbroken team/country name would push the row wider than its card (the
   admin bracket manager's matchup header did exactly that). The sides also carry
   min-width:0 so the text wraps inside the column instead of widening it. */
.matchup-header { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); align-items: center; gap: 1rem; }
.mh-side { display: flex; flex-direction: column; align-items: center; text-align: center; gap: .25rem; min-width: 0; }
.mh-side.is-winner { font-weight: 800; }
.mh-side.is-winner .mh-team { color: var(--primary); }
.mh-team { font-size: 1.15rem; }
.mh-seed { font-size: .75rem; color: #888; }
.mh-country { font-size: .8rem; }
.mh-won { font-size: .68rem; text-transform: uppercase; letter-spacing: .05em; background: var(--accent); color: var(--ink); padding: .05rem .4rem; border-radius: 3px; }
.mh-vs { color: #aaa; font-weight: 700; }
.bp-steps { display: grid; gap: .8rem; grid-template-columns: 1fr; margin-top: .5rem; }
.bp-step { border: 1px solid rgba(20,22,31,.14); border-radius: 6px; padding: .7rem .8rem; position: relative; }
.bp-step.active { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }
.bp-step.done { border-color: var(--primary); }
.bp-num { position: absolute; top: -.7rem; left: .7rem; width: 1.4rem; height: 1.4rem; line-height: 1.4rem; text-align: center; border-radius: 50%; background: var(--primary); color: #fff; font-size: .8rem; font-weight: 800; }
.bp-step h4 { margin: .4rem 0 .3rem; }
.bp-song.picked { color: var(--primary); font-weight: 700; }
.bp-song.banned s { color: #b00; }
.bp-deadline { font-size: 1rem; }
.bp-songs { margin-top: 1rem; }
.bp-songs ul { list-style: none; padding: 0; margin: .3rem 0 0; }
.bp-songs li { padding: .25rem 0; border-bottom: 1px solid rgba(20,22,31,.08); }
.bp-songs li.picked { font-weight: 700; }
.pill-ban { background: #b00; color: #fff; }
.pill-pick { background: var(--primary); color: #fff; }
.walkover-card { border-left: 4px solid #d98a00; }
.entries { display: grid; gap: 1rem; grid-template-columns: 1fr; }
.entry-card { border: 1px solid rgba(20,22,31,.14); border-radius: 6px; padding: .8rem .9rem; }
.entry-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.entry-dl { margin-left: auto; }
.entry-card h4 { margin: .7rem 0 .3rem; }
.votes-table th { text-transform: none; letter-spacing: 0; }
.comment-cols { display: grid; gap: 1rem; grid-template-columns: 1fr; margin-top: 1rem; }
.comments { margin: .3rem 0 0; padding-left: 1.1rem; }
/* Judge comments quote long unbroken osu! timestamp strings
   (01:10:716 (1,1,2,3,4,…)) that otherwise force the whole matchup page to scroll
   sideways on mobile — let them wrap. */
.comments li { padding: .2rem 0; overflow-wrap: break-word; word-break: break-word; }
@media (min-width: 720px) {
  .bp-steps { grid-template-columns: repeat(3, 1fr); }
  .entries, .comment-cols { grid-template-columns: 1fr 1fr; }
}

/* -- pickem --------------------------------------------------------------- */
.pickem-grid { grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr)); }
.pk-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: .5rem; }
.pk-versus { font-weight: 700; }
.pickem-form { display: grid; gap: .5rem; }
.pickem-choice { display: flex; align-items: center; gap: .4rem; font: inherit; font-weight: 700; text-align: left; background: #fff; border: 2px solid rgba(20,22,31,.2); border-radius: 6px; padding: .6rem .8rem; cursor: pointer; }
.pickem-choice:hover { border-color: var(--primary); }
.pickem-choice.chosen { border-color: var(--primary); background: rgba(43,63,224,.08); }
.pickem-lock { font-size: .9rem; margin: .5rem 0 0; }

/* -- pick'em split bar (partials/pickemSplit) ----------------------------- */
/* ONE result, not two. The predecessor gave each side its own full-width      */
/* 0-100% track, stacked — two meters that read as unrelated results. Here a   */
/* SINGLE track holds two flex children whose widths ARE the two percentages   */
/* (the partial rounds one side and derives the other, so they sum to exactly  */
/* 100): A fills from the left, B from the right, and they meet at the split.  */
/* The head row mirrors the pairing (A left, B right). No JS; the widths ride  */
/* on inline style attributes, which the CSP allows.                          */
.pk-split-head { display: flex; align-items: flex-start; gap: .35rem .75rem; margin-bottom: .3rem; }
.pk-side { flex: 1 1 0; min-width: 0; display: flex; flex-wrap: wrap; align-items: center; gap: .15rem .4rem; font-weight: 700; line-height: 1.3; }
.pk-side-b { justify-content: flex-end; text-align: right; }
/* Country names wrap between words or not at all — never mid-word. */
.pk-name { overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none; }
.pk-num { white-space: nowrap; font-size: .9rem; }
.pk-votes { color: #5f626f; }
/* "winner" reads darker than the cyan "your pick" pill (#fff on --ink, 18:1). */
.pk-tag-win { background: var(--ink); color: #fff; }

/* The track itself. A neutral trough shows through wherever a side has no     */
/* share, and carries the zero-vote state on its own (.is-empty, no segments). */
.pk-split-bar { display: flex; height: 1.6rem; border-radius: 4px; overflow: hidden; background: rgba(20,22,31,.10); box-shadow: inset 0 0 0 1px var(--line); }
.pk-split-bar.is-empty { background: rgba(20,22,31,.06); }
.pk-seg { flex: none; min-width: 0; overflow: hidden; display: flex; align-items: center; }
/* Contrast of the in-bar numbers on the default theme: #fff on --primary      */
/* (#2B3FE0) = 7.3:1, --ink on --accent2 (#3FD8F0) = 10.6:1. Both >= 4.5:1.    */
/* A share too narrow to hold its number renders without one (the partial      */
/* decides) rather than clipping it — the head row always has both.            */
/* The edge inset for that number is a MARGIN on the number, never padding on  */
/* the segment: with border-box sizing, padding is a floor on the segment's    */
/* width, so a 1% share would render ~2.4% wide and the pair would overflow    */
/* the track (the split point lands in the wrong place and the far side gets   */
/* clipped). Margin keeps each segment's box exactly its own percentage, so    */
/* the two always tile the track to the pixel.                                 */
.pk-seg-a { background: var(--primary); color: #fff; justify-content: flex-start; }
.pk-seg-b { background: var(--accent2); color: var(--ink); justify-content: flex-end; box-shadow: inset 2px 0 0 rgba(20,22,31,.35); }
.pk-seg-num { font-size: .78rem; font-weight: 800; letter-spacing: .02em; white-space: nowrap; }
.pk-seg-a .pk-seg-num { margin-left: .45rem; }
.pk-seg-b .pk-seg-num { margin-right: .45rem; }
.pk-split-foot { margin: .35rem 0 0; }

/* Decided: the winning side is emphasised (bolder + an ink ring around its    */
/* half of the bar) and the other side is quietly muted (#5f626f on the card   */
/* white = 6.1:1) — readable and identifiable, never struck through, and never */
/* dimmed by opacity (that would drop the in-bar numbers below 4.5:1).         */
.pk-split.is-decided .pk-side.is-win { font-weight: 800; }
.pk-split.is-decided .pk-side.is-loss { color: #5f626f; }
.pk-split.is-decided .pk-side.is-loss .pk-team-link { color: inherit; }
.pk-split.is-decided .pk-seg.is-win { box-shadow: inset 0 0 0 2px var(--ink); }

/* COMPACT head (opts.compact — the clash card, where the two countries are      */
/* already the card's hero and repeating them under the bar is what made the     */
/* card read as a stack of rows). One line: share · state · share, the two       */
/* numbers hugging the ends they fill from, the foot text folded into the        */
/* middle. Both percentages are still present in full, so a share too narrow to  */
/* carry its number inside the bar is still stated here. Wraps rather than       */
/* overflowing on a very narrow card.                                            */
.pk-compact-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: .1rem .5rem; }
.pk-cnum { display: inline-flex; align-items: baseline; gap: .3rem; flex: 1 1 0; min-width: 0;
  font-family: var(--mono); font-size: .78rem; font-weight: 700; color: var(--ink); }
.pk-cnum-b { justify-content: flex-end; }
.pk-cmid { flex: 0 1 auto; font-family: var(--mono); font-size: .68rem; text-transform: uppercase;
  letter-spacing: .04em; color: #5f626f; white-space: nowrap; }
.pk-cnum .pill { font-size: .6rem; padding: .05rem .3rem; }
.pk-split.is-decided .pk-cnum.is-win { font-weight: 800; }
.pk-split.is-decided .pk-cnum.is-loss { color: #5f626f; }

/* -- archive -------------------------------------------------------------- */
.archive-grid { grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); }
.arch-card { display: block; background: #fff; border: 2px solid var(--ink); border-radius: 8px; overflow: hidden; color: var(--ink); }
/* The lift itself comes from the shared --shadow-lift token (SURFACE LIFT block
   at the end of this file); this rule only kills the underline. */
.arch-card:hover { text-decoration: none; }
.arch-banner img { width: 100%; display: block; aspect-ratio: 22 / 5; object-fit: cover; }
.arch-banner-none { aspect-ratio: 22 / 5; background: var(--primary); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 2rem; font-weight: 800; }
.arch-body { padding: .8rem .9rem; }
.arch-body h2 { margin: 0 0 .4rem; font-size: 1.1rem; }
.arch-winner { margin: .2rem 0; }

/* ---------------------------------------------------------------------------
   Participant pages (register / team / submit / banpick / invites). Only classes
   NOT already defined above by the shared/public block live here, so nothing
   overrides the canonical .flag / .seed-badge / .cap-star / .team-status /
   .team-roster / .countdown rules. The ban/pick tracker uses its own .bpt-step
   scope so it can't clash with the public matchup page's .bp-step.
   --------------------------------------------------------------------------- */
.mt { margin-top: .6rem; }
.plain { list-style: none; padding: 0; margin: .4rem 0; }
.plain li { padding: .2rem 0; }
hr { border: 0; border-top: 1px solid rgba(20,22,31,.12); margin: 1rem 0; }

/* Team status colour variants (compose on top of the shared .team-status chip) */
.status-approved { background: #e6f7ea; color: #1a7f37; }
.status-pending  { background: #fff6e0; color: #9a6b00; }
.status-rejected, .status-withdrawn, .status-disqualified { background: #fdeaea; color: #b42318; }

/* Forms */
.field { display: block; margin: 0 0 .7rem; }
.field > span { display: block; font-weight: 600; font-size: .9rem; margin-bottom: .2rem; }
.field input[type=text], .field input[type=file], .field textarea, .field select,
.stack input[type=text], .stack select {
  width: 100%; max-width: 32rem; font: inherit; padding: .45rem .55rem;
  border: 1px solid rgba(20,22,31,.28); border-radius: 4px; background: #fff; color: var(--ink); }
.field textarea { max-width: 100%; resize: vertical; }
.stack { margin-bottom: .4rem; }
form.inline, .inline { display: inline; }
.row-actions { display: inline-flex; gap: .6rem; margin-left: .4rem; }
.linkbtn { background: none; border: 0; color: var(--primary); font: inherit; cursor: pointer; padding: 0; text-decoration: underline; }
.linkbtn.danger { color: #b42318; }
.btn-danger { background: #b42318; color: #fff; }
.btn-go { background: #1a7f37; color: #fff; }
.btn[disabled], input[disabled], select[disabled], textarea[disabled], button[disabled] { opacity: .55; cursor: not-allowed; }
.linkrow { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; }

/* Account / profile chip (also used by /me) */
.me-head { display: flex; gap: .8rem; align-items: center; margin: .6rem 0; }
.me-avatar { border-radius: 6px; border: 1px solid rgba(20,22,31,.15); }
.me-name { margin: 0 0 .1rem; font-size: 1.05rem; }
.changes { margin: .4rem 0; padding-left: 1.1rem; }

/* Larger roster rows on the team hub */
.roster-lg li { font-size: 1.02rem; padding: .3rem 0; }

/* Deadline + big countdown chip */
.deadline-big { font-size: 1.05rem; font-weight: 700; }
.countdown.big { background: var(--accent); color: var(--ink); border-radius: 4px; padding: .1em .5em; }

/* Contribution table + bars */
.contrib-form input[type=text], .contrib-form select { width: 100%; min-width: 6rem; }
.barcell { width: 30%; min-width: 6rem; }
.bar { display: inline-block; height: .8rem; background: var(--primary); border-radius: 2px; vertical-align: middle; }

/* Ban / pick — tracker (scoped, distinct from the public matchup .bp-step) */
.bp-tracker { display: flex; gap: .5rem; margin: .6rem 0; }
.bpt-step { flex: 1; text-align: center; font-weight: 800; font-size: .82rem; letter-spacing: .05em;
  padding: .4rem; border: 2px solid rgba(20,22,31,.2); border-radius: 4px; color: #888; background: #fff; }
.bpt-step.active { border-color: var(--primary); color: var(--primary); background: #fff; }
.bpt-step.done { border-color: var(--ink); color: #fff; background: var(--ink); }
.song-grid { grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); }
.song-card { border: 2px solid rgba(20,22,31,.18); border-radius: 6px; padding: 1rem; background: #fff; }
.song-card.is-banned { border-color: #b42318; opacity: .7; }
.song-card.is-picked { border-color: #1a7f37; }
.song-card h3 { margin: 0 0 .5rem; font-size: 1rem; }
.tag { display: inline-block; font-weight: 800; font-size: .72rem; text-transform: uppercase; border-radius: 3px; padding: .1em .45em; }
.tag-ban { background: #fdeaea; color: #b42318; }
.tag-pick { background: #e6f7ea; color: #1a7f37; }

/* ============================================================ */
/* Judge pages (design 03 §3.3) — minimal-chrome scoring sheets */
/* ============================================================ */

/* Assignment list */
.j-assignments { grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr)); }
.j-assignment { display: block; text-decoration: none; color: inherit; border: 2px solid rgba(20,22,31,.14); transition: border-color .1s; }
.j-assignment:hover { border-color: var(--primary); }
.j-assignment.j-open { border-left: 4px solid var(--accent); }
.j-assignment.j-closed { opacity: .8; }
.j-assignment-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.j-assignment-head h2 { margin: 0; font-size: 1.1rem; }
.j-deadline { margin: .4rem 0 0; font-size: .85rem; }
.j-note { margin-top: 1rem; }

/* Status chip */
.j-chip { font-weight: 800; font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; border-radius: 3px; padding: .12em .5em; white-space: nowrap; }
.j-chip-open { background: var(--accent); color: var(--ink); }
.j-chip-closed { background: rgba(20,22,31,.12); color: var(--ink); }
.j-chip-pending { background: var(--accent2); color: var(--ink); }

/* Progress bar */
.j-progress { display: flex; align-items: center; gap: .5rem; margin: .5rem 0; }
.j-progress-label { font-size: .8rem; font-weight: 700; color: #555; }
.j-progress-track { flex: 1; height: .6rem; background: rgba(20,22,31,.1); border-radius: 3px; overflow: hidden; min-width: 4rem; }
.j-progress-fill { display: block; height: 100%; background: var(--primary); }
.j-progress-num { font-variant-numeric: tabular-nums; font-weight: 700; font-size: .85rem; }

/* Scoring sheet layout: sidebar + main pane */
.j-sheet { display: grid; grid-template-columns: minmax(12rem, 16rem) 1fr; gap: 1rem; align-items: start; }
@media (max-width: 640px) { .j-sheet { grid-template-columns: 1fr; } }
.j-entrylist { position: sticky; top: 1rem; max-height: 80vh; overflow: auto; }
.j-entries { list-style: none; margin: .3rem 0 0; padding: 0; }
.j-entry { display: flex; align-items: center; gap: .5rem; padding: .35rem .4rem; text-decoration: none; color: inherit; border-radius: 4px; }
.j-entry:hover { background: rgba(20,22,31,.05); }
.j-entry-cur { background: var(--primary); color: #fff; }
.j-entry-cur:hover { background: var(--primary); }
.j-entry-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* done/draft/todo dots */
.j-dot { display: inline-block; width: .7rem; height: .7rem; border-radius: 50%; flex: 0 0 auto; border: 1px solid rgba(20,22,31,.3); }
.j-done { background: #1a7f37; border-color: #1a7f37; }
.j-draft { background: var(--accent); border-color: #b9a800; }
.j-todo { background: #fff; }

/* Pane */
.j-pane-head { border-bottom: 2px solid var(--primary); padding-bottom: .4rem; margin-bottom: .6rem; }
.j-anon { margin: 0; font-size: 1.25rem; }
.j-song { margin: .2rem 0 0; }
.j-dl { margin: .6rem 0; }
.j-about { margin: .4rem 0 .8rem; }
.j-about summary { cursor: pointer; font-weight: 700; }
.j-about p { margin: .4rem 0; }
.j-criteria { display: grid; grid-template-columns: repeat(3, 1fr); gap: .8rem; }
@media (max-width: 480px) { .j-criteria { grid-template-columns: 1fr; } }
.j-criteria input { width: 100%; }
.j-savehint { min-height: 1.1em; margin: .2rem 0; font-size: .85rem; }
.j-formactions { display: flex; gap: .6rem; flex-wrap: wrap; }
.j-prevnext { display: flex; justify-content: space-between; gap: .5rem; margin-top: 1rem; }
.j-prevnext .pill[aria-disabled="true"] { opacity: .4; }
.j-mystats { margin-top: 1rem; padding-top: .6rem; border-top: 1px solid rgba(20,22,31,.15); }
.j-mystats h3 { margin: 0 0 .2rem; font-size: 1rem; }

/* Clash voting cards */
.j-matchup-head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; }
.j-matchup-head h2 { margin: 0; font-size: 1.1rem; }
.j-clash-entries { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: .6rem 0; }
@media (max-width: 640px) { .j-clash-entries { grid-template-columns: 1fr; } }
.j-clash-entry { border: 2px solid rgba(20,22,31,.14); border-radius: 6px; padding: .8rem; }
.j-clash-entry-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; flex-wrap: wrap; }
.j-vote-pick { font-weight: 700; font-size: .85rem; display: inline-flex; gap: .3rem; align-items: center; }
.j-abstain { display: inline-flex; gap: .3rem; align-items: center; margin: .3rem 0 .6rem; font-size: .9rem; }
.j-comment-ro { white-space: pre-wrap; }

/* ===================================================================== */
/* ADMIN (contest ops) — namespaced .adm-*; ORG VIEW ribbon + subnav.    */
/* ===================================================================== */
.adm-ribbon { background: #9a6b00; color: #fff; font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; font-size: .74rem; padding: .35rem .8rem; border-radius: 4px; margin: 0 0 .8rem; }
.adm-subnav { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 1rem; border-bottom: 2px solid rgba(20,22,31,.12); padding-bottom: .5rem; }
.adm-subnav a { font-weight: 700; font-size: .88rem; padding: .3rem .7rem; border-radius: 4px; color: var(--ink); }
.adm-subnav a:hover { background: rgba(20,22,31,.06); text-decoration: none; }
.adm-subnav a.active { background: var(--primary); color: #fff; }
.adm-subnav-sep { width: 1px; align-self: stretch; min-height: 1.2rem; background: rgba(20,22,31,.2); margin: 0 .3rem; }
.adm-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: .7rem; margin: 0 0 1rem; }
.adm-tile { display: flex; flex-direction: column; gap: .2rem; background: #fff; border: 2px solid rgba(20,22,31,.14);
  border-radius: 6px; padding: .8rem; color: var(--ink); }
a.adm-tile:hover { border-color: var(--primary); text-decoration: none; }
.adm-tile-num { font-size: 1.7rem; font-weight: 800; line-height: 1; }
.adm-tile-num.warn { color: #b42318; }
.adm-tile-label { font-size: .78rem; color: #555; text-transform: uppercase; letter-spacing: .03em; }
.adm-list { list-style: none; margin: .4rem 0 0; padding: 0; }
.adm-list li { padding: .35rem 0; border-bottom: 1px solid rgba(20,22,31,.08); }
.adm-two-col { display: grid; grid-template-columns: minmax(0,2fr) minmax(14rem,1fr); gap: 1rem; align-items: start; }
.adm-audit { position: sticky; top: 1rem; }
.adm-round-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.adm-actions { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center; margin: .6rem 0; }
.inline-form { display: inline-flex; gap: .35rem; align-items: center; margin: 0; }
.adm-form { margin: .6rem 0 0; }
.adm-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr)); gap: .6rem; margin: 0 0 .8rem; }
.adm-form label { display: flex; flex-direction: column; font-size: .82rem; font-weight: 700; gap: .2rem; }
.adm-form input, .adm-form select { font: inherit; padding: .35rem .45rem; border: 1px solid rgba(20,22,31,.3); border-radius: 4px; }
.adm-check { flex-direction: row !important; align-items: center; gap: .4rem !important; font-weight: 600; }
.adm-checks { display: flex; flex-wrap: wrap; gap: .8rem; margin: .3rem 0 .8rem; }
.adm-songs { margin: .4rem 0 .8rem; }
.adm-song { border: 1px solid rgba(20,22,31,.18); border-radius: 6px; padding: .5rem .7rem; }
.adm-song legend { font-weight: 800; font-size: .8rem; }
.adm-song label { display: flex; flex-direction: column; font-size: .76rem; font-weight: 600; gap: .15rem; margin: .25rem 0; }
.adm-song input { font: inherit; padding: .3rem .4rem; border: 1px solid rgba(20,22,31,.3); border-radius: 4px; }
.adm-reason { min-width: 8rem; }
.adm-members { list-style: none; margin: .4rem 0; padding: 0; }
.adm-member { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; align-items: center;
  padding: .4rem 0; border-bottom: 1px solid rgba(20,22,31,.08); }
.adm-member.is-warn { background: #fff6e0; border-radius: 4px; padding: .4rem .5rem; }
.adm-judge { display: flex; justify-content: space-between; gap: 1rem; align-items: center; flex-wrap: wrap; }
.adm-judge.is-removed { opacity: .6; }
@media (max-width: 720px) { .adm-two-col { grid-template-columns: 1fr; } .adm-audit { position: static; } }

/* Screening declutter — status groups + primary-action-first team cards, with
   destructive / reason-carrying actions folded behind a "Manage" disclosure. */
.adm-finish { margin: 0 0 1rem; }
.adm-screen-group { margin: 0 0 1.1rem; }
.adm-screen-group > summary { list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: .5rem; padding: .45rem .2rem;
  font-weight: 800; border-bottom: 2px solid var(--line); }
.adm-screen-group > summary::-webkit-details-marker { display: none; }
.adm-screen-group > summary::marker { content: ''; }
.adm-screen-group > summary::before { content: '\25B8'; color: var(--primary); font-size: .85em; }
.adm-screen-group[open] > summary::before { content: '\25BE'; }
.adm-group-title { text-transform: uppercase; letter-spacing: .04em; font-size: .82rem; }
.adm-group-count { margin-left: auto; background: rgba(20,22,31,.1); border-radius: 999px; padding: .05rem .55rem; font-size: .78rem; font-weight: 700; }
.adm-screen-group-body { display: flex; flex-direction: column; gap: .8rem; margin-top: .8rem; }
.adm-screen-clear { margin: 0 0 1.1rem; }
.adm-team-sub { margin: .15rem 0 0; }
.adm-team-flag.warn { color: #9a6b00; font-weight: 700; }
.adm-member-actions { margin: 0; }
.adm-team-actions { margin: .6rem 0 0; }
/* The Manage disclosure — its summary is styled as a .btn, so only the default
   marker is suppressed; when open it claims the full row and drops its body below. */
.adm-manage > summary { list-style: none; cursor: pointer; display: inline-flex; }
.adm-manage > summary::-webkit-details-marker { display: none; }
.adm-manage > summary::marker { content: ''; }
.adm-manage[open] { flex-basis: 100%; }
.adm-manage-inline { display: inline-block; }
.adm-manage-body { display: flex; flex-direction: column; gap: .5rem; margin-top: .5rem;
  padding: .6rem; border: 1px solid var(--line); border-radius: 5px; background: rgba(20,22,31,.02); }
.adm-manage-row { display: flex; flex-wrap: wrap; gap: .35rem; align-items: center; margin: 0; }

/* Collapsible reference detail on admin cards (e.g. the Discord bot explainer) —
   keeps the actionable chips/links on top and folds the long prose away. */
.adm-explain { margin-top: .7rem; border-top: 1px solid var(--line); padding-top: .5rem; }
.adm-explain > summary { cursor: pointer; font-weight: 700; font-size: .85rem; color: var(--primary); }
.adm-explain[open] > summary { margin-bottom: .4rem; }
.adm-explain .adm-list { margin: 0; }

/* ===================================================================== */
/* ADMIN-B — bracket manager, anonymization, publishing, announcements.  */
/* ===================================================================== */
.adm-form.inline { display: inline-flex; flex-wrap: wrap; gap: .4rem; align-items: flex-end; margin: 0; }
.adm-form.inline .inline-lbl { display: inline-flex; flex-direction: column; font-size: .76rem; font-weight: 700; gap: .15rem; }
.inline-lbl { font-size: .8rem; font-weight: 700; }
.btn.small { padding: .3rem .6rem; font-size: .8rem; }
.btn.ghost { background: transparent; color: var(--ink); border: 1px solid rgba(20,22,31,.3); }
.btn:disabled { opacity: .45; cursor: not-allowed; }

/* bracket manager */
.mm-card { padding: .8rem .9rem; }
.mm-head { display: flex; justify-content: space-between; align-items: center; gap: .5rem; margin-bottom: .4rem; }
.mm-controls { display: flex; flex-direction: column; gap: .5rem; margin-top: .6rem; border-top: 1px solid rgba(20,22,31,.1); padding-top: .6rem; }
.bp-atoms { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.bp-atom { font-size: .74rem; background: rgba(20,22,31,.06); padding: .12rem .5rem; border-radius: 999px; }
.bp-atom.banned s { color: #b00; }
.bp-atom.picked { background: var(--primary); color: #fff; }
.sub-note { font-size: .68rem; color: var(--primary); font-weight: 700; }
.decide-row { display: flex; flex-wrap: wrap; gap: .5rem; align-items: flex-end; }
.tally { font-weight: 800; font-variant-numeric: tabular-nums; align-self: center; }
.dq-panel { border: 1px dashed rgba(20,22,31,.25); border-radius: 6px; padding: .4rem .6rem; }
.dq-panel summary { font-weight: 700; font-size: .82rem; cursor: pointer; }
.dq-panel form { margin: .5rem 0 0; display: flex; flex-wrap: wrap; gap: .4rem; align-items: flex-end; }

/* publishing */
.pub-list { display: flex; flex-direction: column; gap: .2rem; }
.pub-gate { display: flex; flex-direction: column; gap: .2rem; padding: .7rem 0; border-bottom: 1px solid rgba(20,22,31,.1); }
.pub-gate:last-child { border-bottom: 0; }
.pub-gate-head { display: flex; align-items: center; gap: .5rem; }
.pill.pub-on { background: var(--primary); color: #fff; }
.pill.pub-off { background: rgba(20,22,31,.12); color: var(--ink); }
/* Contest-visibility master switch (migration 004): a private contest must be
   impossible to miss on the publishing page and on an archive card. */
.pub-contest-closed { border: 2px solid #b3261e; }
.pub-state-off { color: #b3261e; }
.pub-state-on, .pub-state-off { margin: 0 0 .4rem; }
.arch-body .pill.pub-off { display: inline-block; margin: 0 0 .35rem; }
/* Publishing: jump nav + per-round dropdowns, so a long gate list compresses and
   the round in focus opens at the top. */
.pub-jump { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin: 0 0 1rem; }
.pub-jump-label { font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: var(--muted); font-weight: 700; }
.pub-jump-chip { font-size: .82rem; padding: .2rem .6rem; border: 1px solid var(--line); border-radius: 999px; text-decoration: none; color: var(--ink); white-space: nowrap; }
.pub-jump-chip.is-next { border-color: var(--primary); background: var(--primary); color: #fff; font-weight: 700; }
.pub-round { padding: 0; }
.pub-round > summary { list-style: none; cursor: pointer; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .3rem 1rem; padding: .85rem 1.2rem; }
.pub-round > summary::-webkit-details-marker { display: none; }
.pub-round > summary::marker { content: ''; }
.pub-round[open] > summary { border-bottom: 1px solid var(--line); }
.pub-round-name { font-weight: 800; font-size: 1.02rem; }
.pub-round-meta { font-family: var(--mono); font-size: .78rem; color: var(--muted); }
.pub-cnt-on { color: var(--primary); font-weight: 700; }
.pub-round > .pub-list { padding: .5rem 1.2rem 1rem; }
.pub-round-next { border-color: var(--primary); box-shadow: 0 0 0 1px var(--primary); }
.pub-next-tag { font-family: var(--mono); font-size: .6rem; text-transform: uppercase; letter-spacing: .06em; background: var(--primary); color: #fff; padding: .08rem .35rem; border-radius: 3px; margin-left: .35rem; }
/* Rounds & songs: each round is a dropdown too (same compress-and-focus idea). */
.adm-round > summary { list-style: none; cursor: pointer; display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: .3rem 1rem; }
.adm-round > summary::-webkit-details-marker { display: none; }
.adm-round > summary::marker { content: ''; }
.adm-round[open] > summary { border-bottom: 1px solid var(--line); padding-bottom: .7rem; margin-bottom: .9rem; }
.adm-round-name { font-weight: 800; font-size: 1.02rem; display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.adm-round-meta { font-family: var(--mono); font-size: .78rem; color: var(--muted); display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; }

/* announcements */
.ann-sev { font-size: .68rem; font-weight: 800; text-transform: uppercase; letter-spacing: .04em; padding: .05rem .4rem; border-radius: 3px; background: rgba(20,22,31,.1); }
.ann-sev-warning { background: #d98a00; color: #fff; }
.ann-sev-event { background: var(--accent2); color: var(--ink); }
.ann-hist { padding: .7rem .9rem; }
.ann-hist-head { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }

/* anonymization */
.anon-tabs { display: flex; flex-wrap: wrap; gap: .3rem; margin: 0 0 1rem; }
.anon-tabs a { font-weight: 700; font-size: .85rem; padding: .3rem .7rem; border-radius: 4px; border: 1px solid rgba(20,22,31,.18); color: var(--ink); }
.anon-tabs a:hover { background: rgba(20,22,31,.06); text-decoration: none; }
.anon-tabs a.active { background: var(--primary); color: #fff; border-color: var(--primary); }
.anon-table td { vertical-align: top; }
.anon-dot { font-size: .7rem; font-weight: 800; text-transform: uppercase; letter-spacing: .03em; padding: .1rem .45rem; border-radius: 3px; white-space: nowrap; }
.anon-ok { background: var(--primary); color: #fff; }
.anon-warn { background: #d98a00; color: #fff; }
.anon-fail { background: #b42318; color: #fff; }
.anon-pending { background: rgba(20,22,31,.12); color: var(--ink); }
.anon-warnings { margin: .3rem 0 0; padding-left: 1.1rem; font-size: .78rem; color: #8a5a00; }
.anon-err { margin: .3rem 0 0; font-size: .78rem; color: #b42318; }
/* The actions cell now carries up to three controls (Re-run · Re-roll · Replace).
   `white-space: nowrap` forced them onto ONE unbreakable line, and because the
   cell is also `position: sticky; right: 0` it then overlaid the Status/Team
   columns it is pinned over at phone widths. Wrap instead of growing sideways. */
.anon-actions { display: flex; flex-wrap: wrap; gap: .3rem; align-items: flex-start; }
.anon-actions .adm-form.inline { margin-bottom: .2rem; }

/* ===================================================================== */
/* Blueprint polish (trailing overrides) — sharpen the remaining sub-cards */
/* to the same minimal radius as .card so every panel reads as a crisp     */
/* rectangle on the grid. Visual only; placed last so it wins on radius.   */
/* ===================================================================== */
.quick, .team-card, .matchup-card, .fa-card, .entry-card, .song-card,
.arch-card, .adm-tile, .j-assignment, .j-clash-entry, .bp-step {
  border-radius: 3px;
}
/* Quick-links get a cyan info tick that fills blue on hover (banner accents). */
.quick { border-left: 3px solid var(--accent2); }
.quick:hover { border-left-color: var(--primary); }

/* ===================================================================== */
/* Caster dashboard + OBS guide (app/routes/caster.js). Blueprint look:    */
/* mono technical labels, sharp rectangles, blue/lime/cyan accents.        */
/* ===================================================================== */
.mono { font-family: var(--mono); }
.cst-url { margin: .55rem 0; }
.cst-url-lbl { display: block; font-family: var(--mono); font-size: .72rem; text-transform: uppercase; letter-spacing: .05em; color: #555; margin-bottom: .2rem; }
.cst-url-row { display: flex; gap: .4rem; align-items: stretch; }
.cst-url-in { flex: 1 1 auto; min-width: 0; font-family: var(--mono); font-size: .8rem; padding: .35rem .5rem; border: 1px solid var(--line); border-radius: 3px; background: #f7f8fa; color: var(--ink); }
.cst-url-in:focus { outline: 2px solid var(--accent2); }
.cst-url-group { border-left: 3px solid var(--accent2); padding: .3rem 0 .3rem .7rem; margin: .8rem 0; }
.cst-url-h { margin: 0 0 .35rem; font-size: .9rem; font-family: var(--mono); }

.cst-board { display: grid; gap: .8rem; grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr)); }
.cst-tile { border: 1px solid var(--line); border-radius: 3px; padding: .6rem .7rem; background: #fff; border-top: 3px solid var(--primary); }
.cst-tile-head { display: flex; justify-content: space-between; align-items: center; gap: .5rem; margin-bottom: .4rem; }
.cst-versus { display: flex; align-items: center; gap: .5rem; justify-content: space-between; }
.cst-team { display: flex; align-items: center; gap: .35rem; flex: 1 1 0; min-width: 0; }
.cst-team-r { justify-content: flex-end; text-align: right; }
.cst-team strong { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cst-score { font-size: 1.05rem; font-weight: 800; padding: 0 .3rem; white-space: nowrap; }
.cst-song { font-size: .82rem; margin-top: .35rem; }
.cst-k { display: inline-block; font-family: var(--mono); font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; color: #fff; background: var(--primary); padding: 0 .35rem; border-radius: 3px; margin-right: .3rem; }
.cst-song .cst-k { background: var(--ink); }

.cst-guide-list { list-style: none; padding: 0; margin: 0; }
.cst-guide-list li { margin: .6rem 0; }
.cst-guide-list code { display: inline-block; word-break: break-all; }
.cst-guide-steps { margin: 0; padding-left: 1.2rem; }
.cst-guide-steps li { margin: .45rem 0; }

/* ===================================================================== */
/* CLASH MATCHUP CARD.                                                    */
/*                                                                        */
/* One box, three bands, no rules anywhere inside it:                     */
/*                                                                        */
/*   .mc-head   tinted mono strip — match no. + status + arrow. The whole  */
/*              strip is the <a> to the matchup page, so the card needs    */
/*              no bordered "Matchup ->" footer and no anchor is nested.   */
/*   .mc-hero   the two countries: big flag / full name / mono note.       */
/*   .mc-foot   the pick'em, FULL-BLEED — flush left and right, flat on    */
/*              the bottom edge, corners clipped by the card's own radius  */
/*              (.matchup-card sets overflow:hidden). It is the card's     */
/*              foundation, not a panel bolted underneath it, so nothing   */
/*              separates it from the hero but space.                      */
/*                                                                        */
/* The bands are told apart by TINT and SPACING only. The predecessor put  */
/* a hairline between the head-to-head and the pick'em and gave the        */
/* pick'em its own border and gutters, which is exactly what made one      */
/* matchup read as two unrelated slabs.                                    */
/* ===================================================================== */

/* Column flex so .mc-foot can be pushed to the bottom with margin-top:auto:
   grid items stretch, so every card in a row ends with its plinth on the same
   line even when one hero wraps to an extra row. */
.matchup-card { container-type: inline-size; }

/* -- band 1: the linked meta strip ---------------------------------------- */
.mc-head { display: flex; align-items: center; gap: .5rem; padding: .3rem .7rem;
  background: rgba(20,22,31,.035); color: #5f626f; min-height: 1.9rem; }
a.mc-head:hover { text-decoration: none; background: rgba(43,63,224,.09); color: var(--primary); }
/* The card clips its overflow (see .matchup-card), so an outline drawn OUTSIDE
   the box would be cut off — inset it instead. Same for the ballot controls. */
.mc-head:focus-visible, .mc-pick:focus-visible, .mc-cta:focus-visible {
  outline: 2px solid var(--ink); outline-offset: -2px; }
.mc-pos { font-size: .66rem; text-transform: uppercase; letter-spacing: .06em; }
.mc-state { border-radius: 3px; padding: .08rem .4rem; font-weight: 700;
  background: rgba(20,22,31,.08); color: #43465a; }
/* Matchup states get their own palette here, at (0,2,0), so the generic TEAM
   status chips further up (.status-pending etc.) can't colour them by accident. */
.mc-state.status-published, .mc-state.status-decided { background: var(--ink); color: #fff; }
.mc-state.status-mapping, .mc-state.status-judging { background: var(--accent2); color: var(--ink); }
.mc-state.status-rolling, .mc-state.status-awaiting_ban,
.mc-state.status-awaiting_pick { background: var(--accent); color: var(--ink); }
.mc-state.status-pending { background: rgba(20,22,31,.08); color: #43465a; }
/* The "open this matchup" affordance: a quiet corner arrow, not a footer row. */
.mc-arrow { margin-left: auto; font-weight: 800; color: var(--primary); line-height: 1; }

/* -- band 2: the hero ------------------------------------------------------ */
/* Each side is a CENTRED COLUMN (flag over name over note) mirrored around a
   small "vs", so the full country name gets half the card's width to wrap in
   and the pair stays symmetric at every size.
   A WORD IS NEVER BROKEN: the side columns are floored at min-content (the
   longest word) rather than free to shrink, so "United Kingdom" wraps between
   its words at worst and never splits as "King / dom" — and because the floor
   is the longest word, the row can never be pushed wider than the card. */
/* align-items:stretch (the grid default) is load-bearing: it makes both side
   columns the height of the row, so the winner's lime rule always lands on the
   same line as the other side's baseline even when one country name wraps to two
   lines and the other does not. */
.mc-hero { display: grid; grid-template-columns: minmax(min-content, 1fr) auto minmax(min-content, 1fr);
  gap: .5rem; padding: .85rem .8rem; }
/* The .45rem bottom padding is on EVERY side, not just the winner: it is the bed
   the winner's lime rule is painted on, and if only the winner carried it that
   side's annotation line would sit ~7px higher than the other's. */
.mc-side { display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  text-align: center; gap: .3rem; min-width: 0; padding-bottom: .45rem; }
.mc-side .mc-team { font-size: 1.05rem; font-weight: 700; line-height: 1.25;
  overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none; }
.mc-team-link { color: inherit; }
.mc-team-link:hover { color: var(--primary); text-decoration: underline; }
/* Country flags carry the identity, so they are the biggest thing on the card.
   .flag-img is object-fit:cover already, so scaling it cannot distort. */
.mc-flag { display: block; line-height: 0; }
.mc-flag .flag-img { width: 44px; height: 29px; border-radius: 2px; }
/* An unknown/empty code (a TBD slot) draws an empty dashed plate the same size,
   so both sides of an unfilled matchup still line up. */
.mc-flag .flag-unknown { display: block; width: 44px; height: 29px; background: transparent;
  border: 1px dashed rgba(20,22,31,.28); border-radius: 2px; color: transparent; }
/* margin-top:auto drops the annotation to the bottom of the (stretched) column,
   so the two seed lines sit on ONE line even when one country name wraps to two
   and the other does not — and the winner's lime rule lands just under it. */
.mc-notes { margin-top: auto; display: flex; align-items: center; gap: .3rem; flex-wrap: wrap; justify-content: center; }
.mc-seed { font-size: .68rem; letter-spacing: .04em; color: #5f626f; }
/* "vs" is pinned beside the FLAGS rather than centred in the row: the flags are
   the top line of the fixture, so the pairing reads across them. */
.mc-vs { align-self: start; font-size: .66rem; text-transform: uppercase; letter-spacing: .12em;
  color: #6b6e7a; padding-top: .5rem; }   /* #6b6e7a on white = 5.1:1 */

/* Decided: the winner is unmistakable from three reinforcing marks — the name
   goes heavy ink, a lime rule underlines that half of the hero (the same device
   as the header nav's hover), and an ink "won" chip replaces nothing but sits
   beside the seed. The loser is quietly muted (#5f626f on white = 6.1:1) and its
   flag is dimmed; never struck through, never greyscaled (that reads as DQ). */
.mc-side.is-winner .mc-team { font-weight: 800; color: var(--ink); }
.mc-side.is-winner { box-shadow: inset 0 -3px 0 var(--accent); }
.mc-won { font-family: var(--mono); font-size: .62rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .08em; background: var(--ink); color: #fff; border-radius: 3px; padding: .08rem .35rem; }
.matchup-card.is-decided .mc-side:not(.is-winner) { color: #5f626f; }
.matchup-card.is-decided .mc-side:not(.is-winner) .mc-team { font-weight: 600; }
.matchup-card.is-decided .mc-side:not(.is-winner) .flag-img { opacity: .55; }

/* -- band 3: the pick'em foundation --------------------------------------- */
/* margin-top:auto pins it to the bottom of the card; the negative-free layout
   below is what makes it FULL-BLEED: the band itself has no horizontal padding,
   so the bar and the ballot halves run edge to edge. Only the small mono kicker
   and the compact percentages are inset. */
.mc-foot { margin-top: auto; }
.mc-pk-k { display: block; font-family: var(--mono); font-size: .64rem; text-transform: uppercase;
  letter-spacing: .06em; color: #5f626f; padding: 0 .8rem .3rem; }

/* Split-bar variant. The compact head is one inset row (share · state · share);
   the track below sits flat on the card's bottom edge with NO border and no
   radius of its own — .matchup-card's overflow:hidden gives it the card's
   corners. Nothing separates it from the hero but the hero's own padding. */
.mc-foot-split .pk-split { margin: 0; }
.mc-foot-split .pk-compact-head { padding: 0 .8rem .3rem; margin: 0; }
.mc-foot-split .pk-split-bar { border-radius: 0; box-shadow: none; height: 1.7rem; }

/* Ballot variant. Two halves on the same left/right geometry as the split bar,
   so an open matchup and a closed one share one foundation shape. The 1px flex
   gap shows the container colour through as the only divider between the two
   controls — a control seam, not a panel seam. */
.mc-ballot { display: flex; gap: 1px; background: var(--line); margin: 0; }
.mc-pick { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: .25rem; font: inherit; font-size: .92rem; font-weight: 700;
  color: var(--ink); background: #fff; border: 0; border-radius: 0; padding: .5rem .5rem .55rem;
  cursor: pointer; text-align: center; transition: background-color .12s, color .12s; }
.mc-pick-n { overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none; }
.mc-pick-f { line-height: 0; }
.mc-pick:hover { background: rgba(43,63,224,.09); color: var(--primary); }
/* The viewer's own pick fills its half blue — a preview of the split bar this
   card will show once voting closes (#fff on --primary = 7.3:1). */
.mc-pick.is-chosen { background: var(--primary); color: #fff; }
.mc-pick.is-chosen:hover { background: var(--primary); color: #fff; filter: brightness(1.08); }
.mc-pick-k { font-family: var(--mono); font-size: .6rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .06em; background: var(--accent); color: var(--ink); border-radius: 3px; padding: .05rem .35rem; }

/* Signed-out call to action: the same slot, one full-bleed blue bar. */
.mc-cta { display: block; text-align: center; background: var(--primary); color: #fff;
  font-family: var(--mono); font-size: .74rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .05em; padding: .6rem .8rem; }
.mc-cta:hover { text-decoration: none; filter: brightness(1.08); }

/* The clash "by round" grid gives each card room for the two hero columns side  */
/* by side. Scoped to .bracket-lists so the admin bracket manager keeps its own  */
/* (narrower) track size — both now floor at min(100%, …) so a track can shrink  */
/* below its nominal width on a phone instead of overflowing the page.           */
.bracket-lists .matchup-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 21rem), 1fr)); }

/* A card narrow enough that the two hero columns would crowd (only reachable if
   the grid is overridden): shrink the flags and the name a step rather than let
   anything overflow. Container query — the card is its own inline-size container. */
@container (max-width: 17rem) {
  .mc-hero { gap: .3rem; padding: .7rem .5rem; }
  .mc-flag .flag-img, .mc-flag .flag-unknown { width: 34px; height: 23px; }
  .mc-side .mc-team { font-size: .95rem; }
}

/* -- team-name links on pick'em cards (item 11) --------------------------- */
.pk-team-link { color: inherit; }
.pk-team-link:hover { color: var(--primary); text-decoration: underline; }
.pk-tags { display: inline-flex; gap: .35rem; align-items: center; }

/* ===================================================================== */
/* TEAM STATE WITHOUT STRIKETHROUGH.                                      */
/* No team name is ever struck through. Disqualified teams read as a      */
/* quiet, muted variant of the same row; qualifier standings shade the    */
/* rows of teams that QUALIFIED and mute the ones passed down by the      */
/* same-country rule (the ↧ marker + its note carry the reason).          */
/* ===================================================================== */

/* Disqualified: muted ink + a slightly greyed flag, never a line-through. */
.mc-side.is-dq { color: #5f626f; opacity: .85; }
.mc-side.is-dq .mc-team-link { color: inherit; }
.mc-side.is-dq .flag-img, .team-card.is-dq .flag-img { filter: grayscale(1); }
.team-card.is-dq .team-name { color: #5f626f; }
/* Matchup header side (public /matchups/:id) — muted, flag greyed, not struck. */
.mh-side.is-dq { color: #5f626f; opacity: .85; }
.mh-side.is-dq .flag-img { filter: grayscale(1); }
.mh-side.is-dq .mh-team { color: #5f626f; }
.bk-team.is-dq { opacity: .6; }
/* Admin seeding: a passed-down team is muted, never struck (the note explains). */
.adm-list li.is-passed { color: #6b6e7a; }

/* Qualifier standings: a qualified row is tinted with the theme primary and */
/* ticked in the gutter; the open/hover states keep the tint (they would     */
/* otherwise be overridden by the generic .ql-row backgrounds above).        */
.ql-row.is-qualified { background: rgba(43,63,224,.10); box-shadow: inset 3px 0 0 var(--primary); }
.ql-details[open] .ql-row.is-qualified { background: rgba(43,63,224,.16); }
.ql-row.is-qualified:hover { background: rgba(43,63,224,.16); }
.ql-row.is-qualified .ql-team { color: var(--ink); }

/* EVERY team that did not qualify is greyed — muted ink on a faint neutral   */
/* wash, never a line-through. #5f626f on the wash measures ~5.3:1, so the    */
/* names stay readable. The two background rules below re-state the open and  */
/* hover states because `.ql-details[open] .ql-row` (0,3,0) outranks a bare   */
/* `.ql-row.is-notqualified` (0,2,0) and would otherwise repaint the row.     */
.ql-row.is-notqualified,
.ql-row.is-notqualified .ql-c,
.ql-row.is-notqualified .ql-anon,
.ql-row.is-notqualified .muted { color: #5f626f; }
.ql-row.is-notqualified { background: rgba(20,22,31,.035); }
.ql-details[open] .ql-row.is-notqualified { background: rgba(20,22,31,.06); }
.ql-row.is-notqualified:hover { background: rgba(20,22,31,.06); }
/* Passed down (country slot already taken) refines the greyed row: same muted */
/* ink as every other non-qualified team (so it is not oddly lighter), lighter */
/* weight, and the ↧ marker + its note (both from the view) carry the reason.  */
.ql-row.is-passed .ql-team { font-weight: 600; }

/* ===================================================================== */
/* HOME SCHEDULE — a reflowing list, never a scrolling table.             */
/* The old 4-column <table class="schedule-table"> sat in a .scroll-x box  */
/* inside the NARROW left column of .home-grid, so it scrolled sideways    */
/* and its song titles were clipped with an ellipsis: information hidden   */
/* at every viewport width. The card is now full content width (it lives   */
/* below .home-grid) and each round is one block that stacks on narrow     */
/* screens and splits into meta | songs from 700px up.                     */
/*                                                                         */
/* Both tracks are minmax(0, …) so a long title can never force a row      */
/* wider than the card, and the titles themselves wrap at spaces           */
/* (.song-line) — no scrollbar, no ellipsis, and no mid-word break.        */
/* ===================================================================== */
.home-sched .hs-list { list-style: none; margin: 0; padding: 0; }
.hs-row { display: grid; grid-template-columns: minmax(0, 1fr); gap: .15rem .9rem;
  padding: .7rem 0; border-top: 1px solid var(--line); }
.hs-row:first-child { border-top: 0; padding-top: .1rem; }
.hs-row:last-child { padding-bottom: .35rem; }
.hs-meta, .hs-songs { min-width: 0; }
/* Name + phase chip on one line; the chip drops under the name only if the
   column is too narrow for both (flex-wrap), never overflowing it. */
.hs-round { margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: .25rem .45rem; }
.hs-round strong { font-size: 1.02rem; }
.hs-when { margin: .1rem 0 0; font-size: .9rem; }
.hs-k { display: block; font-family: var(--mono); font-size: .66rem; text-transform: uppercase;
  letter-spacing: .06em; color: #666; }
/* Nested inside the <ol> of rounds, so state the marker: the browser default for
   a second-level list is a hollow circle, and these should read like the
   now-card's .now-songs bullets. */
.hs-song-list { list-style: disc; margin: .1rem 0 0; padding-left: 1.1rem; }
.hs-song-list li { padding: .05rem 0; }
.hs-tba { margin: .1rem 0 0; }
@media (min-width: 700px) {
  .hs-row { grid-template-columns: minmax(0, 15rem) minmax(0, 1fr); align-items: start; }
}

/* ===================================================================== */
/* Layout fixes: one box per matchup, and a homepage grid with no seams   */
/* or holes. All three were measured in the browser at 1280px before      */
/* changing anything.                                                     */
/* ===================================================================== */

/* A matchup is ONE box — and now literally one element. It used to be a wrapper
   (.mc-cell) holding a bordered head-to-head panel and a separately bordered
   pick'em panel; those were merged into a single bordered cell split by a
   hairline, and are now three bands of .matchup-card itself with no rule between
   them at all (see the CLASH MATCHUP CARD block above). */

/* The schedule sat flush against the quick-links column — measured gap 0px,
   because .home-grid has no bottom margin and .home-sched no top margin. Match
   the grid's own 1rem gutter so the seam reads as deliberate spacing. */
.home-sched { margin-top: 1rem; }

@media (min-width: 860px) {
  /* The two-column row is now the tall schedule (main) beside the tall sidebar
     (quick links + external links); both run to similar height, so neither
     leaves a bare gap and the old fill-the-hole hack is gone. The "Now
     happening" strip sits full-width above this row. */
  .home-main > .card { margin-bottom: 0; }
}

/* "Now happening" as a full-width horizontal strip: round + songs on the left,
   the countdown and CTA pinned right. Wide and short — it can no longer stretch
   into the tall empty box it was when it sat alone in one grid column. */
.now-strip { display: flex; flex-wrap: wrap; align-items: flex-start; gap: .4rem 1.2rem; }
.now-strip-main { flex: 1 1 20rem; min-width: 0; }
.now-strip-side { flex: 0 0 auto; display: flex; flex-direction: column; align-items: flex-start; gap: .3rem; }
.now-strip .now-card h2, .now-strip-main h2 { margin: .35rem 0 .3rem; }
.now-strip .now-songs { margin: .25rem 0 0; }
@media (min-width: 700px) { .now-strip-side { align-items: flex-end; text-align: right; } }

/* ===================================================================== */
/* SURFACE LIFT — one soft drop shadow on every box-level surface.        */
/*                                                                        */
/* Applied to the BOXES only: the things that sit directly on the         */
/* blueprint grid (or on a page section) and own a border. Deliberately   */
/* NOT applied to sub-panels inside an already-lifted box (.mc-head,      */
/* .mc-foot, .bp-step, .j-clash-entry, .adm-song, .pub-gate, .hs-row …)   */
/* and not to chips, pills or badges — stacking shadows inside a card is  */
/* what turns a light lift into a smudge.                                 */
/*                                                                        */
/* Tokens are defined at the top of this file: --shadow (contact + wide   */
/* ambient, both ink at low alpha), --shadow-lift (hover), and            */
/* --shadow-nested (contact only, for a listed box that lives inside a    */
/* card). Placed last so it wins over the earlier per-component rules.    */
/* ===================================================================== */
.card, .matchup-card, .quick, .team-card, .fa-card, .arch-card,
.adm-tile, .j-assignment, .filter-bar, .cst-tile {
  box-shadow: var(--shadow);
}
/* These are listed boxes that always render INSIDE a .card (the matchup page's
   entries, the ban/pick song grid, and the announcement-history rows, which are
   .card articles inside the History card), so they take the contact layer alone
   — enough to lift off the card surface, never a second full shadow. .ann-hist
   is listed here rather than beside its own layout rule because this block is
   placed last on purpose: it has to win over the generic .card lift above. */
.entry-card, .song-card, .ann-hist { box-shadow: var(--shadow-nested); }

/* Hover: the boxes that already answer the pointer with a border colour change
   lift one step further. box-shadow (plus the colour they already animate) is
   the ONLY thing transitioned — no transform, so nothing reflows or resamples. */
.quick, a.adm-tile, .j-assignment, .arch-card, .matchup-card {
  transition: box-shadow .12s ease, border-color .12s ease;
}
.quick:hover, a.adm-tile:hover, .j-assignment:hover, .arch-card:hover,
.matchup-card:hover { box-shadow: var(--shadow-lift); }
/* The clash card has no border-colour hover of its own (its head strip answers
   the pointer instead), so give it a quiet blue edge with the lift. */
.matchup-card:hover { border-color: rgba(43,63,224,.45); }

/* =====================================================================
   ADMIN — WORD SAFETY.

   The public pages got these guards; the admin panel never did, so a
   long unbreakable run (a 38-char team name, a Discord snowflake, a
   136-char Windows path, a 101-char .osz filename) sized its grid/flex
   track to its own min-content and pushed whole pages sideways —
   measured at up to 1221px of horizontal page overflow at 375px.

   Two halves, and BOTH are needed:
     1. min-width:0 on every admin grid/flex CHILD. Tracks default to a
        min-content floor, so without this the token widens the column
        no matter what wrapping is allowed inside it.
     2. a wrapping policy per KIND of string:
          human names  -> overflow-wrap: normal (never split a word —
                          they wrap at spaces or not at all, exactly
                          like .song-line / .pk-name / .mc-team)
          prose        -> overflow-wrap: break-word (a word breaks ONLY
                          when it cannot fit a line on its own)
          machine ids  -> overflow-wrap: anywhere (.adm-id / .adm-fieldval:
                          snowflakes, file paths, filenames — breaking
                          one is harmless and hiding it is not)
   ===================================================================== */

/* 1. Floor every admin track at zero. */
.adm-two-col, .adm-two-col > *, .adm-grid > *, .adm-tiles > *,
.matchup-grid > *, .adm-round-head > *, .adm-member > *, .adm-judge > *,
.adm-card-head > *, .adm-song-long, .adm-team, .adm-round,
.anon-team, .adm-team-cell, .np-pane, .or-col, .sp-card {
  min-width: 0;
}

/* The bracket manager's control stack: card -> .mm-controls -> .adm-form.inline
   -> .inline-lbl -> <select>. EVERY link in that chain has to be bounded, or the
   <select> (whose intrinsic width is its widest <option> — a 175-char song
   title) drags the whole page out with it. */
.mm-controls, .mm-controls > *, .decide-row, .decide-row > *,
.adm-form.inline, .adm-form.inline > *, .inline-lbl,
.dq-panel, .dq-panel form, .dq-panel form > * {
  min-width: 0; max-width: 100%;
}
/* A control never exceeds the box it sits in — this is what caps the song
   <select> at its card instead of at its longest option. */
.adm-form input, .adm-form select, .adm-form textarea,
.inline-form input, .inline-form select, .adm-song input, .adm-song textarea {
  max-width: 100%;
}
.adm-form select, .adm-form textarea, .adm-song input { width: 100%; }
/* Textareas had no admin styling at all (announcements, newspost, outreach,
   proposals) — they inherited the UA look inside otherwise-styled forms. */
.adm-form textarea {
  font: inherit; padding: .35rem .45rem; border: 1px solid rgba(20,22,31,.3);
  border-radius: 4px; resize: vertical; min-width: 0;
}
/* Auto-fill floors that could exceed a 375px viewport. */
.adm-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 12rem), 1fr)); }
.adm-songs { grid-template-columns: repeat(auto-fill, minmax(min(100%, 16rem), 1fr)); }
/* Single column below 720px: minmax(0,1fr), not 1fr, or the collapsed grid is
   still floored at the widest token in either column (measured: it dragged every
   screening team card out to 865px on a 375px screen). */
@media (max-width: 720px) { .adm-two-col { grid-template-columns: minmax(0, 1fr); } }

/* 2a. HUMAN NAMES — wrap between words or not at all, never mid-word. */
.adm-tname, .adm-tsub, .adm-name, .mh-team, .or-card h3, .sp-head h3,
.adm-list li, .adm-team .adm-round-head h2 {
  overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none;
}
.adm-tname { font-weight: 700; }
/* A screening card is headed by the REGISTERED team name, which can be one
   40-character run with no space in it. It is a human name, so it is never
   broken and never truncated — instead the heading scales with the viewport, so
   the names that actually exist fit a 375px card, and min-width:0 above keeps
   even a pathological one from widening the page. */
.adm-team .adm-round-head h2 { font-size: clamp(.9rem, 3.4vw, 1.3rem); }
/* An inline control row WRAPS. Without this a rename form (two text inputs and
   a button, none of which may wrap) held the screening cards 110px past the
   right edge of a phone screen. */
.inline-form { flex-wrap: wrap; }
/* 2b. LOG / MACHINE-OUTPUT PROSE — audit reasons, anonymizer warnings and
   errors, bot-log reasons. These quote stored paths and .osz filenames, and the
   only alternative to a last-resort break is punching a 480px hole out of a
   328px sidebar. break-word (not anywhere): a word breaks ONLY when it cannot
   fit a line on its own, so ordinary words are untouched. Deliberately NOT
   applied to `.adm-list li` at large — the plain admin lists (rosters, the
   pass-down list, banned voters, pick'em matchups) hold human names and keep
   the never-break policy of 2a. */
.adm-audit .adm-list li, .anon-warnings li, .anon-err,
.or-notes, .sp-notes, .sp-comments li, .adm-wrap, .cfg-preview-bar {
  overflow-wrap: break-word;
}
/* 2c. MACHINE IDENTIFIERS — Discord snowflakes, osu! ids, stored paths and
   .osz filenames. Breaking one anywhere is harmless; hiding it is not. */
.adm-id, .adm-fieldval { overflow-wrap: anywhere; }
/* 2d. A SHORT machine NUMBER that stands alone in its own table cell (the osu!
   id column of the username-change history). `anywhere` is wrong here: it lowers
   the cell's min-content contribution to one digit, so at 375px the column
   collapsed to 48px and split "500002" across two lines — a mid-word break with
   nothing to gain, since a 6-8 digit id has no long run to shorten. It keeps the
   never-break policy instead; the table already sits in a .scroll-x wrapper, so
   the few extra pixels cost nothing. */
.adm-num { overflow-wrap: normal; word-break: normal; word-wrap: normal;
  hyphens: none; white-space: nowrap; }

/* Anonymization table: the Result column (leak warnings + anonymizer errors,
   which quote filenames) used to set the table's width, pushing the Re-run /
   Re-roll column ~3 screen-widths off to the right. Cap it and wrap it, then
   pin the actions column to the right edge of the scroll box so the buttons are
   reachable at any width without scrolling at all. */
.anon-result { max-width: 26rem; }
.anon-table .anon-actions {
  position: sticky; right: 0; background: #fff;
  box-shadow: inset 1px 0 0 rgba(20,22,31,.12);
}

/* =====================================================================
   ADMIN — small shared pieces the views were hand-styling inline.
   (An <h2> with style="margin:0", a form floated out of a heading, and
   three <h3>s with hand-rolled sizing are gone in favour of these.)
   ===================================================================== */
/* Card heading + its one control on a row, carrying the same blue rule the
   plain `.card > h2` gets, so a card with a control still reads the same. */
.adm-card-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; flex-wrap: wrap; margin: 0 0 .7rem;
  padding-bottom: .35rem; border-bottom: 2px solid var(--primary);
}
.adm-card-head h2 { margin: 0; padding: 0; border-bottom: 0; }
.adm-sub-h { margin: .6rem 0 .2rem; font-size: .95rem; }
/* A full-width labelled field (contest name, rules markdown) — the two places
   that were sizing an input with a style attribute. */
.adm-form .adm-field-wide { display: block; margin: .4rem 0 .8rem; font-weight: 700; font-size: .82rem; }
.adm-form .adm-field-wide input, .adm-form .adm-field-wide textarea {
  display: block; width: 100%; min-width: 0; font: inherit; font-weight: 400;
  padding: .35rem .45rem; border: 1px solid rgba(20,22,31,.3); border-radius: 4px; margin-top: .2rem;
}
.adm-form .adm-field-wide input { max-width: 32rem; }
.adm-form .adm-field-wide textarea { max-width: 100%; resize: vertical; }
/* Theme preview (setup wizard). Only the per-contest COLOURS stay in style
   attributes — everything structural is here. */
.cfg-preview { border: 2px solid var(--ink); border-radius: 3px; overflow: hidden; margin: .4rem 0 .8rem; }
.cfg-preview-banner { display: block; width: 100%; max-height: 120px; object-fit: cover; }
.cfg-preview-bar { color: #fff; font-weight: 800; padding: .5rem .7rem; }
.cfg-preview-body { padding: .6rem .7rem; }
.cfg-preview-chip { display: inline-block; padding: .1em .5em; border-radius: 3px; font-weight: 700; }
/* Rounds editor: the long song fields print their full value under the input,
   because an <input> clips and admin must never hide a value with no way to
   read it. Mono + break-anywhere: these are titles, artists and stored paths. */
.adm-fieldval {
  display: block; margin-top: .15rem; font-family: var(--mono); font-size: .7rem;
  font-weight: 400; line-height: 1.35; color: #5f626f;
}
/* The bracket MANAGER's cards carry a whole control stack (roll, ban/pick with a
   song select, vote tally + three decide forms, a DQ/substitute panel), so they
   need a wider track than the public clash grid's read-only cards. */
.mm-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 24rem), 1fr)); }
/* The two REGISTERED team names, full card width under the header — they wrap at
   the spaces and are never split mid-word or truncated. */
.mm-teams { margin: .35rem 0 0; text-align: center;
  overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none; }
/* Pick'em matchup line: two country labels and a "vs" that wrap as a unit. */
.pka-versus { display: inline-flex; flex-wrap: wrap; align-items: center; gap: .1rem .4rem; min-width: 0; }
/* The nav strip on /admin/new marks the current page with a span (there is no
   year in the URL yet, so there is no second link to be on). */
.adm-subnav .active { background: var(--primary); color: #fff; font-weight: 700;
  font-size: .88rem; padding: .3rem .7rem; border-radius: 4px; }
/* The org-tools strip sits directly under the main subnav — lighter rule so the
   two do not read as two equal navs. */
.adm-toolsnav { margin-top: -.5rem; border-bottom-width: 1px; }

/* =====================================================================
   ADMIN ORG TOOLS — newspost generator, judge outreach, song proposals.
   These three pages described layouts that had NO rules at all: the
   "two-pane" generator, the outreach "board" and the proposal "grid"
   were all display:block, so every pane and column stacked full width
   and the generated-markdown box rendered as a 168px strip.
   ===================================================================== */

/* -- newspost: inputs left, the deliverable right ------------------------- */
.np-grid { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 900px) { .np-grid { grid-template-columns: minmax(0, 21rem) minmax(0, 1fr); } }
.np-selector { margin-bottom: .6rem; }
.np-form { margin-top: 0; }
.np-warn { margin-bottom: .8rem; }
.np-warn ul { margin: .3rem 0 0; padding-left: 1.1rem; }
.np-output { margin-bottom: 0; }
.np-output-head { display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; flex-wrap: wrap; margin: 0 0 .4rem; padding-bottom: .35rem;
  border-bottom: 2px solid var(--primary); }
.np-output-head h2 { margin: 0; }
/* The page's actual deliverable. Full width of its pane, mono, its own
   horizontal scroll (wrap="off") so a markdown table row stays one row. */
.np-md {
  display: block; width: 100%; max-width: 100%; min-width: 0;
  font-family: var(--mono); font-size: .78rem; line-height: 1.5;
  padding: .6rem .7rem; border: 1px solid var(--line); border-radius: 3px;
  background: #f7f8fa; color: var(--ink); resize: vertical;
}

/* -- judge outreach: a status board -------------------------------------- */
/* 18rem, not 15: at a narrower floor the columns land around 250px and a card
   holding a 28-character handle overflowed it. Three wide columns that wrap to a
   second row read better than five cramped ones, and nothing overflows. */
.or-board { display: grid; gap: .8rem; align-items: start;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); }
.or-col { background: rgba(20,22,31,.03); border: 1px solid var(--line); border-radius: 3px; padding: .6rem; }
.or-col-head { display: flex; align-items: center; justify-content: space-between;
  gap: .5rem; margin: 0 0 .5rem; padding-bottom: .3rem; border-bottom: 2px solid var(--primary); }
.or-col-head h2 { margin: 0; font-family: var(--mono); font-size: .8rem;
  text-transform: uppercase; letter-spacing: .05em; padding: 0; border-bottom: 0; }
.or-card { padding: .6rem .7rem; margin-bottom: .6rem; }
.or-card:last-child { margin-bottom: 0; }
.or-card h3 { margin: 0 0 .2rem; font-size: 1rem; }
.or-notes { margin: .4rem 0 0; font-size: .9rem; color: #444; }
.or-edit { margin: .4rem 0; }
.or-edit summary { cursor: pointer; font-weight: 700; font-size: .82rem; }
.or-convert { margin-top: .4rem; }
.or-add { margin-top: .2rem; }

/* -- song proposals: one card per candidate ------------------------------ */
.sp-grid { display: grid; gap: 1rem; align-items: start;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 22rem), 1fr)); }
.sp-card { padding: .8rem .9rem; margin-bottom: 0; }
.sp-head { display: flex; align-items: flex-start; justify-content: space-between; gap: .5rem; flex-wrap: wrap; }
.sp-head h3 { margin: 0; font-size: 1.02rem; min-width: 0; }
.sp-head-right { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }
.sp-status select { max-width: 100%; }
.sp-notes { margin: .4rem 0 0; font-size: .9rem; color: #444; }
.sp-tallyrow { margin: .5rem 0 .2rem; }
.sp-tally { display: inline-flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.sp-comments { list-style: none; margin: .4rem 0 0; padding: 0; font-size: .9rem; }
.sp-comments li { padding: .25rem 0; border-top: 1px solid rgba(20,22,31,.08); }
.sp-c-vote { font-family: var(--mono); font-weight: 800; font-size: .72rem;
  background: rgba(20,22,31,.08); border-radius: 3px; padding: .05rem .35rem; }
.sp-vote { display: flex; flex-direction: column; align-items: flex-start; gap: .4rem; margin-top: .6rem; }
.sp-vote-row { display: flex; gap: .3rem; flex-wrap: wrap; }
/* −1 / 0 / +1 read as a segmented control, not three primary buttons; the one
   this organizer cast fills blue. */
.btn-vote { background: #fff; color: var(--ink); border: 2px solid rgba(20,22,31,.2);
  padding: .25rem .8rem; font-family: var(--mono); font-weight: 800; }
.btn-vote:hover { border-color: var(--primary); color: var(--primary); filter: none; }
.or-vote-active, .btn-vote.or-vote-active { background: var(--primary); color: #fff; border-color: var(--primary); }
/* The comment is a textarea (an <input> showed ~25 of the 500 stored chars with
   no way to see the rest). */
.sp-vote-comment { display: flex; flex-direction: column; gap: .2rem; width: 100%;
  font-size: .8rem; font-weight: 700; }
.sp-vote-comment textarea { width: 100%; max-width: 100%; min-width: 0; font: inherit;
  font-weight: 400; padding: .35rem .45rem; border: 1px solid rgba(20,22,31,.3);
  border-radius: 4px; resize: vertical; }
.sp-promote { margin-top: .5rem; }
.sp-promote summary { cursor: pointer; font-weight: 700; font-size: .82rem; }
.sp-add { margin-top: .2rem; }
/* Status accent on the card's leading edge (open stays neutral). */
.sp-shortlisted { border-left: 3px solid var(--accent2); }
.sp-chosen { border-left: 3px solid #1a7f37; }
.sp-rejected { border-left: 3px solid rgba(20,22,31,.25); opacity: .75; }

/* An outreach card lives INSIDE the bordered .or-col panel, so it takes the
   contact shadow only — two full shadows stacked read as a smudge (see the
   SURFACE LIFT block above). Proposal cards sit on the page and keep theirs. */
.or-card { box-shadow: var(--shadow-nested); }

/* ===================================================================== */
/* Clash: pick'em is a destination, not an in-card control                */
/* ===================================================================== */

/* The two buttons that open pick'em, in the clash page head. Voting used to
   happen inside each card, which POSTed and redirected the reader off the
   bracket mid-scroll; the action now lives here and the cards only report. */
.page-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: .7rem 0 0; }

/* "Your pick" foundation — the third .mc-foot variant, on the same full-bleed
   geometry as the split bar so an open matchup and a closed one share one
   shape. Flat on the card's bottom edge (the card's overflow:hidden supplies
   the corners), and deliberately NOT interactive: no cursor change, no hover
   state, nothing that implies you can vote from here. #fff on --primary is
   7.3:1. The country name never breaks mid-word. */
.mc-foot-mine .mc-mine {
  display: flex; align-items: center; justify-content: center; gap: .45rem;
  margin: 0; padding: .5rem .8rem;
  background: var(--primary); color: #fff; font-weight: 700; font-size: .92rem;
}
.mc-mine-n { min-width: 0; overflow-wrap: normal; word-break: normal; word-wrap: normal; hyphens: none; }

/* The account chip carries the viewer's own picture, so it lays out as a row
   rather than inline text (an inline <img> sat on the baseline and pushed the
   chip taller than the 52px header). */
.userbox { display: inline-flex; align-items: center; gap: .4rem; }

/* ===================================================================== */
/* Public judge roster (/:year/judges)                                    */
/* ===================================================================== */
.jl-list { list-style: none; margin: .2rem 0 0; padding: 0;
  display: grid; gap: .6rem; grid-template-columns: repeat(auto-fill, minmax(min(100%, 13rem), 1fr)); }
.jl-item { display: flex; align-items: center; gap: .6rem; min-width: 0;
  background: #fff; border: 1px solid var(--line); border-radius: 3px; padding: .5rem .6rem;
  box-shadow: var(--shadow-nested); }
/* min-width:0 so a long osu! username wraps inside the tile instead of forcing
   the grid track wider; the name itself never breaks mid-word. */
.jl-who { display: flex; align-items: center; gap: .4rem; min-width: 0; flex-wrap: wrap; }
.jl-name { font-weight: 700; overflow-wrap: normal; word-break: normal; hyphens: none; }
.jl-cc { line-height: 0; }

/* ===================================================================== */
/* Round-header actions (VOD, download-all), entry links, home links,     */
/* pick'em active/decided sections.                                       */
/* ===================================================================== */
.round-bar-acts { display: inline-flex; flex-wrap: wrap; gap: .4rem; margin-left: auto; }
.round-bar-a { font-family: var(--mono); font-size: .68rem; font-weight: 800; text-transform: uppercase;
  letter-spacing: .04em; color: var(--ink); background: var(--accent); padding: .18rem .5rem; border-radius: 3px; }
.round-bar-a:hover { text-decoration: none; filter: brightness(.96); }

/* An entry can carry two links (permanent osu! beatmap + local .osz); keep them
   together and let them wrap under the team name on a narrow card. */
.entry-links { display: inline-flex; flex-wrap: wrap; gap: .35rem; margin-left: auto; }

/* Off-site contest links in the home sidebar. */
.home-links { background: #fff; border: 1px solid var(--line); border-radius: 3px;
  padding: .7rem .8rem; margin-top: 1rem; box-shadow: var(--shadow-nested); }
.home-links-h { margin: 0 0 .4rem; font-size: .95rem; }
.home-links-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .15rem; }
.home-links-list a { display: flex; align-items: center; gap: .5rem; font-weight: 700; padding: .3rem .3rem; border-radius: 3px; }
.home-links-list a:hover { text-decoration: none; background: rgba(43,63,224,.06); }
.link-ic { flex: 0 0 auto; color: var(--primary); }

/* Pick'em: "Open now" then "Decided" under their own headings. */
.pk-section { margin: 1.2rem 0 .6rem; padding-bottom: .3rem; border-bottom: 2px solid var(--primary); }
.pk-section:first-of-type { margin-top: .4rem; }

/* Admin beatmaps: link each entry to its osu! upload. */
.bm-table td { vertical-align: top; }
.bm-row.is-linked { background: rgba(43,63,224,.04); }
.bm-src { background: var(--accent); color: var(--ink); }
.bm-suggest { margin-top: .3rem; display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .6rem; }
.bm-nomatch { display: block; margin-top: .3rem; }
.bm-controls { display: flex; flex-wrap: wrap; gap: .35rem; justify-content: flex-end; align-items: center; }
.bm-input { min-width: 10rem; }
.bm-set { flex-wrap: wrap; gap: .3rem; }
/* An auto-match awaiting review — amber, so it reads as "not signed off" next to a
   green "confirmed" and a grey "unlinked". */
.pill.bm-review { background: #c8871f; color: #fff; }
.bm-review-t { color: #b3760d; font-family: var(--mono); font-size: .92em; white-space: nowrap; }
/* Replace is a disclosure: closed it's just a compact button on the same row as
   Confirm/Unlink; open, the paste field drops to its own line. Keeps linked rows
   from wrapping to a permanently-wide input. */
.bm-replace { display: inline-flex; }
.bm-replace > summary { list-style: none; cursor: pointer; }
.bm-replace > summary::-webkit-details-marker { display: none; }
.bm-replace > summary::marker { content: ''; }
.bm-replace[open] { flex-basis: 100%; flex-direction: column; align-items: flex-end; gap: .3rem; }
.bm-replace-body { display: flex; }

/* Organizer manual .osz upload (anonymization page) — the file input + button
   sit on one wrapping row once the <details> is opened. */
.anon-upload-form { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; margin-top: .3rem; }
.anon-upload-form input[type="file"] { max-width: 100%; font-size: .8rem; }
.anon-missing-list { list-style: none; margin: .4rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .5rem; }
.anon-missing-row { display: flex; flex-wrap: wrap; align-items: center; gap: .5rem 1rem; padding: .5rem .1rem; border-top: 1px solid rgba(20,22,31,.1); }
.anon-missing-row:first-child { border-top: 0; }
.anon-missing-row .anon-team { min-width: 12rem; }
