function slug(s){ return s.toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/(^-|-$)/g,''); } /* 06 — Cases with category tabs. teaser=true → first N cards, no tabs, see-all link. */ function Cases({ t, teaser = false, n = 4, featured = null }) { const c = t.cases; const [tab, setTab] = useState(0); const cat = c.tabs[tab]; const isAll = tab === 0; const all = c.items.filter(it => isAll || it.cats.includes(cat)); const items = teaser ? (featured ? featured.map(s => c.items.find(it => it.slug === s)).filter(Boolean) : c.items.slice(0, n)) : all; return (
{teaser && }
{!teaser && (
{c.tabs.map((tb, i) => { const active = i === tab; return ( ); })}
)}
{items.map((it, i) => )}
{teaser &&
}
); } function CaseCard({ it, i, readMore }) { const [h, setH] = useState(false); const href = `case.html?c=${it.slug}`; return ( setH(true)} onMouseLeave={()=>setH(false)} style={{ background: h ? 'var(--card-raised)' : 'var(--card-bg)', border:'1px solid var(--border-1)', borderRadius:'var(--r-card)', overflow:'hidden', transition:'all var(--dur) var(--ease-out)', transform: h ? 'translateY(-4px)' : 'none', boxShadow: h ? 'var(--shadow-md)' : 'none', height:'100%', display:'flex', flexDirection:'column', textDecoration:'none', }}>
{it.logo && } {it.metric}
{it.sector}{it.meta}

{it.title}

{it.client}

{it.summary}

{readMore}
); } Object.assign(window, { Cases });