/* v2.css — the September 2026 site design, ported from Reilly's comp
   (blokesvsbookiessite.html, 2026-09-01).

   Everything is prefixed `v2-` and the bare element rules are scoped to
   `body.v2`, so this layer never reaches site.css components or the checkout
   and lead dialogs site.js injects into <body>.

   Load ORDER matters: tokens.css, site.css, then this. */

/* No overflow-x here: `hidden` on body makes BODY the scroll container,
   which breaks position: sticky on iOS Safari. site.css already clips
   horizontal overflow on <html>, which is the same containment without the
   scroll container. */
/* No padding reset here: `body.v2` outranks the plain `body` rule that gives
   the fixed header its room back, and zeroing it put the headline under the
   pill. */
body.v2 {
  margin: 0; background: #0d1117; color: #ffffff;
  /* One face, site-wide (Reilly 2026-09-02). Pages carried this inline; it is
     stated once here so a new page only needs the class. */
  font-family: 'Mona Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 16px; line-height: 1.5; -webkit-font-smoothing: antialiased;
}

.v2 a { color: #8dd6ff; text-decoration: none; }

.v2 a:hover { color: #ffffff; }

.v2 p { margin: 0; }

.v2 h1, .v2 h2, .v2 h3, .v2 h4 {
  margin: 0; font-weight: 460;
  /* site.css puts every heading in Oswald, uppercase. The v2 design sets its
     own type, so the display face is unset here rather than fought inline on
     every heading. */
  font-family: 'Mona Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  text-transform: none;
}
.v2 blockquote { quotes: none; }

.v2 table { border-collapse: collapse; }

.v2-hero-grid {
      position: absolute; inset: 0;
      background-image:
        repeating-linear-gradient(to right, rgba(255,255,255,0.04) 0 1px, transparent 1px 72px),
        repeating-linear-gradient(to bottom, rgba(255,255,255,0.04) 0 1px, transparent 1px 72px);
      -webkit-mask-image: radial-gradient(circle 540px at 50% 36%, #000 0%, transparent 78%);
      mask-image: radial-gradient(circle 540px at 50% 36%, #000 0%, transparent 78%);
      pointer-events: none;
    }

.v2-hero-noise {
      position: absolute; inset: 0; opacity: 0.055; pointer-events: none;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.v2-w3.v2-org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
    }

.v2-hero-halo {
      position: absolute; border-radius: 9999px; filter: blur(60px); pointer-events: none;
      /* Promoted deliberately: these are blurred elements running transform-only
         animations. Without a layer Chrome re-runs a 60px blur over a
         thousand-pixel box every frame, which pegs the renderer on anything
         without a GPU. With one, the blur is computed once and the frames are
         just a composited transform. */
      will-change: transform;
    }

.v2-halo-cyan {
      top: -260px; left: 50%; width: 1080px; height: 680px; margin-left: -540px;
      background: radial-gradient(circle, rgba(6,229,255,0.30) 0%, rgba(6,229,255,0) 68%);
      animation: v2-drift-a 26s ease-in-out infinite alternate;
    }

.v2-halo-violet {
      top: 40px; left: 50%; width: 900px; height: 620px; margin-left: -160px;
      background: radial-gradient(circle, rgba(0,180,255,0.28) 0%, rgba(0,180,255,0) 70%);
      animation: v2-drift-b 34s ease-in-out infinite alternate;
    }

.v2-halo-deep {
      top: 300px; left: 50%; width: 860px; height: 560px; margin-left: -700px;
      background: radial-gradient(circle, rgba(0,102,255,0.48) 0%, rgba(0,102,255,0) 70%);
      animation: v2-drift-c 30s ease-in-out infinite alternate;
    }

@keyframes v2-drift-a {
      from { transform: translate3d(-60px, -20px, 0) scale(1); }
      to   { transform: translate3d(70px, 40px, 0) scale(1.12); }
    }

@keyframes v2-drift-b {
      from { transform: translate3d(40px, 30px, 0) scale(1.08); }
      to   { transform: translate3d(-70px, -30px, 0) scale(0.94); }
    }

@keyframes v2-drift-c {
      from { transform: translate3d(0, 0, 0) scale(0.96); }
      to   { transform: translate3d(90px, -50px, 0) scale(1.15); }
    }

.v2-halo-cobalt {
      top: 220px; left: 50%; width: 800px; height: 560px; margin-left: 40px;
      background: radial-gradient(circle, rgba(0,102,255,0.40) 0%, rgba(0,102,255,0) 70%);
      animation: v2-drift-d 38s ease-in-out infinite alternate;
    }

.v2-halo-azure {
      top: -60px; left: 50%; width: 640px; height: 460px; margin-left: -820px;
      background: radial-gradient(circle, rgba(0,51,204,0.20) 0%, rgba(0,51,204,0) 70%);
      animation: v2-drift-e 42s ease-in-out infinite alternate;
    }

@keyframes v2-drift-d {
      from { transform: translate3d(30px, 20px, 0) scale(1); }
      to   { transform: translate3d(-80px, -60px, 0) scale(1.18); }
    }

@keyframes v2-drift-e {
      from { transform: translate3d(-20px, 40px, 0) scale(1.1); }
      to   { transform: translate3d(80px, -30px, 0) scale(0.92); }
    }

.v2-halo-m1 {
      top: -180px; left: 50%; width: 900px; height: 620px; margin-left: -680px;
      background: radial-gradient(circle, rgba(6,229,255,0.26) 0%, rgba(6,229,255,0) 70%);
      animation: v2-drift-a 30s ease-in-out infinite alternate;
    }

.v2-halo-m2 {
      top: 120px; left: 50%; width: 760px; height: 560px; margin-left: -60px;
      background: radial-gradient(circle, rgba(0,102,255,0.34) 0%, rgba(0,102,255,0) 70%);
      animation: v2-drift-d 36s ease-in-out infinite alternate;
    }

.v2-halo-m3 {
      top: 260px; left: 50%; width: 700px; height: 480px; margin-left: 220px;
      background: radial-gradient(circle, rgba(0,102,255,0.44) 0%, rgba(0,102,255,0) 70%);
      animation: v2-drift-c 32s ease-in-out infinite alternate;
    }

.v2-halo-c1 {
      bottom: -220px; left: 50%; width: 900px; height: 560px; margin-left: -620px;
      background: radial-gradient(circle, rgba(6,229,255,0.26) 0%, rgba(6,229,255,0) 70%);
      animation: v2-drift-a 28s ease-in-out infinite alternate;
    }

.v2-halo-c2 {
      bottom: -140px; left: 50%; width: 760px; height: 500px; margin-left: -40px;
      background: radial-gradient(circle, rgba(0,102,255,0.36) 0%, rgba(0,102,255,0) 70%);
      animation: v2-drift-d 34s ease-in-out infinite alternate;
    }

.v2-hero-sweep {
      position: absolute; top: 50%; left: 50%; width: 2200px; height: 2200px;
      margin: -1100px 0 0 -1100px; pointer-events: none; opacity: 0.22;
      background: conic-gradient(from 0deg,
        rgba(6,229,255,0) 0deg, rgba(6,229,255,0.5) 40deg, rgba(6,229,255,0) 90deg,
        rgba(0,180,255,0) 180deg, rgba(0,180,255,0.40) 220deg, rgba(0,180,255,0) 270deg,
        rgba(6,229,255,0) 360deg);
      filter: blur(60px);
      will-change: transform;
      animation: v2-sweep 48s linear infinite;
      -webkit-mask-image: radial-gradient(circle at 50% 50%, #000 0%, transparent 62%);
      mask-image: radial-gradient(circle at 50% 50%, #000 0%, transparent 62%);
    }

@keyframes v2-sweep {
      from { transform: rotate(0deg); }
      to   { transform: rotate(360deg); }
    }

.v2-mote {
      position: absolute; border-radius: 9999px; pointer-events: none;
      box-shadow: 0 0 12px 2px currentColor;
      will-change: transform, opacity;
    }

@keyframes v2-float-a {
      0%   { transform: translate3d(0, 0, 0) scale(0.6); opacity: 0; }
      12%  { opacity: 1; }
      50%  { transform: translate3d(26px, -70px, 0) scale(1); opacity: 1; }
      88%  { opacity: 1; }
      100% { transform: translate3d(-18px, -150px, 0) scale(0.6); opacity: 0; }
    }

@keyframes v2-float-b {
      0%   { transform: translate3d(0, 0, 0) scale(0.6); opacity: 0; }
      12%  { opacity: 0.95; }
      50%  { transform: translate3d(-34px, -90px, 0) scale(1); opacity: 0.95; }
      88%  { opacity: 0.95; }
      100% { transform: translate3d(20px, -180px, 0) scale(0.6); opacity: 0; }
    }

/* Section-arrival fade (Reilly 2026-09-02, cut way down): this used to run
   ~1s per section with up to a 0.6s stagger on TOP of that, so a page with
   five .v2-rv sections took the better part of two seconds to finish
   settling — on every single load, including clicking straight back and
   forth between Education, Dashboard and the marketing pages, where it read
   as the page "loading up and down" rather than arriving. Same idea, a
   fraction of the time and travel: a quick, barely-there settle instead of
   a staged reveal. (This block also folds in what used to be a second,
   near-duplicate copy of these exact rules further down the file, at
   slightly different numbers — only one copy governs now.) */
.v2-rv {
      opacity: 0;
      animation: v2-rise 280ms cubic-bezier(0.16, 1, 0.3, 1) both;
    }

@keyframes v2-rise {
      from { opacity: 0; transform: translate3d(0, 8px, 0); }
      to   { opacity: 1; transform: translate3d(0, 0, 0); }
    }

.v2-rv-1 { animation-delay: 0s; }

.v2-rv-2 { animation-delay: 0.04s; }

.v2-rv-3 { animation-delay: 0.08s; }

.v2-rv-4 { animation-delay: 0.12s; }

.v2-rv-5 { animation-delay: 0.16s; }

.v2-hero-scanlines {
      position: absolute; inset: 0; opacity: 0.5; pointer-events: none;
      background-image: repeating-linear-gradient(to bottom, rgba(255,255,255,0.018) 0 1px, transparent 1px 4px);
      -webkit-mask-image: linear-gradient(to bottom, transparent, #000 40%, transparent);
      mask-image: linear-gradient(to bottom, transparent, #000 40%, transparent);
    }

/* The draw-on line is JS's job now (drawSpark in v2.js). It used to live
   here with a hardcoded 2400-unit dash, which is a guess at the path's
   length: the record's curve measures ~3150, so the last quarter of the line
   fell inside the dash gap and the green stopped short (Reilly, 2026-09-02).
   Nothing dashes the path from CSS any more — if the script never runs, the
   line simply draws whole, which is the honest failure. */

.v2-marquee {
      overflow: hidden; width: 100%;
      -webkit-mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
      mask-image: linear-gradient(to right, transparent, #000 10%, #000 90%, transparent);
    }

.v2-marquee-track {
      display: flex; gap: 14px; width: max-content;
      animation: v2-marquee-scroll 79s linear infinite;
    }

@keyframes v2-marquee-scroll {
      from { transform: translate3d(0, 0, 0); }
      to   { transform: translate3d(-50%, 0, 0); }
    }

.v2-logo-slot {
      width: 72px; height: 72px; flex-shrink: 0;
      display: flex; align-items: center; justify-content: center;
    }

.v2-marquee-track-rev {
      display: flex; gap: 14px; width: max-content;
      animation: v2-marquee-rev 75s linear infinite;
    }

@keyframes v2-marquee-rev {
      from { transform: translate3d(-50%, 0, 0); }
      to   { transform: translate3d(0, 0, 0); }
    }

.v2-tmq {
      overflow: hidden; width: 100%;
      -webkit-mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
      mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
    }

.v2-tmq-track {
      display: flex; width: max-content;
      animation: v2-tmq-scroll 100s linear infinite;
    }

@keyframes v2-tmq-scroll {
      from { transform: translate3d(0, 0, 0); }
      to   { transform: translate3d(-50%, 0, 0); }
    }

.v2-tm-slot { width: 244px; flex-shrink: 0; padding-right: 14px; box-sizing: border-box; }

.v2-anim-zone:hover .v2-hero-halo, .v2-anim-zone:hover .v2-hero-sweep, .v2-anim-zone:hover .v2-mote {
      animation-play-state: paused;
    }

/* --- Off-screen zones stop animating (Reilly 2026-09-02 perf pass) -------
   The home page runs 27 infinite animations at once: ten blurred halos,
   twelve motes, two marquee tracks, and THREE 2200x2200 conic sweeps each
   carrying a 60px blur. All of them ran forever whether or not they were
   anywhere near the viewport — scrolled to the footer, the hero's entire
   light show was still being composited every frame. On a phone that is
   enough on its own to make scrolling feel like the page is struggling.

   v2.js flips .is-idle on each zone as it leaves the viewport. Nothing
   visible changes: this only ever stops work on pixels nobody is looking
   at, and the animations are all slow ambient drifts that resume in place
   the moment the zone comes back. */
.v2-anim-zone.is-idle .v2-hero-halo,
.v2-anim-zone.is-idle .v2-hero-sweep,
.v2-anim-zone.is-idle .v2-mote,
.v2-marquee.is-idle .v2-marquee-track,
.v2-marquee.is-idle .v2-marquee-track-rev {
  animation-play-state: paused;
}

/* The sweep is the single most expensive thing on the page — a 2200px
   square conic gradient under a 60px blur, rotating. On a phone you see a
   sliver of it through a 375px window, so it is paying almost the full
   cost to deliver almost none of the effect. The halos underneath carry
   the same colour and stay. NOTE: this is a real (if subtle) visual change
   on phones — the slow rotating wash behind the hero is gone there. */
@media (max-width: 720px) {
  .v2-hero-sweep { display: none; }
}

@media (prefers-reduced-motion: reduce) {
.v2-hero-halo, .v2-hero-sweep, .v2-mote, .v2-marquee-track, .v2-marquee-track-rev, .v2-tmq-track, .v2-rv {
        animation: none !important;
      }
.v2-rv { opacity: 1; transform: none; filter: none; }
}

@media (forced-colors: active) {
.v2-seg-item[aria-pressed="true"], .v2-seg-item[aria-current="page"] {
        outline: 2px solid Highlight;
        outline-offset: -2px;
      }
}

.v2-gl { position: relative; overflow: hidden; }

.v2-gl::before {
      content: "";
      position: absolute; inset: 0;
      background: linear-gradient(145deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0) 46%);
      pointer-events: none;
    }

.v2-gl::after {
      content: "";
      position: absolute; top: 0; left: 6%;
      width: 42%; height: 1px;
      background: linear-gradient(90deg, transparent, rgba(255,255,255,0.65), transparent);
      pointer-events: none;
    }

.v2-seg-track {
      display: inline-flex; align-items: center; gap: 4px; padding: 4px;
      background: rgba(0,0,0,0.24); border: 1px solid rgba(255,255,255,0.08);
      border-radius: 9999px; box-shadow: inset 0 1px 3px rgba(0,0,0,0.3);
    }

.v2-seg-item {
      display: inline-flex; align-items: center; justify-content: center;
      padding: 8px 16px; border-radius: 9999px; border: 0; background: transparent;
      color: #a2adb8; font-size: 14px; font-family: inherit; text-decoration: none;
      white-space: nowrap; cursor: pointer;
    }

.v2-seg-item[aria-pressed="true"], .v2-seg-item[aria-current="page"] {
      color: #ffffff; font-weight: 500; background: rgba(255,255,255,0.16);
      box-shadow: inset 0 1px rgba(255,255,255,0.2), 0 2px 8px rgba(0,0,0,0.22);
    }

/* Dropdowns inside the pill track (the record page's desk / sport filters,
   2026-09-25). Same material as .v2-seg-item, with the native select doing
   the work — a listbox that opens where the OS puts it beats a menu we own. */
.v2-seg-label {
      padding-left: 8px; font-size: 12px; font-weight: 500; letter-spacing: 0.04em;
      text-transform: uppercase; color: #78848f; white-space: nowrap;
    }

.v2-seg-select {
      appearance: none; -webkit-appearance: none;
      padding: 8px 30px 8px 14px; border: 0; border-radius: 9999px;
      background-color: rgba(255,255,255,0.16);
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23a2adb8' stroke-width='1.6' stroke-linecap='round'/%3E%3C/svg%3E");
      background-repeat: no-repeat; background-position: right 12px center;
      box-shadow: inset 0 1px rgba(255,255,255,0.2), 0 2px 8px rgba(0,0,0,0.22);
      color: #ffffff; font: inherit; font-size: 14px; font-weight: 500;
      max-width: 13rem; text-overflow: ellipsis; cursor: pointer;
    }

.v2-seg-select:focus-visible { outline: 2px solid #06e5ff; outline-offset: 2px; }

.v2-seg-select option { background-color: #151a22; color: #ffffff; }

.v2-site-header-wrap {
      transition: transform 320ms cubic-bezier(0.4,0,0.2,1), opacity 320ms ease;
    }

.v2-site-header-wrap > header { pointer-events: auto; }

.v2-site-header-wrap.v2-is-hidden {
      transform: translateY(-140%);
      opacity: 0;
    }

@media (prefers-reduced-motion: reduce) {
.v2-site-header-wrap { transition: none; }
}

.v2 table { border-collapse: collapse; width: 100%; }

.v2 th, .v2 td { text-align: left; }

@keyframes v2-drift-a { from { transform: translate3d(-60px,-20px,0) scale(1); } to { transform: translate3d(70px,40px,0) scale(1.12); } }

@keyframes v2-drift-b { from { transform: translate3d(40px,30px,0) scale(1.08); } to { transform: translate3d(-70px,-30px,0) scale(0.94); } }

@keyframes v2-drift-c { from { transform: translate3d(0,0,0) scale(0.96); } to { transform: translate3d(90px,-50px,0) scale(1.15); } }

@keyframes v2-drift-d { from { transform: translate3d(30px,20px,0) scale(1); } to { transform: translate3d(-80px,-60px,0) scale(1.18); } }

@keyframes v2-drift-e { from { transform: translate3d(-20px,40px,0) scale(1.1); } to { transform: translate3d(80px,-30px,0) scale(0.92); } }

@keyframes v2-float-a {
      0% { transform: translate3d(0,0,0) scale(0.6); opacity: 0; }
      12% { opacity: 1; }
      50% { transform: translate3d(26px,-70px,0) scale(1); opacity: 1; }
      88% { opacity: 1; }
      100% { transform: translate3d(-18px,-150px,0) scale(0.6); opacity: 0; }
    }

@keyframes v2-float-b {
      0% { transform: translate3d(0,0,0) scale(0.6); opacity: 0; }
      12% { opacity: 0.95; }
      50% { transform: translate3d(-34px,-90px,0) scale(1); opacity: 0.95; }
      88% { opacity: 0.95; }
      100% { transform: translate3d(20px,-180px,0) scale(0.6); opacity: 0; }
    }

/* Was JetBrains Mono; retired 2026-09-02 (Reilly). The class survives as the
   LABEL role it always was — tracked, tabular figures — now in the one
   family, so the ~60 call sites did not have to be unpicked. */
.v2-mono {
  font-family: 'Mona Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  letter-spacing: 0.015em;
  font-variant-numeric: tabular-nums;
}

.v2-label { font-size: 13px; font-weight: 500; color: #a2adb8; }

.v2-field {
      display: flex; align-items: center; gap: 8px;
      height: 44px; padding: 0 12px; box-sizing: border-box;
      background: #151a22; border: 1px solid #21262d; border-radius: 6px;
      font-size: 15px; color: #ffffff;
    }

.v2-field-ph { color: #78848f; }

.v2-card {
      background: rgba(255,255,255,0.06);
      border: 1px solid rgba(255,255,255,0.1);
      border-radius: 24px;
      padding: 24px;
      box-sizing: border-box;
      position: relative;
      overflow: hidden;
      backdrop-filter: blur(20px) saturate(1.35);
      -webkit-backdrop-filter: blur(20px) saturate(1.35);
    }

.v2-card::before {
      content: "";
      position: absolute; inset: 0;
      background: linear-gradient(145deg, rgba(255,255,255,0.14) 0%, rgba(255,255,255,0) 46%);
      pointer-events: none;
    }

.v2-card::after {
      content: "";
      position: absolute; top: 0; left: 6%;
      width: 42%; height: 1px;
      background: linear-gradient(90deg, transparent, rgba(255,255,255,0.65), transparent);
      pointer-events: none;
    }

/* .v2-rv itself, its keyframes, the per-index delays and the
   reduced-motion override are ALL defined once now, up where .v2-rv first
   appears — this used to be the third of three copies at three different
   speeds (Reilly 2026-09-02 cleanup, while cutting the reveal down to fix
   "loading up and down" between pages). */

.v2-rv-6 { animation-delay: 0.44s; }



/* (A second .v2-card block from the comp's per-page CSS used to sit here and
   imposed display:flex column on every card, which stacked the hub and tools
   chip strips vertically. A card is a surface; layout is the pane's own.) */

.v2-chip {
      display: flex; align-items: center; justify-content: center;
      width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0;
    }

.v2-ghost-btn {
      display: flex; align-items: center; justify-content: center; gap: 8px;
      height: 44px; padding: 0 18px; box-sizing: border-box;
      border: 1px solid rgba(255,255,255,0.3); border-radius: 6px;
      font-size: 15px; font-weight: 500; color: #ffffff;
    }

.v2-bar { height: 6px; border-radius: 9999px; background: #21262d; }

.v2-chip {
      display: flex; align-items: center; justify-content: center;
      width: 38px; height: 38px; border-radius: 10px; flex-shrink: 0;
      background: rgba(0,102,255,0.16); border: 1px solid rgba(141,214,255,0.32);
    }
/* --- responsive ---------------------------------------------------------
   The comp is a desktop export: every size and grid is an inline style, so
   the only lever that beats them is !important. Collapsing every multi-column
   grid to one column is right for every grid on these pages.
   ponytail: blunt attribute-selector override, give a section its own class
   if it ever needs a two-column mobile layout. */
@media (max-width: 900px) {
  .v2 [style*="grid-template-columns"] { grid-template-columns: 1fr !important; }
  .v2 h1 { font-size: 40px !important; }
  .v2 h2 { font-size: 30px !important; }
  .v2 section { padding-left: 0 !important; padding-right: 0 !important; }
  .v2 [style*="justify-content: space-between"] { flex-wrap: wrap; }
}

@media (max-width: 720px) {
  .v2-site-header-wrap { padding: 10px 12px 0 12px !important; }
  .v2-site-header-wrap > header {
    height: auto !important; border-radius: 20px !important;
    flex-wrap: wrap; padding: 8px 10px !important; gap: 8px !important;
  }
  .v2-site-header-wrap nav { order: 3; width: 100%; overflow-x: auto; }
  .v2-site-header-wrap .v2-seg-track { display: flex; width: max-content; }
  .v2-seg-item { padding: 8px 12px !important; font-size: 13px !important; }
  .v2 h1 { font-size: 32px !important; }

  /* --- The phone type scale (Reilly 2026-09-02: "the sizes of everything
     are not correct" on mobile) ------------------------------------------
     Only the headings were ever scaled down. Everything else on these pages
     — the leads, the section headings, the big figures — kept the desktop
     size the comp exported inline, so a 375px screen was being handed 40px
     headings and 18px body copy and the hero alone ran well past a
     screenful. Same blunt attribute-substring lever the grid collapse above
     uses, for the same reason: the sizes are inline, so nothing else beats
     them. Sizes of 16px and under are already right for a phone and are
     deliberately left alone.

     These outrank the .v2 h1 / h2 rules above (an attribute selector counts
     as a class, so 0-2-0 beats 0-1-1) wherever a heading carries its own
     inline size; those rules stay as the fallback for any that doesn't. */
  .v2 [style*="font-size: 64px"] { font-size: 34px !important; }
  .v2 [style*="font-size: 48px"] { font-size: 30px !important; }
  .v2 [style*="font-size: 40px"] { font-size: 27px !important; }
  .v2 [style*="font-size: 36px"] { font-size: 26px !important; }
  .v2 [style*="font-size: 32px"] { font-size: 25px !important; }
  .v2 [style*="font-size: 26px"] { font-size: 21px !important; }
  .v2 [style*="font-size: 24px"] { font-size: 20px !important; }
  .v2 [style*="font-size: 22px"] { font-size: 19px !important; }
  .v2 [style*="font-size: 18px"] { font-size: 16px !important; }
  /* 1.08 is a poster leading. It belongs on one or two words across a
     desktop hero, not on a headline wrapping to three lines on a phone. */
  .v2 [style*="line-height: 1.08"] { line-height: 1.16 !important; }
  .v2 [style*="line-height: 1"][style*="font-size: 48px"] { line-height: 1.16 !important; }
}

/* ==========================================================================
   SITE-WIDE CHROME — the v2 shell on every page.

   The header keeps site.js's markup and every link in it (the tools deck is
   the product's navigation and is not something the comp's four-item pill
   could replace). What changes is the dress: the bar becomes the comp's
   floating pill and the nav links become its segmented track.

   The navbar stays STICKY and in flow on ordinary pages, so nothing that
   sits under it moves. Only the hand-built v2 pages, which own their top
   padding, get the fixed overlay the comp draws.
   ========================================================================== */

/* Fixed, and it stays put (Reilly, 2026-09-01): the pill floats above the
   page and never moves, so no hide-on-scroll and no reappearing act. Because
   it is out of flow, the page owes it the room back, which is the body
   padding below, measured by v2.js so the wrapped two-row phone header gets
   the right number instead of a guess. */
.navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: auto;
  background-color: transparent;
  background-image: none;
  border-bottom: 0;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  padding-top: calc(var(--safe-top) + 14px);
  padding-bottom: 14px;
  align-items: flex-start;
}

/* The pill floats, so the strip above it (the status-bar inset plus the 14px
   gap) was transparent and the page scrolled straight through it — on iOS
   Safari you could read the page ABOVE the header, under the clock. A scrim
   behind the bar: solid ink over that strip, faded out by the bar's bottom so
   the pill still reads as floating. z-index -1 keeps it behind the pill; the
   navbar is fixed, so it is the containing block and its own stacking context. */
.navbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    var(--ink) 0,
    var(--ink) calc(var(--safe-top) + 14px),
    transparent 100%
  );
}

body { padding-top: var(--v2-nav-h, 92px); }

/* --- Pages whose hero runs UNDER the header (Reilly 2026-09-02) ----------
   On the home page and Our Results, the dark hero and its halos should
   start at the very top of the viewport with the pill floating over them,
   rather than beginning below a strip of page background. The header is
   already fixed and out of flow — all that stood in the way was the body
   padding that reserves its room, so these pages drop it and the hero
   section pays that room back as its own top padding instead. Everything
   below the hero is untouched.

   !important because the hero's padding is an inline style (the comp was a
   desktop export and writes its own), the same reason the mobile grid
   collapse further down this file needs it. */
body.v2-nav-over { padding-top: 0; }
body.v2-nav-over > main > section:first-of-type {
  padding-top: calc(var(--v2-nav-h, 92px) + var(--hero-gap, 28px)) !important;
}

.navbar__bar {
  max-width: var(--container-xxl);
  margin-inline: auto;
  height: 64px;
  box-sizing: border-box;
  padding: 0 10px 0 12px;
  gap: 20px;
  border-radius: 9999px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  background: rgba(13, 17, 23, 0.66);
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  backdrop-filter: blur(24px) saturate(1.4);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.14), 0 10px 30px rgba(0, 0, 0, 0.35);
}

.navbar .mark { width: 40px; height: 40px; }

/* The link row becomes the comp's segmented track. It shows at EVERY width
   (Reilly, 2026-09-01): on a phone the comp carries the menu buttons in the
   header, so instead of collapsing to the hamburger the track drops to its
   own row inside the pill and scrolls sideways. The hamburger stays for the
   rest of the deck.

   Note the flex item is the <nav>, not the <ul> inside it, so ordering and
   wrapping have to be set on the wrapper. */
.navbar__bar > nav { margin-inline: auto; min-width: 0; }

/* Dead centre on the pill, not "centred in whatever the logo and the buttons
   left over" (Reilly, 2026-09-02). The link track comes out of the flex row
   and is pinned to the bar's midpoint, so it stays centred whatever the
   buttons on the right weigh. Desktop only: on a phone the track has its own
   row and centring it would fight the sideways scroll. */
@media (min-width: 992px) {
  .navbar__bar { position: relative; }
  .navbar__bar > nav {
    position: absolute; left: 50%; transform: translateX(-50%);
    margin-inline: 0;
  }
  .nav-actions { margin-left: auto; }
}
.nav-links {
  display: flex;
  margin: 0;
  gap: 4px;
  padding: 4px;
  border-radius: 9999px;
  background: rgba(0, 0, 0, 0.24);
  border: 1px solid rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.3);
}

/* --- The phone header (Reilly 2026-09-02) --------------------------------
   Two things and nothing else: the mark on the left, the drawer button on
   the right. Everything the bar carries on a desktop — the four tabs, the
   Discord circle, the account circle, Get access — lives in the drawer,
   which is what the drawer is for.

   What this replaces: the bar used to wrap onto two rows on a phone, the
   second being a sideways-scrolling strip of tabs, with two circles and a
   pill competing for the first. It measured 144px tall, hid its own
   overflow behind a scroll almost nobody discovers, and left the page
   guessing at its height. One row, one predictable height, one way in. */
@media (max-width: 991px) {
  .navbar__bar {
    height: 60px;
    padding: 0 8px 0 12px;
    gap: 12px;
    border-radius: 9999px;
    flex-wrap: nowrap;
  }
  .navbar .mark { width: 34px; height: 34px; }
  /* The tab track and the corner controls are the drawer's job down here. */
  .navbar__bar > nav,
  .nav-actions { display: none; }
  .nav-toggle { margin-left: auto; }
}

.nav-links > li > a {
  border-radius: 9999px;
  padding: 8px 16px;
  color: var(--w60);
}
.nav-links > li > a[aria-current="page"] {
  color: var(--bone);
  font-weight: 500;
  background: rgba(255, 255, 255, 0.16);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.2), 0 2px 8px rgba(0, 0, 0, 0.22);
}

/* The soft slide behind the selected tab (Reilly 2026-09-02: "a smooth
   scroll animation on the tab select in the header"). Purely a decoration —
   [aria-current]'s own background above is what actually marks the active
   tab and sits pixel-identical to the pill at rest, so if site.js's
   positioning ever fails or runs late the header still reads correctly,
   just without the slide. site.js positions it directly relative to
   [data-nav-wrap] (a plain left/top/width/height, not transform — four
   tabs, not worth the extra complexity); desktop only, since the mobile
   track scrolls internally and offsetLeft stops meaning anything there. */
[data-nav-wrap] { position: relative; }
.nav-pill {
  position: absolute;
  top: 0; left: 0;
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.16);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.2), 0 2px 8px rgba(0, 0, 0, 0.22);
  opacity: 0;
  pointer-events: none;
  transition: left 320ms cubic-bezier(0.22, 1, 0.36, 1), width 320ms cubic-bezier(0.22, 1, 0.36, 1);
}
@media (max-width: 991px) { .nav-pill { display: none; } }
@media (prefers-reduced-motion: reduce) { .nav-pill { transition: none; } }

.nav-actions .btn {
  border-radius: 9999px;
  min-height: 44px;
  border-color: rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.14);
}
.nav-actions .btn--ghost, .nav-actions .btn--secondary {
  background: rgba(255, 255, 255, 0.06);
  color: var(--bone);
}
.nav-actions .btn--primary { box-shadow: none; }

/* --- The two round corner controls (Reilly, 2026-09-02) ------------------
   Discord and the member's picture are both 40px circles, the same diameter
   as the mark in the opposite corner, so the bar is bookended rather than
   trailing off into a row of pills. */
.nav-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: 9999px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  background: rgba(255, 255, 255, 0.06);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.14);
  text-decoration: none;
  transition: background-color var(--dur-fast) ease, color var(--dur-fast) ease, border-color var(--dur-fast) ease;
}
/* .navbar .nav-icon, not the bare class (Reilly 2026-09-02, "ensure the
   discord colour stays the same... as we move from page to page"): site.css
   carries a generic ".v2 a { color: var(--accent-lt) }" link rule that only
   exists on pages whose <body> wears the "v2" class. Same specificity as a
   bare ".nav-icon { color: ... }" would have had, it was winning there and
   losing everywhere else — the icon rendered var(--bone) (white) on
   .v2-less pages (account, legal, about) and var(--accent-lt) (light blue)
   on every .v2 page, changing colour on every click between the two kinds.
   Scoping to the ancestor it always actually has outranks that rule
   everywhere, so one colour wins on every page. */
.navbar .nav-icon { color: var(--bone); }
.nav-icon:hover {
  /* Discord's own blurple, so the button says which door it is. */
  background: #5865f2;
  border-color: #5865f2;
  color: #ffffff;
}
.nav-icon svg { display: block; }

.nav-avatar {
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; flex: 0 0 auto;
  border-radius: 9999px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.14);
  /* Same fill as .nav-icon's (the Discord button's) resting state — the
     fallback default here matches it too, so even a render before JS sets
     --avatar-tint still comes out looking like the same control. */
  background: var(--avatar-tint, rgba(255, 255, 255, 0.06));
  text-decoration: none;
  transition: border-color var(--dur-fast) ease;
}
.nav-avatar:hover { border-color: rgba(255, 255, 255, 0.32); }
.nav-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The initial in the Discord button's own off-white glyph colour (Reilly
   2026-09-02, second pass) — the two round corner controls are dressed as a
   matched pair now, not each with its own palette. */
.nav-avatar__fallback {
  font-size: 16px; font-weight: 600; line-height: 1;
  /* var(--bone), matching the Discord icon's colour now that .navbar
     .nav-icon (above) makes that colour hold steady on every page — it used
     to render var(--accent-lt) by accident on half the site, and this
     briefly chased that accident before the real fix landed. */
  color: var(--bone);
  user-select: none;
}

/* The tools deck hangs off the pill, so it needs the pill's own inset. */

/* The header sits IN FLOW and never overlays the page (Reilly, 2026-09-01):
   floating it over the hero pushed the headline under the pill on a phone.
   Same pill, same dress, it just takes its own room. */

/* --- Footer --------------------------------------------------------------- */

.footer {
  padding-top: 64px;
  padding-bottom: calc(40px + env(safe-area-inset-bottom, 0px));
  background-color: var(--ground);
  border-top: 1px solid var(--hair);
}
.footer__cols {
  display: grid;
  grid-template-columns: 1.6fr 1fr 1fr 1fr;
  gap: 24px;
  margin-inline: auto;
  max-width: var(--container-xxl);
}
.footer__brand { display: flex; flex-direction: column; align-items: flex-start; gap: 16px; max-width: 300px; }
.footer__blurb { font-size: 14px; line-height: 1.5; color: var(--w40); }
.footer__col { display: flex; flex-direction: column; gap: 12px; }
.footer__head { font-size: 16px; font-weight: 600; color: var(--bone); }
.footer__col a { font-size: 14px; color: var(--w60); }
.footer__col a:hover { color: var(--accent); }
.footer__row {
  margin-top: 48px;
  padding-top: 24px;
  border-top: 1px solid var(--hair);
}
.footer__copy { font-size: 14px; color: var(--w40); }
.footer__links { margin-inline: auto; max-width: var(--container-xxl); margin-top: 12px; }

@media (max-width: 760px) {
  .footer__cols { grid-template-columns: 1fr 1fr; }
  .footer__brand { grid-column: 1 / -1; }
}

/* --- Buttons -------------------------------------------------------------
   The comp's primary is the CTA blue with white text, not the cyan accent.
   Cyan stays the accent for links, chips and data. */
.btn--primary {
  background-color: var(--accent-2);
  border-color: var(--accent-2);
  color: #ffffff;
}
.btn--primary:hover {
  background-color: #2b7dff;
  border-color: #2b7dff;
  color: #ffffff;
  box-shadow: 0 10px 24px rgba(0, 102, 255, 0.35);
}
.btn--secondary {
  background: rgba(31, 35, 40, 0.4);
  border-color: rgba(255, 255, 255, 0.4);
  color: var(--accent-lt);
}
.btn--secondary:hover { border-color: var(--accent-lt); color: var(--bone); }

/* --- Cards and panels ----------------------------------------------------
   The comp's panel is a glass tile: a white wash at 6%, a hairline at 10%,
   and a 16px corner. site.css's flat card reads the same roles, so this is
   the one place the surface treatment is stated. */
.card, .panel {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: var(--radius-card);
}

/* Figures and micro-labels take the mono face, tabular, like the comp. */
.stat__value, .num, [data-numeral], table.data td, .kpi__value {
  font-variant-numeric: tabular-nums;
}
.stat__label, table.data th {
  font-family: var(--mono);
  letter-spacing: 0.02em;
}

/* --- The mark ------------------------------------------------------------
   v2 logo (Reilly, 2026-09-01): the same bars-and-arrow symbol as the pack
   master, redressed for this design — dark glass tile, cyan-to-blue rim,
   the accent ramp on the arrow. bvb-mark-v2-square.svg is the same drawing
   on a squared tile, for app icons and share cards.

   NOTE: this supersedes the glow tile for on-site use. The pack in
   Resources/11-new-resource-pack still holds the old master, and the PNG
   favicons and icon-192/512 are still the old drawing. */
.mark,
[data-theme="cyan"] .mark { background-image: url("/assets/mark/bvb-mark-v2.svg"); }

/* --- Lift cards ----------------------------------------------------------
   Interactive tiles rise off the page on hover (Reilly, 2026-09-01). */
.v2-lift {
  transition: transform 200ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow 200ms ease, border-color 200ms ease;
}
.v2-lift:hover {
  transform: translateY(-5px);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(0, 0, 0, 0.3);
  border-color: rgba(255, 255, 255, 0.22);
}
@media (prefers-reduced-motion: reduce) { .v2-lift { transition: none; } .v2-lift:hover { transform: none; } }

/* --- Footer / RG line in the body face (Reilly, 2026-09-01) --------------
   The wording is locked; only the dress changes. */
.rg-line {
  font-family: var(--body);
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  font-size: 13px;
}

/* --- Checkout + lead dialogs in the v2 dress ----------------------------- */
dialog.checkout, dialog.lead {
  font-family: var(--body);
  background: #0d1117;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 24px;
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.1), 0 30px 80px rgba(0, 0, 0, 0.6);
}
dialog.checkout::backdrop, dialog.lead::backdrop {
  background: rgba(2, 4, 8, 0.7);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}
.checkout__close {
  width: 36px; height: 36px;
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 9999px;
  background: rgba(255, 255, 255, 0.06);
}
.checkout__foot { font-size: 13px; color: var(--w40); }


/* --- The member-app frame (dashboard, tools board) ----------------------- */
/* The comp's member-app frame: fixed-width sidebar, fluid main. The public
   site chrome (navbar pill, footer) stays off this page; the sidebar IS the
   navigation here, exactly as the comp draws it. */
/* No public header above the app, so the boards' viewport maths, which is
   written against --nav-total, has nothing to subtract here. */
.v2-app { display: grid; grid-template-columns: 264px minmax(0, 1fr); min-height: 100vh; background: #0d1117; --nav-total: 0px; }
.v2-app__side {
  background: #080b0d; border-right: 1px solid #21262d;
  padding: 24px 16px; display: flex; flex-direction: column; gap: 32px;
  position: sticky; top: 0; height: 100vh; box-sizing: border-box;
  /* The tool list makes the column taller than the viewport; without this the
     foot (Back to website, Settings, who you are) is unreachable. */
  overflow-y: auto; scrollbar-width: thin;
}
.v2-app__nav { display: flex; flex-direction: column; gap: 4px; }
/* Every row in the sidebar wears the same face; Tools is a button because it
   opens the list instead of going anywhere. */
.v2-app__nav a {
  display: flex; align-items: center; gap: 12px; padding: 11px 12px;
  border-radius: 8px; text-decoration: none; font-size: 15px; color: #a2adb8;
  border: 1px solid transparent;
  width: 100%; box-sizing: border-box;
  background: none; font-family: inherit; text-align: left; cursor: pointer;
}
.v2-app__nav a:hover { background: rgba(255, 255, 255, 0.05); color: #ffffff; }
.v2-app__nav a[aria-current="page"] {
  background: rgba(0, 102, 255, 0.18); border-color: rgba(0, 102, 255, 0.55);
  color: #ffffff; font-weight: 500;
}
.v2-app__nav a svg { flex-shrink: 0; }
.v2-app__nav a .n { flex-grow: 1; }
.v2-app__main { padding: 32px 32px 48px; display: flex; flex-direction: column; gap: 24px; min-width: 0; }
/* The burger only exists on a phone; on desktop the sidebar is the sidebar. */
.v2-app__burger { display: none; }

/* Phones (Reilly, 2026-09-02): the sidebar used to unfold as a wrapped row of
   every item, brand, tool list and member card, which put ~700px of navigation
   above the content of EVERY app page. It is a sticky top bar now: brand plus
   a burger, and the whole nav drops from it. */
@media (max-width: 900px) {
  /* The phone bar (Reilly, 2026-09-14). It scrolls away with the page in the
     browser and stays pinned in the installed app, and the difference is not
     a style choice — it is which of the two contexts reports a real
     safe-area inset.

     Five attempts went at this from the wrong end (sticky, then fixed, then
     paint above the bar, then a 120px overscan) before the phone was asked
     what it actually computed. It answered env(safe-area-inset-top): 0px
     while page content was visibly under the clock. That is documented
     WebKit behaviour, not a bug: Safari reports the part of the safe area
     its OWN chrome does not cover, and in portrait it counts the status-bar
     strip as its chrome in every toolbar state. So in the browser that inset
     is always 0, and every version that sized the bar by it was sizing by
     nothing.

     What actually paints the page under the clock is viewport-fit=cover
     (WebKit: avoidsUnsafeArea = viewportFit != Cover). With it off, WebKit
     insets the content below the status bar and paints that strip itself
     from theme-color. So each page ships cover for the installed app, where
     the inset IS real, and an inline script beside the viewport meta strips
     it everywhere else. The bar therefore needs no inset of its own in the
     browser and gets one in the app, from the same declaration. */
  .v2-app { grid-template-columns: 1fr; }
  .v2-app__side {
    /* In flow, so it travels with the page: nothing is pinned, so there is
       no strip for anything to be uncovered in. relative, not static, only
       because the drawer measures against this box.

       The padding is 0 in the browser (cover is stripped there, and Safari's
       inset is 0 regardless) and the notch height in the installed app,
       where it is what keeps the burger clear of the clock. */
    position: relative; z-index: 30;
    height: auto; max-height: 100dvh;
    flex-direction: row; align-items: center; flex-wrap: wrap; gap: 0;
    padding: calc(10px + env(safe-area-inset-top, 0px)) 14px 10px;
    border-right: 0; border-bottom: 1px solid #21262d;
    /* The base rule makes this a scroll container so the OPEN drawer can
       scroll. Closed there is nothing to scroll, and a full-width scroll
       container across the top of the page is something iOS can hand a
       touch to and then swallow, which is one way a page stops scrolling.
       It is a scroll container only while it is open. */
    overflow: visible;
  }
  .v2-app__side.is-open { overflow-y: auto; overscroll-behavior: contain; }

  /* The installed app keeps the bar pinned. The complaint there was the
     burger being unreachable under the status bar, never a gap, and this is
     the one context where env() returns the notch height to pad by.
     data-standalone is stamped by v2.js from navigator.standalone. */
  html[data-standalone] .v2-app {
    /* burger 40 + padding 10 twice + the 1px rule under it. */
    padding-top: calc(61px + env(safe-area-inset-top, 0px));
  }
  html[data-standalone] .v2-app__side {
    position: fixed; top: 0; left: 0; right: 0;
    max-height: 100dvh;
  }
  .v2-app__brandrow { flex: 1 1 auto; min-width: 0; }
  .v2-app__brand { padding: 0; min-width: 0; }
  .v2-app__burger {
    display: flex; align-items: center; justify-content: center;
    width: 40px; height: 40px; flex-shrink: 0;
    background: none; border: 1px solid #21262d; border-radius: 8px;
    color: #a2adb8; cursor: pointer;
  }
  .v2-app__burger[aria-expanded="true"] { color: #06e5ff; border-color: rgba(6, 229, 255, 0.45); }

  /* Closed, the bar is brand + burger and nothing else. */
  .v2-app__side > .v2-app__nav,
  .v2-app__side > .v2-app__foot { display: none; flex: 1 0 100%; }
  .v2-app__side.is-open > .v2-app__nav { display: flex; margin: 10px 0 0; }
  .v2-app__side.is-open > .v2-app__foot { display: flex; margin-top: 4px; }
  /* One item per line in the drawer: a wrapped row of six reads as rubble. */
  .v2-app__nav { flex-direction: column; }
  .v2-app__eyebrow { display: none; }
  .v2-app__who { margin-top: 4px; }

  .v2-app__main { padding: 20px 16px 40px; }
  /* The board's viewport-height lock belongs under a sidebar, not under a
     sticky bar: it would push the page a full screen past the fold. */
  .v2-app__main.oc-screen { height: auto; padding: 16px 14px 14px; }
}
/* The live boards keep their own dash styling below the dashboard. */
.v2-app__main .dash { margin-top: 8px; }

/* ==========================================================================
   BOARD SKIN (Reilly, 2026-09-02: "the tools all need an overhaul in the
   format of the new website") — one dress for the fourteen oc-screen boards.

   The per-board rainbow tones are retired: every board wears the brand
   accent, like every other v2 surface. Result colours (--field-up/down)
   were never the tone and are untouched, so arbs still read green where
   green means money.
   ========================================================================== */

main.oc-screen {
  --tone: #06e5ff !important;
  --accent: #06e5ff !important;
  --accent-lt: #8dd6ff !important;
  --accent-dp: #00b4ff !important;
}
/* One flat ground under every app surface (Reilly, 2026-09-02: "for all tools
   just have the flat gray background like in odds comparison"). The boards had
   it; the calculators, the category pages and the tracker were still sitting on
   the public canvas's glow. */
.v2-app__main { background: var(--ground); }

/* Board title in the comp's voice: sentence case, Mona Sans, headline-sized. */
.oc-screen .oc-title {
  font-family: var(--body);
  font-size: 22px;
  font-weight: 460;
  letter-spacing: -0.01em;
  text-transform: none;
  color: #ffffff;
}

/* Filter furniture as comp fields. */
.oc-screen .th-filter, .oc-screen select.th-filter, .oc-screen input.th-filter {
  background: #0d1117;
  border: 1px solid #21262d;
  border-radius: 8px;
  color: #ffffff;
}
.oc-screen .th-filter.is-on {
  background: rgba(6, 229, 255, 0.12);
  border-color: rgba(6, 229, 255, 0.45);
  color: #06e5ff;
  font-weight: 500;
}

/* Tables in the comp frame. */
.oc-screen .table-wrap--glass, .oc-screen .table-wrap {
  border: 1px solid #21262d;
  border-radius: 16px;
  background: rgba(255, 255, 255, 0.04);
}
.oc-screen table.data th {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: 0.02em;
  background: #151a22;
  color: #818b98;
}

/* Flat, uncoloured boards (Reilly, 2026-09-03: "remove all background
   gradients on all tools, just have the gray backdrop"). Two things painted
   colour on a tool surface: the pane's specular sweep, and the per-row --heat
   wash in indigo. Both gone; --ground is the only backdrop and a row's own
   figures are the only colour left in the table. */
.v2-app__main .table-wrap--glass,
.v2-app__main table.data--feed tbody tr { background-image: none; }

/* Panels and cards on boards pick up the glass tile. */
.oc-screen .card {
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 16px;
}

/* iOS focus zoom: Safari zooms the whole page when a focused input's font
   is under 16px, and stays zoomed after blur. Confirmed on the tool panes'
   14px inline inputs (2026-09-02 audit). Phone-width inputs go to 16px —
   inline styles need the !important. */
@media (max-width: 900px) {
  .v2-app input, .v2-app select, .v2-app textarea,
  body.v2 input, body.v2 select, body.v2 textarea {
    font-size: 16px !important;
  }
}

/* The record page's league filter: six-plus segments wrap on a phone
   instead of running off the right edge (2026-09-02 audit). */
@media (max-width: 900px) {
  [data-rec-filters] { flex-wrap: wrap; border-radius: 20px; justify-content: flex-start; }
  [data-range] { flex-wrap: wrap; border-radius: 20px; }
}

/* Mobile drawer under the fixed pill: site.css sizes it against --nav-total,
   which the fixed v2 header no longer reflects. Use the measured height
   (2026-09-02 audit). */
/* The drawer, in the v2 material (Reilly 2026-09-02). It kept site.css's
   older dress — flat ink panel, square edges, condensed uppercase links —
   which read as a different site to the pill hanging above it. Now it is
   the pill's own glass, inset to the same gutter, and the links are the
   sentence-case face the rest of the site uses. It is also the ONLY way
   into the nav on a phone now, so every destination and every action lives
   in it and it is sized to scroll rather than to fit. */
.nav-mobile {
  max-height: calc(100dvh - var(--v2-nav-h, 92px));
  margin: 8px 14px 0;
  padding: 12px 14px 16px;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 22px;
  background: rgba(13, 17, 23, 0.94);
  background-image: none;
  -webkit-backdrop-filter: blur(24px) saturate(1.4);
  backdrop-filter: blur(24px) saturate(1.4);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.14), 0 18px 40px rgba(0, 0, 0, 0.5);
}
.nav-mobile ul { margin: 0 0 12px; }
.nav-mobile li + li { border-top-color: rgba(255, 255, 255, 0.07); }
.nav-mobile a {
  font-family: 'Mona Sans', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
  text-transform: none;
  letter-spacing: -0.01em;
  font-size: 16px;
  font-weight: 500;
  color: #ffffff;
  padding: 13px 4px;
}
.nav-mobile a[aria-current="page"] { color: #06e5ff; }
.nav-mobile .btn-row .btn { min-height: 44px; border-radius: 9999px; }

/* --- Sidebar tools list (Reilly, 2026-09-02: "a drop down menu on the
   side... that lists them cleanly", replacing the chip bubbles). Rendered
   by v2.js from the same NAV the header uses, under the Tools item in the
   member-app sidebar. --- */
/* Everyday / Softwares / Resources: the three folders the sidebar is grouped
   into (Reilly 2026-09-08). A plain label, not a row you can click and not a
   disclosure — every tool is one click from anywhere in the member app. */
.v2-app__toolsgroup {
  display: block; padding: 8px 12px 6px 12px; margin-top: 14px;
  border-top: 1px solid rgba(6, 229, 255, 0.18);
  font-size: 11px; font-weight: 700;
  letter-spacing: 0.1em; text-transform: uppercase; color: #06e5ff;
}
/* Three folders in the nav itself now, not sections of a disclosure. The
   first one opens the list, so it wants no rule above it and no gap. */
.v2-app__toolsgroup:first-child,
.v2-app__nav > .v2-app__toolsgroup:first-of-type {
  padding-top: 0; margin-top: 0; border-top: 0;
}

/* A folder row (Tips > Model bets): the row is the link, the caret at its
   end opens the sport pages beneath it, indented under the row's glyph. */
.v2-app__tree > summary { list-style: none; display: flex; align-items: center; cursor: pointer; }
.v2-app__tree > summary::-webkit-details-marker { display: none; }
.v2-app__tree > summary > a { flex: 1 1 auto; }
.v2-app__caret {
  flex-shrink: 0; width: 28px; height: 28px; margin-left: -34px; position: relative; z-index: 1;
  display: flex; align-items: center; justify-content: center; border-radius: 6px; color: #78848f;
}
.v2-app__caret::before {
  content: ""; width: 7px; height: 7px; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--dur-fast, 120ms) ease;
}
.v2-app__tree[open] > summary .v2-app__caret::before { transform: rotate(45deg); }
.v2-app__caret:hover { background: rgba(255, 255, 255, 0.08); color: #ffffff; }
.v2-app__nav a.v2-app__lead { color: #ffffff; font-weight: 500; }
.v2-app__kids { display: flex; flex-direction: column; gap: 2px; margin-top: 2px; }
.v2-app__kids a { padding: 8px 12px 8px 42px; font-size: 14px; }

/* The invite sits beside the name at the top of the sidebar (Reilly
   2026-09-08), the mark and nothing else: a label next to a logo everyone
   already knows is a word doing no work. */
.v2-app__brandrow { display: flex; align-items: center; gap: 4px; }
.v2-app__brandrow .v2-app__brand { flex: 1 1 auto; min-width: 0; padding-right: 0; }
.v2-app__brandrow .v2-app__brand span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-app__discord {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  width: 28px; height: 28px; border-radius: 8px;
  color: #5865f2; text-decoration: none;
  transition: background-color 140ms ease-in-out, color 140ms ease-in-out;
}
.v2-app__discord:hover { background: rgba(88, 101, 242, 0.16); color: #7c86ff; }

/* Sidebar furniture, generated by v2.js. Was inline on three pages. */
.v2-app__brand { display: flex; align-items: center; gap: 10px; padding: 0 8px; text-decoration: none; }
.v2-app__brand span { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; color: #ffffff; }
.v2-app__eyebrow {
  font-size: 12px; font-weight: 500; text-transform: uppercase;
  letter-spacing: 0.015em; color: #78848f; padding: 0 8px 8px;
}
.v2-app__foot { display: flex; flex-direction: column; gap: 4px; margin-top: auto; }
.v2-app__who {
  display: flex; align-items: center; gap: 12px; padding: 12px;
  margin-top: 12px; border-top: 1px solid #21262d;
  /* Your name opens your account settings (Reilly 2026-09-03). Square top so
     the hover fill sits flush under the divider, rounded foot like every
     other sidebar row. The "Account settings" row above stays the one marked
     current — two highlighted rows for one destination is worse than none. */
  text-decoration: none; border-radius: 0 0 8px 8px;
  transition: background-color var(--dur-fast, 140ms) ease-in-out;
}
.v2-app__who:hover { background: rgba(255, 255, 255, 0.05); }
.v2-app__who:hover .v2-app__whoName { color: #06e5ff; }
/* Sizing only — .nav-avatar (site.css/v2.css, near the header corner rules)
   carries the shape, the charcoal fill and the photo/initial layout; this
   just fits it to the sidebar's smaller footer row. */
.v2-app__avatar { width: 36px; height: 36px; }
.v2-app__whoText { display: flex; flex-direction: column; flex-grow: 1; min-width: 0; }
.v2-app__whoName {
  font-size: 14px; font-weight: 500; color: #ffffff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.v2-app__whoTier { font-size: 12px; color: #8dd6ff; text-transform: uppercase; }

/* --- Tracker density (Reilly, 2026-09-02: "clear all empty space gaps").
   The entry wall becomes two balanced columns with no grid holes, and the
   stat tiles take one row. --- */
/* One gutter down the whole tracker, and it is the stat cards' own 12px
   (Reilly, 2026-09-02: "every block the same amount of space as what's
   between Profit / loss and ROI"). The tab pane is the real stack now —
   the app column holds the three tabs, the pane holds the page. */
.trk-main [data-tracker-app]:not([hidden]) { gap: 12px; }
.trk-main [data-ledger-pane]:not([hidden]) { display: flex; flex-direction: column; gap: 12px; }
/* stat tiles are sized by the page: six across, two on phones */
/* Two columns, zero holes: the first pane (the big form) spans both rows on
   the left, the other two stack on the right and the heights meet. Placement
   is by position, not by name, so customise-mode swaps still move panes. */
/* The tracker home page sits on the SIX-COLUMN rhythm of the stat cards
   above it, same track widths and the same 12px gutter, so every edge below
   lands on an edge above (Reilly, 2026-09-02: "every block the same amount
   of space as what's between Profit / loss and ROI").

   Row one: the log spans four columns, so its right edge is Turnover's, and
   the bankroll takes the last two, so its left edge is Average odds'.
   Row two: half each, three columns apiece.

   Deliberately STRETCHED, not start-aligned: the log card's own content sets
   the row height, so it still ends right below "Log the bet", and the
   bankroll grows to match it rather than sitting as a short strip. Auto
   scale used to be in this row and is gone. */
.trk-entry, .trk-money {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
  align-items: stretch;
}
.trk-entry > [data-pane="add-bet"] { grid-column: span 4; }
.trk-entry > [data-pane="chart"]   { grid-column: span 2; }
.trk-money > [data-pane]           { grid-column: span 3; }

/* The stat cards drop to two across on a phone; the boxes go to one. */
@media (max-width: 900px) {
  .trk-entry, .trk-money { grid-template-columns: 1fr; }
  .trk-entry > [data-pane], .trk-money > [data-pane] { grid-column: auto; }
}

/* The ledger tab pill, centred in the top bar's empty middle. */
.trk-ledger-tabs { margin-inline: auto; }
@media (max-width: 900px) { .trk-ledger-tabs { margin-inline: 0; } }

/* Bookmaker balances as tiles: a logo each, the name on hover, the figure
   under it in small type. One dress, drawn by BVBFeedUI.bookTile, used by the
   tracker's balances board and the dashboard's balances panel — the two used
   to draw a book two different ways on two screens.

   The grid auto-fits, so the same tiles run six across the tracker's
   half-width card and three across the dashboard's narrow column without a
   second rule. */
.book-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 8px; }
.book-tile {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 6px; min-width: 0;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 12px;
}
.book-tile__mark {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 26px;
}
.book-tile__mark img { max-height: 26px; max-width: 100%; width: auto; object-fit: contain; border-radius: 4px; }
/* No logo on file: the book's name in its own colour, same footprint. */
.book-tile__mark span {
  font-size: 12px; font-weight: 600; letter-spacing: 0.02em;
  color: color-mix(in srgb, var(--bc, #a2adb8), white 40%);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
.book-tile__val { font-size: 12px; font-weight: 500; line-height: 1; }
.book-tile--total { background: rgba(0, 180, 255, 0.10); border-color: rgba(0, 180, 255, 0.35); }
.book-tile--total .book-tile__mark span { color: #a2adb8; }
.grid-perf { gap: 16px; }


/* --- Boards inside the member-app frame (Reilly, 2026-09-02: "the live
   boards need an entire overhaul in the fashion of the new dashboard, in
   fact all the tools need that") ------------------------------------------
   The fourteen oc-screen boards, the calculators and the two category pages
   now render as app surfaces: the sidebar is the navigation, the public pill
   header and big footer are gone. The board keeps its own viewport-fit
   geometry; only the padding and the flow gap are the app's. */
.v2-app__main.oc-screen { padding: 24px 24px 16px; gap: 0; }
.v2-app__main.oc-screen .oc-toolbar { padding-bottom: 14px; }
/* .v2-app__main is a flex column with a 24px gap; the content pages inside it
   bring their own section spacing, so the gap would double it. */
.v2-app__main[data-fit]:not(.oc-screen) { gap: 0; padding: 0 0 32px; }
.v2-app__main[data-fit]:not(.oc-screen) > .section:first-child { padding-top: 24px; }

/* …but not on a phone. The rule above drops the frame's side padding because
   the sections inside a [data-fit] page bring their own gutter — and then the
   blanket `.v2 section { padding-inline: 0 !important }` up at the mobile
   breakpoint takes that gutter away. Between them every tool and calculator
   page rendered edge to edge on a phone, text hard against both bezels
   (Reilly, 2026-09-02: "not displaying correctly on mobile"). The frame owes
   the gutter back at exactly the width where the sections stop paying it.
   Same selector as above so it wins on order rather than on !important. */
@media (max-width: 900px) {
  .v2-app__main[data-fit]:not(.oc-screen) { padding-left: 16px; padding-right: 16px; }
}

/* The board's explanatory note sits BELOW the fold (Reilly, 2026-09-03: "the
   writing at the bottom should not be visible unless the user scrolls down,
   the table should take up the whole screen"). A zero-height wrapper
   contributes nothing to the height-locked flex column, so the table takes
   the viewport and the note renders past its bottom edge, reached by
   scrolling on. The RG line is NOT in the wrapper: it is carried once per
   surface, prominently and permanently, and stays in view. Only where the
   lock applies — on a phone main is height auto and the note stays in flow. */
@media (min-width: 901px) {
  .v2-app__main.oc-screen > .oc-below { height: 0; flex: 0 0 auto; overflow: visible; }
  .v2-app__main.oc-screen > .oc-below > .oc-note { margin-top: 0.9rem; }
}

/* The RG line, once per app surface, at the foot of the board it belongs to. */
.v2-app__rg { padding-top: 14px; margin-top: auto; }
.v2-app__rg .rg-line { margin: 0; font-size: 12px; color: #78848f; }

/* The app top bar (Reilly 2026-09-02): the same voice the dashboard, the
   tools board and the course wear, so a category page no longer opens with a
   public marketing hero inside the member frame. */
.v2-app__top { display: flex; flex-direction: column; gap: 6px; padding: 24px 24px 0; }
.v2-app__top h1 {
  margin: 0; font-size: 32px; font-weight: 460; line-height: 1.2;
  letter-spacing: -0.02em; color: #ffffff; text-transform: none;
}
.v2-app__lede { margin: 0; font-size: 15px; color: #a2adb8; }
.v2-app__blurb { margin: 0; max-width: 46rem; font-size: 14px; color: #78848f; }
.v2-app__main[data-fit]:not(.oc-screen) > .v2-app__top + .section { padding-top: 20px; }
/* The category pages' own containers already centre and cap their width; the
   frame only owes them its gutter. */
.v2-app__main .container--xl { max-width: none; }

/* The lesson player brings its own two-column workspace, so the app frame
   gives it the room rather than a second set of gutters. */
.v2-app__main--flush { padding: 0; gap: 0; }

/* ==========================================================================
   THE BLURRED GATE (Reilly, 2026-09-02)

   Education and Dashboard sit in the bar for signed-out visitors too, so both
   pages have to answer a visitor who has no account. They answer with the
   real page behind frosted glass and the offer on top — the shape of the
   product is the pitch.

   site.js (gateBlur) moves the page's own children into .bvb-gated and drops
   .bvb-gate in beside them. The blur MUST NOT be applied to the host: a CSS
   filter blurs descendants, and the card is one.
   ========================================================================== */

.bvb-gatehost { position: relative; }

.bvb-gated {
  filter: blur(7px) saturate(0.85);
  opacity: 0.5;
  pointer-events: none;
  user-select: none;
  /* The blur samples from outside the element's box, so without this the
     frosted edge bleeds over the sidebar hairline. */
  overflow: hidden;
}

.bvb-gate {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  /* Far enough down that the card sits in the reading position rather than
     dead-centre of a very tall page, and sticky so it stays put if the
     blurred page behind it is long enough to scroll. */
  padding: clamp(48px, 12vh, 140px) 20px 20px;
  pointer-events: none;
}

.bvb-gate__card {
  position: sticky;
  top: clamp(48px, 12vh, 140px);
  pointer-events: auto;
  width: 100%;
  max-width: 460px;
  box-sizing: border-box;
  padding: 32px;
  text-align: center;
  background: rgba(13, 17, 23, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 24px;
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.1), 0 30px 80px rgba(0, 0, 0, 0.6);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}

.bvb-gate__title {
  margin: 0 0 10px;
  font-size: 24px;
  font-weight: 500;
  line-height: 1.25;
  letter-spacing: -0.02em;
  color: #ffffff;
  text-transform: none;
}

.bvb-gate__body {
  margin: 0 0 22px;
  font-size: 15px;
  line-height: 1.55;
  color: #a2adb8;
  text-wrap: pretty;
}

.bvb-gate__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
.bvb-gate__actions .btn { min-height: 44px; }

@media (max-width: 600px) {
  /* Footer links are 21px tall on a phone; give the thumb 44px. */
  .footer__col a, .footer__links a { padding: 10px 0; display: inline-block; }
  .tab-bar .badge { padding: 10px 14px; }
  .bvb-gate { padding-top: 40px; }
  .bvb-gate__card { padding: 24px 20px; }
  .bvb-gate__title { font-size: 21px; }
  .bvb-gate__actions .btn { flex: 1 1 100%; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
  .bvb-gated { filter: none; opacity: 0.28; }
}

/* ==========================================================================
   ACCOUNT — the profile photo (Reilly, 2026-09-02)
   The same circle the header corner wears, at a size you can actually judge
   a crop by, with its controls beside it.
   ========================================================================== */

.acct-id { display: flex; flex-direction: column; gap: 28px; }

/* The leads card's header: title on the left, Export CSV on the right, and
   the two stack rather than collide once the card gets narrow. */
.acct-leads__head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 16px; flex-wrap: wrap;
}
.acct-leads__head h3 { margin: 0; }
.acct-leads__head p { margin: 4px 0 0; }
/* The table can be long; cap it and scroll rather than pushing the footer
   thousands of rows down the page. */
.acct-leads__head + div .table-wrap { max-height: 60vh; overflow-y: auto; }

.acct-photo { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }

.acct-photo__ring {
  display: inline-flex; align-items: center; justify-content: center;
  width: 88px; height: 88px; flex: 0 0 auto;
  border-radius: 9999px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.18);
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.14);
  background: var(--avatar-tint, rgba(255, 255, 255, 0.06));
}
.acct-photo__ring img { width: 100%; height: 100%; object-fit: cover; display: block; }
.acct-photo__initial { font-size: 34px; font-weight: 600; line-height: 1; color: var(--bone); }

.acct-photo__do { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.acct-photo__do .btn-row { margin: 0; align-items: center; }
.acct-photo__do p { margin: 0; }

@media (max-width: 520px) {
  .acct-photo { gap: 16px; }
  .acct-photo__ring { width: 68px; height: 68px; }
  .acct-photo__initial { font-size: 26px; }
}

/* The name and email inline edit forms (Reilly 2026-09-02) — dropped into
   the identity lines in a card of their own so the open form reads as a
   deliberate step, not just two more lines in the list. */
.acct-inline {
  display: flex; flex-direction: column; gap: 1rem;
  max-width: 22rem; margin: 0.75rem 0;
  padding: 1rem; border-radius: var(--radius-card);
  background: var(--raised); border: 1px solid var(--hair);
}

/* --- Sortable board headers (Reilly, 2026-09-08: "make all sections in the
   pending and settled section of the bet tracker sortable by col"). The
   header IS the button, so it inherits the th's type and alignment and the
   only thing that changes on hover is the colour. --- */
.th-sort {
  appearance: none; background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; letter-spacing: inherit; text-transform: inherit;
  cursor: pointer; display: inline-flex; align-items: center; gap: 4px;
}
.th-sort:hover { color: #06e5ff; }
th[aria-sort] .th-sort { color: #06e5ff; }
.th-sort__c { font-size: 10px; line-height: 1; }
th.num .th-sort { justify-content: flex-end; }

/* --- Folding folders (Reilly, 2026-09-08: "if you click it it should fold
   up"). The header is a button now, so it needs the button paint stripped
   back to the label it was, and the rows it folds need `hidden` to beat the
   nav's own display:flex. --- */
button.v2-app__toolsgroup {
  width: 100%; appearance: none; background: none; cursor: pointer;
  border-left: 0; border-right: 0; border-bottom: 0;
  text-align: left; font-family: inherit;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
}
button.v2-app__toolsgroup:hover { color: #ffffff; }
.v2-app__foldcaret {
  width: 0; height: 0; flex: none;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor;
  transition: transform 120ms ease;
}
[aria-expanded="false"] > .v2-app__foldcaret { transform: rotate(-90deg); }
.v2-app__nav [hidden] { display: none !important; }

/* ---- Tips: the open-bet wall (Reilly, 2026-09-08) ------------------------
   One panel per open bet, four across. The Discord post without the emoji:
   league and time, the selection with its market and book, then the three
   numbers that decide it, each under its own label. Deliberately monochrome —
   no per-league colour, no tinted pills; only the price and a signed EV carry
   any tone at all. */
/* The board screen is a viewport-height flex column, which stretched a wall
   of two cards down the whole page. Block, so a card is only as tall as it
   needs to be, and the wall scrolls under a fixed RG line. */
/* The promo maximiser's answer is the same shape, a stack of cards rather
   than a table, so it takes the same treatment. */
.oc-screen > #tp-out, .oc-screen > #pm-out { display: block; overflow-y: auto; scrollbar-width: thin; }
/* Four across, about four down (Reilly, 2026-09-10). The six-across pass cut
   detail off the card to make the height; this keeps every field the big card
   carried -- posted price, fair, minimum, every book's price, the note -- and
   buys the room back from the type scale instead. */
.tp-grid {
  display: grid; gap: 10px; align-content: start;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
}
@media (min-width: 1700px) { .tp-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
@media (max-width: 640px) { .tp-grid { grid-template-columns: 1fr; } }

/* Board, rail and results so far (Reilly, 2026-10-08). On a desktop the
   open tips and the winners rail share the top two thirds and each scrolls in
   its own box; the results section takes the bottom third. Under 1100px it is
   one scrolling column and the rail is gone, as it is in the dashboard slot. */
.oc-toolbar .tp-search { width: 170px; }
.tp-main { display: flex; flex-direction: column; min-width: 0; }
.tp-main > p.muted { margin: auto; padding: 2rem 0; text-align: center; }
@media (min-width: 1100px) {
  #tp-out.tp-has-side, #tp-out.tp-has-perf { display: grid; gap: 12px; overflow: hidden;
    grid-template-columns: minmax(0, 1fr) 250px; grid-template-rows: minmax(0, 1fr); }
  #tp-out.tp-has-perf { grid-template-rows: minmax(0, 2fr) minmax(0, 1fr); }
  #tp-out:not(.tp-has-side) > .tp-main { grid-column: 1 / -1; }
  #tp-out > .tp-main { overflow-y: auto; scrollbar-width: thin; min-height: 0; }
}
@media (max-width: 1099px) { .tp-side { display: none; } .tp-perf { margin-top: 16px; } }

/* Glass, the .table-wrap--glass recipe at panel scale. */
.tp-side, .tp-perf__sum, .tp-tile {
  border: 1px solid rgba(255, 255, 255, 0.09); border-radius: 12px;
  background-color: color-mix(in srgb, var(--raised) 46%, transparent);
  background-image: linear-gradient(115deg, rgba(255, 255, 255, 0.07), rgba(255, 255, 255, 0.015) 30%, transparent 50%);
  backdrop-filter: blur(14px) saturate(140%); -webkit-backdrop-filter: blur(14px) saturate(140%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
}
.tp-side { display: flex; flex-direction: column; min-height: 0; padding: 12px 12px 10px; }
.tp-side__h { margin: 0; font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: #8b96a1; }
.tp-side ul { list-style: none; flex: 1 1 auto; min-height: 0; margin: 8px -4px 0 0; padding: 0 4px 0 0; overflow-y: auto; scrollbar-width: thin; overscroll-behavior: contain; }
.tp-side li { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 1px 8px; align-items: baseline;
  padding: 7px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.06); }
.tp-side li:last-child { border-bottom: 0; }
.tp-side__l { font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; color: #6b757f; }
.tp-side__t { font-size: 12px; line-height: 1.3; color: #d6dde4; }
.tp-side__m { grid-column: 1; font-size: 10.5px; color: #7d8792; }
.tp-side__p { font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; }
.tp-side__o { font-size: 11px; color: #8b96a1; text-align: right; font-variant-numeric: tabular-nums; }
.tp-side__sum { margin: 8px 0 0; font-size: 11px; color: #6b757f; line-height: 1.4; font-variant-numeric: tabular-nums; }

/* Results so far: the summary and chart on the left, the Discord-style tiles
   on the right, scrolling sideways when there are more than fit. */
.tp-perf { grid-column: 1 / -1; min-height: 0; display: grid; gap: 12px;
  grid-template-columns: minmax(280px, 0.9fr) minmax(0, 2fr); }
.tp-perf__sum { display: flex; flex-direction: column; gap: 6px; min-height: 0; padding: 12px 14px; }
.tp-perf__head { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.tp-perf__big { font-size: 22px; font-weight: 650; font-variant-numeric: tabular-nums; }
.tp-perf__stats { display: flex; flex-wrap: wrap; gap: 2px 14px; font-size: 12px; color: #8b96a1; font-variant-numeric: tabular-nums; }
.tp-perf__stats b { color: #e6edf3; font-weight: 600; }
.tp-perf__sum .tp-side__sum { margin: 0; }
.tp-chart { flex: 1 1 auto; width: 100%; min-height: 50px; display: block; color: var(--field-up); }
.tp-chart.neg { color: var(--field-down); }
.tp-chart__line { fill: none; stroke: currentColor; stroke-width: 1.75; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.tp-chart__fill { fill: currentColor; opacity: 0.08; }
.tp-chart__zero { stroke: rgba(255, 255, 255, 0.18); stroke-dasharray: 3 3; vector-effect: non-scaling-stroke; }
.tp-perf__tiles { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(150px, 1fr); gap: 10px;
  overflow-x: auto; scrollbar-width: thin; min-height: 0; align-items: stretch; }
.tp-tile { display: flex; flex-direction: column; gap: 3px; padding: 10px 12px; min-width: 0; border-top: 2px solid var(--field-up); }
.tp-tile--down { border-top-color: var(--field-down); }
.tp-tile__h { font-size: 11px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: #8b96a1; }
.tp-tile__h em { font-style: normal; color: var(--accent); margin-left: 4px; }
.tp-tile__pl { font-size: 18px; font-weight: 650; font-variant-numeric: tabular-nums; color: var(--field-up); }
.tp-tile--down .tp-tile__pl { color: var(--field-down); }
.tp-tile__roi { font-size: 12px; font-weight: 600; color: #e6edf3; font-variant-numeric: tabular-nums; }
.tp-tile__k { font-size: 11.5px; color: #8b96a1; font-variant-numeric: tabular-nums; }
@media (max-width: 1099px) { .tp-perf { grid-template-columns: 1fr; } .tp-chart { height: 90px; flex: none; } }

/* The card keeps the big card's shape, at about two thirds the type scale,
   with the EV pulled out of the number row into the top-right corner where
   it is the biggest and the only green thing on it (Reilly, 2026-09-10). */
.tp-card {
  position: relative; overflow: hidden; isolation: isolate; cursor: pointer;
  display: flex; flex-direction: column; box-sizing: border-box;
  padding: 12px 14px 10px; border-radius: 14px;
  background: linear-gradient(160deg, #10161d 0%, #0c1116 55%, #0a0e13 100%);
  border: 1px solid #242b33;
  box-shadow: 0 1px 0 rgba(255,255,255,0.04) inset, 0 10px 26px rgba(0,0,0,0.35);
  transition: border-color 220ms ease, box-shadow 220ms ease, transform 220ms ease;
}
.tp-card::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(120% 80% at 0% 0%, rgba(6,229,255,0.10), transparent 55%);
  opacity: 0; transition: opacity 260ms ease;
}
.tp-card::after {
  content: ""; position: absolute; top: -40%; bottom: -40%; left: -70%; width: 55%;
  pointer-events: none;
  background: linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.05) 35%, rgba(255,255,255,0.16) 50%, rgba(255,255,255,0.05) 65%, transparent 100%);
  transform: translateX(0) skewX(-14deg);
  opacity: 0;
}
.tp-card:hover {
  border-color: rgba(6,229,255,0.45); transform: translateY(-2px);
  box-shadow: 0 1px 0 rgba(255,255,255,0.07) inset, 0 0 0 1px rgba(6,229,255,0.12), 0 14px 36px rgba(0,0,0,0.5), 0 0 32px rgba(6,229,255,0.10);
}
.tp-card:hover::before { opacity: 1; }
.tp-card:hover::after { animation: tp-glisten 900ms cubic-bezier(0.22, 1, 0.36, 1) forwards; }
@keyframes tp-glisten {
  0%   { transform: translateX(0) skewX(-14deg); opacity: 0; }
  15%  { opacity: 1; }
  100% { transform: translateX(340%) skewX(-14deg); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .tp-card, .tp-card::before { transition: none; }
  .tp-card:hover { transform: none; }
  .tp-card:hover::after { animation: none; }
}
.tp-card__top { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.tp-card__league { font-size: 11px; font-weight: 600; letter-spacing: 0.13em; text-transform: uppercase; color: #8b96a1; }
.tp-card__when { display: block; margin-top: 2px; font-size: 10.5px; color: #6b757f; font-variant-numeric: tabular-nums; white-space: nowrap; }
.tp-card__ev { text-align: right; line-height: 1; }
.tp-card__ev b { display: block; font-size: 23px; font-weight: 600; letter-spacing: -0.02em; font-variant-numeric: tabular-nums;
  color: var(--field-up); text-shadow: 0 0 22px rgba(31,221,131,0.28); }
.tp-card__ev .tp-f__k { display: block; margin-top: 2px; }
.tp-card__ev--neg b { color: var(--field-down); text-shadow: none; }
.tp-card__ev--none b { color: #6b757f; text-shadow: none; }
.tp-card__sel { margin-top: 6px; font-size: 16px; font-weight: 550; line-height: 1.22; color: #ffffff; letter-spacing: -0.015em; text-wrap: balance; }
.tp-card__mkt { margin-top: 4px; font-size: 11.5px; color: #8b96a1; line-height: 1.3;
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tp-card__event { margin-top: 3px; font-size: 11px; color: #a2adb8; line-height: 1.3; }
.tp-card__rule { margin: 8px 0 0; border-top: 1px solid #1c2229; }
.tp-card__nums { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 7px 10px; padding-top: 8px; }
.tp-card__alts { margin-top: 8px; padding-top: 7px; border-top: 1px solid #1c2229; }
.tp-card__alts > .tp-f__k { display: block; }
.tp-card__alts ul { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 3px 10px; }
.tp-card__alts li { display: inline-flex; align-items: center; gap: 3px; font-size: 10.5px; color: #c9d1d9; font-variant-numeric: tabular-nums; }
.tp-card__alts li b { font-weight: 550; color: #e6edf3; }
.tp-card__alts li .pos, .tp-card__alts li .neg { font-size: 10.5px; }
.tp-card__alts .book-chip--logo img { height: 0.8rem; max-width: 3.2rem; }
.tp-card__alts .book-chip { font-size: 0.58rem; padding: 0.1rem 0.36rem; }
.tp-card__foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding-top: 8px; margin-top: auto; }
.tp-card__min { font-size: 10px; color: #6b757f; line-height: 1.3; }
.tp-f { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tp-f__k { font-size: 9px; font-weight: 500; letter-spacing: 0.05em; text-transform: uppercase; color: #6b757f; white-space: nowrap; }
.tp-f__v { font-size: 16px; font-weight: 550; color: #e6edf3; font-variant-numeric: tabular-nums; line-height: 1.1; white-space: nowrap; }
.tp-f__v .muted { font-size: 10.5px; font-weight: 400; margin-left: 4px; }
.tp-f__v--price { font-size: 19px; color: #06e5ff; letter-spacing: -0.02em; text-shadow: 0 0 16px rgba(6,229,255,0.25); }
.tp-card__add {
  margin-left: auto; padding: 5px 12px; border-radius: 8px; cursor: pointer;
  font: inherit; font-size: 12px; font-weight: 500; color: #c9d1d9;
  background: transparent; border: 1px solid #30373f; white-space: nowrap;
  transition: background 160ms ease, border-color 160ms ease, color 160ms ease;
}
.tp-card__add:hover { background: rgba(6,229,255,0.10); border-color: rgba(6,229,255,0.5); color: #ffffff; }
.tp-card__add:disabled { color: #6b757f; border-color: #21262d; cursor: default; background: transparent; }
@media (max-width: 640px) {
  .tp-card { padding: 16px 16px 14px; }
  .tp-card__sel { font-size: 20px; }
  .tp-card__ev b { font-size: 28px; }
  .tp-card__foot { gap: 12px; flex-wrap: wrap; }
}

/* The board's filters ride in the toolbar as th-filter selects, like every
   other board (Reilly, 2026-09-08). */
.oc-toolbar > .th-filter[data-f] { font-size: 13px; padding: 7px 10px; }

/* ---- The bet sheet (Reilly, 2026-09-08) ----------------------------------
   Press a bet, anywhere on it, and this slides up: every book's price for the
   same outcome against the sharp fair. One dialog, opened by
   BVBFeedUI.betSheet, the checkout dialog's material with a soft rise in. */
dialog.bet-sheet {
  width: min(640px, 94vw); padding: 0; margin: auto;
  /* dvh where the browser has it: on iOS, vh is the viewport with the
     toolbar HIDDEN, so 88vh ran under the toolbar and the bottom of the
     sheet was cut off (Reilly, 2026-09-14, "this box is fucked on iOS"). */
  max-height: 88vh; max-height: 88dvh;
  overflow: hidden; color: #e6edf3;
  opacity: 0; transform: translateY(18px) scale(0.985);
  transition: opacity 240ms cubic-bezier(0.22, 1, 0.36, 1), transform 240ms cubic-bezier(0.22, 1, 0.36, 1);
}
dialog.bet-sheet.is-open { opacity: 1; transform: none; }
dialog.bet-sheet::backdrop { opacity: 0; transition: opacity 240ms ease; }
dialog.bet-sheet.is-open::backdrop { opacity: 1; }
@media (prefers-reduced-motion: reduce) { dialog.bet-sheet, dialog.bet-sheet::backdrop { transition: none; } }
/* The page must not move under the sheet. On the ROOT, not on body: the UA
   propagates body's overflow to the viewport only while html is
   overflow:visible, and this site's html computes overflow-x: clip, so the
   body rule every other dialog here uses is a no-op that only clips the body
   box. This is the desktop half; iOS ignores overflow for touch, so
   feed-ui.js also swallows any touch that lands on the backdrop or would
   carry the sheet's scroll past its edge. */
html:has(dialog.bet-sheet[open]) { overflow-y: hidden; }
/* ONE scroll region. Before, only the price table scrolled and the head,
   tiles, chart, footer and alert row were all fixed around it: on a phone
   they ate the whole height and the table was left a two-line slot with
   its first row cut in half. Now everything scrolls together and the head
   stays pinned so Close is always in reach. */
.bs {
  display: flex; flex-direction: column;
  max-height: 88vh; max-height: 88dvh;
  overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch;
  outline: 0;   /* it takes focus on open, in place of the close button */
}
/* The column overflows on purpose (it scrolls), so no child may be squeezed:
   a shrinkable flex item collapses to its min-content, which turned the 36px
   close into 22px and threw the head's offset out. */
.bs > * { flex: 0 0 auto; }
.bs__head { position: sticky; top: 0; z-index: 2; background: #0d1117; padding: 26px 76px 18px 28px; }
/* The close is the first child of the scroller, not part of the head, so it
   can stay pinned on its own: a sticky element cannot leave its parent, and
   on a phone the head is 200px of title that must scroll away. The negative
   margin pulls the head up underneath it; the head's right padding keeps the
   title clear. */
.bs__close { position: sticky; top: 14px; z-index: 3; align-self: flex-end; box-sizing: border-box; width: 36px; height: 36px; padding: 0; margin: 14px 14px -50px 0; background: #161b22; }   /* 14 + 36 - 50 = the head starts at 0 */
.bs__league { font-size: 12px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: #8b96a1; }
.bs__title { margin: 6px 0 0; font-size: 26px; font-weight: 550; letter-spacing: -0.02em; line-height: 1.2; color: #ffffff; }
.bs__sub { margin: 6px 0 0; font-size: 15px; color: #8b96a1; }
.bs__event { margin: 8px 0 0; font-size: 14px; color: #a2adb8; }
/* float:none undoes site.css's float on the checkout's close. No ring on a
   pointer press: the ring iOS drew on it was showModal() handing it focus,
   which now goes to .bs. */
.bs .checkout__close { float: none; color: #c9d1d9; font-size: 20px; line-height: 1; cursor: pointer; }
.bs .checkout__close:focus:not(:focus-visible) { outline: 0; }
.bs__refs { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; padding: 0 28px 18px; }
.bs__tile { padding: 12px 14px; border-radius: 12px; background: rgba(255,255,255,0.04); border: 1px solid #21262d; display: flex; flex-direction: column; gap: 4px; }
.bs__k { font-size: 11px; font-weight: 500; letter-spacing: 0.08em; text-transform: uppercase; color: #6b757f; }
.bs__v { font-size: 22px; font-weight: 550; font-variant-numeric: tabular-nums; color: #e6edf3; letter-spacing: -0.01em; }
.bs__v--price { color: #06e5ff; }
.bs__body { padding: 0 28px; border-top: 1px solid #1c2229; }
.bs__table { width: 100%; border-collapse: collapse; font-size: 15px; font-variant-numeric: tabular-nums; }
.bs__table th { position: sticky; top: var(--bs-head, 0px); z-index: 1; background: #0d1117; text-align: left; padding: 12px 0 8px; font-family: var(--mono); font-size: 11px; letter-spacing: 0.02em; color: #818b98; font-weight: 500; border-bottom: 1px solid #21262d; }
.bs__table td { padding: 10px 0; border-bottom: 1px solid rgba(255,255,255,0.055); }
.bs__table th.num, .bs__table td.num { text-align: right; white-space: nowrap; }
.bs__table td b { font-weight: 550; color: #e6edf3; }
.bs__table tr.is-best td b { color: #06e5ff; }
.bs__table tr.is-mine td { background: rgba(6,229,255,0.05); }
.bs__mine { margin-left: 10px; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #06e5ff; }
.bs__table .book-chip--logo img { height: 1.3rem; max-width: 6rem; }
.bs__foot { margin: 0; padding: 14px 28px 22px; color: #6b757f; line-height: 1.45; }
/* On a phone it is a bottom sheet: full width, rising from the bottom edge,
   clear of the home indicator (viewport-fit=cover is on every page, so the
   inset is real). The UA caps a dialog's max-width below 100%, hence the
   explicit override. */
@media (max-width: 640px) {
  dialog.bet-sheet {
    width: 100%; max-width: 100%; margin: auto 0 0;
    max-height: 92vh; max-height: 92dvh;
    border-radius: 22px 22px 0 0; border-bottom: 0;
    transform: translateY(40px);
  }
  dialog.bet-sheet.is-open { transform: none; }
  .bs { max-height: 92vh; max-height: 92dvh; }
  /* A spacer, not padding-bottom: a scroll container's bottom padding is not
     reachable by scrolling in older WebKit, so the last row sat under the
     home indicator. */
  .bs::after { content: ""; flex: 0 0 env(safe-area-inset-bottom, 0px); }
  .bs__head, .bs__refs, .bs__body, .bs__foot, .bs__hist, .bs__watch { padding-left: 18px; padding-right: 18px; }
  /* the head scrolls away here; only the close stays pinned */
  .bs__head { position: static; padding-top: 20px; padding-right: 64px; }
  /* ...so the band the close sits in needs to be opaque, or the rows
     scrolling past show through it -- including through the transparent
     corners of the button's own circle. It rides on .bs rather than on the
     button: a pseudo-element of the button paints ABOVE the button's
     background, which erased the circle. A sticky flex item with a negative
     margin, so it pins without taking height, under the close (z 3) and over
     the pinned column headers (z 1). */
  .bs::before {
    content: ""; position: sticky; top: 0; z-index: 2;
    /* flex 0 0, not just height: `.bs > *` does not match a pseudo-element,
       so this one kept flex-shrink 1 and an overflowing column squeezed it
       to nothing. */
    flex: 0 0 50px; margin-bottom: -50px; background: #0d1117;
  }
  .bs__title { font-size: 22px; }
  .bs__refs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Tips cards open the sheet, so the whole card is a press target and the
   book's logo sits beside the selection (Reilly, 2026-09-08). */
.tp-card { cursor: pointer; }
.tp-card__sel { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tp-card__sel .book-chip--logo img { height: 1.3rem; max-width: 5.4rem; }
.tp-card__sel .book-chip { font-size: 0.72rem; padding: 0.18rem 0.55rem 0.18rem 0.45rem; }
/* Sharps rows press too. */
table.data--feed tbody tr[data-sheet] { cursor: pointer; }

/* ═══ Ask BvB, the member chatbot panel (dashboard and /ask/). Moved here
   from dashboard/index.html when the panel got its own page (Reilly,
   2026-09-11). ═══ */
/* Ask BvB — the premier tool on the page, so it gets the one gold on the
   page (Reilly, 2026-09-10). A gilt rim and a centred title carry it; the
   bubbles and cards underneath stay quiet so the gold reads as rank, not
   decoration. Tokens: champagne #e9c86b, deep gold #a67c1e, ember #3a2a0c. */
.chat { --gold: #e9c86b; --gold-deep: #a67c1e; --gold-dim: rgba(233, 200, 107, 0.16); --ember: #3a2a0c;
  /* Half the height of the Bookmaker balances card, which measures 322px at
     desktop width (Reilly, 2026-09-10). The log takes what is left and
     scrolls to the latest reply. */
  position: relative; margin-top: 16px; display: flex; flex-direction: column; gap: 8px; height: 200px; padding: 14px 20px; box-sizing: border-box;
  background: linear-gradient(180deg, #1a1710 0%, #151a22 38%);
  border: 1px solid var(--gold-deep); border-radius: 16px;
  box-shadow: 0 0 0 1px rgba(233, 200, 107, 0.08), 0 0 48px -18px rgba(233, 200, 107, 0.45); }
.chat::before { content: ""; position: absolute; inset: 0 auto auto 0; width: 100%; height: 1px; border-radius: 16px 16px 0 0;
  background: linear-gradient(90deg, transparent 4%, var(--gold) 50%, transparent 96%); pointer-events: none; }
.chat.is-dragover { outline: 1px dashed var(--gold); outline-offset: 4px; }
.chat__head { position: relative; display: flex; align-items: center; justify-content: center; min-height: 28px; }
.chat__head h3 { font-size: 20px; font-weight: 500; letter-spacing: -0.01em; color: var(--gold) !important; text-align: center; }
.chat__clear { position: absolute; right: 0; top: 50%; transform: translateY(-50%); font: inherit; font-size: 11px; text-transform: uppercase;
  color: var(--gold-deep); background: none; border: 0; padding: 0; cursor: pointer; }
.chat__clear:hover, .chat__clear:focus-visible { color: var(--gold); outline: none; }
.chat__log { display: flex; flex-direction: column; gap: 8px; flex: 1; min-height: 0; overflow-y: auto; padding-right: 4px; }
/* Usage rings, daily then monthly: how much of the allowance is used, no
   figures (Reilly, 2026-09-10). Track is dim gold, fill is gold. */
.chat__usage { position: absolute; left: 0; top: 50%; transform: translateY(-50%); display: inline-flex; gap: 6px; }
.chat-ring-wrap { display: block; width: 16px; height: 16px; line-height: 0; cursor: default; }
.chat-ring { width: 16px; height: 16px; display: block; }
.chat-ring circle { fill: none; stroke-width: 2.5; }
.chat-ring__track { stroke: rgba(233, 200, 107, 0.22); }
.chat-ring__fill { stroke: var(--gold); stroke-linecap: round; transform: rotate(-90deg); transform-origin: 50% 50%; }
.chat-ring--full .chat-ring__fill { stroke: #e06c5a; }
.chat-msg { max-width: 92%; padding: 10px 14px; border-radius: 12px; font-size: 14px; line-height: 1.5; color: #e6edf3; white-space: normal; }
.chat-msg--me { align-self: flex-end; background: var(--ember); border: 1px solid var(--gold-deep); color: #f6e7bd; }
.chat-msg--bot { align-self: flex-start; background: #1b212b; border: 1px solid #21262d; }
.chat-msg--note { background: none; border-color: transparent; color: #a2adb8; font-size: 13px; padding: 4px 14px; }
.chat-card { display: flex; flex-direction: column; gap: 2px; width: 100%; margin: 8px 0; padding: 10px 12px; text-align: left;
  background: #10141a; border: 1px solid #2b323c; border-radius: 10px; color: #e6edf3; font: inherit; cursor: pointer; }
.chat-card:hover, .chat-card:focus-visible { border-color: var(--gold); outline: none; }
.chat-card--bet { border-left: 3px solid var(--gold); }
.chat-card--edge { border-left: 3px solid #06e5ff; }
.chat-card--slip { border-left: 3px solid #78848f; }
.chat-card--gone { display: inline; padding: 2px 6px; margin: 0; font-size: 12px; color: #78848f; cursor: default; }
.chat-card__top { font-size: 11px; letter-spacing: 0.02em; color: #78848f; }
.chat-card__title { font-size: 15px; font-weight: 500; color: #ffffff; }
.chat-card__sub { font-size: 12px; color: #a2adb8; }
.chat-card__line { font-size: 13px; color: #e6edf3; }
.chat-card__line b { font-weight: 600; color: var(--gold); }
.chat__form { display: flex; align-items: flex-end; gap: 8px; }
.chat__attach { flex: 0 0 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center; background: none;
  border: 1px solid var(--gold-deep); border-radius: 6px; color: var(--gold); cursor: pointer; }
.chat__attach:hover, .chat__attach:focus-visible { background: var(--gold-dim); outline: none; }
.chat__input { flex: 1; min-height: 40px; max-height: 160px; padding: 9px 12px; resize: none; background: #10141a; border: 1px solid #2b323c;
  border-radius: 6px; color: #ffffff; font: inherit; font-size: 14px; line-height: 1.4; }
.chat__input:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 3px var(--gold-dim); }
.chat__send { flex: 0 0 auto; height: 40px; padding: 0 20px; background: linear-gradient(180deg, #f1d585, var(--gold) 55%, #cfa94a);
  border: 0; border-radius: 6px; color: #1a1408; font: inherit; font-size: 14px; font-weight: 600; cursor: pointer; }
.chat__send:hover, .chat__send:focus-visible { filter: brightness(1.06); outline: none; }
.chat [data-chat-spend] { color: var(--gold-deep); }
@media (prefers-reduced-motion: no-preference) { .chat__send { transition: filter 120ms ease; } }

/* ── Tips > Schedule (Reilly, 2026-09-14) ─────────────────────────────────
   The week as a list, one row per fixture, each row a native <details> that
   drops open to the bets standing on it. Same palette as the bet cards; no
   card chrome, because a week of fixtures reads as a timetable, not a grid. */
/* #sch-out ends in -out, so site.css's board rule makes it a flex FILL inside
   the fixed-height screen. A timetable is a column that scrolls, not a row. */
#sch-out { display: block; flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-width: thin; }
.sch-day { margin: 0 0 18px; }
/* Every heading on this board folds: the day, the sub-day inside a league or
   bet-type group, and the fixture itself (Reilly, 2026-09-14). */
.sch-day > summary, .sch-sub > summary { cursor: pointer; list-style: none; }
.sch-day > summary::-webkit-details-marker, .sch-sub > summary::-webkit-details-marker { display: none; }
.sch-day__h { position: sticky; top: 0; z-index: 2; display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin: 0 0 6px; padding: 8px 2px; background: var(--oc-bg, #070d16);
  font-size: 11.5px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase; color: #c9d1d9; }
.sch-day__h .muted { font-size: 10.5px; letter-spacing: 0.06em; text-transform: none; font-variant-numeric: tabular-nums; }
.sch-day__h::before, .sch-sub__h::before { content: "\25B8"; display: inline-block; width: 10px; color: #6b757f;
  transition: transform 120ms ease; }
.sch-day[open] > .sch-day__h::before, .sch-sub[open] > .sch-sub__h::before { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) { .sch-day__h::before, .sch-sub__h::before { transition: none; } }
/* A group that is not a day still holds its days, one fold deeper. */
.sch-sub { margin: 0 0 8px; }
.sch-sub__h { position: sticky; top: 30px; z-index: 1; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 0 0 4px; padding: 5px 2px 5px 10px; background: var(--oc-bg, #070d16);
  font-size: 10.5px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #8b96a1; }
.sch-sub__h .muted { font-size: 10px; letter-spacing: 0.06em; text-transform: none; font-variant-numeric: tabular-nums; }
.sch-sub > .sch-fx { margin-left: 10px; }

.sch-fx { border: 1px solid #1c2229; border-radius: 10px; background: #0c1116; margin-bottom: 6px; }
.sch-fx[open] { border-color: rgba(6,229,255,0.35); background: linear-gradient(160deg, #10161d 0%, #0c1116 60%); }
.sch-fx > summary { display: grid; grid-template-columns: 74px 72px minmax(0, 1fr) auto; align-items: center; gap: 10px;
  padding: 10px 12px; cursor: pointer; list-style: none; }
.sch-fx > summary::-webkit-details-marker { display: none; }
.sch-fx > summary:hover { background: rgba(6,229,255,0.05); border-radius: 10px; }
.sch-fx__t { font-size: 12px; color: #ffffff; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sch-fx__t.muted { color: #6b757f; }
.sch-fx__l { font-size: 10px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: #8b96a1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sch-fx__n { min-width: 0; font-size: 13.5px; color: #e6edf3; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sch-fx__c { display: flex; align-items: center; gap: 8px; font-size: 11px; color: #8b96a1; font-variant-numeric: tabular-nums; white-space: nowrap; }
.sch-fx__c b { color: var(--field-up); font-size: 13px; }
.sch-fx__off { color: var(--field-down); }

.sch-panel { padding: 0 12px 12px; }
.sch-tabs { display: flex; gap: 6px; margin: 2px 0 8px; border-bottom: 1px solid #1c2229; }
.sch-tabs button { background: none; border: 0; border-bottom: 2px solid transparent; padding: 6px 2px; margin-right: 12px;
  font: inherit; font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #6b757f; cursor: pointer; }
.sch-tabs button.is-on { color: #06e5ff; border-bottom-color: #06e5ff; }
.sch-tabs button[disabled] { opacity: 0.4; cursor: default; }

.sch-list { display: flex; flex-direction: column; }
.sch-bet { display: grid; grid-template-columns: minmax(0, 1fr) auto 90px 64px 54px; align-items: center; gap: 10px;
  width: 100%; padding: 8px 4px; background: none; border: 0; border-top: 1px solid #161b21;
  font: inherit; color: inherit; text-align: left; cursor: pointer; }
.sch-bet:first-child { border-top: 0; }
.sch-bet:hover { background: rgba(6,229,255,0.06); }
.sch-bet__n { min-width: 0; font-size: 13px; color: #ffffff; }
.sch-bet__n small { display: block; font-size: 10.5px; color: #8b96a1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sch-bet__o { font-size: 13px; color: #e6edf3; font-variant-numeric: tabular-nums; text-align: right; }
.sch-bet__o small { display: block; font-size: 10px; color: #6b757f; }
.sch-bet__ev, .sch-bet__u { font-size: 13px; font-variant-numeric: tabular-nums; text-align: right; }
.sch-bet__u { color: #8b96a1; }
.sch-empty, .sch-note { margin: 6px 2px 0; }
@media (max-width: 760px) {
  .sch-fx > summary { grid-template-columns: 64px minmax(0, 1fr); grid-template-areas: "t n" "l c"; row-gap: 4px; }
  .sch-fx__t { grid-area: t; } .sch-fx__n { grid-area: n; } .sch-fx__l { grid-area: l; } .sch-fx__c { grid-area: c; }
  .sch-bet { grid-template-columns: minmax(0, 1fr) 78px 60px; grid-template-areas: "n o ev" "bk o u"; row-gap: 2px; }
  .sch-bet__n { grid-area: n; } .sch-bet__bk { grid-area: bk; } .sch-bet__o { grid-area: o; }
  .sch-bet__ev { grid-area: ev; } .sch-bet__u { grid-area: u; }
}

/* Account stage under each balance tile (tracker, 2026-09-14). */
.book-slot { display: flex; flex-direction: column; gap: 4px; }
.book-stage { font: inherit; font-size: 11px; padding: 3px 4px; border-radius: 6px; border: 1px solid #21262d; background: #0d1117; color: #6b757f; width: 100%; }
.book-stage--mugging { color: #a2adb8; } .book-stage--promo { color: #f0abfc; }
.book-stage--value { color: #1fdd83; } .book-stage--gubbed { color: #ff5c5c; }

/* Line movement (2026-09-14): the bet sheet's chart and the odds board's Move column. */
.bs__hist { padding: 0 28px 12px; }
.bs__hist-head { display: flex; justify-content: space-between; align-items: baseline; margin: 0 0 6px; font-size: 13px; }
.lc { width: 100%; height: auto; display: block; color: #a2adb8; }
.lc.pos { color: #1fdd83; } .lc.neg { color: #ff5c5c; }
.lc__lab { font: 500 11px var(--mono); fill: #818b98; } .lc__lab--now { fill: currentColor; }
.lc__ax { font: 500 10px var(--mono); fill: #6b757f; }
.oc-spark { width: 96px; color: #818b98; }
.oc-spark .mv-spark.pos { color: #1fdd83; } .oc-spark .mv-spark.neg { color: #ff5c5c; }

/* Price alerts (2026-09-14): the bet sheet's set-alert row and the panel's list. */
.bs__watch { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 28px 22px; border-top: 1px solid rgba(255,255,255,0.055); margin-top: -8px; }
.bs__watch input { width: 84px; }
.push-watch { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.push-watch li { display: flex; justify-content: space-between; align-items: center; gap: 8px; padding: 3px 0; }

/* ---- Locked sidebar rows (2026-09-25): every row is listed, the ones the
   member does not hold are dimmed and wear a padlock; the page behind them
   draws the offer. ---- */
.v2-app__nav a.v2-app__locked { opacity: 0.55; }
.v2-app__nav a.v2-app__locked:hover { opacity: 0.85; }
.v2-app__lock { margin-left: auto; flex: none; opacity: 0.8; }

/* ---- /pricing/: three cards, one toggle ---- */
.pr-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 24px; width: 100%; }
@media (max-width: 900px) { .pr-grid { grid-template-columns: 1fr; } }
.pr-card { display: flex; flex-direction: column; gap: 18px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.1); border-radius: 24px; padding: 28px; backdrop-filter: blur(20px); position: relative; }
.pr-card--featured { border-color: rgba(233,200,107,0.55); box-shadow: 0 0 0 1px rgba(233,200,107,0.25), 0 24px 60px rgba(0,0,0,0.35); }
.pr-card__badge { width: 32px; height: 32px; }
.pr-card__name { font-size: 26px; font-weight: 500; color: #ffffff; line-height: 1.2; }
.pr-card__kicker { font-size: 14px; color: #a2adb8; }
.pr-card__price { display: flex; align-items: baseline; gap: 6px; }
.pr-card__amount { font-size: 44px; font-weight: 500; letter-spacing: -0.03em; color: #ffffff; font-variant-numeric: tabular-nums; }
.pr-card__per { font-size: 15px; color: #a2adb8; }
.pr-card__note { font-size: 13px; color: #78848f; min-height: 1.2em; }
.pr-card ul { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.pr-card li { display: flex; gap: 10px; font-size: 15px; line-height: 1.45; color: #d7dde3; }
.pr-card li::before { content: ""; flex: none; width: 8px; height: 8px; border-radius: 9999px; background: #06e5ff; margin-top: 8px; }
.pr-card li.pr-x { color: #78848f; }
.pr-card li.pr-x::before { background: #3d4145; }
.pr-buy { display: flex; align-items: center; justify-content: center; padding: 14px 24px; min-height: 48px; box-sizing: border-box; background: #0066ff; border-radius: 6px; text-decoration: none; color: #ffffff; font-size: 16px; font-weight: 500; border: 0; cursor: pointer; font: inherit; font-weight: 500; }
.pr-buy--soft { background: rgba(31,35,40,0.4); border: 1px solid rgba(255,255,255,0.4); color: #8dd6ff; }
.pr-buy[disabled] { opacity: 0.5; cursor: not-allowed; }
.pr-buy--own { background: rgba(6,229,255,0.12); border: 1px solid rgba(6,229,255,0.4); color: #8dd6ff; cursor: default; }
.pr-toggle { display: inline-flex; padding: 4px; border-radius: 9999px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.12); gap: 4px; }
.pr-toggle button { border: 0; background: transparent; color: #a2adb8; padding: 8px 18px; border-radius: 9999px; font: inherit; font-size: 15px; cursor: pointer; }
.pr-toggle button[aria-pressed="true"] { background: #ffffff; color: #0d1117; font-weight: 500; }
.pr-save { font-size: 12px; color: #e9c86b; margin-left: 6px; }

/* --- the racing board (Reilly, 2026-09-25) ----------------------------------
   The horses page reads as a meeting, not as a grid: day > track > the card
   from R1 down. Native <details>, so the open/closed state is the browser's
   and there is nothing to re-bind when tips.js repaints. */
.rb { display: flex; flex-direction: column; gap: 8px; }
.rb-day, .rb-venue { border-radius: 12px; border: 1px solid #242b33; overflow: hidden; }
.rb-day { background: linear-gradient(160deg, #10161d 0%, #0c1116 55%, #0a0e13 100%); }
.rb-venue { margin: 0 10px 10px; border-color: #1c232b; background: #0b1015; }
.rb-day__h, .rb-venue__h {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  cursor: pointer; list-style: none; user-select: none;
  transition: color 160ms ease, background 160ms ease;
}
.rb-day__h::-webkit-details-marker, .rb-venue__h::-webkit-details-marker { display: none; }
.rb-day__h { padding: 13px 16px; font-size: 15px; font-weight: 600; color: #e6edf3; letter-spacing: -0.01em; }
.rb-venue__h { padding: 10px 14px; font-size: 13px; font-weight: 550; color: #c9d1d9; }
.rb-day__h:hover, .rb-venue__h:hover { color: var(--accent, #06e5ff); }
/* The twisty. A rotating caret drawn in CSS beats an icon font for one glyph. */
.rb-day__h > span:first-child, .rb-venue__h > span:first-child { display: flex; align-items: center; gap: 9px; min-width: 0; }
.rb-day__h > span:first-child::before, .rb-venue__h > span:first-child::before {
  content: ""; flex: none; width: 5px; height: 5px;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg); transition: transform 180ms ease; opacity: 0.65;
}
.rb-day[open] > .rb-day__h > span:first-child::before,
.rb-venue[open] > .rb-venue__h > span:first-child::before { transform: rotate(45deg); }
.rb-count { flex: none; font-size: 10.5px; letter-spacing: 0.08em; text-transform: uppercase; color: #6b757f; font-variant-numeric: tabular-nums; }

.rb-runs { list-style: none; margin: 0; padding: 0 0 4px; }
.rb-run {
  display: grid; align-items: center; gap: 10px 14px;
  grid-template-columns: 34px minmax(0, 1fr) auto auto;
  padding: 10px 14px; border-top: 1px solid #161b21;
}
.rb-run__r { font-size: 12px; font-weight: 650; color: var(--accent, #06e5ff); font-variant-numeric: tabular-nums; letter-spacing: 0.02em; }
.rb-run__sel { min-width: 0; font-size: 13.5px; color: #e6edf3; }
.rb-run__sel b { font-weight: 600; }
.rb-run__nums { display: flex; gap: 16px; }
.rb-run__add {
  flex: none; padding: 5px 11px; border-radius: 7px; cursor: pointer;
  border: 1px solid #2a323b; background: #131a21; color: #c9d1d9;
  font: inherit; font-size: 11px; transition: border-color 160ms ease, color 160ms ease;
}
.rb-run__add:hover:not(:disabled) { border-color: rgba(6,229,255,0.45); color: var(--accent, #06e5ff); }
.rb-run__add:disabled { opacity: 0.5; cursor: default; }

/* On a phone the four numbers wrap under the selection rather than squeezing
   the horse's name to nothing. */
@media (max-width: 720px) {
  .rb-run { grid-template-columns: 30px minmax(0, 1fr) auto; }
  /* Track stays up on the selection's line; the numbers get the full width
     under it rather than leaving the button stranded on a row of its own. */
  .rb-run__add { grid-column: 3; grid-row: 1; }
  .rb-run__nums { grid-column: 2 / -1; grid-row: 2; gap: 14px; flex-wrap: wrap; }
  .rb-venue { margin: 0 6px 8px; }
}
