/* TSB Online — shared premium performance platform layer
   Loaded after each page's legacy styles so the application can evolve
   without disturbing JavaScript-bound IDs, data contracts, or integrations.

   ── TYPE SYSTEM ────────────────────────────────────────────────────────────
   ONE face: Orbitron. Both --tsb-display and --tsb-body resolve to it, there is
   no second body face, and there are no per-page overrides — :root below is the
   single source of truth. Any other family named anywhere in this sheet was a
   leftover that the site never actually loaded, so it silently rendered in the
   system sans; all of them have been removed.

   The two tokens still mean different things, and rules should keep asking for
   the right one so a future face change lands in one place:

     --tsb-display  brand, headings, scores, Alpha Grade numbers, tiers, ranks,
                    nav/tab labels, buttons, stat figures, badges — anything
                    short, structural, or numeric.
     --tsb-body     paragraph copy, form fields, table cells, comments, errors,
                    fine print — anything you read a sentence of.

   Orbitron is a wide display face and gets hard to read as it shrinks, so the
   compensation runs the other way from a normal UI font: give long copy MORE
   size and MORE leading, and do not tighten tracking to buy space. Nothing that
   is genuinely body copy should sit below ~0.75rem on a phone.

   Loaded per page as:
     <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&display=swap" rel="stylesheet"> */

:root {
  --tsb-bg: #08090b;
  --tsb-bg-raised: #0d0f12;
  --tsb-surface: #13161a;
  --tsb-surface-2: #191d22;
  --tsb-surface-3: #20252b;
  --tsb-red: #d71920;
  --tsb-red-bright: #f02a31;
  --tsb-red-soft: rgba(215, 25, 32, .12);
  --tsb-text: #f7f8fa;
  --tsb-text-2: #c7ccd3;
  --tsb-muted: #8f98a3;
  --tsb-steel: #aeb6c0;
  --tsb-line: rgba(255, 255, 255, .09);
  --tsb-line-strong: rgba(255, 255, 255, .15);
  --tsb-blue: #58b7e8;
  --tsb-gold: #d4ae55;
  --tsb-green: #35c579;
  --tsb-danger: #f25a60;
  --tsb-radius-sm: 8px;
  --tsb-radius: 12px;
  --tsb-radius-lg: 18px;
  --tsb-shadow: 0 18px 50px rgba(0, 0, 0, .28);
  --tsb-shadow-red: 0 18px 55px rgba(160, 0, 8, .16);
  --tsb-display: 'Orbitron', sans-serif;
  --tsb-body: 'Orbitron', sans-serif;
  --tsb-ease: 180ms cubic-bezier(.2, .7, .2, 1);
  /* Shared content column width. Pages fill the laptop viewport already; this
     wider cap lets big external monitors use the screen instead of leaving a
     dead band beside a 1200px column. */
  --tsb-content: 1600px;
}

/* The per-page --tsb-display / --tsb-body overrides that used to live here
   (.tsb-library, .tsb-leaderboard, .tsb-internet-kpi, .tsb-profile, .tsb-alpha,
   .tsb-login, .tsb-admin-dash, .tsb-sign-admin, .tsb-scorecard) are gone. All
   but one simply restated the root pair; .tsb-library had the two faces the
   wrong way round and put a non-brand family on its headings. Every page now
   resolves both tokens to Orbitron from :root above — that is the whole type
   system.

   Do not reintroduce a page-scoped override. If one element on one page needs a
   different voice, set font-family on that element from --tsb-display or
   --tsb-body; do not redefine the tokens under a page class. */

html { color-scheme: dark; scroll-behavior: smooth; }

/* Orbitron is the whole type system, and it renders about a quarter wider and
   heavier than a normal UI font at the same pixel size — so on a desktop at 100%
   the portal read enlarged, enough that it was being viewed at 90% to feel right.
   Nudging the root scale down 10% bakes that comfortable size in as the default.
   Only rem-based text follows this (almost all spacing here is px, so the layout,
   sidebar and touch targets don't move); and only on the wide sidebar layout —
   the phone-first mobile design was tuned against 16px and never felt oversized. */
@media (min-width: 901px) {
  html { font-size: 90%; }
}

/* Installed-app launch screen. Browsers supply the home-screen icon but iOS
   does not consistently create a branded startup view for a PWA, which left a
   plain dark frame while /home loaded. The shared script shows this once per
   standalone app session, never in a normal browser tab. */
body.tsb-page {
  background:
    radial-gradient(circle at 15% -10%, rgba(215, 25, 32, .12), transparent 27rem),
    radial-gradient(circle at 100% 20%, rgba(88, 183, 232, .045), transparent 24rem),
    var(--tsb-bg);
  color: var(--tsb-text);
  font-family: var(--tsb-body);
  line-height: 1.5;
  text-rendering: optimizeLegibility;
}

body.tsb-page::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: .16;
  background-image: linear-gradient(rgba(255,255,255,.022) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.022) 1px, transparent 1px);
  background-size: 48px 48px;
  mask-image: linear-gradient(to bottom, #000, transparent 70%);
}

/* Give form controls the page font (they don't inherit family by default),
   but at ZERO specificity so any page-level rule wins. `body.tsb-page` used to
   contribute (0,1,1), which out-specified single-class page rules like
   `.gate-btn` at (0,1,0); the `font` shorthand then wiped their font-size and
   font-weight, silently rendering buttons across the site at the browser
   default ~16px/normal. Wrapping the scope in :where() keeps the reset (still
   author-origin, so it beats the UA sheet) while letting every page rule
   override it. */
:where(body.tsb-page) :where(button, input, select, textarea) { font: inherit; }
/* Same zero-specificity band, so it is still overridable by anything: a control
   you press is labelled in the display face, a control you type into is set in
   the body face (which `font: inherit` above already gave it). */
:where(body.tsb-page) :where(button) { font-family: var(--tsb-display); }
body.tsb-page :where(a, button, input, select, textarea):focus-visible {
  outline: 2px solid var(--tsb-red-bright);
  outline-offset: 3px;
}

/* ── App feel ───────────────────────────────────────────────────────────────
   The handful of primitives that separate "app" from "web page in a browser"
   on a phone. None of it changes layout; it changes how touch feels.

   1. No tap-highlight. The grey/blue flash a mobile browser paints on every
      tapped link and button is the single loudest "this is a web page" tell.
      A native app never does it — it gives its own press state instead (below).
   2. Crisper text, the way app text is rendered rather than the browser default.
   3. Chrome isn't selectable. Long-pressing a tab label or a heading to get a
      text-selection caret is a browser behaviour; apps don't do it. Scoped to
      navigation and controls ONLY — content, values, phone numbers and emails
      stay fully selectable so they can still be copied. */
body.tsb-page {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
body.tsb-page * { -webkit-tap-highlight-color: transparent; }
/* Taps register instantly and never double-tap-zoom a control. `manipulation`
   keeps panning and pinch-zoom on the page while dropping the double-tap
   gesture on buttons and links — the last source of a perceptible tap delay. */
body.tsb-page :where(a, button, .tsb-tab, .card, .pf-menu-item, .at-check, input[type="checkbox"], label) {
  touch-action: manipulation;
}
body.tsb-page :where(.tsb-tabbar, .tsb-sidebar, .tsb-sheet, .pf-menu, .platform-header, button, .tsb-tab, .pf-menu-item, .tsb-side-item, .tsb-sheet-item) {
  -webkit-user-select: none;
  user-select: none;
}

/* Press feedback. A tappable thing dips slightly under the finger and springs
   back — instant, physical, and the thing tap-highlight used to fake badly.
   Fast in, so it registers on a tap; the release eases back on its own. */
@media (hover: none) {
  body.tsb-page :where(.tsb-tab, .pf-menu-item, .tsb-side-item, .tsb-sheet-item, .portal-btn, .tsb-btn, .quick-tile, .feat-link, .at-check, .doc-row, .ack-banner):active {
    transform: scale(.97);
    transition: transform 60ms ease;
  }
  /* Cards already own their :hover transform, so press them on their own axis
     (a gentle sink) without fighting that rule. */
  body.tsb-page .card:active { transform: translateY(0) scale(.985); transition: transform 60ms ease; }
}
@media (prefers-reduced-motion: reduce) {
  body.tsb-page :where(.tsb-tab, .pf-menu-item, .card, .portal-btn, .tsb-btn, .quick-tile, .doc-row, .ack-banner):active { transform: none; }
}

/* Contain overscroll on the app pages. Stops the pull-to-refresh gesture from
   reloading (an app doesn't reload when you tug the top) and the rubber-band
   bounce from revealing the browser's own background behind the page. */
html.tsb-appnav, html.tsb-appnav body.tsb-page { overscroll-behavior-y: none; }

/* ── Skeleton loading ───────────────────────────────────────────────────────
   App loading state: shapes where the content will be, with a highlight band
   sweeping across them, instead of a spinner or "Loading…". It tells you what's
   coming and how it's laid out, so the arrival of real data is a fill rather
   than a jump. These live inside each page's existing loading container, which
   is already hidden the moment data arrives — so the skeleton is self-clearing
   with no extra script, and there's no show-then-hide flash: it's only ever
   shown during load and removed, never re-shown. */
.sk {
  position: relative;
  overflow: hidden;
  background: rgba(255, 255, 255, .045);
  border-radius: 8px;
}
.sk::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, .06) 55%, transparent);
  animation: sk-sweep 1.35s ease-in-out infinite;
}
@keyframes sk-sweep { to { transform: translateX(100%); } }
@media (prefers-reduced-motion: reduce) { .sk::after { animation: none; } }

.sk-line { height: 11px; border-radius: 6px; }
.sk-circle { border-radius: 50%; }
/* A stack of skeletons should breathe like the rows it stands in for. */
.sk-stack { display: grid; gap: 12px; }
.sk-row {
  display: flex; align-items: center; gap: 13px;
  padding: 14px 16px;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: rgba(255, 255, 255, .015);
}
.sk-row .sk-grow { flex: 1; display: grid; gap: 8px; }

/* ── Back control on detail screens (phones) ────────────────────────────────
   Injected by addBackControl() in tsb-platform.js on every page that isn't a
   bottom-tab root. Desktop navigates from the sidebar, so it's phone-only. */
.tsb-appback { display: none; }
@media (max-width: 900px) {
  html.tsb-appnav .tsb-appback {
    display: inline-flex; align-items: center; gap: 5px;
    margin: 0; padding: 10px clamp(16px, 3vw, 32px) 6px;
    border: 0; background: none; cursor: pointer;
    color: var(--tsb-text-2); font-family: var(--tsb-display);
    font-size: .76rem; font-weight: 700;
  }
  html.tsb-appnav .tsb-appback svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 2.2; stroke-linecap: round; stroke-linejoin: round; }
  html.tsb-appnav .tsb-appback:active { transform: scale(.96); }
}

body.tsb-page :where(.header, body > header) {
  min-height: 90px !important;
  height: auto;
  padding: 10px clamp(16px, 3vw, 40px) !important;
  background: rgba(250, 250, 251, .98) !important;
  border-bottom: 1px solid #d9dde2 !important;
  box-shadow: 0 5px 24px rgba(0, 0, 0, .16);
}

body.tsb-page :where(.header, body > header) > img {
  height: 68px !important;
  max-width: min(300px, 48vw);
  object-fit: contain;
}

body.tsb-page .header-right { gap: 8px; }
body.tsb-page .header .logout-btn { border-color: rgba(0,0,0,.18); background: rgba(0,0,0,.03); color: #555b63; }
body.tsb-page .live-pill,
body.tsb-page .hero-badge {
  border-color: rgba(215, 25, 32, .24);
  background: rgba(215, 25, 32, .08);
  color: #a60f15;
  font-family: var(--tsb-display);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 1.5px;
  text-transform: uppercase;
}
body.tsb-page .live-dot { background: var(--tsb-red-bright); box-shadow: 0 0 0 4px rgba(215,25,32,.1); }

body.tsb-page .site-nav {
  position: sticky;
  top: 0;
  z-index: 180;
  padding: 0 clamp(10px, 3vw, 36px);
  background: rgba(10, 11, 13, .94);
  border-bottom: 1px solid var(--tsb-line);
  box-shadow: 0 8px 30px rgba(0, 0, 0, .22);
  backdrop-filter: blur(18px);
}
body.tsb-page .nav-inner { display: flex; flex-wrap: nowrap; max-width: 1200px; margin: auto; gap: 2px; }
body.tsb-page .nav-link {
  min-height: 48px;
  display: inline-flex;
  align-items: center;
  padding: 0 17px;
  color: var(--tsb-muted);
  border-bottom: 2px solid transparent;
  /* A nav label is a label, not copy — Orbitron. */
  font-family: var(--tsb-display);
  font-size: 11px;
  font-weight: 750;
  letter-spacing: .75px;
  text-transform: uppercase;
  transition: color var(--tsb-ease), background var(--tsb-ease), border-color var(--tsb-ease);
}
body.tsb-page .nav-link:hover { color: #fff; background: rgba(255,255,255,.035); }
body.tsb-page .nav-link.active { color: #fff; border-bottom-color: var(--tsb-red); background: linear-gradient(180deg, transparent, rgba(215,25,32,.08)); }

body.tsb-page :where(.page, .content, main, .page-body) { width: min(var(--tsb-content), 100%); margin-inline: auto; }
body.tsb-page :where(.page, .content, main) { padding-inline: clamp(16px, 3vw, 32px); }

body.tsb-page :where(.card, .what-card, .tip-card, .sc-card, .panel-card, .pod-player-card, .player-card, .course-row, .data-card, .stat-card) {
  border-color: var(--tsb-line);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
}

body.tsb-page :where(.card, .what-card, .tip-card, .sc-card, .pod-player-card, .player-card) {
  background: linear-gradient(145deg, rgba(29, 33, 39, .96), rgba(17, 19, 23, .98));
}

body.tsb-page :where(.card, .what-card, .tip-card, .player-card, .course-row) {
  transition: transform var(--tsb-ease), border-color var(--tsb-ease), box-shadow var(--tsb-ease);
}
body.tsb-page :where(.card, .what-card, .tip-card, .player-card, .course-row):hover {
  transform: translateY(-3px);
  border-color: rgba(215, 25, 32, .32);
  box-shadow: var(--tsb-shadow-red);
}

/* ── Role assignment for the legacy page vocabulary ────────────────────────
   Both tokens resolve to the same family today, so these rules are not there to
   change what you see — they are there so every recurring class across the 19
   pages is on record as a title, a label, a figure or a paragraph. Swapping a
   face later then means editing :root, not auditing 3,900 lines of page-local
   <style>. New work should use the tsb-* components at the end of this file,
   which already declare the right role.

   Headings, titles and names. */
body.tsb-page :where(.card-title, .seg-title, .sc-title, .sec-title, .profile-name, .hero-title, .course-name, h1, h2, h3, h4, h5, h6) {
  font-family: var(--tsb-display);
  letter-spacing: -.01em;
}
/* Tracked micro-labels, eyebrows and tags — the portal's label voice. */
body.tsb-page :where(.sec-label, .card-tag, .chip-k, .kpi-k, .stat-k, .data-label, .field-label, .seg-badge, .click-here-pill, .badge-name, .tier-pts-lbl) {
  font-family: var(--tsb-display);
}
/* Figures. Every number the portal grades someone on reads in Orbitron, and
   tabular figures stop columns of scores from jittering as they update. */
body.tsb-page :where(.chip-v, .kpi-v, .stat-v, .tier-pts-num, .tier-range, .data-val, .dr-val) {
  font-family: var(--tsb-display);
  font-variant-numeric: tabular-nums;
}
/* Body copy. Orbitron is wide, so long copy is given generous leading and is
   never tracked tighter to claw back a line. */
body.tsb-page :where(.card-desc, .seg-sub, .sc-sub, .what-desc, .tier-desc, .sec-body) {
  color: var(--tsb-text-2);
  font-family: var(--tsb-body);
  line-height: 1.65;
}

/* Buttons carry the display face everywhere, whatever a page calls them. A
   button is a label on a target, never a sentence. */
body.tsb-page :where(.btn, .btn-p, .btn-s, .btn-g, .btn-red, .btn-accent, .btn-ghost, .portal-btn, .gate-btn, .admin-modal-btn, .pw-btn, .cta-primary, .logout-btn, .modal-cancel) {
  font-family: var(--tsb-display);
}
body.tsb-page :where(.btn-p, .gate-btn, .admin-modal-btn, .pw-btn, .cta-primary) {
  background: linear-gradient(135deg, var(--tsb-red), #b4080f);
  border: 1px solid rgba(255,255,255,.1);
  box-shadow: 0 9px 22px rgba(190, 6, 15, .2);
  color: #fff;
  transition: transform var(--tsb-ease), filter var(--tsb-ease), box-shadow var(--tsb-ease);
}
body.tsb-page :where(.btn-p, .gate-btn, .admin-modal-btn, .pw-btn, .cta-primary):hover {
  transform: translateY(-1px);
  filter: brightness(1.1);
  box-shadow: 0 12px 28px rgba(190, 6, 15, .28);
}
body.tsb-page :where(.btn-s, .logout-btn, .modal-cancel) {
  border: 1px solid var(--tsb-line-strong);
  background: rgba(255,255,255,.035);
  color: var(--tsb-text-2);
}

body.tsb-page :where(input, select, textarea, .csv-area) {
  border-color: var(--tsb-line-strong);
  background: #0d0f12;
  color: var(--tsb-text);
}
body.tsb-page :where(input, select, textarea, .csv-area):focus {
  border-color: rgba(215,25,32,.65);
  box-shadow: 0 0 0 3px rgba(215,25,32,.1);
}

/* Home / training center */
.tsb-home .page { max-width: var(--tsb-content); padding-top: 28px; }
.portal-hero {
  position: relative;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1.35fr) minmax(280px, .65fr);
  gap: clamp(24px, 5vw, 64px);
  align-items: center;
  padding: clamp(32px, 6vw, 68px);
  margin-bottom: 26px;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius-lg);
  background: linear-gradient(125deg, rgba(36, 10, 13, .96), rgba(17, 19, 23, .97) 52%, rgba(24, 28, 33, .98));
  box-shadow: var(--tsb-shadow);
}
.portal-hero::before {
  content: '';
  position: absolute;
  width: 340px;
  height: 340px;
  right: -80px;
  top: -150px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(215,25,32,.28), transparent 68%);
}
.portal-hero::after {
  content: 'TSB';
  position: absolute;
  right: 4%;
  bottom: -42px;
  color: rgba(255,255,255,.025);
  font-family: var(--tsb-display);
  font-size: clamp(100px, 16vw, 190px);
  font-weight: 900;
  letter-spacing: -12px;
}
.portal-hero-copy, .portal-command { position: relative; z-index: 1; }
.portal-eyebrow { margin-bottom: 13px; color: var(--tsb-red-bright); font-family: var(--tsb-display); font-size: 10px; font-weight: 800; letter-spacing: 2.5px; text-transform: uppercase; }
.portal-hero h1 { max-width: 760px; margin: 0 0 16px; color: #fff; font-size: clamp(2rem, 5vw, 3.75rem); font-weight: 800; line-height: 1.08; }
.portal-hero h1 > span,
.portal-hero h1 > em { display: block; white-space: nowrap; }
.portal-hero h1 em { color: var(--tsb-red-bright); font-style: normal; }
.portal-hero-copy > p { max-width: 670px; color: var(--tsb-text-2); font-size: clamp(15px, 1.6vw, 18px); line-height: 1.7; }
.portal-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
.portal-btn { display: inline-flex; min-height: 45px; align-items: center; justify-content: center; padding: 0 18px; border: 1px solid var(--tsb-line-strong); border-radius: var(--tsb-radius-sm); color: #fff; font-size: 12px; font-weight: 800; letter-spacing: .35px; text-decoration: none; }
.portal-btn.primary { background: linear-gradient(135deg, var(--tsb-red-bright), #b50810); border-color: rgba(255,255,255,.12); box-shadow: 0 10px 26px rgba(215,25,32,.24); }
.portal-btn.secondary { background: rgba(255,255,255,.04); }
.portal-btn:hover { transform: translateY(-2px); filter: brightness(1.08); }
/* ── Profile page menu ─────────────────────────────────────────────────────
   The settings list that replaces the bottom bar's "More" drawer. Deliberately
   a plain grouped list with chevrons — the pattern every phone user already
   knows from their Settings app — rather than more of the cards used elsewhere
   on this page. */
.pf-menu { margin-top: 22px; }
.pf-menu[hidden] { display: none; }
.pf-menu .tsb-nav-group { margin: 18px 2px 7px; }
.pf-menu .tsb-nav-group:first-child { margin-top: 0; }
.pf-menu-list {
  overflow: hidden;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.02);
}
.pf-menu-item {
  display: flex; align-items: center; gap: 13px;
  width: 100%;
  padding: 14px 15px;
  border: 0; border-bottom: 1px solid var(--tsb-line);
  background: none;
  color: var(--tsb-text);
  /* Matches .tsb-side-item / .tsb-sheet-item — it is the same kind of list. */
  font-family: var(--tsb-display); font-size: .78rem; font-weight: 600;
  text-align: left; text-decoration: none; cursor: pointer;
}
.pf-menu-item:last-child { border-bottom: 0; }
.pf-menu-item:hover { background: rgba(255,255,255,.04); }
.pf-menu-item svg {
  width: 19px; height: 19px; flex: none;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
  color: var(--tsb-muted);
}
.pf-menu-item > span:not(.pf-menu-go) { flex: 1; min-width: 0; }
.pf-menu-go { flex: none; color: var(--tsb-muted); font-size: 1.1rem; line-height: 1; }
.pf-menu-logout { color: var(--tsb-danger); }
.pf-menu-logout svg { color: var(--tsb-danger); }

/* Account row inside the mobile "More" sheet — the only route to Log out now
   that the top bar is hidden on phones. */
.tsb-sheet-acct {
  display: flex; align-items: center; gap: 11px;
  padding: 12px 14px; margin-top: 4px;
  border: 1px solid var(--tsb-line); border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.02);
}
.tsb-sheet-acct .tsb-acct-meta { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.tsb-sheet-logout {
  flex: none; min-height: 38px; padding: 0 14px;
  border: 1px solid var(--tsb-line-strong); border-radius: 9px;
  background: rgba(255,255,255,.04); color: var(--tsb-text-2);
  font-family: var(--tsb-display); font-size: .68rem; font-weight: 700; cursor: pointer;
}
.tsb-sheet-logout:hover { border-color: rgba(215,25,32,.45); color: #fff; }

/* ── Home-screen install tip ───────────────────────────────────────────────
   Sits above the bottom tab bar rather than over the content, so it never
   covers what someone is reading, and clears the iPhone home indicator. */
.tsb-install {
  position: fixed;
  right: 10px;
  left: 10px;
  bottom: calc(72px + env(safe-area-inset-bottom));
  z-index: 940;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--tsb-line-strong);
  border-radius: var(--tsb-radius);
  background: rgba(19, 22, 26, .97);
  box-shadow: 0 16px 40px rgba(0,0,0,.5);
  backdrop-filter: blur(14px);
  animation: tsb-install-in 260ms cubic-bezier(.2,.7,.2,1);
}
@keyframes tsb-install-in { from { opacity: 0; transform: translateY(10px); } }
@media (prefers-reduced-motion: reduce) { .tsb-install { animation: none; } }

.tsb-install-icon { flex: none; width: 36px; height: 36px; overflow: hidden; border-radius: 9px; }
.tsb-install-icon img { width: 100%; height: 100%; object-fit: cover; }

/* Orbitron is a wide face, so this copy is kept to two short lines that fit
   without wrapping — the earlier wording ran to five lines on a 375px screen. */
.tsb-install-copy { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.tsb-install-copy strong { color: #fff; font-size: .62rem; font-weight: 800; letter-spacing: .2px; }
.tsb-install-copy span { color: var(--tsb-muted); font-size: .55rem; line-height: 1.4; }

/* Both controls have to beat the global 44px min-height for touch targets
   (see the :where(button…) rule above) — without an override the round
   dismiss button is stretched into an oval. 44px square keeps it both round
   and comfortably tappable. */
.tsb-install-go {
  flex: none; min-height: 36px; height: 36px; padding: 0 12px; border: 0; border-radius: 8px;
  background: linear-gradient(135deg, var(--tsb-red-bright), #b50810); color: #fff;
  font-size: .6rem; font-weight: 800; letter-spacing: .2px; cursor: pointer;
}
.tsb-install-x {
  flex: none; width: 30px; height: 30px; min-height: 30px; padding: 0; border: 0;
  border-radius: 50%; background: rgba(255,255,255,.07); color: var(--tsb-text-2);
  font-size: .95rem; line-height: 1; cursor: pointer;
}

/* ── Required-document banner ──────────────────────────────────────────────
   Signing is a one-time action, so it sits above the hero instead of taking a
   nav slot — a permanent tab for something you do once and never again would
   outlive its own usefulness. Gold rather than brand red: red is this page's
   resting state and would not read as "this one is waiting on you". The
   banner is hidden by default and only unhides once the status GET says the
   document is unsigned, so it never flashes at someone who is already done. */
.ack-banner {
  display: none;
  grid-template-columns: auto 1fr auto;
  gap: 16px;
  align-items: center;
  margin: 0 0 20px;
  padding: 15px 18px;
  border: 1px solid rgba(212,174,85,.28);
  border-left: 3px solid var(--tsb-gold);
  border-radius: var(--tsb-radius);
  background: linear-gradient(135deg, rgba(212,174,85,.11), rgba(212,174,85,.03));
  text-decoration: none;
  transition: transform var(--tsb-ease), border-color var(--tsb-ease);
}
/* Only ever shown by the pre-paint class, so the default above is the one that
   applies to Executives and VA Staff: the banner is never laid out for them at
   any point, rather than being drawn and then taken away. */
html.tsb-ack-due .ack-banner { display: grid; }
.ack-banner:hover { transform: translateY(-1px); border-color: rgba(212,174,85,.55); }
.ack-banner-icon { display: grid; place-items: center; width: 38px; height: 38px; border-radius: 50%; background: rgba(212,174,85,.14); color: var(--tsb-gold); }
.ack-banner-icon svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.ack-banner-copy { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.ack-banner-copy strong { color: var(--tsb-gold); font-family: var(--tsb-display); font-size: 10px; font-weight: 800; letter-spacing: 1.6px; text-transform: uppercase; }
.ack-banner-copy span { color: var(--tsb-text-2); font-size: 13.5px; line-height: 1.5; }
.ack-banner-cta { padding: 10px 16px; border-radius: var(--tsb-radius-sm); background: var(--tsb-gold); color: #1a1206; font-size: 11px; font-weight: 800; letter-spacing: .4px; white-space: nowrap; }

@media (max-width: 640px) {
  .ack-banner { grid-template-columns: auto 1fr; gap: 12px; padding: 13px 14px; }
  .ack-banner-copy span { font-size: 13px; line-height: 1.55; }
  .ack-banner-cta { grid-column: 1 / -1; text-align: center; }
}

.portal-command { padding: 20px; border: 1px solid var(--tsb-line); border-radius: var(--tsb-radius); background: rgba(7,8,10,.54); backdrop-filter: blur(8px); }
.command-label { color: var(--tsb-muted); font-size: 9px; font-weight: 800; letter-spacing: 1.8px; text-transform: uppercase; }
.command-title { margin: 7px 0 4px; color: #fff; font-family: var(--tsb-display); font-size: 17px; font-weight: 750; }
.command-copy { color: var(--tsb-text-2); font-size: 12.5px; line-height: 1.62; }
.command-progress { height: 5px; margin: 16px 0 9px; overflow: hidden; border-radius: 99px; background: rgba(255,255,255,.08); }
.command-progress span { display: block; width: 34%; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--tsb-red), var(--tsb-red-bright)); }
.command-meta { display: flex; justify-content: space-between; color: var(--tsb-muted); font-size: 10px; font-weight: 700; }
.platform-shortcuts { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; margin-bottom: 36px; }
.platform-shortcut { display: flex; min-width: 0; align-items: center; gap: 12px; padding: 16px; border: 1px solid var(--tsb-line); border-radius: var(--tsb-radius); background: rgba(20,23,27,.85); color: #fff; text-decoration: none; transition: var(--tsb-ease); }
.platform-shortcut:hover { transform: translateY(-2px); border-color: rgba(215,25,32,.4); background: var(--tsb-surface-2); }
.platform-shortcut > span:last-child { display: flex; min-width: 0; flex-direction: column; gap: 2px; }
.shortcut-icon { display: grid; width: 38px; height: 38px; flex: 0 0 auto; place-items: center; border: 1px solid rgba(215,25,32,.23); border-radius: 9px; background: var(--tsb-red-soft); color: #f05a60; font-family: var(--tsb-display); font-size: 9px; font-weight: 800; letter-spacing: .7px; }
.shortcut-label { overflow: hidden; color: #fff; font-size: 12px; font-weight: 800; white-space: nowrap; text-overflow: ellipsis; }
.shortcut-meta { color: var(--tsb-muted); font-size: 10px; }
.tsb-home .lib-hero { margin: 36px 0 22px; padding: 0; border: 0; text-align: left; }
.tsb-home .lib-title { color: #fff; font-size: clamp(1.35rem, 2.6vw, 2rem); letter-spacing: -.02em; }
/* A tracked all-caps kicker, not a sentence — stays in the display face. */
.tsb-home .lib-sub { margin-top: 5px; color: var(--tsb-muted); font-family: var(--tsb-display); font-size: 11px; letter-spacing: 1.5px; }
/* Featured destination — the GM welcome tile. A frosted-glass performance
   panel with a red telemetry rail, so it belongs to the same world as the hero. */
.tsb-home .tsb-featured { display: grid; gap: 16px; margin-bottom: 42px; }
.tsb-home .feat-hero {
  position: relative;
  display: grid;
  grid-template-columns: 260px minmax(0, 1fr);
  overflow: hidden;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius-lg);
  background: linear-gradient(115deg, rgba(25,29,34,.72), rgba(13,15,18,.66) 80%);
  -webkit-backdrop-filter: blur(16px) saturate(1.1);
  backdrop-filter: blur(16px) saturate(1.1);
  color: var(--tsb-text);
  text-decoration: none;
  box-shadow: 0 14px 38px rgba(0, 0, 0, .3);
  transition: transform var(--tsb-ease), border-color var(--tsb-ease), box-shadow var(--tsb-ease);
}
.tsb-home .feat-hero::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0; z-index: 2;
  width: 3px;
  background: linear-gradient(180deg, var(--tsb-red-bright), var(--tsb-red));
  opacity: .8;
  transition: opacity var(--tsb-ease), width var(--tsb-ease);
}
.tsb-home .feat-hero:hover {
  transform: translateY(-3px);
  border-color: rgba(240, 42, 49, .45);
  box-shadow: 0 24px 54px rgba(160, 0, 8, .28);
}
.tsb-home .feat-hero:hover::before { opacity: 1; width: 4px; }
.tsb-home .feat-thumb-wrap { position: relative; overflow: hidden; }
.tsb-home .feat-thumb { width: 100%; height: 100%; min-height: 150px; object-fit: cover; display: block; }
.tsb-home .feat-play {
  position: absolute; inset: 0; margin: auto;
  width: 56px; height: 56px; display: grid; place-items: center;
  padding-left: 4px;
  border-radius: 50%;
  background: rgba(215, 25, 32, .92); color: #fff; font-size: 17px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .45);
  transition: transform var(--tsb-ease), background var(--tsb-ease);
}
.tsb-home .feat-hero:hover .feat-play { transform: scale(1.08); background: var(--tsb-red-bright); }
.tsb-home .feat-body {
  display: flex; align-items: center; justify-content: space-between; gap: 20px;
  padding: 22px 26px;
}
.tsb-home .feat-eyebrow {
  margin-bottom: 7px;
  color: var(--tsb-red-bright);
  font-family: var(--tsb-display);
  font-size: 10px; font-weight: 800; letter-spacing: 2.5px; text-transform: uppercase;
}
.tsb-home .feat-title {
  color: #fff;
  font-family: var(--tsb-display);
  font-size: clamp(1.1rem, 1.9vw, 1.4rem); font-weight: 750; letter-spacing: .3px; line-height: 1.14;
}
.tsb-home .feat-desc { margin-top: 6px; color: var(--tsb-text-2); font-size: 13px; line-height: 1.58; }
.tsb-home .feat-cta {
  flex-shrink: 0;
  display: inline-flex; align-items: center; gap: 9px;
  padding: 10px 18px;
  border: 1px solid var(--tsb-line-strong);
  border-radius: 99px;
  background: rgba(240, 42, 49, .08);
  color: var(--tsb-red-bright);
  font-family: var(--tsb-display);
  font-size: 11px; font-weight: 800; letter-spacing: 1px; white-space: nowrap;
  transition: background var(--tsb-ease), color var(--tsb-ease), border-color var(--tsb-ease);
}
.tsb-home .feat-cta .feat-arrow { transition: transform var(--tsb-ease); }
.tsb-home .feat-hero:hover .feat-cta { background: var(--tsb-red-bright); color: #fff; border-color: transparent; }
.tsb-home .feat-hero:hover .feat-cta .feat-arrow { transform: translateX(3px); }

/* Quick-launch grid — compact glass tiles for the secondary destinations. */
.tsb-home .quick-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.tsb-home .quick-tile {
  position: relative;
  display: flex; flex-direction: column; align-items: flex-start;
  min-height: 168px;
  padding: 20px;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: linear-gradient(165deg, rgba(25,29,34,.6), rgba(13,15,18,.55));
  -webkit-backdrop-filter: blur(14px) saturate(1.08);
  backdrop-filter: blur(14px) saturate(1.08);
  color: #fff; text-decoration: none; overflow: hidden;
  transition: transform var(--tsb-ease), border-color var(--tsb-ease), box-shadow var(--tsb-ease), background var(--tsb-ease);
}
.tsb-home .quick-tile::after {
  content: '';
  position: absolute; right: -40px; top: -40px;
  width: 120px; height: 120px; border-radius: 50%;
  background: radial-gradient(circle, rgba(215,25,32,.22), transparent 70%);
  opacity: 0; transition: opacity var(--tsb-ease);
}
.tsb-home .quick-tile:hover {
  transform: translateY(-4px);
  border-color: rgba(240, 42, 49, .45);
  box-shadow: 0 20px 46px rgba(160, 0, 8, .22);
  background: linear-gradient(165deg, rgba(31,36,42,.7), rgba(16,18,22,.62));
}
.tsb-home .quick-tile:hover::after { opacity: 1; }
.tsb-home .quick-icon {
  display: grid; place-items: center;
  width: 62px; height: 62px; margin-bottom: 14px;
  border: 1px solid rgba(215,25,32,.28); border-radius: 17px;
  background: var(--tsb-red-soft); color: #f4565c;
  transition: transform var(--tsb-ease);
}
.tsb-home .quick-tile:hover .quick-icon { transform: translateY(-1px) scale(1.04); }
.tsb-home .quick-icon svg { width: 34px; height: 34px; stroke: currentColor; stroke-width: 1.85; fill: none; stroke-linecap: round; stroke-linejoin: round; }

/* Per-destination accent for the quick-launch icons. Keyed off href rather than
   nth-child so reordering or removing a tile cannot shuffle the colours. The
   eyebrow labels stay red on purpose: red is the page's through-line and the
   icons do the differentiating. Adding a tile? Give it a block here too, or it
   falls back to the red default above. */
.tsb-home .quick-tile[href="/profiles"] .quick-icon { color: #ff8a3d; border-color: rgba(255,138,61,.42); background: linear-gradient(150deg, rgba(255,138,61,.26), rgba(215,25,32,.16)); box-shadow: 0 6px 20px rgba(255,110,50,.20), inset 0 1px 0 rgba(255,255,255,.09); }
.tsb-home .quick-tile[href*="tsb-gm-dashboard-preview"] .quick-icon { color: #63d0ff; border-color: rgba(99,208,255,.42); background: linear-gradient(150deg, rgba(99,208,255,.26), rgba(56,102,235,.16)); box-shadow: 0 6px 20px rgba(70,170,255,.20), inset 0 1px 0 rgba(255,255,255,.09); }
.tsb-home .quick-tile[href="/internet"] .quick-icon { color: #45e6a4; border-color: rgba(69,230,164,.42); background: linear-gradient(150deg, rgba(69,230,164,.26), rgba(18,150,135,.16)); box-shadow: 0 6px 20px rgba(50,215,155,.20), inset 0 1px 0 rgba(255,255,255,.09); }
.tsb-home .quick-eyebrow {
  color: var(--tsb-red-bright);
  font-family: var(--tsb-display);
  font-size: 10.5px; font-weight: 800; letter-spacing: 2px; text-transform: uppercase;
}
.tsb-home .quick-title {
  margin: 5px 0 4px;
  color: #fff; font-family: var(--tsb-display);
  font-size: 16px; font-weight: 750; letter-spacing: .2px; line-height: 1.25;
}
/* --tsb-muted on a tile this dark was the weakest text on the home screen;
   --tsb-text-2 keeps it readable in a lit showroom without shouting. */
.tsb-home .quick-desc { color: var(--tsb-text-2); font-size: 14px; line-height: 1.5; }
.tsb-home .quick-arrow {
  position: absolute; right: 16px; bottom: 16px;
  color: var(--tsb-muted); font-size: 16px; line-height: 1;
  transition: transform var(--tsb-ease), color var(--tsb-ease);
}
.tsb-home .quick-tile:hover .quick-arrow { transform: translateX(3px); color: var(--tsb-red-bright); }

/* Frosted glass on segment course cards + a red glow on hover. */
.tsb-home .card {
  background: linear-gradient(165deg, rgba(25,29,34,.64), rgba(13,15,18,.72)) !important;
  -webkit-backdrop-filter: blur(12px) saturate(1.05);
  backdrop-filter: blur(12px) saturate(1.05);
}
.tsb-home .card:hover { border-color: rgba(240, 42, 49, .4); box-shadow: 0 20px 46px rgba(160, 0, 8, .2); }

/* Framer-style scroll reveal — elements rise + fade as they enter the viewport.
   Gated by .tsb-reveal on <html>, which the head script adds only when
   IntersectionObserver exists and motion is allowed. No class => static content
   (no-JS / reduced-motion), and the hidden state lands before first paint so
   there is no flash. */
.tsb-reveal .tsb-home [data-reveal] { opacity: 0; transform: translateY(28px); will-change: opacity, transform; }
.tsb-reveal .tsb-home [data-reveal].reveal-in {
  opacity: 1; transform: none;
  transition: opacity .62s cubic-bezier(.2,.7,.2,1), transform .62s cubic-bezier(.2,.7,.2,1);
}
.tsb-reveal .tsb-home .quick-grid.reveal-in .quick-tile { opacity: 0; transform: translateY(20px); animation: tsbRise .55s cubic-bezier(.2,.7,.2,1) forwards; }
.tsb-reveal .tsb-home .quick-grid.reveal-in .quick-tile:nth-child(1) { animation-delay: .04s; }
.tsb-reveal .tsb-home .quick-grid.reveal-in .quick-tile:nth-child(2) { animation-delay: .12s; }
.tsb-reveal .tsb-home .quick-grid.reveal-in .quick-tile:nth-child(3) { animation-delay: .2s; }
.tsb-reveal .tsb-home .quick-grid.reveal-in .quick-tile:nth-child(4) { animation-delay: .28s; }
@keyframes tsbRise { to { opacity: 1; transform: none; } }
.tsb-home .card-thumb { background: #0b0d10; }
.tsb-home .card-body { padding: 18px; }
.tsb-home .card-title { font-size: 14px; line-height: 1.4; }
/* Section headers — frosted panels with a per-section accent driven by --seg.
   Inline styles now just set the colour (style="--seg:#…"); everything else is here. */
.tsb-home .seg-header {
  --seg: var(--tsb-red);
  position: relative;
  justify-content: space-between;
  overflow: hidden;
  padding: 15px 20px;
  border: 1px solid var(--tsb-line);
  border-left: 3px solid var(--seg);
  border-radius: var(--tsb-radius);
  background: linear-gradient(120deg, rgba(25,29,34,.62), rgba(13,15,18,.5));
  -webkit-backdrop-filter: blur(10px) saturate(1.05);
  backdrop-filter: blur(10px) saturate(1.05);
}
.tsb-home .seg-header::before {
  content: '';
  position: absolute; left: 0; top: 0; bottom: 0; width: 170px;
  background: linear-gradient(90deg, var(--seg), transparent);
  opacity: .13; pointer-events: none;
}
.tsb-home .seg-header > *, .tsb-home .seg-header .seg-badge { position: relative; z-index: 1; }
.tsb-home .seg-header.basics { --seg: var(--tsb-blue); }
.tsb-home .seg-header.master { --seg: var(--tsb-red-bright); }
.tsb-home .seg-header.genius { --seg: var(--tsb-gold); }
.tsb-home .seg-sub { margin-top: 4px; color: var(--tsb-muted); }

/* The carousel arrows are a desktop affordance, but the script that builds them
   runs at every width and every rule that sizes or positions them lives in the
   desktop query below. On a phone that left them as bare 4px-wide buttons,
   stretched to the global 44px touch-target height — a stray grey bar sitting
   between the course rows. Hidden by default; the desktop query turns them back
   on for a row that actually overflows. */
.tsb-home .seg-scroll-btn { display: none; }

/* Segment course rows → horizontal carousels on desktop. Cards keep ONE
   consistent size (no more big feature card next to small ones), and a section
   holding more than a rowful scrolls left/right via arrows instead of wrapping
   down to a second row. Mobile keeps its native touch-scroll carousel. */
@media (min-width: 681px) {
  .tsb-home .segment { position: relative; }
  .tsb-home .seg-carousel { position: relative; }
  .tsb-home .segment .grid,
  .tsb-home .segment .wasc-flex {
    display: flex;
    flex-wrap: nowrap;
    gap: 18px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    padding: 2px 2px 10px;
    scrollbar-width: none;
  }
  .tsb-home .segment .grid::-webkit-scrollbar,
  .tsb-home .segment .wasc-flex::-webkit-scrollbar { display: none; }
  .tsb-home .segment .grid > .card,
  .tsb-home .segment .wasc-flex > .card,
  .tsb-home .segment .wasc-flex > .card.feature {
    flex: 0 0 330px;
    max-width: 330px;
    scroll-snap-align: start;
  }
  .tsb-home .seg-scroll-btn {
    position: absolute; top: 50%; transform: translateY(-50%);
    z-index: 6; width: 42px; height: 42px; display: none; place-items: center;
    border: 1px solid var(--tsb-line-strong); border-radius: 50%;
    background: rgba(13,15,18,.86);
    -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
    color: #fff; cursor: pointer; box-shadow: 0 8px 22px rgba(0,0,0,.42);
    transition: background var(--tsb-ease), border-color var(--tsb-ease), opacity var(--tsb-ease);
  }
  .tsb-home .seg-carousel.has-overflow > .seg-scroll-btn { display: grid; }
  .tsb-home .seg-scroll-btn.prev { left: -14px; }
  .tsb-home .seg-scroll-btn.next { right: -14px; }
  .tsb-home .seg-scroll-btn:hover { border-color: rgba(240,42,49,.5); background: rgba(20,23,27,.96); }
  .tsb-home .seg-scroll-btn:disabled { opacity: 0; pointer-events: none; }
  .tsb-home .seg-scroll-btn svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 2.2; fill: none; stroke-linecap: round; stroke-linejoin: round; }
}

/* Legacy library aligned to TSB */
.tsb-library .header > img { display: none; }
.tsb-library .header::before { content: ''; display: block; width: min(300px, 48vw); height: 68px; background: url('../images/tsb-online-logo.png') center / contain no-repeat; }
.tsb-library .lib-hero { padding: 34px; border: 1px solid var(--tsb-line); border-radius: var(--tsb-radius-lg); background: linear-gradient(135deg, rgba(215,25,32,.15), rgba(255,255,255,.025)); text-align: left; }
.tsb-library .lib-title { color: #fff; font-family: var(--tsb-display); font-size: clamp(1.5rem, 4vw, 2.7rem); }
.tsb-library .lib-sub { color: var(--tsb-text-2); font-family: var(--tsb-body); }

/* Leaderboard */
.tsb-leaderboard .hero { padding: 44px 24px 38px; background: linear-gradient(180deg, rgba(215,25,32,.08), transparent); }
.tsb-leaderboard .hero h1 { font-size: clamp(1.8rem, 5vw, 3.5rem); }
.tsb-leaderboard .hero h1 em { color: var(--tsb-red-bright); }
.tsb-leaderboard .ptab { min-height: 48px; color: var(--tsb-muted); font-family: var(--tsb-display); font-size: 11px; font-weight: 750; }
.tsb-leaderboard .ptab.active { color: #fff; border-bottom-color: var(--tsb-red); background: rgba(215,25,32,.07); }
.tsb-leaderboard .main { max-width: var(--tsb-content); }

/* ── Leaderboard polish ──────────────────────────────────────────────────
   Brings the board in line with the home page: accent section panels,
   frosted player rows, a stronger podium, and a "show all" toggle so long
   rosters stay scannable. Each group keeps its own accent via --lb-accent. */
.tsb-leaderboard .section-head {
  display: flex; align-items: center; gap: 12px;
  margin-bottom: 14px; padding: 12px 16px;
  border: 1px solid var(--tsb-line);
  border-left: 3px solid var(--lb-accent, var(--tsb-red));
  border-radius: var(--tsb-radius);
  background: linear-gradient(100deg, rgba(255,255,255,.05), rgba(255,255,255,.015) 62%);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
}
.tsb-leaderboard .section-head.internet { --lb-accent: #22d3ee; }
.tsb-leaderboard .section-head.vip { --lb-accent: var(--tsb-gold); }
.tsb-leaderboard .section-head.sales { --lb-accent: var(--tsb-red-bright); }
.tsb-leaderboard .section-head.bdc { --lb-accent: #8b5cf6; }
.tsb-leaderboard .section-head.va { --lb-accent: var(--tsb-green); }
.tsb-leaderboard .section-head.desk { --lb-accent: var(--tsb-blue); }
.tsb-leaderboard .section-head.asst { --lb-accent: var(--tsb-steel); }
.tsb-leaderboard .section-head-title {
  color: #fff; font-family: var(--tsb-display);
  font-size: 13px; font-weight: 800; letter-spacing: 1.4px; text-transform: uppercase;
}
.tsb-leaderboard .section-head-line { flex: 1 1 auto; height: 1px; background: linear-gradient(90deg, var(--tsb-line), transparent); }
.tsb-leaderboard .section-head-count {
  flex: 0 0 auto; padding: 4px 11px;
  border: 1px solid var(--tsb-line); border-radius: 99px;
  background: rgba(255,255,255,.04); color: var(--tsb-muted);
  font-size: 10px; font-weight: 700; letter-spacing: .6px; white-space: nowrap;
}

.tsb-leaderboard .player-card.list-row {
  border: 1px solid var(--tsb-line); border-radius: var(--tsb-radius);
  background: linear-gradient(120deg, rgba(25,29,34,.6), rgba(13,15,18,.55));
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  transition: transform var(--tsb-ease), border-color var(--tsb-ease), background var(--tsb-ease), box-shadow var(--tsb-ease);
}
.tsb-leaderboard .player-card.list-row:hover {
  transform: translateX(3px);
  border-color: rgba(240,42,49,.42);
  background: linear-gradient(120deg, rgba(31,36,42,.72), rgba(16,18,22,.62));
  box-shadow: 0 14px 34px rgba(160,0,8,.18);
}
.tsb-leaderboard .pc-rank { color: var(--tsb-steel); font-family: var(--tsb-display); font-size: 12px; font-weight: 800; }
.tsb-leaderboard .pc-name { color: #fff; font-weight: 700; letter-spacing: .2px; }
.tsb-leaderboard .pc-role { color: var(--tsb-muted); }
.tsb-leaderboard .pc-xp { color: var(--tsb-red-bright); font-family: var(--tsb-display); font-weight: 800; }
.tsb-leaderboard .pc-tier {
  padding: 3px 9px; border: 1px solid var(--tsb-line); border-radius: 99px;
  background: rgba(255,255,255,.04); color: var(--tsb-text-2);
  font-size: 9px; font-weight: 800; letter-spacing: 1px; text-transform: uppercase;
}
.tsb-leaderboard .pc-view-link { color: var(--tsb-muted); transition: color var(--tsb-ease); }
.tsb-leaderboard .player-card.list-row:hover .pc-view-link { color: var(--tsb-red-bright); }

.tsb-leaderboard .podium-title {
  margin-bottom: 22px; color: var(--tsb-text-2);
  font-family: var(--tsb-display); font-size: 12px; font-weight: 800;
  letter-spacing: 3px; text-transform: uppercase;
}
.tsb-leaderboard .pod-player-card {
  border: 1px solid var(--tsb-line);
  background: linear-gradient(170deg, rgba(25,29,34,.68), rgba(13,15,18,.6));
  -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
  transition: transform var(--tsb-ease), box-shadow var(--tsb-ease);
}
.tsb-leaderboard .pod-player-card:hover { transform: translateY(-4px); }
.tsb-leaderboard .rank-1 .pod-player-card { box-shadow: 0 22px 54px rgba(160,0,8,.3); }

.tsb-leaderboard .lb-more { margin-top: 8px; }
/* .player-grid.as-list sets display:flex, which outranks the UA [hidden]
   rule — without this the "collapsed" rows keep rendering. */
.tsb-leaderboard .lb-more[hidden] { display: none !important; }
.tsb-leaderboard .lb-expand {
  display: inline-flex; align-items: center; gap: 9px;
  margin-top: 12px; padding: 9px 16px;
  border: 1px solid var(--tsb-line-strong); border-radius: 99px;
  background: rgba(255,255,255,.04); color: var(--tsb-text-2);
  font-family: var(--tsb-display); font-size: 10px; font-weight: 800;
  letter-spacing: 1.2px; text-transform: uppercase; cursor: pointer;
  transition: border-color var(--tsb-ease), background var(--tsb-ease), color var(--tsb-ease);
}
.tsb-leaderboard .lb-expand:hover { border-color: rgba(240,42,49,.5); background: rgba(240,42,49,.1); color: #fff; }
.tsb-leaderboard .lb-expand-ico {
  width: 0; height: 0;
  border-inline: 4px solid transparent; border-top: 5px solid currentColor;
  transition: transform var(--tsb-ease);
}
.tsb-leaderboard .lb-expand.is-open .lb-expand-ico { transform: rotate(180deg); }

/* Scroll reveal for the data-rendered sections. Deliberately fail-safe: the
   resting state is fully visible and .reveal-in only plays an entrance
   animation. These lists are rebuilt on filter/search, so nodes can be
   swapped out from under the observer — defaulting them to opacity:0 would
   risk leaving the roster permanently invisible. */
.tsb-reveal .tsb-leaderboard [data-reveal].reveal-in {
  animation: tsbRiseIn .55s cubic-bezier(.2, .7, .2, 1) both;
}
@keyframes tsbRiseIn {
  from { opacity: 0; transform: translateY(22px); }
  to { opacity: 1; transform: none; }
}
.tsb-leaderboard .pod-player-card { border-radius: var(--tsb-radius); }
.tsb-leaderboard .rank-1 .pod-player-card { border-color: rgba(215,25,32,.5); background: linear-gradient(180deg, rgba(215,25,32,.14), var(--tsb-surface)); box-shadow: var(--tsb-shadow-red); }
.tsb-leaderboard .player-card { border-radius: var(--tsb-radius); overflow: hidden; }
.tsb-leaderboard .player-card::before { width: 3px; }
.tsb-leaderboard .pc-name,
.tsb-leaderboard .pod-name {
  font-family: var(--tsb-display);
  font-weight: 700;
  letter-spacing: -.2px;
}
.tsb-leaderboard :where(.pc-rank, .pc-tier, .pc-xp, .pod-stat-v, .platform-block) {
  font-family: var(--tsb-display);
}
.tsb-leaderboard :where(.pc-role, .pod-role) {
  font-family: var(--tsb-body);
  font-weight: 600;
}
.tsb-leaderboard .section-head-title { font-family: var(--tsb-display); font-size: 13px; }

/* Alpha Grade */
.tsb-alpha .hero { padding: clamp(42px, 7vw, 78px) 24px 54px; background: radial-gradient(circle at 50% 0, rgba(215,25,32,.2), transparent 35rem), linear-gradient(180deg,#131519,#090a0c); }
.tsb-alpha .hero h1 { font-size: clamp(2rem, 6vw, 4.4rem); }
.tsb-alpha .hero h1 em { color: var(--tsb-red-bright); }
.tsb-alpha .hero-sub { max-width: 760px; color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: 16px; line-height: 1.75; }
.tsb-alpha .content { max-width: var(--tsb-content); }
.tsb-alpha .what-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.tsb-alpha .what-card {
  min-height: 260px;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-radius: var(--tsb-radius);
  padding: 24px 22px;
}
.tsb-alpha .what-icon {
  width: 48px;
  height: 48px;
  display: grid;
  place-items: center;
  margin-bottom: 18px;
  border: 1px solid rgba(215,25,32,.38);
  border-radius: 12px;
  background: linear-gradient(145deg, rgba(215,25,32,.16), rgba(215,25,32,.05));
  color: #ff5259;
  font-size: 12px;
  font-weight: 850;
  letter-spacing: 1px;
}
.tsb-alpha .what-title { min-height: 42px; display: flex; align-items: center; justify-content: center; font-family: var(--tsb-display); font-size: 13px; }
.tsb-alpha .what-desc { flex: 1; }
.tsb-alpha .what-pts { margin-top: 18px; }
/* ── Tier ladder as a climb diagram ──────────────────────────────────────
   Six equal cards read as six unrelated things. Alpha Grade is one continuous
   climb, so the tiers hang off a single rail: a gradient running from the
   Green Pea green up through to MVP gold, with each tier a node on it. The
   shape carries the meaning — you can see there is a top, and where each rung
   sits on the way there — before any of the text is read.

   Built entirely from the existing markup: .tier-badge was a 7px colour strip
   and becomes the node (it already carries each tier's colour), and the rail
   is a pseudo-element on the track. */
.tsb-alpha .tier-track {
  position: relative;
  gap: 0;
  padding-left: 30px;
  border: 0;
  background: none;
  box-shadow: none;
}
.tsb-alpha .tier-track::before {
  content: '';
  position: absolute;
  left: 10px; top: 16px; bottom: 16px;
  width: 2px;
  border-radius: 2px;
  background: linear-gradient(180deg,
    #22A05A 0%, #4FC3F7 20%, #8B5CF6 40%, #CC0000 62%, #C9A84C 82%, #E8152A 100%);
}
.tsb-alpha .tier-row {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: start;
  gap: 2px 14px;
  margin-bottom: 10px;
  padding: 14px 16px;
  overflow: visible;
  border: 1px solid var(--tsb-line);
  border-radius: 12px;
  background: var(--tsb-surface);
}
.tsb-alpha .tier-row:hover { transform: none; border-color: rgba(255,255,255,.14); }
/* The node. Ringed in the page background so the rail appears to pass behind
   it rather than through it. */
.tsb-alpha .tier-row .tier-badge {
  position: absolute;
  left: -27px; top: 17px;
  width: 14px; height: 14px;
  align-self: auto;
  border-radius: 50%;
  box-shadow: 0 0 0 4px var(--tsb-bg);
}
/* The rail already numbers the steps by position. */
.tsb-alpha .tier-emoji { display: none; }
.tsb-alpha .tier-info { grid-column: 1; padding: 0; }
.tsb-alpha .tier-pts { grid-column: 2; padding: 0; text-align: right; }
.tsb-alpha .tip-card { border-radius: var(--tsb-radius); }

@media (max-width: 900px) {
  .tsb-alpha .what-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tsb-home .quick-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* The bottom tab bar anchors the mobile screen; the copyright footer is
     redundant there and just crowds the last row of cards. */
  .tsb-home footer { display: none; }
}

@media (max-width: 600px) {
  .tsb-alpha .what-grid { grid-template-columns: minmax(0, 1fr); }
  .tsb-alpha .what-card { min-height: 0; }
  .tsb-alpha .what-title { min-height: 0; }
}

/* Player profile */
.tsb-profile .hero-banner { border-bottom: 1px solid var(--tsb-line); background: radial-gradient(circle at 15% 35%, rgba(215,25,32,.18), transparent 22rem), linear-gradient(130deg, #171a1f, #0b0d10 68%); }
.tsb-profile .hero-inner { max-width: var(--tsb-content); padding-top: 34px; padding-bottom: 34px; }
.tsb-profile .avatar-ring { border-color: var(--tsb-red); box-shadow: 0 0 0 6px rgba(215,25,32,.1), 0 20px 50px rgba(0,0,0,.3); }
.tsb-profile .profile-name { color: #fff; font-size: clamp(1.8rem, 5vw, 3rem); }
.tsb-profile .profile-role { color: var(--tsb-text-2); font-family: var(--tsb-body); }
.tsb-profile .xp-card { border-color: rgba(215,25,32,.28); background: linear-gradient(145deg, rgba(215,25,32,.12), rgba(17,19,23,.8)); box-shadow: var(--tsb-shadow-red); }
.tsb-profile .page-body { max-width: var(--tsb-content); }
.tsb-profile .body-grid { gap: 22px; }
.tsb-profile .stats-section { max-width: var(--tsb-content); padding-inline: 24px; }
.tsb-profile .stats-section > div { max-width: var(--tsb-content) !important; }
/* Alpha Grade Breakdown: the four cards sit in one row on desktop, dropping to
   two on narrow screens so the labels don't wrap into slivers. */
.tsb-profile .stats-section > div > div:nth-child(2) { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; }
.tsb-profile .section-card, .tsb-profile .side-card { border-color: var(--tsb-line); background: linear-gradient(145deg, rgba(27,30,35,.96), rgba(16,18,21,.98)); box-shadow: 0 12px 36px rgba(0,0,0,.18); }
.tsb-profile .sec-head { color: var(--tsb-text-2); font-family: var(--tsb-display); }
.tsb-profile .course-row { background: rgba(255,255,255,.025); }
.tsb-profile .data-label { color: var(--tsb-muted); }

/* Daily scorecard */
.tsb-scorecard .scorecard-title { color: #17191d; font-family: var(--tsb-display); }
.tsb-scorecard .hero { background: linear-gradient(135deg, rgba(215,25,32,.12), rgba(255,255,255,.025)); border-bottom-color: var(--tsb-line); }
.tsb-scorecard .hero-inner { max-width: var(--tsb-content); }
.tsb-scorecard .hero-title { color: #fff; font-size: clamp(1.45rem, 4vw, 2.35rem); }
.tsb-scorecard .steps { position: sticky; top: 48px; z-index: 160; border-color: var(--tsb-line); background: rgba(12,14,16,.95); backdrop-filter: blur(16px); }
.tsb-scorecard .step-tab { min-height: 58px; color: var(--tsb-muted); font-family: var(--tsb-display); font-size: 12px; font-weight: 750; }
.tsb-scorecard .step-tab.active { color: #fff; border-bottom-color: var(--tsb-red); background: rgba(215,25,32,.065); }
.tsb-scorecard main { max-width: var(--tsb-content); }
.tsb-scorecard .sc-card { border-radius: var(--tsb-radius-lg); padding: clamp(18px, 3vw, 30px); }
.tsb-scorecard .sec-grade-card, .tsb-scorecard .player-row { border-color: var(--tsb-line); background: rgba(255,255,255,.025); }
.tsb-scorecard .csv-table-wrap { overflow-x: auto; border: 1px solid var(--tsb-line); border-radius: var(--tsb-radius-sm); }
.tsb-scorecard table { min-width: 660px; }
.tsb-scorecard th { position: sticky; top: 0; background: #171a1f; color: var(--tsb-steel); font-size: 10px; letter-spacing: 1px; }

@media (min-width: 761px) and (max-width: 1366px) {
  .tsb-home .page { padding-top: 20px; }
  .portal-hero {
    grid-template-columns: minmax(0, 1.2fr) minmax(250px, .8fr);
    gap: clamp(22px, 2.6vw, 34px);
    padding: clamp(28px, 3.2vw, 44px);
  }
  .portal-eyebrow { margin-bottom: 10px; font-size: 9px; letter-spacing: 2px; }
  .portal-hero h1 {
    max-width: 620px;
    margin-bottom: 13px;
    font-size: clamp(2.1rem, 3.5vw, 3rem);
    line-height: 1.07;
    letter-spacing: -.025em;
  }
  .portal-hero-copy > p { font-size: 15px; line-height: 1.58; }
  .portal-actions { margin-top: 18px; }
  .portal-command { padding: 17px; }
}

@media (max-width: 760px) {
  .portal-hero { grid-template-columns: 1fr; }
  .portal-command { max-width: 520px; }
  .portal-hero h1 { font-size: 2.2rem; }
  .platform-shortcuts { grid-template-columns: repeat(2, 1fr); }
}

@media (min-width: 681px) and (max-width: 1000px) {
  .tsb-profile .stats-section > div > div:nth-child(2) { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }
}

@media (max-width: 680px) {
  body.tsb-page { -webkit-text-size-adjust: 100%; }
  body.tsb-page :where(.header, body > header) { min-height: 68px !important; padding: 8px 12px !important; flex-direction: row !important; justify-content: center !important; gap: 8px !important; }
  body.tsb-page :where(.header, body > header) > img { height: 48px !important; max-width: 48vw; }
  body.tsb-page .header-right { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); flex-direction: row; align-items: center; }
  body.tsb-page .live-pill { padding: 3px 7px; font-size: 8px; letter-spacing: .8px; }
  body.tsb-page .site-nav { padding-inline: 6px; -webkit-mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent); mask-image: linear-gradient(90deg, #000 0, #000 calc(100% - 24px), transparent); }
  body.tsb-page .nav-inner { overflow-x: auto; scroll-snap-type: x proximity; scrollbar-width: none; }
  body.tsb-page .nav-inner::-webkit-scrollbar { display: none; }
  body.tsb-page .nav-link { min-height: 44px; flex: 0 0 auto; padding-inline: 13px; scroll-snap-align: center; font-size: 10px; }
  body.tsb-page :where(button, .portal-btn, input, select) { min-height: 44px; }
  body.tsb-page :where(.card-desc, .seg-sub, .sc-sub, .what-desc, .tier-desc, .sec-body) { font-size: 13px; }
  body.tsb-page :where(.page, .content, main) { padding-inline: 14px; }
  .portal-hero { padding: 28px 22px; border-radius: 14px; }
  .portal-hero h1 { font-size: 2rem; }
  .portal-hero-copy > p { font-size: 15px; line-height: 1.62; }
  .portal-actions { flex-direction: column; }
  .portal-btn { width: 100%; }
  .portal-command { padding: 17px; }
  .platform-shortcuts { grid-template-columns: 1fr; }
  .platform-shortcut { min-height: 70px; padding: 14px; }
  .tsb-home .feat-hero { grid-template-columns: 1fr; }
  .tsb-home .feat-thumb { min-height: 0; height: 172px; }
  .tsb-home .feat-body { flex-direction: column; align-items: flex-start; gap: 14px; padding: 18px 20px; }
  .tsb-home .feat-cta { align-self: stretch; justify-content: center; }
  .tsb-home .quick-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
  .tsb-home .quick-tile { min-height: 148px; padding: 16px; }
  .tsb-home .card-body { padding: 16px; }
  .tsb-home .grid, .tsb-library .grid { grid-template-columns: minmax(0, 1fr); }
  .tsb-home .segment, .tsb-library .segment { margin-bottom: 30px; }
  .tsb-home .seg-header, .tsb-library .seg-header { align-items: flex-start; padding: 14px; }
  .tsb-library .lib-hero { padding: 24px 20px; }
  .tsb-library .header { justify-content: space-between !important; }
  .tsb-library .header::before { width: 138px; height: 48px; }
  .tsb-library .header-right { position: static; transform: none; flex-direction: column; align-items: flex-end; gap: 3px; }
  .tsb-library .logout-btn { padding: 2px 6px; font-size: 8px; }
  .tsb-library .lib-hero + div > a { flex-direction: column; align-items: stretch !important; }
  .tsb-library .lib-hero + div > a > img { width: 100% !important; height: 168px !important; object-fit: cover; content: url('../images/tsb-thumb-leaderboard.png'); }
  .tsb-library .lib-hero + div > a > div { padding: 18px !important; }
  .tsb-leaderboard .header { justify-content: space-between !important; }
  .tsb-leaderboard .header > img { max-width: 138px; }
  .tsb-leaderboard .header-right { position: static; transform: none; }
  .tsb-leaderboard .admin-toggle { padding: 4px 7px; font-size: 10px; }
  .tsb-leaderboard .hero { padding: 34px 18px 30px; }
  .tsb-leaderboard .hero-sub { padding-inline: 8px; font-size: 13px; }
  .tsb-leaderboard main { padding-top: 24px; }
  .tsb-leaderboard .pod-stats-row > div:last-child { display: none; }
  .tsb-leaderboard .pod-view-link { font-size: 9px; }
  .tsb-alpha .hero { padding: 38px 18px 34px; }
  .tsb-alpha .hero h1 { font-size: 2.05rem; }
  .tsb-alpha .hero-sub { font-size: 14px; line-height: 1.65; }
  .tsb-alpha .content { padding-top: 32px; }
  /* "What Earns Your Alpha Grade" on a phone. The desktop card above is a
     260px-tall centred column, which cost most of a screen each — three items
     meant three swipes to read a list. Here each becomes a compact row: number
     pinned in a narrow left column, text left-aligned beside it, so all of them
     are readable at once. Needs .tsb-alpha specificity to beat that block. */
  .tsb-alpha .what-grid { gap: 10px; margin-bottom: 32px; }
  .tsb-alpha .what-card {
    min-height: 0;
    display: grid;
    grid-template-columns: 30px 1fr;
    align-items: start;
    gap: 2px 12px;
    padding: 13px 14px;
    text-align: left;
  }
  .tsb-alpha .what-icon {
    grid-column: 1; grid-row: 1 / span 3;
    width: 30px; height: 30px; margin: 0;
    font-size: 11px;
  }
  /* The desktop rule centres this in a 42px-tall flex box. */
  .tsb-alpha .what-title {
    grid-column: 2; min-height: 0;
    display: block; justify-content: flex-start;
    font-size: 12px; letter-spacing: .6px;
  }
  .tsb-alpha .what-desc { grid-column: 2; flex: none; font-size: 12.5px; line-height: 1.55; }
  .tsb-alpha .what-pts { grid-column: 2; justify-self: start; margin-top: 7px; }
  .tsb-alpha .sec-title { font-size: 1.15rem; letter-spacing: .5px; margin-bottom: 13px; }
  .tsb-alpha .sec-label { font-size: 11px; letter-spacing: 2px; margin-bottom: 7px; }

  /* Tier ladder, phone: the threshold is the number that matters, so it stays
     legible while the prose shrinks. */
  .tsb-alpha .tier-track { padding-left: 26px; }
  .tsb-alpha .tier-track::before { left: 8px; }
  .tsb-alpha .tier-row { padding: 12px 13px; margin-bottom: 8px; gap: 2px 10px; }
  .tsb-alpha .tier-row .tier-badge { left: -25px; top: 15px; width: 12px; height: 12px; }
  .tsb-alpha .tier-name { font-size: .92rem; letter-spacing: 1px; }
  .tsb-alpha .tier-range { font-size: 11px; }
  .tsb-alpha .tier-desc { font-size: 12px; line-height: 1.55; margin-top: 4px; }
  .tsb-alpha .tier-pts-num { font-size: 1rem; }
  .tsb-alpha .tier-pts-lbl { font-size: 9px; }
  .tsb-profile > header { min-height: 106px !important; flex-direction: column !important; }
  .tsb-profile > header > div:first-of-type { position: static !important; transform: none !important; }
  .tsb-profile > header > div:last-of-type { display: none !important; }
  /* The identity header was taking the whole first screen — a 112px spinning
     avatar, a 2.2rem name, the role and the phone number, before a single
     piece of the actual profile. An app states who you are in a strip and gets
     on with it, so the avatar and name come down and the block sits in roughly
     a third of the height. */
  .tsb-profile .hero-inner { padding: 18px 16px 20px !important; gap: 12px !important; }
  .tsb-profile .avatar-ring { width: 74px !important; height: 74px !important; }
  .tsb-profile .profile-name { font-size: 1.32rem; line-height: 1.14; margin-bottom: 3px; }
  .tsb-profile .meta-row { gap: 7px; }
  .tsb-profile .xp-section { padding: 0 16px 24px; }
  .tsb-profile .xp-header { flex-direction: column; align-items: stretch; gap: 18px; }
  .tsb-profile .tier-ladder { width: 100%; }
  .tsb-profile .tier-step { font-size: .52rem; letter-spacing: .5px; }
  .tsb-profile .stats-section { padding: 0 0 24px; }
  .tsb-profile .stats-section > div { padding: 20px 16px 0 !important; }
  .tsb-profile .stats-section > div > div:nth-child(2) { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
  .tsb-profile .stats-section .stat-card { min-width: 0; padding: 12px !important; }
  .tsb-profile .section-card, .tsb-profile .side-card { border-radius: 12px; }
  .tsb-profile .course-pct { display: none; }
  .tsb-profile .course-row { padding: 13px 10px; gap: 8px; }
  .tsb-profile .course-chip { flex: 0 0 auto; }
  .tsb-profile .modal-box { width: calc(100vw - 24px); max-height: calc(100vh - 24px); overflow-y: auto; }
  .tsb-profile .sb-section .sec-head { flex-wrap: wrap; }
  .tsb-profile #kpi-date { flex-basis: 100%; margin: 2px 0 0 13px !important; white-space: nowrap; letter-spacing: .5px; }
  .tsb-scorecard .steps { top: 44px; justify-content: flex-start; overflow-x: auto; }
  .tsb-scorecard .step-tab { flex: 0 0 auto; min-width: 150px; }
  .tsb-scorecard .header { justify-content: space-between !important; }
  .tsb-scorecard .header > img { max-width: 142px; }
  .tsb-scorecard .header-right { position: static; transform: none; flex-direction: column; align-items: flex-end; }
  .tsb-scorecard .scorecard-title { max-width: 118px; font-size: 10px; line-height: 1.25; text-align: right; }
  .tsb-scorecard .logout-btn { margin-top: 0 !important; padding: 2px 7px; font-size: 9px; }
  .tsb-scorecard .hero { padding-block: 22px; }
  .tsb-scorecard .hero-inner { align-items: flex-start; padding-inline: 14px; }
  .tsb-scorecard .type-toggle { width: 100%; }
  .tsb-scorecard .type-btn { flex: 1; min-height: 42px; }
  .tsb-scorecard .date-wrap { width: 100%; justify-content: space-between; }
  .tsb-scorecard .date-input { max-width: 175px; }
  .tsb-scorecard .sc-card { padding: 18px 15px; }
  .tsb-scorecard .player-row { flex-wrap: wrap; padding-block: 12px; }
  .tsb-scorecard .nav-row { flex-direction: column-reverse; align-items: stretch; }
  .tsb-scorecard .nav-row button { width: 100%; }
}

@media (max-width: 430px) {
  .portal-hero { margin-inline: -2px; padding: 25px 18px; }
  .portal-eyebrow { font-size: 8px; letter-spacing: 1.7px; }
  .portal-hero h1 { font-size: clamp(1.45rem, 7vw, 1.72rem); line-height: 1.15; }
  .command-title { font-size: 14px; }
  .tsb-home .lib-title { font-size: 1.35rem; }
  .tsb-leaderboard .olympic-wrap { gap: 4px; }
  .tsb-leaderboard .pod-player-card { padding: 10px 5px; }
  .tsb-leaderboard .pod-av { width: 50px; height: 50px; }
  .tsb-leaderboard .rank-1 .pod-av { width: 58px; height: 58px; }
  .tsb-leaderboard .pod-role { font-size: 9px; }

  /* Leaderboard rows, phone. Three things go:
     — the rank drops from 1.1rem to a quiet label; it orders the list, it isn't
       the headline
     — the role, because every row already sits under a section heading that
       says it ("SALES ASSOCIATES · 24 players"), so it was printed twice
     — "View Profile →", because the whole card is already an <a> to that
       profile; it was a second tap target for what tapping anywhere does
     Needs .player-card.list-row specificity to beat the rules in profiles.html
     that deliberately switch both back on below 600px. */
  .tsb-leaderboard .player-card.list-row .pc-rank { width: 26px; font-size: 12px; }
  .tsb-leaderboard .player-card.list-row .pc-role { display: none; }
  .tsb-leaderboard .player-card.list-row .pc-foot { display: none; }
  .tsb-leaderboard .player-card.list-row .pc-name { font-size: .84rem; }

  /* Send Invite is left exactly as it was — it's admin-only and Ray is happy
     with it. Pulling it inline stole the width the name needs and truncated
     "Frankie Macias" to "F". */

  /* Group headers become headings, not cards. A bordered, blurred, rounded
     panel per group was competing with the players listed inside it — on a
     phone the label needs to be a label. */
  .tsb-leaderboard .section-head {
    margin-bottom: 9px;
    padding: 0 2px 7px;
    border: 0;
    border-bottom: 1px solid var(--tsb-line);
    border-radius: 0;
    background: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .tsb-leaderboard .section-head-title { font-size: 11px; letter-spacing: 1.1px; }
  .tsb-leaderboard .section-head-count {
    padding: 0; border: 0; background: none; font-size: 10px;
  }

  /* The dropdown beneath it already reads "All Players" — the kicker was just
     restating the page the tab bar names. */
  .tsb-leaderboard .leaderboard-filter-kicker { display: none; }

  /* Managers appear under their own heading with no rank shown, so a separate
     "MANAGEMENT — NOT RANKED" banner explains something already visible. */
  .tsb-leaderboard .mgmt-divider { display: none; }

  /* This page defines its own --f-body, so the search field was rendering in a
     different face from everything around it. */
  .tsb-leaderboard .search-input { font-family: var(--tsb-body); font-size: .8rem; }

  /* The filter is moved next to the search on phones (see the relocation in
     profiles.html) — a whole sticky bar across the top for one dropdown is
     website furniture, and it kept the podium off the first screen. Sat beside
     search it reads as what it is: another way to narrow the same list. */
  .tsb-leaderboard .search-row {
    display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  }
  .tsb-leaderboard .search-row .search-wrap { flex: 1 1 auto; min-width: 0; }
  .tsb-leaderboard .search-row .leaderboard-filterbar {
    position: static;
    flex: 0 0 auto;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .tsb-leaderboard .search-row .leaderboard-filter-copy { display: none; }
  /* Capped so the search field keeps a usable width — unconstrained, the
     filter took about two thirds of the row and left "Searc…". */
  .tsb-leaderboard .search-row .leaderboard-select-shell { max-width: 168px; }
  .tsb-leaderboard .search-row .search-wrap { min-width: 120px; }
  /* The padding here is NOT decoration: the icon and arrow are absolutely
     positioned, and this is the space reserved for them. Flattening it to a
     symmetric value ran the label underneath the icon. */
  .tsb-leaderboard .search-row .leaderboard-filter-trigger {
    min-height: 40px;
    padding: 0 30px 0 32px;
    font-size: .72rem;
  }
  .tsb-leaderboard .search-row .leaderboard-select-icon { left: 11px; }
  /* A long option name ellipses rather than widening the control. */
  .tsb-leaderboard .search-row .leaderboard-filter-trigger > span:not([class]) {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  /* Right-anchored so a long option name grows leftwards instead of off-screen. */
  .tsb-leaderboard .search-row .leaderboard-filter-menu { right: 0; left: auto; min-width: 190px; }

  /* A copyright line is website chrome. An app screen ends at its content. */
  .tsb-leaderboard footer { display: none; }

  /* The remaining row furniture, sized for a phone rather than a desktop table. */
  .tsb-leaderboard .player-card.list-row .pc-tier { font-size: 9px; }
  .tsb-leaderboard .player-card.list-row .pc-xp { font-size: 11px; }
  .tsb-leaderboard .pod-stat-v { font-size: .88rem; }
  .tsb-profile .stats-grid, .tsb-profile .badges-grid { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; gap: 8px; }
  .tsb-profile .course-ico { display: none; }
  .tsb-alpha .tier-row { padding-inline: 13px; }
}

/* Mobile training carousels --------------------------------------------- */
@media (max-width: 680px) {
  .tsb-home #training-center {
    white-space: nowrap;
  }

  .training-feature-carousel,
  .tsb-home .segment .grid {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    overflow-y: hidden;
    margin-inline: -14px;
    padding: 5px 14px 13px;
    scroll-padding-inline: 14px;
    scroll-snap-type: inline mandatory;
    overscroll-behavior-inline: contain;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .training-feature-carousel::-webkit-scrollbar,
  .tsb-home .segment .grid::-webkit-scrollbar { display: none; }

  .training-feature-carousel { margin-bottom: 30px; }
  .training-feature-slide {
    min-width: 0;
    flex: 0 0 min(76vw, 296px);
    margin-bottom: 0 !important;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
  .training-feature-slide .feat-link { height: 100%; }
  .training-feature-slide .feat-thumb { height: 112px !important; }
  .training-feature-slide .feat-link > div { padding: 12px 13px !important; }
  .training-feature-slide .feat-link [style*="font-size:1.5rem"] {
    font-size: 1.05rem !important;
    line-height: 1.35;
    letter-spacing: .7px !important;
  }
  .training-feature-slide .feat-link [style*="font-size:12px"] { font-size: 11px !important; }
  .training-feature-slide .click-here-pill { font-size: 9px; letter-spacing: .65px; }

  .tsb-home .segment .grid { grid-template-columns: none; }
  .tsb-home .segment .grid > .card,
  .tsb-home .segment .grid > .card.feature {
    min-width: 0;
    max-width: none;
    flex: 0 0 min(74vw, 288px);
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
  .tsb-home .segment .wasc-flex { flex-wrap: nowrap; align-items: stretch; }
  .tsb-home .segment .card-thumb {
    height: 132px;
    min-height: 0 !important;
    aspect-ratio: auto;
  }
  .tsb-home .segment .card-body { padding: 13px; }
  .tsb-home .segment .card-title { font-size: 13px; line-height: 1.4; }
  .tsb-home .segment .card-desc { font-size: 12px; line-height: 1.55; }
  .tsb-home .segment .card-tag { font-size: 8px; letter-spacing: .35px; }
}

/* Unified platform header ------------------------------------------------ */
body.tsb-page .platform-header {
  position: sticky !important;
  top: 0;
  z-index: 300;
  display: flex !important;
  min-height: 72px !important;
  align-items: center !important;
  justify-content: flex-start !important;
  gap: clamp(12px, 2vw, 26px) !important;
  padding: 0 clamp(16px, 3vw, 38px) !important;
  background: rgba(9, 11, 14, .97) !important;
  border-bottom: 1px solid rgba(255,255,255,.1) !important;
  box-shadow: 0 12px 34px rgba(0,0,0,.28) !important;
  backdrop-filter: blur(20px);
}

.platform-brand {
  position: relative;
  order: 1;
      width: 236px;
      height: 48px;
  flex: 0 0 auto;
  overflow: hidden;
  border: 1px solid rgba(255,255,255,.14);
  border-radius: 9px;
    background: transparent;
    box-shadow: none;
}

.platform-brand img {
  position: absolute;
  top: 50%;
  left: 50%;
    width: 100% !important;
    height: 100% !important;
    max-width: none !important; object-fit: contain;
  transform: translate(-50%, -50%);
}
.platform-brand picture { display: block; width: 100%; height: 100%; }

.tsb-library .platform-header::before { display: none !important; }
.tsb-library .platform-header > .platform-brand { display: block; }

body.tsb-page .platform-nav {
  position: static;
  order: 2;
  min-width: 0;
  flex: 1 1 auto;
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
  backdrop-filter: none;
  mask-image: none;
}

body.tsb-page .platform-nav .nav-inner {
  max-width: none;
  justify-content: center;
  overflow: visible;
}

body.tsb-page .platform-nav .nav-link {
  position: relative;
  min-height: 70px;
  padding: 0 clamp(10px, 1.4vw, 18px);
  border: 0;
  background: transparent;
  color: #8f98a3;
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: .65px;
}

body.tsb-page .platform-nav .nav-link::after {
  content: '';
  position: absolute;
  right: 12px;
  bottom: 0;
  left: 12px;
  height: 2px;
  border-radius: 2px 2px 0 0;
  background: transparent;
}

body.tsb-page .platform-nav .nav-link:hover { color: #fff; }
body.tsb-page .platform-nav .nav-link.active { color: #fff; }
body.tsb-page .platform-nav .nav-link.active::after { background: var(--tsb-red-bright); box-shadow: 0 -4px 15px rgba(215,25,32,.4); }
.platform-nav-icon { display: none; }
/* The profile action button already covers this destination on desktop. Match
   every spelling of the href the pages use, extensionless included. */
body.tsb-page .platform-nav .nav-link[href="/profile.html"],
body.tsb-page .platform-nav .nav-link[href="profile.html"],
body.tsb-page .platform-nav .nav-link[href="/profile"] { display: none; }

/* The Daily Scorecard is an admin tool behind an access code, so advertising it
   to every player was a tab that could only ever dead-end in a gate they can't
   pass. The pre-paint gate stamps .tsb-admin, so this resolves in the first
   frame — a player never glimpses it. */
html:not(.tsb-admin) body.tsb-page .platform-nav .nav-link[href="/scorecard"],
html:not(.tsb-admin) body.tsb-page .platform-nav .nav-link[href="/scorecard.html"],
html:not(.tsb-admin) body.tsb-page .platform-nav .nav-link[href="scorecard.html"] { display: none !important; }

body.tsb-page .platform-header > .header-right,
body.tsb-page .platform-header > .profile-header-tools {
  position: static !important;
  order: 3;
  display: flex;
  flex: 0 0 auto;
  align-items: center !important;
  flex-direction: row !important;
  gap: 7px;
  transform: none !important;
}

body.tsb-page .platform-header .live-pill {
  padding: 5px 9px;
  border-color: rgba(215,25,32,.28);
  background: rgba(215,25,32,.1);
  color: #ff858a;
}
body.tsb-page .platform-header .logout-btn,
body.tsb-page .platform-header .admin-toggle,
body.tsb-page .platform-header .profile-header-tools :where(a, button) {
  min-height: 34px;
  display: inline-flex;
  align-items: center;
  padding: 0 10px;
  border: 1px solid rgba(255,255,255,.12);
  border-radius: 8px;
  background: rgba(255,255,255,.045);
  color: #c4cad2;
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 750;
  text-decoration: none;
}
body.tsb-page .platform-header .admin-toggle[hidden] { display: none !important; }
body.tsb-page .platform-header .scorecard-title { color: #d6dbe1; font-size: 10px; }
.platform-status-pill {
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 0 10px;
  border: 1px solid rgba(215,25,32,.28);
  border-radius: 999px;
  background: rgba(215,25,32,.1);
  color: #ff858a;
  font-family: var(--tsb-display);
  font-size: 9px;
  font-weight: 750;
  letter-spacing: .75px;
  text-transform: uppercase;
  white-space: nowrap;
}
.platform-status-dot {
  width: 6px;
  height: 6px;
  flex: 0 0 6px;
  border-radius: 50%;
  background: var(--tsb-red-bright);
  box-shadow: 0 0 0 4px rgba(215,25,32,.1);
}

/* The platform bar is one shared product surface. Every string in it is a label
   or a control, so the whole bar is pinned to the display face rather than
   inheriting the page's body copy. */
body.tsb-page .platform-header,
body.tsb-page .platform-header .nav-link,
body.tsb-page .platform-header .platform-profile-action,
body.tsb-page .platform-header .live-pill,
body.tsb-page .platform-header .logout-btn,
body.tsb-page .platform-header .admin-toggle,
body.tsb-page .platform-header .scorecard-title,
body.tsb-page .platform-header .profile-header-tools,
body.tsb-page .platform-header .profile-header-tools :where(a, button) {
  font-family: var(--tsb-display);
}

.platform-profile-action {
  order: 4;
  min-height: 38px;
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 7px;
  padding: 0 13px;
  border: 1px solid rgba(255,255,255,.13);
  border-radius: 9px;
  background: rgba(255,255,255,.045);
  color: #e4e7eb;
  font-size: 11px;
  font-weight: 800;
  text-decoration: none;
  transition: border-color var(--tsb-ease), background var(--tsb-ease), transform var(--tsb-ease);
}
.platform-profile-action:hover,
.platform-profile-action.active { border-color: rgba(215,25,32,.42); background: rgba(215,25,32,.1); color: #fff; transform: translateY(-1px); }

/* Session state is resolved by a tiny blocking script in <head> that stamps
   .tsb-authed / .tsb-anon on <html> before first paint, so the label is
   correct in the very first frame and never swaps under the user. Both labels
   ship in the markup; CSS picks one. Anonymous is the default, so a visitor
   with no JS still gets a working "Login" affordance rather than a blank. */
.tsb-when-authed { display: none; }
html.tsb-authed .tsb-when-authed { display: inline; }
html.tsb-authed .tsb-when-anon { display: none; }

/* Logged-out state ("Login") stays visually distinct at rest, not just on
   hover — the whole point is to be noticeable without interaction. */
html:not(.tsb-authed) .platform-profile-action { border-color: rgba(215,25,32,.5); background: rgba(215,25,32,.16); color: #fff; }
html:not(.tsb-authed) .platform-profile-action .platform-profile-icon { background: rgba(215,25,32,.3); }
html:not(.tsb-authed) .platform-profile-action:hover { border-color: rgba(215,25,32,.75); background: rgba(215,25,32,.24); }
.platform-profile-icon { display: grid; width: 22px; height: 22px; place-items: center; border-radius: 50%; background: rgba(215,25,32,.14); font-size: 11px; }
.platform-profile-icon svg { width: 15px; height: 15px; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }

.platform-menu-toggle {
  order: 5;
  width: 42px;
  height: 40px;
  display: none;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: 4px;
  border: 1px solid rgba(255,255,255,.13);
  border-radius: 9px;
  background: rgba(255,255,255,.045);
  cursor: pointer;
}
.platform-menu-toggle span { width: 17px; height: 2px; border-radius: 2px; background: #e8ebef; transition: transform var(--tsb-ease), opacity var(--tsb-ease); }
.platform-header.menu-open .platform-menu-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.platform-header.menu-open .platform-menu-toggle span:nth-child(2) { opacity: 0; }
.platform-header.menu-open .platform-menu-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }
.platform-menu-backdrop { display: none; }

.tsb-scorecard .steps { top: 72px; }

@media (max-width: 900px) {
  body.tsb-page .platform-header {
    min-height: 62px !important;
    flex-wrap: nowrap !important;
    gap: 8px !important;
    padding: 7px 10px !important;
  }
  .platform-brand { width: 48px; height: 48px; margin-right: auto; border-radius: 11px; }
  .platform-brand img {
    top: 0;
    left: 0;
    width: 100% !important;
    height: 100% !important;
    object-fit: cover;
    transform: none;
  }
  .platform-profile-action { order: 2; width: 40px; min-height: 40px; justify-content: center; padding: 0; }
  .platform-profile-label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .platform-profile-icon { width: 24px; height: 24px; }
  .platform-menu-toggle { display: flex; order: 3; }
  body.tsb-page .platform-nav {
    position: fixed;
    top: 70px;
    right: 10px;
    bottom: auto;
    z-index: 295;
    display: none;
    width: min(320px, calc(100vw - 20px));
    height: 290px !important;
    max-height: calc(100dvh - 82px);
    margin: 0;
    overflow-y: auto;
    padding-bottom: 60px;
    border: 1px solid rgba(255,255,255,.11);
    border-radius: 12px;
    background: #0d0f12;
    box-shadow: 0 24px 60px rgba(0,0,0,.58), 0 0 0 1px rgba(215,25,32,.06);
  }
  body.tsb-page .platform-header.menu-open .platform-nav { display: block; }
  body.tsb-page .platform-nav .nav-inner { display: grid; grid-template-columns: 1fr; gap: 0; overflow: visible; }
  /* The profile action collapses to a bare icon at this width, so the menu
     re-shows the labelled destination the desktop rule hides. Must list every
     href spelling the desktop rule matches, or the link stays hidden here. */
  body.tsb-page .platform-nav .nav-link,
  body.tsb-page .platform-nav .nav-link[href="/profile.html"],
  body.tsb-page .platform-nav .nav-link[href="profile.html"],
  body.tsb-page .platform-nav .nav-link[href="/profile"] {
    min-height: 46px;
    display: flex;
    gap: 12px;
    justify-content: flex-start;
    padding: 0 18px;
    border-right: 0;
    border-bottom: 1px solid rgba(255,255,255,.055);
    font-size: 10px;
  }
  body.tsb-page .platform-nav .platform-nav-icon {
    width: 19px;
    height: 19px;
    display: inline-flex;
    flex: 0 0 19px;
    align-items: center;
    justify-content: center;
    color: #a8b0ba;
  }
  body.tsb-page .platform-nav .platform-nav-icon svg {
    width: 100%;
    height: 100%;
    stroke: currentColor;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
  }
  body.tsb-page .platform-nav .nav-link:hover .platform-nav-icon,
  body.tsb-page .platform-nav .nav-link.active .platform-nav-icon { color: var(--tsb-red-bright); }
  body.tsb-page .platform-nav .nav-link::after { top: 10px; right: auto; bottom: 10px; left: 0; width: 2px; height: auto; }
  body.tsb-page .platform-header > .header-right,
  body.tsb-page .platform-header > .profile-header-tools { display: none !important; }
  body.tsb-page .platform-header.menu-open > .header-right,
  body.tsb-page .platform-header.menu-open > .profile-header-tools {
    position: fixed !important;
    top: 300px !important;
    right: 10px !important;
    z-index: 3;
    width: min(320px, calc(100vw - 20px));
    min-height: 60px;
    display: flex !important;
    justify-content: flex-end;
    padding: 10px 14px;
    border-right: 1px solid rgba(255,255,255,.11);
    border-bottom: 1px solid rgba(255,255,255,.11);
    border-left: 1px solid rgba(255,255,255,.11);
    border-top: 1px solid rgba(255,255,255,.07);
    border-radius: 0 0 12px 12px;
    background: #0d0f12;
  }
  body.tsb-page .platform-header.menu-open .platform-status-pill { margin-right: auto; }
  .tsb-profile > .platform-header { min-height: 62px !important; flex-direction: row !important; }
  .platform-menu-backdrop:not([hidden]) {
    position: fixed;
    inset: 62px 0 0;
    z-index: 290;
    min-height: 0;
    display: block;
    width: 100%;
    border: 0;
    border-radius: 0;
    background: rgba(0,0,0,.58);
    backdrop-filter: blur(2px);
    cursor: default;
  }
  /* Only html gets the scroll lock — locking body too breaks position:sticky
     on .platform-header (it stops tracking scroll and jumps off-screen). */
  html.platform-menu-active { overflow: hidden; }
  .tsb-leaderboard .leaderboard-filterbar,
  .tsb-scorecard .steps { top: 62px; }
}

@media (max-width: 390px) {
  .platform-brand { width: 48px; }
  .platform-brand img { width: 100% !important; }
}

/* Profile rank journey --------------------------------------------------- */
.tsb-profile .xp-header {
  gap: clamp(28px, 5vw, 64px);
}

.tsb-profile .xp-score-block {
  display: flex;
  flex: 0 0 auto;
  align-items: baseline;
  gap: 11px;
}

.tsb-profile .tier-ladder {
  width: min(620px, 60vw);
  display: grid;
  grid-template-columns: repeat(6, minmax(56px, 1fr));
  align-items: start;
}

.tsb-profile .tier-step {
  position: relative;
  min-width: 0;
  display: grid;
  justify-items: center;
  align-content: start;
  gap: 2px;
  color: #657080;
  font-size: 10px;
  font-weight: 800;
  line-height: 1.15;
  letter-spacing: .8px;
  text-align: center;
  text-transform: uppercase;
}

.tsb-profile .tier-step::before {
  content: '';
  z-index: 2;
  width: 12px;
  height: 12px;
  margin: 0 auto 7px;
  border: 3px solid #27303d;
  border-radius: 50%;
  background: #11161d;
  box-shadow: 0 0 0 3px #0c1015;
}

.tsb-profile .tier-step:not(:last-child)::after {
  content: '';
  position: absolute;
  z-index: 1;
  top: 5px;
  left: calc(50% + 8px);
  width: calc(100% - 16px);
  height: 2px;
  background: #252d38;
}

.tsb-profile .tier-step.done::before,
.tsb-profile .tier-step.curr::before {
  border-color: #ff4a51;
  background: #d71920;
}

.tsb-profile .tier-step.done:not(:last-child)::after {
  background: linear-gradient(90deg, #d71920, #ff4a51);
}

.tsb-profile .tier-step.curr::before {
  box-shadow: 0 0 0 3px rgba(215,25,32,.18), 0 0 14px rgba(215,25,32,.55);
}

.tsb-profile .tier-step.done { color: #aeb6c1; }
.tsb-profile .tier-step.curr { color: #fff; }
.tsb-profile .tier-icon {
  min-height: 17px;
  color: currentColor;
  font-family: var(--tsb-display);
  font-size: 13px;
  line-height: 1;
  opacity: .42;
}
.tsb-profile .tier-step.done .tier-icon { opacity: .72; }
.tsb-profile .tier-step.curr .tier-icon { color: #ff4a51; opacity: 1; }
.tsb-profile .tier-step-name { white-space: nowrap; }
.tsb-profile .tier-step-min {
  margin-top: 2px;
  color: #4f5967;
  font-size: 8px;
  font-weight: 700;
  letter-spacing: .45px;
  white-space: nowrap;
}
.tsb-profile .tier-step.curr .tier-step-min { color: #9ca5b2; }
.tsb-profile .xp-bar-track { height: 5px; margin: 18px 0 0; }

/* Balanced profile workspace -------------------------------------------- */
.tsb-profile .body-grid {
  grid-template-columns: minmax(0, 1.45fr) minmax(310px, .75fr);
  align-items: start;
  gap: 24px;
}
.tsb-profile .profile-main-column,
.tsb-profile .profile-side-column { min-width: 0; }
/* The panel now leads the main column, and the card inside it (.pd-card) owns
   its own two-column grid. The rules that used to force a grid onto .data-card
   here never applied anyway — .data-card sat inside .sb-section, so it was
   never the direct child this selector required. */
.tsb-profile .profile-personal-panel { margin-bottom: 34px; }
.tsb-profile .profile-main-column > .vinny-card { margin-top: 28px; }
.tsb-profile .redundant-cert-summary { display: none; }

/* Consistent, non-emoji interface markers ------------------------------- */
.tsb-leaderboard .leaderboard-select-icon svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.tsb-leaderboard .search-icon { width: 14px; height: 14px; font-size: 0; }
.tsb-leaderboard .search-icon::before {
  content: '';
  position: absolute;
  width: 8px;
  height: 8px;
  border: 1.5px solid #7f8996;
  border-radius: 50%;
}
.tsb-leaderboard .search-icon::after {
  content: '';
  position: absolute;
  width: 5px;
  height: 1.5px;
  background: #7f8996;
  transform: translate(7px, 8px) rotate(45deg);
  transform-origin: left center;
}
.tsb-leaderboard .section-head-icon { display: none; }
.tsb-leaderboard .pod-medal-emoji {
  width: auto;
  height: auto;
  display: block;
  margin: 0 auto 10px;
  border: 0;
  background: transparent;
  font-family: sans-serif;
  font-size: 23px;
}
.tsb-leaderboard .rank-1 .pod-medal-emoji {
  font-size: 28px;
}
.tsb-profile .rank-medal {
  color: #111;
  font-family: var(--tsb-display);
  font-size: 8px;
  font-weight: 900;
  letter-spacing: -.2px;
}
.tsb-profile .auo-icon { font-family: var(--tsb-display); font-size: 7px; font-weight: 800; letter-spacing: .5px; }
.tsb-profile :where(.course-ico, .badge-emoji, .cert-ico) {
  display: grid;
  place-items: center;
  border: 1px solid rgba(215,25,32,.23);
  border-radius: 7px;
  background: rgba(215,25,32,.08);
  color: #dfe3e8;
  font-family: var(--tsb-display);
  font-size: 7px;
  font-weight: 800;
  letter-spacing: .25px;
}
.tsb-profile .course-ico { width: 30px; height: 30px; }
.tsb-profile .badge-emoji { width: 38px; height: 38px; margin: 0 auto 9px; }
.tsb-profile .cert-ico { width: 28px; height: 24px; margin-bottom: 3px; }
.tsb-profile .drop-icon {
  color: #cbd1d8;
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1px;
}
.tsb-alpha .what-icon,
.tsb-alpha .tier-emoji {
  color: #e04a50;
  font-family: var(--tsb-display);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1px;
}
.tsb-alpha .what-icon {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  margin-bottom: 12px;
  border: 1px solid rgba(215,25,32,.28);
  border-radius: 8px;
  background: rgba(215,25,32,.09);
}
.tsb-alpha .tier-emoji { min-width: 58px; text-align: center; }
.tsb-scorecard .section-code {
  display: inline-grid;
  width: 30px;
  height: 25px;
  place-items: center;
  margin-right: 9px;
  border: 1px solid rgba(215,25,32,.24);
  border-radius: 6px;
  background: rgba(215,25,32,.08);
  color: #ea555b;
  font-family: var(--tsb-display);
  font-size: 7px;
  letter-spacing: .4px;
  vertical-align: middle;
}
.tsb-scorecard .mvp-control-label {
  margin-left: 5px;
  color: var(--tsb-gold);
  font-family: var(--tsb-display);
  font-size: 8px;
  font-weight: 800;
  letter-spacing: .7px;
}

/* Leaderboard role filter ----------------------------------------------- */
.tsb-leaderboard .leaderboard-filterbar {
  position: sticky;
  top: 72px;
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 12px clamp(16px, 4vw, 44px);
  border-bottom: 1px solid var(--tsb-line);
  background: rgba(13, 15, 18, .96);
  box-shadow: 0 10px 28px rgba(0,0,0,.22);
  backdrop-filter: blur(18px);
}
.tsb-leaderboard .leaderboard-filter-copy { display: grid; gap: 1px; }
.tsb-leaderboard .leaderboard-filter-kicker {
  color: #f2f4f7;
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1.4px;
  text-transform: uppercase;
}
/* #697482 sat around 3.2:1 on this bar — under 4.5:1 for text this size. */
.tsb-leaderboard .leaderboard-filter-hint { color: #8b96a4; font-size: 12.5px; font-weight: 600; }
.tsb-leaderboard .leaderboard-select-shell {
  position: relative;
  width: min(330px, 48vw);
  min-height: 44px;
  display: flex;
  align-items: center;
  border: 1px solid rgba(215,25,32,.34);
  border-radius: 10px;
  background: linear-gradient(180deg, rgba(215,25,32,.09), rgba(255,255,255,.025));
  box-shadow: inset 0 1px rgba(255,255,255,.04);
}
.tsb-leaderboard .leaderboard-select-icon {
  position: absolute;
  left: 13px;
  z-index: 1;
  color: #f04449;
  font-size: 13px;
  pointer-events: none;
}
.tsb-leaderboard .leaderboard-filter-trigger {
  width: 100%;
  min-height: 44px;
  display: flex;
  align-items: center;
  padding: 0 42px 0 37px;
  border: 0;
  outline: 0;
  background: transparent;
  color: #fff;
  font-family: var(--tsb-body);
  font-size: 14px;
  font-weight: 700;
  text-align: left;
  cursor: pointer;
}
.tsb-leaderboard .leaderboard-select-arrow {
  position: absolute;
  right: 14px;
  width: 7px;
  height: 7px;
  border-right: 2px solid #8d97a4;
  border-bottom: 2px solid #8d97a4;
  transform: translateY(-2px) rotate(45deg);
  transition: transform var(--tsb-ease);
  pointer-events: none;
}
.tsb-leaderboard .leaderboard-select-shell.open .leaderboard-select-arrow { transform: translateY(2px) rotate(225deg); }
.tsb-leaderboard .leaderboard-select-shell:focus-within {
  border-color: #e62b32;
  box-shadow: 0 0 0 3px rgba(215,25,32,.12);
}
.tsb-leaderboard .leaderboard-filter-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 30;
  width: min(330px, calc(100vw - 24px));
  max-height: min(430px, 58vh);
  overflow-y: auto;
  padding: 7px;
  border: 1px solid rgba(255,255,255,.13);
  border-radius: 12px;
  background: #111418;
  box-shadow: 0 22px 55px rgba(0,0,0,.58), 0 0 0 1px rgba(215,25,32,.08);
  scrollbar-width: thin;
  scrollbar-color: #454d58 transparent;
}
.tsb-leaderboard .leaderboard-filter-menu[hidden] { display: none; }
.tsb-leaderboard .leaderboard-filter-menu button {
  position: relative;
  width: 100%;
  min-height: 42px;
  display: flex;
  align-items: center;
  padding: 0 35px 0 13px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: #aeb6c1;
  font-family: var(--tsb-body);
  font-size: 12px;
  font-weight: 650;
  text-align: left;
  cursor: pointer;
}
.tsb-leaderboard .leaderboard-filter-menu button:hover,
.tsb-leaderboard .leaderboard-filter-menu button:focus-visible {
  outline: 0;
  background: rgba(255,255,255,.055);
  color: #fff;
}
.tsb-leaderboard .leaderboard-filter-menu button[aria-selected="true"] {
  background: rgba(215,25,32,.13);
  color: #fff;
}
.tsb-leaderboard .leaderboard-filter-menu button[aria-selected="true"]::after {
  content: '';
  position: absolute;
  right: 14px;
  width: 7px;
  height: 4px;
  border-left: 2px solid #f05a60;
  border-bottom: 2px solid #f05a60;
  transform: translateY(-1px) rotate(-45deg);
}

@media (max-width: 900px) {
  .tsb-profile .xp-header {
    flex-direction: column;
    align-items: stretch;
    gap: 22px;
  }
  .tsb-profile .xp-score-block { justify-content: flex-start; }
  .tsb-profile .tier-ladder { width: 100%; }
  .tsb-profile .body-grid { grid-template-columns: 1fr; }
  .tsb-leaderboard .leaderboard-filterbar { top: 62px; }
}

@media (max-width: 600px) {
  .tsb-leaderboard .leaderboard-filterbar { backdrop-filter: none; }
  /* Compact dropdown anchored to the trigger, not a full-width/height bottom
     sheet — 12 options at 42px each is ~500px, no need to cover most of the
     screen. Internal scroll handles the overflow past max-height. */
  .tsb-leaderboard .leaderboard-filter-menu {
    position: absolute;
    top: calc(100% + 8px);
    right: 0;
    left: auto;
    bottom: auto;
    width: min(260px, calc(100vw - 24px));
    max-height: min(320px, 50vh);
    border-radius: 12px;
  }
}

@media (max-width: 430px) {
  .tsb-profile .xp-header { gap: 19px; }
  .tsb-profile .tier-ladder { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  .tsb-profile .tier-step { font-size: 8px; letter-spacing: .4px; }
  .tsb-profile .tier-step-min { font-size: 7px; letter-spacing: .15px; }
  .tsb-profile .tier-step::before { margin-bottom: 6px; }
  .tsb-leaderboard .leaderboard-filterbar { gap: 11px; padding: 10px 12px; }
  .tsb-leaderboard .leaderboard-filter-hint { display: none; }
  .tsb-leaderboard .leaderboard-select-shell { width: min(245px, 68vw); }
  .tsb-leaderboard .leaderboard-filter-kicker { font-size: 8px; letter-spacing: .7px; }
}

/* Compact mobile hierarchy for every Training Center section. */
@media (max-width: 680px) {
  .tsb-home .segment { margin-bottom: 25px; }
  .tsb-home .segment .seg-header {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 7px;
    align-items: start;
    margin-bottom: 10px;
    padding: 11px 12px;
    border-left-width: 3px !important;
    border-radius: 10px;
    box-shadow: inset 0 1px rgba(255,255,255,.015);
  }
  .tsb-home .segment .seg-header.basics { border-left-color: var(--tsb-blue) !important; }
  .tsb-home .segment .seg-header.master { border-left-color: var(--tsb-red-bright) !important; }
  .tsb-home .segment .seg-header.genius { border-left-color: var(--tsb-gold) !important; }
  .tsb-home .segment .seg-header > div { min-width: 0; }
  .tsb-home .segment .seg-title,
  .tsb-home .segment .seg-title.basics,
  .tsb-home .segment .seg-title.master,
  .tsb-home .segment .seg-title.genius {
    color: #f4f6f8 !important;
    font-size: 14px !important;
    line-height: 1.35;
    letter-spacing: .55px;
  }
  .tsb-home .segment .seg-sub {
    margin-top: 3px;
    /* Was #9099a5 — a hair under 4.5:1 on this panel. */
    color: #a4adb9;
    font-size: 12px;
    font-weight: 550;
    line-height: 1.5;
    letter-spacing: .1px;
  }
  .tsb-home .segment .seg-badge {
    min-height: 25px;
    display: inline-flex;
    width: max-content;
    max-width: 100%;
    align-items: center;
    padding: 3px 8px;
    font-size: 8px;
    line-height: 1.2;
    letter-spacing: .55px;
  }
}

@media (min-width: 520px) and (max-width: 680px) {
  .tsb-home .segment .seg-header { grid-template-columns: minmax(0, 1fr) auto; gap: 10px; }
  .tsb-home .segment .seg-badge { justify-self: end; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; }
}

/* ══════════════════════════════════════════════════════════════════════════
   APP NAVIGATION  (scripts/tsb-platform.js#buildAppNav)
   Desktop: header hamburger opens a left slide-in sidebar with everything.
   Mobile:  a fixed app-style bottom tab bar; admins get a "More" sheet.
   All scoped to html.tsb-appnav so the static header stays a no-JS fallback.
   ══════════════════════════════════════════════════════════════════════════ */

:root { --tsb-sidebar-w: 252px; }

/* Once JS builds the app nav, retire the old inline nav + its mobile dropdown. */
html.tsb-appnav body.tsb-page .platform-nav { display: none !important; }

/* Desktop: a persistent left rail owns navigation (SaaS-portal style), so the
   old top header and its hamburger are retired and the page is inset to the
   right of the rail. */
@media (min-width: 901px) {
  html.tsb-appnav body.tsb-page .platform-header { display: none !important; }
  html.tsb-appnav .platform-menu-toggle { display: none !important; }
  html.tsb-appnav body.tsb-page { padding-left: var(--tsb-sidebar-w); }
  /* Sub-navs that stuck below the 72px header now stick to the very top.
     The leaderboard filter bar belongs here too — left out originally, it kept
     reserving 72px for a header that no longer renders, leaving a gap above it
     once it stuck while scrolling. */
  html.tsb-appnav .tsb-leaderboard .leaderboard-filterbar,
  html.tsb-appnav .tsb-scorecard .steps { top: 0; }
}

html.tsb-appnav.platform-menu-active { overflow: hidden; }

/* ── Shared backdrop ── */
.tsb-nav-backdrop {
  position: fixed; inset: 0; z-index: 940;
  background: rgba(4, 5, 7, .62);
  backdrop-filter: blur(3px);
  animation: tsbFade var(--tsb-ease);
}
.tsb-nav-backdrop[hidden] { display: none; }
@keyframes tsbFade { from { opacity: 0; } to { opacity: 1; } }

.tsb-nav-group {
  margin: 0 0 8px;
  padding: 0 4px;
  color: var(--tsb-muted);
  font-family: var(--tsb-display);
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 2px;
  text-transform: uppercase;
}
.tsb-nav-group + .tsb-nav-group { margin-top: 20px; }

/* ── Persistent desktop sidebar ── */
.tsb-sidebar {
  position: fixed; top: 0; left: 0; z-index: 950;
  display: flex; flex-direction: column;
  width: var(--tsb-sidebar-w); height: 100dvh;
  padding: 20px 14px calc(16px + env(safe-area-inset-bottom));
  background: linear-gradient(180deg, #101318, var(--tsb-bg) 92%);
  border-right: 1px solid var(--tsb-line-strong);
  overflow-y: auto;
}

/* The logo is white lettering on a dark ground, so it sits directly on the
   dark rail — no plate. */
.tsb-sidebar-head { display: block; margin: 2px 4px 20px; padding: 2px 4px; text-decoration: none; }
.tsb-sidebar-head img { display: block; width: 100%; height: auto; }
.tsb-sidebar-nav { flex: 1 1 auto; }

/* Account block, pinned to the bottom of the rail. */
.tsb-sidebar-foot { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--tsb-line); display: flex; align-items: center; gap: 8px; }
.tsb-acct { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 11px; padding: 8px 10px; border-radius: 11px; text-decoration: none; transition: background var(--tsb-ease); }
.tsb-acct:hover { background: rgba(255,255,255,.05); }
.tsb-acct-av {
  display: grid; place-items: center; width: 36px; height: 36px; flex: 0 0 36px;
  border-radius: 50%; background: rgba(215,25,32,.16); border: 1px solid rgba(215,25,32,.34);
  color: #ff9a9e; font-family: var(--tsb-display); font-size: 12px; font-weight: 800;
}
.tsb-acct-av svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; fill: none; }
.tsb-acct-meta { min-width: 0; display: flex; flex-direction: column; }
.tsb-acct-name { color: #fff; font-family: var(--tsb-display); font-size: 12px; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tsb-acct-role { color: var(--tsb-muted); font-size: 10px; font-weight: 600; letter-spacing: .3px; }
.tsb-acct-logout {
  display: grid; place-items: center; width: 36px; height: 36px; flex: 0 0 36px;
  border: 1px solid var(--tsb-line); border-radius: 10px; background: rgba(255,255,255,.03);
  color: var(--tsb-muted); cursor: pointer; transition: color var(--tsb-ease), border-color var(--tsb-ease);
}
.tsb-acct-logout:hover { color: #ff9a9e; border-color: rgba(215,25,32,.45); }
.tsb-acct-logout svg { width: 18px; height: 18px; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; fill: none; }
.tsb-sidebar-close {
  display: grid; place-items: center; width: 36px; height: 36px;
  border: 1px solid var(--tsb-line); border-radius: 9px;
  background: rgba(255,255,255,.04); color: #cfd4da; cursor: pointer;
}
.tsb-sidebar-close:hover { border-color: rgba(215,25,32,.45); color: #fff; }
.tsb-sidebar-close svg { width: 17px; height: 17px; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.tsb-sidebar-nav { display: flex; flex-direction: column; }

.tsb-side-item {
  position: relative;
  display: flex; align-items: center; gap: 13px;
  min-height: 54px; padding: 0 14px; margin-bottom: 3px;
  border-radius: 11px;
  color: var(--tsb-text-2);
  font-family: var(--tsb-display); font-size: 14px; font-weight: 700; letter-spacing: .3px;
  text-decoration: none;
  transition: background var(--tsb-ease), color var(--tsb-ease);
}
.tsb-side-item svg { width: 26px; height: 26px; flex: 0 0 26px; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; fill: none; }

/* Sidebar accents, matching the home page's quick-launch tiles. Icons only —
   the labels stay white, because a six-item rail in six colours reads as a
   paint chart rather than navigation. Keyed off href like the tile accents so
   reordering the nav cannot shuffle them; anything without an entry here keeps
   the red default, and Home is left red deliberately since it is the active
   item most of the time. */
#tsb-sidebar .tsb-nav-group { font-size: 10.5px; }
#tsb-sidebar .tsb-side-item[href="/leaderboard"] svg { stroke: #ff8a3d; }
#tsb-sidebar .tsb-side-item[href*="tsb-gm-dashboard-preview"] svg { stroke: #63d0ff; }
#tsb-sidebar .tsb-side-item[href="/alpha-grade"] svg { stroke: #c9a4ff; }
#tsb-sidebar .tsb-side-item[href="/attendance"] svg { stroke: #45e6a4; }
#tsb-sidebar .tsb-side-item[href="/profile"] svg { stroke: #7fb2ff; }
#tsb-sidebar .tsb-side-item[href="/sign"] svg { stroke: #ffd166; }
.tsb-side-item:hover { background: rgba(255,255,255,.05); color: #fff; }
.tsb-side-item.is-active { background: rgba(215,25,32,.13); color: #fff; }
.tsb-side-item.is-active::before {
  content: ''; position: absolute; left: -16px; top: 50%; transform: translateY(-50%);
  width: 4px; height: 24px; border-radius: 0 3px 3px 0;
  background: var(--tsb-red-bright); box-shadow: 0 0 14px rgba(240,42,49,.7);
}

/* ── Mobile bottom tab bar ── */
.tsb-tabbar { display: none; }

/* ── Mobile "More" sheet ── */
.tsb-sheet { display: none; }

/* ─────────────────────────────  MOBILE  ───────────────────────────── */
@media (max-width: 900px) {
  /* Hamburger is not the mobile key — the bottom bar is. */
  html.tsb-appnav .platform-menu-toggle { display: none !important; }
  /* Desktop-only surfaces off on phones. */
  html.tsb-appnav .tsb-sidebar { display: none; }
  html.tsb-appnav .platform-profile-action { display: none !important; }

  /* The logo band goes too. This is meant to live on a home screen, and a
     native app doesn't spend ~110px of an 812px screen restating its own name
     — the icon on the home screen already did that. Brand lives in the icon
     and the page's own heading; navigation lives in the bottom bar. Still
     scoped to html.tsb-appnav, so a no-JS visitor keeps the static header. */
  html.tsb-appnav body.tsb-page .platform-header { display: none !important; }

  /* And the page hero with it. The tab bar already says "Leaderboard" or
     "Alpha Grade", so a full-viewport restatement of the page name is pure
     repetition — it pushed the podium, and the whole score breakdown, below
     the fold on a phone. Content leads instead. Desktop has no tab bar, so it
     keeps the hero. */
  html.tsb-appnav .tsb-leaderboard .hero,
  html.tsb-appnav .tsb-alpha .hero { display: none; }

  /* These sticky bars were offset by 62px to sit below the old top header.
     With the header hidden that offset is a dead black band at the top of the
     page, and it pushed the bar down over the "Top performers" heading, which
     then showed through it. The desktop rules already zero this; mobile has
     the same need now. */
  html.tsb-appnav .tsb-leaderboard .leaderboard-filterbar,
  html.tsb-appnav .tsb-scorecard .steps { top: 0; }

  /* Clear of the fixed bottom bar, and of the phone's status bar now that no
     header sits above the content.

     Installed on iOS the status-bar style is `black`, so the web view already
     starts below the clock and battery and the inset reports 0 — but that only
     holds while the style stays `black`; switching it to `black-translucent`
     would drop the content straight under the clock. Reading the inset means
     that change can never silently collide. The max() floor keeps a little
     breathing room in a plain browser tab, where the inset genuinely is 0 and
     the first heading would otherwise sit flush against the top edge. */
  html.tsb-appnav body.tsb-page {
    padding-top: max(env(safe-area-inset-top), 8px);
    padding-bottom: calc(66px + env(safe-area-inset-bottom));
  }

  html.tsb-appnav .tsb-tabbar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 930;
    display: flex; align-items: stretch;
    height: calc(64px + env(safe-area-inset-bottom));
    padding-bottom: env(safe-area-inset-bottom);
    background: rgba(11, 13, 16, .96);
    border-top: 1px solid var(--tsb-line-strong);
    box-shadow: 0 -14px 34px rgba(0,0,0,.44);
    backdrop-filter: blur(18px);
  }
  .tsb-tab {
    position: relative;
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
    padding: 8px 2px 6px;
    border: 0; background: transparent; cursor: pointer;
    color: var(--tsb-muted);
    font-family: var(--tsb-display); font-size: 8.5px; font-weight: 700; letter-spacing: .4px;
    text-decoration: none;
  }
  .tsb-tab svg { width: 23px; height: 23px; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; fill: none; transition: transform var(--tsb-ease); }
  .tsb-tab span { line-height: 1; white-space: nowrap; }
  .tsb-tab.is-active { color: #fff; }
  .tsb-tab.is-active svg { color: var(--tsb-red-bright); transform: translateY(-1px); }
  .tsb-tab.is-active::before {
    content: ''; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
    width: 30px; height: 3px; border-radius: 0 0 3px 3px;
    background: var(--tsb-red-bright); box-shadow: 0 3px 12px rgba(240,42,49,.65);
  }

  html.tsb-appnav .tsb-sheet {
    position: fixed; inset: 0; z-index: 945; display: block;
    visibility: hidden;
  }
  html.tsb-appnav .tsb-sheet.is-open { visibility: visible; }
  .tsb-sheet-panel {
    position: absolute; left: 0; right: 0; bottom: 0;
    /* Capped and scrollable: with the account row added, an admin's sheet is
       tall enough to run off a small screen. Scrolling here is also what the
       swipe-to-dismiss handler checks before it starts a drag. */
    max-height: 86vh; overflow-y: auto; overscroll-behavior: contain;
    padding: 10px 18px calc(22px + env(safe-area-inset-bottom));
    background: linear-gradient(180deg, #14171c, #0d0f12);
    border-top: 1px solid var(--tsb-line-strong);
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -24px 60px rgba(0,0,0,.6);
    transform: translateY(102%);
    transition: transform 260ms cubic-bezier(.22,.7,.2,1);
  }
  .tsb-sheet.is-open .tsb-sheet-panel { transform: translateY(0); }
  .tsb-sheet-grab { display: block; width: 40px; height: 4px; margin: 2px auto 16px; border-radius: 2px; background: rgba(255,255,255,.2); }
  .tsb-sheet-item {
    display: flex; align-items: center; gap: 13px;
    min-height: 52px; padding: 0 14px; margin-bottom: 4px;
    border: 1px solid var(--tsb-line); border-radius: 12px;
    background: rgba(255,255,255,.025);
    color: var(--tsb-text-2);
    font-family: var(--tsb-display); font-size: 12px; font-weight: 700;
    text-decoration: none;
  }
  .tsb-sheet-item svg { width: 21px; height: 21px; flex: 0 0 21px; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; fill: none; }
  .tsb-sheet-item.is-active { border-color: rgba(215,25,32,.4); background: rgba(215,25,32,.12); color: #fff; }
}

@media (prefers-reduced-motion: reduce) {
  .tsb-sidebar, .tsb-sheet-panel { transition: none; }
  .tsb-nav-backdrop { animation: none; }
}

/* ══════════════════════════════════════════════════════════════════════════
   SHARED COMPONENTS
   ══════════════════════════════════════════════════════════════════════════
   Every page in this portal has been rebuilding buttons, cards, headings and
   badges in its own <style> block — roughly 3,900 lines of page-local CSS
   against 2,400 shared, which is why no two screens agree on what a secondary
   button looks like. This section is the shared answer. Page work should reach
   for these first and only write page-local CSS for something genuinely unique
   to that screen.

   NAMING — extends the tsb-* convention already used above; no new scheme.
     .tsb-thing              the component
     .tsb-thing-part         a part of it            (.tsb-card-title)
     .tsb-thing.variant      a bare modifier class   (.tsb-btn.primary)
     .tsb-thing.is-state     a runtime state         (.is-open, .is-active)
   Bare modifiers and .is-* states match .portal-btn.primary and
   .tsb-side-item.is-active, which is what the existing JS already writes.

   COLOUR CONTRACT
     red   (--tsb-red / --tsb-red-bright) — brand, and the one primary action on
           a screen. Not a decorative border, not every card edge.
     gold  (--tsb-gold) — earned achievement and things awaiting the user. Never
           a generic highlight.
     grey  everything structural. Most surfaces have no accent at all.
     --tsb-danger is destructive only, and is deliberately styled as a tinted
     outline rather than a solid fill so it never reads as the brand red.

   SPECIFICITY — these are single-class selectors so a page can override them
   with two. The exception is the form controls, which have to out-specify the
   `body.tsb-page :where(input, select, textarea, .csv-area)` reset near the top
   of this file and so carry a `body.tsb-page` scope.

   MOBILE — sized for 320px up. Anything that can outgrow the viewport (tables,
   button rows, modals) scrolls or wraps inside its own box; nothing here can
   make the page scroll sideways. */

/* ── Buttons ───────────────────────────────────────────────────────────────
   <button class="tsb-btn primary">Save</button>
   <a class="tsb-btn secondary sm" href="…">Cancel</a>
   Variants: primary | secondary | ghost | danger.  Sizes: sm | (default) | lg.
   Extras: .block (full width), .is-busy, :disabled / .is-disabled.
   The default 44px height is the touch target; .sm opts down to 38px and is
   for dense admin toolbars, not for anything a phone user taps often. */
.tsb-btn {
  position: relative;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  max-width: 100%;
  padding: 0 18px;
  border: 1px solid transparent;
  border-radius: var(--tsb-radius-sm);
  background: none;
  color: var(--tsb-text);
  font-family: var(--tsb-display);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: .4px;
  line-height: 1;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: transform var(--tsb-ease), filter var(--tsb-ease),
              background var(--tsb-ease), border-color var(--tsb-ease),
              color var(--tsb-ease), box-shadow var(--tsb-ease);
}
.tsb-btn svg { width: 17px; height: 17px; flex: 0 0 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* A label long enough to need it truncates rather than widening past its row. */
.tsb-btn > span { overflow: hidden; text-overflow: ellipsis; }

.tsb-btn.primary {
  border-color: rgba(255,255,255,.12);
  background: linear-gradient(135deg, var(--tsb-red-bright), #b4080f);
  color: #fff;
  box-shadow: 0 9px 22px rgba(190, 6, 15, .22);
}
.tsb-btn.primary:hover { transform: translateY(-1px); filter: brightness(1.1); box-shadow: 0 12px 28px rgba(190, 6, 15, .3); }

.tsb-btn.secondary {
  border-color: var(--tsb-line-strong);
  background: rgba(255,255,255,.045);
  color: var(--tsb-text);
}
.tsb-btn.secondary:hover { border-color: rgba(240,42,49,.5); background: rgba(240,42,49,.09); color: #fff; }

/* No border at rest — for the third-priority action, where an outline would
   just add another box to a row that already has two. */
.tsb-btn.ghost { background: transparent; color: var(--tsb-text-2); }
.tsb-btn.ghost:hover { background: rgba(255,255,255,.06); color: #fff; }

/* Destructive. Tinted outline, never a solid red fill: a solid red button is
   the primary action on this site, and "Delete" must not look like "Save". */
.tsb-btn.danger {
  border-color: rgba(242,90,96,.42);
  background: rgba(242,90,96,.1);
  color: var(--tsb-danger);
}
.tsb-btn.danger:hover { border-color: var(--tsb-danger); background: rgba(242,90,96,.18); color: #fff; }

.tsb-btn.sm { min-height: 38px; padding: 0 14px; font-size: 11px; }
.tsb-btn.lg { min-height: 52px; padding: 0 26px; font-size: 13px; }
.tsb-btn.block { width: 100%; }

.tsb-btn:disabled,
.tsb-btn.is-disabled,
.tsb-btn[aria-disabled="true"] {
  transform: none;
  filter: none;
  opacity: .45;
  box-shadow: none;
  cursor: not-allowed;
  pointer-events: none;
}
/* While a request is in flight: still visible, no longer clickable, and the
   label stays put so the row does not reflow under the finger. */
.tsb-btn.is-busy { opacity: .6; cursor: progress; pointer-events: none; }

/* A row of buttons. Wraps rather than overflowing, and on a phone the actions
   stack full-width — two 44px targets side by side at 320px are too narrow. */
.tsb-btn-row { display: flex; flex-wrap: wrap; gap: 10px; }
.tsb-btn-row.end { justify-content: flex-end; }
@media (max-width: 480px) {
  .tsb-btn-row { flex-direction: column; align-items: stretch; }
  .tsb-btn-row > .tsb-btn { width: 100%; }
}

/* ── Surfaces ──────────────────────────────────────────────────────────────
   .tsb-surface  a plain raised block — the default container.
   .tsb-card     the same with the portal's gradient and shadow; add
                 .is-interactive when the whole card is a link or button.
   Neither has a coloured edge by default. Accent one with .accent-gold only
   when the card represents something earned or something owed. */
.tsb-surface {
  padding: 16px;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.022);
}
.tsb-card {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: linear-gradient(145deg, rgba(29, 33, 39, .96), rgba(17, 19, 23, .98));
  box-shadow: 0 10px 30px rgba(0, 0, 0, .18);
  color: inherit;
  text-decoration: none;
}
.tsb-card.is-interactive {
  display: block;
  transition: transform var(--tsb-ease), border-color var(--tsb-ease), box-shadow var(--tsb-ease);
}
.tsb-card.is-interactive:hover {
  transform: translateY(-3px);
  border-color: rgba(215, 25, 32, .32);
  box-shadow: var(--tsb-shadow-red);
}
.tsb-card.accent-gold { border-left: 3px solid var(--tsb-gold); }
.tsb-card.accent-brand { border-left: 3px solid var(--tsb-red); }

.tsb-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 16px 18px 0; }
.tsb-card-body { padding: 16px 18px; }
.tsb-card-head + .tsb-card-body { padding-top: 10px; }
.tsb-card-foot {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px 18px;
  border-top: 1px solid var(--tsb-line);
}
.tsb-card-title { color: #fff; font-family: var(--tsb-display); font-size: 14px; font-weight: 750; letter-spacing: -.01em; line-height: 1.3; }
.tsb-card-sub { margin-top: 5px; color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: 12.5px; line-height: 1.6; }

/* ── Page and section headings ─────────────────────────────────────────────
   <div class="tsb-page-head">
     <p class="tsb-eyebrow">Training</p>
     <h1 class="tsb-page-title">Phone Pop 5</h1>
     <p class="tsb-page-sub">…</p>
   </div>
   The eyebrow is red by default because it is usually naming the product area.
   Use .gold only when the eyebrow itself is the achievement. */
.tsb-page-head { margin-bottom: 20px; }
.tsb-eyebrow {
  margin-bottom: 9px;
  color: var(--tsb-red-bright);
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 2.2px;
  text-transform: uppercase;
}
.tsb-eyebrow.gold { color: var(--tsb-gold); }
.tsb-eyebrow.muted { color: var(--tsb-steel); }
.tsb-page-title {
  margin: 0;
  color: #fff;
  font-family: var(--tsb-display);
  font-size: clamp(1.45rem, 4vw, 2.2rem);
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.12;
}
.tsb-page-sub {
  max-width: 68ch;
  margin: 10px 0 0;
  color: var(--tsb-text-2);
  font-family: var(--tsb-body);
  font-size: 14px;
  line-height: 1.7;
}

/* A run-in section heading: title, rule, optional count. Mirrors the pattern
   the leaderboard already uses, so it reads as the same portal. */
.tsb-section-head {
  display: flex; align-items: center; gap: 12px;
  margin: 30px 0 14px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--tsb-line);
}
.tsb-section-title {
  color: #fff;
  font-family: var(--tsb-display);
  font-size: 12px;
  font-weight: 800;
  letter-spacing: 1.4px;
  text-transform: uppercase;
}
.tsb-section-line { flex: 1 1 auto; height: 1px; background: linear-gradient(90deg, var(--tsb-line), transparent); }
.tsb-section-count { flex: 0 0 auto; color: var(--tsb-muted); font-family: var(--tsb-display); font-size: 10px; font-weight: 700; letter-spacing: .6px; white-space: nowrap; }

/* ── Badges and pills ──────────────────────────────────────────────────────
   <span class="tsb-badge gold">MVP</span>
   Neutral by default. brand | gold | good | info | warn | danger set the tone;
   add .solid for the rare case that must read at a glance across a room. */
.tsb-badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  padding: 4px 10px;
  border: 1px solid var(--tsb-line);
  border-radius: 99px;
  background: rgba(255,255,255,.04);
  color: var(--tsb-text-2);
  font-family: var(--tsb-display);
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 1px;
  line-height: 1.3;
  text-transform: uppercase;
  white-space: nowrap;
  vertical-align: middle;
}
.tsb-badge.brand  { border-color: rgba(215,25,32,.34); background: rgba(215,25,32,.12); color: #ff858a; }
.tsb-badge.gold   { border-color: rgba(212,174,85,.36); background: rgba(212,174,85,.12); color: var(--tsb-gold); }
.tsb-badge.good   { border-color: rgba(53,197,121,.32); background: rgba(53,197,121,.1);  color: var(--tsb-green); }
.tsb-badge.info   { border-color: rgba(88,183,232,.32); background: rgba(88,183,232,.1);  color: var(--tsb-blue); }
.tsb-badge.warn   { border-color: rgba(212,174,85,.3);  background: rgba(212,174,85,.08); color: #e0be6b; }
.tsb-badge.danger { border-color: rgba(242,90,96,.36);  background: rgba(242,90,96,.12);  color: var(--tsb-danger); }
.tsb-badge.solid.brand  { border-color: transparent; background: var(--tsb-red); color: #fff; }
.tsb-badge.solid.gold   { border-color: transparent; background: var(--tsb-gold); color: #1a1206; }
.tsb-badge.solid.good   { border-color: transparent; background: var(--tsb-green); color: #05210f; }
/* A badge with a leading status dot. */
.tsb-badge-dot { width: 6px; height: 6px; flex: 0 0 6px; border-radius: 50%; background: currentColor; }

/* ── Progress ──────────────────────────────────────────────────────────────
   <div class="tsb-progress"><span class="tsb-progress-fill" style="width:34%"></span></div>
   Width is the only thing a script needs to set. Brand red by default; .gold
   for a track that measures something earned. */
.tsb-progress {
  height: 6px;
  overflow: hidden;
  border-radius: 99px;
  background: rgba(255,255,255,.08);
}
.tsb-progress.sm { height: 4px; }
.tsb-progress.lg { height: 9px; }
.tsb-progress-fill {
  display: block;
  width: 0;
  height: 100%;
  border-radius: inherit;
  background: linear-gradient(90deg, var(--tsb-red), var(--tsb-red-bright));
  transition: width 420ms cubic-bezier(.2,.7,.2,1);
}
.tsb-progress.gold .tsb-progress-fill { background: linear-gradient(90deg, #b8933f, var(--tsb-gold)); }
.tsb-progress.good .tsb-progress-fill { background: linear-gradient(90deg, #1e9d5c, var(--tsb-green)); }
/* The number beside a track. Tabular so it does not jitter as it counts up. */
.tsb-progress-meta {
  display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
  margin-top: 7px;
  color: var(--tsb-muted);
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── Forms ─────────────────────────────────────────────────────────────────
   <label class="tsb-field">
     <span class="tsb-label">Email</span>
     <input class="tsb-input" type="email">
     <span class="tsb-hint">Work address.</span>
     <span class="tsb-error">Not a valid address.</span>
   </label>
   .tsb-error is hidden until the field carries .is-invalid, so the markup can
   ship the message and the script only toggles one class.

   Scoped to body.tsb-page because the input reset near the top of this file is
   `body.tsb-page :where(input, select, textarea, .csv-area)` and would
   otherwise win on specificity. 16px on the control is deliberate: iOS Safari
   zooms the viewport on focus for anything smaller, which on this portal reads
   as the page lurching sideways. */
.tsb-field { display: block; margin-bottom: 16px; }
.tsb-field-row { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr)); }
.tsb-label {
  display: block;
  margin-bottom: 7px;
  color: var(--tsb-text-2);
  font-family: var(--tsb-display);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 1.2px;
  text-transform: uppercase;
}
.tsb-label .tsb-req { margin-left: 3px; color: var(--tsb-red-bright); }

body.tsb-page :where(.tsb-input, .tsb-select, .tsb-textarea) {
  width: 100%;
  min-height: 46px;
  display: block;
  padding: 11px 14px;
  border: 1px solid var(--tsb-line-strong);
  border-radius: var(--tsb-radius-sm);
  background: var(--tsb-bg-raised);
  color: var(--tsb-text);
  font-family: var(--tsb-body);
  font-size: 16px;
  font-weight: 600;
  line-height: 1.4;
}
body.tsb-page .tsb-textarea { min-height: 108px; resize: vertical; line-height: 1.6; }
body.tsb-page .tsb-select {
  /* Room for the chevron drawn below; the arrow is on the wrapper, not here,
     so a native select still shows its own control on platforms that force it. */
  padding-right: 38px;
  cursor: pointer;
}
body.tsb-page :where(.tsb-input, .tsb-select, .tsb-textarea)::placeholder { color: var(--tsb-muted); opacity: 1; }
body.tsb-page :where(.tsb-input, .tsb-select, .tsb-textarea):focus {
  outline: 0;
  border-color: rgba(215,25,32,.65);
  box-shadow: 0 0 0 3px rgba(215,25,32,.12);
}
body.tsb-page :where(.tsb-input, .tsb-select, .tsb-textarea):disabled {
  opacity: .5;
  cursor: not-allowed;
}
body.tsb-page :where(.tsb-input, .tsb-select, .tsb-textarea).is-invalid {
  border-color: rgba(242,90,96,.7);
  box-shadow: 0 0 0 3px rgba(242,90,96,.12);
}
.tsb-select-wrap { position: relative; }
.tsb-select-wrap::after {
  content: '';
  position: absolute; right: 16px; top: 50%;
  width: 7px; height: 7px;
  border-right: 2px solid var(--tsb-muted);
  border-bottom: 2px solid var(--tsb-muted);
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none;
}
.tsb-hint { display: block; margin-top: 6px; color: var(--tsb-muted); font-family: var(--tsb-body); font-size: 12px; line-height: 1.5; }
/* Errors are announced, not colour-coded alone: keep the leading "!" glyph or
   an aria-describedby in the markup. */
.tsb-error { display: none; margin-top: 6px; color: var(--tsb-danger); font-family: var(--tsb-body); font-size: 12px; font-weight: 600; line-height: 1.5; }
.tsb-field:has(.is-invalid) .tsb-error,
.tsb-error.is-shown { display: block; }

/* ── Tables ────────────────────────────────────────────────────────────────
   ALWAYS wrap: <div class="tsb-table-wrap"><table class="tsb-table">…
   The wrapper is what scrolls, so a wide report can never push the page
   sideways on a phone. Give each <td> a data-label and the stacked phone
   layout below labels itself. */
.tsb-table-wrap {
  width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.015);
}
.tsb-table {
  width: 100%;
  min-width: 560px;
  border-collapse: collapse;
  font-family: var(--tsb-body);
  font-size: 13px;
}
.tsb-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  padding: 11px 14px;
  border-bottom: 1px solid var(--tsb-line-strong);
  background: var(--tsb-surface-2);
  color: var(--tsb-steel);
  font-family: var(--tsb-display);
  font-size: 9.5px;
  font-weight: 800;
  letter-spacing: 1.1px;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}
.tsb-table td {
  padding: 12px 14px;
  border-bottom: 1px solid var(--tsb-line);
  color: var(--tsb-text-2);
  line-height: 1.5;
  vertical-align: middle;
}
.tsb-table tbody tr:last-child td { border-bottom: 0; }
.tsb-table tbody tr:hover td { background: rgba(255,255,255,.025); color: var(--tsb-text); }
/* Any column of figures. Right-aligned and tabular so the digits line up. */
.tsb-table .num { text-align: right; font-family: var(--tsb-display); font-variant-numeric: tabular-nums; }
.tsb-table-empty { padding: 26px 14px; color: var(--tsb-muted); font-family: var(--tsb-body); font-size: 13px; text-align: center; }

/* Phones: a five-column report is unreadable at 375px however it scrolls, so a
   table marked .stack-mobile becomes one card per row with the header text
   carried in on each cell. Opt in — a two-column table is fine as a table. */
@media (max-width: 600px) {
  .tsb-table.stack-mobile { min-width: 0; font-size: 13px; }
  .tsb-table.stack-mobile thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
  .tsb-table.stack-mobile tr {
    display: block;
    margin: 10px;
    border: 1px solid var(--tsb-line);
    border-radius: var(--tsb-radius-sm);
    background: rgba(255,255,255,.02);
  }
  .tsb-table.stack-mobile td {
    display: flex; align-items: baseline; justify-content: space-between; gap: 14px;
    padding: 9px 13px;
    text-align: right;
  }
  .tsb-table.stack-mobile td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    color: var(--tsb-muted);
    font-family: var(--tsb-display);
    font-size: 9px;
    font-weight: 800;
    letter-spacing: .9px;
    text-align: left;
    text-transform: uppercase;
  }
  .tsb-table.stack-mobile td:empty { display: none; }
}

/* ── Alerts ────────────────────────────────────────────────────────────────
   <div class="tsb-alert warn" role="status">
     <p class="tsb-alert-title">Heads up</p>
     <p class="tsb-alert-body">…</p>
   </div>
   Neutral by default. The left rule carries the tone so the fill can stay
   quiet — a full-bleed coloured panel for every notice is how a page ends up
   shouting at someone who has read the message four times. */
.tsb-alert {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin: 0 0 16px;
  padding: 13px 16px;
  border: 1px solid var(--tsb-line);
  border-left: 3px solid var(--tsb-steel);
  border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.03);
}
.tsb-alert.info   { border-color: rgba(88,183,232,.24);  border-left-color: var(--tsb-blue);   background: rgba(88,183,232,.07); }
.tsb-alert.good   { border-color: rgba(53,197,121,.24);  border-left-color: var(--tsb-green);  background: rgba(53,197,121,.07); }
.tsb-alert.warn   { border-color: rgba(212,174,85,.26);  border-left-color: var(--tsb-gold);   background: rgba(212,174,85,.07); }
.tsb-alert.danger { border-color: rgba(242,90,96,.28);   border-left-color: var(--tsb-danger); background: rgba(242,90,96,.08); }
.tsb-alert.brand  { border-color: rgba(215,25,32,.26);   border-left-color: var(--tsb-red);    background: rgba(215,25,32,.08); }
.tsb-alert-icon { flex: 0 0 20px; width: 20px; height: 20px; margin-top: 1px; }
.tsb-alert-icon svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
.tsb-alert-copy { min-width: 0; flex: 1 1 auto; }
.tsb-alert-title {
  margin: 0;
  color: #fff;
  font-family: var(--tsb-display);
  font-size: 10.5px;
  font-weight: 800;
  letter-spacing: 1.3px;
  text-transform: uppercase;
}
.tsb-alert-body { margin: 5px 0 0; color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: 13px; line-height: 1.6; }
.tsb-alert.info .tsb-alert-icon   { color: var(--tsb-blue); }
.tsb-alert.good .tsb-alert-icon   { color: var(--tsb-green); }
.tsb-alert.warn .tsb-alert-icon   { color: var(--tsb-gold); }
.tsb-alert.danger .tsb-alert-icon { color: var(--tsb-danger); }
.tsb-alert.brand .tsb-alert-icon  { color: var(--tsb-red-bright); }

/* ── Empty states ──────────────────────────────────────────────────────────
   What a list shows when there is genuinely nothing in it — which on this
   portal usually means "you have not done this yet", so it gets an action
   rather than an apology. Distinct from the skeletons below: skeletons mean
   "loading", this means "loaded, and empty". */
.tsb-empty {
  display: grid;
  justify-items: center;
  gap: 6px;
  padding: 38px 22px;
  border: 1px dashed var(--tsb-line-strong);
  border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.015);
  text-align: center;
}
.tsb-empty-icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  margin-bottom: 6px;
  border: 1px solid var(--tsb-line-strong);
  border-radius: 12px;
  background: rgba(255,255,255,.03);
  color: var(--tsb-steel);
}
.tsb-empty-icon svg { width: 21px; height: 21px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; }
.tsb-empty-title { color: var(--tsb-text); font-family: var(--tsb-display); font-size: 13px; font-weight: 750; letter-spacing: .2px; }
.tsb-empty-text { max-width: 42ch; color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: 13px; line-height: 1.6; }
.tsb-empty .tsb-btn { margin-top: 12px; }

/* ── Skeletons ─────────────────────────────────────────────────────────────
   The .sk family is defined near the top of this file and is unchanged:
     .sk         the shimmering block (put it on any sized element)
     .sk-line    a line of text        — set a width to vary the ragged edge
     .sk-circle  an avatar
     .sk-stack   a gap-12 column of them
     .sk-row     a bordered row: .sk-circle + .sk-grow holding two .sk-line
   Added here: a generic rectangle and a card-shaped placeholder, so a grid of
   tiles can be stood in for without inventing per-page shapes. Everything
   lives inside the page's existing loading container, which is removed the
   moment data arrives — never toggled back on, so there is no flash. */
.sk-block { height: 100%; min-height: 64px; border-radius: var(--tsb-radius-sm); }
.sk-card {
  display: grid;
  gap: 10px;
  padding: 16px;
  border: 1px solid var(--tsb-line);
  border-radius: var(--tsb-radius);
  background: rgba(255,255,255,.015);
}
.sk-card .sk-line:first-child { width: 62%; height: 14px; }
.sk-card .sk-line:last-child { width: 88%; }
.sk-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(min(100%, 230px), 1fr)); }

/* ── Modals ────────────────────────────────────────────────────────────────
   <div class="tsb-modal" hidden>
     <div class="tsb-modal-panel" role="dialog" aria-modal="true">…</div>
   </div>
   Toggle the `hidden` attribute, then add .is-open on the next frame to play
   the entrance. Sits above the sidebar (950) and the bottom tab bar (930).
   On a phone the panel is a bottom sheet — the same shape as the existing
   "More" sheet, so there is one modal gesture on this portal, not two. */
.tsb-modal {
  position: fixed;
  inset: 0;
  z-index: 960;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(4, 5, 7, .7);
  backdrop-filter: blur(3px);
}
/* Author-origin, so it beats the .tsb-modal display:flex above the way
   .lb-more[hidden] and .leaderboard-filter-menu[hidden] already do. */
.tsb-modal[hidden] { display: none; }
.tsb-modal-panel {
  width: min(460px, 100%);
  max-height: min(86vh, 720px);
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 22px;
  border: 1px solid var(--tsb-line-strong);
  border-radius: var(--tsb-radius-lg);
  background: linear-gradient(180deg, #14171c, #0d0f12);
  box-shadow: 0 30px 70px rgba(0,0,0,.62);
  transform: translateY(10px);
  opacity: 0;
  transition: transform 220ms cubic-bezier(.22,.7,.2,1), opacity 220ms ease;
}
.tsb-modal.is-open .tsb-modal-panel { transform: none; opacity: 1; }
.tsb-modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; margin-bottom: 16px; }
.tsb-modal-title { margin: 0; color: #fff; font-family: var(--tsb-display); font-size: 15px; font-weight: 800; letter-spacing: -.01em; line-height: 1.25; }
.tsb-modal-sub { margin: 6px 0 0; color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: 13px; line-height: 1.6; }
.tsb-modal-body { color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: 13.5px; line-height: 1.65; }
.tsb-modal-close {
  display: grid; place-items: center;
  width: 36px; height: 36px; min-height: 36px; flex: 0 0 36px;
  padding: 0;
  border: 1px solid var(--tsb-line); border-radius: 9px;
  background: rgba(255,255,255,.04);
  color: var(--tsb-text-2);
  cursor: pointer;
}
.tsb-modal-close:hover { border-color: rgba(215,25,32,.45); color: #fff; }
.tsb-modal-close svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 2.1; stroke-linecap: round; }
.tsb-modal-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; margin-top: 20px; }

@media (max-width: 600px) {
  .tsb-modal { align-items: flex-end; padding: 0; }
  .tsb-modal-panel {
    width: 100%;
    max-height: 88vh;
    padding: 18px 18px calc(20px + env(safe-area-inset-bottom));
    border-radius: 20px 20px 0 0;
    border-width: 1px 0 0;
    transform: translateY(100%);
  }
  /* Full-width stacked actions: the confirm and the cancel are both 44px
     targets and will not sit side by side at 320px without shrinking. */
  .tsb-modal-actions { flex-direction: column-reverse; }
  .tsb-modal-actions > .tsb-btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .tsb-modal-panel { transition: none; opacity: 1; transform: none; }
  .tsb-progress-fill { transition: none; }
}


/* The composer inherits the readable stack the admin pages use: Orbitron is
   kept for the dialog heading, but chips, hints and the person list are content
   and read badly in a wide geometric display face at these sizes. */
.ppl-dialog { --au-read: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
.ppl-dialog .pd-s, .ppl-dialog .pd-hint, .ppl-dialog .pd-l,
.ppl-dialog .bc-person, .ppl-dialog .bc-role, .ppl-dialog .ppl-btn,
.ppl-dialog .pd-i, .ppl-dialog .bc-say { font-family: var(--au-read); }

/* ── The broadcast composer ──────────────────────────────────────────────────
   Lives here rather than inside one page's <style> because the composer itself
   is now shared: scripts/tsb-broadcast.js builds the dialog and both the People
   and Messages pages open it. Kept together with .ppl-btn, which the dialog's
   own buttons use. */
  .ppl-btn { min-height: 36px; display: inline-flex; align-items: center; gap: 7px; padding: 0 14px; border: 1px solid var(--tsb-line-strong); border-radius: 8px; background: rgba(255,255,255,.04); color: var(--tsb-text-2); font-family: var(--tsb-body); font-size: .72rem; font-weight: 600; cursor: pointer; }
  .ppl-btn:hover:not([disabled]) { border-color: rgba(215,25,32,.4); color: #fff; }
  .ppl-btn[disabled] { opacity: .5; cursor: default; }
  .ppl-btn.primary { border-color: transparent; background: linear-gradient(135deg, var(--tsb-red-bright), #b50810); color: #fff; }
  .ppl-btn.primary:hover:not([disabled]) { filter: brightness(1.08); }
  .ppl-scrim { position: fixed; inset: 0; z-index: 1000; display: grid; place-items: center; padding: 18px; background: rgba(4,5,7,.72); backdrop-filter: blur(5px); }
  .ppl-scrim[hidden] { display: none; }
  .ppl-dialog { width: min(560px, 100%); max-height: 88vh; overflow-y: auto; padding: 24px; border: 1px solid var(--tsb-line-strong); border-radius: var(--tsb-radius-lg); background: var(--tsb-bg-raised); box-shadow: var(--tsb-shadow); }
  .pd-h { margin: 0 0 4px; color: #fff; font-family: var(--tsb-display); font-size: 1.05rem; font-weight: 800; }
  .pd-s { margin: 0 0 20px; color: var(--tsb-muted); font-size: .7rem; line-height: 1.55; }
  .pd-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
  .pd-f { display: flex; flex-direction: column; gap: 6px; }
  .pd-f.wide { grid-column: 1 / -1; }
  .pd-l { color: var(--tsb-text-2); font-size: .64rem; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; }
  .pd-l .req { color: var(--tsb-red-bright); }
  .bc-picks { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
  /* Group chips are SHORTCUTS, not state.
     They used to carry a lit red "on" style, set whenever every member of the
     group happened to be ticked — and because the composer opens with the whole
     floor selected, nine of twelve were lit at rest. "On" meant nothing and the
     control was a wall of red. Nothing here is lit now; who is selected is
     answered by the summary line and the ticked list, which are the real state. */
  .bc-groups { margin-bottom: 12px; }
  .bc-row { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: start; gap: 10px; }
  .bc-chiprow { display: flex; flex-wrap: wrap; gap: 6px; }
  .bc-group {
    display: inline-flex; align-items: center; gap: 7px; min-height: 32px; padding: 0 13px;
    border: 1px solid var(--tsb-line-strong); border-radius: 999px;
    background: rgba(255,255,255,.03); color: var(--tsb-text);
    font-family: var(--au-read, inherit); font-size: .8rem; font-weight: 600; cursor: pointer;
    transition: transform .12s cubic-bezier(.16,1,.3,1), border-color .15s ease, background .15s ease;
  }
  .bc-group:hover { border-color: rgba(215,25,32,.45); background: rgba(255,255,255,.06); }
  .bc-group:active { transform: translateY(1px); }
  .bc-group:focus-visible { outline: 2px solid var(--tsb-red-bright); outline-offset: 2px; }
  .bc-group .c { color: var(--tsb-muted); font-variant-numeric: tabular-nums; font-size: .74rem; }

  /* The one group that was clicked. At most one chip can carry this, so the red
     reads as "this is your audience" rather than as decoration. */
  .bc-group.on { border-color: transparent; background: var(--tsb-red-bright); color: #fff; font-weight: 700; }
  .bc-group.on .c { color: rgba(255,255,255,.8); }
  .bc-group.on:hover { background: var(--tsb-red-bright); filter: brightness(1.06); }

  /* Its own line, so a wrapping chip row never orphans the label beside it. */
  .bc-chip-label {
    padding-top: 8px; color: var(--tsb-muted); font-size: .66rem; font-weight: 700;
    letter-spacing: .8px; text-transform: uppercase; white-space: nowrap;
  }

  .bc-tip { margin: 8px 0 0; color: var(--tsb-muted); font-family: var(--au-read, inherit); font-size: .74rem; line-height: 1.55; }

  /* Positions fold away: they overlap the teams (a person has both), so showing
     the two side by side invited a sum that means nothing. */
  .bc-more { margin-top: 10px; }
  .bc-more > summary {
    cursor: pointer; list-style: none; display: inline-flex; align-items: center; gap: 6px;
    color: var(--tsb-text-2); font-family: var(--au-read, inherit); font-size: .78rem; font-weight: 600;
  }
  .bc-more > summary::-webkit-details-marker { display: none; }
  .bc-more > summary::before { content: "▸"; color: var(--tsb-muted); }
  .bc-more[open] > summary::before { content: "▾"; }
  .bc-more > summary:hover { color: #fff; }
  .bc-more > .bc-chiprow { margin-top: 10px; }

  @media (max-width: 520px) {
    .bc-row { grid-template-columns: 1fr; gap: 6px; }
    .bc-chip-label { padding-top: 0; }
  }
  @media (prefers-reduced-motion: reduce) {
    .bc-group { transition: none; }
    .bc-group:active { transform: none; }
  }
  /* The composer's own status line. It used to write into the host page's
     #error / #okMsg, which meant the message vanished on any page that did not
     happen to have those elements. */
  .bc-say { margin: 0 0 14px; padding: 10px 13px; border-radius: 8px; font-family: var(--au-read, inherit); font-size: .8rem; font-weight: 600; line-height: 1.6; }
  .bc-say[hidden] { display: none; }
  .bc-say.err { border: 1px solid rgba(242,90,96,.35); background: rgba(242,90,96,.1); color: var(--tsb-danger); }
  .bc-say.ok { border: 1px solid rgba(53,197,121,.3); background: rgba(53,197,121,.1); color: var(--tsb-green); }
  .bc-people { max-height: 190px; overflow-y: auto; border: 1px solid var(--tsb-line-strong); border-radius: 8px; padding: 6px 10px; }
  .bc-person { display: flex; align-items: center; gap: 8px; padding: 4px 0; font-size: .74rem; color: var(--tsb-text-2); }
  .bc-person input { accent-color: var(--tsb-red-bright); width: 15px; height: 15px; flex: none; }
  .bc-person.off-by-default .bc-role { color: var(--tsb-red-bright); }
  .bc-role { margin-left: auto; font-size: .66rem; color: var(--tsb-muted); }
  .pd-l .pd-opt { color: var(--tsb-muted); font-weight: 500; text-transform: none; letter-spacing: 0; }
  .pd-i { min-height: 40px; padding: 0 12px; border: 1px solid var(--tsb-line-strong); border-radius: 8px; background: rgba(255,255,255,.03); color: var(--tsb-text); font-family: var(--tsb-body); font-size: .76rem; }
  .pd-i:focus { outline: 2px solid var(--tsb-red-bright); outline-offset: 1px; }
  .pd-hint { color: var(--tsb-muted); font-size: .62rem; line-height: 1.5; }
  .pd-hint.warn { color: var(--tsb-gold); }
  .pd-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 10px; margin-top: 22px; }
  .pd-err { margin-top: 16px; padding: 11px 13px; border: 1px solid rgba(242,90,96,.35); border-radius: 9px; background: rgba(242,90,96,.1); color: var(--tsb-danger); font-size: .72rem; font-weight: 600; }
  .pd-err[hidden] { display: none; }
    .pd-grid { grid-template-columns: 1fr; }
