
(function() {
'use strict';

var INSTINCTOR_WM_ON = false;

var VERT = `
attribute vec2 a_pos;
varying vec2 v_uv;
void main() {
    v_uv = vec2(a_pos.x * 0.5 + 0.5, 0.5 - a_pos.y * 0.5);
    gl_Position = vec4(a_pos, 0.0, 1.0);
}`;

var FRAG = `
precision mediump float;
uniform sampler2D u_color;
uniform sampler2D u_depth;
uniform vec2      u_mouse;
uniform float     u_strength;
uniform float     u_aspect;
uniform float     u_img_aspect;
uniform vec2      u_focal;        // crop focal point 0-1 (per-device); 0.5,0.5 = centred
uniform vec2      u_chroma;
uniform float     u_time;
uniform vec2      u_shake;       // global camera shake offset
uniform sampler2D u_wm_tex;      // free-tier watermark (flat overlay)
uniform float     u_wm;          // 1 = draw watermark, 0 = off
varying vec2      v_uv;

vec2 coverUV(vec2 uv) {
    // Crop toward u_focal (0-1) instead of always centring, so a narrow
    // (mobile) viewport can show the subject side of a wide image. The focal
    // maps to the visible-window centre, clamped so the window never leaves
    // the image: centre = focal*(1-scale) + scale*0.5 (0.5 → centred).
    if (u_aspect > u_img_aspect) {
        float scale = u_img_aspect / u_aspect;
        float fc = u_focal.y * (1.0 - scale) + scale * 0.5;
        return vec2(uv.x, (uv.y - 0.5) * scale + fc);
    } else {
        float scale = u_aspect / u_img_aspect;
        float fc = u_focal.x * (1.0 - scale) + scale * 0.5;
        return vec2((uv.x - 0.5) * scale + fc, uv.y);
    }
}

float hash(vec2 p) {
    p = fract(p * vec2(127.1, 311.7));
    p += dot(p, p + 19.19);
    return fract(p.x * p.y);
}
float noise(vec2 p) {
    vec2 i = floor(p); vec2 f = fract(p);
    f = f * f * (3.0 - 2.0 * f);
    return mix(mix(hash(i), hash(i+vec2(1,0)), f.x),
               mix(hash(i+vec2(0,1)), hash(i+vec2(1,1)), f.x), f.y);
}

void main() {
    // Camera shake — horizontal only so scene never scrolls vertically
    // live = 0 when strength is 0: every depth/motion term collapses and
    // the canvas renders the plain image, perfectly still. v2.1.1437.
    float live = step(0.0001, u_strength);
    vec2 baseUV = coverUV(clamp(v_uv + vec2(u_shake.x * live, 0.0), 0.001, 0.999));

    // Noise micro-jitter — horizontal only
    float n1 = noise(baseUV * 3.5 + vec2(u_time * 0.07, u_time * 0.05));
    float jitterX = (n1 - 0.5) * 0.006 * live;

    float d = texture2D(u_depth, baseUV).r;
    float dSL = texture2D(u_depth, clamp(baseUV - vec2(0.01, 0.0), 0.001, 0.999)).r;
    float dSR = texture2D(u_depth, clamp(baseUV + vec2(0.01, 0.0), 0.001, 0.999)).r;
    float dPar = d * 0.5 + dSL * 0.25 + dSR * 0.25;
    vec2 mouse = u_mouse - 0.5;
    float shiftX = mouse.x * dPar * u_strength * 0.08;
    float zoom = 1.0 + u_strength * 0.04;
    vec2 uv = (baseUV - 0.5) / zoom + 0.5;
    uv += vec2(shiftX + jitterX, 0.0);

    // ── Heat shimmer — very distant areas only, gentle ──
    float heatMask = pow(max(0.0, 1.0 - d), 4.0);
    float hn1 = noise(uv * 10.0 + vec2(u_time * 0.4, u_time * 0.3));
    float hn2 = noise(uv * 14.0 + vec2(-u_time * 0.5, u_time * 0.35));
    uv += vec2((hn1 - 0.5) * 0.0008, (hn2 - 0.5) * 0.0006) * heatMask * live;

    uv = clamp(uv, 0.001, 0.999);

    // Depth neighbors — normals, edge detection, chromatic fringe
    float dL = texture2D(u_depth, clamp(baseUV - vec2(0.003, 0.0), 0.001, 0.999)).r;
    float dR = texture2D(u_depth, clamp(baseUV + vec2(0.003, 0.0), 0.001, 0.999)).r;
    float dU = texture2D(u_depth, clamp(baseUV - vec2(0.0, 0.003), 0.001, 0.999)).r;
    float dD = texture2D(u_depth, clamp(baseUV + vec2(0.0, 0.003), 0.001, 0.999)).r;
    // Chromatic split — frame EDGES ONLY. A radial mask keeps the image centre
    // clean and ramps the R/B split in over the outer band toward the frame.
    // (Removed the old depthEdgeFar*8 term, which fringed every internal depth
    // edge -> heavy colour artifacts across the whole image.)
    float rEdge = smoothstep(0.6, 1.0, length(uv - 0.5) * 2.0);
    // Halved 2.2 -> 1.1 (Sergei 2026-08-07): the R/B split read as a coloured
    // fringe rather than as depth, most visible on hard edges against flat
    // backgrounds. One multiplier covers every caller of this engine.
    vec2 chroma = u_chroma * rEdge * 1.1 * live;
    vec2 uvR = clamp(uv + chroma, 0.001, 0.999);
    vec2 uvB = clamp(uv - chroma, 0.001, 0.999);
    float r = texture2D(u_color, uvR).r;
    float g = texture2D(u_color, uv).g;
    float b = texture2D(u_color, uvB).b;
    float a = texture2D(u_color, uv).a;

    vec3 color = vec3(r, g, b);

    // Depth-contrast: distant areas lose contrast slightly (haze without color shift)
    float fog = (1.0 - d) * (1.0 - d) * 0.12 * live;
    float fogLuma = dot(color, vec3(0.299, 0.587, 0.114));
    color = mix(color, vec3(fogLuma), fog * 0.5); // subtle desaturation only
    color = mix(color, color * 0.92 + 0.04, fog * 0.4); // slight contrast compression

    // Bidirectional shimmer — subtle, never overexposes
    float luma = dot(color, vec3(0.299, 0.587, 0.114));
    float signed = luma * 2.0 - 1.0;
    float shimmer = 1.0 + signed * abs(signed) * (sin(u_time * 1.3) * 0.018 + sin(u_time * 3.7 + 0.9) * 0.007);
    color *= mix(1.0, shimmer, live);
    color = min(color, vec3(1.0)); // never overexpose

    // Film grain
    float grain = (hash(uv + fract(u_time)) - 0.5) * 0.035;
    color += grain * live;

    // ── Specular hot spots from depth normals ──
    vec3 depthNormal = normalize(vec3((dL - dR) * 5.0, (dU - dD) * 5.0, 0.5));
    vec2 restPos = vec2(0.32, 0.28);
    vec2 lightPos = mix(restPos, u_mouse, 0.55);
    vec3 lightDir = normalize(vec3((lightPos.x - 0.5) * 2.0, -(lightPos.y - 0.5) * 2.0, 1.0));
    float spec = pow(max(0.0, dot(depthNormal, lightDir)), 32.0) * 0.07 * d * live;
    color += vec3(spec * 1.0, spec * 0.95, spec * 0.85);

    // ── Depth-weighted color grading ──
    float depthWarm = d * d * live;
    color *= mix(vec3(0.97, 0.98, 1.02), vec3(1.03, 1.01, 0.97), depthWarm);
    color += depthWarm * vec3(0.025, 0.018, 0.006);

    // ── Volumetric dust — discrete floating specks ──
    for (int di = 0; di < 18; di++) {
        float ds = float(di) * 0.05556;
        float dDepth = 0.25 + ds * 0.5;
        vec2 dpos = vec2(
            fract(sin(ds * 127.1 + 1.7) * 43758.5) + sin(u_time * (0.013 + ds * 0.009) + ds * 5.0) * 0.15,
            fract(sin(ds * 311.7 + 3.1) * 21345.7) + cos(u_time * (0.011 + ds * 0.007) + ds * 4.2) * 0.12
        );
        dpos = fract(dpos);
        dpos += mouse * dDepth * 0.04;
        float dd = length(uv - dpos);
        float dg = exp(-dd * dd * 12000.0);
        float dpulse = 0.4 + 0.6 * sin(u_time * (0.5 + ds * 1.3) + ds * 6.28);
        color += vec3(0.9, 0.85, 0.75) * dg * dpulse * 0.08;
    }

    // ── Fireflies ──
    vec3 particles = vec3(0.0);
    for (int i = 0; i < 12; i++) {
        float s = float(i) * 0.08333;
        float f1 = 0.056 + s * 0.084;
        float f2 = 0.036 + s * 0.060;
        float f3 = 0.044 + s * 0.072;
        vec2 ppos = vec2(
            0.5 + sin(u_time * f1 + s * 6.283) * 0.32 + sin(u_time * f2 + s * 3.14) * 0.12,
            0.5 + cos(u_time * f2 + s * 5.0)  * 0.26 + sin(u_time * f3 + s * 7.9)  * 0.10
        );
        float pDepth = texture2D(u_depth, clamp(ppos, 0.001, 0.999)).r;
        vec2 mouseOff = (u_mouse - 0.5) * u_strength * 0.015;
        ppos += mouseOff * (pDepth - 0.5) * 2.0;
        vec2 toMouse = u_mouse - ppos;
        float mdist = length(toMouse);
        ppos += toMouse * (1.0 - exp(-mdist * 2.5)) * 0.08;
        float dist = length(uv - ppos);
        float glow = exp(-dist * dist * 38.0);
        vec3 pcol = vec3(0.88 + s * 0.12, 0.55 + s * 0.38, 0.06 + s * 0.24);
        float pulse = 0.45 + 0.55 * sin(u_time * (0.9 + s * 2.1) + s * 6.283);
        particles += pcol * glow * pulse * 0.055;
    }
    color += particles;

    // ── Screen shine — vertical glass reflection on tilt ──
    vec2 shineCenter = vec2(1.0 - u_mouse.x, 0.5);
    vec2 sd = uv - shineCenter;
    float shine = exp(-sd.x * sd.x * 28.0 - sd.y * sd.y * 1.8);
    float tiltRaw = abs(u_mouse.x - 0.5) * 2.0;
    float tiltAmt = smoothstep(0.12, 0.35, tiltRaw);
    shine *= tiltAmt * 0.05;
    float s2x = shineCenter.x + 0.12;
    float s2dx = uv.x - s2x;
    float shine2 = exp(-s2dx * s2dx * 50.0 - sd.y * sd.y * 3.0) * tiltAmt * 0.025;
    color += (shine + shine2) * vec3(1.0, 0.98, 0.94);

    // ── Free-tier watermark ──
    // Sampled at flat v_uv (NOT the displaced uv) so it stays pinned to the
    // screen and never parallax-smears, and composited here so it lives in the
    // framebuffer (no CSS/DOM layer to remove). u_wm is 0 on Pro/standalone, so
    // this collapses to a no-op there. Gated by the live flag too: when depth
    // strength is 0 the image renders flat (no 3D effect), so there is nothing
    // to watermark and the brand mark stays off.
    vec4 wm = texture2D(u_wm_tex, v_uv);
    color = mix(color, wm.rgb, clamp(wm.a, 0.0, 1.0) * u_wm * live);

    gl_FragColor = vec4(color, a);
}`;

var instances = {}; 

function initSection(el) {

    

    
    
    var id = (el === document.body) ? '__body__' : (el.dataset.id || el.id || Math.random().toString(36).slice(2));
    if (instances[id]) return; 

    

    var _vw = window.innerWidth || 0;
    var colorUrl, depthUrl;
    if (_vw > 0 && _vw <= 767 && el.getAttribute('data-depth-image-mobile') && el.getAttribute('data-depth-map-mobile')) {
        colorUrl = el.getAttribute('data-depth-image-mobile');
        depthUrl = el.getAttribute('data-depth-map-mobile');
    } else if (_vw > 0 && _vw <= 1024 && el.getAttribute('data-depth-image-tablet') && el.getAttribute('data-depth-map-tablet')) {
        colorUrl = el.getAttribute('data-depth-image-tablet');
        depthUrl = el.getAttribute('data-depth-map-tablet');
    } else {
        colorUrl = el.getAttribute('data-depth-image');
        depthUrl = el.getAttribute('data-depth-map');
    }
    var strength   = parseFloat(el.getAttribute('data-depth-strength') || '50') / 100;
    
    var useOscil   = true;
    var useChroma  = true;

    
    
    if (!colorUrl || !depthUrl) return;
    
    if (el.dataset && (el.dataset.depthDead === depthUrl || el.dataset.depthDead === colorUrl)) return;

    

    

    

    
    
    var pending = { _pending: true, destroy: function() {} };
    instances[id] = pending;

    var colorImg = new Image();
    var depthImg = new Image();
    var loaded   = 0;

    function onLoad() {
        loaded++;

        
        if (loaded === 2 && instances[id] === pending) start(el, id, colorImg, depthImg, strength, useOscil, useChroma);
    }

    colorImg.crossOrigin = /^data:/.test(colorUrl) ? null : 'anonymous';
    depthImg.crossOrigin = /^data:/.test(depthUrl) ? null : 'anonymous';
    colorImg.onload = onLoad;
    depthImg.onload = onLoad;

    

    
    function _signalDead(kind, url) {
        if (window._impulseDepthDebug) console.warn('[depth-parallax] ' + kind + ' load failed:', url);
        try {
            if (window.parent && window.parent !== window) {
                window.parent.postMessage({ type: 'impulse-depth-image-dead', kind: kind, url: url }, '*');
            }
        } catch(e) {}
    }

    
    
    function _markDead(kind, url) {
        try { el.dataset.depthDead = url; } catch (e) {}
        if (instances[id] === pending) delete instances[id];
        _signalDead(kind, url);
    }
    colorImg.onerror = function() { _markDead('color', colorUrl); };
    depthImg.onerror = function() { _markDead('depth', depthUrl); };
    colorImg.src = colorUrl;
    depthImg.src = depthUrl;
}

function destroySection(el) {
    var id = (el === document.body) ? '__body__' : (el.dataset.id || el.id);
    if (!id || !instances[id]) return;
    if (typeof instances[id].destroy === 'function') instances[id].destroy();
    delete instances[id];
}

function destroyAll() {
    Object.keys(instances).forEach(function(id) {
        if (instances[id] && typeof instances[id].destroy === 'function') instances[id].destroy();
    });
    instances = {};
}

function _wmDrawPattern(cx, w, h) {
    cx.clearRect(0, 0, w, h);
    var unit = Math.min(w, h);
    var fs   = Math.max(14, Math.round(unit * 0.045));
    var main = 'Instinctor', sub = 'Upgrade to remove';
    var mainFont = '700 ' + fs + 'px system-ui, -apple-system, "Segoe UI", Arial, sans-serif';
    var subFont  = '600 ' + Math.round(fs * 0.46) + 'px system-ui, -apple-system, "Segoe UI", Arial, sans-serif';
    cx.save();
    cx.translate(w / 2, h / 2);
    cx.rotate(-28 * Math.PI / 180);
    cx.textAlign = 'center'; cx.textBaseline = 'middle';
    cx.lineJoin = 'round';
    cx.shadowBlur = 0;
    var diag = Math.sqrt(w * w + h * h);
    cx.font = mainFont;
    var stepY = fs * 7.4, stepX = cx.measureText(main).width + fs * 6.5;
    var rows = Math.ceil(diag / stepY) + 1, cols = Math.ceil(diag / stepX) + 1;
    for (var r = -rows; r <= rows; r++) {
        var offX = (r % 2 === 0) ? 0 : stepX / 2, y = r * stepY;
        for (var c = -cols; c <= cols; c++) {
            var x = c * stepX + offX;
            cx.font = mainFont;
            cx.lineWidth = Math.max(1.5, fs * 0.045); cx.strokeStyle = 'rgba(0,0,0,0.22)';
            cx.strokeText(main, x, y - fs * 0.5);
            cx.fillStyle = 'rgba(255,255,255,0.30)';
            cx.fillText(main, x, y - fs * 0.5);
            cx.font = subFont;
            cx.lineWidth = Math.max(1, fs * 0.03); cx.strokeStyle = 'rgba(0,0,0,0.22)';
            cx.strokeText(sub, x, y + fs * 0.42);
            cx.fillStyle = 'rgba(255,255,255,0.20)';
            cx.fillText(sub, x, y + fs * 0.42);
        }
    }
    cx.restore();
}

function start(el, id, colorImg, depthImg, strength, useOscil, useChroma) {
    var isBodyEl = (el === document.body);

    
    var canvas = document.createElement('canvas');
    if (isBodyEl) {

        
        
        canvas.id = 'impulse-depth-canvas';
        canvas.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:-1';

        
        var _staleDp = document.getElementById('impulse-depth-canvas');
        if (_staleDp && _staleDp.parentNode) _staleDp.parentNode.removeChild(_staleDp);
        document.body.appendChild(canvas);
    } else {
        canvas.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:0';
        el.style.position = el.style.position || 'relative';
        el.insertBefore(canvas, el.firstChild);
    }

    var hiddenImgs = isBodyEl ? [] : Array.prototype.slice.call(el.querySelectorAll('img'));
    hiddenImgs.forEach(function(img) { img.style.visibility = 'hidden'; });

    var gl = canvas.getContext('webgl', { alpha: false, antialias: false });
    if (!gl) { canvas.remove(); return; }

    function compile(type, src) {
        var s = gl.createShader(type);
        gl.shaderSource(s, src);
        gl.compileShader(s);
        if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
            console.error('[depth-parallax] Shader error:', gl.getShaderInfoLog(s));
            return null;
        }
        return s;
    }

    var vs = compile(gl.VERTEX_SHADER, VERT);
    var fs = compile(gl.FRAGMENT_SHADER, FRAG);
    if (!vs || !fs) { canvas.remove(); return; }

    var prog = gl.createProgram();
    gl.attachShader(prog, vs); gl.attachShader(prog, fs);
    gl.linkProgram(prog);
    if (!gl.getProgramParameter(prog, gl.LINK_STATUS)) {
        console.error('[depth-parallax] Link error:', gl.getProgramInfoLog(prog));
        canvas.remove(); return;
    }
    gl.useProgram(prog);

    var buf = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, buf);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,-1, 1,-1, -1,1, 1,1]), gl.STATIC_DRAW);
    var aPos = gl.getAttribLocation(prog, 'a_pos');
    gl.enableVertexAttribArray(aPos);
    gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);

    var uColor    = gl.getUniformLocation(prog, 'u_color');
    var uDepth    = gl.getUniformLocation(prog, 'u_depth');
    var uMouse    = gl.getUniformLocation(prog, 'u_mouse');
    var uStrength = gl.getUniformLocation(prog, 'u_strength');
    var uChroma   = gl.getUniformLocation(prog, 'u_chroma');
    var uTime     = gl.getUniformLocation(prog, 'u_time');
    var uShake    = gl.getUniformLocation(prog, 'u_shake');
    var uAspect   = gl.getUniformLocation(prog, 'u_aspect');
    var uImgAspect = gl.getUniformLocation(prog, 'u_img_aspect');
    var uFocal    = gl.getUniformLocation(prog, 'u_focal');
    var uWmTex    = gl.getUniformLocation(prog, 'u_wm_tex');
    var uWm       = gl.getUniformLocation(prog, 'u_wm');

    function uploadTex(unit, img) {
        var tex = gl.createTexture();
        gl.activeTexture(gl.TEXTURE0 + unit);
        gl.bindTexture(gl.TEXTURE_2D, tex);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        return tex;
    }

    var colorTex = uploadTex(0, colorImg);
    var depthTex = uploadTex(1, depthImg);

    gl.uniform1i(uColor, 0);
    gl.uniform1i(uDepth, 1);
    gl.uniform1f(uStrength, strength);
    gl.uniform2f(uChroma, 0.0, 0.0);
    gl.uniform1f(uTime, 0.0);
    gl.uniform2f(uShake, 0.0, 0.0);
    gl.uniform1f(uImgAspect, colorImg.naturalWidth / (colorImg.naturalHeight || 1));

    

    
    
    function _parseFocal(s) {
        if (!s) return null;
        var p = String(s).split(',');
        var fx = parseFloat(p[0]), fy = parseFloat(p[1]);
        if (isNaN(fx) || isNaN(fy)) return null;
        return [Math.max(0, Math.min(1, fx)), Math.max(0, Math.min(1, fy))];
    }
    var _ga = el.getAttribute ? function(n){ return el.getAttribute(n); } : function(){ return null; };
    var _focalD = _parseFocal(_ga('data-depth-focal'))        || [0.5, 0.5];
    var _focalT = _parseFocal(_ga('data-depth-focal-tablet'))  || _focalD;
    var _focalM = _parseFocal(_ga('data-depth-focal-mobile'))  || _focalT;
    function _activeFocal() {
        var w = window.innerWidth || 0;
        if (w > 0 && w <= 767)  return _focalM;
        if (w > 0 && w <= 1024) return _focalT;
        return _focalD;
    }
    var _liveFocal = null; 
    function _applyFocal() {
        var f = _liveFocal || _activeFocal();
        gl.uniform2f(uFocal, f[0], f[1]);
    }
    _applyFocal();

    

    

    
    
    var _inEditorFrame = (function () { try { return !!(window.parent && window.parent !== window); } catch (e) { return false; } })();
    var _wmOn = INSTINCTOR_WM_ON && !_inEditorFrame;
    gl.uniform1i(uWmTex, 2);
    gl.uniform1f(uWm, 0.0);
    var wmTex = gl.createTexture();
    gl.activeTexture(gl.TEXTURE2);
    gl.bindTexture(gl.TEXTURE_2D, wmTex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.activeTexture(gl.TEXTURE0);
    function _buildWm(w, h) {
        if (!_wmOn) return;
        var wc = document.createElement('canvas');
        wc.width  = Math.max(1, w | 0);
        wc.height = Math.max(1, h | 0);
        var wcx = wc.getContext('2d');
        if (!wcx) return;
        _wmDrawPattern(wcx, wc.width, wc.height);
        gl.activeTexture(gl.TEXTURE2);
        gl.bindTexture(gl.TEXTURE_2D, wmTex);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, wc);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
        gl.activeTexture(gl.TEXTURE0);
        gl.uniform1f(uWm, 1.0);
    }

    var ro = new ResizeObserver(function() {
        var w = isBodyEl ? window.innerWidth  : el.offsetWidth;
        var h = isBodyEl ? window.innerHeight : el.offsetHeight;

        
        var dpr = Math.min(window.devicePixelRatio || 1, 2);
        var bw  = Math.max(1, Math.round(w * dpr));
        var bh  = Math.max(1, Math.round(h * dpr));
        canvas.width  = bw;
        canvas.height = bh;
        gl.viewport(0, 0, bw, bh);
        gl.uniform1f(uAspect, w / (h || 1));
        _applyFocal();           
        _buildWm(bw, bh);        
    });
    ro.observe(el);

    var mx = 0.5, my = 0.5, tmx = 0.5, tmy = 0.5;
    var _mouseActive = false, _mouseIdleTimer = null, _mouseIdleSince = 0;

    function _markActive() {
        _mouseActive = true;
        clearTimeout(_mouseIdleTimer);
        _mouseIdleTimer = setTimeout(function() {
            _mouseActive = false;
            _mouseIdleSince = performance.now();
        }, 800);
    }

    function onMove(e) {
        var r = el.getBoundingClientRect();
        tmx = (e.clientX - r.left) / r.width;
        tmy = (e.clientY - r.top)  / r.height;
        _markActive();
    }

    
    
    var _gyroBaseB = null, _gyroBaseG = null, _gyroX = 0, _gyroY = 0;
    var _touchAnchorX = 0.5;       
    var _touchDrag = false;        
    var _touchStartPx = 0;         
    var _touchStartAnchor = 0.5;   
    var _touchAxis = null;         

    function onGyro(e) {
        if (e.beta === null && e.gamma === null) return;
        var b = e.beta || 0, g = e.gamma || 0;
        if (_gyroBaseB === null) { _gyroBaseB = b; _gyroBaseG = g; }
        
        _gyroBaseB += (b - _gyroBaseB) * 0.005;
        _gyroBaseG += (g - _gyroBaseG) * 0.005;

        

        
        
        if (!_touchDrag) {
            _gyroX = Math.max(-1, Math.min(1, (g - _gyroBaseG) / 10)) * 0.8;
            _gyroY = Math.max(-1, Math.min(1, (b - _gyroBaseB) / 10)) * 0.8;
        }
    }

    var _touchStartPxY = 0;        
    var _touchTarget = isBodyEl ? document : el;
    function onTouchStart(e) {
        if (e.touches.length !== 1) return;
        _touchDrag = true;
        _touchAxis = null;
        _touchStartPx = e.touches[0].clientX;
        _touchStartPxY = e.touches[0].clientY;
        _touchStartAnchor = _touchAnchorX;
    }
    function onTouchMove(e) {
        if (!_touchDrag || e.touches.length !== 1) return;
        var dx = e.touches[0].clientX - _touchStartPx;
        var dy = e.touches[0].clientY - _touchStartPxY;
        
        if (!_touchAxis) {
            if (Math.abs(dx) < 8 && Math.abs(dy) < 8) return; 
            _touchAxis = Math.abs(dx) >= Math.abs(dy) ? 'h' : 'v';
        }
        if (_touchAxis !== 'h') { _touchDrag = false; return; } 
        var r = (isBodyEl ? { width: window.innerWidth } : el.getBoundingClientRect());
        _touchAnchorX = Math.min(1, Math.max(0, _touchStartAnchor + dx / r.width));
        tmx = _touchAnchorX;
        _markActive();
    }
    function onTouchEnd() {
        _touchDrag = false;
        _touchAxis = null;
    }
    _touchTarget.addEventListener('touchstart', onTouchStart, { passive: true });
    document.addEventListener('touchmove', onTouchMove, { passive: true });
    document.addEventListener('touchend', onTouchEnd, { passive: true });

    document.addEventListener('mousemove', onMove);
    if (window._impulseGyro) window._impulseGyro.register(onGyro);
    else window.addEventListener('deviceorientation', onGyro);

    

    

    

    

    
    var animId;
    var _frameToggle = 0;
    function render() {
        animId = requestAnimationFrame(render);

        _frameToggle = 1 - _frameToggle;
        if (_frameToggle) return;

        var t = performance.now() / 1000;

        

        if (!_mouseActive && useOscil) {
            var fadeIn = Math.min(1, (performance.now() - _mouseIdleSince) / 2000);
            var ox = Math.sin(t * 0.11) * 0.06 + Math.sin(t * 0.07) * 0.03;
            var oy = Math.sin(t * 0.09 + 1.2) * 0.04 + Math.sin(t * 0.13) * 0.02;
            tmx = Math.min(1, Math.max(0, _touchAnchorX + ox * fadeIn + _gyroX));
            tmy = Math.min(1, Math.max(0, 0.5 + oy * fadeIn + _gyroY));
        }
        mx += (tmx - mx) * 0.04;
        my += (tmy - my) * 0.04;

        gl.uniform2f(uMouse, mx, my);
        gl.uniform1f(uTime, t);

        var shx = (Math.sin(t * 0.37) * 0.4 + Math.sin(t * 0.83) * 0.3 + Math.sin(t * 1.17) * 0.2) * 0.0008;
        gl.uniform2f(uShake, shx, 0.0);

        var breathe = useOscil ? 1.0 + Math.sin(t * 0.57) * 0.20 : 1.0;
        gl.uniform1f(uStrength, strength * breathe);

        var cx = useChroma ? (mx - 0.5) * 0.004 + Math.sin(t * 0.23) * 0.002 : 0.0;
        var cy = useChroma ? (my - 0.5) * 0.003 + Math.sin(t * 0.19 + 0.8) * 0.0015 : 0.0;
        gl.uniform2f(uChroma, cx, cy);

        gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);

        

    }
    render();

    instances[id] = {
        canvas: canvas,
        gl: gl,
        prog: prog,
        setMouse: function(x, y) { tmx = x; tmy = y; },
        setStrength: function(s) { strength = s; },

        setFocal: function(fx, fy) {
            _liveFocal = (fx === null || fx === undefined) ? null
                       : [Math.max(0, Math.min(1, fx)), Math.max(0, Math.min(1, fy))];
            _applyFocal();
        },
        destroy: function() {
            cancelAnimationFrame(animId);
            ro.disconnect();
            document.removeEventListener('mousemove', onMove);
            _touchTarget.removeEventListener('touchstart', onTouchStart);
            document.removeEventListener('touchmove', onTouchMove);
            document.removeEventListener('touchend', onTouchEnd);
            if (window._impulseGyro) window._impulseGyro.unregister(onGyro);
            else window.removeEventListener('deviceorientation', onGyro);
            gl.deleteTexture(colorTex);
            gl.deleteTexture(depthTex);
            gl.deleteBuffer(buf);
            gl.deleteProgram(prog);
            if (canvas.parentNode) canvas.parentNode.removeChild(canvas);
            hiddenImgs.forEach(function(img) { img.style.visibility = ''; });
        }
    };
}

var _ioInstance = null;
function _ensureIO() {
    if (_ioInstance || typeof IntersectionObserver === 'undefined') return _ioInstance;
    _ioInstance = new IntersectionObserver(function(entries) {
        entries.forEach(function(entry) {
            var el = entry.target;
            if (entry.isIntersecting) {
                
                initSection(el);
            } else {

                
                
                destroySection(el);
            }
        });
    }, { rootMargin: '75% 0% 75% 0%', threshold: 0 });
    return _ioInstance;
}

function _armSection(el) {
    var io = _ensureIO();
    if (!io) { initSection(el); return; }  
    if (el._depthIOArmed) return;
    el._depthIOArmed = true;

    

    
    var r = el.getBoundingClientRect();
    var vh = window.innerHeight || document.documentElement.clientHeight || 0;
    var vw = window.innerWidth  || document.documentElement.clientWidth  || 0;
    var marginV = vh * 0.75;
    var marginH = vw * 0.75;
    if (r.bottom > -marginV && r.top  < vh + marginV
     && r.right  > -marginH && r.left < vw + marginH) {
        initSection(el);
    }
    io.observe(el);
}

function initAll() {
    document.querySelectorAll('[data-depth-image][data-depth-map]').forEach(function(el) {

        if (el.getAttribute('data-depth-mode') === 'relief') return;

        if (el === document.body) {
            var eff = el.getAttribute('data-screen-cloud-effect') || el.getAttribute('data-cloud-effect');
            var enabled = el.getAttribute('data-depth-enabled');
            if (eff !== 'depth_image' && enabled !== '1') return;
            initSection(el);
            return;
        }

        

        
        _armSection(el);
    });
}

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

(function(){
    if (typeof MutationObserver === 'undefined') return;
    var pending = null;
    var mo = new MutationObserver(function(records){
        
        if (pending) return;
        pending = setTimeout(function(){
            pending = null;
            initAll();
        }, 30);
    });
    var startObserve = function(){
        if (document.body) mo.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-depth-image','data-depth-map'] });
    };
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', startObserve);
    } else {
        startObserve();
    }
})();

window.addEventListener('message', function(e) {
    if (!e.data) return;
    if (e.data.type === 'impulse-depth-mouse') {
        Object.keys(instances).forEach(function(id) {
            if (instances[id] && instances[id].setMouse) instances[id].setMouse(e.data.x, e.data.y);
        });
    }
    if (e.data.type === 'impulse-depth-strength') {

        
        
        var _s = parseFloat(e.data.strength);
        if (isNaN(_s)) _s = 0.5;
        Object.keys(instances).forEach(function(id) {
            var inst = instances[id];
            if (inst && inst.setStrength) inst.setStrength(_s);
        });
    }
    if (e.data.type === 'impulse-depth-focal') {

        var _fx = (e.data.x === null || e.data.x === undefined) ? null : parseFloat(e.data.x);
        var _fy = (e.data.y === null || e.data.y === undefined) ? null : parseFloat(e.data.y);
        Object.keys(instances).forEach(function(id) {
            if (instances[id] && instances[id].setFocal) instances[id].setFocal(_fx, _fy);
        });
    }
    if (e.data.type === 'impulse-depth-init') {
        var el = document.querySelector('.impulse-el[data-id="' + e.data.id + '"]');
        if (el) initSection(el.querySelector('.impulse-section') || el);
    }
    if (e.data.type === 'impulse-depth-destroy') {
        var el = document.querySelector('.impulse-el[data-id="' + e.data.id + '"]');
        if (el) destroySection(el.querySelector('.impulse-section') || el);
    }
});

function setMouseAll(x, y) {
    Object.keys(instances).forEach(function(id) {
        if (instances[id] && instances[id].setMouse) instances[id].setMouse(x, y);
    });
}
window.ImpulseDepthParallax = { init: initSection, initAll: initAll, destroy: destroySection, destroyAll: destroyAll, instances: instances, setMouse: setMouseAll };

})();
