HEX
Server: nginx/1.18.0
System: Linux vcwordpress 5.15.0-174-generic #184-Ubuntu SMP Fri Mar 13 18:41:50 UTC 2026 x86_64
User: root (0)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //var/www/viitorx.stgviitor.com/wp-content/themes/viitorx/js/front-page-animations.js
        /* ─── SMOOTH SCROLL (Lenis) ────────────────────────────── */
        const lenis = new Lenis({
            duration: 1.08,
            easing: (t) => Math.min(1, 1.001 - Math.pow(2, -10 * t)),
            orientation: 'vertical',
            gestureOrientation: 'vertical',
            smoothWheel: true,
            /* Keeps Lenis.scroll in sync with real scroll on touch — required for GSAP ScrollTrigger pin + scrub */
            syncTouch: true,
            syncTouchLerp: 0.068,
            touchInertiaMultiplier: 42,
            wheelMultiplier: 1,
            touchMultiplier: 1.03,
            infinite: false,
            /* intl-tel-input / Smart Phone CF7 — country picker is a nested scroll panel; Lenis must not swallow wheel/touch */
            prevent(node) {
                return (
                    !!node &&
                    typeof node.closest === 'function' &&
                    Boolean(node.closest('.iti__dropdown-content'))
                );
            },
        });

        /** Touch-scroll tuning per viewport size.
         *  Phone (≤640px): near-native 1:1 multiplier + low inertia so short cards don't fly past.
         *  Tablet (641–1024px): moderate multiplier with slightly reduced inertia.
         *  Desktop: high-quality smooth wheel with stronger easing. */
        (function viitorxLenisTouchTune() {
            var mqPhone  = window.matchMedia('(max-width: 640px)');
            var mqTablet = window.matchMedia('(min-width: 641px) and (max-width: 1024px)');

            function apply() {
                var o = lenis.options;
                if (!o) return;

                if (mqPhone.matches) {
                    /* Phone: 1:1 with finger, minimal inertia — prevents flying past short sticky cards */
                    o.syncTouchLerp          = 0.12;
                    o.touchInertiaMultiplier = 12;
                    o.touchMultiplier        = 1.0;
                    o.duration               = 0.72;
                    o.lerp                   = 0.14;
                } else if (mqTablet.matches) {
                    /* Tablet: slightly faster than native, moderate inertia */
                    o.syncTouchLerp          = 0.09;
                    o.touchInertiaMultiplier = 22;
                    o.touchMultiplier        = 1.2;
                    o.duration               = 0.88;
                    o.lerp                   = 0.12;
                } else {
                    /* Desktop: full smooth wheel */
                    o.syncTouchLerp          = 0.068;
                    o.touchInertiaMultiplier = 42;
                    o.touchMultiplier        = 1.03;
                    o.duration               = 1.08;
                    o.lerp                   = 0.1;
                }

                if (typeof lenis.resize === 'function') lenis.resize();
            }

            apply();
            [mqPhone, mqTablet].forEach(function(mq) {
                if (mq.addEventListener) mq.addEventListener('change', apply);
                else if (mq.addListener) mq.addListener(apply);
            });
        })();

        /* ScrollTrigger.scrollerProxy removed: Lenis 1.1.x + ST proxy caused indexOf/undefined errors and frozen scroll.
           Official Lenis+GSAP setup: ticker raf + ScrollTrigger.update only (see lenis README). */

        lenis.on('scroll', ScrollTrigger.update);
        gsap.ticker.add((time) => {
            lenis.raf(time * 1000);
        });
        gsap.ticker.lagSmoothing(0);

        gsap.registerPlugin(ScrollTrigger);
        ScrollTrigger.config({
            ignoreMobileResize: true
        });

        /**
         * Bridge ScrollTrigger ↔ Lenis: ST must read/write the same scroll value Lenis animates.
         * Without scrollerProxy, pin/unpin corrects native scroll while Lenis still holds
         * animatedScroll → visible jump. (See Lenis README "GSAP ScrollTrigger".)
         */
        (function setupLenisScrollTriggerBridge() {
            var scrollerEl = document.scrollingElement || document.documentElement;
            ScrollTrigger.defaults({
                scroller: scrollerEl
            });
            ScrollTrigger.scrollerProxy(scrollerEl, {
                scrollTop: function(value) {
                    if (arguments.length) {
                        lenis.scrollTo(value, {
                            immediate: true,
                            force: true
                        });
                    }
                    var ls = lenis && lenis.scroll;
                    if (typeof ls === 'number' && !isNaN(ls)) {
                        return ls;
                    }
                    return (document.scrollingElement || document.documentElement).scrollTop || 0;
                },
                scrollLeft: function() {
                    return window.scrollX || scrollerEl.scrollLeft || 0;
                },
                getBoundingClientRect: function() {
                    return {
                        top: 0,
                        left: 0,
                        width: window.innerWidth,
                        height: window.innerHeight
                    };
                }
            });
        })();

        /* Pin spacing changes document flow; Lenis must re-measure after ST refresh. */
        ScrollTrigger.addEventListener('refresh', function() {
            lenis.resize();
        });

        requestAnimationFrame(function() {
            ScrollTrigger.refresh();
        });

        /**
         * Native hash scrolling breaks with Lenis + ScrollTrigger refresh (stays at top).
         * Also: / → /#partners often fires hashchange only (no load). Cross-page loads need load handler.
         */
        function viitorxScrollToHash() {
            var hash = window.location.hash;
            if (!hash || hash === '#') {
                return;
            }
            var raw = hash.slice(1);
            var id = raw;
            try {
                id = decodeURIComponent(raw.replace(/\+/g, ' '));
            } catch (e) {
                /* keep raw */
            }
            var target = document.getElementById(id);
            if (!target) {
                return;
            }
            var pad = parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop);
            var off = Number.isFinite(pad) && pad > 0 ? -pad : -88;
            if (typeof lenis !== 'undefined' && typeof lenis.scrollTo === 'function') {
                lenis.scrollTo(target, {
                    offset: off,
                    lerp: 0.14,
                    force: true
                });
            } else {
                target.scrollIntoView({
                    behavior: 'smooth',
                    block: 'start'
                });

            }
        }

        function viitorxRefreshLayoutThenHash() {
            ScrollTrigger.refresh();
            if (typeof lenis !== 'undefined' && typeof lenis.resize === 'function') {
                lenis.resize();
            }
            requestAnimationFrame(function() {
                ScrollTrigger.refresh();
                if (typeof lenis !== 'undefined' && typeof lenis.resize === 'function') {
                    lenis.resize();
                }
                viitorxScrollToHash();
                window.setTimeout(viitorxScrollToHash, 160);
            });
        }

        window.addEventListener('load', viitorxRefreshLayoutThenHash, {
            once: true
        });
        window.addEventListener('hashchange', function() {
            viitorxRefreshLayoutThenHash();
        });

        /* ─── CUSTOM CURSOR (GSAP Polish) ───────────────────────── */
        const cursor = document.getElementById('cursor');
        const ring = document.getElementById('cursor-ring');

        let mouseX = 0,
            mouseY = 0;
        let ringX = 0,
            ringY = 0;

        // Use quickSetter for high performance
        const xCursorSetter = gsap.quickSetter(cursor, "x", "px");
        const yCursorSetter = gsap.quickSetter(cursor, "y", "px");
        const xRingSetter = gsap.quickSetter(ring, "x", "px");
        const yRingSetter = gsap.quickSetter(ring, "y", "px");

        window.addEventListener('mousemove', (e) => {
            mouseX = e.clientX;
            mouseY = e.clientY;

            xCursorSetter(mouseX);
            yCursorSetter(mouseY);
        });

        // Smooth ring movement
        gsap.ticker.add(() => {
            const dt = 1.0 - Math.pow(1.0 - 0.2, gsap.ticker.deltaRatio());
            ringX += (mouseX - ringX) * dt;
            ringY += (mouseY - ringY) * dt;
            xRingSetter(ringX);
            yRingSetter(ringY);
        });

        // Hover interactions
        document.querySelectorAll('a, button, .partner-card, .work-slide, .offerings-stage').forEach(el => {
            el.addEventListener('mouseenter', () => {
                gsap.to(ring, {
                    scale: 1.5,
                    background: 'rgba(200, 153, 106, 0.1)',
                    borderColor: 'transparent',
                    duration: 0.3
                });
                gsap.to(cursor, {
                    scale: 0.5,
                    duration: 0.3
                });
            });
            el.addEventListener('mouseleave', () => {
                gsap.to(ring, {
                    scale: 1,
                    background: 'transparent',
                    borderColor: 'var(--copper)',
                    duration: 0.3
                });
                gsap.to(cursor, {
                    scale: 1,
                    duration: 0.3
                });
            });
        });

        /* ─── REVIEWS: pin + scrub (all breakpoints) · Lenis resize on refresh ───── */
        (function() {
            const reviewsSection = document.getElementById('reviews');
            const stickyEl = document.getElementById('reviews-sticky');
            const track = document.getElementById('reviews-cards-track');
            const scattered = track ? track.querySelector('.reviews-scattered') : null;
            const cards = track ? track.querySelectorAll('.review-card') : [];
            if (!reviewsSection || !stickyEl || !track || !scattered || !cards.length) return;

            gsap.registerPlugin(ScrollTrigger);

            const PAD = 72;
            let travelPx = 520;

            function measureTravel() {
                var maxBottom = 0;
                cards.forEach(function(card) {
                    var b = card.offsetTop + card.offsetHeight;
                    if (b > maxBottom) maxBottom = b;
                });
                scattered.style.height = (maxBottom + PAD) + 'px';
                var vh = window.innerHeight;
                var raw = maxBottom + PAD - Math.round(vh * 0.9);
                var mobileCap = window.innerWidth <= 991 ? Math.round(vh * 0.65) : maxBottom + PAD - 120;
                travelPx = Math.max(280, Math.min(Math.round(raw), mobileCap));
                return travelPx;
            }

            const mm = gsap.matchMedia();

            function attachReviews() {
                measureTravel();

                function reviewsPinStartPx() {
                    var nav = document.getElementById('sticky-nav');
                    var h = nav && nav.getBoundingClientRect ? Math.ceil(nav.getBoundingClientRect().height) : 0;
                    if (!Number.isFinite(h) || h < 44) {
                        h = 88;
                    }
                    return Math.min(Math.max(h, 64), 120);
                }

                var stCfg = {
                    /* Trigger = pinned viewport — avoids padding offset vs #reviews firing "early" vs Lenis (pin jitter). */
                    trigger: stickyEl,
                    start: function() {
                        return 'top top+=' + reviewsPinStartPx();
                    },
                    end: function() {
                        return '+=' + travelPx;
                    },
                    /* Number scrub (not true): smoother linkage with Lenis animated scroll + pin */
                    scrub: 1,
                    pin: true,
                    pinSpacing: true,
                    anticipatePin: false,
                    invalidateOnRefresh: true,
                    fastScrollEnd: false,
                    onRefresh: function() {
                        measureTravel();
                        if (typeof lenis !== 'undefined' && lenis.resize) lenis.resize();
                    }
                };

                const tl = gsap.timeline({
                    scrollTrigger: stCfg
                });

                tl.to(
                    track, {
                        y: function() {
                            return -travelPx;
                        },
                        ease: 'none',
                        invalidateOnRefresh: true
                    },
                    0
                );

                return function() {
                    tl.kill();
                    gsap.set(track, {
                        clearProps: 'transform'
                    });
                    scattered.style.height = '';
                };
            }

            /* transform pin on all breakpoints — position:fixed pin + Lenis often fights scrollerProxy */
            mm.add('(min-width: 992px)', function() {
                return attachReviews();
            });

            mm.add('(max-width: 991px)', function() {
                return attachReviews();
            });

            function refreshReviewsST() {
                measureTravel();
                if (typeof ScrollTrigger !== 'undefined') {
                    ScrollTrigger.refresh();
                }
            }

            var reviewsResizeTimer;

            function onReviewsResize() {
                clearTimeout(reviewsResizeTimer);
                reviewsResizeTimer = setTimeout(refreshReviewsST, 120);
            }

            queueMicrotask(refreshReviewsST);
            requestAnimationFrame(refreshReviewsST);
            gsap.delayedCall(0.18, refreshReviewsST);
            if (document.fonts && document.fonts.ready) {
                document.fonts.ready.then(refreshReviewsST);
            }
            window.addEventListener('resize', onReviewsResize, {
                passive: true
            });
            window.addEventListener(
                'orientationchange',
                function() {
                    gsap.delayedCall(0.12, refreshReviewsST);
                }, {
                    passive: true
                }
            );
        })();

        /* ─── HOW THE MAGIC HAPPENS: scrubbed path draw → connectors → alternating step copy ───── */
        (function() {
            var section = document.getElementById('magic-how');
            var pathEl = document.getElementById('magicHowPathStroke');
            if (!section || !pathEl || typeof gsap === 'undefined') return;

            gsap.registerPlugin(ScrollTrigger);

            function magicHowPinStartPx() {
                var nav = document.getElementById('sticky-nav');
                var h = nav && nav.getBoundingClientRect ? Math.ceil(nav.getBoundingClientRect().height) : 0;
                if (!Number.isFinite(h) || h < 44) h = 88;
                return Math.min(Math.max(h, 64), 120);
            }

            function prefersReducedMotion() {
                return (
                    typeof window.matchMedia !== 'undefined' &&
                    window.matchMedia('(prefers-reduced-motion: reduce)').matches
                );
            }

            function lenisResizeSoon() {
                if (typeof lenis !== 'undefined' && lenis.resize) lenis.resize();
            }

            /** Enough scroll runway when not pinning (mobile stack can be taller than the viewport). */
            function magicHowUnpinnedScrollPx() {
                var vh = window.innerHeight || 800;
                return Math.round(Math.max(vh, 560) * 1.6);
            }

            /** Scroll distance while #magic-how is pinned — must fit full timeline (path + connectors + labels + foot). */
            /** Shorter runway = tighter section (less GSAP pin-spacer gap below pinned block); scrub still spans full TL. */
            function magicHowPinnedScrollPx() {
                var vh = window.innerHeight || 800;
                return Math.round(Math.max(vh, 640) * 2.22);
            }

            /** Force visible final state — DOM only so scrub timelines stay in sync (no tl.progress fight). */
            function magicHowForceComplete() {
                if (!section) return;

                section._magicHowCueDismissed = true;
                var pl =
                    typeof pathEl.getTotalLength === 'function' ? pathEl.getTotalLength() : 1200;
                gsap.set(pathEl, {
                    strokeDasharray: pl,
                    strokeDashoffset: 0,
                    opacity: 1,
                    visibility: 'visible',
                });
                gsap.set(section.querySelectorAll('.magic-how-cc'), {
                    autoAlpha: 1
                });
                section.querySelectorAll('.magic-how-overlay .magic-how-step-label').forEach(function(el) {
                    var isTop = el.getAttribute('data-enter') === 'top';
                    gsap.set(el, {
                        autoAlpha: 1,
                        xPercent: -50,
                        yPercent: isTop ? -100 : 0,
                        transformOrigin: isTop ? '50% 100%' : '50% 0%',
                        y: 0,
                    });
                });
                var scrollCueFc = document.getElementById('magicHowScrollCue');
                if (scrollCueFc) {
                    gsap.set(scrollCueFc, {
                        autoAlpha: 0
                    });
                }
                var footnote = section.querySelector('.magic-how-footnote');
                if (footnote) gsap.set(footnote, {
                    autoAlpha: 1
                });
                gsap.set(section.querySelectorAll('.magic-how-desktop-node-marker'), {
                    autoAlpha: 1,
                    clearProps: 'transform',
                });
                gsap.set(section.querySelectorAll('.magic-how-mobile-pin'), {
                    autoAlpha: 1,
                });
                gsap.set(section.querySelectorAll('.magic-how-mobile-step p'), {
                    autoAlpha: 1,
                    clearProps: 'transform',
                });
                var rf = section.querySelector('#magicHowMobileRailFill');
                if (rf) {
                    rf.style.height =
                        rf.parentElement && rf.parentElement.offsetHeight ?
                        Math.max(rf.parentElement.offsetHeight - 12, 64) + 'px' :
                        rf.style.height || '100%';
                    gsap.set(rf, {
                        scaleY: 1,
                        transformOrigin: 'top center',
                        autoAlpha: 1
                    });
                }
                requestAnimationFrame(lenisResizeSoon);
            }

            function initDesktopMagic() {
                var pinRoot = document.getElementById('magic-how-pin-root') || section;
                var connectors = gsap.utils.toArray(section.querySelectorAll('.magic-how-cc'));
                var labels = gsap.utils.toArray(section.querySelectorAll('.magic-how-overlay .magic-how-step-label'));
                var nodeMarkers = gsap.utils.toArray(section.querySelectorAll('.magic-how-desktop-node-marker'));
                var foot = section.querySelector('.magic-how-footnote');
                var pl = typeof pathEl.getTotalLength === 'function' ? pathEl.getTotalLength() : 1200;
                var stMagicHowCueEarly = null;
                section._magicHowCueDismissed = false;

                gsap.set(pathEl, {
                    strokeDasharray: pl,
                    strokeDashoffset: pl,
                    visibility: 'visible',
                    opacity: 1,
                });
                gsap.set(connectors, {
                    autoAlpha: 0
                });
                labels.forEach(function(el) {
                    var isTop = el.getAttribute('data-enter') === 'top';
                    var iy = isTop ? -42 : 44;
                    gsap.set(el, {
                        autoAlpha: 0,
                        xPercent: -50,
                        yPercent: isTop ? -100 : 0,
                        transformOrigin: isTop ? '50% 100%' : '50% 0%',
                        y: iy,
                    });
                });
                gsap.set(nodeMarkers, {
                    autoAlpha: 0
                });
                var scrollCue = document.getElementById('magicHowScrollCue');
                if (scrollCue) {
                    gsap.set(scrollCue, {
                        autoAlpha: 0
                    });
                }
                if (foot) gsap.set(foot, {
                    autoAlpha: 0
                });

                function syncMagicHowScrollCueVisibility() {
                    if (!scrollCue) {
                        return;
                    }
                    if (section._magicHowCueDismissed) {
                        gsap.set(scrollCue, {
                            autoAlpha: 0
                        });
                        return;
                    }
                    var r = section.getBoundingClientRect();
                    var vh = window.innerHeight || document.documentElement.clientHeight || 800;
                    var inView = r.top < vh - 4 && r.bottom > 48;
                    var atl = section._magicHowTl;
                    var prog = atl && typeof atl.progress === 'function' ? atl.progress() : 0;
                    /* Hide before footnote / end of scrub so fixed cue doesn't sit on the paragraph */
                    var show = inView && prog < 0.76;
                    gsap.to(scrollCue, {
                        autoAlpha: show ? 1 : 0,
                        duration: 0.14,
                        ease: 'power2.out',
                        overwrite: 'auto',
                    });
                }

                var tl = gsap.timeline({
                    scrollTrigger: {
                        trigger: pinRoot,
                        start: function() {
                            return 'top top+=' + magicHowPinStartPx();
                        },
                        end: function() {
                            return '+=' + magicHowPinnedScrollPx();
                        },
                        pin: true,
                        pinSpacing: true,
                        anticipatePin: 1,
                        scrub: 1.35,
                        fastScrollEnd: false,
                        invalidateOnRefresh: true,
                        onRefresh: lenisResizeSoon,
                        onUpdate: syncMagicHowScrollCueVisibility,
                        onLeaveBack: function() {
                            syncMagicHowScrollCueVisibility();
                        },
                        onEnterBack: function() {
                            section._magicHowCueDismissed = false;
                            syncMagicHowScrollCueVisibility();
                        },
                        onLeave: magicHowForceComplete,
                    },
                });

                section._magicHowTl = tl;

                /* Path length at each node (matches circle centres in SVG) — stroke reveals step-by-step. */
                function pathLengthNearestTo(px, py, totalLen) {
                    var bestT = 0;
                    var bestD = Infinity;
                    var n = typeof totalLen === 'number' ? totalLen : pl;
                    if (typeof pathEl.getPointAtLength !== 'function' || !(n > 8)) return n / 8;
                    var samples = 320;
                    for (var s = 0; s <= samples; s++) {
                        var t = (s / samples) * n;
                        var p = pathEl.getPointAtLength(t);
                        var d = (p.x - px) * (p.x - px) + (p.y - py) * (p.y - py);
                        if (d < bestD) {
                            bestD = d;
                            bestT = t;
                        }
                    }
                    return bestT;
                }

                var nodeCentres = [
                    [108.9, 247.4],
                    [343.3, 252],
                    [574.3, 212.1],
                    [801.3, 155.9],
                    [1033.4, 135.4],
                    [1268.1, 120],
                    [1484.5, 22.8],
                ];

                var lenAtDot = [];
                for (var ni = 0; ni < nodeCentres.length; ni++) {
                    lenAtDot.push(pathLengthNearestTo(nodeCentres[ni][0], nodeCentres[ni][1], pl));
                }
                /* Keep sample order monotone along curve (fixes rare jitter if path bends back). */
                for (var fix = 1; fix < lenAtDot.length; fix++) {
                    if (lenAtDot[fix] < lenAtDot[fix - 1]) {
                        lenAtDot[fix] = lenAtDot[fix - 1] + 16;
                    }
                }

                /* Step 0 is visible from section entry — pre-reveal path to first node. */
                gsap.set(pathEl, { strokeDashoffset: pl - lenAtDot[0] });
                if (nodeMarkers[0]) gsap.set(nodeMarkers[0], { autoAlpha: 1 });
                if (connectors[0]) gsap.set(connectors[0], { autoAlpha: 1 });
                if (labels[0]) gsap.set(labels[0], { autoAlpha: 1, y: 0 });

                var pathRevealTotalDur = 3.45;
                var labelFadeDur = 0.48;
                var prevAlong = lenAtDot[0];

                /*
                 * Last path segment must include the stroke "tail" in the same tween as dot 7.
                 * If tail is a separate later tween, scrubbing back runs the tail first and the last
                 * marker stays visible while the path retracts — looks like a dot "left behind".
                 */
                for (let step = 1; step < 7; step++) {
                    var along = step === 6 ? pl : lenAtDot[step];
                    var chunk = along - prevAlong;
                    prevAlong = along;
                    var frac = pl > 8 && isFinite(chunk) && chunk > 0 ? chunk / pl : 1 / 7;
                    tl.to(pathEl, {
                        strokeDashoffset: pl - along,
                        duration: pathRevealTotalDur * frac,
                        ease: 'none'
                    });
                    tl.to(
                        nodeMarkers[step], {
                            autoAlpha: 1,
                            duration: 0.2,
                            ease: 'power2.out'
                        },
                        '>0'
                    );
                    tl.to(connectors[step], {
                        autoAlpha: 1,
                        duration: 0.1
                    }, '-=0.04');
                    tl.to(
                        labels[step], {
                            autoAlpha: 1,
                            y: 0,
                            duration: labelFadeDur,
                            ease: 'power2.out',
                        },
                        '<0.06'
                    );
                }

                if (foot) {
                    tl.to(foot, {
                        autoAlpha: 1,
                        duration: 0.52,
                        ease: 'power2.out'
                    }, '>-0.08');
                }

                stMagicHowCueEarly = ScrollTrigger.create({
                    trigger: section,
                    start: 'top bottom',
                    end: 'bottom top',
                    onEnter: syncMagicHowScrollCueVisibility,
                    onLeave: function() {
                        if (!scrollCue) {
                            return;
                        }
                        gsap.set(scrollCue, {
                            autoAlpha: 0
                        });
                    },
                    onLeaveBack: function() {
                        if (!scrollCue) {
                            return;
                        }
                        gsap.set(scrollCue, {
                            autoAlpha: 0
                        });
                    },
                });

                requestAnimationFrame(function() {
                    requestAnimationFrame(function() {
                        if (typeof ScrollTrigger !== 'undefined') {
                            ScrollTrigger.refresh();
                        }
                        syncMagicHowScrollCueVisibility();
                    });
                });

                return function cleanup() {
                    if (stMagicHowCueEarly) {
                        stMagicHowCueEarly.kill();
                        stMagicHowCueEarly = null;
                    }
                    if (section._magicHowTl === tl) {
                        section._magicHowTl = null;
                    }
                    if (tl.scrollTrigger) tl.scrollTrigger.kill();
                    tl.kill();
                    gsap.set(pathEl, {
                        clearProps: 'strokeDashoffset,strokeDasharray'
                    });
                };
            }

            function initMobileMagic() {
                var railFill = section.querySelector('#magicHowMobileRailFill');
                var stack = section.querySelector('.magic-how-mobile-rail');
                var pins = gsap.utils.toArray(section.querySelectorAll('.magic-how-mobile-pin'));
                var paragraphs = gsap.utils.toArray(section.querySelectorAll('.magic-how-mobile-step p'));
                var foot = section.querySelector('.magic-how-footnote');

                function measureRail() {
                    if (!railFill || !stack) return;
                    var tall = stack.offsetHeight;
                    gsap.set(railFill, {
                        height: Math.max(tall - 8, 64)
                    });
                }

                measureRail();

                /* Pins + copy stay visible — Lenis ↔ ScrollTrigger scrub often desyncs on phones and left
                   autoAlpha at 0 (visibility:hidden), so scrub-only reveals looked "broken". Rail fill only scrubs. */
                gsap.set(railFill, {
                    scaleY: 0,
                    transformOrigin: 'top center',
                    autoAlpha: 1,
                });
                /* Force visible — prior desktop init or aborted tweens sometimes leave visibility:hidden on touch. */
                if (pins.length) gsap.set(pins, {
                    autoAlpha: 1
                });
                if (paragraphs.length) gsap.set(paragraphs, {
                    autoAlpha: 1,
                    clearProps: 'transform'
                });
                if (foot) gsap.set(foot, {
                    autoAlpha: 1
                });

                var tl = gsap.timeline({
                    scrollTrigger: {
                        trigger: section,
                        start: function() {
                            return 'top top+=' + magicHowPinStartPx();
                        },
                        end: function() {
                            return '+=' + magicHowUnpinnedScrollPx();
                        },
                        scrub: 1,
                        fastScrollEnd: false,
                        invalidateOnRefresh: true,
                        onRefresh: function() {
                            measureRail();
                            lenisResizeSoon();
                        },
                        onLeave: magicHowForceComplete,
                    },
                });

                section._magicHowTl = tl;

                tl.to(railFill, {
                    scaleY: 1,
                    duration: 1,
                    ease: 'none',
                });

                return function cleanup() {
                    if (section._magicHowTl === tl) {
                        section._magicHowTl = null;
                    }
                    if (tl.scrollTrigger) tl.scrollTrigger.kill();
                    tl.kill();
                    gsap.set(railFill, {
                        clearProps: 'transform,scale,height'
                    });
                };
            }

            if (prefersReducedMotion()) {
                gsap.set(pathEl, {
                    clearProps: 'strokeDashoffset,strokeDasharray'
                });
                gsap.set(section.querySelectorAll('.magic-how-cc,.magic-how-footnote'), {
                    autoAlpha: 1,
                });
                section.querySelectorAll('.magic-how-overlay .magic-how-step-label').forEach(function(el) {
                    var isTop = el.getAttribute('data-enter') === 'top';
                    gsap.set(el, {
                        autoAlpha: 1,
                        xPercent: -50,
                        yPercent: isTop ? -100 : 0,
                        transformOrigin: isTop ? '50% 100%' : '50% 0%',
                        y: 0,
                    });
                });
                gsap.set(section.querySelectorAll('.magic-how-desktop-node-marker'), {
                    autoAlpha: 1
                });
                gsap.set(section.querySelectorAll('.magic-how-mobile-pin,.magic-how-mobile-step p'), {
                    autoAlpha: 1,
                });
                var rf = section.querySelector('#magicHowMobileRailFill');
                if (rf) gsap.set(rf, {
                    scaleY: 1
                });
                return;
            }

            var mm = gsap.matchMedia();
            var dk = null;
            var mk = null;

            mm.add('(min-width: 1024px)', function() {
                dk = initDesktopMagic();
                return dk;
            });
            mm.add('(max-width: 1023px)', function() {
                mk = initMobileMagic();
                return mk;
            });

            window.addEventListener(
                'resize',
                function() {
                    gsap.delayedCall(0.05, lenisResizeSoon);
                }, {
                    passive: true
                }
            );
        })();

        /* ─── OFFERINGS: pin step 1, then each card rises from bottom — text docks under prior row; images stack on top ─ */
        (function() {
            const wrap = document.getElementById('offerings-stack-wrap');
            const stage = document.getElementById('offerings-stage');
            const copyCol = document.getElementById('offering-progressive-copy');
            const blocks = gsap.utils.toArray('.offering-progressive-block');
            const images = gsap.utils.toArray('.offering-visual-layer');
            if (!wrap || !stage || !copyCol || blocks.length < 2 || images.length !== blocks.length) return;

            gsap.registerPlugin(ScrollTrigger);

            gsap.matchMedia().add('(min-width: 1200px)', function() {
                const n = blocks.length;
                const seg = n - 1;

                function getOfferingsDesktopNavPx() {
                    var navEl = document.getElementById('sticky-nav');
                    var h = navEl && navEl.getBoundingClientRect ? Math.ceil(navEl.getBoundingClientRect().height) : 88;
                    if (!Number.isFinite(h) || h < 44) {
                        h = 88;
                    }
                    return Math.min(Math.max(h, 64), 140);
                }

                /*
                 * Rows slide from below the slot: yPercent + autoAlpha so nothing shows before that step.
                 * LEAD_SLOTS: extra timeline time before row 2 starts — maps to scroll after pin so #2 is not partial yet.
                 */
                const STEP = 1;
                const SYNC = 0.64;
                const RISE_EASE = 'power2.out';
                const RISE_FROM_PERCENT = 132;
                const LEAD_SLOTS = 0.22;

                const progressive = document.querySelector('.offering-progressive');

                const OFFERING_SCROLL_FACTOR = 1.08;
                const OFFERING_SCRUB_SMOOTHING = 0.66;
                const LEAD_SCROLL_BONUS_PX = function() {
                    return Math.round(window.innerHeight * 0.09);
                };

                const tailPadRaw = STEP * seg - (LEAD_SLOTS + (seg - 1) + SYNC);

                const blockSeparatorBorder = 'rgba(255, 255, 255, 0.12)';

                function readOfferingSpacing() {
                    const el = progressive || wrap;
                    const cs = getComputedStyle(el);
                    const pad = parseFloat(cs.getPropertyValue('--offering-pad-before-rule')) || 32;
                    const gap = parseFloat(cs.getPropertyValue('--offering-gap-after-block')) || 40;
                    return {
                        pad,
                        gap
                    };
                }

                function stripInnerForMeasure(inner) {
                    if (!inner) return;
                    gsap.set(inner, {
                        clearProps: 'transform'
                    });
                    inner.style.position = '';
                    inner.style.left = '';
                    inner.style.right = '';
                    inner.style.top = '';
                    inner.style.bottom = '';
                    inner.style.width = '';
                    inner.style.height = '';
                    inner.style.opacity = '';
                }

                function measureBlockHeights() {
                    const sp = readOfferingSpacing();
                    blocks.slice(1).forEach((block, idx) => {
                        const inner = block.querySelector('.offering-progressive-block-inner');
                        const bi = idx + 1;
                        const isLast = bi === n - 1;

                        stripInnerForMeasure(inner);

                        const was = {
                            height: block.style.height,
                            minHeight: block.style.minHeight,
                            overflow: block.style.overflow,
                            visibility: block.style.visibility,
                            position: block.style.position,
                            width: block.style.width,
                            paddingBottom: block.style.paddingBottom,
                            marginBottom: block.style.marginBottom,
                            borderBottomWidth: block.style.borderBottomWidth,
                            borderBottomStyle: block.style.borderBottomStyle,
                            borderBottomColor: block.style.borderBottomColor,
                        };

                        if (inner) {
                            gsap.set(inner, {
                                yPercent: 0,
                                opacity: 1
                            });
                        }
                        block.style.height = 'auto';
                        block.style.minHeight = '0';
                        block.style.overflow = 'visible';
                        block.style.visibility = 'hidden';
                        block.style.position = 'absolute';
                        block.style.left = '0';
                        block.style.width = copyCol.offsetWidth + 'px';
                        block.style.paddingBottom = `${sp.pad}px`;
                        block.style.marginBottom = isLast ? '0px' : `${sp.gap}px`;
                        if (!isLast) {
                            block.style.borderBottomWidth = '1px';
                            block.style.borderBottomStyle = 'solid';
                            block.style.borderBottomColor = blockSeparatorBorder;
                        } else {
                            block.style.borderBottomWidth = '0';
                            block.style.borderBottomStyle = 'solid';
                            block.style.borderBottomColor = blockSeparatorBorder;
                        }

                        const raw = Math.max(block.offsetHeight, block.scrollHeight);
                        let h = Math.ceil(raw);
                        h += Math.max(4, Math.min(14, Math.round(h * 0.02)));

                        block.dataset.targetHeight = String(h);

                        block.style.height = was.height;
                        block.style.minHeight = was.minHeight;
                        block.style.overflow = was.overflow;
                        block.style.visibility = was.visibility;
                        block.style.position = was.position;
                        block.style.left = '';
                        block.style.width = was.width;
                        block.style.paddingBottom = was.paddingBottom;
                        block.style.marginBottom = was.marginBottom;
                        block.style.borderBottomWidth = was.borderBottomWidth;
                        block.style.borderBottomStyle = was.borderBottomStyle;
                        block.style.borderBottomColor = was.borderBottomColor;
                    });
                }

                copyCol.classList.remove('offerings-copy--ready');
                copyCol.style.visibility = 'hidden';

                measureBlockHeights();
                let spacing = readOfferingSpacing();

                gsap.set(blocks[0], {
                    paddingBottom: spacing.pad,
                    marginBottom: spacing.gap,
                    borderBottomWidth: 1,
                    borderBottomStyle: 'solid',
                    borderBottomColor: blockSeparatorBorder,
                });

                /**
                 * Stack rows 2…n absolutely at their final resting tops (below card 1 + gap).
                 * Full row height from the start + yPercent — same swipe mechanic as visual layers inside the frame.
                 */
                function layoutOfferingTextStacks() {
                    spacing = readOfferingSpacing();
                    const cr = copyCol.getBoundingClientRect();
                    const mb0 =
                        parseFloat(window.getComputedStyle(blocks[0]).marginBottom) || 0;
                    var stackTop =
                        blocks[0].getBoundingClientRect().bottom -
                        copyCol.getBoundingClientRect().top +
                        mb0;

                    blocks.slice(1).forEach(function(block, idx) {
                        const innerEl = block.querySelector('.offering-progressive-block-inner');
                        stripInnerForMeasure(innerEl);

                        gsap.set(block, {
                            clearProps: 'transform,height'
                        });
                        block.style.position = 'absolute';
                        block.style.left = '0';
                        block.style.width = Math.round(cr.width) + 'px';
                        block.style.margin = '0';
                        block.style.overflow = 'hidden';

                        var H = parseFloat(block.dataset.targetHeight) || 120;
                        var ih = idx + 1;

                        gsap.set(block, {
                            top: stackTop,
                            height: H,
                            yPercent: RISE_FROM_PERCENT,
                            autoAlpha: 0,
                            zIndex: 10 + ih,
                            force3D: true,
                        });

                        stackTop += H;
                        if (ih < n - 1) {
                            stackTop += spacing.gap;
                        }
                    });

                    copyCol.style.minHeight = Math.ceil(stackTop) + 'px';
                }

                layoutOfferingTextStacks();

                copyCol.style.visibility = '';
                copyCol.classList.add('offerings-copy--ready');

                /* Images: full opacity always — motion is yPercent only so overlaps never look semi-transparent (no autoAlpha). */
                images.forEach(function(img, i) {
                    img.style.zIndex = String(10 + i);
                    gsap.set(img, {
                        force3D: true
                    });
                    gsap.set(img, {
                        opacity: 1,
                        yPercent: i === 0 ? 0 : RISE_FROM_PERCENT,
                    });
                });

                wrap.style.minHeight = `${Math.round(n * 100 * OFFERING_SCROLL_FACTOR + 9)}vh`;

                const tl = gsap.timeline({
                    scrollTrigger: {
                        trigger: wrap,
                        start: function() {
                            return 'top top+=' + getOfferingsDesktopNavPx();
                        },
                        end: function() {
                            return (
                                '+=' +
                                (seg * window.innerHeight * OFFERING_SCROLL_FACTOR + LEAD_SCROLL_BONUS_PX())
                            );
                        },
                        scrub: OFFERING_SCRUB_SMOOTHING,
                        pin: stage,
                        pinSpacing: true,
                        pinType: 'transform',
                        anticipatePin: 0,
                        invalidateOnRefresh: true,
                        id: 'offerings-progressive',
                        onRefresh: function() {
                            spacing = readOfferingSpacing();
                            measureBlockHeights();
                            gsap.set(blocks[0], {
                                paddingBottom: spacing.pad,
                                marginBottom: spacing.gap,
                                borderBottomWidth: 1,
                                borderBottomStyle: 'solid',
                                borderBottomColor: blockSeparatorBorder,
                            });
                            layoutOfferingTextStacks();
                            copyCol.classList.add('offerings-copy--ready');
                        },
                        onToggle: function() {
                            requestAnimationFrame(function() {
                                lenis.resize();
                            });
                        },
                    },
                });

                for (let i = 1; i < n; i++) {
                    const slot = LEAD_SLOTS + (i - 1);
                    const block = blocks[i];
                    const nextImg = images[i];

                    tl.fromTo(
                        block, {
                            autoAlpha: 0,
                            yPercent: RISE_FROM_PERCENT
                        }, {
                            autoAlpha: 1,
                            yPercent: 0,
                            duration: SYNC,
                            ease: RISE_EASE,
                        },
                        slot
                    );

                    tl.fromTo(
                        nextImg, {
                            opacity: 1,
                            yPercent: RISE_FROM_PERCENT
                        }, {
                            opacity: 1,
                            yPercent: 0,
                            duration: SYNC,
                            ease: RISE_EASE,
                        },
                        slot
                    );
                }

                const tailPad = Math.max(0, tailPadRaw);
                if (tailPad > 0.008) {
                    const padObj = {
                        _noop: 0
                    };
                    tl.to(
                        padObj, {
                            _noop: 1,
                            duration: tailPad,
                            ease: 'none'
                        },
                        LEAD_SLOTS + (seg - 1) + SYNC
                    );
                }

                requestAnimationFrame(function() {
                    ScrollTrigger.refresh(true);
                });

                let resizeOfferings;
                const onOfferingsResize = function() {
                    clearTimeout(resizeOfferings);
                    resizeOfferings = setTimeout(function() {
                        measureBlockHeights();
                        layoutOfferingTextStacks();
                        ScrollTrigger.refresh();
                    }, 180);
                };
                window.addEventListener('resize', onOfferingsResize);

                return function offeringsDesktopCleanup() {
                    clearTimeout(resizeOfferings);
                    window.removeEventListener('resize', onOfferingsResize);
                    tl.kill();
                };
            });
        })();

        /* ─── OFFERINGS (mobile/tablet): CSS sticky stacking - JavaScript pinning disabled ─ */
        (function() {
            // Disabled: Now using pure CSS sticky positioning instead of JavaScript pinning
            return;

            if (typeof ScrollTrigger === 'undefined') return;
            gsap.registerPlugin(ScrollTrigger);

            const mmOfferingsMobile = gsap.matchMedia();
            mmOfferingsMobile.add('(max-width: 1199px)', function() {
                const slides = gsap.utils.toArray('.offering-mobile-slide');
                if (!slides.length) return;

                /**
                 * Pins must align below fixed #sticky-nav + sticky "Offerings" bar.
                 * start: 'top top' pins to y=0 → slides sit under the heading; use nav + title height instead.
                 */
                function getOfferingsMobilePinStartPx() {
                    var navPx = 88;
                    var navEl = document.getElementById('sticky-nav');
                    if (navEl) {
                        var bh = Math.ceil(navEl.getBoundingClientRect().height);
                        if (Number.isFinite(bh) && bh > 40) {
                            navPx = Math.max(bh, 64);
                        }
                    } else {
                        var raw = parseFloat(
                            getComputedStyle(document.documentElement).getPropertyValue('--nav-bar-outer-height')
                        );
                        if (Number.isFinite(raw) && raw > 40) {
                            navPx = raw;
                        }
                    }
                    const titleEl = document.querySelector('.offerings-mobile-sticky-title');
                    var th = titleEl ? titleEl.offsetHeight : 52;
                    if (!Number.isFinite(th) || th < 24) {
                        th = 52;
                    }
                    return Math.round(navPx + th);
                }

                const nSlides = slides.length;
                const triggers = slides.map(function(slide, i) {
                    return ScrollTrigger.create({
                        trigger: slide,
                        start: function() {
                            return 'top ' + getOfferingsMobilePinStartPx() + 'px';
                        },
                        end: function() {
                            var h = Math.round(window.innerHeight);
                            /* Last card: shorter pin travel so Work doesn't sit one full viewport away */
                            if (i === nSlides - 1) {
                                return '+=' + Math.max(140, Math.round(h * 0.22));
                            }
                            return '+=' + h;
                        },
                        pin: true,
                        pinSpacing: false,
                        id: 'offering-mobile-pin-' + i,
                        invalidateOnRefresh: true,
                        anticipatePin: 0,
                    });
                });

                requestAnimationFrame(function() {
                    ScrollTrigger.refresh();
                });

                return function() {
                    triggers.forEach(function(t) {
                        t.kill();
                    });
                };
            });
        })();

        /* ── STICKY NAV + MOBILE DRAWER (Lenis scroll + overlay) ─ */
        (function() {
            const stickyNav = document.getElementById('sticky-nav');
            const navDrawer = document.getElementById('nav-drawer');
            const navToggle = document.getElementById('nav-menu-toggle');
            const navScrim = document.getElementById('nav-drawer-scrim');
            const mqWide = window.matchMedia('(min-width: 901px)');
            /** Show bar as soon as the user scrolls (not after hero fraction). Small floor avoids noisy sub-pixel / bounce. */
            const STICKY_TRIGGER_PX = 4;

            if (!stickyNav) return;

            /**
             * Measured sticky bar height (works when nav is translated off-screen).
             * --anchor-under-nav-offset: bar + cushion for scroll-margin / sticky pins.
             * --nav-under-sticky-offset: exact bar bottom (drawer panel top).
             */
            function syncStickyNavDependentLayout() {
                requestAnimationFrame(() => {
                    const raw = stickyNav ? stickyNav.getBoundingClientRect().height : 0;
                    const bar = raw > 0 ? Math.ceil(raw) : 88;
                    const navPx = Math.max(bar, 64);
                    /* Flush under bar: sticky "Offerings" + ScrollTrigger pin line */
                    document.documentElement.style.setProperty('--nav-bar-outer-height', `${navPx}px`);
                    /* Hash / scroll-padding — small cushion only (not used for sticky title top) */
                    document.documentElement.style.setProperty('--anchor-under-nav-offset', `${navPx + 8}px`);
                    if (navDrawer) {
                        navDrawer.style.setProperty('--nav-under-sticky-offset', `${navPx}px`);
                    }
                    if (typeof ScrollTrigger !== 'undefined') {
                        requestAnimationFrame(function() {
                            ScrollTrigger.refresh();
                        });
                    }
                });
            }

            const updateStickyNavVisibility = () => {
                const scrollY = window.scrollY || window.pageYOffset || 0;
                const scrolled = scrollY > STICKY_TRIGGER_PX;
                const menuOpen = stickyNav.classList.contains('menu-open');
                if (scrolled || menuOpen) {
                    stickyNav.classList.add('visible');
                } else {
                    stickyNav.classList.remove('visible');
                }
            };

            function setNavDrawerOpen(open) {
                if (!navDrawer || !navToggle) return;
                document.body.classList.toggle('nav-drawer-visible', open);
                document.documentElement.classList.toggle('nav-drawer-open', open);
                stickyNav.classList.toggle('menu-open', open);
                navToggle.setAttribute('aria-expanded', open ? 'true' : 'false');
                navToggle.setAttribute('aria-label', open ? 'Close menu' : 'Open menu');
                navDrawer.setAttribute('aria-hidden', open ? 'false' : 'true');
                if (open) {
                    navDrawer.removeAttribute('inert');
                } else {
                    navDrawer.setAttribute('inert', '');
                }
                if (typeof lenis.stop === 'function' && typeof lenis.start === 'function') {
                    if (open) lenis.stop();
                    else lenis.start();
                }
                updateStickyNavVisibility();
                if (open) {
                    syncStickyNavDependentLayout();
                }
                if (open) {
                    const first = navDrawer.querySelector('.nav-drawer-list a');
                    if (first) first.focus({
                        preventScroll: true
                    });
                } else {
                    navToggle.focus({
                        preventScroll: true
                    });
                }
            }

            function closeNavDrawer() {
                if (document.body.classList.contains('nav-drawer-visible')) {
                    setNavDrawerOpen(false);
                }
            }

            lenis.on('scroll', updateStickyNavVisibility);
            updateStickyNavVisibility();
            syncStickyNavDependentLayout();

            let navOffsetResizeTimer;
            window.addEventListener('resize', () => {
                clearTimeout(navOffsetResizeTimer);
                navOffsetResizeTimer = setTimeout(() => {
                    syncStickyNavDependentLayout();
                    if (document.body.classList.contains('nav-drawer-visible')) {
                        updateStickyNavVisibility();
                    }
                }, 120);
            });
            window.addEventListener('orientationchange', () => {
                syncStickyNavDependentLayout();
            });

            if (navDrawer && navToggle) {
                navToggle.addEventListener('click', () => {
                    setNavDrawerOpen(!document.body.classList.contains('nav-drawer-visible'));
                });
                if (navScrim) {
                    navScrim.addEventListener('click', closeNavDrawer);
                }
                navDrawer.querySelectorAll('.nav-drawer-list a').forEach((a) => {
                    a.addEventListener('click', (e) => {
                        const href = a.getAttribute('href');
                        if (!href || href.charAt(0) !== '#') {
                            closeNavDrawer();
                            return;
                        }
                        e.preventDefault();
                        closeNavDrawer();
                        const target = document.querySelector(href);
                        if (target && typeof lenis.scrollTo === 'function') {
                            const pad = parseFloat(getComputedStyle(document.documentElement).scrollPaddingTop);
                            const off = Number.isFinite(pad) && pad > 0 ? -pad : -88;
                            lenis.scrollTo(target, {
                                offset: off,
                                lerp: 0.12
                            });
                        } else if (target) {
                            target.scrollIntoView({
                                behavior: 'smooth'
                            });
                        }
                    });
                });
                mqWide.addEventListener('change', (ev) => {
                    if (ev.matches) closeNavDrawer();
                });
                document.addEventListener('keydown', (e) => {
                    if (e.key === 'Escape' && document.body.classList.contains('nav-drawer-visible')) {
                        e.preventDefault();
                        closeNavDrawer();
                    }
                });
            }
        })();

        /* ── SCROLL REVEAL ──────────────────────────── */
        const revealEls = document.querySelectorAll('.reveal');
        const revealObs = new IntersectionObserver((entries) => {
            entries.forEach(e => {
                if (e.isIntersecting) {
                    e.target.classList.add('in-view');
                }
            });
        }, {
            threshold: 0.12
        });
        revealEls.forEach(el => revealObs.observe(el));

        /* ── INSIGHTS CARDS ANIMATION ──────────────────────────── */
        const insightCards = document.querySelectorAll('.insight-card');
        const insightsObs = new IntersectionObserver((entries) => {
            entries.forEach(e => {
                if (e.isIntersecting) {
                    e.target.classList.add('animate-in');
                }
            });
        }, {
            threshold: 0.15,
            rootMargin: '0px 0px -50px 0px'
        });
        insightCards.forEach(card => insightsObs.observe(card));

        /* Tablet insights: ArrowLeft/ArrowRight scroll the horizontal strip when focused */
        (function() {
            var slider = document.querySelector('#insights .insights-slider');
            if (!slider || typeof window.matchMedia !== 'function') return;
            var mqTablet = window.matchMedia('(min-width: 641px) and (max-width: 1024px)');
            slider.addEventListener('keydown', function(ev) {
                if (!mqTablet.matches || (ev.key !== 'ArrowLeft' && ev.key !== 'ArrowRight')) return;
                var card = slider.querySelector('.insight-card');
                var stride = card ? card.getBoundingClientRect().width + 24 : 360;
                ev.preventDefault();
                slider.scrollBy({
                    left: (ev.key === 'ArrowRight' ? 1 : -1) * stride,
                    behavior: 'smooth'
                });
            });
        })();


        /* ── WHERE WE BELONG: desktop pinned crossfade (one slot, dissolves 02–04 over 01) ─ */
        (function() {
            var wrap = document.querySelector('#process .process-wrapper');
            var container = document.querySelector('#process .sectors-container.sectors-stack--dissolve');
            if (!wrap || !container || typeof gsap === 'undefined' || typeof ScrollTrigger === 'undefined') {
                return;
            }

            var reducedMotion =
                typeof window.matchMedia !== 'undefined' &&
                window.matchMedia('(prefers-reduced-motion: reduce)').matches;
            if (reducedMotion) {
                return;
            }

            function processSectorPinStartPx() {
                var nav = document.getElementById('sticky-nav');
                var navH = nav && nav.getBoundingClientRect ? Math.ceil(nav.getBoundingClientRect().height) : 0;
                if (!Number.isFinite(navH) || navH < 44) {
                    navH = 88;
                }
                navH = Math.min(Math.max(navH, 64), 120);
                /* Line up pin with space below sticky eyebrow so 01/04 stays visible (eyebrow is z-index 24). */
                var eyebrow = document.querySelector('#process > h2.process-eyebrow');
                var eyeH = 0;
                if (eyebrow && eyebrow.getBoundingClientRect) {
                    eyeH = Math.ceil(eyebrow.getBoundingClientRect().height);
                }
                if (!Number.isFinite(eyeH) || eyeH < 52) {
                    eyeH = 76;
                }
                var gap = 12;
                return Math.round(navH + eyeH + gap);
            }

            function processDissolveEndPx() {
                var vh = window.innerHeight || 800;
                /* Longer runway = gentler progress through crossfades (pairs with smooth scrub). */
                return Math.round(Math.max(vh, 640) * 3.1);
            }

            var mm = gsap.matchMedia();
            mm.add('(min-width: 1200px)', function() {
                var slides = gsap.utils.toArray(container.querySelectorAll('.sector-slide'));
                if (slides.length < 2) {
                    return;
                }

                gsap.set(slides[0], {
                    autoAlpha: 1
                });
                gsap.set(slides.slice(1), {
                    autoAlpha: 0
                });

                var seg = 0.92;
                var ease = 'power1.inOut';

                var tl = gsap.timeline({
                    scrollTrigger: {
                        trigger: wrap,
                        start: function() {
                            return 'top top+=' + processSectorPinStartPx();
                        },
                        end: function() {
                            return '+=' + processDissolveEndPx();
                        },
                        pin: true,
                        pinSpacing: true,
                        /* Smooth follow with Lenis — avoid tight scrub + fastScrollEnd (feels "snappy" / forced). */
                        scrub: 1.28,
                        anticipatePin: 0,
                        invalidateOnRefresh: true,
                        fastScrollEnd: false,
                        onRefresh: function() {
                            if (typeof lenis !== 'undefined' && typeof lenis.resize === 'function') {
                                lenis.resize();
                            }
                        },
                    },
                });

                for (var i = 0; i < slides.length - 1; i++) {
                    var t = i * seg;
                    tl.to(
                        slides[i], {
                            autoAlpha: 0,
                            duration: seg,
                            ease: ease,
                        },
                        t
                    ).to(
                        slides[i + 1], {
                            autoAlpha: 1,
                            duration: seg,
                            ease: ease,
                        },
                        t
                    );
                }

                return function cleanup() {
                    if (tl.scrollTrigger) {
                        tl.scrollTrigger.kill();
                    }
                    tl.kill();
                    gsap.set(slides, {
                        clearProps: 'opacity,visibility',
                    });
                };
            });
        })();

        /* ── PROCESS SECTION: KPI count-up when a slide crosses into view ─ */
        (function() {
            var reduced = typeof window.matchMedia !== 'undefined' &&
                window.matchMedia('(prefers-reduced-motion: reduce)').matches;

            function easeOutCubic(t) {
                return 1 - Math.pow(1 - t, 3);
            }

            function runCounters(slide) {
                slide.querySelectorAll('.js-count-stat').forEach(function(item) {
                    var end = parseFloat(item.getAttribute('data-count-to'));
                    var suf = item.getAttribute('data-count-suffix') || '';
                    var el = item.querySelector('.stat-count-display');
                    if (!el || !Number.isFinite(end)) return;

                    if (reduced) {
                        el.textContent = Math.round(end) + suf;
                        return;
                    }

                    var duration = 2600;
                    var start = performance.now();

                    function frame(now) {
                        var u = Math.min(1, (now - start) / duration);
                        var val = easeOutCubic(u) * end;
                        el.textContent = Math.round(val) + suf;
                        if (u < 1) {
                            requestAnimationFrame(frame);
                        }
                    }

                    requestAnimationFrame(frame);
                });
            }

            /* Trigger counter animation only once for the sticky stats section */
            const processStatsSection = document.getElementById('process-stats-sticky');
            if (processStatsSection) {
                var done = false;
                var io = new IntersectionObserver(function(entries) {
                    entries.forEach(function(e) {
                        if (done || !e.isIntersecting) return;
                        if (e.intersectionRatio < 0.05) return;
                        done = true;
                        runCounters(processStatsSection);
                    });
                }, {
                    threshold: [0, 0.05, 0.15, 0.3]
                });
                io.observe(processStatsSection);
            }
        })();

        /* ── HERO CANVAS — animated glowing wave ───── */
        (function() {
            const canvas = document.getElementById('hero-canvas');
            if (!canvas) return; // Canvas removed in favor of video
            const ctx = canvas.getContext('2d');
            let W, H, t = 0;

            function resize() {
                W = canvas.width = canvas.offsetWidth || window.innerWidth;
                H = canvas.height = canvas.offsetHeight || window.innerHeight;
            }
            window.addEventListener('resize', resize);
            resize();

            function drawWave() {
                ctx.clearRect(0, 0, W, H);

                // Dark background gradient
                const bg = ctx.createLinearGradient(0, 0, 0, H);
                bg.addColorStop(0, '#0d0b09');
                bg.addColorStop(0.5, '#120e0a');
                bg.addColorStop(1, '#0a0806');
                ctx.fillStyle = bg;
                ctx.fillRect(0, 0, W, H);

                // Radial ambient glow (right center)
                const glow = ctx.createRadialGradient(W * 0.62, H * 0.52, 0, W * 0.62, H * 0.52, W * 0.45);
                glow.addColorStop(0, 'rgba(170,100,40,0.18)');
                glow.addColorStop(0.5, 'rgba(120,65,20,0.08)');
                glow.addColorStop(1, 'transparent');
                ctx.fillStyle = glow;
                ctx.fillRect(0, 0, W, H);

                // Draw main wave ribbon
                const waveY = H * 0.55;
                const amp = H * 0.18;
                const speed = 0.0008;

                // Multiple layered wave paths for depth
                const layers = [{
                        alpha: 0.06,
                        width: 120,
                        offset: 0
                    },
                    {
                        alpha: 0.12,
                        width: 60,
                        offset: 0.3
                    },
                    {
                        alpha: 0.22,
                        width: 28,
                        offset: 0.6
                    },
                    {
                        alpha: 0.55,
                        width: 8,
                        offset: 1.0
                    },
                    {
                        alpha: 0.25,
                        width: 22,
                        offset: 1.3
                    },
                    {
                        alpha: 0.10,
                        width: 50,
                        offset: 1.6
                    },
                ];

                layers.forEach(layer => {
                    ctx.beginPath();
                    ctx.moveTo(0, H);

                    const pts = 200;
                    for (let i = 0; i <= pts; i++) {
                        const x = (i / pts) * W;
                        const progress = i / pts;

                        // Complex wave formula matching the sinuous dune shape
                        const y = waveY -
                            Math.sin(progress * Math.PI * 1.8 + t * speed * 800 + layer.offset) * amp * 0.9 +
                            Math.sin(progress * Math.PI * 0.9 + t * speed * 600) * amp * 0.4 -
                            Math.cos(progress * Math.PI * 2.5 + t * speed * 700) * amp * 0.15 +
                            (progress < 0.5 ? -progress * amp * 0.6 : -(1 - progress) * amp * 0.6);

                        if (i === 0) ctx.moveTo(x, y);
                        else ctx.lineTo(x, y);
                    }

                    // Close to bottom
                    ctx.lineTo(W, H);
                    ctx.lineTo(0, H);
                    ctx.closePath();

                    // Warm copper gradient fill
                    const waveGrad = ctx.createLinearGradient(0, waveY - amp, 0, waveY + amp * 0.5);
                    waveGrad.addColorStop(0, `rgba(210,155,80,${layer.alpha * 0.3})`);
                    waveGrad.addColorStop(0.4, `rgba(195,135,60,${layer.alpha})`);
                    waveGrad.addColorStop(0.7, `rgba(160,100,35,${layer.alpha * 0.8})`);
                    waveGrad.addColorStop(1, `rgba(80,45,15,${layer.alpha * 0.2})`);

                    ctx.strokeStyle = `rgba(220,165,90,${layer.alpha * 1.2})`;
                    ctx.lineWidth = layer.width;
                    ctx.stroke();
                    ctx.fillStyle = waveGrad;
                    ctx.fill();
                });

                // Bright highlight ridge
                ctx.beginPath();
                const ridgePts = 200;
                for (let i = 0; i <= ridgePts; i++) {
                    const x = (i / ridgePts) * W;
                    const progress = i / ridgePts;
                    const y = waveY -
                        Math.sin(progress * Math.PI * 1.8 + t * speed * 800) * amp * 0.9 +
                        Math.sin(progress * Math.PI * 0.9 + t * speed * 600) * amp * 0.4 -
                        (progress < 0.5 ? progress * amp * 0.6 : (1 - progress) * amp * 0.6);
                    if (i === 0) ctx.moveTo(x, y);
                    else ctx.lineTo(x, y);
                }

                // Bright glowing ridge line
                const ridgeGrad = ctx.createLinearGradient(0, 0, W, 0);
                ridgeGrad.addColorStop(0, 'rgba(240,200,140,0)');
                ridgeGrad.addColorStop(0.25, 'rgba(255,210,150,0.4)');
                ridgeGrad.addColorStop(0.5, 'rgba(255,230,180,0.9)');
                ridgeGrad.addColorStop(0.75, 'rgba(255,210,150,0.5)');
                ridgeGrad.addColorStop(1, 'rgba(240,200,140,0.1)');

                ctx.strokeStyle = ridgeGrad;
                ctx.lineWidth = 2.5;
                ctx.shadowColor = 'rgba(255,210,140,0.8)';
                ctx.shadowBlur = 20;
                ctx.stroke();
                ctx.shadowBlur = 0;

                t++;
                requestAnimationFrame(drawWave);
            }
            drawWave();
        })();

        /* ── 3D OBJECT CANVASES (offerings) ─────────── */
        (function() {
            // Crystal / shard shape — canvas 1
            drawRotatingCrystal('obj-crystal', {
                r: 180,
                g: 180,
                b: 195
            });
            drawRotatingCrystal('obj-sphere', {
                r: 170,
                g: 140,
                b: 120
            });
            drawRotatingCrystal('obj-brain', {
                r: 150,
                g: 150,
                b: 165
            });
            drawRotatingCrystal('obj-hex', {
                r: 160,
                g: 140,
                b: 120
            });
            drawRotatingCrystal('obj-hex-2', {
                r: 180,
                g: 160,
                b: 140
            });

            function drawRotatingCrystal(id, tint) {
                const canvas = document.getElementById(id);
                if (!canvas) return;
                const ctx = canvas.getContext('2d');
                const W = canvas.width,
                    H = canvas.height;
                let angle = 0;
                const speed = 0.004 + Math.random() * 0.003;

                // Generate random facets
                const facets = [];
                const N = 18 + Math.floor(Math.random() * 12);
                for (let i = 0; i < N; i++) {
                    const pts = 3 + Math.floor(Math.random() * 3);
                    const points = [];
                    const baseR = 60 + Math.random() * 110;
                    const baseA = (i / N) * Math.PI * 2;
                    for (let j = 0; j < pts; j++) {
                        const a = baseA + (j / pts) * Math.PI * 0.8 - Math.PI * 0.4;
                        const r = baseR * (0.4 + Math.random() * 0.7);
                        points.push({
                            x: Math.cos(a) * r,
                            y: Math.sin(a) * r * 0.75
                        });
                    }
                    const brightness = 0.4 + Math.random() * 0.6;
                    const isMetal = Math.random() > 0.4;
                    facets.push({
                        points,
                        brightness,
                        isMetal,
                        depth: Math.random()
                    });
                }

                function draw() {
                    ctx.clearRect(0, 0, W, H);

                    const cx = W / 2,
                        cy = H / 2;
                    const cosA = Math.cos(angle),
                        sinA = Math.sin(angle);

                    // Sort by depth
                    const sorted = [...facets].sort((a, b) => a.depth - b.depth);

                    sorted.forEach(f => {
                        ctx.beginPath();
                        f.points.forEach((p, i) => {
                            const rx2 = p.x * cosA - p.y * sinA * 0.3;
                            const ry2 = p.x * sinA * 0.3 + p.y * cosA;
                            if (i === 0) ctx.moveTo(cx + rx2, cy + ry2);
                            else ctx.lineTo(cx + rx2, cy + ry2);
                        });
                        ctx.closePath();

                        const lum = Math.round(f.brightness * 220);
                        const r = Math.round(lum * (tint.r / 220));
                        const g = Math.round(lum * (tint.g / 220));
                        const b = Math.round(lum * (tint.b / 220));

                        if (f.isMetal) {
                            const grad = ctx.createLinearGradient(cx - 80, cy - 80, cx + 80, cy + 80);
                            grad.addColorStop(0, `rgba(${r+30},${g+30},${b+30},0.95)`);
                            grad.addColorStop(0.4, `rgba(${r},${g},${b},0.85)`);
                            grad.addColorStop(1, `rgba(${Math.max(0,r-40)},${Math.max(0,g-40)},${Math.max(0,b-40)},0.9)`);
                            ctx.fillStyle = grad;
                        } else {
                            ctx.fillStyle = `rgba(${r},${g},${b},0.7)`;
                        }

                        ctx.fill();
                        ctx.strokeStyle = `rgba(255,255,255,0.12)`;
                        ctx.lineWidth = 0.5;
                        ctx.stroke();
                    });

                    // Glow center
                    const glowR = ctx.createRadialGradient(cx, cy, 0, cx, cy, 180);
                    glowR.addColorStop(0, `rgba(${tint.r},${tint.g},${tint.b},0.08)`);
                    glowR.addColorStop(1, 'transparent');
                    ctx.fillStyle = glowR;
                    ctx.fillRect(0, 0, W, H);

                    angle += speed;
                    requestAnimationFrame(draw);
                }
                draw();
            }
        })();