"use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import Image from "next/image"; import { AnimatePresence, motion, useMotionValue, useSpring } from "framer-motion"; import { ArrowRight } from "@/components/ui/icons"; import AnimatedButton from "@/components/ui/AnimatedButton"; /** * Portfolio list with a cursor-trace image reveal. On desktop, hovering a * project row floats its image at the cursor (spring-eased) and dims the rest * of the list. On touch/small screens it falls back to stacked image cards so * every project stays visible and crawlable. Brand-strict: dark tokens, * Google Sans, orange/gold accents. */ const EASE = [0.21, 0.47, 0.32, 0.98]; const rise = { hidden: { opacity: 0, y: 28 }, show: { opacity: 1, y: 0, transition: { duration: 0.6, ease: EASE } }, }; /* Filter buckets — mirror the Portfolio menu (Games / Analytics / Websites / Media). */ const FILTERS = [ { key: "all", label: "All" }, { key: "games", label: "Games" }, { key: "analytics", label: "Analytics" }, { key: "websites", label: "Websites" }, { key: "media", label: "Media" }, ]; export default function PortfolioList({ projects }) { const [hovered, setHovered] = useState(null); const [filter, setFilter] = useState("all"); // Preselect a filter from the URL (?filter=games) — used by the nav dropdown. useEffect(() => { const p = new URLSearchParams(window.location.search).get("filter"); if (p && FILTERS.some((f) => f.key === p)) setFilter(p); }, []); const filtered = filter === "all" ? projects : projects.filter((p) => p.group === filter); const selectFilter = (key) => { setFilter(key); setHovered(null); }; // Cursor position (relative to the list) → spring-smoothed for a trailing feel. const mx = useMotionValue(0); const my = useMotionValue(0); const x = useSpring(mx, { stiffness: 260, damping: 30, mass: 0.6 }); const y = useSpring(my, { stiffness: 260, damping: 30, mass: 0.6 }); const onMove = (e) => { const r = e.currentTarget.getBoundingClientRect(); mx.set(e.clientX - r.left); my.set(e.clientY - r.top); }; return (
{/* Header */} Our work

Selected{" "} work.

A few of the products we've designed, built and scaled — across fintech, healthcare, AI, gaming and more.

{/* Filter tabs */} {FILTERS.map((f) => { const active = filter === f.key; return ( ); })} {/* Empty state (e.g. a filter with no projects yet) */} {filtered.length === 0 && (

New {FILTERS.find((f) => f.key === filter)?.label.toLowerCase()} work is on the way —{" "} start a project .

)} {/* Desktop — cursor-trace list */}
setHovered(null)} className={ "relative mt-10 hidden border-t border-border " + (filtered.length ? "lg:block" : "") } > {/* Floating cursor image */}
{hovered !== null && ( {filtered[hovered].title} {filtered[hovered].category} )}
{/* Rows */}
    {filtered.map((p, i) => (
  • setHovered(i)} className={ "group flex items-center justify-between gap-6 border-b border-border py-8 transition-all duration-300 " + (hovered === null || hovered === i ? "opacity-100" : "opacity-40") } >
    {String(i + 1).padStart(2, "0")}

    {p.title}

    {p.category} {p.year}
  • ))}
{/* Mobile — image cards */} {filtered.map((p, i) => (
{p.title} {p.category}

{String(i + 1).padStart(2, "0")} {p.title}

{p.year}
))}
{/* CTA */}

Have a product in mind?

Start a project
); }