

(function(){
'use strict';

function _laDV(el, key, def) {
    var w = window.innerWidth;
    if (w <= 767) { var mv = el.getAttribute('data-' + key + '-mobile'); if (mv !== null) return parseFloat(mv); }
    if (w <= 1024) { var tv = el.getAttribute('data-' + key + '-tablet'); if (tv !== null) return parseFloat(tv); }
    var v = parseFloat(el.dataset[key.replace(/-([a-z])/g, function(m,c){ return c.toUpperCase(); })]); return isNaN(v) ? def : v;
}

if (!window._instinctorPluginUrl) window._instinctorPluginUrl = '';

function easeHeavySpring(t) {
    if (t <= 0) return 0;
    if (t >= 1) return 1;
    var s = 2.0, s1 = s + 1, u = t - 1;
    return 1 + s1 * u * u * u + s * u * u;
}
function applyEase(t, ease) {
    t = t < 0 ? 0 : t > 1 ? 1 : t;

    if (ease === 'linear') return t;
    return easeHeavySpring(t);
}

function rafLoop(dur, onFrame, onDone) {
    var start = null, raf;
    function step(ts) {
        if (!start) start = ts;
        var t = (ts - start) / dur;
        if (t > 1) t = 1;
        onFrame(t);
        if (t < 1) { raf = requestAnimationFrame(step); }
        else { if (onDone) onDone(); }
    }
    raf = requestAnimationFrame(step);
    return function() { cancelAnimationFrame(raf); };
}

function lerp(a, b, t) { return a + (b - a) * t; }
function clamp(v, lo, hi) { return v < lo ? lo : v > hi ? hi : v; }
function rnd(a, b) { return a + Math.random() * (b - a); }

function _laDPR() { return Math.min(window.devicePixelRatio || 1, 2); }

function sampleLogo(asset, w, h, bgColor, bounds, opts) {
    var dpr = _laDPR();
    var ofc = document.createElement('canvas');
    ofc.width = Math.max(1, Math.round(w * dpr));
    ofc.height = Math.max(1, Math.round(h * dpr));
    var ctx = ofc.getContext('2d');

    
    
    ofc._laDPR = dpr;
    ofc._laCssW = w;
    ofc._laCssH = h;

    
    w  = w  * dpr;
    h  = h  * dpr;

    if (bgColor && bgColor !== 'transparent' && bgColor !== 'rgba(0, 0, 0, 0)') {
        ctx.fillStyle = bgColor;
        ctx.fillRect(0, 0, w, h);
    }

    var bx = (bounds ? bounds.x : 0) * dpr;
    var by = (bounds ? bounds.y : 0) * dpr;
    var bw = (bounds ? bounds.w : w / dpr) * dpr;
    var bh = (bounds ? bounds.h : h / dpr) * dpr;
    var cx = bx + bw / 2;
    var cy = by + bh / 2;

    if (asset.tagName === 'IMG') {
        try { ctx.drawImage(asset, bx, by, bw, bh); }
        catch(e) {}
    } else {
        var cs = window.getComputedStyle(asset);

        
        var fSize = (parseFloat(cs.fontSize) || 64) * dpr;
        var fFamily = cs.fontFamily || 'sans-serif';
        var fWeight = cs.fontWeight || '700';
        var color   = cs.color      || '#ffffff';

        var text = (function _readT(a){
            if (!a.childNodes || !a.childNodes.length) return a.textContent || '';
            var t = '';
            for (var i = 0; i < a.childNodes.length; i++) {
                var n = a.childNodes[i];
                if (n.nodeType === 3) t += n.nodeValue;
                else if (n.nodeName === 'BR') t += '\n';
                else if (n.textContent) t += n.textContent;
            }
            return t;
        })(asset).replace(/\\n/g, '\n');

        
        
        var _laParent = asset.parentElement;
        var _laJC = _laParent ? window.getComputedStyle(_laParent).justifyContent : 'center';
        var _laAlign = _laJC === 'flex-start' ? 'left' : (_laJC === 'flex-end' ? 'right' : 'center');

        

        if (_laParent) _laParent._laLastAlign = _laAlign;
        ctx.textAlign    = _laAlign;
        ctx.textBaseline = 'middle';
        var maxW = bw * 0.96;
        var lh = fSize * 1.15;
        
        function wrapLines(txt, fs) {
            ctx.font = fWeight + ' ' + Math.round(fs) + 'px ' + fFamily;
            var raw = txt.split('\n');
            var out = [];
            for (var r = 0; r < raw.length; r++) {
                var words = raw[r].split(/\s+/);
                var line = '';
                for (var j = 0; j < words.length; j++) {
                    var test = line ? line + ' ' + words[j] : words[j];
                    if (ctx.measureText(test).width > maxW && line) {
                        out.push(line);
                        line = words[j];
                    } else {
                        line = test;
                    }
                }
                if (line) out.push(line);
            }
            return out;
        }
        var lines = wrapLines(text, fSize);
        
        var finalSize = fSize;
        for (var li = 0; li < lines.length; li++) {
            var lw = ctx.measureText(lines[li]).width;
            if (lw > maxW && lw > 0) finalSize = Math.min(finalSize, fSize * maxW / lw);
        }
        if (finalSize < fSize) {
            lines = wrapLines(text, finalSize);
            lh = finalSize * 1.15;
        }
        ctx.font = fWeight + ' ' + Math.round(finalSize) + 'px ' + fFamily;
        if (opts && opts.gradFrom && opts.gradTo) {
            var angle = ((opts.gradAngle || 0) * Math.PI) / 180;

            

            

            
            var _gMaxLineW = 0;
            for (var _glI = 0; _glI < lines.length; _glI++) {
                var _glW = ctx.measureText(lines[_glI]).width;
                if (_glW > _gMaxLineW) _gMaxLineW = _glW;
            }
            var gLen = (_gMaxLineW > 0 ? _gMaxLineW : bw) * 0.5;
            var grd   = ctx.createLinearGradient(
                cx - Math.cos(angle) * gLen, cy - Math.sin(angle) * gLen,
                cx + Math.cos(angle) * gLen, cy + Math.sin(angle) * gLen);
            grd.addColorStop(0, opts.gradFrom);
            grd.addColorStop(1, opts.gradTo);
            ctx.fillStyle = grd;
        } else {
            ctx.fillStyle = color;
        }
        var totalH = lines.length * lh;
        var startY = cy - totalH / 2 + lh / 2;

        
        
        var _laAnchorX = _laAlign === 'left' ? bx : (_laAlign === 'right' ? bx + bw : cx);
        for (var li2 = 0; li2 < lines.length; li2++) {
            ctx.fillText(lines[li2], _laAnchorX, startY + li2 * lh);
        }
    }
    return ofc;
}

function makeCanvas(container, _wOverride, _hOverride) {
    var old = container.querySelector('.impulse-la-canvas');
    if (old) old.parentNode.removeChild(old);
    var old3d = container.querySelector('.impulse-la-3d-canvas');
    if (old3d) old3d.parentNode.removeChild(old3d);
    
    var cw = (typeof _wOverride === 'number' && _wOverride > 0) ? _wOverride : (container.offsetWidth  || 400);
    var ch = (typeof _hOverride === 'number' && _hOverride > 0) ? _hOverride : (container.offsetHeight || 200);
    var dpr = _laDPR();
    var cv = document.createElement('canvas');
    cv.className = 'impulse-la-canvas';
    cv.width  = Math.max(1, Math.round(cw * dpr));
    cv.height = Math.max(1, Math.round(ch * dpr));
    cv._laDPR  = dpr;
    cv._laCssW = cw;
    cv._laCssH = ch;
    cv.style.cssText = 'position:absolute;top:0;left:0;width:'+cw+'px;height:'+ch+'px;pointer-events:none;z-index:5';
    container.appendChild(cv);
    return cv;
}

function _laText(a) {
    if (!a) return '';
    var t;
    if (!a.childNodes || !a.childNodes.length) {
        t = a.textContent || '';
    } else {
        t = '';
        for (var i = 0; i < a.childNodes.length; i++) {
            var n = a.childNodes[i];
            if (n.nodeType === 3) t += n.nodeValue;
            else if (n.nodeName === 'BR') t += '\n';
            else if (n.textContent) t += n.textContent;
        }
    }
    return t.replace(/\\n/g, '\n');
}

function finishAnim(cv, asset) {
    
    var container = cv.parentNode;
    if (!container) return;

    var go = container._laGradOpts;
    var bounds = container._laBounds;
    if (!go || !bounds || asset.tagName === 'IMG') return;

    var ctx2 = cv.getContext('2d');
    var w = cv.width, h = cv.height;

    

    var _faDpr = cv._laDPR || 1;

    var targetAngle = go.gradAngle;
    var currentAngle = go.gradAngle;

    container._laGradGen = (container._laGradGen || 0) + 1;
    var myGen = container._laGradGen;

    if (container._laGradScopeCleanup) { container._laGradScopeCleanup(); container._laGradScopeCleanup = null; }

    function redrawGrad() {
        
        var liveFrom  = container.dataset.gradientFrom || container.dataset.color || go.gradFrom;
        var liveTo    = container.dataset.gradientTo   || go.gradTo;
        var liveAngle = _laDV(container, 'gradient-angle', go.gradAngle);
        
        go.gradAngle = liveAngle;

        var cs2 = window.getComputedStyle(asset);
        var fSize2   = (parseFloat(cs2.fontSize)   || go.fSize) * _faDpr;
        var fFamily2 = cs2.fontFamily             || go.fFamily;
        var fWeight2 = cs2.fontWeight             || go.fWeight;
        var text2    = _laText(asset)             || go.text;

        ctx2.clearRect(0, 0, w, h);
        var bgNow = window.getComputedStyle(container).backgroundColor;
        if (bgNow && bgNow !== 'transparent' && bgNow !== 'rgba(0, 0, 0, 0)') {
            ctx2.fillStyle = bgNow; ctx2.fillRect(0, 0, w, h);
        }

        
        var bx = bounds.x * _faDpr, by = bounds.y * _faDpr,
            bw = bounds.w * _faDpr, bh = bounds.h * _faDpr;
        var cx = bx + bw * 0.5, cy = by + bh * 0.5;
        var rad = currentAngle * Math.PI / 180;

        var _gJC = container ? window.getComputedStyle(container).justifyContent : 'center';
        var _gAlign = _gJC === 'flex-start' ? 'left' : (_gJC === 'flex-end' ? 'right' : 'center');
        ctx2.textAlign = _gAlign;
        ctx2.textBaseline = 'middle';
        var _gt = (text2 || '').replace(/\\n/g, '\n');
        var _gmaxW = bw * 0.96;
        var _gfs = fSize2;
        ctx2.font = fWeight2 + ' ' + Math.round(_gfs) + 'px ' + fFamily2;
        var _graw = _gt.split('\n');
        var _glines = [];
        for (var _gr = 0; _gr < _graw.length; _gr++) {
            var _gw = _graw[_gr].split(/\s+/); var _gl = '';
            for (var _gj = 0; _gj < _gw.length; _gj++) {
                var _gtest = _gl ? _gl + ' ' + _gw[_gj] : _gw[_gj];
                if (ctx2.measureText(_gtest).width > _gmaxW && _gl) { _glines.push(_gl); _gl = _gw[_gj]; }
                else _gl = _gtest;
            }
            if (_gl) _glines.push(_gl);
        }

        

        var _gMaxLineW = 0;
        for (var _glm = 0; _glm < _glines.length; _glm++) {
            var _glw = ctx2.measureText(_glines[_glm]).width;
            if (_glw > _gMaxLineW) _gMaxLineW = _glw;
        }
        var gLen = (_gMaxLineW > 0 ? _gMaxLineW : bw) * 0.5;
        if (liveTo) {
            var grd = ctx2.createLinearGradient(
                cx - Math.cos(rad)*gLen, cy - Math.sin(rad)*gLen,
                cx + Math.cos(rad)*gLen, cy + Math.sin(rad)*gLen);
            grd.addColorStop(0, liveFrom);
            grd.addColorStop(1, liveTo);
            ctx2.fillStyle = grd;
        } else {
            ctx2.fillStyle = liveFrom;
        }
        var _glh = _gfs * 1.15;
        var _gtotalH = _glines.length * _glh;
        var _gstartY = cy - _gtotalH / 2 + _glh / 2;
        var _gAnchorX = _gAlign === 'left' ? bx : (_gAlign === 'right' ? bx + bw : cx);
        for (var _gli = 0; _gli < _glines.length; _gli++) ctx2.fillText(_glines[_gli], _gAnchorX, _gstartY + _gli * _glh);
    }

    function gradLoop() {
        
        if (container._laGradGen !== myGen) return;
        var diff = targetAngle - currentAngle;
        while (diff >  180) diff -= 360;
        while (diff < -180) diff += 360;
        currentAngle += diff * 0.08;
        redrawGrad();
        container._laGradRaf = requestAnimationFrame(gradLoop);
    }

    var scope = container.closest('.impulse-section') || container.parentElement || container;
    function onMove(e) {

        

        
        
        if ((parseFloat(_laDV(container, 'tilt', 0)) || 0) <= 0) {
            targetAngle = go.gradAngle;
            return;
        }
        var r = container.getBoundingClientRect();
        var ecx = r.left + r.width  * 0.5;
        var ecy = r.top  + r.height * 0.5;
        targetAngle = Math.atan2(e.clientY - ecy, e.clientX - ecx) * 180 / Math.PI + 90;
    }
    function onLeave() { targetAngle = go.gradAngle; }
    scope.addEventListener('mousemove', onMove);
    scope.addEventListener('mouseleave', onLeave);
    container._laGradScopeCleanup = function() {
        scope.removeEventListener('mousemove', onMove);
        scope.removeEventListener('mouseleave', onLeave);
    };

    gradLoop();
}

var P = {};

P.build_columns = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {

    
    var hasTextBounds = bounds && (bounds.textW > 0);
    var bx      = hasTextBounds ? bounds.textX : (bounds ? bounds.x : 0);
    var bw      = hasTextBounds ? bounds.textW : (bounds ? bounds.w : w);
    var COLS    = Math.max(4, Math.ceil(bw / 24));
    var cw      = bw / COLS;
    var stagger = 0.55;
    var delays  = [];
    for (var i = 0; i < COLS; i++)
        delays.push((i / COLS) * stagger * 0.7 + Math.random() * stagger * 0.3);

    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        for (var i = 0; i < COLS; i++) {
            var p  = applyEase(clamp((t - delays[i]) / (1 - stagger), 0, 1), ease);
            if (p <= 0) continue;
            var colX   = bx + i * cw;
            
            var yOff   = (1 - p) * (h + 8);
            ctx.save();
            ctx.globalAlpha = Math.min(1, p * 2.2);
            ctx.beginPath();
            ctx.rect(colX, 0, cw + 0.5, h);
            ctx.clip();
            
            ctx.drawImage(src, colX, 0, cw, h, colX, yOff, cw, h);
            if (p < 0.92 && yOff > 0) {
                var entryY = Math.min(yOff, h - 1);
                var lg = ctx.createLinearGradient(0, entryY, 0, entryY + 12);
                lg.addColorStop(0, 'rgba(255,255,255,' + ((1-p)*0.55).toFixed(2) + ')');
                lg.addColorStop(1, 'rgba(255,255,255,0)');
                ctx.fillStyle = lg;
                ctx.fillRect(colX, entryY, cw, 12);
            }
            ctx.restore();
        }
    }, onDone);
};

P.build_glitch = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx = bounds ? bounds.x : 0, by = bounds ? bounds.y : 0;
    var bw = bounds ? bounds.w : w,  bh2 = bounds ? bounds.h : h;

    var bars = [];
    for (var i = 0; i < 14; i++) {
        var s = Math.sin(i * 127.1 + 311.7);
        var s2 = Math.sin(i * 74.3 + 199.1);
        var s3 = Math.sin(i * 43.1 + 87.3);
        bars.push({
            
            y   : by + ((s  * 43758.5453) % 1 + 1) % 1 * bh2,
            bh  : ((s2 * 43758.5453) % 1 + 1) % 1 * 22 + 3,
            dx0 : (((s3 * 43758.5453) % 1 + 1) % 1 - 0.5) * bw * 0.28,
            t0  : ((Math.sin(i * 17.1) * 43758.5453) % 1 + 1) % 1 * 0.45,
            t1  : ((Math.sin(i * 29.3) * 43758.5453) % 1 + 1) % 1 * 0.32 + 0.5
        });
    }

    var imgD = src.getContext('2d').getImageData(0, 0, w, h);
    var chR = document.createElement('canvas'), chB = document.createElement('canvas');
    chR.width = chB.width = w; chR.height = chB.height = h;
    var idR = chR.getContext('2d').createImageData(w, h);
    var idB = chB.getContext('2d').createImageData(w, h);
    
    var logoOnly = document.createElement('canvas');
    logoOnly.width = w; logoOnly.height = h;
    var loCtx = logoOnly.getContext('2d');
    loCtx.drawImage(src, 0, 0);
    var loD = loCtx.getImageData(0, 0, w, h);
    
    var bgR = imgD.data[0], bgG = imgD.data[1], bgB2 = imgD.data[2];
    for (var i = 0; i < imgD.data.length; i += 4) {
        var pr = imgD.data[i], pg = imgD.data[i+1], pb = imgD.data[i+2], pa = imgD.data[i+3];
        
        var isBg = (pa > 200 && Math.abs(pr-bgR)<8 && Math.abs(pg-bgG)<8 && Math.abs(pb-bgB2)<8);
        var a = isBg ? 0 : pa;
        idR.data[i] = pr; idR.data[i+1] = 0;  idR.data[i+2] = 0;  idR.data[i+3] = a;
        idB.data[i] = 0;  idB.data[i+1] = 0;  idB.data[i+2] = pb; idB.data[i+3] = a;
    }
    chR.getContext('2d').putImageData(idR, 0, 0);
    chB.getContext('2d').putImageData(idB, 0, 0);

    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);

        var baseAlpha = applyEase(clamp(t / 0.82, 0, 1), ease);

        var caStrength = clamp(1 - t / 0.75, 0, 1);
        var caOff = caStrength * bw * 0.022;
        if (caOff > 0.3) {
            ctx.save();
            ctx.beginPath(); ctx.rect(bx, by, bw, bh2); ctx.clip();
            ctx.globalAlpha = baseAlpha;
            ctx.globalCompositeOperation = 'screen';
            ctx.drawImage(chR, -caOff, 0, w, h);
            ctx.drawImage(chB,  caOff, 0, w, h);
            ctx.restore();
        }

        ctx.save();
        ctx.globalAlpha = baseAlpha * (caOff > 0.3 ? 0.82 : 1);
        ctx.drawImage(src, 0, 0, w, h);
        ctx.restore();

        bars.forEach(function(bar) {
            if (t < bar.t0 || t > bar.t1) return;
            var barP = (t - bar.t0) / (bar.t1 - bar.t0);
            var dx   = bar.dx0 * (1 - applyEase(barP, 'ease-in-out'));
            if (Math.abs(dx) < 0.5) return;
            var alpha = Math.sin(barP * Math.PI) * 0.9;
            var caBar = Math.abs(dx) * 0.12;

            ctx.save();
            ctx.beginPath(); ctx.rect(bx, bar.y, bw, bar.bh); ctx.clip();

            ctx.globalAlpha = alpha * 0.75;
            ctx.globalCompositeOperation = 'screen';
            ctx.drawImage(chR, dx - caBar, 0, w, h);
            ctx.drawImage(chB, dx + caBar, 0, w, h);

            ctx.globalCompositeOperation = 'source-over';
            ctx.globalAlpha = alpha * 0.85;
            ctx.drawImage(src, dx, 0, w, h);
            ctx.restore();
        });

        if (t < 0.12) {
            var flashA = (0.12 - t) / 0.12 * 0.18;
            ctx.save();
            ctx.globalAlpha = flashA;
            ctx.fillStyle = '#fff';
            ctx.fillRect(bx, by, bw, bh2);
            ctx.restore();
        }
    }, onDone);
};

P.build_decode = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var levels = [48, 24, 12, 6, 3, 1];
    
    var frames = levels.map(function(bs) {
        if (bs === 1) return src;
        var ofc = document.createElement('canvas');
        ofc.width  = Math.max(1, Math.ceil(w / bs));
        ofc.height = Math.max(1, Math.ceil(h / bs));
        var oc = ofc.getContext('2d');
        oc.imageSmoothingEnabled = false;
        oc.drawImage(src, 0, 0, ofc.width, ofc.height);
        return ofc;
    });

    return rafLoop(dur * 2, function(t) {
        var fPos     = applyEase(t, ease) * (levels.length - 1);
        var fIdx     = Math.min(Math.floor(fPos), levels.length - 1);
        var blend    = fPos - fIdx;
        var fIdxNext = Math.min(fIdx + 1, levels.length - 1);

        ctx.clearRect(0, 0, w, h);
        ctx.imageSmoothingEnabled = false;
        
        ctx.save();
        ctx.globalAlpha = 1 - blend * 0.4;
        ctx.drawImage(frames[fIdx], 0, 0, w, h);
        ctx.restore();
        
        if (blend > 0) {
            ctx.save();
            ctx.globalAlpha = blend;
            ctx.drawImage(frames[fIdxNext], 0, 0, w, h);
            ctx.restore();
        }
        ctx.imageSmoothingEnabled = true;
    }, onDone);
};

P.build_slots = function(cv, ctx, src, w, h, dur, ease, onDone) {
    
    var cssW = cv._laCssW || w;
    var COLS = Math.max(5, Math.min(64, Math.ceil(cssW / 48)));
    var cw   = w / COLS;
    var reels = [];
    for (var i = 0; i < COLS; i++) {
        reels.push({
            delay     : (i / COLS) * 0.35 + Math.random() * 0.12,
            extraSpin : rnd(0.5, 1.5)
        });
    }

    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        for (var i = 0; i < COLS; i++) {
            var reel = reels[i];
            var rawP = clamp((t - reel.delay) / (1 - 0.47), 0, 1);
            if (rawP <= 0) continue;
            var p   = applyEase(rawP, 'cubic-bezier(0.4, 0, 0.2, 1)');
            var cx  = i * cw;
            var totalTravel = h * (1 + reel.extraSpin) * 2.8;
            var dy  = -(1 - p) * totalTravel;
            var offset = ((dy % h) + h) % h;
            
            var sx = Math.floor(cx);
            var sw = Math.min(Math.ceil(cw + 1), w - sx);
            if (sw <= 0) continue;
            ctx.save();
            ctx.globalAlpha = Math.min(1, rawP * 3);
            ctx.drawImage(src, sx, 0, sw, h, sx, offset - h, sw, h);
            ctx.drawImage(src, sx, 0, sw, h, sx, offset,     sw, h);
            ctx.drawImage(src, sx, 0, sw, h, sx, offset + h, sw, h);
            ctx.restore();
        }
    }, onDone);
};

P.build_unfold = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var LAYERS  = Math.max(10, Math.ceil(h / 18));
    var lh      = h / LAYERS;
    var halfL   = (LAYERS - 1) / 2;
    var stagger = 0.55;
    var layers  = [];
    for (var i = 0; i < LAYERS; i++) {
        var distFromCenter = Math.abs(i - halfL) / halfL;
        layers.push({
            y        : i * lh,
            fromAbove: i < LAYERS / 2,
            delay    : distFromCenter * stagger
        });
    }

    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        layers.forEach(function(layer) {
            var p = applyEase(clamp((t - layer.delay) / (1 - stagger), 0, 1), ease);
            if (p <= 0) return;
            var foldOrigin = layer.fromAbove ? layer.y + lh : layer.y;
            var drawY = foldOrigin - (foldOrigin - layer.y) * p;
            var drawH = lh * p;
            if (drawH < 0.5) return;
            ctx.save();
            ctx.globalAlpha = Math.min(1, p * 1.8);
            ctx.beginPath();
            ctx.rect(0, drawY, w, drawH + 0.5);
            ctx.clip();
            ctx.transform(1, 0, 0, p, 0, foldOrigin * (1 - p));
            ctx.drawImage(src, 0, 0, w, h);
            ctx.restore();
        });
    }, onDone);
};

P.build_streaks = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var COLS  = Math.ceil(w / 20);
    var cw    = w / COLS;
    var stagger = 0.52;
    var cols  = [];
    for (var i = 0; i < COLS; i++) {
        
        var base = (i / COLS) * stagger * 0.6;
        var jitter = Math.random() * stagger * 0.4;
        
        var fromBelow = Math.random() < 0.5;
        var dist = rnd(h * 1.1, h * 2.4);
        cols.push({ x: i * cw + cw/2, delay: base + jitter,
            fromBelow: fromBelow, dist: dist });
    }

    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        cols.forEach(function(col) {
            var p    = applyEase(clamp((t - col.delay) / (1 - stagger), 0, 1), 'cubic-bezier(0.4, 0, 0.2, 1)');
            if (p <= 0) return;
            var origin  = col.fromBelow ? h + col.dist : -col.dist;
            var currentY = lerp(origin, 0, p); 
            var speed    = 1 - p;

            ctx.save();
            ctx.beginPath();
            ctx.rect(col.x - cw/2, 0, cw + 0.5, h);
            ctx.clip();

            ctx.globalAlpha = Math.min(1, p * 2.5);
            ctx.drawImage(src, 0, 0, w, h);

            if (speed > 0.04) {
                var trailLen = h * 0.55 * speed;
                var tY0 = col.fromBelow ? currentY + h : currentY;
                var tY1 = col.fromBelow ? tY0 - trailLen : tY0 + trailLen;
                var tg  = ctx.createLinearGradient(0, tY0, 0, tY1);
                var a   = (speed * 0.75).toFixed(3);
                tg.addColorStop(0, 'rgba(255,255,255,' + a + ')');
                tg.addColorStop(0.35, 'rgba(200,230,255,' + (speed*0.35).toFixed(3) + ')');
                tg.addColorStop(1, 'rgba(200,220,255,0)');
                ctx.globalAlpha = Math.min(1, p * 1.8);
                ctx.fillStyle = tg;
                ctx.fillRect(col.x - cw/2, Math.min(tY0,tY1), cw, Math.abs(tY1-tY0) + 1);
            }
            ctx.restore();
        });
    }, onDone);
};

P.build_dissolve = function(cv, ctx, src, w, h, dur, ease, onDone) {
    
    var total = w * h;
    var thresholds = new Float32Array(total);
    for (var i = 0; i < total; i++) thresholds[i] = Math.random();

    var order = new Int32Array(total);
    for (var i = 0; i < total; i++) order[i] = i;
    order.sort(function(a, b) { return thresholds[a] - thresholds[b]; });

    var srcData = src.getContext('2d').getImageData(0, 0, w, h);

    var maskId = ctx.createImageData(w, h);
    var lastCount = 0;

    return rafLoop(dur * 2, function(t) {
        
        var p = applyEase(t, ease);
        
        var count = Math.min(total, Math.floor(p * total));

        for (var i = lastCount; i < count; i++) {
            var px = order[i];
            var si = px * 4;
            maskId.data[si]   = srcData.data[si];
            maskId.data[si+1] = srcData.data[si+1];
            maskId.data[si+2] = srcData.data[si+2];
            maskId.data[si+3] = srcData.data[si+3];
        }
        lastCount = count;

        ctx.clearRect(0, 0, w, h);
        ctx.putImageData(maskId, 0, 0);

        if (p < 0.75) {
            ctx.save();
            var shimA = (0.18 * (1 - p / 0.75)).toFixed(3);
            ctx.globalAlpha = parseFloat(shimA);
            ctx.globalCompositeOperation = 'source-atop';
            ctx.fillStyle = 'rgba(220,235,255,1)';
            ctx.fillRect(0, 0, w, h);
            ctx.restore();
        }
    }, onDone);
};

P.build_emerge = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var COLS  = Math.ceil(w / 38), ROWS = Math.ceil(h / 38);
    var tw    = w / COLS, th = h / ROWS;
    var vx    = w / 2, vy = h / 2;
    var tiles = [];
    for (var r = 0; r < ROWS; r++) {
        for (var c = 0; c < COLS; c++) {
            var cx = c * tw + tw / 2, cy = r * th + th / 2;
            var dist = Math.sqrt((cx-vx)*(cx-vx) + (cy-vy)*(cy-vy));
            tiles.push({ cx: cx, cy: cy,
                delay: (dist / (Math.max(w,h) * 0.72)) * 0.5 + Math.random() * 0.08 });
        }
    }
    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        tiles.forEach(function(tile) {
            var p  = applyEase(clamp((t - tile.delay) / (1-0.58), 0, 1), 'cubic-bezier(0.4, 0, 0.2, 1)');
            if (p <= 0) return;
            var sc  = p;
            var scx = lerp(vx, tile.cx, p);
            var scy = lerp(vy, tile.cy, p);
            ctx.save();
            ctx.globalAlpha = Math.min(1, p * 2.5);
            ctx.beginPath();
            ctx.rect(scx - tw*sc/2, scy - th*sc/2, tw*sc + 0.5, th*sc + 0.5);
            ctx.clip();
            
            ctx.translate(scx, scy);
            ctx.scale(sc, sc);
            ctx.translate(-tile.cx, -tile.cy);
            ctx.drawImage(src, 0, 0, w, h);
            ctx.restore();
        });
    }, onDone);
};

P.build_zslices = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var N     = Math.ceil(h / 22);
    var sh    = h / N;
    var mid   = (N - 1) / 2;
    var vx    = w / 2, vy = h / 2;
    var slices = [];
    for (var i = 0; i < N; i++) {
        var cy_home = i * sh + sh / 2;
        var dist = Math.abs(i - mid) / (N / 2);
        slices.push({ cy_home: cy_home, dist: dist,
            delay: dist * 0.42 + Math.random() * 0.06 });
    }
    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        slices.forEach(function(sl) {
            var p  = applyEase(clamp((t - sl.delay) / (1 - 0.48), 0, 1), ease);
            if (p <= 0) return;
            var sc  = p;                           
            var scx = lerp(vx, vx, p);             
            var scy = lerp(vy, sl.cy_home, p);     
            var dw  = w * sc, dh = sh * sc;
            ctx.save();
            ctx.globalAlpha = Math.min(1, p * 2.2);
            ctx.beginPath();
            ctx.rect(scx - dw/2, scy - dh/2, dw + 0.5, dh + 0.5);
            ctx.clip();
            ctx.translate(scx, scy);
            ctx.scale(sc, sc);
            ctx.translate(-vx, -sl.cy_home);
            ctx.drawImage(src, 0, 0, w, h);
            ctx.restore();
        });
    }, onDone);
};

P.build_converge = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx = bounds ? bounds.x : 0, by = bounds ? bounds.y : 0;
    var bw = bounds ? bounds.w : w,  bh2 = bounds ? bounds.h : h;
    var COLS  = Math.max(3, Math.ceil(bw / 36)), ROWS = Math.max(2, Math.ceil(bh2 / 36));
    var tw    = bw / COLS, th = bh2 / ROWS;
    var vx    = bx + bw / 2, vy = by + bh2 / 2;
    var START = 2.6;
    var maxDist = Math.sqrt(bw*bw/4 + bh2*bh2/4) || 1;
    var tiles = [];
    for (var r = 0; r < ROWS; r++) {
        for (var c = 0; c < COLS; c++) {
            var tcx = bx + c * tw + tw / 2, tcy = by + r * th + th / 2;
            var sx = vx + (tcx - vx) * START;
            var sy = vy + (tcy - vy) * START;
            var dist = Math.sqrt((tcx-vx)*(tcx-vx)+(tcy-vy)*(tcy-vy));
            tiles.push({ cx: tcx, cy: tcy, sx: sx, sy: sy,
                delay: (1 - dist / maxDist) * 0.28 + Math.random() * 0.08
            });
        }
    }
    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        tiles.forEach(function(tile) {
            var p   = applyEase(clamp((t - tile.delay) / (1 - 0.36), 0, 1), ease);
            if (p <= 0) return;
            var sc  = lerp(START, 1, p);
            var scx = lerp(tile.sx, tile.cx, p);
            var scy = lerp(tile.sy, tile.cy, p);
            var hw  = tw * sc / 2, hh = th * sc / 2;
            if (scx + hw < 0 || scx - hw > w || scy + hh < 0 || scy - hh > h) return;
            ctx.save();
            ctx.globalAlpha = Math.min(1, p * 1.5);
            ctx.beginPath();
            ctx.rect(scx - hw, scy - hh, tw*sc + 0.5, th*sc + 0.5);
            ctx.clip();
            ctx.translate(scx, scy);
            ctx.scale(sc, sc);
            ctx.translate(-tile.cx, -tile.cy);
            ctx.drawImage(src, 0, 0, w, h);
            ctx.restore();
        });
    }, onDone);
};

function clipPolyHP(poly, px, py, nx, ny) {
    if (!poly.length) return poly;
    var out = [];
    for (var _i = 0; _i < poly.length; _i++) {
        var _a = poly[_i], _b = poly[(_i+1) % poly.length];
        var _da = (_a[0]-px)*nx + (_a[1]-py)*ny;
        var _db = (_b[0]-px)*nx + (_b[1]-py)*ny;
        if (_da <= 0) out.push(_a);
        if ((_da < 0 && _db > 0) || (_da > 0 && _db < 0)) {
            var _tt = _da / (_da - _db);
            out.push([_a[0]+_tt*(_b[0]-_a[0]), _a[1]+_tt*(_b[1]-_a[1])]);
        }
    }
    return out;
}

function computeVCells(seeds, w, h) {
    return seeds.map(function(seed, i) {
        var poly = [[0,0],[w,0],[w,h],[0,h]];
        seeds.forEach(function(other, j) {
            if (j === i) return;
            var mx=(seed[0]+other[0])/2, my=(seed[1]+other[1])/2;
            poly = clipPolyHP(poly, mx, my, other[0]-seed[0], other[1]-seed[1]);
        });
        return { poly: poly, cx: seed[0], cy: seed[1] };
    });
}

function extractRGBChannels(src, w, h) {
    var imgD = src.getContext('2d').getImageData(0, 0, w, h);
    return [0,1,2].map(function(ch) {
        var ofc = document.createElement('canvas');
        ofc.width = w; ofc.height = h;
        var id = ofc.getContext('2d').createImageData(w, h);
        for (var _i = 0; _i < imgD.data.length; _i += 4) {
            id.data[_i]   = ch===0 ? imgD.data[_i]   : 0;
            id.data[_i+1] = ch===1 ? imgD.data[_i+1] : 0;
            id.data[_i+2] = ch===2 ? imgD.data[_i+2] : 0;
            id.data[_i+3] = imgD.data[_i+3];
        }
        ofc.getContext('2d').putImageData(id, 0, 0);
        return ofc;
    });
}

P.voronoi_crack = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx=bounds?bounds.x:0, by=bounds?bounds.y:0, bw=bounds?bounds.w:w, bh2=bounds?bounds.h:h;
    var seeds=[], N=22;
    for(var i=0;i<N;i++) seeds.push([bx+rnd(bw*0.04,bw*0.96), by+rnd(bh2*0.04,bh2*0.96)]);
    var bcx=bx+bw/2, bcy=by+bh2/2;
    var cells=computeVCells(seeds,w,h);
    var maxD=0;
    cells.forEach(function(c){var d=Math.sqrt((c.cx-bcx)*(c.cx-bcx)+(c.cy-bcy)*(c.cy-bcy));if(d>maxD)maxD=d;});
    maxD=maxD||1;
    return rafLoop(dur*1.4, function(t) {
        ctx.clearRect(0,0,w,h);
        
        ctx.save(); ctx.beginPath(); ctx.rect(bx,by,bw,bh2); ctx.clip();
        
        cells.forEach(function(c) {
            if (!c.poly.length) return;
            var d=Math.sqrt((c.cx-bcx)*(c.cx-bcx)+(c.cy-bcy)*(c.cy-bcy))/maxD;
            var fp=applyEase(clamp((t-d*0.45)/(1-0.45),0,1),'cubic-bezier(0.4, 0, 0.2, 1)');
            if (fp<=0) return;
            ctx.save();
            ctx.translate(c.cx,c.cy); ctx.scale(fp,fp); ctx.translate(-c.cx,-c.cy);
            ctx.beginPath(); ctx.moveTo(c.poly[0][0],c.poly[0][1]);
            c.poly.slice(1).forEach(function(pt){ctx.lineTo(pt[0],pt[1]);}); ctx.closePath();
            ctx.clip();
            ctx.translate(c.cx,c.cy); ctx.scale(1/fp,1/fp); ctx.translate(-c.cx,-c.cy);
            ctx.globalAlpha=Math.min(1,fp*1.5); ctx.drawImage(src,0,0,w,h); ctx.restore();
        });
        
        var crackA=applyEase(clamp(t/0.3,0,1),ease)*(1-applyEase(clamp((t-0.4)/0.5,0,1),'linear'))*0.45;
        if (crackA>0.01) {
            ctx.save(); ctx.strokeStyle='rgba(255,255,255,'+crackA.toFixed(3)+')'; ctx.lineWidth=0.7;
            cells.forEach(function(c) {
                if (!c.poly.length) return;
                ctx.beginPath(); ctx.moveTo(c.poly[0][0],c.poly[0][1]);
                c.poly.forEach(function(pt){ctx.lineTo(pt[0],pt[1]);}); ctx.closePath(); ctx.stroke();
            }); ctx.restore();
        }
        ctx.restore();
    }, onDone);
};

P.voronoi_pop = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var seeds=[], N=20;
    for(var i=0;i<N;i++) seeds.push([rnd(w*0.05,w*0.95),rnd(h*0.05,h*0.95)]);
    var cells=computeVCells(seeds,w,h);
    var maxD=0;
    cells.forEach(function(c){var d=Math.sqrt((c.cx-w/2)*(c.cx-w/2)+(c.cy-h/2)*(c.cy-h/2));if(d>maxD)maxD=d;});
    maxD=maxD||1;
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        cells.forEach(function(c) {
            if (!c.poly.length) return;
            var d=Math.sqrt((c.cx-w/2)*(c.cx-w/2)+(c.cy-h/2)*(c.cy-h/2))/maxD;
            var p=applyEase(clamp((t-d*0.45)/(1-0.45),0,1),'cubic-bezier(0.4, 0, 0.2, 1)');
            if (p<=0) return;
            ctx.save();
            ctx.translate(c.cx,c.cy); ctx.scale(p,p); ctx.translate(-c.cx,-c.cy);
            ctx.beginPath(); ctx.moveTo(c.poly[0][0],c.poly[0][1]);
            c.poly.slice(1).forEach(function(pt){ctx.lineTo(pt[0],pt[1]);}); ctx.closePath();
            ctx.clip();
            ctx.translate(c.cx,c.cy); ctx.scale(1/p,1/p); ctx.translate(-c.cx,-c.cy);
            ctx.globalAlpha=Math.min(1,p*2); ctx.drawImage(src,0,0,w,h); ctx.restore();
        });
    }, onDone);
};

P.warp_pinch = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var SH=3;
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        var wAmt=1-applyEase(t,'cubic-bezier(0.4, 0, 0.2, 1)');
        for (var sy=0; sy<h; sy+=SH) {
            var sh=Math.min(SH,h-sy), cy=sy+sh/2;
            var rel=Math.abs(cy/h-0.5)*2; 
            var scX=1-(1-rel)*wAmt*0.96;
            if (scX<0.01) continue;
            var dw=w*scX, dx=(w-dw)/2;
            ctx.drawImage(src,0,sy,w,sh,dx,sy,dw,sh);
        }
    }, onDone);
};

P.warp_swirl = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var SH=3, maxShift=w*0.42;
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        var wAmt=1-applyEase(t,'cubic-bezier(0.4, 0, 0.2, 1)');
        for (var sy=0; sy<h; sy+=SH) {
            var sh=Math.min(SH,h-sy), cy=sy+sh/2;
            var rel=(cy/h-0.5)*Math.PI*2;
            var shift=Math.sin(rel)*maxShift*wAmt;
            ctx.drawImage(src,0,sy,w,sh,shift,sy,w,sh);
        }
    }, onDone);
};

P.datamosh_sweep = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx=bounds?bounds.x:0, by=bounds?bounds.y:0, bw=bounds?bounds.w:w, bh2=bounds?bounds.h:h;
    var COLS=10, ROWS=5, tw=bw/COLS, th=bh2/ROWS;
    var tiles=[];
    for(var r=0;r<ROWS;r++){for(var c=0;c<COLS;c++){
        var wc=(c+Math.floor(rnd(1,COLS-1)))%COLS;
        var wr=(r+Math.floor(rnd(1,ROWS-1)))%ROWS;
        
        var tx2=bx+c*tw, ty2=by+r*th;
        
        var swx=bx+wc*tw, swy=by+wr*th;
        tiles.push({x:tx2,y:ty2,wx:swx,wy:swy,col:c});
    }}
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        var sweep=applyEase(t,ease)*(COLS+2)-1;
        tiles.forEach(function(tile) {
            ctx.save(); ctx.beginPath(); ctx.rect(tile.x,tile.y,tw+0.5,th+0.5); ctx.clip();
            if (tile.col < sweep-1) {
                ctx.drawImage(src,tile.x,tile.y,tw,th,tile.x,tile.y,tw,th);
            } else if (tile.col < sweep+0.5) {
                var frac=clamp(sweep-tile.col,0,1);
                ctx.globalAlpha=1-frac;
                ctx.drawImage(src,tile.wx,tile.wy,tw,th,tile.x,tile.y,tw,th);
                ctx.globalAlpha=frac;
                ctx.drawImage(src,tile.x,tile.y,tw,th,tile.x,tile.y,tw,th);
            } else {
                ctx.globalAlpha=0.85;
                ctx.drawImage(src,tile.wx,tile.wy,tw,th,tile.x,tile.y,tw,th);
            }
            ctx.restore();
        });
        if (t>0.02&&t<0.98) {
            var sx=bx+sweep*tw;
            var sg=ctx.createLinearGradient(sx-6,0,sx+6,0);
            sg.addColorStop(0,'rgba(255,255,255,0)');
            sg.addColorStop(0.5,'rgba(160,220,255,'+Math.min(1,1-applyEase(t,ease)).toFixed(3)+')');
            sg.addColorStop(1,'rgba(255,255,255,0)');
            ctx.fillStyle=sg; ctx.fillRect(sx-6,by,12,bh2);
        }
    }, onDone);
};

P.liquid_pour = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx=bounds?bounds.x:0, bw=bounds?bounds.w:w;
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        var p=applyEase(t,ease);
        var surfY=p*(h+12)-6;
        var step=6;
        
        ctx.save();
        ctx.beginPath();
        ctx.moveTo(bx, 0);
        for(var x=bx; x<=bx+bw; x+=step){
            var ripple=Math.sin((x-bx)/bw*Math.PI*3+t*8)*3.5*(1-p*0.7);
            ctx.lineTo(x, surfY+ripple);
        }
        ctx.lineTo(bx+bw, 0);
        ctx.closePath(); ctx.clip();
        ctx.drawImage(src,0,0,w,h);
        
        if(p<0.92){
            var sg=ctx.createLinearGradient(0,surfY-30,0,surfY+4);
            sg.addColorStop(0,'rgba(255,255,255,0)');
            sg.addColorStop(0.5,'rgba(220,230,240,'+(0.45*(1-p*0.5)).toFixed(3)+')');
            sg.addColorStop(1,'rgba(255,255,255,0.12)');
            ctx.fillStyle=sg; ctx.fillRect(bx,surfY-30,bw,36);
        }
        ctx.restore();
        
        if(p>0.02&&p<0.97){
            ctx.save();
            ctx.strokeStyle='rgba(255,255,255,'+(0.55*(1-p)).toFixed(3)+')';
            ctx.lineWidth=1.5; ctx.beginPath();
            for(var x=bx; x<=bx+bw; x+=step){
                var ry=surfY+Math.sin((x-bx)/bw*Math.PI*3+t*8)*3.5*(1-p*0.7);
                x===bx?ctx.moveTo(x,ry):ctx.lineTo(x,ry);
            }
            ctx.stroke(); ctx.restore();
        }
    }, onDone);
};

P.fiber_bundle = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx2=bounds?bounds.x:0, by2=bounds?bounds.y:0, bw2=bounds?bounds.w:w, bh3=bounds?bounds.h:h;
    var N=260, imgD=src.getContext('2d').getImageData(0,0,w,h), fibers=[];
    var gcols=Math.ceil(Math.sqrt(N*bw2/bh3)), grows=Math.ceil(N/gcols);
    for(var gr=0;gr<grows&&fibers.length<N;gr++){for(var gc=0;gc<gcols&&fibers.length<N;gc++){
        var tx=Math.round(bx2+(gc+0.5)/gcols*bw2), ty=Math.round(by2+(gr+0.5)/grows*bh3);
        var ii=(clamp(ty,0,h-1)*w+clamp(tx,0,w-1))*4;
        if(imgD.data[ii+3]<12)continue;
        
        var edge=Math.floor(Math.random()*4);
        var ex,ey;
        if(edge===0){ex=Math.random()*w;ey=-8;}
        else if(edge===1){ex=Math.random()*w;ey=h+8;}
        else if(edge===2){ex=-8;ey=Math.random()*h;}
        else{ex=w+8;ey=Math.random()*h;}
        
        var cp1=[lerp(ex,tx,0.3)+rnd(-w*0.15,w*0.15), lerp(ey,ty,0.3)+rnd(-h*0.15,h*0.15)];
        var cp2=[lerp(ex,tx,0.7)+rnd(-w*0.1,w*0.1),   lerp(ey,ty,0.7)+rnd(-h*0.1,h*0.1)];
        fibers.push({ex:ex,ey:ey,tx:tx,ty:ty,cp1:cp1,cp2:cp2,
            col:'rgba('+imgD.data[ii]+','+imgD.data[ii+1]+','+imgD.data[ii+2]+',',
            al:imgD.data[ii+3]/255,
            delay:Math.random()*0.5, r:rnd(0.8,1.8)});
    }}
    
    var bezPt=function(p,x0,y0,x1,y1,x2,y2,x3,y3){
        var q=1-p;
        return [q*q*q*x0+3*q*q*p*x1+3*q*p*p*x2+p*p*p*x3, q*q*q*y0+3*q*q*p*y1+3*q*p*p*y2+p*p*p*y3];
    };
    
    var fbImgD=src.getContext('2d').getImageData(0,0,w,h);
    var fbBgR=fbImgD.data[0],fbBgG=fbImgD.data[1],fbBgB=fbImgD.data[2];
    var fbAlpha=(function(){
        var ofc=document.createElement('canvas'); ofc.width=w; ofc.height=h;
        var oc=ofc.getContext('2d'); var id=oc.createImageData(w,h);
        for(var _i=0;_i<fbImgD.data.length;_i+=4){
            var isBg=fbImgD.data[_i+3]>200&&Math.abs(fbImgD.data[_i]-fbBgR)<10&&Math.abs(fbImgD.data[_i+1]-fbBgG)<10&&Math.abs(fbImgD.data[_i+2]-fbBgB)<10;
            id.data[_i]=id.data[_i+1]=id.data[_i+2]=255; id.data[_i+3]=isBg?0:fbImgD.data[_i+3];
        } oc.putImageData(id,0,0); return ofc;
    })();
    
    var landedId=ctx.createImageData(w,h);
    var srcId=fbImgD;
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        
        fibers.forEach(function(f) {
            var fp=applyEase(clamp((t-f.delay)/(1-0.5),0,1),ease);
            if(fp<0.98||f._landed)return;
            f._landed=true;
            
            var R=Math.ceil(f.r+0.5);
            for(var dy=-R;dy<=R;dy++){for(var dx=-R;dx<=R;dx++){
                if(dx*dx+dy*dy>R*R*1.2)continue;
                var px=clamp(Math.round(f.tx+dx),0,w-1), py=clamp(Math.round(f.ty+dy),0,h-1);
                var si=(py*w+px)*4;
                landedId.data[si]=srcId.data[si]; landedId.data[si+1]=srcId.data[si+1];
                landedId.data[si+2]=srcId.data[si+2]; landedId.data[si+3]=srcId.data[si+3];
            }}
        });
        
        ctx.putImageData(landedId,0,0);
        
        fibers.forEach(function(f) {
            if(f._landed)return;
            var fp=applyEase(clamp((t-f.delay)/(1-0.5),0,1),ease);
            if(fp<=0||fp>=0.98)return;
            var SEGS=12;
            ctx.save(); ctx.strokeStyle=f.col+(f.al*Math.min(1,fp*3)).toFixed(3)+')';
            ctx.lineWidth=f.r; ctx.beginPath();
            for(var s=0;s<=SEGS;s++){
                var bp=bezPt(s/SEGS*fp,f.ex,f.ey,f.cp1[0],f.cp1[1],f.cp2[0],f.cp2[1],f.tx,f.ty);
                s===0?ctx.moveTo(bp[0],bp[1]):ctx.lineTo(bp[0],bp[1]);
            }
            ctx.stroke(); ctx.restore();
        });
        
        var fp=applyEase(clamp((t-0.82)/0.18,0,1),'linear');
        if(fp>0){
            var ofc=document.createElement('canvas'); ofc.width=w; ofc.height=h;
            var oc=ofc.getContext('2d');
            oc.drawImage(src,0,0,w,h);
            oc.globalCompositeOperation='destination-in';
            oc.drawImage(fbAlpha,0,0);
            ctx.save(); ctx.globalAlpha=fp; ctx.drawImage(ofc,0,0,w,h); ctx.restore();
        }
    }, onDone);
};

P.dof_rack = function(cv, ctx, src, w, h, dur, ease, onDone) {
    
    var scales=[0.03,0.07,0.15,0.35,1];
    var blurs=scales.map(function(sc) {
        var ofc=document.createElement('canvas'); ofc.width=w; ofc.height=h;
        var oc=ofc.getContext('2d');
        oc.imageSmoothingEnabled=true; oc.imageSmoothingQuality='low';
        oc.drawImage(src,0,0,Math.max(1,Math.round(w*sc)),Math.max(1,Math.round(h*sc)));
        var ofc2=document.createElement('canvas'); ofc2.width=w; ofc2.height=h;
        var oc2=ofc2.getContext('2d');
        oc2.imageSmoothingEnabled=true; oc2.imageSmoothingQuality='low';
        oc2.drawImage(ofc,0,0,Math.round(w*sc),Math.round(h*sc),0,0,w,h);
        return ofc2;
    });
    return rafLoop(dur*1.5, function(t) {
        ctx.clearRect(0,0,w,h);
        var p=applyEase(t,ease);
        var fPos=p*(blurs.length-1);
        var fi=Math.min(Math.floor(fPos),blurs.length-2);
        var blend=fPos-fi;
        ctx.imageSmoothingEnabled=true;
        ctx.globalAlpha=1-blend; ctx.drawImage(blurs[fi],0,0,w,h);
        ctx.globalAlpha=blend;   ctx.drawImage(blurs[fi+1],0,0,w,h);
        ctx.globalAlpha=1;
    }, onDone);
};

P.dof_tilt = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx=bounds?bounds.x:0, by=bounds?bounds.y:0, bw=bounds?bounds.w:w, bh2=bounds?bounds.h:h;
    
    var blurred=(function(){
        var o1=document.createElement('canvas'); o1.width=w; o1.height=h;
        var c1=o1.getContext('2d');
        c1.imageSmoothingEnabled=true; c1.imageSmoothingQuality='low';
        c1.drawImage(src,0,0,Math.max(1,Math.round(w*0.06)),Math.max(1,Math.round(h*0.06)));
        var o2=document.createElement('canvas'); o2.width=w; o2.height=h;
        var c2=o2.getContext('2d');
        c2.imageSmoothingEnabled=true;
        c2.drawImage(o1,0,0,Math.round(w*0.06),Math.round(h*0.06),0,0,w,h);
        return o2;
    })();
    var SH=2, cx=bx+bw/2;
    return rafLoop(dur*1.5, function(t) {
        ctx.clearRect(0,0,w,h);
        var p=applyEase(t,ease);
        
        var angle=(1-p)*Math.PI*0.4;
        var cosA=Math.cos(angle);
        var blurFrac=clamp(1-p*2.2,0,1); 
        
        for(var sy=by; sy<by+bh2; sy+=SH){
            var sh=Math.min(SH,by+bh2-sy);
            if(sh<=0) break;
            var scaledW=bw*cosA, offX=cx-scaledW/2;
            if(scaledW<1) continue;
            ctx.save();
            
            if(blurFrac>0.01){
                ctx.globalAlpha=blurFrac;
                ctx.drawImage(blurred,bx,sy,bw,sh,offX,sy,scaledW,sh);
            }
            ctx.globalAlpha=Math.max(0,1-blurFrac*0.9);
            ctx.drawImage(src,bx,sy,bw,sh,offX,sy,scaledW,sh);
            ctx.restore();
        }
    }, onDone);
};

P.noise_elastic = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var SH=3, maxShift=w*0.14;
    return rafLoop(dur*1.3, function(t) {
        ctx.clearRect(0,0,w,h);
        
        var osc=Math.sin(t*Math.PI*5)*Math.exp(-t*3.8);
        for(var sy=0;sy<h;sy+=SH){
            var sh2=Math.min(SH,h-sy), cy2=sy+sh2/2;
            var ny=cy2/h;
            var dx=Math.sin(ny*Math.PI*3.2+1.1)*maxShift*osc;
            ctx.drawImage(src,0,sy,w,sh2,dx,sy,w,sh2);
        }
    }, onDone);
};

P.matrix_rain = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var CHARS='01アイウエオカキクケコABCDEF#$%@*';
    var bx=bounds?bounds.x:0, bw=bounds?bounds.w:w;
    var COL_W=Math.max(10,Math.round(bw/Math.ceil(bw/12)));
    var COLS=Math.ceil(bw/COL_W);
    var CH=Math.max(9,Math.round(h/Math.ceil(h/12)));
    var cols2=[];
    for(var c=0;c<COLS;c++){
        cols2.push({x:bx+c*COL_W, lockTime:0.15+Math.random()*0.7,
            speed:rnd(0.5,1.2), offset:Math.random()*CHARS.length, wakes:[]});
    }
    return rafLoop(dur, function(t) {
        ctx.clearRect(0,0,w,h);
        ctx.font=CH+'px monospace';
        cols2.forEach(function(col) {
            var locked=t>=col.lockTime;
            var rows=Math.ceil(h/CH)+2;
            if(!locked){
                var scroll=(t*col.speed*CHARS.length*1.8+col.offset);
                for(var row=0;row<rows;row++){
                    var ch=CHARS[Math.floor(scroll+row)%CHARS.length];
                    var fade=Math.max(0,(rows-row)/(rows))*0.9;
                    var bright=row===rows-1?1:0.3;
                    ctx.fillStyle='rgba(60,220,100,'+( fade*bright).toFixed(2)+')';
                    ctx.fillText(ch, col.x, row*CH);
                }
            } else {
                
                var flashP=applyEase(clamp((t-col.lockTime)/0.12,0,1),ease);
                ctx.save();
                ctx.beginPath(); ctx.rect(col.x,0,COL_W,h); ctx.clip();
                ctx.drawImage(src,0,0,w,h);
                if(flashP<1){ctx.globalAlpha=(1-flashP)*0.55;ctx.fillStyle='rgba(60,255,80,1)';ctx.fillRect(col.x,0,COL_W,h);}
                ctx.restore();
            }
        });
    }, onDone);
};

function maskReveal(shapeFn, cv, ctx, src, w, h, dur, ease, onDone, maxROverride) {
    var maxR = maxROverride || ((w + h) / 2 * 1.15);
    dur = dur * 4;
    return rafLoop(dur, function(t) {
        ctx.clearRect(0, 0, w, h);
        var p = applyEase(t, ease);
        var r = p * maxR;
        ctx.save();
        ctx.beginPath();
        shapeFn(ctx, w/2, h/2, r);
        ctx.clip();
        ctx.drawImage(src, 0, 0, w, h);
        ctx.restore();
    }, onDone);
}

P.mask_iris = function(cv, ctx, src, w, h, dur, ease, onDone) {
    return maskReveal(function(c, cx, cy, r) {
        c.arc(cx, cy, r, 0, Math.PI * 2);
    }, cv, ctx, src, w, h, dur, ease, onDone);
};

P.drop_spring = function(cv, ctx, src, w, h, dur, ease, onDone, bounds) {
    var bx = bounds ? bounds.x : 0, by = bounds ? bounds.y : 0;
    var bw = bounds ? bounds.w : w,  bh2 = bounds ? bounds.h : h;

    var startOff = -(by + bh2 + 40);
    
    cv.parentNode.style.overflow = 'visible';

    return rafLoop(dur * 1.5, function(t) {
ctx.clearRect(0, 0, w, h);

var sp = springEval(t, 0.28, 10);
var yOff = startOff * (1 - sp);  

var alpha = Math.min(1, t * 6);

cv.style.transform = 'translateY(' + yOff + 'px)';

ctx.save();
ctx.globalAlpha = alpha;
ctx.drawImage(src, 0, 0, w, h);
ctx.restore();

var shadowY = by + bh2 + 3 - yOff; 
var dist = Math.abs(yOff);
var shadowA = clamp(1 - dist / (Math.abs(startOff) * 0.9), 0, 1) * 0.28 * alpha;
var shadowW = bw * (0.55 + (1 - dist / Math.abs(startOff)) * 0.25);
if (shadowA > 0.01) {
    ctx.save();
    ctx.globalAlpha = shadowA;
    var sg = ctx.createRadialGradient(bx + bw/2, shadowY, 0, bx + bw/2, shadowY, shadowW/2);
    sg.addColorStop(0, 'rgba(0,0,0,0.7)');
    sg.addColorStop(1, 'rgba(0,0,0,0)');
    ctx.fillStyle = sg;
    ctx.beginPath();
    ctx.ellipse(bx + bw/2, shadowY, shadowW/2, 6, 0, 0, Math.PI * 2);
    ctx.fill();
    ctx.restore();
}
    }, onDone);
};

P.neon_ignite = function(cv, ctx, src, w, h, dur, ease, onDone) {
    var M = 80;

    

    var _nDpr = cv._laDPR || 1;
    cv.width  = w + M*2; cv.height = h + M*2;
    cv.style.left   = (-M / _nDpr) + 'px';
    cv.style.top    = (-M / _nDpr) + 'px';
    cv.style.width  = ((w+M*2) / _nDpr) + 'px';
    cv.style.height = ((h+M*2) / _nDpr) + 'px';
    cv.parentNode.style.overflow = 'visible';
    var W = w+M*2, H = h+M*2;

    var imgD=src.getContext('2d').getImageData(0,0,w,h);
    var rS=0,gS=0,bS=0,n=0;
    for(var i=0;i<imgD.data.length;i+=20){
        var a=imgD.data[i+3]; if(a<30) continue;
        rS+=imgD.data[i]; gS+=imgD.data[i+1]; bS+=imgD.data[i+2]; n++;
    }
    var r=n?Math.round(rS/n):255, g=n?Math.round(gS/n):180, b=n?Math.round(bS/n):80;
    var mx=Math.max(r,g,b), mn=Math.min(r,g,b), mid=(mx+mn)/2;
    function bst(c){ return Math.min(255,Math.round(mid+(c-mid)*2.4)); }
    var gr=bst(r), gg=bst(g), gb=bst(b);

    var fl=[0,.04,.02,.08,.18,.06,.3,.1,.45,.12,.6,.18,.72,.25,.65,.38,.8,.28,.9,.55,.87,.72,.95,.88,1];

    return rafLoop(1e9, function(t) {
ctx.clearRect(0,0,W,H);

var warmT = clamp(t / (dur/1e9 * 1.3), 0, 1);  

var ms     = t * 1e9;  
var warmMs = dur * 1.3;
var wP     = clamp(ms / warmMs, 0, 1); 

if(wP < 0.25){
    var coldA = clamp(wP/0.2, 0, 1) * 0.22;
    ctx.save(); ctx.globalAlpha=coldA; ctx.filter='grayscale(1) brightness(0.3)';
    ctx.drawImage(src,M,M,w,h); ctx.restore();
    return;
}

var ignP = clamp((wP-0.22)/0.43, 0, 1);
var fi=ignP*(fl.length-2), fiI=Math.floor(fi);
var flickBase = fl[fiI]+(fl[fiI+1]-fl[fiI])*(fi-fiI);

var setP = clamp((wP-0.60)/0.40, 0, 1);

var sec = ms / 1000;
var organicFlicker = 0.88
    + 0.055 * Math.sin(sec * 7.3)
    + 0.035 * Math.sin(sec * 17.1 + 1.2)
    + 0.025 * Math.sin(sec * 31.7 + 2.4)
    + 0.018 * Math.sin(sec * 53.9 + 0.7);

var sputter = (Math.abs(Math.sin(sec * 2.3 + 0.5)) > 0.94) ? 0.65 : 1.0;
organicFlicker *= sputter;

var gAlpha = Math.max(0, flickBase * (1-setP) + organicFlicker * setP);

ctx.save();
ctx.shadowColor='rgba('+gr+','+gg+','+gb+',0.7)'; ctx.shadowBlur=45*gAlpha;
ctx.globalAlpha=gAlpha*0.55; ctx.drawImage(src,M,M,w,h); ctx.restore();
ctx.save();
ctx.shadowColor='rgba('+gr+','+gg+','+gb+',0.9)'; ctx.shadowBlur=12*gAlpha;
ctx.globalAlpha=gAlpha*0.75; ctx.drawImage(src,M,M,w,h); ctx.restore();
ctx.save(); ctx.globalAlpha=Math.min(1,gAlpha); ctx.drawImage(src,M,M,w,h); ctx.restore();
if(setP>0.5){
    ctx.save(); ctx.shadowColor='rgba(255,255,255,0.55)'; ctx.shadowBlur=3*setP;
    ctx.globalAlpha=0.18*setP; ctx.drawImage(src,M,M,w,h); ctx.restore();
}
    }, onDone);
};

var _FX_SVG_NS = 'http://www.w3.org/2000/svg';
var _fxFilterSeq = 0;
function _buildFxFilter(type, freq, flow, octaves) {
    var id  = 'impulse-la-fx' + (_fxFilterSeq++);
    var svg = document.createElementNS(_FX_SVG_NS, 'svg');
    svg.setAttribute('width', '0'); svg.setAttribute('height', '0');
    svg.style.cssText = 'position:absolute;width:0;height:0;overflow:visible;pointer-events:none';
    
    var flowFe = flow > 0
        ? '<feOffset in="turb" result="turb" dx="0" dy="0">' +
            '<animate attributeName="dx" values="0;26;0;-26;0" dur="' + flow + 's" repeatCount="indefinite" calcMode="linear"/>' +
            '<animate attributeName="dy" values="-26;0;26;0;-26" dur="' + flow + 's" repeatCount="indefinite" calcMode="linear"/>' +
          '</feOffset>'
        : '';
    svg.innerHTML =
        '<filter id="' + id + '" x="-50%" y="-50%" width="200%" height="200%">' +
          '<feTurbulence type="' + type + '" baseFrequency="' + freq + '" ' +
            'numOctaves="' + (octaves || 2) + '" seed="6" stitchTiles="stitch" result="turb"/>' +
          flowFe +
          '<feGaussianBlur in="SourceGraphic" stdDeviation="0" result="src"/>' +
          '<feDisplacementMap in="src" in2="turb" scale="0" ' +
            'xChannelSelector="R" yChannelSelector="G"/>' +
        '</filter>';
    document.body.appendChild(svg);
    return { id: id, svg: svg,
             disp: svg.querySelector('feDisplacementMap'),
             blur: svg.querySelector('feGaussianBlur') };
}

function _fxElasticOut(t) {
    if (t <= 0) return 0;
    if (t >= 1) return 1;
    var per = 0.42;
    return Math.pow(2, -9 * t) * Math.sin((t - per / 4) * (2 * Math.PI) / per) + 1;
}

function _makeFxPreset(spec) {
    return function(cv, ctx, src, w, h, dur, ease, onDone) {
        ctx.clearRect(0, 0, w, h);
        ctx.drawImage(src, 0, 0, w, h);
        var f = _buildFxFilter(spec.type, spec.freq, spec.flow, spec.octaves);
        var prevFilter    = cv.style.filter    || '';
        var prevOpacity   = cv.style.opacity   || '';
        var prevTransform = cv.style.transform || '';
        function cleanup() {
            cv.style.filter    = prevFilter;
            cv.style.opacity   = prevOpacity;
            cv.style.transform = prevTransform;
            if (f.svg.parentNode) f.svg.parentNode.removeChild(f.svg);
        }
        cv.style.filter = 'url(#' + f.id + ')';
        var stop = rafLoop(dur, function(t) {
            
            var prog = spec.spring ? _fxElasticOut(t) : applyEase(t, ease);
            var inv  = 1 - prog;
            f.disp.setAttribute('scale', (spec.disp * inv).toFixed(1));
            
            if (spec.blur) f.blur.setAttribute('stdDeviation', (spec.blur * Math.max(0, inv)).toFixed(2));
            if (spec.drop) cv.style.transform = prevTransform + ' translateY(' + (spec.drop * inv).toFixed(1) + 'px)';
            
            cv.style.opacity = Math.min(1, t * 3.5);
            if (t >= 1) cleanup();
        }, onDone);
        
        return function() { stop(); cleanup(); };
    };
}
P.fx_noise   = _makeFxPreset({ type:'fractalNoise', freq:0.022, disp:80,  blur:0, flow:0,  drop:0,  spring:false });

P.fx_ripple  = _makeFxPreset({ type:'fractalNoise', freq:0.011, disp:58,  blur:0, flow:8,  drop:0,  spring:false });

P.fx_liquid  = _makeFxPreset({ type:'fractalNoise', freq:0.005, disp:108, blur:9, flow:10, drop:0,  spring:true  });
P.fx_shatter = _makeFxPreset({ type:'turbulence',   freq:0.045, disp:110, blur:0, flow:0,  drop:0,  spring:false });
P.fx_static  = _makeFxPreset({ type:'fractalNoise', freq:0.55,  disp:26,  blur:0, flow:0,  drop:0,  spring:false });
P.fx_melt    = _makeFxPreset({ type:'fractalNoise', freq:0.013, disp:72,  blur:5, flow:0,  drop:44, spring:false });

P.fx_glass   = _makeFxPreset({ type:'fractalNoise', freq:0.014, disp:66,  blur:3, flow:0,  drop:0,   spring:false, octaves:1 });

P.fx_wave    = _makeFxPreset({ type:'fractalNoise', freq:'0.004 0.022', disp:56, blur:0, flow:6, drop:0, spring:false });

P.fx_smoke   = _makeFxPreset({ type:'fractalNoise', freq:0.009, disp:74,  blur:7, flow:7,  drop:-40, spring:false, octaves:3 });

P.fx_storm   = _makeFxPreset({ type:'turbulence',   freq:0.028, disp:128, blur:0, flow:9,  drop:0,   spring:false, octaves:4 });

P.fx_warp    = _makeFxPreset({ type:'fractalNoise', freq:0.004, disp:124, blur:0, flow:0,  drop:0,   spring:true });

function applyAssetSvgColors(asset, colorsJson) {
    try {
        var map = typeof colorsJson === 'string' ? JSON.parse(colorsJson) : colorsJson;
        if (!map || !Object.keys(map).length) return;
    } catch(e) { return; }
    
    var _hasChange = Object.keys(map).some(function(o) {
        var r = map[o]; return r && r.toLowerCase() !== o.toLowerCase();
    });
    if (!_hasChange) return;
    var origSrc = asset._svgOrigSrc || asset.src || asset.getAttribute('src') || '';
    if (!origSrc || !/\.svg(\?|$)/i.test(origSrc)) return;
    function _esc(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }
    function _doApply(svgText) {
        var result = svgText;
        Object.keys(map).forEach(function(orig) {
            var rep = map[orig];
            if (!rep || rep.toLowerCase() === orig.toLowerCase()) return;
            
            var e = _esc(orig);
            
            result = result.replace(new RegExp('(=["\'])' + e + '(["\'])', 'gi'), '$1' + rep + '$2');
            
            result = result.replace(new RegExp(':' + e + '([;} ])', 'gi'), ':' + rep + '$1');
        });
        var blob = new Blob([result], { type: 'image\/svg+xml' });
        if (asset._svgBlobUrl) URL.revokeObjectURL(asset._svgBlobUrl);
        asset._svgBlobUrl = URL.createObjectURL(blob);
        asset.src = asset._svgBlobUrl;
    }
    if (asset._svgSourceCache) {
        _doApply(asset._svgSourceCache);
    } else {
        fetch(origSrc)
            .then(function(r) { return r.text(); })
            .then(function(svgText) {
                asset._svgSourceCache = svgText;
                asset._svgOrigSrc     = origSrc;
                _doApply(svgText);
            })
            .catch(function() {});
    }
}

function quickRedraw(container) {
    var cv     = container.querySelector('.impulse-la-canvas');
    var asset  = container.querySelector('.impulse-la-asset');
    if (!cv || !asset || asset.tagName === 'IMG') return;

    var bounds = container._laBounds;
    if (!bounds) return;

    var ctx2   = cv.getContext('2d');
    
    var w = cv.width, h = cv.height;
    
    var cw = cv._laCssW || container.offsetWidth  || (w / (cv._laDPR || 1));
    var ch = cv._laCssH || container.offsetHeight || (h / (cv._laDPR || 1));
    var bgColor = window.getComputedStyle(container).backgroundColor;

    var gradFrom  = container.dataset.gradientFrom || container.dataset.color || '';
    var gradTo    = container.dataset.gradientTo   || '';
    var gradAngle = _laDV(container, 'gradient-angle', 135);
    var opts = (gradFrom && gradTo) ? {gradFrom:gradFrom, gradTo:gradTo, gradAngle:gradAngle} : null;

    var ah2 = asset.offsetHeight || ch;
    var by2 = Math.max(0, Math.round((ch - ah2) / 2));
    bounds = { x: 0, y: by2, w: cw, h: Math.min(ah2, ch), bg: bgColor };
    container._laBounds = bounds;

    var newSrc = sampleLogo(asset, cw, ch, bgColor, bounds, opts);
    ctx2.clearRect(0, 0, w, h);
    
    ctx2.drawImage(newSrc, 0, 0);

    container._laSrc = newSrc;

    if (container._laGradOpts) {
        container._laGradOpts.gradFrom  = gradFrom  || container._laGradOpts.gradFrom;
        container._laGradOpts.gradTo    = gradTo    || container._laGradOpts.gradTo;
        container._laGradOpts.gradAngle = gradAngle;
    }
}

function ensureFontLoaded(asset, cb) {
    if (asset.tagName === 'IMG') { cb(); return; }
    if (!document.fonts) { cb(); return; }
    var cs = window.getComputedStyle(asset);
    var spec = cs.fontWeight + ' ' + cs.fontSize + ' ' + cs.fontFamily;
    try { document.fonts.load(spec).then(cb, cb); }
    catch (e) { cb(); }
}

function runAnimator(container) {
    
    if (document.body.getAttribute('data-screen-mode') === '1' && !container._laPreTrigger) {
        var _scEl = container.closest && (container.closest('.impulse-screen') || container.closest('[data-type="section"]'));
        if (_scEl && !_scEl.classList.contains('impulse-screen-active')) return;
    }
    container._laPreTrigger = false;
    
    container._laRunQueued = true;
    var asset = container.querySelector('.impulse-la-asset');
    if (!asset) { container._laRunQueued = false; return; }

    
    
    container._laLastBuiltPreset = container.dataset.preset || '';

    if (container._laCancel)     { container._laCancel(); container._laCancel = null; }
    if (container._laDelayTimer) { clearTimeout(container._laDelayTimer); container._laDelayTimer = null; }
    if (container._la3dCancel)   { container._la3dCancel(); container._la3dCancel = null; }
    if (container._laGradRaf)    { cancelAnimationFrame(container._laGradRaf); container._laGradRaf = null; }
    if (container._laGradScopeCleanup) { container._laGradScopeCleanup(); container._laGradScopeCleanup = null; }
    container._laGradGen = (container._laGradGen || 0) + 1; 
    container._laPreCanvas = null; container._laPreSrc = null; container._laPreWarm = false;
    container._laSettled = false;

    var preset   = container.dataset.preset    || 'build_columns';
    if (preset.indexOf('3d_') === 0) { run3DAnimator(container); return; }
    var dur      = _laDV(container, 'duration', 1400);
    var delay    = _laDV(container, 'delay', 0);
    var ease     = container.dataset.easing    || 'ease-in-out';

    asset.style.opacity = '0';

    var _oldCv = container.querySelector('.impulse-la-canvas');
    if (_oldCv) { var _oc = _oldCv.getContext('2d'); _oc.clearRect(0, 0, _oldCv.width, _oldCv.height); }

    ensureFontLoaded(asset, function() {
        if (delay > 0 && !container._laSkipDelay) {
            container._laDelayTimer = setTimeout(start, delay);
        } else {
            container._laSkipDelay = false;
            start();
        }
    });

    function _computeBounds(cw, ch, textW) {
        
        var ah = asset.offsetHeight || ch;
        var by = Math.max(0, Math.round((ch - ah) / 2));
        var bh = Math.min(ah, ch);
        var jc = window.getComputedStyle(container).justifyContent;
        var b = { x: 0, y: by, w: cw, h: bh };
        if (textW && textW > 0 && textW < cw) {
            var tx;
            if (jc === 'flex-start')      tx = 0;
            else if (jc === 'flex-end')   tx = cw - textW;
            else                           tx = (cw - textW) / 2;
            b.textX = Math.round(tx);
            b.textW = Math.round(textW);
        }
        return b;
    }

    function start() {
        var cw = container.offsetWidth  || 400;
        var ch = container.offsetHeight || 200;
        if (cw < 10 || ch < 10) return;

        

        var _measuredTextW = 0;
        if (asset.tagName !== 'IMG') {
            var _cs    = window.getComputedStyle(asset);
            var _fSize = parseFloat(_cs.fontSize) || 64;

            

            container._laRasterFS = Math.round(_fSize);
            var _fFam  = _cs.fontFamily   || 'sans-serif';
            var _fWt   = _cs.fontWeight   || '700';
            
            var _txt = (function _readPreT(a){
                if (!a.childNodes || !a.childNodes.length) return (a.textContent || '');
                var t = '';
                for (var i = 0; i < a.childNodes.length; i++) {
                    var n = a.childNodes[i];
                    if (n.nodeType === 3) t += n.nodeValue;
                    else if (n.nodeName === 'BR') t += '\n';
                    else if (n.textContent) t += n.textContent;
                }
                return t;
            })(asset).replace(/\\n/g, '\n').trim();
            if (_txt) {
                var _mc = document.createElement('canvas');
                var _mx = _mc.getContext('2d');
                _mx.font = _fWt + ' ' + Math.round(_fSize) + 'px ' + _fFam;
                var _maxW = cw * 0.96;
                var _raw = _txt.split('\n');
                var _lc = 0;
                var _widestLineW = 0;
                for (var _r = 0; _r < _raw.length; _r++) {
                    var _words = _raw[_r].split(/\s+/);
                    var _line = '';
                    for (var _j = 0; _j < _words.length; _j++) {
                        var _test = _line ? _line + ' ' + _words[_j] : _words[_j];
                        if (_mx.measureText(_test).width > _maxW && _line) {
                            
                            var _wLineW = _mx.measureText(_line).width;
                            if (_wLineW > _widestLineW) _widestLineW = _wLineW;
                            _lc++;
                            _line = _words[_j];
                        } else {
                            _line = _test;
                        }
                    }
                    if (_line) {
                        var _wLineW2 = _mx.measureText(_line).width;
                        if (_wLineW2 > _widestLineW) _widestLineW = _wLineW2;
                        _lc++;
                    }
                }
                if (_lc < 1) _lc = 1;
                _measuredTextW = _widestLineW;
                var _needH = Math.ceil(_lc * _fSize * 1.15 + _fSize * 0.5);
                if (_needH > ch) {
                    ch = _needH;
                    
                    container.style.minHeight = _needH + 'px';
                }
            }
        }

        var cv  = makeCanvas(container, cw, ch);
        
        var w   = cv.width;
        var h   = cv.height;
        var ctx = cv.getContext('2d');

        var bgColor = window.getComputedStyle(container).backgroundColor;
        var bounds  = _computeBounds(cw, ch, _measuredTextW);
        bounds.bg   = bgColor;
        container._laBounds = bounds;

        

        
        
        var _bDpr = cv._laDPR || 1;
        var _devBounds = { x: bounds.x * _bDpr, y: bounds.y * _bDpr,
                           w: bounds.w * _bDpr, h: bounds.h * _bDpr, bg: bounds.bg };

        
        if (bounds.textW > 0) {
            _devBounds.textX = bounds.textX * _bDpr;
            _devBounds.textW = bounds.textW * _bDpr;
        }

        var _gradFrom  = container.dataset.gradientFrom || container.dataset.color || '';
        var _gradTo    = container.dataset.gradientTo    || '';
        var _gradAngle = _laDV(container, 'gradient-angle', 0);
        container._laGradOpts = (_gradFrom && _gradTo)
            ? { gradFrom:_gradFrom, gradTo:_gradTo, gradAngle:_gradAngle,
                fSize:   parseFloat(window.getComputedStyle(asset).fontSize)||64,
                fFamily: window.getComputedStyle(asset).fontFamily||'sans-serif',
                fWeight: window.getComputedStyle(asset).fontWeight||'700',
                text:    _laText(asset)||'' }
            : null;

        
        var src = sampleLogo(asset, cw, ch, bgColor, bounds,
            (_gradFrom && _gradTo) ? { gradFrom:_gradFrom, gradTo:_gradTo, gradAngle:_gradAngle } : null);

        asset.style.opacity = '0';

        var fn = P[preset];
        if (!fn) { asset.style.opacity = '1'; return; } 

        container._laSrc = src;
        
        function playOnce() {
            ctx.clearRect(0, 0, w, h);
            ctx.globalAlpha = 1;
            ctx.globalCompositeOperation = 'source-over';
            ctx.setTransform(1, 0, 0, 1, 0, 0);
            container._laCancel = fn(cv, ctx, container._laSrc || src, w, h, dur, ease, function() {
                container._laSettled = true;

                
                
                finishAnim(cv, asset);
            }, _devBounds);
        }
        playOnce();
    }
}

function preWarmAnimator(container) {
    if (container._laPreWarm) return;
    var asset = container.querySelector('.impulse-la-asset');
    if (!asset) return;
    
    if (window._impulseSoftBackReveal === true) {
        asset.style.transition = 'opacity 280ms ease-in-out';
        asset.style.opacity = '1';
        container._laSettled = true;
        return;
    }
    var preset = container.dataset.preset || '';
    if (!preset) return;
    container._laPreWarm = true;

    var _sc = container.closest && container.closest('.impulse-screen');
    var _canvasVisible = !_sc
        || _sc.classList.contains('impulse-screen-active')
        || _sc.classList.contains('impulse-screen-entering');
    if (!_canvasVisible) {
        
        ensureFontLoaded(asset, function() {});
        return;
    }

    container._laRunQueued = true;
    
    container._laPreTrigger = true;
    ensureFontLoaded(asset, function() {
        
        if (!container._laPreTrigger) return;
        if (container._laCancel && !container._laSettled) return;
        if (preset.indexOf('3d_') === 0) {
            run3DAnimator(container);
        } else {
            runAnimator(container);
        }
    });
}
function run3DAnimator(container) {
    
    if (window._impulseWebGLOK === false) {
        var _asset0 = container.querySelector('.impulse-la-asset');
        if (_asset0) _asset0.style.opacity = '1';
        return;
    }

    if (document.body.getAttribute('data-screen-mode') === '1' && !container._laPreTrigger) {
        var _scEl3 = container.closest && (container.closest('.impulse-screen') || container.closest('[data-type="section"]'));
        if (_scEl3 && !_scEl3.classList.contains('impulse-screen-active')) return;
    }
    container._laPreTrigger = false;

    if (!window.THREE) {
        
        _ensureThree(function() { run3DAnimator(container); });
        return;
    }
    
    var asset = container.querySelector('.impulse-la-asset');
    if (!asset) return;
    if (container._la3dRaf)      { cancelAnimationFrame(container._la3dRaf); container._la3dRaf = null; }

    
    
    if (container._la3dRenderer) {
        try { container._la3dRenderer.forceContextLoss(); } catch(_e) {}
        container._la3dRenderer.dispose();
        container._la3dRenderer = null;
    }
    var oldCv = container.querySelector('.impulse-la-3d-canvas');
    if (oldCv) oldCv.parentNode.removeChild(oldCv);
    var old2d = container.querySelector('.impulse-la-canvas');
    if (old2d) old2d.parentNode.removeChild(old2d);

    var preset  = container.dataset.preset || '';
    var dur     = _laDV(container, 'duration', 1400);
    
    var delay = (document.body.getAttribute('data-screen-mode') === '1') ? 0 :
        (container._laSkipDelay ? 0 : _laDV(container, 'delay', 0));
    container._laSkipDelay = false;
    asset.style.opacity = '0';

    var cw = container.offsetWidth  || 400;
    var ch = container.offsetHeight || 200;
    var dpr = Math.min(window.devicePixelRatio || 1, 2);

    var aw = asset.scrollWidth || asset.offsetWidth || cw;
    var ah = asset.offsetHeight || ch;
    
    var _hpad = Math.ceil((parseFloat(window.getComputedStyle(asset).fontSize) || 64) * 0.25);
    var ofc = document.createElement('canvas');
    ofc.width = (aw + _hpad * 2) * dpr; ofc.height = ah * dpr;
    var ofx = ofc.getContext('2d');
    ofx.scale(dpr, dpr);
    if (asset.tagName === 'IMG') {
        try { ofx.drawImage(asset, _hpad, 0, aw, ah); } catch(e) {}
    } else {
        var _cs = window.getComputedStyle(asset);
        var _fontSpec = (_cs.fontWeight||'700') + ' ' + (parseFloat(_cs.fontSize)||64) + 'px ' + (_cs.fontFamily||'sans-serif');
        ofx.font = _fontSpec;
        ofx.textAlign = 'center'; ofx.textBaseline = 'middle';

        

        

        

        var _3dGradFrom  = container.dataset.gradientFrom || container.dataset.color || '';
        var _3dGradTo    = container.dataset.gradientTo   || '';
        var _3dGradAngle = parseFloat(container.dataset.gradientAngle || '135');
        var _txC = (aw + _hpad * 2) / 2;
        var _tyC = ah / 2;
        if (_3dGradFrom && _3dGradTo) {

            
            
            var _tw = ofx.measureText(asset.textContent || '').width;
            var _gLen = (_tw > 0 ? _tw : aw) * 0.5;
            var _rad = _3dGradAngle * Math.PI / 180;
            var _grd = ofx.createLinearGradient(
                _txC - Math.cos(_rad) * _gLen, _tyC - Math.sin(_rad) * _gLen,
                _txC + Math.cos(_rad) * _gLen, _tyC + Math.sin(_rad) * _gLen);
            _grd.addColorStop(0, _3dGradFrom);
            _grd.addColorStop(1, _3dGradTo);
            ofx.fillStyle = _grd;
        } else {

            
            
            var _3dSolid = container.dataset.color || _cs.color;

            
            if (!_3dSolid || _3dSolid === 'rgba(0, 0, 0, 0)' || _3dSolid === 'transparent') {
                _3dSolid = '#ffffff';
            }
            ofx.fillStyle = _3dSolid;
        }
        ofx.fillText(asset.textContent || '', _txC, _tyC);
    }

    
    
    container._la3dSrc = ofc;
    var tex = new THREE.CanvasTexture(ofc);
    tex.minFilter = tex.magFilter = THREE.LinearFilter;
    tex.generateMipmaps = false;

    

    

    
    
    if (THREE.SRGBColorSpace) tex.colorSpace = THREE.SRGBColorSpace;
    tex.needsUpdate = true;

    var perspective = 800;
    var fov    = 2 * Math.atan(ch / 2 / perspective) * (180 / Math.PI);
    var scene  = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(fov, cw / ch, 1, 8000);
    camera.position.z = perspective;

    var canvas3d = document.createElement('canvas');
    canvas3d.className = 'impulse-la-3d-canvas';
    canvas3d.setAttribute('data-la-id', container.id || '');
    
    (function(){ var _a = container; for(var _i=0;_i<6&&_a&&_a!==document.body;_i++,_a=_a.parentNode) _a.style.overflow='visible'; })();

    

    canvas3d.style.cssText = 'position:absolute;top:0;left:50%;transform:translateX(-50%);pointer-events:none;z-index:5;visibility:hidden';
    container.appendChild(canvas3d);
    var renderer;
    try { renderer = new THREE.WebGLRenderer({ canvas: canvas3d, alpha: true, antialias: true }); }
    catch(e) {
        
        if (window._impulseWebGLOK !== false) {
            window._impulseWebGLOK = false;
            if (window.console && console.info) {
                console.info('[Impulse] WebGL unavailable — 3D engines fall back to flat rendering for the rest of this session.');
            }
        }
        var _assetFail = container.querySelector('.impulse-la-asset');
        if (_assetFail) _assetFail.style.opacity = '1';
        return;
    }
    renderer.setPixelRatio(dpr);
    renderer.setSize(cw, ch);
    renderer.setClearColor(0x000000, 0);
    canvas3d.style.width = cw + 'px'; canvas3d.style.height = ch + 'px';
    container._la3dRenderer = renderer;

    var _texW = aw + _hpad * 2;
    var mat   = new THREE.MeshBasicMaterial({ map:tex, transparent:true, depthWrite:false, side:THREE.DoubleSide });
    var plane = new THREE.Mesh(new THREE.PlaneGeometry(_texW, ah), mat);
    scene.add(plane);
    
    var _lfPlanes = [];
    if (preset === '3d_lightfield') {
        var _lfOffsets = [[-12,-6,-320,0.18],[8,4,-180,0.14],[-5,8,-100,0.12],[6,-4,-240,0.16]];
        _lfOffsets.forEach(function(o) {
            var gm = new THREE.MeshBasicMaterial({map:tex,transparent:true,depthWrite:false,side:THREE.DoubleSide,opacity:0});
            var gp = new THREE.Mesh(new THREE.PlaneGeometry(_texW,ah), gm);
            gp._lx=o[0];gp._ly=o[1];gp._lz=o[2];gp._la=o[3];
            scene.add(gp); _lfPlanes.push(gp);
        });
    }

    var _instId    = parseInt(container.getAttribute('data-id') || '0', 10) || 0;
    var _jitter    = ((_instId * 2654435761) >>> 0) / 4294967296;
    var orbitSpd   = 0.055 * (0.80 + _jitter * 0.40);
    var _idlePhase = _jitter * Math.PI * 2;
    var mouseSens  = 0.28;

    var mX = 0.5, mY = 0.5, tMX = 0.5, tMY = 0.5, _lastMouse = 0, _idle = true;
    function _onMouse(e) { tMX = e.clientX/window.innerWidth; tMY = e.clientY/window.innerHeight; _lastMouse = Date.now(); _idle = false; }
    window.addEventListener('mousemove', _onMouse);

    function _eo(x) { return 1 - Math.pow(1-x,3); }  
    function _es(x) { 
        var c1=1.70158; return 1+(c1+1)*Math.pow(x-1,3)+c1*Math.pow(x-1,2);
    }
    function _sp(t, splitAt, overshoot) { 
        if (splitAt === undefined) splitAt = 0.8;
        if (overshoot === undefined) overshoot = 0.06;
        return t < splitAt ? _eo(t/splitAt) : 1 - overshoot*Math.sin((t-splitAt)/(1-splitAt)*Math.PI);
    }
    function _bounce(t) {
        var n=7.5625,d=2.75;
        if(t<1/d) return n*t*t;
        if(t<2/d){t-=1.5/d;return n*t*t+0.75;}
        if(t<2.5/d){t-=2.25/d;return n*t*t+0.9375;}
        t-=2.625/d; return n*t*t+0.984375;
    }

    var _P = {
        '3d_depth_in':   function(t,p){ var sp=_sp(t); p.position.z=-(1-sp)*2200; p.material.opacity=Math.min(1,t*2.5); p.scale.setScalar(0.35+0.65*_eo(t)); },
        '3d_zoom_out':   function(t,p){ var sp=_sp(t,0.75,0.04); p.scale.setScalar(3.2-2.2*sp); p.material.opacity=Math.min(1,t*1.4); p.position.z=(1-_eo(t))*180; },
        '3d_tilt_in':    function(t,p){ var sp=_sp(t); p.rotation.y=(1-sp)*1.48; p.material.opacity=Math.min(1,t*3); p.position.z=-(1-_eo(t))*300; },
        '3d_flip_x':     function(t,p){ var sp=_sp(t); p.rotation.x=-(1-sp)*Math.PI*0.48; p.material.opacity=Math.min(1,t*3); p.position.z=-(1-_eo(t))*250; },
        '3d_spin_in':    function(t,p){ var e=_eo(t); p.rotation.y=(1-e)*Math.PI*2.5; p.position.z=-(1-e)*1800; p.scale.setScalar(0.2+0.8*e); p.material.opacity=Math.min(1,t*1.8); },
        '3d_fall':       function(t,p){ var e=_eo(t); var sp=_sp(t); p.position.y=(1-sp)*ch*0.65; p.rotation.x=(1-e)*(-0.4); p.position.z=-(1-e)*500; p.material.opacity=Math.min(1,t*2); },
        '3d_swing':      function(t,p){ var sp=_sp(t,0.8,0.07); var ang=(1-sp)*1.38; p.rotation.y=-ang; p.position.x=-Math.sin(ang)*aw*0.48; p.material.opacity=Math.min(1,t*2.5); },
        '3d_unfold':     function(t,p){ var sp=_sp(t,0.75,0.05); p.scale.y=Math.max(0.001,sp); p.rotation.x=(1-sp)*0.4; p.material.opacity=Math.min(1,t*2); },
        '3d_curtain':    function(t,p){ var sp=_sp(t,0.75,0.04); p.scale.x=Math.max(0.001,sp); p.position.z=-(1-_eo(t))*200; p.material.opacity=Math.min(1,t*2.5); },
        '3d_bounce':     function(t,p){ var b=_bounce(Math.min(1,t*1.1)); p.position.y=-(1-b)*ch*0.65; p.position.z=-(1-_eo(t))*350; p.material.opacity=Math.min(1,t*2); },
        
        '3d_lightfield': function(t,p) {
                             
                             p.material.opacity = 0; }, 
    };
    
    var _fragPresets = ['3d_shatter_in','3d_rain','3d_stagger_h','3d_explode_in','3d_crystal','3d_unfurl'];
    var _isFragPreset = _fragPresets.indexOf(preset) !== -1;

    var _frags = [];
    if (_isFragPreset) {
        mat.opacity = 0;
        var cols = preset === '3d_stagger_h' ? 4 : preset === '3d_unfurl' ? 10 : 6;
        var rows = preset === '3d_stagger_h' ? 6 : preset === '3d_unfurl' ? 1 : 3;
        var tw = _texW / cols, th = ah / rows;
        
        var twi = Math.round(tw), thi = Math.round(th);
        for (var fr = 0; fr < rows; fr++) {
            for (var fc = 0; fc < cols; fc++) {
                var fGeo = new THREE.PlaneGeometry(twi, thi);
                var uvs = fGeo.attributes.uv;
                
                var u0=Math.round(fc*_texW/cols)/_texW, u1=Math.round((fc+1)*_texW/cols)/_texW;
                var v0=1-Math.round((fr+1)*ah/rows)/ah, v1=1-Math.round(fr*ah/rows)/ah;
                uvs.setXY(0,u0,v1);uvs.setXY(1,u1,v1);uvs.setXY(2,u0,v0);uvs.setXY(3,u1,v0);
                uvs.needsUpdate = true;
                var fMat = new THREE.MeshBasicMaterial({ map:tex, transparent:true, depthWrite:false, side:THREE.DoubleSide });
                var fMesh = new THREE.Mesh(fGeo, fMat);
                var fx = Math.round(fc*_texW/cols) + twi/2 - _texW/2;
                var fy = ah/2 - Math.round(fr*ah/rows) - thi/2;
                fMesh.position.set(fx, fy, 0);
                
                var rng  = (Math.sin((fr*cols+fc)*127.1 + 1)*43758.5) % 1;
                rng = rng < 0 ? rng+1 : rng;
                var rng2 = (Math.sin((fr*cols+fc)*269.5 + 7)*83758.5) % 1;
                rng2 = rng2 < 0 ? rng2+1 : rng2;
                var fCfg;
                switch(preset) {
                    case '3d_rain':
                        fCfg = { sx:fx+(rng-0.5)*tw*0.5, sy:fy+ch*0.9+rng2*ch*0.5, sz:-200-rng*500, rx:0.3, delay:rng*0.25+fr*0.06 };
                        break;
                    case '3d_stagger_h':
                        fCfg = { sx:fx+(rng-0.5)*aw*0.1, sy:fy, sz:-600-fr*500, rx:0, delay:fr/(rows-1||1)*0.55 };
                        break;
                    case '3d_explode_in':
                        fCfg = { sx:0, sy:0, sz:-300, rx:0, delay:rng*0.2 };
                        break;
                    case '3d_crystal':
                        fCfg = { sx:fx+(rng-0.5)*aw*0.6, sy:fy+(rng2-0.5)*ah*0.6, sz:-300-rng*700, rx:(rng-0.5)*0.8, delay:rng*0.35 };
                        break;
                    case '3d_unfurl':

                        fCfg = { sx: fx + (fc - cols/2) * tw * 1.8,
                                 sy: fy, sz: -100 - rng * 200,
                                 ry: (1 - fc/cols) * Math.PI * 0.88, 
                                 rx: 0, delay: (1 - fc/cols) * 0.5 + rng * 0.05 };
                        break;
                    default: 
                        fCfg = { sx:fx+(rng-0.5)*aw*1.2, sy:fy+(rng2-0.5)*ah*1.2, sz:-1200-rng*1800, rx:0, delay:rng*0.45 };
                }
                fMesh._fx=fx; fMesh._fy=fy; fMesh._fz=0;
                fMesh._sx=fCfg.sx; fMesh._sy=fCfg.sy; fMesh._sz=fCfg.sz;
                fMesh._rx=fCfg.rx; fMesh._ry=fCfg.ry||0; fMesh._delay=fCfg.delay;
                fMat.opacity = 0;
                scene.add(fMesh);
                _frags.push(fMesh);
            }
        }
    }

    var _t0 = null, _animDone = false, _clock = 0;
    function _render(ts) {
        container._la3dRaf = requestAnimationFrame(_render);
        if (_t0 === null) _t0 = ts;
        _clock = (ts - _t0) / 1000;

        if (!_idle && Date.now() - _lastMouse > 2000) _idle = true;
        if (_idle) {
            tMX = 0.5 + 0.15*Math.sin(_clock*0.4+_idlePhase);
            tMY = 0.5 + 0.05*Math.sin(_clock*0.27+_idlePhase*0.7);
        }
        mX += (tMX-mX)*orbitSpd; mY += (tMY-mY)*orbitSpd;

        if (!_animDone) {
            var rawT = Math.min(1, _clock / (dur/1000));
            
            var _dofPresets = {'3d_depth_in':1,'3d_zoom_out':1,'3d_shatter_in':1,'3d_rain':1,'3d_explode_in':1,'3d_crystal':1};
            if (_dofPresets[preset]) { canvas3d.style.filter = 'blur(' + Math.round((1-rawT)*(1-rawT)*6) + 'px)'; }
            
            if (preset === '3d_lightfield' && !_animDone) {
                var lfe = _eo(rawT);
                var lfs = _sp(rawT, 0.8);
                _lfPlanes.forEach(function(gp) {
                    gp.position.set(gp._lx*(1-lfs), gp._ly*(1-lfs), gp._lz*(1-lfe));
                    gp.material.opacity = gp._la * Math.sin(rawT * Math.PI) * (1 - rawT*0.7);
                });
                mat.opacity = _eo(rawT);
                plane.position.z = -600*(1-_eo(rawT));
                plane.scale.setScalar(0.7 + 0.3*_eo(rawT));
            }
            if (_isFragPreset) {
                _frags.forEach(function(fm) {
                    var ft = Math.max(0, Math.min(1, (rawT-fm._delay)/(1-fm._delay+0.001)));
                    var sp = _sp(ft, 0.8, 0.04);
                    fm.position.x = fm._sx + (fm._fx-fm._sx)*sp;
                    fm.position.y = fm._sy + (fm._fy-fm._sy)*sp;
                    fm.position.z = fm._sz*(1-_eo(ft));
                    fm.rotation.x = fm._rx*(1-_eo(ft));
                    fm.rotation.y = fm._ry*(1-_sp(ft,0.85,0.05));
                    fm.material.opacity = Math.min(1, ft*2.5);
                });
            } else if (_P[preset]) {
                _P[preset](rawT, plane);
            }
            if (rawT >= 1) {
                canvas3d.style.filter = '';
                if (_isFragPreset) {

                    var _fadeStart = performance.now();
                    var _fadeDur = 120; 
                    
                    _frags.forEach(function(fm) { fm.position.set(fm._fx,fm._fy,0); fm.rotation.x=0; });
                    var _doFade = function(ts) {
                        var fp = Math.min(1, (ts - _fadeStart) / _fadeDur);
                        mat.opacity = fp;
                        _frags.forEach(function(fm) { fm.material.opacity = 1 - fp; });
                        renderer.render(scene, camera);
                        if (fp < 1) { requestAnimationFrame(_doFade); }
                        else {
                            
                            _frags.forEach(function(fm) { scene.remove(fm); fm.geometry.dispose(); fm.material.dispose(); });
                            _frags.length = 0;
                            _animDone = true;
                        }
                    };
                    cancelAnimationFrame(container._la3dRaf);
                    container._la3dRaf = requestAnimationFrame(_doFade);
                    return;
                } else {
                    plane.position.set(0,0,0); plane.rotation.set(0,0,0); plane.scale.setScalar(1);
                    plane.material.opacity = 1;
                    _animDone = true;
                }
            }
        } else {
            var ry=(mX-0.5)*mouseSens, rx=-(mY-0.5)*mouseSens*0.5;
            plane.rotation.y=ry; plane.rotation.x=rx;
            _frags.forEach(function(fm){fm.rotation.y=ry;fm.rotation.x=rx;});
        }
        renderer.render(scene, camera);

        if (canvas3d.style.visibility === 'hidden') canvas3d.style.visibility = '';
    }

    container._la3dCancel = function() {
        if (container._la3dRaf) { cancelAnimationFrame(container._la3dRaf); container._la3dRaf=null; }
        if (container._la3dVisObs) { try { container._la3dVisObs.disconnect(); } catch(e){} container._la3dVisObs=null; }
        window.removeEventListener('mousemove', _onMouse);
        tex.dispose(); mat.dispose();
        _lfPlanes.forEach(function(gp){gp.geometry.dispose();gp.material.dispose();});
        _frags.forEach(function(fm){fm.geometry.dispose();fm.material.dispose();});

        
        try { renderer.forceContextLoss(); } catch(_e) {}
        renderer.dispose();
        var cv3=container.querySelector('.impulse-la-3d-canvas');
        if(cv3) cv3.parentNode.removeChild(cv3);
        asset.style.opacity='';
        container._la3dRenderer=null;
    };

    var _go = function() { if(delay>0) container._laDelayTimer=setTimeout(function(){ _t0=null; container._la3dRaf=requestAnimationFrame(_render); },delay); else { _t0=null; container._la3dRaf=requestAnimationFrame(_render); } };
    
    ensureFontLoaded(asset, _go);

    if (typeof IntersectionObserver !== 'undefined' && !container._la3dVisObs) {
        container._la3dVisObs = new IntersectionObserver(function(entries) {
            entries.forEach(function(en) {
                if (en.isIntersecting) {
                    if (!container._la3dRaf && container._la3dRenderer) {
                        container._la3dRaf = requestAnimationFrame(_render);
                    }
                } else {
                    if (container._la3dRaf) {
                        cancelAnimationFrame(container._la3dRaf);
                        container._la3dRaf = null;
                    }
                }
            });
        }, { rootMargin: '200px 0px', threshold: 0 });
        container._la3dVisObs.observe(container);
    }
}

function initLogoAnimators() {
    
    if (document.fonts && document.fonts.load) {
        document.querySelectorAll('.impulse-logo-animator .impulse-la-asset').forEach(function(asset) {
            if (asset.tagName === 'IMG') return;
            var cs = window.getComputedStyle(asset);
            var fSpec = (cs.fontWeight||'700') + ' ' + (cs.fontSize||'64px') + ' ' + (cs.fontFamily||'sans-serif');
            document.fonts.load(fSpec);
        });
    }

    document.querySelectorAll('.impulse-logo-animator').forEach(function(c) {
        if (c._laInited) return;
        c._laInited = true;

        
        
        var _bodyScreen = (window._impulseScreenMode
            || document.body.getAttribute('data-screen-mode') === '1'
            || !!document.querySelector('.impulse-screen-wrapper'));
        var _useScreenFocus = _bodyScreen;
        if (_useScreenFocus) {

            

            

            var _asset = c.querySelector('.impulse-la-asset');
            var _hasCv = !!c.querySelector('.impulse-la-canvas, .impulse-la-3d-canvas');
            if (_asset && !_hasCv) _asset.style.opacity = '1';

            

            

            

            

            
            
            if (c.dataset.preset && !_hasCv) {
                var _sec = c.closest('.impulse-screen, [data-type="section"]');
                if (_sec && _sec.classList.contains('impulse-screen-active')) {

                    

                    

                    

                    

                    
                    
                    requestAnimationFrame(function() {
                        if (c._laRunQueued || c._laCancel || c._la3dRaf) return;
                        runAnimator(c);
                    });
                }
            }
            return;
        }
        if (c.dataset.scrollTrigger === '1') {
            if (!('IntersectionObserver' in window)) {
                requestAnimationFrame(function() { runAnimator(c); });
                return;
            }
            var obs = new IntersectionObserver(function(entries, observer) {
                entries.forEach(function(entry) {
                    if (!entry.isIntersecting) return;
                    runAnimator(c);

                    observer.unobserve(c);
                });
            }, { threshold: 0.1 });
            obs.observe(c);
        } else {
            requestAnimationFrame(function() { runAnimator(c); });
        }
    });
}

function _laReady() {
    if (document.fonts && document.fonts.ready) {
        document.fonts.ready.then(initLogoAnimators);
    } else {
        initLogoAnimators();
    }
}
if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', _laReady);
} else {
    _laReady();
}

(function _laEditorDeviceHeal() {
    var framed; try { framed = (window.self !== window.top); } catch (e) { framed = true; }
    if (!framed) return;
    var i = 0, checks = [200, 400, 700, 1100, 1700, 2500, 3500];
    function _tick() {
        document.querySelectorAll('.impulse-logo-animator').forEach(function (c) {
            var asset = c.querySelector('.impulse-la-asset');
            if (!asset || c._laRasterFS === undefined) return;
            var live = Math.round(parseFloat(window.getComputedStyle(asset).fontSize) || 0);
            if (live > 0 && Math.abs(live - c._laRasterFS) > 1) {
                c._laRasterFS = live; 
                try { _cancelAnimator(c); c._laSettled = false; runAnimator(c); } catch (e) {}
            }
        });
        if (++i < checks.length) setTimeout(_tick, checks[i] - checks[i - 1]);
    }
    setTimeout(_tick, checks[0]);
})();

window.impulseLogoAnimatorRun    = function(id) { var c = document.getElementById(id); if (c) runAnimator(c); };
window.impulseLogoAnimatorRunAll = initLogoAnimators;

window.impulseDbg = function() {
    var out = {};
    var wrapper = document.querySelector('.impulse-content-wrapper');
    var sec = document.querySelector('.impulse-screen section.impulse-section, .impulse-screen-active section.impulse-section, .impulse-screen-wrapper section.impulse-section');
    if (!sec) sec = document.querySelector('section.impulse-section');
    var con = sec && sec.querySelector(':scope > .impulse-container');
    if (wrapper) {
        var wcs = getComputedStyle(wrapper);
        out.wrapper = { h: wrapper.offsetHeight, jc: wcs.justifyContent, disp: wcs.display, fd: wcs.flexDirection };
    }
    if (sec) {
        var scs = getComputedStyle(sec);
        out.section = {
            h: sec.offsetHeight,
            jc: scs.justifyContent,
            disp: scs.display,
            fd: scs.flexDirection,
            padT: scs.paddingTop,
            padB: scs.paddingBottom
        };
    }
    if (con) {
        var ccs = getComputedStyle(con);
        out.container = {
            h: con.offsetHeight,
            jc: ccs.justifyContent,
            disp: ccs.display,
            fd: ccs.flexDirection,
            flex: ccs.flex,
            childCount: con.children.length,
            firstChildTop: con.firstElementChild ? Math.round(con.firstElementChild.getBoundingClientRect().top - con.getBoundingClientRect().top) : null
        };
    }
    out.viewport = { h: window.innerHeight, screenMode: document.body.getAttribute('data-screen-mode') === '1' };
    out.animators = Array.prototype.map.call(document.querySelectorAll('.impulse-logo-animator'), function(c) {
        var a = c.querySelector('.impulse-la-asset');
        var cv = c.querySelector('.impulse-la-canvas');
        return {
            id: c.id,
            preset: c.dataset.preset || '(none)',
            assetOpacity: a ? getComputedStyle(a).opacity : 'NO ASSET',
            assetInlineOp: a ? a.style.opacity : '',
            hasCanvas: !!cv,
            canvasVisible: cv ? (getComputedStyle(cv).opacity !== '0' && cv.offsetWidth > 0) : false
        };
    });
    console.log('[IMPULSE FRONTEND DBG]', out);
    return out;
};

function _ensureThree(cb) {
    if (window.THREE) { cb(); return; }
    if (window._threeLoading) { window._threeLoading.push(cb); return; }
    window._threeLoading = [cb];
    var s = document.createElement('script');
    
    s.src = (window._instinctorPluginUrl || '') + 'assets/js/vendor/three.min.js';
    s.onload = function() {
        var q = window._threeLoading; window._threeLoading = null;
        for (var i = 0; i < q.length; i++) q[i]();
    };
    document.head.appendChild(s);
}

function _cancelAnimator(c) {
    if (c._laCancel)          { c._laCancel();          c._laCancel          = null; }
    if (c._la3dCancel)        { c._la3dCancel();        c._la3dCancel        = null; }
    if (c._laGradRaf)         { cancelAnimationFrame(c._laGradRaf); c._laGradRaf = null; }
    if (c._laGradScopeCleanup){ c._laGradScopeCleanup(); c._laGradScopeCleanup = null; }
    if (c._laDelayTimer)      { clearTimeout(c._laDelayTimer);  c._laDelayTimer = null; }
    if (c._la3dRaf)           { cancelAnimationFrame(c._la3dRaf); c._la3dRaf  = null; }
    
    c._laRunQueued = false;
    c._laGradGen = (c._laGradGen || 0) + 1;
}

window.impulsePreWarmScreen = function(screenEl) {
    screenEl.querySelectorAll('.impulse-logo-animator').forEach(preWarmAnimator);
    requestAnimationFrame(function() {
        if (window.impulseOnScreenFocus) window.impulseOnScreenFocus(screenEl);
    });
};
window.impulsePreTriggerScreen = window.impulsePreWarmScreen;

var _impAnimPrevReset = window.impulseAnimatorReset;
window.impulseAnimatorReset = function(screenEl) {
    
    if (_impAnimPrevReset) { try { _impAnimPrevReset(screenEl); } catch (e) {} }
    screenEl.querySelectorAll('.impulse-logo-animator').forEach(function(c) {
        _cancelAnimator(c);
        var cv = c.querySelector('.impulse-la-canvas');
        if (cv) cv.parentNode.removeChild(cv);
        var cv3 = c.querySelector('.impulse-la-3d-canvas');
        if (cv3) cv3.parentNode.removeChild(cv3);
        var asset = c.querySelector('.impulse-la-asset');
        if (asset) asset.style.opacity = '0';
        c._laSettled = false;
        c._laInited  = false;
        c._laPreCanvas = null;
        c._laPreSrc    = null;
        c._laPreWarm   = false;
    });
};

var _impAnimPrevFocus = window.impulseOnScreenFocus;
window.impulseOnScreenFocus = function(screenEl) {
    
    if (_impAnimPrevFocus) { try { _impAnimPrevFocus(screenEl); } catch (e) {} }
    
    var isBackward = (window._impulseSoftBackReveal === true);
    screenEl.querySelectorAll('.impulse-logo-animator').forEach(function(c) {
        
        if (c._laCancel || c._laRunQueued) return;
        if (isBackward) {
            
            _cancelAnimator(c);
            var canv = c.querySelector('.impulse-la-canvas');
            if (canv && canv.parentNode) canv.parentNode.removeChild(canv);
            var canv3 = c.querySelector('.impulse-la-3d-canvas');
            if (canv3 && canv3.parentNode) canv3.parentNode.removeChild(canv3);
            var assetEl = c.querySelector('.impulse-la-asset');
            if (assetEl) {
                assetEl.style.transition = 'opacity 280ms ease-in-out';
                assetEl.style.opacity = '1';
            }
            c._laSettled   = true;
            c._laRunQueued = false;
            c._laInited    = false;
            c._laPreWarm   = false;
            return;
        }
        
        var asset = c.querySelector('.impulse-la-asset');
        if (asset) asset.style.opacity = '0';
        
        var oldCv = c.querySelector('.impulse-la-canvas');
        if (oldCv) oldCv.style.opacity = '0';
        var old3d = c.querySelector('.impulse-la-3d-canvas');
        if (old3d) old3d.style.opacity = '0';
        _cancelAnimator(c);
        c._laSettled   = false;
        c._laInited    = false;

        
        
        c._laSkipDelay = false;

        runAnimator(c);
    });

    if (window.ImpulseScreens && window.ImpulseScreens.extendCooldown) {
        var maxDur = 0;
        screenEl.querySelectorAll('.impulse-logo-animator').forEach(function(c) {
            if (c.dataset.scrollTrigger !== '1') return;
            var d = _laDV(c, 'duration', 1400) + _laDV(c, 'delay', 0);
            if (d > maxDur) maxDur = d;
        });
        if (maxDur > 0) window.ImpulseScreens.extendCooldown(maxDur);
    }

    if (window.Impulse3DText) {
        var _dt3dInstances = window.Impulse3DText.instances || {};
        screenEl.querySelectorAll('.impulse-display-text[data-dt-3d-mode]').forEach(function(dtEl) {
            var wrap = dtEl.closest('.impulse-el');
            var id = wrap ? wrap.dataset.id : (dtEl.id ? dtEl.id.replace(/^dt-/, '') : '');
            if (id && !_dt3dInstances[id]) window.Impulse3DText.init(id);
        });
    }
};

(function() {
    
    document.querySelectorAll('.impulse-logo-animator').forEach(function(c) {
        var svgImg = c.querySelector('.impulse-la-asset[data-svg-colors]');
        if (svgImg) applyAssetSvgColors(svgImg, svgImg.dataset.svgColors);
    });

    

    

    

})();

document.addEventListener('click', function(e) {
    var btn = e.target.closest('.impulse-la-replay');
    if (!btn) return;
    var c = btn.closest('.impulse-logo-animator');
    if (c) runAnimator(c);
});

window.addEventListener('message', function(e) {
    if (!e.data || e.data.type !== 'la_update') return;
    var id = e.data.id;
    var c = id ? document.getElementById(id) : document.querySelector('.impulse-logo-animator');
    if (!c) return;

    var _map = {
        preset:'preset', duration:'duration', delay:'delay',
        spring:'spring', scrollTrigger:'scrollTrigger', color:'color',
        gradientFrom:'gradientFrom', gradientTo:'gradientTo', gradientAngle:'gradientAngle',
        tilt:'tilt', depth:'depth', gapBelow:'gapBelow', flash:'flash', fontSize:'fontSize',
        fontWeight:'fontWeight', fontFamily:'fontFamily', imageWidth:'imageWidth',
        imageSrc:'imageSrc', svgColors:'svgColors', contentType:'contentType'
    };

    

    
    var _prevColor   = c.dataset.color || '';
    var _prevGFrom   = c.dataset.gradientFrom || '';
    var _prevGTo     = c.dataset.gradientTo || '';
    var _prevGAngle  = c.dataset.gradientAngle || '';
    for (var k in _map) {
        if (e.data[k] !== undefined) c.dataset[_map[k]] = e.data[k];
    }
    if (e.data.gapBelow !== undefined) c.style.marginBottom = (parseInt(e.data.gapBelow) || 0) > 0 ? e.data.gapBelow + 'px' : '';

    var asset = c.querySelector('.impulse-la-asset');

    

    
    
    var _prevText = asset ? (asset.innerHTML || '') : '';
    if (asset) {
        var ct = e.data.contentType || c.dataset.contentType || 'text';
        if (ct === 'image' && e.data.imageSrc) {
            if (asset.tagName !== 'IMG') {
                var img = document.createElement('img');
                img.className = 'impulse-la-asset';
                if (e.data.imageWidth) img.style.width = e.data.imageWidth + 'px';
                img.src = e.data.imageSrc;
                asset.parentNode.replaceChild(img, asset);
                asset = img;
            } else {
                asset.src = e.data.imageSrc;
                if (e.data.imageWidth) asset.style.width = e.data.imageWidth + 'px';
            }
        } else if (ct === 'text') {
            if (asset.tagName === 'IMG') {
                var div = document.createElement('div');
                div.className = 'impulse-la-asset';
                asset.parentNode.replaceChild(div, asset);
                asset = div;
            }
            if (e.data.text !== undefined) {

                

                

                
                var _esc = document.createElement('div');
                _esc.textContent = e.data.text || '';
                asset.innerHTML = _esc.innerHTML
                    .replace(/&amp;nbsp;/g, '&nbsp;')
                    .replace(/&lt;(\/?(?:br|b|i|u|strong|em|sup|sub|small|mark))\s*\/?&gt;/gi,
                        function(_m, tag) { return '<' + tag.toLowerCase() + '>'; })

                    

                    .replace(/&amp;(#x[0-9a-fA-F]+|#[0-9]+|[a-zA-Z][a-zA-Z0-9]{1,31});/g, '&$1;')
                    .replace(/\r\n|\n/g, '<br>');
            }
            if (e.data.fontSize)   asset.style.fontSize   = e.data.fontSize + 'px';
            if (e.data.fontWeight) asset.style.fontWeight  = e.data.fontWeight;

            

            

            

            

            
            
            var _laGtNow = c.dataset.gradientTo || '';
            var _laGfNow = c.dataset.gradientFrom || '';
            if (_laGtNow && _laGfNow) {
                asset.style.color = 'transparent';
                asset.style.webkitTextFillColor = 'transparent';
            } else if (e.data.color) {
                asset.style.color = e.data.color;
                asset.style.webkitTextFillColor = '';

                
                asset.style.backgroundImage = '';
                asset.style.webkitBackgroundClip = '';
                asset.style.backgroundClip = '';
            }
            if (e.data.fontFamily) asset.style.fontFamily  = "'" + e.data.fontFamily + "',sans-serif";
        }
        if (ct === 'image' && e.data.svgColors) applyAssetSvgColors(asset, e.data.svgColors);
    }

    var _presetChanged = (e.data.preset && e.data.preset !== (c._laLastBuiltPreset || ''));
    if (_presetChanged) {
        c._laLastBuiltPreset = e.data.preset;
        _cancelAnimator(c);
        c._laPreCanvas = null; c._laPreSrc = null; c._laPreWarm = false;

        

        if (asset) {
            var _gf2 = c.dataset.gradientFrom || '';
            var _gt2 = c.dataset.gradientTo   || '';
            if (_gf2 && _gt2) {
                asset.style.color = 'transparent';
                asset.style.webkitTextFillColor = 'transparent';
            } else if (c.dataset.color) {
                asset.style.color = c.dataset.color;
                asset.style.webkitTextFillColor = '';
            }
        }

        clearTimeout(c._laPresetFireTimer);
        c._laPresetFireTimer = setTimeout(function() {
            c._laPresetFireTimer = null;
            runAnimator(c);
        }, 200);
    } else {
        
        var _newColor  = c.dataset.color || '';
        var _newGFrom  = c.dataset.gradientFrom || '';
        var _newGTo    = c.dataset.gradientTo || '';
        var _newGAngle = c.dataset.gradientAngle || '';
        var _newText   = asset ? (asset.innerHTML || '') : '';

        

        
        var _alignChanged = false;
        if (e.data.align !== undefined) {
            var _newAlign = (e.data.align === 'left' || e.data.align === 'right') ? e.data.align : 'center';
            var _alignJC  = _newAlign === 'left' ? 'flex-start' : (_newAlign === 'right' ? 'flex-end' : 'center');
            if (c.style.justifyContent !== _alignJC) c.style.justifyContent = _alignJC;
            _alignChanged = (_newAlign !== (c._laLastAlign || 'center'));
        }
        if (_newColor  !== _prevColor  ||
            _newGFrom  !== _prevGFrom  ||
            _newGTo    !== _prevGTo    ||
            _newGAngle !== _prevGAngle ||
            _newText   !== _prevText   ||
            _alignChanged) {
            try { quickRedraw(c); } catch (_qE) {}
        }
    }
});

if (window.ResizeObserver) {
    var _laResizeDebounce = {};
    var _laLastSize       = new WeakMap();
    var _laRO = new ResizeObserver(function(entries) {
        entries.forEach(function(entry) {
            var c = entry.target;

            

            var w = Math.round(c.offsetWidth);
            if (w < 1) return;
            var prev = _laLastSize.get(c);
            if (prev === w) return;
            _laLastSize.set(c, w);
            var id = c.id || 'la';
            clearTimeout(_laResizeDebounce[id]);
            _laResizeDebounce[id] = setTimeout(function() {
                _cancelAnimator(c);
                c._laSettled = false;
                runAnimator(c);
            }, 200);
        });
    });
    document.querySelectorAll('.impulse-logo-animator').forEach(function(c) {
        _laRO.observe(c);
    });
}

})();
