/* 13 — Carousels
   Owns: the bxSlider chrome reset, the video card, and the two containers that
         lay it out — the carousel track and the .rw-video-grid
   Depends on: 01-tokens

   bxSlider ships a visible frame on .bx-wrapper — white background, a 5px
   white border and a grey drop shadow — from
   plugins/bb-plugin/css/jquery.bxslider.css. That frame is what reads as "the
   testimonial module has a background": the review cards themselves are
   already white with a 20px radius, supplied by the rw-testimonials plugin's
   own stylesheet, and are not touched here.

   Deliberately NOT in 90-agape.css. Removing bxSlider's default frame is not
   an Agape-specific decision — any site running this carousel wants the cards
   to float on the section background rather than sit in a white box — so it
   belongs upstream where a fork inherits it.

   :has() because .bx-wrapper is generated by bxSlider as an ANCESTOR of our
   .rw-testimonial-carousel, so it cannot be reached by descent. This keeps the
   reset off any other bxSlider instance on the site, which is why it is not
   simply `.bx-wrapper { … }`. */
.bx-wrapper:has(.rw-testimonial-carousel),
.bx-wrapper:has(.rw-video-carousel) {
    background: transparent;
    border: 0;
    box-shadow: none;
}

/* ── Arrows ─────────────────────────────────────────────────
   bxSlider's defaults are a sprite: a background-image of grey rollover
   circles, with the link text pushed away by `text-indent: -9999px`. Replaced
   with Font Awesome chevrons.

   Both resets matter. `background: none` drops the sprite; `text-indent: 0` is
   what lets a pseudo-element show at all — inherited, -9999px would carry the
   glyph off-screen with the label.

   The family list covers the two Font Awesome builds this site can serve.
   bb-plugin ships 7.2.0, whose stylesheet also registers the "Font Awesome 5
   Free" and legacy "FontAwesome" names, and ShiftNav loads its own FA4 under
   the latter. Solid needs weight 900; the glyphs are chevron-left (f053) and
   chevron-right (f054), whose codepoints are unchanged across those versions.

   :is(a, button) because THE TAG DEPENDS ON WHICH bxSlider BUILD RUNS, and that
   is not ours to decide. Two load on every page: bb-plugin's fork (handle
   jquery-bxslider-js) and rw-testimonials' vendored 4.2.17 (handle bxslider).
   Upstream bxSlider renders the arrows as <a>; bb-plugin's fork renders
   <button class="… fl-content-ui-button"> unless `fallbackHTML` is set.

   Since rw-testimonials 1.2.4 the plugin stashes its own copy as
   jQuery.fn.rwBxSlider and hands jQuery.fn.bxSlider back to whoever held it —
   deliberately, so the two builds stop fighting over the global. The
   consequence here is that rw-video-carousel.js, which calls the global,
   now gets bb-plugin's fork and therefore <button> arrows. Selecting on the
   tag is what broke; selecting on either tag cannot break again whichever
   build wins next. */
.bx-wrapper:has(.rw-testimonial-carousel) .bx-controls-direction :is(a, button),
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction :is(a, button) {
    display: flex;
    align-items: center;
    justify-content: center;
    /* border-box so --video-arrow-size is the WHOLE arrow, border included.
       Not inherited: bb-plugin's stylesheet does `all: unset` on
       .bx-wrapper button, which resets box-sizing to content-box, so its fork's
       <button> arrows measured 42px against upstream's 40px <a> ones. The
       offsets below are derived from this number — it has to be true. */
    box-sizing: border-box;
    width: var(--video-arrow-size);
    height: var(--video-arrow-size);
    background: none;
    /* bxSlider hides its "Prev"/"Next" labels with text-indent: -9999px, which
       a ::before would inherit and follow off-screen. Reset that, then collapse
       the label with font-size: 0 and give the glyph its own size below.
       font-size: 0 rather than restoring the indent because the label stays in
       the accessibility tree — the arrows keep their names for screen
       readers, which text-indent also preserved but visually-hidden tricks
       like display: none would not. */
    text-indent: 0;
    font-size: 0;
    border: 1px solid var(--border-strong);
    border-radius: 50%;
    color: var(--brand-dark);
    transition: var(--transition-base);
    /* bxSlider ships these at z-index: 9999, which beats Magnific Popup's
       stacking (1042-1046) — the arrows painted on top of the open lightbox.
       Nothing needs a value that high: within .bx-wrapper only .bx-loading
       carries a z-index (2000) and the slides carry none, so 1 is enough to
       sit above the track while staying under the lightbox. */
    z-index: 1;
}

.bx-wrapper:has(.rw-testimonial-carousel) .bx-controls-direction :is(a, button)::before,
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction :is(a, button)::before {
    font-family: "Font Awesome 7 Free", "Font Awesome 5 Free", "FontAwesome";
    font-weight: 900;
    font-size: 0.875rem;
    line-height: 1;
}

.bx-wrapper:has(.rw-testimonial-carousel) .bx-prev::before,
.bx-wrapper:has(.rw-video-carousel) .bx-prev::before { content: "\f053"; }

.bx-wrapper:has(.rw-testimonial-carousel) .bx-next::before,
.bx-wrapper:has(.rw-video-carousel) .bx-next::before { content: "\f054"; }

.bx-wrapper:has(.rw-testimonial-carousel) .bx-controls-direction :is(a, button):hover,
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction :is(a, button):hover,
.bx-wrapper:has(.rw-testimonial-carousel) .bx-controls-direction :is(a, button):focus-visible,
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction :is(a, button):focus-visible {
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--white);
}

/* bxSlider centres the arrows vertically over the slides, which on the video
   carousel puts them on top of the play buttons. Move them below the track and
   right, out of the artwork. Kept rather than turned off — the design frames
   show no arrows, but an auto-advancing carousel with no manual control cannot
   be operated by keyboard.

   `bottom` is negative because it is measured on .bx-wrapper, whose box ends at
   the bottom of the track: pushing the arrows below it means pushing them
   outside. There is room — bxSlider's own 60px wrapper margin plus the module's
   leaves ~130px of clear space under the track at every width.

   THE OFFSET MUST INCLUDE THE ARROW'S OWN HEIGHT. This previously read
   `calc(var(--grid-gap) * -2)`, and --grid-gap is clamp(16px, 2vw, 30px), so
   the offset ranged 32-60px against a 40px arrow. It only cleared the track
   while 2 * grid-gap exceeded that: measured, the clearance ran 18px at 1600
   but 9px at 1280, 0 at 1024 and -10px at 767 — by tablet the arrows had
   climbed back over the bottom of the cards. Any expression that does not start
   from --video-arrow-size has the same bug at some width.

   The gap token is what varies now, and only between 12px and 20px, so the
   arrows sit a consistent short distance under the track at every size rather
   than drifting with the viewport. */
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction :is(a, button) {
    top: auto;
    bottom: calc((var(--video-arrow-size) + var(--video-arrow-gap)) * -1);
    margin-top: 0;
}

/* Right-aligned to the track. The pair's own spacing is deliberately NOT the
   responsive gap: two arrows that belong to each other should stay the same
   distance apart at every width, and only their distance from the track above
   needs to breathe. */
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction .bx-prev {
    left: auto;
    right: calc(var(--video-arrow-size) + 12px);
}

.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction .bx-next { right: 0; }

/* bb-plugin's fork flags EVERY link and button inside the enclosing .fl-module
   as .disabled for the duration of a transition (jquery.bxslider.js:1378, undone
   at :893) — a "working" latch, not a real disabled state. Its own stylesheet
   fades .disabled arrows to opacity .2, so with `auto` running that reads as the
   arrows blinking every few seconds.

   Only meaningful with infiniteLoop: false, where bxSlider genuinely parks on an
   end and the fade is the intended affordance. This carousel loops, so its
   arrows are never truly at an end and the class is always transient.

   Scoped to the arrows: the same latch lands on the .rw-video-card anchors too,
   where nothing styles .disabled, so there is nothing to undo there. */
.bx-wrapper:has(.rw-video-carousel) .bx-controls-direction :is(a, button).disabled {
    opacity: 1;
    cursor: pointer;
}


/* ── Video carousel ─────────────────────────────────────────
   Markup contract — a BB HTML module containing:

     <div class="rw-video-carousel">
       <a class="rw-video-card" href="https://vimeo.com/123456789">
         <img class="rw-video-card__thumb" src="…" alt="" loading="lazy">
         <span class="rw-video-card__title">Project Title 1</span>
       </a>
       …one <a> per video…
     </div>

   The <img> is OPTIONAL. Without it the card is a flat --video-card-bg panel
   with the play button on it, which is exactly what the design frames show.

   The anchor's href is the public Vimeo URL, not an embed URL: Magnific Popup
   ships a Vimeo pattern that rewrites it to player.vimeo.com with autoplay,
   so the markup stays readable and works without JS as a plain link to the
   video. The play button is drawn with pseudo-elements rather than markup, so
   the anchor's accessible name is just the title text. */

.rw-video-card {
    position: relative;
    display: block;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background-color: var(--video-card-bg);
    color: var(--white);
    text-decoration: none;
    border-radius: var(--rw-small-radius);
}

.rw-video-card:hover,
.rw-video-card:focus-visible { color: var(--white); text-decoration: none; }

.rw-video-card:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.rw-video-card__thumb {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Play button: ::before is the disc, ::after the triangle. Drawn rather than
   an icon font or SVG so it needs no asset and inherits the card's colours. */
.rw-video-card::before,
.rw-video-card::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    z-index: 1;
}

.rw-video-card::before {
    width: var(--video-play-size);
    height: var(--video-play-size);
    margin: calc(var(--video-play-size) / -2) 0 0 calc(var(--video-play-size) / -2);
    border-radius: 50%;
    background-color: var(--white);
    transition: transform var(--header-transition);
    opacity: 0.7;
}

/* Nudged right of centre: a triangle's optical centre sits left of its
   bounding box, so centring the box makes it look off. */
.rw-video-card::after {
    margin: -9px 0 0 -5px;
    border-left: 16px solid var(--black);
    border-top: 9px solid transparent;
    border-bottom: 9px solid transparent;
}

.rw-video-card:hover::before,
.rw-video-card:focus-visible::before { transform: scale(1.08); }

@media (prefers-reduced-motion: reduce) {
    .rw-video-card::before { transition: none; }
}

/* Title sits on a gradient rather than the raw card, so it stays readable once
   a thumbnail is behind it. Harmless on the flat-colour version.

   ONE LINE, ellipsised. A long title otherwise wraps to three lines, grows
   upwards and collides with the play button — the state the Entertainment
   Weekly card was in.

   The cut is made by the card's own width, not by a character count, so it
   stays right whatever the slide width becomes. It is NOT a fixed number of
   characters and cannot be: this is a proportional face, so a title of Is and
   quotes fits far more than one of Ms and Ws. Measured on the live titles at
   the 340px slide width and --video-title-padding at its 20px ceiling, the cut
   falls around 38 characters (it was ~36 while this used --card-padding's
   30px). If a specific count is ever the requirement rather than a
   consequence, cap .rw-video-card__title with a max-width in ch — do not move
   the padding to get there, which is what sets the gradient's breathing room.

   A character limit in the shortcode could not track the width at all, and
   would truncate the accessible name too — here the full title stays in the
   DOM and is what a screen reader announces.

   text-overflow needs all three declarations: without nowrap the text wraps
   instead of overflowing, and without overflow: hidden there is nothing for the
   ellipsis to replace. */
.rw-video-card__title {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 1;
    padding: var(--video-title-padding);
    /* Doubled on top so the text clears the steep part of the gradient below
       it rather than sitting in the fade. */
    padding-top: calc(var(--video-title-padding) * 2);
    background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.55));
    color: var(--white);
    font-weight: 600;
    font-size: 0.875rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* bxSlider assigns inline widths to slides and computes the viewport height
   from them, so the cards get their height from aspect-ratio and nothing here
   sets one. If the viewport height ever comes out wrong, fix it with
   `adaptiveHeight` in the init — not with an !important here, which would only
   be needed to beat bxSlider's own inline style and is barred outside
   99-utilities.css. */


/* ── Lightbox close button ──────────────────────────────────
   Magnific Popup renders this popup with .mfp-close-btn-in, which places the
   close button INSIDE the content — directly over Vimeo's own controls in the
   top-right of the player. Moved clear of the video entirely.

   Two things make the offsets actually apply:

   1. The scope is a DESCENDANT selector. mainClass puts .rw-video-popup on
      .mfp-wrap, while .mfp-iframe-holder lands on the .mfp-container INSIDE
      it — so a compound `.rw-video-popup.mfp-iframe-holder` matches nothing.
   2. width is reset from MP's `width: 100%`. With a full-width box, `right`
      shifts the whole block and the glyph does not visibly move, because MP
      right-aligns the text inside it.

   Colour is set explicitly: .mfp-close-btn-in .mfp-close forces #333, which
   would be near-invisible now the button sits on the dark overlay rather than
   on the video.

   Scoped so Beaver Builder's own Magnific Popup lightboxes, which share every
   .mfp-* class, keep their default close button. */
.rw-video-popup .mfp-close {
    top: -44px;
    right: -44px;
    width: 44px;
    padding-right: 0;
    text-align: center;
    color: var(--white);
    opacity: 0.85;
}

.rw-video-popup .mfp-close:hover,
.rw-video-popup .mfp-close:focus-visible { opacity: 1; }


/* ── Video grid ─────────────────────────────────────────────
   The same .rw-video-card, laid out as a grid instead of a track. Emitted by
   [rw_video_grid] (includes/vimeo-thumbnails.php) as:

     <div class="rw-grid rw-video-grid">…cards…</div>

   .rw-grid supplies `display: grid` and the shared --grid-gap. The columns are
   here rather than from .rw-cols-3 in 12-cards.css ON PURPOSE: those fold at
   767/991 to suit text cards, and a 16:9 thumbnail with a one-line title reads
   perfectly well two-up much earlier than a card of body copy does. Carrying
   both class families on one element would also leave two rules of identical
   specificity settling the layout on source order, which is not a thing to
   leave to the enqueue list.

   min-width, against this theme's usual max-width house style, because the
   ladder IS mobile-first: one column is the base case, not an override of a
   three-column default. Written the other way round the single-column rule
   would have to restate itself below every step. The base covers anything
   narrower than 640px, including below the 320px the spec starts at. */
.rw-video-grid {
    grid-template-columns: minmax(0, 1fr);
}

@media (min-width: 640px) {
    .rw-video-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 960px) {
    .rw-video-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}
