// 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 */}
{/* 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) {
const pattern = [
{ c: 4, r: 2 }, { c: 2, r: 1 }, { c: 2, r: 1 },
{ c: 3, r: 2 }, { c: 3, r: 2 },
{ c: 3, r: 2 }, { c: 3, r: 2 },
{ c: 6, r: 2 },
];
return pattern[i % pattern.length];
}
function ExplainersSection({ onPlay, setRoute }) {
const mobile = useMobile();
const items = window.VM_EXPLAINERS || [];
if (!items.length) return null;
// With an odd count the first video goes full-width so the 2-column rows never leave a gap.
const featureFirst = !mobile && items.length % 2 === 1;
return (
— Beyond the logo
Explainers &
SaaS reels.
Product explainers, launch films and UI animation for software teams.
Scoped and quoted per project.
{items.map((it, i) => {
const feature = featureFirst && i === 0;
const tile = { ...it, year: it.duration, style: it.type };
return (
onPlay({ src: it.src })} />
);
})}
);
}
function PlainGrid({ items, onPlay }) {
return (
{items.map((it, i) =>
onPlay(it)} />
)}
);
}
Object.assign(window, { Work });