/* ==========================================================================
   banner-ads.php — page-specific additions
   ==========================================================================

   Loaded AFTER assets/text-ads/css/style.css, which is the shared ad-format
   design and is not edited from here. That sheet owns every section, token
   and breakpoint on this page; this one exists only for the two blocks the
   banner CONTENT needs and the text design has no equivalent for:

     .ba-slide / .ba-stage   the S1 hero size carousel
     .ba-tablet / .ba-sizepick   the S3 tablet and its size picker

   Everything is scoped under `.banner-ads-page` so that including this sheet
   can never reach text-ads.php. Tokens (--c-*, --font-*, --r-*) all come from
   the shared sheet; nothing here re-declares a colour or a face.
   ========================================================================== */

/* A `.ba-creative` block lived here: a 300x250 unit built in markup, standing
   where text-ads puts its text-ad card, in the hero and again inside the S3
   tablet. Both call sites have since gone — the hero shows the size carousel
   and the tablet shows the real creatives — so the styling went with them
   rather than being left behind unreferenced. */

/* --------------------------------------------------------------------------
   S1 — the hero size carousel

   Three slots on one axis: the current size centred, the previous one out to
   the left and the next one out to the right, both far enough that the page
   edge cuts them in half. Every 3s the row shifts one place — right becomes
   centre, centre becomes left — which is the whole animation. Positions are
   absolute and the MOVE IS ONE TRANSFORM per slide, so the six of them never
   reflow anything and the browser can keep them on the compositor.

   The centre slot is not a fixed frame. Each export is fitted INTO the box by
   `max-width`/`max-height` with `width/height: auto`, so a Leaderboard lands
   wide and shallow and a Half Page tall and narrow — the aspect ratio is the
   whole point of the section and it survives untouched. Fitting each one to
   the box rather than scaling all six by one factor is deliberate: the files
   are not on a common scale (936x120 next to 1456x180), and a shared factor
   sized off the widest would leave the Square at under 180px.
   -------------------------------------------------------------------------- */
.banner-ads-page {
  /* The centre box. Everything else is derived from it. */
  --ba-box-w: clamp(230px, 30vw, 470px);
  --ba-box-h: clamp(190px, 26.5vw, 420px);
  /* Centre-to-side distance, and it is nearly a HALF viewport for a reason:
     at 50vw the side slot's centre line lands exactly on the page edge and the
     slide is cut precisely in half. 49 sits just inside that.

     This has to hold for the NARROWEST size, not the average. At 47.5vw the
     five wider sizes were bitten by the edge and the Wide Skyscraper was not:
     scaled it is only 67px across, so its whole body fitted in the 48px of
     margin the throw left and it sat fully visible in the corner, reading as a
     third banner rather than a hint of the next one. 49vw puts the side slot's
     centre line 12px OUTSIDE the content edge, which is past half of even that
     one, so every size is cut and the rule stops depending on width at all.

     Measured at 1920: Wide Skyscraper -21..45, Leaderboard -129..153. */
  --ba-throw: clamp(190px, 49vw, 980px);
  --ba-side-scale: 0.6;
  /* One duration for the move and the fade, so a slide arriving and a slide
     leaving are the same gesture. */
  --ba-glide: 900ms;
  /* The S3 tablet's size swap: shorter than the carousel's glide, because that
     one answers a click and anything slower reads as lag. */
  --ba-swap: 420ms;
  --ba-ease: cubic-bezier(0.65, 0, 0.35, 1);
  /* The picker's check, inlined so it inherits currentColor through a mask. */
  --ba-tick: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

/* THE CLIP. Slides are thrown most of a half-viewport out, and without this
   they would extend the page's scrollWidth and put a horizontal scrollbar on
   the whole document — the same trap `.ta-steps__waveclip` exists to avoid.

   `clip`, not `hidden`: `overflow-x: hidden` forces a scroll container and
   drags `overflow-y` to `auto` with it, which would cut the hero's own bloom
   off at the top and bottom. `clip` cuts sideways and leaves the vertical axis
   genuinely visible. Safe to clip this section at all on this page only
   because there is no card flight here to fly out of it. */
.banner-ads-page .ta-hero { overflow-x: clip; }

/* Holds the box the note is anchored to. `.ta-hero__note` positions itself at
   `left: calc(100% + gap)` of this element, so this has to stay the CENTRE
   slot's width — not the carousel's — or the note lands off screen. The height
   is explicit because every slide inside is absolute and would otherwise leave
   this box at zero and collapse the hero. */
.banner-ads-page .ba-stage {
  position: relative;
  width: var(--ba-box-w);
  height: var(--ba-box-h);
  margin-inline: auto;
}

.banner-ads-page .ba-slider {
  position: absolute;
  inset: 0;
  /* Above the hero copy, for the same reason `.ta-hero__flyer` is: the
     revealed headline keeps a transform and so paints in the positioned layer,
     and it comes later in the markup. */
  z-index: 5;
}

.banner-ads-page .ba-slide {
  position: absolute;
  top: 50%;
  left: 50%;
  margin: 0;
  line-height: 0;
  /* Parked state: every slide that is not one of the three visible slots sits
     centred, small and invisible, so whichever way the index moves a slide
     grows out of the middle rather than jumping in from a corner. */
  transform: translate(-50%, -50%) scale(0.4);
  opacity: 0;
  pointer-events: none;
  transition:
    transform var(--ba-glide) var(--ba-ease),
    opacity   var(--ba-glide) var(--ba-ease);
}

/* The flight's element. `.ba-slide` above holds the carousel's transform and
   this one holds the flight's, so the two clocks never write the same
   property. `top left` because the flight is measured corner-to-corner: the
   delta puts this box's top-left on the tablet slot's top-left and the scale
   matches their widths, which is only true from that origin. */
.banner-ads-page .ba-slide__inner {
  display: block;
  transform-origin: top left;
  will-change: transform;
}

/* THE SIDE SLOTS STAY WHERE THEY ARE WHILE ONE IS IN THE AIR.

   They were faded out here, on the reasoning that the reader is leaving the
   hero and only the travelling slide matters. It reads as a fault rather than
   as an intention: the instant the scroll starts, two banners the reader can
   still plainly see blink out, and on the way back up they blink in again a
   third of a screen later.

   So the rule is gone and the row simply holds. Nothing further is needed to
   freeze it — the carousel's clock is stopped for the whole flight (`flying`
   in hero-slider.js), and with no class changing there is nothing for the
   transition above to run. Prev and next stand exactly where the reader left
   them until the banner comes home to them. */

/* Re-arranging the row with the glide switched off. Held for one reflow when
   a size is chosen down at the tablet, so the hero is already built around
   that size before the reader scrolls back up into it — see paintInstant(). */
.banner-ads-page .ba-slider.is-instant .ba-slide { transition: none; }

/* The tablet's own copy of the creative is hidden while one is flying towards
   it, so the slot is not occupied twice; it comes back the moment the flight
   lands on it. Same trick as `.ta-has-flight` on text-ads, keyed to this
   page's own flight instead.

   THE TWO ATTRIBUTE SELECTORS ARE LOAD-BEARING. The rule that reveals the
   chosen creative is `[data-size="x"] .ba-tablet__ad[data-size="x"]` — three
   classes and two attributes. Written with classes alone this rule scored
   lower and lost, so `is-receiving` was set the whole way down and the slot
   was still filled: the flying box arrived on top of a copy of itself. Matching
   the attribute count puts it back in front. */
.banner-ads-page .ba-tablet.is-receiving[data-size] .ba-tablet__ad[data-size] {
  visibility: hidden;
  opacity: 0;
}

.banner-ads-page .ba-slide img {
  display: block;
  /* `auto` + the two maxima IS the fit: an export smaller than the box keeps
     its own pixels, a larger one scales down on its longest side and takes the
     other with it. Never stretched, because only one of the pair ever binds. */
  width: auto;
  height: auto;
  max-width: var(--ba-box-w);
  max-height: var(--ba-box-h);
  border-radius: clamp(8px, 0.83vw, 16px);
  /* THE HERO CARD'S OWN BLOOM, not a new one.

     This is `.ta-hero__card-wrap .ta-adcard`'s three-stop shadow verbatim —
     the one text-ads re-measured off the design render by stepping out from
     the card's edge (86 / 73 / 50 / 36 / 23 at 12 / 24 / 48 / 72 / 120px).
     A hand-rolled shadow sat here first and read flat next to it: one tight
     rim, one core and one wide wash is what carries the light out past 100px
     instead of stopping at the box.

     `--ta-glow` is the multiplier initCardFlight() runs 1 -> 0 as the card
     leaves the hero on text-ads. There is no flight on this page, so nothing
     writes it and every stop falls back to 1 — full strength, always, which
     is what the reference shows.

     It rides the slides' `transform: scale()`, so a side slot's bloom shrinks
     with it and stays in proportion. */
  box-shadow:
    0 0 14px rgba(150, 200, 255, calc(0.96 * var(--ta-glow, 1))),
    0 0 80px 9px rgba(13, 92, 255, var(--ta-glow, 1)),
    0 0 170px 12px rgba(13, 92, 255, calc(0.68 * var(--ta-glow, 1)));
}

/* --- the three live slots ------------------------------------------------ */
.banner-ads-page .ba-slide.is-center {
  transform: translate(-50%, -50%) scale(1);
  opacity: 1;
  z-index: 3;
}
.banner-ads-page .ba-slide.is-prev {
  transform: translate(calc(-50% - var(--ba-throw)), -50%) scale(var(--ba-side-scale));
  opacity: 1;
  z-index: 2;
}
.banner-ads-page .ba-slide.is-next {
  transform: translate(calc(-50% + var(--ba-throw)), -50%) scale(var(--ba-side-scale));
  opacity: 1;
  z-index: 2;
}

/* Phones: the throw is already most of the screen, so a side slide is a sliver
   and its shadow is most of what shows. Dropping them leaves the centre alone,
   which is the only slot that can be read at this width. */
@media (max-width: 575px) {
  .banner-ads-page .ba-slide.is-prev,
  .banner-ads-page .ba-slide.is-next { opacity: 0; }
}

/* The carousel is decoration that moves on its own clock, which is exactly
   what this setting is about. The centre still shows; it just stops advancing
   (hero-slider.js does not start its timer either) and nothing glides. */
@media (prefers-reduced-motion: reduce) {
  .banner-ads-page .ba-slide { transition: none; }
}

/* --- the carousel is draggable ------------------------------------------- */

/* The hit area is the CENTRE box, not the whole hero: it is where the readable
   banner is, and widening it would put an invisible drag surface over the
   headline and the CTA row sitting underneath. The side slots stay inert —
   they are cut in half by the page edge and there is nothing to grab. */
.banner-ads-page .ba-slider {
  cursor: grab;
  /* The slides carry the artwork and `pointer-events: none`, so every pointer
     event lands here, on one element, whichever size happens to be centred. */
  touch-action: pan-y;   /* horizontal is ours; the page still scrolls */
  -webkit-user-select: none;
          user-select: none;
}
.banner-ads-page .ba-slider.is-dragging { cursor: grabbing; }

/* Chrome and Safari start their own image-drag from a mousedown on an <img>,
   which steals the gesture and leaves `pointerup` unfired — the drag would
   stick on. The JS cancels `dragstart` as well; this stops it being offered. */
.banner-ads-page .ba-slide img {
  -webkit-user-drag: none;
  user-select: none;
}

/* A drag has to answer faster than the automatic step. 900ms is right for a
   change of scene nobody asked for; under the hand it reads as lag, so the
   glide shortens for as long as the drag lasts and goes back afterwards. */
.banner-ads-page .ba-slider.is-dragging { --ba-glide: 340ms; }

/* --------------------------------------------------------------------------
   S3 — the tablet demonstrates the six sizes

   text-ads' tablet is a fixed mock page with one card in it. This one changes
   shape: the reader picks a banner size and the mock page rearranges around
   it, because that rearranging IS the point — a Leaderboard runs across the
   top and pushes the article down, a Skyscraper stands in a rail beside it, a
   Square sits in the right-hand column.

   Every number comes down as a custom property from the <style> block
   banner-ads.php generates out of `$adSizes`, so the measured figures live in
   one place next to the note recording where they were measured. Nothing here
   hard-codes a size.
   -------------------------------------------------------------------------- */

/* The mock article. text-ads places its twelve bars directly on the screen at
   a fixed left/width; here they go in a column that moves, and the bars fill
   it. Their TOPS are untouched — the column is full height, so a `top: 10.98%`
   bar still resolves against the screen and `--ba-doc-t` slides all twelve at
   once for the two layouts that run along the top. */
.banner-ads-page .ba-tablet__doc {
  position: absolute;
  left: var(--ba-doc-l);
  top: var(--ba-doc-t);
  width: var(--ba-doc-w);
  height: 100%;
  /* The column and the creative both move; without this they arrive at their
     new places on different frames and the swap reads as two events. */
  transition:
    left  var(--ba-swap) var(--ba-ease),
    top   var(--ba-swap) var(--ba-ease),
    width var(--ba-swap) var(--ba-ease);
}

/* Overrides text-ads' screen-relative placement: inside the column the bars
   are simply full width, and the short ones keep their original PROPORTION
   of it (313/618), not their original screen percentage. */
.banner-ads-page .ba-tablet__doc .ta-device__bar {
  left: 0;
  width: 100%;
}
.banner-ads-page .ba-tablet__doc .ta-device__bar--short { width: 50.65%; }

/* --- the creative ------------------------------------------------------- */

/* All six are in the DOM; the chosen one is shown. `height: auto` is what
   keeps each at its true proportion — width is the only figure given, and the
   file's own aspect supplies the rest, which is why a Leaderboard cannot come
   out looking like a Square however the box is sized. */
.banner-ads-page .ba-tablet__ad {
  position: absolute;
  left: var(--ba-ad-l);
  top: 10.82%;                  /* measured: all six share one top edge */
  width: var(--ba-ad-w);
  height: auto;
  max-width: none;
  border-radius: clamp(4px, 0.7vw, 13px);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ba-swap) var(--ba-ease), visibility var(--ba-swap);
}
/* `[data-size]` on the tablet decides which one, so the whole swap is one
   attribute — and the geometry above is already the chosen size's, because
   the custom properties come off the same attribute. */
.banner-ads-page .ba-tablet[data-size="square"]           .ba-tablet__ad[data-size="square"],
.banner-ads-page .ba-tablet[data-size="medium-rectangle"] .ba-tablet__ad[data-size="medium-rectangle"],
.banner-ads-page .ba-tablet[data-size="wide-skyscraper"]  .ba-tablet__ad[data-size="wide-skyscraper"],
.banner-ads-page .ba-tablet[data-size="half-page"]        .ba-tablet__ad[data-size="half-page"],
.banner-ads-page .ba-tablet[data-size="mobile-banner"]    .ba-tablet__ad[data-size="mobile-banner"],
.banner-ads-page .ba-tablet[data-size="leaderboard"]      .ba-tablet__ad[data-size="leaderboard"] {
  opacity: 1;
  visibility: visible;
}

/* ROOM BELOW THE TABLET, and it is the picker that needs it.

   S3 is pinned while S4 climbs over it (initSectionStack), and the pill sits
   at the tablet's bottom-right — the first corner S4 covers. So the window in
   which the control can actually be clicked is however much scroll passes
   between the pill clearing the top of the screen and S4 reaching it.

   Measured at 1600x950 by walking the page and hit-testing the pill: with the
   section as text-ads leaves it that window is 300px, about two wheel notches,
   for a control the reader is meant to sit and play with. Bottom padding
   lengthens it directly — 220px of padding buys 450px of window.

   DESKTOP ONLY, and that qualifier is the whole point. `initSectionStack`
   runs at >= 1200 and nowhere else, so below it nothing climbs over this
   section, the picker is reachable for thousands of pixels of scroll, and
   this padding buys nothing at all — it only opens a hole.

   Unscoped it did exactly that. text-ads sets `padding-bottom: 0` on this
   section at <= 767 so the aurora plate meets S4's top edge with no seam and
   the device sits flush against the next section; two classes to its one, this
   rule outscored that and left 96px of dead band under the phone frame. Below
   1200 text-ads' own figures are the right ones and now stand.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) {
  .banner-ads-page .ta-what { padding-bottom: clamp(96px, 11vw, 220px); }
}

/* --------------------------------------------------------------------------
   The size picker

   Bottom-right of the screen, measured off the reference renders: the pill's
   right edge sits 4% in from the screen edge and its bottom 6% up. The list
   opens UPWARDS and lands on top of the pill — in the reference the last row
   sits exactly where the closed pill was, so the panel is bottom-anchored to
   the same line rather than floating above it.
   -------------------------------------------------------------------------- */
.banner-ads-page .ba-sizepick {
  position: absolute;
  right: 4%;
  bottom: 6%;
  z-index: 4;
  /* Everything in here is sized from this, so the control scales with the
     tablet instead of being pinned to a pixel size that only works at 1920. */
  font-size: clamp(9px, 0.85vw, 16px);
}

.banner-ads-page .ba-sizepick__btn,
.banner-ads-page .ba-sizepick__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.6em;
  width: 100%;
  font-family: var(--font-body);
  font-size: 1em;
  line-height: 1.35;
  text-align: left;
  white-space: nowrap;
}

/* The label is a one-cell grid holding every size name at once: the live one
   and a hidden copy of each of the others, all in the same cell. The cell
   takes the width of the widest, so the pill stops resizing as the selection
   changes — the jump was 113px on "Square" to 189px on "Medium Rectangle".

   `justify-items: start` keeps the live name left-aligned inside that cell
   rather than centred in it, which is where the reference has it. */
.banner-ads-page .ba-sizepick__label {
  display: grid;
  grid-template-areas: "name";
  justify-items: start;
  min-width: 0;
}
.banner-ads-page .ba-sizepick__label > * {
  grid-area: name;
  white-space: nowrap;
}
/* `visibility`, not `display: none` — a hidden box still occupies its grid
   cell and so still counts towards the width, which is the entire job. */
.banner-ads-page .ba-sizepick__ghost {
  visibility: hidden;
  pointer-events: none;
}

.banner-ads-page .ba-sizepick__btn {
  padding: 0.72em 1.05em;
  color: var(--c-blue);
  font-weight: 600;
  background-color: var(--c-surface);
  border: 1px solid rgba(255, 255, 255, 0.22);
  /* Fully rounded ends, per the reference. */
  border-radius: 999px;
  cursor: pointer;
  transition: border-color 180ms var(--ba-ease), background-color 180ms var(--ba-ease);
}
.banner-ads-page .ba-sizepick__btn:hover { border-color: rgba(255, 255, 255, 0.4); }
.banner-ads-page .ba-sizepick__btn:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 3px;
}
/* The pill is what the panel replaces, so it goes while the panel is up. */
.banner-ads-page .ba-sizepick.is-open .ba-sizepick__btn { opacity: 0; }

.banner-ads-page .ba-sizepick__list {
  position: absolute;
  right: 0;
  /* Bottom-anchored to the pill's own bottom edge, so the list grows upward
     and its last row lands where the pill was. */
  bottom: 0;
  min-width: 100%;
  margin: 0;
  padding: 0.5em 0;
  list-style: none;
  background-color: var(--c-surface);
  border: 1px solid rgba(255, 255, 255, 0.22);
  border-radius: clamp(6px, 0.8vw, 14px);
  /* Closed. Kept in the layout rather than `display: none` so the panel can be
     measured and animated; `visibility` is what takes it off hit-testing. */
  opacity: 0;
  visibility: hidden;
  transform: translateY(0.4em);
  transition:
    opacity 200ms var(--ba-ease),
    transform 200ms var(--ba-ease),
    visibility 200ms;
}
.banner-ads-page .ba-sizepick.is-open .ba-sizepick__list {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

.banner-ads-page .ba-sizepick__opt {
  padding: 0.5em 1.05em;
  color: var(--c-grey);
  cursor: pointer;
  transition: color 140ms var(--ba-ease), background-color 140ms var(--ba-ease);
}
.banner-ads-page .ba-sizepick__opt:hover,
.banner-ads-page .ba-sizepick__opt.is-cursor {
  color: var(--c-white);
  background-color: rgba(255, 255, 255, 0.06);
}
.banner-ads-page .ba-sizepick__opt[aria-selected="true"] {
  color: var(--c-blue);
  font-weight: 600;
}

/* The check. Drawn as a mask rather than an <img> so it takes `currentColor`
   — the row it sits on is blue when selected and the pill's is too, which an
   exported glyph would need a second file to manage. */
.banner-ads-page .ba-sizepick__tick {
  flex: none;
  width: 1.05em;
  height: 1.05em;
  background-color: currentColor;
  -webkit-mask-image: var(--ba-tick);
          mask-image: var(--ba-tick);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
  -webkit-mask-position: center;
          mask-position: center;
  -webkit-mask-size: contain;
          mask-size: contain;
}
/* Only the selected row shows one, but every row RESERVES the space, so the
   labels do not shuffle sideways as the selection moves. */
.banner-ads-page .ba-sizepick__opt[aria-selected="false"] .ba-sizepick__tick { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  .banner-ads-page .ba-tablet__doc,
  .banner-ads-page .ba-tablet__ad,
  .banner-ads-page .ba-sizepick__list { transition: none; }
}
