/**
 * Audace — Pure-JS sticky-slide effect for `.is-style-slides-container`.
 *
 * Flash-free guarantee:
 * - `visibility` is NEVER written to individual slides. Toggling it is
 *   inherently racy because the browser can paint between the transform write
 *   and the visibility write. Instead, off-screen slides are purely hidden
 *   by geometry: translateY(slideH) places them exactly one viewport-height
 *   below the visible area. They are physically off-screen; they cannot flash.
 * - During teardown/rebuild the entire container is hidden with
 *   `visibility: hidden` (which CSS-inherits to all children including
 *   position:fixed ones). It is restored only after update() has written
 *   correct transforms for every slide in the same frame. The user sees
 *   nothing between teardown and the first correct paint.
 */
(function () {
    'use strict';

    var MIN_WIDTH          = 782;
    var CONTAINER_SELECTOR = '.is-style-slides-container';
    var SLIDE_SELECTOR     = ':scope > .is-style-slide';

    var containers = [];
    var scheduled  = false;

    // ─── Helpers ──────────────────────────────────────────────────────────────

    function topOffset() {
        var adminBar = document.getElementById('wpadminbar');
        var header   = document.querySelector(
            '.wp-block-template-part.site-header,' +
            'header.wp-block-template-part,' +
            '#masthead'
        );
        var adminH  = adminBar ? adminBar.getBoundingClientRect().height : 0;
        var headerH = header   ? header.getBoundingClientRect().height   : 0;
        return adminH + headerH;
    }

    function layoutRect(el) {
        var r = el.getBoundingClientRect();
        return { left: r.left + window.scrollX, width: r.width };
    }

    function ensureSlideInner(slide) {
        var first = slide.firstElementChild;
        if (
            slide.children.length === 1 &&
            first &&
            first.classList.contains('audace-slide-inner')
        ) {
            first.style.display = 'flow-root';
            return first;
        }
        var inner = slide.ownerDocument.createElement('div');
        inner.className = 'audace-slide-inner';
        inner.style.display = 'flow-root';
        while (slide.firstChild) inner.appendChild(slide.firstChild);
        slide.appendChild(inner);
        return inner;
    }

    function bottomStickyHeight(slide) {
        var cover = slide.querySelector(
            ':scope > .wp-block-column:nth-child(2) .wp-block-cover.is-style-bottom-sticky'
        );
        return cover ? cover.getBoundingClientRect().height : 0;
    }

    function tallestOverflowingCol(slide, maxH) {
        var cols = slide.querySelectorAll(':scope > .wp-block-column');
        if (cols.length) {
            var pick   = null;
            var picked = 0;
            for (var i = 0; i < cols.length; i++) {
                var over = Math.round(cols[i].scrollHeight) - maxH;
                if (over > picked) { picked = over; pick = cols[i]; }
            }
            return pick;
        }
        if (Math.round(slide.scrollHeight) > maxH + 1) {
            return ensureSlideInner(slide);
        }
        return null;
    }

    // Strip all scripted styles from a slide synchronously.
    // The container is already `visibility:hidden` when this runs so
    // there is nothing to flash regardless of style-write ordering.
    function clearSlide(slide) {
        [
            'position', 'top', 'left', 'width', 'height',
            'zIndex', 'transform', 'overflow',
            'justifyContent', 'alignItems', 'willChange'
        ].forEach(function (p) { slide.style[p] = ''; });

        slide.querySelectorAll(
            ':scope > .wp-block-column, :scope > .audace-slide-inner'
        ).forEach(function (c) {
            c.style.transform  = '';
            c.style.translate  = '';
            c.style.willChange = '';
        });
    }

    // ─── Build / teardown ─────────────────────────────────────────────────────

    function teardown() {
        containers.forEach(function (c) {
            c.el.style.height   = '';
            c.el.style.position = '';
            if (c.resizeObs) c.resizeObs.disconnect();
            c.slides.forEach(function (s) { clearSlide(s.el); });
        });
        containers = [];
    }

    function build() {
        // Step 1 — Hide all containers synchronously before touching anything.
        // `visibility:hidden` is inherited by all descendants including
        // position:fixed children. Nothing can paint during teardown/rebuild.
        var containerEls = document.querySelectorAll(CONTAINER_SELECTOR);
        containerEls.forEach(function (el) {
            el.style.visibility = 'hidden';
        });

        // Step 2 — Tear down previous state (safe: containers are hidden).
        teardown();

        // Step 3 — Measure and build new state.
        var topH   = topOffset();
        var slideH = window.innerHeight - topH;
        var mobile = window.innerWidth < MIN_WIDTH;

        containerEls.forEach(function (container) {
            var slideEls = container.querySelectorAll(SLIDE_SELECTOR);
            if (!slideEls.length) return;

            var slides = [];
            var cursor = 0;

            slideEls.forEach(function (el, idx) {
                var bsH     = mobile ? bottomStickyHeight(el) : 0;
                var colMaxH = slideH - bsH;
                var tall    = tallestOverflowingCol(el, colMaxH);

                var availH = colMaxH;
                if (tall && tall.classList.contains('audace-slide-inner')) {
                    var cs = getComputedStyle(el);
                    availH = colMaxH -
                        (parseFloat(cs.paddingTop)    || 0) -
                        (parseFloat(cs.paddingBottom) || 0);
                    el.style.justifyContent = 'flex-start';
                    el.style.alignItems     = 'stretch';
                }

                var tallH = tall
                    ? Math.round(Math.max(tall.offsetHeight || 0, tall.scrollHeight || 0))
                    : 0;
                var pin   = tall ? Math.max(0, tallH - availH) : 0;
                var entry = idx === 0 ? 0 : slideH;
                var exit  = idx === slideEls.length - 1 ? slideH : 0;

                slides.push({
                    el: el, tallCol: tall,
                    entry: entry, pin: pin, exit: exit, start: cursor
                });
                cursor += entry + pin + exit;
            });

            container.style.position = 'relative';
            container.style.height   = cursor + 'px';

            // Park every slide off-screen via transform. Geometry is the only
            // hiding mechanism — no visibility writes on individual slides.
            slideEls.forEach(function (el, idx) {
                el.style.position   = 'fixed';
                el.style.top        = topH + 'px';
                el.style.height     = slideH + 'px';
                el.style.zIndex     = String(idx + 1);
                el.style.overflow   = 'hidden';
                el.style.willChange = 'transform';
                el.style.transform  = 'translate3d(0,' + slideH + 'px,0)';
            });

            var obs = null;
            if (typeof ResizeObserver !== 'undefined') {
                obs = new ResizeObserver(scheduleBuild);
                slideEls.forEach(function (el) { obs.observe(el); });
            }

            containers.push({
                el:        container,
                slides:    slides,
                total:     cursor,
                maxH:      slideH,
                resizeObs: obs
            });
        });

        // Step 4 — Wait for layout to settle, then paint correctly.
        // First rAF: browser commits the new container height to layout.
        // Second rAF: getBoundingClientRect() is now accurate.
        // update() writes correct transforms; then we lift visibility — all
        // in the same style flush, so the user never sees an intermediate state.
        requestAnimationFrame(function () {
            requestAnimationFrame(function () {
                update();
                containerEls.forEach(function (el) {
                    el.style.visibility = '';
                });
            });
        });
    }

    var buildTimer = 0;
    function scheduleBuild() {
        clearTimeout(buildTimer);
        buildTimer = setTimeout(build, 150);
    }

    // ─── Per-frame update ─────────────────────────────────────────────────────

    function update() {
        var scrollY = window.scrollY;
        var topH    = topOffset();

        containers.forEach(function (c) {
            var lr   = layoutRect(c.el);
            var cTop = c.el.getBoundingClientRect().top + window.scrollY;
            var p    = scrollY - (cTop - topH);

            var insideContainer = p >= 0 && p <= c.total;

            var last      = c.slides[c.slides.length - 1];
            var exitStart = last.start + last.entry + last.pin;
            var containerShift = p > exitStart
                ? -Math.min(p - exitStart, last.exit)
                : 0;

            c.slides.forEach(function (s) {
                var local = p - s.start;

                s.el.style.left  = lr.left  + 'px';
                s.el.style.width = lr.width + 'px';
                s.el.style.top   = topH     + 'px';

                var translateY = 0;
                var colOffset  = 0;

                if (!insideContainer || local < 0) {
                    // Off-screen: parked one full viewport-height below.
                    // Pure geometry — no visibility toggle, no race condition.
                    translateY = c.maxH;
                } else if (local < s.entry) {
                    translateY = c.maxH - local;
                } else if (local < s.entry + s.pin) {
                    colOffset  = local - s.entry;
                } else {
                    colOffset  = s.pin;
                }

                translateY += containerShift;

                s.el.style.transform = 'translate3d(0,' + translateY + 'px,0)';

                if (s.tallCol) {
                    var isInner = s.tallCol.classList.contains('audace-slide-inner');
                    if (isInner) {
                        s.tallCol.style.translate = colOffset
                            ? '0 ' + (-colOffset) + 'px' : '';
                    } else {
                        s.tallCol.style.transform = colOffset
                            ? 'translate3d(0,' + (-colOffset) + 'px,0)' : '';
                    }
                }
            });
        });
    }

    function scheduleUpdate() {
        if (scheduled) return;
        scheduled = true;
        requestAnimationFrame(function () {
            scheduled = false;
            if (containers.length) update();
        });
    }

    // ─── Resize ───────────────────────────────────────────────────────────────

    var lastBuildW = window.innerWidth;
    function onResize() {
        if (window.innerWidth === lastBuildW) return;
        lastBuildW = window.innerWidth;
        scheduleBuild();
    }

    // ─── Public API ───────────────────────────────────────────────────────────

    window.audaceScrollYFor = function (el) {
        if (!el) return null;
        var topH = topOffset();
        for (var i = 0; i < containers.length; i++) {
            var c = containers[i];
            for (var j = 0; j < c.slides.length; j++) {
                var s = c.slides[j];
                if (s.el === el || s.el.contains(el)) {
                    var cTop = c.el.getBoundingClientRect().top + window.scrollY;
                    return (cTop - topH) + s.start + s.entry;
                }
            }
        }
        var r = el.getBoundingClientRect();
        return window.scrollY + r.top - topH;
    };

    window.audaceSlideState = function () { return containers; };

    // ─── Boot ─────────────────────────────────────────────────────────────────

    function init() {
        build();
        window.addEventListener('scroll', scheduleUpdate, { passive: true });
        window.addEventListener('resize', onResize);
        window.addEventListener('load',   build);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();