// Background depth: two Blender plates of the same hazy room under two lights, and a shade that deepens // towards the end of the page. Scroll sets a target; the layers glide to it with inertia, each on its own // path, so the room drifts past at two depths instead of tracking the wheel. Transforms and opacity only. // Under reduced motion the page keeps the first plate and the shade follows the scroll without movement. const root = document.querySelector(".bg-depth"); const [a, b, shade] = root ? root.children : []; if (a && b && shade) { const still = matchMedia("(prefers-reduced-motion: reduce)").matches; const target = () => { const max = document.documentElement.scrollHeight - innerHeight; return max > 0 ? Math.min(1, Math.max(0, scrollY / max)) : 0; }; let p = target(), last = 0, raf = 0; const draw = () => { shade.style.opacity = Math.pow(p, 1.3).toFixed(3); if (still) return; const e = p * p * (3 - 2 * p); a.style.transform = `translate3d(${(2.6 * Math.sin(p * Math.PI * 1.2)).toFixed(2)}%, ${(-6 * p).toFixed(2)}%, 0) scale(${(1.04 + 0.08 * p).toFixed(4)})`; b.style.transform = `translate3d(${(-3.2 * Math.sin(p * Math.PI * 0.9 + 0.4)).toFixed(2)}%, ${(-8.5 * p).toFixed(2)}%, 0) scale(${(1.07 + 0.09 * p).toFixed(4)})`; b.style.opacity = e.toFixed(3); }; const tick = (now) => { const dt = last ? Math.min(64, now - last) : 16.7; last = now; const goal = target(), k = 1 - Math.pow(1 - 0.075, dt / 16.7); // the same glide at any frame rate p += (goal - p) * (still ? 1 : k); draw(); if (Math.abs(goal - p) > 0.0004) raf = requestAnimationFrame(tick); else { raf = 0; last = 0; } }; const wake = () => { if (!raf) raf = requestAnimationFrame(tick); }; addEventListener("scroll", wake, { passive: true }); addEventListener("resize", wake); draw(); }