/* =============================================================================
   BEST PRICE — the odds screen (/nfl/best-price and /mlb/best-price;
   docs/ev-terminal-plan.md, docs/best-price-data-spine.md "Section").

   MOVED 2026-09-10 from css/nfl/best-price.css to css/core/ for the MLB board.
   All 433 lines came across unchanged — they had ZERO sport-specific rules to
   begin with (the only two "nfl" strings were comments, both fixed in the
   move). Everything hangs off
   .pb-best-price / #best-price-root / body.best-price-active, and the accent is
   --theme-accent*, a USER preference (themeManager.js:143), never a per-sport
   value — so the two sports link the SAME sheet from their own gated blocks,
   exactly as core/pickem.css is linked twice (templates/index.html:179 / :204).
   Sport overrides, if any are ever needed, go under .pb-best-price--mlb at the
   very END of this file (see the marker down there — the phone layer sits in
   the middle, so a naive append would beat it at equal specificity).

   The board CSS of the design reference DataUpdater/scripts/best_price_template.html
   (built page: design-prototypes/best-price.html), ported 2026-09-07:
     * the demo's app-shell mirrors are DROPPED (its :root tokens, top bar,
       bottom nav, parlay pill, drawer, backdrop) — core/themes.css owns the
       tokens, the shell owns the chrome; teal = var(--theme-accent*) and is
       reserved for the best price and the ADD flash, as designed;
     * every rule is scoped under .pb-best-price (the #best-price-root class)
       and the class names the demo shared with the workbench sheets are
       renamed: .pf-chip(s) -> .bp-chip(s) (.active-chip -> .is-on),
       .table-parlay-btn -> .bp-add, .tv-player-name -> .bp-name,
       .team-code -> .bp-code, .glass-panel -> .bp-panel;
     * no :root declarations (pickem.css rule): the board-local vars live on
       .pb-best-price;
     * the PHONE LAYER: the app ships NO viewport meta, so a phone renders the
       ~980px canvas scaled down (pickem.css:26-30, bottom-nav.css:171). The
       demo's <=640px block is re-expressed at the app's compact breakpoint,
       @media (max-width: 1000px), same structure (segmented bar + Great
       prices, the chip strip, full-width search, group headers with a square
       chevron, one card per bet, "All books" chips + Other lines), every size
       ~1.8x (the hub's own scale: nav 64->124, trend pill 13->23px). The JS
       switches on the same query (bestPricePage.js MQ).
   Controls (founder 2026-09-07): ONE row — the bet-type chips, the view
   chips and Great prices in the same chip style, the compact search and
   "Collapse all" with its sliders icon — and the freshness line as small
   muted right-aligned text under it. The BOARD below stays the design
   reference's (one sticky header tier at --bp-th, 48px rows, 44px avatars,
   the ADD pill in the line cell, boxed .bp-c / .bp-cell prices): a restyle
   of the table was reverted the same day at the founder's call.
   Shell-state rules mirror core/pickem.css (body.best-price-active is the
   full equivalent of the direct-load doc-write #best-price-init-styles in
   templates/index.html — keep the two in sync).
   ============================================================================= */

/* ── Sticky enabler (pickem.css precedent): keep the root a non-scroller ── */
html.is-best-price,
html.is-best-price body { overflow-x: clip; }

/* ── Page shell ── */
body.best-price-active {
  background: #000;
  padding-bottom: calc(var(--pb-bottom-nav-height, 64px) + max(env(safe-area-inset-bottom), 12px));
}
body.best-price-active .pb-bottom-nav { display: flex !important; }
body.best-price-active #cover-page,
body.best-price-active .navbar,
body.best-price-active .workbench-layout,
body.best-price-active .sidebar,
body.best-price-active #main-app-wrapper,
body.best-price-active #loading-animation { display: none !important; }
body.best-price-active .landing-nav { display: block !important; position: fixed; top: 0; left: 0; right: 0; z-index: 1000; }
body.best-price-active .landing-nav .sport-selector { display: flex !important; }
#best-price-root { display: none; }   /* revealed by the doc-write + the module */
body.best-price-active #best-price-root { display: block; min-height: 100vh; }

/* ── Board-local vars + the page's own box (clears the fixed landing-nav) ── */
.pb-best-price {
  --bp-th: 66px;          /* the sticky book-header row's height (game rows stick under it) */
  --bp-top: 84px;         /* the fixed landing-nav's height on the desktop canvas (pickem.css .app) */
  color: var(--theme-text-primary, #fff);
  font-size: 14px;
  line-height: 1.4;
  padding: calc(var(--bp-top) + env(safe-area-inset-top, 0px)) 0 0;
}
.pb-best-price *, .pb-best-price *::before, .pb-best-price *::after { box-sizing: border-box; }
.pb-best-price button, .pb-best-price input { font-family: inherit; }
.pb-best-price img { max-width: 100%; }

/* ── Loading / unavailable states (the app has no inlined capture to fall back to) ── */
.pb-best-price .bp-state { padding: 48px 16px; text-align: center; color: rgba(255,255,255,.6); font-size: 15px; }
.pb-best-price .bp-state.is-error { color: rgba(255,255,255,.75); }
.pb-best-price .bp-state .bp-retry { display: inline-block; margin-top: 14px; padding: 8px 20px; border-radius: 999px; border: 1px solid rgba(var(--theme-accent-rgb,0,211,189),.4); background: rgba(var(--theme-accent-rgb,0,211,189),.1); color: var(--theme-accent-bright,#00F5D4); font-weight: 600; font-size: 13px; cursor: pointer; }
.pb-best-price .bp-state .bp-retry:hover { background: rgba(var(--theme-accent-rgb,0,211,189),.2); }

/* ── ADD (the stub): a tap flashes "Coming soon" in the accent for 1.5 s (bestPricePage.js addFlash) ── */
.pb-best-price .bp-add.bp-soon,
.pb-best-price .bp-add.bp-soon:hover { background: rgba(var(--theme-accent-rgb,0,211,189),.16); border-color: rgba(var(--theme-accent-rgb,0,211,189),.6); color: var(--theme-accent-bright,#00F5D4); }

/* ==== the board (design reference, scoped) ==== */
/* ---- the board fills the shell; the table wrap is the one scroller (both axes) so the
   sticky book-logo header and the sticky game headers sit on the scrollport edge ---- */
.pb-best-price #bp-section{width:100%;padding:0 12px;margin-top:6px;position:relative;z-index:1}
.pb-best-price .bp-panel{position:relative;isolation:isolate}   /* no card chrome: the board lies on the page */
.pb-best-price #bp-board{display:flex;flex-direction:column;min-height:600px;max-height:calc(100vh - var(--bp-top) - var(--pb-bottom-nav-height,64px) - 20px);padding:0}
.pb-best-price .bp-board-inner{padding:2px 0 10px;flex:0 0 auto}
.pb-best-price .bp-table-wrap{flex:1 1 auto;min-height:0;overflow:auto;margin:0 0 16px;padding:0}
.pb-best-price .bp-panel-title{margin:0;font-size:16px;font-weight:600;color:#fff}

/* ---- controls, one row: the bet-type chips (wrap), the view chips + Great prices, then the search + Collapse all;
   the freshness line as small muted text right-aligned under the row ---- */
.pb-best-price .bp-board-head{display:flex;align-items:center;gap:9px;background:#171717;border-radius:12px;padding:9px 10px}
.pb-best-price .bp-viewgrp{display:inline-flex;align-items:center;gap:5px;flex:0 0 auto}
.pb-best-price .bp-view{display:inline-flex;align-items:center;gap:6px}
.pb-best-price .bp-head-right{display:flex;align-items:center;gap:8px;flex:0 0 auto}
/* *** THE FRESHNESS LINE THAT HAS NEVER RENDERED, ON EITHER LAYOUT, ON EITHER
   SPORT (fixed 2026-09-11) *** This rule said `display:none` — flatly, always —
   and the phone layer said it a second time. So paintLive() dutifully cleared
   `hidden` on #bp-live, its own child, inside a parent that was display:none,
   and "Live data · pulled N min ago" was never on screen. Both jsdom suites
   passed throughout: they assert on el.hidden and el.textContent, which were
   correct; only the CSS was wrong.
   That is not cosmetic. Prices on the hourly tier can be an hour old
   (docs/odds-pipeline), the board offers them as takeable deep links, and
   docs/best-price-data-spine.md calls this "the first freshness element".
   The `:has()` rule below is the one that was meant to hide it — an EMPTY row
   when #bp-live is still [hidden] (no meta.servedAt on the snapshot) — and it
   only works if the base state is visible. */
.pb-best-price .bp-board-row2{display:flex;justify-content:flex-end;align-items:center;margin-top:6px;min-height:12px}
.pb-best-price .bp-board-row2:has(> [hidden]){display:none}
.pb-best-price .bp-spacer{flex:1 1 auto}
.pb-best-price .bp-search{position:relative;display:flex;align-items:center}
.pb-best-price .bp-search svg{position:absolute;left:13px;width:14px;height:14px;fill:none;stroke:rgba(255,255,255,.45);stroke-width:2;pointer-events:none}
.pb-best-price .bp-search input{width:150px;height:36px;padding:0 12px 0 34px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.12);border-radius:8px;color:rgba(255,255,255,.95);font-size:12.5px;font-weight:500;outline:none;transition:border-color .15s,background .15s}
.pb-best-price .bp-search input::placeholder{color:rgba(255,255,255,.4)}
.pb-best-price .bp-search input:focus{background:rgba(var(--theme-accent-rgb,0,211,189),.06);border-color:rgba(var(--theme-accent-rgb,0,211,189),.5)}
/* the one freshness element: "Live data · pulled N min ago", only when the snapshot came from the app's feed */
.pb-best-price .bp-live{font-size:11px;color:rgba(255,255,255,.45);white-space:nowrap;line-height:1}
.pb-best-price .bp-live::before{content:'';display:inline-block;width:6px;height:6px;border-radius:50%;background:#2dd4bf;margin-right:6px;vertical-align:0}
.pb-best-price .bp-fold{appearance:none;display:inline-flex;align-items:center;gap:6px;height:36px;padding:0 12px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.12);border-radius:8px;color:rgba(255,255,255,.85);font-size:12.5px;font-weight:600;cursor:pointer;white-space:nowrap}
.pb-best-price .bp-fold:hover{background:rgba(255,255,255,.08);border-color:rgba(255,255,255,.2)}
.pb-best-price .bp-fold-ico{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;flex:0 0 auto}
.pb-best-price .bp-chips{display:flex;flex-wrap:wrap;gap:5px;flex:1 1 auto;min-width:0}
.pb-best-price .bp-chip{appearance:none;height:36px;border:1px solid rgba(255,255,255,.12);border-radius:8px;background:rgba(255,255,255,.04);color:rgba(255,255,255,.78);font-size:12px;font-weight:500;padding:0 8px;cursor:pointer;white-space:nowrap;line-height:1;transition:background .15s,border-color .15s,color .15s}
.pb-best-price .bp-chip:hover{background:rgba(255,255,255,.08);color:rgba(255,255,255,.9)}
/* the active chip — a chosen bet type, the current view, Great prices on: teal outline + teal text */
.pb-best-price .bp-chip.is-on,.pb-best-price .bp-seg.is-active{background:rgba(var(--theme-accent-rgb,0,211,189),.08);border-color:rgba(var(--theme-accent-rgb,0,211,189),.6);color:var(--theme-accent-bright,#00F5D4)}
/* ---- PHONE-ONLY CHROME, OFF on this canvas (2026-09-11). Both of these ship
   in boardMarkup() for every layout and are switched on inside
   @media (max-width: 1000px) further down; the desktop board must not grow a
   second title bar or a third sort button. "Best value" is the phone's flat
   ranked feed and desktop is deliberately NOT given it (spec §2) — desktop's
   own flat view is "Biggest gap", which the phone hides in return. ---- */
.pb-best-price .bp-mtop{display:none}
.pb-best-price .bp-view .bp-seg-value{display:none}

/* ---- the odds screen (header treatment from workbench-table.css .props-table) ---- */
.pb-best-price .bp-table{width:100%;min-width:1330px;border-collapse:separate;border-spacing:0;table-layout:fixed;font-size:13.5px}
.pb-best-price .bp-table th{position:sticky;top:0;z-index:6;height:var(--bp-th);background:#171717;border-bottom:6px solid var(--theme-bg-base,#000);background-clip:padding-box;color:rgba(var(--theme-accent-rgb,0,211,189),.92);padding:6px 4px;text-align:center;vertical-align:middle;font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.5px;white-space:nowrap;user-select:none;}
.pb-best-price .bp-table th:first-child{border-radius:12px 0 0 12px}
.pb-best-price .bp-table th:last-child{border-radius:0 12px 12px 0}
.pb-best-price .bp-table th.col-id,.pb-best-price .bp-table th.col-line{text-align:left;padding-left:10px}
/* fixed layout: the header row sets every column; the identity column must not collapse now that ADD lives in the line cell */
.pb-best-price .bp-table th.col-id{width:210px}
.pb-best-price .bp-table th.col-line{width:206px}
.pb-best-price .bp-table th.col-fair{width:62px}
.pb-best-price .bp-table th.col-book{width:56px}
.pb-best-price .bp-table th.col-gap{width:58px}
.pb-best-price .bp-table th.col-chev{width:26px}
.pb-best-price .bp-table th.col-fair{color:var(--theme-accent-bright,#00F5D4)}
.pb-best-price .bp-bh{display:flex;flex-direction:column;align-items:center;gap:5px;line-height:1}
.pb-best-price .bp-bh .bp-bk,.pb-best-price .bp-bh .bp-mono{display:block}
.pb-best-price .bp-bh .bp-bkname{font-size:9px;letter-spacing:-.2px;font-weight:600;letter-spacing:0;text-transform:none;color:rgba(255,255,255,.74);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.pb-best-price .bp-table td{padding:0 4px;height:48px;vertical-align:middle;text-align:center;color:rgba(255,255,255,.88);background:rgba(255,255,255,.07)}
.pb-best-price .bp-table tr.first td{border-top:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-table tr.last td{border-bottom:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-table td.cell-id,.pb-best-price .bp-table td.cell-chev{border-top:1px solid rgba(255,255,255,.10);border-bottom:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-table td.cell-id{border-left:1px solid rgba(255,255,255,.10);border-radius:12px 0 0 12px}
.pb-best-price .bp-table td.cell-chev{border-right:1px solid rgba(255,255,255,.10);border-radius:0 12px 12px 0}
.pb-best-price .bp-table tr.bp-sp td{height:8px;padding:0;background:transparent;border:0}
/* the row highlight is about the LINE you are on, so the rowspan'd player cell stays out of it
   (it belongs to the whole block, and lighting it made hovering either line flash the player) */
.pb-best-price .bp-table tr.bp-r:hover td:not(.cell-id){background:rgba(255,255,255,.105)}
.pb-best-price .bp-table tr.is-open td{background:rgba(255,255,255,.055)}
.pb-best-price .bp-table tr.bp-r{cursor:pointer}
.pb-best-price .bp-table td.cell-action{width:52px;padding:0 2px}
.pb-best-price .bp-table td.cell-id{text-align:left;padding-left:12px}
.pb-best-price .bp-table td.cell-line{text-align:left;padding-left:8px;font-size:14px;font-weight:400;white-space:nowrap;color:rgba(255,255,255,.92)}
.pb-best-price .bp-linewrap{display:flex;align-items:center;gap:9px}
.pb-best-price .bp-linewrap .bp-add{flex:0 0 auto}
.pb-best-price .bp-table td.cell-fair{padding:2px 4px}
.pb-best-price .bp-table td.cell-bk{padding:3px 2px}
.pb-best-price .bp-table td.cell-chev{padding:0 4px 0 2px}
.pb-best-price .bp-add{width:44px;padding:5px 9px;font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.3px;border:1px solid rgba(255,255,255,.16);background:rgba(255,255,255,.04);color:rgba(255,255,255,.82);border-radius:6px;cursor:pointer;transition:all .15s ease;white-space:nowrap}
.pb-best-price .bp-add:hover{background:rgba(var(--theme-accent-rgb,0,211,189),.14);border-color:rgba(var(--theme-accent-rgb,0,211,189),.5);color:var(--theme-accent-bright,#00F5D4)}
/* the "Coming soon" flash wraps to two lines inside the ADD's own box (a flex item sizes to
   max-content, so the cap is what keeps the line label from jumping for 1.5 s) */
.pb-best-price .bp-add.bp-soon{max-width:44px;white-space:normal;line-height:1.1;font-size:8.5px;letter-spacing:0;padding:3px 3px}
.pb-best-price .bp-player{display:flex;align-items:center;gap:9px;min-width:0}
.pb-best-price .bp-avatar{width:44px;height:44px;border-radius:50%;object-fit:cover;flex:0 0 auto;background:#101216;border:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-avatar-team,.pb-best-price .bp-avatar-init{display:inline-flex;align-items:center;justify-content:center;font-size:10px;font-weight:600;color:rgba(255,255,255,.6)}
.pb-best-price .bp-avatar-init{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.12);letter-spacing:.3px}
.pb-best-price .bp-avatar-team img{width:70%;height:70%;object-fit:contain}
/* [hidden] re-asserted (css/nfl/nfl-game-detail.css:166 does the same — path spelled out now that
   this sheet lives in core/): the display rules above (the avatar
   fallback beside a headshot, #bp-live, #bp-fold …) would otherwise beat the UA's [hidden] rule */
.pb-best-price [hidden]{display:none!important}
.pb-best-price .bp-player-text{display:flex;flex-direction:column;line-height:1.15;min-width:0}
.pb-best-price .bp-name{font-size:14.5px;font-weight:600;color:rgba(255,255,255,.95);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pb-best-price .bp-team{display:inline-flex;align-items:center;gap:4px;font-size:11.5px;color:rgba(255,255,255,.58);margin-top:3px}
.pb-best-price .bp-team-logo{width:16px;height:16px;object-fit:contain}
.pb-best-price .bp-code{font-weight:700;letter-spacing:.3px}
.pb-best-price .bp-sep{color:rgba(255,255,255,.3)}
.pb-best-price .bp-mkword{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pb-best-price .bp-price{font-weight:600;font-size:13px;color:rgba(255,255,255,.85);font-variant-numeric:tabular-nums;white-space:nowrap}
.pb-best-price .bp-price.pos{color:#4ade80}
.pb-best-price .bp-price.over{color:#f87171}
.pb-best-price .bp-dash{color:rgba(255,255,255,.3)}
.pb-best-price .bp-na{font-size:11px;font-weight:400;color:rgba(255,255,255,.35);white-space:nowrap}
.pb-best-price .bp-fairv{display:block;font-size:14px;font-weight:700;font-variant-numeric:tabular-nums;color:#fff;line-height:1.1}
.pb-best-price .bp-table td.cell-gap{padding:0 2px}
.pb-best-price .bp-gap{display:inline-block;padding:3px 7px;border-radius:999px;font-size:11px;font-weight:700;font-variant-numeric:tabular-nums;white-space:nowrap;color:#fff;background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.16);line-height:1.2}
.pb-best-price .bp-gap.mid{background:rgba(255,255,255,.05);border-color:rgba(255,255,255,.1);color:rgba(255,255,255,.85);font-weight:600}
.pb-best-price .bp-gap.lo{background:transparent;border-color:transparent;color:rgba(255,255,255,.38);font-weight:500;padding:0}
.pb-best-price .bp-table tr.bp-lr .bp-gap{font-size:10.5px}
.pb-best-price .bp-great{display:inline-block;margin-top:3px;padding:2px 7px;border-radius:999px;font-size:9.5px;font-weight:700;letter-spacing:.2px;white-space:nowrap;background:rgba(var(--theme-accent-rgb,0,211,189),.16);color:var(--theme-accent-bright,#00F5D4);border:1px solid rgba(var(--theme-accent-rgb,0,211,189),.45);line-height:1.2}
/* a book cell: the price at the row's line, or the book's own line (captioned) */
.pb-best-price .bp-c{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;height:40px;border-radius:8px;background:linear-gradient(180deg,rgba(255,255,255,.07) 0%,rgba(255,255,255,.03) 100%);border:1px solid rgba(255,255,255,.05);color:rgba(255,255,255,.86);text-decoration:none;font-weight:500;font-variant-numeric:tabular-nums;white-space:nowrap;line-height:1;min-width:0}
.pb-best-price a.bp-c:hover{background:rgba(var(--theme-accent-rgb,0,211,189),.12);border-color:rgba(var(--theme-accent-rgb,0,211,189),.45)}
.pb-best-price .bp-c.empty{background:transparent;border-color:transparent}
.pb-best-price .bp-c.empty::before{content:"–";color:rgba(255,255,255,.2);font-size:13px}
.pb-best-price .bp-c.own{color:rgba(255,255,255,.62)}
.pb-best-price .bp-c.tiny{color:rgba(255,255,255,.38)}
.pb-best-price .bp-c.best{background:linear-gradient(180deg,rgba(var(--theme-accent-rgb,0,211,189),.12) 0%,rgba(var(--theme-accent-rgb,0,211,189),.04) 100%);border-color:rgba(var(--theme-accent-rgb,0,211,189),.75);color:var(--theme-accent-bright,#00F5D4);font-weight:700}
.pb-best-price .bp-c .bp-cap{font-size:9px;font-weight:400;color:rgba(255,255,255,.5);white-space:nowrap;line-height:1}
.pb-best-price .bp-c.best .bp-cap{color:rgba(0,245,212,.8)}
.pb-best-price .bp-bk{border-radius:5px;object-fit:contain;flex:0 0 auto;display:inline-block;vertical-align:middle}
.pb-best-price .bp-mono{display:inline-flex;align-items:center;justify-content:center;border-radius:5px;font-weight:700;letter-spacing:.2px;color:hsl(var(--h) 70% 74%);background:hsl(var(--h) 45% 16%);border:1px solid hsl(var(--h) 50% 30%);flex:0 0 auto;line-height:1}
.pb-best-price .bp-pill{display:inline-block;padding:4px 10px;border-radius:999px;font-size:11px;font-weight:700;letter-spacing:.2px;white-space:nowrap;border:1px solid transparent}
.pb-best-price .bp-pill.great{background:rgba(var(--theme-accent-rgb,0,211,189),.16);color:var(--theme-accent-bright,#00F5D4);border-color:rgba(var(--theme-accent-rgb,0,211,189),.45)}
.pb-best-price .bp-pill.fair{background:rgba(255,255,255,.06);color:rgba(255,255,255,.7);border-color:rgba(255,255,255,.1)}
.pb-best-price .bp-pill.over{background:rgba(239,68,68,.14);color:#f87171;border-color:rgba(239,68,68,.35)}
.pb-best-price .bp-chev{appearance:none;width:24px;height:24px;border:0;border-radius:6px;background:transparent;color:rgba(255,255,255,.35);cursor:pointer;padding:0;display:inline-flex;align-items:center;justify-content:center}
.pb-best-price .bp-chev:hover{color:var(--theme-accent-bright,#00F5D4);background:rgba(var(--theme-accent-rgb,0,211,189),.1)}
.pb-best-price .bp-chev svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .15s}
.pb-best-price tr.is-open .bp-chev svg{transform:rotate(180deg)}
.pb-best-price .bp-empty{padding:40px;text-align:center;color:rgba(255,255,255,.5)}
.pb-best-price .bp-morerow td{height:auto;padding:8px 0 10px;border:0;background:transparent}   /* the button sits on the page, not on a card */
.pb-best-price .bp-more{padding:7px 18px;border-radius:999px;border:1px solid rgba(var(--theme-accent-rgb,0,211,189),.35);background:rgba(var(--theme-accent-rgb,0,211,189),.08);color:var(--theme-accent-bright,#00F5D4);font-weight:600;font-size:12px;cursor:pointer}
.pb-best-price .bp-more:hover{background:rgba(var(--theme-accent-rgb,0,211,189),.18)}

/* ---- game header rows: chevron, away @ home with logos, the kickoff chip; sticky under the book row ---- */
.pb-best-price .bp-table tr.bp-game td{position:sticky;top:var(--bp-th);z-index:5;height:50px;padding:0 10px 0 8px;text-align:left;background:#171717;border:0;border-bottom:6px solid var(--theme-bg-base,#000);background-clip:padding-box;border-radius:12px;cursor:pointer}
.pb-best-price .bp-table tr.bp-game:hover td{background:#1e1e1e}

.pb-best-price .bp-gh{display:flex;align-items:center;gap:12px}
.pb-best-price .bp-gchev{color:rgba(255,255,255,.5)}
.pb-best-price tr.bp-game .bp-gchev svg{transform:rotate(180deg)}
.pb-best-price tr.bp-game.is-closed .bp-gchev svg{transform:none}
.pb-best-price .bp-gteams{display:inline-flex;align-items:center;gap:7px;font-size:15px;font-weight:700;letter-spacing:.3px;color:rgba(255,255,255,.95)}
.pb-best-price .bp-glogo{width:26px;height:26px;object-fit:contain}
.pb-best-price .bp-gat{font-weight:500;color:rgba(255,255,255,.4);margin:0 2px}
.pb-best-price .bp-kick{display:inline-flex;align-items:center;padding:5px 12px;border-radius:999px;font-size:12px;font-weight:500;color:rgba(255,255,255,.8);background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.14);white-space:nowrap}
.pb-best-price .bp-gcount{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:12px;font-weight:500;color:rgba(255,255,255,.55);white-space:nowrap}
.pb-best-price .bp-gcount svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.pb-best-price tr.bp-game.is-closed .bp-gcount svg{transform:rotate(-90deg)}

/* ---- block expansion: the ladder is more rows of the open block, on the board's own columns (the sticky book header labels them) ---- */
.pb-best-price .bp-table tr.bp-lr td{height:36px;background:rgba(255,255,255,.04);border-top:0;border-bottom:0;border-radius:0}
.pb-best-price .bp-table tr.bp-lr.lead td{border-top:1px solid rgba(255,255,255,.09)}
.pb-best-price .bp-table tr.bp-lr.first:not(.lead) td{border-top:1px solid rgba(255,255,255,.06)}
.pb-best-price .bp-table tr.bp-lr.is-main td{background:rgba(255,255,255,.06)}
.pb-best-price .bp-table tr.bp-lr td.cell-id{border-left:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-table tr.bp-lr td.cell-chev{border-right:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-table tr.bp-lr td.cell-bk{padding:2px 2px}
.pb-best-price .bp-table tr.bp-lr .bp-fairv{font-size:13px;font-weight:600;color:rgba(255,255,255,.78)}
/* the identity column carries the line's best price (with its book) and the typical price */
.pb-best-price .bp-lmeta{display:flex;align-items:center;gap:12px;font-size:12.5px;font-variant-numeric:tabular-nums;white-space:nowrap}
.pb-best-price .bp-lbest{display:inline-flex;align-items:center;gap:5px;color:rgba(255,255,255,.92);font-weight:700}
.pb-best-price .bp-lbest .bp-bk,.pb-best-price .bp-lbest .bp-mono{border-radius:4px}
.pb-best-price .bp-ltyp{color:rgba(255,255,255,.45);font-weight:400}
.pb-best-price .bp-ll{display:flex;align-items:center;gap:8px;font-weight:400;font-size:13.5px;color:rgba(255,255,255,.85);white-space:nowrap;min-width:0;overflow:hidden}
.pb-best-price .bp-ll>span{overflow:hidden;text-overflow:ellipsis}
.pb-best-price .bp-ll .bp-mainpill{flex:0 0 auto}
.pb-best-price .bp-ll.is-main{color:#fff;font-weight:600}
.pb-best-price .bp-ll .bp-ladd,.pb-best-price .bp-ll .bp-ghost{flex:0 0 auto;padding:4px 8px;font-size:10px}
.pb-best-price .bp-ghost{visibility:hidden;font-style:normal}
.pb-best-price .bp-mainpill{display:inline-block;padding:2px 6px;border-radius:999px;font-size:9.5px;font-style:normal;font-weight:600;letter-spacing:.2px;background:rgba(255,255,255,.06);color:rgba(255,255,255,.6);border:1px solid rgba(255,255,255,.14);line-height:1.3}
.pb-best-price svg.bp-ext{position:absolute;top:3px;right:3px;width:9px;height:9px;fill:none;stroke:currentColor;stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;opacity:.42;pointer-events:none}
.pb-best-price a.bp-c:hover .bp-ext,.pb-best-price a.bp-cell:hover .bp-ext{opacity:1}
.pb-best-price .bp-c.best .bp-ext,.pb-best-price .bp-cell.best .bp-ext{opacity:.7}
/* gold: the tap lands with the bet already in the slip */
.pb-best-price .bp-c.slip .bp-ext,.pb-best-price .bp-cell.slip .bp-ext{stroke:#f5c042;opacity:.9}
.pb-best-price a.bp-c.slip:hover .bp-ext,.pb-best-price a.bp-cell.slip:hover .bp-ext{opacity:1}
/* a ladder cell: a boxed price; the best price on the line is the outlined one; nothing offered is a dash */
.pb-best-price .bp-cell{position:relative;display:flex;align-items:center;justify-content:center;height:30px;border-radius:8px;background:linear-gradient(180deg,rgba(255,255,255,.07) 0%,rgba(255,255,255,.03) 100%);border:1px solid rgba(255,255,255,.05);color:rgba(255,255,255,.84);text-decoration:none;font-weight:500;white-space:nowrap;min-width:0}
.pb-best-price a.bp-cell:hover{background:rgba(var(--theme-accent-rgb,0,211,189),.10);border-color:rgba(var(--theme-accent-rgb,0,211,189),.45)}
.pb-best-price .bp-cell.empty{background:transparent;border-color:transparent}
.pb-best-price .bp-cell.empty::before{content:"–";color:rgba(255,255,255,.2)}
.pb-best-price .bp-cell.best{background:linear-gradient(180deg,rgba(var(--theme-accent-rgb,0,211,189),.12) 0%,rgba(var(--theme-accent-rgb,0,211,189),.04) 100%);border-color:rgba(var(--theme-accent-rgb,0,211,189),.75);color:var(--theme-accent-bright,#00F5D4);font-weight:600}
.pb-best-price .bp-cell.best.lone{border-color:rgba(var(--theme-accent-rgb,0,211,189),.4)}
.pb-best-price .bp-cell.tiny{color:rgba(255,255,255,.4)}
.pb-best-price .bp-cell.mine{box-shadow:inset 0 0 0 1px rgba(255,255,255,.14)}
/* the foot of an open block: "More lines" and the card's bottom corners */
.pb-best-price .bp-table tr.bp-expand td{padding:0 12px 8px;background:rgba(255,255,255,.04);text-align:left;height:auto;border:1px solid rgba(255,255,255,.10);border-top:0;border-radius:0 0 12px 12px}
.pb-best-price .bp-table tr.bp-expand td:empty{padding-bottom:6px}
/* the open block loses its bottom edge: its rows and the ladder read as one card */
.pb-best-price .bp-table tr.is-open td.cell-id{border-bottom:0;border-radius:12px 0 0 0}
.pb-best-price .bp-table tr.is-open td.cell-chev{border-bottom:0;border-radius:0 12px 0 0}
.pb-best-price .bp-table tr.is-open.last td{border-bottom:0}
.pb-best-price .bp-lines{appearance:none;margin:6px 0 0;padding:5px 12px;border-radius:999px;border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.04);color:rgba(255,255,255,.8);font-size:12px;font-weight:600;cursor:pointer}
.pb-best-price .bp-lines:hover{background:rgba(var(--theme-accent-rgb,0,211,189),.12);border-color:rgba(var(--theme-accent-rgb,0,211,189),.45);color:var(--theme-accent-bright,#00F5D4)}

/* ---- game blocks: both team logos where a player would be ---- */
.pb-best-price .bp-avatar-game{display:inline-flex;align-items:center;justify-content:center;overflow:hidden}
.pb-best-price .bp-avatar-game img{width:58%;height:58%;object-fit:contain;flex:0 0 auto}
.pb-best-price .bp-avatar-game img:first-child{margin-right:-12%}
.pb-best-price .bp-avatar-game img:last-child{margin-left:-12%}

/* ---- the phone list (#bp-mobile, shown under 640px): group headers, then one card per block ---- */
.pb-best-price .bp-mg{display:flex;align-items:center;gap:10px;min-height:56px;padding:6px 0;cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent}
.pb-best-price .bp-mg+.bp-mg{border-top:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-mg-l{display:flex;align-items:center;gap:10px;min-width:0;flex:1 1 auto}
.pb-best-price .bp-mg-logos{display:inline-flex;align-items:center;gap:2px;flex:0 0 auto}
.pb-best-price .bp-mg-t{display:flex;flex-direction:column;min-width:0;line-height:1.2}
.pb-best-price .bp-mg-name{display:inline-flex;align-items:center;gap:6px;font-size:16px;font-weight:700;color:rgba(255,255,255,.95);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pb-best-price .bp-mg-name .bp-gat{font-size:14px}
.pb-best-price .bp-mg-sub{font-size:12px;font-weight:400;color:rgba(255,255,255,.5);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}
.pb-best-price .bp-mg-n{font-size:12px;font-weight:500;color:rgba(255,255,255,.55);white-space:nowrap;flex:0 0 auto}
.pb-best-price .bp-mg-chev{width:40px;height:40px;border:1px solid rgba(255,255,255,.12);border-radius:10px;background:rgba(255,255,255,.04);color:rgba(255,255,255,.6);flex:0 0 auto}
.pb-best-price .bp-mg-chev svg{width:16px;height:16px;transform:rotate(180deg)}
.pb-best-price .bp-mg.is-closed .bp-mg-chev svg{transform:none}
/* ---- THE COLLAPSED ROW (2026-09-11, spec §1). It replaces .bp-mc, the card
   that carried a header, a line per side and an "All books · N" bar — 134pt of
   screen for one bet. One row, one line high, and THE ROW IS THE DISCLOSURE
   CONTROL: role="button" tabindex="0" aria-expanded, driven by the click and
   keydown handlers on #bp-mobile (bestPricePage.js).
   The .bp-mc* and .bp-mf* rules that used to live here are gone with it —
   nothing emits those class names any more. .bp-mg* (the By game group header)
   and everything from .bp-ml down is KEPT: the expansion still uses it. ---- */
.pb-best-price .bp-mrow{display:flex;align-items:center;gap:10px;min-height:60px;padding:8px 0;border-bottom:1px solid rgba(255,255,255,.09);cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent}
.pb-best-price .bp-mrow:focus-visible{outline:2px solid var(--theme-accent-bright,#00F5D4);outline-offset:-2px}
.pb-best-price .bp-mrow.is-open{background:rgba(255,255,255,.05);border-bottom-color:transparent}
/* the "Great price" mark: the words live in the expansion (there is no room for
   them on a 60pt row), so the closed row says it with a teal edge instead */
.pb-best-price .bp-mrow.is-great{box-shadow:inset 3px 0 0 rgba(var(--theme-accent-rgb,0,211,189),.75)}
.pb-best-price .bp-mrank{flex:0 0 auto;text-align:right;font-variant-numeric:tabular-nums;font-weight:600;color:rgba(255,255,255,.38);line-height:1}
.pb-best-price .bp-mrow .bp-avatar{flex:0 0 auto}
.pb-best-price .bp-mrow-t{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;line-height:1.2}
.pb-best-price .bp-mrow-name{font-size:15px;font-weight:600;color:rgba(255,255,255,.95);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.pb-best-price .bp-mrow-sub{display:flex;align-items:center;gap:6px;min-width:0;margin-top:2px;font-size:12px;color:rgba(255,255,255,.58);white-space:nowrap}
.pb-best-price .bp-mrow-line{flex:0 0 auto;color:rgba(255,255,255,.82);font-weight:500}
/* only the trailing context (bet type / matchup) is allowed to truncate: the
   line itself is the bet and must never be half a number */
.pb-best-price .bp-mrow-mk{min-width:0;overflow:hidden;text-overflow:ellipsis}
/* the matchup chip: gameTag() has only ever emitted this class, never had a
   rule of its own, and on the flat feed it is the only thing naming the game.
   IT SHRINKS (flex:0 1). gameTag's string is "NE @ SEA · Wed 8:20 PM" and it is
   SHARED with the desktop gap view, so the phone cannot shorten the text — only
   let it give way. The sub line's priority is: the bet never truncates
   (.bp-mrow-line is flex:0 0), then the chip and the bet type shrink in
   proportion to their own width, which puts nearly all of the loss on the long
   kickoff and leaves the matchup and the market word both readable. */
.pb-best-price #bp-mobile .bp-gtag{flex:0 1 auto;min-width:0;overflow:hidden;text-overflow:ellipsis;padding:1px 6px;border:1px solid rgba(255,255,255,.14);border-radius:5px;font-size:10px;font-weight:600;letter-spacing:.2px;color:rgba(255,255,255,.5)}
.pb-best-price .bp-mchev{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;color:rgba(255,255,255,.35)}
.pb-best-price .bp-mchev svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;transition:transform .15s}
.pb-best-price .bp-mrow.is-open .bp-mchev{color:rgba(255,255,255,.8)}
.pb-best-price .bp-mrow.is-open .bp-mchev svg{transform:rotate(180deg)}
.pb-best-price .bp-ml{display:flex;align-items:center;gap:10px;height:48px;padding:0 12px;border-top:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-ml-meta{display:flex;align-items:center;gap:6px;min-width:0}
.pb-best-price #bp-mobile .bp-add{flex:0 0 auto;min-width:56px;height:40px;padding:0 10px;font-size:11px}
.pb-best-price .bp-ml-t{flex:1 1 auto;min-width:0;display:flex;flex-direction:column;line-height:1.2}
.pb-best-price .bp-ml-lbl{font-size:14px;font-weight:500;color:rgba(255,255,255,.92);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0}
.pb-best-price .bp-ml-t .bp-ml-fair{margin-top:2px}
.pb-best-price .bp-ml .bp-spacer,.pb-best-price .bp-mo-r .bp-spacer{min-width:4px}
.pb-best-price .bp-ml-fair{font-size:11px;font-weight:500;color:rgba(255,255,255,.5);white-space:nowrap;font-variant-numeric:tabular-nums;flex:0 0 auto}
.pb-best-price .bp-mb{position:relative;display:inline-flex;align-items:center;justify-content:center;gap:6px;width:96px;height:40px;border-radius:8px;flex:0 0 auto;background:linear-gradient(180deg,rgba(255,255,255,.07) 0%,rgba(255,255,255,.03) 100%);border:1px solid rgba(255,255,255,.08);color:rgba(255,255,255,.86);text-decoration:none;font-weight:600;font-size:14px;font-variant-numeric:tabular-nums;white-space:nowrap;line-height:1}
.pb-best-price .bp-mb.best{background:linear-gradient(180deg,rgba(var(--theme-accent-rgb,0,211,189),.12) 0%,rgba(var(--theme-accent-rgb,0,211,189),.04) 100%);border-color:rgba(var(--theme-accent-rgb,0,211,189),.75);color:var(--theme-accent-bright,#00F5D4);font-weight:700}
.pb-best-price .bp-mb.empty{color:rgba(255,255,255,.3);font-weight:400}
.pb-best-price .bp-mb.sm{width:84px;font-size:13px}
.pb-best-price .bp-mb .bp-bk,.pb-best-price .bp-mb .bp-mono{border-radius:4px}
.pb-best-price .bp-mb .bp-ext{top:2px;right:2px;width:8px;height:8px;opacity:.7}
.pb-best-price .bp-mb.slip .bp-ext,.pb-best-price .bp-mk.slip .bp-ext{stroke:#f5c042;opacity:.9}
/* the expansion, now a SIBLING of the row it belongs to (it used to be the tail
   of the .bp-mc card): it sits directly under the row so the tapped row does
   not move, and it closes the block with its own bottom border */
.pb-best-price .bp-mx{padding:2px 12px 12px;background:rgba(255,255,255,.035);border-bottom:1px solid rgba(255,255,255,.09)}
.pb-best-price .bp-mx .bp-ml:first-child{border-top:0}
/* the line the collapsed row was showing: the user tapped that price, so it is
   marked here rather than reordered to the top (ladderOf() marks "Main line"
   off b.lines[0], and resorting the lines would desync the pill from its rung) */
.pb-best-price .bp-ml.is-lead .bp-ml-lbl{color:#fff;font-weight:700}
.pb-best-price .bp-mx-lbl{font-size:11px;font-weight:600;letter-spacing:.5px;text-transform:uppercase;color:rgba(255,255,255,.45);margin:12px 0 6px}
.pb-best-price .bp-mx-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(96px,1fr));gap:6px}
.pb-best-price .bp-mk{position:relative;display:flex;align-items:center;justify-content:center;gap:6px;height:44px;border-radius:8px;background:linear-gradient(180deg,rgba(255,255,255,.07) 0%,rgba(255,255,255,.03) 100%);border:1px solid rgba(255,255,255,.06);color:rgba(255,255,255,.86);text-decoration:none;font-weight:500;font-size:13px;font-variant-numeric:tabular-nums;white-space:nowrap;min-width:0;line-height:1}
.pb-best-price .bp-mk-v{display:flex;flex-direction:column;align-items:center;gap:3px}
.pb-best-price .bp-mk .bp-cap{font-size:9px;font-weight:400;color:rgba(255,255,255,.5);line-height:1}
.pb-best-price .bp-mk.own{color:rgba(255,255,255,.62)}
.pb-best-price .bp-mk.tiny{color:rgba(255,255,255,.38)}
.pb-best-price .bp-mk.best{background:linear-gradient(180deg,rgba(var(--theme-accent-rgb,0,211,189),.12) 0%,rgba(var(--theme-accent-rgb,0,211,189),.04) 100%);border-color:rgba(var(--theme-accent-rgb,0,211,189),.75);color:var(--theme-accent-bright,#00F5D4);font-weight:700}
.pb-best-price .bp-mk.best .bp-cap{color:rgba(0,245,212,.8)}
.pb-best-price .bp-mk .bp-ext{opacity:.7}
.pb-best-price .bp-mo-r{display:flex;align-items:center;gap:10px;height:44px}
.pb-best-price .bp-mo-r.first{border-top:1px solid rgba(255,255,255,.10)}
.pb-best-price .bp-mx-lbl+.bp-mo-r{border-top:0}
.pb-best-price .bp-mo-sp{width:56px;flex:0 0 auto}
.pb-best-price .bp-mo-lbl{display:inline-flex;align-items:center;gap:6px;font-size:13px;color:rgba(255,255,255,.85);white-space:nowrap;min-width:0}
.pb-best-price .bp-mo-lbl>span{overflow:hidden;text-overflow:ellipsis}
.pb-best-price .bp-mo-lbl .bp-mainpill{flex:0 0 auto}
.pb-best-price .bp-mo-r.is-main .bp-mo-lbl{color:#fff;font-weight:600}
.pb-best-price .bp-mo .bp-lines{display:block;width:100%;height:40px;margin:8px 0 0;padding:0 14px}
.pb-best-price .bp-m-more{padding:2px 0 12px;text-align:center}
.pb-best-price .bp-m-more .bp-more{height:40px;padding:0 20px}
.pb-best-price #bp-mobile .bp-empty{padding:40px 12px}

/* ==== the phone layer: the app's compact breakpoint, authored at 2.513x ====
   RE-AUTHORED 2026-09-11 (founder call; docs/… direction 04). It used to be
   the demo's <=640px block scaled ~1.8x, and that was the wrong constant.

   *** THE ARITHMETIC, AND WHY EVERY NUMBER BELOW LOOKS TOO BIG ***
   The shell ships NO viewport meta (templates/index.html), so a phone lays
   this board out on the browser's default 980px canvas and scales the whole
   thing to fit. On an iPhone 15 that is 390/980 = 0.398x, so an authored N px
   lands at 0.398 * N PHYSICAL pt. _site_footer.html:45-48 states the same
   conversion and sizes its own links to 110px for exactly this reason:

       authored px = physical pt * 2.513          (1 pt = 2.513 authored px)

   At 1.8x every tap target on this board was 72px = 28.7pt, against Apple's
   44pt floor and Material's 48dp — measured on the shipped board, all ten
   control classes at once. 44pt is 110.6 authored px, so the control height
   is 110 and the multiplier off the old sizes is 110/72 = 1.528.

   HOW THE REST WAS SIZED — not by multiplying everything by 1.528:
     * HEIGHTS of anything a thumb hits go to the 44pt floor (110px) or above:
       the segmented bar, Great prices, every market chip, ADD, the best-price
       box, the group chevron, Retry, "More lines", "Show more". The rows that
       CONTAIN a 110px control get 120-130px so the control is not wedged.
     * TYPE moves to the pt ramp below, ~1.4x, because 9.6pt body text is the
       real complaint — not 1.528x, which would have overshot into 15pt
       captions. The fair and the gap were the two smallest strings on the
       card at 8.0pt; they are 11.1pt now.
     * WIDTHS grow only as far as the type inside them needs. The best-price
       box was 172px/68.4pt — already well clear of the 44pt floor in its
       short dimension — so it goes to 190px purely to fit the 38px price
       beside a 44px book mark, not to 263px.
   The pt ramp these round numbers come from (px = pt * 2.513, rounded):
     8pt 20 · 9pt 23 · 10pt 25 · 11pt 28 · 12pt 30 · 13pt 33 · 14pt 35
     15pt 38 · 16pt 40 · 17pt 43 · 18pt 45 · 20pt 50 · 24pt 60 · 28pt 70
     32pt 80 · 36pt 90 · 40pt 100 · 44pt 110 · 48pt 120
   The IMAGE sizes are the same convention but live in JS, because the board
   writes them as inline width/height: bestPricePage.js' `M` map.

   The page scrolls, the board loses its fixed height, the controls stack:
   the segmented bar with Great prices, the bet-type chips as one scrolling
   strip, the search full width with the freshness line under it. ---- */
@media (max-width: 1000px) {
  .pb-best-price {
    --bp-top: 108px;
    font-size: 35px;
    /* the fixed filter bar's own height: 110px of control + 18px of padding
       either side. Named because THREE rules need the same number — the bar,
       its right-edge fade, and the bottom padding that keeps the bar off the
       last row of the feed. */
    --bp-bar: 146px;
  }
  .pb-best-price #bp-section { padding: 0 30px; margin-top: 16px; }
  .pb-best-price #bp-board { min-height: 0; max-height: none; }

  /* ---- CONTROLS MOVE TO THE THUMB ZONE (2026-09-11, spec §3) ----
     Everything used to sit in the top 156pt — the hardest reach on a phone —
     while the fixed bottom nav owned the reachable zone and said nothing about
     the board. Now: a small header at the top (title · freshness · search),
     and one fixed, horizontally scrollable filter strip pinned directly above
     the bottom nav.
     HOW: #bp-controls goes display:contents so its three children become flex
     items of #bp-board and can be ordered independently — the header and the
     freshness line above the list, the filter strip out of flow entirely.
     #bp-controls keeps its box on desktop, and [hidden] beats display:contents
     (.pb-best-price [hidden]{display:none!important}), so _showState()'s
     `ctl.hidden = true` still takes the whole thing down. */
  .pb-best-price #bp-controls { display: contents; }
  .pb-best-price .bp-mtop { order: -3; display: flex; align-items: center; gap: 18px; padding: 12px 0 4px; }
  .pb-best-price .bp-mtop-t { font-size: 42px; font-weight: 700; letter-spacing: -.3px; color: rgba(255,255,255,.96); }
  .pb-best-price .bp-mq { appearance: none; flex: 0 0 auto; width: 110px; height: 110px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid rgba(255,255,255,.12); border-radius: 26px; background: rgba(255,255,255,.04); color: rgba(255,255,255,.7); cursor: pointer; padding: 0; }
  .pb-best-price .bp-mq svg { width: 42px; height: 42px; fill: none; stroke: currentColor; stroke-width: 2; }
  .pb-best-price #bp-controls.is-search .bp-mq { border-color: rgba(var(--theme-accent-rgb,0,211,189),.6); color: var(--theme-accent-bright,#00F5D4); }

  /* THE FIXED FILTER STRIP. bottom: the shell's own clearance expression for
     the bottom nav — body.best-price-active uses exactly this to clear it
     (core/bottom-nav.css owns --pb-bottom-nav-height: 124px at this
     breakpoint; the 124 here is only the fallback for a page that somehow
     loads this sheet without that one).
     The three control groups join ONE scroll row by dropping their own boxes
     (display:contents) so their buttons become flex items of this strip; order
     puts the two segmented buttons first, Great prices next, the bet types
     after. Without that the strip would be three independent scrollers. */
  .pb-best-price .bp-board-head {
    position: fixed; left: 0; right: 0; z-index: 40;
    bottom: calc(var(--pb-bottom-nav-height, 124px) + max(env(safe-area-inset-bottom), 12px));
    display: flex; align-items: center; gap: 18px;
    height: var(--bp-bar); padding: 0 30px;
    background: linear-gradient(180deg, rgba(0,0,0,0) 0%, #000 34%, #000 100%);
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; scrollbar-width: none;
  }
  .pb-best-price .bp-board-head::-webkit-scrollbar { display: none; }
  /* THE SCROLL AFFORDANCE the old strip never had: it is ~2.05 canvas-widths
     wide with the scrollbar suppressed, so half the bet types were simply
     undiscoverable. A fade over the right edge, clipping the chip under it,
     is the signal. position:fixed because the ::after of a scroller scrolls
     away with its content; pointer-events:none so it never eats a chip tap. */
  .pb-best-price .bp-board-head::after {
    content: ''; position: fixed; pointer-events: none; z-index: 41;
    right: 0; width: 90px; height: var(--bp-bar);
    bottom: calc(var(--pb-bottom-nav-height, 124px) + max(env(safe-area-inset-bottom), 12px));
    background: linear-gradient(90deg, rgba(0,0,0,0) 0%, #000 78%);
  }
  .pb-best-price .bp-viewgrp { display: contents; }
  .pb-best-price .bp-view { display: contents; }
  .pb-best-price .bp-seg { order: 1; flex: 0 0 auto; height: 110px; padding: 0 34px; font-size: 33px; border-radius: 999px; }
  .pb-best-price .bp-view .bp-seg-value { display: inline-flex; align-items: center; }
  .pb-best-price .bp-view [data-sort="gap"] { display: none; }   /* desktop's flat view; the phone's is "Best value" */
  .pb-best-price #bp-great { order: 2; height: 110px; padding: 0 34px; font-size: 33px; flex: 0 0 auto; }
  .pb-best-price #bp-markets { display: contents; }
  .pb-best-price #bp-markets .bp-chip { order: 3; height: 110px; padding: 0 34px; font-size: 33px; flex: 0 0 auto; border-radius: 999px; }

  /* THE SEARCH, revealed. It takes the header's place at the TOP of the board,
     not a slot in the strip: a position:fixed element anchored to the bottom of
     the layout viewport is exactly what the iOS keyboard covers, so a search
     box down there disappears the instant it is focused. Fixed, not in flow,
     because .bp-head-right's parent is the fixed strip — a fixed child of a
     fixed parent is still positioned against the viewport, and an ancestor's
     overflow does not clip it (its containing-block chain does not pass
     through the scroller). #bp-fold stays display:none through all of it. */
  .pb-best-price .bp-head-right { display: none; }
  .pb-best-price #bp-controls.is-search .bp-mtop { display: none; }
  .pb-best-price #bp-controls.is-search .bp-head-right {
    display: flex; position: fixed; z-index: 45;
    top: calc(var(--bp-top) + env(safe-area-inset-top, 0px));
    left: 0; right: 0; padding: 12px 30px 4px; background: #000;
  }
  .pb-best-price .bp-search { width: 100%; }
  .pb-best-price .bp-search svg { left: 34px; width: 40px; height: 40px; }
  .pb-best-price .bp-search input { width: 100%; height: 110px; padding: 0 30px 0 94px; font-size: 35px; border-radius: 22px; }
  /* the freshness line, directly under the title row. It is #bp-live's own
     wrapper and the wrapper cannot move in the markup (the desktop suites pin
     it inside #bp-controls, right after .bp-board-head), so it is ordered
     there instead. And it RENDERS now — see the base rule's note. */
  .pb-best-price .bp-board-row2 { order: -2; justify-content: flex-start; margin: 0 0 8px; min-height: 0; }
  .pb-best-price .bp-live { font-size: 28px; padding: 0 2px; }
  .pb-best-price .bp-live::before { width: 16px; height: 16px; margin-right: 14px; }
  .pb-best-price .bp-fold { display: none; }
  .pb-best-price .bp-table-wrap { margin: 0 0 28px; }
  .pb-best-price .bp-state { padding: 100px 30px; font-size: 38px; }
  .pb-best-price .bp-state .bp-retry { margin-top: 34px; padding: 0 50px; height: 110px; line-height: 108px; font-size: 33px; }

  /* the feed. The bottom padding is the fixed strip's height plus a gap: the
     bar must not cover the last row (spec §3). */
  .pb-best-price #bp-mobile { padding: 0 30px calc(var(--bp-bar) + 30px); }
  .pb-best-price .bp-mg { gap: 22px; min-height: 140px; padding: 14px 0; }
  .pb-best-price .bp-mg-l { gap: 22px; }
  .pb-best-price .bp-mg-logos { gap: 6px; }
  .pb-best-price .bp-mg-name { font-size: 42px; gap: 14px; }
  .pb-best-price .bp-mg-name .bp-gat { font-size: 33px; }
  .pb-best-price .bp-mg-sub { font-size: 30px; margin-top: 6px; }
  .pb-best-price .bp-mg-n { font-size: 28px; }
  .pb-best-price .bp-mg-chev { width: 110px; height: 110px; border-radius: 26px; }
  .pb-best-price .bp-mg-chev svg { width: 40px; height: 40px; }
  /* THE COLLAPSED ROW — 150px = 60pt, the study's number and the whole point of
     the rebuild (the card it replaces was 134pt).
     The width budget on the 980 canvas, inside #bp-mobile's 30px gutters:
       920 - 45 rank - 110 avatar - 190 best box - 40 chevron - 4*22 gaps = 447
     for the name and its sub line. A 40px name at ~20px a glyph is ~22
     characters, which clears the longest names on either slate. In By game
     there is no rank, so it is 467. */
  .pb-best-price .bp-mrow { gap: 22px; min-height: 150px; padding: 14px 0; }
  .pb-best-price .bp-mrank { width: 45px; font-size: 26px; }
  .pb-best-price .bp-mrow .bp-avatar { width: 110px; height: 110px; }
  .pb-best-price .bp-mrow-name { font-size: 40px; }
  .pb-best-price .bp-mrow-sub { font-size: 30px; gap: 14px; margin-top: 6px; }
  .pb-best-price #bp-mobile .bp-gtag { font-size: 24px; padding: 3px 12px; border-radius: 8px; }
  .pb-best-price .bp-mchev { width: 40px; }
  .pb-best-price .bp-mchev svg { width: 36px; height: 36px; }
  .pb-best-price .bp-mrow.is-great { box-shadow: inset 7px 0 0 rgba(var(--theme-accent-rgb,0,211,189),.75); }
  .pb-best-price #bp-mobile .bp-gap { font-size: 26px; padding: 6px 16px; }
  .pb-best-price #bp-mobile .bp-gap.lo { padding: 0; }
  .pb-best-price #bp-mobile .bp-great { font-size: 24px; padding: 5px 14px; }
  /* 130 not 110: the row CONTAINS a 110px ADD and a 110px best box */
  .pb-best-price .bp-ml { gap: 22px; height: 130px; padding: 0 6px; }
  .pb-best-price .bp-ml-meta { gap: 14px; }
  .pb-best-price #bp-mobile .bp-add { min-width: 110px; height: 110px; padding: 0 22px; font-size: 26px; border-radius: 14px; }
  /* "Coming soon" wraps to two lines inside the same 110x110 box: the row does not jump */
  .pb-best-price #bp-mobile .bp-add.bp-soon { max-width: 110px; white-space: normal; line-height: 1.05; font-size: 21px; letter-spacing: 0; padding: 0 10px; }
  .pb-best-price .bp-ml-lbl { font-size: 35px; }
  .pb-best-price .bp-ml-t .bp-ml-fair { margin-top: 6px; }
  .pb-best-price .bp-ml-fair { font-size: 28px; }
  .pb-best-price .bp-mb { width: 190px; height: 110px; gap: 14px; border-radius: 18px; font-size: 38px; }
  .pb-best-price .bp-mb.sm { width: 165px; font-size: 33px; }
  .pb-best-price .bp-mb .bp-bk, .pb-best-price .bp-mb .bp-mono { border-radius: 9px; }
  .pb-best-price .bp-mb .bp-ext { top: 6px; right: 6px; width: 18px; height: 18px; }
  .pb-best-price .bp-mx { padding: 0 6px 24px; }
  .pb-best-price .bp-mx-lbl { font-size: 26px; margin: 28px 0 14px; }
  /* three across, the study's grid. Pinned rather than auto-filled: the chip
     count varies from 4 to 14 books per line and an auto-fill grid would
     re-flow the expansion's height every time the filter changed. */
  .pb-best-price .bp-mx-grid { grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 6px; }
  .pb-best-price .bp-mk { height: 120px; gap: 14px; border-radius: 18px; font-size: 33px; }
  .pb-best-price .bp-mk-v { gap: 7px; }
  .pb-best-price .bp-mk .bp-cap { font-size: 22px; }
  .pb-best-price .bp-mk .bp-ext { width: 22px; height: 22px; top: 7px; right: 7px; }
  .pb-best-price .bp-mo-r { gap: 22px; height: 120px; }
  .pb-best-price .bp-mo-sp { width: 110px; }
  .pb-best-price .bp-mo-lbl { font-size: 33px; gap: 14px; }
  .pb-best-price #bp-mobile .bp-mainpill { font-size: 24px; padding: 6px 14px; }
  .pb-best-price .bp-mo .bp-lines { height: 110px; margin: 20px 0 0; padding: 0 34px; font-size: 31px; }
  .pb-best-price .bp-m-more { padding: 6px 0 28px; }
  .pb-best-price .bp-m-more .bp-more { height: 110px; padding: 0 50px; font-size: 31px; }
  .pb-best-price #bp-mobile .bp-empty { padding: 100px 30px; font-size: 35px; }
}

/* ---- the hovered book column, and the row's cheapest price ---- */
.pb-best-price .bp-table td.cell-bk.bp-col-hot{background:rgba(255,255,255,.11)}
.pb-best-price .bp-table th.col-book.bp-col-hot{background:#22262c}
/* the row's cheapest usable price: the mirror of .best, deliberately quieter — it is information, not an action */
.pb-best-price .bp-c.worst,.pb-best-price .bp-cell.worst,.pb-best-price .bp-mk.worst{background:linear-gradient(180deg,rgba(255,101,119,.10) 0%,rgba(255,101,119,.03) 100%);border-color:rgba(255,101,119,.42);color:#ff8b98}
.pb-best-price a.bp-c.worst:hover,.pb-best-price a.bp-cell.worst:hover,.pb-best-price a.bp-mk.worst:hover{background:rgba(255,101,119,.16);border-color:rgba(255,101,119,.6)}
.pb-best-price .bp-c.worst .bp-cap{color:rgba(255,139,152,.75)}

/* =============================================================================
   ==== END OF THE SHARED SHEET — SPORT OVERRIDES ONLY BELOW THIS MARKER ====

   Everything above is sport-agnostic and is what BOTH boards get. Anything that
   must differ per sport hangs off a modifier the adapter puts on the root
   (bestPricePage.js render(): root.className gets sport.rootClass — NFL
   'pb-best-price', MLB 'pb-best-price pb-best-price--mlb'), exactly the way
   core/pickem.css carries .pb-pickem--nfl.

   WHY THE MARKER EXISTS, and why you must not append above it: the phone layer
   — @media (max-width: 1000px) — is NOT at the end of this file. It is in the
   middle (:362-436), with three deliberate rules after it at :438-444 (the
   hovered book column and the .worst price) because those are shared rules
   that were meant to win. A sport override dropped in "at the bottom" of the
   file without noticing that lands AFTER the media block and, at equal
   specificity, beats the phone layout it never meant to touch: the symptom is
   a phone board that silently renders at desktop sizes for one sport only.
   Put every sport rule here, under the modifier, and that cannot happen — the
   modifier adds a class, so it outranks the base rule by specificity rather
   than by source order.

   DELIBERATELY EMPTY as of 2026-09-10. The MLB board needed no override on the
   first pass: same markup, same 44px avatars, same columns; the only two things
   that differ are behind JS (the logo folder is mlb-team-logos-v2 and the team
   nickname comes from teamMappingsMLB, both adapter fields — mlb/bestPrice/index.js).
   Leaving the section here rather than adding it with the first override is the
   point: the next person has a labelled place to put one.
   ============================================================================= */
