
(function () {
  'use strict';
  if (typeof window === 'undefined') return;

  function clamp(v, a, b) { return v < a ? a : (v > b ? b : v); }

  

  function focalFromEl(el) {
    function parse(v) {
      if (!v) return null;
      var p = String(v).split(',');
      var x = parseFloat(p[0]), y = parseFloat(p[1]);
      if (isNaN(x) || isNaN(y)) return null;
      return [clamp(x, 0, 1), clamp(y, 0, 1)];
    }
    if (!el || !el.getAttribute) return [0.5, 0.5];
    var fD = parse(el.getAttribute('data-depth-focal'))        || [0.5, 0.5];
    var fT = parse(el.getAttribute('data-depth-focal-tablet')) || fD;
    var fM = parse(el.getAttribute('data-depth-focal-mobile')) || fT;
    var w = window.innerWidth || 0;
    if (w > 0 && w <= 767)  return fM;
    if (w > 0 && w <= 1024) return fT;
    return fD;
  }

  function init(container, opts) {
    opts = opts || {};
    var THREE = window.THREE;
    if (!THREE) { console.warn('ImpulseDepthMesh: THREE not loaded'); return null; }

    var colorUrl = opts.colorUrl || container.getAttribute('data-depth-image');
    var depthUrl = opts.depthUrl || container.getAttribute('data-depth-map');
    if (!colorUrl || !depthUrl) { console.warn('ImpulseDepthMesh: missing image/depth'); return null; }

    var LIGHT_MAX = 0.7, DOLLY_MAX = 0.1;
    var strength = clamp(opts.strength != null ? opts.strength : 0.5, 0, LIGHT_MAX); 
    var overscan = opts.zoom     != null ? opts.zoom     : 1.0;   
    var travel   = clamp(opts.travel != null ? opts.travel : 0.07, 0, DOLLY_MAX);    
    var pivot    = clamp(opts.pivot  != null ? opts.pivot  : 0.5, 0, 1);             
    var invert   = !!opts.invertDepth;
    var segments = opts.segments || 220;

    
    var focalAuto = !opts.focal;
    var focal     = opts.focal || focalFromEl(container);

    var canvas = document.createElement('canvas');
    canvas.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;display:block';

    try { if (window.getComputedStyle(container).position === 'static') container.style.position = 'relative'; } catch (e) {}
    container.appendChild(canvas);

    var renderer = new THREE.WebGLRenderer({ canvas: canvas, antialias: true, alpha: true });
    renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2));

    
    if (THREE.LinearSRGBColorSpace) renderer.outputColorSpace = THREE.LinearSRGBColorSpace;

    var scene  = new THREE.Scene();

    
    var camera = new THREE.OrthographicCamera(-1, 1, 1, -1, -10, 100);
    camera.position.set(0, 0, 5);
    var meshHW = 1, meshHH = 1; 

    var loader = new THREE.TextureLoader();
    loader.setCrossOrigin('anonymous');

    
    function _dead(url) {
        return function () {
            try { if (el && el.dataset) el.dataset.depthDead = url; } catch (e) {}
            if (window._impulseDepthDebug) console.warn('[depth-mesh] load failed:', url);
        };
    }
    var colorTex = loader.load(colorUrl, onColor, undefined, _dead(colorUrl));
    var depthTex = loader.load(depthUrl, onDepth, undefined, _dead(depthUrl));
    if (THREE.LinearSRGBColorSpace) colorTex.colorSpace = THREE.LinearSRGBColorSpace; 
    depthTex.minFilter = THREE.LinearFilter;
    depthTex.magFilter = THREE.LinearFilter;

    var geo = new THREE.PlaneGeometry(2, 2, segments, segments);
    var mat = new THREE.ShaderMaterial({
      uniforms: {
        u_color:    { value: colorTex },
        u_depth:    { value: depthTex },
        u_strength: { value: strength },
        u_invert:   { value: invert ? 1.0 : 0.0 },
        u_texel:    { value: new THREE.Vector2(1 / 1024, 1 / 1024) },
        u_parallax: { value: new THREE.Vector2(0, 0) },
        u_pivot:    { value: pivot },
        u_time:     { value: 0 },
        u_light:    { value: new THREE.Vector3(-0.35, 0.5, 0.9) }
      },
      vertexShader: [
        'uniform sampler2D u_depth;',
        'uniform float u_strength;',
        'uniform float u_invert;',
        'uniform vec2 u_texel;',
        'uniform vec2 u_parallax;',
        'uniform float u_pivot;',
        'varying vec2 vUv;',
        'varying vec3 vNormal;',
        'varying float vDepth;',
        'float raw(vec2 uv){ float d = texture2D(u_depth, uv).r; return mix(d, 1.0 - d, u_invert); }',

        'float depthAt(vec2 uv){',
        '  vec2 t = u_texel * 2.0;',
        '  float s = raw(uv) * 0.28;',
        '  s += (raw(uv+vec2(t.x,0.)) + raw(uv-vec2(t.x,0.)) + raw(uv+vec2(0.,t.y)) + raw(uv-vec2(0.,t.y))) * 0.12;',
        '  s += (raw(uv+t) + raw(uv-t) + raw(uv+vec2(t.x,-t.y)) + raw(uv+vec2(-t.x,t.y))) * 0.06;',
        '  return s;',
        '}',
        'void main(){',
        '  vUv = uv;',
        '  float d  = depthAt(uv);',
        '  vDepth = d;',
        '  float dl = depthAt(uv - vec2(u_texel.x, 0.0));',
        '  float dr = depthAt(uv + vec2(u_texel.x, 0.0));',
        '  float dd = depthAt(uv - vec2(0.0, u_texel.y));',
        '  float du = depthAt(uv + vec2(0.0, u_texel.y));',
        '  vNormal = normalize(vec3((dl - dr) * u_strength * 9.0, (dd - du) * u_strength * 9.0, 1.0));',

        '  vec3 pos = position;',
        '  pos.z += (d - 0.5) * u_strength;',

        
        
        '  pos.xy += u_parallax * (d - u_pivot);',
        '  gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);',
        '}'
      ].join('\n'),
      fragmentShader: [
        'precision highp float;',
        'uniform sampler2D u_color;',
        'uniform vec3 u_light;',
        'uniform float u_time;',
        'uniform vec2 u_texel;',
        'varying vec2 vUv;',
        'varying vec3 vNormal;',
        'varying float vDepth;',
        'float hash(vec2 p){ p = fract(p * vec2(127.1, 311.7)); p += dot(p, p + 19.19); return fract(p.x * p.y); }',
        'void main(){',

        
        '  float rEdge = smoothstep(0.55, 1.0, length(vUv - 0.5) * 1.9);',
        '  vec2 chroma = vec2(u_texel.x, 0.0) * rEdge * 0.55;',   
        '  vec3 col;',
        '  col.r = texture2D(u_color, vUv + chroma).r;',
        '  col.g = texture2D(u_color, vUv).g;',
        '  col.b = texture2D(u_color, vUv - chroma).b;',

        '  vec3 N = normalize(vNormal);',
        '  vec3 L = normalize(u_light);',
        '  float diff = dot(N, L);',
        '  float shade = clamp(1.0 + (diff - 0.83) * 0.5, 0.72, 1.28);',
        '  vec3 H = normalize(L + vec3(0.0, 0.0, 1.0));',
        
        '  float spec = pow(clamp(dot(N, H), 0.0, 1.0), 28.0) * 0.10 * (0.4 + 0.6 * vDepth);',
        '  col = col * shade + spec;',
        
        '  float far = (1.0 - vDepth) * (1.0 - vDepth);',
        '  float luma = dot(col, vec3(0.299, 0.587, 0.114));',
        '  col = mix(col, vec3(luma), far * 0.06);',
        '  col = mix(col, col * 0.94 + 0.03, far * 0.10);',
        
        '  float sgn = luma * 2.0 - 1.0;',
        '  col *= 1.0 + sgn * abs(sgn) * (sin(u_time * 1.3) * 0.012 + sin(u_time * 3.7 + 0.9) * 0.005);',
        
        '  col += (hash(vUv + fract(u_time)) - 0.5) * 0.028;',
        '  gl_FragColor = vec4(clamp(col, 0.0, 1.0), 1.0);',
        '}'
      ].join('\n')
    });
    var mesh = new THREE.Mesh(geo, mat);
    scene.add(mesh);

    var mx = 0.5, my = 0.5, tmx = 0.5, tmy = 0.5, t = 0;
    function onMove(e) {
      var r = container.getBoundingClientRect();
      tmx = (e.clientX - r.left) / r.width;
      tmy = (e.clientY - r.top) / r.height;
    }
    function onLeave() { tmx = 0.5; tmy = 0.5; }
    container.addEventListener('mousemove', onMove);
    container.addEventListener('mouseleave', onLeave);

    function onColor(tex) {
      var im = tex.image;
      if (im && im.width) {
        var ar = im.width / im.height;
        if (ar >= 1) { mesh.scale.set(1, 1 / ar, 1); meshHW = 1; meshHH = 1 / ar; }
        else         { mesh.scale.set(ar, 1, 1); meshHW = ar; meshHH = 1; }
        fitCamera();
      }
    }
    function fitCamera() {
      var w = container.clientWidth || 1, h = container.clientHeight || 1;
      var ca = w / h;

      

      var base   = 1.3 * travel * Math.max(pivot, 1.0 - pivot) + 0.015;
      var shrink = Math.max(0.1, 1.0 - base);
      var fhh = Math.min(meshHH, meshHW / ca) * shrink / Math.max(1.0, overscan);
      var fhw = fhh * ca;

      
      
      var sx = Math.max(0, meshHW - fhw), sy = Math.max(0, meshHH - fhh);
      var lx = Math.max(0, sx - base * meshHW), ly = Math.max(0, sy - base * meshHH);
      var cx = clamp((focal[0] - 0.5) * 2 * sx, -lx, lx);
      var cy = clamp((0.5 - focal[1]) * 2 * sy, -ly, ly);
      camera.left = cx - fhw; camera.right = cx + fhw; camera.top = cy + fhh; camera.bottom = cy - fhh;
      camera.updateProjectionMatrix();
    }
    function onDepth(tex) {
      var im = tex.image;
      if (im && im.width) mat.uniforms.u_texel.value.set(1 / im.width, 1 / im.height);
    }
    function resize() {
      var w = container.clientWidth || 1, h = container.clientHeight || 1;
      renderer.setSize(w, h, false);
      if (focalAuto) focal = focalFromEl(container);   
      fitCamera();
    }

    var raf, _lastW = 0, _lastH = 0;
    function tick() {

      
      
      var cw = container.clientWidth || 1, ch = container.clientHeight || 1;
      if (cw !== _lastW || ch !== _lastH) { _lastW = cw; _lastH = ch; renderer.setSize(cw, ch, false); fitCamera(); }
      t += 0.016;
      mx += (tmx - mx) * 0.07;
      my += (tmy - my) * 0.07;

      var px = ((mx - 0.5) * 2.0 * travel + Math.sin(t * 0.35) * travel * 0.12);
      var py = (-(my - 0.5) * 2.0 * travel + Math.cos(t * 0.27) * travel * 0.10);
      mat.uniforms.u_parallax.value.set(px, py);
      mat.uniforms.u_time.value = t;
      renderer.render(scene, camera);
      raf = requestAnimationFrame(tick);
    }

    resize();
    window.addEventListener('resize', resize);

    
    
    var _ro = (typeof ResizeObserver !== 'undefined') ? new ResizeObserver(function () { resize(); }) : null;
    if (_ro) _ro.observe(container);

    setTimeout(resize, 120);
    setTimeout(resize, 600);
    tick();

    return {
      mesh: mesh, material: mat,
      setStrength: function (s) { strength = clamp(s, 0, LIGHT_MAX); mat.uniforms.u_strength.value = strength; },
      setZoom: function (z) { overscan = Math.max(1.0, z); fitCamera(); },
      setTravel: function (a) { travel = clamp(a, 0, DOLLY_MAX); fitCamera(); },
      setPivot: function (p) { pivot = clamp(p, 0, 1); mat.uniforms.u_pivot.value = pivot; fitCamera(); },
      setFocal: function (f) { if (f && f.length === 2) { focal = [clamp(f[0], 0, 1), clamp(f[1], 0, 1)]; focalAuto = false; fitCamera(); } },
      refreshFocal: function () { if (focalAuto) { focal = focalFromEl(container); fitCamera(); } },
      setInvert: function (v) { mat.uniforms.u_invert.value = v ? 1.0 : 0.0; },
      destroy: function () {
        cancelAnimationFrame(raf);
        if (_ro) _ro.disconnect();
        window.removeEventListener('resize', resize);
        container.removeEventListener('mousemove', onMove);
        container.removeEventListener('mouseleave', onLeave);
        geo.dispose(); mat.dispose(); colorTex.dispose(); depthTex.dispose();
        renderer.dispose();
        if (canvas.parentNode) canvas.parentNode.removeChild(canvas);
      }
    };
  }

  

  

  var _live = (typeof WeakMap !== 'undefined') ? new WeakMap() : null;
  function _get(el) { return _live ? _live.get(el) : el.__depthMesh; }
  function _set(el, v) { if (_live) _live.set(el, v); else el.__depthMesh = v; }
  function _del(el) { if (_live) _live['delete'](el); else el.__depthMesh = null; }
  function _num(el, attr, dflt) { var v = parseFloat(el.getAttribute(attr)); return isNaN(v) ? dflt : v; }

  function _optsFromEl(el) {

    
    
    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');
    }
    return {
      colorUrl: colorUrl,
      depthUrl: depthUrl,

      travel:   (_num(el, 'data-depth-strength', 0) / 100) * 0.1,  
      strength: _num(el, 'data-depth-light', 50) / 100,            
      pivot:    _num(el, 'data-depth-pivot',  50) / 100,           
      zoom:     _num(el, 'data-depth-zoom',  100) / 100            
    };
  }
  function _isRelief(el) {
    return !!(el && el.getAttribute && el.getAttribute('data-depth-mode') === 'relief'
      && el.getAttribute('data-depth-image') && el.getAttribute('data-depth-map'));
  }

  
  function _cinematic(el) {
    return el === document.body || el === document.documentElement
      || (el.hasAttribute && (el.hasAttribute('data-depth-enabled') || el.hasAttribute('data-screen-mode')));
  }

  function initEl(el) {
    if (!_isRelief(el) || _cinematic(el) || _get(el)) return;
    
    if (el.dataset && el.dataset.depthDead
        && (el.dataset.depthDead === el.getAttribute('data-depth-map')
         || el.dataset.depthDead === el.getAttribute('data-depth-image'))) return;
    try {
      var cs = window.getComputedStyle(el);
      if (cs && cs.position === 'static') el.style.position = 'relative';
      if (cs && cs.overflow === 'visible') el.style.overflow = 'hidden';
    } catch (e) {}
    var api = init(el, _optsFromEl(el));
    if (api) _set(el, api);
  }
  function destroyEl(el) { var api = _get(el); if (api) { try { api.destroy(); } catch (e) {} _del(el); } }
  function updateEl(el) {
    var api = _get(el);
    if (!api) { initEl(el); return; }
    var o = _optsFromEl(el);
    api.setStrength(o.strength); api.setTravel(o.travel); api.setPivot(o.pivot); api.setZoom(o.zoom);
    if (api.refreshFocal) api.refreshFocal();   
  }

  var _io = null;
  function _obs() {
    if (_io || typeof IntersectionObserver === 'undefined') return _io;
    _io = new IntersectionObserver(function (entries) {
      for (var i = 0; i < entries.length; i++) {
        if (entries[i].isIntersecting) initEl(entries[i].target); else destroyEl(entries[i].target);
      }
    }, { rootMargin: '120px' });
    return _io;
  }
  function _arm(el) {

    
    
    var vis = true;
    try {
      var r = el.getBoundingClientRect();
      var vh = window.innerHeight || 800, vw = window.innerWidth || 1200;
      vis = r.bottom > -200 && r.top < vh + 200 && r.right > -200 && r.left < vw + 200;
    } catch (e) {}
    if (vis) { initEl(el); return; }
    var io = _obs();
    if (io) io.observe(el); else initEl(el);
  }

  function initAll(root) {
    root = root || document;
    var els = root.querySelectorAll('[data-depth-mode="relief"][data-depth-image][data-depth-map]');
    for (var i = 0; i < els.length; i++) _arm(els[i]);
  }
  function destroyAll() { if (_io) { _io.disconnect(); _io = null; } }

  if (typeof document !== 'undefined') {
    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', function () { initAll(); });
    else setTimeout(function () { initAll(); }, 40);

    if (typeof MutationObserver !== 'undefined') {
      var mo = new MutationObserver(function (muts) {
        for (var i = 0; i < muts.length; i++) {
          var m = muts[i];
          if (m.type === 'attributes') {
            if (_isRelief(m.target)) updateEl(m.target); else destroyEl(m.target);
          } else {
            for (var j = 0; j < m.addedNodes.length; j++) {
              var n = m.addedNodes[j];
              if (n.nodeType === 1) { if (_isRelief(n)) _arm(n); initAll(n); }
            }
            for (var k = 0; k < m.removedNodes.length; k++) {
              if (m.removedNodes[k].nodeType === 1) destroyEl(m.removedNodes[k]);
            }
          }
        }
      });
      try {
        mo.observe(document.documentElement, {
          childList: true, subtree: true, attributes: true,
          attributeFilter: ['data-depth-mode', 'data-depth-image', 'data-depth-map', 'data-depth-strength',
            'data-depth-light', 'data-depth-pivot', 'data-depth-zoom',
            'data-depth-focal', 'data-depth-focal-tablet', 'data-depth-focal-mobile']
        });
      } catch (e) {}
    }

    

    window.addEventListener('message', function (ev) {
      var d = ev && ev.data;
      if (!d || d.type !== 'impulse-depth-switch') return;
      var scope = d.id ? '.impulse-el[data-id="' + d.id + '"] ' : '';
      var els = document.querySelectorAll(scope + '[data-depth-image][data-depth-map]');
      for (var i = 0; i < els.length; i++) {
        var el = els[i];
        destroyEl(el);
        var cv = el.querySelectorAll('canvas');
        for (var k = 0; k < cv.length; k++) cv[k].remove();
        if (d.strength != null) el.setAttribute('data-depth-strength', d.strength);
        if (d.mode === 'relief') {
          el.setAttribute('data-depth-mode', 'relief');
          if (d.light != null) el.setAttribute('data-depth-light', d.light);
          if (d.pivot != null) el.setAttribute('data-depth-pivot', d.pivot);
          if (d.zoom != null) el.setAttribute('data-depth-zoom', d.zoom);
        } else {
          el.setAttribute('data-depth-mode', 'parallax');
        }
      }
      if (window.ImpulseDepthParallax) { try { window.ImpulseDepthParallax.destroyAll(); window.ImpulseDepthParallax.initAll(); } catch (e) {} }
      initAll();
    });

    window.addEventListener('message', function (ev) {
      var d = ev && ev.data;
      if (!d || d.type !== 'impulse-depth-relief') return;
      
      var sel = d.id ? '.impulse-el[data-id="' + d.id + '"] [data-depth-mode="relief"][data-depth-image][data-depth-map]'
                     : '[data-depth-mode="relief"][data-depth-image][data-depth-map]';
      var els = document.querySelectorAll(sel);
      for (var i = 0; i < els.length; i++) {
        var el = els[i];
        if (d.strength != null) el.setAttribute('data-depth-strength', d.strength);
        if (d.light != null) el.setAttribute('data-depth-light', d.light);
        if (d.pivot != null) el.setAttribute('data-depth-pivot', d.pivot);
        if (d.zoom != null) el.setAttribute('data-depth-zoom', d.zoom);
        updateEl(el);
      }
    });
  }

  window.ImpulseDepthMesh = { init: init, initAll: initAll, destroyAll: destroyAll, initEl: initEl, destroyEl: destroyEl, updateEl: updateEl };
})();
