/* =========================================================================
   ad-unit.css — THE ADVERTISEMENT COMPONENT.

   Loaded by BOTH pages that render an advertisement:

     home.html   the public hero, where a visitor sees it
     index.html  the admin console, where the preview is built by the same
                 renderer in admin-news-ads.js

   It is a separate file for exactly that reason. These rules used to live
   inside home.html's inline <style>, which meant the admin preview produced
   the correct DOM with nothing to style it — the marquee wrapped onto four
   stacked lines instead of scrolling on one, and the media rendered square
   instead of 16:10. An administrator was approving a layout no visitor would
   ever see, which is the drift that sharing the renderer was supposed to
   prevent.

   It is not in theme.css because theme.css sits ~500 bytes under the
   leanness ceiling asserted by test/design-system.js, and this is several
   times that. Raising that ceiling to make room would retire a check that is
   doing real work.
   ========================================================================= */

/* =========================================================================
   ADVERTISEMENT
   =========================================================================
   BORDERLESS BY CONSTRUCTION. A promotional unit that looks like a bordered
   box reads as a banner ad and gets ignored; this is separated from the page
   by depth instead — two shadows at different spreads, a soft inner highlight
   along the top edge, and a mask that fades the media into the page rather
   than cutting it off.

   The three regions are in DOM order top-to-bottom, so the reading order and
   the visual order are the same thing and a screen reader gets the promotion
   in the sequence it was designed to be read.
   ========================================================================= */
.ad-unit{
  position:relative;
  border-radius:20px;
  overflow:hidden;
  isolation:isolate;
  /* No border. Depth does the separating. */
  box-shadow:
    0 1px 2px rgba(0,0,0,.35),
    0 18px 44px -18px rgba(0,0,0,.72),
    0 44px 90px -50px rgba(58,99,248,.42);
  background:var(--hp-card);
  transform:translateZ(0);
  transition:transform .28s ease, box-shadow .28s ease;
}
.ad-unit:hover{
  transform:translateY(-3px) translateZ(0);
  box-shadow:
    0 2px 4px rgba(0,0,0,.35),
    0 26px 60px -18px rgba(0,0,0,.78),
    0 60px 120px -55px rgba(58,99,248,.5);
}
/* A hairline of light along the top edge — the detail that makes a surface
   read as raised rather than pasted on. */
.ad-unit::before{
  content:"";position:absolute;inset-block-start:0;inset-inline:0;height:1px;z-index:4;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.22),transparent);
  pointer-events:none;
}

/* ---- THE HOME SHOWCASE --------------------------------------------------
   Full-bleed (on a phone) / near-full-bleed (desktop) cinema screen under
   the hero copy. It is a sibling of `.wrap`, so it escapes the content
   column. Aspect-ratio on the screen reserves the box from first paint;
   the creative and the empty fallback occupy that same box, so neither
   arrival nor absence can shift the page.

   Depth, in techniques not adjectives:
     1. token shadows (--shadow-2 over --shadow-3) under a served creative
     2. a radial --primary-soft wash that sits behind the screen and
        extends past its box (the lamp in the dark room)
     3. a static perspective rotateX on the screen, origin at the bottom
     4. a vertical-only scroll parallax on .ad-media (--ad-parallax)
     5. a top-edge mask on the EMPTY screen only. A served advertisement
        drops it: the marquee sits in that top band, and fading the frame
        there made the moving line unreadable. Side fade lives on the
        MEDIA, not the stage, so the description over the picture is not
        faded with the photograph.

   prefers-reduced-motion: reduce turns (3) and (4) off. Shadows and the
   wash stay: they are not motion.
   ------------------------------------------------------------------------ */
.ad-showcase{
  position:relative;
  z-index:1;
  width:100%;
  padding-inline:var(--sp-6);
  padding-block-start:var(--sp-6);
  padding-block-end:var(--sp-8);
  overflow:visible;
}
@media (max-width:560px){
  .ad-showcase{padding-inline:0;padding-block-start:var(--sp-5)}
}
.ad-showcase-screen{
  position:relative;
  width:100%;
  aspect-ratio: var(--ad-ratio, 2.35);
  isolation:isolate;
  /* Same transform on the empty wash and a served creative, so the
     reserved box does not visually shrink when an advertisement arrives.
     Layout is the aspect-ratio; this transform does not participate in it. */
  transform:perspective(1600px) rotateX(2.2deg);
  transform-origin:50% 100%;
  /* Empty-state dissolve into the hero. Cleared on .has-ad so the
     marquee and the description stay fully opaque. */
  -webkit-mask-image:linear-gradient(to bottom, transparent 0%, #000 16%, #000 100%);
  mask-image:linear-gradient(to bottom, transparent 0%, #000 16%, #000 100%);
}
.ad-showcase.has-ad .ad-showcase-screen{
  box-shadow:var(--shadow-2), var(--shadow-3);
  -webkit-mask-image:none;
  mask-image:none;
}
.ad-showcase-screen::before{
  content:"";
  position:absolute;
  inset:-12% -6% -16%;
  z-index:0;
  pointer-events:none;
  background:radial-gradient(closest-side at 50% 48%, var(--primary-soft), transparent 76%);
}
.ad-showcase-fallback{
  position:absolute;inset:0;z-index:0;
  background:
    radial-gradient(80% 70% at 18% 20%, var(--primary-soft), transparent 62%),
    radial-gradient(70% 80% at 88% 78%, var(--primary-soft), transparent 64%),
    linear-gradient(165deg, var(--surface-2), var(--plane));
  /* Empty state dissolves at the sides as well as the top. There is no
     sponsored bar to protect. */
  -webkit-mask-image:linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
  mask-image:linear-gradient(to right, transparent 0%, #000 8%, #000 92%, transparent 100%);
}
@media (max-width:900px){
  .ad-showcase-screen{aspect-ratio: var(--ad-ratio, 16 / 9)}
}
@media (max-width:560px){
  .ad-showcase-screen{
    aspect-ratio: var(--ad-ratio, 4 / 5);
    transform:perspective(900px) rotateX(1.1deg);
  }
}

/* When the advertisement named its own shape, keep that ratio on every
   viewport — a landscape file must not become 4:5 on a phone — and cap
   the height so a portrait file cannot push the rest of the page off-screen. */
.ad-showcase.has-ad-size .ad-showcase-screen{
  aspect-ratio: var(--ad-ratio, 2.35);
  max-block-size: 80vh;
  inline-size: min(100%, calc(80vh * var(--ad-ratio, 2.35)));
  margin-inline: auto;
}

/* ---- THE ADMIN-CHOSEN SIZE, WHEREVER THE UNIT IS DRAWN ----------------
   On the component itself rather than scoped to the hero, because the admin
   console renders the same unit with the same renderer and must show the
   same result — that is the entire reason the preview shares the renderer.
   Scoped to the hero, this rule left the preview at its natural size while
   the custom properties sat on it unread, so choosing a size appeared to do
   nothing.

   `min(..., 100%)` is what makes any chosen width safe: a value wider than
   whatever column the unit is sitting in collapses to that column instead
   of overflowing it. So the same declaration serves the hero, the preview,
   and any surface either of them is ever placed in. */
.ad-unit{inline-size:min(var(--ad-w, 100%), 100%)}

/* ---- THE HERO COLUMN HAS TO AGREE TO THE WIDTH ------------------------
   `min(..., 100%)` above keeps a chosen width safe, and on its own it also
   made every large width identical. The hero is a fixed two-column grid, so
   the column was always half of it, and the 100% the unit collapsed to was
   544px at every desktop viewport:

       chosen 280px -> rendered 280px
       chosen 400px -> rendered 400px
       chosen 560px -> rendered 544px    <- capped
       chosen 640px -> rendered 544px    <- capped, identical
       chosen 720px -> rendered 544px    <- capped, identical

   The form's upper half was therefore inert: three different settings drew
   the same box. home.js copies the chosen width onto the grid so the column
   can grow to it.

   BOUNDED AT 60%, deliberately. The headline shares this grid, and an
   advertisement that can take the whole row would let one setting wreck the
   page it sits on. 60% of a 1132px hero is 679px, which reaches all but the
   last 41px of the 720px maximum while leaving the headline over 400px — so
   the bound costs almost nothing and removes the failure mode entirely.

   INSIDE A MIN-WIDTH QUERY, WHICH IS LOAD-BEARING. home.html stacks the hero
   with `.hero-grid{grid-template-columns:minmax(0,1fr)}` under a
   `max-width:900px` query. A media query adds no specificity, so this rule —
   two classes against that rule's one — won on a phone as well, and the
   advertisement kept a fixed side column: the headline was squeezed into 117px
   of a 430px screen and its words were cut off. The two-column layout is the
   only place a side column means anything, so the width only applies there. */
@media (min-width: 901px){
  .hero-grid.has-ad-width{
    grid-template-columns:minmax(0, 1fr) minmax(0, min(var(--ad-w, 50%), 60%));
  }
}

#heroVisual .ad-unit,
#heroVisual .ad-unit.has-height,
.ad-showcase .ad-unit,
.ad-showcase .ad-unit.is-rotating,
.ad-showcase .ad-unit.has-height{
  position:absolute;
  inset:0;
  inline-size:100%;
  aspect-ratio:auto;
  min-block-size:0;
  max-block-size:none;
  block-size:auto;
  border-radius:0;
  overflow:hidden;
  background:transparent;
  box-shadow:none;
  display:flex;
  flex-direction:column;
  transform:none;
}

/* ---- WHEN A HEIGHT IS SET ---------------------------------------------
   `aspect-ratio` and an explicit height cannot both win, so the ratio is
   switched off rather than left to fight it: the administrator has said
   what shape they want. The clamp is what keeps the result inside what the
   hero can actually hold, whatever number arrived. */
.ad-unit.has-height{
  aspect-ratio:auto;
  block-size:clamp(220px, var(--ad-h, 360px), 560px);
  min-block-size:0;
  max-block-size:560px;
}

/* ---- THE SAME RULE, LOUD ENOUGH TO BE HEARD IN THE HERO ---------------
   THIS IS NOT A DUPLICATE. `#heroVisual .ad-unit` above scores an id, so it
   beat `.ad-unit.has-height` on specificity and the height a chosen box was
   given was silently discarded. Measured before this block existed, at every
   viewport that shows the two-column hero:

       chosen 220px -> rendered 360px     (the hero's min-block-size won)
       chosen 320px -> rendered 360px
       chosen 440px -> rendered 430px     (the hero's max-block-size won)
       chosen 560px -> rendered 430px

   So the form offered a 220-560px range and the page could only express
   360-430px of it. Two thirds of the control did nothing, and because the
   media is `object-fit:cover`, the little that did move REFRAMED THE PICTURE
   instead of resizing the box — which is exactly what "the box does not
   resize, the image zooms instead" describes.

   Written as its own block, with the id in the selector, so the intent is
   legible: the hero's responsive defaults apply until an administrator states
   a height, and then the stated height applies. */
#heroVisual .ad-unit.has-height{
  aspect-ratio:auto;
  block-size:auto;
  min-block-size:0;
  max-block-size:none;
}

/* The stage carries a 260px floor so an advertisement with no height still
   has a usable picture area. That floor becomes a second, hidden minimum once
   a height IS set: 260px of stage plus the marquee and the call to action
   cannot fit in a 220px box, so the box grows and the chosen height is lost
   again. Releasing it only in the has-height case keeps the default intact. */
.ad-unit.has-height .ad-stage{min-block-size:0}
/* The stage takes whatever the marquee and CTA do not, so the unit honours
   its aspect ratio instead of being pushed taller by its own contents. */
#heroVisual .ad-stage{flex:1 1 auto;min-block-size:0;overflow:hidden}
#heroVisual .ad-unit:hover,
.ad-showcase .ad-unit:hover{transform:none;box-shadow:none}
.ad-showcase .ad-unit::before{content:none}
/* Side fade on the PICTURE. It used to sit on the stage, which also holds
   the description — in a right-to-left page that text starts on the faded
   edge and became the thing you could not read. */
.ad-showcase .ad-media{
  transform:translate3d(0, var(--ad-parallax, 0px), 0) scale(var(--media-zoom, 1));
  -webkit-mask-image:linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
  mask-image:linear-gradient(to right, transparent 0%, #000 7%, #000 93%, transparent 100%);
}

@media (prefers-reduced-motion: reduce){
  .ad-unit, .ad-unit:hover, #heroVisual .ad-unit:hover,
  .ad-showcase, .ad-showcase-screen, .ad-showcase .ad-unit,
  .ad-showcase .ad-unit:hover,
  .ad-showcase.has-ad .ad-showcase-screen{
    transition:none;transform:none
  }
  /* Static framing zoom is not motion. Parallax and perspective are. */
  .ad-showcase .ad-media{
    transform:scale(var(--media-zoom, 1))
  }
}
@media (max-width: 900px){
  .hero-grid{gap:36px}
}

/* ---- SEVERAL ADVERTISEMENTS TAKING TURNS IN ONE BOX --------------------
   Only present when there are two or more. A single advertisement keeps the
   original DOM — regions as direct children, in normal flow — so the common
   case is untouched by any of this.

   The slides are absolutely positioned so they occupy the same space and the
   box does not grow to the height of all of them stacked. That means the UNIT
   must supply its own height, which it does: `aspect-ratio` in the hero, or an
   explicit `block-size` when a height was chosen. Both are set on the unit, not
   derived from content, so removing the slides from flow cannot collapse it. */
.ad-unit.is-rotating{position:relative}
.ad-slides{position:absolute;inset:0}
.ad-slide{
  position:absolute;inset:0;
  display:flex;flex-direction:column;
  opacity:0;
  /* Not `display:none`: a cross-fade needs both slides painted, and a hidden
     video would restart from the beginning every time it came round. */
  pointer-events:none;
  transition:opacity .5s ease;
}
.ad-slide.is-active{opacity:1;pointer-events:auto;z-index:1}
/* The stage inside a slide behaves exactly as it does inside a unit: it takes
   what the marquee and the call to action leave. */
.ad-slide .ad-stage{flex:1 1 auto;min-block-size:0;overflow:hidden}

/* ---- THE DOTS ----------------------------------------------------------
   Above the slides and OUTSIDE them, so they survive every transition instead
   of fading out with the slide they belong to.

   Anchored to the bottom edge of the unit and centred. The call to action bar
   is the bottom of each slide, so the room for them is made by padding that
   bar rather than by guessing its height here — it wraps to two lines on a
   phone, and any fixed offset would be wrong on one of the two. */
.ad-dots{
  position:absolute;z-index:3;
  inset-block-end:9px;
  inset-inline:0;
  display:flex;justify-content:center;gap:7px;
  pointer-events:none;
}
.ad-unit.is-rotating .ad-cta{padding-block-end:28px}
.ad-dots > *{pointer-events:auto}
.ad-dot{
  /* 22px of hit area around a 7px mark. The mark is what is seen; the target
     is what is tapped, and a 7px target on a phone is not a target. */
  inline-size:22px;block-size:22px;padding:0;border:0;background:none;
  display:grid;place-items:center;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.ad-dot::before{
  content:"";
  inline-size:7px;block-size:7px;border-radius:50%;
  background:rgba(255,255,255,.42);
  box-shadow:0 0 0 1px rgba(0,0,0,.25);
  transition:background .18s ease, inline-size .18s ease, border-radius .18s ease;
}
.ad-dot:hover::before{background:rgba(255,255,255,.72)}
/* The active dot becomes a short bar rather than a bigger circle: the shape
   difference survives being seen at a glance over a busy photograph, where a
   difference in size alone does not. */
.ad-dot.is-active::before{background:#fff;inline-size:18px;border-radius:4px}
.ad-dot:focus-visible{outline:2px solid #9DB6FF;outline-offset:1px;border-radius:6px}

@media (prefers-reduced-motion: reduce){
  .ad-slide{transition:none}
  .ad-dot::before{transition:none}
}
@media (forced-colors: active){
  .ad-dot::before{background:CanvasText}
  .ad-dot.is-active::before{background:Highlight}
}

/* ---- TOP: the moving promotional line ---------------------------------- */
.ad-marquee{
  position:relative;z-index:3;
  overflow:hidden;
  padding-block:10px;
  /* Opaque enough that the line reads on any photograph. A 42% wash left
     the words looking like part of the picture. */
  background:rgba(7,9,16,.88);
  backdrop-filter:blur(10px);
  border-block-end:1px solid rgba(255,255,255,.16);
}
.ad-marquee-track{
  display:flex;gap:0;inline-size:max-content;
  animation:adMarquee 26s linear infinite;
  will-change:transform;
}
[dir="rtl"] .ad-marquee-track{animation-name:adMarqueeRtl}
@keyframes adMarquee   { from{transform:translateX(0)} to{transform:translateX(-50%)} }
@keyframes adMarqueeRtl{ from{transform:translateX(0)} to{transform:translateX(50%)} }
/* Hover used to pause the line so a reader could catch it. The operator
   asked that it never stop under the pointer — pausing looked like a freeze. */
.ad-marquee-item{
  padding-inline:26px;white-space:nowrap;
  font-size:13.5px;font-weight:700;letter-spacing:.01em;color:#fff;
  text-shadow:0 1px 2px rgba(0,0,0,.9);
}
@media (prefers-reduced-motion: reduce){
  .ad-marquee-track{animation:none;inline-size:auto;overflow-x:auto}
  .ad-marquee-track > .ad-marquee-dup{display:none}
}

/* ---- CENTRE: media as background, description over it ------------------ */
.ad-stage{
  position:relative;
  min-block-size:260px;
  display:flex;align-items:flex-end;
  /* THE STAGE ALWAYS CLIPS.
     This is structural to how zoom works, not a hero detail. A scaled
     picture is larger than its box by definition, and without clipping here
     it spills over the marquee and the call to action. It was scoped to
     `#heroVisual` and the admin preview inherited `visible`, so a zoomed
     preview would have shown an overflow a visitor never sees — the preview
     lying about the product again. Measured at zoom 160 before this line. */
  overflow:hidden;
}
/* ---- MEDIA FITTING ----------------------------------------------------
   The box is now whatever size an administrator chose, and the uploaded
   file is whatever shape it happens to be. Those two will rarely agree, and
   the three ways of resolving that are not equally good:

     fill      stretches. Distorts every face and every logo. Never.
     contain   preserves the shape and leaves bars of empty box around it.
               Honest, but in a promotional unit it reads as a mistake.
     cover     preserves the shape and crops the overflow.

   `cover` is right here: it fills the box at ANY width and height with no
   distortion, which is exactly what changing the box size demands. A
   promotional image is composed to be looked at rather than measured, so
   losing some edge is acceptable where warping the subject is not.

   `object-position: center` because the subject of a promotional image is
   almost always centred, so the crop that keeps the middle keeps the point.
   ---------------------------------------------------------------------- */
.ad-media{
  position:absolute;inset:0;z-index:0;
  inline-size:100%;block-size:100%;
  object-fit:cover;
  /* THE FOCAL POINT. Which part of the picture survives the crop. Defaults
     to centre, which is where the subject of a promotional image usually
     is; an administrator moves it when it is not. */
  object-position:var(--media-x, 50%) var(--media-y, 50%);
  /* THE ZOOM, AND WHY IT CANNOT RESIZE THE BOX.
     A transform does not participate in layout. Scaling the picture cannot
     change the width or height of anything around it — not the stage, not
     the unit, not the Hero grid — even in principle. That is what makes box
     size and media zoom independent structurally rather than by convention.
     The stage clips the overflow, so zooming in crops rather than spilling. */
  transform:scale(var(--media-zoom, 1));
  transform-origin:var(--media-x, 50%) var(--media-y, 50%);
  /* Its own layer, so resizing the box repaints the picture rather than the
     whole unit — otherwise the marquee visibly hitches while dragging. */
  will-change:transform;
  backface-visibility:hidden;
  /* Edge melt on the photograph only. Overlay copy is a sibling above this
     and must not inherit a fade. */
  -webkit-mask-image:radial-gradient(125% 125% at 50% 45%, #000 78%, rgba(0,0,0,.82) 100%);
  mask-image:radial-gradient(125% 125% at 50% 45%, #000 78%, rgba(0,0,0,.82) 100%);
}
/* A file smaller than the box in BOTH axes. Enlarging it past its own
   resolution looks soft and cheap, so it sits at its natural size on the
   branded ground instead — set by the renderer, which knows the intrinsic
   dimensions the CSS cannot see. */
.ad-media.is-small{object-fit:scale-down}
/* A scrim, not a tint: text over an arbitrary photograph needs a predictable
   floor of contrast, and it has to be strongest where the words are. */
.ad-stage::after{
  content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  /* Light over most of the picture; the reading plate on .ad-copy is what
     carries the words, so the photograph itself is not drowned. */
  background:linear-gradient(180deg,
    rgba(6,8,14,.06) 0%,
    rgba(6,8,14,.18) 52%,
    rgba(6,8,14,.42) 100%);
}
.ad-copy{
  position:relative;z-index:2;
  padding:22px 24px 18px;
  max-inline-size:62ch;
  background:linear-gradient(180deg,
    rgba(6,8,14,0) 0%,
    rgba(6,8,14,.62) 28%,
    rgba(6,8,14,.9) 100%);
}
/* One block of copy, sized to be read over a photograph rather than to
   compete with the Hero's own headline beside it. */
.ad-copy p{
  margin:0;font-size:clamp(16px,1.6vw,18px);line-height:1.7;
  color:#fff;font-weight:700;
  text-shadow:0 1px 2px rgba(0,0,0,.95), 0 2px 18px rgba(0,0,0,.75);
  display:-webkit-box;-webkit-line-clamp:4;-webkit-box-orient:vertical;overflow:hidden;
}
/* No media configured: a branded gradient rather than a grey hole. */
.ad-stage.is-blank{
  background:
    radial-gradient(70% 120% at 12% 0%, rgba(58,99,248,.34), transparent 62%),
    /* The accent's darker step, not a second hue — see DESIGN_SYSTEM.md. */
    radial-gradient(60% 110% at 92% 100%, rgba(42,75,204,.32), transparent 64%),
    linear-gradient(160deg, var(--bg-surface-2), var(--bg-base));
}
.ad-stage.is-blank::after{
  background:linear-gradient(180deg,transparent 40%,rgba(6,8,14,.55) 100%);
}

/* ---- BOTTOM: the call to action ---------------------------------------- */
.ad-cta{
  position:relative;z-index:3;
  display:flex;align-items:center;justify-content:space-between;gap:14px;
  padding:14px 20px;
  background:rgba(9,10,13,.72);
  backdrop-filter:blur(6px);
  border-block-start:1px solid rgba(255,255,255,.07);
  flex-wrap:wrap;
}
.ad-disclosure{
  font-size:10.5px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:rgba(226,232,245,.72);
}
:root:lang(ar) .ad-disclosure, :root:lang(ckb) .ad-disclosure{
  letter-spacing:normal;text-transform:none;font-size:11.5px;
}
/* Home cinema: the label is a chip in the same pairing as .btn-primary so
   it stays identifiable as advertising in every theme, and cannot dissolve
   into editorial type. */
.ad-showcase .ad-disclosure{
  display:inline-flex;
  align-items:center;
  padding-block:var(--sp-1);
  padding-inline:var(--sp-3);
  border-radius:var(--radius-pill);
  background:var(--primary);
  color:var(--text-inverse);
  font-size:var(--fs-caption);
  letter-spacing:.08em;
  opacity:1;
}
:root:lang(ar) .ad-showcase .ad-disclosure,
:root:lang(ckb) .ad-showcase .ad-disclosure{
  letter-spacing:normal;text-transform:none;font-size:var(--fs-caption);
}

/* ---- THE DESTINATION ICONS --------------------------------------------
   Up to four badges, no visible text. The mark is the whole control, so it
   has to be large enough to hit and obviously interactive.

   36px, which is not arbitrary: it is the smallest square that still clears
   the 24px minimum for adjacent targets once the 10px gap is counted, on the
   assumption that a thumb — not a mouse — is what reaches for these. The SVG
   inside stays 18px so the badge reads as a button with a mark in it rather
   than as a mark with a border.

   `margin-inline-start:auto` pushes the row away from the disclosure and up
   against the call to action, and works unchanged in a right-to-left page,
   which `margin-left` would not. */
.ad-social-row{
  display:flex;align-items:center;gap:10px;
  margin-inline-start:auto;
}
.ad-social{
  display:grid;place-items:center;
  inline-size:36px;block-size:36px;
  border-radius:11px;
  color:#EAF0FF;
  background:rgba(255,255,255,.10);
  border:1px solid rgba(255,255,255,.16);
  transition:background .16s ease, border-color .16s ease,
             transform .16s ease, color .16s ease;
}
.ad-social svg{inline-size:18px;block-size:18px;display:block}
.ad-social:hover,
.ad-social:focus-visible{
  background:rgba(255,255,255,.19);
  border-color:rgba(255,255,255,.30);
  transform:translateY(-1px);
}
/* The focus ring is drawn against the dark bar the badges sit on, not against
   the page background, so the page's default ring colour would disappear. */
.ad-social:focus-visible{outline:2px solid #9DB6FF;outline-offset:2px}

/* EACH PLATFORM'S OWN COLOUR, ON HOVER ONLY.
   At rest the row is one material and reads as a set. On hover the badge
   adopts the brand's colour, which confirms what the mark says without
   turning the resting state into a row of competing logos.

   Colours are the brands' own, used on a hover background rather than as a
   recoloured logo, which is what most brand guidelines actually permit. */
.ad-social.is-telegram:hover  {background:#229ED9;border-color:#229ED9;color:#fff}
.ad-social.is-whatsapp:hover  {background:#25D366;border-color:#25D366;color:#0A2B14}
.ad-social.is-facebook:hover  {background:#1877F2;border-color:#1877F2;color:#fff}
.ad-social.is-instagram:hover {background:#E1306C;border-color:#E1306C;color:#fff}
.ad-social.is-youtube:hover   {background:#FF0000;border-color:#FF0000;color:#fff}
.ad-social.is-tiktok:hover    {background:#000;border-color:#69C9D0;color:#fff}
.ad-social.is-x:hover         {background:#000;border-color:#000;color:#fff}
.ad-social.is-snapchat:hover  {background:#FFFC00;border-color:#FFFC00;color:#1A1A00}
.ad-social.is-viber:hover     {background:#7360F2;border-color:#7360F2;color:#fff}
.ad-social.is-linkedin:hover  {background:#0A66C2;border-color:#0A66C2;color:#fff}
.ad-social.is-discord:hover   {background:#5865F2;border-color:#5865F2;color:#fff}
.ad-social.is-website:hover   {background:var(--accent, #3A63F8);border-color:var(--accent, #3A63F8);color:#fff}

@media (prefers-reduced-motion: reduce){
  .ad-social, .ad-social:hover, .ad-social:focus-visible{transition:none;transform:none}
}
/* Forced colours: the brand fills are dropped by the browser anyway, so make
   sure the badge is still a visible, bounded control. */
@media (forced-colors: active){
  .ad-social{border-color:CanvasText;color:CanvasText}
}

:root[data-theme="light"] .ad-unit,
:root[data-theme="system"] .ad-unit{
  box-shadow:
    0 1px 2px rgba(26,31,43,.08),
    0 18px 44px -20px rgba(26,31,43,.28),
    0 44px 90px -55px rgba(42,75,216,.30);
}
@media (prefers-color-scheme: dark){
  :root[data-theme="system"] .ad-unit{
    box-shadow:
      0 1px 2px rgba(0,0,0,.35),
      0 18px 44px -18px rgba(0,0,0,.72),
      0 44px 90px -50px rgba(58,99,248,.42);
  }
}

@media(max-width:640px){
  .ad-stage{min-block-size:220px}
  .ad-copy{padding:20px 18px 16px}
  .ad-cta{padding:12px 16px}
  /* Four badges, a disclosure and a button do not fit one line on a phone.
     The row keeps its own line and stays a comfortable size — shrinking the
     targets instead is the wrong trade on the device where they are tapped. */
  .ad-social-row{margin-inline-start:0;gap:8px}
  .ad-social{inline-size:34px;block-size:34px}
}
