/**
 * Page Designer — Section Slider Public Runtime CSS
 * Namespace: .pd-sl-host  (active host), .pd-sl-rt-*  (runtime chrome)
 *
 * Activates only when host has data-pd-sl-on="1" and the runtime
 * has stamped data-pd-sl-mounted="1". Otherwise the host renders
 * as a normal Section/Column.
 *
 * Configuration flows through CSS variables (set by the panel via
 * comp.addStyle) — no hard-coded values:
 *   --pd-sl-speed           Transition duration (ms)
 *   --pd-sl-gap             Gap between slides when perview > 1 (px)
 *   --pd-sl-cwidth          Custom-width (vw) when ratio = custom
 *   --pd-sl-height          Custom-height (px) when ratio = custom
 *   --pd-sl-overlay-color   Overlay tint colour
 *   --pd-sl-overlay-opacity Overlay opacity (0–1)
 *   --pd-sl-arrow-color     Nav arrow tint
 *   --pd-sl-dot-color       Pagination dot tint (inactive)
 *   --pd-sl-dot-active      Pagination dot tint (active)
 *   --pd-sl-kb-dur          Ken Burns duration (s)
 *   --pd-sl-pin-dist        Scroll-pin wrapper height (vh)
 */

/* ── HOST viewport ────────────────────────────────────────────────── */
[data-pd-sl-on="1"] {
    position: relative;
    overflow: hidden;
    perspective: 1200px;
    transform-style: preserve-3d;
}
/* Aspect ratios — applied via attribute selector so swapping ratio
   in the panel updates live.

   Friendly tile values (panel-emitted defaults):
     hero        → full-width banner with fixed 500px height
     standard    → 16 : 9 widescreen
     square      → 1 : 1 in-page gallery
     tall        → 4 : 5 mobile-first portrait
     fullwindow  → 100vw × 100vh splash
     custom      → user-driven width (vw) + height (px)

   Legacy raw-ratio values (16:9, 4:3, 1:1, 21:9, 3:2, fullscreen, auto)
   are kept below for back-compat with sliders saved before the tile
   redesign. */
[data-pd-sl-on="1"][data-pd-sl-ratio="hero"]       { height: 500px; aspect-ratio: auto; }
[data-pd-sl-on="1"][data-pd-sl-ratio="standard"]   { aspect-ratio: 16 / 9; }
[data-pd-sl-on="1"][data-pd-sl-ratio="square"]     { aspect-ratio: 1 / 1; }
[data-pd-sl-on="1"][data-pd-sl-ratio="tall"]       { aspect-ratio: 4 / 5; }
[data-pd-sl-on="1"][data-pd-sl-ratio="fullwindow"] { width: 100vw; height: 100vh; aspect-ratio: auto; }

[data-pd-sl-on="1"][data-pd-sl-ratio="16:9"]       { aspect-ratio: 16 / 9; }
[data-pd-sl-on="1"][data-pd-sl-ratio="4:3"]        { aspect-ratio: 4 / 3; }
[data-pd-sl-on="1"][data-pd-sl-ratio="1:1"]        { aspect-ratio: 1 / 1; }
[data-pd-sl-on="1"][data-pd-sl-ratio="21:9"]       { aspect-ratio: 21 / 9; }
[data-pd-sl-on="1"][data-pd-sl-ratio="3:2"]        { aspect-ratio: 3 / 2; }
[data-pd-sl-on="1"][data-pd-sl-ratio="custom"]     {
    /* Width inherits the host Section's natural container width (100%);
       only the height is user-driven. --pd-sl-cwidth is still read as a
       back-compat opt-in for legacy sliders that had a vw width set,
       but no override fires when the var is unset. */
    height: var(--pd-sl-height, 600px);
    aspect-ratio: auto;
}
[data-pd-sl-on="1"][data-pd-sl-ratio="custom"][style*="--pd-sl-cwidth"] {
    width: var(--pd-sl-cwidth);
}
[data-pd-sl-on="1"][data-pd-sl-ratio="fullscreen"] { width: 100vw; height: 100vh; aspect-ratio: auto; }
[data-pd-sl-on="1"][data-pd-sl-ratio="auto"]       { aspect-ratio: auto; }

/* ── DEVICE-VARIANT CASCADE PYRAMID ──────────────────────────────────
 * Tablet (≤992px) and Mobile (≤480px) media queries below mirror PD's
 * GrapesJS device manager breakpoints (editor.js deviceManager block).
 *
 * Cascade is desktop → tablet → mobile. Source order + matching @media
 * means a -t override beats the base rule at tablet viewport, and a -m
 * override beats both at mobile viewport. The blanket reset selectors
 * `[data-pd-sl-ratio-t]:not([data-pd-sl-ratio-t=""])` ensure the base
 * desktop rule's aspect-ratio/height/width values don't leak through
 * when a tablet/mobile override is present. */
@media (max-width: 992px) {
    [data-pd-sl-on="1"][data-pd-sl-ratio-t]:not([data-pd-sl-ratio-t=""]) {
        aspect-ratio: auto;
        height: auto;
    }
    [data-pd-sl-on="1"][data-pd-sl-ratio-t="hero"]       { height: 500px; aspect-ratio: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-t="standard"]   { aspect-ratio: 16 / 9; height: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-t="square"]     { aspect-ratio: 1 / 1;  height: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-t="tall"]       { aspect-ratio: 4 / 5;  height: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-t="fullwindow"] { width: 100vw; height: 100vh; aspect-ratio: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-t="custom"]     {
        height: var(--pd-sl-height-t, var(--pd-sl-height, 600px));
        aspect-ratio: auto;
    }
    /* Custom-height on the desktop rule still gets a chance to win at
       tablet viewport when no tablet ratio override is set — base height
       var falls through naturally. */
    [data-pd-sl-on="1"][data-pd-sl-ratio="custom"]:not([data-pd-sl-ratio-t]):not([data-pd-sl-ratio-t=""]) {
        height: var(--pd-sl-height-t, var(--pd-sl-height, 600px));
    }
    /* Hero with NO device override (attr absent OR cleared-to-empty): a fixed
       500px banner crops a cover image's sides hard once the box is narrower
       than ~16:9 (a phone showed only the middle half of each slide). Fall
       back to a fluid 16:9 band capped at the designed 500px, so the art fits
       edge to edge (layout rules: fluid-first, nothing cut off). An explicit
       -t/-m ratio tile bypasses this via the :not() guards. */
    [data-pd-sl-on="1"][data-pd-sl-ratio="hero"]:not([data-pd-sl-ratio-t]:not([data-pd-sl-ratio-t=""])):not([data-pd-sl-ratio-m]:not([data-pd-sl-ratio-m=""])) {
        height: auto;
        aspect-ratio: 16 / 9;
        max-height: 500px;
    }
}
@media (max-width: 480px) {
    [data-pd-sl-on="1"][data-pd-sl-ratio-m]:not([data-pd-sl-ratio-m=""]) {
        aspect-ratio: auto;
        height: auto;
    }
    [data-pd-sl-on="1"][data-pd-sl-ratio-m="hero"]       { height: 500px; aspect-ratio: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-m="standard"]   { aspect-ratio: 16 / 9; height: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-m="square"]     { aspect-ratio: 1 / 1;  height: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-m="tall"]       { aspect-ratio: 4 / 5;  height: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-m="fullwindow"] { width: 100vw; height: 100vh; aspect-ratio: auto; }
    [data-pd-sl-on="1"][data-pd-sl-ratio-m="custom"]     {
        height: var(--pd-sl-height-m, var(--pd-sl-height-t, var(--pd-sl-height, 600px)));
        aspect-ratio: auto;
    }
    [data-pd-sl-on="1"][data-pd-sl-ratio="custom"]:not([data-pd-sl-ratio-t]):not([data-pd-sl-ratio-m]) {
        height: var(--pd-sl-height-m, var(--pd-sl-height-t, var(--pd-sl-height, 600px)));
    }
}

/* ── Per-slide hide-on-device ────────────────────────────────────────
 * Slides carrying data-pd-sl-slide-hide-{d,t,m}="1" disappear at the
 * matching viewport. The runtime (slider_section_public.js) also skips
 * hidden slides in its index so navigation walks past them. Specificity
 * (0,3,1) cleanly beats the base .pd-sl-rt-slide rules without
 * !important. */
@media (min-width: 992.01px) {
    [data-pd-sl-on="1"] > [data-pd-sl-slide-hide-d="1"] { display: none; }
}
@media (min-width: 480.01px) and (max-width: 992px) {
    [data-pd-sl-on="1"] > [data-pd-sl-slide-hide-t="1"] { display: none; }
}
@media (max-width: 480px) {
    [data-pd-sl-on="1"] > [data-pd-sl-slide-hide-m="1"] { display: none; }
}

/* ── Per-slide background image, device-aware ────────────────────────
 * Runtime stamps inline CSS vars --pd-sl-slide-img-d / -t / -m on each
 * slide based on data-pd-sl-slide-img / -t / -m attrs. CSS picks the
 * right one per viewport using the fallback chain. */
.pd-sl-rt-slide[style*="--pd-sl-slide-img-d"] {
    background-image: var(--pd-sl-slide-img-d);
    background-size: cover;
    background-position: center;
}
@media (max-width: 992px) {
    .pd-sl-rt-slide[style*="--pd-sl-slide-img-t"] {
        background-image: var(--pd-sl-slide-img-t);
    }
    /* When only a tablet override exists (no desktop base), still paint. */
    .pd-sl-rt-slide[style*="--pd-sl-slide-img-t"]:not([style*="--pd-sl-slide-img-d"]) {
        background-image: var(--pd-sl-slide-img-t);
        background-size: cover;
        background-position: center;
    }
}
@media (max-width: 480px) {
    .pd-sl-rt-slide[style*="--pd-sl-slide-img-m"] {
        background-image: var(--pd-sl-slide-img-m);
    }
    .pd-sl-rt-slide[style*="--pd-sl-slide-img-m"]:not([style*="--pd-sl-slide-img-d"]):not([style*="--pd-sl-slide-img-t"]) {
        background-image: var(--pd-sl-slide-img-m);
        background-size: cover;
        background-position: center;
    }
}

/* ── Per-slide FX preset bg ────────────────────────────────────────
 * When a slide carries data-pd-sl-slide-fx (set via the slider
 * panel's FX Preset picker), the FX runtime paints a live shader as
 * the slide's background. The slide's image bg is suppressed so the
 * FX layer is the sole visual source — mirrors the mutually-exclusive
 * Image / Video / FX choice in the slide settings card. Specificity
 * (0,2,1) beats the image rule above (0,1,1). */
.pd-sl-rt-slide[data-pd-sl-slide-fx]:not([data-pd-sl-slide-fx=""]):not([data-pd-sl-slide-fx="none"]) {
    background-image: none;
}

/* ── Per-slide FX surface block-out ────────────────────────────────
 * When the FX runtime mounts on a slide (data-pd-fx-preset stamped
 * by pd_sl_pnl.js's applySlideFxMirror), it appends the FX layers
 * (.pd-blk-fx-surface / -accent-overlay / -scrim / -fallback /
 * -click-burst) as direct children of the slide root. Without this
 * override they're caught by the generic `.pd-sl-rt-slide > *` rule
 * further down, which forces grid-item layout (align-self:center +
 * margin:6px + max-h/w:calc(100% - 12px)) and squeezes the FX into
 * a single 2×2 cell instead of letting it fill the slide. Pull the
 * layers out of the cell grid so the effect covers the slide edge-
 * to-edge — same pattern as the slide-video block-out below.
 * Specificity (0,2,1) beats .pd-sl-rt-slide > * (0,1,1). */
.pd-sl-rt-slide > .pd-blk-fx-surface,
.pd-sl-rt-slide > .pd-blk-fx-accent-overlay,
.pd-sl-rt-slide > .pd-blk-fx-scrim,
.pd-sl-rt-slide > .pd-blk-fx-fallback,
.pd-sl-rt-slide > .pd-blk-fx-click-burst {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    max-width: none;
    max-height: none;
    align-self: stretch;
    justify-self: stretch;
    z-index: 0;
}

/* ── FX-bearing slide must stay full-bleed ─────────────────────────
 * When a slide uses a Background FX preset, the slider runtime mirrors
 * data-pd-sl-slide-fx onto data-pd-fx-preset on that slide. fx.css then
 * matches it with its root rule
 *   [data-pd-fx-preset]:not([data-pd-fx-preset=""]) { position: relative }
 * whose specificity (0,2,0) TIES the slider's own slide rule
 *   [data-pd-sl-on="1"] > .pd-sl-rt-slide { position: absolute; inset: 0 }
 * (also 0,2,0). On the public page frontend_page.php loads fx.css AFTER
 * this file, so position:relative wins the tie, the slide collapses to
 * its content height, and the FX surface (height:100%) shrinks with it —
 * the effect renders as a short band at the top instead of covering the
 * whole slide (it looks correct in the editor canvas only because the
 * canvas loads the two sheets in the opposite order). Re-assert the
 * full-bleed positioning at specificity (0,4,0) so the slide wins
 * regardless of stylesheet load order. */
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-fx-preset]:not([data-pd-fx-preset=""]) {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* ── Per-slide background video ─────────────────────────────────────
 * Runtime injects <video class="pd-sl-rt-slide-video"> as the FIRST
 * direct child of any slide whose data-pd-sl-slide-video is set. The
 * video extends OVER the slide's padding (default inset:0 for an
 * absolutely-positioned child references the containing block's
 * padding-box, which would leave the slide's own padding visible as a
 * frame around the video). The runtime stamps the slide's computed
 * padding into --pd-sl-slide-pad-{t,r,b,l} so the video can
 * negative-offset itself to fill the border-box edge-to-edge — the
 * same area the slide's bg-image / poster covers.
 *
 * GPU compositing — transform: translateZ(0) + will-change forces the
 * video onto its own composited layer so any parent transform
 * animation (Ken Burns) stays frame-synced with it. Without this the
 * video lagged the slide's transform by a frame, reading as a "jump."
 *
 * pointer-events:none keeps clicks falling through to the slide-link
 * (and the editor's selection layer in canvas mode). */
/* Selector must out-specify the slide-content lift rule further down
   in this file (`.pd-sl-rt-slide > *` — specificity 0,1,1 — which
   pins every direct child to position:relative + z-index:2 for caption
   layering). Without the child-combinator prefix, that wildcard would
   force the video to position:relative and the negative-inset offsets
   below would be ignored, leaving the video at its intrinsic size in
   the slide's flex flow with asymmetric letterboxing on every edge. */
.pd-sl-rt-slide > .pd-sl-rt-slide-video {
    position: absolute;
    /* For REPLACED elements (<video>, <img>) absolutely positioned,
       CSS spec resolves width:auto / height:auto to the INTRINSIC
       size even when both opposing anchors are specified — so a
       1280×720 video with `right:-36px; bottom:-36px; width:auto`
       silently dropped the right/bottom anchors and rendered at
       1280×720 pinned to the top-left. Use explicit calc dimensions
       (padding-box 100% + the negated padding on each axis) to give
       the video a real width/height that equals the slide's border-
       box, then `object-fit: cover` centers the source frame. */
    top:    calc(0px - var(--pd-sl-slide-pad-t, 0px));
    left:   calc(0px - var(--pd-sl-slide-pad-l, 0px));
    width:  calc(100% + var(--pd-sl-slide-pad-l, 0px) + var(--pd-sl-slide-pad-r, 0px));
    height: calc(100% + var(--pd-sl-slide-pad-t, 0px) + var(--pd-sl-slide-pad-b, 0px));
    /* Neutralise the generic `.pd-sl-rt-slide > *` grid-item caps
       (margin:6px + max-h/w:calc(100% - 12px) + align/justify-self)
       — without these resets the explicit width/height above are
       capped 12px smaller AND shifted 6px by margin, leaving the
       slide's underlying bg visible as a thin frame around the
       video. */
    margin: 0;
    max-width: none;
    max-height: none;
    align-self: stretch;
    justify-self: stretch;
    object-fit: cover;
    object-position: center;
    z-index: 0;
    pointer-events: none;
    background: transparent;
    border: 0;
    will-change: transform;
    transform: translateZ(0);
    backface-visibility: hidden;
}

/* ── Ken Burns on video — redirect parent's transform to the video ─
 * When a slide hosts a <video>, animating the parent's transform
 * leaves the video element a frame behind on its compositor layer
 * (HTML <video> is always promoted). The visible result is a
 * stuttering "jump" at every animation tick.
 *
 * Fix: neutralize the slide-level Ken Burns slot (keep the entry
 * transition slot active), then re-stamp the same animation onto the
 * video element itself. The video already has will-change:transform
 * so its own transform animates entirely on the GPU — no parent
 * recomposition, no jitter.
 *
 * `animation-iteration-count: infinite; animation-direction: alternate`
 * also kills the end-of-cycle snap: the pan/zoom plays forward, then
 * reverses, then forward, indefinitely — never re-entering frame 0
 * from frame 100% in a single tick. */
.pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src]) {
    animation-name: var(--pd-sl-tr-anim, none), none;
}
.pd-sl-rt-slide.pd-sl-rt-slide-kb.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src]) > .pd-sl-rt-slide-video,
[data-pd-sl-on="1"][data-pd-sl-kb="1"] > .pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src]) > .pd-sl-rt-slide-video {
    animation-name:            var(--pd-sl-kb-anim, none);
    animation-duration:        var(--pd-sl-slide-kb-dur, var(--pd-sl-kb-dur, 12s));
    animation-timing-function: ease-in-out;
    animation-iteration-count: infinite;
    animation-direction:       alternate;
    animation-fill-mode:       both;
}

/* ── FOUC guard ─────────────────────────────────────────────────────
 * slider_section_public.js (the runtime that mounts hosts and stamps
 * .pd-sl-rt-slide on every direct child) is loaded by the deferred-FX
 * batch — confirmed via PerformanceObserver: it lands ~1800ms after
 * page load. Until mount, the host's direct children are NOT yet
 * .pd-sl-rt-slide, so the position:absolute rule below doesn't match
 * and slides render as default-flow Sections/Columns → stacked
 * vertically inside the host for ~1.8s on every canvas refresh.
 *
 * This rule hides every child after the first while the host is
 * still unmounted, so the user sees one slide instead of N stacked.
 * Once the runtime stamps data-pd-sl-mounted="1" the rule stops
 * matching and the runtime's own position:absolute + opacity logic
 * takes over.
 *
 * Specificity 0,3,1 — comfortably above default-flow rules without
 * !important. */
[data-pd-sl-on="1"]:not([data-pd-sl-mounted]) > *:not(:first-child) {
    display: none;
}
/* And pre-style the first child to mimic a mounted .pd-sl-rt-slide so
 * the host's aspect-ratio constraint takes effect immediately. Without
 * this, the first child is a normal block-flow element with its own
 * content height — the host stretches to content height, which reads
 * as "Auto" aspect ratio for ~1.8s until the runtime mounts and the
 * real .pd-sl-rt-slide rule (position:absolute; inset:0) takes over. */
[data-pd-sl-on="1"]:not([data-pd-sl-mounted]) > *:first-child {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

/* ── SLIDES (host's direct children once mount stamps them) ──────── */
[data-pd-sl-on="1"] > .pd-sl-rt-slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    transition:
        transform     var(--pd-sl-speed, 500ms) var(--pd-sl-easing, ease),
        opacity       var(--pd-sl-speed, 500ms) var(--pd-sl-easing, ease),
        clip-path     var(--pd-sl-speed, 500ms) var(--pd-sl-easing, ease),
        filter        var(--pd-sl-speed, 500ms) var(--pd-sl-easing, ease);
    will-change: transform, opacity;
    opacity: 0;
    pointer-events: none;
    z-index: 0;
}
/* Depth stagger — chains transition-delay across slides 2-10 so
   layered transitions can offset their start times. */
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(2)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 1); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(3)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 2); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(4)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 3); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(5)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 4); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(6)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 5); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(7)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 6); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(8)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 7); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(9)  { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 8); }
[data-pd-sl-on="1"] > .pd-sl-rt-slide:nth-child(10) { transition-delay: calc(var(--pd-sl-depth-stagger, 0ms) * 9); }

/* ── Grab cursor when swipe + grab are both ON ───────────────────── */
[data-pd-sl-on="1"][data-pd-sl-swipe="1"][data-pd-sl-grab="1"]         { cursor: grab; }
[data-pd-sl-on="1"][data-pd-sl-swipe="1"][data-pd-sl-grab="1"]:active  { cursor: grabbing; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide.pd-sl-rt-slide-active {
    opacity: 1;
    pointer-events: auto;
    z-index: 1;
}

/* When the host ALSO has an FX (or Glass/SVGFX) background tool active,
   the FX surface sits at z-index 0 by default — slides (z-index 0/1)
   would cover it. Lift FX surface above the resting layer but BELOW
   the active slide so the slider chrome (arrows/dots/overlay) and
   transparent-bg slides reveal the FX behind them. */
[data-pd-sl-on="1"] > .pd-blk-fx-surface,
[data-pd-sl-on="1"] > .pd-blk-fx-fallback {
    z-index: 0;
}
[data-pd-sl-on="1"] > .pd-blk-fx-accent-overlay,
[data-pd-sl-on="1"] > .pd-blk-fx-scrim {
    z-index: 0;
}

/* ── TRANSITION PRESETS ──────────────────────────────────────────── */
/* slide (default) — horizontal pan. Uses translateX. */
[data-pd-sl-on="1"][data-pd-sl-effect="slide"] > .pd-sl-rt-slide                       { transform: translateX(100%); }
[data-pd-sl-on="1"][data-pd-sl-effect="slide"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { transform: translateX(0); }
[data-pd-sl-on="1"][data-pd-sl-effect="slide"] > .pd-sl-rt-slide.pd-sl-rt-slide-prev   { transform: translateX(-100%); }

/* fade — opacity-only crossfade. Active rule above covers it. */
[data-pd-sl-on="1"][data-pd-sl-effect="fade"] > .pd-sl-rt-slide                       { transform: none; }
[data-pd-sl-on="1"][data-pd-sl-effect="fade"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { transform: none; }

/* zoom — scale on enter/exit. */
[data-pd-sl-on="1"][data-pd-sl-effect="zoom"] > .pd-sl-rt-slide                       { transform: scale(1.15); }
[data-pd-sl-on="1"][data-pd-sl-effect="zoom"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { transform: scale(1); }
[data-pd-sl-on="1"][data-pd-sl-effect="zoom"] > .pd-sl-rt-slide.pd-sl-rt-slide-prev   { transform: scale(0.92); }

/* split-tb — slide splits vertically; top half exits up, bottom exits down. */
[data-pd-sl-on="1"][data-pd-sl-effect="split-tb"] > .pd-sl-rt-slide {
    clip-path: inset(0 0 0 0);
    transform: translateY(0);
}
[data-pd-sl-on="1"][data-pd-sl-effect="split-tb"] > .pd-sl-rt-slide.pd-sl-rt-slide-prev {
    transform: translateY(-50%);
    clip-path: inset(0 0 50% 0);
}

/* split-lr — slide splits horizontally; left half exits left, right exits right. */
[data-pd-sl-on="1"][data-pd-sl-effect="split-lr"] > .pd-sl-rt-slide {
    clip-path: inset(0 0 0 0);
    transform: translateX(0);
}
[data-pd-sl-on="1"][data-pd-sl-effect="split-lr"] > .pd-sl-rt-slide.pd-sl-rt-slide-prev {
    transform: translateX(-50%);
    clip-path: inset(0 50% 0 0);
}

/* mask-circ — slide enters through an expanding circle mask. */
[data-pd-sl-on="1"][data-pd-sl-effect="mask-circ"] > .pd-sl-rt-slide                       { clip-path: circle(0% at 50% 50%); }
[data-pd-sl-on="1"][data-pd-sl-effect="mask-circ"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { clip-path: circle(75% at 50% 50%); }

/* mask-slash — diagonal wipe via polygon. */
[data-pd-sl-on="1"][data-pd-sl-effect="mask-slash"] > .pd-sl-rt-slide                       { clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); }
[data-pd-sl-on="1"][data-pd-sl-effect="mask-slash"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { clip-path: polygon(0 0, 120% 0, 100% 100%, -20% 100%); }

/* curtain — vertical wipe top-to-bottom. */
[data-pd-sl-on="1"][data-pd-sl-effect="curtain"] > .pd-sl-rt-slide                       { clip-path: inset(100% 0 0 0); }
[data-pd-sl-on="1"][data-pd-sl-effect="curtain"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { clip-path: inset(0 0 0 0); }

/* flip3d — perspective rotateY between slides. */
[data-pd-sl-on="1"][data-pd-sl-effect="flip3d"] > .pd-sl-rt-slide                       { transform: rotateY(90deg); transform-origin: center; }
[data-pd-sl-on="1"][data-pd-sl-effect="flip3d"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { transform: rotateY(0); }
[data-pd-sl-on="1"][data-pd-sl-effect="flip3d"] > .pd-sl-rt-slide.pd-sl-rt-slide-prev   { transform: rotateY(-90deg); }

/* dissolve — blur + opacity, like a soft cross-dissolve. */
[data-pd-sl-on="1"][data-pd-sl-effect="dissolve"] > .pd-sl-rt-slide                       { filter: blur(12px); }
[data-pd-sl-on="1"][data-pd-sl-effect="dissolve"] > .pd-sl-rt-slide.pd-sl-rt-slide-active { filter: blur(0); }

/* ── LAYER DEPTH MODE (opt-in: data-pd-sl-depth-on="1") ──────────────
 * A 3D transition where each slide flies in / out along its own Z-depth
 * + direction. The slide's transform is driven INLINE by
 * slider_section_public.js applyLayerDepth() (inline always beats the
 * effect-preset rules above), so here we only:
 *   1. Neutralise the clip-path / filter presets so depth mode is the
 *      sole motion (these come AFTER all effect rules → win on source
 *      order at equal specificity; no !important needed).
 *   2. Lift each slide's background image onto an overscanned ::before
 *      layer so it can lag behind the content for a true parallax feel
 *      (--pd-sl-bgx / --pd-sl-bgy are set per slide by applyLayerDepth).
 * Every selector is gated on [data-pd-sl-depth-on="1"] (default OFF), so
 * normal sliders and all other transition effects are untouched. */
[data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide,
[data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide.pd-sl-rt-slide-active,
[data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide.pd-sl-rt-slide-prev {
    clip-path: none;
    filter: none;
}
/* Hand the slide's own background image off to the parallax layer. */
[data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide[style*="--pd-sl-slide-img"] {
    background-image: none;
}
[data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide[style*="--pd-sl-slide-img"]::before {
    content: "";
    position: absolute;
    inset: -35%;                 /* overscan so the parallax shift never reveals an edge (cap 20% in JS) */
    z-index: -1;                 /* behind the slide's content, in front of nothing */
    pointer-events: none;
    background-image: var(--pd-sl-slide-img-d);
    background-size: cover;
    background-position: center;
    transform: translate3d(var(--pd-sl-bgx, 0%), var(--pd-sl-bgy, 0%), 0);
    transition: transform var(--pd-sl-speed, 500ms) var(--pd-sl-easing, ease);
    will-change: transform;
}
@media (max-width: 992px) {
    [data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide[style*="--pd-sl-slide-img-t"]::before {
        background-image: var(--pd-sl-slide-img-t);
    }
}
@media (max-width: 480px) {
    [data-pd-sl-on="1"][data-pd-sl-depth-on="1"] > .pd-sl-rt-slide[style*="--pd-sl-slide-img-m"]::before {
        background-image: var(--pd-sl-slide-img-m);
    }
}

/* ── OVERLAY ────────────────────────────────────────────────────── */
.pd-sl-rt-overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: var(--pd-sl-overlay-color, #000);
    opacity: var(--pd-sl-overlay-opacity, 0.35);
    z-index: 2;
}

/* ── Defense-in-depth: any runtime-injected chrome on a host that
   isn't currently slider-enabled (toggle is off) is hidden via CSS,
   even if the runtime hasn't yet stripped the DOM. Belt to the
   unmount()-stage suspenders. */
:not([data-pd-sl-on="1"]) > .pd-sl-rt-chrome,
:not([data-pd-sl-on="1"]) > .pd-sl-rt-overlay { display: none; }

/* ── NAVIGATION ARROWS ──────────────────────────────────────────── */
.pd-sl-rt-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    /* The scrim has to be dark enough for the glyph to read against ANY slide
       behind it, since a slider can hold a pale photo or a white panel. At 40%
       the effective backdrop measured ~#999 and the old pale-blue glyph came
       out at 1.35:1 — invisible. 58% lands near #6b6b6b, where the white
       default below measures ~5.3:1. */
    background: rgba(0, 0, 0, 0.58);
    /* Default ink is white BECAUSE the scrim above is dark; the pale blue it
       replaced was a brand tint that only ever worked on a dark slide. An
       instance can still set any colour through --pd-sl-arrow-color. */
    color: var(--pd-sl-arrow-color, #fff);
    border: none;
    border-radius: 50%;
    font-size: 16px;
    cursor: pointer;
    z-index: 5;
    transition: background 0.15s ease;
}
.pd-sl-rt-arrow:hover { background: rgba(0, 0, 0, 0.74); }
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="inside"]  .pd-sl-rt-arrow-prev { left: 12px; }
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="inside"]  .pd-sl-rt-arrow-next { right: 12px; }
/* Outside — the host's overflow:hidden (line 27) clips anything painted
   beyond its edge, so the arrows cannot live OUTSIDE the host box. Instead
   we inset the slide area to open a side gutter (attr-scoped, so Inside /
   Overlap sliders are untouched) and drop the arrows into that gutter —
   which reads as arrows flanking the slides without being clipped. */
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="outside"] > .pd-sl-rt-slide {
    left:  var(--pd-sl-arrow-gutter, 56px);
    right: var(--pd-sl-arrow-gutter, 56px);
    width: auto;
}
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="outside"] .pd-sl-rt-arrow-prev { left: 8px; }
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="outside"] .pd-sl-rt-arrow-next { right: 8px; }
/* Overlap — slides stay full-bleed; arrows sit flush at the very edge so
   the glyph reads over the slide corner while staying inside the clip. */
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="overlap"] .pd-sl-rt-arrow-prev { left: 0; }
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="overlap"] .pd-sl-rt-arrow-next { right: 0; }
[data-pd-sl-on="1"][data-pd-sl-arrows="0"] .pd-sl-rt-arrow { display: none; }

/* ── PAGINATION DOTS ────────────────────────────────────────────── */
.pd-sl-rt-dots {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    bottom: 12px;
    display: flex;
    gap: 6px;
    z-index: 5;
}
/* Below slider — same clip constraint as Outside arrows: the host's
   overflow:hidden eats anything past its edge, so the dots cannot sit
   BELOW the host. Instead inset the slide bottom to open a dots strip
   and seat the dots inside it. Attr-scoped → Inside-bottom untouched. */
[data-pd-sl-on="1"][data-pd-sl-dot-pos="below"] > .pd-sl-rt-slide {
    bottom: var(--pd-sl-dots-gutter, 28px);
    height: auto;
}
[data-pd-sl-on="1"][data-pd-sl-dot-pos="below"] .pd-sl-rt-dots { bottom: 8px; }
.pd-sl-rt-dot {
    width: 10px;
    height: 10px;
    padding: 0;      /* UA <button> padding (1px 6px) otherwise widens the 10px dot to 12px */
    position: relative;
    border-radius: 50%;
    background: var(--pd-sl-dot-color, #89b4fa);
    opacity: 0.5;
    border: none;
    cursor: pointer;
    transition: opacity 0.15s ease, transform 0.15s ease, width 0.15s ease;
}
.pd-sl-rt-dot:hover { opacity: 0.85; }
.pd-sl-rt-dot.pd-sl-rt-dot-active {
    background: var(--pd-sl-dot-active, #89b4fa);
    opacity: 1;
    transform: scale(1.2);
}
[data-pd-sl-on="1"][data-pd-sl-dot-shape="dash"]   .pd-sl-rt-dot { width: 24px; height: 4px; border-radius: 2px; }
[data-pd-sl-on="1"][data-pd-sl-dot-shape="bars"]   .pd-sl-rt-dot { width: 18px; height: 6px; border-radius: 1px; }
[data-pd-sl-on="1"][data-pd-sl-dot-shape="number"] .pd-sl-rt-dot { background: transparent; color: var(--pd-sl-dot-color, #89b4fa); }
[data-pd-sl-on="1"][data-pd-sl-dots="0"] .pd-sl-rt-dots { display: none; }
/* Touch comfort (layout rule 6: ≥44px tap targets — grow the TAP AREA, never
   deform the control). Excluded from pd_device_polish GAP 1, each dot instead
   carries an invisible 44px-tall tap pad spanning its half of the widened gap,
   so pads tile edge-to-edge without overlapping a neighbour's. Visual size is
   untouched on every device. */
@media (max-width: 768px) {
    [data-pd-sl-on="1"] .pd-sl-rt-dots { gap: 18px; }
    [data-pd-sl-on="1"] .pd-sl-rt-dot::before {
        content: "";
        position: absolute;
        left: -9px;
        right: -9px;
        top: 50%;
        height: 44px;
        transform: translateY(-50%);
    }
    [data-pd-sl-on="1"] .pd-sl-rt-arrow { width: 44px; height: 44px; }
}

/* ── COUNTER ────────────────────────────────────────────────────── */
.pd-sl-rt-counter {
    position: absolute;
    top: 12px;
    right: 12px;
    padding: 4px 9px;
    background: rgba(0, 0, 0, 0.5);
    color: #fff;
    border-radius: 4px;
    font-size: 11px;
    font-family: system-ui, sans-serif;
    z-index: 5;
}

/* ── PROGRESS BAR ───────────────────────────────────────────────── */
.pd-sl-rt-progress {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 3px;
    background: rgba(255, 255, 255, 0.1);
    z-index: 5;
    /* Resolved bar colours: prefer the progress-bar's own picker, fall
       back to the slider accent (so already-published sliders without
       the new attr still paint), then the shared hex. The Second colour
       falls back to the Bar colour. No new hardcoded colours. */
    --pd-sl-prog-c:  var(--pd-sl-prog-color,  var(--pd-sl-arrow-color, #89b4fa));
    --pd-sl-prog-c2: var(--pd-sl-prog-color2, var(--pd-sl-prog-c));
}
.pd-sl-rt-progress-fill {
    height: 100%;
    width: 0;
    background: var(--pd-sl-prog-c);
}
/* Keep the progress bar inline with the slides when arrows are "Outside":
   match the same side gutter the slide is inset by, so the bar's width
   tracks the visible slide instead of overhanging into the arrow gutters.
   Attr-scoped → Inside / Overlap (full-bleed slides) untouched. */
[data-pd-sl-on="1"][data-pd-sl-arrow-pos="outside"] .pd-sl-rt-progress {
    left:  var(--pd-sl-arrow-gutter, 56px);
    right: var(--pd-sl-arrow-gutter, 56px);
}

/* ── PROGRESS BAR — visual treatments (panel: Progress Bar tab) ─────
   Every option is attribute-scoped so it only affects sliders that
   opted in, and every colour derives from the slider accent
   (--pd-sl-arrow-color, set by the Navigation Icons colour picker) via
   color-mix — no new hardcoded colours are introduced. The fill's
   width/transition stay JS-driven; these rules only touch the surface
   (background / shadow / radius / track / placement). */

/* Thickness — host bar height. */
[data-pd-sl-on="1"][data-pd-sl-prog-thick="thin"]   .pd-sl-rt-progress { height: 3px; }
[data-pd-sl-on="1"][data-pd-sl-prog-thick="medium"] .pd-sl-rt-progress { height: 5px; }
[data-pd-sl-on="1"][data-pd-sl-prog-thick="thick"]  .pd-sl-rt-progress { height: 9px; }

/* Position — top or bottom edge of the slider. */
[data-pd-sl-on="1"][data-pd-sl-prog-pos="top"]    .pd-sl-rt-progress { top: 0; bottom: auto; }
[data-pd-sl-on="1"][data-pd-sl-prog-pos="bottom"] .pd-sl-rt-progress { bottom: 0; top: auto; }

/* Fill direction — RTL anchors the fill to the right so it grows leftward. */
[data-pd-sl-on="1"][data-pd-sl-prog-dir="rtl"] .pd-sl-rt-progress { display: flex; justify-content: flex-end; }

/* Rounded ends. */
[data-pd-sl-on="1"][data-pd-sl-prog-round="1"] .pd-sl-rt-progress,
[data-pd-sl-on="1"][data-pd-sl-prog-round="1"] .pd-sl-rt-progress-fill { border-radius: 999px; }

/* Click-to-seek — make the bar feel interactive (handler wired in JS). */
[data-pd-sl-on="1"][data-pd-sl-prog-seek="1"] .pd-sl-rt-progress { cursor: pointer; }

/* Track styling — derived from the Bar colour. */
[data-pd-sl-on="1"][data-pd-sl-prog-track="subtle"] .pd-sl-rt-progress { background: color-mix(in srgb, var(--pd-sl-prog-c) 16%, transparent); }
[data-pd-sl-on="1"][data-pd-sl-prog-track="hidden"] .pd-sl-rt-progress { background: transparent; }
[data-pd-sl-on="1"][data-pd-sl-prog-track="solid"]  .pd-sl-rt-progress { background: color-mix(in srgb, var(--pd-sl-prog-c) 30%, transparent); }

/* Fill effect — Solid is the base rule (already var-coloured above).
   Two-tone effects blend the Bar colour with the Second colour. */
[data-pd-sl-on="1"][data-pd-sl-prog-fill="gradient"] .pd-sl-rt-progress-fill {
    background: linear-gradient(90deg, var(--pd-sl-prog-c), var(--pd-sl-prog-c2));
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="agradient"] .pd-sl-rt-progress-fill {
    background: linear-gradient(90deg, var(--pd-sl-prog-c), var(--pd-sl-prog-c2), var(--pd-sl-prog-c));
    background-size: 300% 100%;
    animation: pd-sl-prog-agrad 2.2s linear infinite;
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="striped"] .pd-sl-rt-progress-fill {
    background-image: repeating-linear-gradient(45deg,
        var(--pd-sl-prog-c)  0,   var(--pd-sl-prog-c)  8px,
        var(--pd-sl-prog-c2) 8px, var(--pd-sl-prog-c2) 16px);
    background-size: 22px 100%;
    animation: pd-sl-prog-stripe 0.6s linear infinite;
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="shimmer"] .pd-sl-rt-progress-fill {
    background: color-mix(in srgb, var(--pd-sl-prog-c) 65%, transparent);
    position: relative;
    overflow: hidden;
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="shimmer"] .pd-sl-rt-progress-fill::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 40%;
    background: linear-gradient(90deg, transparent, var(--pd-sl-prog-c), transparent);
    animation: pd-sl-prog-shimmer 1.4s ease-in-out infinite;
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="glow"] .pd-sl-rt-progress-fill {
    background: var(--pd-sl-prog-c);
    box-shadow: 0 0 6px var(--pd-sl-prog-c),
                0 0 12px color-mix(in srgb, var(--pd-sl-prog-c) 60%, transparent);
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="pulse"] .pd-sl-rt-progress-fill {
    background: var(--pd-sl-prog-c);
    animation: pd-sl-prog-pulse 1.1s ease-in-out infinite;
}
[data-pd-sl-on="1"][data-pd-sl-prog-fill="liquid"] .pd-sl-rt-progress-fill {
    background: linear-gradient(90deg, var(--pd-sl-prog-c), var(--pd-sl-prog-c2));
    background-size: 200% 100%;
    box-shadow: 0 0 6px color-mix(in srgb, var(--pd-sl-prog-c) 70%, transparent);
    animation: pd-sl-prog-liquid 2.6s ease-in-out infinite;
}
@keyframes pd-sl-prog-agrad   { 0% { background-position: 0% 0; }   100% { background-position: 300% 0; } }
@keyframes pd-sl-prog-stripe  { from { background-position: 0 0; }  to { background-position: 22px 0; } }
@keyframes pd-sl-prog-shimmer { 0% { transform: translateX(-120%); } 100% { transform: translateX(320%); } }
@keyframes pd-sl-prog-pulse   { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
@keyframes pd-sl-prog-liquid  { 0% { background-position: 0 0; } 50% { background-position: 100% 0; } 100% { background-position: 0 0; } }

/* ── PLAY / PAUSE BUTTON ────────────────────────────────────────── */
.pd-sl-rt-playpause {
    position: absolute;
    top: 12px;
    left: 12px;
    width: 32px;
    height: 32px;
    background: rgba(0, 0, 0, 0.5);
    color: #fff;
    border: none;
    border-radius: 50%;
    cursor: pointer;
    font-size: 12px;
    z-index: 5;
}

/* ── KEN BURNS (background image zoom + pan, runs on active slide) ─
   Variant rules pick the animation name into --pd-sl-kb-anim. The
   single combined animation declaration further below stacks Ken
   Burns and the per-slide entry transition so both effects can play
   together when both are enabled. */
[data-pd-sl-on="1"][data-pd-sl-kb="1"] > .pd-sl-rt-slide.pd-sl-rt-slide-active                                            { --pd-sl-kb-anim: pdSlKbDefault; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="in"]       > .pd-sl-rt-slide.pd-sl-rt-slide-active             { --pd-sl-kb-anim: pdSlKbIn; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="out"]      > .pd-sl-rt-slide.pd-sl-rt-slide-active             { --pd-sl-kb-anim: pdSlKbOut; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="panleft"]  > .pd-sl-rt-slide.pd-sl-rt-slide-active             { --pd-sl-kb-anim: pdSlKbPanLeft; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="panright"] > .pd-sl-rt-slide.pd-sl-rt-slide-active             { --pd-sl-kb-anim: pdSlKbPanRight; }
@keyframes pdSlKbIn       { from { transform: scale(1);    } to { transform: scale(1.15);            } }
@keyframes pdSlKbOut      { from { transform: scale(1.15); } to { transform: scale(1);               } }
@keyframes pdSlKbPanLeft  { from { transform: scale(1.1) translateX(2%);  } to { transform: scale(1.1) translateX(-2%); } }
@keyframes pdSlKbPanRight { from { transform: scale(1.1) translateX(-2%); } to { transform: scale(1.1) translateX(2%);  } }
@keyframes pdSlKbDefault  { from { transform: scale(1);    } to { transform: scale(1.05);            } }

/* ── SCROLL-PINNED DRIVE MODE ───────────────────────────────────── */
.pd-sl-rt-scroll-wrap {
    position: relative;
}
.pd-sl-rt-scroll-host {
    position: sticky;
    top: 0;
    height: 100vh;
}

/* ── REDUCED MOTION (opt-in via data-pd-sl-respect-motion="1") ──── */
@media (prefers-reduced-motion: reduce) {
    [data-pd-sl-on="1"][data-pd-sl-respect-motion="1"] > .pd-sl-rt-slide {
        animation: none;
        transition: opacity 200ms ease;
        transform: none;
    }
}

/* ── PER-SLIDE OVERLAY OVERRIDE ───────────────────────────────────
 * The slide is `display: grid` (cell-picker layout). The overlay must
 * tint the ENTIRE slide (border-box) — above the slide bg/image (z 0)
 * but below the slide content (z 2). An absolutely-positioned pseudo
 * of a grid container with AUTO grid-placement takes the grid
 * container's PADDING box as its containing block (CSS Grid §9.2), so
 * `inset: 0` fills the whole slide regardless of the slide's padding —
 * including a padding the user customises via the Design panel.
 *
 * (Earlier this rule spanned every grid track and negative-inset by a
 * hardcoded 18px to claw back the padding strip. That silently
 * under-covered the instant the slide padding differed from 18px — e.g.
 * a 36px Design-panel padding left an 18px un-tinted frame. The
 * auto-placement + inset:0 form is padding-independent and needs no JS
 * pad-var stamping; verified full-bleed in Chrome, and Firefox/Safari
 * follow the same spec rule.) */
.pd-sl-rt-slide.pd-sl-rt-slide-overlay-on::before {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    /* Per-slide vars win (Dedicated Overlay override); otherwise fall back
       to the slider-wide GLOBAL tint vars (--pd-sl-overlay-*), which inherit
       from the host onto the slide so the global Overlay renders through this
       SAME behind-content scrim (z-index 1) instead of a host-level z-index 2
       layer that washed over nested blocks. */
    background: var(--pd-sl-slide-ov-color, var(--pd-sl-overlay-color, #000));
    opacity: var(--pd-sl-slide-ov-opacity, var(--pd-sl-overlay-opacity, 0.6));
    z-index: 1;
}

/* ── PER-SLIDE TRANSITION OVERRIDE ────────────────────────────────
   Variants pick the entry-transition name into --pd-sl-tr-anim.
   See the combined animation declaration further below. */
/* When a slide has the per-slide Dedicated Transition on (carries
   .pd-sl-rt-slide-trans), the slider-wide effect's transform / clip-path /
   filter must be neutralised so it doesn't double-fire alongside the
   per-slide @keyframes. Specificity (0,4-5,0) beats the slider-wide
   [data-pd-sl-effect="…"] rules. */
[data-pd-sl-on="1"][data-pd-sl-effect] > .pd-sl-rt-slide.pd-sl-rt-slide-trans,
[data-pd-sl-on="1"][data-pd-sl-effect] > .pd-sl-rt-slide.pd-sl-rt-slide-trans.pd-sl-rt-slide-active,
[data-pd-sl-on="1"][data-pd-sl-effect] > .pd-sl-rt-slide.pd-sl-rt-slide-trans.pd-sl-rt-slide-prev {
    transform: none;
    clip-path: none;
    filter: none;
}
/* ── Editor-canvas preview gate ────────────────────────────────────
 * In the editor canvas (body.pd-sl-canvas-mode is stamped by
 * slider_section.js on the iframe body), every panel-driven attribute
 * write fires a GrapesJS view-update that re-renders the slide DOM,
 * which restarts any CSS animation on it. That made the Dedicated
 * Transition replay on every click, slider, or unrelated edit —
 * bad UX. Default: suppress the transition slot of the animation
 * stack (slot 1) while keeping the Ken Burns slot (slot 2) intact.
 * The Dedicated Transition panel arms .pd-sl-trans-preview-armed on
 * the slide for a single preview pass (then strips it) — a higher
 * specificity rule below restores the transition slot while armed.
 * Specificity (0,4,1) beats the base trans rule (0,3,1) below. */
body.pd-sl-canvas-mode .pd-sl-rt-slide.pd-sl-rt-slide-trans.pd-sl-rt-slide-active {
    animation-name: none, var(--pd-sl-kb-anim, none);
}
body.pd-sl-canvas-mode .pd-sl-rt-slide.pd-sl-rt-slide-trans.pd-sl-trans-preview-armed.pd-sl-rt-slide-active {
    animation-name: var(--pd-sl-tr-anim, none), var(--pd-sl-kb-anim, none);
}

.pd-sl-rt-slide.pd-sl-rt-slide-trans.pd-sl-rt-slide-active                                              { --pd-sl-tr-anim: pdSlSlideFade; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="slide"].pd-sl-rt-slide-active             { --pd-sl-tr-anim: pdSlSlideSlide; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="slide-right"].pd-sl-rt-slide-active       { --pd-sl-tr-anim: pdSlSlideSlideRight; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="fade"].pd-sl-rt-slide-active              { --pd-sl-tr-anim: pdSlSlideFade;  }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="zoom"].pd-sl-rt-slide-active              { --pd-sl-tr-anim: pdSlSlideZoom;  }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="split-tb"].pd-sl-rt-slide-active          { --pd-sl-tr-anim: pdSlSlideSplitTb; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="split-lr"].pd-sl-rt-slide-active          { --pd-sl-tr-anim: pdSlSlideSplitLr; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="mask-circ"].pd-sl-rt-slide-active         { --pd-sl-tr-anim: pdSlSlideMaskCirc; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="mask-slash"].pd-sl-rt-slide-active        { --pd-sl-tr-anim: pdSlSlideMaskSlash; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="curtain"].pd-sl-rt-slide-active           { --pd-sl-tr-anim: pdSlSlideCurtain; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="flip3d"].pd-sl-rt-slide-active            { --pd-sl-tr-anim: pdSlSlideFlip3d; }
.pd-sl-rt-slide.pd-sl-rt-slide-trans[data-pd-sl-slide-effect="dissolve"].pd-sl-rt-slide-active          { --pd-sl-tr-anim: pdSlSlideDissolve; }
@keyframes pdSlSlideSlide      { from { transform: translateX(20%); opacity: 0; }                                to { transform: translateX(0); opacity: 1; } }
@keyframes pdSlSlideSlideRight { from { transform: translateX(-20%); opacity: 0; }                               to { transform: translateX(0); opacity: 1; } }
@keyframes pdSlSlideFade       { from { opacity: 0; }                                                            to { opacity: 1; } }
@keyframes pdSlSlideZoom       { from { transform: scale(0.9); opacity: 0; }                                     to { transform: scale(1); opacity: 1; } }
@keyframes pdSlSlideSplitTb    { from { clip-path: inset(50% 0 50% 0); opacity: 0.4; }                           to { clip-path: inset(0 0 0 0); opacity: 1; } }
@keyframes pdSlSlideSplitLr    { from { clip-path: inset(0 50% 0 50%); opacity: 0.4; }                           to { clip-path: inset(0 0 0 0); opacity: 1; } }
@keyframes pdSlSlideMaskCirc   { from { clip-path: circle(0% at 50% 50%); }                                      to { clip-path: circle(75% at 50% 50%); } }
@keyframes pdSlSlideMaskSlash  { from { clip-path: polygon(0 0, 0 0, 0 100%, 0 100%); }                          to { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); } }
@keyframes pdSlSlideCurtain    { from { clip-path: inset(0 0 100% 0); }                                          to { clip-path: inset(0 0 0 0); } }
@keyframes pdSlSlideFlip3d     { from { transform: perspective(1200px) rotateY(90deg); opacity: 0; }             to { transform: perspective(1200px) rotateY(0); opacity: 1; } }
@keyframes pdSlSlideDissolve   { from { opacity: 0; filter: blur(8px); }                                         to { opacity: 1; filter: blur(0); } }

/* ── PER-SLIDE KEN BURNS OVERRIDE ─────────────────────────────────
   Per-slide writes shadow any slider-wide --pd-sl-kb-anim set above. */
.pd-sl-rt-slide.pd-sl-rt-slide-kb.pd-sl-rt-slide-active                                                 { --pd-sl-kb-anim: pdSlKbDefault; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="in"].pd-sl-rt-slide-active                   { --pd-sl-kb-anim: pdSlKbIn; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="out"].pd-sl-rt-slide-active                  { --pd-sl-kb-anim: pdSlKbOut; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="panleft"].pd-sl-rt-slide-active              { --pd-sl-kb-anim: pdSlKbPanLeft; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="panright"].pd-sl-rt-slide-active             { --pd-sl-kb-anim: pdSlKbPanRight; }

/* ── Ken Burns — video-amplified keyframes ───────────────────────────
 * The image keyframes (above, scale ~1.1 + translate ±2%) read fine
 * on a static photo, but on a moving video frame the slow pan is
 * masked by the video's own motion content — `direction: alternate`
 * + a 12-28s duration works out to roughly 2px/sec of horizontal
 * travel, which is invisible against any non-static footage. Video-
 * specific keyframes use ~4× amplitude so the burn reads clearly
 * against moving content while still being a tasteful background
 * effect (no zoom past 1.3×, no pan past ±8%). Selected via :has()
 * so image slides keep the gentler image-tuned values. */
@keyframes pdSlKbInVid       { from { transform: scale(1);    } to { transform: scale(1.30);            } }
@keyframes pdSlKbOutVid      { from { transform: scale(1.30); } to { transform: scale(1);               } }
@keyframes pdSlKbPanLeftVid  { from { transform: scale(1.20) translateX(8%);  } to { transform: scale(1.20) translateX(-8%); } }
@keyframes pdSlKbPanRightVid { from { transform: scale(1.20) translateX(-8%); } to { transform: scale(1.20) translateX(8%);  } }
@keyframes pdSlKbDefaultVid  { from { transform: scale(1);    } to { transform: scale(1.20);            } }

/* Per-slide swap — :has() bumps specificity above the image rules. */
.pd-sl-rt-slide.pd-sl-rt-slide-kb.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])                                                                  { --pd-sl-kb-anim: pdSlKbDefaultVid; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="in"].pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])                                    { --pd-sl-kb-anim: pdSlKbInVid; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="out"].pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])                                   { --pd-sl-kb-anim: pdSlKbOutVid; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="panleft"].pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])                               { --pd-sl-kb-anim: pdSlKbPanLeftVid; }
.pd-sl-rt-slide.pd-sl-rt-slide-kb[data-pd-sl-slide-kb-dir="panright"].pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])                              { --pd-sl-kb-anim: pdSlKbPanRightVid; }
/* Slider-wide swap (when the host's data-pd-sl-kb="1" drives KB). */
[data-pd-sl-on="1"][data-pd-sl-kb="1"] > .pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])                                           { --pd-sl-kb-anim: pdSlKbDefaultVid; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="in"]       > .pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])             { --pd-sl-kb-anim: pdSlKbInVid; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="out"]      > .pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])             { --pd-sl-kb-anim: pdSlKbOutVid; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="panleft"]  > .pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])             { --pd-sl-kb-anim: pdSlKbPanLeftVid; }
[data-pd-sl-on="1"][data-pd-sl-kb="1"][data-pd-sl-kb-dir="panright"] > .pd-sl-rt-slide.pd-sl-rt-slide-active:has(> .pd-sl-rt-slide-video[src])             { --pd-sl-kb-anim: pdSlKbPanRightVid; }

/* ── COMBINED ANIMATION (transition + Ken Burns stack) ────────────
   Every active slide gets two animation slots. Slot 1 = entry
   transition (--pd-sl-tr-anim), slot 2 = Ken Burns (--pd-sl-kb-anim).
   Either slot falls back to `none` when its respective effect is off,
   making the slot inert. When both are on, transition runs first
   (0s delay) and Ken Burns starts at --pd-sl-slide-speed so the
   entry plays to completion before the long zoom/pan kicks in.
   Without this combined declaration the single-property `animation`
   shorthand from whichever rule appears later in source order
   replaces the other, and one of the two effects silently no-ops. */
.pd-sl-rt-slide.pd-sl-rt-slide-active {
    animation-name:            var(--pd-sl-tr-anim, none),         var(--pd-sl-kb-anim, none);
    animation-duration:        var(--pd-sl-slide-speed, 500ms),    var(--pd-sl-slide-kb-dur, var(--pd-sl-kb-dur, 12s));
    animation-timing-function: ease,                               ease;
    animation-delay:           0s,                                 var(--pd-sl-slide-speed, 0s);
    animation-fill-mode:       both,                               forwards;
}

/* ── LINK OVERLAY ──────────────────────────────────────────────────
   Selector must be more specific than ".pd-sl-rt-slide > *" (line ~361)
   which forces every direct slide child to position:relative + z-index:2
   for caption layering. Without the (0,2,0) bump, the anchor inherits
   position:relative, inset:0 becomes a no-op, the anchor collapses to
   0×0, and clicks miss it entirely. z-index:3 places the (transparent)
   anchor above captions so it catches whole-slide clicks. */
.pd-sl-rt-slide > .pd-sl-rt-slide-link {
    position: absolute;
    inset: 0;
    display: block;
    text-decoration: none;
    color: inherit;
    z-index: 3;
}

/* ── SLIDE LAYOUT (2-D ROW × COLUMN GRID) ──────────────────────────
 * Each slide is a CSS Grid with R row tracks and C column tracks.
 * R and C come from two custom properties: --pd-sl-rows and
 * --pd-sl-cols.
 *
 * Default R is aspect-ratio driven (Hero 2, Standard 3 … Tall 5).
 * Default C is always 1 (most designs are a vertical stack).
 *
 * Per-slide overrides: `data-pd-sl-slide-rows="R"` and
 * `data-pd-sl-slide-cols="C"` on the slide itself override the
 * aspect-derived defaults so every slide can carry its own grid.
 *
 * Block placement: each preset block carries
 * `data-pd-sl-row="<1..R>"` + `data-pd-sl-col="<1..C>"` and is
 * placed into that grid cell. Up / Down / Left / Right buttons in
 * the Slider panel mutate these attributes; collisions are auto
 * resolved by swapping the sibling's cell with the mover's old
 * cell (so any single cell holds 0-or-1 blocks under normal use).
 *
 * Legacy data attributes (data-pd-sl-pin*) are no longer consumed;
 * old slides re-flow under the new model.
 */
.pd-sl-rt-slide {
    display: grid;
    grid-template-rows:    repeat(var(--pd-sl-rows, 2), 1fr);
    grid-template-columns: repeat(var(--pd-sl-cols, 2), 1fr);
    padding: 18px;
}
/* Slide-cell-hosted Columns: stretch to fill their cell and use flex
   centering so the inner Core Block content (Heading, Image, Video, …)
   sits at the vertical + horizontal center of the cell instead of
   stacking from the top. Higher specificity than the generic
   .pd-sl-rt-slide > * rule below so this wins. */
.pd-sl-rt-slide > .gjs-cell {
    align-self: stretch;
    justify-self: stretch;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
}
/* Auto-fit for replaced elements inside slide-cell Columns. Scoped
   to .pd-sl-rt-slide descendants only — Columns elsewhere on the
   canvas are NOT affected. `object-fit: contain` preserves aspect
   ratio and never crops the user's content; an aspect mismatch
   between the asset and the cell shows as transparent letterbox
   bars on the short axis. Composite HTML blocks (Heading, Cards,
   Progress Bars, …) are NOT replaced elements and keep their
   authored intrinsic size — that's the standard editor behaviour. */
.pd-sl-rt-slide > .gjs-cell img,
.pd-sl-rt-slide > .gjs-cell video {
    width: 100%;
    height: 100%;
    object-fit: contain;
}
.pd-sl-rt-slide > .gjs-cell iframe {
    width: 100%;
    height: 100%;
    border: 0;
}
.pd-sl-rt-slide > * {
    position: relative;
    z-index: 2;
    align-self: center;
    justify-self: stretch;
    min-height: 0;
    min-width: 0;
    /* Lock every grid item to its cell bounds. max-height/width:100%
       caps the block at one cell; overflow:hidden clips any internal
       content that exceeds it. (container-type intentionally NOT set
       — `size` collapses content-based height when align-self isn't
       stretch, which breaks block layouts; revisit later if we wire
       cell-relative typography.) */
    /* 6px margin keeps the cell content (real blocks, "+" adders,
       drag-hover outline, armed border) visibly inset from the
       gridlines instead of overlapping them. max-height/width
       adjusted to calc(100% - 12px) so a content-filled block can
       still fit without pushing past the cell boundary. */
    margin: 6px;
    max-height: calc(100% - 12px);
    max-width: calc(100% - 12px);
    overflow: hidden;
}
/* Per-block grid placement. Up to 8 rows × 4 cols covers every
   reasonable layout. Blocks without these attrs fall back to
   grid auto-placement. */
.pd-sl-rt-slide > [data-pd-sl-row="1"] { grid-row: 1; }
.pd-sl-rt-slide > [data-pd-sl-row="2"] { grid-row: 2; }
.pd-sl-rt-slide > [data-pd-sl-row="3"] { grid-row: 3; }
.pd-sl-rt-slide > [data-pd-sl-row="4"] { grid-row: 4; }
.pd-sl-rt-slide > [data-pd-sl-row="5"] { grid-row: 5; }
.pd-sl-rt-slide > [data-pd-sl-row="6"] { grid-row: 6; }
.pd-sl-rt-slide > [data-pd-sl-row="7"] { grid-row: 7; }
.pd-sl-rt-slide > [data-pd-sl-row="8"] { grid-row: 8; }
.pd-sl-rt-slide > [data-pd-sl-col="1"] { grid-column: 1; }
.pd-sl-rt-slide > [data-pd-sl-col="2"] { grid-column: 2; }
.pd-sl-rt-slide > [data-pd-sl-col="3"] { grid-column: 3; }
.pd-sl-rt-slide > [data-pd-sl-col="4"] { grid-column: 4; }
/* Aspect-ratio → default row + column count (when slide has no override).
   Default is 2×2 across every aspect — a balanced starting grid the user
   can refine via the SLIDE LAYOUT dropdowns in the panel. */
[data-pd-sl-on="1"][data-pd-sl-ratio="hero"]       > .pd-sl-rt-slide { --pd-sl-rows: 2; --pd-sl-cols: 2; }
[data-pd-sl-on="1"][data-pd-sl-ratio="standard"]   > .pd-sl-rt-slide { --pd-sl-rows: 2; --pd-sl-cols: 2; }
[data-pd-sl-on="1"][data-pd-sl-ratio="square"]     > .pd-sl-rt-slide { --pd-sl-rows: 2; --pd-sl-cols: 2; }
[data-pd-sl-on="1"][data-pd-sl-ratio="tall"]       > .pd-sl-rt-slide { --pd-sl-rows: 2; --pd-sl-cols: 2; }
[data-pd-sl-on="1"][data-pd-sl-ratio="fullwindow"] > .pd-sl-rt-slide { --pd-sl-rows: 2; --pd-sl-cols: 2; }
[data-pd-sl-on="1"][data-pd-sl-ratio="custom"]     > .pd-sl-rt-slide { --pd-sl-rows: 2; --pd-sl-cols: 2; }
/* Per-slide overrides take precedence over aspect-derived defaults.
   The aspect rule above is `[data-pd-sl-on="1"][data-pd-sl-ratio="…"] > .pd-sl-rt-slide`
   — specificity (0,0,3,0). We must match that specificity here AND
   rely on source order to win, hence the `[data-pd-sl-on="1"] >`
   prefix on every per-slide override. */
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-rows="1"] { --pd-sl-rows: 1; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-rows="2"] { --pd-sl-rows: 2; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-rows="3"] { --pd-sl-rows: 3; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-rows="4"] { --pd-sl-rows: 4; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-rows="5"] { --pd-sl-rows: 5; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-rows="6"] { --pd-sl-rows: 6; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-cols="1"] { --pd-sl-cols: 1; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-cols="2"] { --pd-sl-cols: 2; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-cols="3"] { --pd-sl-cols: 3; }
[data-pd-sl-on="1"] > .pd-sl-rt-slide[data-pd-sl-slide-cols="4"] { --pd-sl-cols: 4; }

/* ── DRAG-OVER CELL HIGHLIGHT ───────────────────────────────────────
 * Replaces GrapesJS' default per-position drop indicator with a
 * single highlight on the cell under the cursor. pd_sl_pnl.js
 * appends a .pd-sl-rt-cell-hover div as a direct child of the slide
 * during drag, sized via inline grid-row / grid-column to the target
 * cell. Removed on drag end.
 *
 * GrapesJS' base CSS uses `!important` for placeholder background /
 * border / min-height / max-height. We use `visibility: hidden`
 * instead of `display: none` — GJS doesn't set visibility, so our
 * normal-priority rule wins without `!important`. The 4px element
 * still occupies its layout slot but is rendered invisible. */
[data-pd-sl-on="1"] .gjs-placeholder,
[data-pd-sl-on="1"] .gjs-placeholder-int,
[data-pd-sl-on="1"] .gjs-placeholder-v,
[data-pd-sl-on="1"] .gjs-placeholder-h,
[data-pd-sl-on="1"] [class*="gjs-placeholder"] {
    visibility: hidden;
}
.pd-sl-rt-slide > .pd-sl-rt-cell-hover {
    outline: 2px dashed #a6e3a1;
    outline-offset: -2px;
    pointer-events: none;
    /* Above the grid-line overlay (::after z-index 5) so the dashed
       outline visually replaces the solid grid lines on this cell's
       perimeter while the drag is in progress. On drag end the
       highlight node is removed and the solid grid lines return. */
    z-index: 6;
    border-radius: 2px;
}

/* ── EMPTY-CELL "+" ADDER (Webflow + Bricks 2.0 pattern) ──────────────
 * pd_sl_pnl.js injects a `<button class="pd-sl-rt-cell-add">` into
 * every empty cell of the slide that's currently editing. Click →
 * activates the Blocks tab in the left panel so the user can drag
 * a preset into the cell. DOM-only — never persists into saved HTML.
 *
 * Hidden during drag (when a sibling .pd-sl-rt-cell-hover exists) so
 * the dashed-outline drag indicator owns the visual focus. */
.pd-sl-rt-slide > .pd-sl-rt-cell-add {
    align-self: stretch;
    justify-self: stretch;
    min-height: 0;
    min-width: 0;
    appearance: none;
    background: transparent;
    border: 1px dashed rgba(166, 227, 161, 0.35);
    color: rgba(166, 227, 161, 0.55);
    font-size: 28px;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    pointer-events: auto;
    transition: background 120ms ease, color 120ms ease, border-color 120ms ease, transform 120ms ease;
    z-index: 3;
}
.pd-sl-rt-slide > .pd-sl-rt-cell-add:hover {
    background: rgba(166, 227, 161, 0.10);
    color: #a6e3a1;
    border-color: #a6e3a1;
    transform: scale(1.02);
}
/* Armed state: the cell the user clicked to target. Solid green
   outline + pulse so it's obvious which cell the next block will land
   in while the user picks from the Blocks panel. */
.pd-sl-rt-slide > .pd-sl-rt-cell-add[data-pd-sl-armed="1"] {
    background: rgba(166, 227, 161, 0.18);
    color: #a6e3a1;
    border: 2px solid #a6e3a1;
    border-style: solid;
    box-shadow: 0 0 0 2px rgba(166, 227, 161, 0.25);
    animation: pd-sl-rt-armed-pulse 1.4s ease-in-out infinite;
}
.pd-sl-rt-slide > .pd-sl-rt-cell-add[data-pd-sl-armed="1"] > i.fa-plus::before {
    content: '\f00c'; /* fa-check */
}
@keyframes pd-sl-rt-armed-pulse {
    0%, 100% { box-shadow: 0 0 0 2px rgba(166, 227, 161, 0.25); }
    50%      { box-shadow: 0 0 0 6px rgba(166, 227, 161, 0.10); }
}
.pd-sl-rt-slide:has(> .pd-sl-rt-cell-hover) > .pd-sl-rt-cell-add {
    visibility: hidden;
}


/* ── EDITOR-ONLY GRID OVERLAY ──────────────────────────────────────
 * Visualises the current N×M grid on the slide that's expanded in
 * the Slider Settings panel. Same green (#a6e3a1) used for
 * [data-pd-block].gjs-selected outlines.
 *
 * The marker attribute `data-pd-sl-grid-overlay="1"` is stamped onto
 * the slide's DOM element ONLY (never via comp.addAttributes), so it
 * is never serialised into the saved HTML. pd_sl_pnl.js re-stamps
 * after any model attr change because GrapesJS's view.updateAttributes
 * strips DOM-only attrs.
 *
 * Outline = grid bounds. Inner background-image lines = cell
 * dividers (1px). 18px inset matches the slide's grid padding so the
 * overlay aligns with the actual cell area.
 */
.pd-sl-rt-slide[data-pd-sl-grid-overlay="1"]::after {
    content: '';
    position: absolute;
    inset: 18px;
    pointer-events: none;
    z-index: 5;
    background-image:
        linear-gradient(to right,  #a6e3a1 0, #a6e3a1 1px, transparent 1px),
        linear-gradient(to bottom, #a6e3a1 0, #a6e3a1 1px, transparent 1px);
    background-size:
        calc(100% / var(--pd-sl-cols, 2)) 100%,
        100% calc(100% / var(--pd-sl-rows, 2));
    background-position: 0 0, 0 0;
    background-repeat: repeat;
    /* Each gradient layer paints a 1px line at the START of every tile,
       i.e. at x=0 + every column boundary and y=0 + every row boundary.
       We don't want the x=0 / y=0 perimeter lines — clip the top 1px
       and left 1px so only the interior dividers remain. No right /
       bottom edge lines exist because background-repeat doesn't draw
       a tile that starts exactly at 100% (it falls outside the box). */
    clip-path: inset(1px 0 0 1px);
}

/* ══════════════════════════════════════════════════════════════════════
   ADMIN OVERRIDE PAGES — contain the full-window / full-screen breakout
   ----------------------------------------------------------------------
   The Full-window and Full-screen ratios size the slider host to 100vw so
   it fills the browser edge-to-edge on a public page. On the ADMIN side the
   left sidebar offsets the content, so a viewport-wide slider spills back
   over the sidebar/chrome. `#wrapper` exists ONLY on admin (public pages
   have no #wrapper), so scope the clamp there: cap the host to its content
   area instead of the raw viewport. Public pages keep the true 100vw splash.
   Higher #id specificity beats the ratio attribute rules above — no
   !important, mirrors the full-width column fix in pd_col_visibility_public. */
#wrapper [data-pd-sl-on="1"] {
    max-width: 100%;
}

