I want to see
/* ============================================================ Home — pinned sidebar + condensing avatar + parallax work feed ============================================================ */ const { useState, useEffect, useRef } = React; // Fades the hero out as the user scrolls down. function useHeroFade(ref, fadePx = 320) { useEffect(() => { const el = ref.current; if (!el) return; if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; let raf = null; const update = () => { raf = null; const p = Math.max(0, Math.min(1, (window.scrollY || window.pageYOffset) / fadePx)); el.style.opacity = (1 - p).toFixed(3); }; const onScroll = () => { if (raf == null) raf = requestAnimationFrame(update); }; update(); window.addEventListener("scroll", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); if (raf) cancelAnimationFrame(raf); }; }, [fadePx]); } // Drives --p (0..1) directly on a ref node via scroll — no React re-render. function useCondense(ref, maxPx, enabled) { useEffect(() => { const el = ref.current; if (!el) return; if (!enabled) { el.style.setProperty("--p", 0); return; } const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; if (reduce) { el.style.setProperty("--p", 0); return; } let raf = null; const update = () => { raf = null; const y = window.scrollY || window.pageYOffset; const p = Math.max(0, Math.min(1, y / maxPx)); el.style.setProperty("--p", p.toFixed(3)); el.classList.toggle("is-condensed", p > 0.5); }; const onScroll = () => { if (raf == null) raf = requestAnimationFrame(update); }; update(); window.addEventListener("scroll", onScroll, { passive: true }); return () => { window.removeEventListener("scroll", onScroll); if (raf) cancelAnimationFrame(raf); }; }, [maxPx, enabled]); } // Stacking cards: scales/darkens each card as the next one covers it. function useCardStack(containerRef, enabled) { useEffect(() => { const container = containerRef.current; if (!container) return; const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches; const cards = Array.from(container.querySelectorAll(".work__item")); const inners = cards.map((c) => c.querySelector(".work__card")); const clear = () => inners.forEach((el) => { if (el) { el.style.transform = ""; el.style.filter = ""; el.style.removeProperty("--line-op"); } }); if (!enabled || reduce) { clear(); return; } let raf = null; const update = () => { raf = null; for (let i = 0; i < cards.length; i++) { const inner = inners[i]; const next = cards[i + 1]; if (!inner) continue; if (!next) { inner.style.transform = ""; inner.style.filter = ""; continue; } const cr = cards[i].getBoundingClientRect(); const nr = next.getBoundingClientRect(); const gap = nr.top - cr.top; // shrinks as next rises over this card const prog = 1 - Math.max(0, Math.min(1, gap / cr.height)); const scale = (1 - prog * 0.04).toFixed(3); inner.style.transform = `scale(${scale})`; inner.style.filter = ""; // fade this card's hairline as the next one covers it inner.style.setProperty("--line-op", (1 - Math.min(1, prog * 2)).toFixed(3)); } }; const onScroll = () => { if (raf == null) raf = requestAnimationFrame(update); }; update(); window.addEventListener("scroll", onScroll, { passive: true }); window.addEventListener("resize", onScroll); return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); if (raf) cancelAnimationFrame(raf); clear(); }; }, [enabled]); } const FILTER_TAGS = ["Design & Advertising", "Generative AI", "Social Media", "Festivals & Events", "Marketplace"]; function Home({ lang, t, nav, tweaks }) { const sideRef = useRef(null); const workRef = useRef(null); const [activeTag, setActiveTag] = useState(null); useCondense(sideRef, 340, tweaks.avatarCondense); useHeroFade(sideRef); useCardStack(workRef, tweaks.cardStack); const filtered = activeTag ? PROJECTS.filter((p) => p.tags && p.tags.includes(activeTag)) : PROJECTS; const selectTag = (tag) => { setActiveTag(tag); if (workRef.current) { window.scrollTo({ top: workRef.current.offsetTop, behavior: "smooth" }); } }; return (
I want to see