function WorkCard({ item, onOpen }) { const { Card, Badge, Button } = window.ArnelBiscarraDesignSystem_2766cb; return (
onOpen(item)}>
{item.img ? {item.title} : }
{item.badge}
{item.title}
{item.cta && (
)}
); } function WorkScrollCard({ item, onOpen }) { return (
onOpen(item)}> {item.img ? {item.title} : }
{item.title}
{item.badge} {item.href && { e.stopPropagation(); window.open(item.href, '_blank'); }}>Visit Site}
); } function WorkScrollGallery({ items, onOpen }) { const pinRef = React.useRef(null); const trackRef = React.useRef(null); const introRef = React.useRef(null); React.useEffect(() => { const pin = pinRef.current, track = trackRef.current, intro = introRef.current; if (!pin || !track) return; // Mobile keeps the same vertical-drives-horizontal pin scroll as desktop // (mobile pin tuning lives in css/responsive.css). const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduced) return; let current = 0, target = 0; const computeTarget = () => { const rect = pin.getBoundingClientRect(); const total = pin.offsetHeight - window.innerHeight; if (total <= 0) { target = 0; return; } const progress = Math.max(0, Math.min(1, -rect.top / total)); const maxScroll = Math.max(0, track.scrollWidth - track.parentElement.clientWidth); target = -progress * maxScroll; }; // This used to run unconditionally for the page's entire lifetime — // a forced getBoundingClientRect() every frame, 60x/sec, even while // scrolling through totally unrelated sections. Now gated by an // IntersectionObserver so the loop only runs while this (tall, // 160-320vh) pinned section is anywhere near the viewport. let rafId = null; const loop = () => { computeTarget(); current += (target - current) * 0.08; if (Math.abs(target - current) < 0.05) current = target; track.style.transform = `translate3d(${current}px,0,0)`; if (intro) { const fadeDist = 260; const opacity = Math.max(0, 1 - Math.abs(current) / fadeDist); intro.style.opacity = opacity; intro.style.pointerEvents = opacity < 0.05 ? 'none' : ''; } rafId = requestAnimationFrame(loop); }; const startLoop = () => { if (rafId === null) rafId = requestAnimationFrame(loop); }; const stopLoop = () => { if (rafId !== null) { cancelAnimationFrame(rafId); rafId = null; } }; const io = new IntersectionObserver((entries) => { if (entries[0].isIntersecting) startLoop(); else stopLoop(); }, { rootMargin: '50% 0px 50% 0px' }); io.observe(pin); window.addEventListener('resize', computeTarget); return () => { stopLoop(); io.disconnect(); window.removeEventListener('resize', computeTarget); }; }, [items.length]); return (
/ SELECTED WORK

Websites, Built to Perform.

A look at the sites I've designed and developed — fast, modern, and made to convert.

SCROLL
{items.map((item) => (
))}
/ AND MORE

More Than What's Shown.

The projects featured here are only a small selection of my work. I've built 10+ additional websites for clients over the years. Some are no longer online due to expired hosting, domain renewals, or business closures.

); } function Work() { const { Tag } = window.ArnelBiscarraDesignSystem_2766cb; const data = window.SITE_DATA; const [filter, setFilter] = React.useState('All'); const [active, setActive] = React.useState(null); const visible = (cat) => filter === 'All' || filter === cat; const printItems = data.work.filter((w) => w.cat === 'Print'); const otherItems = data.work.filter((w) => w.cat !== 'Print'); const websiteItems = data.work.filter((w) => w.cat === 'Websites'); const logoApparelItems = data.work.filter((w) => w.cat === 'Logos' || w.cat === 'Apparel'); return (
/ SELECTED WORK

{filter === 'All' ? ( <>
{logoApparelItems.map((item, i) => ( ))}
) : (
{otherItems.map((item, i) => (
))}
)} {visible('Print') && (
{printItems.map((item) => ( ))}
)} setActive(null)} />
); } window.Work = Work;