/* ── Slider — container layout mode (cores as slides) ─────────────────────
   Companion to the `slider` toggle on the container (see the registry
   entry in inc/component-registry.php for the architecture rationale).
   Markup (components/section.php):

     <div class="dx-container dx-container--{width} dx-slider" data-dx-slider …>
       <ul class="dx-container--list dx-slider__track dx-slider__track--snap" …>
         <li class="dx-core">…            ← real slides
         <li class="dx-core" data-dx-clone aria-hidden>…  ← loop clones
       </ul>
       <button class="dx-slider__arrow dx-slider__arrow--prev">
       <button class="dx-slider__arrow dx-slider__arrow--next">
       <div class="dx-slider__dots">      ← dot buttons built by JS
     </div>

   The WRAPPER carries the container role (width cap + centering via the
   reused `.dx-container.dx-container--{width}` classes — no parallel
   width system); the <ul> keeps `.dx-container--list` (list reset + gap)
   and becomes the track. Two track modes:

     --snap        native CSS scroll-snap: the browser owns swipe /
                   momentum / snapping. dx-slider-front.js adds arrows,
                   dots, autoplay, and the seamless loop jump. With JS
                   disabled the track degrades to a swipeable strip.
     --continuous  pure-CSS marquee (logo strips): the core set is
                   duplicated server-side and the track translates by
                   exactly one content-set width, looping forever. No JS.

   Rules here are deliberately UNLAYERED so they beat the layered
   `.dx-container--list` grid base in section.css (same trick the
   balanced-grid breakpoints use).

   No new colours (closed palette): arrows and dots ride on
   `currentColor`, which resolves through the section's mode class —
   dark sections get light chrome and vice versa, for free.            */

.dx-slider {
    position: relative;
    /* Column stack: track, then dots. Per CLAUDE.md "use gap, not
       margin" — the wrapper owns the rhythm between them. */
    display: flex;
    flex-direction: column;
    gap: var(--dx-space-s);
}

/* ── Track (shared) ────────────────────────────────────────────────────── */

.dx-slider__track {
    display: flex;
    flex-wrap: nowrap;
    /* gap is inherited from `.dx-container--list` (the same
       `--dx-container-list-gap` chain the grid uses) — one gap field,
       both layout modes. */
}

/* ── Snap mode ─────────────────────────────────────────────────────────── */

.dx-slider__track--snap {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    /* Hide the scrollbar — navigation is swipe / arrows / dots. */
    scrollbar-width: none;
}
.dx-slider__track--snap::-webkit-scrollbar {
    display: none;
}

/* Slide sizing — the container-native responsive formula. Each slide's
   basis is an equal share of the track for the configured visible count
   (`--dx-slider-items`, gaps accounted for), FLOORED at Target column
   width (`--dx-container-min-core-width`, falling back to the global
   `--dx-grid-min-column` token). As the container narrows, slides hit
   the floor and the visible count drops 4 → 3 → 2 → 1 automatically —
   no media queries, no per-breakpoint settings, correct in any width
   context (half-width containers, squeezed editor canvas, anywhere).
   The outer `min(100%, …)` keeps the floor from ever exceeding the
   track itself. */
.dx-slider__track--snap > .dx-core {
    flex: 0 0
        max(
            calc(
                (100% - (var(--dx-slider-items, 1) - 1) * var(--dx-container-list-gap, var(--dx-space-l)))
                    / var(--dx-slider-items, 1)
            ),
            min(100%, var(--dx-container-min-core-width, var(--dx-grid-min-column, 220px)))
        );
    min-width: 0;
    scroll-snap-align: start;
}

/* ── Continuous mode (marquee) ─────────────────────────────────────────── */

.dx-slider--continuous {
    overflow: hidden;
}

.dx-slider__track--continuous {
    width: max-content;
    animation: dx-slider-marquee var(--dx-slider-marquee-duration, 40s) linear infinite;
}

/* Marquee slides are sized by Target column width directly (an equal-
   share % is meaningless inside a max-content track). Same token chain
   as the snap floor — one width knob for both modes. */
.dx-slider__track--continuous > .dx-core {
    flex: 0 0 var(--dx-container-min-core-width, var(--dx-grid-min-column, 220px));
    min-width: 0;
}

.dx-slider--continuous:hover .dx-slider__track--continuous {
    animation-play-state: paused;
}

/* The -50% translate needs two identical halves; the flex gap between
   the last clone and the wrapped-around start is accounted for by the
   extra half-gap term (total width = 2·set + (2n−1)·gap, so one full
   set + its trailing gap = 50% + gap/2). */
@keyframes dx-slider-marquee {
    to {
        transform: translateX(calc(-50% - var(--dx-container-list-gap, var(--dx-space-l)) / 2));
    }
}

@media (prefers-reduced-motion: reduce) {
    .dx-slider__track--continuous {
        animation: none;
        width: 100%;
        overflow-x: auto;
    }
}

/* ── Fade edges (`slider_fade` toggle) ─────────────────────────────────── */

/* The classic edge treatment (logo marquees, peeking-slide carousels):
   the track's left/right edges fade to TRANSPARENCY via a CSS mask,
   revealing whatever background sits behind — any mode colour, image,
   or gradient, with zero knowledge of it. (An overlaid gradient of the
   background colour would break on image backgrounds and add palette
   surface; the mask's black/transparent stops are pure alpha geometry,
   not colours.)

   The mask target differs by mode because a mask rides its ELEMENT's
   box: in snap mode it sits on the scroll container (content scrolls
   inside, fade stays put at the edges); in continuous mode the <ul>
   itself translates, so the mask sits on the static overflow-hidden
   wrapper instead.

   Fade width: 6rem, capped at 15% per side so narrow containers
   (mobile, squeezed editor canvas) never dissolve most of the track.
   Override per instance via Custom CSS: `%root% { --dx-slider-fade:
   3rem; }` on the container. */
.dx-slider--fade > .dx-slider__track--snap {
    -webkit-mask-image: linear-gradient(
        to right,
        transparent,
        #000 var(--dx-slider-fade, min(6rem, 15%)),
        #000 calc(100% - var(--dx-slider-fade, min(6rem, 15%))),
        transparent
    );
    mask-image: linear-gradient(
        to right,
        transparent,
        #000 var(--dx-slider-fade, min(6rem, 15%)),
        #000 calc(100% - var(--dx-slider-fade, min(6rem, 15%))),
        transparent
    );
}

.dx-slider--fade.dx-slider--continuous {
    -webkit-mask-image: linear-gradient(
        to right,
        transparent,
        #000 var(--dx-slider-fade, min(6rem, 15%)),
        #000 calc(100% - var(--dx-slider-fade, min(6rem, 15%))),
        transparent
    );
    mask-image: linear-gradient(
        to right,
        transparent,
        #000 var(--dx-slider-fade, min(6rem, 15%)),
        #000 calc(100% - var(--dx-slider-fade, min(6rem, 15%))),
        transparent
    );
}

/* ── Arrows ────────────────────────────────────────────────────────────── */

/* Server-rendered but hidden until dx-slider-front.js wires them
   (`.dx-slider--ready`) — with JS disabled the slider degrades to a
   clean swipeable strip instead of showing dead buttons. */
.dx-slider__arrow,
.dx-slider__dots {
    display: none;
}
.dx-slider--ready .dx-slider__arrow {
    display: inline-flex;
}
.dx-slider--ready .dx-slider__dots {
    display: flex;
}

.dx-slider__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1;
    align-items: center;
    justify-content: center;
    width: var(--dx-space-2xl);
    height: var(--dx-space-2xl);
    padding: 0;
    background: none;
    border: none;
    color: currentColor;
    cursor: pointer;
}
/* Horizontal anchor — 0 (default) sits the arrow flush inside the
   container's edge; the per-instance "Arrow offset" slider pushes BOTH
   arrows symmetrically outward (`--dx-slider-arrow-offset` on the
   wrapper, emitted by section.php). 50px = 50px outside the edge. */
.dx-slider__arrow--prev {
    left: calc(-1 * var(--dx-slider-arrow-offset, 0px));
}
.dx-slider__arrow--next {
    right: calc(-1 * var(--dx-slider-arrow-offset, 0px));
}
.dx-slider__arrow[disabled] {
    /* Non-loop slider at either end — the arrow disappears rather than
       dimming (a dimmed glyph would need an opacity variant of the mode
       colour; the palette is closed). visibility keeps the element in
       layout so nothing shifts when it comes back. */
    visibility: hidden;
}
.dx-slider__arrow:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}

/* Chevron glyph — pure CSS border geometry (no icon asset, mirrors the
   dots' CSS-circle approach). Inherits currentColor via border-color. */
.dx-slider__arrow::before {
    content: "";
    width: 0.6em;
    height: 0.6em;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(135deg); /* points left (prev) */
}
.dx-slider__arrow--next::before {
    transform: rotate(-45deg); /* points right */
}

/* ── Dots ──────────────────────────────────────────────────────────────── */

.dx-slider__dots {
    justify-content: center;
    gap: var(--dx-space-2xs);
}

.dx-slider__dot {
    width: 10px;
    height: 10px;
    padding: 0;
    background: transparent;
    border: 1px solid currentColor;
    border-radius: 50%;
    color: currentColor;
    cursor: pointer;
}
.dx-slider__dot.is-active {
    /* Active = filled, inactive = ring — state without an alpha or a
       second colour. */
    background: currentColor;
}
.dx-slider__dot:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 2px;
}
