/* ==========================================================================
   search.css — the phone Search screen (/nfl/search, /mlb/search).
   docs/phone-search-tab-implementation.md

   Authored for the app's 980px phone canvas (the shell ships no viewport
   meta, so a phone lays out 980px and scales it ~0.398). Every length below
   is CANVAS px: glass pt = px / 2.513.

   NO MEDIA QUERY, on purpose. window.PBSearchView refuses to mount unless
   matchMedia('(max-width: 1000px)') matches, and it tears itself down (and
   rewrites the URL onto the workbench) the moment a resized window leaves
   that layer — so `.pb-search` cannot exist at a desktop width and these
   flat canvas px can never paint there. The one rule that is NOT scoped to
   the view is the display gate at the bottom of section 0, and that one is
   keyed to a body class the same teardown removes.

   Values are copied from the app, not invented:
     field            best-price.css:126-130 + :580-587   (#bp-q phone)
     pills            nfl-game-detail.css:2834-2877       (.ngd-posrow phone)
     rows             best-price.css:335-352 + :627-634   (.bp-mrow phone)
     cards / tokens   nfl-game-detail.css:26-76, :2320-2332, :1823-1828
     group heading    nfl-game-detail.css:1284-1294 + :2314-2319
     back link        nfl-game-detail.css:175-200 + :1839-1851
     injury badge     nfl-game-detail.css:1669-1682 + :2438-2442
     chassis gutter   nfl-game-detail.css:1804-1811 (35px side, 200px bottom)

   Namespace: pb-search-* only. The card and panel hosts carry their OWN ids
   (#nfl-search-card / #mlb-search-card, #nfl-search-defense /
   #mlb-search-matchup) — never the game pages' — because the card sheets
   beside this one are generated copies scoped to exactly those ids.
   ========================================================================== */

/* ── 0. the view's display gate ──────────────────────────────────────────
   LOAD-BEARING. The Search view is in-shell, so the workbench columns and
   the schedule list are still in the document underneath it. Hiding them
   inline does not hold: the Research hub re-shows #content-section on every
   pushState, and this view pushes one per screen. A body class the view
   owns is the only hide that survives that. */
body.pb-search-active #content-section,
body.pb-search-active #schedule-container { display: none !important; }

/* The view's container. `.main-content` is a horizontal flex ROW
   (style.css:235), so a child with no flex of its own gets sized by its
   content — the same claim #nfl-game-detail makes at nfl-game-detail.css:100.
   The element only exists while the view is mounted (searchCore creates it on
   mount and REMOVES it on teardown), so this rule is inert everywhere else. */
#pb-search-root {
  /* At least one viewport tall, whatever the screen holds: a one-row result
     used to leave the site footer (socials + links, ~800 canvas px, outside
     #main-app-wrapper) sitting mid-screen. The research hub's own rule
     (research-hub.css .research-hub-landing { min-height: 100vh }); the
     fixed bottom nav overlays the last ~150 px, so the footer starts just
     under the fold. */
  min-height: 100vh;
  flex: 1 1 auto;
  min-width: 0;
  width: 100%;
}

.pb-search {
  /* Accent comes from the theme, exactly as the game page's does
     (nfl-game-detail.css:27), so Search follows the user's palette. */
  --pbs-accent: rgb(var(--theme-accent-rgb, 0, 211, 189));
  --pbs-accent-06: rgba(var(--theme-accent-rgb, 0, 211, 189), .06);
  --pbs-accent-50: rgba(var(--theme-accent-rgb, 0, 211, 189), .50);
  --pbs-accent-75: rgba(var(--theme-accent-rgb, 0, 211, 189), .75);
  --pbs-ink-000: #000000;
  --pbs-ink-050: #0B0C0C;
  --pbs-ink-150: #1A1D1D;
  --pbs-line-soft: #171A1A;
  --pbs-line-hi: #2C3232;
  --pbs-sep: rgba(255, 255, 255, .09);
  --pbs-fg: #F2F5F4;
  --pbs-fg-dim: #8A9391;
  --pbs-fg-mute: #59615F;
  --pbs-fg-faint: #414847;
  --pbs-red: #FF5C5C;
  --pbs-green: #22C55E;                 /* the Last-5 hit bar's green (nfl-search-card.css) */
  --pbs-amber: #F5B544;
  --pbs-live: #FF3B47;
  --pbs-gut: 35px;

  /* body is Avenir 200 (style.css:151-158), so the family is set here —
     the same two stacks the game page declares (nfl-game-detail.css:68-72). */
  --pbs-sans: "Inter", "Inter Tight", -apple-system, BlinkMacSystemFont,
              "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --pbs-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  position: relative;
  min-height: 100%;
  background: var(--pbs-ink-000);
  color: var(--pbs-fg);
  font-family: var(--pbs-sans);
  font-weight: 400;
  font-size: 30px;
  line-height: 1.35;
  -webkit-font-smoothing: antialiased;
  /* clears the 124px bottom nav and the parlay pill that floats above it */
  padding-bottom: 200px;
}

.pb-search *,
.pb-search *::before,
.pb-search *::after { box-sizing: border-box; }

/* :where() keeps the reset at (0,1,0) so every component rule below, which is
   also (0,1,0) but comes later, still wins its own padding and alignment. */
.pb-search :where(button) {
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}

.pb-search :where(a) { color: inherit; text-decoration: none; }

.pb-search h2,
.pb-search h3,
.pb-search p { margin: 0; }

/* ── 1. sticky bar: field + position pills ───────────────────────────── */

.pb-search-bar {
  position: sticky;
  top: 0;
  z-index: 8;
  display: flex;
  flex-direction: column;
  gap: 22px;
  padding: 22px var(--pbs-gut) 24px;
  background: var(--pbs-ink-000);
  border-bottom: 1px solid var(--pbs-line-soft);
}

.pb-search-field {
  position: relative;
  display: flex;
  align-items: center;
}

.pb-search-field .pb-search-mag {
  position: absolute;
  left: 34px;
  width: 40px;
  height: 40px;
  fill: none;
  stroke: rgba(255, 255, 255, .45);
  stroke-width: 2;
  pointer-events: none;
}

.pb-search-field input {
  width: 100%;
  height: 110px;
  margin: 0;
  padding: 0 124px 0 94px;
  background: rgba(255, 255, 255, .04);
  border: 1px solid rgba(255, 255, 255, .12);
  border-radius: 22px;
  color: rgba(255, 255, 255, .95);
  font-family: inherit;
  /* 42, not 35: iOS zooms the page on focus when a text control's type
     lands under 16 physical px. On the 980 canvas a phone is 0.38-0.44x
     (375-430 wide), so 35px was 13.4-15.4 on glass and every tap into the
     field nudged the page in; 42px is 16.1 on the narrowest phone. */
  font-size: 42px;
  font-weight: 500;
  outline: none;
  -webkit-appearance: none;
  appearance: none;
  transition: border-color .15s, background .15s;
}

.pb-search-field input::placeholder { color: rgba(255, 255, 255, .4); }
.pb-search-field input::-webkit-search-cancel-button,
.pb-search-field input::-webkit-search-decoration { -webkit-appearance: none; display: none; }

.pb-search-field input:focus,
.pb-search-field.is-active input {
  background: var(--pbs-accent-06);
  border-color: var(--pbs-accent-50);
}

.pb-search-field.is-active .pb-search-mag { stroke: var(--pbs-accent-75); }

/* The clear button only exists once something is typed. */
.pb-search-x {
  position: absolute;
  right: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 110px;
  height: 110px;
  color: rgba(255, 255, 255, .55);
}
.pb-search-x svg { width: 38px; height: 38px; }
.pb-search-field input:placeholder-shown ~ .pb-search-x { display: none; }

/* .ngd-posrow, phone (nfl-game-detail.css:2834-2877) */
.pb-search-posrow {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 16px;
  width: 100%;
}
.pb-search--mlb .pb-search-posrow { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.pb-search-posrow button {
  position: relative;
  width: 100%;
  min-width: 0;
  min-height: 96px;
  padding: 0 8px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-hi);
  border-radius: 24px;
  color: var(--pbs-fg-dim);
  font-size: 29px;
  font-weight: 600;
  text-align: center;
  transition: color .14s, border-color .14s;
}
.pb-search--mlb .pb-search-posrow button { font-size: 26px; font-weight: 700; }

/* 96px box, 110px touch target */
.pb-search-posrow button::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -7px;
  bottom: -7px;
}
.pb-search-posrow button.is-on {
  border-color: var(--pbs-accent);
  color: var(--pbs-accent);
}

/* ── 2. sections ─────────────────────────────────────────────────────── */

.pb-search-body { display: block; }

.pb-search-sec { padding: 26px 0 8px; }
.pb-search-sec + .pb-search-sec { border-top: 1px solid var(--pbs-line-soft); }

.pb-search-h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  min-height: 78px;
  padding: 0 var(--pbs-gut);
}

/* .ngd-pgroup-h (nfl-game-detail.css:1284-1294 + :2314-2319) */
.pb-search-eyebrow {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--pbs-fg-mute);
}

.pb-search-act {
  position: relative;
  min-height: 78px;
  padding-left: 24px;
  color: var(--pbs-accent);
  font-size: 28px;
  font-weight: 600;
}
.pb-search-act::before {
  content: "";
  position: absolute;
  left: 0;
  right: -10px;
  top: -16px;
  bottom: -16px;
}

.pb-search-note {
  padding: 0 var(--pbs-gut) 18px;
  color: var(--pbs-fg-dim);
  font-size: 28px;
}

.pb-search-foot {
  padding: 26px var(--pbs-gut) 10px;
  color: var(--pbs-fg-mute);
  font-size: 28px;
}

/* ── 3. rows (.bp-mrow phone: best-price.css:335-352, :627-634) ──────── */

.pb-search-rows { display: block; }

.pb-search-row {
  display: flex;
  align-items: center;
  gap: 22px;
  width: 100%;
  min-height: 150px;
  padding: 14px var(--pbs-gut);
  border-bottom: 1px solid var(--pbs-sep);
  user-select: none;
}
.pb-search-rows .pb-search-row:last-child { border-bottom: 0; }
.pb-search-row:active { background: rgba(255, 255, 255, .05); }

.pb-search-row-t {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.pb-search-row-n {
  display: flex;
  align-items: center;
  min-width: 0;
  font-size: 40px;
  font-weight: 600;
  color: var(--pbs-fg);
}
.pb-search-row-nm {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pb-search-row-n em {
  font-style: normal;
  color: var(--pbs-accent);
}

.pb-search-row-s {
  margin-top: 8px;
  font-size: 30px;
  color: var(--pbs-fg-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pb-search-live { color: var(--pbs-live); font-weight: 700; }
.pb-search-final { color: var(--pbs-fg-mute); font-weight: 700; }

.pb-search-row-r {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  padding-left: 12px;
  text-align: right;
}
.pb-search-line {
  font-family: var(--pbs-mono);
  font-size: 36px;
  font-weight: 500;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--pbs-fg);
}
.pb-search-mkt { font-size: 26px; color: var(--pbs-fg-dim); }
.pb-search-noline { font-size: 28px; color: var(--pbs-fg-faint); }

.pb-search-chev {
  flex: 0 0 auto;
  display: inline-flex;
  width: 40px;
  color: rgba(255, 255, 255, .35);
}
.pb-search-chev svg { width: 36px; height: 36px; }

/* team row */
.pb-search-crestwrap {
  flex: none;
  display: grid;
  place-items: center;
  width: 110px;
  height: 110px;
  border-radius: 50%;
  background: var(--pbs-ink-150);
}
.pb-search-crestwrap img { width: 74px; height: 74px; object-fit: contain; }

/* ── 4. avatars (ngd-avatar: nfl-game-detail.css:2917-2945) ──────────── */

.pb-search-ava {
  position: relative;
  display: grid;
  place-items: center;
  flex: none;
  width: 110px;
  height: 110px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--pbs-ink-150);
}
.pb-search-ava img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  background: var(--pbs-ink-150);
}
/* MLB falls back to the team crest, exactly like the game card's
   img.is-fallback (mlb-game-phone.css:873-876). It sits UNDER the headshot;
   a headshot that 404s removes itself (onerror) and uncovers it. */
.pb-search-ava .pb-search-ava-crest {
  object-fit: contain;
  padding: 16px;
  background: none;
}
.pb-search-ava i {
  font-style: normal;
  font-size: 30px;
  font-weight: 700;
  letter-spacing: .02em;
  color: var(--pbs-fg-mute);
}
.pb-search-ava.is-chip { width: 120px; height: 120px; }
.pb-search-ava.is-chip i { font-size: 34px; }
.pb-search-ava.is-lg { width: 130px; height: 130px; }
.pb-search-ava.is-lg i { font-size: 36px; }

/* ── 5. injury tag (.ngd-inj-b phone: :1669-1682, :2438-2442) ────────── */

.pb-search-inj {
  flex: none;
  margin-left: 14px;
  padding: 6px 12px 7px;
  border-radius: 10px;
  background: var(--pbs-ink-150);
  color: var(--pbs-fg-dim);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: .02em;
  line-height: 1;
  white-space: nowrap;
}
.pb-search-inj.is-warn { background: rgba(245, 181, 68, .12); color: var(--pbs-amber); }
.pb-search-inj.is-out { background: rgba(255, 92, 92, .13); color: var(--pbs-red); }

/* ── 6. recent chips ─────────────────────────────────────────────────── */

.pb-search-recents {
  display: flex;
  gap: 24px;
  padding: 6px var(--pbs-gut) 18px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.pb-search-recents::-webkit-scrollbar { display: none; }

.pb-search-chip {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
  width: 202px;
  padding: 4px 0 6px;
  text-align: center;
}
.pb-search-chip-n {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 100%;
  line-height: 1.16;
}
.pb-search-chip-f {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 26px;
  color: var(--pbs-fg-dim);
}
.pb-search-chip-l {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 30px;
  font-weight: 600;
  color: var(--pbs-fg);
}

/* ── 7. teams grid ───────────────────────────────────────────────────── */

.pb-search-teams {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px;
  padding: 6px var(--pbs-gut) 18px;
}
.pb-search-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 16px;
  min-height: 190px;
  padding: 20px 8px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
}
.pb-search-tile img { width: 88px; height: 88px; object-fit: contain; }
.pb-search-tile-n {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 26px;
  font-weight: 600;
  color: var(--pbs-fg-dim);
  text-align: center;
}

/* The tile's week state, NFL (founder 2026-09-18): W / L in the team's own
   colour with a centred dot, or a pulsing dot for a game in play. A
   decorated tile lays the name line out as a row; the name keeps the
   ellipsis as the last resort, and a long name steps down one size first
   (26 → 22px) so "W · Commanders" fits the 214px tile. */
.pb-search-tile-n.is-win,
.pb-search-tile-n.is-loss,
.pb-search-tile-n.is-tie,
.pb-search-tile-n.is-live {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}
.pb-search-tile-n.is-long { font-size: 22px; }
.pb-search-tile-nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pb-search-tile-r { font-weight: 700; }
.pb-search-tile-sep { color: var(--pbs-fg-mute); font-weight: 400; }
.pb-search-tile-n.is-win { color: var(--pbs-green); }
.pb-search-tile-n.is-loss { color: var(--pbs-red); }
.pb-search-tile-n.is-tie { color: var(--pbs-fg); }
.pb-search-tile-dot {
  flex: none;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--pbs-live);
  box-shadow: 0 0 0 0 rgba(255, 59, 71, .55);
  animation: pb-search-livedot 1.6s ease-out infinite;
}
@keyframes pb-search-livedot {
  0%   { box-shadow: 0 0 0 0 rgba(255, 59, 71, .55); }
  70%  { box-shadow: 0 0 0 12px rgba(255, 59, 71, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 59, 71, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .pb-search-tile-dot,
  .pb-search-skel { animation: none; }
}

/* ── 8. no match ─────────────────────────────────────────────────────── */

.pb-search-none {
  margin: 10px var(--pbs-gut) 0;
  padding: 72px 40px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
  color: var(--pbs-fg-dim);
  font-size: 33px;
  text-align: center;
}
.pb-search-dym {
  padding: 34px var(--pbs-gut) 14px;
  color: var(--pbs-fg-dim);
  font-size: 30px;
}
.pb-search-dym b { color: var(--pbs-fg); font-weight: 600; }

/* ── 9. topbar back link (.ngd-back phone: :1839-1851) ───────────────── */

/* Sticky, like the list screens' bar (section 1): a team screen runs 7,000 to
   12,000 px, and the way back must not be thousands of px up. A direct child
   of .pb-search, so it shares the bar's containing block; never inside the
   card hosts, which are flex columns with the game page's gap. */
.pb-search-topbar {
  position: sticky;
  top: 0;
  z-index: 8;
  display: flex;
  align-items: center;
  gap: 26px;
  min-height: 110px;
  padding: 16px var(--pbs-gut) 0;
  background: var(--pbs-ink-000);
  border-bottom: 1px solid var(--pbs-line-soft);
}
.pb-search-back {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  min-height: 110px;
  padding: 0 14px 0 0;
  color: var(--pbs-accent);
  font-size: 33px;
  font-weight: 700;
}
.pb-search-back svg { flex: none; width: 34px; height: 34px; }

/* forward twin, used by "View full stats" */
.pb-search-fwd {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  min-height: 110px;
  color: var(--pbs-accent);
  font-size: 33px;
  font-weight: 700;
}
.pb-search-fwd svg { flex: none; width: 34px; height: 34px; }

/* ── 10. team header ─────────────────────────────────────────────────── */

.pb-search-teamh {
  display: flex;
  align-items: center;
  gap: 28px;
  padding: 18px var(--pbs-gut) 34px;
  border-bottom: 1px solid var(--pbs-line-soft);
}
.pb-search-teamh-crest { flex: none; width: 140px; height: 140px; object-fit: contain; }
.pb-search-teamh-t { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
.pb-search-teamh-n {
  font-size: 50px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--pbs-fg);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pb-search-teamh-m {
  margin-top: 10px;
  font-size: 30px;
  color: var(--pbs-fg-dim);
}
/* The header before the index lands (searchCore teamBody `pending`): the
   code is real, the crest's slot and the game line are ghosts. Both are
   sized to what replaces them — 140 for the crest, and a 28px bar inside
   the 30px line's own box — so the header does not resize under the
   reader when the answer arrives. */
.pb-search-teamh.is-pending .pb-search-teamh-crest { border-radius: 24px; }
.pb-search-teamh-m > .pb-search-skel-bar { height: 28px; margin: 6px 0; }

/* The top of a game, as a link to its page (founder 2026-10-04): the team
   header before kickoff (searchCore teamBody, an <a> when the adapter knows
   the page) and NFL's score card once it is on (nflSearchCard scoreCardHtml).
   The :where(a) reset keeps their ink; these keep the tap quiet and say it
   landed, as a result row does. */
.pb-search-teamh[href],
.pb-search-lv[href] {
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.pb-search-teamh[href]:active { background: rgba(255, 255, 255, .05); }
.pb-search-lv[href]:active { background: var(--pbs-ink-150); }
/* A live/final team screen whose box score did not mount: the roster, and
   over it the way to the game the score card would have been. */
.pb-search-gamefwd { padding: 4px var(--pbs-gut) 0; }

.pb-search-grp { padding: 30px 0 8px; }
.pb-search-grp + .pb-search-grp { border-top: 1px solid var(--pbs-line-soft); }
/* .ngd-pgroup-h */
.pb-search-grph {
  margin: 0 0 20px;
  padding: 0 var(--pbs-gut);
  font-size: 24px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--pbs-fg-mute);
}

/* ── 11. player view: identity, card host, panel host ────────────────── */

/* An <a> to his dashboard (searchCore identityHtml): the :where(a) reset
   above keeps its ink, the -n / -m lines set their own, and the chevron
   rides the right edge as on a team row. */
.pb-search-pident {
  display: flex;
  align-items: center;
  gap: 22px;
  padding: 12px var(--pbs-gut) 30px;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.pb-search-pident:active { background: rgba(255, 255, 255, .05); }
.pb-search-pident-t { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
.pb-search-pident-n {
  display: flex;
  align-items: center;
  min-width: 0;
  font-size: 45px;
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--pbs-fg);
}
.pb-search-pident-m {
  margin-top: 8px;
  font-size: 30px;
  color: var(--pbs-fg-dim);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* The hosts. Everything INSIDE them is the game page's own markup under the
   generated card sheets, so these rules stay at (0,1,0) and lose to every
   #<id> rule those sheets ship — the card's own display:flex, gap and type
   must win. The BOX is ours, and the box alone is id-qualified below.

   The card gutter is per sport, and it is not a preference — it is what each
   generated sheet left behind. The generator drops the source root's
   page-geometry declarations, so NFL's card arrives with no gutter of its
   own (the game page's own `#nfl-game-detail{padding:16px 35px 200px}` at
   nfl-game-detail.css:1804 went with them) and the host supplies 35. MLB's
   card keeps its gutter on a CHILD — `.mlb-gd-tab-panel[data-panel="props"]`
   is padded 35 a side in mlb-search-card.css — so a host gutter there would
   be 70, twice the screen's. The ids also out-rank the one root rule the
   generator does carry over (nfl-search-card.css's tablet-band
   `@media (max-width:1040px){#nfl-search-card{padding-left:14px}}`), which
   a bare class would lose to and inset the card to 14 while every row above
   it sat at 35. */
.pb-search-cardhost,
.pb-search-panelhost {
  display: block;
  font-family: var(--pbs-sans);
}
#nfl-search-card.pb-search-cardhost { padding: 0 var(--pbs-gut); }
#mlb-search-card.pb-search-cardhost { padding: 0; }
/* ...which leaves the empty state, which is OURS and lives in the same host,
   without a gutter on the MLB side. */
#mlb-search-card.pb-search-cardhost .pb-search-empty { margin: 0 var(--pbs-gut); }

/* The team screen's list is the same host under the team header (searchCore
   teamHostHtml). The header carries its own bottom padding and rule; only
   the NFL host — whose card list arrives with no gutter of its own — needs
   air above its first position heading. */
#nfl-search-card.pb-search-teamcards { padding-top: 30px; }

/* The panel host is always emitted and always last (searchCore hostsHtml).
   Its own sheet may re-pad it; this is the default it starts from. */
.pb-search-panelhost { padding: 0 var(--pbs-gut); }
.pb-search-panelhost:not(:empty) { padding-top: 34px; }

/* Empty state. The modifier is scoped to the search host so it outranks the
   generated sheet's own "#nfl-search-card .ngd-empty" (1-1-0), whose 24px
   italic is sized for a row inside a game card, not for a whole screen. */
#nfl-search-card.pb-search-cardhost .pb-search-empty,
#mlb-search-card.pb-search-cardhost .pb-search-empty,
.pb-search-cardhost .pb-search-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  padding: 80px 40px 56px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
  color: var(--pbs-fg-dim);
  font-family: var(--pbs-sans);
  font-style: normal;
  font-size: 31px;
  text-align: center;
}
.pb-search-empty-t {
  max-width: 100%;
  font-size: 31px;
  color: var(--pbs-fg-dim);
}
.pb-search-empty-s {
  max-width: 740px;
  font-size: 28px;
  line-height: 1.45;
  color: var(--pbs-fg-mute);
  text-wrap: balance;
}
/* 96px box, 110px touch target — the posrow extension (:220), so the box
   stays the size the empty state was tuned to while the floor :566 gives
   every other .pb-search-fwd still holds. */
.pb-search-empty .pb-search-fwd { position: relative; min-height: 96px; margin-top: 2px; }
.pb-search-empty .pb-search-fwd::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -7px;
  bottom: -7px;
}

/* ── 12. loading: the ghost layout, and blur-to-sharp ─────────────────
   The rankings page's treatment (defenseRankingsView.js injected sheet:
   .dr-skel pulse, .is-loading > * blur 7px / opacity .5 / pointer-events
   none, a .28s transition), which the trends and home-run-zone pages share.
   Three hosts carry it here, each with its own is-loading toggle: the list
   shell (the index on its way — only the body under the bar is blurred; the
   field stays sharp and typeable), the card host (the game page's card on
   its way) and the team host (its list on its way). The ghost shapes copy
   the real row / tile / card geometry so nothing moves when the data lands.
   The class comes off a frame AFTER the real content is written (searchCore
   unblur), or the transition has no start state and is skipped. */

.pb-search-skel {
  display: block;
  background: rgba(148, 163, 184, .14);
  border-radius: 14px;
  animation: pb-search-pulse 1.4s ease-in-out infinite;
}
@keyframes pb-search-pulse { 0%, 100% { opacity: .55; } 50% { opacity: .9; } }

.pb-search-skel-row {
  display: flex;
  align-items: center;
  gap: 22px;
  min-height: 150px;                 /* .pb-search-row */
  padding: 14px var(--pbs-gut);
  border-bottom: 1px solid var(--pbs-sep);
}
.pb-search-skel-rows .pb-search-skel-row:last-child { border-bottom: 0; }
.pb-search-skel-ava { flex: none; width: 110px; height: 110px; border-radius: 50%; }   /* .pb-search-ava */
.pb-search-skel-t { flex: 1; display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.pb-search-skel-bar { height: 30px; border-radius: 15px; }
.pb-search-skel-bar.is-sub { height: 24px; }
.pb-search-skel-bar.is-r { flex: none; width: 150px; height: 40px; }   /* the line cell */
.pb-search-skel-tile { min-height: 190px; border-radius: 34px; }         /* .pb-search-tile */

/* the card host: a card's header and four market rows */
.pb-search-skel-card {
  padding: 20px 26px 26px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
}
.pb-search-skel-row.is-card { padding: 0 0 20px; border-bottom: 0; }
.pb-search-skel-mkt { height: 96px; margin-top: 16px; border-radius: 22px; }
#mlb-search-card.pb-search-cardhost .pb-search-skel-card { margin: 0 var(--pbs-gut); }

/* blur-to-sharp. The bar is outside [data-search-body], so a reader can
   type through the whole load; the ghosts and the real body that replaces
   them are what sharpen. The panel host has its own (nflSearchDefense). */
.pb-search [data-search-body] > *,
.pb-search .pb-search-cardhost > * { transition: filter .28s ease, opacity .28s ease; }
.pb-search.is-loading [data-search-body] > *,
#nfl-search-card.is-loading > *,
#mlb-search-card.is-loading > * {
  filter: blur(7px);
  opacity: .5;
  pointer-events: none;
}

/* ── 13. a game in play, or done: the score line and the live box ─────
   Both card hosts paint the same score line over their box score
   (nflSearchCard.js scoreHtml, mlbSearchCard.js scoreLine): away @ home,
   points, and the clock or "Final". The NFL box under it is the game page's
   own (.ngd-box*, carried into nfl-search-card.css by the generator); the
   MLB box is Search's own markup, sized like the NFL one so the two sports
   read as one screen. No <img> in any of it: the strip and the tables are
   re-rendered on every poll. */

.pb-search-lv {
  display: flex;
  align-items: center;
  gap: 22px;
  padding: 6px 0 26px;
  font-family: var(--pbs-sans);
}
.pb-search-lv-side { display: flex; align-items: baseline; gap: 14px; }
.pb-search-lv-side b {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: .06em;
  color: var(--pbs-fg-dim);
}
.pb-search-lv-side.is-mine b { color: var(--pbs-fg); }
.pb-search-lv-side i {
  font-style: normal;
  font-family: var(--pbs-mono);
  font-size: 48px;
  font-weight: 700;
  color: var(--pbs-fg);
  font-variant-numeric: tabular-nums;
}
.pb-search-lv-at { font-size: 26px; color: var(--pbs-fg-mute); }
.pb-search-lv-st {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 26px;
  font-weight: 700;
  letter-spacing: .04em;
  color: var(--pbs-fg-mute);
}
.pb-search-lv.is-live .pb-search-lv-st { color: var(--pbs-live); }
.pb-search-lv-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: currentColor;
  animation: pb-search-pulse 1.4s ease-in-out infinite;
}
/* the MLB host carries no gutter of its own (search.css §11) */
#mlb-search-card.pb-search-cardhost .pb-search-lv,
#mlb-search-card.pb-search-cardhost .pb-search-box { margin: 0 var(--pbs-gut); }

/* the MLB box: the NFL box's phone geometry (nfl-search-card.css
   .ngd-box-tbl at <=1000px: 27px cells, 29px names, mono numerals) */
.pb-search-box {
  display: flex;
  flex-direction: column;
  gap: 30px;
  padding: 26px 30px 30px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
}
.pb-search-box-h {
  margin: 0 0 6px;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--pbs-fg-mute);
}
.pb-search-box-tbl {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
  font-size: 27px;
}
.pb-search-box-tbl th {
  padding: 8px 0 12px;
  text-align: right;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--pbs-fg-mute);
}
.pb-search-box-tbl th:first-child { text-align: left; width: 46%; }
.pb-search-box-tbl td {
  padding: 12px 0;
  text-align: right;
  font-family: var(--pbs-mono);
  font-size: 27px;
  color: var(--pbs-fg-dim);
  font-variant-numeric: tabular-nums;
  border-bottom: 1px solid var(--pbs-line-soft);
}
.pb-search-box-tbl td:first-child {
  text-align: left;
  font-family: var(--pbs-sans);
  font-weight: 600;
  font-size: 29px;
  color: var(--pbs-fg);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.pb-search-box-tbl td:first-child i {
  display: inline-block;
  width: 34px;
  font-style: normal;
  font-family: var(--pbs-mono);
  font-size: 22px;
  color: var(--pbs-fg-mute);
}
.pb-search-box-pos {
  margin-left: 10px;
  font-size: 20px;
  font-weight: 600;
  letter-spacing: .08em;
  color: var(--pbs-fg-mute);
}
.pb-search-box-tbl tr:last-child td { border-bottom: 0; }
.pb-search-box-tbl td.is-hi { color: var(--pbs-accent); }
.pb-search-box-empty { font-size: 27px; color: var(--pbs-fg-dim); }

/* ── 14. the player's live line: score card + one card per section ────
   (founder 2026-09-17, late). The score card carries two crests and is
   built once per mount, then patched by text on every poll; the line cards
   hold no <img> and are swapped whole. Hero number, three tiles, a ratio bar
   where the section has a ratio (completion, catch rate, hits per at-bat),
   and two derived stats in the footer — no icons, no judgement. */

/* The score card sits 26px above what follows it — the rhythm the line
   cards keep between themselves — and ONLY that: the generated sheets make
   both hosts a flex column with the game page's own gap (#nfl-search-card
   18px, 45px in the phone band; #mlb-search-card 18px), right between prop
   cards but opening under the score card on top of this margin (71px on a
   phone — founder 2026-09-18). The live mounts mark the host and the gap
   is zero there. */
.pb-search-lv--card {
  gap: 26px;
  margin: 0 0 26px;
  padding: 24px 30px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
}
.pb-search-lv--card .pb-search-lv-side { align-items: center; gap: 18px; }
.pb-search-lv-crest { flex: none; width: 88px; height: 88px; object-fit: contain; }
.pb-search-lv-t { display: flex; flex-direction: column; gap: 2px; }
.pb-search-lv--card .pb-search-lv-side b { font-size: 24px; letter-spacing: .14em; }
.pb-search-lv--card .pb-search-lv-side i { font-size: 62px; line-height: 1; }
.pb-search-lv--card .pb-search-lv-at { font-size: 28px; }
.pb-search-lv--card .pb-search-lv-st {
  flex-direction: column;
  align-items: flex-end;
  gap: 6px;
  /* No rule between the score and the clock (founder 2026-09-20). The gap
     does the separating; the hairline only chopped the card in two. The
     padding stays — it is what keeps "Final" off the points. */
  padding-left: 22px;
}
.pb-search-lv--card .pb-search-lv-st > span {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  font-size: 34px;
  font-weight: 700;
}
.pb-search-lv--card .pb-search-lv-st small {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: .18em;
  color: var(--pbs-fg-mute);
}
.pb-search-lv--card:not(.is-live) .pb-search-lv-dot { display: none; }
#mlb-search-card.pb-search-cardhost .pb-search-lv--card,
#mlb-search-card.pb-search-cardhost .pb-search-ln { margin-left: var(--pbs-gut); margin-right: var(--pbs-gut); }
/* the gutter rule above (margin: 0 gut, 1-2-0) had zeroed the MLB card's
   bottom margin; the same 26px as NFL's, at the same weight */
#mlb-search-card.pb-search-cardhost .pb-search-lv--card { margin-bottom: 26px; }
#nfl-search-card.pb-search-livehost,
#mlb-search-card.pb-search-livehost { gap: 0; }

.pb-search-ln {
  margin: 0 0 26px;
  padding: 30px 34px 26px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 34px;
  font-family: var(--pbs-sans);
}
.pb-search-ln--empty { font-size: 27px; color: var(--pbs-fg-dim); }
.pb-search-ln-h {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 28px;
}
.pb-search-ln-h b {
  font-size: 24px;
  font-weight: 600;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--pbs-fg-dim);
}
.pb-search-ln-h span { font-size: 22px; letter-spacing: .1em; color: var(--pbs-fg-mute); }

/* hero left, tiles then the bar on the right */
.pb-search-ln-grid {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  grid-template-rows: auto auto;
  column-gap: 24px;
  row-gap: 16px;
  align-items: stretch;
}
.pb-search-ln-hero {
  grid-row: 1 / span 2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  min-width: 0;
}
.pb-search-ln-hero b {
  font-family: var(--pbs-mono);
  font-size: 128px;
  font-weight: 700;
  line-height: .92;
  letter-spacing: -.03em;
  color: var(--pbs-fg);
  font-variant-numeric: tabular-nums;
}
.pb-search-ln-hero small,
.pb-search-ln-tile small,
.pb-search-ln-bar small,
.pb-search-ln-fcell small {
  font-size: 20px;
  font-weight: 600;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--pbs-fg-mute);
}
.pb-search-ln-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px;
}
.pb-search-ln-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  min-height: 150px;
  padding: 18px 8px;
  background: var(--pbs-ink-150);
  border-radius: 24px;
  text-align: center;
}
.pb-search-ln-tile b {
  font-family: var(--pbs-mono);
  font-size: 56px;
  font-weight: 700;
  line-height: 1;
  color: var(--pbs-fg);
  font-variant-numeric: tabular-nums;
}
.pb-search-ln-tile.is-good b { color: var(--pbs-accent); }
.pb-search-ln-tile.is-bad b { color: var(--pbs-red); }
/* written as they read ("PASS TDs", "INTs"): the sheet does not recase them */
.pb-search-ln-tile small { font-size: 18px; letter-spacing: .1em; text-transform: none; }
.pb-search-ln-grid.no-bar .pb-search-ln-tiles { grid-row: 1 / span 2; align-content: center; }
.pb-search-ln-bar {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 16px 22px;
  background: var(--pbs-ink-150);
  border-radius: 20px;
}
.pb-search-ln-bar b {
  font-family: var(--pbs-mono);
  font-size: 32px;
  font-weight: 700;
  color: var(--pbs-fg);
  white-space: nowrap;
}
.pb-search-ln-track {
  flex: 1;
  height: 16px;
  border-radius: 8px;
  background: rgba(255, 255, 255, .08);
  overflow: hidden;
}
.pb-search-ln-track i { display: block; height: 100%; background: var(--pbs-accent); border-radius: 8px; }
.pb-search-ln-bar em {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-style: normal;
  font-family: var(--pbs-mono);
  font-size: 30px;
  font-weight: 700;
  color: var(--pbs-fg);
  white-space: nowrap;
}
.pb-search-ln-bar em small { font-size: 16px; letter-spacing: .1em; }
.pb-search-ln-foot {
  display: grid;
  grid-template-columns: 1fr 1fr;
  margin-top: 26px;
  padding-top: 22px;
  border-top: 1px solid var(--pbs-sep);
}
/* each half centred on itself, so the divider between them sits in the middle */
.pb-search-ln-fcell { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 0 16px; text-align: center; }
.pb-search-ln-fcell + .pb-search-ln-fcell { border-left: 1px solid var(--pbs-sep); }
.pb-search-ln-fcell small { text-transform: none; letter-spacing: 0; font-size: 24px; color: var(--pbs-fg-dim); }
.pb-search-ln-fcell b {
  font-family: var(--pbs-mono);
  font-size: 42px;
  font-weight: 700;
  color: var(--pbs-fg);
  font-variant-numeric: tabular-nums;
}

/* ── 15. the box score's team tabs (founder 2026-09-20) ───────────────────
   A live or final team screen shows ONE team's box score, so the header
   slot is a two-way switch instead of a title: away on the left, home on
   the right, the selected side filled. searchCore renders it (teamSwitchHtml)
   and the tap never remounts — the card host swaps the tables from the feed
   it already holds — so the crests below are never re-created either. */

.pb-search-tsw {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 18px var(--pbs-gut) 26px;
  padding: 8px;
  background: var(--pbs-ink-050);
  border: 1px solid var(--pbs-line-soft);
  border-radius: 999px;
}
.pb-search-tsw-b {
  flex: 1 1 0;
  min-width: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  padding: 18px 16px;
  border-radius: 999px;
  font-size: 31px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--pbs-fg-dim);
  transition: background .16s ease, color .16s ease;
}
.pb-search-tsw-b > span {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.pb-search-tsw-crest { flex: none; width: 52px; height: 52px; object-fit: contain; }
.pb-search-tsw-b.is-on { background: var(--pbs-fg); color: var(--pbs-ink-000); }
.pb-search-tsw-b:active { background: rgba(255, 255, 255, .06); }
.pb-search-tsw-b.is-on:active { background: var(--pbs-fg); }

/* The box score itself is NOT here. It is the game page's own list
   (gameDetailNFLLive.js boxListHtml under nfl-game-detail.css §13.7), and
   scripts/gen_search_card_css.py carries those rules into
   nfl-search-card.css scoped to #nfl-search-card — the same route the
   player card takes, so /nfl/game/<slug> and /nfl/search cannot drift.
   Only the tabs above are Search's own, because they are its header. */
