// Landing — efectos: fondo dither WebGL animado e interactivo, imanes, tilt, contadores
const DITHER_VERT = `attribute vec2 p;void main(){gl_Position=vec4(p,0.,1.);}`;
const DITHER_FRAG = `precision highp float;
uniform vec2 u_res;uniform float u_time;uniform vec2 u_mouse;uniform float u_mouse_on;uniform float u_cell;uniform float u_motion;uniform float u_sky;
float hash(vec2 p){p=fract(p*vec2(123.34,456.21));p+=dot(p,p+45.32);return fract(p.x*p.y);}
float noise(vec2 p){vec2 i=floor(p),f=fract(p);f=f*f*(3.-2.*f);float a=hash(i),b=hash(i+vec2(1.,0.)),c=hash(i+vec2(0.,1.)),d=hash(i+vec2(1.,1.));return mix(mix(a,b,f.x),mix(c,d,f.x),f.y);}
float fbm(vec2 p){float v=0.,a=.5;for(int i=0;i<4;i++){v+=a*noise(p);p=p*2.1+vec2(7.3,1.7);a*=.5;}return v;}
float bayer2(vec2 a){a=floor(a);return fract(a.x/2.+a.y*a.y*.75);}
float bayer4(vec2 a){return bayer2(.5*a)*.25+bayer2(a);}
vec3 q(vec3 c){vec3 best=vec3(0.);float bd=1e9;
#define T(col) {vec3 P=col;float d=dot(c-P,c-P);if(d<bd){bd=d;best=P;}}
T(vec3(.988,.988,.992))T(vec3(.878,.953,.973))T(vec3(.722,.894,.949))T(vec3(.800,.808,.965))T(vec3(.643,.769,.941))T(vec3(.933,.969,.769))T(vec3(.784,.925,.431))T(vec3(.643,.839,.306))T(vec3(.471,.667,.259))T(vec3(.329,.471,.220))T(vec3(.204,.314,.188))T(vec3(.925,.886,.698))
return best;}
void main(){
vec2 px=floor(gl_FragCoord.xy/u_cell);vec2 uv=(px+.5)/(u_res/u_cell);uv.y=1.-uv.y;
float t=u_time*u_motion;float asp=u_res.x/u_res.y;
vec2 dv=(uv-u_mouse)*vec2(asp,1.);float d=length(dv);
float rip=sin(d*26.-u_time*3.2)*exp(-d*4.5)*.03*u_mouse_on;
vec2 w=uv+normalize(dv+vec2(1e-4))*rip;
w.y+=.008*sin(w.x*7.+t*.9)*u_motion;
float u=w.x,v=w.y;
if(u_sky>.5){v*=.55;vec3 col=mix(vec3(.588,.824,.933),vec3(.886,.953,.973),clamp(v/.6,0.,1.));
float cl=fbm(vec2(u*3.5+11.+t*.06,v*6.+3.+t*.01));col=mix(col,vec3(1.),clamp((cl-.42)/.22,0.,1.));
float lv=fbm(vec2(u*2.+20.-t*.03,v*3.));col=mix(col,vec3(.784,.784,.965),clamp((lv-.58)*4.,0.,1.));
gl_FragColor=vec4(q(col+(bayer4(px)-.5)*.2),1.);return;}
float r3=.58+.12*fbm(vec2(u*3.+7.+t*.02,3.3));float r2=.70+.12*fbm(vec2(u*4.+2.+t*.035,9.1));float r1=.84+.07*fbm(vec2(u*2.+5.+t*.05,1.2));
vec3 col;
if(v<r3){col=mix(vec3(.588,.824,.933),vec3(.886,.953,.973),clamp(v/.6,0.,1.));
float cl=fbm(vec2(u*3.5+11.+t*.06,v*6.+3.+t*.01));col=mix(col,vec3(1.),clamp((cl-.42)/.22,0.,1.));
float lv=fbm(vec2(u*2.+20.-t*.03,v*3.));col=mix(col,vec3(.784,.784,.965),clamp((lv-.58)*4.,0.,1.));}
else if(v<r2)col=mix(vec3(.518,.659,.376),vec3(.361,.502,.267),fbm(vec2(u*8.,v*8.+50.)));
else if(v<r1)col=mix(vec3(.784,.925,.431),vec3(.588,.784,.275),fbm(vec2(u*10.+3.,v*10.+70.)));
else col=mix(vec3(.667,.839,.306),vec3(.455,.651,.243),fbm(vec2(u*6.+9.,v*12.+90.)));
col+=exp(-d*6.)*.14*u_mouse_on;
gl_FragColor=vec4(q(col+(bayer4(px)-.5)*.2),1.);}`;

function Dither({ cell = 3, motion = 1, interactive = false, sky = false, className = "dither" }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const c = ref.current, gl = c.getContext("webgl", { antialias: false, alpha: false, powerPreference: "low-power" });
    if (!gl) { c.style.background = "linear-gradient(#bfe6f3,#dff3c9 60%,#a4d64e)"; c.dataset.ready = "1"; return; }
    const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches, hover = matchMedia("(hover:hover)").matches;
    const mk = (t, s) => { const sh = gl.createShader(t); gl.shaderSource(sh, s); gl.compileShader(sh); if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) console.error(gl.getShaderInfoLog(sh)); return sh; };
    const prog = gl.createProgram(); gl.attachShader(prog, mk(gl.VERTEX_SHADER, DITHER_VERT)); gl.attachShader(prog, mk(gl.FRAGMENT_SHADER, DITHER_FRAG)); gl.linkProgram(prog); gl.useProgram(prog);
    const 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);
    const loc = gl.getAttribLocation(prog, "p"); gl.enableVertexAttribArray(loc); gl.vertexAttribPointer(loc, 2, gl.FLOAT, false, 0, 0);
    const U = n => gl.getUniformLocation(prog, n), uRes = U("u_res"), uTime = U("u_time"), uMouse = U("u_mouse"), uOn = U("u_mouse_on"), uCell = U("u_cell"), uMotion = U("u_motion"), uSky = U("u_sky");
    gl.uniform1f(uSky, sky ? 1 : 0);
    const dpr = Math.min(1.5, window.devicePixelRatio || 1);
    let W = 0, H = 0, raf = 0, visible = true, t0 = performance.now();
    const m = { x: .5, y: .5, tx: .5, ty: .5, on: 0, ton: 0 };
    const frame = (now) => {
      raf = 0; if (!visible || document.hidden) return;
      m.x += (m.tx - m.x) * .08; m.y += (m.ty - m.y) * .08; m.on += (m.ton - m.on) * .06;
      gl.uniform2f(uRes, W, H); gl.uniform1f(uTime, (now - t0) / 1000); gl.uniform2f(uMouse, m.x, m.y); gl.uniform1f(uOn, m.on); gl.uniform1f(uCell, cell * dpr); gl.uniform1f(uMotion, reduce ? 0 : motion);
      gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); c.dataset.ready = "1";
      if (!reduce) raf = requestAnimationFrame(frame);
    };
    const kick = () => { if (!raf && visible && !document.hidden) raf = requestAnimationFrame(frame); };
    const resize = () => { W = Math.max(1, Math.round(c.clientWidth * dpr)); H = Math.max(1, Math.round(c.clientHeight * dpr)); if (c.width !== W || c.height !== H) { c.width = W; c.height = H; gl.viewport(0, 0, W, H); } kick(); };
    const ro = new ResizeObserver(resize); ro.observe(c); resize();
    const io = new IntersectionObserver(([e]) => { visible = e.isIntersecting; kick(); }); io.observe(c);
    const host = c.parentElement;
    const move = e => { const r = c.getBoundingClientRect(); m.tx = (e.clientX - r.left) / r.width; m.ty = (e.clientY - r.top) / r.height; m.ton = 1; kick(); };
    const leave = () => { m.ton = 0; };
    if (interactive && hover) { host.addEventListener("pointermove", move); host.addEventListener("pointerleave", leave); }
    document.addEventListener("visibilitychange", kick);
    return () => { cancelAnimationFrame(raf); ro.disconnect(); io.disconnect(); document.removeEventListener("visibilitychange", kick); host.removeEventListener("pointermove", move); host.removeEventListener("pointerleave", leave); const lose = gl.getExtension("WEBGL_lose_context"); if (lose) lose.loseContext(); };
  }, []);
  return <canvas ref={ref} className={className} aria-hidden="true"></canvas>;
}

const canHover = () => matchMedia("(hover:hover)").matches && !matchMedia("(prefers-reduced-motion: reduce)").matches;
function Magnetic({ as: Tag = "a", strength = .32, className = "", children, ...props }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el || !canHover()) return;
    const move = e => { const r = el.getBoundingClientRect(); el.style.transform = `translate(${(e.clientX - r.left - r.width / 2) * strength}px,${(e.clientY - r.top - r.height / 2) * strength}px)`; };
    const leave = () => { el.style.transform = ""; };
    el.addEventListener("pointermove", move); el.addEventListener("pointerleave", leave);
    return () => { el.removeEventListener("pointermove", move); el.removeEventListener("pointerleave", leave); };
  }, []);
  return <Tag ref={ref} className={`${className} mag`} {...props}>{children}</Tag>;
}

function useTilt(max = 5) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el || !canHover()) return;
    let raf = 0, rx = 0, ry = 0;
    const move = e => { const r = el.getBoundingClientRect(), px = (e.clientX - r.left) / r.width - .5, py = (e.clientY - r.top) / r.height - .5; rx = -py * max * 2; ry = px * max * 2; el.style.setProperty("--mx", `${(px + .5) * 100}%`); el.style.setProperty("--my", `${(py + .5) * 100}%`); if (!raf) raf = requestAnimationFrame(() => { raf = 0; el.style.transform = `perspective(1100px) rotateX(${rx}deg) rotateY(${ry}deg)`; }); };
    const leave = () => { cancelAnimationFrame(raf); raf = 0; el.style.transform = ""; };
    el.addEventListener("pointermove", move); el.addEventListener("pointerleave", leave);
    return () => { el.removeEventListener("pointermove", move); el.removeEventListener("pointerleave", leave); cancelAnimationFrame(raf); };
  }, []);
  return ref;
}
function Tilt({ className = "", style, children, max }) { const ref = useTilt(max); return <article ref={ref} className={`${className} tilt`} style={style}>{children}</article>; }

function CountUp({ to, decimals = 0, prefix = "", suffix = "", duration = 1600 }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current;
    const fmt = n => { const [i, d] = n.toFixed(decimals).split("."); return prefix + i.replace(/\B(?=(\d{3})+(?!\d))/g, ".") + (d ? "," + d : "") + suffix; };
    if (matchMedia("(prefers-reduced-motion: reduce)").matches) { el.textContent = fmt(to); return; }
    el.textContent = fmt(0);
    const io = new IntersectionObserver(([e]) => { if (!e.isIntersecting) return; io.disconnect(); const t0 = performance.now(); const step = now => { const p = Math.min(1, (now - t0) / duration), k = 1 - Math.pow(1 - p, 3); el.textContent = fmt(to * k); if (p < 1) requestAnimationFrame(step); }; requestAnimationFrame(step); }, { threshold: .4 });
    io.observe(el); return () => io.disconnect();
  }, []);
  return <span ref={ref}></span>;
}

Object.assign(window, { Dither, Magnetic, Tilt, CountUp, canHover });
