/* ============================================================================
 * FanPulse · Ad unit chrome (fp_ad_labeled + AdServer slots)
 * Navy-dark native — the shell has no light mode, so there is one set of rules.
 * Ad chrome deliberately stays achromatic: lime is the first-party CTA colour
 * and must never read as part of a paid unit.
 * Sizing hints only — creatives bring their own art.
 * ========================================================================== */

.fpv-ad {
  position: relative;
  margin: 0 0 var(--v-gap, 16px);
}

/* Disclosure label. Small, muted, wide-tracked — legible without competing
   with the creative, matching renderNative()'s SPONSORED header. */
.fp-ad-label {
  display: block;
  margin: 0 0 6px;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 1.6px;
  line-height: 1.2;
  text-transform: uppercase;
  color: var(--c-muted, #8F9AAD);
  user-select: none;
}

.fpv-ad img {
  display: block;
  max-width: 100%;
  height: auto;
}

/* ── 970x90 leaderboard (and the 320x100 mobile strip that replaces it via
      device_target) — centred, capped at the creative's native width. ── */
.fpv-ad-banner {
  max-width: 970px;
  margin-inline: auto;
  margin-bottom: 20px;
}
.fpv-ad-banner img {
  width: 100%;
  border-radius: 10px;
  border: 1px solid var(--c-border-2, #1A2336);
}

/* ── Right-rail units: 300x250 rect and 300x600 sky. ── */
.fpv-ad-rect img,
.fpv-ad-sky img {
  width: 100%;
  max-width: 300px;
  margin-inline: auto;
  border-radius: 10px;
  border: 1px solid var(--c-border-2, #1A2336);
}
.fpv-ad-rect  { max-width: 300px; }
.fpv-ad-sky   { max-width: 300px; }

/* ── Masonry safety. .fpv-wall is CSS columns (feed-v2.css) — an ad unit
      split across a column break is unservable. Mirrors the wall's own
      break rules so injected units behave like cards. ── */
.fpv-wall .fpv-ad {
  break-inside: avoid;
  -webkit-column-break-inside: avoid;
  display: inline-block;
  width: 100%;
}

/* Reduce visual weight of the label on narrow screens where the 320x100
   strip already sits tight against the feed. */
@media (max-width: 880px) {
  .fpv-ad-banner { margin-bottom: 14px; }
  .fp-ad-label   { letter-spacing: 1.2px; }
}

/* ── Reserved heights. An unfilled slot prints nothing (fp_ad_labeled returns
      ''), so these only hold space for a unit that is already in the DOM and
      stop the page jumping when its creative image loads. Label (≈17px) +
      creative. ── */
.fpv-ad-rect   { margin-inline: auto; }
/* Only image creatives get a reservation: the house tiles and partner cards are
   short text cards and would sit in an empty box. */
.fpv-ad-banner:has(img) { min-height: 107px; }
.fpv-ad-rect:has(img)   { min-height: 267px; }
.fpv-ad-sky:has(img)    { min-height: 617px; }
@media (max-width: 880px) {
  .fpv-ad-banner:has(img) { min-height: 117px; max-width: 320px; }
}

/* Partner card (AdServer::partnerFallback): fills an unsold rail slot. */
.fp-partner { display: grid; gap: 6px; padding: 14px 16px; }
.fp-partner-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.fp-partner-head b { font-size: 14px; color: var(--fp-text); }
.fp-partner-body { font-size: 12px; color: var(--fp-muted); }
.fp-partner-cta { font-size: 13px; font-weight: 700; color: var(--fp-text); }

/* ── Affiliate label (fp_tickets_link, the partner card). ── */
.fp-aff-tag {
  flex: 0 0 auto;
  font-size: 9px;
  font-weight: 800;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  color: var(--fp-muted);
  user-select: none;
}

/* Linked network names inside existing meta lines keep the line's own colour;
   the underline is the only cue. */
.fp-net-link { color: inherit; text-decoration: underline; text-decoration-color: var(--fp-dim); text-underline-offset: 2px; }
.fp-net-link:hover { text-decoration-color: currentColor; }

.ln > .fpv-ad { margin: 0 auto; }

/* ── Game page: Tickets + Where to watch (live-v2.php, game.php?classic=1).
      The phone card from the owner's designs (Ad designs/mobile screen.png):
      ON TV line, Get tickets + Where to watch, and the Compare tickets panel
      that Get tickets opens. The pieces are aff_ui.php's (fp-affiliate.css);
      this block sets the card and the design's layout around them. The card
      is its own size container: two rows on a phone, one row when wide. ── */
.fp-aff-game {
  container: fp-aff-game / inline-size;
  display: grid;
  gap: 12px;
  min-width: 0;
  padding: 12px;
  border: 1px solid var(--fp-border);
  border-radius: 14px;
  background: var(--fp-surface-base);
}
.fp-aff-game__bar { display: grid; gap: 8px; min-width: 0; }
.fp-aff-game__actions { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: 8px; }
.fp-aff-game__actions .aff-btn { min-width: 0; }
@container fp-aff-game (min-width: 560px) {
  .fp-aff-game__bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
  .fp-aff-game__actions { grid-auto-columns: minmax(176px, auto); margin-left: auto; }
}

/* Logo sizes on this card, measured against the text in the design: the ON TV
   ESPN mark is about one and a half times the label's height, and the ticket
   wordmarks run from StubHub (smallest) to Vivid Seats (largest). */
.fp-aff-game .aff-tv__nets { margin-left: 12px; }
.fp-aff-game .aff-tv__nets .aff-logo--espn-red { --aff-h: 15px; }
.fp-aff-game .aff-tix__row .aff-logo--stubhub-white { --aff-h: 19px; }
.fp-aff-game .aff-tix__row .aff-logo--seatgeek-red { --aff-h: 23px; }
.fp-aff-game .aff-tix__row .aff-logo--vividseats-stacked-caps { --aff-h: 24px; }

/* Compare tickets: in the design the heading sits inside the panel's box, the
   dividers stop short of its sides, and View seats is a dark pill with a blue
   edge (chart cyan mixed into the panel navy). */
.fp-aff-game .aff-tix--list { gap: 0; }
.fp-aff-game .aff-tix__head--list {
  padding: 9px 10px 8px;
  border: 1px solid var(--fp-border);
  border-bottom: 0;
  border-radius: 10px 10px 0 0;
  background: color-mix(in srgb, var(--fp-surface-panel) 45%, transparent);
  color: var(--fp-text-2);
}
.fp-aff-game .aff-tix__head--list + .aff-tix__list { border-top: 0; border-radius: 0 0 10px 10px; }
.fp-aff-game .aff-tix__row { position: relative; min-height: 48px; padding: 7px 12px 7px 16px; }
.fp-aff-game .aff-tix__row + .aff-tix__row { border-top: 0; }
.fp-aff-game .aff-tix__row .aff-btn--line {
  border-color: color-mix(in srgb, var(--fp-chart-1) 25%, var(--fp-surface-panel));
  background: var(--fp-surface-base);
}
.fp-aff-game .aff-tix__row .aff-btn--line:hover { border-color: var(--fp-text); }
.fp-aff-game .aff-tix__head--list + .aff-tix__list .aff-tix__row::before,
.fp-aff-game .aff-tix__row + .aff-tix__row::before {
  content: '';
  position: absolute;
  top: 0;
  left: 12px;
  right: 12px;
  height: 1px;
  background: var(--fp-border);
}
.fp-aff-game .aff-tix__list + .aff-tix__foot { margin-top: 12px; }
.fp-aff-game .aff-tix__foot + .aff-tix__foot { margin-top: 10px; }

/* Phone game page (fp-game-v5.css flattens the columns and orders every card):
   the card stays right under the scoreboard. */
@media (max-width: 1180px) {
  .fp-game .fp-aff-game { order: 0; }
}

/* ── Ticket links in upcoming-games lists (fp_tickets_link): the designs'
      compact "Tickets ↗" button (Ad designs/live now.png, Your Teams cards).
      Lime edge and lime label on the card's own background. ── */
.fp-tix-mini {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  min-height: 34px;
  padding: 0 16px;
  border: 1.5px solid var(--fp-lime);
  border-radius: var(--fp-radius-xs);
  background: transparent;
  color: var(--fp-lime);
  font: 700 14px/1.1 var(--font-body);
  white-space: nowrap;
  text-decoration: none;
  transition: background .15s;
}
.fp-tix-mini:hover { background: var(--fp-lime-glow); color: var(--fp-lime); }
.fp-tix-mini:focus-visible { outline: 2px solid var(--fp-text); outline-offset: 2px; }
.fp-tix-mini .aff-arr { width: 12px; height: 12px; }

/* One disclosure line above a list whose rows carry untagged ticket links. */
.fp-aff-note { margin: 0 0 10px; font-size: 11px; color: var(--fp-muted); }

/* schedule.php Upcoming rows: the row link keeps its grid; the button sits at
   the right edge and drops under the row on phones. */
.fp-tix-row { display: flex; align-items: center; border-bottom: 1px solid var(--c-border-2); }
.fp-tix-row > a:first-child { flex: 1 1 auto; min-width: 0; border-bottom: 0 !important; }
.fp-tix-row > .fp-tix-mini { flex: 0 0 auto; margin-right: var(--sp-4, 16px); }
@media (max-width: 560px) {
  .fp-tix-row { flex-wrap: wrap; }
  .fp-tix-row > a:first-child { flex-basis: 100%; }
  .fp-tix-row > .fp-tix-mini { margin: 0 var(--sp-4, 16px) var(--sp-3, 12px) auto; }
}

/* live-now Your Teams cards, team page next-game card. */
.ln-tix,
.fp-tix-line { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.fp-tix-line { margin-top: 10px; }

/* ============================================================================
 * Placement study v3 slots (includes/partials/ad_slot.php, AdSlots.php).
 *
 *   <div class="fp-slot fp-slot--WxH" data-slot="ID" style="--slot-w;--slot-h">
 *     <span class="fp-ad-label">…</span>          label above, outside the box
 *     <a class="fp-slot__a">creative</a>          the box: exact ratio, reserved
 *   </div>
 *
 * The box is sized from the slot, not from the image, so nothing moves when
 * the creative loads. In a column narrower than the creative the box scales
 * down at the same ratio. Token colours only.
 * ========================================================================== */
.fp-slot {
  --slot-ar: 300 / 250;
  width: min(100%, var(--slot-w, 300px));
  margin: 0 auto 16px;
  flex: none;
  position: static;                 /* never pinned: the rail scrolls past it */
}
.fp-slot--970x90  { --slot-ar: 970 / 90; }
.fp-slot--728x90  { --slot-ar: 728 / 90; }
.fp-slot--300x250 { --slot-ar: 300 / 250; }
.fp-slot--300x600 { --slot-ar: 300 / 600; }
.fp-slot--200x200 { --slot-ar: 1 / 1; }
.fp-slot--320x100 { --slot-ar: 320 / 100; }
.fp-slot--320x50  { --slot-ar: 320 / 50; }
.fp-slot > .fp-ad-label { margin: 0 0 6px; }   /* wraps on the 200px square; never truncates the disclosure */
.fp-slot:not(.fp-slot--house) > :not(.fp-ad-label) {
  display: block;
  width: 100%;
  aspect-ratio: var(--slot-ar);
  overflow: hidden;
  border-radius: 8px;
  background: var(--c-bg-2);
  outline: 1px solid var(--c-border-2);
  outline-offset: -1px;
}
.fp-slot:not(.fp-slot--house) > :not(.fp-ad-label) .fpv-ad { margin: 0; }
.fp-slot picture { display: contents; }
.fp-slot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.fp-slot__a:focus-visible { outline: 2px solid var(--c-text); outline-offset: 2px; }
.fp-slot--house { width: 100%; }

/* W1 leaderboard: 970x90 where it fits, the 728x90 creative below 1024px
   (ad_slot.php swaps the image), gone below 728px where M1 takes over. */
@media (max-width: 1023.98px) {
  .fp-slot--970x90 { width: min(100%, 728px); --slot-ar: 728 / 90; }
}
@media (max-width: 727.98px) {
  .fp-slot[data-slot="W1"] { display: none; }
}
.fp-slot[data-slot="W1"] { margin-bottom: 20px; }

/* Phone placements (M1 after Upcoming Games, M2 between posts): phones and
   the app only. Above 880px the shell shows its rails instead. */
@media (min-width: 880.02px) {
  .fp-slot[data-slot="M1"],
  .fp-slot[data-slot="M2"] { display: none; }
}
.fp-slot[data-slot="M1"] { margin-top: 4px; }

/* M2 inside the masonry wall behaves like a card: never split by a column break. */
.fpv-wall .fp-slot { break-inside: avoid; -webkit-column-break-inside: avoid; }
.fpv-wall > .fp-slot { display: block; width: min(100%, var(--slot-w, 300px)); margin-inline: auto; }
.fp-slot[data-slot="M2"] { margin-block: 8px 24px; }

/* N1: under the expanded left menu only, and only where it fits. It starts
   hidden; fp-affiliate.js adds .is-fit when the left rail has room for it below
   the menu without scrolling (the menu's height depends on the viewer's sections,
   so no fixed max-height is right for everyone). The rail is a drawer on phones. */
.fp-nav-slot { display: none; margin-top: 16px; padding-left: 4px; }
.fp-nav-slot.is-fit { display: block; }
.fp-nav-slot .fp-slot { margin: 0; }
@media (max-width: 880px) { .fp-nav-slot.is-fit { display: none; } }

/* Right rail stack (R1/R2): rectangles centred in the 340px rail. The study's
   rule is that the page scrolls and the stack is never pinned to the viewport,
   so a rail that carries a display slot drops the shell's sticky, self-scrolling
   behaviour (layout.php) and scrolls with the page; the stack continues below
   the first screen. */
.shell-right .fp-slot { margin-inline: auto; }
@media (min-width: 1024px) {
  .shell-right:has(.fp-slot) { position: static; max-height: none; overflow: visible; }
}

/* Immersive feed (home-v2): M2 fills its own snap stop so the swipe lands on
   it whole, like a post. Never over the action rail or the bottom nav. */
.fp-imm-adcard {
  scroll-snap-align: start;
  scroll-snap-stop: always;
  min-height: var(--fp-imm-track-h,
    calc(100dvh - var(--fp-imm-header-h, 64px) - var(--fp-imm-widgets-h, 0px) - var(--fp-bottom-nav, 0px)));
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.fp-imm-adcard .fp-slot { margin: 0 auto; }
.fp-imm-adcard .fp-slot[data-slot="M2"] { display: block; }
.fp-imm-widgets .fp-slot[data-slot="M1"] { margin: 8px auto 10px; }

/* M3: the phone game screen, under the court. Chat takes the room, the wide
   layout has no slot, and a short phone keeps the court and controls instead. */
.v2-m3 { padding: 8px 12px 0; }
.v2-m3 .fp-slot { margin: 0 auto; }
@container v2 (min-width: 880px) {
  .v2-m3 { display: none; }
}
@container v2 (max-width: 879.98px) {
  .v2-screen[data-tab="chat"] .v2-m3 { display: none; }
}
@media (max-height: 699.98px) {
  .v2-m3 { display: none; }
}
