// work.jsx — full portfolio const { useState: useStateW, useMemo: useMemoW } = React; function Work({ setRoute }) { const [filter, setFilter] = useStateW("All"); const [active, setActive] = useStateW(null); const [view, setView] = useStateW("mosaic"); // mosaic | grid const filters = ["All", "Minimal", "Kinetic", "3D"]; const items = useMemoW(() => { if (filter === "All") return VM_PORTFOLIO; return VM_PORTFOLIO.filter((it) => it.style === filter); }, [filter]); return (
{/* header */}
— Index of work

Recent{" "} work
logos in motion.

Every project below was animated from scratch in After Effects. Hover to preview · click to open full-quality.

{/* controls */}
{filters.map((f) => { const active = filter === f; return ( ); })}
VIEW —
{/* grid */}
{view === "mosaic" ? : }
{/* CTA strip */}
— Next slot opens June 3
Want yours in the next batch?
{active && setActive(null)} />}
); } function iconBtnStyle(active) { return { width: 32, height: 32, borderRadius: 8, display: "flex", alignItems: "center", justifyContent: "center", background: active ? "var(--glass-hi)" : "transparent", color: active ? "var(--ink)" : "var(--ink-3)", border: "1px solid", borderColor: active ? "var(--line-2)" : "transparent", cursor: "pointer" }; } function MosaicGrid({ items, onPlay }) { return (
{items.map((it, i) => { const span = mosaicSpan(i); return ( onPlay(it)} big={span.c >= 4} /> ); })}
); } function mosaicSpan(i) { // pattern: big-big-small repeats const pattern = [ { c: 4, r: 2 }, { c: 2, r: 1 }, { c: 2, r: 1 }, { c: 3, r: 2 }, { c: 3, r: 2 }, { c: 2, r: 1 }, { c: 2, r: 1 }, { c: 2, r: 1 }]; return pattern[i % pattern.length]; } function mosaicAspect(i) { const span = mosaicSpan(i); // Big tiles wider/short if (span.c >= 4) return "16/9"; if (span.c === 3) return "16/10"; return "4/3"; } function PlainGrid({ items, onPlay }) { return (
{items.map((it, i) => onPlay(it)} /> )}
); } Object.assign(window, { Work });