/* ═══════════════════════════════════════════════════════════════
   BLUNDERCHECK — SCOREBOARD PAIRINGS
   Shared by:
     · /pairings                    (weekly tournament, SwissSys-driven)
     · /admin/ladder/.../challenge  (Club Challenge, Pairings tab)

   Built for OBS browser-source capture: full browser width, no page
   chrome, everything scaled by a single --sb-scale variable that
   scoreboard.js binary-searches so the board list exactly fills the
   viewport height.

   All custom properties are namespaced --sb-* and scoped to .sb-root
   so this file can never collide with a host page's own theme vars.
═══════════════════════════════════════════════════════════════ */

.sb-root {
  /* Site theme — album-accurate palette, same values as main-v3.css */
  --sb-black:      #080808;
  --sb-dark:       #0f0f0f;
  --sb-surface:    #161616;
  --sb-surface2:   #1e1e1e;
  --sb-border:     #2a2a2a;

  --sb-green:      #78c14c;
  --sb-green-light:#96d96a;
  --sb-green-deep: #3d6226;

  --sb-purple:     #9c4097;
  --sb-purple-lt:  #d494cf;
  --sb-purple-deep:#5c1f59;

  --sb-gold:       #c9a84c;
  --sb-gold-light: #e8c97a;
  --sb-gold-deep:  #7a6230;

  --sb-white:      #f5f2ed;
  --sb-off-white:  #dedad3;
  --sb-muted:      #c4c0b8;

  --sb-font-display: 'Bebas Neue', 'Impact', sans-serif;
  --sb-font-body:    'DM Sans', system-ui, sans-serif;
  --sb-font-mono:    'Share Tech Mono', monospace;

  /* Master scale — driven by scoreboard.js. 1 = design size. */
  --sb-scale: 1;
  /* Secondary multiplier applied to player names only. Names are the one
     thing here that can be arbitrarily long ("Vandenberghe-Castellanos, A."),
     so once the master scale has filled the screen vertically, scoreboard.js
     trims this until no name is clipped. Everything else keeps full size. */
  --sb-name-scale: 1;
  /* Column count for the board grid — also driven by scoreboard.js. */
  --sb-cols: 1;

  width: 100%;
  min-height: 100vh;
  background: var(--sb-black);
  color: var(--sb-white);
  font-family: var(--sb-font-body);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Faint board-grid texture, same idiom as the rest of the site */
.sb-root::before {
  content: '';
  position: fixed; inset: 0;
  background-image:
    linear-gradient(rgba(120,193,76,0.035) 1px, transparent 1px),
    linear-gradient(90deg, rgba(120,193,76,0.035) 1px, transparent 1px);
  background-size: 64px 64px;
  pointer-events: none;
  z-index: 0;
}

.sb-root > * { position: relative; z-index: 1; }

/* Embedded variant — the scoreboard rendered inside another page (the Club
   Challenge screen) rather than owning the whole viewport. Drops the full-height
   claim, the opaque background and the fixed grid overlay, all of which belong
   to the standalone broadcast page only. */
.sb-root.sb-embed {
  min-height: 0;
  background: transparent;
  overflow: visible;
}
.sb-root.sb-embed::before { content: none; }
.sb-root.sb-embed .sb-sections { padding: 0; }

/* ── Top bar: tournament title ──────────────────────────────── */

.sb-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: calc(var(--sb-scale) * 10px) calc(var(--sb-scale) * 24px);
  /* Wraps so a narrow OBS source drops the round/LIVE block under the title
     instead of squeezing the title into a sliver. */
  flex-wrap: wrap;
  padding: calc(var(--sb-scale) * 14px) clamp(10px, calc(var(--sb-scale) * 28px), 4vw);
  background: linear-gradient(180deg, var(--sb-surface2), var(--sb-dark));
  border-bottom: calc(var(--sb-scale) * 3px) solid var(--sb-gold);
  box-shadow: 0 calc(var(--sb-scale) * 10px) calc(var(--sb-scale) * 30px) rgba(0,0,0,0.6);
}

/* Title block. The flex-basis is the width below which the round/LIVE block
   moves to its own line; min-width:0 still lets it shrink on a tiny source. */
.sb-top-main {
  flex: 1 1 520px;
  min-width: 0;
}

.sb-eyebrow {
  font-family: var(--sb-font-mono);
  font-size: max(11px, calc(var(--sb-scale) * 13px));
  letter-spacing: calc(var(--sb-scale) * 4px);
  text-transform: uppercase;
  color: var(--sb-green);
  margin-bottom: calc(var(--sb-scale) * 2px);
  overflow-wrap: anywhere;
}

/* The title never truncates. It shrinks with the viewport width (the vw caps)
   and wraps onto more lines when it still doesn't fit, so the full tournament
   name is always on screen however narrow the OBS source gets. */
.sb-title {
  font-family: var(--sb-font-display);
  font-size: min(max(min(40px, 7vw), calc(var(--sb-scale) * 68px)), 7vw);
  line-height: 0.95;
  letter-spacing: calc(var(--sb-scale) * 3px);
  color: var(--sb-white);
  text-transform: uppercase;
  margin: 0;
  text-shadow:
    calc(var(--sb-scale) * 3px) calc(var(--sb-scale) * 3px) 0 var(--sb-purple-deep),
    0 0 calc(var(--sb-scale) * 26px) rgba(201,168,76,0.25);
  white-space: normal;
  overflow-wrap: anywhere;
  text-wrap: balance;
}

.sb-subtitle {
  font-family: var(--sb-font-mono);
  font-size: max(11px, calc(var(--sb-scale) * 13px));
  letter-spacing: calc(var(--sb-scale) * 2px);
  color: var(--sb-muted);
  margin-top: calc(var(--sb-scale) * 4px);
  overflow-wrap: anywhere;
}

.sb-top-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: calc(var(--sb-scale) * 10px) calc(var(--sb-scale) * 18px);
  flex-shrink: 0;
  max-width: 100%;
}

.sb-round {
  font-family: var(--sb-font-display);
  font-size: max(28px, calc(var(--sb-scale) * 46px));
  line-height: 1;
  letter-spacing: calc(var(--sb-scale) * 2px);
  color: var(--sb-gold-light);
  white-space: nowrap;
}
.sb-round small {
  display: block;
  font-family: var(--sb-font-mono);
  font-size: calc(var(--sb-scale) * 11px);
  letter-spacing: calc(var(--sb-scale) * 3px);
  color: var(--sb-muted);
}

.sb-live {
  display: flex;
  align-items: center;
  gap: calc(var(--sb-scale) * 8px);
  font-family: var(--sb-font-mono);
  font-size: calc(var(--sb-scale) * 12px);
  letter-spacing: calc(var(--sb-scale) * 3px);
  color: var(--sb-green);
  border: 1px solid var(--sb-green-deep);
  border-radius: 999px;
  padding: calc(var(--sb-scale) * 5px) calc(var(--sb-scale) * 14px);
  white-space: nowrap;
}
.sb-live i {
  width: calc(var(--sb-scale) * 9px);
  height: calc(var(--sb-scale) * 9px);
  border-radius: 50%;
  background: var(--sb-green);
  box-shadow: 0 0 calc(var(--sb-scale) * 10px) var(--sb-green);
  animation: sbPulse 1.8s ease-in-out infinite;
}
@keyframes sbPulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ── Section grouping ───────────────────────────────────────── */

/* flex: 0 0 auto is load-bearing. With flex-grow/shrink the sections column
   would compress itself to whatever height was left over, so scrollHeight
   could never exceed the viewport and the binary-search fit in scoreboard.js
   would always bottom out at its maximum scale. Letting this box take its
   natural height (and overflow the root when too big) is what gives the
   search something real to measure. */
.sb-sections {
  flex: 0 0 auto;
  /* Auto margins centre the board list in whatever height is left over. On a
     two-board night the list can only grow so far before names start clipping,
     so there will be slack — centring makes that slack look deliberate instead
     of leaving everything jammed under the header. */
  margin-top: auto;
  margin-bottom: auto;
  padding: calc(var(--sb-scale) * 14px) calc(var(--sb-scale) * 22px)
           calc(var(--sb-scale) * 18px);
  display: flex;
  flex-direction: column;
  gap: calc(var(--sb-scale) * 16px);
}

.sb-section-bar {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: calc(var(--sb-scale) * 6px) calc(var(--sb-scale) * 20px);
  background: linear-gradient(90deg,
              rgba(120,193,76,0.20), rgba(156,64,151,0.14) 60%, transparent);
  border-left: calc(var(--sb-scale) * 6px) solid var(--sb-green);
  padding: calc(var(--sb-scale) * 7px) calc(var(--sb-scale) * 16px);
  margin-bottom: calc(var(--sb-scale) * 10px);
}

.sb-section-name {
  font-family: var(--sb-font-display);
  font-size: max(20px, calc(var(--sb-scale) * 32px));
  letter-spacing: calc(var(--sb-scale) * 4px);
  background: var(--sb-green);
  color: #080808;
  text-transform: uppercase;
  line-height: 1;
  padding: calc(var(--sb-scale) * 5px) calc(var(--sb-scale) * 18px)
           calc(var(--sb-scale) * 3px);
  border-radius: 999px;
  box-shadow: calc(var(--sb-scale) * 3px) calc(var(--sb-scale) * 3px) 0 #000,
              0 0 calc(var(--sb-scale) * 18px) rgba(120,193,76,0.35);
}
.sb-section-meta {
  font-family: var(--sb-font-mono);
  font-size: max(11px, calc(var(--sb-scale) * 13px));
  letter-spacing: calc(var(--sb-scale) * 2px);
  color: var(--sb-muted);
  text-transform: uppercase;
}
.sb-section-meta.right { margin-left: auto; }

/* Single unnamed section — hide the bar entirely, the title carries it */
.sb-section-bar.sb-hide { display: none; }

/* ── Board grid ─────────────────────────────────────────────── */

.sb-boards {
  display: grid;
  grid-template-columns: repeat(var(--sb-cols), minmax(0, 1fr));
  gap: calc(var(--sb-scale) * 10px) calc(var(--sb-scale) * 16px);
  align-content: start;
}

.sb-board {
  display: grid;
  grid-template-columns:
      calc(var(--sb-scale) * 118px)  /* board banner */
      minmax(0, 1fr)                 /* white        */
      calc(var(--sb-scale) * 54px)   /* vs           */
      minmax(0, 1fr)                 /* black        */
      calc(var(--sb-scale) * 116px); /* result       */
  align-items: center;
  gap: calc(var(--sb-scale) * 10px);
  background: linear-gradient(180deg, var(--sb-surface2), var(--sb-surface));
  border: 1px solid var(--sb-border);
  border-left: calc(var(--sb-scale) * 8px) solid var(--sb-purple);
  border-radius: calc(var(--sb-scale) * 8px);
  box-shadow: calc(var(--sb-scale) * 4px) calc(var(--sb-scale) * 4px) 0 rgba(0,0,0,0.55);
  padding: calc(var(--sb-scale) * 12px) calc(var(--sb-scale) * 16px);
  min-width: 0;
}

/* Finished boards are marked by the green edge and the coloured result cell,
   never by dimming. Fading them was fine in theory but wrong in the room: in a
   small event most boards finish early, so most of the screen ended up greyed
   out while the header stayed bright. Every name stays at full brightness. */
.sb-board.sb-done {
  border-left-color: var(--sb-green);
  background: linear-gradient(180deg, var(--sb-surface2), var(--sb-surface));
}

/* ── Board number ───────────────────────────────────────────────
   The word BOARD is spelled out on a solid gold banner rather than
   abbreviated. At broadcast distance an abbreviation reads as noise; a
   filled banner reads as a label even when the viewer can't resolve the
   letters. The chunky offset shadow is the same trick the challenge-card
   badges used — it gives the block physical presence against a flat
   background. */

.sb-bd {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  background: var(--sb-black);
  border: calc(var(--sb-scale) * 3px) solid var(--sb-gold);
  border-radius: calc(var(--sb-scale) * 8px);
  box-shadow: calc(var(--sb-scale) * 3px) calc(var(--sb-scale) * 3px) 0 #000,
              0 0 calc(var(--sb-scale) * 20px) rgba(201,168,76,0.22);
  overflow: hidden;
  line-height: 1;
}
.sb-bd-label {
  background: var(--sb-gold);
  color: #080808;
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 17px);
  letter-spacing: calc(var(--sb-scale) * 3px);
  text-align: center;
  text-transform: uppercase;
  padding: calc(var(--sb-scale) * 3px) 0 calc(var(--sb-scale) * 2px);
}
.sb-bd-num {
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 52px);
  color: var(--sb-gold-light);
  text-align: center;
  line-height: 1;
  padding: calc(var(--sb-scale) * 4px) calc(var(--sb-scale) * 6px)
           calc(var(--sb-scale) * 6px);
  text-shadow: 0 0 calc(var(--sb-scale) * 16px) rgba(201,168,76,0.5);
}

/* ── Player sides ───────────────────────────────────────────── */

.sb-side {
  display: flex;
  align-items: center;
  gap: calc(var(--sb-scale) * 10px);
  min-width: 0;
}
.sb-side.sb-black { justify-content: flex-end; text-align: right; }

/* WHITE / BLACK badge — the word, a colour swatch, and a fixed width so
   the two rows of a stacked board line up on the name. Colour alone never
   carries the meaning; the word is always there. */
.sb-chip {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.42em;
  min-width: calc(var(--sb-scale) * 118px);
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 24px);
  letter-spacing: calc(var(--sb-scale) * 2px);
  line-height: 1;
  text-transform: uppercase;
  padding: calc(var(--sb-scale) * 5px) calc(var(--sb-scale) * 14px);
  border-radius: 999px;
  box-shadow: calc(var(--sb-scale) * 2px) calc(var(--sb-scale) * 2px) 0 rgba(0,0,0,0.65);
}
.sb-chip.w {
  background: var(--sb-white);
  color: #0b0b0b;
  border: calc(var(--sb-scale) * 2px) solid #ffffff;
}
.sb-chip.b {
  background: #0b0b0b;
  color: var(--sb-white);
  border: calc(var(--sb-scale) * 2px) solid #6f6f6f;
}
.sb-swatch {
  width: 0.7em; height: 0.7em;
  border-radius: 50%;
  flex: 0 0 auto;
}
.sb-chip.w .sb-swatch { background: #ffffff; border: 1px solid #8a8a8a; }
.sb-chip.b .sb-swatch { background: #000000; border: 1px solid #6f6f6f; }

.sb-name {
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * var(--sb-name-scale) * 52px);
  line-height: 1;
  letter-spacing: calc(var(--sb-scale) * 1.5px);
  color: var(--sb-white);
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  min-width: 0;
}
/* Last resort, set by scoreboard.js: a name that still doesn't fit at the
   smallest name scale wraps onto a second line rather than being cut off. */
.sb-name.sb-name-wrap {
  white-space: normal;
  overflow-wrap: anywhere;
  overflow: visible;
  line-height: 1.05;
}

.sb-rtg {
  flex: 0 0 auto;
  font-family: var(--sb-font-mono);
  font-size: calc(var(--sb-scale) * 19px);
  color: var(--sb-muted);
  letter-spacing: calc(var(--sb-scale) * 1px);
}

.sb-vs {
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 30px);
  letter-spacing: calc(var(--sb-scale) * 2px);
  color: var(--sb-purple-lt);
  text-align: center;
  text-transform: uppercase;
  text-shadow: calc(var(--sb-scale) * 2px) calc(var(--sb-scale) * 2px) 0 #000;
}

.sb-bye {
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 30px);
  letter-spacing: calc(var(--sb-scale) * 4px);
  color: var(--sb-muted);
  text-transform: uppercase;
  opacity: 0.7;
}

/* ── Result ─────────────────────────────────────────────────── */

.sb-res {
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 34px);
  line-height: 1;
  letter-spacing: calc(var(--sb-scale) * 1px);
  text-align: center;
  color: var(--sb-muted);
  border: calc(var(--sb-scale) * 2px) solid var(--sb-border);
  border-radius: calc(var(--sb-scale) * 6px);
  box-shadow: calc(var(--sb-scale) * 2px) calc(var(--sb-scale) * 2px) 0 rgba(0,0,0,0.5);
  padding: calc(var(--sb-scale) * 8px) calc(var(--sb-scale) * 6px);
  overflow-wrap: anywhere;
}
.sb-res.sb-pending {
  color: var(--sb-green);
  border-color: var(--sb-green-deep);
  background: rgba(120,193,76,0.08);
  font-family: var(--sb-font-mono);
  font-size: calc(var(--sb-scale) * 15px);
  letter-spacing: calc(var(--sb-scale) * 3px);
}
.sb-res.sb-white-win { color: var(--sb-white);      border-color: var(--sb-border);      background: rgba(245,242,237,0.07); }
.sb-res.sb-black-win { color: var(--sb-purple-lt);  border-color: var(--sb-purple-deep); background: rgba(156,64,151,0.12); }
.sb-res.sb-draw      { color: var(--sb-gold-light); border-color: var(--sb-gold-deep);   background: rgba(201,168,76,0.10); }

/* ── Empty state ────────────────────────────────────────────── */

/* Sections no longer grow, so the empty state carries its own height
   instead of inheriting one from the column. */
.sb-empty {
  min-height: 55vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  color: var(--sb-muted);
  text-align: center;
}
.sb-empty .glyph { font-size: 7rem; color: rgba(120,193,76,0.14); line-height: 1; }
.sb-empty .head {
  font-family: var(--sb-font-display);
  font-size: 2.4rem; letter-spacing: 5px; color: var(--sb-green);
}
.sb-empty p { font-size: 1.05rem; max-width: 40ch; }

/* ── Footer strip ───────────────────────────────────────────── */

.sb-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 4px 16px;
  padding: calc(var(--sb-scale) * 6px) calc(var(--sb-scale) * 24px);
  border-top: 1px solid var(--sb-border);
  background: var(--sb-dark);
  font-family: var(--sb-font-mono);
  font-size: calc(var(--sb-scale) * 11px);
  letter-spacing: calc(var(--sb-scale) * 3px);
  color: var(--sb-muted);
  text-transform: uppercase;
}
.sb-foot .sb-stamp { color: var(--sb-green-deep); }

/* ── Result-entry buttons (Club Challenge pairings tab only) ──
   The weekly tournament board is read-only; the Club Challenge board
   is the director's working screen, so each board carries its own
   result buttons. They sit in a second grid row spanning full width. */

.sb-board.sb-actionable { grid-template-rows: auto auto; }
.sb-actions {
  grid-column: 1 / -1;
  display: flex;
  gap: calc(var(--sb-scale) * 8px);
  margin-top: calc(var(--sb-scale) * 8px);
  padding-top: calc(var(--sb-scale) * 8px);
  border-top: 1px solid var(--sb-border);
}
.sb-btn {
  flex: 1 1 0;
  font-family: var(--sb-font-display);
  font-size: calc(var(--sb-scale) * 20px);
  letter-spacing: calc(var(--sb-scale) * 2px);
  padding: calc(var(--sb-scale) * 8px) calc(var(--sb-scale) * 10px);
  border-radius: calc(var(--sb-scale) * 6px);
  border: calc(var(--sb-scale) * 2px) solid var(--sb-border);
  background: var(--sb-surface);
  color: var(--sb-off-white);
  cursor: pointer;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: background .12s ease, border-color .12s ease, transform .08s ease;
}
.sb-btn:hover { border-color: var(--sb-green); background: rgba(120,193,76,0.12); }
.sb-btn:active { transform: translateY(1px); }
.sb-btn.w:hover { border-color: var(--sb-white); background: rgba(245,242,237,0.12); }
.sb-btn.b:hover { border-color: var(--sb-purple-lt); background: rgba(156,64,151,0.16); }
.sb-btn.d:hover { border-color: var(--sb-gold); background: rgba(201,168,76,0.12); }

/* ── Resolved strip (Club Challenge) ────────────────────────── */

.sb-resolved {
  display: flex;
  flex-wrap: wrap;
  gap: calc(var(--sb-scale) * 8px);
  padding-top: calc(var(--sb-scale) * 10px);
  margin-top: calc(var(--sb-scale) * 4px);
  border-top: 1px solid var(--sb-border);
}
.sb-resolved-chip {
  font-family: var(--sb-font-mono);
  font-size: calc(var(--sb-scale) * 13px);
  letter-spacing: calc(var(--sb-scale) * 1px);
  color: var(--sb-muted);
  background: var(--sb-surface);
  border: 1px solid var(--sb-border);
  border-radius: 999px;
  padding: calc(var(--sb-scale) * 4px) calc(var(--sb-scale) * 12px);
}
.sb-resolved-chip b { color: var(--sb-green); font-weight: 400; }

/* ── Wide (single-column) layout ────────────────────────────────
   White on the left, VS in the middle, black on the right. The colour badge
   sits ON TOP of the name rather than beside it: inline, a spelled-out
   WHITE/BLACK pill costs roughly 200px per side at broadcast scale, which is
   200px the name doesn't get. Lifting it to its own line hands almost the
   full half-width back to the name, so the label stays large AND the name
   roughly doubles. */

.sb-root:not(.sb-stacked) .sb-side {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "chip chip"
    "name rtg";
  align-items: center;
  justify-items: start;
  gap: calc(var(--sb-scale) * 5px) calc(var(--sb-scale) * 12px);
}
.sb-root:not(.sb-stacked) .sb-side > .sb-chip { grid-area: chip; }
.sb-root:not(.sb-stacked) .sb-side > .sb-name { grid-area: name; width: 100%; }
.sb-root:not(.sb-stacked) .sb-side > .sb-bye  { grid-area: name; }
.sb-root:not(.sb-stacked) .sb-side > .sb-rtg  { grid-area: rtg; }

/* Black mirrors it — badge and name both hug the right edge. */
.sb-root:not(.sb-stacked) .sb-side.sb-black {
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "chip chip"
    "rtg  name";
  justify-items: end;
}
.sb-root:not(.sb-stacked) .sb-side.sb-black > .sb-name { text-align: right; }

/* ── Stacked board layout ───────────────────────────────────────
   Applied by scoreboard.js whenever the grid runs 2+ columns. Two
   "Lastname, Firstname" entries side by side need roughly 1100px to stay
   legible, so in a 640px column the horizontal card would either clip both
   names or shrink them to nothing. Stacking white over black hands each name
   the full card width, which is what makes 20+ boards readable at once. */

.sb-root.sb-stacked .sb-board {
  grid-template-columns:
      calc(var(--sb-scale) * 118px)
      minmax(0, 1fr)
      calc(var(--sb-scale) * 112px);
  grid-template-areas:
    "bd white res"
    "bd black res";
  row-gap: calc(var(--sb-scale) * 6px);
}
.sb-root.sb-stacked .sb-bd    { grid-area: bd; }
.sb-root.sb-stacked .sb-white { grid-area: white; }
.sb-root.sb-stacked .sb-black {
  grid-area: black;
  justify-content: flex-start;
  text-align: left;
}
.sb-root.sb-stacked .sb-res   { grid-area: res; }
.sb-root.sb-stacked .sb-vs    { display: none; }

/* Black's chip is rendered after the name (it sits on the right in the
   horizontal layout). Reordering it rather than duplicating markup keeps
   both rows starting with their colour square. */
.sb-root.sb-stacked .sb-black .sb-chip { order: -1; }

/* flex:1 1 0 rather than the default shrink-to-fit. A shrink-to-fit box is
   exactly as wide as its text, which makes "is this text wider than its box?"
   unanswerable — the two measurements are equal whether or not it fits. Giving
   the name the whole remaining track makes the comparison meaningful, and has
   the side benefit of right-aligning the rating. */
.sb-root.sb-stacked .sb-name { flex: 1 1 0; }

.sb-root.sb-stacked .sb-board.sb-actionable {
  grid-template-areas:
    "bd white   res"
    "bd black   res"
    "actions actions actions";
}
.sb-root.sb-stacked .sb-actions { grid-area: actions; }

/* ── Narrow fallback (phones / vertical OBS sources) ─────────── */

@media (max-width: 700px) {
  .sb-root { --sb-cols: 1; }
  .sb-board {
    grid-template-columns: calc(var(--sb-scale) * 64px) minmax(0,1fr) calc(var(--sb-scale) * 76px);
    grid-template-areas:
      "bd white  res"
      "bd black  res";
    row-gap: calc(var(--sb-scale) * 4px);
  }
  .sb-bd    { grid-area: bd; }
  .sb-white { grid-area: white; }
  .sb-black { grid-area: black; justify-content: flex-start; text-align: left; }
  .sb-res   { grid-area: res; }
  .sb-vs    { display: none; }
}


/* ── Pre-fit hiding ──────────────────────────────────────────────────────────
   The board renders at design scale until scoreboard.js has measured it, and
   design scale is much larger than whatever will actually fit — so without
   this every page load showed a flash of oversized boards snapping down. The
   element still occupies layout (visibility, not display), which is what lets
   the fit routine measure it while it's hidden. scoreboard.js adds .sb-fitted
   when it's done, and unconditionally after 1.5s as a failsafe. */
.sb-root:not(.sb-fitted) { visibility: hidden; }
