/* Indianapolis Industrial League — Centralized Theme */

/* ——— Tokens ——————————————————————————————————— */
:root{
  /* Lint-silencers / safe defaults (Bootstrap sets these at runtime) */
  --mx: 50%; /* spotlight x-pos fallback */
  --my: 50%; /* spotlight y-pos fallback */
  --bs-success-rgb: 25, 135, 84;   /* Bootstrap 5 default */
  --bs-danger-rgb: 220, 53, 69;    /* Bootstrap 5 default */
  --bs-warning-rgb: 255, 193, 7;   /* Bootstrap 5 default */
  /* Brand / IIL purple */
  --bs-primary: #5e2ea3;            /* deep purple */
  --bs-primary-rgb: 94, 46, 163;    /* same in rgb */
  /* Text that reads ON a --bs-primary surface, and the surface's own hover/active
     states. These three move together and must be read as a set — see backlog 4.
     --bs-primary is redefined light in the dark theme so purple *text* stays legible
     there, and nothing re-derived the other direction: white on the dark theme's
     #c6b0f0 is 1.93:1, which is what every Save button, primary badge, active
     pagination page, avatar and skip link were rendering at.
     Guarded by test_primary_surface_contrast.py, which computes the ratios rather
     than asserting the selectors exist. */
  --bs-primary-ink: #ffffff;        /* 8.77:1 on #5e2ea3 */
  --bs-primary-hover-bg: #4d2588;   /* 10.85:1 */
  --bs-primary-active-bg: #441f7a;  /* 12.19:1 */
  --bs-link-color: var(--bs-primary);
  --bs-link-hover-color: #4d2588;
  --bs-focus-ring-color: rgba(var(--bs-primary-rgb), .25);
  --row-hover: rgba(var(--bs-primary-rgb), .11); /* pale purple hover in light theme */

  /* My Record / Recent Trend series colours — one source of truth for the donut, its
     legend swatches and the trend chips. Before these, the same three colours were
     restated in three files that referenced none of each other: readColors() in
     dashboard.js, the .bg-danger/.bg-secondary utilities on the legend, and the
     .trend-chip rules below.

     Stored as bare R,G,B triples, and that is load-bearing rather than stylistic:
     readColors() builds BOTH the solid `rgba(x,1)` and the .25 hover dim out of the same
     string, exactly the way --bs-primary-rgb is used above. Storing a hex here still
     paints the solid arcs correctly and silently kills every dim, which is the failure
     drawEmphasis shows and nothing else can see. test_record_colors.py asserts the
     format for that reason.

     Values are existing design_tokens.css rungs, per the ramp rule in the dark block
     below — that file is loaded on every app page (base.html:44-45), but it stores hex
     only, and CSS cannot decompose a hex into the triple this needs. Hence the mirror,
     with the rung named in each comment so a reskin knows both ends. */
  --record-win-rgb:  200, 217, 62;   /* #C8D93E — --iil-accent, tennis green */
  --record-loss-rgb: 175, 169, 236;  /* #AFA9EC — --iil-purple-300 */
  /* Tie aliases the neutral this file already declares for ties (the --bs-info block
     just below). It needs no dark override — --bs-info-rgb has one, and var() resolves
     through it per theme. This is the set's one indirection and it is deliberate: the
     alternative is restating --bs-info-rgb's two values as literals, which is the exact
     drift these tokens exist to end. It costs two explicit checks, both of which exist:
     the test's resolver follows the hop rather than regexing the specified value, and
     the dark tie segment is smoke-checked in a real engine (it must paint 156,163,175,
     not the light fallback). */
  --record-tie-rgb:  var(--bs-info-rgb);

  /* Elevation */
  --elev-1: 0 2px 8px rgba(16,24,40,.08), 0 1px 1px rgba(16,24,40,.06);
  --elev-2: 0 6px 24px rgba(16,24,40,.16), 0 2px 6px rgba(16,24,40,.10);

  /* Glass */
  --glass-bg: rgba(255,255,255,.72);
  --glass-border: rgba(255,255,255,.35);
  --glass-hairline: rgba(0,0,0,.06);

  /* Muted text */
  --muted: rgba(17,24,39,.62);
  --muted-sub: rgba(17,24,39,.45);

  /* Text tokens used by dark helpers */
  --text-on-dark: #e8e8e8;
  --text-on-dark-strong: #ffffff;

  /* Donut center color (light) */
  --donut-cutout: #ffffff;
}
/* Use neutral gray for "info" (ties, neutral badges) */
:root{
  --bs-info: #6b7280;              /* slate-500 */
  --bs-info-rgb: 107, 114, 128;
}
html[data-theme="dark"]{
  --bs-info: #9ca3af;              /* slate-400 */
  --bs-info-rgb: 156, 163, 175;
}
html[data-theme="dark"]{
  --bs-primary: #c6b0f0;           /* lighter purple for readability */
  --bs-primary-rgb: 198, 176, 240;
  /* The dark half of the set declared in :root. The ink is near-black purple, and the
     button *lightens* on hover rather than darkening — the light theme's #4d2588 /
     #441f7a are dark purples, so reusing them here would put near-black ink on
     near-black purple. Lightening is also the right direction for a light control on a
     dark page. Every value below is an existing rung of design_tokens.css's ramp, not a
     new colour: two one-off hexes in this block is how a token system starts to drift.
     Contrast rises monotonically through the states, so no state is harder to read than
     the button at rest. */
  --bs-primary-ink: var(--iil-purple-900);       /* 8.35:1 on #c6b0f0 */
  --bs-primary-hover-bg: var(--iil-purple-200);  /* 10.37:1 */
  --bs-primary-active-bg: var(--iil-purple-100); /* 13.97:1 */

  /* Bootstrap's own colour variables, which this theme never defined until now.
     Bootstrap 5.3 applies `color: var(--bs-body-color)` DIRECTLY on .card, and
     `background: var(--bs-body-bg)` on .popover / .card / .dropdown-menu. An explicit
     declaration on the element beats an inherited one no matter how specific the
     ancestor rule is, so `html[data-theme="dark"] body{ color: ... }` below never
     reached anything inside a card: unclassed text rendered at Bootstrap's LIGHT
     default (#212529) on dark glass, at 1.17:1. That is what made the dashboard
     empty states unreadable, and it is the root of the per-class patch list further
     down this file — .text-muted, .form-text, .text-body-secondary and the rest all
     escape only because they carry !important.

     Colour and background must move together. Setting the colour alone would leave
     .popover at --bs-body-bg (#fff) with near-white text on it.

     Surfaces that already carry an explicit dark override — .modal-content, the form
     controls, .toast, .offcanvas, .dropdown-menu, .pagination, .popover, .glass-card,
     .glass-table — are unaffected; their own declarations still win. This reaches
     exactly the surfaces nobody had patched yet, which is the set that was broken.

     Measured against the real stylesheets: unclassed text inside a card went from
     1.17:1 to 10.20:1, and a .table without .glass-table went from black-on-white to
     white-on-#12141a. */
  --bs-body-color: rgba(255,255,255,.92);
  --bs-body-color-rgb: 255, 255, 255;
  --bs-body-bg: #12141a;
  --bs-body-bg-rgb: 18, 20, 26;
  /* .table reads BOTH of these: --bs-emphasis-color for cell text, and
     --bs-emphasis-color-rgb for the striped / hover / active row washes. They have to
     move together or a table gets white text over black-tinted stripes. */
  --bs-emphasis-color: #ffffff;
  --bs-emphasis-color-rgb: 255, 255, 255;
  /* These four change nothing visible today — every component in this app that reads
     them (.form-text, .text-body-tertiary, disabled inputs, .input-group-text,
     .list-group) already carries its own dark patch further down this file. They are
     here because completing the set is the point: the next Bootstrap component
     someone drops into a template resolves through these, and without them it
     arrives broken in dark mode exactly the way the empty states did. */
  --bs-secondary-color: var(--muted);
  --bs-tertiary-color: var(--muted-sub);
  --bs-secondary-bg: #1b1e2a;
  --bs-tertiary-bg: #171a23;

  --glass-bg: rgba(30,34,45,.62);
  --glass-border: rgba(255,255,255,.18);
  --glass-hairline: rgba(255,255,255,.12);
  --muted: rgba(255,255,255,.74);
  --muted-sub: rgba(255,255,255,.55);
  --donut-cutout: #171a23;
  --row-hover: rgba(var(--bs-primary-rgb), .10); /* slightly stronger for dark */

  /* Only the loss series moves. --record-win-rgb is the accent, which is legible on both
     grounds and is deliberately theme-invariant, and --record-tie-rgb is an alias that
     follows --bs-info-rgb's own dark value above — redeclaring either here would be a
     second copy that could drift from the first. */
  --record-loss-rgb: 60, 52, 137;    /* #3C3489 — --iil-purple-700, quiet on dark */
}

/* ——— Components ——————————————————————————————— */
/* Glass card */
.glass-card{
  position: relative;
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: 16px;
  background-clip: padding-box;
  box-shadow: var(--elev-1);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
  transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  will-change: auto; /* avoid GPU nudge unless needed */
  isolation: isolate;
  overflow: visible;
}

html[data-theme="dark"] .glass-card {
  background: rgba(23,26,35,.35);
  border-color: rgba(125, 86, 200, .28);
}
.glass-card:hover {
  transform: translateY(-2px);
  box-shadow:
    0 10px 22px rgba(17, 12, 28, .18),
    0 3px 8px rgba(17, 12, 28, .12);
  border-color: rgba(125, 86, 200, .32);
}
.glass-card:focus-within {
  outline: none;
  box-shadow:
    0 0 0 3px rgba(var(--bs-primary-rgb), .1),
    0 12px 24px rgba(17, 12, 28, .16);
}

/* optional spotlight using the --mx/--my vars the JS sets */
 .glass-card::before {
  content: "";
  position: absolute; inset: 0;
  background: radial-gradient(220px 220px at var(--mx,50%) var(--my,50%),
              rgba(125, 86, 200, .18), transparent 60%);
  opacity: .0;
  transition: opacity .18s ease;
  border-radius: inherit;
  pointer-events: none;
}
.glass-card:hover::before { opacity: .7; }
.glass-card::after{ content:""; position:absolute; inset:0; border-radius:inherit; pointer-events:none; box-shadow: inset 0 0 0 1px var(--glass-hairline); }
/* Opt-in: only cards with .hover-lift get lift effect */
.glass-card.hover-lift{ transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease; will-change: transform; }
.glass-card.hover-lift:hover{ transform: translateY(-2px); box-shadow: var(--elev-2); border-color: rgba(255,255,255,.5); z-index:5; }
.glass-card.hover-lift:active{ transform: translateY(0); box-shadow: var(--elev-1); }

/* Welcome checklist card (backlog 145) — layout only; the brand colors on the
   header band are the two fixed hex values from the brief and stay inline. */
.welcome-card-header{
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
}
/* Absent entirely (not just hidden) when the member has no team, so flex gap
   never reserves the 48px it would otherwise sit in. */
.welcome-header-mark{ flex-shrink: 0; }
/* Overrides .iil-team-page-logo / .iil-team-panel-badge's My-Team sizing
   (160px / 120px) down to a 48px circular mark for this compact header —
   same logo-or-monogram resolution, different context. */
.welcome-header-mark .iil-team-page-logo{
  width: 48px; height: 48px; max-width: 48px; max-height: 48px;
  border-radius: 50%; object-fit: cover;
}
.welcome-header-mark .iil-team-panel-badge{
  width: 48px; height: 48px; border-radius: 50%; font-size: 1rem;
}
.welcome-header-greeting{ flex: 1; min-width: 0; }
.welcome-header-greeting h5{ font-size: 17px; }
.welcome-header-subline{ font-size: 13px; }
.welcome-header-close{ flex-shrink: 0; }
/* .btn-close is themed globally for dark mode (html[data-theme="dark"]
   .btn-close), assuming a light card behind it — wrong assumption here, since
   this header is always the same dark purple regardless of site theme.
   !important wins over that unrelated rule rather than depending on which one
   happens to sit later in the cascade. */
.welcome-header-close{ filter: invert(1) grayscale(100%) brightness(200%) !important; }
.welcome-row{ border-bottom: 0.5px solid var(--glass-hairline); } /* this repo's "--border" equivalent */
.welcome-row:last-child{ border-bottom: none; }


/* Empty states
   Four classes that read as a deliberate component in the markup and had no CSS rule
   anywhere in the repo until now — they rendered as unstyled stacked divs at body
   size. Both themes get their colour from the --muted tokens rather than a literal,
   so the dark values stay in one place with the rest of the theme. */
.empty-state{
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: .25rem;
  padding: 1.5rem 1rem;
}
.empty-icon{
  font-size: 2rem;
  line-height: 1;
  margin-bottom: .25rem;
  /* --muted-sub, not a dimmer value: an icon is a large graphic and only has to
     clear 3:1, but it is the thing that reads as "this box is empty on purpose"
     rather than "this box failed to load". */
  color: var(--muted-sub);
}
.empty-text{
  font-weight: 600;
  color: var(--muted);
}
.empty-sub{
  font-size: .875rem;
  color: var(--muted-sub);
  max-width: 32ch;
}


/* Lined titles */
.card-title--lined{ position:relative; padding-bottom:.25rem; }
.card-title--lined::after{ content:""; position:absolute; left:0; bottom:-2px; width:100%; height:1px; background: linear-gradient(90deg, rgba(0,0,0,.06), rgba(0,0,0,.12) 50%, rgba(0,0,0,.06)); }
html[data-theme="dark"] .card-title--lined::after{ background: linear-gradient(90deg, rgba(255,255,255,.06), rgba(255,255,255,.10) 50%, rgba(255,255,255,.06)); }

/* Glassy navbar */
.glass-nav{ position: sticky; top:0; z-index:1030; background: var(--glass-bg) !important; border-bottom: 1px solid var(--glass-border); box-shadow: var(--elev-1); backdrop-filter: saturate(140%) blur(10px); -webkit-backdrop-filter: saturate(140%) blur(10px); }
.glass-nav .nav-link{ color: inherit; opacity:.92; position:relative; }
.glass-nav .nav-link::after{ content:""; position:absolute; left:.5rem; right:.5rem; bottom:.25rem; height:2px; background: transparent; border-radius:2px; transition: background .18s ease; }
.glass-nav .nav-link:hover::after, .glass-nav .nav-link.active::after{ background: var(--bs-primary); }
.glass-nav .nav-link.active{ background-color: rgba(var(--bs-primary-rgb), .12); color: var(--bs-primary); border-radius:.5rem; }
html[data-theme="dark"] .glass-nav .nav-link.active{ background-color: rgba(255,255,255,.10); color:#fff; }
.glass-nav .dropdown-menu{ box-shadow: var(--elev-1); border: 1px solid var(--glass-border); backdrop-filter: saturate(140%) blur(8px); -webkit-backdrop-filter: saturate(140%) blur(8px); background: var(--glass-bg); }
.glass-nav .nav-item.dropdown.show > .nav-link,
.glass-nav .nav-link.dropdown-toggle[aria-expanded="true"],
.glass-nav .nav-link:focus{ background-color: rgba(var(--bs-primary-rgb), .10); color: var(--bs-primary); border-radius:.5rem; }

/* Theme toggle + context switcher — ensure icon is purple in light, white in dark */
html:not([data-theme="dark"]) #themeToggle,
html:not([data-theme="dark"]) #contextSwitcher{
  border-color: rgba(var(--bs-primary-rgb), .6);
  color: var(--bs-primary);
  background: transparent;
}
html:not([data-theme="dark"]) #themeToggle:hover,
html:not([data-theme="dark"]) #contextSwitcher:hover{ background: rgba(var(--bs-primary-rgb), .10); }
html:not([data-theme="dark"]) #themeToggle .bi,
html:not([data-theme="dark"]) #contextSwitcher .bi{ color: var(--bs-primary); }
html[data-theme="dark"] #themeToggle,
html[data-theme="dark"] #contextSwitcher{
  border-color: rgba(255,255,255,.6);
  color: #fff;
  background: transparent;
}
html[data-theme="dark"] #themeToggle:hover,
html[data-theme="dark"] #contextSwitcher:hover{ background: rgba(255,255,255,.12); }
html[data-theme="dark"] #themeToggle .bi,
html[data-theme="dark"] #contextSwitcher .bi{ color: #fff; }

/* Buttons — ensure Bootstrap picks up our purple tokens.
   The --bs-btn-*-color lines are not decoration: Bootstrap's own default is #fff, and
   it arrives without ever appearing in this file, so the dark theme's light purple
   carried white text at 1.93:1 with nothing here to grep for. */
.btn-primary{
  --bs-btn-bg: var(--bs-primary);
  --bs-btn-border-color: var(--bs-primary);
  --bs-btn-color: var(--bs-primary-ink);
  --bs-btn-hover-bg: var(--bs-primary-hover-bg);
  --bs-btn-hover-border-color: var(--bs-primary-hover-bg);
  --bs-btn-hover-color: var(--bs-primary-ink);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
  --bs-btn-active-bg: var(--bs-primary-active-bg);
  --bs-btn-active-border-color: var(--bs-primary-active-bg);
  --bs-btn-active-color: var(--bs-primary-ink);
  --bs-btn-disabled-bg: var(--bs-primary);
  --bs-btn-disabled-border-color: var(--bs-primary);
  --bs-btn-disabled-color: var(--bs-primary-ink);
}
.btn-outline-primary{
  --bs-btn-color: var(--bs-primary);
  --bs-btn-border-color: var(--bs-primary);
  --bs-btn-hover-bg: var(--bs-primary);
  --bs-btn-hover-border-color: var(--bs-primary);
  /* Hover fills with --bs-primary, so hover text has the same problem the solid
     button had — in a state that only exists under the cursor, which is why it went
     unnoticed for as long as the solid one did. */
  --bs-btn-hover-color: var(--bs-primary-ink);
  --bs-btn-active-bg: var(--bs-primary);
  --bs-btn-active-border-color: var(--bs-primary);
  --bs-btn-active-color: var(--bs-primary-ink);
}

/* Bootstrap paints these from --bs-primary-rgb and writes white text itself, so like
   the button colours above they had no representation in this file at all. Used on the
   dashboard, the playoffs admin page and the eligibility partial. */
.badge.bg-primary,
.text-bg-primary{
  color: var(--bs-primary-ink) !important;
}

/* Profile pill */
.profile-link{ display:flex; align-items:center; gap:.35rem; padding:.25rem .6rem; border-radius:999px; text-decoration:none !important; color:inherit; transition: background-color .18s ease, color .18s ease; }
/* Public-page header greeting: same layout as .profile-link, but non-interactive */
.header-user-simple{ display:flex; align-items:center; gap:.35rem; padding:.25rem .6rem; cursor:default; }
.profile-link:hover, .profile-link:focus{ background-color: rgba(var(--bs-primary-rgb), .12); color: var(--bs-primary); }
html[data-theme="dark"] .profile-link:hover, html[data-theme="dark"] .profile-link:focus{ background-color: rgba(255,255,255,.10); color:#fff; }

/* Notification bell */
.glass-nav .notif-link .bi{ font-size:1.35rem; color: var(--bs-primary); line-height:1; }
html[data-theme="dark"] .glass-nav .notif-link .bi{ color:#fff; }
.glass-nav .notif-link{ position:relative; padding-right:.75rem; }
.glass-nav .notif-link .notif-badge{ position:absolute; right:-.20rem; top:.10rem; font-size:.70rem; box-shadow:0 0 0 2px var(--glass-bg); }

/* Footer */
.glass-footer{ position:fixed; left:0; right:0; bottom:0; z-index:1020; background: linear-gradient(180deg, rgba(40,22,78,.86), rgba(40,22,78,.96)); border-top:1px solid rgba(255,255,255,.20); box-shadow: 0 -6px 20px rgba(15,23,42,.12); backdrop-filter: saturate(140%) blur(10px); -webkit-backdrop-filter: saturate(140%) blur(10px); color:#fff; }
html[data-theme="dark"] .glass-footer{ background: linear-gradient(180deg, rgba(40,22,78,.86), rgba(40,22,78,.96)); border-top-color: rgba(255,255,255,.20); }
.glass-footer small, .glass-footer span{ color:#fff; }
.glass-footer .brand-chunk{ display:inline-flex; align-items:center; gap:.5rem; }
.glass-footer .brand-chunk svg{ height:1em; width:1em; vertical-align:-.125em; filter: drop-shadow(0 1px 0 rgba(0,0,0,.15)); }
/* Legal links. The rule above colours `small` and `span` white but not `a`, so without
   this the links render in the default link colour on a dark purple bar and are all but
   invisible — and these are the links a carrier reviewer is looking for. */
.glass-footer .footer-legal a{ color:#fff; text-decoration:underline; text-underline-offset:.15em; opacity:.85; }
.glass-footer .footer-legal a:hover, .glass-footer .footer-legal a:focus{ color:#fff; opacity:1; }

/* ——— Dark mode global styles (restored) ——— */
html[data-theme="dark"] body{
  background: #12141a;
  color: rgba(255,255,255,.92);
}
html[data-theme="dark"] .bg-light{ background-color: #131622 !important; }
html[data-theme="dark"] .dropdown-menu{
  background-color: #1b1e2a;
  color: rgba(255,255,255,.9);
  border-color: rgba(255,255,255,.12);
}
html[data-theme="dark"] .dropdown-item{ color: rgba(255,255,255,.9); }
html[data-theme="dark"] .dropdown-item:hover{ background-color: rgba(255,255,255,.06); }
html[data-theme="dark"] .text-muted{ color: var(--muted) !important; }
html[data-theme="dark"] .text-secondary{ color: var(--muted) !important; }
html[data-theme="dark"] small,
html[data-theme="dark"] .small{ color: var(--muted-sub) !important; }
/* Bootstrap form help text — used by {{ field.help_text }} and .form-text captions */
html[data-theme="dark"] .form-text{ color: var(--muted) !important; }
/* Bootstrap 5.3 semantic muted classes — used in modals and inline captions */
html[data-theme="dark"] .text-body-secondary{ color: var(--muted) !important; }
html[data-theme="dark"] .text-body-tertiary{ color: var(--muted-sub) !important; }
html[data-theme="dark"] a,
html[data-theme="dark"] .nav-link{ color: rgba(255,255,255,.92); }
html[data-theme="dark"] a:hover,
html[data-theme="dark"] .nav-link:hover{ color: #fff; }
/* Outline buttons on dark */
html[data-theme="dark"] .btn-outline-primary{
  --bs-btn-color: rgba(255,255,255,.92);
  --bs-btn-border-color: rgba(255,255,255,.65);
  --bs-btn-hover-bg: var(--bs-primary);
  --bs-btn-hover-border-color: var(--bs-primary);
  --bs-btn-active-bg: var(--bs-primary);
  --bs-btn-active-border-color: var(--bs-primary);
}
html[data-theme="dark"] .btn-outline-secondary{
  --bs-btn-color: rgba(255,255,255,.92);
  --bs-btn-border-color: rgba(255,255,255,.45);
  --bs-btn-hover-bg: rgba(255,255,255,.16);
  --bs-btn-hover-border-color: rgba(255,255,255,.55);
  --bs-btn-active-bg: rgba(255,255,255,.2);
  --bs-btn-active-border-color: rgba(255,255,255,.6);
}
html[data-theme="dark"] .btn-outline-light{
  --bs-btn-color: rgba(255,255,255,.95);
  --bs-btn-border-color: rgba(255,255,255,.7);
  --bs-btn-hover-bg: rgba(255,255,255,.15);
  --bs-btn-hover-border-color: rgba(255,255,255,.85);
}


/* Ripple */
.rippleable{ position:relative; overflow:hidden; }
.rippleable .ripple{ position:absolute; border-radius:50%; pointer-events:none; transform: scale(0); opacity:.35; background: rgba(var(--bs-primary-rgb), .45); animation: ripple .6s ease-out; }
@keyframes ripple{ to{ transform: scale(3.2); opacity:0; } }

/* Focus ring */
:where(a, button, .btn, .nav-link, .dropdown-item):focus{ outline: 2px solid rgba(var(--bs-primary-rgb), .6); outline-offset: 2px; box-shadow: none !important; }

/* Optional utilities */
.text-muted-sub{ color: var(--muted-sub) !important; }
.elev-1{ box-shadow: var(--elev-1) !important; }
.elev-2{ box-shadow: var(--elev-2) !important; }

/* Badge legibility in dark mode */
html[data-theme="dark"] .badge.bg-light,
html[data-theme="dark"] .text-bg-light{
  background-color: rgba(255,255,255,.18) !important;
  color: #fff !important;
}

/* Heading contrast in dark mode */
html[data-theme="dark"] h1,
html[data-theme="dark"] h2,
html[data-theme="dark"] h3,
html[data-theme="dark"] h4,
html[data-theme="dark"] h5,
html[data-theme="dark"] h6{ color:#fff !important; }

/* Make glass tables inherit the card (so they’re not white in dark mode) */
.table-glass-transparent{
  --bs-table-bg: transparent;
}
.table-glass-transparent > :not(caption) > * > *{
  background-color: transparent;
}

/* Season selector theming (scoped hook so we don’t affect every select) */
html[data-theme="dark"] .theme-select{
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.9);
  border-color: var(--glass-border);
}
html[data-theme="dark"] .theme-select:focus{
  box-shadow: 0 0 0 .2rem rgba(198,176,240,.20);
  border-color: var(--bs-primary);
}

/* Dark mode table text color */
html[data-theme="dark"] .glass-table th,
html[data-theme="dark"] .glass-table td {
  color: rgba(255,255,255,.92);
}
html[data-theme="dark"] .glass-table th {
  color: #fff;
  font-weight: 600;
}

/* Dark mode table captions and small text */
html[data-theme="dark"] .glass-table caption,
html[data-theme="dark"] .glass-table small,
html[data-theme="dark"] .glass-table .small {
  color: var(--muted-sub) !important;
}

/* Dark mode pagination */
html[data-theme="dark"] .pagination .page-link{
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.92);
  border-color: var(--glass-border);
}
html[data-theme="dark"] .pagination .page-link:hover{
  background-color: rgba(255,255,255,.08);
  color: #fff;
  border-color: var(--glass-border);
}
html[data-theme="dark"] .pagination .page-link:focus{
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .25);
  border-color: var(--bs-primary);
}
html[data-theme="dark"] .pagination .page-item.active .page-link{
  background-color: var(--bs-primary);
  border-color: var(--bs-primary);
  /* Was #fff, inside a dark-only rule — so the page you are actually on was the least
     readable number in the pager. */
  color: var(--bs-primary-ink);
}
html[data-theme="dark"] .pagination .page-item.disabled .page-link{
  background-color: rgba(30,34,45,.6);
  color: rgba(255,255,255,.45);
  border-color: var(--glass-border);
}

/* Dark mode form checks (checkboxes and radios) */
html[data-theme="dark"] .form-check-input {
  background-color: rgba(30,34,45,.82);
  border-color: var(--glass-border);
}
html[data-theme="dark"] .form-check-input:checked {
  /* Not var(--bs-primary): the tick is a white SVG that Bootstrap supplies as a
     background-image, so it cannot read --bs-primary-ink. Filling with the ramp's
     mid purple keeps a white mark at 6.93:1 instead of the 1.93:1 it had on the dark
     theme's light purple — a checkbox whose checked state was nearly invisible. */
  background-color: var(--iil-purple-600);
  border-color: var(--iil-purple-600);
}
html[data-theme="dark"] .form-check-input:focus {
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .25);
  border-color: var(--bs-primary);
}

/* Dark mode input groups */
html[data-theme="dark"] .input-group-text {
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.9);
  border-color: var(--glass-border);
}

/* ——— Dark mode alerts ——— */
html[data-theme="dark"] .alert{
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.92);
  border-color: var(--glass-border);
}
/* Contextual alert tints */
html[data-theme="dark"] .alert-primary{
  background-color: rgba(var(--bs-primary-rgb), .18);
  border-color: rgba(var(--bs-primary-rgb), .35);
  color: #fff;
}
html[data-theme="dark"] .alert-success{
  background-color: rgba(var(--bs-success-rgb), .18);
  border-color: rgba(var(--bs-success-rgb), .35);
  color: #d1fae5; /* soft minty text for contrast */
}
html[data-theme="dark"] .alert-danger{
  background-color: rgba(var(--bs-danger-rgb), .18);
  border-color: rgba(var(--bs-danger-rgb), .35);
  color: #fee2e2;
}
html[data-theme="dark"] .alert-warning{
  background-color: rgba(var(--bs-warning-rgb), .18);
  border-color: rgba(var(--bs-warning-rgb), .38);
  color: #fef3c7;
}
html[data-theme="dark"] .alert-info{
  background-color: rgba(var(--bs-info-rgb), .18);
  border-color: rgba(var(--bs-info-rgb), .35);
  color: #dbeafe;
}
html[data-theme="dark"] .alert-secondary{
  background-color: rgba(148,163,184,.18); /* slate-ish */
  border-color: rgba(148,163,184,.35);
  color: rgba(255,255,255,.92);
}
html[data-theme="dark"] .alert-light{
  background-color: rgba(255,255,255,.14);
  border-color: rgba(255,255,255,.28);
  color: #fff;
}
html[data-theme="dark"] .alert-dark{
  background-color: rgba(17,20,28,.82);
  border-color: rgba(255,255,255,.18);
  color: rgba(255,255,255,.92);
}

/* ——— Dark mode modals ——— */
html[data-theme="dark"] .modal-content{
  background-color: rgba(30,34,45,.90);
  color: rgba(255,255,255,.92);
  border: 1px solid var(--glass-border);
  box-shadow: var(--elev-2);
  backdrop-filter: saturate(140%) blur(8px);
  -webkit-backdrop-filter: saturate(140%) blur(8px);
}
html[data-theme="dark"] .modal-header,
html[data-theme="dark"] .modal-footer{
  border-color: var(--glass-border);
}
html[data-theme="dark"] .modal-title{ color:#fff; }

/* Close button visible on dark */
html[data-theme="dark"] .btn-close{
  filter: invert(1) grayscale(100%) opacity(.85);
}

/* Dim the backdrop a touch more on dark */
html[data-theme="dark"] .modal-backdrop.show{
  opacity: .6;
  background-color: #0b0e14;
}

/* ——— Toasts ——— */
.toast{
  border-color: var(--glass-border);
  box-shadow: var(--elev-1);
  backdrop-filter: saturate(140%) blur(6px);
  -webkit-backdrop-filter: saturate(140%) blur(6px);
}
.toast-header{
  border-bottom-color: var(--glass-border);
}
html:not([data-theme="dark"]) .toast{
  background-color: rgba(255,255,255,.86);
}
html[data-theme="dark"] .toast{
  background-color: rgba(30,34,45,.90);
  color: rgba(255,255,255,.92);
}
html[data-theme="dark"] .toast-header{
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.9);
  border-bottom-color: var(--glass-border);
}
html[data-theme="dark"] .btn-close.toast{
  filter: invert(1) grayscale(100%) opacity(.85);
}

/* ——— Offcanvas ——— */
.offcanvas{
  border-left: 1px solid var(--glass-border);
  border-right: 1px solid var(--glass-border);
  box-shadow: var(--elev-2);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
}
html:not([data-theme="dark"]) .offcanvas{
  background-color: rgba(255,255,255,.86);
}
html[data-theme="dark"] .offcanvas{
  background-color: rgba(30,34,45,.92);
  color: rgba(255,255,255,.92);
}
html[data-theme="dark"] .offcanvas-header,
html[data-theme="dark"] .offcanvas-footer{
  border-color: var(--glass-border);
}
html[data-theme="dark"] .offcanvas .btn-close{
  filter: invert(1) grayscale(100%) opacity(.85);
}
/* Dark mode: global select & input overrides */
html[data-theme="dark"] select.form-select,
html[data-theme="dark"] input.form-control,
html[data-theme="dark"] textarea.form-control {
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.92);
  border-color: var(--glass-border);
}

html[data-theme="dark"] select.form-select:focus,
html[data-theme="dark"] input.form-control:focus,
html[data-theme="dark"] textarea.form-control:focus {
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .25);
}

/* Light mode inputs (glassy) */
input.form-control,
select.form-select,
textarea.form-control {
  background-color: var(--glass-bg);
  color: inherit;
  border: 1px solid var(--glass-border);
  box-shadow: var(--elev-1);
  backdrop-filter: saturate(140%) blur(6px);
  -webkit-backdrop-filter: saturate(140%) blur(6px);
}

/* Focus states (light mode) */
:where(.form-control, .form-select):focus {
  border-color: var(--bs-primary);
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .25);
  background-color: var(--glass-bg);
}

/* Input group aesthetics */
.input-group .form-control {
  background-color: var(--glass-bg);
  border-color: var(--glass-border);
}
.input-group .btn,
.input-group-text {
  background-color: var(--glass-bg);
  border-color: var(--glass-border);
}
.input-group .btn:hover {
  background-color: rgba(var(--bs-primary-rgb), .06);
}
/* Password eye button stability */
.input-group .toggle-password {
  min-width: 2.5rem; /* reserve width so icon swap doesn’t jiggle */
}
.input-group .toggle-password i {
  display: inline-block;
  width: 1.25em; /* fixed glyph width */
  text-align: center;
}
.input-group .toggle-password:focus {
  outline: none;
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
}
/* Override Bootstrap's info badge to neutral gray */
.badge.bg-info,
.text-bg-info {
  background-color: var(--bs-info) !important;
  color: #fff !important;
}

/* Dark mode badge tweak (slightly lighter gray for contrast) */
html[data-theme="dark"] .badge.bg-info,
html[data-theme="dark"] .text-bg-info {
  background-color: var(--bs-info) !important;
  color: #fff !important;
}

/* Force "info" text to neutral gray (for Tie labels, etc.) */
.text-info{
  color: rgba(var(--bs-info-rgb), 1) !important;
}
a.text-info:hover,
a.text-info:focus{
  color: rgba(var(--bs-info-rgb), 1) !important;
}
/* ——— Glass list groups (light & dark) ——— */
.glass-card .list-group { background: transparent; }
.glass-card .list-group-item {
  background-color: transparent;
  border-color: var(--glass-hairline);
  color: inherit; /* follow card text color */
}

/* Hover/active states */
.list-group-item-action:hover,
.list-group-item-action:focus {
  background-color: rgba(var(--bs-primary-rgb), .06);
}
.list-group-item.active {
  background-color: var(--bs-primary);
  border-color: var(--bs-primary);
  color: var(--bs-primary-ink);
}

/* Dark mode tweaks */
html[data-theme="dark"] .glass-card .list-group-item {
  color: rgba(255,255,255,.92);
  border-color: var(--glass-border);
}
html[data-theme="dark"] .list-group-item-action:hover,
html[data-theme="dark"] .list-group-item-action:focus {
  background-color: rgba(255,255,255,.06);
}
.navbar-logo {
  height: 90px; /* adjust to taste */
  opacity: 1;
  transition: opacity 160ms ease-in-out;
}
.navbar-logo.is-fading { opacity: 0;
}

/* Team logo header on My Team — same responsive approach as .iil-hero-logo
   (public homepage), scaled down: context for the page, not the content. */
.iil-team-page-logo {
  width: 100%;
  max-width: 160px;
  height: auto;
  max-height: 160px;
  object-fit: contain;
}

/* Monogram fallback beside the logo upload on Manage Team, sized to match
   .iil-team-page-logo above. The public team card has its own badge in
   public_home.css, but base.html doesn't load that file, so authenticated pages
   need their own rule. The fill is Team.color, applied inline — team colors are
   per-row data and never CSS tokens. */
.iil-team-panel-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 120px;
  height: 120px;
  border-radius: 1rem;
  color: #fff;
  font-weight: 700;
  font-size: 2.25rem;
  letter-spacing: .02em;
  text-shadow: 0 1px 2px rgba(0,0,0,.28);
}

/* Navbar logo sizing & navbar height */
.navbar .navbar-brand { padding-top: 0; padding-bottom: 0; line-height: 1; }
.navbar .navbar-logo { height: 56px !important; width: auto; display: block; }
.glass-nav { padding-top: 8px; padding-bottom: 8px; min-height: 72px; }

@media (min-width: 992px) {
  .navbar .navbar-logo { height: 64px !important; }
  .glass-nav { min-height: 80px; }
}

/* Sticky, glassy table header */
.table-head-glass th {
  position: sticky;
  top: 0;
  z-index: 1025; /* above rows, below navbar (1030) */
  background: var(--glass-bg);
  backdrop-filter: saturate(140%) blur(8px);
  -webkit-backdrop-filter: saturate(140%) blur(8px);
  border-bottom: 1px solid var(--glass-border);
}

/* Handy utility we used in templates */
.minw-220 { min-width: 220px !important; }

/* Stronger, smarter focus styling */
:where(a, button, .btn, .nav-link, .dropdown-item):focus-visible {
  outline: 2px solid rgba(var(--bs-primary-rgb), .65);
  outline-offset: 2px;
  box-shadow: none !important;
}

/* Skip link for keyboard users (add <a href="#main" class="skip-link">Skip to content</a> in base.html) */
.skip-link {
  position: absolute; left: 0; top: -40px;
  background: var(--bs-primary); color: var(--bs-primary-ink); padding: .5rem .75rem; border-radius: .5rem;
  transition: top .15s ease; z-index: 2000;
}
.skip-link:focus { top: .5rem; }

/* Reduced motion support */
@media (prefers-reduced-motion: reduce) {
  .glass-card { transition: none; }
  .glass-card:hover { transform: none; }
  .rippleable .ripple { animation-duration: .01ms; animation-iteration-count: 1; }
}

/* Dark-mode validation on form controls */
html[data-theme="dark"] .form-control.is-invalid,
html[data-theme="dark"] .form-select.is-invalid {
  border-color: rgba(255, 99, 132, .75);
  background-image: none;
}
html[data-theme="dark"] .form-control.is-valid,
html[data-theme="dark"] .form-select.is-valid {
  border-color: rgba(34, 197, 94, .75);
  background-image: none;
}
tr.row-link {
  cursor: pointer;
  transition: background-color .2s ease, transform .2s ease;
}
tr.row-link:hover { background-color: var(--row-hover); }
html[data-theme="dark"] tr.row-link:hover { background-color: var(--row-hover);
}
/* Apply the same hover treatment to all Bootstrap table-hover rows */
.table-hover > tbody > tr:hover > * {
  background-color: var(--row-hover) !important;
}
tr.table-active > * {
  background-color: rgba(var(--bs-primary-rgb), .18) !important;
}
html[data-theme="dark"] tr.table-active > * {
  background-color: rgba(255,255,255, .14) !important;
}

/* —— Tooltips & Popovers (themed) —— */
.tooltip-inner{ background: rgba(30,34,45,.92); color:#fff; border:1px solid var(--glass-border); }
.tooltip .tooltip-arrow{ display:none; }
/* Drive popovers through Bootstrap's own custom properties rather than setting
   `background` directly: the arrow is drawn from --bs-popover-bg and the header
   strip from --bs-popover-header-bg, so overriding only .popover leaves both
   mismatched against the body. */
.popover{
  /* Fully opaque, unlike the tooltip: a popover can carry a dense table, and even a
     2% alpha lets the page's own table rules ghost through the numbers. */
  --bs-popover-bg: #1e222d;
  --bs-popover-header-bg: rgba(255,255,255,.06);
  --bs-popover-header-color: #fff;
  --bs-popover-body-color: #fff;
  --bs-popover-border-color: var(--glass-border);
  box-shadow: var(--elev-1);
}
html:not([data-theme="dark"]) .popover{
  --bs-popover-bg: #fff;
  --bs-popover-header-bg: rgba(0,0,0,.03);
  --bs-popover-header-color: #111827;
  --bs-popover-body-color: #111827;
  --bs-popover-border-color: rgba(0,0,0,.08);
}
html:not([data-theme="dark"]) .tooltip-inner{
  background: rgba(255,255,255,.95); color:#111827; border-color: rgba(0,0,0,.08);
}

/* —— Enter Scores legend popover —— */
.scoring-legend-popover{ --bs-popover-max-width: 320px; }

/* —— "How Propose Lineup works" popover —— */
/* Wider than the 276px default: the content is a six-item bulleted list, and at the
   default width every bullet wraps to three or four lines. */
.propose-help-popover{ --bs-popover-max-width: 360px; }
/* Scoped to .scoring-legend, never .table — the app's table chrome (glass background,
   sticky headers, row hover) has no business inside a popover. */
.scoring-legend{ width:100%; border-collapse:collapse; }
.scoring-legend th,
.scoring-legend td{ padding:.15rem 0; font-weight:400; }
.scoring-legend td{ text-align:right; padding-left:.75rem; font-variant-numeric: tabular-nums; }
.scoring-legend tbody th{ text-align:left; }
.scoring-legend thead th{
  font-weight:600; font-size:.75rem; text-transform:uppercase; letter-spacing:.02em;
  padding-bottom:.25rem; border-bottom:1px solid var(--glass-border); opacity:.75;
}
.scoring-legend thead th:not(:first-child){ text-align:right; }
.scoring-legend-note{
  margin-top:.6rem; padding-top:.5rem; border-top:1px solid var(--glass-border);
  font-size:.8rem; opacity:.8;
}
html:not([data-theme="dark"]) .scoring-legend thead th,
html:not([data-theme="dark"]) .scoring-legend-note{ border-color: rgba(0,0,0,.1); }

/* —— Scrollbars (subtle, consistent) —— */
*{ scrollbar-width: thin; scrollbar-color: rgba(var(--bs-primary-rgb),.35) transparent; }
::-webkit-scrollbar{ height:10px; width:10px; }
::-webkit-scrollbar-thumb{ background: rgba(var(--bs-primary-rgb),.35); border-radius: 8px; }
::-webkit-scrollbar-thumb:hover{ background: rgba(var(--bs-primary-rgb),.55); }

/* —— Content link hover (not nav) —— */
main a:hover{ text-decoration: underline; }

/* —— Print stylesheet (captain-friendly) —— */
@media print {
  .glass-nav, .glass-footer, .btn, .toast, .alert, .offcanvas { display:none !important; }
  .glass-card, .table { box-shadow:none !important; border:1px solid #ccc; background:#fff !important; }
  body { color:#000 !important; background:#fff !important; }
  .table thead th { position: static !important; background:#fff !important; }
}

/* —— Global interaction transitions —— */
.btn, .btn:focus, .btn:hover,
.nav-link, .dropdown-item,
.form-control, .form-select,
.list-group-item,
.card, .glass-card,
.table, .table-hover > tbody > tr > *,
.tooltip, .popover,
.profile-link,
.toast {
  transition: color .2s ease, background-color .2s ease,
              border-color .2s ease, box-shadow .2s ease;
}

/* Hint browsers about color schemes for native UI elements */
:root { color-scheme: light; }
html[data-theme="dark"] { color-scheme: dark; }

/* —— Table density utilities —— */
.table-compact > :not(caption) > * > * { padding: .5rem .6rem; }
.table-super-compact > :not(caption) > * > * { padding: .35rem .5rem; }

/* —— Mobile-friendly tables —— */
/* Smooth scrolling for horizontally scrollable tables */
.glass-card .table-responsive { -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
/* Make sticky headers play nicely inside responsive wrappers */
.glass-card .table-responsive .table thead th { position: sticky; top: 0; z-index: 1025; }

/* Font sizing & spacing tweaks on small screens */
@media (max-width: 576px) {
  .glass-card .table { font-size: .95rem; }
  .table-compact > :not(caption) > * > * { padding: .4rem .5rem; }
  .table-super-compact > :not(caption) > * > * { padding: .3rem .45rem; }
}

/* —— Optional helper: force a table to have a min width so columns don’t crush on mobile —— */
.table-min-640 { min-width: 640px; }
.table-min-768 { min-width: 768px; }

/* Prevent content from hiding under the fixed glass footer. Bumped from 64px when the
   footer gained a second row for the legal links; on a narrow viewport that row wraps
   again, hence the extra headroom. */
body { padding-bottom: 104px; }

/* Inline eye icon inside inputs */
.input-with-eye {
  position: relative;
}
.input-with-eye .form-control {
  padding-right: 2.5rem; /* room for the eye */
}
.input-with-eye .eye-btn {
  position: absolute;
  top: 50%;
  right: .6rem;
  transform: translateY(-50%);
  border: 0;
  background: transparent;
  padding: 0;
  line-height: 1;
  cursor: pointer;
}
.input-with-eye .eye-btn:focus {
  outline: none;
  box-shadow: 0 0 0 .2rem rgba(var(--bs-primary-rgb), .15);
  border-radius: .375rem; /* subtle focus halo */
}
.input-with-eye .eye-btn i {
  display: inline-block;
  width: 1.25em; /* fix width, no jiggle when swapping icons */
  text-align: center;
}
/* Safety sizing so hero/login images never blow up */
.login-hero-logo,
.footer-brand-logo,
.navbar-brand img {
  display: block;
  max-width: min(560px, 80vw);
  width: auto;
  height: auto;
}

/* Ensure auth buttons don’t look gigantic without Bootstrap */
.auth-card .btn {
  font-size: 1rem;
  padding: .6rem 1rem;
  line-height: 1.2;
}
/* Keep toasts above nav/modals */
#toast-stack {
  z-index: 1080; /* same as before, now CSP-safe */
  position: relative; /* ensures z-index takes effect */
}
/* Login page images (moved from inline styles) */
.login-title-img {
  max-height: 80px;
  object-fit: contain;
  height: auto;
  width: auto;
  display: inline-block; /* kept centered by .text-center wrapper */
}

.login-footer-logo {
  max-height: 250px;
  object-fit: contain;
  height: auto;
  width: auto;
  display: inline-block;
}

/* Optional safety cap so images never overflow if Bootstrap fails to load */
.login-title-img,
.login-footer-logo {
  max-width: 90vw;
}
/* ===== Dashboard utilities ===== */
.max-420 { max-width: 420px; }
/* No `.d-none { display: none !important }` here — see docs/BACKLOG.md 32. Bootstrap
   already defines it; redeclaring it in this file gave it equal specificity and a later
   source position, so it also beat every `d-{breakpoint}-*` show utility at every
   width. It was labelled "just in case", meaning a Bootstrap CDN failure — which it
   cannot help with, since the breakpoint utilities and the grid would be gone too. */
.z-1080 { z-index: 1080 !important; }

/* "Report a site problem" floating trigger.

   Every value here is set against the app's existing fixed-position stack, so change it
   only against that list:
     1020  .glass-footer   fixed full-width bottom band, 104px tall (body padding-bottom)
     1030  .glass-nav      sticky top
     1050  .navbar-collapse.show   mobile full-screen nav overlay
     1080  #toast-stack    fixed bottom-0 end-0 — the same corner as this button
     2000  .loader-overlay fixed inset-0

   1040 is the only value that gets all four relationships right: above the footer band it
   sits on, below the mobile nav so an open menu covers it rather than the button floating
   over the menu, below transient toasts, and below the loader. The bottom offset clears
   the 104px footer; toasts rise from bottom-0 into the space underneath, and they win on
   z-index for the moment they exist, which is the intended order.

   No print rule needed — it is a .btn, and theme.css's print block already hides those.

   Icon-only, and circular, on purpose. A text pill at this size is the tallest thing in
   the corner and reads as a banner; the icon keeps the same 44px tap target in a much
   lower profile, which is what makes it survive a narrow viewport without competing with
   the footer. `aria-label` and `title` carry the name — there is no visible text, so
   removing either leaves an unlabelled control.

   Colour: brand purple surface in both themes with a white glyph in light and the tennis
   green in dark, where a white glyph on purple sits flatter against the dark page. Lime
   is deliberately NOT the surface — filled lime is the primary-CTA treatment ("Play in
   IIL", "Join us"), and a utility control should not read at that weight. */
.feedback-fab {
  position: fixed;
  right: 1rem;
  /* Cleared against the *measured* footer, not the 104px body padding. That padding is
     deliberately sized for the tallest case, so anchoring to it left the button stranded
     in open space on a wide viewport.

     Rendered heights, Chromium with real Bootstrap and the authenticated footer (four
     legal links): 59px at >=576px, 88px below it, where the legal row wraps to two lines.
     The wrap happens exactly at Bootstrap's `sm` breakpoint, so one media query covers
     it. Both values leave ~12px of air above the footer. Re-measure if the legal row
     gains a link — a fifth would wrap earlier. */
  bottom: 72px;
  z-index: 1040;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: 50%;
  border: 1px solid transparent;
  background: var(--iil-purple-600);
  color: #fff;
  box-shadow: 0 4px 14px rgba(15, 23, 42, .28);
}
.feedback-fab:hover,
.feedback-fab:focus {
  background: var(--iil-purple-700);
  color: #fff;
}
.feedback-fab:focus-visible {
  outline: 3px solid var(--iil-purple-300);
  outline-offset: 2px;
}
html[data-theme="dark"] .feedback-fab {
  background: var(--iil-purple-800);
  color: var(--iil-accent);
  border-color: rgba(200, 217, 62, .38);
  box-shadow: 0 4px 14px rgba(0, 0, 0, .45);
}
html[data-theme="dark"] .feedback-fab:hover,
html[data-theme="dark"] .feedback-fab:focus {
  background: var(--iil-purple-700);
  color: var(--iil-accent);
}
html[data-theme="dark"] .feedback-fab:focus-visible {
  outline: 3px solid var(--iil-accent);
  outline-offset: 2px;
}
@media (max-width: 575.98px) {
  .feedback-fab { right: .75rem; bottom: 100px; }
}

/* Donut: layout + depth */
.donut-wrap { position: relative; padding: 8px 0 4px; }
.donut-chart { width: 280px; height: 280px; border-radius: 50%; position: relative; margin: 0 auto; box-shadow: 0 2px 10px rgba(15,23,42,.08) inset; }
.donut-chart::before {
  content:""; position:absolute; inset:-6px; border-radius:50%;
  background: radial-gradient(120% 120% at 30% 20%, rgba(255,255,255,.55), rgba(255,255,255,0) 46%),
              radial-gradient(120% 120% at 70% 80%, rgba(0,0,0,.06), rgba(0,0,0,0) 40%);
  filter: blur(.2px); pointer-events:none;
}
.donut-chart::after {
  content:""; position:absolute; inset:20%; background: var(--donut-cutout, #fff); border-radius:50%;
  box-shadow: inset 0 12px 30px rgba(0,0,0,.06), inset 0 -8px 16px rgba(0,0,0,.04);
}
/* The centre number is a win count, so it tracks the win series rather than the brand
   purple it tracked when win *was* purple. It cannot simply be rgb(var(--record-win-rgb)):
   the accent on the light cutout (#ffffff) is 1.56:1, unreadable at any size, though it is
   11.13:1 on the dark cutout. --iil-accent-strong is the theme-aware pair design_tokens.css
   already declares for exactly this — #2A3505 on light, the accent itself on dark. */
.donut-center { position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); font-size:60px; color: var(--iil-accent-strong); line-height:1; }
.donut-center-sub { position:absolute; left:50%; top:calc(50% + 42px); transform:translateX(-50%); font-size:12px; color:#6c757d; letter-spacing:.4px; text-transform:uppercase; }
#myRecordLegend { display:flex; align-items:center; justify-content:center; gap:18px; margin-top:10px; }
.legend-item { cursor:pointer; padding:4px 8px; border-radius:8px; transition: background-color .15s ease; }
.legend-item:hover { background-color: rgba(0,0,0,.04); }
.legend-swatch { display:inline-block; width:10px; height:10px; border-radius:2px; margin-right:6px; vertical-align:middle; }
/* The swatches legend arcs that readColors() paints from --record-*-rgb, so they read the
   same tokens. They used .bg-primary/.bg-danger/.bg-secondary, which agreed with the JS
   only by coincidence and could not have followed it anywhere. */
.record-swatch-win  { background: rgb(var(--record-win-rgb)); }
.record-swatch-loss { background: rgb(var(--record-loss-rgb)); }
.record-swatch-tie  { background: rgb(var(--record-tie-rgb)); }
.donut-wrap.hovering { filter: drop-shadow(0 8px 16px rgba(0,0,0,.12)); transform: translateY(-1px); transition: filter .15s ease, transform .15s ease; }

/* Donut hover-hit arcs */
.donut-hit { position:absolute; inset:0; display:flex; align-items:center; justify-content:center; pointer-events:none; }
.donut-svg { width:280px; height:280px; }
.donut-arc { pointer-events: stroke; cursor: pointer; }

/* Tiles */
.tile-link { display:block; height:100%; }
@media (min-width: 992px) { .tile-tall { min-height:460px; } }
.dashboard-first-row { margin-top:24px; padding-top:8px; }
@media (min-width: 992px) { .dashboard-first-row { margin-top:28px; padding-top:10px; } }

/* Avatar + trend chips */
.avatar { width:72px; height:72px; border-radius:50%; display:flex; align-items:center; justify-content:center; color: var(--bs-primary-ink); background: var(--bs-primary); font-weight:700; font-size:28px; box-shadow: inset 0 0 0 2px rgba(255,255,255,.35); }
.trend { display:flex; gap:6px; }
.trend-chip { width:18px; height:6px; border-radius:4px; opacity:.9; }
.trend-chip.win { background: rgb(var(--record-win-rgb)); }
.trend-chip.loss { background: rgb(var(--record-loss-rgb)); }
.trend-chip.tie { background: rgb(var(--record-tie-rgb)); }

/* Big gradient numbers */
.big-number { background: linear-gradient(180deg, var(--bs-primary), #5a34ad); -webkit-background-clip:text; background-clip:text; color:transparent; }

/* Focus ring + buttons */
.btn { border-radius: 999px; }
.btn-primary:focus-visible, .btn-outline-primary:focus-visible { box-shadow: 0 0 0 .25rem rgba(var(--bs-primary-rgb), .25); }

/* Reduce motion */
@media (prefers-reduced-motion: reduce){ .glass-card, .glass-card:hover { transition:none; transform:none; } }

/* Theme variables used by donut center background */
html[data-theme="dark"] .donut-chart::after {
  background: var(--donut-cutout, #171a23);
  box-shadow: inset 0 12px 24px rgba(0,0,0,.28), inset 0 -8px 16px rgba(0,0,0,.20);
}
/* tables that scroll within a card */
.table-scroll-70 { max-height: 70vh; overflow: auto; }

/* ...but not on a phone. 70vh inside an ~844px viewport is a ~590px inner scroll region
   competing with the page for momentum --- a second scroll surface, which is the whole
   defect. admin_scores.css said this first, for one screen, and named the reason.

   **max-height only. `overflow` is deliberately left alone.** All 18 sites in the tree
   are `class="table-responsive table-scroll-70"` --- the two are never split --- and
   `.table-responsive` is what supplies `overflow-x: auto`. A same-specificity
   `overflow: visible` loading later wins on overflow-x and would kill horizontal
   scrolling on all sixteen templates at once, pushing every wide table into page-level
   horizontal overflow. Restacking those tables into cards is the job of the ships after
   this one; until then the x-scroll is what makes them usable at all.

   With no max-height the container is content-height, so nothing scrolls vertically
   inside it. The sticky thead is unaffected either way: its selector
   (`.glass-card .table-responsive .table thead th`, above) only applies *inside*
   `.table-responsive`, and that wrapper is a scroll container on both axes on its own ---
   `overflow-x: auto` with `overflow-y` at its initial `visible` computes the visible axis
   to `auto`. So the header's nearest scrolling ancestor is that wrapper before this rule
   and after it, and it can never resolve to the viewport and slide under the sticky
   .glass-nav.

   767.98px is Bootstrap's md boundary, matching admin_scores.css and the d-md-*
   utilities in manage_scores.html. Two breakpoints that nearly agree is a worse bug than
   one that is wrong. */
@media (max-width: 767.98px) {
  .table-scroll-70 { max-height: none; }
}

/* A page header that puts an h1 and its controls in one flex row has no room at 390px.
   Six screens ended up pushing the *document* wider than the viewport this way — not a
   table scrolling inside its wrapper, but the whole page moving under the reader's
   thumb. `docs/MOBILE_AUDIT.md` measured My Results at +106px, and the season picker's
   `minw-220` select is 294px of it.

   This is `admin_scores.css`'s `.admin-scores-head` promoted to a shared class rather
   than copied: that rule was the working prototype and is now deleted, so there is one
   answer instead of two drifting ones.

   `> div` as well as `> form`, and both halves are load-bearing — the seven headers that
   take this class are three different shapes:
     * h1 + form                     — schedule_list, my_results (+ a stray <p>),
                                       captain/dashboard, admin playoffs, manage_scores
     * h1 + div wrapping a form      — admin playoff_eligibility
     * h1 + div of buttons, no form  — enter_scores
   `.admin-scores-head > form` never matched on Enter Scores at all; only the
   `flex-direction` half was doing any work there.

   Lineup Builder is deliberately *not* on this list. The audit names it as an offender,
   but that row was measured on a redirect to the captain dashboard — it has no season
   picker, and its header already carries `flex-wrap`. */
@media (max-width: 767.98px) {
  .page-head {
    flex-direction: column;
    align-items: stretch !important;
    gap: .75rem;
  }
  .page-head > form,
  .page-head > div {
    width: 100%;
  }
}

/* dropdown width in notif bell */
.dropdown-wide { min-width: 320px; }


/* auth card helpers */
.auth-viewport { min-height: 80vh; }
.auth-card { width: 100%; max-width: 480px; }
.logo-80 { max-height: 80px; }
.logo-100h { height: 100px; display: block; margin: 0 auto; } /* for footer logo in emails previewed in web */

/* width helpers to replace <th style="width:..."> */
.w-08 { width: 8%; }
.w-10 { width: 10%; } .w-12 { width:12%; } .w-14 { width:14%; } .w-15 { width:15%; }
.w-16 { width:16%; } .w-18 { width:18%; } .w-20 { width:20%; } .w-22 { width:22%; }
.w-24 { width:24%; } .w-25 { width:25%; } .w-26 { width:26%; } .w-28 { width:28%; }
.w-30 { width:30%; } .w-2-25rem{width:2.25rem;} .w-9rem{width:9rem;} .w-6rem{width:6rem;} /* add more if you need */

.cursor-pointer { cursor: pointer; }

.white-space {white-space: pre-wrap}

.text-purple {
  color: #6f42c1 !important; /* adjust hex to your league’s purple */
}

/* === Navbar toggler: purple lined icon (visible in light mode) === */
.glass-nav .navbar-toggler .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%235e2ea3' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}
html[data-theme="dark"] .glass-nav .navbar-toggler .navbar-toggler-icon {
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='%23ffffff' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");
}

/* Mobile overlay navbar + stacked dropdowns */
@media (max-width: 991.98px) {
  .glass-nav .container { position: static; }

  .navbar .navbar-collapse.show {
    position: fixed;
    inset: 0;
    padding: 1rem;
    background: var(--glass-bg);
    backdrop-filter: saturate(140%) blur(10px);
    -webkit-backdrop-filter: saturate(140%) blur(10px);
    border-top: 1px solid var(--glass-border);
    z-index: 1050;
    overflow-y: auto;
  }
  body.nav-open { overflow: hidden; }

  /* Vertical stacks */
  .navbar .navbar-collapse .navbar-nav { align-items: stretch !important; }
  .navbar .navbar-collapse .nav-link { padding: .75rem 1rem; font-size: 1.05rem; }

  /* Dropdowns render inline under their toggles */
  .navbar .navbar-collapse .dropdown-menu {
    position: static;
    float: none;
    display: none; /* toggled by .show */
    width: 100%;
    padding: 0;
    margin: .25rem 0 0 .75rem; /* indent under parent */
    border: none;
    background: transparent;
    box-shadow: none;
  }
  .navbar .navbar-collapse .dropdown-menu.show { display: block; }
  .navbar .navbar-collapse .dropdown-item { padding: .5rem 1rem; border-radius: .5rem; }

  /* Offcanvas dropdowns: inline and visible when .show */
  .offcanvas .dropdown-menu {
    position: static;
    float: none;
    display: none;
    width: 100%;
    margin: .25rem 0 0 .75rem;
    border: none;
    background: transparent;
    box-shadow: none;
  }
  .offcanvas .dropdown-menu.show { display: block; }
}
/* Ensure offcanvas overlays everything in our glass layout */
.offcanvas.glass-offcanvas { z-index: 2000; }
.offcanvas-backdrop { z-index: 1990; }

/* The desktop nav's `d-none d-lg-flex` needed a scoped re-override here only because
   the blanket `.d-none` above was beating Bootstrap's utility. With that gone,
   Bootstrap's own `.d-lg-flex` works — backlog 32. */
/* Light default is already handled by your glass styles */

/* === Standings table: dark-mode fixes (use data-theme) === */
html[data-theme="dark"] .table-standings-theme,
.theme-dark .table-standings-theme {
  /* Override Bootstrap table vars so background doesn’t stay white */
  --bs-table-bg: transparent;
  --bs-table-striped-bg: rgba(255, 255, 255, 0.03);
  --bs-table-striped-color: inherit;
  --bs-table-hover-bg: rgba(255, 255, 255, 0.05);
  --bs-table-hover-color: inherit;
  color: var(--text-on-dark, #e8e8e8);
  background: transparent;
}

html[data-theme="dark"] .table-standings-theme thead.table-head-glass th,
.theme-dark .table-standings-theme thead.table-head-glass th {
  background-color: rgba(255, 255, 255, 0.06);
  color: var(--text-on-dark-strong, #ffffff);
  border-bottom-color: rgba(255, 255, 255, 0.12);
}

html[data-theme="dark"] .table-standings-theme td,
html[data-theme="dark"] .table-standings-theme th,
.theme-dark .table-standings-theme td,
.theme-dark .table-standings-theme th {
  border-color: rgba(255, 255, 255, 0.08);
}

html[data-theme="dark"] .table-standings-theme tbody tr,
.theme-dark .table-standings-theme tbody tr {
  background-color: transparent; /* keep glass card showing through */
}

/* Full-screen loader overlay */
.loader-overlay {
  position: fixed;
  inset: 0;
  display: none;                  /* hidden by default */
  align-items: center;
  justify-content: center;
  gap: 12px;
  background: rgba(10, 8, 20, 0.45); /* glassy backdrop */
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  z-index: 2000;                  /* above offcanvas/toasts */
}

.loader-overlay.show {
  display: flex;
  animation: loaderFadeIn 120ms ease-out;
}

@keyframes loaderFadeIn {
  from { opacity: 0; } to { opacity: 1; }
}

/* Spinning tennis ball */
.loader-ball {
  width: 72px;
  height: 72px;
  animation: spin 0.9s linear infinite;
  filter: drop-shadow(0 4px 10px rgba(0,0,0,.25));
  opacity: 1;
  transition: opacity 160ms ease-in-out;
}
.loader-ball.is-fading {
  opacity: 0;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

.loader-text {
  font-weight: 600;
  color: #5a2aa8;
  text-shadow: 0 1px 2px rgba(0,0,0,.25);
}

/* Dark theme tweaks */
[data-theme="dark"] .loader-text {
  color: #bca7ff;
}
/* Better placeholder + help text contrast in dark theme */
[data-theme="dark"] .placeholder-contrast::placeholder {
  color: rgba(255,255,255,0.7) !important;
  opacity: 1; /* Safari/iOS */
}

[data-theme="dark"] .help-contrast {
  color: rgba(255,255,255,0.75) !important;
}
/* —— Captain lineup builder —————————————————————————————————————————— */
/* Compact selects inside slot cards without touching the global form sizing.
   Moved out of an inline <style> in lineup_builder.html when its sibling
   <script> had to leave for CSP reasons; keeping them together is simpler
   than splitting the page's styling across two places. */
.lineup-card select.form-select,
.lineup-card select,
.lineup-card textarea,
.lineup-card input[type="text"] {
  padding-top: .25rem;
  padding-bottom: .25rem;
  min-height: 2rem;
}
.lineup-card--forfeit { opacity: .55; }

/* Replaces style="top: 84px" on the availability column. */
.sticky-sidebar { top: 84px; }

/* Bootstrap 5.3 semantic warning classes — used by the "Available but
   playoff-ineligible" card. Bootstrap ships no dark-mode value for either, so
   the emphasis text rendered near-black on the dark glass surface and the
   subtle border disappeared entirely. Same class of fix as the .form-text /
   .text-body-secondary overrides above. */
html[data-theme="dark"] .text-warning-emphasis {
  color: #fde68a !important;
}
html[data-theme="dark"] .border-warning-subtle {
  border-color: rgba(var(--bs-warning-rgb), .38) !important;
}

/* Availability panel + publish conflicts: subtle badges and the danger panel
   both lean on Bootstrap semantic classes with no dark values. */
html[data-theme="dark"] .badge.bg-success-subtle {
  background-color: rgba(var(--bs-success-rgb), .22) !important;
  color: #86efac !important;
}
html[data-theme="dark"] .badge.bg-secondary-subtle {
  background-color: rgba(255,255,255,.12) !important;
  color: rgba(255,255,255,.72) !important;
}
html[data-theme="dark"] .badge.bg-primary-subtle {
  background-color: rgba(var(--bs-primary-rgb), .28) !important;
  color: #d6c6ff !important;
}
html[data-theme="dark"] .publish-conflicts .text-body-secondary {
  color: rgba(255,255,255,.75) !important;
}

/* ——— Nav dropdown surface ————————————————————————————
   The nav menus inherited --glass-bg (.72 light / .62 dark) and the page read
   straight through them. A menu is a surface you read off, not a pane you look
   through: near-opaque in both themes, with the blur left in only as a backstop
   behind the last few percent of alpha. Declared after the earlier
   .glass-nav .dropdown-menu and html[data-theme="dark"] .dropdown-menu rules so
   it wins on source order at equal specificity. */
:root { --dropdown-surface: rgba(255,255,255,.97); }
html[data-theme="dark"] { --dropdown-surface: rgba(24,27,37,.97); }
.dropdown-menu { background-color: var(--dropdown-surface); }
html[data-theme="dark"] .dropdown-menu { background-color: var(--dropdown-surface); }
.glass-nav .dropdown-menu { background: var(--dropdown-surface); }

/* ——— Fixture page ————————————————————————————————— */
/* Header strip: team monogram chips. The fill is Team.color, applied inline —
   team colors are per-row data and never CSS tokens. */
.fixture-monogram {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  flex: 0 0 auto;
  border-radius: .65rem;
  color: #fff;
  font-weight: 700;
  font-size: .85rem;
  letter-spacing: .02em;
  text-shadow: 0 1px 2px rgba(0,0,0,.28);
}
/* text-truncate only truncates when the flex child may shrink below content width. */
.min-w-0 { min-width: 0; }

/* Win indicator on a scored court. The check is never the only signal — every one
   carries aria-label="won"/"tied" — but it still has to be readable, and Bootstrap's
   .text-success (#198754) is far too dark against the dark glass surface. */
.court-win { color: #198754; }
html[data-theme="dark"] .court-win { color: #58d39a; }

/* One row per court: keep the two name columns from wrapping mid-name on desktop. */
.court-table td { vertical-align: middle; }

/* Sub matches, one line per plan. */
.sub-line-list > li:first-child { border-top: 0 !important; }

/* ——— Availability form ——————————————————————————————
   /availability/ never got the glass treatment and rendered a white Bootstrap
   .card in dark mode. The container is .glass-card now; these cover the bits
   Bootstrap still leaves at light-mode values inside it. */
.availability-form .form-label { font-weight: 600; }
html[data-theme="dark"] .availability-form .form-label { color: rgba(255,255,255,.92); }
html[data-theme="dark"] .availability-form .form-text { color: var(--muted) !important; }
html[data-theme="dark"] .availability-form select.form-select,
html[data-theme="dark"] .availability-form input.form-control {
  background-color: rgba(30,34,45,.82);
  color: rgba(255,255,255,.92);
  border-color: var(--glass-border);
}
/* Bootstrap leaves ::placeholder at its light-mode grey, which is all but invisible
   on the dark input fill — the note field's "Optional note" hint vanished. */
html[data-theme="dark"] .availability-form input.form-control::placeholder {
  color: var(--muted-sub);
  opacity: 1;
}


/* ——— Section navigation strip ———————————————————————————
   The pages beside this one, rendered by partials/_section_nav.html as a sibling of the
   navbar. Desktop is an inline tab row with an underline on the current page; below md it
   becomes a horizontally scrolling pill row, full-bleed so the scroll affordance reads.

   Horizontal scroll is right here and is not in tension with removing it from tables. A
   nav strip reveals labels the reader can see exist and choose to ignore; a scrolling
   table hides a column they needed and gave no sign existed.

   No d-none/d-{bp}-* utilities in the markup: theme.css redeclaring one of those was
   backlog 32, and test_dnone_cascade.py holds an exact set of the templates allowed to
   combine them. The breakpoint lives here instead. */
.section-nav {
  border-bottom: 1px solid var(--glass-border);
  margin-bottom: 1.5rem;
}

/* The navbar's own mb-4 is right when nothing follows it and wrong when the strip does.
   :has(+) states exactly that, and specificity (0,2,0) beats the .mb-4 utility's (0,1,0)
   without !important. A negative top margin on the strip would instead hardcode
   Bootstrap's 1.5rem and drift silently if that utility ever changed. Where :has() is
   unsupported the nav keeps its margin and the strip sits 1.5rem lower --- degraded, not
   broken. Depends on the strip being the nav's next *element* sibling, which
   test_section_nav_render.py asserts by parsing rather than by substring, since the HTML
   comment and newline between them are invisible to `+` and not to a string match. */
.glass-nav:has(+ .section-nav) { margin-bottom: 0; }

.section-nav__list {
  display: flex;
  gap: .25rem;
  align-items: stretch;
  list-style: none;
  margin: 0 auto;
  padding: 0;
  max-width: 1320px;
}

.section-nav__cell { display: flex; }

.section-nav__item {
  display: inline-flex;
  align-items: center;
  padding: .5rem .85rem;
  color: inherit;
  opacity: .82;
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color .18s ease, border-color .18s ease, background-color .18s ease;
}

.section-nav__item:hover,
.section-nav__item:focus-visible { opacity: 1; color: var(--bs-primary); }

.section-nav__item--active {
  opacity: 1;
  color: var(--bs-primary);
  font-weight: 600;
  border-bottom-color: var(--bs-primary);
}

html[data-theme="dark"] .section-nav__item:hover,
html[data-theme="dark"] .section-nav__item:focus-visible { color: #fff; }

html[data-theme="dark"] .section-nav__item--active {
  color: #fff;
  border-bottom-color: #fff;
}

@media (max-width: 767.98px) {
  /* Full-bleed: the strip escapes the page gutters so a half-cut pill at the edge shows
     there is more to reach. 100vw rather than 100% because the parent is a container. */
  .section-nav {
    margin-left: calc(50% - 50vw);
    margin-right: calc(50% - 50vw);
    margin-bottom: 1rem;
    /* The pills are a 44px tap target inside a 45px strip, so with no padding their
       rounded background sits flat on the bottom rule and reads as one welded block ---
       the underline treatment above md wants its marker to meet that line, a filled pill
       does not. Matches the row's own `gap` so the spacing around a pill is even on all
       four sides. */
    padding: .375rem 0;
  }

  .section-nav__list {
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding: 0 .75rem;
    gap: .375rem;
  }

  .section-nav__list::-webkit-scrollbar { display: none; }

  /* Pills below md, underline above. 44px is the tap target; the border-bottom marker
     would be ambiguous in a scrolling row where the row edge is already a line. */
  .section-nav__item {
    min-height: 44px;
    padding: .5rem .9rem;
    border-bottom: 0;
    border-radius: 999px;
    background-color: rgba(var(--bs-primary-rgb), .06);
  }

  .section-nav__item--active {
    background-color: rgba(var(--bs-primary-rgb), .16);
  }

  html[data-theme="dark"] .section-nav__item { background-color: rgba(255,255,255,.06); }
  html[data-theme="dark"] .section-nav__item--active {
    background-color: rgba(255,255,255,.16);
  }
}
