// Portfolio section — multiple layout variants
const Portfolio = ({ style = 'editorial', onOpen }) => {
const [filter, setFilter] = React.useState('All');
const tags = ['All', 'Web', 'AI', 'Commerce', 'SaaS'];
const filtered = filter === 'All' ? PORTFOLIO : PORTFOLIO.filter(p => p.tag.toLowerCase().includes(filter.toLowerCase()));
return (
{tags.map(t => (
))}
{PORTFOLIO.length} CASES · 2023–2026
{style === 'editorial' && }
{style === 'cards' && }
{style === 'list' && }
);
};
const PortfolioEditorial = ({ items, onOpen }) => (
{items.map((p, i) => {
const big = i === 0;
const span = big ? 12 : (i % 3 === 1 ? 7 : 5);
return (
onOpen?.(p)} className="clickable" style={{gridColumn:`span ${span}`}}>
);
})}
);
const CaseCard = ({ p, big }) => (
{p.tag}
{p.featured && Featured}
{p.year}
{p.metric[0].toUpperCase()}
{p.metric[1]}
STACK
{p.stack.join(' · ')}
);
const CaseVisual = ({ p }) => {
// Simple abstract visualization unique per project
const seed = p.id.charCodeAt(0);
return (
);
};
const PortfolioCards = ({ items, onOpen }) => (
{items.map(p => (
onOpen?.(p)} className="clickable">
))}
);
const PortfolioList = ({ items, onOpen }) => (
{items.map((p, i) => (
onOpen?.(p)} className="clickable" style={{
display:'grid', gridTemplateColumns:'80px 1.5fr 1.2fr 1fr 60px', gap:24, alignItems:'center',
padding:'24px 28px', borderTop: i>0 ? '1px solid var(--line)' : 'none',
transition:'background .2s ease'
}}
onMouseEnter={e=>e.currentTarget.style.background='var(--bg-2)'}
onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
{String(i+1).padStart(2,'0')} · {p.year}
{p.metric[0].toUpperCase()}
{p.metric[1]}
{p.stack.join(' · ')}
{Icon.arrow()}
))}
);
window.Portfolio = Portfolio;