  :root {
    --bg: #0b1220;
    --card-bg: #131c2e;
    --card-border: #223049;
    --surface-2: #1a2438;
    --text: #eef2f8;
    --text-dim: #9fb0c9;
    --accent: #4fd1c5;
    --accent-rgb: 79,209,197;
    --win: #2ecc71;
    --win-rgb: 46,204,113;
    --lose: #55617a;
    --warn: #f5a524;
    --warn-rgb: 245,165,36;
    --out: #ef4444;
    --out-rgb: 239,68,68;
    --star: #f5c518;
    --demo: #a855f7;
    --demo-rgb: 168,85,247;
    --live: #ff3b30;
    --live-rgb: 255,59,48;
    --edge-b: #7ab8ff;
    /* Sampled from the Blitz Edge brand mark - used only for the
       "called it" badge on a final game, kept distinct from --win so a
       correct call reads as a Blitz Edge signal rather than a generic
       win/loss color. */
    --blitz-orange: #fd7f01;
    --blitz-orange-fill-text: #062b16;
    --pill-good-text: #6be3a1;
    --pill-bad-text: #ff8080;
    --pill-good-fill-text: #062b16;
    --pill-bad-fill-text: #2a0505;
    --pill-out-text: #ff9d9d;
    --pill-questionable-text: #ffcf85;
    --pill-purple-text: #d6b8ff;
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239fb0c9' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  }
  html[data-theme="light"] {
    --bg: #dbe2ec;
    --card-bg: #ffffff;
    --card-border: #c3cedd;
    --surface-2: #e7ecf3;
    --text: #16202f;
    --text-dim: #5b6b80;
    --accent: #0f8f83;
    --accent-rgb: 15,143,131;
    --win: #1f9d54;
    --win-rgb: 31,157,84;
    --lose: #8a95a8;
    --warn: #b5720a;
    --warn-rgb: 181,114,10;
    --out: #d33a3a;
    --out-rgb: 211,58,58;
    --star: #a8770a;
    --demo: #8b3fd1;
    --demo-rgb: 139,63,209;
    --live: #d92d20;
    --live-rgb: 217,45,32;
    --edge-b: #1d6fc9;
    /* Darkened from the same brand orange to hold AA text contrast
       (~4.8:1) on a white card - the raw logo orange only clears ~2.6:1. */
    --blitz-orange: #b35900;
    --blitz-orange-fill-text: #ffffff;
    --pill-good-text: #1a7a43;
    --pill-bad-text: #b42318;
    --pill-good-fill-text: #ffffff;
    --pill-bad-fill-text: #ffffff;
    --pill-out-text: #b42318;
    --pill-questionable-text: #92530a;
    --pill-purple-text: #6b21a8;
    --select-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%235b6b80' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  }
  * { box-sizing: border-box; }
  /* Kills the grey/blue flash iOS and Android paint over any tapped control,
     and the 300ms double-tap-to-zoom delay that makes buttons feel laggy.
     Applied to controls only - text still selects normally. */
  button, a, [role="button"], summary, label, select {
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;
  }
  /* Nav chrome shouldn't be long-press selectable. */
  .tab-bar, .cmd-bar, .week-nav, header.top { -webkit-user-select: none; user-select: none; }
  body {
    margin: 0;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
    background: var(--bg);
    color: var(--text);
    transition: background 0.15s, color 0.15s;
  }
  .app { max-width: 1100px; margin: 0 auto; padding: 20px 16px 60px; }
  /* The Home tab is the only three-column screen in the app (main + rail),
     and 1100px squeezes the rail to the point the leaderboard rows wrap.
     Scoped to this tab rather than widened globally so the week view, team
     pages, and game pages keep the measure they were designed at. */
  .app.app-home { max-width: 1240px; }
  /* Grid (not absolute-position) so the icon cluster and the logo occupy
     separate tracks and can never overlap, no matter how wide either one
     gets on a given screen. Column 1 is a blank spacer that mirrors
     column 3's width so the logo (column 2) still lands roughly centered,
     but if the cluster needs more room than the spacer has to give, the
     grid simply narrows column 1 - it never lets column 2/3 collide. */
  header.top {
    display: grid; grid-template-columns: 1fr auto 1fr; align-items: start;
    gap: 8px; margin-bottom: 16px; text-align: center;
  }
  .brand-row {
    grid-column: 2; display: flex; align-items: center; justify-content: center;
    gap: 14px; cursor: pointer; margin-bottom: 6px;
  }
  .sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }

  .header-right-cluster {
    grid-column: 3; grid-row: 1; justify-self: end; align-self: start;
    display: flex; align-items: center; gap: 8px; padding-top: 2px;
  }
  /* BrandWordmark renders both a dark-bg and light-bg PNG of the real
     logo artwork; only one is shown at a time based on the active theme. */
  .brand-logo-light { display: none; }
  [data-theme="light"] .brand-logo-dark { display: none !important; }
  [data-theme="light"] .brand-logo-light { display: block !important; }

  /* Shrink the wordmark on narrow phones so the spacer/logo/cluster grid
     doesn't need to overflow horizontally to fit everything on one row. */
  @media (max-width: 480px) {
    .brand-logo-img { height: 60px !important; }
  }
  @media (max-width: 380px) {
    .brand-logo-img { height: 49px !important; }
    .header-right-cluster { gap: 6px; }
  }

  /* ---------- Compact sticky header (mobile only) ----------
     Above 720px .hdr-bar dissolves into header.top's grid and nothing here
     applies, so the desktop header is byte-for-byte what it was. */
  .hdr-bar { display: contents; }
  .hdr-lead, .hdr-center { display: none; }

  @media (max-width: 720px) {
    /* header.top steps out of the way so .hdr-bar sticks against .app - a
       52px-tall parent would let it scroll straight off. */
    header.top { display: contents; }
    /* Columns 1 and 3 are both 82px so the centre pill lands optically
       centred. Column 3 has to be allowed to grow past that, though: signed
       out the cluster is a "Sign in" button plus its caret, which measures
       93px, and a hard 82px track clipped 11px of it off the right edge of
       the screen. minmax keeps the 82px floor (so the signed-in avatar
       still balances the mark on the left) while letting the wider
       signed-out button size to its content. */
    .hdr-bar {
      display: grid; grid-template-columns: 82px minmax(0, 1fr) minmax(82px, auto); align-items: center;
      position: sticky; top: 0; z-index: 450;
      height: 52px; margin: -20px -16px 6px; padding: 0 8px;
      background: var(--card-bg);
      background: color-mix(in srgb, var(--bg) 72%, transparent);
      -webkit-backdrop-filter: saturate(180%) blur(20px);
      backdrop-filter: saturate(180%) blur(20px);
      border-bottom: 1px solid transparent;
      transition: border-color 0.2s;
    }
    /* The hairline only earns its keep once there's content behind the bar. */
    .hdr-bar.is-scrolled { border-bottom-color: var(--card-border); }
    .hdr-lead { display: flex; align-items: center; grid-column: 1; }
    .hdr-center { display: flex; align-items: center; justify-content: center; grid-column: 2; min-width: 0; }
    .header-right-cluster { grid-column: 3; grid-row: 1; justify-self: end; padding-top: 0; }

    .hdr-mark, .hdr-back {
      display: flex; align-items: center; background: none; border: none;
      padding: 0; cursor: pointer; color: var(--text); font: inherit;
    }
    .hdr-mark { padding-left: 4px; }
    .hdr-back {
      width: 40px; height: 40px; justify-content: center; margin-left: -6px;
      border-radius: 999px; transition: background 0.12s;
    }
    .hdr-back:active { background: rgba(var(--accent-rgb),0.14); color: var(--accent); }
    .brand-logo-mini .brand-logo-img { height: 21px !important; }

    .hdr-title {
      display: flex; flex-direction: column; align-items: center;
      font-size: 0.95rem; font-weight: 700; line-height: 1.15;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
    }
    .hdr-sub { font-size: 0.64rem; font-weight: 600; color: var(--text-dim); letter-spacing: 0.3px; }

    /* Home keeps the full wordmark below the bar; it fades and shrinks into
       the small one as you scroll (see the scroll effect in App). */
    .brand-row { display: none; }
    .app-home .brand-row {
      display: flex; grid-column: auto; margin: 2px 0 10px;
      transform-origin: top center; will-change: transform, opacity;
    }
    .app { padding-top: 20px; }

    /* One week control, not two: the header pill owns it on mobile, so the
       command bar's own picker steps aside. */
    .cmd-bar .week-nav, .cmd-bar .cmd-divider { display: none; }
    .cmd-favs::before {
      content: "Favourites"; font-size: 0.68rem; font-weight: 700;
      text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim);
      flex-shrink: 0; margin-right: 2px;
    }
    .cmd-bar-bare { background: none; border: none; padding: 4px 0; margin: 12px 0 4px; }
    /* The header carries the back affordance now. Kept in the DOM, not
       unmounted - the tour navigates by clicking it. */
    .back-btn.crumb-back { display: none; }
  }

  /* The header pill: a raised control with a chevron. Plain centred text
     with a caret didn't read as tappable in review. */
  .hdr-week { position: relative; display: flex; align-items: center; max-width: 100%; min-width: 0; }
  .hdr-week-pill {
    display: inline-flex; align-items: center; gap: 6px;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 999px;
    color: var(--text); font-family: inherit; font-size: 0.84rem; font-weight: 700;
    padding: 8px 13px 8px 15px; cursor: pointer; white-space: nowrap;
    transition: transform 0.1s ease, border-color 0.15s;
  }
  .hdr-week-pill .week-nav-chevron { color: var(--accent); flex-shrink: 0; }
  .hdr-week-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .hdr-week-pill { max-width: 100%; min-width: 0; }
  .hdr-week-pill:active { transform: scale(0.95); border-color: var(--accent); }
  .hdr-week-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  /* Anchored to the pill and centred under it, and above the sticky bar's
     own z-index so it can't render behind the blur. */
  .week-nav-panel-hdr {
    top: calc(100% + 8px); left: 50%; right: auto; transform: translateX(-50%);
    z-index: 460; max-height: 60vh; max-height: 60dvh;
  }

  .account-menu { position: relative; flex-shrink: 0; }
  .account-btn {
    display: flex; align-items: center; justify-content: center;
    border-radius: 999px; border: 1px solid var(--card-border); cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
    padding: 0; font: inherit;
  }
  .account-btn-signin {
    height: 38px; padding: 0 14px; background: var(--card-bg); color: var(--text-dim); font-size: 13px; font-weight: 600;
  }
  .account-btn-signin:hover, .account-btn-signin:focus-visible { color: var(--accent); border-color: var(--accent); background: rgba(var(--accent-rgb),0.08); }
  .account-signedout-row { display: flex; align-items: stretch; }
  .account-signedout-row .account-btn-signin { border-radius: 999px 0 0 999px; border-right: none; }
  .account-menu-toggle {
    display: flex; align-items: center; justify-content: center; width: 26px; height: 38px;
    border-radius: 0 999px 999px 0; border: 1px solid var(--card-border); background: var(--card-bg);
    color: var(--text-dim); cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .account-menu-toggle:hover, .account-menu-toggle:focus-visible { color: var(--accent); border-color: var(--accent); background: rgba(var(--accent-rgb),0.08); }
  .account-menu-toggle-chevron { font-size: 10px; line-height: 1; }
  .account-avatar {
    width: 38px; height: 38px; background: var(--accent); color: #062018; border-color: var(--accent);
    font-size: 15px; font-weight: 700; overflow: hidden;
  }
  /* Solid accent teal is only right for the dark-on-teal initial-letter
     fallback. A chosen icon (many of which skew blue/teal themselves,
     e.g. the cap) can end up low-contrast against that same teal, so a
     real avatar gets a neutral card-toned circle instead - same idea as
     the softer background already used behind the picker in Settings. */
  .account-avatar.has-glyph { background: var(--surface-2); border-color: var(--card-border); }
  .account-avatar .avatar-glyph-img { width: 66%; height: 66%; object-fit: contain; }
  /* Emoji glyphs need a bigger font-size than plain text to read at the same
     visual size (emoji metrics carry extra built-in padding) - this used to
     only be set for the bolt/check icons, so every other emoji option in
     PROFILE_AVATARS shrank along with the "D"-initial fallback on the
     mobile breakpoint below. Apply to all emoji glyphs, not just those two. */
  .account-avatar span { font-size: 17px; }
  .account-avatar:hover, .account-avatar:focus-visible { filter: brightness(1.08); }
  .account-dropdown {
    position: absolute; top: 46px; right: 0; min-width: 170px; z-index: 20;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.28); padding: 6px; text-align: left;
  }
  .account-dropdown-name {
    padding: 8px 10px 6px; font-size: 12.5px; font-weight: 600; color: var(--text);
    border-bottom: 1px solid var(--card-border); margin-bottom: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .account-dropdown-item {
    display: block; width: 100%; text-align: left; background: none; border: none; cursor: pointer;
    padding: 8px 10px; border-radius: 6px; font-size: 13.5px; color: var(--text-dim);
    /* The signed-out menu's Archive entry is a real <a> rather than a button
       so it can be middle-clicked, copied, and crawled like the link it is.
       Buttons don't inherit font or carry an underline; anchors do the
       opposite. Setting both here keeps the two element types identical. */
    font-family: inherit; text-decoration: none; box-sizing: border-box;
  }
  .account-dropdown-item:hover, .account-dropdown-item:focus-visible { background: rgba(var(--accent-rgb),0.08); color: var(--accent); }
  @media (max-width: 480px) {
    .account-btn-signin { height: 34px; padding: 0 12px; font-size: 12.5px; }
    .account-menu-toggle { height: 34px; }
    .account-avatar { width: 34px; height: 34px; font-size: 13px; }
    .account-avatar span { font-size: 15px; }
  }

  .controls-row {
    display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap;
    margin: 14px 0 8px;
  }
  @media (max-width: 480px) {
    .controls-row { gap: 10px; }
    .team-view-header { padding: 14px; gap: 12px; }
    .team-view-header img { width: 52px; height: 52px; }
  }
  .back-btn {
    display: inline-flex; align-items: center; gap: 6px;
    background: var(--card-bg); color: var(--text); border: 1px solid var(--card-border);
    border-radius: 999px; padding: 9px 16px 9px 14px; font-size: 0.85rem; font-weight: 600; cursor: pointer;
    transition: border-color 0.15s, background 0.15s, color 0.15s;
  }
  .back-btn:hover { border-color: var(--accent); color: var(--accent); background: rgba(var(--accent-rgb),0.08); }

  .theme-toggle {
    display: inline-flex; align-items: center; gap: 2px; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 999px; padding: 3px;
  }
  .theme-toggle-btn {
    display: flex; align-items: center; gap: 5px; background: transparent; border: none;
    color: var(--text-dim); padding: 6px 10px; border-radius: 999px; cursor: pointer;
    font-size: 0.72rem; font-weight: 600; transition: background 0.15s, color 0.15s;
  }
  .theme-toggle-btn:hover { color: var(--text); }
  .theme-toggle-btn.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); }
  .theme-toggle-label { display: none; }
  @media (min-width: 480px) { .theme-toggle-label { display: inline; } }

  .book-picker { position: relative; display: inline-flex; }
  .book-picker-trigger {
    display: flex; align-items: center; gap: 6px; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 999px; color: var(--text);
    font-size: 0.78rem; font-weight: 600; padding: 7px 12px; cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .book-picker-trigger:hover { border-color: var(--accent); }
  .book-picker-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .book-picker-label-dim { color: var(--text-dim); font-weight: 500; }
  .book-picker-chevron { color: var(--text-dim); transition: transform 0.18s; display: flex; }
  .book-picker-chevron.open { transform: rotate(180deg); }
  .book-picker-panel {
    position: absolute; top: calc(100% + 10px); left: 0; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 14px; box-shadow: 0 16px 40px rgba(0,0,0,0.35);
    padding: 8px; min-width: 200px; max-height: 320px; overflow-y: auto; z-index: 30;
  }
  .book-picker-item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
    background: none; border: none; color: var(--text); font-size: 0.84rem; text-align: left;
    padding: 9px 12px; border-radius: 8px; cursor: pointer; transition: background 0.12s;
  }
  .book-picker-item:hover { background: rgba(var(--accent-rgb),0.08); }
  .book-picker-item.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); font-weight: 700; }
  .book-picker-hint {
    font-size: 0.66rem; color: var(--text-dim); padding: 8px 12px 4px; border-top: 1px solid var(--card-border);
    margin-top: 4px;
  }

  /* Team-view "Team:" switcher - a custom dropdown (not a native <select>)
     so each row can carry its own favorite-star toggle button. */
  .team-switcher { position: relative; display: inline-flex; }
  .team-switcher-trigger {
    display: flex; align-items: center; gap: 6px; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 999px; color: var(--text);
    font-size: 0.85rem; font-weight: 600; padding: 7px 12px; cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .team-switcher-trigger:hover { border-color: var(--accent); }
  .team-switcher-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .team-switcher-chevron { color: var(--text-dim); transition: transform 0.18s; display: flex; }
  .team-switcher-chevron.open { transform: rotate(180deg); }
  .team-switcher-panel {
    position: absolute; top: calc(100% + 10px); left: 0; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 14px; box-shadow: 0 16px 40px rgba(0,0,0,0.35);
    padding: 8px; min-width: 240px; max-height: 360px; overflow-y: auto; z-index: 30;
  }
  .team-switcher-group-label {
    font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-dim);
    font-weight: 700; padding: 10px 12px 6px;
  }
  .team-switcher-group-label:first-child { padding-top: 4px; }
  .team-switcher-item {
    display: flex; align-items: center; justify-content: space-between; gap: 4px; width: 100%;
    border-radius: 8px; transition: background 0.12s;
  }
  .team-switcher-item:hover { background: rgba(var(--accent-rgb),0.08); }
  .team-switcher-item.active { background: rgba(var(--accent-rgb),0.16); }
  .team-switcher-item.active .team-switcher-item-name { color: var(--accent); font-weight: 700; }
  .team-switcher-item-name {
    flex: 1; background: none; border: none; color: var(--text); font-size: 0.86rem; text-align: left;
    padding: 8px 4px 8px 12px; cursor: pointer; font-family: inherit; border-radius: 8px;
  }
  /* FavoriteStar is normally absolutely-positioned to sit on a team badge's
     corner - here it needs to sit inline as a normal row item instead. */
  .team-switcher-item .star-btn { position: static; flex-shrink: 0; margin-right: 8px; }

  .week-nav {
    position: relative; display: flex; align-items: center; gap: 0; flex-shrink: 0;
  }
  .week-nav-arrow {
    display: flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: 6px; border: none; background: transparent;
    color: var(--text-dim); cursor: pointer; transition: background 0.15s, color 0.15s;
  }
  .week-nav-arrow:hover:not(:disabled) { background: rgba(255,255,255,0.06); color: var(--text); }
  .week-nav-arrow:disabled { opacity: 0.3; cursor: not-allowed; }
  .week-nav-trigger {
    display: flex; align-items: center; gap: 6px; background: transparent; border: none;
    color: var(--text); font-size: 0.82rem; font-weight: 700; letter-spacing: 0.2px; white-space: nowrap;
    padding: 4px 6px; border-radius: 6px; cursor: pointer; transition: background 0.15s;
  }
  .week-nav-trigger:hover { background: rgba(255,255,255,0.05); }
  .week-nav-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .week-nav-result-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--demo); flex-shrink: 0; }
  .week-nav-chevron { color: var(--text-dim); transition: transform 0.18s; display: flex; }
  .week-nav-chevron.open { transform: rotate(180deg); }
  .week-nav-panel {
    position: absolute; top: calc(100% + 10px); left: 0; transform: none;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.35); padding: 8px; min-width: 240px; max-height: 380px;
    overflow-y: auto; z-index: 30;
  }
  .week-nav-group-label {
    font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-dim);
    font-weight: 700; padding: 10px 12px 6px;
  }
  .week-nav-group-label:first-child { padding-top: 4px; }
  .week-nav-item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
    background: none; border: none; color: var(--text); font-size: 0.86rem; text-align: left;
    padding: 9px 12px; border-radius: 8px; cursor: pointer; transition: background 0.12s;
  }
  .week-nav-item:hover { background: rgba(var(--accent-rgb),0.08); }
  .week-nav-item.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); font-weight: 700; }
  .week-nav-result-badge {
    font-size: 0.62rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.3px;
    background: rgba(var(--demo-rgb),0.18); color: var(--pill-purple-text); padding: 2px 7px; border-radius: 999px;
    flex-shrink: 0;
  }

  /* Command bar: week picker + favorites + an on-demand legend toggle, all
     in one flat bar with a surface-2/8px-radius style instead of the week
     picker floating alone as a heavier pill next to a separate favorites
     row and a permanently-visible legend underneath. */
  .cmd-bar {
    display: flex; align-items: center; gap: 10px; flex-wrap: nowrap;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 8px;
    padding: 7px 10px; margin: 18px 0 8px;
  }
  .cmd-divider { width: 1px; align-self: stretch; background: var(--card-border); flex-shrink: 0; }
  .cmd-favs {
    display: flex; align-items: center; gap: 6px; flex: 1; min-width: 0; overflow-x: auto;
    scrollbar-width: none; -ms-overflow-style: none;
  }
  .cmd-favs::-webkit-scrollbar { display: none; }
  .cmd-fav-btn {
    background: none; border: none; padding: 0; cursor: pointer; flex-shrink: 0; display: flex;
    border-radius: 50%; transition: transform 0.12s;
  }
  .cmd-fav-btn:hover { transform: scale(1.12); }
  .cmd-fav-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  /* Game type picker: what the reader's own pool scores, which is what the
     Blitz Edge row on every card below reads through. Accent-tinted rather
     than the outline treatment the legend toggle used, because unlike that
     toggle it holds state worth noticing at a glance. */
  .cmd-gametype { position: relative; flex-shrink: 0; margin-left: auto; }
  .gametype-trigger {
    display: flex; align-items: center; gap: 6px; background: rgba(var(--accent-rgb),0.10);
    border: 1px solid rgba(var(--accent-rgb),0.45); border-radius: 999px; color: var(--accent);
    font-size: 0.72rem; font-weight: 700; padding: 5px 10px; cursor: pointer; white-space: nowrap;
    transition: background 0.15s;
  }
  .gametype-trigger:hover { background: rgba(var(--accent-rgb),0.18); }
  .gametype-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .gametype-trigger .week-nav-chevron { color: inherit; }
  .gametype-icon { height: 13px; width: auto; display: block; flex: 0 0 auto; }
  .gametype-label-short { display: none; }
  .gametype-panel {
    position: absolute; top: calc(100% + 10px); right: 0;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    box-shadow: 0 16px 40px rgba(0,0,0,0.35); padding: 8px; min-width: 232px; z-index: 30;
  }
  .gametype-group-label {
    font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-dim);
    font-weight: 700; padding: 4px 12px 6px;
  }
  .gametype-item {
    display: block; width: 100%; background: none; border: none; color: var(--text); text-align: left;
    padding: 9px 12px; border-radius: 8px; cursor: pointer; transition: background 0.12s;
  }
  .gametype-item:hover { background: rgba(var(--accent-rgb),0.08); }
  .gametype-item.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); }
  .gametype-item-name { font-size: 0.86rem; font-weight: 700; display: block; }
  .gametype-item-desc { font-size: 0.7rem; color: var(--text-dim); display: block; margin-top: 2px; }
  .gametype-item.active .gametype-item-desc { color: rgba(var(--accent-rgb),0.85); }
  @media (max-width: 720px) {
    .cmd-bar { padding: 6px 8px; gap: 6px; }
    .week-nav-trigger { font-size: 0.74rem; padding: 4px 4px; gap: 4px; }
  }
  @media (max-width: 480px) {
    .gametype-label-full { display: none; }
    .gametype-label-short { display: inline; }
  }

  .favorites-bar {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-start;
    margin: 10px 0 18px;
  }
  .favorites-bar .fav-label { font-size: 0.72rem; color: var(--text-dim); margin-right: 4px; }
  .fav-chip {
    display: flex; align-items: center; gap: 5px; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 999px; padding: 3px 10px 3px 3px; cursor: pointer; font-size: 0.78rem;
    transition: border-color 0.15s;
  }
  .fav-chip:hover { border-color: var(--star); }

  /* Primary tab navigation - horizontal underline strip on desktop, fixed
     bottom bar on mobile. Same markup (TabBar component) both ways; the
     icon-badge active-state language (filled circle + accent) carries
     through to both, only the container's position/layout and row
     direction change via the media query below. Extra bottom padding on
     .app keeps page content clear of the fixed bar on mobile. */

  /* ==========================================================
     Home tab - signed-out landing
     ========================================================== */
  .home-hero {
    display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, .95fr);
    gap: 34px; align-items: center; margin: 26px 0 10px;
  }
  .home-hero h2.home-display {
    margin: 12px 0 12px; font-size: 2.6rem; line-height: 1.08;
    font-weight: 800; letter-spacing: -.03em;
  }
  .home-hero h2.home-display em { font-style: normal; color: var(--accent); }
  .home-lede { margin: 0 0 22px; font-size: 1rem; line-height: 1.55; color: var(--text-dim); max-width: 46ch; }
  .home-eyebrow {
    font-size: .68rem; font-weight: 800; letter-spacing: .1em;
    text-transform: uppercase; color: var(--text-dim);
  }
  .home-acts { display: flex; gap: 11px; flex-wrap: wrap; }
  .home-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    border-radius: 9px; border: 1px solid var(--card-border); background: var(--surface-2);
    color: var(--text); font-family: inherit; font-size: .84rem; font-weight: 700;
    padding: 10px 15px; cursor: pointer; white-space: nowrap; text-decoration: none;
  }
  .home-btn.primary { background: var(--accent); border-color: var(--accent); color: #04121a; }
  .home-trust { margin-top: 16px; font-size: .78rem; color: var(--text-dim); display: flex; gap: 16px; flex-wrap: wrap; }
  .home-trust span { display: inline-flex; align-items: center; gap: 6px; }

  .home-card { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px; }
  .home-edge { padding: 16px 16px 14px; }
  .home-edge-head { display: flex; align-items: center; gap: 9px; margin-bottom: 12px; }
  .home-edge-head img { height: 22px; width: auto; }
  .home-matchup { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 10px; padding: 8px 0 14px; }
  /* Same winner treatment as the week view's game card - a green box around
     the picked side - rather than this card's own orange pill, so a person
     coming from Home to the games list sees one visual language. */
  .home-mt {
    display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 10px 6px; border-radius: 12px; border: 1px solid transparent;
  }
  .home-mt.pick { background: rgba(var(--win-rgb),0.07); border-color: rgba(var(--win-rgb),0.32); }
  .home-mt b { font-size: .84rem; }
  .home-at { font-size: .8rem; color: var(--text-dim); font-weight: 700; }
  .home-pickflag { font-size: .6rem; font-weight: 800; letter-spacing: .05em; color: var(--win); text-transform: uppercase; }
  .home-combo {
    border-radius: 10px; background: var(--surface-2); border: 1px solid var(--card-border); overflow: hidden;
  }
  .home-combo .hc-top { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 10px 12px; background: rgba(var(--accent-rgb),.12); }
  .home-combo .hc-logo { height: 24px; width: auto; display: block; flex: 0 0 auto; }
  .home-combo .hc-conf { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
  .home-combo .hc-conf-lbl { font-size: .72rem; color: var(--text-dim); }
  .home-combo .hc-conf-pct { font-size: .95rem; font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; }
  /* Same two-row treatment as the game card's .cb-odds (both moneylines,
     fixed rows) so the two surfaces keep reading identically - that match is
     the whole reason this row was built to copy that one. */
  .home-combo .hc-odds {
    border-top: 1px solid var(--card-border); padding: 7px 12px;
    display: flex; flex-direction: column; gap: 3px;
    font-size: .74rem; color: var(--text-dim); font-variant-numeric: tabular-nums;
  }
  .home-combo .hc-odds-line { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center; }
  .home-combo .hc-odds b { color: var(--text); font-weight: 800; white-space: nowrap; }
  .home-combo .hc-stat { white-space: nowrap; }
  .home-combo .hc-stat + .hc-stat::before { content: "\00b7"; opacity: .5; margin: 0 6px; }
  .home-combo .hc-odds-k { font-size: .62rem; text-transform: uppercase; letter-spacing: .4px; font-weight: 800; margin-right: 5px; }
  .home-why-line { margin: 12px 0 0; font-size: .82rem; line-height: 1.55; color: var(--text-dim); }
  .home-why-line b { color: var(--text); }

  .home-sec-head { display: flex; align-items: baseline; justify-content: space-between; margin: 38px 0 12px; }
  .home-sec-head h3 { margin: 0; font-size: 1.25rem; font-weight: 800; letter-spacing: -.01em; }

  .home-fmt-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
  .home-fmt { padding: 16px 14px 15px; text-align: center; }
  .home-fmt img { width: 66px; height: 66px; object-fit: contain; }
  .home-fmt h4 { margin: 8px 0 4px; font-size: .92rem; font-weight: 800; }
  .home-fmt p { margin: 0; font-size: .76rem; line-height: 1.5; color: var(--text-dim); }

  /* Two cards today. The third ("bring the pool you already run") is built
     but hidden behind HOME_IMPORT_PILLAR until pool import actually ships -
     flip that flag and this grid to repeat(3, 1fr) together. */
  .home-value { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; }
  .home-value.has-import { grid-template-columns: repeat(3, 1fr); }
  .home-value-cell { padding: 16px; }
  .home-value-cell h4 { margin: 0 0 5px; font-size: .9rem; font-weight: 800; }
  .home-value-cell p { margin: 0; font-size: .8rem; line-height: 1.55; color: var(--text-dim); }
  .home-tag {
    display: inline-block; margin-bottom: 9px; font-size: .62rem; font-weight: 800; letter-spacing: .08em;
    text-transform: uppercase; color: var(--accent); border: 1px solid rgba(var(--accent-rgb), .4);
    border-radius: 999px; padding: 3px 9px;
  }
  .home-close { text-align: center; padding: 26px 20px; margin-top: 22px; }
  .home-close h3 { margin: 0 0 6px; font-size: 1.25rem; font-weight: 800; }
  .home-close p { margin: 0 0 16px; font-size: .86rem; color: var(--text-dim); }
  .home-close .home-acts { justify-content: center; }

  @media (max-width: 860px) {
    /* The hero dissolves so the four formats can sit between the pitch and
       the (tall) Blitz Edge card - a new visitor gets "here's how you'd
       play" before the model demo, which scrolls badly at the top. */
    .home-landing { display: flex; flex-direction: column; align-items: stretch; }
    .home-hero { display: contents; }
    .home-hero-copy { order: 1; }
    .home-landing > .home-sec-head.fmt { order: 2; }
    .home-landing > .home-fmt-grid { order: 3; }
    .home-edge { order: 4; margin-top: 26px; }
    .home-landing > .home-sec-head.why { order: 5; }
    .home-landing > .home-value { order: 6; }
    .home-landing > .home-close { order: 7; }
    .home-hero h2.home-display { font-size: 1.85rem; }
    .home-fmt-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
    .home-value, .home-value.has-import { grid-template-columns: 1fr; }
  }


  /* ==========================================================
     Home tab - signed-in dashboard
     ========================================================== */
  .hd-greet { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; flex-wrap: wrap; margin: 22px 0 0; }
  .hd-greet h2 { margin: 0 0 4px; font-size: 1.85rem; font-weight: 800; letter-spacing: -.02em; }
  .hd-greet p { margin: 0; color: var(--text-dim); font-size: .9rem; }
  .hd-greet-acts { display: flex; gap: 10px; }

  /* The one thing a home page owes you that a league page doesn't: what's
     due, across every league at once, and when it locks. */
  .hd-week {
    display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
    padding: 14px 18px; margin: 18px 0 20px; border-radius: 14px;
    background: linear-gradient(90deg, rgba(253,127,1,.16), rgba(253,127,1,0) 55%), var(--card-bg);
    border: 1px solid var(--card-border); border-left: 3px solid var(--blitz-orange);
  }
  [data-theme="light"] .hd-week {
    background: linear-gradient(90deg, rgba(179,89,0,.12), rgba(179,89,0,0) 55%), var(--card-bg);
  }
  .hd-week-main { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
  .hd-week-title { font-size: 1rem; font-weight: 800; }
  .hd-week-count { font-variant-numeric: tabular-nums; font-weight: 800; color: var(--blitz-orange); font-size: 1rem; }
  .hd-week-sub { font-size: .82rem; color: var(--text-dim); }
  .hd-week > .home-btn { margin-left: auto; }

  /* Same column structure as .hd-layout below (main + 340px rail, 22px
     gutter), so the three record tiles span exactly the league table's width
     and the streak tile lines up with the leaderboard/games rail. The inner
     14px gap is the tiles' own rhythm and is deliberately tighter than the
     22px gutter that separates the two columns. */
  /* margin-top is on the grid itself, not inherited from .hd-week above it -
     that bar only renders when picks are outstanding, so relying on its
     margin left the tiles flush against the greeting's buttons. */
  .hd-stats { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 22px; align-items: start; margin-top: 18px; }
  .hd-stats-main { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
  /* Column + auto-margin footer so tiles sharing a row read as one band even
     when one caption wraps to two lines and its neighbour's doesn't. */
  .hd-tile { padding: 15px 16px 14px; position: relative; display: flex; flex-direction: column; }
  .hd-tile .home-eyebrow { display: block; margin-bottom: 8px; }
  /* Only the pager tile needs the top-right gutter reserved. */
  .hd-tile.has-pager .home-eyebrow { padding-right: 62px; }
  .hd-val { display: flex; align-items: baseline; gap: 8px; font-size: 1.9rem; font-weight: 800; letter-spacing: -.02em; }
  .hd-val small { font-size: .88rem; font-weight: 700; color: var(--accent); }
  .hd-foot { margin-top: auto; padding-top: 7px; font-size: .74rem; color: var(--text-dim); }
  .hd-val.accent { color: var(--accent); }
  .hd-val.orange { color: var(--blitz-orange); }
  .hd-val.win { color: var(--win); }
  .hd-val.dim { color: var(--text-dim); }

  /* Shown only when the member is in more than one league. Manual, not
     auto-rotating: a tile that changes while you're reading it is worse
     than one you have to tap. */
  .hd-pager { display: inline-flex; align-items: center; gap: 2px; }
  .hd-pager button {
    width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center;
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text-dim);
    cursor: pointer; font-size: .7rem; line-height: 1; padding: 0; font-family: inherit;
  }
  .hd-pager button:hover { color: var(--accent); border-color: rgba(var(--accent-rgb), .5); }
  .hd-pager .of { font-size: .66rem; color: var(--text-dim); font-variant-numeric: tabular-nums; padding: 0 4px; }
  .hd-tile .hd-pager { position: absolute; top: 12px; right: 12px; }

  .hd-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 22px; align-items: start; margin-top: 30px; }
  .hd-sec-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 12px; }
  .hd-sec-head h3 { margin: 0; font-size: 1.25rem; font-weight: 800; letter-spacing: -.01em; }
  .hd-link { font-size: .74rem; font-weight: 700; color: var(--accent); background: none; border: none; cursor: pointer; font-family: inherit; padding: 0; }

  .hd-leagues { display: flex; flex-direction: column; gap: 12px; }
  .hd-row {
    display: grid; grid-template-columns: 70px minmax(150px, 1.4fr) repeat(3, minmax(84px, 1fr)) auto;
    align-items: center; gap: 18px; padding: 14px 16px;
  }
  .hd-row-badge { width: 62px; height: 62px; object-fit: contain; }
  .hd-row-name { font-size: 1rem; font-weight: 800; margin: 0 0 2px; }
  .hd-req-badge { font-family: inherit; font-size: .64rem; font-weight: 800; letter-spacing: .06em;
    text-transform: uppercase; padding: 3px 8px; border-radius: 999px; cursor: pointer;
    background: rgba(var(--blitz-orange-rgb), .16); color: var(--blitz-orange);
    border: 1px solid rgba(var(--blitz-orange-rgb), .45); }
  .hd-req-badge:hover { background: rgba(var(--blitz-orange-rgb), .26); }
  .hd-row-fmt { font-size: .8rem; color: var(--text-dim); margin: 0 0 7px; }
  .hd-row-meta { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: .74rem; color: var(--text-dim); }
  .hd-col .home-eyebrow { display: block; margin-bottom: 5px; }
  .hd-big { font-size: 1.28rem; font-weight: 800; letter-spacing: -.01em; }
  .hd-big.accent { color: var(--accent); }
  .hd-big.orange { color: var(--blitz-orange); }
  .hd-big.win { color: var(--win); }
  .hd-big.dim { color: var(--text-dim); }
  .hd-small { margin-top: 4px; font-size: .74rem; color: var(--text-dim); }
  .hd-delta { font-size: .74rem; font-weight: 700; }
  .hd-delta.up { color: var(--win); }
  .hd-delta.down { color: var(--out); }
  .hd-prog { height: 5px; border-radius: 3px; background: var(--surface-2); margin-top: 7px; overflow: hidden; }
  .hd-prog i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }

  .hd-rail { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 16px; }
  .hd-rail-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 14px 16px 10px; border-bottom: 1px solid var(--card-border); }
  .hd-rail-league { display: flex; align-items: center; gap: 8px; min-width: 0; }
  .hd-rail-league img { width: 22px; height: 22px; object-fit: contain; flex-shrink: 0; }
  .hd-rail-league b { font-size: .82rem; font-weight: 800; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .hd-lb-row { display: grid; grid-template-columns: 20px 1fr auto auto; gap: 12px; align-items: center; padding: 9px 16px; font-size: .86rem; }
  .hd-lb-row.me { background: rgba(var(--accent-rgb), .1); }
  .hd-lb-row.me .hd-lb-name { color: var(--accent); font-weight: 800; }
  .hd-lb-rank { color: var(--text-dim); font-weight: 700; }
  .hd-lb-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .hd-lb-rec { font-variant-numeric: tabular-nums; color: var(--text-dim); }
  .hd-lb-pct { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 700; min-width: 34px; text-align: right; }
  .hd-lb-status { font-size: .8rem; font-weight: 700; }
  .hd-lb-status.alive { color: var(--win); }
  .hd-lb-status.out { color: var(--lose); }
  /* Alive but carrying a strike - only reachable past a 1-strike league. */
  .hd-lb-status.warn { color: var(--warn); }
  .hd-rail-foot { padding: 10px 16px 14px; font-size: .72rem; color: var(--text-dim); }
  .hd-game-row { display: flex; align-items: center; gap: 12px; padding: 11px 16px; }
  .hd-game-row + .hd-game-row { border-top: 1px solid var(--card-border); }
  .hd-gm { flex: 1; min-width: 0; }
  .hd-gm-teams { font-size: .88rem; font-weight: 700; }
  .hd-gm-time { font-size: .74rem; color: var(--text-dim); margin-top: 2px; }
  .hd-chip { display: flex; align-items: center; gap: 8px; padding: 6px 9px; border-radius: 10px; background: var(--surface-2); border: 1px solid var(--card-border); }
  .hd-chip .lbl { font-size: .56rem; font-weight: 800; letter-spacing: .07em; color: var(--text-dim); line-height: 1.25; }
  .hd-chip .pct { font-size: .84rem; font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; }

  .hd-invite { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; padding: 18px 20px; margin-top: 16px; }
  .hd-invite h4 { margin: 0 0 3px; font-size: 1rem; font-weight: 800; }
  .hd-invite p { margin: 0; font-size: .84rem; color: var(--text-dim); }
  .hd-invite .acts { margin-left: auto; display: flex; gap: 10px; }
  .hd-empty { padding: 26px 20px; text-align: center; }
  .hd-empty p { margin: 0 0 14px; color: var(--text-dim); font-size: .88rem; }

  /* Mobile: the rail's three cards become segmented tabs, and the main
     column dissolves so the rail can slot between the league list and the
     invite CTA without duplicating markup. */
  .hd-rail-tabs { display: none; gap: 6px; margin-bottom: 12px; }
  .hd-rail-tabs button {
    flex: 1; padding: 9px 6px; border-radius: 9px; font-size: .76rem; font-weight: 700; cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text-dim); font-family: inherit;
  }
  .hd-rail-tabs button[aria-pressed="true"] { background: var(--accent); border-color: var(--accent); color: #04121a; }

  @media (max-width: 860px) {
    .hd-greet h2 { font-size: 1.4rem; }
    .hd-greet-acts { width: 100%; }
    .hd-greet-acts .home-btn { flex: 1; }
    .hd-week > .home-btn { margin-left: 0; width: 100%; }
    /* Single column stack: the main group dissolves so all four tiles sit
       in one 2-up grid rather than three-then-one. */
    /* align-items:start is for the desktop main+rail split; once all four
       tiles share one grid they must stretch to their row's height. */
    .hd-stats { grid-template-columns: repeat(2, 1fr); gap: 10px; align-items: stretch; }
    .hd-stats-main { display: contents; }
    .hd-val { font-size: 1.5rem; }
    .hd-layout { display: flex; flex-direction: column; align-items: stretch; }
    .hd-main { display: contents; }
    .hd-main > * { order: 1; }
    .hd-rail-wrap { order: 2; margin-top: 20px; }
    .hd-main > .hd-invite { order: 3; }
    .hd-rail { position: static; }
    .hd-rail-tabs { display: flex; }
    .hd-rail > .home-card { display: none; }
    .hd-rail > .home-card.rail-on { display: block; }
    .hd-row {
      grid-template-columns: 52px 1fr;
      grid-template-areas: "badge name" "stats stats" "cta cta";
      gap: 10px 12px;
    }
    .hd-row-badge { grid-area: badge; width: 48px; height: 48px; }
    .hd-row-id { grid-area: name; }
    .hd-row-cols { grid-area: stats; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
    .hd-row-cta { grid-area: cta; }
    .hd-row-cta .home-btn { width: 100%; }
    .hd-big { font-size: 1.05rem; }
    .hd-invite .acts { margin-left: 0; width: 100%; }
    .hd-invite .acts .home-btn { flex: 1; }
  }
  .hd-row-cols { display: contents; }
  @media (max-width: 860px) { .hd-row-cols { display: grid; } }


  /* ==========================================================
     League create wizard + join/browse redesign
     ========================================================== */
  .lgx-layout { display: grid; grid-template-columns: minmax(0, 1fr) 360px; gap: 22px; align-items: start; }
  .lgx-rail { display: flex; flex-direction: column; gap: 16px; position: sticky; top: 16px; }
  .lgx-card { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px; }
  .lgx-head { padding: 14px 16px 10px; border-bottom: 1px solid var(--card-border); }
  .lgx-eyebrow { font-size: .68rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--text-dim); }
  .lgx-label { display: block; font-size: .68rem; font-weight: 800; letter-spacing: .09em;
    text-transform: uppercase; color: var(--text-dim); margin-bottom: 8px; }
  .lgx-help { font-size: .8rem; color: var(--text-dim); margin: 0 0 12px; }
  .lgx-count { text-align: right; font-size: .72rem; color: var(--text-dim); margin-top: 5px; }
  .lgx-body { padding: 22px; }
  /* .league-input is sized for the old narrow form column; inside the
     wizard the fields own their full width. .lgx-set keeps its own
     min-width rule, which is why this is scoped rather than global. */
  .lgx-body > .league-input, .lgx-body > .league-textarea,
  .lgx-two .league-input { width: 100%; }
  .lgx-foot { display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 18px 22px; border-top: 1px solid var(--card-border); }
  .lgx-two { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; }

  /* stepper */
  .lgx-steps { display: flex; align-items: center; padding: 18px 22px; border-bottom: 1px solid var(--card-border); }
  .lgx-step { display: flex; align-items: center; gap: 9px; white-space: nowrap; background: none;
    border: none; padding: 0; font-family: inherit; color: inherit; }
  .lgx-step[data-clickable="1"] { cursor: pointer; }
  .lgx-step .n { width: 26px; height: 26px; border-radius: 50%; display: grid; place-items: center;
    font-size: .78rem; font-weight: 800; background: var(--surface-2);
    border: 1px solid var(--card-border); color: var(--text-dim); }
  .lgx-step.on .n { background: var(--accent); border-color: var(--accent); color: #04121a; }
  .lgx-step.done .n { background: transparent; border-color: var(--accent); color: var(--accent); }
  .lgx-step .t { font-size: .86rem; font-weight: 700; color: var(--text-dim); }
  .lgx-step.on .t { color: var(--text); }
  .lgx-rule { flex: 1; height: 1px; background: var(--card-border); margin: 0 16px; min-width: 18px; }

  /* format + privacy pickers */
  .lgx-fmt-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
  .lgx-fmt { position: relative; text-align: left; padding: 14px 14px 40px; border-radius: 12px;
    cursor: pointer; background: var(--card-bg); border: 1px solid var(--card-border);
    color: var(--text); font-family: inherit; }
  .lgx-fmt:hover { border-color: rgba(var(--accent-rgb), .5); }
  .lgx-fmt.sel { border-color: var(--accent); background: rgba(var(--accent-rgb), .07); }
  .lgx-fmt img { width: 38px; height: 38px; object-fit: contain; margin-bottom: 8px; }
  .lgx-fmt b { display: block; font-size: .9rem; margin-bottom: 4px; }
  .lgx-fmt span { font-size: .76rem; line-height: 1.45; color: var(--text-dim); }
  .lgx-fmt .tick { position: absolute; left: 14px; bottom: 12px; width: 20px; height: 20px;
    border-radius: 50%; border: 1px solid var(--card-border); display: grid; place-items: center;
    font-size: .7rem; color: transparent; }
  .lgx-fmt.sel .tick { background: var(--accent); border-color: var(--accent); color: #04121a; }
  .lgx-priv { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
  .lgx-priv button { text-align: left; padding: 14px; border-radius: 12px; cursor: pointer;
    background: var(--card-bg); border: 1px solid var(--card-border); color: var(--text); font-family: inherit; }
  .lgx-priv button.sel { border-color: var(--accent); background: rgba(var(--accent-rgb), .07); }
  .lgx-priv b { display: flex; align-items: center; gap: 7px; font-size: .88rem; margin-bottom: 5px; }
  .lgx-priv span { font-size: .76rem; line-height: 1.45; color: var(--text-dim); }

  /* settings rows */
  .lgx-set { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px;
    padding: 14px 0; border-bottom: 1px solid var(--card-border); }
  .lgx-set:last-child { border-bottom: none; }
  .lgx-set .t b { display: block; font-size: .88rem; margin-bottom: 3px; }
  .lgx-set .t span { font-size: .78rem; line-height: 1.5; color: var(--text-dim); }
  .lgx-set .c { flex-shrink: 0; }
  .lgx-set select, .lgx-set input { min-width: 170px; }
.league-btn-request { background: transparent; border-color: rgba(var(--blitz-orange-rgb), .5);
    color: var(--blitz-orange); }
  .lgx-locked { font-size: .72rem; color: var(--text-dim); border: 1px dashed var(--card-border);
    border-radius: 8px; padding: 8px 11px; }

  /* preview rail */
  .lgx-pv { display: flex; align-items: center; gap: 14px; padding: 16px; }
  .lgx-pv img { width: 62px; height: 62px; object-fit: contain; }
  .lgx-pv-name { font-size: 1.1rem; font-weight: 800; margin: 0 0 6px; }
  .lgx-pv-name.empty { color: var(--text-dim); }
  .lgx-pill { display: inline-block; white-space: nowrap; font-size: .64rem; font-weight: 800;
    letter-spacing: .07em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px;
    background: rgba(var(--accent-rgb), .14); color: var(--accent); }
  .lgx-pv-meta { display: flex; gap: 14px; flex-wrap: wrap; font-size: .76rem;
    color: var(--text-dim); padding: 0 16px 14px; }
  .lgx-about { padding: 16px; }
  .lgx-about h4 { margin: 0 0 8px; font-size: .9rem; font-weight: 800; }
  .lgx-about p { margin: 0 0 12px; font-size: .82rem; line-height: 1.55; color: var(--text-dim); }
  .lgx-about ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 9px; }
  .lgx-about li { display: flex; align-items: flex-start; gap: 9px; font-size: .82rem; color: var(--text-dim); }

  /* invite step */
  .lgx-chips { display: flex; flex-wrap: wrap; gap: 8px; padding: 10px; border-radius: 10px;
    background: var(--surface-2); border: 1px solid var(--card-border); min-height: 46px; align-items: center; }
  .lgx-chip { display: inline-flex; align-items: center; gap: 7px; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 999px; padding: 5px 10px; font-size: .8rem; }
  .lgx-chip button { background: none; border: none; color: var(--text-dim); cursor: pointer;
    font-size: .9rem; line-height: 1; padding: 0; font-family: inherit; }
  .lgx-chips input { flex: 1; min-width: 150px; background: none; border: none; color: var(--text);
    font-size: .85rem; outline: none; font-family: inherit; }
  .lgx-sum { display: grid; grid-template-columns: repeat(2, 1fr); gap: 12px; }
  .lgx-sum-cell { padding: 12px 14px; border-radius: 10px; background: var(--surface-2);
    border: 1px solid var(--card-border); }
  .lgx-sum-cell .lgx-eyebrow { display: block; margin-bottom: 5px; }
  .lgx-sum-cell b { font-size: .92rem; }

  /* join: code entry + browse */
  .lgx-code { display: grid; grid-template-columns: minmax(0, 1fr) 260px; }
  .lgx-code-left { padding: 20px; }
  .lgx-code-right { padding: 20px; border-left: 1px solid var(--card-border); display: flex;
    flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 10px; }
  .lgx-code-right .env { font-size: 2.4rem; opacity: .5; }
  .lgx-code-right p { margin: 0; font-size: .78rem; color: var(--text-dim); line-height: 1.5; }
  .lgx-code-row { display: flex; gap: 10px; margin-top: 4px; }
  .lgx-code-row input { flex: 1; letter-spacing: .14em; text-transform: uppercase; }
  .lgx-browse-head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
    flex-wrap: wrap; padding: 18px 20px 14px; border-bottom: 1px solid var(--card-border); }
  .lgx-tools { display: flex; gap: 10px; margin-left: auto; }
  .lgx-search { position: relative; }
  .lgx-search input { padding-right: 34px; min-width: 210px; }
  .lgx-search .mag { position: absolute; right: 11px; top: 50%; transform: translateY(-50%);
    color: var(--text-dim); font-size: .85rem; pointer-events: none; }

  .lgx-th, .lgx-tr { display: grid; grid-template-columns: minmax(0, 2.4fr) 112px 84px 86px 108px 138px;
    align-items: center; gap: 14px; padding: 12px 20px; }
  .lgx-th { font-size: .68rem; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
    color: var(--text-dim); border-bottom: 1px solid var(--card-border); }
  .lgx-tr { border-bottom: 1px solid var(--card-border); }
  .lgx-tr:last-child { border-bottom: none; }
  .lgx-lg { display: flex; align-items: center; gap: 12px; min-width: 0; }
  .lgx-lg img { width: 44px; height: 44px; object-fit: contain; flex-shrink: 0; }
  .lgx-lg .nm { min-width: 0; }
  /* Flex rather than block so the Official badge sits beside the name
     instead of inside the name's own ellipsis - a truncated long league
     name used to eat the badge with it. */
  .lgx-lg .nm b { display: flex; align-items: center; gap: 6px; min-width: 0;
    font-size: .92rem; font-weight: 800; }
  .lgx-lg .nm b .lgx-lg-name { display: block; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; font-size: .92rem; color: var(--text); }
  /* Brand orange, same token and same reasoning as the game card's "Called
     it" badge: this is a Blitz Odds signal, not a generic status colour. */
  .lgx-lg .nm b .lgx-official { display: inline-block; flex-shrink: 0; font-size: .62rem;
    font-weight: 800; letter-spacing: .04em; text-transform: uppercase; line-height: 1;
    padding: 3px 6px; border-radius: 4px; white-space: nowrap;
    color: var(--blitz-orange); border: 1px solid var(--blitz-orange); }
  .lgx-lg .nm span { display: block; font-size: .76rem; color: var(--text-dim); margin-top: 2px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .lgx-vis { display: inline-flex; align-items: center; gap: 5px; font-size: .76rem;
    color: var(--text-dim); white-space: nowrap; }
  .lgx-vis.pub { color: var(--win); }
  .lgx-num { font-variant-numeric: tabular-nums; font-size: .88rem; }
  .lgx-num.full { color: var(--out); font-weight: 700; }
  .lgx-dim { color: var(--text-dim); }
  .lgx-mmeta { display: none; }
  .lgx-empty { padding: 26px 20px; text-align: center; color: var(--text-dim); font-size: .88rem; }
  .lgx-more { display: flex; justify-content: center; padding: 16px; }

  /* owner request queue */
  .lgx-req { display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 14px 20px; border-bottom: 1px solid var(--card-border); }
  .lgx-req:last-child { border-bottom: none; }
  .lgx-req .who { display: flex; align-items: center; gap: 12px; min-width: 0; }
  .lgx-req .av { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
    font-size: 1.1rem; background: var(--surface-2); border: 1px solid var(--card-border); flex-shrink: 0; }
  .lgx-req b { display: block; font-size: .9rem; }
  .lgx-req .when { font-size: .78rem; color: var(--text-dim); }
  .lgx-req .acts { display: flex; gap: 8px; flex-shrink: 0; }
  .lgx-req.settled { opacity: .55; }
  .lgx-badge { display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px;
    border-radius: 999px; background: var(--blitz-orange); color: #0b1220; font-size: .66rem;
    font-weight: 800; margin-left: 6px; }
  [data-theme="light"] .lgx-badge { color: #fff; }

  @media (max-width: 860px) {
    .lgx-layout { grid-template-columns: 1fr; }
    .lgx-rail { position: static; }
    /* The rail explains what's directly above it on a phone - About, What's
       next, How to join and the game-type glossary all restate a card the
       reader has already passed. */
    .lgx-rail .lgx-about, .lgx-rail .lgx-next { display: none; }
    .lgx-two { grid-template-columns: 1fr; gap: 18px; }
    .lgx-fmt-grid { grid-template-columns: repeat(2, 1fr); gap: 10px; }
    .lgx-sum { grid-template-columns: 1fr; }
    .lgx-steps { padding: 14px; }
    .lgx-step .t { display: none; }
    .lgx-rule { margin: 0 8px; }
    .lgx-set { flex-direction: column; gap: 10px; }
    .lgx-set .c, .lgx-set select, .lgx-set input { width: 100%; min-width: 0; }
    /* Invite-code entry collapses onto the browse card instead of owning
       half the first screen; the envelope panel is decoration and goes. */
    .lgx-code { grid-template-columns: 1fr; border-bottom-left-radius: 0; border-bottom-right-radius: 0;
      border-bottom: none; }
    .lgx-code-right { display: none; }
    .lgx-code-left { padding: 14px 16px; }
    .lgx-code-row { gap: 8px; margin-top: 8px; }
    .lgx-code-row input { min-width: 0; }
    .lgx-browse { margin-top: 0 !important; border-top-left-radius: 0; border-top-right-radius: 0; }
    .lgx-browse-head { padding: 14px 16px 12px; }
    .lgx-tools { margin-left: 0; width: 100%; flex-wrap: wrap; gap: 8px; }
    .lgx-search { flex: 1 1 100%; }
    .lgx-search input { min-width: 0; width: 100%; }
    .lgx-tools select { flex: 1 1 0; min-width: 0; }
    .lgx-th { display: none; }
    .lgx-tr { grid-template-columns: 1fr; gap: 10px; padding: 14px 16px; }
    .lgx-tr .lgx-dcell { display: none; }
    .lgx-mmeta { display: flex; gap: 14px; flex-wrap: wrap; align-items: center;
      font-size: .78rem; color: var(--text-dim); }
    .lgx-tr .league-btn { width: 100%; }
    .lgx-req { flex-direction: column; align-items: stretch; }
    .lgx-req .acts .league-btn { flex: 1; }
  }

  .tab-bar {
    display: flex; gap: 28px; margin: 18px 0 0; border-bottom: 1px solid var(--card-border);
  }
  .tab-btn {
    display: flex; align-items: center; gap: 11px; background: none; border: none; cursor: pointer;
    color: var(--text-dim); font-family: inherit; font-size: 0.92rem; font-weight: 700;
    padding: 0 0 14px; position: relative; transition: color 0.15s;
  }
  .tab-btn:hover { color: var(--text); }
  .tab-btn.active { color: var(--text); }
  .tab-btn.active::after {
    content: ""; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
    background: var(--accent); border-radius: 2px;
  }
  .tab-icon {
    width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
    font-size: 13px; line-height: 1; flex-shrink: 0; background: rgba(255,255,255,0.05);
    border: 1px solid var(--card-border); transition: background 0.15s, border-color 0.15s;
  }
  .tab-btn.active .tab-icon { background: rgba(var(--accent-rgb),0.16); border-color: var(--accent); }
  /* Illustrated tab icons.
     The ring + fill that frames an emoji fights the artwork - these icons
     already carry their own outline and a full palette, so the circle reads
     as a second border around a badge that's mostly border. .has-img drops
     it and the active state is carried by the art itself instead: every
     inactive icon is desaturated and dimmed, the current one is full colour.
     That also keeps the bar from being five competing full-colour emblems.

     Slightly larger than the emoji box (28 vs 26) because these are detailed
     raster marks, not glyphs - below ~28px the interior detail stops
     resolving. The label under each one is still what identifies the tab;
     the icon is the accent. */
  /* 40px on desktop, against 26px for the emoji box these replaced. That
     isn't a stylistic preference: at 28px the calendar, notebook, newspaper
     and shield all collapsed into indistinguishable navy-and-teal blobs,
     and the desktop bar - a horizontal row with the label beside the icon,
     not under it - has room the phone's bottom bar doesn't. The label grows
     slightly with it so the icon doesn't overpower it. Both are overridden
     in the mobile block below, which keeps its own smaller sizing. */
  /* Count of games in progress, pinned to the Games tab's icon. Absolute,
     so it hangs off the corner without widening the button and shifting
     the other three tabs sideways the moment a game kicks off.
     The 2px ring is the bar's own background rather than a border colour:
     the badge overlaps artwork that is itself full-colour, and without a
     gap between the two the red pill reads as part of the illustration. */
  .tab-icon { position: relative; }
  .tab-live-count {
    position: absolute; top: -3px; right: -6px; min-width: 16px; height: 16px; padding: 0 4px;
    border-radius: 999px; background: var(--live); color: #fff;
    font-size: 0.6rem; font-weight: 800; line-height: 1; font-variant-numeric: tabular-nums;
    display: flex; align-items: center; justify-content: center;
    box-shadow: 0 0 0 2px var(--bg);
    animation: pulse 1.4s infinite;
  }
  /* White on --live clears AA at this weight in both themes (dark #ff3b30 →
     3.4:1, light #d92d20 → 4.9:1); it's bold 0.6rem text, which WCAG treats
     as large only at 14pt+, so the dark-theme figure is the one to watch if
     --live ever moves. The count is never the only signal anyway - the
     aria-label carries it, and the games themselves are one tap away. */
  @media (prefers-reduced-motion: reduce) {
    .tab-live-count { animation: none; }
  }
  /* Against the translucent mobile bar rather than the page. Both resolve
     to something close, but the fixed bar is blurred over the list behind
     it, so matching --bg there leaves a faint halo. */
  @media (max-width: 720px) {
    .tab-live-count { box-shadow: 0 0 0 2px var(--card-bg); }
  }
  .tab-icon.has-img { width: 40px; height: 40px; background: none; border-color: transparent; }
  .tab-btn.active .tab-icon.has-img { background: none; border-color: transparent; }
  .tab-icon-img {
    width: 100%; height: 100%; object-fit: contain; display: block;
    /* Tuned for the dark theme, which is the default. The artwork is mostly
       dark navy, so desaturating it against a dark bar takes it most of the
       way to invisible - hence the brightness lift, which pulls the inactive
       marks back up off the background without making them read as active.
       The light theme doesn't have that problem and overrides it below. */
    opacity: 0.62; filter: grayscale(0.6) saturate(0.85) brightness(1.3);
    transition: opacity 0.15s, filter 0.15s, transform 0.15s;
  }
  html[data-theme="light"] .tab-icon-img { opacity: 0.55; filter: grayscale(0.65) saturate(0.85); }
  .tab-btn:hover .tab-icon-img,
  .tab-btn.active .tab-icon-img { opacity: 1; filter: none; }
  .tab-btn.active .tab-icon-img { transform: scale(1.06); }
  /* The desaturate/brighten swap is the only signal these icons carry, and
     it's a transition - so honour a reduced-motion preference by making the
     end states instant rather than animated. The states themselves stay. */
  @media (prefers-reduced-motion: reduce) {
    .tab-icon-img { transition: none; }
    .tab-btn.active .tab-icon-img { transform: none; }
  }
  @media (max-width: 720px) {
    /* Must clear the fixed .tab-bar below, whose height is 84px PLUS
       env(safe-area-inset-bottom) - see its padding rule. A flat value works
       only while the inset is 0; once viewport-fit=cover made env() resolve
       to a real number, a notched iPhone grew the bar to ~118px while this
       stayed at 96, and the last footer line (the Privacy Policy link) ended
       up permanently underneath it - not merely clipped, but unreachable,
       since the page cannot scroll past its own end. The same inset is added
       here so the two move together; the 96px keeps ~12px of clearance. */
    .app { padding-bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
    .tab-bar {
      position: fixed; left: 0; right: 0; bottom: 0; z-index: 500; margin: 0; gap: 0;
      /* Translucent + blurred rather than an opaque fill, so the list reads as
         continuing underneath the bar. The solid card-bg is the fallback for
         browsers without backdrop-filter (they just get today's look). */
      background: var(--card-bg);
      background: color-mix(in srgb, var(--card-bg) 74%, transparent);
      -webkit-backdrop-filter: saturate(180%) blur(22px);
      backdrop-filter: saturate(180%) blur(22px);
      border-bottom: none; border-top: 1px solid var(--card-border);
      /* Extra bottom padding (beyond the safe-area inset) keeps the tap
         targets clear of iOS's edge-swipe gesture zone - without it, taps
         near the bottom of the bar can land on whatever's behind it. */
      padding: 8px 4px calc(14px + env(safe-area-inset-bottom, 0px));
    }
    .tab-btn {
      flex: 1; flex-direction: column; gap: 4px; padding: 8px 2px; font-size: 0.64rem;
      min-height: 48px;
      /* Touch has no hover, so :hover above never fires on a phone and every
         tap felt inert. A brief scale is the feedback instead. */
      transition: transform 0.1s ease;
    }
    .tab-btn:active { transform: scale(0.9); }
    .tab-btn.active::after { content: none; }
    .tab-icon { width: 24px; height: 24px; font-size: 12px; background: none; border-color: transparent; }
    .tab-btn.active .tab-icon { background: rgba(var(--accent-rgb),0.16); border-color: transparent; }
    /* 30px here rather than the desktop 28: the bottom bar is the primary
       navigation on a phone and these marks need the extra couple of pixels
       to stay readable. It fits inside the existing 48px min tap target -
       30 + 4px gap + a 0.64rem label is ~48. No accent pill behind the
       active one; the colour swap already carries it, and a tinted circle
       under a full-colour badge just muddies both. */
    .tab-icon.has-img { width: 30px; height: 30px; }
    .tab-btn.active .tab-icon.has-img { background: none; }
    .tab-btn.active { color: var(--accent); }
  }

  .tab-placeholder { text-align: center; color: var(--text-dim); font-size: 0.85rem; padding: 40px 16px; }


  /* ---------------------------------------------------------------------
     Playbook tab (replaces the old Hot Picks layout; the .hot-picks-* and
     .bet-card rules below are still used by the Betting Angles section and
     the track record panel, so they stay).

     NOTHING HERE IS position:fixed OR position:sticky. On a phone the tab
     bar is a fixed bottom bar at z-index 500 (see .tab-bar's media query),
     and a sticky "Submit sheet" CTA would sit underneath it - exactly the
     bug 63a8128d4 had to fix on the Add to League sheet. Every submit
     button in this tab stays in normal document flow. If a sticky CTA is
     ever wanted here it has to clear z-index 500 and carry its own
     env(safe-area-inset-bottom) padding.
     --------------------------------------------------------------------- */
  .pb-view { margin-top: 18px; }
  .pb-eyebrow {
    font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.11em;
    color: var(--text-dim); font-weight: 800;
  }
  .pb-title { font-size: 1.55rem; font-weight: 850; margin: 5px 0 6px; letter-spacing: -0.02em; }
  .pb-built { font-size: 0.79rem; color: var(--text-dim); margin: 0; }
  .pb-built b { color: var(--text); font-weight: 700; }

  .pb-standing {
    display: flex; margin: 16px 0 4px;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px;
    overflow: hidden;
  }
  .pb-standing div { flex: 1; padding: 11px 12px; border-left: 1px solid var(--card-border); }
  .pb-standing div:first-child { border-left: 0; }
  .pb-standing .n { font-size: 1.2rem; font-weight: 850; display: block; line-height: 1.15; font-variant-numeric: tabular-nums; }
  .pb-standing .l { font-size: 0.64rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.06em; font-weight: 700; }
  .pb-standing .warnish .n { color: var(--warn); }

  /* ---- sheet card ---- */
  .pb-sheet {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; margin-bottom: 14px; overflow: hidden;
  }
  .pb-sheet-head {
    display: flex; align-items: center; gap: 12px; padding: 12px 14px; width: 100%;
    background: none; border: 0; font-family: inherit; color: var(--text);
    cursor: pointer; text-align: left;
  }
  /* The same icon+wordmark lockup every other league surface uses (Create a
     League, league search, the Home dashboard rows, the invite email) rather
     than a text chip of its own - see LEAGUE_FORMAT_ICON_URLS for why these
     are never shrunk to icon size. Matches .hd-row-badge's sizing. */
  .pb-fmt-lockup { width: 62px; height: 50px; flex: none; object-fit: contain; }
  .pb-sheet-headmain { flex: 1; min-width: 0; }
  .pb-sheet-name { display: block; font-weight: 800; font-size: 0.98rem; line-height: 1.2; }
  .pb-sheet-meta { display: block; font-size: 0.72rem; color: var(--text-dim); margin-top: 2px; }
  .pb-sheet-meta .dot { opacity: 0.5; margin: 0 5px; }
  .pb-sheet-status { text-align: right; font-size: 0.7rem; font-weight: 700; flex: none; }
  .pb-sheet-status .sub { font-weight: 600; opacity: 0.8; display: block; }
  .pb-status-todo { color: var(--warn); }
  .pb-status-done { color: var(--win); }
  .pb-chev { color: var(--text-dim); transition: transform 0.18s; flex: none; margin-left: 8px; display: inline-flex; }
  .pb-sheet.is-closed .pb-chev { transform: rotate(-90deg); }
  .pb-sheet-body { border-top: 1px solid var(--card-border); }

  /* ---- sheet body lead strip + "how to read a row" panel ---- */
  .pb-sheet-lead {
    display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap;
    padding: 9px 14px; background: var(--surface-2); border-bottom: 1px solid var(--card-border);
    font-size: 0.74rem; color: var(--text-dim);
  }
  .pb-sheet-lead b { color: var(--text); font-weight: 750; }
  .pb-lead-key { display: flex; align-items: center; gap: 12px; font-size: 0.66rem; }
  .pb-lead-key > span { display: inline-flex; align-items: center; gap: 5px; }
  .pb-lead-key i {
    width: 24px; height: 6px; border-radius: 3px; display: inline-block;
    background: linear-gradient(90deg, var(--accent) 60%, rgba(var(--accent-rgb),0.18) 60%);
  }
  .pb-lead-key u {
    width: 3px; height: 12px; border-radius: 2px; background: var(--blitz-orange);
    display: inline-block; margin: 0 5px;
  }
  .pb-info-btn {
    flex: none; width: 18px; height: 18px; border-radius: 50%; padding: 0; cursor: pointer;
    background: none; border: 1px solid var(--card-border); color: var(--text-dim);
    font-family: inherit; font-size: 0.62rem; font-weight: 900; line-height: 1;
    display: inline-grid; place-items: center;
  }
  .pb-info-btn:hover, .pb-info-btn[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); }
  .pb-info-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  .pb-explain {
    padding: 12px 14px 14px; background: var(--surface-2);
    border-bottom: 1px solid var(--card-border); font-size: 0.78rem; line-height: 1.55;
  }
  .pb-explain h4 {
    margin: 0 0 8px; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.09em;
    color: var(--text-dim); font-weight: 850;
  }
  .pb-explain dl { margin: 0; display: grid; grid-template-columns: 92px minmax(0,1fr); gap: 7px 12px; align-items: baseline; }
  .pb-explain dt { display: flex; align-items: center; gap: 7px; font-weight: 800; font-size: 0.72rem; white-space: nowrap; }
  .pb-explain dd { margin: 0; color: var(--text-dim); }
  .pb-explain dd b { color: var(--text); font-weight: 700; }
  .pb-explain .swatch-bar { width: 26px; height: 6px; border-radius: 3px; background: var(--accent); flex: none; }
  .pb-explain .swatch-notch { width: 3px; height: 13px; border-radius: 2px; background: var(--blitz-orange); flex: none; margin: 0 11px; }
  .pb-explain .swatch-num { font-variant-numeric: tabular-nums; color: var(--blitz-orange); font-weight: 800; flex: none; width: 26px; text-align: center; }
  .pb-explain .swatch-pill {
    font-size: 0.58rem; font-weight: 800; color: var(--edge-b); flex: none;
    border: 1px solid var(--edge-b); border-radius: 999px; padding: 1px 6px;
  }
  .pb-explain .swatch-path {
    width: 26px; height: 6px; border-radius: 3px; flex: none;
    background: repeating-linear-gradient(90deg, var(--accent) 0 6px, transparent 6px 10px);
  }
  .pb-explain .caveat { margin: 10px 0 0; color: var(--text-dim); border-top: 1px solid var(--card-border); padding-top: 9px; }

  /* ---- confidence ladder / straight-up tiles ----
     One row component for both formats. The confidence ladder has to read
     1..N top to bottom, so it splits into two columns (column flow, so the
     left column is 16..9); straight-up has no ranking to preserve, so it
     tiles in normal row flow and can go three wide. Below 900px both are a
     single column. */
  .pb-grid { display: grid; grid-template-columns: 1fr; }
  /* Column dividers are tied to the breakpoint that creates the column, so a
     single-column phone layout never inherits a stray left border. */
  @media (min-width: 900px) {
    .pb-grid.is-ladder { grid-template-columns: 1fr 1fr; grid-auto-flow: column; }
    .pb-grid.is-ladder > .pb-row2.col-2 { border-left: 1px solid var(--card-border); }
    .pb-grid.is-tiles { grid-template-columns: 1fr 1fr; }
    .pb-grid.is-tiles > .pb-row2.t2-right { border-left: 1px solid var(--card-border); }
  }
  @media (min-width: 1240px) {
    .pb-grid.is-tiles { grid-template-columns: 1fr 1fr 1fr; }
    /* the 2-up divider would land mid-column once there are three */
    .pb-grid.is-tiles > .pb-row2.t2-right { border-left: 0; }
    .pb-grid.is-tiles > .pb-row2.t3-right { border-left: 1px solid var(--card-border); }
  }

  .pb-row2 {
    display: grid; grid-template-columns: 40px minmax(0,1fr) auto;
    align-items: center; column-gap: 10px; row-gap: 3px;
    padding: 9px 14px; border-bottom: 1px solid var(--card-border);
  }
  .pb-row2.no-pts { grid-template-columns: minmax(0,1fr) auto; }
  .pb-row2:hover { background: rgba(var(--accent-rgb),0.045); }
  .pb-row2.is-locked { opacity: 0.5; }

  .pb-pts {
    grid-column: 1; grid-row: 1 / span 2; text-align: center;
    font-size: 1.05rem; font-weight: 850; font-variant-numeric: tabular-nums; line-height: 1;
  }
  .pb-pts small { display: block; font-size: 0.5rem; color: var(--text-dim); font-weight: 700; letter-spacing: 0.05em; margin-top: 2px; }
  .pb-row2.is-top .pb-pts { color: var(--blitz-orange); }

  .pb-match { grid-column: 2; grid-row: 1; display: flex; align-items: center; gap: 6px; min-width: 0; font-size: 0.88rem; }
  .pb-row2.no-pts .pb-match { grid-column: 1; }
  .pb-match .pick { font-weight: 800; letter-spacing: -0.01em; }
  .pb-match .at { font-size: 0.7rem; color: var(--text-dim); font-weight: 700; margin: 0 1px; }
  .pb-match .opp { font-weight: 650; color: var(--text-dim); }
  .pb-match .opp img { opacity: 0.6; }

  .pb-meter { grid-column: 2; grid-row: 2; display: flex; align-items: center; gap: 8px; min-width: 0; }
  .pb-row2.no-pts .pb-meter { grid-column: 1; }
  /* One probability axis carrying two marks: the teal fill is the model's
     number, the notch is the market's. The notch overhangs the track, so no
     overflow:hidden here. */
  .pb-bar {
    position: relative; flex: 1; min-width: 56px; height: 7px; border-radius: 4px;
    background: rgba(var(--accent-rgb),0.15);
  }
  .pb-bar i { display: block; height: 100%; background: var(--accent); border-radius: 4px; }
  .pb-bar u {
    position: absolute; top: -3px; bottom: -3px; width: 3px; border-radius: 2px;
    background: var(--blitz-orange); transform: translateX(-1.5px);
  }
  .pb-sub2 { font-size: 0.7rem; color: var(--text-dim); white-space: nowrap; }
  .pb-sub2 .sep { opacity: 0.45; margin: 0 4px; }
  .pb-sub2 .dog { color: var(--blitz-orange); font-weight: 800; }

  .pb-right {
    grid-column: 3; grid-row: 1 / span 2; display: flex; flex-direction: column;
    align-items: flex-end; justify-content: center; gap: 1px; min-width: 58px;
  }
  .pb-row2.no-pts .pb-right { grid-column: 2; }
  .pb-pct { font-size: 1rem; font-weight: 850; font-variant-numeric: tabular-nums; line-height: 1.1; }
  .pb-delta { font-size: 0.64rem; font-weight: 750; color: var(--text-dim); font-variant-numeric: tabular-nums; }
  .pb-delta.wide { color: var(--blitz-orange); }

  .pb-have {
    font-size: 0.62rem; font-weight: 800; color: var(--win); letter-spacing: 0.04em;
    border: 1px solid rgba(var(--win-rgb),0.35); background: rgba(var(--win-rgb),0.12);
    border-radius: 999px; padding: 1px 7px; margin-left: 6px;
  }
  .pb-have.is-diff { color: var(--warn); border-color: rgba(var(--warn-rgb),0.35); background: rgba(var(--warn-rgb),0.12); }

  /* ---- ats plays ---- */
  .pb-play { display: flex; align-items: flex-start; gap: 10px; padding: 11px 14px; border-bottom: 1px solid var(--card-border); }
  .pb-play:last-child { border-bottom: 0; }
  .pb-play-rank { width: 34px; flex: none; text-align: center; font-size: 0.7rem; font-weight: 800; color: var(--text-dim); padding-top: 3px; font-variant-numeric: tabular-nums; }
  .pb-play-main { flex: 1; min-width: 0; }
  .pb-play-pick { display: flex; align-items: center; gap: 7px; font-weight: 800; font-size: 0.9rem; }
  .pb-play-note { font-size: 0.74rem; color: var(--text-dim); margin-top: 3px; line-height: 1.45; }
  .pb-edge-pill {
    display: inline-block; background: rgba(var(--win-rgb),0.14); color: var(--pill-good-text);
    border: 1px solid rgba(var(--win-rgb),0.3); border-radius: 999px;
    font-size: 0.63rem; font-weight: 800; padding: 1px 7px; margin-left: 6px; white-space: nowrap;
  }
  .pb-play-val { flex: none; text-align: right; width: 54px; }
  .pb-play-val .n { font-size: 1rem; font-weight: 850; display: block; line-height: 1.1; font-variant-numeric: tabular-nums; }
  .pb-play-val .l { font-size: 0.54rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 700; }
  .pb-noedge { padding: 10px 14px; font-size: 0.74rem; color: var(--text-dim); display: flex; align-items: flex-start; gap: 8px; }
  .pb-noedge .dash { color: var(--lose); font-weight: 800; }
  .pb-noedge b { color: var(--text); font-weight: 700; }

  /* ---- survivor ---- */
  .pb-surv-lead { padding: 14px 14px 0; font-size: 0.8rem; color: var(--text-dim); }
  .pb-surv-pick { display: flex; align-items: center; gap: 11px; padding: 12px 14px; }
  .pb-surv-pick .who { flex: 1; min-width: 0; }
  .pb-surv-pick .name { font-size: 1rem; font-weight: 850; }
  .pb-surv-pick .vs { font-size: 0.74rem; color: var(--text-dim); margin-top: 2px; }
  .pb-surv-pick .pct { font-size: 1.35rem; font-weight: 850; color: var(--warn); font-variant-numeric: tabular-nums; }
  .pb-why {
    margin: 0 14px 12px; padding: 11px 12px;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 10px;
    font-size: 0.79rem; line-height: 1.5;
  }
  .pb-why .h {
    font-size: 0.61rem; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--warn); font-weight: 850; margin-bottom: 5px;
  }
  .pb-why b { font-weight: 750; }
  .pb-holds { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 14px 14px; }
  .pb-hold {
    display: flex; align-items: center; gap: 6px;
    background: var(--surface-2); border: 1px dashed var(--card-border);
    border-radius: 999px; padding: 4px 10px 4px 6px; font-size: 0.71rem; color: var(--text-dim);
  }
  .pb-hold b { color: var(--text); font-weight: 750; }


  /* ---- survivor path (the season plan, not just this week) ----
     Horizontally scrollable inside its own container so a 18-week plan
     never drags the page sideways on a phone. */
  .pb-path-scroll { overflow-x: auto; padding: 4px 14px 10px; -webkit-overflow-scrolling: touch; }
  .pb-path { display: flex; align-items: flex-end; min-width: min-content; }
  .pb-node { flex: none; width: 52px; text-align: center; position: relative; padding-bottom: 24px; }
  .pb-node .wk { font-size: 0.55rem; color: var(--text-dim); font-weight: 800; letter-spacing: 0.05em; margin-bottom: 5px; }
  .pb-node .crest { position: relative; z-index: 1; display: flex; justify-content: center; }
  .pb-node .pct { font-size: 0.62rem; color: var(--text-dim); font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
  /* the rail joining consecutive weeks, drawn behind the crests */
  .pb-node::before {
    content: ""; position: absolute; left: -50%; right: 50%; top: 30px; height: 2px;
    background: var(--warn); opacity: 0.32;
  }
  .pb-node:first-child::before { display: none; }
  .pb-node.is-later::before { background: var(--lose); opacity: 0.32; }
  .pb-node.is-later .crest { opacity: 0.78; }
  /* running survival probability, decaying left to right */
  .pb-node .surv { position: absolute; left: 8px; right: 8px; bottom: 8px; height: 4px; background: var(--surface-2); border-radius: 2px; overflow: hidden; }
  .pb-node .surv i { display: block; height: 100%; background: var(--warn); opacity: 0.8; }
  .pb-node.is-now { width: 66px; }
  .pb-node.is-now .wk { color: var(--warn); }
  .pb-node.is-now .pct { color: var(--text); font-weight: 800; font-size: 0.7rem; }
  .pb-node.is-now .crest img { box-shadow: 0 0 0 3px rgba(var(--warn-rgb),0.18); border-radius: 50%; }
  .pb-path-foot {
    display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
    padding: 0 14px 12px; font-size: 0.74rem; color: var(--text-dim);
  }
  .pb-path-foot b { color: var(--text); font-weight: 750; }
  /* ---- sheet footer ---- */
  .pb-foot {
    display: flex; gap: 8px; padding: 12px 14px; flex-wrap: wrap;
    border-top: 1px solid var(--card-border); background: var(--surface-2);
  }
  .pb-btn {
    appearance: none; font-family: inherit; cursor: pointer; border-radius: 10px;
    font-size: 0.83rem; font-weight: 800; padding: 10px 14px; border: 1px solid transparent;
  }
  .pb-btn:disabled { opacity: 0.55; cursor: default; }
  /* Sized to their label on desktop - flex:1 in a 1180px footer turned the
     submit button into a full-bleed slab. The stretch only earns its keep as
     a thumb target, so it's scoped to the phone breakpoint, where the two
     buttons stack rather than sharing a row (sharing one wrapped the primary
     label onto two lines at 390px). */
  .pb-btn-primary { background: var(--accent); color: #05221f; flex: 0 0 auto; padding: 10px 22px; }
  .pb-btn-warn { background: var(--warn); color: #241500; flex: 0 0 auto; padding: 10px 22px; }
  .pb-btn-ghost { background: transparent; border-color: var(--card-border); color: var(--text-dim); }
  .pb-btn-ghost:hover { color: var(--text); border-color: rgba(var(--accent-rgb),0.4); }
  .pb-foot-msg { flex-basis: 100%; font-size: 0.74rem; margin: 2px 0 0; }
  .pb-foot-msg.is-error { color: var(--pill-bad-text); }
  .pb-foot-msg.is-ok { color: var(--pill-good-text); }
  @media (max-width: 700px) {
    .pb-fmt-lockup { width: 48px; height: 40px; }
    /* the lockup already reads CONFIDENCE / ATS / SURVIVOR / STRAIGHT-UP, and
       repeating it in the meta line is what wrapped it onto two lines at 390px */
    .pb-fmt-word { display: none; }
    .pb-btn-primary, .pb-btn-warn, .pb-btn-ghost { flex: 1 0 100%; }
    .pb-explain dl { grid-template-columns: 1fr; gap: 2px; }
    .pb-explain dd { margin-bottom: 6px; }
  }


  /* ---- Playbook preview (signed out, or signed in with no leagues) ----
     A working preview of the real sheets rather than a plain list, because
     this is the acquisition surface: a straight-up list of teams and
     percentages is something the Games tab already does better, and shows
     nothing of what the tab is actually for. */
  .pbp-fmt-tabs { display: flex; gap: 6px; margin: 18px 0 4px; flex-wrap: wrap; }
  .pbp-fmt-tab {
    background: var(--card-bg); border: 1px solid var(--card-border); color: var(--text-dim);
    border-radius: 10px; padding: 9px 15px; font-family: inherit; font-size: 0.82rem;
    font-weight: 700; cursor: pointer; text-align: left;
  }
  .pbp-fmt-tab b { display: block; font-size: 0.82rem; font-weight: 800; }
  .pbp-lbl-short { display: none; }
  .pbp-fmt-tab > span { display: block; font-size: 0.68rem; font-weight: 600; opacity: 0.8; margin-top: 1px; }
  .pbp-fmt-tab:hover { color: var(--text); border-color: rgba(var(--accent-rgb),0.4); }
  .pbp-fmt-tab.active { background: rgba(var(--accent-rgb),0.14); color: var(--accent); border-color: var(--accent); }

  .pbp-panel { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px; overflow: hidden; margin-top: 14px; }
  .pbp-panel-head { display: flex; align-items: baseline; gap: 10px; padding: 13px 16px; border-bottom: 1px solid var(--card-border); flex-wrap: wrap; }
  .pbp-panel-title { font-weight: 800; font-size: 0.95rem; }
  .pbp-panel-note { font-size: 0.75rem; color: var(--text-dim); }
  .pbp-panel-note b { color: var(--text); font-weight: 700; }

  .pbp-thead {
    display: grid; gap: 12px; padding: 8px 16px; border-bottom: 1px solid var(--card-border);
    font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.07em;
    color: var(--text-dim); font-weight: 800; background: var(--surface-2);
  }
  .pbp-row { display: grid; gap: 12px; padding: 11px 16px; border-bottom: 1px solid var(--card-border); align-items: center; }
  .pbp-row:last-child { border-bottom: 0; }
  .pbp-thead.is-su,   .pbp-row.is-su   { grid-template-columns: 34px minmax(190px,1.5fr) 128px 1fr 92px; }
  .pbp-thead.is-conf, .pbp-row.is-conf { grid-template-columns: 46px minmax(190px,1.4fr) 116px 1fr 84px; }
  .pbp-thead.is-ats,  .pbp-row.is-ats  { grid-template-columns: 34px minmax(180px,1.3fr) 104px 104px 76px 1fr 84px; }
  .pbp-num-col { text-align: right; }

  .pbp-rank { font-size: 0.78rem; font-weight: 800; color: var(--text-dim); text-align: center; }
  .pbp-pts { text-align: center; }
  .pbp-pts b { font-size: 1.05rem; font-weight: 850; color: var(--blitz-orange); display: block; line-height: 1.1; }
  .pbp-pts span { font-size: 0.5rem; color: var(--text-dim); font-weight: 800; letter-spacing: 0.05em; }

  .pbp-match { min-width: 0; }
  .pbp-match-line { display: flex; align-items: center; gap: 7px; font-weight: 750; font-size: 0.9rem; }
  .pbp-match-line .pick { font-weight: 850; }
  .pbp-match-line .vs { color: var(--text-dim); font-weight: 600; font-size: 0.82rem; }
  .pbp-when { font-size: 0.68rem; color: var(--text-dim); margin-top: 3px; }
  .pbp-when .net { color: var(--edge-b); font-weight: 700; }

  .pbp-cell-label { font-size: 0.82rem; font-weight: 750; }
  .pbp-cell-sub { font-size: 0.67rem; color: var(--text-dim); margin-top: 2px; }
  .pbp-big { font-size: 1.05rem; font-weight: 850; text-align: right; font-variant-numeric: tabular-nums; }
  .pbp-big small { display: block; font-size: 0.52rem; color: var(--text-dim); font-weight: 800; letter-spacing: 0.05em; text-transform: uppercase; margin-top: 1px; }

  .pbp-why { font-size: 0.74rem; color: var(--text-dim); line-height: 1.4; min-width: 0; }
  .pbp-why b { color: var(--text); font-weight: 700; }
  .pbp-why.is-none { opacity: 0.5; }
  .pbp-tag { display: inline-block; border-radius: 999px; font-size: 0.6rem; font-weight: 800; padding: 1px 7px; border: 1px solid; white-space: nowrap; }
  .pbp-tag-out { color: var(--pill-bad-text); border-color: rgba(var(--out-rgb),0.4); background: rgba(var(--out-rgb),0.12); }
  .pbp-tag-q { color: var(--pill-questionable-text); border-color: rgba(var(--warn-rgb),0.4); background: rgba(var(--warn-rgb),0.12); }
  .pbp-tag-edge { color: var(--pill-good-text); border-color: rgba(var(--win-rgb),0.35); background: rgba(var(--win-rgb),0.12); }

  /* The wrapper exists only so these cells can reflow together on a phone;
     on desktop they stay independent grid children. */
  .pbp-meta { display: contents; }
  .pbp-mob-k { display: none; }

  /* Locked rows: the same blurred-peek treatment the survivor path uses, so
     the sheet visibly keeps going instead of hitting a wall. Rendered from
     the real next rows, not placeholder bars - the silhouette of a 13-point
     game or another edge pill is part of what's being previewed. Masked so
     it fades out rather than ending on a hard edge. */
  .pbp-peek {
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 30%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 30%, transparent 100%);
    pointer-events: none; user-select: none;
  }
  .pbp-peek .pbp-row { filter: blur(4px); opacity: 0.5; }

  /* The gate floats at the top of the blurred region rather than sitting
     below it. Underneath it the blur keeps going and fades out, so the
     "there's more here" signal survives while the call to action is right
     where the content stops being readable - no scrolling past the thing
     you're being asked to unlock. min-height keeps the container taller than
     the overlay when only a row or two is left to blur. */
  .pbp-locked { position: relative; min-height: 128px; }
  .pbp-gate {
    padding: 16px; display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 12px;
  }
  /* The overlay behaviour is scoped to .pbp-locked rather than living on
     .pbp-gate itself. The survivor sheet uses the same gate without a blurred
     tail to sit on, and while it was unscoped that gate had no positioned
     ancestor - so it positioned against the viewport and landed on top of the
     site header. Any future gate outside a .pbp-locked wrapper now stays in
     normal flow instead of flying to the top of the page. */
  .pbp-locked > .pbp-gate {
    position: absolute; top: 10px; left: 12px; right: 12px; z-index: 2;
    box-shadow: 0 10px 26px rgba(0,0,0,0.45);
  }
  .pbp-gate-main { flex: 1; min-width: 240px; }
  .pbp-gate-title { font-weight: 800; font-size: 0.9rem; }
  .pbp-gate-sub { font-size: 0.76rem; color: var(--text-dim); margin-top: 3px; }
  .pbp-gate-actions { display: flex; gap: 8px; flex-wrap: wrap; }
  /* The same .pbp-gate shell reused inside a game card's Full Details panel.
     Playbook's version sits in a wide tab layout; here it sits in a 390px
     card, where the side-by-side title/actions row leaves the two buttons
     ~120px each and wraps "Create a free account" onto three lines. Stacking
     is the only change - colours, radius and copy stay identical, because the
     point of reusing the component is that a reader meets one gate, not two
     that almost match. */
  .full-details-panel .pbp-gate,
  .game-page-section .pbp-gate { flex-direction: column; align-items: stretch; gap: 12px; }
  .full-details-panel .pbp-gate .pbp-gate-actions,
  .game-page-section .pbp-gate .pbp-gate-actions { flex-wrap: nowrap; }
  .full-details-panel .pbp-gate .pb-btn,
  .game-page-section .pbp-gate .pb-btn { flex: 1 1 auto; padding: 10px 8px; }
  /* Flags the section heading of anything currently gated. Deliberately the
     tinted treatment rather than a filled pill: filled orange is reserved for
     a badge stating that someone HAS something (see .notif-channel-tag.pro),
     and this says the opposite. */
  .gate-flag {
    display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
    background: rgba(253,127,1,0.16); color: var(--blitz-orange);
    font-size: 0.6rem; font-weight: 900; letter-spacing: 0.4px; vertical-align: 1px;
    text-transform: uppercase;
  }
  html[data-theme="light"] .gate-flag { background: rgba(179,89,0,0.12); }
  /* The write-up teaser. Line-clamped rather than faded: a gradient has to
     match whatever surface sits behind it, and .edge-detail is a tinted panel
     on a card, so a fade needs four theme-specific backdrops and still washes
     out the visible line at desktop width where the lead sentence is short
     enough not to overflow. */
  /* A cap, not the truncation mechanism - rationaleTeaser() decides what the
     teaser says, by sentence. This only stops an unusually long pair of
     sentences from filling a phone screen before the gate under it. */
  .edge-teaser {
    display: -webkit-box; -webkit-line-clamp: 5; -webkit-box-orient: vertical; overflow: hidden;
  }

  /* survivor path in the preview */
  .pbp-path-wrap { padding: 16px; }
  .pbp-path-scroll { overflow-x: auto; padding-bottom: 6px; -webkit-overflow-scrolling: touch; }
  .pbp-path { display: flex; align-items: flex-end; min-width: min-content; }
  .pbp-node { flex: none; width: 84px; text-align: center; position: relative; padding-bottom: 22px; }
  .pbp-node .wk { font-size: 0.55rem; color: var(--text-dim); font-weight: 800; letter-spacing: 0.05em; margin-bottom: 5px; }
  .pbp-node .crest { position: relative; z-index: 1; display: flex; justify-content: center; }
  .pbp-node .p { font-size: 0.66rem; color: var(--text-dim); font-weight: 700; margin-top: 4px; font-variant-numeric: tabular-nums; }
  .pbp-node .opp { font-size: 0.6rem; color: var(--lose); margin-top: 1px; }
  .pbp-node::before { content: ""; position: absolute; left: -50%; right: 50%; top: 32px; height: 2px; background: var(--warn); opacity: 0.3; }
  .pbp-node:first-child::before { display: none; }
  .pbp-node.is-later .crest { opacity: 0.8; }
  .pbp-node.is-later::before { background: var(--lose); opacity: 0.3; }
  .pbp-node .surv { position: absolute; left: 10px; right: 10px; bottom: 6px; height: 4px; background: var(--surface-2); border-radius: 2px; overflow: hidden; }
  .pbp-node .surv i { display: block; height: 100%; background: var(--warn); opacity: 0.8; }
  .pbp-node.is-now .wk { color: var(--warn); }
  .pbp-node.is-now .p { color: var(--text); font-weight: 850; font-size: 0.74rem; }
  .pbp-node.is-locked .crest { filter: blur(5px); opacity: 0.35; }
  .pbp-node.is-locked .p, .pbp-node.is-locked .opp { filter: blur(4px); opacity: 0.4; }
  .pbp-path-foot { font-size: 0.76rem; color: var(--text-dim); padding: 6px 16px 2px; }
  .pbp-path-foot b { color: var(--text); font-weight: 750; }

  @media (max-width: 760px) {
    .pbp-thead { display: none; }
    .pbp-row.is-su, .pbp-row.is-conf, .pbp-row.is-ats {
      grid-template-columns: 38px minmax(0,1fr) auto; row-gap: 6px; column-gap: 10px; padding: 12px 14px;
    }
    .pbp-row .pbp-rank, .pbp-row .pbp-pts { grid-column: 1; grid-row: 1; }
    .pbp-row .pbp-match { grid-column: 2; grid-row: 1; }
    .pbp-row .pbp-big { grid-column: 3; grid-row: 1; }
    /* The numbers reflow onto their own line rather than being hidden - the
       model's line, the market's, and the edge between them are the reason
       to read the row at all. */
    .pbp-row .pbp-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px; grid-column: 2 / -1; grid-row: 2; }
    /* The confidence and straight-up rows wrap their cells in a div so they
       stay one grid child on desktop; without this it stays a block here and
       the line and the O/U stack onto two rows for no reason. */
    .pbp-row .pbp-meta > div { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; }
    .pbp-row .pbp-meta .pbp-cell-sub { display: inline; margin: 0; }
    .pbp-row .pbp-why { grid-column: 2 / -1; grid-row: 3; }
    .pbp-mob-k { display: inline; font-size: 0.58rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); font-weight: 800; margin-right: 3px; }
    .pbp-big { font-size: 1rem; }
    .pbp-big small { font-size: 0.5rem; }
    /* The panel note is useful but it was eating four lines before any data
       showed up. */
    .pbp-panel-head { padding: 11px 14px; gap: 4px; }
    .pbp-panel-note { font-size: 0.72rem; line-height: 1.4; }
    /* The four type buttons wrap into 2x2 on a phone, but as flex items each
       sized to its own text, so "Against the Spread" was wider than
       "Confidence" and the second column started at a different x on each
       row. An explicit two-column grid makes all four the same width, and
       grid-auto-rows keeps them the same height too - the survivor
       description wraps to two lines at 360px and without it that row sat
       taller than the one above. */
    .pbp-fmt-tabs { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; grid-auto-rows: 1fr; }
    .pbp-fmt-tab { padding: 8px 10px; }
    .pbp-fmt-tab b { font-size: 0.78rem; }
    .pbp-fmt-tab > span { font-size: 0.63rem; }
    .pbp-lbl-full { display: none; }
    .pbp-lbl-short { display: inline; }
    .pbp-gate { padding: 14px; gap: 12px; }
    /* .pb-btn-primary/.pb-btn-ghost go full-width at this breakpoint for the
       sheet footer, where two long labels genuinely need their own rows. The
       gate's pair is short enough to share one, and stacking them pushed
       "Sign in" down a row for no gain, so the gate opts back out: the
       primary sizes to its label and the ghost takes the rest of the row. */
    .pbp-gate-actions { width: 100%; flex-wrap: nowrap; }
    .pbp-gate-actions .pb-btn { padding-left: 9px; padding-right: 9px; font-size: 0.8rem; white-space: nowrap; }
    .pbp-gate-actions .pb-btn-primary { flex: 0 1 auto; }
    .pbp-gate-actions .pb-btn-ghost { flex: 1 1 auto; }
    .pbp-node { width: 76px; }
  }

  /* ---- what changed ---- */
  .pb-chg { display: flex; gap: 11px; padding: 13px 14px; background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px; margin-bottom: 9px; }
  .pb-chg-icon { width: 30px; height: 30px; flex: none; border-radius: 8px; display: grid; place-items: center; font-size: 0.85rem; }
  .pb-ci-line { background: var(--surface-2); border: 1px solid var(--edge-b); }
  .pb-ci-new { background: rgba(var(--accent-rgb),0.13); border: 1px solid rgba(var(--accent-rgb),0.35); }
  .pb-chg-body { flex: 1; min-width: 0; }
  .pb-chg-title { font-weight: 780; font-size: 0.86rem; display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
  .pb-chg-when { font-size: 0.63rem; color: var(--lose); font-weight: 700; margin-left: auto; flex: none; }
  .pb-chg-text { font-size: 0.77rem; color: var(--text-dim); margin-top: 4px; line-height: 1.5; }
  .pb-chg-text b { color: var(--text); font-weight: 700; }
  .pb-chg-affects {
    margin-top: 7px; font-size: 0.68rem; font-weight: 750; display: inline-block;
    background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 999px; padding: 3px 9px; color: var(--text-dim);
  }
  .pb-chg-affects b { color: var(--accent); font-weight: 800; }
  .pb-arrow { color: var(--text-dim); margin: 0 3px; }


  /* ---- What Changed ----
     The old version rendered every line move as the same sentence, so a move
     that flipped a pick looked identical to one that changed nothing. These
     styles exist to make the consequence the thing you read first. */
  .pbc-verdict-strip {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 16px 18px; margin-bottom: 16px; display: flex; gap: 26px; flex-wrap: wrap; align-items: center;
  }
  .pbc-verdict-strip .lead { flex: 1; min-width: 260px; font-size: 0.86rem; line-height: 1.5; color: var(--text-dim); }
  .pbc-verdict-strip .lead b { color: var(--text); font-weight: 800; }
  .pbc-stat { text-align: center; }
  .pbc-stat .n { font-size: 1.45rem; font-weight: 850; line-height: 1.1; display: block; font-variant-numeric: tabular-nums; }
  .pbc-stat .l { font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); font-weight: 800; }
  .pbc-stat.is-warn .n { color: var(--warn); }
  .pbc-stat.is-good .n { color: var(--pill-good-text); }

  /* Filter chips rather than plain tabs: the counts are the point. A week
     where nothing changed a pick is the most useful answer this tab gives,
     and a bare tab would hide that behind a tap. */
  .pbc-filters { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 4px; }
  .pbc-filt {
    background: var(--card-bg); border: 1px solid var(--card-border); color: var(--text-dim);
    border-radius: 10px; padding: 8px 14px; font-family: inherit; font-size: 0.8rem; font-weight: 700;
    cursor: pointer; display: flex; align-items: baseline; gap: 8px;
  }
  .pbc-filt:hover:not(:disabled) { color: var(--text); border-color: rgba(var(--accent-rgb),0.4); }
  .pbc-filt.active { background: rgba(var(--accent-rgb),0.14); color: var(--accent); border-color: var(--accent); }
  .pbc-filt .c { font-size: 0.9rem; font-weight: 850; font-variant-numeric: tabular-nums; }
  .pbc-lbl-short { display: none; }
  .pbc-filt:disabled { opacity: 0.45; cursor: default; }
  .pbc-filt.is-zero .c { color: var(--pill-good-text); }

  .pbc-empty {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px;
    padding: 22px 18px; text-align: center; font-size: 0.84rem; color: var(--text-dim);
  }

  .pbc-card {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 14px 16px; margin-bottom: 10px; display: grid; gap: 14px; align-items: center;
    grid-template-columns: minmax(150px,190px) minmax(240px,1fr) minmax(210px,300px);
  }
  .pbc-who { min-width: 0; }
  .pbc-who-line { display: flex; align-items: center; gap: 6px; font-weight: 800; font-size: 0.9rem; flex-wrap: wrap; }
  .pbc-who-line .at { color: var(--text-dim); font-weight: 600; }
  .pbc-who-sub { font-size: 0.68rem; color: var(--text-dim); margin-top: 3px; }

  /* One axis carrying the model's number, the opening line and the current
     one, so "did the market come to us or move away" is readable at a glance.
     Model sits above the rail and the two market marks below, which keeps
     their labels apart when the market lands on top of the model. */
  .pbc-axis { position: relative; height: 92px; }
  .pbc-axis-cap { position: absolute; top: 0; left: 0; font-size: 0.6rem; color: var(--text-dim); font-weight: 700; }
  .pbc-axis-rail { position: absolute; left: 0; right: 0; top: 42px; height: 2px; background: var(--card-border); }
  .pbc-axis-move { position: absolute; top: 41px; height: 4px; border-radius: 2px; }
  .pbc-axis-move.is-toward { background: rgba(var(--out-rgb),0.55); }
  .pbc-axis-move.is-away { background: rgba(var(--win-rgb),0.55); }
  .pbc-mark { position: absolute; transform: translateX(-50%); text-align: center; }
  .pbc-mark i { display: block; width: 2px; background: var(--text-dim); border-radius: 1px; margin: 0 auto; }
  .pbc-mark span { display: block; font-size: 0.55rem; font-weight: 800; letter-spacing: 0.04em; white-space: nowrap; color: var(--text-dim); }
  .pbc-mark.is-model { top: 14px; }
  .pbc-mark.is-model span { color: var(--accent); margin-bottom: 2px; }
  .pbc-mark.is-model i { height: 16px; background: var(--accent); width: 3px; }
  .pbc-mark.is-open, .pbc-mark.is-now { top: 36px; }
  .pbc-mark.is-open i, .pbc-mark.is-now i { height: 14px; }
  .pbc-mark.is-open span, .pbc-mark.is-now span { margin-top: 3px; }
  .pbc-mark.is-now i { background: var(--text); }
  .pbc-mark.is-now span { color: var(--text); }
  .pbc-mark.is-open i { background: var(--lose); }
  /* Dropping the tick here would float the label back up to roughly where
     the normal one sits, so the crowded mark keeps a longer tick instead. */
  .pbc-mark.is-low i { height: 32px; opacity: 0.5; }

  .pbc-verdict { min-width: 0; }
  .pbc-vtag {
    display: inline-block; border-radius: 999px; font-size: 0.62rem; font-weight: 850;
    padding: 2px 9px; border: 1px solid; letter-spacing: 0.02em; margin-bottom: 5px;
  }
  .pbc-vtag.is-join, .pbc-vtag.is-grow { color: var(--pill-good-text); border-color: rgba(var(--win-rgb),0.4); background: rgba(var(--win-rgb),0.13); }
  .pbc-vtag.is-drop { color: var(--pill-bad-text); border-color: rgba(var(--out-rgb),0.4); background: rgba(var(--out-rgb),0.13); }
  .pbc-vtag.is-shrink { color: var(--warn); border-color: rgba(var(--warn-rgb),0.4); background: rgba(var(--warn-rgb),0.13); }
  .pbc-vtag.is-none { color: var(--text-dim); border-color: var(--card-border); background: var(--surface-2); }
  .pbc-vtext { font-size: 0.76rem; color: var(--text-dim); line-height: 1.45; }
  .pbc-vtext b { color: var(--text); font-weight: 750; }

  /* Same blurred-peek treatment the preview sheets use, so the report
     visibly keeps going rather than stopping at a wall. Masked to fade out
     so it reads as "continues" rather than "cut off". */
  .pbc-peek {
    -webkit-mask-image: linear-gradient(180deg, #000 0%, #000 30%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 0%, #000 30%, transparent 100%);
    pointer-events: none; user-select: none;
  }
  /* Covers both things this container wraps: change cards and angle rows. */
  .pbc-peek .pbc-card, .pbc-peek .pba-row { filter: blur(4px); opacity: 0.5; }

  .pbc-locked { position: relative; min-height: 132px; }
  .pbc-gate {
    padding: 16px; display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 14px;
  }
  /* Scoped for the same reason as .pbp-gate above. */
  .pbc-locked > .pbc-gate {
    position: absolute; top: 10px; left: 0; right: 0; z-index: 2;
    box-shadow: 0 10px 26px rgba(0,0,0,0.45);
  }
  .pbc-gate-main { flex: 1; min-width: 240px; }
  .pbc-gate-title { font-weight: 800; font-size: 0.9rem; }
  .pbc-gate-sub { font-size: 0.76rem; color: var(--text-dim); margin-top: 3px; }
  .pbc-gate-actions { display: flex; gap: 8px; flex-wrap: wrap; }

  .pbc-quiet {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px;
    padding: 12px 16px; font-size: 0.78rem; color: var(--text-dim);
  }
  .pbc-quiet b { color: var(--text); font-weight: 750; }
  .pbc-quiet-list { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }
  .pbc-chip {
    display: flex; align-items: center; gap: 5px; background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 999px; padding: 3px 10px 3px 5px;
    font-size: 0.7rem; color: var(--text-dim);
  }
  .pbc-chip b { color: var(--text); font-weight: 700; }

  @media (max-width: 760px) {
    .pbc-card { grid-template-columns: 1fr; gap: 10px; padding: 13px 14px; }
    /* Numbers first, explanation second. The three counts are the summary;
       reading six lines of prose to reach them is the wrong order on a
       phone, where the prose filled most of the first screen. */
    .pbc-verdict-strip { gap: 10px 16px; padding: 14px; }
    .pbc-verdict-strip .lead { order: 2; flex-basis: 100%; font-size: 0.8rem; }
    .pbc-stat { order: 1; flex: 1; }
    .pbc-stat .n { font-size: 1.2rem; }
    /* The axis was sized for a desktop column and left a lot of empty
       vertical space once the card went single-column. */
    .pbc-axis { height: 76px; }
    .pbc-axis-rail, .pbc-axis-move { top: 36px; }
    .pbc-axis-move { top: 35px; }
    .pbc-mark.is-model { top: 10px; }
    .pbc-mark.is-open, .pbc-mark.is-now { top: 30px; }
    .pbc-mark.is-low i { height: 30px; }
    .pbc-who-line { font-size: 0.86rem; }
    /* All three chips on one row, using the short labels - the full ones
       don't fit at 360px and shrinking the type enough to force it made the
       counts hard to read without actually fitting. */
    .pbc-gate { padding: 14px; gap: 12px; }
    /* .pb-btn-primary/.pb-btn-ghost go full-width at this breakpoint for the
       sheet footer, where two long labels genuinely need their own rows. The
       gate's pair is short enough to share one, and stacking them pushed
       "Sign in" down a row for no gain, so the gate opts back out: the
       primary sizes to its label and the ghost takes the rest of the row. */
    .pbc-gate-actions { width: 100%; flex-wrap: nowrap; }
    .pbc-gate-actions .pb-btn { padding-left: 9px; padding-right: 9px; font-size: 0.8rem; white-space: nowrap; }
    .pbc-gate-actions .pb-btn-primary { flex: 0 1 auto; }
    .pbc-gate-actions .pb-btn-ghost { flex: 1 1 auto; }
    .pbc-filters { gap: 6px; flex-wrap: nowrap; }
    .pbc-filt { flex: 1 1 0; min-width: 0; justify-content: center; padding: 8px 6px; gap: 6px; }
    .pbc-lbl-full { display: none; }
    .pbc-lbl-short { display: inline; }
  }

  /* ---- Betting Angles ---- */
  .pba-row { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px; margin-bottom: 10px; overflow: hidden; }
  .pba-row-main {
    display: grid; gap: 14px; align-items: center; padding: 13px 16px;
    grid-template-columns: 34px minmax(180px,1.1fr) minmax(150px,1fr) minmax(190px,1fr) 92px;
  }
  .pba-rank { text-align: center; font-size: 0.78rem; font-weight: 800; color: var(--text-dim); font-variant-numeric: tabular-nums; }
  .pba-pick { min-width: 0; }
  .pba-pickline { display: flex; align-items: center; gap: 7px; font-weight: 850; font-size: 0.92rem; }
  .pba-when { font-size: 0.68rem; color: var(--text-dim); margin-top: 3px; }
  .pba-when .net { color: var(--edge-b); font-weight: 700; }
  .pba-mid { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
  .pba-k { font-size: 0.56rem; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-dim); font-weight: 800; }
  .pba-cl { font-size: 0.8rem; font-weight: 750; }
  .pba-tag { display: inline-block; border-radius: 999px; font-size: 0.6rem; font-weight: 800; padding: 1px 7px; border: 1px solid; white-space: nowrap; }
  .pba-tag-lean { color: var(--text-dim); border-color: var(--card-border); background: var(--surface-2); }

  /* The model's probability against the one the price implies. The gap
     between the two bars is the entire claim of a moneyline angle, so it gets
     drawn rather than described. */
  .pba-vbar { position: relative; height: 8px; background: var(--surface-2); border-radius: 4px; overflow: hidden; margin-top: 5px; }
  .pba-vbar i { position: absolute; left: 0; top: 0; bottom: 0; display: block; }
  .pba-vbar i.mod { background: var(--accent); opacity: 0.5; }
  .pba-vbar i.imp { background: var(--lose); opacity: 0.55; }
  .pba-vlab { display: flex; justify-content: space-between; font-size: 0.6rem; color: var(--text-dim); margin-top: 4px; font-weight: 700; }

  .pba-big { text-align: right; }
  .pba-big .n { font-size: 1.05rem; font-weight: 850; display: block; line-height: 1.1; font-variant-numeric: tabular-nums; }
  .pba-big .l { font-size: 0.54rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 800; }

  .pba-books-toggle {
    width: 100%; text-align: left; background: transparent; border: 0; border-top: 1px solid var(--card-border);
    color: var(--accent); font-family: inherit; font-size: 0.73rem; font-weight: 800; padding: 8px 16px; cursor: pointer;
  }
  .pba-books-toggle:hover { text-decoration: underline; }
  .pba-books { border-top: 1px solid var(--card-border); padding: 8px 16px 12px; }
  .pba-book { display: flex; align-items: center; justify-content: space-between; font-size: 0.77rem; color: var(--text-dim); padding: 4px 0; }
  .pba-book.is-best { color: var(--pill-good-text); font-weight: 750; }
  .pba-book .v { font-weight: 750; color: var(--text); font-variant-numeric: tabular-nums; }
  .pba-book.is-best .v { color: var(--pill-good-text); }
  .pba-best-tag {
    font-size: 0.58rem; font-weight: 900; letter-spacing: 0.04em; color: var(--pill-good-text);
    background: rgba(var(--win-rgb),0.15); padding: 1px 6px; border-radius: 5px; margin-left: 6px;
  }
  .pba-books-note { font-size: 0.68rem; color: var(--lose); margin-top: 8px; }

  .pba-disclaimer {
    font-size: 0.74rem; color: var(--text-dim); background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 12px; padding: 12px 16px; margin-top: 18px;
  }

  @media (max-width: 760px) {
    .pba-row-main { grid-template-columns: 34px minmax(0,1fr) auto; row-gap: 8px; column-gap: 10px; padding: 12px 14px; }
    .pba-rank { grid-column: 1; grid-row: 1; }
    .pba-pick { grid-column: 2; grid-row: 1; }
    .pba-big { grid-column: 3; grid-row: 1; }
    .pba-mid { grid-column: 2 / -1; grid-row: 2; flex-direction: row; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; }
    .pba-mid.pba-value { flex-direction: column; align-items: stretch; }
    .pba-row .pbp-why { grid-column: 2 / -1; grid-row: 3; }
  }

  /* ---- section heads / empty ---- */
  .pb-section-h { margin: 22px 0 10px; }
  .pb-section-h h3 { font-size: 0.98rem; font-weight: 820; margin: 0; }
  .pb-section-h p { font-size: 0.74rem; color: var(--text-dim); margin: 2px 0 0; }
  .pb-empty { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px; padding: 26px 20px; text-align: center; }
  .pb-empty h3 { margin: 0 0 6px; font-size: 1.05rem; font-weight: 820; }
  .pb-empty p { margin: 0 auto 14px; font-size: 0.82rem; color: var(--text-dim); max-width: 380px; line-height: 1.5; }
  .pb-empty-actions { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; }
  .pb-note { font-size: 0.78rem; color: var(--text-dim); }

  @media (max-width: 420px) {
    .pb-title { font-size: 1.35rem; }
    .pb-sheet-status { font-size: 0.66rem; }
    .pb-standing .n { font-size: 1.05rem; }
  }

  /* Betting Angles + track record (formerly the whole Hot Picks tab) */
  .hot-picks-view { margin-top: 18px; }
  .hot-picks-title { font-size: 1.15rem; font-weight: 800; margin: 0 0 4px; }
  .hot-picks-sub { font-size: 0.82rem; color: var(--text-dim); margin: 0 0 20px; }
  .hot-picks-subtabs { display: flex; gap: 6px; margin: 4px 0 20px; flex-wrap: wrap; }
  .hot-picks-subtab-btn {
    background: var(--card-bg); color: var(--text-dim); border: 1px solid var(--card-border);
    border-radius: 999px; padding: 7px 16px; font-size: 0.8rem; font-weight: 600; cursor: pointer;
    font-family: inherit; transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .hot-picks-subtab-btn:hover { color: var(--text); border-color: rgba(var(--accent-rgb),0.4); }
  .hot-picks-subtab-btn.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); border-color: var(--accent); }
  .hot-picks-section { margin-bottom: 24px; }
  .hot-picks-list { display: flex; flex-direction: column; gap: 8px; }
  .hot-pick-row {
    display: flex; align-items: center; gap: 12px; width: 100%; text-align: left; cursor: pointer;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px; padding: 12px 14px;
    font-family: inherit; color: var(--text); transition: border-color 0.15s;
  }
  .hot-pick-row:hover { border-color: var(--accent); }
  .hot-pick-rank {
    width: 26px; height: 26px; border-radius: 50%; background: rgba(var(--accent-rgb),0.15); color: var(--accent);
    display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 0.8rem; flex-shrink: 0;
  }
  .hot-pick-main { flex: 1; min-width: 0; }
  .hot-pick-teams { display: flex; align-items: center; gap: 8px; font-size: 0.88rem; font-weight: 700; margin-bottom: 2px; }
  .hot-pick-teams img { flex-shrink: 0; }
  .hot-pick-summary { font-size: 0.8rem; color: var(--text); margin-bottom: 3px; }
  .hot-pick-advantage { font-size: 0.74rem; color: var(--text-dim); line-height: 1.4; }
  .hot-pick-conf { font-weight: 800; color: var(--accent); font-size: 1rem; flex-shrink: 0; }

  .hot-picks-market-groups { display: flex; flex-direction: column; gap: 18px; }
  .hot-picks-market-label { font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); font-weight: 700; margin-bottom: 8px; }
  .hot-picks-bets { display: flex; flex-direction: column; gap: 10px; }
  .hot-picks-empty { font-size: 0.82rem; color: var(--text-dim); }
  .bet-card {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px;
    overflow: hidden; transition: border-color 0.15s;
  }
  .bet-card:hover { border-color: var(--accent); }
  .bet-card-main {
    display: flex; gap: 12px; width: 100%; text-align: left; cursor: pointer; align-items: flex-start;
    background: transparent; border: none; padding: 12px 14px; font-family: inherit; color: var(--text);
  }
  .bet-card-books { border-top: 1px solid var(--card-border); }
  .bet-card-books-toggle {
    width: 100%; text-align: left; background: transparent; border: none; cursor: pointer;
    font-family: inherit; color: var(--accent); font-size: 0.74rem; font-weight: 700;
    padding: 8px 14px; letter-spacing: 0.2px;
  }
  .bet-card-books-toggle:hover { text-decoration: underline; }
  .bet-card-books-list { padding: 0 14px 10px; display: flex; flex-direction: column; gap: 2px; }
  .bet-card-book-row {
    display: flex; align-items: center; justify-content: space-between; font-size: 0.78rem;
    color: var(--text-dim); padding: 3px 0;
  }
  .bet-card-book-row.is-best { color: var(--win); font-weight: 700; }
  .bet-card-book-label { display: flex; align-items: center; gap: 6px; }
  .bet-card-book-best-tag {
    font-size: 0.6rem; font-weight: 800; letter-spacing: 0.3px; color: var(--win);
    background: rgba(var(--win-rgb),0.15); padding: 2px 5px; border-radius: 4px;
  }
  .bet-card-book-value { font-weight: 700; color: var(--text); }
  .bet-card-book-row.is-best .bet-card-book-value { color: var(--win); }
  .bet-tag {
    flex-shrink: 0; font-size: 0.62rem; font-weight: 800; letter-spacing: 0.4px; padding: 4px 8px;
    border-radius: 6px; margin-top: 2px; white-space: nowrap;
  }
  .bet-tag-accent { background: rgba(var(--accent-rgb),0.15); color: var(--accent); }
  .bet-tag-win { background: rgba(var(--win-rgb),0.15); color: var(--win); }
  .bet-tag-warn { background: rgba(var(--warn-rgb),0.15); color: var(--warn); }
  .bet-pick { font-weight: 700; font-size: 0.9rem; margin-bottom: 3px; }
  .bet-card-matchup {
    display: flex; align-items: center; gap: 6px; font-size: 0.72rem; color: var(--text-dim);
    font-weight: 600; margin-bottom: 4px;
  }
  .bet-card-matchup img { flex-shrink: 0; }
  .bet-reason { font-size: 0.78rem; color: var(--text-dim); line-height: 1.45; }

  @media (max-width: 760px) {
    /* The tag sits inline, so a wider one ("MONEYLINE") pushed that card's
       body further right than its neighbour's ("TOTAL") and the cards read
       as misaligned. It also squeezed the matchup line into wrapping. On a
       phone the tag goes on its own line, so every card's content starts at
       the same edge and the matchup gets the full width. */
    .bet-card-main { flex-direction: column; gap: 8px; }
    .bet-tag { align-self: flex-start; margin-top: 0; }
  }

  /* Hot Picks - Track Record panel */
  .track-record-panel {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px;
    padding: 14px 16px; margin-bottom: 24px;
  }
  .track-record-empty { padding: 14px 16px; }
  .track-record-empty-note { font-size: 0.8rem; color: var(--text-dim); margin: 4px 0 0; }
  .track-record-header { margin-bottom: 12px; }
  .track-record-title {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--accent);
    font-weight: 700; margin-bottom: 6px;
  }
  .track-record-headline { font-size: 0.92rem; color: var(--text); line-height: 1.4; }
  .track-record-headline strong { color: var(--accent); font-size: 1.05rem; }
  .track-record-markets { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
  .track-record-market-row {
    display: flex; align-items: center; justify-content: space-between; font-size: 0.82rem;
    padding: 6px 0; border-bottom: 1px solid var(--card-border);
  }
  .track-record-market-row:last-child { border-bottom: none; }
  .track-record-market-label { color: var(--text-dim); }
  .track-record-market-stat { font-weight: 700; color: var(--text); }
  .track-record-market-record { font-weight: 400; color: var(--text-dim); font-size: 0.78rem; }
  .track-record-weeks {
    display: flex; flex-direction: column; gap: 4px; max-height: 220px; overflow-y: auto;
    padding-top: 8px; border-top: 1px solid var(--card-border);
  }
  .track-record-week-row {
    display: flex; align-items: center; justify-content: space-between; font-size: 0.76rem;
    color: var(--text-dim); padding: 3px 0;
  }
  .track-record-week-row span:first-child { color: var(--text); font-weight: 600; flex-shrink: 0; margin-right: 10px; }
  .track-record-week-marks { text-align: right; }

  /* News tab */
  .news-view { margin-top: 18px; }
  .news-view-header {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
    margin: 0 0 16px;
  }
  .news-view-title { font-size: 1.15rem; font-weight: 800; margin: 0; }
  .news-view-empty { color: var(--text-dim); font-size: 0.85rem; margin-top: 18px; }

  .news-team-filter { position: relative; display: inline-flex; }
  .news-team-filter-trigger {
    display: flex; align-items: center; gap: 7px; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 999px; color: var(--text);
    font-size: 0.82rem; font-weight: 600; padding: 6px 12px 6px 8px; cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
  }
  .news-team-filter-trigger:hover { border-color: var(--accent); }
  .news-team-filter-trigger:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .news-team-filter-chevron { color: var(--text-dim); transition: transform 0.18s; display: flex; }
  .news-team-filter-chevron.open { transform: rotate(180deg); }
  .news-team-filter-panel {
    position: absolute; top: calc(100% + 10px); right: 0; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 14px; box-shadow: 0 16px 40px rgba(0,0,0,0.35);
    padding: 8px; width: 260px; max-height: 380px; display: flex; flex-direction: column; z-index: 30;
  }
  .news-team-filter-search {
    width: 100%; box-sizing: border-box; background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 8px; padding: 8px 10px; font-size: 16px; color: var(--text); font-family: inherit;
    margin-bottom: 6px; flex-shrink: 0;
  }
  @media (hover: hover) and (pointer: fine) {
    .news-team-filter-search { font-size: 0.84rem; }
  }
  .news-team-filter-search:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
  .news-team-filter-list { overflow-y: auto; }
  .news-team-filter-item {
    display: flex; align-items: center; gap: 10px; width: 100%; background: none; border: none;
    color: var(--text); font-size: 0.85rem; text-align: left; padding: 8px; border-radius: 8px;
    cursor: pointer; font-family: inherit; transition: background 0.12s;
  }
  .news-team-filter-item:hover { background: rgba(var(--accent-rgb),0.08); }
  .news-team-filter-item.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); font-weight: 700; }
  .news-team-filter-all-icon { font-size: 16px; width: 18px; text-align: center; flex-shrink: 0; line-height: 1; }
  .news-team-filter-empty { color: var(--text-dim); font-size: 0.8rem; padding: 12px 8px; text-align: center; }

  .news-card-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px;
  }
  .news-card {
    display: flex; flex-direction: column; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; overflow: hidden; text-decoration: none; color: inherit;
    box-shadow: 0 4px 14px rgba(0,0,0,0.12); transition: transform 0.15s, box-shadow 0.15s, border-color 0.15s;
  }
  .news-card:hover { transform: translateY(-3px); box-shadow: 0 10px 28px rgba(0,0,0,0.22); border-color: var(--accent); }
  .news-card:hover .news-card-img { transform: scale(1.05); }
  .news-card-img-wrap { position: relative; aspect-ratio: 16 / 9; overflow: hidden; background: var(--surface-2); flex-shrink: 0; }
  .news-card-img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; display: block; }
  .news-card-img-fallback {
    width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg, rgba(var(--accent-rgb),0.22), rgba(var(--accent-rgb),0.06));
    font-size: 30px;
  }
  .news-card-source-badge {
    position: absolute; top: 8px; left: 8px; background: rgba(0,0,0,0.62); color: #fff; font-size: 0.62rem;
    font-weight: 700; letter-spacing: 0.3px; text-transform: uppercase; padding: 3px 8px; border-radius: 999px;
  }
  .news-card-body { padding: 12px 14px 14px; display: flex; flex-direction: column; gap: 6px; flex: 1; }
  .news-card-headline {
    font-size: 0.88rem; font-weight: 700; line-height: 1.35; transition: color 0.15s;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  }
  .news-card:hover .news-card-headline { color: var(--accent); }
  .news-card-meta { font-size: 0.72rem; color: var(--text-dim); margin-top: auto; }

  /* Top story gets the whole row and a wider image instead of matching
     every other card - visual priority for the most recent headline. */
  .news-card-featured { grid-column: 1 / -1; flex-direction: row; }
  .news-card-featured .news-card-img-wrap { width: 42%; aspect-ratio: auto; }
  .news-card-featured .news-card-body { justify-content: flex-start; padding: 18px 22px; gap: 10px; }
  .news-card-featured .news-card-headline { font-size: 1.15rem; -webkit-line-clamp: 4; }
  .news-card-featured .news-card-meta { margin-top: 0; }
  .news-card-excerpt {
    font-size: 0.82rem; color: var(--text-dim); line-height: 1.5;
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
  }
  @media (max-width: 640px) {
    .news-card-featured { flex-direction: column; }
    .news-card-featured .news-card-img-wrap { width: 100%; aspect-ratio: 16 / 9; }
  }

  .news-load-more-btn {
    display: block; margin: 20px auto 4px; background: var(--card-bg); border: 1px solid var(--card-border);
    color: var(--text); font-size: 0.85rem; font-weight: 700; padding: 10px 20px; border-radius: 999px;
    cursor: pointer; transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .news-load-more-btn:hover { border-color: var(--accent); color: var(--accent); background: rgba(var(--accent-rgb),0.08); }

  .how-it-works-copy { font-size: 0.85rem; color: var(--text-dim); line-height: 1.6; margin: 0; }
  /* The modal used to be one unbroken paragraph. It now covers formats,
     cards, leagues, alerts and settings, which is more than a wall of text
     can carry - these are the subheads that break it up. Deliberately the
     same uppercase-eyebrow treatment as .sec-label inside a game card's
     Full Details panel, since this modal is describing those sections. */
  .how-it-works-sec { margin-top: 18px; }
  .how-it-works-sec:first-of-type { margin-top: 6px; }
  .how-it-works-sec h3 {
    font-size: 0.72rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.07em;
    color: var(--text); margin: 0 0 6px;
  }
  .how-it-works-copy + .how-it-works-copy { margin-top: 8px; }
  .how-it-works-foot {
    margin-top: 20px; padding-top: 12px; border-top: 1px solid var(--card-border);
    font-size: 0.78rem; color: var(--text-dim); line-height: 1.55; margin-bottom: 0;
  }
  .demo-banner {
    background: rgba(var(--demo-rgb),0.14); border: 1px solid rgba(var(--demo-rgb),0.4); color: var(--pill-purple-text);
    border-radius: 10px; padding: 10px 14px; font-size: 0.8rem; margin: 8px 0 20px;
  }
  .bye-banner {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
    background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 10px; padding: 10px 14px; margin: 8px 0 20px;
  }
  .bye-banner-label { font-size: 0.8rem; font-weight: 700; color: var(--text-dim); white-space: nowrap; }
  .bye-teams { display: flex; flex-wrap: wrap; gap: 8px; }
  .bye-team-chip {
    display: flex; align-items: center; gap: 6px; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 999px; padding: 4px 12px 4px 4px; font-size: 0.78rem;
  }
  /* minmax(min(400px, 100%), 1fr) rather than minmax(400px, 1fr): a bare
     400px track floor is a hard minimum that auto-fit cannot go below, so on
     any viewport narrower than ~416px (iPhone SE through iPhone 15/16, i.e.
     most phones) the grid forced the document wider than the screen and the
     whole page scrolled sideways - dragging the fixed bottom tab bar with it
     and pushing the last tab off-screen. Wrapping the floor in min() lets the
     track collapse to the container width on narrow screens while keeping the
     400px two-up breakpoint everywhere it actually fits. */
  .schedule-grid {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(400px, 100%), 1fr)); gap: 16px;
    align-items: start;
  }
  .game-card {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 16px; display: flex; flex-direction: column; gap: 10px;
    width: 100%; box-sizing: border-box;
    /* Anchors .card-ribbon, the final-game verdict tag pinned to the card's
       top-right corner (see the GAMECARD REDESIGN block below). */
    position: relative;
  }
  .game-card.is-historical { border-color: rgba(var(--demo-rgb),0.35); }
  .game-card.is-division {
    border-color: var(--accent); border-width: 2px;
    background-color: rgba(var(--accent-rgb),0.07);
    box-shadow: 0 0 0 1px rgba(var(--accent-rgb),0.3), 0 0 18px rgba(var(--accent-rgb),0.14);
  }
  .game-card.is-live { border-color: var(--live); box-shadow: 0 0 0 1px rgba(var(--live-rgb),0.3); }
  .game-card.is-international { border-color: #2dd4bf; box-shadow: 0 0 0 1px rgba(45,212,191,0.3), 0 0 18px rgba(45,212,191,0.12); }
  .game-meta { display: flex; justify-content: space-between; font-size: 0.75rem; color: var(--text-dim); }
  /* Only on a final card, where .card-ribbon occupies the top-right corner -
     the right-aligned network label would otherwise run under it. */
  .game-meta.has-ribbon { padding-right: 104px; }
  /* "Missed" is 16px narrower than "Called it" - reclaim it so a miss card
     doesn't wrap its date line for clearance it doesn't need. */
  .game-meta.has-ribbon.ribbon-short { padding-right: 88px; }
  .game-meta-left { display: flex; align-items: center; flex-wrap: wrap; }
  .division-tag {
    display: inline-flex; align-items: center; margin-left: 6px; padding: 2px 8px;
    border-radius: 999px; background: var(--accent); color: var(--card-bg);
    font-size: 0.65rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px;
  }
  .international-tag {
    display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; padding: 2px 8px;
    border-radius: 999px; background: #2dd4bf; color: #06322e; white-space: nowrap;
    font-size: 0.65rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px;
  }
  .international-tag .intl-icon { flex-shrink: 0; }
  .weather-pill {
    display: inline-flex; align-items: center; margin-left: 6px; padding: 2px 8px;
    border-radius: 999px; background: rgba(var(--text-dim-rgb, 148,163,184),0.14); color: var(--text-dim);
    font-size: 0.68rem; font-weight: 600; white-space: nowrap;
  }
  .weather-pill-dome { opacity: 0.85; }
  .weather-pill-windy { background: rgba(56,189,248,0.14); color: #38bdf8; }
  .weather-wind { font-weight: 700; }
  .live-tag {
    display: inline-flex; align-items: center; gap: 4px; color: var(--live); font-weight: 700;
    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap;
  }
  .live-dot {
    width: 7px; height: 7px; border-radius: 50%; background: var(--live);
    animation: pulse 1.4s infinite;
  }
  @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }
  .down-distance {
    margin-left: 6px; padding-left: 6px; border-left: 1px solid rgba(var(--live-rgb),0.4);
    color: var(--text-dim); font-weight: 600; text-transform: none; letter-spacing: normal;
  }
  .down-distance.is-redzone { color: var(--live); font-weight: 700; }
  /* Most recent play, shown as its own line under the game-meta row (LIVE
     tag + down/distance) rather than crammed inline - ESPN's play text can
     run 15-25 words, too long to sit next to the score without wrapping
     awkwardly. Muted/italic so it reads as a caption, not a headline. */
  .last-play {
    font-size: 0.72rem; color: var(--text-dim); font-style: italic;
    margin-top: 2px; line-height: 1.35; overflow-wrap: break-word;
  }
  /* Replaces the scheduled date/time in game-meta-left once a game goes
     live - the score itself, styled as a highlighted pill so it reads as
     the headline fact on the card rather than blending into the meta row
     the way the plain date/time text used to. */
  .live-score-inline {
    display: inline-flex; align-items: center; font-weight: 800; font-size: 0.82rem; letter-spacing: 0.2px;
    color: var(--live); background: rgba(var(--live-rgb),0.14); border: 1px solid rgba(var(--live-rgb),0.35);
    border-radius: 6px; padding: 2px 9px;
  }
  /* Football glyph inline with the top-of-card score, next to whichever
     side has the ball. Sized down from native emoji size (~1.15em at
     default) to ~0.72em so it reads as a small icon riding the score
     text's baseline rather than a mismatched, oversized glyph. */
  .poss-ball {
    display: inline-flex; align-items: center; font-size: 0.72em; margin: 0 3px; line-height: 1;
    transform: translateY(-0.5px);
  }
  /* Generic "something just changed" flash, applied (via LiveScoreFlash)
     only on the poll where a live score actually moves - not on every
     30s poll regardless of change. Uses the win-green accent rather than
     the live-red already used by the LIVE badge, so a scoring change
     reads as its own distinct signal instead of blending into the rest
     of the live styling. Ends by fading back to whatever background the
     element normally has (transparent for a plain score, the
     live-score-inline pill's tint for the top-of-card score), so it
     reads as a held highlight rather than a hard flash-then-snap. */
  @keyframes score-flash {
    0% { background-color: rgba(var(--win-rgb), 0.75); transform: scale(1.14); }
    70% { background-color: rgba(var(--win-rgb), 0.75); transform: scale(1.14); }
    100% { background-color: rgba(var(--win-rgb), 0); transform: scale(1); }
  }
  .score-flash { display: inline-flex; border-radius: 6px; animation: score-flash 3.2s ease-out; }
  .matchup-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
  .team-block { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px; border-radius: 10px; position: relative; }
  /* Two different things that used to fight each other: green used to mean
     "the model picked this team" while a purple ring meant "this team won",
     so a missed call showed the losing team tinted green. Now green is only
     ever the outcome (plus a checkmark, so colour isn't the sole signal) and
     the model's pick is a teal dashed outline at every stage of the game. */
  .team-block { border: 1px solid transparent; }
  .team-block.blitz-pick { border: 1px dashed rgba(var(--accent-rgb), 0.6); background: rgba(var(--accent-rgb), 0.07); }
  .team-block.actual-winner { border: 1px solid rgba(var(--win-rgb), 0.55); background: rgba(var(--win-rgb), 0.16); }
  .team-block.actual-winner.blitz-pick { border-style: solid; }
  .won-check {
    position: absolute; top: 4px; right: 4px; width: 17px; height: 17px; border-radius: 999px;
    background: var(--win); color: var(--pill-good-fill-text); font-size: 0.7rem; font-weight: 900;
    display: flex; align-items: center; justify-content: center; line-height: 1;
  }
  .team-click { display: flex; flex-direction: column; align-items: center; gap: 6px; cursor: pointer; background: none; border: none; padding: 0; color: inherit; font: inherit; }
  .team-click:hover .team-name { text-decoration: underline; }
  .badge-wrap { position: relative; width: 46px; height: 46px; }
  /* Team crest - a badge image (see /team-badges/{TEAM}.png). The art is a
     tilted football-with-checkmark badge (not a circular icon), already
     edge-defined with its own dark outline/shadow, so it's shown in full via
     object-fit: contain rather than center-cropped into a circle - a cover
     crop would clip the checkmark tail and the badge's pointed ends.
     width/height are set inline per instance via the size prop on TeamLogo,
     so only the shared visual rules live here. background-color (also set
     inline, team primary color) shows briefly while the image loads and is
     what remains visible if onError hides a missing image. */
  .team-crest {
    object-fit: contain;
    display: block;
    user-select: none; flex-shrink: 0;
  }
  .star-btn {
    position: absolute; top: -6px; right: -6px; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 50%; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center;
    cursor: pointer; font-size: 0.75rem; color: var(--text-dim); line-height: 1; padding: 0;
  }
  .star-btn.active { color: var(--star); border-color: var(--star); }
  .team-name { font-size: 0.8rem; text-align: center; color: var(--text); }
  /* Season record entering the game, directly under the name - dimmer and
     smaller so the block still reads as a team first (same treatment as
     .pickem-team-record on the Picks sheet). Tabular figures keep the two
     sides' records level with each other. Pulled up a little against the
     block's 6px gap so name and record read as one unit, with the Blitz
     Pick tag keeping its full spacing below. */
  .team-record {
    font-size: 0.66rem; font-weight: 700; color: var(--text-dim);
    font-variant-numeric: tabular-nums; line-height: 1; margin-top: -3px;
  }
  .team-block.actual-winner .team-record { color: var(--text); }
  .win-tag {
    font-size: 0.55rem; color: var(--accent); font-weight: 800; text-transform: uppercase;
    letter-spacing: 0.4px; border: 1px solid rgba(var(--accent-rgb), 0.5); border-radius: 999px; padding: 1px 6px;
  }
  .win-tag-hidden { visibility: hidden; }
  .at-sep { color: var(--text-dim); font-size: 0.75rem; padding: 0 4px; }
  .confidence-bar-wrap { display: flex; align-items: center; gap: 8px; font-size: 0.7rem; color: var(--text-dim); }
  /* .odds-row / .odds-field(s) / .odds-label / .odds-agree are gone with the
     Full Details "Betting Lines" block - see the .cb-odds note below for why.
     .odds-source and .odds-fallback-note stay: the Playbook rows and the
     international-venue note still use them. */
  .odds-source { font-size: 0.64rem; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.3px; }
  .odds-fallback-note { font-size: 0.68rem; color: var(--text-dim); font-style: italic; }
  .confidence-bar { flex: 1; height: 6px; border-radius: 4px; background: var(--lose); overflow: hidden; display: flex; }
  .confidence-bar > div { height: 100%; }
  .stat-compare { display: grid; grid-template-columns: 1fr 1fr auto; column-gap: 10px; row-gap: 8px; font-size: 0.72rem; color: var(--text-dim); }
  .stat-compare .col { display: flex; flex-direction: column; gap: 3px; }
  .stat-compare .label { color: var(--text); font-weight: 600; margin-bottom: 2px; white-space: nowrap; }
  .diff-explainer {
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 8px;
    padding: 6px 10px; font-size: 0.74rem; margin-top: 2px;
  }
  .diff-explainer summary {
    cursor: pointer; list-style: none; color: var(--text); font-weight: 600;
    display: flex; align-items: center; justify-content: space-between;
  }
  .diff-explainer summary::-webkit-details-marker { display: none; }
  .diff-explainer summary::after { content: "\25B8"; color: var(--text-dim); font-size: 0.65rem; transition: transform 0.15s ease; }
  .diff-explainer[open] summary::after { transform: rotate(90deg); }
  .diff-explainer p { color: var(--text-dim); font-size: 0.68rem; line-height: 1.4; margin: 6px 0 0; }

  /* ============================================================
     GAMECARD REDESIGN - compact default state + Full Details
     ============================================================ */
  .game-meta-right { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; flex-shrink: 0; max-width: 55%; text-align: right; }
  .game-network { font-weight: 600; white-space: normal; word-break: break-word; }

  .live-strip {
    background: rgba(var(--live-rgb),0.08); border: 1px solid rgba(var(--live-rgb),0.25);
    border-radius: 10px; padding: 9px 11px; margin: 6px 0; display: flex; flex-direction: column; gap: 5px;
  }
  .live-strip-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .live-strip-network { font-size: 0.72rem; font-weight: 700; color: var(--text-dim); white-space: nowrap; }

  .final-score-mid { display: flex; flex-direction: column; align-items: center; gap: 2px; flex-shrink: 0; padding: 0 2px; }
  .final-score-mid .fs-nums { font-size: 1rem; font-weight: 800; white-space: nowrap; }
  .final-score-mid .fs-sep { color: var(--text-dim); font-weight: 600; margin: 0 3px; }
  .final-score-mid .fs-label { font-size: 0.58rem; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.4px; }
  /* Verdict tag for a finished game. Replaces the old .final-call-badge,
     which was 0.56rem unstyled text stacked third in the narrow middle
     column under the score and "FINAL" and got lost there. A hit is filled
     brand orange; a miss is filled --lose grey rather than red, because at a
     realistic miss rate a red fill puts several alarm-coloured tags on every
     week screen and collides with the --live red used in the same corner
     region. The grey is a label on the miss, not the only signal for it -
     the split between the green outcome block and the teal pick block
     carries that on its own. */
  .card-ribbon {
    position: absolute; top: 0; right: 0; padding: 5px 11px 5px 13px;
    border-radius: 0 13px 0 12px; font-size: 0.68rem; font-weight: 900;
    letter-spacing: 0.5px; text-transform: uppercase; white-space: nowrap;
    display: flex; align-items: center; gap: 5px; line-height: 1.15;
  }
  .card-ribbon.correct { background: var(--blitz-orange); color: var(--blitz-orange-fill-text); }
  .card-ribbon.incorrect { background: var(--lose); color: #fff; }

  /* Two bands: the Blitz Edge lockup and the pick's confidence on top (one
     left-aligned group), the sportsbook line underneath. The odds band uses
     the same centered dot-separated treatment as the Home page's edge card
     so the two surfaces read identically. */
  .combo-block {
    border: 1px solid var(--card-border, rgba(148,163,184,0.25)); border-radius: 10px; overflow: hidden;
    margin: 10px 0; background: var(--card-bg-2, rgba(148,163,184,0.05));
  }
  .cb-edge { display: flex; align-items: center; justify-content: center; gap: 12px; padding: 7px 12px; background: rgba(var(--accent-rgb),0.12); }
  .cb-logo { height: 26px; width: auto; display: block; flex: 0 0 auto; }
  .cb-conf { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; white-space: nowrap; }
  .cb-conf-lbl { font-size: 0.72rem; color: var(--text-dim); }
  .cb-conf-team { font-size: 0.82rem; font-weight: 800; color: var(--text); }
  .cb-conf-pct { font-size: 0.98rem; font-weight: 800; color: var(--win); font-variant-numeric: tabular-nums; }
  /* A muted read (no ats edge, no line posted, a survivor team worth
     holding) is the model declining to recommend - it keeps the row's
     shape but drops the brand mark and the win-green, so a glance down a
     week of cards separates calls from non-calls. */
  .cb-edge.is-muted { background: rgba(148,163,184,0.08); }
  .cb-conf-pct.is-muted { color: var(--text-dim); }
  .cb-dash { color: var(--text-dim); font-size: 1rem; line-height: 1; flex: 0 0 auto; }
  .cb-note {
    border-top: 1px dashed var(--card-border); padding: 6px 12px; text-align: center;
    font-size: 0.68rem; color: var(--text-dim); line-height: 1.45;
  }
  /* Two fixed rows - spread/total, then both moneylines. Both sides of the
     ML live here now rather than just the favourite's: the Full Details
     "Betting Lines" block that used to carry the dog's number was printing
     the same spread, total and pair of moneylines a second time (and
     Compare Sportsbooks, three sections further down the same panel, lists
     every book's line a third), so it was deleted and this row absorbed the
     one number it had that this one didn't.
     Fixed rows rather than one wrapping line: a blowout's four-digit prices
     (CHI +650 / PHI -1000) reflow a single row at 390px, so cards down a
     week would each be a different height and the numbers wouldn't line up.
     Two rows always costs one line on the games that would have fit and
     buys a stable card in exchange. */
  .cb-odds {
    border-top: 1px solid var(--card-border); padding: 7px 12px;
    display: flex; flex-direction: column; gap: 3px;
    font-size: 0.74rem; color: var(--text-dim); font-variant-numeric: tabular-nums;
  }
  .cb-odds-line { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: center; }
  .cb-odds b { color: var(--text); font-weight: 800; white-space: nowrap; }
  /* Each stat is one unbreakable unit and carries its own leading separator,
     so a row that does have to wrap breaks between stats, never inside a
     price. */
  .cb-stat { white-space: nowrap; }
  .cb-stat + .cb-stat::before { content: "\00b7"; opacity: 0.5; margin: 0 6px; }
  .cb-odds-k { font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.4px; font-weight: 800; margin-right: 5px; }
  .cb-odds-none { font-size: 0.72rem; font-weight: 600; text-align: center; }
  /* Line attribution, relocated out of the deleted Betting Lines block: the
     book has to be named on the surface that shows its numbers, and this is
     now the only one that does. */
  .cb-odds-src {
    border-top: 1px dashed var(--card-border); padding: 5px 12px; text-align: center;
    font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.3px; color: var(--text-dim);
  }
  .cb-odds-fallback {
    display: block; margin-top: 2px; text-transform: none; letter-spacing: 0;
    font-style: italic; font-size: 0.68rem;
  }
  /* Only rendered once a game has started, where the spread above it is the
     frozen kickoff line rather than the live market. Without this the row
     reads as a stale number - the same line, checked against any book, now
     disagrees, and nothing on the card says why. */
  .cb-odds-asof { opacity: 0.75; }

  .gc-actions { display: flex; border-top: 1px solid var(--card-border); margin: 10px -14px -12px; }
  .gc-action {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px; padding: 11px 8px;
    background: none; border: none; cursor: pointer; font-size: 0.76rem; font-weight: 700; font-family: inherit;
    color: var(--text);
  }
  /* Scoped to the Add to League button by name rather than by position. It
     used to be :first-child:not(:only-child), which was equivalent while
     the row only ever held those two buttons - but the follow bell below
     can appear without Add to League (a live game), and that selector would
     then have painted "Full Details" in accent and hung a divider on the
     wrong side of it. */
  .gc-action[data-tour="add-to-league"] { border-right: 1px solid var(--card-border); color: var(--accent); }
  /* The league-return variant occupies the same slot and keeps the accent,
     but reads as a confirmation rather than an invitation, so it doesn't
     shout at the same volume as an unfilled Add to League. */
  .gc-action.gc-league-return { font-size: 0.72rem; }
  .gc-action:hover { background: rgba(var(--accent-rgb),0.08); }
  .gc-chevron { transition: transform 0.2s ease; display: inline-block; }
  .gc-chevron.expanded { transform: rotate(180deg); }

  /* The follow bell sits last in the row and deliberately doesn't flex: the
     two text buttons need the width more, and an icon toggle grown to a
     third of a phone screen reads as a third equal action rather than a
     switch attached to this card. */
  .gc-follow {
    flex: 0 0 auto; padding-left: 13px; padding-right: 13px;
    border-left: 1px solid var(--card-border); color: var(--text-dim);
  }
  .gc-follow.is-following { color: var(--blitz-orange); }
  .gc-follow:disabled { opacity: 0.5; cursor: default; }
  .gc-follow-bell { display: inline-flex; align-items: center; }

  /* ---- Share picks -----------------------------------------------
     The sheet reuses .add-to-league-sheet's chrome (backdrop, radius,
     padding) because one opens straight into the other and they should
     read as the same surface; only what's inside differs. */
  .share-sheet .share-preview {
    border: 1px solid var(--card-border); border-radius: 10px; overflow: hidden;
    background: #0b1220; margin-bottom: 14px; min-height: 120px;
    display: flex; align-items: center; justify-content: center;
  }
  .share-sheet .share-preview img { display: block; width: 100%; }
  .share-preview-loading { font-size: 0.78rem; color: var(--text-dim); padding: 32px 16px; text-align: center; }
  .share-opt {
    display: block; width: 100%; text-align: left; padding: 11px 13px; margin-bottom: 8px;
    border-radius: 10px; border: 1px solid var(--card-border); background: none; color: var(--text);
    font-family: inherit; cursor: pointer;
  }
  .share-opt:hover { border-color: var(--accent); background: rgba(var(--accent-rgb),0.07); }
  .share-opt-primary { border-color: var(--accent); background: rgba(var(--accent-rgb),0.1); }
  .share-opt:disabled { opacity: 0.45; cursor: default; }
  .share-opt:disabled:hover { border-color: var(--card-border); background: none; }
  .share-opt-name { display: block; font-size: 0.84rem; font-weight: 700; }
  .share-opt-desc { display: block; font-size: 0.7rem; color: var(--text-dim); margin-top: 1px; }
  .share-copied { font-size: 0.74rem; color: var(--accent); font-weight: 700; margin-top: 2px; }

  /* Share sits in the slot Add to League vacates once a game is live or
     final, so it inherits that slot's accent treatment and divider
     rather than needing chrome of its own. */
  .gc-action[data-tour="share-game"] { border-right: 1px solid var(--card-border); color: var(--accent); }

  /* Week-entry share, on the picks progress card. flex:0 0 auto so it
     never steals width from the count, which is the reason that card
     exists. */
  .picks-progress-share {
    flex: 0 0 auto; display: flex; align-items: center; gap: 6px; padding: 8px 12px;
    border-radius: 9px; border: 1px solid var(--accent); background: rgba(var(--accent-rgb),0.1);
    color: var(--accent); font-family: inherit; font-size: 0.75rem; font-weight: 800; cursor: pointer;
  }
  .picks-progress-share:hover { background: rgba(var(--accent-rgb),0.18); }
  @media (max-width: 380px) { .picks-progress-share span { display: none; } }
  /* Below 360px three controls genuinely don't fit. The label is what goes,
     not the button: the bell keeps its aria-label, and it stays a 40px-wide
     tap target rather than being dropped to a smaller one. */
  @media (max-width: 359px) {
    .gc-follow-label { display: none; }
    /* Icon-only, so the label is no longer supplying width. Padded back out
       to a 44px-wide target rather than left at the ~40px the icon alone
       gives - height stays at the row's shared 36px, since making only this
       button taller would misalign it against the two beside it. */
    .gc-follow { padding-left: 15px; padding-right: 15px; }
  }

  .full-details-panel { padding-top: 12px; display: flex; flex-direction: column; gap: 14px; }
  .full-details-panel .sec-label { font-size: 0.68rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); margin: 0 0 8px; }
  .sec-label-format {
    display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
    background: rgba(var(--accent-rgb),0.14); color: var(--accent); font-size: 0.62rem; font-weight: 800;
    letter-spacing: 0.3px; vertical-align: 1px;
  }
  .edge-detail { background: rgba(var(--accent-rgb),0.06); border: 1px solid rgba(var(--accent-rgb),0.25); border-radius: 10px; padding: 12px; }
  .edge-detail-logo { height: 30px; width: auto; display: block; margin: 0 0 10px; }
  .edge-detail p { margin: 0; font-size: 0.8rem; line-height: 1.5; color: var(--text); }
  /* Was the last line of the Betting Lines block. It's a statement about the
     model rather than about a line, so it moved in with the reasoning it's
     describing instead of dying with the block. */
  .edge-detail p.edge-market-note {
    margin-top: 10px; padding-top: 9px; border-top: 1px dashed rgba(var(--accent-rgb),0.3);
    font-size: 0.72rem; font-weight: 600;
  }
  .edge-detail p.edge-market-note.agree { color: var(--win); }
  .edge-detail p.edge-market-note.disagree { color: var(--warn); }

  .short-boxscore-loading { font-size: 0.76rem; color: var(--text-dim); padding: 4px 0; }
  .short-boxscore-stats { display: flex; flex-direction: column; gap: 5px; }
  .short-boxscore-head { display: grid; grid-template-columns: 1fr 1.4fr 1fr; font-size: 0.68rem; font-weight: 800; color: var(--text-dim); text-align: center; }
  .short-boxscore-row { display: grid; grid-template-columns: 1fr 1.4fr 1fr; align-items: center; font-size: 0.78rem; font-weight: 700; text-align: center; }
  .short-boxscore-label { font-size: 0.66rem; font-weight: 600; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.3px; }
  .view-boxscore-link {
    display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; padding: 9px;
    border: 1px solid var(--card-border); border-radius: 10px; background: none; color: var(--accent);
    font-family: inherit; font-size: 0.76rem; font-weight: 800; cursor: pointer; margin-top: 8px;
  }

  /* z-index 1000/1001 puts this above the fixed mobile .tab-bar (z-index 500),
     which otherwise covers the bottom ~80px of the sheet - i.e. exactly the
     Cancel/Submit Pick row. Matches the other overlays (BoxScoreModal, the
     injury modal, the settings modal), which all sit at 1000. */
  .add-to-league-sheet-backdrop {
    position: fixed; inset: 0; background: rgba(4,8,16,0.55); z-index: 1000;
  }
  .add-to-league-sheet {
    position: fixed; left: 50%; bottom: 0; transform: translateX(-50%); width: 100%; max-width: 480px;
    background: var(--card-bg); border: 1px solid var(--card-border); border-bottom: none;
    border-radius: 18px 18px 0 0; z-index: 1001; max-height: 85vh;
    /* dvh tracks the collapsing mobile URL bar; the vh line above is the
       fallback for browsers without it. Bottom padding clears the iOS home
       indicator so Submit Pick isn't flush against the gesture zone. */
    max-height: 85dvh;
    padding: 16px 18px calc(20px + env(safe-area-inset-bottom, 0px));
    display: flex; flex-direction: column; overflow: hidden;
  }
  .add-to-league-sheet h3 { margin: 0 0 4px; font-size: 1rem; }
  .add-to-league-sub { font-size: 0.8rem; color: var(--text-dim); margin: 0 0 14px; }
  .add-to-league-step-label { font-size: 0.68rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); margin: 0 0 8px; }
  .add-to-league-team-row { display: flex; gap: 10px; margin-bottom: 16px; }
  .add-to-league-team-opt {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 10px 8px;
    border: 2px solid var(--card-border); border-radius: 12px; cursor: pointer; background: none; font-family: inherit;
  }
  .add-to-league-team-opt.selected { border-color: var(--accent); background: rgba(var(--accent-rgb),0.1); }
  /* flex: 1 1 auto + min-height: 0 makes this the piece that gives way when
     the league list is long, so the CTA row below always stays on screen. */
  .add-to-league-list {
    flex: 1 1 auto; min-height: 0; overflow-y: auto; -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px;
  }
  .add-to-league-item {
    display: flex; align-items: center; gap: 12px; padding: 10px 12px; border: 1px solid var(--card-border);
    border-radius: 12px; cursor: pointer;
  }
  .add-to-league-item.checked { border-color: var(--accent); background: rgba(var(--accent-rgb),0.08); }
  .add-to-league-item-name { font-size: 0.84rem; font-weight: 700; }
  .add-to-league-item-meta { font-size: 0.7rem; color: var(--text-dim); }
  .add-to-league-cta { display: flex; gap: 10px; flex-shrink: 0; }
  .add-to-league-cta button {
    flex: 1; border-radius: 10px; padding: 12px; font-size: 0.86rem; font-weight: 800; cursor: pointer;
    font-family: inherit; border: 1px solid var(--card-border); background: var(--surface-2, transparent); color: var(--text);
  }
  .add-to-league-cta button.primary { background: var(--accent); border-color: var(--accent); color: var(--card-bg); }
  .add-to-league-cta button.primary:disabled { opacity: 0.45; cursor: default; }
  .add-to-league-per-league-status { font-size: 0.72rem; margin-top: 2px; }
  .add-to-league-per-league-status.err { color: var(--pill-bad-text); }
  .add-to-league-added-strip {
    display: flex; align-items: center; gap: 7px; font-size: 0.74rem; font-weight: 700; color: var(--win);
    background: rgba(var(--win-rgb),0.1); padding: 9px 14px; border-radius: 8px; margin: 10px -14px -12px;
  }

  .rank-pill {
    display: inline-block; padding: 1px 6px; border-radius: 999px; font-size: 0.68rem; margin-left: 4px;
    border: 1px solid transparent;
  }
  .rank-good { background: rgba(var(--win-rgb),0.18); color: var(--pill-good-text); }
  .rank-mid { background: rgba(var(--warn-rgb),0.18); color: var(--warn); }
  .rank-bad { background: rgba(var(--out-rgb),0.18); color: var(--pill-bad-text); }
  .rank-good-slight { background: rgba(var(--win-rgb),0.10); color: var(--pill-good-text); }
  .rank-good-moderate {
    background: rgba(var(--win-rgb),0.24); color: var(--pill-good-text);
    border-color: rgba(var(--win-rgb),0.65); font-weight: 700;
  }
  .rank-good-significant { background: var(--win); color: var(--pill-good-fill-text); border-color: var(--win); font-weight: 800; }
  .rank-bad-slight { background: rgba(var(--out-rgb),0.10); color: var(--pill-bad-text); }
  .rank-bad-moderate {
    background: rgba(var(--out-rgb),0.24); color: var(--pill-bad-text);
    border-color: rgba(var(--out-rgb),0.65); font-weight: 700;
  }
  .rank-bad-significant { background: var(--out); color: var(--pill-bad-fill-text); border-color: var(--out); font-weight: 800; }
  /* Now lives inside a card's Full Details panel, directly under the pills
     it explains, so the top margin (and no bottom one) is what separates it
     from the stat table above. */
  .gradation-legend {
    display: flex; flex-wrap: wrap; gap: 14px; align-items: center; font-size: 0.68rem;
    color: var(--text-dim); background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 8px; padding: 8px 12px; margin-top: 10px;
  }
  /* flex-wrap so the label + rank pills reflow on a narrow card instead of
     overflowing the legend box (the parent already wraps; the groups didn't). */
  .gradation-legend-group { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  .gradation-legend-label { font-weight: 600; color: var(--text); margin-right: 2px; }
  .gradation-legend .rank-pill { margin-left: 0; }
  .impact-note {
    font-size: 0.72rem; border-radius: 8px; padding: 6px 10px;
    display: flex; flex-direction: column; gap: 5px; align-items: stretch;
  }
  /* The curated line. Was the whole of .impact-note before the ESPN layer
     was added underneath it; the flex row moved here so a second, sourced
     line can sit below without disturbing it. */
  .impact-head { display: flex; gap: 6px; align-items: flex-start; }

  /* The ESPN layer. Deliberately quieter than the line above - it's the wire
     report, not the analysis. Only rendered when it carries something the
     curated line doesn't: a disagreement, a return date, or the fact that
     ESPN has no record at all. Agreement with nothing to add renders no
     second line, which is most rows. */
  .injury-source {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
    font-size: 0.68rem; line-height: 1.45; padding-top: 5px;
    border-top: 1px dashed currentColor; opacity: 0.85; width: 100%;
  }
  .injury-source-tag {
    font-weight: 750; letter-spacing: 0.3px; text-transform: uppercase; font-size: 0.6rem;
    padding: 1px 6px; border-radius: 999px; white-space: nowrap;
    color: var(--edge-b); background: rgba(122,184,255,0.14); border: 1px solid rgba(122,184,255,0.3);
  }
  html[data-theme="light"] .injury-source-tag { background: rgba(29,111,201,0.1); border-color: rgba(29,111,201,0.3); }
  .injury-source-time { color: var(--text-dim); font-size: 0.64rem; }
  .injury-source-body { flex: 1 1 auto; min-width: 0; }
  .injury-return { color: var(--text-dim); }
  /* Agreement collapses to one line; a return date rides inline on it rather
     than earning a second row of its own. */
  .injury-return-inline { color: var(--text-dim); font-weight: 600; white-space: nowrap; }
  .injury-none-note { font-size: 0.64rem; color: var(--text-dim); font-style: italic; }
  /* Brand orange, not loss-red: a disagreement between two sources is a
     signal, not a bad outcome. */
  .injury-disagree {
    font-weight: 700; font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.3px;
    padding: 1px 6px; border-radius: 999px; white-space: nowrap;
    color: var(--blitz-orange); background: rgba(253,127,1,0.14); border: 1px solid rgba(253,127,1,0.35);
  }
  html[data-theme="light"] .injury-disagree { background: rgba(179,89,0,0.1); border-color: rgba(179,89,0,0.35); }
  .injury-fresh {
    font-weight: 700; font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.3px;
    padding: 1px 6px; border-radius: 999px; white-space: nowrap;
    color: var(--win); background: rgba(var(--win-rgb),0.14); border: 1px solid rgba(var(--win-rgb),0.35);
  }
  .impact-out { background: rgba(var(--out-rgb),0.12); color: var(--pill-out-text); border: 1px solid rgba(var(--out-rgb),0.3); }
  .impact-questionable { background: rgba(var(--warn-rgb),0.12); color: var(--pill-questionable-text); border: 1px solid rgba(var(--warn-rgb),0.3); }
  .impact-active { background: rgba(159,176,201,0.1); color: var(--text-dim); border: 1px solid var(--card-border); }
  .game-page { max-width: 720px; margin: 0 auto; }

  /* Shared section chrome for the sportsbook-compare and head-to-head blocks
     on the per-game page. Reuses .section-title/.table-scroll/.linescore-table/
     .teamstats-table class names so HTML lifted straight out of the static
     historical archive pages (H2HRow's box-score fetch) picks up matching
     styling with zero extra mapping. */
  .game-page-section {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 16px; margin-top: 16px;
  }
  .section-title {
    display: flex; align-items: center; gap: 6px; font-size: 0.78rem; text-transform: uppercase;
    letter-spacing: 0.5px; color: var(--text-dim); font-weight: 700; margin: 0 0 12px;
  }
  .section-title-icon { height: 14px; width: auto; display: block; flex-shrink: 0; }
  .table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .linescore-table, .teamstats-table, .odds-compare-table {
    width: 100%; border-collapse: collapse; font-size: 0.82rem;
  }
  .linescore-table th, .linescore-table td,
  .teamstats-table th, .teamstats-table td,
  .odds-compare-table th, .odds-compare-table td {
    padding: 7px 10px; text-align: center; border-bottom: 1px solid var(--card-border);
  }
  .linescore-table th, .teamstats-table th, .odds-compare-table th {
    color: var(--text-dim); font-weight: 600; font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.3px;
  }
  .linescore-table td:first-child, .linescore-table th:first-child,
  .teamstats-table td:first-child, .teamstats-table th:first-child,
  .odds-compare-table td:first-child, .odds-compare-table th:first-child {
    text-align: left;
  }
  .odds-compare-table td.odds-best { color: var(--win); font-weight: 800; }
  .odds-row-locked td { color: var(--text-dim); }
  .odds-row-locked td:first-child { opacity: 0.65; }
  .odds-redact {
    display: inline-block; height: 9px; width: 70%; min-width: 26px; border-radius: 3px;
    vertical-align: middle;
    background: repeating-linear-gradient(135deg,
      rgba(var(--accent-rgb),0.20) 0 5px, rgba(var(--accent-rgb),0.08) 5px 10px);
  }
  .odds-est-tag { color: var(--text-dim); font-weight: 500; font-size: 0.68rem; font-style: italic; }
  .odds-asof-note { font-size: 0.68rem; color: var(--text-dim); margin-top: 8px; }

  .h2h-summary { font-size: 0.82rem; color: var(--text-dim); margin: -4px 0 12px; }
  .h2h-list { display: flex; flex-direction: column; gap: 8px; }
  .h2h-row { border: 1px solid var(--card-border); border-radius: 10px; overflow: hidden; }
  .h2h-row-header {
    width: 100%; display: block; padding: 9px 12px 10px;
    background: none; border: none; color: var(--text); cursor: pointer; text-align: left;
    font-family: inherit;
  }
  /* Line 1: context on the left, the box score affordance pinned right.
     Line 2 (.h2h-matchup) then gets the full row width to itself, which is
     what the old three-column layout couldn't give it - the matchup was
     squeezed between a nowrap date and a nowrap toggle and wrapped into a
     multi-line stack on a phone. */
  .h2h-caption-row {
    display: flex; align-items: baseline; justify-content: space-between; gap: 10px;
    margin-bottom: 3px;
  }
  .h2h-caption {
    min-width: 0; color: var(--text-dim); font-size: 0.65rem; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.04em;
  }
  .h2h-matchup { min-width: 0; font-size: 0.88rem; font-weight: 700; }
  .h2h-at { color: var(--text-dim); font-weight: 600; margin: 0 3px; }
  .h2h-winner { color: var(--win); font-weight: 800; }
  .h2h-toggle {
    flex: none; color: var(--accent); font-size: 0.7rem; font-weight: 700; white-space: nowrap;
  }
  /* Sits inside a .game-page-section, which already provides the padding the
     news feed's version of this button compensates for with its own top
     margin. */
  .h2h-more-btn { margin: 12px auto 0; font-size: 0.78rem; padding: 9px 18px; }
  /* Same control inside a game card's Full Details panel, where the notes
     above it are tighter, so it sits closer to them. */
  .injury-more-btn { margin: 10px auto 2px; font-size: 0.74rem; padding: 8px 16px; }
  .h2h-boxscore { padding: 4px 12px 14px; border-top: 1px dashed var(--card-border); }
  .h2h-teamstats { margin-top: 14px; }
  .h2h-fullpage-link {
    display: inline-block; margin-top: 10px; color: var(--accent); font-size: 0.72rem; font-weight: 700; text-decoration: none;
  }
  .h2h-fullpage-link:hover { text-decoration: underline; }
  .injury-report { margin-bottom: 18px; }
  .injury-report-title { font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); margin: 0 0 8px; }
  .injury-list { display: flex; flex-direction: column; gap: 6px; }

  .injury-info-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 15px; height: 15px; margin-left: 6px; padding: 0; vertical-align: middle;
    position: relative; top: -1px;
    border: none; border-radius: 50%; background: rgba(var(--accent-rgb),0.18);
    color: var(--accent); opacity: 0.9; font-size: 0.68rem; font-weight: 800; font-style: italic; font-family: Georgia, "Times New Roman", serif; line-height: 1;
    cursor: pointer; transition: background 0.15s ease, opacity 0.15s ease, transform 0.1s ease;
  }
  .injury-info-btn:hover, .injury-info-btn:focus-visible {
    opacity: 1; background: rgba(var(--accent-rgb),0.32); transform: scale(1.12);
  }
  .injury-modal-overlay {
    position: fixed; inset: 0; background: rgba(4,8,16,0.6); backdrop-filter: blur(2px);
    display: flex; align-items: center; justify-content: center; padding: 16px; z-index: 1000;
  }
  .injury-modal {
    position: relative; width: 100%; max-width: 420px; max-height: 85vh; max-height: 85dvh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px;
    padding: 22px 20px 18px; box-shadow: 0 24px 60px rgba(0,0,0,0.45);
  }
  .injury-modal-close {
    position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; border-radius: 50%;
    border: 1px solid var(--card-border); background: var(--surface-2); color: var(--text);
    font-size: 1.1rem; line-height: 1; cursor: pointer;
  }
  .injury-modal-header { display: flex; align-items: flex-start; gap: 10px; padding-right: 30px; margin-bottom: 14px; }
  .injury-modal-status-icon { font-size: 1.1rem; margin-top: 2px; }
  .injury-modal-name { font-size: 1.05rem; font-weight: 700; }
  .injury-modal-meta { font-size: 0.78rem; color: var(--text-dim); margin-top: 2px; }
  .injury-modal-body { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
  .injury-modal-row { display: flex; gap: 8px; font-size: 0.85rem; }
  .injury-modal-label { flex: 0 0 60px; color: var(--text-dim); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.4px; padding-top: 2px; }
  .injury-modal-value { font-weight: 600; }
  .injury-modal-note { font-size: 0.85rem; color: var(--text); line-height: 1.45; margin: 4px 0 0; }
  .injury-modal-empty { color: var(--text-dim); }
  .injury-modal-disclaimer { font-size: 0.68rem; color: var(--text-dim); margin: 14px 0 0; padding-top: 10px; border-top: 1px solid var(--card-border); }

  /* Player name -> career history (NCAA/NFL background, rookie marker) */
  .roster-player-name-btn, .depth-player-name-btn {
    background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit;
    cursor: pointer; text-align: left;
  }
  .roster-player-name-btn:hover, .roster-player-name-btn:focus-visible,
  .depth-player-name-btn:hover, .depth-player-name-btn:focus-visible {
    color: var(--accent); text-decoration: underline;
  }
  .rookie-badge {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 15px; height: 15px; padding: 0 3px; margin-left: 5px;
    border-radius: 4px; background: rgba(var(--accent-rgb),0.18); color: var(--accent);
    font-size: 0.62rem; font-weight: 800; line-height: 1; vertical-align: 2px;
  }
  .roster-injury-flag {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 15px; height: 15px; padding: 0 3px; margin-left: 5px;
    border-radius: 4px; flex-shrink: 0;
    font-size: 0.66rem; font-weight: 800; line-height: 1; vertical-align: 2px;
  }
  .roster-injury-flag.impact-out { background: rgba(var(--out-rgb),0.18); color: var(--out); }
  .roster-injury-flag.impact-questionable { background: rgba(var(--warn-rgb),0.18); color: var(--pill-questionable-text); }
  .player-modal-headshot {
    width: 52px; height: 52px; border-radius: 50%; object-fit: cover; background: var(--surface-2);
    flex-shrink: 0; border: 1px solid var(--card-border);
  }
  .player-modal-section-title {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim);
    font-weight: 700; margin: 14px 0 6px;
  }
  .injury-modal-body .player-modal-section-title:first-child { margin-top: 0; }
  .player-history-table { width: 100%; border-collapse: collapse; font-size: 0.82rem; margin-top: 2px; }
  .player-history-table th, .player-history-table td { text-align: left; padding: 4px 6px 4px 0; border-bottom: 1px solid var(--card-border); }
  .player-history-table th { color: var(--text-dim); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.3px; font-weight: 700; }
  .player-history-table tr:last-child td { border-bottom: none; }
  @media (max-width: 640px) {
    .injury-modal-overlay { align-items: flex-end; padding: 0; }
    .injury-modal {
      max-width: 100%; max-height: 80vh; border-radius: 16px 16px 0 0;
      padding: 20px 16px 24px; animation: injury-sheet-up 0.18s ease-out;
    }
  }
  @keyframes injury-sheet-up {
    from { transform: translateY(16px); opacity: 0.6; }
    to { transform: translateY(0); opacity: 1; }
  }

  /* ---- Terms of Service consent (TermsConsentModal) ----
     z-index sits above every other overlay in the app (highest in use is
     3200) because the blocking variant has to cover whatever the reader
     happened to have open when their profile came back needing acceptance,
     including the Netlify Identity widget itself. */
  .terms-modal-overlay {
    position: fixed; inset: 0; background: rgba(4,8,16,0.72); backdrop-filter: blur(3px);
    display: flex; align-items: center; justify-content: center; padding: 16px; z-index: 3400;
  }
  .terms-modal {
    position: relative; width: 100%; max-width: 460px; max-height: 92vh; max-height: 92dvh;
    overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px;
    padding: 22px 20px 18px; box-shadow: 0 24px 60px rgba(0,0,0,0.5);
  }
  /* Colour set explicitly rather than inherited from body. Every other
     modal title in here relies on that inheritance and gets away with it,
     but this one can render over the Identity widget's own stacking
     context, and a heading that silently falls back to the UA default
     black on a dark card is unreadable. */
  .terms-modal-title { font-size: 1.08rem; font-weight: 800; margin: 0 0 6px; color: var(--text); }
  .terms-modal-lede { font-size: 0.85rem; line-height: 1.55; color: var(--text-dim); margin: 0 0 14px; }
  .terms-modal-points { margin: 0 0 16px; padding: 0; list-style: none; }
  .terms-modal-points li {
    position: relative; font-size: 0.82rem; line-height: 1.5; color: var(--text);
    padding-left: 20px; margin-bottom: 8px;
  }
  .terms-modal-points li::before {
    content: ""; position: absolute; left: 4px; top: 0.55em; width: 6px; height: 6px;
    border-radius: 50%; background: var(--accent);
  }
  /* The whole row is the hit target, not just the 18px box - this is the one
     control the reader has to operate before they can get an account, and a
     checkbox-sized tap target on a phone is the wrong place to be stingy. */
  .terms-modal-check {
    display: flex; gap: 11px; align-items: flex-start; cursor: pointer;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 11px;
    padding: 13px 14px; margin: 0 0 14px;
  }
  .terms-modal-check:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
  .terms-modal-check input {
    flex: 0 0 auto; width: 18px; height: 18px; margin: 1px 0 0; accent-color: var(--accent); cursor: pointer;
  }
  .terms-modal-check span { font-size: 0.84rem; line-height: 1.5; color: var(--text); }
  .terms-modal-check a { color: var(--accent); font-weight: 650; }
  /* --out, not --lose: --lose is the muted grey used for a losing team's
     row, not an error red. */
  .terms-modal-error {
    font-size: 0.8rem; line-height: 1.45; color: var(--out); margin: 0 0 12px;
  }
  .terms-modal-actions { display: flex; gap: 9px; }
  .terms-modal-btn {
    flex: 1; font-family: inherit; font-size: 0.88rem; font-weight: 700; padding: 11px 12px;
    border-radius: 10px; cursor: pointer; border: 1px solid var(--card-border);
    background: var(--surface-2); color: var(--text); transition: opacity 0.15s;
  }
  .terms-modal-btn-primary {
    background: var(--accent); border-color: var(--accent); color: #08131f;
  }
  .terms-modal-btn:disabled { opacity: 0.45; cursor: not-allowed; }
  .terms-modal-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .terms-modal-foot {
    font-size: 0.73rem; line-height: 1.5; color: var(--text-dim); margin: 13px 0 0; text-align: center;
  }
  @media (max-width: 480px) {
    .terms-modal-overlay { align-items: flex-end; padding: 0; }
    .terms-modal {
      max-width: none; max-height: 94vh; max-height: 94dvh;
      border-radius: 16px 16px 0 0; border-bottom: 0; padding: 20px 16px calc(16px + env(safe-area-inset-bottom));
    }
    .terms-modal-actions { flex-direction: column-reverse; }
  }

  .settings-modal-overlay {
    position: fixed; inset: 0; background: rgba(4,8,16,0.6); backdrop-filter: blur(2px);
    display: flex; align-items: center; justify-content: center; padding: 16px; z-index: 1000;
  }
  .settings-modal {
    position: relative; width: 100%; max-width: 440px; max-height: 88vh; max-height: 88dvh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px;
    padding: 22px 20px 18px; box-shadow: 0 24px 60px rgba(0,0,0,0.45);
  }
  .settings-modal-close {
    position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; border-radius: 50%;
    border: 1px solid var(--card-border); background: var(--surface-2); color: var(--text);
    font-size: 1.1rem; line-height: 1; cursor: pointer;
  }
  .settings-modal-title { font-size: 1.05rem; font-weight: 800; margin: 0 0 16px; padding-right: 30px; }
  .settings-section { margin-bottom: 20px; }
  .settings-section:last-child { margin-bottom: 4px; }
  .settings-section-title {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim);
    font-weight: 700; margin: 0 0 10px;
  }
  .settings-section .book-picker { width: 100%; display: block; }
  .settings-section .theme-toggle { width: 100%; }
  .settings-section .book-picker-trigger { width: 100%; justify-content: space-between; box-sizing: border-box; }
  .settings-section .book-picker-panel { position: static; margin-top: 8px; box-shadow: none; width: 100%; box-sizing: border-box; }
  .settings-section .theme-toggle { justify-content: space-between; }
  .settings-section .theme-toggle-btn { flex: 1; justify-content: center; }
  .settings-section .theme-toggle-label { display: inline; }

  /* ---- Profile tab (SettingsModal) ---- */
  .settings-tabbar {
    display: flex; gap: 6px; background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 10px; padding: 4px; margin-bottom: 18px;
  }
  .settings-tab-btn {
    flex: 1; border: none; background: transparent; color: var(--text-dim); font-family: inherit;
    font-size: 0.82rem; font-weight: 700; padding: 8px 6px; border-radius: 7px; cursor: pointer;
    transition: background 0.15s, color 0.15s;
  }
  .settings-tab-btn.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); }
  .settings-tab-btn:hover:not(.active) { color: var(--text); }

  /* ---- Notifications tab (SettingsModal) ---- */
  .notif-row { display: flex; gap: 12px; align-items: flex-start; padding: 12px 0; }
  .notif-row + .notif-row { border-top: 1px solid var(--card-border); }
  .notif-row-body { flex: 1; min-width: 0; }
  .notif-row-name { font-size: 0.88rem; font-weight: 650; margin: 0 0 3px; color: var(--text); }
  .notif-row-desc { font-size: 0.78rem; color: var(--text-dim); margin: 0; line-height: 1.5; }
  .notif-when {
    display: inline-block; margin-top: 8px; font-size: 0.72rem; font-weight: 650;
    color: var(--accent); background: rgba(var(--accent-rgb),0.12);
    border: 1px solid rgba(var(--accent-rgb),0.3); padding: 3px 9px; border-radius: 999px;
  }
  .notif-switch {
    position: relative; width: 46px; height: 27px; flex: 0 0 46px; border-radius: 999px;
    cursor: pointer; background: var(--lose); border: none; padding: 0;
    transition: background 0.16s; -webkit-appearance: none; appearance: none;
  }
  .notif-switch::after {
    content: ""; position: absolute; top: 3px; left: 3px; width: 21px; height: 21px;
    border-radius: 50%; background: #fff; transition: transform 0.16s;
    box-shadow: 0 1px 3px rgba(0,0,0,0.35);
  }
  .notif-switch[aria-checked="true"] { background: var(--accent); }
  .notif-switch[aria-checked="true"]::after { transform: translateX(19px); }
  .notif-switch:disabled { opacity: 0.45; cursor: not-allowed; }
  .notif-loading-note {
    font-size: 0.74rem; font-weight: 600; color: var(--text-dim);
    margin: 0 0 12px; letter-spacing: 0.01em;
  }
  .notif-switch:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
  .notif-tz {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
    font-size: 0.78rem; color: var(--text-dim); margin-top: 14px; padding-top: 14px;
    border-top: 1px solid var(--card-border);
  }
  .notif-tz b { color: var(--text); font-weight: 600; }
  .notif-cap {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    font-size: 0.78rem; color: var(--text-dim); background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 9px; padding: 11px 13px; margin-top: 18px;
  }
  .notif-cap b { color: var(--text); }
  .notif-status {
    font-size: 0.72rem; font-weight: 600; margin: 8px 0 0; min-height: 1em;
  }
  .notif-status.err { color: var(--out); }
  .notif-status.ok { color: var(--win); }

  /* ---- Push section (Alerts tab) ---- */
  .notif-section-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
  .notif-section-head .settings-section-title { margin: 0; }
  .notif-channel-tag {
    font-size: 0.64rem; font-weight: 750; letter-spacing: 0.4px; text-transform: uppercase;
    padding: 3px 8px; border-radius: 999px; white-space: nowrap;
  }
  .notif-channel-tag.push { color: var(--edge-b); background: rgba(122,184,255,0.12); border: 1px solid rgba(122,184,255,0.32); }
  html[data-theme="light"] .notif-channel-tag.push { background: rgba(29,111,201,0.10); border-color: rgba(29,111,201,0.3); }
  .notif-channel-tag.email { color: var(--accent); background: rgba(var(--accent-rgb),0.12); border: 1px solid rgba(var(--accent-rgb),0.3); }
  /* Pro badging is built but hidden until the tier is announced - the label
     is meant to land ahead of any gating, not alongside it. */
  .notif-channel-tag.pro { color: var(--blitz-orange); background: rgba(253,127,1,0.12); border: 1px solid rgba(253,127,1,0.32); }
  html[data-theme="light"] .notif-channel-tag.pro { background: rgba(179,89,0,0.10); border-color: rgba(179,89,0,0.3); }

  .notif-enable {
    border: 1px solid var(--card-border); border-radius: 11px; padding: 14px;
    background: var(--surface-2); margin-bottom: 16px;
  }
  .notif-enable-title { font-size: 0.86rem; font-weight: 700; margin: 0 0 5px; }
  .notif-enable-desc { font-size: 0.78rem; color: var(--text-dim); margin: 0 0 12px; line-height: 1.5; }
  .notif-enable-desc:last-child { margin-bottom: 0; }
  .notif-enable-btn {
    font: inherit; font-size: 0.82rem; font-weight: 700; width: 100%; padding: 10px;
    border-radius: 9px; border: none; background: var(--accent); color: #06231f; cursor: pointer;
  }
  html[data-theme="light"] .notif-enable-btn { color: #fff; }
  .notif-enable-btn:disabled { opacity: 0.55; cursor: not-allowed; }
  .notif-enable-btn:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
  .notif-enable.blocked { border-color: rgba(var(--out-rgb),0.45); background: rgba(var(--out-rgb),0.07); }
  .notif-enable.ready { border-color: rgba(122,184,255,0.4); background: rgba(122,184,255,0.07); }
  html[data-theme="light"] .notif-enable.ready { background: rgba(29,111,201,0.06); border-color: rgba(29,111,201,0.3); }
  .notif-device { display: flex; align-items: center; gap: 8px; font-size: 0.78rem; color: var(--text-dim); flex-wrap: wrap; }
  .notif-device-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--win); flex: 0 0 7px; }
  .notif-device-name { color: var(--text); font-weight: 600; }
  .notif-device-actions { display: flex; gap: 10px; margin-left: auto; }
  .notif-link-btn {
    font-size: 0.74rem; color: var(--accent); background: none; border: none; cursor: pointer;
    font-family: inherit; font-weight: 650; padding: 0;
  }
  .notif-link-btn:disabled { opacity: 0.5; cursor: not-allowed; }
  .notif-link-btn.danger { color: var(--out); }

  .notif-seg {
    display: flex; gap: 4px; margin-top: 9px; background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 8px; padding: 3px;
  }
  .notif-seg-btn {
    flex: 1; border: none; background: transparent; color: var(--text-dim); font-family: inherit;
    font-size: 0.72rem; font-weight: 700; padding: 6px 4px; border-radius: 6px; cursor: pointer; white-space: nowrap;
  }
  .notif-seg-btn[aria-pressed="true"] { background: rgba(var(--accent-rgb),0.16); color: var(--accent); }
  .notif-seg-btn:disabled { opacity: 0.45; cursor: not-allowed; }
  .notif-seg-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

  .notif-sub { padding-left: 12px; border-left: 2px solid var(--card-border); margin: 2px 0 2px 2px; }
  .notif-sub .notif-row { border-top: none; }

  .notif-fav-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 12px; align-items: center; }
  .notif-fav-chip {
    display: inline-flex; align-items: center; gap: 5px; font-size: 0.74rem; font-weight: 700;
    padding: 4px 10px; border-radius: 999px; background: var(--surface-2);
    border: 1px solid var(--card-border); color: var(--text);
  }
  .notif-fav-chip .star { color: var(--star); font-size: 0.8rem; }

  .notif-follow-list { display: flex; flex-direction: column; gap: 6px; margin: 2px 0 10px; }
  .notif-follow-row {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    padding: 8px 10px; border-radius: 9px; background: var(--surface-2);
    border: 1px solid var(--card-border);
  }
  .notif-follow-teams { font-size: 0.78rem; font-weight: 700; color: var(--text); }
  .notif-follow-when { font-size: 0.68rem; color: var(--text-dim); font-weight: 600; }
  .notif-follow-drop {
    background: none; border: none; font: inherit; font-size: 0.72rem; font-weight: 700;
    color: var(--text-dim); cursor: pointer; padding: 4px 6px; flex-shrink: 0;
  }
  .notif-follow-drop:hover { color: var(--pill-bad-text); }
  .notif-follow-drop:disabled { opacity: 0.5; cursor: default; }
  .notif-empty {
    font-size: 0.78rem; color: var(--text-dim); background: var(--surface-2);
    border: 1px dashed var(--card-border); border-radius: 9px; padding: 14px;
    text-align: center; line-height: 1.5;
  }
  .notif-empty b { color: var(--text); }
  .notif-quiet { display: flex; align-items: center; gap: 8px; margin-top: 10px; font-size: 0.78rem; color: var(--text-dim); flex-wrap: wrap; }
  .notif-quiet select {
    font: inherit; font-size: 0.76rem; font-weight: 600; color: var(--text); background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 7px; padding: 5px 8px;
  }
  @media (prefers-reduced-motion: reduce) {
    .notif-switch, .notif-switch::after { transition: none; }
  }

  .avatar-preview-row { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
  .avatar-preview {
    width: 56px; height: 56px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
    font-size: 1.6rem; background: var(--accent); color: #062018; font-weight: 800; border: 2px solid var(--accent);
    flex-shrink: 0; overflow: hidden;
  }
  /* With a glyph picked this is a container for artwork, so it drops the
     solid accent fill the initial-letter state needs for contrast - same
     split as .account-avatar / .account-avatar.has-glyph. */
  .avatar-preview.has-glyph { background: rgba(var(--accent-rgb),0.16); color: inherit; }
  .avatar-preview-name { font-size: 0.95rem; font-weight: 700; color: var(--text); }
  .avatar-preview-hint { font-size: 0.76rem; color: var(--text-dim); margin-top: 2px; }
  .avatar-grid {
    display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px;
    border: 1px solid var(--card-border); border-radius: 12px; padding: 10px; background: var(--surface-2);
  }
  .avatar-option {
    aspect-ratio: 1; border-radius: 50%; border: 2px solid transparent; background: rgba(var(--accent-rgb),0.08);
    display: flex; align-items: center; justify-content: center; font-size: 1.15rem; cursor: pointer;
    transition: border-color 0.15s, background 0.15s; padding: 0; overflow: hidden;
  }
  .avatar-option:hover { background: rgba(var(--accent-rgb),0.16); }
  .avatar-option.selected { border-color: var(--accent); background: rgba(var(--accent-rgb),0.22); }
  .avatar-glyph-img { width: 70%; height: 70%; object-fit: contain; }
  .avatar-preview .avatar-glyph-img { width: 62%; height: 62%; }
  .branded-orange { color: var(--blitz-orange); }
  .branded-teal { color: var(--accent); }

  .profile-field { margin-bottom: 16px; }
  .profile-input {
    width: 100%; background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 9px;
    color: var(--text); font-family: inherit; font-size: 0.92rem; padding: 10px 12px; outline: none;
    transition: border-color 0.15s; box-sizing: border-box;
  }
  .profile-input:focus { border-color: var(--accent); }
  .profile-input:disabled { color: var(--text-dim); cursor: not-allowed; }
  .profile-field-hint { font-size: 0.74rem; color: var(--text-dim); margin-top: 5px; line-height: 1.4; }
  .profile-field-label { display: block; font-size: 0.78rem; color: var(--text-dim); margin-bottom: 5px; }
  /* Inline validation for the password panel. Tinted rather than plain red
     text so it reads as a state of the form, matching the delete panel's
     error, and it sits above the button so it can't be missed by someone
     already reaching for it. */
  .profile-error {
    margin: 10px 0 0; font-size: 0.8rem; font-weight: 600; line-height: 1.4;
    color: var(--out); background: rgba(var(--out-rgb), 0.09);
    border: 1px solid rgba(var(--out-rgb), 0.3); border-radius: 8px; padding: 9px 12px;
  }
  .profile-row-inline { display: flex; gap: 8px; align-items: center; }
  .profile-row-inline .profile-input { flex: 1; }
  .link-btn {
    background: none; border: none; color: var(--accent); font-family: inherit; font-size: 0.82rem;
    font-weight: 700; cursor: pointer; padding: 4px 0; white-space: nowrap;
  }
  .link-btn:hover { text-decoration: underline; }
  .save-btn {
    width: 100%; background: var(--accent); color: #06201d; border: none; border-radius: 10px;
    font-family: inherit; font-size: 0.9rem; font-weight: 800; padding: 12px; cursor: pointer;
    margin-top: 4px; transition: filter 0.15s;
  }
  .save-btn:hover { filter: brightness(1.06); }
  .save-btn:disabled { opacity: 0.5; cursor: not-allowed; }
  .profile-toast {
    margin-top: 10px; background: var(--win); color: #06210f; font-weight: 700; font-size: 0.82rem;
    padding: 9px 14px; border-radius: 8px; text-align: center;
  }

  /* Account deletion, at the bottom of the Profile tab. Bordered and tinted
     rather than styled like the rest of the settings sections: it's the one
     control on the screen that can't be undone, and it should not look like
     a neighbour of "Display Name". Deliberately quiet until opened - the
     outline button reads as available, not as a dare. */
  .danger-section {
    margin-top: 26px; border: 1px solid rgba(var(--out-rgb), 0.35);
    background: rgba(var(--out-rgb), 0.05); border-radius: 12px; padding: 14px;
  }
  .danger-title { color: var(--out); }
  .danger-copy { font-size: 0.8rem; color: var(--text-dim); line-height: 1.5; margin: 0 0 10px; }
  .danger-list {
    font-size: 0.8rem; color: var(--text-dim); line-height: 1.55;
    margin: 0 0 10px; padding-left: 18px;
  }
  .danger-list li { margin-bottom: 2px; }
  .danger-label { font-size: 0.74rem; color: var(--text-dim); margin-bottom: 5px; line-height: 1.4; }
  .danger-btn {
    background: rgba(var(--out-rgb), 0.12); border: 1px solid rgba(var(--out-rgb), 0.5);
    color: var(--out); font-family: inherit; font-size: 0.85rem; font-weight: 800;
    padding: 10px 16px; border-radius: 9px; cursor: pointer; transition: border-color 0.15s;
  }
  .danger-btn:hover:not(:disabled) { border-color: var(--out); }
  .danger-btn:disabled { opacity: 0.45; cursor: not-allowed; }
  /* Desktop: Cancel then the destructive action, right-aligned. */
  .danger-actions { display: flex; gap: 10px; align-items: center; justify-content: flex-end; margin-top: 12px; }
  .danger-error {
    margin: 10px 0 0; font-size: 0.78rem; color: var(--out); font-weight: 700; line-height: 1.45;
  }
  @media (max-width: 420px) {
    /* Stacked once the row would wrap mid-button. column-reverse, not
       column: it keeps Cancel at the BOTTOM, nearest the thumb, so the
       button sitting where a thumb naturally lands is the harmless one. */
    .danger-actions { flex-direction: column-reverse; align-items: stretch; }
    .danger-actions .danger-btn { width: 100%; }
  }

  /* Suspension screen - replaces the whole app, so it owns the viewport
     rather than sitting inside .app's layout. Centred on desktop, top-aligned
     with breathing room on a phone (vertically centring a tall card on a
     short screen pushes the sign-out button off the bottom). */
  .susp-screen {
    min-height: 100vh; min-height: 100dvh; display: flex; align-items: center;
    justify-content: center; padding: 24px 16px; box-sizing: border-box;
  }
  .susp-card {
    width: 100%; max-width: 460px; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; padding: 26px 22px; text-align: center;
  }
  .susp-title { font-size: 1.25rem; font-weight: 800; color: var(--text); margin: 16px 0 10px; }
  .susp-copy { font-size: 0.88rem; color: var(--text-dim); line-height: 1.6; margin: 0 0 12px; }
  .susp-copy a { color: var(--accent); font-weight: 700; }
  .susp-reason {
    font-size: 0.85rem; color: var(--text); line-height: 1.55; text-align: left;
    background: rgba(var(--out-rgb), 0.08); border-left: 3px solid var(--out);
    padding: 10px 12px; border-radius: 0 8px 8px 0; margin: 0 0 14px;
  }
  .susp-btn { margin-top: 6px; width: 100%; }
  @media (min-width: 560px) {
    .susp-card { padding: 32px 30px; }
    .susp-btn { width: auto; min-width: 160px; }
  }

  /* ---- account screens: signup, confirm-your-email, forgot, reset ----

     These live outside .app the same way .susp-screen does, and for the same
     reason: each of them is the whole screen, not a panel inside a working
     app. The confirm-email one in particular gates an account the server
     refuses at every authenticated endpoint (see lib/auth.mts), so rendering
     the normal shell behind it would offer a wall of controls that silently
     fail. */
  /* A modal over the app, not a page that replaces it.

     These are things somebody opened on purpose from a page they were
     already reading - a preview gate, a league CTA, the account menu - and
     replacing the whole screen loses the thing that made them want an
     account thirty seconds ago. It also breaks the back button's meaning:
     "cancel" and "go back to the last page" become the same gesture when
     they shouldn't be.

     Deliberately NOT true of UnverifiedScreen or SuspendedScreen, which stay
     full-screen. Those aren't things anyone opened; they're states the
     server put the account in, where every control behind a modal would be
     one the server refuses. */
  .authscreen {
    position: fixed; inset: 0; z-index: 1200;
    display: flex; align-items: center; justify-content: center;
    padding: 24px 16px calc(24px + env(safe-area-inset-bottom, 0px));
    box-sizing: border-box; overflow-y: auto; -webkit-overflow-scrolling: touch;
  }
  .authscreen-backdrop {
    position: fixed; inset: 0; background: rgba(4,8,16,0.55); z-index: 1199;
  }
  /* Gated on the modal flag so the gate screens, which reuse .authcard, keep
     their plain centred-on-the-page treatment. */
  .authscreen.is-modal .authcard {
    position: relative; z-index: 1201; margin: auto;
    box-shadow: 0 18px 50px rgba(0,0,0,0.45);
  }
  .authcard-close {
    position: absolute; top: 10px; right: 10px; background: none; border: 0;
    color: var(--text-dim); font-size: 1.35rem; line-height: 1; cursor: pointer;
    padding: 6px 9px; border-radius: 8px;
  }
  .authcard-close:hover { color: var(--text); background: var(--surface-2); }
  /* The gate screens keep the old full-page behaviour. */
  .authscreen.is-page {
    position: static; min-height: 100vh; min-height: 100dvh; z-index: auto; overflow: visible;
  }
  .authcard {
    width: 100%; max-width: 430px; background: var(--card-bg);
    border: 1px solid var(--card-border); border-radius: 14px; padding: 24px 20px;
  }
  .authcard-center { text-align: center; }
  .auth-eyebrow {
    margin: 0 0 6px; font-size: 0.66rem; font-weight: 800; letter-spacing: .07em;
    text-transform: uppercase; color: var(--accent);
  }
  .auth-title { margin: 0 0 8px; font-size: 1.22rem; font-weight: 800; line-height: 1.25; color: var(--text); }
  .auth-sub { margin: 0 0 4px; font-size: 0.86rem; line-height: 1.55; color: var(--text-dim); }
  .auth-icon {
    width: 52px; height: 52px; border-radius: 14px; display: grid; place-items: center;
    font-size: 1.5rem; margin: 0 0 14px;
    background: rgba(var(--accent-rgb),0.14); border: 1px solid rgba(var(--accent-rgb),0.32);
  }
  .authcard-center .auth-icon { margin-left: auto; margin-right: auto; }
  .auth-icon.is-warn { background: rgba(var(--warn-rgb),0.14); border-color: rgba(var(--warn-rgb),0.34); }
  .auth-icon.is-err { background: rgba(var(--out-rgb),0.13); border-color: rgba(var(--out-rgb),0.32); }

  .auth-fields { margin: 18px 0 0; }
  .auth-field { margin: 0 0 14px; text-align: left; }
  .auth-field label {
    display: block; font-size: 0.72rem; font-weight: 700; letter-spacing: .03em;
    text-transform: uppercase; color: var(--text-dim); margin: 0 0 6px;
  }
  .auth-field label .auth-opt { text-transform: none; font-weight: 600; }
  .auth-field input {
    width: 100%; box-sizing: border-box; font-family: inherit; font-size: 0.95rem;
    color: var(--text); background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 10px; padding: 11px 12px;
  }
  .auth-field input:focus { outline: none; border-color: rgba(var(--accent-rgb),0.6); }
  .auth-field.has-err input { border-color: rgba(var(--out-rgb),0.65); }
  .auth-hint { margin: 6px 0 0; font-size: 0.72rem; color: var(--text-dim); }
  .auth-err { margin: 6px 0 0; font-size: 0.74rem; line-height: 1.5; color: var(--pill-bad-text); }
  /* Terms acceptance, inside the signup form's field stack. The whole row is
     the hit target rather than just the 18px box - this is the one control
     that has to be operated before an account can exist, and a
     checkbox-sized tap target on a phone is the wrong place to be stingy. */
  .auth-terms-check {
    display: flex; gap: 10px; align-items: flex-start; cursor: pointer;
    padding: 2px 0 0; margin: 2px 0 0;
  }
  .auth-terms-check input {
    flex: 0 0 auto; width: 17px; height: 17px; margin: 1px 0 0;
    accent-color: var(--accent); cursor: pointer;
  }
  .auth-terms-check span { font-size: 0.78rem; line-height: 1.5; color: var(--text-dim); }
  .auth-terms-check a { color: var(--accent); font-weight: 650; }
  .auth-terms-check:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 6px; }
  .auth-err a, .auth-linkish {
    color: var(--accent); font-weight: 700; text-decoration: none; background: none;
    border: 0; font-family: inherit; font-size: inherit; cursor: pointer; padding: 0;
  }
  .auth-linkish:hover, .auth-err a:hover { text-decoration: underline; }

  /* The password field's reveal toggle. Absolutely positioned inside the row
     rather than beside the input, so the input keeps the full width every
     other field has and the fields still line up. */
  .auth-pw-row { position: relative; }
  .auth-pw-row input { padding-right: 62px; }
  .auth-pw-toggle {
    position: absolute; right: 8px; top: 50%; transform: translateY(-50%);
    background: none; border: 0; color: var(--text-dim); cursor: pointer;
    font-size: 0.7rem; font-weight: 800; letter-spacing: .04em; font-family: inherit; padding: 6px;
  }
  .auth-pw-toggle:hover { color: var(--text); }

  /* Four segments, not a numeric score: the point is "longer is better",
     and a percentage invites people to optimise a number that doesn't
     measure what they think it does. */
  .auth-meter { display: flex; gap: 4px; margin: 8px 0 0; }
  .auth-meter i { flex: 1; height: 4px; border-radius: 2px; background: var(--card-border); }
  .auth-meter i.on { background: var(--accent); }
  .auth-meter i.warn { background: var(--warn); }

  .auth-actions { display: flex; flex-direction: column; gap: 8px; margin: 18px 0 0; }
  .auth-btn {
    appearance: none; font-family: inherit; cursor: pointer; border-radius: 10px;
    font-size: 0.86rem; font-weight: 800; padding: 12px 18px; border: 1px solid transparent;
    width: 100%;
  }
  .auth-btn:disabled { opacity: 0.55; cursor: default; }
  .auth-btn-primary { background: var(--accent); color: #05221f; }
  .auth-btn-ghost { background: transparent; border-color: var(--card-border); color: var(--text-dim); }
  .auth-btn-ghost:hover { color: var(--text); border-color: rgba(var(--accent-rgb),0.4); }
  .auth-alt { margin: 16px 0 0; font-size: 0.8rem; color: var(--text-dim); text-align: center; }

  /* Provider button. Neutral surface rather than Google's white-on-white or
     their dark variant, so one treatment reads correctly in both themes -
     the mark itself keeps its own colours, which is the part their
     guidelines actually pin down. */
  .auth-btn-provider {
    display: flex; align-items: center; justify-content: center; gap: 10px;
    background: var(--surface-2); border-color: var(--card-border); color: var(--text);
    margin-top: 18px;
  }
  .auth-btn-provider:hover { border-color: rgba(var(--accent-rgb),0.45); }
  .auth-btn-provider svg { flex: 0 0 auto; }

  .auth-divider {
    display: flex; align-items: center; gap: 12px; margin: 16px 0 0;
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-dim);
  }
  .auth-divider::before, .auth-divider::after {
    content: ""; flex: 1; height: 1px; background: var(--card-border);
  }

  .auth-addr {
    display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.8rem; background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 8px; padding: 5px 9px; margin: 4px 0 0; color: var(--text);
    max-width: 100%; overflow-wrap: anywhere;
  }
  .auth-note {
    margin: 16px 0 0; padding: 11px 12px; border-radius: 10px; font-size: 0.78rem;
    line-height: 1.55; text-align: left; background: var(--surface-2);
    border: 1px solid var(--card-border); color: var(--text-dim);
  }
  .auth-note.is-ok { border-left: 3px solid var(--win); }
  .auth-note.is-err { border-left: 3px solid var(--out); }

  /* The ?verified= outcome, shown at the top of the app rather than as its
     own screen: by the time this renders the person is already where they
     wanted to be, and a full-screen interstitial between them and it would
     be a step for no reason. */
  .verify-banner {
    display: flex; gap: 10px; align-items: flex-start; margin: 0 0 12px;
    padding: 12px 14px; border-radius: 12px; font-size: 0.82rem; line-height: 1.5;
    background: var(--card-bg); border: 1px solid var(--card-border);
  }
  .verify-banner.is-ok { border-left: 4px solid var(--win); }
  .verify-banner.is-warn { border-left: 4px solid var(--warn); }
  .verify-banner.is-err { border-left: 4px solid var(--out); }
  .verify-banner strong { display: block; margin: 0 0 2px; color: var(--text); }
  .verify-banner p { margin: 0; color: var(--text-dim); }
  .verify-banner-x {
    margin-left: auto; background: none; border: 0; color: var(--text-dim);
    font-size: 1.05rem; cursor: pointer; line-height: 1; padding: 2px 4px; flex: 0 0 auto;
  }
  @media (min-width: 560px) {
    .authcard { padding: 30px 28px; }
  }

  .fav-picker-list { max-height: 320px; overflow-y: auto; border: 1px solid var(--card-border); border-radius: 12px; padding: 6px; }
  .fav-picker-group-label {
    font-size: 0.65rem; text-transform: uppercase; letter-spacing: 0.6px; color: var(--text-dim);
    font-weight: 700; padding: 10px 8px 6px;
  }
  .fav-picker-group-label:first-child { padding-top: 4px; }
  .fav-picker-item {
    display: flex; align-items: center; gap: 10px; width: 100%; border-radius: 8px; padding: 6px 8px;
    transition: background 0.12s;
  }
  .fav-picker-item:hover { background: rgba(var(--accent-rgb),0.08); }
  .fav-picker-item img { width: 24px; height: 24px; object-fit: contain; flex-shrink: 0; }
  .fav-picker-item-name { flex: 1; font-size: 0.86rem; }
  .fav-picker-item .star-btn { position: static; flex-shrink: 0; }
  @media (max-width: 640px) {
    .settings-modal-overlay { align-items: flex-end; padding: 0; }
    .settings-modal {
      max-width: 100%; max-height: 85vh; border-radius: 16px 16px 0 0;
      padding: 20px 16px 24px; animation: injury-sheet-up 0.18s ease-out;
    }
  }

  .leagues-signed-out { text-align: center; padding: 16px 4px 6px; }
  .leagues-signed-out p { color: var(--text-dim); font-size: 0.9rem; margin: 0 0 14px; }
  .leagues-empty { color: var(--text-dim); font-size: 0.85rem; margin: 0; }
  .leagues-error { color: var(--out); font-size: 0.82rem; margin: 8px 0 0; }
  .game-permalink {
    display: inline-block; margin-top: 2px; color: var(--accent); font-size: 0.8rem;
    font-weight: 600; text-decoration: none;
  }
  .game-permalink:hover { text-decoration: underline; }
  .leagues-inline-link {
    background: none; border: none; padding: 0; font: inherit; color: inherit;
    text-decoration: underline; cursor: pointer;
  }
  .leagues-join-link-error { margin: 0 0 4px; padding: 10px 12px; background: rgba(220, 80, 80, 0.08); border: 1px solid var(--out); border-radius: 8px; }
  .leagues-auto-join-status { text-align: center; padding: 40px 20px; color: var(--text-dim); }

  .league-list { display: flex; flex-direction: column; gap: 8px; }
  .league-list-item {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    border: 1px solid var(--card-border); border-radius: 10px; padding: 10px 12px;
  }
  /* My Leagues rows only (search results reuse .league-list-item with a
     different inner shape). The tappable name/badge button is a flex item
     with no min-width floor, so gear + invite chip always land in the same
     place on every row instead of being pushed out by the longest format
     label - and on a phone the two actions wrap onto their own line rather
     than overflowing the card. Also drops the button's own border/padding,
     which was drawing a second box inside the row's box. */
  .league-list-item-mine { flex-wrap: wrap; justify-content: flex-end; row-gap: 8px; }
  .league-list-item-mine .league-list-item-btn {
    flex: 1 1 190px; width: auto; min-width: 0; border: none; padding: 0; border-radius: 8px;
  }
  .league-list-item-mine .league-list-item-btn:hover .league-list-item-name,
  .league-list-item-mine .league-list-item-btn:focus-visible .league-list-item-name { color: var(--accent); }
  .league-list-item-mine .league-list-item-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
  .league-list-item-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 8px; margin-left: auto; }
  .league-list-item-main { flex: 1; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
  .league-list-item-name { font-size: 0.9rem; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .league-list-item-meta { font-size: 0.74rem; color: var(--text-dim); }
  .league-invite-chip {
    flex-shrink: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78rem; font-weight: 700;
    letter-spacing: 1px; background: var(--surface-2); border: 1px dashed var(--card-border); color: var(--accent);
    border-radius: 8px; padding: 6px 10px; cursor: pointer;
  }
  .league-invite-chip:hover, .league-invite-chip:focus-visible { border-color: var(--accent); }

  /* Share the join link. Separate from .league-invite-chip on purpose: the
     chip displays and copies the CODE (which is still what somebody reads
     out over the phone or types into the join box), and this shares the
     URL. One control doing both would have to either hide the code or lie
     about what it copies. Sized to .league-btn so the two sit level in the
     header's action row. */
  .league-share-btn {
    flex-shrink: 0; display: inline-flex; align-items: center; gap: 6px;
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text);
    border-radius: 8px; padding: 9px 14px; font-size: 0.86rem; font-weight: 700; cursor: pointer;
  }
  .league-share-btn:hover:not(:disabled), .league-share-btn:focus-visible { border-color: var(--accent); color: var(--accent); }
  .league-share-btn:disabled { opacity: 0.5; cursor: default; }
  .league-share-btn svg { width: 15px; height: 15px; flex: none; }
  .league-share-btn.is-done { border-color: var(--win); color: var(--win); }
  .league-share-btn-sm { padding: 6px 12px; font-size: 0.8rem; }
  /* Icon-only, for the My Leagues row - that row already packs a name, a
     meta line, Manage/Leave and the code chip, and a fourth word wrapped it
     onto a second line on a phone. Confirmation is a checkmark swap rather
     than a label change for the same reason: there's no room to grow. */
  .league-share-btn-icon { padding: 6px 9px; position: relative; }
  /* The visual box matches the code chip beside it (~35x29) so the row stays
     level, but that's well under a 44px touch target. This expands the hit
     area to a measured 46x46 without moving anything - the alternative, growing the
     button itself, pushes the chip out of alignment on every row. */
  .league-share-btn-icon::after { content: ""; position: absolute; inset: -10px -7px; }

  /* The link, spelled out, above the email field in the invite panel.
     Read-only text rather than an <input readonly>: nobody needs to edit
     it, and a text input invites a tap that selects half the URL. */
  .league-share-row { display: flex; gap: 8px; align-items: stretch; }
  .league-share-url {
    flex: 1; min-width: 0; display: flex; align-items: center;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78rem;
    color: var(--text-dim); background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 8px; padding: 9px 12px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .league-share-url b { color: var(--accent); font-weight: 700; }
  @media (max-width: 560px) {
    .league-share-row { flex-direction: column; }
    .league-share-row .league-share-btn { justify-content: center; }
    /* The header's action row went from three controls to four, and four
       overflow a 390px card: .league-header-actions is flex-shrink:0 with
       flex-shrink:0 children, so it sized to max-content and pushed ~17px
       past the card's padding rather than wrapping inside it. Claiming the
       full line gives the existing flex-wrap something finite to wrap
       against. */
    .league-header-actions { flex: 1 1 100%; min-width: 0; }
  }

  .league-form { display: flex; gap: 8px; }
  .league-input {
    flex: 1; min-width: 0; background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 8px;
    padding: 9px 12px; color: var(--text); font-size: 16px;
  }
  @media (hover: hover) and (pointer: fine) {
    .league-input { font-size: 0.88rem; }
  }
  .league-input:focus-visible { outline: none; border-color: var(--accent); }
  .league-input-code { text-transform: uppercase; letter-spacing: 1.5px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
  .league-btn {
    flex-shrink: 0; background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text);
    border-radius: 8px; padding: 9px 16px; font-size: 0.86rem; font-weight: 700; cursor: pointer;
  }
  .league-btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
  .league-btn:disabled { opacity: 0.5; cursor: default; }
  .league-btn-primary { background: var(--accent); border-color: var(--accent); color: #062018; }
  .league-btn-primary:hover:not(:disabled) { filter: brightness(1.08); color: #062018; }
  .league-btn-block { width: 100%; }
  /* Row-sized variant. The league list packs a name, meta line, this button
     and the invite code onto one row, and the default 9px/16px padding
     pushed the code onto its own line on a phone. */
  .league-btn-sm { padding: 6px 12px; font-size: 0.8rem; }

  /* "N waiting" on an owned private league. Blitz orange rather than the
     accent so it reads as an alert needing action, not another control -
     the same colour the "Called it" badge uses for a Blitz signal. Itself a
     button, since the only useful response to seeing it is to open the
     queue. */
  .league-list-waiting {
    flex-shrink: 0; background: var(--blitz-orange); border: 1px solid var(--blitz-orange);
    color: #241000; border-radius: 999px; padding: 4px 10px; font-size: 0.74rem;
    font-weight: 800; cursor: pointer; white-space: nowrap; line-height: 1.2;
  }
  .league-list-waiting:hover { filter: brightness(1.08); }

  .league-form-create { display: flex; flex-direction: column; gap: 10px; }
  .league-format-picker { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
  .league-format-option {
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text-dim);
    border-radius: 8px; padding: 8px 10px; font-size: 0.82rem; font-weight: 700; cursor: pointer;
  }
  .league-format-option:hover { border-color: var(--accent); }
  .league-format-option.selected { background: rgba(var(--accent-rgb),0.14); border-color: var(--accent); color: var(--accent); }
  .league-format-hint { font-size: 0.78rem; color: var(--text-dim); margin: -2px 0 0; line-height: 1.4; }

  .league-settings-form { display: flex; flex-direction: column; gap: 12px; }

  .league-settings-row { display: flex; flex-direction: column; gap: 5px; font-size: 0.8rem; font-weight: 700; color: var(--text-dim); }
  .league-settings-row-checkbox { flex-direction: row; align-items: center; justify-content: space-between; }
  .league-settings-row-checkbox input { width: 18px; height: 18px; accent-color: var(--accent); }
  .league-format-pill-locked { cursor: default; }
  .league-danger-zone { border-top: 1px dashed var(--card-border); padding-top: 16px; }
  .league-btn-danger { border-color: var(--out); color: var(--out); }
  .league-btn-danger:hover:not(:disabled) { background: rgba(var(--out-rgb),0.12); border-color: var(--out); color: var(--out); }
  .league-delete-confirm { display: flex; flex-direction: column; gap: 8px; font-size: 0.84rem; }
  .league-delete-confirm p { margin: 0; color: var(--text-dim); }
  .league-delete-confirm-actions { display: flex; gap: 8px; }
  .league-delete-confirm-actions .league-btn { flex: 1; }

  /* ---- Leagues landing page (replaces the old Leagues popup) ----------- */
  /* Left-aligned, not centered. Every other tab's content starts at the
     app container's left edge (a game card sits at the same x as the nav),
     so a 640px block centered under a full-width header read as floating
     in the middle of the page with nothing anchoring it. `auto` stays on
     the right margin only, so the block still stops growing rather than
     stretching edge to edge. */
  .leagues-page { max-width: 640px; margin: 18px auto 0 0; display: flex; flex-direction: column; gap: 18px; }
  .leagues-page-intro { color: var(--text-dim); font-size: 0.88rem; line-height: 1.5; margin: -8px 0 0; }
  .leagues-page-section {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 18px 16px; box-shadow: 0 16px 40px rgba(0,0,0,0.35);
  }
  .leagues-page-section-title { font-size: 1rem; font-weight: 800; margin: 0 0 4px; }
  .leagues-page-section-sub { font-size: 0.78rem; color: var(--text-dim); margin: 0 0 14px; line-height: 1.4; }
  .leagues-page-signin-banner {
    display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
    background: rgba(var(--accent-rgb),0.08); border: 1px solid var(--card-border); border-radius: 12px;
    padding: 12px 14px; font-size: 0.85rem; color: var(--text-dim);
  }
  .leagues-page-signin-banner strong { color: var(--text); }

  .league-list-item-btn {
    display: flex; align-items: center; justify-content: flex-start; gap: 10px; width: 100%;
    border: 1px solid var(--card-border); border-radius: 10px; padding: 10px 12px; background: none;
    color: inherit; text-align: left; cursor: pointer; font: inherit;
  }
  .league-list-item-btn:hover, .league-list-item-btn:focus-visible { border-color: var(--accent); }
  .league-search-item-desc {
    font-size: 0.76rem; color: var(--text-dim); margin: 2px 0 0; line-height: 1.35;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  }
  .league-search-row { display: flex; gap: 8px; margin-bottom: 12px; }
  .league-search-item-tags { display: flex; align-items: center; gap: 6px; margin-top: 4px; flex-wrap: wrap; }
  .league-invite-toggle {
    background: none; border: none; color: var(--accent); font-size: 0.78rem; font-weight: 700;
    cursor: pointer; padding: 0; margin: 12px 0 0;
  }

  .league-leave-confirm {
    display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%;
    font-size: 0.84rem;
  }
  .league-leave-confirm-actions { display: flex; gap: 6px; flex-shrink: 0; }

  .league-visibility-picker { display: grid; grid-template-columns: repeat(2, 1fr); gap: 6px; }
  .league-visibility-option {
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text-dim);
    border-radius: 8px; padding: 8px 6px; font-size: 0.76rem; font-weight: 700; cursor: pointer; text-align: center;
  }
  .league-visibility-option:hover { border-color: var(--accent); }
  .league-visibility-option.selected { background: rgba(var(--accent-rgb),0.14); border-color: var(--accent); color: var(--accent); }

  .league-textarea {
    width: 100%; box-sizing: border-box; background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 8px; padding: 9px 12px; color: var(--text); font-size: 16px; font-family: inherit;
    resize: vertical; min-height: 52px;
  }
  @media (hover: hover) and (pointer: fine) {
    .league-textarea { font-size: 0.88rem; }
  }
  .league-textarea:focus-visible { outline: none; border-color: var(--accent); }

  /* ---- Leagues landing: format badge images -----------------------------
     Full icon+wordmark lockup, boxed at a fixed size next to the league
     name/format-picker label. See LEAGUE_FORMAT_ICON_URLS above for why
     these are never shrunk into a tiny inline icon. */
  .format-icon-badge {
    flex-shrink: 0; width: 88px; height: 68px; border-radius: 10px; overflow: hidden; display: flex;
    align-items: center; justify-content: center; background: var(--surface-2);
  }
  .format-icon-badge img { width: 100%; height: 100%; object-fit: contain; display: block; }

  /* ---- Leagues landing: mobile tab bar (My Leagues / Search / Create) -- */
  .leagues-tabbar {
    display: none; gap: 6px; margin-bottom: 4px; background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 12px; padding: 4px;
  }
  .leagues-tab {
    flex: 1; background: none; border: none; color: var(--text-dim); font-family: inherit; font-size: 0.8rem;
    font-weight: 800; padding: 9px 6px; border-radius: 9px; cursor: pointer; display: flex; align-items: center;
    justify-content: center; gap: 6px;
  }
  .leagues-tab.active { background: var(--card-bg); color: var(--text); box-shadow: 0 4px 12px rgba(0,0,0,0.25); }
  .leagues-tab .tab-count {
    background: rgba(var(--accent-rgb),0.18); color: var(--accent); border-radius: 999px; font-size: 0.66rem;
    padding: 1px 6px; font-weight: 800;
  }

  /* ---- Leagues landing: desktop 2-col grid, Search/Create sharing the
     right column via a subtab bar (defaults to Search) --------------------
     Scoped to a min-width query on purpose: an unconditional rule here
     would out-specificity the mobile tab bar's display:none below (both
     would be 3-class selectors, so specificity - not source order - would
     decide the conflict) and bleed the inactive subtab through on mobile. */
  /* minmax(0, Nfr) rather than a bare `Nfr`: a plain fr track floors at the
     column's min-content width, and the Search/Create column's intrinsic
     width (subtab bar + search field + result rows) is wide enough that it
     was claiming that floor and leaving the remainder to My Leagues. The
     declared 1 : 1.3 was being ignored entirely - at 1440px the tracks
     resolved to 188px / 436px, which is why league cards on the left wrapped
     their format and member counts onto four cramped lines. Allowing both
     tracks to shrink below min-content makes the ratio actually hold. */
  .leagues-columns { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr); gap: 16px; align-items: start; }
  .leagues-search-create-col { display: flex; flex-direction: column; }
  .leagues-subtabbar {
    display: flex; gap: 6px; margin-bottom: 14px; background: var(--surface-2); border: 1px solid var(--card-border);
    border-radius: 12px; padding: 4px;
  }
  .leagues-subtab {
    flex: 1; background: none; border: none; color: var(--text-dim); font-family: inherit; font-size: 0.82rem;
    font-weight: 800; padding: 9px 6px; border-radius: 9px; cursor: pointer; display: flex; align-items: center;
    justify-content: center; gap: 6px;
  }
  .leagues-subtab.active { background: var(--card-bg); color: var(--text); box-shadow: 0 4px 12px rgba(0,0,0,0.25); }

  /* Only the landing page goes wide - it's the one view here with two
     columns to fit, and below 860px it still wants the full container
     rather than a 640px column with dead space beside it. The per-league
     view and the settings panel keep the narrower 640px measure, since a
     full-width column of pick cards or form fields is harder to read,
     not easier. */
  .leagues-page-landing { max-width: 1000px; }

  /* Create and the browse redesign need the full measure, not the landing
     page's narrower column. */
  .leagues-page-wide { max-width: 1240px; margin: 18px auto 0; }
  /* The league detail view (header, subtabs, picks, standings) - the one
     screen in here that's a dashboard rather than a form. The base
     .leagues-page is 640px pinned left (`margin: 18px auto 0 0`), which on a
     1280px display left more than half the window empty and squeezed the
     standings and league-picks tables into a column narrower than their
     content. Forms keep the narrow measure on purpose: a settings field set
     at 1240px is worse, not better. */
  .leagues-page-league { max-width: 1240px; margin: 18px auto 0; }

  /* ---- Manage League: full-width two-column layout ---------------------
     The base .leagues-page is 640px pinned left, which is right for the
     create/join forms but wrong here: half of this screen is a join queue
     somebody is waiting on plus an invite box, so it's as much a dashboard
     as a form - the same argument .leagues-page-league already makes for
     the league detail view, at the same measure. */
  .leagues-page-manage { max-width: 1240px; margin: 18px auto 0; }
  .lgx-manage { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 18px; align-items: start; }
  /* Sticky so the pending queue stays in view while the owner scrolls the
     scoring fields - it's the time-sensitive half of the screen and the
     reason most people tap Manage at all. */
  .lgx-manage-rail { display: flex; flex-direction: column; gap: 18px; position: sticky; top: 16px; }
  .lgx-manage-main { display: flex; flex-direction: column; gap: 18px; min-width: 0; }
  /* Danger Zone is its own card rather than a section inside the settings
     card purely so the stacked order below can put it last - sharing a card
     with the settings form would strand Invite Members underneath a Delete
     League button. */
  .lgx-manage-danger .league-danger-zone { border-top: none; padding-top: 0; }
  /* Two-up fields rather than one input stretched across the column:
     "Points per correct pick" is a number between 1 and 10, and a 530px box
     for it is wasted width, not used width. Only the fields whose content
     actually uses the measure (name, description, format, the checkbox
     rows, the submit button) span both tracks. */
  .league-settings-form-grid {
    display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 12px 16px; align-items: start;
  }
  .league-settings-form-grid > .lgx-span-2,
  .league-settings-form-grid > .league-settings-row-checkbox { grid-column: 1 / -1; }
  @media (max-width: 900px) {
    .leagues-page-manage { max-width: 640px; margin: 18px auto 0 0; }
    .lgx-manage { grid-template-columns: 1fr; }
    .league-settings-form-grid { grid-template-columns: 1fr; }
    /* The rail dissolves so its cards become grid items in their own right.
       That's what keeps the stacked order what it is today - queue,
       settings, invites - instead of dragging invites above the settings
       just because it shares a column with the queue on desktop. */
    .lgx-manage-rail, .lgx-manage-main { display: contents; }
    .lgx-manage-requests { order: -1; }
    .lgx-manage-invite { order: 1; }
    .lgx-manage-danger { order: 2; }
  }
  .lgx-crumb { display: flex; align-items: center; gap: 8px; font-size: .78rem; color: var(--text-dim); }
  .lgx-crumb button { background: none; border: none; padding: 0; font-family: inherit;
    font-size: inherit; color: var(--accent); cursor: pointer; }
  .lgx-crumb button:hover { text-decoration: underline; }
  /* Breadcrumb on a pointer, single back button on a phone. A trail is both
     too wide and too small a tap target on a narrow screen, and the pill is
     exactly the one-step-back behaviour these screens had before. Only one
     of the pair is ever rendered visible. */
  .crumb-back { display: none; align-self: flex-start; }
  /* The game page laid this spacing on the old back link itself; .league-view
     and .controls-row supply their own gap, so scope it to .game-page rather
     than putting a margin on every crumb. */
  .game-page > .crumb-nav, .game-page > .crumb-back { margin-bottom: 14px; }
  @media (max-width: 860px) {
    .crumb-nav { display: none; }
    .crumb-back { display: inline-flex; }
  }
  .lgx-title { margin: 0 0 6px; font-size: 2rem; font-weight: 800; letter-spacing: -.02em; }
  .lgx-subtitle { margin: 0; color: var(--text-dim); font-size: .9rem; }
  @media (max-width: 860px) { .lgx-title { font-size: 1.5rem; } }

  @media (min-width: 860px) {
    .leagues-search-create-col .leagues-page-section { display: none; }
    .leagues-search-create-col .leagues-page-section.subtab-active { display: block; }
  }
  @media (max-width: 859px) {
    .leagues-columns { display: block; }
    .leagues-tabbar { display: flex; }
    .leagues-subtabbar { display: none; }
    .leagues-columns > .leagues-page-section, .leagues-search-create-col { display: none; margin-bottom: 14px; }
    .leagues-columns > .leagues-page-section.mobile-active, .leagues-search-create-col.mobile-active { display: block; }
    /* The outer mobile-active toggle above only decides whether this whole
       column (search + create together) is showing at all. Within it, the
       two inner panels still need their own hide/show pass - otherwise
       both stay visible since neither is a .leagues-columns direct child
       matched by the rule above. */
    .leagues-search-create-col .leagues-page-section { display: none; }
    .leagues-search-create-col .leagues-page-section.subtab-active { display: block; }
  }

  /* ---- Leagues landing: always-visible invite code box + results divider */
  .league-code-box {
    display: flex; align-items: center; gap: 8px; background: rgba(var(--accent-rgb),0.08);
    border: 1px dashed var(--accent); border-radius: 10px; padding: 10px 12px; margin: 12px 0 16px;
  }
  .league-code-box-label { flex-shrink: 0; font-size: 0.78rem; font-weight: 700; color: var(--text); white-space: nowrap; }
  .league-code-box .league-input-code { flex: 1; min-width: 70px; background: var(--card-bg); padding: 9px 8px; text-align: center; }
  .league-results-divider {
    display: flex; align-items: center; gap: 10px; margin: 4px 0 12px; font-size: 0.72rem; font-weight: 700;
    color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px;
  }
  .league-results-divider::before, .league-results-divider::after { content: ""; flex: 1; height: 1px; background: var(--card-border); }
  .league-results-count { font-size: 0.78rem; color: var(--text-dim); margin: 0 0 10px; }
  .league-load-more { margin-top: 8px; }

  /* ---- Leagues landing: horizontal icon+label format picker ------------- */
  .league-format-option {
    display: flex; align-items: center; text-align: left; width: 100%; min-height: 76px;
    background: var(--surface-2); border: 1.5px solid var(--card-border); color: var(--text-dim);
    border-radius: 10px; padding: 8px 12px; font-size: 0.88rem; font-weight: 700; cursor: pointer;
  }
  .league-format-option .fmt-icon-row { display: flex; align-items: center; gap: 12px; width: 100%; }
  .league-format-option .fmt-icon {
    flex-shrink: 0; height: 52px; width: auto; border-radius: 8px; overflow: hidden; display: flex;
    align-items: center; justify-content: center; background: var(--surface-2);
  }
  .league-format-option .fmt-icon img { height: 100%; width: auto; object-fit: contain; display: block; }
  .league-format-option:hover { border-color: var(--card-border); filter: brightness(1.15); }
  .league-format-option.selected { background: rgba(var(--accent-rgb),0.14); border-color: var(--accent); color: var(--text); }

  /* ---- Pick'em league view (This Week / Standings) --------------------- */
  /* Ported from the approved mockup - same tokens as the rest of the app,
     namespaced under league-view-* / pickem-* / standings-* so nothing here
     collides with the unrelated main-analyzer .game-card etc. */
  .league-view { display: flex; flex-direction: column; gap: 16px; }
  .league-view-empty {
    text-align: center; padding: 40px 16px; color: var(--text-dim);
    display: flex; flex-direction: column; align-items: center; gap: 14px;
  }
  .league-picker-select {
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 8px;
    padding: 8px 12px; color: var(--text); font-size: 0.85rem; font-weight: 700; align-self: flex-start;
  }

  .league-header {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 16px; box-shadow: 0 16px 40px rgba(0,0,0,0.35);
  }
  .league-header-top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
  .league-header-name { font-size: 1.1rem; font-weight: 800; margin: 0 0 3px; }
  .league-header-meta { font-size: 0.76rem; color: var(--text-dim); display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
  .league-format-pill {
    display: inline-flex; align-items: center; padding: 2px 9px; border-radius: 999px;
    background: rgba(var(--accent-rgb),0.16); color: var(--accent); font-size: 0.66rem;
    font-weight: 800; text-transform: uppercase; letter-spacing: 0.3px;
  }
  .league-header-actions { display: flex; gap: 8px; align-items: center; flex-shrink: 0; flex-wrap: wrap; }
  .league-invite-panel {
    margin-top: 14px; padding-top: 14px; border-top: 1px dashed var(--card-border);
    display: flex; flex-direction: column; gap: 10px;
  }
  .league-invite-panel form { display: flex; flex-direction: column; gap: 10px; }
  .league-invite-panel-actions { display: flex; gap: 8px; justify-content: flex-end; }
  .league-invite-result { font-size: 0.82rem; }
  .league-invite-result p { margin: 0 0 4px; line-height: 1.4; }
  .league-invite-result-ok { color: var(--accent); }
  .league-invite-panel-standalone { margin-top: 0; padding-top: 0; border-top: none; }
  .league-week-nav {
    display: flex; align-items: center; justify-content: space-between; margin-top: 14px;
    padding-top: 12px; border-top: 1px dashed var(--card-border);
  }
  .league-week-nav-btn {
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text);
    border-radius: 8px; width: 30px; height: 30px; font-size: 0.9rem; cursor: pointer;
  }
  .league-week-nav-btn:disabled { opacity: 0.4; cursor: default; }
  .league-week-nav-label { font-size: 0.84rem; font-weight: 700; }

  .league-subtabs { display: flex; gap: 6px; background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 12px; padding: 4px; }
  .league-subtab {
    flex: 1; text-align: center; padding: 8px 6px; border-radius: 9px; font-size: 0.78rem;
    font-weight: 700; color: var(--text-dim); cursor: pointer; user-select: none; background: none; border: none;
  }
  .league-subtab.active { background: var(--accent); color: #062018; }
  .league-subtab-pip {
    display: inline-grid; place-items: center; min-width: 18px; height: 18px; padding: 0 5px;
    margin-left: 6px; border-radius: 999px; background: var(--blitz-orange); color: #0b1220;
    font-size: .64rem; font-weight: 800; vertical-align: middle;
  }
  [data-theme="light"] .league-subtab-pip { color: #fff; }
  .league-subtab.active .league-subtab-pip { background: #062018; color: var(--accent); }
  [data-theme="light"] .league-subtab.active .league-subtab-pip { background: #062018; color: var(--accent); }

  /* Four tabs don't fit on one phone row - about 418px of content in a
     358px bar. flex:1 shrank them and clipped the count pip off the
     Requests tab; letting the row scroll instead just moved the pip
     off-screen, which is no better for the one element that most needs to
     be noticed. Two rows of two keeps every tab, and its pip, visible
     without scrolling. Only applies when the fourth tab exists. */
  @media (max-width: 640px) {
    .league-subtabs.has-requests { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
    .league-subtabs.has-requests .league-subtab { flex: none; }
  }

  /* ---- owner join-request queue ---- */
  .lgq-card { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px; }
  .lgq-eyebrow { font-size: .68rem; font-weight: 800; letter-spacing: .1em; text-transform: uppercase; color: var(--accent); }
  .lgq-head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
    flex-wrap: wrap; padding: 16px 20px 14px; border-bottom: 1px solid var(--card-border); }
  .lgq-head p { margin: 6px 0 0; font-size: .8rem; color: var(--text-dim); }
  .lgq-req { display: flex; align-items: center; justify-content: space-between; gap: 14px;
    padding: 14px 20px; border-bottom: 1px solid var(--card-border); }
  .lgq-req:last-of-type { border-bottom: none; }
  .lgq-req .who { display: flex; align-items: center; gap: 12px; min-width: 0; }
  .lgq-req .av { width: 38px; height: 38px; border-radius: 50%; display: grid; place-items: center;
    font-size: 1.05rem; background: var(--surface-2); border: 1px solid var(--card-border); flex-shrink: 0; }
  .lgq-req b { display: block; font-size: .9rem; }
  .lgq-req .when { font-size: .78rem; color: var(--text-dim); }
  .lgq-req .acts { display: flex; gap: 8px; flex-shrink: 0; }
  .lgq-req.settled { opacity: .55; }
  .lgq-req .verdict { font-size: .8rem; color: var(--text-dim); }
  .lgq-req .verdict.ok { color: var(--win); }
  .lgq-sec { padding: 12px 20px 10px; border-top: 1px solid var(--card-border);
    border-bottom: 1px solid var(--card-border); }
  .lgq-empty { padding: 34px 20px; text-align: center; }
  .lgq-empty .big { font-size: 1.9rem; opacity: .4; display: block; margin-bottom: 10px; }
  .lgq-empty p { margin: 0 0 6px; font-size: .9rem; font-weight: 700; }
  .lgq-empty span { font-size: .82rem; color: var(--text-dim); }
  .lgq-note { display: flex; gap: 10px; align-items: flex-start; padding: 14px 20px;
    font-size: .82rem; line-height: 1.55; color: var(--text-dim); border-top: 1px solid var(--card-border); }
  .lgq-note.warn { color: var(--warn); border-top: none; border-bottom: 1px solid var(--card-border); }
  @media (max-width: 640px) {
    .lgq-req { flex-direction: column; align-items: stretch; }
    .lgq-req .acts .league-btn { flex: 1; }
  }

  .picks-progress {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 16px; display: flex; align-items: center; gap: 14px;
  }
  .picks-progress-ring { position: relative; width: 52px; height: 52px; flex-shrink: 0; }
  .picks-progress-ring svg { transform: rotate(-90deg); }
  .picks-progress-ring-track { fill: none; stroke: var(--surface-2); stroke-width: 5; }
  .picks-progress-ring-fill { fill: none; stroke: var(--accent); stroke-width: 5; stroke-linecap: round; transition: stroke-dashoffset 0.4s ease; }
  .picks-progress-ring-num { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; font-size: 0.72rem; font-weight: 800; }
  .picks-progress-text { flex: 1; }
  .picks-progress-title { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-dim); font-weight: 700; margin: 0 0 2px; }
  .picks-progress-count { font-size: 1rem; font-weight: 800; margin: 0; }
  .picks-progress-count .dim { color: var(--text-dim); font-weight: 600; font-size: 0.82rem; }
  .picks-progress-deadline { font-size: 0.72rem; color: var(--warn); margin: 2px 0 0; font-weight: 600; }

  .survivor-rule-note {
    display: flex; align-items: flex-start; gap: 8px;
    background: rgba(var(--warn-rgb),0.08); border: 1px solid rgba(var(--warn-rgb),0.28);
    border-radius: 10px; padding: 9px 12px; font-size: 0.74rem; color: var(--text-dim); line-height: 1.45;
  }
  .survivor-rule-note b { color: var(--text); font-weight: 700; }
  .pickem-replace-hint { font-size: 0.66rem; color: var(--warn); font-weight: 700; margin: 0; }

  /* Games fill the width instead of stacking one per row. auto-fill with a
     320px floor gives three columns at 1240px, two around 1000px, and one
     below 860px, without needing a breakpoint per step. align-items:start
     stops a short card being stretched to match a taller neighbour (an ats
     card has no confidence row; a confidence card does). */
  .pickem-grid {
    display: grid; gap: 12px; align-items: start;
    grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  }

  .pickem-game-card {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 14px; display: flex; flex-direction: column; gap: 10px;
  }
  .pickem-game-card.is-locked { opacity: 0.72; }
  /* A final game is the one locked card people actually come back to read,
     so the locked dimming lifts once there's a result to show. The border
     carries the grade at a glance; the badge on the pick carries the word. */
  .pickem-game-card.is-final { opacity: 1; }
  .pickem-game-card.is-won { border-color: rgba(var(--win-rgb),0.45); }
  .pickem-game-card.is-lost { border-color: rgba(var(--out-rgb),0.4); }
  .pickem-game-meta { display: flex; justify-content: space-between; align-items: center; font-size: 0.72rem; color: var(--text-dim); }
  .pickem-lock-tag { display: inline-flex; align-items: center; gap: 4px; color: var(--text-dim); font-weight: 700; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.4px; }
  .pickem-final-tag { display: inline-flex; align-items: center; gap: 4px; color: var(--text); font-weight: 800; font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.4px; }
  .pickem-pick-option.is-winner .pickem-team-name,
  .pickem-pick-option.is-winner .pickem-team-score { color: var(--win); }
  .pickem-pick-option.is-loser { opacity: 0.55; }
  .pickem-pick-option.is-loser.selected { opacity: 0.8; }
  .pickem-team-score { font-size: 1.35rem; font-weight: 800; line-height: 1; margin-top: 2px; font-variant-numeric: tabular-nums; }
  .pickem-pick-badge {
    position: absolute; top: 6px; right: 6px; display: inline-flex; align-items: center; gap: 3px;
    font-size: 0.62rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.3px;
    padding: 2px 7px; border-radius: 6px; line-height: 1.3;
  }
  .pickem-pick-badge.won { background: var(--win); color: #062018; }
  .pickem-pick-badge.lost { background: var(--out); color: #fff; }
  .pickem-pick-badge.void { background: var(--lose); color: var(--text); }
  .pickem-result-row {
    display: flex; align-items: center; gap: 8px; background: var(--surface-2); border-radius: 8px;
    padding: 8px 10px; font-size: 0.74rem; color: var(--text-dim);
  }
  .pickem-result-row b { color: var(--text); font-weight: 700; }
  .pickem-result-icon { font-weight: 900; font-size: 0.85rem; width: 14px; text-align: center; flex-shrink: 0; }
  .pickem-result-icon.won { color: var(--win); }
  .pickem-result-icon.lost { color: var(--out); }
  .pickem-result-icon.void { color: var(--text-dim); }
  .pickem-result-pts { margin-left: auto; font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums; }
  .pickem-result-pts.won { color: var(--win); }
  .pickem-result-pts.lost { color: var(--out); }
  .pickem-result-pts.void { color: var(--text-dim); }
  .pickem-edge-value.is-called { color: var(--blitz-orange); }
  .pickem-edge-value.is-missed { color: var(--text-dim); }
  .picks-progress-record { font-size: 1rem; font-weight: 800; margin: 0; font-variant-numeric: tabular-nums; }
  .picks-progress-record .dim { color: var(--text-dim); font-weight: 600; font-size: 0.82rem; }
  .picks-progress-pending { font-size: 0.72rem; color: var(--text-dim); margin: 2px 0 0; font-weight: 600; }
  .pickem-pick-row { display: flex; align-items: stretch; gap: 8px; }
  .pickem-pick-option {
    flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px;
    padding: 12px 8px; border-radius: 10px; border: 1.5px solid var(--card-border);
    background: var(--surface-2); cursor: pointer; transition: border-color 0.12s, background 0.12s;
    position: relative; font: inherit; color: inherit;
    -webkit-tap-highlight-color: transparent; outline: none;
  }
  .pickem-pick-option:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .pickem-pick-option:hover:not(:disabled) { border-color: rgba(var(--accent-rgb),0.5); }
  .pickem-pick-option.selected { border-color: var(--accent); background: rgba(var(--accent-rgb),0.12); box-shadow: 0 0 0 1px var(--accent); }
  .pickem-pick-option:disabled { opacity: 0.55; cursor: default; }
  .pickem-pick-option.is-used { opacity: 0.45; }
  .pickem-pick-check {
    position: absolute; top: 6px; right: 6px; width: 18px; height: 18px; border-radius: 50%;
    background: var(--accent); color: #062018; display: flex; align-items: center; justify-content: center; font-size: 0.62rem; font-weight: 900;
  }
  .pickem-team-name { font-size: 0.78rem; font-weight: 700; text-align: center; line-height: 1.2; }
  /* Sits directly under the abbreviation, dimmer and smaller than it, so
     the button still reads as a team first and a record second. Tabular
     figures keep the two sides' records aligned across the pair. */
  .pickem-team-record {
    font-size: 0.66rem; font-weight: 700; color: var(--text-dim);
    font-variant-numeric: tabular-nums; line-height: 1;
  }
  .pickem-pick-option.selected .pickem-team-record { color: var(--text); }
  .pickem-team-spread { font-size: 0.68rem; font-weight: 700; color: var(--text-dim); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
  .pickem-team-prob { font-size: 0.66rem; font-weight: 700; color: var(--text-dim); }
  .pickem-team-prob.is-favored { color: var(--win); }
  .pickem-edge-row {
    background: var(--surface-2); border-left: 3px solid var(--blitz-orange); border-radius: 0 8px 8px 0;
    padding: 8px 10px; display: flex; flex-direction: column; gap: 5px;
  }
  .pickem-edge-row.is-muted { border-left-color: var(--lose); }
  .pickem-edge-main { display: flex; align-items: center; gap: 8px; }
  .pickem-edge-icon { width: 17px; height: 17px; object-fit: contain; flex-shrink: 0; }
  .pickem-edge-dash { color: var(--text-dim); font-weight: 700; width: 17px; text-align: center; flex-shrink: 0; }
  .pickem-edge-team { font-size: 0.8rem; font-weight: 700; color: var(--text); }
  .pickem-edge-value { margin-left: auto; font-size: 0.78rem; font-weight: 800; color: var(--blitz-orange); white-space: nowrap; }
  .pickem-edge-row.is-muted .pickem-edge-team,
  .pickem-edge-row.is-muted .pickem-edge-value { color: var(--text-dim); }
  .pickem-edge-note { font-size: 0.68rem; color: var(--text-dim); line-height: 1.45; }
  .pickem-suggested-chip {
    display: inline-flex; align-items: center; gap: 4px; font-size: 0.64rem; font-weight: 700;
    color: var(--accent); background: rgba(var(--accent-rgb),0.12); border: 1px solid rgba(var(--accent-rgb),0.35);
    border-radius: 6px; padding: 2px 7px; cursor: pointer; margin-left: 6px;
  }
  .pickem-suggested-chip:hover { background: rgba(var(--accent-rgb),0.2); }
  .pickem-suggested-chip:disabled { opacity: 0.5; cursor: default; }
  .pickem-team-used-tag {
    font-size: 0.62rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px;
    color: var(--out); background: rgba(var(--out-rgb), 0.12); border-radius: 5px; padding: 1px 6px;
  }
  .pickem-at-sep { display: flex; align-items: center; color: var(--text-dim); font-size: 0.72rem; padding: 0 2px; }
  .pickem-confidence-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; background: var(--surface-2); border-radius: 8px; padding: 8px 10px; font-size: 0.74rem; }
  .pickem-confidence-label { color: var(--text-dim); font-weight: 600; }
  .pickem-confidence-needed { color: var(--accent); font-weight: 700; }
  .pickem-confidence-select { background: var(--card-bg); border: 1px solid var(--card-border); color: var(--text); border-radius: 6px; padding: 4px 8px; font-size: 0.78rem; font-weight: 700; font-family: inherit; }
  .pickem-pick-error { font-size: 0.68rem; color: var(--out); background: rgba(var(--out-rgb),0.1); border: 1px solid rgba(var(--out-rgb),0.3); border-radius: 6px; padding: 5px 8px; }
  /* Deliberately mirrors .gc-actions on the game card: full-bleed to the
     card's edge, hairline above, same weight and size. A pick card and a
     game card are the two places a matchup appears in this app, and the
     link between them should read as the same control on both. The negative
     margins match .pickem-game-card's 14px padding and its 14px radius. */
  .pickem-details-link {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    margin: 2px -14px -14px; padding: 11px 8px;
    border: none; border-top: 1px solid var(--card-border); border-radius: 0 0 14px 14px;
    background: none; cursor: pointer; font: inherit; font-size: 0.76rem; font-weight: 700; color: var(--text);
    -webkit-tap-highlight-color: transparent;
  }
  .pickem-details-link:hover { background: rgba(var(--accent-rgb),0.08); color: var(--accent); }
  .pickem-details-link:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
  .pickem-details-chev { color: var(--accent); font-size: 0.9rem; line-height: 1; }
  /* A game page opened from a pick sheet is long - card, compare table,
     head-to-head - so the crumb back button at the top is off screen by the
     time someone's finished reading. This repeats it at the bottom rather
     than making them scroll back up or reach for the browser. */
  .back-to-picks-bottom { display: flex; justify-content: center; margin-top: 18px; }
  .pickem-autosave-note { font-size: 0.68rem; color: var(--text-dim); display: flex; align-items: center; gap: 5px; justify-content: flex-end; }
  .pickem-autosave-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--win); }

  .league-standings-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; }
  .league-standings-table th {
    text-align: left; color: var(--text-dim); font-size: 0.66rem; text-transform: uppercase;
    letter-spacing: 0.3px; font-weight: 700; padding: 6px 8px; border-bottom: 1px solid var(--card-border);
  }
  .league-standings-table td { padding: 9px 8px; border-bottom: 1px solid var(--card-border); }
  .league-standings-table tr.is-me { background: rgba(var(--accent-rgb),0.08); }
  .league-standings-rank {
    font-weight: 800; width: 28px; white-space: nowrap;
    font-variant-numeric: tabular-nums;
  }
  /* Wide enough for "10 ▼2" without wrapping the arrow under the number. */
  .standings-table-block .league-standings-rank { width: 52px; }
  .league-standings-rank.gold { color: var(--star); }
  .league-standings-user { display: flex; align-items: center; gap: 8px; font-weight: 700; }
  .league-standings-avatar {
    width: 24px; height: 24px; border-radius: 50%; background: var(--accent); color: #062018;
    display: flex; align-items: center; justify-content: center; font-size: 0.66rem; font-weight: 800; flex-shrink: 0;
    overflow: hidden;
  }
  .league-standings-avatar.has-glyph { background: var(--surface-2); border: 1px solid var(--card-border); }
  .league-standings-avatar .avatar-glyph-img { width: 66%; height: 66%; object-fit: contain; }
  /* Same reasoning as .account-avatar span - emoji glyphs need a bigger
     font-size than the initial-letter fallback to read at the same size. */
  .league-standings-avatar span { font-size: 11px; }
  .league-standings-points { font-weight: 800; }
  .league-standings-record { font-variant-numeric: tabular-nums; white-space: nowrap; }
  .league-status-alive { color: var(--win); font-weight: 700; font-size: 0.72rem; }
  .league-status-eliminated { color: var(--out); font-weight: 700; font-size: 0.72rem; text-decoration: line-through; opacity: 0.7; }
  /* Only reachable in a league with more than one strike allowed - a
     member who has used a strike but isn't out yet. */
  .league-status-warn { color: var(--warn); font-weight: 700; font-size: 0.72rem; }

  /* A member the scoring pass hasn't touched yet (new league, mid-season
     joiner, or just before the first Sunday finishes). They're real
     members and belong in the table, but a 0-0 record and a 0% win rate
     would read as "played and lost everything" rather than "hasn't
     played" - hence the dash treatment and the dimmed row. */
  .standings-dash { color: var(--text-dim); opacity: 0.65; }
  .league-standings-table tr.is-unplayed td { color: var(--text-dim); }
  .league-standings-table tr.is-unplayed .league-standings-user { font-weight: 600; }

  .standings-pending-note {
    font-size: 0.72rem; color: var(--text-dim); margin: 0 0 10px; line-height: 1.45;
    background: var(--surface-2); border-radius: 8px; padding: 8px 10px;
  }
  .standings-table-block { display: flex; flex-direction: column; }
  .standings-table-head { display: flex; align-items: baseline; justify-content: space-between; margin: 0 0 8px; }
  .standings-table-head h3 {
    margin: 0; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.4px;
    color: var(--text-dim); font-weight: 700;
  }
  .standings-table-count { font-size: 0.72rem; color: var(--text-dim); font-weight: 600; }

  /* The signed-in user's own line, lifted above the table - see
     StandingsMeCard for why. Accent-tinted so it reads as "this one is
     yours" at a glance without needing to be found by name. */
  .standings-me-card {
    background: linear-gradient(180deg, rgba(var(--accent-rgb),0.12), rgba(var(--accent-rgb),0.04));
    border: 1px solid rgba(var(--accent-rgb),0.35); border-radius: 14px;
    padding: 14px; display: flex; flex-direction: column; gap: 12px;
  }
  .standings-me-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
  .standings-me-you {
    font-size: 0.6rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.6px;
    color: var(--accent); background: rgba(var(--accent-rgb),0.16);
    border: 1px solid rgba(var(--accent-rgb),0.35); border-radius: 5px; padding: 2px 7px;
  }
  .standings-me-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; }
  .standings-me-stat { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  .standings-me-stat-label {
    font-size: 0.6rem; text-transform: uppercase; letter-spacing: 0.4px;
    color: var(--text-dim); font-weight: 700;
  }
  .standings-me-stat-value {
    font-size: 1rem; font-weight: 800; line-height: 1.15;
    display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap;
    font-variant-numeric: tabular-nums;
  }
  .standings-me-stat-value.gold { color: var(--star); }
  .standings-me-stat-value .dim { color: var(--text-dim); font-weight: 600; font-size: 0.78rem; }
  .standings-me-note { margin: 0; font-size: 0.68rem; color: var(--text-dim); line-height: 1.45; }

  /* ---- Standings scope switch (This Week / Overall) --------------------
     Standings open on This Week by default (see readStandingsScope): the
     season table tells a Week 6 joiner they're last with a dash, which is
     the least inviting thing a pool can show someone on their first visit.
     The weekly table resets every Sunday and puts them level. */
  .standings-scope {
    display: flex; align-items: center; justify-content: space-between;
    gap: 10px; flex-wrap: wrap; margin-bottom: 4px;
  }
  .standings-scope-toggle {
    display: inline-flex; gap: 4px; background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 999px; padding: 4px;
  }
  .standings-scope-btn {
    border: 1px solid transparent; background: none; cursor: pointer; border-radius: 999px;
    padding: 6px 15px; font-size: 0.76rem; font-weight: 800; color: var(--text-dim);
    font-family: inherit; transition: background 0.12s, color 0.12s;
  }
  .standings-scope-btn[aria-pressed="true"] {
    background: var(--card-bg); color: var(--text); border-color: var(--card-border);
    box-shadow: 0 1px 3px rgba(0,0,0,0.25);
  }
  .standings-scope-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .standings-scope-status {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 0.7rem; font-weight: 700; color: var(--text-dim);
  }
  .standings-scope-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--win); flex-shrink: 0; }
  .standings-scope-status.is-live { color: var(--live); }
  .standings-scope-status.is-live .standings-scope-dot { background: var(--live); animation: standings-live-pulse 1.6s ease-in-out infinite; }
  @keyframes standings-live-pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.35; } }
  @media (prefers-reduced-motion: reduce) {
    .standings-scope-status.is-live .standings-scope-dot { animation: none; }
  }
  .standings-week-progress {
    height: 4px; border-radius: 999px; background: var(--surface-2);
    border: 1px solid var(--card-border); overflow: hidden; margin-bottom: 4px;
  }
  .standings-week-progress span { display: block; height: 100%; background: var(--accent); }

  /* Secondary line on the "you" card - the context that doesn't earn a
     headline slot but answers the next question (how far back am I, what's
     my best week). */
  .standings-me-secondary {
    display: flex; flex-wrap: wrap; gap: 6px 8px;
    padding-top: 10px; border-top: 1px dashed rgba(var(--accent-rgb),0.3);
  }
  .standings-me-chip {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 0.68rem; font-weight: 700; color: var(--text-dim);
    background: rgba(var(--accent-rgb),0.08); border: 1px solid rgba(var(--accent-rgb),0.22);
    border-radius: 999px; padding: 4px 10px; font-variant-numeric: tabular-nums;
  }
  .standings-me-chip b { color: var(--text); font-weight: 800; }

  .standings-move { font-size: 0.68rem; font-weight: 800; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .standings-move.up { color: var(--win); }
  .standings-move.down { color: var(--out); }
  .standings-move.flat { color: var(--text-dim); opacity: 0.6; }

  /* One bar per scored week, tallest for a week won. Read as a shape, not
     as individual values - the exact finish is in the title/aria label. */
  .standings-form { display: inline-flex; align-items: flex-end; gap: 3px; height: 17px; }
  .standings-form-bar { width: 6px; border-radius: 2px; background: var(--lose); flex-shrink: 0; }
  .standings-form-bar.good { background: var(--accent); }
  .standings-form-bar.best { background: var(--star); }
  .standings-form-bar.none { background: var(--card-border); height: 3px; }

  .standings-mark-ok { color: var(--win); font-weight: 800; font-size: 0.74rem; white-space: nowrap; }
  .standings-mark-no { color: var(--out); font-weight: 800; font-size: 0.74rem; white-space: nowrap; }
  .standings-teams-used { font-size: 0.72rem; color: var(--text-dim); font-weight: 600; }
  .standings-legend { margin: 8px 0 0; font-size: 0.66rem; color: var(--text-dim); line-height: 1.5; }

  /* The season table carries eight columns; on a phone it scrolls rather
     than crushing the name column. Win % and Weeks won drop out first -
     both are derivable from what's left, unlike Avg / wk, which is the
     column that makes a mid-season joiner comparable at all. */
  .standings-table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .league-standings-table th.num, .league-standings-table td.num {
    text-align: right; font-variant-numeric: tabular-nums;
  }
  @media (max-width: 620px) {
    .league-standings-table .standings-col-wide { display: none; }
  }

  /* ---- League Picks sub-tab (who picked what, post-kickoff) ------------ */
  .league-picks-intro {
    font-size: 0.72rem; color: var(--text-dim); line-height: 1.45;
    background: var(--surface-2); border-radius: 8px; padding: 8px 10px;
  }
  .league-picks-game {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 12px; display: flex; flex-direction: column; gap: 10px;
  }
  /* Pre-kickoff games stay in the list rather than being filtered out, so
     the week reads as a complete slate and it's obvious the rest is coming
     rather than missing. Dimmed, with no pick data attached at all. */
  .league-picks-game.is-hidden { opacity: 0.6; }
  .league-picks-game-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
  .league-picks-matchup { display: flex; align-items: center; gap: 5px; font-size: 0.82rem; font-weight: 700; }
  .league-picks-matchup .is-winner { color: var(--win); }
  .league-picks-at { color: var(--text-dim); font-weight: 600; margin: 0 1px; }
  .league-picks-time { font-size: 0.68rem; color: var(--text-dim); font-weight: 600; }
  .league-picks-locked-note { margin: 0; font-size: 0.72rem; color: var(--text-dim); font-weight: 600; }

  .league-picks-columns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
  .league-picks-column {
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 10px;
    padding: 8px; display: flex; flex-direction: column; gap: 5px; min-width: 0;
  }
  .league-picks-column.is-winner { border-color: rgba(var(--win-rgb),0.45); background: rgba(var(--win-rgb),0.08); }
  .league-picks-column-head {
    display: flex; align-items: center; gap: 5px; font-size: 0.72rem; font-weight: 800;
    padding-bottom: 5px; border-bottom: 1px solid var(--card-border);
  }
  .league-picks-column-count {
    margin-left: auto; font-size: 0.66rem; font-weight: 700; color: var(--text-dim);
    background: var(--card-bg); border-radius: 5px; padding: 1px 6px;
  }
  .league-picks-none { margin: 2px 0; font-size: 0.68rem; color: var(--text-dim); font-style: italic; }
  .league-picks-person {
    display: flex; align-items: center; gap: 5px; font-size: 0.72rem; font-weight: 600;
    min-width: 0;
  }
  .league-picks-person-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .league-picks-person.is-me .league-picks-person-name { color: var(--accent); font-weight: 800; }
  .league-picks-person.is-correct .league-picks-mark { color: var(--win); }
  .league-picks-person.is-wrong { opacity: 0.65; }
  .league-picks-person.is-wrong .league-picks-mark { color: var(--out); }
  .league-picks-mark { font-weight: 900; font-size: 0.68rem; }
  .league-picks-conf {
    margin-left: auto; font-size: 0.62rem; font-weight: 800; color: var(--text-dim);
    background: var(--card-bg); border-radius: 4px; padding: 0 5px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  }
  .league-picks-nopick {
    margin: 0; font-size: 0.66rem; color: var(--text-dim); line-height: 1.4;
    border-top: 1px solid var(--card-border); padding-top: 8px;
  }

  @media (max-width: 420px) {
    .standings-me-stats { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  }

  /* Clickable wrapper around a live/final score - opens the box score
     modal. Uses "all: unset" so it inherits whatever styling the score
     content already carries (pill background, bold weight, etc.) rather
     than fighting it with default button chrome; only cursor/focus affordances are added back. */
  .score-link-btn {
    all: unset; display: inline-flex; align-items: center; cursor: pointer; border-radius: 6px;
  }
  .score-link-btn:hover, .score-link-btn:focus-visible {
    opacity: 0.82; outline: 2px solid rgba(var(--accent-rgb),0.45); outline-offset: 2px;
  }
  /* Small always-visible badge after every clickable score - the button's
     own hover/focus affordance only shows once someone's already pointing
     at it, which doesn't help on a touch screen. A plain glyph reads as
     near-invisible clutter against the card, so this is a filled pill
     (accent-colored background, not just a muted icon) so it reads as
     its own distinct, obviously-tappable element even at a glance. */
  .boxscore-hint-icon {
    display: inline-flex; align-items: center; justify-content: center;
    margin-left: 6px; padding: 3px; border-radius: 5px;
    background: rgba(var(--accent-rgb),0.22); color: var(--accent);
    flex-shrink: 0;
  }
  .score-link-btn:hover .boxscore-hint-icon, .score-link-btn:focus-visible .boxscore-hint-icon {
    background: rgba(var(--accent-rgb),0.4);
  }

  .boxscore-modal-overlay {
    position: fixed; inset: 0; background: rgba(4,8,16,0.6); backdrop-filter: blur(2px);
    display: flex; align-items: center; justify-content: center; padding: 16px; z-index: 1000;
  }
  .boxscore-modal {
    position: relative; width: 100%; max-width: 480px; max-height: 85vh; max-height: 85dvh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 16px;
    padding: 22px 20px 18px; box-shadow: 0 24px 60px rgba(0,0,0,0.45);
  }
  .boxscore-modal-close {
    position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; border-radius: 50%;
    border: 1px solid var(--card-border); background: var(--surface-2); color: var(--text);
    font-size: 1.1rem; line-height: 1; cursor: pointer;
  }
  .boxscore-modal-header {
    display: flex; align-items: center; justify-content: center; gap: 12px;
    padding-right: 30px; margin-bottom: 16px;
  }
  .boxscore-modal-title { font-size: 1.05rem; font-weight: 800; text-align: center; }
  .boxscore-loading, .boxscore-error { color: var(--text-dim); font-size: 0.85rem; padding: 12px 0; text-align: center; }
  .boxscore-error { color: var(--pill-bad-text); }
  /* Winning team, once the game is actually final (never mid-live) - a ring
     around their header logo, a tinted/bolded row in the linescore table,
     and a small check next to their abbreviation wherever it shows up. */
  /* inline-flex + line-height:0 so the ring hugs the logo's actual circular
     bounds instead of an inline box's taller, baseline-shifted line box
     (which made the ring sit uneven/off-center around the image). A gap
     matching the modal background sits between the logo and the ring so it
     reads as a halo rather than a line touching the artwork's edge. */
  .boxscore-team-logo {
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 50%; line-height: 0;
  }
  .boxscore-team-logo.is-winner { box-shadow: 0 0 0 3px var(--card-bg), 0 0 0 5px var(--win); }
  /* Wraps a header logo plus its possession-ball marker (when live) so the
     ball sits right next to that team's logo instead of buried in the
     status strip below - it's the first thing the eye should land on. */
  .boxscore-header-team { display: inline-flex; align-items: center; gap: 3px; }
  .boxscore-header-ball { font-size: 1.05rem; transform: none; margin: 0; }
  .boxscore-winner-row td { font-weight: 800; background: rgba(var(--win-rgb),0.1); }
  .boxscore-winner-check { color: var(--win); font-weight: 800; margin-left: 4px; }
  /* Live game strip - possession, down/distance, clock - sits between the
     team-logo header and the linescore table so it's the first thing
     visible when a live game's box score is opened. */
  .boxscore-live-status {
    margin: 2px 0 14px; padding: 8px 10px;
    background: rgba(var(--live-rgb),0.08); border: 1px solid rgba(var(--live-rgb),0.25);
    border-radius: 8px;
  }
  .boxscore-live-top { display: flex; align-items: center; flex-wrap: wrap; gap: 0; }
  .boxscore-live-status .last-play { margin-top: 6px; }
  .boxscore-linescore-table {
    width: 100%; border-collapse: collapse; font-size: 0.8rem; margin-bottom: 16px;
  }
  .boxscore-linescore-table th, .boxscore-linescore-table td {
    text-align: center; padding: 5px 6px; border-bottom: 1px solid var(--card-border);
  }
  .boxscore-linescore-table th { color: var(--text-dim); font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.3px; }
  .boxscore-linescore-table td:first-child, .boxscore-linescore-table th:first-child { text-align: left; font-weight: 700; }
  .boxscore-linescore-table .boxscore-final-col { font-weight: 800; }
  .boxscore-stats-section, .boxscore-players-section { margin-bottom: 6px; }
  .boxscore-stats-table { width: 100%; border-collapse: collapse; font-size: 0.84rem; }
  .boxscore-stats-table th { color: var(--text-dim); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.3px; padding: 4px 0 8px; }
  .boxscore-stats-table th:first-child, .boxscore-stats-table th:last-child { text-align: center; }
  .boxscore-stats-table td { padding: 6px 4px; border-bottom: 1px solid var(--card-border); }
  .boxscore-stats-table tr:last-child td { border-bottom: none; }
  .boxscore-stats-table td:first-child, .boxscore-stats-table td:last-child { text-align: center; font-weight: 700; width: 30%; }
  .boxscore-stat-label { text-align: center; color: var(--text-dim); font-size: 0.76rem; }
  /* Full per-player stat lines (all athletes, not just the top performer),
     one team visible at a time via tabs (Passing/Rushing/Receiving/Fumbles
     stacked per team was a lot of vertical scroll for a modal). Fumbles'
     LOST column is what actually surfaces turnovers per player, alongside
     the INT column already in the Passing table. */
  .boxscore-team-tabs { display: flex; gap: 6px; margin-bottom: 10px; }
  .boxscore-team-tab-btn {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
    background: var(--surface-2); border: 1px solid var(--card-border); border-radius: 8px;
    padding: 7px 10px; font-size: 0.8rem; font-weight: 700; color: var(--text-dim); cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
  }
  .boxscore-team-tab-btn:hover { color: var(--text); }
  .boxscore-team-tab-btn.active {
    color: var(--accent); border-color: var(--accent); background: rgba(var(--accent-rgb),0.1);
  }
  .boxscore-player-category { margin-bottom: 10px; }
  .boxscore-player-category:last-child { margin-bottom: 0; }
  .boxscore-player-category-title {
    font-size: 0.66rem; text-transform: uppercase; letter-spacing: 0.4px; color: var(--text-dim);
    font-weight: 700; margin: 0 0 3px;
  }
  .boxscore-player-table { width: 100%; border-collapse: collapse; font-size: 0.78rem; table-layout: fixed; }
  .boxscore-player-table th, .boxscore-player-table td { padding: 3px 4px; text-align: right; }
  .boxscore-player-table th:first-child, .boxscore-player-table td:first-child { text-align: left; }
  .boxscore-player-table th {
    color: var(--text-dim); font-size: 0.62rem; text-transform: uppercase; letter-spacing: 0.2px; font-weight: 700;
  }
  .boxscore-player-table td:first-child { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .boxscore-player-table tr:not(:last-child) td { border-bottom: 1px solid var(--card-border); }
  @media (max-width: 640px) {
    .boxscore-modal-overlay { align-items: flex-end; padding: 0; }
    .boxscore-modal {
      max-width: 100%; max-height: 85vh; border-radius: 16px 16px 0 0;
      padding: 20px 16px 24px; animation: injury-sheet-up 0.18s ease-out;
    }
  }

  /* ---- First-run tutorial tour: dims the page, cuts a spotlight hole over
     the real element being explained, and shows a callout bubble pointing
     at it. Sits above everything (including the box score / settings
     modals, z-index 1000) since some steps run with a modal open behind
     the spotlight.

     The dimming comes from exactly one layer at a time, controlled inline
     from JS: when a spotlight ring is showing, .tour-backdrop is set fully
     transparent and the ring's own 9999px box-shadow spread is what dims
     everything outside its box - leaving the ring's interior (the actual
     highlighted element) at full brightness, not viewed through a second
     dark layer. .tour-backdrop only paints its own dark background for
     steps with no ring (welcome, mockup previews). */
  .tour-overlay {
    position: fixed; inset: 0; z-index: 2000; pointer-events: none;
  }
  .tour-backdrop {
    position: fixed; inset: 0; background: rgba(4,8,16,0.72);
    pointer-events: auto; transition: background-color 0.25s ease;
  }
  .tour-spotlight-ring {
    position: fixed; border-radius: 10px; pointer-events: none;
    box-shadow: 0 0 0 4px rgba(var(--accent-rgb),0.55), 0 0 0 9999px rgba(4,8,16,0.72);
    transition: top 0.25s ease, left 0.25s ease, width 0.25s ease, height 0.25s ease, opacity 0.2s ease;
  }
  .tour-spotlight-ring.is-settling { opacity: 0.4; }
  .tour-callout {
    position: fixed; z-index: 2001; width: 300px; max-width: calc(100vw - 32px);
    max-height: calc(100vh - 32px); max-height: calc(100dvh - 32px);
    overflow-y: auto; overscroll-behavior: contain;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px;
    padding: 16px 16px 14px; box-shadow: 0 24px 60px rgba(0,0,0,0.5); pointer-events: auto;
    transition: top 0.25s ease, left 0.25s ease, opacity 0.2s ease;
  }
  .tour-callout.is-settling { opacity: 0.5; }
  .tour-callout-eyebrow {
    font-size: 0.64rem; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 800;
    color: var(--accent); margin-bottom: 6px;
  }
  .tour-callout-title { font-size: 0.98rem; font-weight: 800; margin-bottom: 6px; }
  .tour-callout-body { font-size: 0.82rem; color: var(--text-dim); line-height: 1.55; margin: 0 0 14px; }
  /* Mockup preview - shown inside the callout instead of a live spotlight
     when there's no live game or open box score to point at right now.
     Built from the same classes as the real UI (live-tag, last-play,
     boxscore-linescore-table) so it looks authentic, with a small
     "Example" chip so it's clearly labeled as illustrative sample data
     rather than something currently happening. */
  .tour-mockup-preview {
    position: relative; margin: 2px 0 14px; padding: 12px 10px 10px;
    background: var(--surface-2); border: 1px dashed var(--card-border); border-radius: 10px;
  }
  .tour-mockup-tag {
    position: absolute; top: -9px; left: 10px; background: var(--card-bg);
    border: 1px solid var(--card-border); color: var(--text-dim);
    font-size: 0.6rem; font-weight: 800; text-transform: uppercase; letter-spacing: 0.4px;
    padding: 2px 8px; border-radius: 999px;
  }
  .tour-mockup-live-row { margin-bottom: 6px; }
  .tour-mockup-score { font-size: 0.92rem; font-weight: 800; margin: 6px 0 4px; }
  .tour-mockup-table { margin: 6px 0 0; }
  .tour-callout-footer { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
  .tour-callout-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
  .tour-btn {
    border: none; background: none; color: var(--text-dim);
    font-family: inherit; font-size: 0.82rem; font-weight: 700; padding: 9px 6px; cursor: pointer;
    flex-shrink: 0;
  }
  .tour-btn:hover { color: var(--text); }
  .tour-btn-primary {
    background: var(--accent); color: var(--bg); border-radius: 999px;
    padding: 9px 20px; min-width: 76px; text-align: center;
    box-shadow: 0 2px 10px rgba(var(--accent-rgb),0.35);
  }
  .tour-btn-primary:hover { opacity: 0.92; color: var(--bg); }
  /* Sits inside the callout, on its own line below Back/Next, left-aligned -
     a plain text link rather than a button so it doesn't compete visually
     with the primary Next action. */
  .tour-skip-link {
    display: block; margin-top: 10px; padding: 0;
    background: none; border: none; color: var(--text-dim);
    font-family: inherit; font-size: 0.76rem; font-weight: 700; text-decoration: underline;
    text-underline-offset: 2px; cursor: pointer;
  }
  .tour-skip-link:hover { color: var(--text); }
  @media (max-width: 640px) {
    .tour-callout { width: calc(100vw - 32px); }
  }

  .result-row {
    display: flex; justify-content: space-between; align-items: center; font-size: 0.78rem;
    padding: 6px 10px; border-radius: 8px; background: rgba(var(--demo-rgb),0.1); border: 1px solid rgba(var(--demo-rgb),0.3);
  }
  .result-row .score { font-weight: 700; }
  .result-row .verdict.correct { color: var(--win); }
  .result-row .verdict.incorrect { color: var(--pill-bad-text); }
  footer.app-footer { text-align: left; color: var(--text-dim); font-size: 0.72rem; margin-top: 32px; }
  footer.app-footer p { margin: 0 0 10px; }
  footer.app-footer p:last-child { margin-bottom: 0; }
  .footer-gambling-notice { font-size: 0.74rem; line-height: 1.5; }
  /* Copyright + NFL non-affiliation. Set apart from the source credits above
     it with a hairline rule so it reads as the legal footer line rather than
     another data-source sentence. */
  .footer-legal { border-top: 1px solid var(--card-border); padding-top: 10px; font-size: 0.7rem; line-height: 1.5; }

  .team-view-header {
    display: flex; align-items: center; gap: 16px; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; padding: 18px; margin-bottom: 18px;
  }
  .team-view-header img { width: 64px; height: 64px; object-fit: contain; }
  .team-view-header h2 { margin: 0 0 4px; font-size: 1.3rem; }
  .team-view-header .rec-line { color: var(--text-dim); font-size: 0.85rem; }

  /* Running tally of prediction/spread accuracy, shown once at least one
     game has finished - not before, since 0/0 isn't useful information.
     Title (optional - the week view uses it to name the period, the team
     page omits it since the page header already gives that context) stacks
     above the stats row so a long period name (e.g. "Hall of Fame Game")
     never has to share a row with the numbers. */
  .accuracy-summary {
    display: flex; flex-direction: column; gap: 10px; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; padding: 14px 18px; margin-bottom: 18px;
  }
  .accuracy-summary-title {
    font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim); font-weight: 700;
  }
  .accuracy-summary-stats { display: flex; flex-wrap: wrap; gap: 10px 20px; }
  .accuracy-stat { display: flex; align-items: center; gap: 10px; flex: 1 1 200px; min-width: 0; }
  .accuracy-stat-label {
    font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.5px; color: var(--text-dim);
    font-weight: 700; flex-shrink: 0;
  }
  .accuracy-stat-value { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 0.85rem; font-weight: 700; }
  .accuracy-count.correct { color: var(--win); }
  .accuracy-count.incorrect { color: var(--text-dim); font-weight: 500; }
  /* Below ~360px even "Predictions"/"Spreads" can get tight next to the
     numbers - stack label above value rather than let them crowd together. */
  @media (max-width: 380px) {
    .accuracy-stat { flex-direction: column; align-items: flex-start; gap: 2px; }
  }

  /* The Result column used to cram score + clock + down/distance into one
     nowrap span, with the play-by-play text right after it - long enough
     combined that it could push the table wider than its container and
     get clipped off the right edge instead of actually scrolling into
     view. Now only the short, atomic pieces (the score, the clock) stay
     nowrap; the clock/down-distance line and the last-play caption are
     their own lines below the score and wrap normally within the column,
     so nothing depends on the viewport being wide enough to fit it all
     on one row. overflow-x stays as a safety net for edge cases, not the
     primary way to see the Result column. */
  .team-schedule-table-wrap {
    overflow-x: auto; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 12px;
  }
  .result-cell { overflow-wrap: break-word; word-break: break-word; }
  .result-cell .live-tag { flex-wrap: wrap; }
  .live-clock-line {
    display: flex; align-items: center; flex-wrap: wrap; gap: 4px;
    margin-top: 3px; font-size: 0.72rem; color: var(--text-dim); font-weight: 600;
  }
  .team-schedule-table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 0.85rem; }
  .team-schedule-table th {
    text-align: left; color: var(--text-dim); font-weight: 600; font-size: 0.7rem; text-transform: uppercase;
    letter-spacing: 0.5px; padding: 10px 14px; border-bottom: 1px solid var(--card-border); white-space: nowrap;
  }
  .team-schedule-table td { padding: 12px 14px; border-bottom: 1px solid var(--card-border); vertical-align: middle; overflow-wrap: break-word; }
  .team-schedule-table td:first-child, .team-schedule-table th:first-child { white-space: nowrap; }
  .team-schedule-table tr:nth-child(even) td { background: var(--surface-2); }
  .team-schedule-table tbody tr:last-child td { border-bottom: none; }
  .team-schedule-table tbody tr:hover td { background: rgba(var(--accent-rgb),0.04); }
  .team-schedule-table tr.bye-row td { color: var(--text-dim); font-style: italic; }
  .team-schedule-table tr.bye-row:hover td { background: transparent; }
  .team-schedule-table tr.playoff-row td { background: rgba(var(--demo-rgb),0.06); }
  .team-schedule-table tr.playoff-row:hover td { background: rgba(var(--demo-rgb),0.1); }
  .team-schedule-table tr.live-row td { background: rgba(var(--live-rgb),0.08); }
  .team-schedule-table tr.live-row:hover td { background: rgba(var(--live-rgb),0.12); }

  /* Date/time cells break into deliberate lines instead of the browser
     wrapping the raw string at an arbitrary, ugly point. */
  .date-cell { white-space: nowrap; }
  .date-cell .date-line { display: block; }
  .date-cell .time-line {
    display: block; color: var(--text-dim); font-size: 0.76rem; margin-top: 2px;
  }
  .table-odds { display: flex; flex-direction: column; gap: 2px; font-size: 0.8rem; white-space: nowrap; }
  .table-odds .odds-line { font-weight: 600; }
  .table-odds .odds-fallback-note-sm { font-size: 0.66rem; color: var(--text-dim); font-style: italic; }
  /* Prediction now lives inside the Odds column, set off with a thin
     divider so it still reads as its own fact rather than part of the
     odds line above it. */
  .pred-inline {
    display: block; margin-top: 5px; padding-top: 5px; border-top: 1px solid var(--card-border);
    font-weight: 700; font-size: 0.78rem;
  }
  .pred-inline .pred-conf-inline { font-weight: 500; color: var(--text-dim); font-size: 0.72rem; }

  /* Mobile card layout for the team schedule (replaces the table under ~860px so
     columns like Date & Time don't get crushed into multi-line wraps). */
  .schedule-mobile-list { display: flex; flex-direction: column; gap: 10px; }
  .schedule-mobile-card {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 12px;
    padding: 12px; display: flex; flex-direction: column; gap: 8px;
  }
  .schedule-mobile-card.playoff-row { border-color: rgba(var(--demo-rgb),0.35); }
  .schedule-mobile-card.bye-row { color: var(--text-dim); font-style: italic; }
  .schedule-mobile-card.live-row { border-color: var(--live); box-shadow: 0 0 0 1px rgba(var(--live-rgb),0.3); }
  .schedule-mobile-top { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 0.78rem; }
  .schedule-mobile-week { font-weight: 700; color: var(--text); }
  .schedule-mobile-date { color: var(--text-dim); text-align: right; }
  .schedule-mobile-row { font-size: 0.85rem; }
  .schedule-mobile-odds {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px; font-size: 0.74rem;
    background: var(--surface-2); border-radius: 8px; padding: 6px 10px;
  }
  .opp-cell { display: flex; align-items: center; flex-wrap: wrap; row-gap: 4px; column-gap: 8px; }
  .opp-cell img { width: 26px; height: 26px; object-fit: contain; flex-shrink: 0; }
  .opp-cell > span:not(.international-tag) { overflow-wrap: break-word; min-width: 0; }
  .opp-cell .international-tag { margin-left: 0; }
  .pred-win { color: var(--win); font-weight: 700; }
  .pred-lose { color: var(--text-dim); }
  .hw-tag { font-size: 0.68rem; color: var(--text-dim); margin-left: 6px; }
  .verdict-pill { font-size: 0.68rem; padding: 1px 6px; border-radius: 999px; margin-left: 6px; }
  .verdict-pill.correct { background: rgba(var(--win-rgb),0.18); color: var(--pill-good-text); }
  .verdict-pill.incorrect { background: rgba(var(--out-rgb),0.18); color: var(--pill-bad-text); }

  /* ---- Team page hero ----------------------------------------------------
     Replaces .team-view-header + .accuracy-summary (both kept above: the
     week view still renders .accuracy-summary for a period). The team's
     name is deliberately smaller here than the old 1.3rem <h2> - the sticky
     app header and the team switcher directly above both already name the
     team, so this is the third mention and doesn't need to shout. */
  .team-hero {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; padding: 14px 16px; margin-bottom: 10px;
  }
  .team-hero-top { display: flex; align-items: center; gap: 12px; }
  .team-hero-id { min-width: 0; flex: 1; }
  .team-hero-id h2 { margin: 0; font-size: 1.12rem; font-weight: 800; letter-spacing: -0.01em; line-height: 1.15; }
  .team-hero-sub {
    display: flex; align-items: center; flex-wrap: wrap; gap: 5px;
    margin-top: 3px; font-size: 0.78rem; color: var(--text-dim);
  }
  .team-hero-sub strong { color: var(--text); font-weight: 700; }
  .team-hero-sep { opacity: 0.5; }
  .team-hero-next { margin-top: 2px; }

  /* Off/Def ranks as a labelled grid. As a run-on line ("Off: Total #7 Rush
     #24 Pass #5  Def: ...") it wrapped between a label and its own pill,
     which is how "Pass" and "#25" ended up on different lines at 390px. */
  .team-rank-grid {
    display: grid; grid-template-columns: auto repeat(3, 1fr);
    gap: 5px 8px; align-items: center; margin-top: 12px;
    /* Capped rather than fluid: at 1100px the three rank columns spread so
       far apart that the grid stopped reading as a table. It only ever holds
       six pills, so it wants a table's width, not the card's. */
    max-width: 340px;
  }
  .team-rank-grid .trg-head {
    font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-dim); text-align: center;
  }
  .team-rank-grid .trg-side {
    font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-dim); font-weight: 800;
  }
  .team-rank-grid .trg-cell { text-align: center; }

  .team-hero-chips { display: flex; gap: 8px; margin-top: 12px; }
  .team-hero-chip {
    flex: 1 1 0; min-width: 0; background: var(--surface-2); border-radius: 10px;
    padding: 7px 8px; text-align: center;
  }
  .team-hero-chip .thc-key {
    display: block; font-size: 0.58rem; letter-spacing: 0.08em; text-transform: uppercase;
    color: var(--text-dim); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  /* Three chips across a 320px screen leaves ~90px each, which clipped
     "Blitz calls" to "Blitz cal...". Drop the letter-spacing and let the
     label use both lines rather than lose a word. */
  @media (max-width: 360px) {
    .team-hero-chips { gap: 6px; }
    .team-hero-chip { padding: 6px; }
    .team-hero-chip .thc-key { white-space: normal; letter-spacing: 0.04em; }
    .sched-list-hint { display: none; }
  }
  .team-hero-chip .thc-val { display: block; font-size: 0.9rem; font-weight: 800; margin-top: 2px; }

  /* ---- Injury report, collapsed to a count summary -----------------------
     The list is unbounded (one card per tracked player) and sat between the
     header and the tabs, so on mobile the schedule started a full viewport
     down. The summary row carries the same information a glance needs.
     #team-injury-report stays on the wrapper so the /teams/{slug}/injuries
     deep link from a push notification still scrolls here - TeamView opens
     the panel in the same effect that does the scroll. */
  .team-injuries {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px; margin-bottom: 10px; overflow: hidden;
  }
  .team-injury-toggle {
    display: flex; align-items: center; gap: 8px; width: 100%;
    background: transparent; border: none; color: var(--text);
    font: inherit; text-align: left; padding: 11px 14px; cursor: pointer;
  }
  .team-injury-toggle .tit-label {
    font-size: 0.68rem; letter-spacing: 0.07em; text-transform: uppercase;
    font-weight: 800; color: var(--text-dim); flex-shrink: 0;
  }
  .team-injury-toggle .tit-count {
    display: inline-flex; align-items: center; gap: 4px;
    font-size: 0.76rem; font-weight: 700; white-space: nowrap;
  }
  .team-injury-toggle .tit-count i { width: 7px; height: 7px; border-radius: 50%; flex: none; }
  .team-injury-toggle .tit-count.is-out { color: var(--out); }
  .team-injury-toggle .tit-count.is-out i { background: var(--out); }
  .team-injury-toggle .tit-count.is-q { color: var(--warn); }
  .team-injury-toggle .tit-count.is-q i { background: var(--warn); }
  .team-injury-toggle .tit-count.is-ok { color: var(--text-dim); font-weight: 500; }
  .team-injury-toggle .tit-counts { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px; min-width: 0; }
  .team-injury-toggle .tit-chevron { margin-left: auto; color: var(--text-dim); display: flex; flex: none; }
  .team-injury-body { padding: 0 14px 12px; }
  /* Under ~380px three counts plus the chevron is too much for one row -
     let the counts wrap under the label rather than squeeze. */
  /* Two rows rather than one under 380px: label and chevron on top, the
     whole count group underneath. As a wrapping flex row the chevron ended
     up mid-line, between the label and the counts. */
  @media (max-width: 380px) {
    .team-injury-toggle { display: grid; grid-template-columns: 1fr auto; row-gap: 5px; column-gap: 8px; }
    .team-injury-toggle .tit-label { grid-column: 1; grid-row: 1; align-self: center; }
    .team-injury-toggle .tit-chevron { grid-column: 2; grid-row: 1; margin-left: 0; justify-self: end; }
    .team-injury-toggle .tit-counts { grid-column: 1 / -1; grid-row: 2; }
  }

  /* ---- Mobile schedule card ----------------------------------------------
     Three lines, and the card itself is the link to the matchup's own page
     (/games/{season}/{week}/{away}-at-{home}/) - which every one of these
     rows has, and none of them linked to before. */
  .sched-list-head {
    display: flex; justify-content: space-between; align-items: center; gap: 10px;
    font-size: 0.66rem; letter-spacing: 0.05em; text-transform: uppercase;
    color: var(--text-dim); margin: 2px 2px 8px;
  }
  .sched-list-hint { text-align: right; }
  .sched-section {
    font-size: 0.64rem; letter-spacing: 0.09em; text-transform: uppercase;
    font-weight: 800; color: var(--text-dim); margin: 14px 2px 7px;
  }
  .sched-section:first-of-type { margin-top: 0; }

  .sched-card {
    display: block; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 12px; padding: 10px 12px; color: inherit; text-decoration: none;
    -webkit-tap-highlight-color: transparent;
  }
  .sched-card:hover { border-color: rgba(var(--accent-rgb), 0.5); }
  .sched-card.is-next { border-color: rgba(var(--accent-rgb), 0.55); box-shadow: 0 0 0 1px rgba(var(--accent-rgb), 0.22); }
  .sched-card.is-playoff { border-color: rgba(var(--demo-rgb), 0.4); }
  .sched-card.is-live { border-color: var(--live); box-shadow: 0 0 0 1px rgba(var(--live-rgb), 0.3); }

  .sched-meta {
    display: flex; align-items: center; gap: 5px; min-width: 0;
    font-size: 0.7rem; color: var(--text-dim); margin-bottom: 7px;
  }
  .sched-meta .sched-wk { font-weight: 800; color: var(--text); letter-spacing: 0.03em; flex: none; }
  .sched-meta .sched-dot { opacity: 0.5; flex: none; }
  .sched-meta .sched-when { flex: none; white-space: nowrap; }
  /* The network is the only part of the meta line that can be long ("Prime
     Video", "NFL Network"), so it's the only part allowed to truncate -
     everything else keeps its full value and the row stays one line. */
  .sched-meta .sched-net { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sched-meta-right { margin-left: auto; display: flex; align-items: center; gap: 5px; flex: none; }

  .sched-main { display: flex; align-items: center; gap: 9px; min-width: 0; }
  .sched-main .team-crest { flex: none; }
  .sched-opp {
    font-size: 0.95rem; font-weight: 700; min-width: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .sched-opp .sched-at { color: var(--text-dim); font-weight: 600; }
  .sched-wl { margin-left: auto; flex: none; font-size: 0.98rem; font-weight: 800; }
  .sched-wl.is-w { color: var(--win); }
  .sched-wl.is-l { color: var(--out); }
  .sched-live { margin-left: auto; flex: none; font-size: 0.76rem; }

  .sched-line2 { display: flex; align-items: center; gap: 8px; margin-top: 7px; font-size: 0.75rem; }
  .sched-edge {
    display: inline-flex; align-items: center; gap: 5px; font-weight: 700;
    padding: 3px 8px; border-radius: 999px; background: var(--surface-2); flex: none;
  }
  .sched-edge.is-w { color: var(--win); }
  .sched-edge.is-l { color: var(--text-dim); }
  .sched-edge .sched-pct { font-variant-numeric: tabular-nums; }
  /* Brand orange for a correct call, matching the GameCard "Called it" badge
     (round 15) - a right pick is a Blitz Edge signal, deliberately not the
     same green as the actual-win colour on the row above it. */
  .sched-call {
    display: inline-flex; align-items: center; gap: 4px; flex: none;
    font-size: 0.68rem; font-weight: 800; padding: 3px 8px; border-radius: 999px;
  }
  .sched-call.is-yes { background: rgba(253, 127, 1, 0.14); color: var(--blitz-orange); }
  .sched-call.is-no { background: rgba(var(--out-rgb), 0.12); color: var(--pill-bad-text); }
  .sched-live-clock { display: flex; align-items: center; gap: 6px; flex: none; font-weight: 600; color: var(--text-dim); }
  .sched-odds {
    margin-left: auto; text-align: right; color: var(--text-dim);
    font-variant-numeric: tabular-nums; white-space: nowrap;
    overflow: hidden; text-overflow: ellipsis; min-width: 0;
  }
  .sched-book-note { font-style: italic; }
  .sched-card .last-play { margin-top: 6px; }

  .sched-bye {
    display: flex; align-items: center; gap: 9px; padding: 9px 12px;
    border: 1px dashed var(--card-border); border-radius: 12px;
    color: var(--text-dim); font-size: 0.82rem;
  }
  .sched-bye .sched-wk { font-weight: 800; color: var(--text); letter-spacing: 0.03em; }

  /* Team tabs (Schedule / Roster & Depth Chart / News on mobile) */
  .team-tabs { display: flex; gap: 4px; margin: 18px 0 4px; border-bottom: 1px solid var(--card-border); flex-wrap: wrap; }
  .team-tab-btn {
    background: transparent; border: none; color: var(--text-dim); padding: 10px 14px; font-size: 0.85rem;
    font-weight: 600; cursor: pointer; border-bottom: 2px solid transparent; margin-bottom: -1px;
    transition: color 0.15s, border-color 0.15s;
  }
  .team-tab-btn:hover { color: var(--text); }
  .team-tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }

  /* On mobile the strip pins under the 52px .hdr-bar so tab navigation stays
     reachable from anywhere in a 20-game schedule, and scrolls sideways
     rather than wrapping - at 320px "News" used to drop onto a second row,
     below the active tab's underline. scroll-margin-top matches the header
     so selectTab()'s scrollIntoView doesn't park the strip underneath it. */
  @media (max-width: 720px) {
    .team-tabs {
      position: sticky; top: 52px; z-index: 20; scroll-margin-top: 52px;
      margin: 12px 0 10px; padding: 4px 0;
      flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
      background: var(--bg); -webkit-overflow-scrolling: touch;
    }
    .team-tabs::-webkit-scrollbar { display: none; }
    .team-tab-btn { flex: none; white-space: nowrap; padding: 8px 12px; }
  }

  /* Team view layout: main column + news column on desktop, stacked (with News as its own tab) on mobile */
  .team-view-layout { display: grid; grid-template-columns: 1fr 300px; gap: 20px; align-items: start; margin-top: 16px; }
  /* Stacks earlier than the mobile-card breakpoint (860px, see useIsMobile)
     on purpose: from 860px up to ~1099px the desktop table is already in
     play (not the mobile cards), but reserving 300px + gap for the news
     column left too little room for the table - particularly the Result
     column's live score/clock, which doesn't wrap (see .live-tag) and was
     getting clipped. Stacking the news column below instead of squeezing
     it in beside the table at these in-between widths avoids that; the
     .app max-width (1100px) means side-by-side never gets less room than
     this even on much wider screens, so 1100px is also where it's safe to
     switch back. */
  @media (max-width: 1099px) { .team-view-layout { grid-template-columns: 1fr; } }
  .team-view-main { min-width: 0; }

  /* Team news panel */
  .team-news-panel {
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 14px; padding: 16px;
  }
  .news-list { display: flex; flex-direction: column; gap: 12px; margin-top: 8px; }
  .news-item { display: flex; gap: 10px; text-decoration: none; color: inherit; }
  .news-item-img { width: 68px; height: 68px; object-fit: cover; border-radius: 8px; flex-shrink: 0; background: var(--surface-2); }
  .news-item-body { min-width: 0; }
  .news-item-headline { font-size: 0.84rem; font-weight: 600; line-height: 1.3; }
  .news-item:hover .news-item-headline { color: var(--accent); }
  .news-item-meta { font-size: 0.7rem; color: var(--text-dim); margin-top: 4px; }
  .news-pagination {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--card-border);
  }
  .news-page-btn {
    display: inline-flex; align-items: center; gap: 4px; background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 8px; color: var(--text);
    font-size: 0.76rem; font-weight: 600; padding: 6px 10px; cursor: pointer;
  }
  .news-page-btn:hover:not(:disabled), .news-page-btn:focus-visible:not(:disabled) {
    border-color: var(--accent); color: var(--accent);
  }
  .news-page-btn:disabled { opacity: 0.4; cursor: default; }
  .news-page-indicator { font-size: 0.72rem; color: var(--text-dim); }

  /* Depth chart */
  .depth-chart-block { margin-bottom: 22px; }
  .depth-chart-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px; margin-top: 8px; }
  .depth-slot { background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px; padding: 10px 12px; box-shadow: 0 1px 2px rgba(0,0,0,0.04); }
  .depth-slot-label { font-size: 0.7rem; font-weight: 700; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.4px; margin-bottom: 6px; }
  .depth-slot-players { display: flex; flex-direction: column; gap: 3px; }
  .depth-player { display: flex; align-items: center; gap: 6px; font-size: 0.83rem; }
  .depth-player-rank { color: var(--text-dim); font-size: 0.7rem; width: 12px; flex-shrink: 0; }
  .depth-player-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .depth-player-jersey { color: var(--text-dim); font-size: 0.72rem; margin-left: auto; flex-shrink: 0; }

  /* Full roster */
  .full-roster-block .roster-group { margin-bottom: 18px; }
  .roster-group-label { font-weight: 700; text-transform: capitalize; margin-bottom: 6px; font-size: 0.85rem; }
  .roster-table-wrap {
    overflow-x: auto; background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 12px;
  }
  .roster-table { font-size: 0.8rem; }
  .roster-table th { padding-left: 14px; }
  .roster-table td:first-child { padding-left: 14px; }
  .roster-table tbody tr:nth-child(even) td { background: var(--surface-2); }
  .roster-table tbody tr:hover td { background: rgba(var(--accent-rgb),0.08); }
  .roster-table tbody tr:last-child td { border-bottom: none; }

  /* Offense / Defense / Special Teams sub-tabs within Roster & Depth Chart */
  .roster-side-tabs { display: flex; gap: 6px; margin: 4px 0 16px; flex-wrap: wrap; }
  .roster-side-tab-btn {
    background: var(--card-bg); color: var(--text-dim); border: 1px solid var(--card-border);
    border-radius: 999px; padding: 7px 16px; font-size: 0.8rem; font-weight: 600; cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
  }
  .roster-side-tab-btn:hover { color: var(--text); border-color: rgba(var(--accent-rgb),0.4); }
  .roster-side-tab-btn.active { background: rgba(var(--accent-rgb),0.16); color: var(--accent); border-color: var(--accent); }

  /* ==================== ADMIN CONSOLE ==================== */
  .admin-overlay {
    position: fixed; inset: 0; z-index: 3000;
    background: var(--bg); overflow-y: auto;
    -webkit-overflow-scrolling: touch;
  }
  .admin-shell { max-width: 900px; margin: 0 auto; padding: 0 12px 80px; }

  .admin-hd {
    position: sticky; top: 0; z-index: 5; background: var(--bg);
    border-bottom: 1px solid var(--card-border); padding: 12px 0 0;
  }
  .admin-hd-row { display: flex; align-items: center; gap: 10px; padding-bottom: 10px; }
  .admin-hd h2 { font-size: 1.02rem; margin: 0; font-weight: 800; letter-spacing: -.01em; }
  .admin-badge {
    font-size: .6rem; font-weight: 800; letter-spacing: .09em; text-transform: uppercase;
    color: var(--blitz-orange); border: 1px solid rgba(253,127,1,.45);
    background: rgba(253,127,1,.1); padding: 3px 7px; border-radius: 5px;
  }
  .admin-close {
    margin-left: auto; background: none; border: none; color: var(--text-dim);
    font-size: 1.5rem; line-height: 1; cursor: pointer; padding: 0 4px; font-family: inherit;
  }
  .admin-close:hover { color: var(--text); }

  .admin-tabs { display: flex; gap: 2px; overflow-x: auto; scrollbar-width: none; }
  .admin-tabs::-webkit-scrollbar { display: none; }
  .admin-tab {
    flex: 0 0 auto; background: none; border: none; border-bottom: 2px solid transparent;
    color: var(--text-dim); font-family: inherit; font-size: .8rem; font-weight: 700;
    padding: 9px 12px; cursor: pointer; white-space: nowrap;
  }
  .admin-tab[aria-selected="true"] { color: var(--accent); border-bottom-color: var(--accent); }
  .admin-tab-dot {
    display: inline-block; margin-left: 5px; min-width: 16px; height: 16px; line-height: 16px;
    padding: 0 4px; border-radius: 8px; font-size: .6rem; font-weight: 800;
    background: var(--out); color: #fff; vertical-align: 1px;
  }
  .admin-body { padding-top: 14px; }

  .admin-search { margin-bottom: 10px; }
  .admin-search input,
  .admin-field input,
  .admin-field select,
  .admin-inline select,
  .admin-inj-act select {
    width: 100%; background: var(--surface-2); border: 1px solid var(--card-border);
    color: var(--text); font-family: inherit; font-size: .85rem;
    padding: 9px 10px; border-radius: 8px;
  }
  .admin-search input:focus, .admin-field input:focus,
  .admin-field select:focus, .admin-inline select:focus { outline: none; border-color: var(--accent); }

  .admin-field { display: block; margin-bottom: 10px; }
  .admin-field > span {
    display: block; font-size: .68rem; font-weight: 700; color: var(--text-dim);
    margin-bottom: 4px; text-transform: uppercase; letter-spacing: .04em;
  }
  /* The exact string a destructive confirm box expects, shown verbatim.
     It CANNOT live inside .admin-field > span above, which uppercases
     everything it contains: a league called "Sunday Money" was being
     displayed as "SUNDAY MONEY" and then rejected when that was typed back,
     because the match was case-sensitive. Hence text-transform:none, its own
     row, and user-select:all so one tap grabs the whole thing on a phone. */
  .admin-confirm-token {
    display: block; margin: 0 0 6px; padding: 7px 9px; border-radius: 8px;
    background: var(--surface-2); border: 1px dashed var(--card-border);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .8rem; font-weight: 700; color: var(--text);
    text-transform: none; letter-spacing: 0; line-height: 1.4;
    word-break: break-word; -webkit-user-select: all; user-select: all;
  }
  .admin-filters { display: grid; gap: 8px; margin-bottom: 12px; }
  @media (min-width: 620px) { .admin-filters { grid-template-columns: repeat(3, 1fr); } }

  .admin-list { list-style: none; margin: 0 0 10px; padding: 0;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px; overflow: hidden; }
  .admin-list li + li { border-top: 1px solid var(--card-border); }
  .admin-row {
    display: flex; align-items: center; gap: 10px; width: 100%;
    background: none; border: none; color: inherit; font-family: inherit;
    padding: 11px 12px; cursor: pointer; text-align: left;
  }
  .admin-row:hover { background: var(--surface-2); }
  .admin-av {
    flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%;
    background: var(--surface-2); border: 1px solid var(--card-border);
    display: grid; place-items: center; font-size: .74rem; font-weight: 800; color: var(--text-dim);
  }
  .admin-row-main { min-width: 0; flex: 1; }
  .admin-row-title { font-size: .86rem; font-weight: 700; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
  .admin-row-sub { font-size: .72rem; color: var(--text-dim); display: block;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .admin-row-meta { flex: 0 0 auto; font-size: .7rem; color: var(--text-dim); text-align: right; }
  .admin-chev { flex: 0 0 auto; color: var(--text-dim); font-size: .95rem; }

  .admin-pill {
    font-size: .6rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase;
    padding: 2px 6px; border-radius: 4px; white-space: nowrap;
  }
  .admin-pill-role { color: var(--blitz-orange); background: rgba(253,127,1,.14); border: 1px solid rgba(253,127,1,.4); }
  .admin-pill-off { color: var(--text-dim); background: var(--surface-2); border: 1px solid var(--card-border); }
  .admin-muted { color: var(--text-dim); }
  /* Terms acceptance trail. Collapsed by default - for almost every account
     it repeats the single acceptance already shown above it, and it only
     earns space on the ones that have re-accepted after a version bump. */
  .admin-terms-history { margin-top: 8px; }
  .admin-terms-history summary {
    cursor: pointer; font-size: 0.76rem; color: var(--text-dim); padding: 4px 0;
  }
  .admin-terms-history summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .admin-terms-history .admin-kv { font-size: 0.76rem; }
  /* Loss-red, matching the Delete zone rather than the muted "Unconfirmed"
     pill: an unconfirmed account is a state it arrived in, a suspended one
     is a decision somebody made, and the row should read differently. */
  .admin-pill-susp { color: var(--out); background: rgba(var(--out-rgb),.14); border: 1px solid rgba(var(--out-rgb),.45); }

  .admin-btn {
    display: inline-block; background: var(--surface-2); border: 1px solid var(--card-border);
    color: var(--text); font-family: inherit; font-size: .78rem; font-weight: 700;
    padding: 8px 12px; border-radius: 8px; cursor: pointer; text-decoration: none; text-align: center;
  }
  .admin-btn:hover { border-color: var(--accent); color: var(--accent); }
  .admin-btn-sm { padding: 6px 9px; font-size: .72rem; }
  .admin-btn-block { display: block; width: 100%; }
  .admin-btn-warn { background: rgba(var(--warn-rgb),.12); border-color: rgba(var(--warn-rgb),.45); color: var(--warn); }
  .admin-btn-warn:hover { border-color: var(--warn); color: var(--warn); }
  .admin-btn-danger { background: rgba(var(--out-rgb),.12); border-color: rgba(var(--out-rgb),.5); color: var(--out); }
  .admin-btn-danger:hover { border-color: var(--out); color: var(--out); }
  .admin-btn:disabled { opacity: .45; cursor: not-allowed; }

  .admin-sec { margin-bottom: 18px; }
  .admin-sec h4 {
    font-size: .68rem; font-weight: 800; letter-spacing: .07em; text-transform: uppercase;
    color: var(--text-dim); margin: 0 0 8px;
  }
  .admin-kv { display: flex; justify-content: space-between; align-items: center; gap: 12px;
    font-size: .8rem; padding: 6px 0; }
  .admin-kv + .admin-kv { border-top: 1px solid rgba(34,48,73,.6); }
  .admin-toggle { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: .82rem; cursor: pointer; }
  .admin-toggle input { width: 18px; height: 18px; accent-color: var(--accent); flex: 0 0 auto; }
  .admin-inline { display: flex; gap: 6px; margin-top: 10px; align-items: stretch; }
  .admin-inline select { flex: 1 1 auto; min-width: 0; }
  .admin-hint { font-size: .72rem; color: var(--text-dim); line-height: 1.45; margin: 8px 0 0; }
  .admin-empty { padding: 26px 14px; text-align: center; color: var(--text-dim); font-size: .82rem; }
  .admin-note {
    font-size: .74rem; color: var(--text-dim); line-height: 1.45; margin: 0 0 12px;
    background: var(--surface-2); border-left: 2px solid var(--warn);
    padding: 8px 10px; border-radius: 0 6px 6px 0;
  }
  .admin-zone { border: 1px solid rgba(var(--out-rgb),.35); background: rgba(var(--out-rgb),.06);
    border-radius: 10px; padding: 12px; }
  .admin-zone h4 { color: var(--out); }
  /* Suspension is reversible, deletion isn't, and the two sit next to each
     other at the bottom of the same sheet. Amber vs. red is the only thing
     stopping them reading as one undifferentiated danger area. */
  .admin-zone-warn { border-color: rgba(var(--warn-rgb),.4); background: rgba(var(--warn-rgb),.07); }
  .admin-zone-warn h4 { color: var(--warn); }

  .admin-status { font-size: .78rem; color: var(--text-dim); padding: 8px 0; }
  .admin-status-bad { color: var(--out); font-weight: 700; }
  .admin-status-ok { color: var(--win); font-weight: 700; }

  /* ---- sheet ---- */
  .admin-sheet-overlay {
    position: fixed; inset: 0; z-index: 3100; background: rgba(2,6,14,.72);
    display: flex; align-items: flex-end; justify-content: center;
  }
  .admin-sheet {
    width: 100%; max-width: 460px; max-height: 88vh; max-height: 88dvh; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-radius: 14px 14px 0 0;
  }
  @media (min-width: 720px) {
    .admin-sheet-overlay { align-items: center; }
    .admin-sheet { border-radius: 14px; max-height: 84vh; }
  }
  .admin-sheet-hd {
    position: sticky; top: 0; background: var(--card-bg); z-index: 2;
    padding: 14px 14px 10px; border-bottom: 1px solid var(--card-border);
    display: flex; align-items: flex-start; gap: 10px;
  }
  .admin-sheet-title { font-size: .95rem; font-weight: 800; }
  .admin-sheet-close {
    margin-left: auto; background: none; border: none; color: var(--text-dim);
    font-size: 1.35rem; line-height: 1; cursor: pointer; padding: 0 4px; font-family: inherit;
  }
  .admin-sheet-bd { padding: 14px; }

  /* ---- picks editor ---- */
  .admin-picks { list-style: none; margin: 0; padding: 0;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px 10px 0 0; overflow: hidden; }
  .admin-pick { padding: 11px 12px; }
  .admin-pick + .admin-pick { border-top: 1px solid var(--card-border); }
  .admin-pick-top { display: flex; align-items: baseline; gap: 8px; font-size: .82rem; }
  .admin-pick-teams { font-weight: 700; }
  .admin-pick-result { margin-left: auto; font-size: .72rem; color: var(--text-dim); }
  .admin-pick-ctl { display: flex; align-items: stretch; gap: 6px; margin-top: 8px; }
  .admin-seg {
    flex: 1 1 0; min-width: 0; height: 36px; padding: 0 8px;
    background: var(--surface-2); border: 1px solid var(--card-border);
    color: var(--text-dim); font-family: inherit; font-size: .76rem; font-weight: 700;
    border-radius: 7px; cursor: pointer;
  }
  .admin-seg[aria-pressed="true"] {
    background: rgba(var(--accent-rgb),.14); border-color: rgba(var(--accent-rgb),.55); color: var(--accent);
  }
  .admin-pick.dirty .admin-seg[aria-pressed="true"] {
    background: rgba(var(--warn-rgb),.14); border-color: rgba(var(--warn-rgb),.6); color: var(--warn);
  }
  /* Scoped past the shared .admin-field input rule, which sets width:100% and
     would otherwise win on specificity and blow the row out. */
  .admin-pick-ctl .admin-conf {
    flex: 0 0 58px; width: 58px; height: 36px; padding: 0 6px;
    background: var(--surface-2); border: 1px solid var(--card-border);
    color: var(--text); font-family: inherit; font-size: .82rem; font-weight: 800;
    text-align: center; border-radius: 7px;
  }
  .admin-conf-lbl {
    flex: 0 0 auto; align-self: center; font-size: .62rem; font-weight: 800;
    color: var(--text-dim); text-transform: uppercase; letter-spacing: .04em;
  }
  .admin-pick-was { font-size: .68rem; color: var(--warn); margin-top: 5px; }
  .admin-savebar {
    display: flex; align-items: center; gap: 8px; padding: 10px 12px;
    background: var(--card-bg); border: 1px solid var(--card-border); border-top: none;
    border-radius: 0 0 10px 10px;
  }
  .admin-savebar-text { flex: 1; font-size: .72rem; color: var(--text-dim); }

  /* ---- injuries ---- */
  .admin-inj {
    background: var(--card-bg); border: 1px solid var(--card-border);
    border-left: 2px solid var(--out); border-radius: 8px; padding: 12px; margin-bottom: 8px;
  }
  .admin-inj.downgrade { border-left-color: var(--warn); }
  .admin-inj-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
  .admin-inj-name { font-size: .88rem; font-weight: 800; }
  .admin-inj-move { display: flex; align-items: center; gap: 8px; margin: 9px 0 4px; font-size: .78rem; flex-wrap: wrap; }
  .admin-inj-arrow { color: var(--text-dim); }
  /* Whose statement each chip is. Without a label the row reads as one
     transition when it's really two sources being compared. */
  .admin-inj-who {
    font-size: .6rem; font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-dim);
  }
  .admin-inj-mine {
    display: flex; align-items: center; gap: 6px;
    margin-left: 4px; padding-left: 10px; border-left: 1px solid var(--card-border);
  }
  .admin-inj-src { font-size: .7rem; color: var(--text-dim); line-height: 1.45; }
  .admin-inj-warn { font-size: .7rem; color: var(--warn); font-weight: 700; margin-top: 7px; }
  .admin-inj-act { display: flex; gap: 6px; margin-top: 10px; align-items: stretch; }
  .admin-inj-act select { flex: 1 1 auto; min-width: 0; }
  /* Scoped past the shared .admin-field input rule (width:100%), same
     specificity trap the confidence box hit. */
  .admin-inj-act .admin-inj-score {
    flex: 0 0 56px; width: 56px; padding: 8px 6px; text-align: center;
    background: var(--surface-2); border: 1px solid var(--card-border);
    color: var(--text); font-family: inherit; font-size: .82rem; font-weight: 800;
    border-radius: 8px;
  }
  .admin-inj-score-hint { font-size: .68rem; color: var(--text-dim); margin-top: 7px; line-height: 1.4; }
  .admin-inj-purge {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--card-border);
  }
  .admin-inj-purge-ask { flex: 1 1 220px; font-size: .72rem; color: var(--warn); font-weight: 700; line-height: 1.45; }
  /* The queue row's player name is a button into the depth-chart sheet. It
     has to keep looking like the heading it replaced, so it inherits
     .admin-inj-name's type and only adds the affordance. */
  .admin-inj-name-btn {
    background: none; border: none; padding: 0; margin: 0; font: inherit;
    color: var(--text); cursor: pointer; border-bottom: 1px dashed var(--card-border);
  }
  .admin-inj-name-btn:hover, .admin-inj-name-btn:focus-visible {
    color: var(--accent); border-bottom-color: currentColor; outline: none;
  }

  /* ---- depth chart sheet ---- */
  .admin-depth-sum {
    font-size: .8rem; line-height: 1.5; background: var(--surface-2);
    border: 1px solid var(--card-border); border-radius: 8px; padding: 10px 11px; margin-bottom: 12px;
  }
  .admin-depth-sum strong { font-weight: 800; }
  .admin-depth-slot {
    border: 1px solid var(--card-border); border-radius: 8px; padding: 10px 11px; margin-bottom: 8px;
  }
  .admin-depth-slot-hd { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
  .admin-depth-slot-label { font-size: .82rem; font-weight: 800; }
  .admin-depth-slot-side {
    font-size: .62rem; font-weight: 800; letter-spacing: .05em; text-transform: uppercase; color: var(--text-dim);
  }
  .admin-depth-pill {
    margin-left: auto; font-size: .62rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
    padding: 3px 7px; border-radius: 5px; white-space: nowrap;
    color: var(--text-dim); background: var(--surface-2); border: 1px solid var(--card-border);
  }
  .admin-depth-pill.is-starter { color: var(--win); background: rgba(var(--win-rgb),.14); border-color: rgba(var(--win-rgb),.45); }
  .admin-depth-list { list-style: none; margin: 9px 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
  .admin-depth-row {
    display: flex; align-items: center; gap: 8px; font-size: .8rem;
    padding: 5px 7px; border-radius: 6px; min-width: 0;
  }
  .admin-depth-row.is-player {
    background: rgba(var(--accent-rgb),.14); border: 1px solid rgba(var(--accent-rgb),.45); padding: 4px 6px; font-weight: 800;
  }
  .admin-depth-row .adr-n { flex: 0 0 14px; color: var(--text-dim); font-size: .7rem; font-weight: 700; }
  .admin-depth-row .adr-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .admin-depth-row .adr-tag { flex: 0 0 auto; font-size: .62rem; font-weight: 800; color: var(--warn); letter-spacing: .04em; text-transform: uppercase; }
  .admin-depth-row .adr-jersey { margin-left: auto; flex: 0 0 auto; color: var(--text-dim); font-size: .72rem; }
  .admin-st {
    font-size: .64rem; font-weight: 800; letter-spacing: .04em; text-transform: uppercase;
    padding: 3px 7px; border-radius: 5px; white-space: nowrap;
  }
  .admin-st.st-active { color: var(--win); background: rgba(var(--win-rgb),.12); border: 1px solid rgba(var(--win-rgb),.35); }
  .admin-st.st-questionable { color: var(--star); background: rgba(245,197,24,.12); border: 1px solid rgba(245,197,24,.4); }
  .admin-st.st-doubtful { color: var(--warn); background: rgba(var(--warn-rgb),.13); border: 1px solid rgba(var(--warn-rgb),.45); }
  .admin-st.st-out, .admin-st.st-ir { color: var(--out); background: rgba(var(--out-rgb),.13); border: 1px solid rgba(var(--out-rgb),.4); }

  /* ---- activity ---- */
  /* Alert delivery log. The rows are denser than the audit log's - a pass
     can hold several readers - so each row stacks its outcome above the
     subject rather than sitting on one line that wraps unpredictably at
     phone width. */
  .admin-alert-devices {
    border: 1px solid var(--card-border); border-radius: 10px; padding: 12px 13px;
    margin: 4px 0 14px; background: var(--surface-2);
  }
  .admin-alert-devices.bad { border-color: var(--out); background: rgba(var(--out-rgb),0.09); }
  .admin-alert-devices-title { margin: 0 0 8px; font-size: .8rem; font-weight: 700; color: var(--text); }
  .admin-alert-devices.bad .admin-alert-devices-title { color: var(--out); }
  .admin-alert-device-list { list-style: none; margin: 0; padding: 0; font-size: .76rem; }
  .admin-alert-device-list li { display: flex; flex-wrap: wrap; gap: 4px 8px; padding: 3px 0; }
  .admin-alert-device-name { font-weight: 600; color: var(--text); }
  .admin-alert-device-meta { color: var(--text-dim); }
  .admin-alert-prefs {
    margin: 9px 0 0; padding-top: 9px; border-top: 1px solid var(--card-border);
    font-size: .74rem; color: var(--text-dim);
  }
  .admin-alert-prefs b { color: var(--text); font-weight: 600; }
  .admin-alert-log li { flex-wrap: wrap; }
  .admin-alert-row { display: block; margin-bottom: 4px; }
  .admin-alert-row:last-child { margin-bottom: 0; }
  .admin-alert-outcome {
    display: inline-block; font-size: .68rem; font-weight: 700; letter-spacing: .02em;
    padding: 1px 7px; border-radius: 999px; margin-right: 7px;
    background: var(--surface-2); color: var(--text-dim); border: 1px solid var(--card-border);
  }
  .admin-alert-outcome.good { color: var(--win); border-color: rgba(var(--win-rgb),0.45); }
  .admin-alert-outcome.bad { color: var(--out); border-color: rgba(var(--out-rgb),0.5); }
  .admin-alert-what { color: var(--text-dim); font-size: .74rem; }

  .admin-log { list-style: none; margin: 0; padding: 0; font-size: .78rem;
    background: var(--card-bg); border: 1px solid var(--card-border); border-radius: 10px; overflow: hidden; }
  .admin-log li { padding: 10px 12px; display: flex; gap: 10px; }
  .admin-log li + li { border-top: 1px solid var(--card-border); }
  .admin-log time { flex: 0 0 auto; color: var(--text-dim); font-size: .7rem; width: 92px; }
  .admin-log-who { color: var(--accent); font-weight: 700; }

  .admin-toast {
    position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%);
    background: var(--surface-2); border: 1px solid var(--card-border); color: var(--text);
    font-size: .8rem; font-weight: 600; padding: 10px 16px; border-radius: 999px;
    z-index: 3200; max-width: 92vw; text-align: center;
  }

