/* ==========================================================================
   TSF SPONSOR RAIL
   Companion to ads.php and js/tsf-ads.js.

   Replaces bare AdRotate output: 728x90 creatives on the page background, a red
   12px "Advertiser" heading above each one, and a 9px city/state line under it.
   Sizes are unchanged — the creatives are still 728x90 and still delivered by
   AdRotate. Only the frame around them is new.

   ONE LAYOUT AT EVERY WIDTH: a horizontal rail that creeps sideways on its own.
   The first pass stacked on desktop and railed on mobile, which left a 728px
   column stranded in the middle of a 1400px container with dead air either side.
   A rail fills whatever width it is given — ~1.9 cards at 1400px, ~2.5 at 1900 —
   and the motion is the point of the slot, so there is no reason desktop should
   sit still.

   Nothing here costs vertical space beyond the creative itself. The "Sponsored"
   disclosure and the city/state caption are both overlays, not rows.
   ========================================================================== */

.tsf-ads {
    --tsf-ad-w: 728px;   /* creative width — the cap, not a fixed size */
    --tsf-ad-gap: 14px;

    position: relative;  /* the disclosure pill anchors to this */
    box-sizing: border-box;
    width: 100%;
    margin: 0 auto;
    font-family: var(--tsf-font-body, "Roboto", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif);
}

.tsf-ads *,
.tsf-ads *::before,
.tsf-ads *::after { box-sizing: border-box; }

/* --------------------------------------------------------------------------
   DISCLOSURE PILL
   One label for the block — the per-advert <h4>Advertiser</h4> is stripped in
   ads.php (see the note there on why a stylesheet can't do it).

   Overlaid in the top-right rather than set above the rail, so it costs zero
   vertical space. pointer-events:none matters: it sits over live ad inventory
   and must never eat a click meant for the creative underneath.
   -------------------------------------------------------------------------- */
.tsf-ads-eyebrow {
    position: absolute;
    top: 6px;
    right: 8px;
    z-index: 2;
    pointer-events: none;
    padding: 3px 10px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.68);
    border: 1px solid rgba(255, 255, 255, 0.09);
    color: var(--tsf-text-muted, #8A8A8A);
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--tsf-tracking-caps, 0.12em);
    line-height: 1.5;
}

@supports (backdrop-filter: blur(4px)) {
    .tsf-ads-eyebrow { backdrop-filter: blur(4px); background: rgba(0, 0, 0, 0.5); }
}

/* --------------------------------------------------------------------------
   THE RAIL
   A native horizontal scroller, not a transform track: js/tsf-ads.js only nudges
   scrollLeft, which leaves the whole thing flickable. See the note there.

   `safe center` centres a group too narrow to overflow — the single-ad regions,
   of which there are a dozen — while degrading to flex-start the moment it does
   overflow. Plain `center` would push the first card behind the scroll origin
   and make it unreachable.

   No scroll-snap: snapping fights a continuous scroll, yanking the track back to
   the nearest card on every frame the JS advances it.
   -------------------------------------------------------------------------- */
.tsf-ads-track {
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    justify-content: safe center;
    gap: var(--tsf-ad-gap);
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.tsf-ads-track::-webkit-scrollbar { display: none; }

/* --------------------------------------------------------------------------
   THE CARD
   Height is the creative's height and nothing else — the caption overlays it.
   -------------------------------------------------------------------------- */
.tsf-ad {
    position: relative;
    flex: 0 0 auto;
    width: min(var(--tsf-ad-w), 88%);
    overflow: hidden;
    background: var(--tsf-surface, #0F0F0F);
    border: 1px solid var(--tsf-line-soft, #2A2A2A);
    border-radius: var(--tsf-radius-md, 10px);
    transition: border-color var(--tsf-transition, 180ms ease-in-out),
                box-shadow var(--tsf-transition, 180ms ease-in-out);
}

.tsf-ad:hover,
.tsf-ad:focus-within {
    border-color: var(--tsf-red, #FF0000);
    box-shadow: 0 0 0 1px rgba(255, 0, 0, 0.3), 0 0 18px rgba(255, 0, 0, 0.22);
}

.tsf-ad > a { display: block; line-height: 0; }

/* The creative's own `display:inline-block` inline style is removed server-side;
   without that this rule could not win, and the banner would not scale. */
.tsf-ad img {
    display: block;
    width: 100%;
    height: auto;
}

/* --------------------------------------------------------------------------
   CITY / STATE CAPTION
   Same content and same ?ref=adcap deep link as before. Now a quiet pill resting
   on the creative instead of a row beneath it: it adds no height, and at 60%
   opacity it reads as provenance rather than competing with the ad it labels.
   Full strength on hover, when someone is actually looking at it.
   -------------------------------------------------------------------------- */
.tsf-ad-cap {
    position: absolute;
    left: 7px;
    bottom: 7px;
    z-index: 1;
    max-width: calc(100% - 14px);
    padding: 2px 9px;
    border-radius: 999px;
    /* Near-opaque, and the pill itself is NOT faded. Small type on a moving
       surface needs its contrast to come from the plate behind it — dimming the
       whole thing to 0.6 over a half-transparent backdrop meant the text was
       fighting whatever artwork happened to be sliding underneath it. */
    background: rgba(0, 0, 0, 0.88);
    border: 1px solid rgba(255, 255, 255, 0.08);
    font-size: 10px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    line-height: 1.6;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    opacity: 0.92;
    transition: opacity var(--tsf-transition, 180ms ease-in-out);
}

/* Blur lets the plate sit a little lighter without losing legibility, because it
   removes the busy detail behind the text rather than just veiling it. */
@supports (backdrop-filter: blur(6px)) {
    .tsf-ad-cap { backdrop-filter: blur(6px) saturate(0.6); background: rgba(0, 0, 0, 0.74); }
}

.tsf-ad:hover .tsf-ad-cap,
.tsf-ad:focus-within .tsf-ad-cap { opacity: 1; }

.tsf-ad-cap a {
    color: var(--tsf-text, #CBCBCB);
    text-decoration: none;
    transition: color var(--tsf-transition, 180ms ease-in-out);
}
.tsf-ad-cap a:hover { color: var(--tsf-red, #FF0000); }
.tsf-ad-cap a:focus-visible { outline: 2px solid var(--tsf-red, #FF0000); outline-offset: 2px; }

/* An 8:1 leaderboard gets SHORT fast as it scales down: at a 343px card the
   creative is 42px tall, and even a 9px pill covers ~40% of the ad someone paid
   for. Measured: 20px of pill on 42px of creative is 47%.

   So below ~700px — where the creative drops under about 76px — the caption goes
   away rather than shrinking further. Nothing is really lost: this is the same
   width band where it used to render at 9px, and the creative itself still links
   to the advertiser. If the listing deep link matters more than the coverage,
   the alternative is a caption ROW on small screens only, at the cost of the
   height this redesign just reclaimed. */
@media (max-width: 699px) {
    .tsf-ad-cap { display: none; }
}

/* --------------------------------------------------------------------------
   SCROLL ARROWS
   The rail pauses under the cursor so an ad can be read. It was never actually
   unscrollable — Shift+wheel and trackpad swipe both work on an overflow-x:auto
   container — but the scrollbar is hidden and a plain wheel scrolls the page, so
   nothing advertised either. A paused rail with no visible affordance is a
   stranded advertiser for everyone who doesn't know the shortcut.

   Visible at rest rather than hover-only — the whole point is that someone who
   cannot see the next advert knows how to get to it.
   -------------------------------------------------------------------------- */
.tsf-ads-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    display: none;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.16);
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.72);
    color: var(--tsf-text-bright, #FFFFFF);
    cursor: pointer;
    opacity: 0.45;
    transition: opacity var(--tsf-transition, 180ms ease-in-out),
                background-color var(--tsf-transition, 180ms ease-in-out);
}

@supports (backdrop-filter: blur(4px)) {
    .tsf-ads-nav { backdrop-filter: blur(4px); background: rgba(0, 0, 0, 0.55); }
}

/* Only when the rail has somewhere to go — a one-advert region has nothing to
   page through, and js/tsf-ads.js owns that class. */
.tsf-ads.is-scrollable .tsf-ads-nav { display: flex; }

.tsf-ads:hover .tsf-ads-nav { opacity: 0.85; }
.tsf-ads-nav:hover { opacity: 1; background: var(--tsf-red, #FF0000); border-color: var(--tsf-red, #FF0000); }
.tsf-ads-nav:focus-visible { opacity: 1; outline: 2px solid var(--tsf-red, #FF0000); outline-offset: 2px; }

.tsf-ads-nav svg {
    width: 17px; height: 17px;
    fill: none; stroke: currentColor;
    stroke-width: 2.5; stroke-linecap: round; stroke-linejoin: round;
}

.tsf-ads-nav--prev { left: 6px; }
.tsf-ads-nav--next { right: 6px; }

/* Touch already has the better gesture, and with no hover to reveal them these
   would sit permanently on top of paid inventory. */
@media (hover: none) and (pointer: coarse) {
    .tsf-ads.is-scrollable .tsf-ads-nav { display: none; }
}

/* Short creatives leave no room for a 34px control. */
@media (max-width: 699px) {
    .tsf-ads-nav { width: 26px; height: 26px; }
    .tsf-ads-nav svg { width: 13px; height: 13px; }
}

/* --------------------------------------------------------------------------
   MOTION
   The rail moves without being asked, so it is opt-out at the OS level.
   js/tsf-ads.js checks the same query and never starts the loop; this covers a
   preference that changes after load.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .tsf-ads-track { scroll-behavior: auto; }
    .tsf-ad,
    .tsf-ad-cap { transition: none; }
}

/* --------------------------------------------------------------------------
   SLOT
   The wrapper the region swap replaces into. Height is one card at every width
   now, so a region with six ads and a region with one are the same height and
   swapping between them shifts nothing.
   -------------------------------------------------------------------------- */
.tsf-ads-slot.is-loading { opacity: 0.45; transition: opacity 120ms ease-in-out; }
