/**
 * Banner image slider.
 *
 * Deliberately a file of its own rather than an addition to front.css: themes override
 * modules/an_homeproducts/views/css/front.css wholesale (vapestore does), which silently
 * dropped every slider rule and left the carousel hidden behind Owl's
 * `.owl-carousel { display: none }`. A separate stylesheet is registered directly by the
 * module, so it always loads and there is one copy of these rules, not two.
 */

/* ---- no call-to-action label over a banner ----
 *
 * The theme's banner templates append
 *   <span class="an_homeproducts-banner-textlink">{l s='Shop now' d='Shop.Theme.Global'}</span>
 * which renders as "Köpa" on a Swedish shop, and the theme's front.css reveals it on hover via
 *   .an_homeproducts-banner-link:hover + .an_homeproducts-banner-content .an_homeproducts-banner-textlink
 * The banner is already clickable edge to edge through .an_homeproducts-banner-link, so the
 * label is redundant.
 *
 * It IS removed from the templates themselves (module and theme copies alike, guarded by
 * tests/test-banner-no-cta.php) -- but those template fixes have repeatedly failed to reach the
 * live site, because a theme's override folder is deployed separately from the module and has
 * lagged behind three times now. This stylesheet is registered by the module itself and
 * demonstrably does arrive, so the rule is repeated here as the belt to that braces.
 *
 * Scoped to .an_homeproducts-banner so it can only ever affect this module's banners; verified
 * against the live shop that no .an_homeproducts-banner-textlink exists outside one.
 *
 * To bring the label back, delete this block AND restore the span in the theme's banner
 * templates -- both, or it will look like this rule is being ignored.
 */
.an_homeproducts-banner .an_homeproducts-banner-textlink {
    display: none !important;
}

/* The slider banner is sized by the banner's own `col`, exactly like every other banner
   template. It shows one image at a time within that width. */
.an_homeproducts-banner-slider {
    position: relative;
}

.an_homeproducts-banner-slides {
    width: 100%;
    /* Anchor for the prev/next buttons, so they centre on the images rather than on the
       images plus any caption above them. */
    position: relative;
}

/* Owl hides .owl-carousel until it adds .owl-loaded. When the slider has a single image
   the JS skips Owl entirely and strips the class, but this keeps the markup visible in
   the window before init and if the script never runs at all. */
.an_homeproducts-banner-slides:not(.owl-loaded) {
    display: block;
}
.an_homeproducts-banner-slides:not(.owl-loaded) .an_homeproducts-banner-slide + .an_homeproducts-banner-slide {
    display: none;
}

.an_homeproducts-banner-slide img {
    display: block;
    width: 100%;
    height: auto;
    max-width: 100%;
}

.an_homeproducts-banner-slider .an_homeproducts-banner-content {
    margin-bottom: 15px;
}

/* ---- transitions ----
   Owl applies the animateIn / animateOut class names to the incoming and outgoing items;
   each needs matching keyframes. These are prefixed `anhp` so they cannot collide with
   animate.css, which some themes also load and which defines fadeIn/slideInLeft/etc.
   with different semantics.

   Owl's own stylesheet pins animation-duration to 1s; the initialiser writes the configured
   "Banner slider speed" into this custom property so the setting governs it instead.

   Only the duration is overridden -- Owl's `.owl-carousel .animated` already sets
   `animation-fill-mode: both`, and repeating it here just gave two places to change. */
.an_homeproducts-banner-slides .animated {
    animation-duration: var(--anhp-fade-duration, 600ms);
}

/* Owl is always handed the same two class names, `anhpOut` and `anhpIn` -- see ANHP_OUT_CLASS
   in init.slider.js for why they must never change. Which keyframes they resolve to is decided
   here, from two attributes the initialiser writes on the container:

     data-anhp-effect   fade | slide-left | slide-right | zoom
     data-anhp-dir      fwd  | back        (which way this particular transition is going)

   Doing it in CSS rather than by rewriting Owl's settings is what makes "previous" reverse
   correctly: Owl adds and removes one fixed name, so its cleanup always matches, and no stale
   animation class is ever left on an item. */

/* Fade and zoom happen in place -- there is no direction to mirror. */
.an_homeproducts-banner-slides[data-anhp-effect="fade"] .anhpOut { animation-name: anhpFadeOut; }
.an_homeproducts-banner-slides[data-anhp-effect="fade"] .anhpIn { animation-name: anhpFadeIn; }
.an_homeproducts-banner-slides[data-anhp-effect="zoom"] .anhpOut { animation-name: anhpZoomOut; }
.an_homeproducts-banner-slides[data-anhp-effect="zoom"] .anhpIn { animation-name: anhpZoomIn; }

/* "Slide to left": going forward, the current image leaves towards the left and the next
   arrives from the right, so the eye reads movement leftwards. */
.an_homeproducts-banner-slides[data-anhp-effect="slide-left"] .anhpOut { animation-name: anhpSlideOutLeft; }
.an_homeproducts-banner-slides[data-anhp-effect="slide-left"] .anhpIn { animation-name: anhpSlideInRight; }
/* ...and going back, the mirror, so "previous" visibly rewinds. */
.an_homeproducts-banner-slides[data-anhp-effect="slide-left"][data-anhp-dir="back"] .anhpOut { animation-name: anhpSlideOutRight; }
.an_homeproducts-banner-slides[data-anhp-effect="slide-left"][data-anhp-dir="back"] .anhpIn { animation-name: anhpSlideInLeft; }

/* "Slide to right" is the same thing the other way round. */
.an_homeproducts-banner-slides[data-anhp-effect="slide-right"] .anhpOut { animation-name: anhpSlideOutRight; }
.an_homeproducts-banner-slides[data-anhp-effect="slide-right"] .anhpIn { animation-name: anhpSlideInLeft; }
.an_homeproducts-banner-slides[data-anhp-effect="slide-right"][data-anhp-dir="back"] .anhpOut { animation-name: anhpSlideOutLeft; }
.an_homeproducts-banner-slides[data-anhp-effect="slide-right"][data-anhp-dir="back"] .anhpIn { animation-name: anhpSlideInRight; }

@keyframes anhpFadeOut {
    from { opacity: 1; }
    to { opacity: 0; }
}
@keyframes anhpFadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}

/* "Slide to left": the current image leaves towards the left, the next arrives from the
   right, so the eye reads movement leftwards. */
@keyframes anhpSlideOutLeft {
    from { transform: translateX(0); opacity: 1; }
    to { transform: translateX(-100%); opacity: 0; }
}
@keyframes anhpSlideInRight {
    from { transform: translateX(100%); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

/* ...and the mirror image for "slide to right". */
@keyframes anhpSlideOutRight {
    from { transform: translateX(0); opacity: 1; }
    to { transform: translateX(100%); opacity: 0; }
}
@keyframes anhpSlideInLeft {
    from { transform: translateX(-100%); opacity: 0; }
    to { transform: translateX(0); opacity: 1; }
}

@keyframes anhpZoomOut {
    from { transform: scale(1); opacity: 1; }
    to { transform: scale(1.12); opacity: 0; }
}
@keyframes anhpZoomIn {
    from { transform: scale(0.92); opacity: 0; }
    to { transform: scale(1); opacity: 1; }
}

/* Respect a reduced-motion preference: swap instantly rather than animating.
 *
 * `animation-duration: 1ms`, NOT `animation: none`. Owl's Animate plugin attaches its cleanup
 * with `.one(animationend, clear)`, and that cleanup is the only caller of
 * core.onTransitionEnd() -- the call that takes the carousel out of its `animating` state.
 * Suppressing the animation outright means animationend never fires, so the carousel stays
 * "animating" forever: the outgoing slide keeps .owl-animated-out (absolutely positioned, so
 * it sits frozen on top of the new one) and Autoplay._next() bails on every tick because
 * core.is('busy') never goes false. The slider advances exactly once and then dies.
 *
 * A 1ms animation is imperceptible -- it satisfies the preference -- but it still starts and
 * finishes, so the event fires and Owl's state machine completes. This is why the common
 * accessibility reset uses `animation-duration: 0.01ms !important` rather than `none`.
 */
@media (prefers-reduced-motion: reduce) {
    .an_homeproducts-banner-slides .animated {
        animation-duration: 1ms !important;
    }
}
/* No positioning override on .owl-animated-out -- deliberately.
 *
 * It used to be `position: absolute; top: 0; left: 0; width: 100%`, on the theory that the
 * outgoing slide had to be taken out of flow or it would push the incoming one sideways. That
 * was wrong, and it is what produced the "shadow sliding past before the image".
 *
 * Owl already overlays the two itself. Animate.swap() calls core.speed(0) so the stage jumps
 * instantly to the new offset, then writes an inline
 * `left: coordinates(previous) - coordinates(next)` onto the outgoing item to pull it back over
 * the frame. That works because Owl's own CSS makes .owl-item `position: relative`, where `left`
 * offsets from the item's static position.
 *
 * Forcing `position: absolute` changed the meaning of that inline `left`: the containing block
 * becomes .owl-stage, which is TRANSLATED, so `left` was measured from the stage's own origin --
 * off-screen to the left. Measured on a 307px-wide frame, the outgoing slide sat at x = -614 and
 * drifted to -907: a full frame outside the viewport, crossing the screen as a ghost.
 *
 * Owl also already supplies the stacking (`.owl-animated-out { z-index: 1 }` and
 * `.owl-animated-in { z-index: 0 }`), so there is nothing left for this module to add.
 */

/* ---- prev / next ----
   Both selector forms are covered because Owl 2.x emits either <button> or <div>
   depending on version.

   Sized in steps, driven by a custom property so there is one number to change per breakpoint.

   Deliberately NOT a viewport-relative unit: a banner's width comes from its `col`, so the same
   `5.5vw` control is a modest circle on a full-width banner and an oversized one on a col-3
   banner sitting next to three others. Stepped px keeps it predictable at every column width.

   These are smaller than the 44px/36px they replace: 32px at desktop down to 24px on a phone. */
.an_homeproducts-banner-slides {
    --anhp-nav-size: 24px;
    --anhp-nav-inset: 4px;
}
@media (min-width: 576px) {
    .an_homeproducts-banner-slides {
        --anhp-nav-size: 28px;
        --anhp-nav-inset: 6px;
    }
}
@media (min-width: 992px) {
    .an_homeproducts-banner-slides {
        --anhp-nav-size: 32px;
        --anhp-nav-inset: 8px;
    }
}
.an_homeproducts-banner-slides .owl-nav {
    margin: 0;
}
.an_homeproducts-banner-slides .owl-nav button.owl-prev,
.an_homeproducts-banner-slides .owl-nav button.owl-next,
.an_homeproducts-banner-slides .owl-nav .owl-prev,
.an_homeproducts-banner-slides .owl-nav .owl-next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--anhp-nav-size);
    height: var(--anhp-nav-size);
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.9);
    color: #333;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.22);
    cursor: pointer;
    /* Revealed on hover -- see the `@media (hover: hover)` block below, which is what actually
       hides them. Visible by default here so that any environment without hover support (touch,
       and any browser that does not match the query) keeps working. */
    opacity: 0.85;
    transition: opacity 0.2s ease, background 0.2s ease;
    z-index: 3;
}
.an_homeproducts-banner-slides .owl-nav .owl-prev {
    left: var(--anhp-nav-inset);
}
.an_homeproducts-banner-slides .owl-nav .owl-next {
    right: var(--anhp-nav-inset);
}
.an_homeproducts-banner-slides .owl-nav button.owl-prev:hover,
.an_homeproducts-banner-slides .owl-nav button.owl-next:hover,
.an_homeproducts-banner-slides .owl-nav .owl-prev:hover,
.an_homeproducts-banner-slides .owl-nav .owl-next:hover,
.an_homeproducts-banner-slides .owl-nav [class*="owl-"]:hover {
    background: #fff;
    color: #333;
}
/* Disabled arrows (loop off, sitting at either end) stay faint even once revealed. Kept more
   specific than the hover reveal so it wins in both states. */
.an_homeproducts-banner-slides .owl-nav .owl-prev.disabled,
.an_homeproducts-banner-slides .owl-nav .owl-next.disabled,
.an_homeproducts-banner-slides .owl-nav .disabled {
    opacity: 0.3;
    cursor: default;
}
.an_homeproducts-banner-slides .owl-nav .owl-prev:focus-visible,
.an_homeproducts-banner-slides .owl-nav .owl-next:focus-visible {
    opacity: 1;
    outline: 2px solid currentColor;
    outline-offset: 2px;
}
.an_homeproducts-banner-slides .anhp-banner-nav-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100%;
    line-height: 0;
}
/* The chevron carries width/height attributes for the no-CSS case; scale it to the button so
   shrinking the control shrinks the glyph with it instead of cropping it. */
.an_homeproducts-banner-slides .anhp-banner-nav-icon svg {
    width: 55%;
    height: 55%;
}

/* ---- reveal the arrows on hover ----
   Gated on `hover: hover` deliberately. A touch device never produces a hover state, so hiding
   the arrows there would leave no way to reach them at all -- the query keeps them permanently
   visible on touch and only opts pointer devices into the reveal.

   :focus-within is part of the same rule so keyboard users can still reach the controls: tabbing
   to a button brings both into view exactly as hovering does. */
@media (hover: hover) and (pointer: fine) {
    /* Expressed as ONE rule that hides, rather than a hide rule plus a competing reveal rule.
       The pair version needed the reveal to out-specify the hide, and getting that wrong fails
       silently and confusingly -- an earlier attempt here left the arrows stuck at opacity 0
       even while :focus-within matched. With :not() there is nothing to out-specify: when the
       slider is hovered or holds focus this rule simply stops applying, and the base
       `opacity: 0.85` from the button styling above shows through by itself.

       :focus-within is included so keyboard users can reach the controls -- tabbing to one
       brings both into view exactly as hovering does. Owl is configured with
       navElement: 'button' precisely so that focus is possible; its default <div> nav cannot
       take focus at all, which would make hidden arrows permanently unreachable. */
    .an_homeproducts-banner-slides:not(:hover):not(:focus-within) .owl-nav .owl-prev,
    .an_homeproducts-banner-slides:not(:hover):not(:focus-within) .owl-nav .owl-next {
        opacity: 0;
        /* Not visibility/display: those would drop the buttons out of the tab order, and the
           fade needs something to animate. Pointer events are disabled so an invisible control
           cannot swallow a click meant for the banner's own link. */
        pointer-events: none;
    }
}

/* The chevron SVG is aria-hidden decoration, so each button carries a text label for screen
   readers. Clipped rather than `display:none` or `visibility:hidden`, both of which remove it
   from the accessibility tree along with the pixels. */
.an_homeproducts-banner-slides .anhp-banner-nav-label {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* A narrow banner (small `col`) cannot carry full-size controls without covering the
   image, so scale them down. */
/* The old `@media (max-width: 767px)` override that forced 36px arrows is gone: it now sits
   BELOW the mobile-first sizing above and would have made phones (36px) larger than desktops
   (32px). Size and inset are handled entirely by --anhp-nav-size / --anhp-nav-inset. */
