/* Remove number input spinners globally */
input[type=number]::-webkit-outer-spin-button,
input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type=number] { -moz-appearance: textfield; }

/* ============================================================
   Design tokens — Floodlight
   Dark is the CSS BASE. Light is the single override block below.
   Every colour in this file comes from here. See
   tests/Unit/CssTokenGuardTest.php.

   Which is not the same as which theme a visitor SEES. Since issue 104
   the layouts stamp data-theme="light" unless somebody has chosen dark,
   so light is the default and this file's base is the fallback rather
   than the common case. Keep the base dark anyway: inverting it would
   rewrite every token in both blocks to change what amounts to one
   condition in one line of JavaScript.

   Adding a token? It may skip the light override ONLY if the surface
   it names never changes shade between themes — the rail chrome and
   its washes and shadows, which differ in strength but not in ground.
   Everything else needs a value in both blocks. A theme-flipping token
   used on a surface that does not flip disappears in one theme: that
   is what --sb-on-rail exists to prevent, and it has shipped as a bug
   here three times.
   ============================================================ */
:root {
  /* ground */
  --sb-bg:            #0E1216;
  --sb-surface:       #171C22;
  --sb-surface-2:     #1F252C;
  --sb-card:          #171C22;

  /* line */
  --sb-border:        #2B333B;
  --sb-border-strong: #3B454F;

  /* text */
  --sb-text:          #FFFFFF;
  --sb-muted:         #98A2AD;
  --sb-dim:           #6E7883;

  /* Near-black, not white: #FFF on either orange is ~2.4-2.9:1 and fails
     WCAG AA for body text, while #0E1216 clears 7:1 on both. */
  --sb-on-accent:     #0E1216;

  /* accent */
  --sb-accent:        #FF8A1F;
  --sb-accent-hover:  #FF9A3D;
  --sb-accent-tint:   rgba(255,138,31,.15);
  /* Opaque counterpart to --sb-accent-tint, for surfaces under sticky
     elements that can't tolerate a translucent background (e.g.
     .sr-my-col). Roughly what the translucent tint resolves to over
     --sb-bg. */
  --sb-accent-tint-solid: #322417;

  /* state — separate from the accent on purpose */
  --sb-ok:            #31B47A;
  --sb-ok-tint:       rgba(49,180,122,.16);
  --sb-warn:          #E0A32C;
  --sb-warn-hover:    #E8B354;
  --sb-warn-tint:     rgba(224,163,44,.16);
  --sb-bad:           #E04D4D;
  --sb-bad-tint:      rgba(224,77,77,.15);

  /* Text for hover/active states on --sb-bad/--sb-muted grounds. Neither
     white nor near-black clears 4.5:1 on both of those across both themes
     at once, but the dark-base near-black / light-override white split
     below does: near-black on dark --sb-bad #E04D4D is 4.78:1, near-black
     on dark --sb-muted #98A2AD is 7.26:1; white on light --sb-bad #D13B3B
     is 4.77:1, white on light --sb-muted #5B6570 is 5.93:1. */
  --sb-on-state:      #0E1216;

  /* Text for hover/active states on --sb-warn grounds specifically: near-
     black clears 4.5:1 on both dark --sb-warn #E0A32C (8.46:1) and light
     --sb-warn #C88A16 (6.36:1), so this one doesn't need a per-theme
     override the way --sb-on-state does. */
  --sb-on-warn:       #0E1216;

  /* Text for the rail chrome. Until issue #80 this was declared once with
     NO light override, because the rail was near-black in both themes -
     and the trap it guarded against was writing --sb-text here, which is
     white in dark and #0E1216 in light, the same value as the old light
     rail, so the text painted itself invisible on its own ground.
     The rail is now paper in the light theme (see the chrome block
     below), so this token flips with it and the whole rail family below
     has a light value. The trap has not gone away: it has moved. Every
     colour used ON the rail must come from a token that flips WITH the
     rail, never from --sb-text, --sb-muted or --sb-accent, which flip on
     their own schedule for the page underneath. */
  --sb-on-rail:       #FFFFFF;

  /* chrome — dark here, paper in the light theme since issue #80. Every
     token in this block therefore has a light value, and so does
     --sb-on-rail above: a light rail with a dark-rail token left behind
     is text or a line that vanishes. */
  --sb-rail:          #0A0D10;
  --sb-rail-dim:      #8A94A0;
  --sb-rail-line:     #1C232A;
  /* Ground of a hovered or current row in the rail. Split from
     --sb-rail-line in #80 because the two stopped agreeing: the light
     rail wants a white row lifted off its paper ground and a grey line,
     and one token cannot be both. This value is what --sb-rail-line was
     doing for rows in the dark theme, so the dark rail is unchanged. */
  --sb-rail-row:      #1C232A;
  /* The accent as it appears ON the rail. --sb-accent is tuned for the
     page: #FF7A00 on light paper is 2.5:1, which fails for the bottom
     tab label and the brand mark that use it. Dark theme keeps the page
     accent exactly; the light theme darkens it until it clears 4.5:1 on
     the paper rail. */
  --sb-rail-accent:   #FF8A1F;
  /* Elevated dark surface for popovers/menus/banners anchored to the rail
     chrome (e.g. the phone topnav's dropdown) — same tone as dark theme's
     --sb-surface-2, but declared separately and non-flipping because these
     surfaces stay dark even in light theme. */
  --sb-rail-raised:   #1F252C;
  /* Overlay washes for hover/active/border states on the rail chrome.
     Three steps. White-alpha here, black-alpha in the light theme: a
     white wash on paper is invisible, which is the failure #80 had to
     fix along with the ground itself. */
  --sb-rail-wash-sm:  rgba(255,255,255,.06);
  --sb-rail-wash-md:  rgba(255,255,255,.12);
  --sb-rail-wash-lg:  rgba(255,255,255,.2);
  /* Accent tint over the rail chrome specifically — distinct from
     --sb-accent-tint, which flips per theme; this one doesn't, because the
     rail it sits on doesn't either. */
  --sb-rail-accent-wash: rgba(255,138,31,.25);

  /* Near-black ground for the two hero gradients, which run from it into
     the accent. It used to be --sb-rail, and #80 took the rail light -
     these two surfaces are not the rail and stay dark in both themes, so
     they have their own token and their own text colour. Neither has a
     light override, because neither changes shade. */
  --sb-ink:           #0A0D10;
  --sb-on-ink:        #FFFFFF;

  /* The plate a team crest is drawn on (issue #79). White in both themes
     and therefore no light override: 43 of the 67 crest files carry dark
     ink on transparency, so the plate is what makes them legible, and a
     plate that flipped with the theme would take the legibility with it.
     Its hairline does flip - it only has work to do where the plate meets
     a white card. */
  --sb-crest-plate:      #FFFFFF;
  --sb-crest-plate-line: rgba(255,255,255,0);

  /* A translucent dark laid over artwork to say "spent" - used over a crest
     whose round was lost (issue #82). No light override for the same reason
     --sb-crest-plate has none: it darkens a white plate in both themes, and
     a scrim that flipped with the theme would stop reading as a scrim. */
  --sb-scrim:            rgba(14,18,22,.62);

  /* shape */
  --sb-radius:        8px;
  --sb-radius-sm:     4px;

  /* shadow — a near-black shadow needs more alpha to read against an
     already-dark ground than it does against a light one; the light-theme
     override below turns both down rather than reusing this value. */
  --sb-shadow:        rgba(0,0,0,.24);
  --sb-shadow-strong: rgba(0,0,0,.45);
}

:root[data-theme="light"] {
  --sb-bg:            #FFFFFF;
  --sb-surface:       #FFFFFF;
  --sb-surface-2:     #F4F6F8;
  --sb-card:          #FFFFFF;

  --sb-border:        #DFE3E8;
  --sb-border-strong: #C6CDD5;

  --sb-text:          #0E1216;
  --sb-muted:         #5B6570;
  --sb-dim:           #8A94A0;

  --sb-accent:        #FF7A00;
  --sb-accent-hover:  #E56D00;
  --sb-accent-tint:   #FFF1E2;
  --sb-accent-tint-solid: #FFF1E2; /* already opaque in this theme */

  --sb-ok:            #2E9D68;
  --sb-ok-tint:       #E6F5EE;
  --sb-warn:          #C88A16;
  --sb-warn-hover:    #A6740F;
  --sb-warn-tint:     #FDF3E0;
  --sb-bad:           #D13B3B;
  --sb-bad-tint:      #FCECEC;

  --sb-on-state:      #FFFFFF;

  /* Issue #80: the rail was #0E1216 here - a black slab beside a white
     page. It is now paper, one step off the page rather than its
     opposite, and every token that paints on it follows. */
  --sb-rail:          #F4F6F8;
  --sb-rail-dim:      #5B6570;
  --sb-rail-line:     #E4E8EC;
  --sb-rail-row:      #FFFFFF;
  --sb-rail-raised:   #FFFFFF;
  --sb-rail-accent:   #B35600;
  --sb-on-rail:       #0E1216;

  --sb-rail-wash-sm:  rgba(14,18,22,.04);
  --sb-rail-wash-md:  rgba(14,18,22,.08);
  --sb-rail-wash-lg:  rgba(14,18,22,.14);
  --sb-rail-accent-wash: rgba(255,122,0,.18);

  --sb-crest-plate-line: rgba(14,18,22,.10);

  --sb-shadow:        rgba(0,0,0,.08);
  --sb-shadow-strong: rgba(0,0,0,.18);
}

/* ============================================================
   Bootstrap 5.0.1 component surfaces
   5.0.1 compiles literal colours into its components and exposes no
   per-component CSS variables (that arrived in 5.3), so each surface is
   restated here ONCE against tokens. These replace the ~200 lines of
   dark-only overrides this file used to carry.
   ============================================================ */
.form-control,
.form-select {
  background-color: var(--sb-surface-2);
  border-color: var(--sb-border);
  color: var(--sb-text);
}
.form-control::placeholder { color: var(--sb-dim); }
.form-control:focus,
.form-select:focus {
  background-color: var(--sb-surface-2);
  border-color: var(--sb-accent);
  color: var(--sb-text);
  box-shadow: 0 0 0 .2rem var(--sb-accent-tint);
}
.form-control:disabled,
.form-control[readonly] { background-color: var(--sb-surface); color: var(--sb-muted); }
.form-check-input:not(:checked) { background-color: var(--sb-surface-2); border-color: var(--sb-border); }
.form-check-input:checked { background-color: var(--sb-accent); border-color: var(--sb-accent); }
/* The checkmark glyph is a background SVG with a baked-in white stroke
   (%23fff), same as .btn-close's glyph - white on --sb-accent is 2.36:1
   dark / 2.61:1 light, the exact pairing --sb-on-accent's own comment
   warns about. Restated with the near-black on-accent colour instead;
   --sb-on-accent doesn't change between themes so one rule covers both. */
.form-check-input[type=checkbox]:checked {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230e1216' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10l3 3l6-6'/%3e%3c/svg%3e");
}

.table { color: var(--sb-text); border-color: var(--sb-border); }
.table > :not(caption) > * > * { border-bottom-color: var(--sb-border); }
/* Bootstrap paints its own hover wash as an inset box-shadow using
   --bs-table-accent-bg, which our background-color override doesn't
   touch - left alone it stacks under the colour below as a faint extra
   wash on every hover. */
.table-hover > tbody > tr:hover > * { background-color: var(--sb-surface-2); color: var(--sb-text); box-shadow: none; }
/* This app only ever puts .table-light on a <thead> (never a striped or
   hoverable tbody), so the missing --bs-table-striped-bg/-active-bg/
   -hover-bg restatements below are dead weight, not a live gap. If
   .table-light is ever applied to a tbody row, those will need
   restating here too - Bootstrap reads them for striping/hover/active. */
.table-light,
thead.table-light > tr > th {
  --bs-table-bg: var(--sb-surface-2);
  --bs-table-color: var(--sb-muted);
  --bs-table-border-color: var(--sb-border);
  background-color: var(--sb-surface-2);
  color: var(--sb-muted);
  border-color: var(--sb-border);
}

.modal-content { background-color: var(--sb-card); border-color: var(--sb-border); color: var(--sb-text); }
.modal-header  { border-bottom-color: var(--sb-border); }
.modal-footer  { border-top-color: var(--sb-border); }

.dropdown-menu { background-color: var(--sb-card); border-color: var(--sb-border); }
.dropdown-item { color: var(--sb-text); }
.dropdown-item:hover,
.dropdown-item:focus { background-color: var(--sb-surface-2); color: var(--sb-text); }
.dropdown-item.active { background-color: var(--sb-accent); color: var(--sb-on-accent); }
.dropdown-divider { border-top-color: var(--sb-border); }

/* The close glyph is a background SVG with a baked-in fill, so it is one of
   the few things that genuinely cannot be tokenised. */
.btn-close { filter: invert(1) grayscale(100%) brightness(200%); }
[data-theme="light"] .btn-close { filter: none; } /* allow-theme-rule: SVG glyph fill, not a CSS colour */

.btn-primary {
  background-color: var(--sb-accent);
  border-color: var(--sb-accent);
  color: var(--sb-on-accent);
}
.btn-primary:hover,
.btn-primary:focus {
  background-color: var(--sb-accent-hover);
  border-color: var(--sb-accent-hover);
  color: var(--sb-on-accent);
}

/* These previously existed only as dark-theme-scoped rules; that block is
   gone (superseded by the token layer above) and nothing in the block
   above replaced them, so they were silently falling through to Bootstrap
   5.0.1's compiled light defaults on a dark ground. */
.card {
  background-color: var(--sb-card);
  border-color: var(--sb-border);
  color: var(--sb-text);
}
.card-header {
  background-color: var(--sb-surface-2);
  border-bottom-color: var(--sb-border);
}

.nav-tabs           { border-bottom-color: var(--sb-border); }
.nav-tabs .nav-link { color: var(--sb-muted); border-color: transparent; }
.nav-tabs .nav-link:hover  { color: var(--sb-text); border-color: var(--sb-border) var(--sb-border) transparent; }
.nav-tabs .nav-link.active { color: var(--sb-text); background-color: var(--sb-card); border-color: var(--sb-border) var(--sb-border) var(--sb-card); }

.popover        { background-color: var(--sb-card); border-color: var(--sb-border); }
.popover-header { background-color: var(--sb-surface-2); border-bottom-color: var(--sb-border); color: var(--sb-text); }
.popover-body   { color: var(--sb-text); }

.alert-success { background: var(--sb-ok-tint); border-color: var(--sb-ok); color: var(--sb-ok); }
.alert-danger  { background: var(--sb-bad-tint); border-color: var(--sb-bad); color: var(--sb-bad); }
.alert-warning { background: var(--sb-warn-tint); border-color: var(--sb-warn); color: var(--sb-warn); }
/* No dedicated "info" token exists; the brand accent doubles for it, same
   as it did before the rebrand (info was Bootstrap's default blue, now
   the app has one accent instead of a separate info hue). */
.alert-info    { background: var(--sb-accent-tint); border-color: var(--sb-accent); color: var(--sb-accent); }

hr              { border-color: var(--sb-border); opacity: 1; }
.text-muted     { color: var(--sb-muted) !important; }
.bg-light       { background-color: var(--sb-surface-2) !important; color: var(--sb-text) !important; }
.badge.bg-secondary { background-color: var(--sb-surface-2) !important; color: var(--sb-muted); }

/* ============================================================
   Base
   ============================================================ */
body {
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--sb-bg);
  color: var(--sb-text);
}

a { color: var(--sb-accent); }
a:hover { color: var(--sb-accent-hover); }

/* ============================================================
   Layout
   ============================================================ */
.sb-layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  background: var(--sb-bg);
}

/* Centered max-width wrapper — used inside sb-main and sb-hero */
.sb-container {
  width: 100%;
  max-width: 1280px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 20px;
  padding-right: 20px;
}
.sb-container--fluid { max-width: none; }

.sb-main {
  flex: 1;
  padding-top: 24px;
  padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px));
  overflow-x: hidden;
}

@media (min-width: 992px) {
  .sb-main { padding-bottom: 24px; }
}

/* ============================================================
   Navbar — Single Dark Topnav
   ============================================================ */

/* Outer nav wrapper */
.sb-navbar {
  background: var(--sb-rail);
  padding: 0;
  box-shadow: 0 1px 8px var(--sb-shadow-strong);
}

/* ── Single dark topnav bar ── */
.sb-topnav {
  display: flex;
  align-items: center;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 20px;
  height: 48px;
  width: 100%;
  gap: 2px;
}

.sb-topnav-links {
  display: flex;
  align-items: stretch;
  gap: 0;
  flex: 1;
  margin-left: 8px;
}

.sb-topnav-right {
  display: flex;
  align-items: center;
  gap: 8px;
}

.sb-topnav .sb-nav-label {
  font-size: .72rem;
  color: var(--sb-rail-dim);
  white-space: nowrap;
}

/* Brand override for dark background */
.sb-topnav .sb-brand {
  color: var(--sb-on-rail) !important;
}
.sb-topnav .sb-brand .sb-brand-dot {
  color: var(--sb-rail-accent);
}

/* Theme button and hamburger override for dark bar */
.sb-topnav .sb-toggler     { color: var(--sb-rail-dim); }
.sb-topnav .sb-toggler:hover { color: var(--sb-on-rail); }


/* ── Brand ── */
.sb-brand {
  font-weight: 800;
  font-size: 1.1rem;
  letter-spacing: -.3px;
  text-decoration: none;
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

.sb-brand .sb-brand-dot {
  color: var(--sb-accent);
}

.sb-brand-mark { height: 32px; }


/* ── Nav links (for colored/dark bars) ── */
.sb-nav-link {
  color: var(--sb-on-rail);
  text-decoration: none;
  font-size: .82rem;
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 10px;
  border: none;
  border-radius: 4px;
  transition: color .15s, background .15s;
  white-space: nowrap;
}

.sb-nav-link:hover {
  color: var(--sb-on-rail);
  background: var(--sb-rail-wash-md);
}

.sb-nav-link.active {
  color: var(--sb-on-rail);
  background: var(--sb-rail-wash-lg);
}

.sb-nav-link.dropdown-toggle::after { display: none; }

/* Hamburger button */
.sb-toggler {
  background: none;
  border: none;
  padding: 4px 6px;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
}

.sb-toggler:hover { color: var(--sb-text); }
.sb-toggler:focus { outline: none; box-shadow: none; }

/* Mobile legacy bar (kept for admin) */
.sb-mobile-bar {
  display: flex;
  align-items: center;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 16px;
  height: 52px;
  width: 100%;
}

/* Mobile collapse panel (dark rail background) */
.sb-mobile-collapse {
  background: var(--sb-rail);
  border-top: 1px solid var(--sb-rail-wash-md);
  padding-bottom: 8px;
}

.sb-mobile-group {
  padding: 10px 16px 4px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.sb-mobile-group + .sb-mobile-group {
  border-top: 1px solid var(--sb-rail-wash-sm);
}

.sb-mobile-label {
  font-size: .65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .6px;
  color: var(--sb-rail-dim);
  padding: 0 2px 4px;
}

.sb-mobile-collapse .sb-nav-link {
  padding: 7px 10px;
  border-radius: 6px;
}

.sb-mobile-collapse .sb-nav-link:hover {
  background: var(--sb-rail-wash-md);
}

.sb-mobile-collapse .sb-nav-link.active {
  background: var(--sb-rail-wash-lg);
  color: var(--sb-on-rail);
}

/* Login pill (on white top bar) */
.sb-nav-pill {
  color: var(--sb-accent) !important;
  border: 2px solid var(--sb-accent);
  border-radius: 999px;
  padding: 6px 20px !important;
  font-weight: 600;
  font-size: .875rem;
  text-decoration: none;
  transition: background .15s, color .15s;
  line-height: 1.4;
  margin-left: auto;
  white-space: nowrap;
}

.sb-nav-pill:hover {
  background: var(--sb-accent);
  color: var(--sb-on-accent) !important;
}

.sb-nav-pill--ghost {
  border-color: transparent;
  color: var(--sb-muted) !important;
  font-weight: 500;
  margin-left: auto;
}
.sb-nav-pill--ghost + .sb-nav-pill {
  margin-left: 4px;
}
.sb-nav-pill--ghost:hover {
  background: transparent;
  color: var(--sb-accent) !important;
}

/* ── The top bar at phone width (issue 130) ──
   A guest gets no bottom nav, so this bar is their whole navigation: five
   controls plus the brand, and at 375px they came to 371px inside 335px of
   content box. The page scrolled sideways and the sign-in button sat past
   the right edge, which on a phone means unreachable rather than untidy.
   Nothing is dropped - the bar is trimmed until it fits. */
@media (max-width: 575.98px) {
  .sb-topnav { padding: 0 12px; }
  .sb-brand-mark { height: 28px; }
  /* The label inside these is already d-none below sm, so the pill is an
     icon in 40px of padding. */
  .sb-nav-pill { padding: 6px 12px !important; }
}

/* Below 360px - an iPhone SE, and the narrowest phone still in use - even
   the trimmed bar does not fit, and the wordmark is the only thing on it
   that says nothing the logo beside it does not. */
@media (max-width: 359.98px) {
  .sb-brand span { display: none; }
}

/* ============================================================
   Stats hero bar
   ============================================================ */
.sb-hero {
  background: var(--sb-rail);
  border-bottom: 1px solid var(--sb-rail-line);
}

.sb-hero-inner {
  display: flex;
  align-items: center;
  padding-top: 10px;
  padding-bottom: 10px;
}

.sb-hero-stat {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
}

.sb-hero-value {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1;
}

.sb-hero-label {
  font-size: .62rem;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--sb-rail-dim);
}

.sb-hero-divider {
  width: 1px;
  height: 28px;
  background: var(--sb-rail-wash-md);
  margin: 0 8px;
}

/* ============================================================
   Content cards
   ============================================================ */
/* ============================================================
   Admin: league list badges and the destructive action (issue #91)

   The badges were written as a light-theme palette inline - a pale blue
   ground with a mid-blue label - which on the dark theme was a bright
   chip nobody had looked at. They use the same tint/ink pairing the rest
   of the app's status badges do, so "public" reads like every other
   affirmative state on the site rather than like a fifth colour.
   ============================================================ */
.al-badge {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 20px;
  font-size: .65rem;
  font-weight: 700;
  border: 1px solid transparent;
}

.al-badge-public  { background: var(--sb-ok-tint); color: var(--sb-ok); border-color: var(--sb-ok); }
.al-badge-private { background: var(--sb-surface-2); color: var(--sb-muted); border-color: var(--sb-border); }

/* Deleting a league is the one irreversible thing on this page, so it
   keeps the danger colour rather than becoming a neutral button. */
.al-btn-danger {
  padding: 3px 10px;
  font-size: .75rem;
  color: var(--sb-bad);
  background: var(--sb-surface);
  border: 1px solid var(--sb-bad);
}

.al-btn-danger:hover { background: var(--sb-bad); color: var(--sb-on-state); }

/* The admin dialogs carried their own drop shadow inline, as a literal
   black at twelve percent - tuned for a white page, invisible on a dark
   one. --sb-shadow-strong is the same shadow the rest of the app's
   raised surfaces use, and it is retuned per theme. */
.sb-admin-modal {
  border-radius: 16px;
  overflow: hidden;
  border: 1px solid var(--sb-border);
  box-shadow: 0 8px 32px var(--sb-shadow-strong);
}

/* ============================================================
   Sign-in dialog (issues 106, 113)

   The header was a slab of --sb-ink, the near-black the hero gradients
   use. It was drawn before the light theme became the default (104) and
   read as a different application pasted onto a white card: the wordmark
   and the tabs are the least important things in the dialog and carried
   the most weight in it. The band is gone - the card is one surface now,
   and the two tabs are a segmented control.
   ============================================================ */
.sb-auth-header {
  background: var(--sb-surface);
  color: var(--sb-text);
  padding: 20px 24px 0;
}

/* Issue 108: while a code is in flight the dialog drops its tabs, and the
   header has nothing left to give it a bottom edge. Same rule as the
   full-page card: one job on screen. Barely any bottom padding, because the
   pane below adds its own and the header is only a wordmark by then. */
.sb-auth-header-bare { padding-bottom: 6px; }

/* The same mark the rail and the top nav draw, at label size: Sport in
   the text colour, Bet in the accent. */
.sb-auth-wordmark {
  font-weight: 800;
  font-size: .82rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--sb-text);
}

.sb-auth-wordmark i { font-style: normal; color: var(--sb-accent); }

/* The track. A segmented control rather than underlined tabs because the
   two are a choice between equals, and because "which one am I on" was
   not answerable at a glance when the pair sat on a dark band. */
.sb-auth-tabs {
  display: flex;
  gap: 4px;
  margin-top: 16px;
  padding: 4px;
  border-radius: 12px;
  background: var(--sb-surface-2);
}

.sb-auth-tab {
  flex: 1 1 0;
  height: 36px;
  padding: 0;
  border: none;
  border-radius: 9px;
  background: none;
  font-size: .875rem;
  font-weight: 600;
  color: var(--sb-muted);
}

.sb-auth-tab:hover { color: var(--sb-text); }

/* The selected segment is marked by the accent TINT rather than by a
   lifted white thumb, which is the obvious drawing of this control and
   the one that breaks: --sb-surface is lighter than --sb-surface-2 in
   the light theme and darker in the dark one, so a thumb that lifts on
   paper sinks on ink. The tint separates by hue instead, which survives
   the flip - and --sb-text on it clears AA in both themes, which the
   accent as a text colour would not. */
.sb-auth-tab.active {
  background: var(--sb-accent-tint);
  box-shadow: 0 1px 3px var(--sb-shadow);
  color: var(--sb-text);
  font-weight: 700;
}


/* ============================================================
   Help page (issue #94)

   The screenshot frame used to be an inline <style> block in
   help.blade.php whose border gave --sb-border a hard-coded pale-grey
   fallback - a colour literal living in a Blade view, which is one of
   the literals CssTokenGuardTest's fourth budget counts. It is a plain
   token here instead, and the fallback is gone: --sb-border is defined
   in both themes, so there was never anything to fall back to.
   ============================================================ */
.help-screenshot {
  display: block;
  width: 100%;
  max-width: 480px;
  margin-top: 12px;
  border: 1px solid var(--sb-border);
  border-radius: 10px;
  box-shadow: 0 2px 8px var(--sb-shadow);
}

/* The match icon is a Material Icons glyph whose name comes from the
   format (a ball, a basketball), so it is sized here rather than with the
   inline style the football-only version carried. */
.help-stage-icon {
  font-size: 1rem;
  vertical-align: middle;
}

/* A qualification under a section's main sentence: the shape of the
   final, whether a half-filled table is accepted. */
.help-note {
  margin: 6px 0 0;
  font-size: .82rem;
  color: var(--sb-muted);
}

/* ============================================================
   Team crests (issue #79)

   A crest is a transparent PNG or SVG of somebody else's logo, and 43 of
   the 67 files carry dark ink: LDLC ASVEL is entirely near-black, Armani
   Olimpia Milan keeps its red roundel and loses its wordmark, and half the
   World Cup flags have a dark field. On the dark card they disappeared.

   So every crest is drawn on a white plate. It is white in both themes
   rather than flipping, because the plate is not decoration - it is the
   thing making the artwork legible, and a plate that went dark with the
   theme would take the legibility with it. In the light theme it is
   invisible against the card except for its hairline, which is the only
   part that flips.

   This rule is deliberately early in the file: the classes that size a
   crest come later and are meant to win, so .standing-flag keeps its
   circle and .at-flag its own border.

   The class is applied by resources/views/components/team-crest.blade.php
   and nowhere else by hand, except the two Alpine-driven crests in the
   prediction modal, whose src is computed in JavaScript.
   ============================================================ */
.sb-crest {
  box-sizing: border-box;
  padding: 2px;
  border: 1px solid var(--sb-crest-plate-line);
  border-radius: 6px;
  background: var(--sb-crest-plate);
  object-fit: contain;
}

/* Crests drawn at 20px or less, where a 2px plate eats too much of the
   artwork to leave it recognisable. */
.sb-crest--sm { padding: 1px; border-radius: 4px; }

.sb-card {
  background: var(--sb-card);
  border: 1px solid var(--sb-border);
  border-radius: 12px;
  padding: 16px;
  box-shadow: 0 1px 4px var(--sb-shadow);
  margin-bottom: 16px;
  overflow-x: auto;
}

.sb-card-title {
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--sb-muted);
  margin-bottom: 12px;
}

.sb-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 20px;
  border-radius: 999px;
  font-size: .875rem;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  transition: background .15s, color .15s, border-color .15s;
}

.sb-btn-primary {
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  border: none;
}

.sb-btn-primary:hover { background: var(--sb-accent-hover); color: var(--sb-on-accent); }

.sb-btn-secondary {
  background: transparent;
  color: var(--sb-accent);
  border: 2px solid var(--sb-accent);
  padding: 6px 18px;
}

.sb-btn-secondary:hover { background: var(--sb-accent); color: var(--sb-on-accent); }

/* ============================================================
   Cookie consent banner
   ============================================================ */
.sb-cookie-banner {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1030;
  background: var(--sb-rail-raised);
  color: var(--sb-on-rail);
  padding: 12px 20px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  font-size: .84rem;
  box-shadow: 0 -2px 12px var(--sb-shadow-strong);
}
@media (max-width: 991px) {
  .sb-cookie-banner { bottom: 56px; }
}
.sb-cookie-text a { color: var(--sb-rail-accent); text-decoration: underline; }
.sb-cookie-actions { display: flex; gap: 8px; flex-shrink: 0; }
.sb-cookie-btn {
  padding: 6px 16px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-size: .82rem;
  font-weight: 600;
  line-height: 1.4;
}
.sb-cookie-btn-primary { background: var(--sb-accent); color: var(--sb-on-accent); }
.sb-cookie-btn-primary:hover { background: var(--sb-accent-hover); }
.sb-cookie-btn-secondary { background: transparent; color: var(--sb-rail-dim); border: 1px solid var(--sb-rail-line); }
.sb-cookie-btn-secondary:hover { background: var(--sb-rail-wash-lg); color: var(--sb-on-rail); }

/* ============================================================
   Bottom tab bar (mobile)
   ============================================================ */
.sb-bottom-nav {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1025;
  background: var(--sb-rail);
  border-top: 1px solid var(--sb-rail-line);
  display: flex;
  padding: 6px 0 env(safe-area-inset-bottom, 6px);
}

.sb-tab {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  color: var(--sb-rail-dim);
  text-decoration: none;
  padding: 4px 0;
  transition: color .15s;
}

.sb-tab:hover { color: var(--sb-on-rail); }
.sb-tab.active { color: var(--sb-rail-accent); }

.sb-tab-label { font-size: .62rem; }
button.sb-tab { background: none; border: none; outline: none; }
button.sb-tab::after { display: none; }
.sb-bottom-nav .dropup { flex: 1; display: flex; }


.sb-auth-body {
  width: 100%;
  max-width: 440px;
  margin: 0 auto;
}


.sb-auth-divider {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 20px;
  color: var(--sb-muted);
  font-size: .8rem;
}

.sb-auth-divider::before,
.sb-auth-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--sb-border);
}

.sb-auth-input {
  display: block;
  width: 100%;
  padding: 14px 16px;
  margin-bottom: 12px;
  background: var(--sb-surface-2);
  border: 1.5px solid var(--sb-border);
  border-radius: 12px;
  font-size: .95rem;
  color: var(--sb-text);
  outline: none;
  transition: border-color .15s, box-shadow .15s;
  font-family: inherit;
}

.sb-auth-input::placeholder { color: var(--sb-dim); }

.sb-auth-input:focus {
  border-color: var(--sb-accent);
  box-shadow: 0 0 0 3px var(--sb-accent-tint);
}

.sb-auth-input-error { border-color: var(--sb-bad) !important; }


.sb-auth-btn-primary {
  display: block;
  width: 100%;
  padding: 14px;
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  border: none;
  border-radius: 999px;
  font-size: 1rem;
  font-weight: 700;
  cursor: pointer;
  transition: background .15s;
  margin-bottom: 12px;
  font-family: inherit;
  text-align: center;
}

.sb-auth-btn-primary:hover { background: var(--sb-accent-hover); }


/* Alert overrides in auth context */
.sb-auth-body .alert { border-radius: 10px; font-size: .875rem; }

/* ------------------------------------------------------------
   Auth — the two steps of the sign-in card (issue #75)

   The card shows the code step OR the method step, never both.
   Everything below belongs to one of them; nothing is shared with
   the register form, which still has its own single-step layout.
   ------------------------------------------------------------ */

/* Alpine is deferred, so an x-show element is painted once before it
   is evaluated. Only the countdown states carry x-cloak today. */
[x-cloak] { display: none !important; }

/* The address and the mark that says where it went, on one row. It was a
   centred badge over a centred lead over a green box repeating the lead,
   which is three ways of saying one thing (issue 114). */
.sb-auth-sent {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 16px;
}

.sb-auth-lead {
  margin: 0;
  font-size: .85rem;
  line-height: 1.45;
  color: var(--sb-muted);
}

.sb-auth-sent-to { color: var(--sb-text); font-weight: 600; }

.sb-auth-back {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  background: none;
  border: none;
  padding: 4px 6px 4px 0;
  margin-bottom: 8px;
  font-family: inherit;
  font-size: .85rem;
  font-weight: 500;
  color: var(--sb-muted);
  cursor: pointer;
}

.sb-auth-back:hover { color: var(--sb-text); }

.sb-auth-icon-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: 999px;
  background: var(--sb-accent-tint);
  color: var(--sb-accent);
  font-size: 1rem;
}

.sb-auth-hint {
  margin: 0 0 14px;
  font-size: .78rem;
  line-height: 1.45;
  color: var(--sb-muted);
}

/* The countdown, and the only acknowledgement a resend gets: a second code
   otherwise redraws the same screen with the same sentence on it. */
.sb-auth-meta {
  margin: 0 0 14px;
  font-size: .78rem;
  text-align: center;
  color: var(--sb-muted);
}

.sb-auth-meta-ok { color: var(--sb-ok); font-weight: 600; }

/* The field is the screen. Its label is for screen readers only - eight
   dots and a maxlength say the rest, and a heading over the sole input of
   a card about that input is a line nobody needs. */
.sb-auth-code-input {
  margin-bottom: 8px;
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: .3em;
  text-indent: .3em;
  text-align: center;
}

/* One sentence where there used to be a panel holding three. The resend
   sits inside it as a link, so the sentence reads as a sentence. */
.sb-auth-helper {
  margin: 0;
  font-size: .78rem;
  line-height: 1.5;
  color: var(--sb-muted);
}

.sb-auth-resend-form { display: inline; }

.sb-auth-resend-btn {
  background: none;
  border: none;
  padding: 0;
  font-family: inherit;
  font-size: inherit;
  font-weight: 600;
  color: var(--sb-accent);
  cursor: pointer;
}

.sb-auth-resend-btn:hover:not(:disabled) { color: var(--sb-accent-hover); text-decoration: underline; }
.sb-auth-resend-btn:disabled { color: var(--sb-dim); cursor: not-allowed; }


/* ============================================================
   Charity card
   ============================================================ */
.sb-charity-card {
  background: var(--sb-warn-tint);
  border: 1px solid var(--sb-warn);
  border-radius: 16px;
  padding: 20px 22px;
  margin-bottom: 16px;
}
.sb-charity-card-inner {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.sb-charity-icon {
  font-size: 2rem;
  line-height: 1;
  flex-shrink: 0;
}
.sb-charity-body { flex: 1; min-width: 0; }
.sb-charity-title {
  font-size: 1rem;
  font-weight: 700;
  color: var(--sb-warn);
  margin: 0 0 6px;
}
.sb-charity-text {
  font-size: .84rem;
  color: var(--sb-warn);
  margin: 0 0 8px;
  line-height: 1.55;
}
.sb-charity-link {
  font-size: .82rem;
  font-weight: 600;
  color: var(--sb-warn);
  text-decoration: none;
}
.sb-charity-link:hover { text-decoration: underline; color: var(--sb-warn); }
.sb-charity-link-inline { color: var(--sb-warn); font-weight: 600; }
.sb-charity-stat-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  background: var(--sb-warn);
  border-radius: 12px;
  padding: 10px 16px;
  min-width: 90px;
}
.sb-charity-amount {
  font-size: 1.35rem;
  font-weight: 800;
  color: var(--sb-on-accent);
  line-height: 1.2;
}
.sb-charity-stat-label {
  font-size: .68rem;
  font-weight: 600;
  color: var(--sb-on-accent);
  text-align: center;
  margin-top: 2px;
}
@media (max-width: 480px) {
  .sb-charity-card-inner { flex-wrap: wrap; }
  .sb-charity-stat-box { width: 100%; flex-direction: row; gap: 8px; justify-content: center; padding: 8px 12px; }
}

/* ============================================================
   Public leaderboard
   ============================================================ */
.lb-pub-top { background: var(--sb-warn-tint); }
.lb-pub-medal { font-size: 1.2rem; }
.lb-pub-rank { font-size: .85rem; color: var(--sb-muted); font-weight: 600; }

/* ============================================================
   Charity page
   ============================================================ */
.ch-hero {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 24px;
  align-items: center;
  background: linear-gradient(135deg, var(--sb-ink) 0%, var(--sb-accent) 100%);
  border-radius: 16px;
  padding: 32px 28px;
  color: var(--sb-on-ink);
}
.ch-hero-title {
  font-size: clamp(1.5rem, 4vw, 2.2rem);
  font-weight: 800;
  margin: 0 0 10px;
}
.ch-hero-sub {
  font-size: .93rem;
  opacity: .9;
  line-height: 1.65;
  margin: 0 0 20px;
}
.ch-stat-row {
  display: flex;
  gap: 20px;
  flex-wrap: wrap;
  margin-bottom: 20px;
}
.ch-stat {
  display: flex;
  flex-direction: column;
}
.ch-stat-value {
  font-size: 1.6rem;
  font-weight: 800;
  line-height: 1.1;
  color: var(--sb-warn);
}
.ch-stat-label {
  font-size: .72rem;
  opacity: .75;
  margin-top: 2px;
}
.ch-cta {
  display: inline-block;
  background: var(--sb-warn);
  color: var(--sb-on-warn);
  font-weight: 700;
  font-size: .88rem;
  padding: 9px 20px;
  border-radius: 8px;
  text-decoration: none;
}
.ch-cta:hover { background: var(--sb-warn-hover); color: var(--sb-on-warn); }
.ch-hero-img-wrap { flex-shrink: 0; }
.ch-hero-img {
  width: 160px;
  border-radius: 12px;
  object-fit: cover;
  display: block;
}
@media (max-width: 560px) {
  .ch-hero { grid-template-columns: 1fr; }
  .ch-hero-img-wrap { display: none; }
}

.ch-payments-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 16px;
}
.ch-payment-card {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--sb-border);
  border-radius: 10px;
  overflow: hidden;
  text-decoration: none;
  transition: box-shadow .15s, transform .15s;
}
.ch-payment-card:hover {
  box-shadow: 0 4px 16px var(--sb-shadow);
  transform: translateY(-2px);
}
.ch-payment-label {
  font-size: .78rem;
  font-weight: 600;
  color: var(--sb-text);
  padding: 8px 10px;
  background: var(--sb-bg);
  border-bottom: 1px solid var(--sb-border);
}
.ch-payment-img {
  width: 100%;
  height: 140px;
  object-fit: cover;
  display: block;
}

/* ============================================================
   Guest hero banner  (orange gradient like Pickdee)
   ============================================================ */
.sb-hero-banner {
  background: linear-gradient(135deg, var(--sb-ink) 0%, var(--sb-accent) 100%);
  border-radius: 16px;
  padding: 36px 28px;
  color: var(--sb-on-ink);
  margin-bottom: 16px;
}

.sb-hero-banner h2 {
  font-size: clamp(1.6rem, 5vw, 2.4rem);
  font-weight: 800;
  line-height: 1.15;
  margin-bottom: 12px;
}

.sb-hero-banner p {
  font-size: 1rem;
  font-weight: 500;
  opacity: .92;
  margin-bottom: 16px;
}

.sb-hero-banner ol {
  font-size: .9rem;
  padding-left: 18px;
  opacity: .9;
  margin: 0;
  line-height: 1.8;
}

/* ============================================================
   Upcoming games list
   ============================================================ */
.upcoming-list {
  display: flex;
  flex-direction: column;
}

.upcoming-row {
  display: grid;
  grid-template-columns: 52px 1fr auto 1fr auto;
  align-items: center;
  gap: 6px;
  padding: 7px 4px;
  border-bottom: 1px solid var(--sb-border);
  text-decoration: none;
  color: var(--sb-text);
  border-radius: 6px;
  transition: background .15s;
}
.upcoming-row:last-child { border-bottom: none; }
.upcoming-row:hover { background: var(--sb-surface-2); color: var(--sb-text); }
.upcoming-row-pred { cursor: pointer; }
.upcoming-row-pred:hover { background: var(--sb-accent-tint); }

.upcoming-date {
  display: flex;
  flex-direction: column;
  font-size: .72rem;
  color: var(--sb-muted);
  white-space: nowrap;
  line-height: 1.3;
}
.upcoming-time {
  font-size: .72rem;
  font-weight: 600;
  color: var(--sb-muted);
}

.upcoming-team {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}
.upcoming-home { justify-content: flex-end; }
.upcoming-away { justify-content: flex-start; }

.upcoming-name {
  font-size: .82rem;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Also used by partials/fixture-deck.blade.php, not just the upcoming list
   its name implies. */
.upcoming-flag {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  object-fit: contain;
}

.upcoming-scores {
  display: flex;
  align-items: center;
  gap: 3px;
  white-space: nowrap;
  justify-content: center;
  min-width: 72px;
}

.usc-actual {
  font-size: .82rem;
  font-weight: 700;
  color: var(--sb-text);
}

.usc-sep {
  font-size: .72rem;
  color: var(--sb-muted);
}

.usc-pred {
  font-size: .78rem;
  color: var(--sb-muted);
}

.usc-pred-only {
  font-size: .82rem;
  font-weight: 700;
  color: var(--sb-accent);
}

.upcoming-pts {
  font-size: .78rem;
  font-weight: 700;
  min-width: 36px;
  text-align: right;
  flex-shrink: 0;
}
.upt-scored { color: var(--sb-accent); }
.upt-empty  { color: transparent; }
.upcoming-pts { display: flex; flex-direction: column; align-items: flex-end; }
.upt-streak { font-size: .62rem; font-weight: 700; color: var(--sb-accent); line-height: 1; }

.upcoming-all-link {
  font-size: .75rem;
  font-weight: 500;
  color: var(--sb-accent);
  text-decoration: none;
}
.upcoming-all-link:hover { text-decoration: underline; }

/* (lb-name-col / lb-sub removed — breakdown moved to popover) */

.lb-total {
  font-size: .9rem;
  font-weight: 700;
  color: var(--sb-text);
  width: 72px;
  text-align: right;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.lb-me-total { color: var(--sb-accent); }

/* ============================================================
   Match cards
   ============================================================ */
.match-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 12px;
}

.match-card {
  background: var(--sb-card);
  border: 1px solid var(--sb-border);
  border-radius: 12px;
  overflow: hidden;
  transition: box-shadow .15s, transform .15s;
}

.match-card:hover {
  box-shadow: 0 4px 16px var(--sb-shadow);
  transform: translateY(-1px);
}

.match-card-header {
  padding: 7px 12px;
  background: var(--sb-surface-2);
  border-bottom: 1px solid var(--sb-border);
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: .72rem;
  color: var(--sb-muted);
  font-weight: 500;
  letter-spacing: .2px;
}

.match-card-body {
  display: flex;
  align-items: center;
  padding: 14px 12px;
  gap: 8px;
}

.match-team {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 7px;
  min-width: 0;
}

.match-home { flex-direction: row; }
.match-away { flex-direction: row-reverse; }

.match-flag {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  flex-shrink: 0;
  object-fit: cover;
  border: 1px solid var(--sb-border);
}

.match-team-name {
  font-size: .8rem;
  font-weight: 600;
  color: var(--sb-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.match-away .match-team-name { text-align: right; }

.match-vs {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 3px;
  flex-shrink: 0;
  min-width: 72px;
}

.match-score-actual {
  font-size: 1.25rem;
  font-weight: 800;
  color: var(--sb-text);
  letter-spacing: 1px;
  line-height: 1;
}

.match-score-actual.match-no-score {
  font-size: .85rem;
  color: var(--sb-muted);
  font-weight: 500;
}

.match-score-pred {
  font-size: .7rem;
  color: var(--sb-muted);
  background: var(--sb-surface-2);
  border-radius: 4px;
  padding: 1px 6px;
}

.match-card-footer {
  padding: 7px 12px;
  border-top: 1px solid var(--sb-border);
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.match-pts {
  font-size: .75rem;
  font-weight: 700;
  color: var(--sb-accent);
  background: var(--sb-accent-tint);
  border-radius: 999px;
  padding: 2px 10px;
}

.match-pts-popover {
  cursor: pointer;
  text-decoration: none;
}

/* ============================================================
   Leaderboard rows
   ============================================================ */
.lb-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 0 6px;
  border-bottom: 2px solid var(--sb-border);
  margin-bottom: 2px;
  font-size: .65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--sb-muted);
}
/* The header and a data row are two separate flex rows that have to line up
   column for column, and .lb-name is the only flexible item in either - so
   everything to its right is packed against the right edge and a track that
   sizes to its own content throws the whole group out of step with the other
   row. .lb-header-total and .lb-total were exactly that: `min-width` plus a
   .65rem uppercase label against a .9rem figure, sizing to two different
   widths and shifting every column left of them. Every track below is a
   fixed width, identical in both rows. */
.lb-header-rank   { width: 26px; text-align: center; flex-shrink: 0; }
.lb-header-name   { flex: 1; }
.lb-header-sub    { width: 56px; flex-shrink: 0; }
.lb-header-sub-bingo { color: var(--sb-muted); }
.lb-header-total  { width: 72px; text-align: right; flex-shrink: 0; font-weight: 700; }
.lb-header-chevron { width: 12px; flex-shrink: 0; }

/* 56px clears the widest figure these columns carry ("4,800.0" at .72rem is
   about 45px). At the old 38px every four-digit score overflowed its own
   track and bled into its neighbours, which is the other half of why the
   columns read as crooked. */
.lb-sub-col {
  font-size: .72rem;
  font-weight: 500;
  color: var(--sb-muted);
  flex-shrink: 0;
  width: 56px;
  font-variant-numeric: tabular-nums;
}

/* .lb-header-sub / .lb-sub-col carry Bootstrap's d-none/d-md-block utilities
   (!important), so centering their icon/text must win at the same md
   breakpoint with matching specificity - plain text-align/flex here gets
   silently overridden by d-md-block's `display: block !important`. */
@media (min-width: 768px) {
  .lb-header-sub.d-md-block,
  .lb-sub-col.d-md-block {
    display: flex !important;
    align-items: center;
    /* Right, not centre: these columns are numbers, so their last digits
       should line up down the column and under the header's icon. Centring
       lines up the middles instead, which drifts with the value's width. */
    justify-content: flex-end;
  }
}
.lb-sub-bingo  { color: var(--sb-text); font-weight: 700; }
.lb-sub-streak { color: var(--sb-accent); font-weight: 700; }

.lb-separator {
  text-align: center;
  letter-spacing: 4px;
  color: var(--sb-muted);
  font-size: .65rem;
  padding: 3px 0;
  border-bottom: 1px solid var(--sb-border);
  opacity: .5;
}
.lb-show-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  background: none;
  border: none;
  border-top: 1px solid var(--sb-border);
  padding: 8px 0 0;
  margin-top: 4px;
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--sb-muted);
  cursor: pointer;
}
.lb-show-more:hover { color: var(--sb-accent); }

.lb-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 4px 0;
  border-bottom: 1px solid var(--sb-border);
}
.lb-entry:nth-child(even) > .lb-row:not(.lb-me-row) { background: var(--sb-surface-2); }
.lb-entry:last-child .lb-row { border-bottom: none; }

.lb-me-row {
  background: var(--sb-accent-tint) !important;
  border-radius: 6px;
  padding: 5px 8px;
  margin: 0 -8px;
  border-left: 3px solid var(--sb-accent);
}

.lb-rank {
  width: 26px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .75rem;
  font-weight: 700;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}

.lb-rank-1 { color: var(--sb-text); }
.lb-rank-2 { color: var(--sb-text); }
.lb-rank-3 { color: var(--sb-text); }
.lb-rank-n { color: var(--sb-muted); font-weight: 500; }

.lb-name {
  flex: 1;
  font-size: .875rem;
  font-weight: 500;
  color: var(--sb-text);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.lb-name.lb-me-name { font-weight: 700; color: var(--sb-accent); }

.lb-name-btn {
  cursor: pointer;
  border-bottom: 1px dotted var(--sb-muted);
}
.lb-name-btn:hover { color: var(--sb-accent); }

.lb-bar-wrap {
  flex: 2;
  height: 5px;
  background: var(--sb-surface-2);
  border-radius: 3px;
  overflow: hidden;
}

.lb-bar {
  height: 100%;
  background: var(--sb-accent);
  border-radius: 3px;
  transition: width .4s ease;
}

.lb-points {
  font-size: .875rem;
  font-weight: 700;
  color: var(--sb-text);
  min-width: 44px;
  text-align: right;
}

/* ============================================================
   Prediction odds toggle
   ============================================================ */
.pred-odds-toggle {
  background: none;
  border: none;
  font-size: .7rem;
  color: var(--sb-muted);
  padding: 2px 6px 2px 2px;
  cursor: pointer;
  border-radius: 4px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  letter-spacing: .02em;
  transition: color .15s;
}

.pred-odds-toggle:hover,
.pred-odds-toggle.active { color: var(--sb-accent); }

.pred-odds-panel {
  display: flex;
  justify-content: space-around;
  padding: 6px 0 8px;
  border-top: 1px solid var(--sb-border);
  margin-top: 4px;
  gap: 4px;
}

.pred-odds-col {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  flex: 1;
  text-align: center;
}

.pred-odds-label {
  font-size: .68rem;
  color: var(--sb-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 90px;
}

.pred-odds-pts {
  font-size: .8rem;
  font-weight: 700;
  color: var(--sb-accent);
}

/* ============================================================
   Standings prediction grid
   ============================================================ */
.stnl-list {
  display: flex;
  flex-direction: column;
}

.stnl-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 6px 2px;
  gap: 8px;
  border-bottom: 1px solid var(--sb-border);
}

.stnl-row:last-child { border-bottom: none; }

.stnl-team {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  flex: 1;
}

.standing-flag {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}

.stnl-name {
  font-size: .8rem;
  font-weight: 600;
  color: var(--sb-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.stnl-preds {
  display: flex;
  gap: 6px;
  flex-shrink: 0;
}

.standing-pos-badge {
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .72rem;
  font-weight: 700;
  color: #fff; /* allow-colour-literal: fixed medal identity, not a themed surface */
}

.pos-1 { background: #D4AF37; } /* allow-colour-literal: fixed medal identity, not a themed surface */
.pos-2 { background: #94a3b8; } /* allow-colour-literal: fixed medal identity, not a themed surface */
.pos-3 { background: #CD7F32; } /* allow-colour-literal: fixed medal identity, not a themed surface */
.pos-4 { background: #7B4F2E; color: #fddcb5; } /* allow-colour-literal: fixed medal identity, not a themed surface */
.pos-zero { opacity: .3; }

/* ============================================================
   Profile page
   ============================================================ */
.profile-avatar {
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  font-size: 1.3rem;
  font-weight: 800;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  letter-spacing: -.5px;
}

.profile-eye {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  color: var(--sb-dim);
  cursor: pointer;
  font-size: 1rem;
  padding: 4px;
  line-height: 1;
  z-index: 10;
}
.profile-eye:hover { color: var(--sb-muted); }

/* keep for any other pages that still use the old pattern */
.profile-field {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 7px 0;
  border-bottom: 1px solid var(--sb-border);
}
.profile-field:last-of-type { border-bottom: none; }

.profile-label {
  width: 130px;
  flex-shrink: 0;
  font-size: .82rem;
  color: var(--sb-muted);
  font-weight: 500;
}

.profile-input { flex: 1; min-width: 0; }

/* ============================================================
   Prediction results — grouped by group & matchday
   ============================================================ */
.pred-page {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.pred-event-header {
  font-size: .8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--sb-accent);
  border-bottom: 2px solid var(--sb-accent);
  padding-bottom: 4px;
  margin-bottom: 10px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  cursor: pointer;
  user-select: none;
}
.pred-event-chevron {
  font-size: .75rem;
  transition: transform .2s;
  opacity: .6;
}
.pred-event-header.collapsed .pred-event-chevron {
  transform: rotate(-90deg);
}
.pred-event-header.collapsed {
  margin-bottom: 0;
  border-bottom-color: var(--sb-border);
}

.pred-event-groups {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

/* Mobile: one card per row */
@media (max-width: 599px) {
  .pred-event-groups {
    grid-template-columns: 1fr;
  }
}

/* Mobile portrait: hide team names, flags only */
@media (max-width: 767px) and (orientation: portrait) {
  .pred-team-name { display: none; }
}

/* kept for backwards compat if used elsewhere */
.pred-group-header {
  font-size: .72rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--sb-accent);
  border-bottom: 2px solid var(--sb-accent);
  padding-bottom: 4px;
  margin-bottom: 10px;
}

.pred-group-cards {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.pred-day-card {
  background: var(--sb-card);
  border: 1px solid var(--sb-border);
  border-radius: 8px;
  padding: 10px 12px;
}

.pred-day-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: .78rem;
  font-weight: 600;
  color: var(--sb-muted);
  padding-bottom: 7px;
  margin-bottom: 5px;
  border-bottom: 1px solid var(--sb-border);
}

.pred-day-date {
  font-weight: 400;
}

.pred-game {
  display: grid;
  grid-template-columns: 52px 1fr auto 1fr 36px;
  align-items: center;
  gap: 6px;
  padding: 7px 0;
}
.pred-game > span:last-child {
  display: flex;
  justify-content: flex-end;
}

.pred-game + .pred-game {
  border-top: 1px solid var(--sb-border);
}

.modal .pred-game {
  grid-template-columns: 1fr auto 1fr;
}

.admin-result-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 6px;
  padding: 7px 0;
}
.admin-result-row + .admin-result-row {
  border-top: 1px solid var(--sb-border);
}

.pred-team-home {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
  min-width: 0;
}

.pred-team-away {
  display: flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
}

.pred-team-name {
  font-size: .85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 140px;
}

.pred-flag {
  width: 26px;
  height: 26px;
  flex-shrink: 0;
  object-fit: contain;
}

.pred-scores {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 0 8px;
}

.pred-scores-inputs {
  display: flex;
  align-items: center;
  gap: 3px;
}

.pred-time {
  font-size: .8rem;
  font-weight: 600;
  color: var(--sb-muted);
  letter-spacing: .3px;
  line-height: 1;
}

.pred-score {
  width: 30px !important;
  text-align: center;
  padding: 2px 2px !important;
  font-size: .95rem;
  font-weight: 700;
  line-height: 1.3;
  border-radius: 8px;
  font-variant-numeric: tabular-nums;
  color: var(--sb-text);
  border-color: var(--sb-border);
  background: var(--sb-surface-2);
}

/* ── Admin result entry: the saved/pending/failed states (issue #91) ──
   These were an inline green border in the Blade and four more colours
   assigned as hex strings from JavaScript. A colour set from script cannot
   be a token - it is a string, and it outlives the theme it was written
   under, so a green border entered under the dark theme stayed that exact
   green in the light one. The state is a class now and the colour lives
   here, which also means the two places that set it (the server rendering
   an already-scored game, and the autosave reporting back) cannot drift
   to different greens. */
.pred-score--saved   { border-color: var(--sb-ok); }
.pred-score--partial { border-color: var(--sb-warn); }
.pred-score--failed  { border-color: var(--sb-bad); }
.pred-score--cleared { border-color: var(--sb-border); }

.pred-sep {
  font-size: .95rem;
  font-weight: 700;
  color: var(--sb-muted);
  line-height: 1;
}

.pred-game-locked {
  opacity: .5;
}
.pred-game-finished {
  /* finished games shown at full opacity */
}
.pred-actual {
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: .5px;
  color: var(--sb-text);
  line-height: 1;
}
.pred-pts {
  min-width: 36px;
  text-align: right;
  font-size: .78rem;
  font-weight: 700;
  padding-left: 6px;
  white-space: nowrap;
  cursor: default;
}
.pred-pts-scored {
  color: var(--sb-accent);
  cursor: pointer;
}
.pred-pts-zero {
  color: var(--sb-muted);
}
.pred-pts-hidden {
  visibility: hidden;
}
.pred-score-locked {
  background: var(--sb-surface-2) !important;
  color: var(--sb-muted) !important;
  cursor: not-allowed;
}
/* A basketball score runs to three digits and does not fit the football box. */
.pred-score-wide {
  width: 42px !important;
}
.pred-score-error {
  border-color: var(--sb-bad) !important;
  box-shadow: 0 0 0 2px var(--sb-bad-tint) !important;
}
.pred-score-msg {
  font-size: .72rem;
  font-weight: 600;
  color: var(--sb-bad);
  text-align: center;
  padding: 0 6px 6px;
}

.pred-pw-check {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  padding: 2px 3px;
  color: var(--sb-dim);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  flex-shrink: 0;
  transition: color .15s;
}

.pred-pw-check:hover {
  color: var(--sb-accent);
}

.pred-pw-check.active {
  color: var(--sb-accent);
}

/* Also used by partials/fixture-deck.blade.php, not just the upcoming list
   its name implies. */
.upcoming-pw-badge {
  font-size: .65rem;
  color: var(--sb-accent);
  opacity: .75;
  flex-shrink: 0;
}


/* ============================================================
   Prediction results cards (old — kept for main page widget)
   ============================================================ */
.pr-cards-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: center;
}

.pr-card {
  width: 17rem;
}

.pr-flag {
  height: 40px;
}

.pr-team-name {
  font-size: .72rem;
  color: var(--sb-muted);
  margin-top: 3px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
  padding: 0 4px;
}

.pr-date {
  font-size: .7rem;
  color: var(--sb-muted);
  margin-bottom: 4px;
}

.pr-colon {
  font-size: 1.1rem;
  font-weight: 700;
  color: var(--sb-accent);
  line-height: 1;
}

/* ============================================================
   Prediction standings — 2-column group grid
   ============================================================ */
.ps-groups-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

@media (max-width: 600px) {
  .ps-groups-grid { grid-template-columns: 1fr; }
}

/* One undivided table (a league season has no groups to split across cards) */
.ps-groups-grid--single { grid-template-columns: 1fr; }

.ps-group-card {
  border: 1px solid var(--sb-border);
  border-radius: 8px;
  overflow: hidden;
}

.ps-group-title {
  font-size: .7rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--sb-accent);
  background: var(--sb-accent-tint);
  padding: 5px 10px;
}

.ps-group-row {
  display: grid;
  /* team | position | one column per bracket stage the format plays | final */
  grid-template-columns: minmax(0, 1fr) 38px repeat(var(--ps-stage-count, 4), 34px) 38px;
  gap: 2px;
  align-items: center;
  padding: 3px 6px;
  font-size: .78rem;
}

.ps-group-header-row {
  font-size: .65rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--sb-muted);
  border-bottom: 1px solid var(--sb-border);
  padding-bottom: 4px;
  margin-bottom: 1px;
}

.prediction-row {
  border-bottom: 1px solid var(--sb-border);
  transition: background .1s;
}

.prediction-row:last-child { border-bottom: none; }

.prediction-row:hover { background: var(--sb-accent-tint); }

.ps-group-row > *:first-child {
  min-width: 0;
}

.ps-team-name {
  font-size: .78rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex: 1;
  min-width: 0;
}

.ps-legend {
  font-size: .78rem;
  color: var(--sb-muted);
  max-width: 480px;
}
.ps-legend p { margin-bottom: 2px; }

.ps-input {
  width: 38px;
  height: 28px;
  text-align: center;
  border: 1px solid var(--sb-border);
  border-radius: 6px;
  font-size: .8rem;
  color: var(--sb-text);
  background: var(--sb-surface-2);
  padding: 0;
  -moz-appearance: textfield;
}
.ps-input::-webkit-outer-spin-button,
.ps-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ps-input:focus {
  outline: none;
  border-color: var(--sb-accent);
  box-shadow: 0 0 0 2px var(--sb-accent-tint);
}
.ps-input:disabled { background: var(--sb-surface); color: var(--sb-muted); }
.ps-input-ok    { border-color: var(--sb-ok) !important; box-shadow: 0 0 0 2px var(--sb-ok-tint) !important; }
.ps-input-error { border-color: var(--sb-bad) !important; box-shadow: 0 0 0 2px var(--sb-bad-tint) !important; }

/* ---- Standings ladder (issue 139) --------------------------------------
   Only formats whose positions run across the whole table draw this; a
   football group card keeps the 1-4 number box and the grid above. */

.ps-group-row--ladder {
  /* rank | team | arrows | one column per stage | final */
  grid-template-columns: 30px minmax(0, 1fr) 46px repeat(var(--ps-stage-count, 4), 34px) 38px;
}

.prediction-row[draggable="true"] { cursor: grab; }
.prediction-row.ps-dragging { opacity: .4; }

.prediction-row.ps-drop-target {
  box-shadow: inset 0 -2px 0 var(--sb-accent);
  background: var(--sb-accent-tint);
}

.ps-rank-cell {
  display: flex;
  align-items: center;
  gap: 3px;
  min-width: 0;
}

.ps-rank {
  font-weight: 700;
  font-size: .82rem;
  font-variant-numeric: tabular-nums;
  color: var(--sb-accent);
  min-width: 15px;
  text-align: right;
}

.ps-grip {
  color: var(--sb-muted);
  font-size: .7rem;
  line-height: 1;
}

.ps-nudge { display: flex; gap: 2px; justify-content: center; }

.ps-nudge-btn {
  width: 21px;
  height: 24px;
  padding: 0;
  font-size: .5rem;
  line-height: 1;
  border: 1px solid var(--sb-border);
  border-radius: 5px;
  background: var(--sb-surface-2);
  color: var(--sb-text);
}
.ps-nudge-btn:hover:not(:disabled) { border-color: var(--sb-accent); color: var(--sb-accent); }
.ps-nudge-btn:disabled { opacity: .35; cursor: default; }
.ps-nudge-btn:focus-visible {
  outline: none;
  border-color: var(--sb-accent);
  box-shadow: 0 0 0 2px var(--sb-accent-tint);
}

/* The saved order and the drawn order disagree — see psCommit()'s fail path. */
.ps-ladder-error { box-shadow: 0 0 0 2px var(--sb-bad-tint); border-color: var(--sb-bad); }

@media (max-width: 420px) {
  .ps-group-row--ladder { grid-template-columns: 24px minmax(0, 1fr) 46px repeat(var(--ps-stage-count, 4), 28px) 32px; }
  .ps-grip { display: none; }
}

/* ============================================================
   Rules page
   ============================================================ */
.sb-card-icon {
  color: var(--sb-accent);
  font-size: 1.1em;
  vertical-align: -0.1em;
  margin-right: 4px;
}
.rules-page {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.rules-section {
  font-size: .88rem;
  color: var(--sb-text);
  line-height: 1.6;
}
.rules-section p { margin-bottom: .5rem; }

.rules-stage {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  margin: 12px 0;
}

.rules-stage-badge {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  font-size: .78rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-top: 2px;
}

.rules-stage strong { display: block; margin-bottom: 4px; }

.rules-link {
  color: var(--sb-accent);
  font-weight: 600;
  text-decoration: none;
}
.rules-link:hover { text-decoration: underline; }

.rules-list {
  padding-left: 1.2rem;
  margin: 6px 0;
  font-size: .85rem;
  line-height: 1.7;
}

.rules-formula {
  margin-top: 10px;
  padding: 8px 14px;
  background: var(--sb-surface-2);
  border-left: 3px solid var(--sb-accent);
  border-radius: 4px;
  font-size: .85rem;
  font-weight: 600;
  color: var(--sb-accent);
}

.rules-scoring-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 10px;
  margin-bottom: 16px;
}

.rules-scoring-item {
  background: var(--sb-surface-2);
  border: 1px solid var(--sb-border);
  border-radius: 8px;
  padding: 12px;
  text-align: center;
}

.rules-scoring-pts {
  font-size: 1.2rem;
  font-weight: 800;
  color: var(--sb-accent);
}

.rules-scoring-label {
  font-size: .75rem;
  color: var(--sb-muted);
  margin-top: 4px;
}

.rules-table-title {
  font-size: .8rem;
  font-weight: 600;
  color: var(--sb-accent);
  margin-bottom: 8px;
}

.rules-table {
  width: 100%;
  border-collapse: collapse;
  font-size: .82rem;
}

.rules-table th,
.rules-table td {
  text-align: center;
  padding: 6px 8px;
  border: 1px solid var(--sb-border);
}

.rules-table thead th {
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  font-weight: 600;
}

.rules-table tbody th {
  background: var(--sb-surface-2);
  font-weight: 600;
  color: var(--sb-accent);
}

.rules-pts-pos  { background: var(--sb-accent-tint); color: var(--sb-accent); font-weight: 700; }
.rules-pts-zero { color: var(--sb-muted); }

.rules-table-caption {
  font-size: .72rem;
  color: var(--sb-muted);
  margin-top: 4px;
}

/* ============================================================
   Admin dashboard tiles
   ============================================================ */
.admin-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 20px 10px;
  background: var(--sb-card);
  border: 1px solid var(--sb-border);
  border-radius: 10px;
  text-decoration: none;
  color: var(--sb-text);
  transition: box-shadow .15s, transform .15s;
}
.admin-tile:hover {
  box-shadow: 0 4px 16px var(--sb-shadow);
  transform: translateY(-2px);
  color: var(--sb-accent);
}
.admin-tile-icon {
  font-size: 1.8rem;
  color: var(--sb-accent);
}
.admin-tile-label {
  font-size: .78rem;
  font-weight: 600;
  text-align: center;
}

/* ============================================================
   Admin users table
   ============================================================ */
.au-table { font-size: .875rem; }
.au-col-id      { width: 40px; }
.au-col-auth    { width: 64px; }
.au-col-admin   { width: 140px; }
.au-col-actions { width: 48px; }

.au-id       { font-size: .75rem; color: var(--sb-muted); }
.au-email    { font-size: .82rem; color: var(--sb-muted); }
.au-username { font-weight: 600; font-size: .875rem; }
.au-fullname { font-size: .75rem; color: var(--sb-muted); }

.au-avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  font-size: .75rem; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}

.au-auth-badge   { font-size: .9rem; }
.au-auth-google  { color: #4285f4; } /* allow-colour-literal: Google's brand blue, not a themed role */
.au-auth-email   { color: var(--sb-muted); }

.au-admin-select { font-size: .8rem; width: 80px; }
.au-admin-elevated { border-color: var(--sb-accent) !important; color: var(--sb-accent); font-weight: 600; }

.au-action-btn {
  width: 30px; height: 30px; padding: 0;
  border-radius: 6px; border: 1px solid var(--sb-border);
  background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .85rem; transition: background .15s, border-color .15s, color .15s;
}
.au-action-delete { color: var(--sb-bad); }
.au-action-delete:hover { background: var(--sb-bad); border-color: var(--sb-bad); color: var(--sb-on-state); }
.au-action-promote { color: var(--sb-warn); }
.au-action-promote:hover { background: var(--sb-warn); border-color: var(--sb-warn); color: var(--sb-on-warn); }
.au-action-hide { color: var(--sb-muted); }
.au-action-hide:hover { background: var(--sb-muted); border-color: var(--sb-muted); color: var(--sb-on-state); }
.au-action-hidden-active { color: var(--sb-accent); border-color: var(--sb-accent); }
.au-action-hidden-active:hover { background: var(--sb-accent); color: var(--sb-on-accent); }
.au-row-hidden td { opacity: .55; }
.au-row-hidden .au-avatar { background: var(--sb-muted) !important; }
.au-hidden-badge {
  font-size: .65rem; font-weight: 600; letter-spacing: .03em; text-transform: uppercase;
  background: var(--sb-muted); color: var(--sb-on-state); border-radius: 4px; padding: 1px 5px;
  vertical-align: middle; margin-left: 4px;
}

/* ============================================================
   Admin teams grid (.at-*)
   ============================================================ */
.at-group-card  { border: 1px solid var(--sb-border); border-radius: 8px; overflow: hidden; }
.at-group-header {
  font-size: .7rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .5px; color: var(--sb-accent);
  background: var(--sb-accent-tint); padding: 5px 10px;
  border-bottom: 1px solid var(--sb-border);
}
/* flag | name | # | Š16 | Š8 | KF | PF | F */
.at-group-row {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) 34px 34px 34px 34px 34px 38px;
  gap: 2px;
  align-items: center;
  padding: 3px 6px;
  font-size: .78rem;
}
.at-header-row {
  font-size: .65rem; font-weight: 700; text-transform: uppercase;
  color: var(--sb-muted);
  border-bottom: 1px solid var(--sb-border);
  padding-bottom: 4px; margin-bottom: 1px;
}
.at-team-row {
  border-bottom: 1px solid var(--sb-border);
  transition: background .1s;
}
.at-team-row:last-child { border-bottom: none; }
.at-team-row:hover { background: var(--sb-accent-tint); }

.at-flag {
  width: 24px; height: 16px; object-fit: cover;
  border-radius: 2px; border: 1px solid var(--sb-border);
  display: block;
}
.at-flag-empty {
  width: 24px; height: 16px; display: flex;
  align-items: center; justify-content: center;
  font-size: .65rem; color: var(--sb-muted);
  border: 1px dashed var(--sb-border); border-radius: 2px;
}
.at-name {
  font-size: .78rem; font-weight: 500;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.at-tiny-input {
  width: 34px; height: 26px; padding: 0; text-align: center;
  border: 1px solid var(--sb-border); border-radius: 6px;
  font-size: .78rem; color: var(--sb-text); background: var(--sb-surface-2);
  -moz-appearance: textfield;
}
.at-tiny-input:focus {
  outline: none; border-color: var(--sb-accent);
  box-shadow: 0 0 0 2px var(--sb-accent-tint);
}
.at-chk { width: 1rem; height: 1rem; cursor: pointer; }
.at-pos-warn { border-color: var(--sb-warn) !important; background: var(--sb-warn-tint); }

/* ============================================================
   Admin groups table (.ag-*)
   ============================================================ */
.ag-table       { font-size: .875rem; }
.ag-col-id      { width: 36px; }
.ag-col-name    { min-width: 100px; }
.ag-col-fee     { width: 70px; }
.ag-col-ratio   { width: 70px; }
.ag-col-desc    { min-width: 140px; }
.ag-col-actions { width: 80px; }

.ag-id          { font-size: .75rem; color: var(--sb-muted); }
.ag-tiny-input  { width: 60px; display: inline-block; padding: 2px 4px; }

.ag-action-btn {
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .85rem; transition: background .15s, border-color .15s, color .15s;
}
.ag-action-delete       { color: var(--sb-bad); }
.ag-action-delete:hover { background: var(--sb-bad); border-color: var(--sb-bad); color: var(--sb-on-state); }

.ag-insert-row td { border-top: 2px solid var(--sb-border); background: var(--sb-surface-2); }

/* ============================================================
   Admin games table (.agm-*)
   ============================================================ */
/* Admin games table (.agm-*) */
.agm-table          { font-size: .875rem; table-layout: fixed; width: 100%; }
.agm-table td,
.agm-table th       { padding: .35rem .6rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.agm-col-id         { width: 36px; }
.agm-col-date       { width: 130px; }
.agm-col-home       { width: 22%; text-align: right; }
.agm-col-vs         { width: 36px; text-align: center; }
.agm-col-away       { width: 22%; }
.agm-col-stage      { width: 180px; }
.agm-col-actions    { width: 48px; }
.agm-id             { font-size: .75rem; color: var(--sb-muted); }
.agm-datetime       { color: var(--sb-muted); font-size: .8rem; }
.agm-team           { font-weight: 500; }
.agm-vs-sep         { color: var(--sb-muted); font-size: .72rem; font-weight: 400; }
.agm-badge-stage    { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: .72rem; background: var(--sb-surface); color: var(--sb-muted); border: 1px solid var(--sb-border); white-space: nowrap; }
.agm-action-btn     { width: 30px; padding-left: 0; padding-right: 0; }
.agm-insert-row td  { border-top: 2px solid var(--sb-border); background: var(--sb-surface-2); }

/* ============================================================
   Admin events table (.ae-*)
   ============================================================ */
.ae-table        { font-size: .875rem; }
.ae-col-id       { width: 36px; }
.ae-col-name     { min-width: 160px; }
.ae-col-short    { width: 64px; }
.ae-col-switch   { width: 72px; }
.ae-col-actions  { width: 80px; }

.ae-id           { font-size: .75rem; color: var(--sb-muted); }
.ae-tiny-input   { width: 52px; display: inline-block; padding: 2px 4px; }
.ae-tiny-select  { width: 70px; display: inline-block; padding: 2px 4px; }

.ae-action-btn {
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .85rem; transition: background .15s, border-color .15s, color .15s;
}
.ae-action-delete       { color: var(--sb-bad); }
.ae-action-delete:hover { background: var(--sb-bad); border-color: var(--sb-bad); color: var(--sb-on-state); }

.ae-insert-row td { border-top: 2px solid var(--sb-border); background: var(--sb-surface-2); }

/* ============================================================
   Admin audit (.adt-*)
   ============================================================ */
.adt-tabs { border-bottom: 1px solid var(--sb-border); }
.adt-tabs .nav-link {
  font-size: .82rem;
  color: var(--sb-muted);
  border: none;
  padding: .4rem .85rem;
  border-radius: 6px 6px 0 0;
  margin-bottom: -1px;
}
.adt-tabs .nav-link.active {
  color: var(--sb-accent);
  background: var(--sb-accent-tint);
  font-weight: 600;
  border-bottom: 2px solid var(--sb-accent);
}
.adt-tabs .nav-link:hover:not(.active) {
  color: var(--sb-text);
  background: var(--sb-surface);
}

.adt-table { font-size: .835rem; }
.adt-table thead th {
  font-size: .72rem;
  font-weight: 600;
  color: var(--sb-muted);
  text-transform: uppercase;
  letter-spacing: .04em;
  white-space: nowrap;
}

.adt-col-user   { width: 130px; }
.adt-col-method { width: 120px; }
.adt-col-ip     { width: 150px; }
.adt-col-date   { width: 130px; white-space: nowrap; }
.adt-col-match  { min-width: 160px; }
.adt-col-score  { width: 68px; }
.adt-col-arrow  { width: 28px; text-align: center; }

.adt-mono       { font-family: monospace; font-size: .82rem; color: var(--sb-muted); }
.adt-date       { color: var(--sb-muted); font-size: .82rem; }
.adt-match      { font-size: .82rem; }
.adt-score-old  { color: var(--sb-muted); font-size: .82rem; }
.adt-score-new  { font-weight: 600; font-size: .82rem; }
.adt-badge-google { background: #4285f4; } /* allow-colour-literal: Google's brand blue, not a themed role */

/* ============================================================
   Admin messages table (.am-*)
   ============================================================ */
.am-table       { font-size: .875rem; }
.am-col-id      { width: 36px; }
.am-col-msg     { min-width: 200px; }
.am-col-group   { width: 160px; }
.am-col-active  { width: 72px; }
.am-col-actions { width: 80px; white-space: nowrap; }

.am-id     { font-size: .75rem; color: var(--sb-muted); }
.am-select { font-size: .82rem; }

.am-action-btn {
  width: 30px; height: 30px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: .85rem; transition: background .15s, border-color .15s, color .15s;
}
.am-action-delete       { color: var(--sb-bad); }
.am-action-delete:hover { background: var(--sb-bad); border-color: var(--sb-bad); color: var(--sb-on-state); }

.am-insert-row td { border-top: 2px solid var(--sb-border); background: var(--sb-surface-2); }

/* ============================================================
   Summary results table (.sr-*)
   ============================================================ */
.sr-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 4px 0 12px;
}

.sr-tab {
  font-size: .78rem;
  font-weight: 600;
  padding: 4px 14px;
  border-radius: 999px;
  border: 1.5px solid var(--sb-border);
  background: var(--sb-surface-2);
  color: var(--sb-muted);
  cursor: pointer;
  transition: all .15s;
  white-space: nowrap;
  line-height: 1.5;
}
.sr-tab:hover { border-color: var(--sb-accent); color: var(--sb-accent); }
.sr-tab-active { background: var(--sb-accent) !important; border-color: var(--sb-accent) !important; color: var(--sb-on-accent) !important; }

.sr-scroll-wrap {
  overflow: auto;
  max-height: calc(100vh - 180px);
  border: 1px solid var(--sb-border);
  border-radius: 12px;
}

.sr-table {
  font-size: .8rem;
  white-space: nowrap;
  border-collapse: separate;
  border-spacing: 0;
  width: max-content;
  min-width: 100%;
  margin: 0;
}

.sr-table thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  background: var(--sb-surface-2);
  color: var(--sb-text);
  padding: 6px 8px 8px;
  border-bottom: 2px solid var(--sb-border-strong);
  border-right: 1px solid var(--sb-border);
  font-weight: 600;
}

.sr-sticky-col {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--sb-card);
  box-shadow: 4px 0 10px var(--sb-shadow);
}

.sr-table thead .sr-sticky-col { z-index: 3; }

.sr-hdr-game { min-width: 130px; text-align: left !important; }
.sr-hdr-user {
  min-width: 42px;
  max-width: 56px;
  text-align: center;
  padding: 8px 6px !important;
}

.sr-hdr-user .sr-username { display: none; }

.sr-user-avatar {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: var(--sb-surface);
  border: 1.5px solid var(--sb-border-strong);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .7rem;
  font-weight: 800;
  margin: 0 auto 4px;
  text-transform: uppercase;
  line-height: 1;
}

.sr-game-td {
  padding: 8px 12px !important;
  border-right: 2px solid var(--sb-border);
  border-bottom: 1px solid var(--sb-border);
  vertical-align: middle;
}

.sr-game-flags {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 3px;
}

.sr-flag { width: 20px; height: 20px; object-fit: contain; flex-shrink: 0; }

.sr-actual-score {
  font-weight: 800;
  font-size: .82rem;
  min-width: 40px;
  text-align: center;
  background: var(--sb-surface-2);
  border: 1px solid var(--sb-border);
  border-radius: 6px;
  padding: 1px 6px;
  letter-spacing: .02em;
}

.sr-game-names {
  font-size: .69rem;
  color: var(--sb-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 168px;
}

.sr-pred-td {
  text-align: center;
  padding: 8px 6px !important;
  vertical-align: middle;
  border-bottom: 1px solid var(--sb-border);
  border-right: 1px solid var(--sb-border);
}

.sr-pts-link {
  display: block;
  font-weight: 800;
  font-size: .9rem;
  color: var(--sb-text);
  text-decoration: none;
  margin-bottom: 4px;
  line-height: 1.2;
}
.sr-pts-link:hover { color: var(--sb-accent); }

.sr-pred-badge {
  font-size: .71rem;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 5px;
  display: inline-block;
  line-height: 1.5;
  letter-spacing: .01em;
}
.sr-pred-ok      { background: var(--sb-ok-tint); color: var(--sb-ok); }
.sr-pred-partial { background: var(--sb-warn-tint); color: var(--sb-warn); }
.sr-pred-fail    { background: var(--sb-bad-tint); color: var(--sb-bad); }
.sr-pred-pending { background: var(--sb-surface-2); color: var(--sb-muted); }
.sr-pw-w { font-size: .6em; font-weight: 700; vertical-align: super; opacity: .75; }

.sr-table tbody tr:not(.sr-round-header):hover .sr-pred-td  { background: var(--sb-accent-tint); }
.sr-table tbody tr:not(.sr-round-header):hover .sr-sticky-col { background: var(--sb-accent-tint); }
.sr-table tbody tr:not(.sr-round-header):nth-child(even) .sr-pred-td { background: var(--sb-surface-2); }
.sr-table tbody tr:not(.sr-round-header):nth-child(even) .sr-sticky-col { background: var(--sb-surface-2); }
.sr-table tbody tr:not(.sr-round-header):nth-child(even):hover .sr-pred-td { background: var(--sb-accent-tint); }
.sr-table tbody tr:not(.sr-round-header):nth-child(even):hover .sr-sticky-col { background: var(--sb-accent-tint); }

/* Round group header rows */
.sr-round-header { cursor: pointer; user-select: none; }
.sr-round-header td {
  background: var(--sb-surface-2);
  border-top: 2px solid var(--sb-border);
  border-bottom: 2px solid var(--sb-border);
  border-right: 1px solid var(--sb-border);
  padding: 9px 10px !important;
}
.sr-round-header:hover td { background: var(--sb-accent-tint); }
.sr-round-header .sr-sticky-col {
  background: var(--sb-surface-2);
  border-left: 3px solid var(--sb-accent) !important;
  border-right: 2px solid var(--sb-border) !important;
}
.sr-round-header:hover .sr-sticky-col { background: var(--sb-accent-tint); }

.sr-round-name-td > div { max-width: 108px; overflow: hidden; }
.sr-round-label { font-weight: 700; font-size: .8rem; text-transform: uppercase; letter-spacing: .05em; color: var(--sb-accent); }
.sr-round-count { font-size: .72rem; color: var(--sb-muted); font-weight: 400; }
.sr-round-total {
  font-weight: 800;
  font-size: .82rem;
  color: var(--sb-accent);
  text-align: center;
  padding: 9px 8px !important;
  border-right: 1px solid var(--sb-border);
}

.sr-pop { font-size: .78rem; min-width: 160px; }
.sr-pop-sm { font-size: .72rem; min-width: 0; }
.sr-pop-row { display: flex; justify-content: space-between; gap: 16px; padding: 2px 0; }
.sr-pop-row span { color: var(--sb-muted); }
.sr-pop-row strong { font-weight: 700; }
.sr-pop-sub { padding-left: 10px; opacity: .8; }
.pop-narrow { max-width: 172px !important; }

/* Logged-in user's column highlight — must be opaque so sticky header/footer don't bleed */
.sr-my-col { background: var(--sb-accent-tint-solid) !important; }
.sr-hdr-user.sr-my-col { border-bottom: 2px solid var(--sb-accent); background: var(--sb-accent-tint-solid) !important; }
.sr-grand-total td.sr-my-col { background: var(--sb-accent-tint-solid) !important; }
.sr-my-avatar { background: var(--sb-accent) !important; color: var(--sb-on-accent) !important; }

/* Grand total footer row */
.sr-grand-total td {
  position: sticky;
  bottom: 0;
  z-index: 2;
  background: var(--sb-surface-2);
  border-top: 2px solid var(--sb-accent);
  font-weight: 800;
  font-size: .82rem;
  color: var(--sb-accent);
  text-align: center;
  padding: 9px 6px !important;
}
.sr-grand-total .sr-sticky-col {
  z-index: 3;
  text-align: left;
  padding: 9px 12px !important;
  font-size: .78rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  border-right: 2px solid var(--sb-border) !important;
}

/* ============================================================
   Trend chart (.ch-*)
   ============================================================ */
.ch-header {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 16px 20px 12px;
  border-bottom: 1px solid var(--sb-border);
}
.ch-title { font-weight: 700; font-size: 1rem; color: var(--sb-text); }
.ch-meta  { font-size: .8rem; color: var(--sb-muted); }
.ch-wrap  { padding: 8px 8px 8px; height: calc(100vh - 190px); }

/* ============================================================
   Standings Summary Table  (.sst-*)
   ============================================================ */
.sst-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 24px;
  padding: 10px 16px;
  font-size: .72rem;
  color: var(--sb-muted);
  border-bottom: 1px solid var(--sb-border);
  background: var(--sb-surface-2);
}
/* Wraps, like its sibling below. Five badges and five labels do not fit
   a phone on one line, and the row overflowed its card (issue 130). */
.sst-legend-colors { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.sst-legend-stages { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.sst-stage-lbl {
  display: inline-block;
  font-size: .6rem;
  font-weight: 700;
  color: var(--sb-muted);
  background: var(--sb-border);
  border-radius: 3px;
  padding: 1px 4px;
  margin-right: 2px;
}

.sst-table {
  width: 100%;
  border-collapse: collapse;
  font-size: .8rem;
}

.sst-head-row th {
  padding: 8px 6px;
  font-size: .72rem;
  font-weight: 700;
  color: var(--sb-on-accent);
  background: var(--sb-accent);
  white-space: nowrap;
  text-align: center;
  border: 1px solid rgba(255,255,255,.12); /* allow-colour-literal: hairline on an accent-coloured header, no on-accent-transparent token exists */
  letter-spacing: .3px;
}
.sst-team-hdr { text-align: left !important; min-width: 140px; padding-left: 12px !important; }
.sst-player-hdr { min-width: 100px; }

.sst-group-row td {
  padding: 4px 12px;
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--sb-muted);
  background: var(--sb-surface-2);
  border-top: 2px solid var(--sb-border);
}

.sst-team-row { border-bottom: 1px solid var(--sb-border); }
.sst-team-row:hover { background: var(--sb-accent-tint); }

.sst-team-cell {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 5px 10px 5px 12px;
  white-space: nowrap;
  border-right: 2px solid var(--sb-border);
}
.sst-flag { width: 22px; height: 22px; object-fit: contain; flex-shrink: 0; }
.sst-team-name { font-size: .8rem; font-weight: 500; color: var(--sb-text); }

.sst-pred-cell {
  padding: 4px 4px 2px;
  text-align: center;
  vertical-align: middle;
  border: 1px solid var(--sb-border);
}

.sst-badges {
  display: flex;
  gap: 2px;
  justify-content: center;
  align-items: center;
}

.sst-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 4px;
  font-size: .62rem;
  font-weight: 700;
  line-height: 1;
  flex-shrink: 0;
}
.sst-pop-badge { cursor: help; }
.sst-hit     { background: var(--sb-ok-tint); color: var(--sb-ok); }
.sst-miss    { background: var(--sb-bad-tint); color: var(--sb-bad); }
.sst-partial { background: var(--sb-warn-tint); color: var(--sb-warn); }
.sst-pending { background: var(--sb-warn-tint); color: var(--sb-warn); }
.sst-none    { background: var(--sb-surface-2); color: var(--sb-dim); }

.sst-pts {
  font-size: .62rem;
  font-weight: 700;
  color: var(--sb-accent);
  margin-top: 2px;
  line-height: 1;
}

/* ============================================================
   Legacy / Admin utility classes
   ============================================================ */
p { margin: 0; }

.input-size-3 { width: 36px; text-align: center; padding: 0 !important; }
.input-size-2 { width: 44px; text-align: center; }
.input-size-1 { width: 35px !important; }

.form-control-small {
  display: block; width: 100%; height: 20px;
  padding: 2px 5px; font-size: 13px; text-align: center;
  font-weight: bold; background: var(--sb-surface-2); border: 0;
  border-radius: 4px;
}

.popover { max-width: 600px; width: 320px; }
.modal-footer { justify-content: space-around; }
.modal-title  { font-size: .9rem; }

@media (max-width: 540px)  { .hide-text-sm { display: none; } }
@media (max-width: 768px)  { .hide-text-md { display: none; } }
@media (max-width: 992px)  { .hide-text-lg { display: none; } }

/* ============================================================
   Leaderboard trend panel
   ============================================================ */
.lb-row-expandable { cursor: pointer; }
.lb-row-expandable:hover { background: var(--sb-accent-tint) !important; }
.lb-me-row.lb-row-expandable:hover { background: var(--sb-accent-tint) !important; }


.lb-trend-chevron {
  font-size: .7rem;
  color: var(--sb-muted);
  flex-shrink: 0;
  width: 12px;
  text-align: center;
}

.lb-trend-panel {
  padding: 10px 0 6px;
  border-bottom: 1px solid var(--sb-border);
}

.lb-trend-panel-inner {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}

.lb-trend-chart { flex: 2; min-width: 0; }

.lb-trend-chart-label {
  font-size: .65rem;
  color: var(--sb-muted);
  margin-bottom: 4px;
}

.lb-trend-chart svg { width: 100%; height: auto; display: block; }

/* The rank sparkline (issue #91). Its colours were stroke=""/fill=""
   presentation attributes holding light-theme literals, and they are
   rules here now rather than var() in those same attributes. Not because
   var() fails there - it was checked, and `fill="var(--sb-warn)"` does
   resolve in current Chromium - but because support for var() in a
   presentation attribute is recent and uneven across engines, and the
   rule from #90 is the stronger one regardless: a view does not name a
   colour, it names a class. */
.lb-trend-grid {
  stroke: var(--sb-border);
  stroke-width: .5;
}
.lb-trend-grid--dashed { stroke-dasharray: 3 3; }
/* Amber, matching .lb-trend-legend-rank below - the legend is the key to
   this line, so the two have to come from the same token or the key stops
   pointing at anything. */
.lb-trend-line {
  fill: none;
  stroke: var(--sb-warn);
  stroke-width: 2;
  stroke-linejoin: round;
}
.lb-trend-dot { fill: var(--sb-warn); }
/* The ring around the latest round separates that dot from the line
   running under it, so it is the panel's own ground - not white, which
   was a ring in the dark theme rather than a gap. */
.lb-trend-dot--last {
  stroke: var(--sb-card);
  stroke-width: 1.5;
}
.lb-trend-tick {
  fill: var(--sb-muted);
  font-size: 8px;
}

.lb-trend-legend {
  display: flex;
  gap: 10px;
  font-size: .6rem;
  margin-top: 3px;
}
.lb-trend-legend-pts  { color: var(--sb-accent); }
.lb-trend-legend-rank { color: var(--sb-warn); }

.lb-trend-table {
  flex: 1;
  border-left: 1px solid var(--sb-border);
  padding-left: 12px;
  min-width: 110px;
}

.lb-trend-table-header {
  display: flex;
  justify-content: space-between;
  font-size: .65rem;
  color: var(--sb-muted);
  margin-bottom: 4px;
  font-weight: 600;
}

.lb-trend-row {
  display: flex;
  justify-content: space-between;
  font-size: .68rem;
  padding: 2px 0;
  gap: 4px;
}

.lb-trend-rnd  { color: var(--sb-muted); width: 24px; }
.lb-trend-rpts { color: var(--sb-text); }
.lb-trend-rank { color: var(--sb-text); text-align: right; }
.lb-trend-rank-up   { color: var(--sb-ok); font-weight: 600; }
.lb-trend-rank-down { color: var(--sb-bad); font-weight: 600; }


/* ============================================================
   Standings points detail table (partials/pointsStandings)
   ============================================================ */
.pst-pts      { font-weight: 600; color: var(--sb-accent); font-size: .72rem; min-width: 40px; text-align: right; }
.pst-zero     { color: var(--sb-muted); font-size: .72rem; min-width: 40px; text-align: right; }
.pst-hoverable { cursor: help; }

.pst-stage { margin-bottom: 2px; }
.pst-stage-header {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 0 5px;
  border-bottom: 2px solid var(--sb-border);
  cursor: pointer;
  user-select: none;
  list-style: none;
}
.pst-stage-label {
  flex: 1;
  font-size: .65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--sb-muted);
}
.pst-stage-total {
  font-size: .72rem;
  font-weight: 700;
  color: var(--sb-accent);
}
.pst-stage-chevron {
  font-size: .65rem;
  color: var(--sb-muted);
  transition: transform .2s;
}
.pst-stage-header.collapsed .pst-stage-chevron { transform: rotate(-90deg); }
.pst-stage-row {
  display: flex;
  align-items: center;
  padding: 3px 0;
  border-bottom: 1px solid var(--sb-border);
}
.pst-stage-row:last-child { border-bottom: none; }
.pst-stage-team {
  flex: 1;
  font-size: .8rem;
  font-weight: 500;
  color: var(--sb-text);
}
.pst-stage-row-zero { opacity: .5; }
.pst-group-label {
  font-size: .6rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--sb-muted);
  padding: 6px 0 2px;
  opacity: .7;
}
.pst-zero-toggle {
  display: flex;
  align-items: center;
  gap: 5px;
  width: 100%;
  font-size: .68rem;
  color: var(--sb-muted);
  background: none;
  border: none;
  padding: 5px 0 2px;
  cursor: pointer;
  user-select: none;
  text-align: left;
}
.pst-zero-toggle .bi-chevron-down { font-size: .6rem; transition: transform .2s; }
.pst-zero-toggle.collapsed .bi-chevron-down { transform: rotate(-90deg); }

/* ============================================================
   Head-to-head comparison
   ============================================================ */
.cmp-card { max-width: 520px; margin: 0 auto; }

.cmp-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 4px 0 16px;
  border-bottom: 1px solid var(--sb-border);
  margin-bottom: 16px;
}
.cmp-player {
  flex: 1;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.cmp-player-me  { align-items: flex-start; text-align: left; }
.cmp-player-them { align-items: flex-end; text-align: right; }

.cmp-rank {
  font-size: .72rem;
  color: var(--sb-muted);
  font-weight: 600;
}
.cmp-rank::before { content: '#'; }

.cmp-username {
  font-size: .95rem;
  font-weight: 700;
  color: var(--sb-text);
  word-break: break-word;
}

.cmp-total {
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--sb-text);
  line-height: 1.1;
}
.cmp-winner { color: var(--sb-accent); }
.cmp-loser  { color: var(--sb-muted); }

.cmp-vs {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
.cmp-vs-label {
  font-size: .7rem;
  font-weight: 700;
  color: var(--sb-muted);
  letter-spacing: .08em;
  text-transform: uppercase;
}
.cmp-record {
  display: flex;
  gap: 4px;
  font-size: .72rem;
  font-weight: 700;
}
.cmp-record-w { color: var(--sb-ok); }
.cmp-record-d { color: var(--sb-muted); }
.cmp-record-l { color: var(--sb-bad); }

.cmp-section-title {
  font-size: .72rem;
  font-weight: 700;
  color: var(--sb-muted);
  text-transform: uppercase;
  letter-spacing: .07em;
  margin-bottom: 8px;
}

/* Stats table */
.cmp-stats-table { margin-bottom: 20px; }
.cmp-stats-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 6px;
  padding: 5px 0;
  border-bottom: 1px solid var(--sb-border);
  align-items: center;
  font-size: .85rem;
}
.cmp-stats-row:last-child { border-bottom: none; }
.cmp-stats-header {
  font-size: .72rem;
  font-weight: 700;
  color: var(--sb-muted);
  padding-bottom: 6px;
}
.cmp-stats-me    { text-align: left;   font-weight: 600; }
.cmp-stats-label { text-align: center; color: var(--sb-muted); font-size: .78rem; }
.cmp-stats-them  { text-align: right;  font-weight: 600; }
.cmp-stat-win  { color: var(--sb-accent); }
.cmp-stat-lose { color: var(--sb-muted); }

/* Rounds table */
.cmp-rounds-table { margin-bottom: 16px; }
.cmp-round-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  gap: 6px;
  padding: 5px 0;
  border-bottom: 1px solid var(--sb-border);
  align-items: center;
  font-size: .85rem;
}
.cmp-round-row:last-child { border-bottom: none; }
.cmp-round-header {
  font-size: .72rem;
  font-weight: 700;
  color: var(--sb-muted);
  padding-bottom: 6px;
}
.cmp-round-me    { text-align: left;   font-weight: 600; }
.cmp-round-name  { text-align: center; color: var(--sb-muted); font-size: .78rem; white-space: nowrap; }
.cmp-round-them  { text-align: right;  font-weight: 600; }

.cmp-back { padding-bottom: 4px; }

/* Leaderboard username link to compare page */
a.lb-name-link {
  color: inherit;
  text-decoration: none;
}
a.lb-name-link:hover {
  color: var(--sb-accent);
  text-decoration: underline;
}

/* ============================================================
   Tournament progress bar (bar/count/today reused by .sb-topline)
   ============================================================ */
.sb-progress-bar-wrap { flex: 1; min-width: 60px; height: 6px; background: var(--sb-border); border-radius: 99px; overflow: hidden; }
.sb-progress-bar-fill { height: 100%; background: linear-gradient(90deg, var(--sb-accent), var(--sb-accent-hover)); border-radius: 99px; transition: width .4s ease; }
.sb-progress-count  { color: var(--sb-muted); white-space: nowrap; }
.sb-progress-today  { background: var(--sb-warn); color: var(--sb-on-warn); font-size: .68rem; font-weight: 700; border-radius: 4px; padding: 2px 7px; white-space: nowrap; }

/* ============================================================
   Activity feed
   ============================================================ */
.af-list { display: flex; flex-direction: column; }
.af-item {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 7px 0;
  border-bottom: 1px solid var(--sb-border);
}
.af-item:last-child { border-bottom: none; }
.af-icon { font-size: 1rem; flex-shrink: 0; line-height: 1.5; }
.af-body { font-size: .78rem; color: var(--sb-text); line-height: 1.4; }
.af-user { font-weight: 700; }
.af-text { color: var(--sb-muted); }

/* Locale dropdown on the profile page */
.sb-locale-select {
    background: var(--sb-card);
    border: 1px solid var(--sb-border);
    border-radius: 6px;
    padding: 5px 10px;
    font-size: .88rem;
    color: var(--sb-text);
    cursor: pointer;
    transition: border-color .15s;
    width: auto;
}
.sb-locale-select:focus { outline: none; border-color: var(--sb-accent); }
.sb-locale-select--full { padding: 7px 12px; font-size: .9rem; }


.af-players { font-size: .75rem; font-weight: 600; color: var(--sb-text); margin-top: 1px; }

/* ============================================================
   Command rail + shell
   The rail is near-black in BOTH themes, so every colour here comes from
   the non-flipping set: --sb-rail, --sb-rail-dim, --sb-rail-line,
   --sb-on-rail, --sb-accent. A theme-flipping token used here disappears
   in one theme or the other — see the --sb-on-rail comment in :root.
   ============================================================ */
.sb-shell { display: grid; grid-template-columns: 212px 1fr; min-height: 100vh; }
@media (max-width: 991.98px) { .sb-shell { grid-template-columns: 1fr; } }

/* Grid item wrapping header + .sb-main. .sb-main's `flex: 1` (declared with
   the pre-rail layout, above) only does something if its parent is a flex
   column that itself has a height to grow into - give it both explicitly
   rather than relying on the incidental shared --sb-bg between this and
   <body> to hide the gap on short pages. min-width: 0 overrides the grid
   item default of min-width: auto, which otherwise refuses to let this
   column shrink below its content's min-content width - on a phone
   viewport that floor is wider than the screen, forcing the whole page to
   scroll horizontally with content clipped off-screen. */
.sb-shell-main { display: flex; flex-direction: column; min-height: 100%; min-width: 0; }

.sb-rail {
  background: var(--sb-rail);
  flex-direction: column;
  padding: 14px 0;
  border-right: 1px solid var(--sb-rail-line);
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}
.sb-rail-brand {
  display: flex; align-items: center; gap: 8px;
  padding: 0 16px 16px;
  font-weight: 800; font-size: 1.22rem; letter-spacing: .02em;
  color: var(--sb-on-rail); text-transform: uppercase; text-decoration: none;
}
.sb-rail-brand i { font-style: normal; color: var(--sb-rail-accent); }
.sb-rail-mark { height: 26px; width: auto; }

.sb-rail-nav { display: flex; flex-direction: column; }
.sb-rail-link {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 16px;
  color: var(--sb-rail-dim);
  font-weight: 600; font-size: .92rem; letter-spacing: .04em;
  text-transform: uppercase; text-decoration: none;
  border-left: 3px solid transparent;
}
.sb-rail-link:hover { color: var(--sb-on-rail); background: var(--sb-rail-row); }
.sb-rail-link.active {
  color: var(--sb-on-rail);
  border-left-color: var(--sb-rail-accent);
  background: var(--sb-rail-row);
}
.sb-rail-link .material-icons { font-size: 1.05rem; }

.sb-rail-sep   { height: 1px; background: var(--sb-rail-line); margin: 10px 16px; }
.sb-rail-label { padding: 0 16px 5px; font-size: .7rem; letter-spacing: .16em; text-transform: uppercase; color: var(--sb-rail-dim); }
.sb-rail-foot  { margin-top: auto; padding: 14px 16px; display: flex; flex-direction: column; gap: 10px; border-top: 1px solid var(--sb-rail-line); }

/* ── Rail context block ──────────────────────────────────────
   The tournament scopes every link below it, so it sits directly under the
   brand rather than in the footer, and its name wraps instead of being
   ellipsised — truncating the one piece of context that says what the menu
   means was fault 1 of issue #69. The same .sb-rail-card markup is reused
   inside .sb-mobile-collapse, which is painted from the same --sb-rail
   chrome, so the phone gets the card too instead of losing it.
   ------------------------------------------------------------ */
.sb-rail-context {
  padding: 0 16px 14px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--sb-rail-line);
}
.sb-rail-card {
  background: var(--sb-rail-raised);
  border: 1px solid var(--sb-rail-line);
  border-radius: var(--sb-radius-sm);
  padding: 9px 10px;
}
.sb-rail-card-label {
  display: block;
  font-size: .62rem; font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--sb-rail-dim);
  margin-bottom: 3px;
}
.sb-rail-card-name {
  display: block;
  font-size: .82rem; font-weight: 700; line-height: 1.3;
  color: var(--sb-on-rail);
  overflow-wrap: anywhere;
}
.sb-rail-card-row {
  margin-top: 9px; padding-top: 9px;
  border-top: 1px solid var(--sb-rail-line);
}
.sb-rail-card > .sb-rail-card-row:first-child { margin-top: 0; padding-top: 0; border-top: 0; }

/* Shared "leave this context" action: switch tournament on the player rail,
   back to the site on the admin one. Replaces the orphaned exit link that
   used to float in the footer. */
.sb-rail-switch {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: .72rem; font-weight: 700; letter-spacing: .04em;
  color: var(--sb-rail-accent); text-decoration: none;
  border-radius: var(--sb-radius-sm);
}
.sb-rail-card > .sb-rail-switch { margin-top: 7px; }
.sb-rail-switch:hover { color: var(--sb-rail-accent); text-decoration: underline; }
.sb-rail-switch:focus-visible { outline: 2px solid var(--sb-accent); outline-offset: 2px; }

/* Guest rail's prominent call-to-action. Only --sb-accent / --sb-on-accent
   here (both non-flipping-safe on the rail) - hover is opacity, not a
   second colour, so no --sb-accent-hover is needed. */
.sb-rail-login {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  padding: 10px 14px;
  border-radius: 8px;
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  font-weight: 700; font-size: .92rem;
  text-decoration: none;
}
.sb-rail-login:hover { color: var(--sb-on-accent); opacity: .9; }

/* league-switcher (partials/league-switcher.blade.php) styles itself for the
   dark topnav, so its toggle is constrained here to the 212px rail column
   instead of being edited. Scoped to the direct-child .dropdown so it does
   not also catch the account chip's toggle in the footer strip, which needs
   to keep its own flex centring. */
.sb-rail-context > .sb-rail-card > .sb-rail-card-row > .dropdown > .dropdown-toggle {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding-left: 0;
}
.sb-rail-context .dropdown-menu { max-width: 212px; }

/* ── Guest language switch (issue 136) ───────────────────────
   partials/locale-switch: two halves, the current one filled. Sized by
   where it sits - full rail width under the logo, pill-high in the phone
   top bar. It is a <form>, so Bootstrap's reboot margin is zeroed. */
.sb-lang {
  display: grid; grid-template-columns: 1fr 1fr;
  margin: 0; padding: 3px;
  border: 1px solid var(--sb-rail-line);
  border-radius: var(--sb-radius-sm);
  background: var(--sb-rail-wash-sm);
}
.sb-lang-opt {
  border: 0; background: transparent; cursor: pointer;
  border-radius: calc(var(--sb-radius-sm) - 2px);
  color: var(--sb-rail-dim);
  font-family: inherit; font-weight: 700; letter-spacing: .06em;
  transition: color .15s, background .15s;
}
.sb-lang-opt:hover { color: var(--sb-on-rail); background: var(--sb-rail-wash-md); }
.sb-lang-opt[aria-current="true"] { color: var(--sb-on-accent); background: var(--sb-accent); }
.sb-lang-opt:focus-visible { outline: 2px solid var(--sb-accent); outline-offset: 1px; }

.sb-rail-lang { padding: 0 16px 14px; }
.sb-rail-lang .sb-lang-opt { padding: 5px 0; font-size: .74rem; }

.sb-topnav .sb-lang { display: inline-grid; grid-template-columns: auto auto; height: 28px; padding: 2px; }
.sb-topnav .sb-lang-opt { padding: 0 8px; font-size: .68rem; }

/* ── Rail account section (issue 135) ─────────────────────────
   The signed-in footer: labelled rail links instead of an avatar popup.
   Pinned to the bottom like .sb-rail-foot, but without its side padding,
   because each .sb-rail-link carries its own. */
.sb-rail-account {
  margin-top: auto; padding-top: 12px;
  border-top: 1px solid var(--sb-rail-line);
}
/* Sign-out is a <button> (it submits the logout form), drawn as a link. */
button.sb-rail-link {
  width: 100%; background: transparent; text-align: left;
  border: 0; border-left: 3px solid transparent;
  font-family: inherit;
}
/* A person's name is not a menu label: keep its own case. */
.sb-rail-link--you { text-transform: none; letter-spacing: 0; }
.sb-rail-you { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-rail-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; flex-shrink: 0;
  border-radius: 50%;
  background: var(--sb-accent); color: var(--sb-on-accent);
  font-size: .6rem; font-weight: 700; letter-spacing: -.2px;
}


/* ============================================================
   Dashboard tiles + fixture deck
   ============================================================ */
.sb-tiles { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
@media (max-width: 767.98px) { .sb-tiles { grid-template-columns: repeat(2, 1fr); } }

.sb-tile {
  background: var(--sb-surface-2);
  border-radius: var(--sb-radius);
  padding: 11px 13px;
  display: flex; flex-direction: column; gap: 2px;
}
.sb-tile--hi { background: var(--sb-accent-tint); }
.sb-tile--hi .sb-tile-v { color: var(--sb-accent); }
.sb-tile-l { font-size: .74rem; letter-spacing: .13em; text-transform: uppercase; color: var(--sb-muted); }
.sb-tile-v { font-weight: 800; font-size: 1.72rem; line-height: 1.05; font-variant-numeric: tabular-nums; }
.sb-tile-d { font-size: .74rem; color: var(--sb-muted); font-variant-numeric: tabular-nums; }
.sb-tile-d.up { color: var(--sb-ok); }
.sb-tile-d.dn { color: var(--sb-bad); }

/* The round arrow that drives a strip whose scrollbar is hidden. Two surfaces
   wear it now - the dashboard fixture deck and the survival summary's runs -
   so its appearance is declared here once and each surface adds nothing but
   its own placement. The Alpine that drives them is deliberately NOT shared:
   the deck moves one element, the summary a synchronised set. */
.sb-scroll-nav {
  width: 30px; height: 30px; padding: 0; line-height: 1;
  display: grid; place-items: center;
  border: 1px solid var(--sb-border);
  border-radius: 50%;
  background: var(--sb-surface-2);
  color: var(--sb-text);
  font-size: 1.05rem;
  cursor: pointer;
  box-shadow: 0 2px 8px var(--sb-shadow-strong);
}
.sb-scroll-nav:hover { border-color: var(--sb-accent); color: var(--sb-accent); }
.sb-scroll-nav:focus-visible { outline: 2px solid var(--sb-accent); outline-offset: 2px; }

.sb-deck-wrap { position: relative; }
/* The scrollbar is hidden deliberately: on a desktop trackpad it sat under
   the cards as a permanent grey bar that read as part of the design rather
   than as a control. The arrows in .sb-deck-nav replace it, and native
   wheel/swipe/keyboard scrolling is untouched. */
.sb-deck {
  display: flex; gap: 10px; overflow-x: auto; padding: 12px 14px;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.sb-deck::-webkit-scrollbar { display: none; }
/* Placement only - the look comes from .sb-scroll-nav above. */
.sb-deck-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
}
.sb-deck-nav--prev { left: 4px; }
.sb-deck-nav--next { right: 4px; }
@media (prefers-reduced-motion: reduce) { .sb-deck { scroll-behavior: auto; } }
.sb-deck-card {
  flex: 0 0 218px;
  scroll-snap-align: start;
  background: var(--sb-surface-2);
  border: 1px solid var(--sb-border);
  border-radius: var(--sb-radius);
  padding: 11px 12px;
  display: flex; flex-direction: column; gap: 9px;
  color: inherit; text-decoration: none;
}
.sb-deck-card--open { border-color: var(--sb-accent); }
.sb-deck-when  { font-size: .76rem; letter-spacing: .1em; text-transform: uppercase; color: var(--sb-muted); }
.sb-deck-side  { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: .9rem; }
.sb-deck-name  { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sb-deck-score { margin-left: auto; font-weight: 800; font-size: 1.1rem; font-variant-numeric: tabular-nums; }
.sb-deck-foot {
  margin-top: 2px; padding-top: 8px;
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  border-top: 1px solid var(--sb-border);
  font-size: .78rem; letter-spacing: .08em; text-transform: uppercase; color: var(--sb-muted);
}
.sb-deck-go { color: var(--sb-accent); font-weight: 700; }

.sb-topline { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.sb-topline-ev { font-weight: 800; font-size: 1.3rem; text-transform: uppercase; white-space: nowrap; }

/* ============================================================
   Survival — prediction/survival.blade.php
   The last screen to reach Floodlight (issue #70). The grid is a
   radiogroup: a visually hidden <input type=radio> drives every
   state of the card beside it, so the lock the server computes is
   the same `disabled` the browser honours.
   ============================================================ */
.sv-hero {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 22px;
  align-items: center;
  background: var(--sb-card);
  border: 1px solid var(--sb-border-strong);
  border-radius: 12px;
  box-shadow: 0 1px 4px var(--sb-shadow);
  padding: 18px 20px;
  margin-bottom: 16px;
}
.sv-hero-n {
  font-weight: 800;
  font-size: 3rem;
  line-height: .9;
  color: var(--sb-accent);
  font-variant-numeric: tabular-nums;
}
.sv-label {
  font-size: .7rem;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--sb-muted);
}
.sv-hero-count .sv-label { margin-top: 6px; }
.sv-hero-sum { margin-top: 9px; }
.sv-hero-sum b { color: var(--sb-text); font-variant-numeric: tabular-nums; }

.sv-pips { display: flex; gap: 5px; flex-wrap: wrap; }
.sv-pip {
  width: 26px; height: 26px;
  border-radius: var(--sb-radius-sm);
  display: grid; place-items: center;
  font-size: .68rem; font-weight: 700;
  background: var(--sb-surface-2);
  border: 1px solid var(--sb-border);
  color: var(--sb-dim);
  font-variant-numeric: tabular-nums;
}
.sv-pip--hit { background: var(--sb-ok); border-color: var(--sb-ok); color: var(--sb-on-state); }
.sv-pip--now { background: var(--sb-accent-tint); border-color: var(--sb-accent); color: var(--sb-accent); }

.sv-verdict { margin-top: 8px; font-size: .82rem; font-weight: 600; }
.sv-verdict--ok  { color: var(--sb-ok); }
.sv-verdict--bad { color: var(--sb-bad); }

.sv-stake { text-align: right; }
.sv-stake b {
  display: block;
  font-size: 1.5rem;
  font-weight: 800;
  color: var(--sb-text);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 575.98px) {
  /* The run count and the stake keep each other company on the first row;
     the pips and the running total take the full width underneath. */
  .sv-hero { grid-template-columns: 1fr auto; gap: 14px; padding: 14px; }
  .sv-hero-count { grid-area: 1 / 1 / 2 / 2; }
  .sv-stake { grid-area: 1 / 2 / 2 / 3; align-self: end; }
  .sv-hero-mid { grid-area: 2 / 1 / 3 / -1; }
  .sv-hero-n { font-size: 2.4rem; }
}

.sv-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; flex-wrap: wrap; margin-bottom: 12px;
}
.sv-head-t {
  font-size: .82rem; letter-spacing: .1em; text-transform: uppercase;
  font-weight: 700; margin: 0; color: var(--sb-text);
}
.sv-head-n { font-size: .76rem; color: var(--sb-dim); }

.sv-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 9px;
}
@media (max-width: 420px) { .sv-grid { grid-template-columns: 1fr; } }

/* min-width: 0 because this is the grid item. Without it the track takes
   its automatic minimum from the longest team name - 'Crvena Zvezda
   Meridianbet Belgrade' made the column 376px inside a 301px card - and
   the row ran off the right of a phone with its Namie/Isvykoje badge on
   it (issue 130). The ellipsis on .sv-who was already there and could
   never fire, because nothing ever asked the name to be narrower. */
.sv-slot { position: relative; display: flex; min-width: 0; }
.sv-radio { position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none; }
.sv-team {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--sb-border);
  border-radius: var(--sb-radius);
  background: var(--sb-surface-2);
  cursor: pointer;
  margin: 0;
  transition: border-color .13s, background .13s;
}
.sv-team:hover { border-color: var(--sb-border-strong); }
.sv-radio:checked + .sv-team {
  border-color: var(--sb-accent);
  background: var(--sb-accent-tint);
  box-shadow: inset 0 0 0 1px var(--sb-accent);
}
.sv-radio:focus-visible + .sv-team { outline: 2px solid var(--sb-accent); outline-offset: 2px; }
.sv-radio:disabled + .sv-team { opacity: .45; cursor: not-allowed; }
.sv-radio:disabled + .sv-team:hover { border-color: var(--sb-border); }
.sv-radio:disabled:checked + .sv-team { opacity: .8; border-color: var(--sb-accent); }

.sv-crest { width: 28px; height: 28px; object-fit: contain; flex: none; }
.sv-who { min-width: 0; flex: 1; }
.sv-who b {
  display: block; font-size: .85rem; font-weight: 600;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sv-who > span {
  display: block; font-size: .72rem; color: var(--sb-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sv-ha {
  font-size: .62rem; font-weight: 700; letter-spacing: .05em;
  padding: 2px 6px; border-radius: var(--sb-radius-sm);
  background: var(--sb-surface); color: var(--sb-muted);
  border: 1px solid var(--sb-border); flex: none;
}
.sv-ha--home { color: var(--sb-accent); border-color: var(--sb-accent); background: var(--sb-accent-tint); }
.sv-mark { color: var(--sb-dim); flex: none; font-size: .95rem; }

.sv-hint { margin: 12px 0 0; font-size: .76rem; color: var(--sb-dim); }

.sv-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.sv-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 10px 5px 6px; border-radius: 999px;
  background: var(--sb-surface-2); border: 1px solid var(--sb-border);
  font-size: .76rem; color: var(--sb-muted);
}
.sv-chip b { color: var(--sb-ok); font-weight: 700; font-size: .68rem; font-variant-numeric: tabular-nums; }
.sv-chip-crest { width: 18px; height: 18px; object-fit: contain; }

.sv-rules { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
.sv-rule { display: flex; gap: 10px; align-items: flex-start; }
.sv-rn {
  width: 21px; height: 21px; border-radius: 50%; flex: none;
  background: var(--sb-accent-tint); color: var(--sb-accent);
  display: grid; place-items: center; font-size: .66rem; font-weight: 800;
}
.sv-rule p { margin: 0; font-size: .82rem; color: var(--sb-muted); line-height: 1.55; }

.sv-pts-wrap { overflow-x: auto; margin-top: 14px; }
.sv-pts { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.sv-pts td, .sv-pts th {
  padding: 6px 9px; text-align: center; font-size: .8rem;
  border-bottom: 1px solid var(--sb-border);
}
.sv-pts th {
  font-size: .64rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--sb-dim); font-weight: 700;
}
.sv-pts tr:last-child td { border-bottom: 0; }
.sv-pts-l { color: var(--sb-dim); font-size: .68rem; letter-spacing: .09em; text-transform: uppercase; }
/* The per-round values were the only cells in this table with no colour of
   their own, taking whatever body happened to be (issue #85). That is the
   one kind of cell that can go invisible without any rule being wrong -
   inheritance is not a colour decision, it is the absence of one. Now every
   cell in the table names its own token, so the row cannot be dragged to
   white by an ancestor, an extension, or a future container. */
.sv-pts-v { color: var(--sb-text); }
.sv-pts-tot { color: var(--sb-accent); font-weight: 700; }

.sv-over { color: var(--sb-muted); }

/* ============================================================
   Survival summary — summary/survivals.blade.php (issue #82)
   A leaderboard, not a table: one row per player, and that
   player's whole season beside their name as a run of
   crest tiles. The outcome of a round lives in the tile's
   BORDER COLOUR and nowhere else, which is why nothing here
   fades a tile with opacity or filter - either would take the
   border down with the crest and erase the only signal.
   ============================================================ */
.svs-legend {
  display: flex; flex-wrap: wrap; gap: 6px 14px;
  margin-bottom: 14px;
}
.svs-legend-item {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: .72rem; color: var(--sb-dim);
}

/* The arrows are overlaid on the list, not on any one row: one pair drives
   every run at once, so they belong to the board rather than to a player. */
.svs-board { position: relative; }
.svs-nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
}
.svs-nav--prev { left: -10px; }
.svs-nav--next { right: -10px; }

.svs-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }

/* The name's track is a fixed width, not `auto`, and that is load-bearing:
   each row is its own grid, so a content-sized name column would start every
   player's run at a different x and one shared scrollLeft would still leave
   round n in a different place on every row. A width that does not depend on
   the name is what makes the runs comparable. */
.svs-row {
  display: grid;
  grid-template-columns: 26px minmax(0, 11rem) minmax(0, 1fr) auto auto;
  column-gap: 12px;
  align-items: center;
  padding: 10px 12px;
  border: 1px solid var(--sb-border);
  border-radius: var(--sb-radius);
  background: var(--sb-surface-2);
}
.svs-row--me { border-color: var(--sb-accent); background: var(--sb-accent-tint); }

.svs-rank {
  font-size: .8rem; font-weight: 700; text-align: center;
  color: var(--sb-dim); font-variant-numeric: tabular-nums;
}
.svs-rank--top { color: var(--sb-accent); }

.svs-id { display: flex; align-items: center; gap: 8px; min-width: 0; }
.svs-avatar {
  width: 22px; height: 22px; border-radius: 50%; flex: none;
  background: var(--sb-accent); color: var(--sb-on-accent);
  display: grid; place-items: center;
  font-size: .62rem; font-weight: 800;
}
/* Capped on purpose: without a ceiling a long username would push the avatar
   and itself past the fixed track and eat into the run. */
.svs-name {
  font-size: .85rem; font-weight: 600; color: var(--sb-text);
  max-width: 9rem;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.svs-stat { text-align: right; }
.svs-stat-n {
  display: block; font-size: 1.15rem; font-weight: 800; line-height: 1.1;
  color: var(--sb-text); font-variant-numeric: tabular-nums;
}
.svs-stat--pts .svs-stat-n { color: var(--sb-accent); }
.svs-stat-l {
  display: block; font-size: .58rem; letter-spacing: .1em;
  text-transform: uppercase; color: var(--sb-muted);
}

/* The run shares the name's line and never wraps - a player is one line at
   every width, or a screen holds four of them. A 38-round season is ~1060px
   of tiles and fits no window, so the run scrolls sideways inside its own
   cell instead. Its scrollbar is hidden for the same reason the fixture
   deck hides its own: a permanent grey bar under every row read as part of
   the design rather than as a control. The arrows over the list replace it,
   and wheel, trackpad and touch scrolling are untouched.

   Every run holds one tile per event, so they are all the same width and a
   single shared scrollLeft (see survivalRuns() in the view) keeps round n in
   the same column on every row - which is the whole reason this is a
   leaderboard and not a pile of strips. */
.svs-run {
  display: flex; gap: 3px;
  min-width: 0;
  overflow-x: auto;
  scroll-behavior: smooth;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.svs-run::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: reduce) { .svs-run { scroll-behavior: auto; } }

.svs-tile {
  position: relative;
  box-sizing: border-box;
  display: block;
  /* 35px is the largest tile the row carries for free: the streak/points
     block beside it stands about 34px tall, so the tile sets the row height
     by a single pixel rather than growing it. */
  width: 35px; height: 35px;
  flex: none;
  border: 1.5px solid var(--sb-border);
  border-radius: 3px;
  background: var(--sb-crest-plate);
  overflow: hidden;
}
/* The crest fills the tile. The plate the shared component draws (issue #79)
   is what keeps a dark crest legible and stays; its own hairline and radius
   do not, because a second outline inside the outcome border is
   just noise. */
.svs-tile .sb-crest {
  display: block;
  width: 100%; height: 100%;
  max-width: 100%;
  padding: 0; border: 0; border-radius: 2px;
  object-fit: contain;
}

.svs-tile--hit { border-color: var(--sb-ok); }
.svs-tile--miss { border-color: var(--sb-bad); }
/* The scrim is its own element on purpose: an inset shadow declared on the
   tile would paint under the child <img>, so the crest would come out
   undimmed. On the overlay - which has no children - it lands on top. */
.svs-tile--miss::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 2px;
  box-shadow: inset 0 0 0 40px var(--sb-scrim);
}
.svs-tile--pending { border-style: dashed; border-color: var(--sb-accent); }
.svs-tile--none {
  border-style: dashed;
  background: transparent;
  opacity: .5;
}

.svs-empty { margin: 0; color: var(--sb-muted); font-size: .85rem; }

@media (max-width: 480px) {
  /* Still one line per player: the run scrolls at every width, so nothing
     here wraps it. Narrow the gutters, the name and the figures instead,
     and the run cell simply gets what is left. */
  .svs-row { grid-template-columns: 22px minmax(0, 5rem) minmax(0, 1fr) auto auto; column-gap: 8px; padding: 10px; }
  /* The initials go, not the name: they say nothing the name does not, and
     their 30px is the difference between a name and an abbreviation here. */
  .svs-avatar { display: none; }
  .svs-name { max-width: 5rem; }
  .svs-stat-n { font-size: 1rem; }
}

/* ============================================================
   Leagues page — leagues/index.blade.php (issue #90)
   This page was written before the token layer and kept its whole
   appearance in three places nothing inspects: a <style> block in the
   Blade, inline style="" attributes, and colour strings inside the
   page's own JavaScript. All three were light-theme values, so the
   active row painted itself near-white and swallowed its own name in
   the dark theme.

   The JavaScript-built rows (.lg-person-*, .lg-result) are the reason
   this block exists at all rather than just a corrected <style>: a
   colour written from a template literal is invisible to every guard
   the project has, so the script now only ever applies class names.
   ============================================================ */

/* ── Pending invites ── */
.lg-invite-count {
  background: var(--sb-accent);
  color: var(--sb-on-accent);
  font-size: .62rem;
  vertical-align: middle;
}
.lg-invite-row {
  background: var(--sb-surface-2);
  border: 1px solid var(--sb-border);
  border-radius: var(--sb-radius);
}
.lg-invite-name { font-size: .875rem; font-weight: 600; }
.lg-invite-by   { font-size: .78rem; color: var(--sb-muted); margin-left: 8px; }

/* ── Buttons shared by the list and the modals ── */
.lg-btn-sm     { font-size: .78rem; padding: 4px 14px; }
.lg-btn-create { font-size: .78rem; padding: 5px 14px; flex-shrink: 0; }
.lg-btn-cancel { font-size: .82rem; padding: 6px 18px; }
.lg-btn-save   { font-size: .82rem; padding: 6px 22px; }
.lg-btn-delete { font-size: .82rem; padding: 6px 16px; display: none; }
/* The quiet sibling of .btn-primary: a surface, not a colour. Used for
   "decline" and "cancel", which must never compete with the action they
   sit beside. */
.lg-btn-quiet {
  background: var(--sb-surface-2);
  color: var(--sb-muted);
  border: 1px solid var(--sb-border);
}
/* Destructive, and outlined rather than filled for the same reason: the
   button that deletes a league should be reachable, not inviting. */
.lg-btn-danger {
  background: var(--sb-surface);
  color: var(--sb-bad);
  border: 1px solid var(--sb-bad);
}

/* ── League rows ── */
.lg-card { overflow: hidden; }
.lg-list-title { margin-bottom: 0; }

.lg-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 4px;
  border-top: 1px solid var(--sb-border);
}
.lg-row:first-of-type { border-top: none; }
/* The row the player is actually playing. The accent tint is the same
   "this one is yours" ground .svs-row--me uses, and it is the whole
   point of issue #90: the literal here was a pale blue that stayed pale
   while the text above it went white. */
.lg-row--active {
  background: var(--sb-accent-tint);
  border-radius: var(--sb-radius);
  padding: 11px 10px;
  margin: 0 -4px;
  border-top: none !important;
  border-left: 3px solid var(--sb-accent);
}
.lg-row--active + .lg-row { border-top: none; }

.lg-row-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lg-row-name {
  font-size: .875rem;
  font-weight: 700;
  color: var(--sb-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lg-row-desc {
  font-size: .72rem;
  color: var(--sb-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.lg-badge {
  font-size: .65rem;
  font-weight: 700;
  padding: 2px 9px;
  border-radius: 20px;
  white-space: nowrap;
}
/* Amber ground, amber outline, amber star - but the word itself is
   --sb-text, not --sb-warn. At this size (.65rem, 10px) amber on its own
   tint measures 2.7:1 in the light theme, which is a label you squint at;
   the tint and the star carry the "owner" signal perfectly well without
   asking the one part that has to be READ to carry it too. .lb-pub-top is
   the same ground under ordinary text. */
.lg-badge--owner {
  background: var(--sb-warn-tint);
  color: var(--sb-text);
  border: 1px solid var(--sb-warn);
}
.lg-badge--owner i { color: var(--sb-warn); }
.lg-badge--member {
  background: var(--sb-surface-2);
  color: var(--sb-muted);
  border: 1px solid var(--sb-border);
}
.lg-member-count {
  font-size: .78rem;
  color: var(--sb-muted);
  white-space: nowrap;
}
.lg-member-count i { font-size: .7rem; }

.lg-row-actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}
/* The <form> wrappers exist only to carry a CSRF token and must not
   become a flex item of their own, or the buttons stop lining up. */
.lg-form-contents { display: contents; }
.lg-action-btn {
  width: 30px;
  height: 30px;
  border-radius: 7px;
  border: 1.5px solid;
  background: var(--sb-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .85rem;
  cursor: pointer;
  transition: background .12s, opacity .12s;
  padding: 0;
}
.lg-action-btn:hover { opacity: .75; }
.lg-action-btn--accent { color: var(--sb-accent); border-color: var(--sb-accent); }
.lg-action-btn--muted  { color: var(--sb-muted);  border-color: var(--sb-border); }
.lg-action-btn--danger { color: var(--sb-bad);    border-color: var(--sb-bad); }

.lg-empty {
  padding: 20px 0;
  text-align: center;
  color: var(--sb-muted);
  font-size: .85rem;
}

/* ── Modals ── */
.lg-modal-narrow { max-width: 450px; }
.lg-modal-content { border-radius: 12px; overflow: hidden; }
/* The header ground is the accent in both themes, so its hairline comes
   from the accent family too - a neutral border token would flip out
   from under it and show as a hard light-on-dark seam in one theme. */
.lg-modal-header {
  background: var(--sb-accent);
  border-bottom: 1px solid var(--sb-accent-hover);
  padding: 14px 20px;
}
.lg-modal-title { color: var(--sb-on-accent); font-size: .88rem; font-weight: 700; }
.lg-modal-body { padding: 18px 20px; }
.lg-modal-body--form { padding: 20px 24px; }
.lg-modal-league { font-size: .95rem; font-weight: 700; color: var(--sb-text); margin-bottom: 12px; }
.lg-input { border-radius: var(--sb-radius); border-color: var(--sb-border); }
.lg-label { font-size: .78rem; font-weight: 600; color: var(--sb-muted); }
.lg-required { color: var(--sb-bad); }
.lg-hint { font-size: .72rem; color: var(--sb-muted); }
.lg-check-label { font-size: .82rem; }
.lg-hidden { display: none; }

/* ── Invite modal: search results and the lists built in JavaScript ── */
.lg-results {
  border: 1px solid var(--sb-border);
  border-radius: var(--sb-radius);
  overflow: hidden;
  display: none;
  margin-top: 6px;
}
/* A plain <button> inherits the UA's buttontext, which is near-black in
   both themes - it has to name its own colour or it disappears on the
   dark surface. */
.lg-result {
  display: block;
  width: 100%;
  text-align: left;
  padding: 9px 14px;
  background: none;
  border: none;
  color: var(--sb-text);
  font-size: .82rem;
  cursor: pointer;
}
/* The separator belongs between rows, not on the first one - which is
   what the `i > 0` test in the old template literal was doing. */
.lg-result + .lg-result { border-top: 1px solid var(--sb-border); }
.lg-result:hover { background: var(--sb-accent-tint); }
/* The result row is not a flex container the way .lg-person is, so the
   handle spaces itself off the name here. */
.lg-result .lg-person-handle { margin-left: 6px; }
.lg-result-empty { padding: 10px 14px; font-size: .8rem; color: var(--sb-muted); }

.lg-section { margin-top: 14px; display: none; }
.lg-section-title {
  font-size: .72rem;
  font-weight: 700;
  color: var(--sb-muted);
  text-transform: uppercase;
  letter-spacing: .04em;
  margin-bottom: 6px;
}
.lg-section-items {
  display: flex;
  flex-direction: column;
  gap: 4px;
  max-height: 140px;
  overflow-y: auto;
}
.lg-section-items--short { max-height: 100px; }

.lg-person {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 4px 8px;
  background: var(--sb-surface-2);
  border-radius: 6px;
  font-size: .78rem;
}
/* An invite nobody has answered yet is a pending state, not a member. */
.lg-person--pending {
  background: var(--sb-warn-tint);
  border: 1px solid var(--sb-warn);
}
.lg-person-name { font-weight: 600; }
.lg-person-handle { color: var(--sb-muted); }
/* Same ground and outline as .lg-badge--owner, and the same reason for
   reading its label in --sb-text rather than in amber. */
.lg-person-badge {
  font-size: .65rem;
  background: var(--sb-warn-tint);
  color: var(--sb-text);
  border: 1px solid var(--sb-warn);
  border-radius: 10px;
  padding: 1px 7px;
  font-weight: 700;
}

/* ── League payments (issue 140) ─────────────────────────────
   partials/league-payments-form: one amount box per member. An unpaid row
   carries a warn stripe so what still needs chasing reads at a glance. */
.lp-summary { display: flex; flex-wrap: wrap; gap: 6px 18px; margin-bottom: 12px; font-size: .85rem; color: var(--sb-muted); }
.lp-summary strong { color: var(--sb-text); }
.lp-fee { font-size: .78rem; }
.lp-table th { font-size: .72rem; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--sb-muted); }
.lp-col-amount { width: 150px; }
.lp-name { font-weight: 600; font-size: .86rem; margin: 0; }
.lp-handle { color: var(--sb-muted); font-size: .75rem; margin-left: 6px; }
.lp-tag { font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--sb-muted); border: 1px solid var(--sb-border); border-radius: 999px; padding: 1px 6px; margin-left: 6px; }
.lp-row--unpaid td:first-child { box-shadow: inset 3px 0 0 var(--sb-warn); }
.lp-input { font-variant-numeric: tabular-nums; text-align: right; }
.lp-actions { display: flex; justify-content: flex-end; margin-top: 14px; }

/* ── Rehearsal banner (issue 150) ─────────────────────────────
   partials/rehearsal-banner: only on the Oracle rehearsal copy. Warn
   colours so it reads as a caution, not as part of the design. */
.sb-rehearsal-banner {
  padding: 6px 16px;
  background: var(--sb-warn-tint);
  border-bottom: 1px solid var(--sb-warn);
  color: var(--sb-text);
  font-size: .82rem; font-weight: 600; text-align: center;
}
.sb-rehearsal-banner a { color: inherit; text-decoration: underline; margin-left: 6px; }
