function useInView(ref, threshold = 0.15) { const [visible, setVisible] = React.useState(false); React.useEffect(() => { const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) setVisible(true); }, { threshold }); if (ref.current) obs.observe(ref.current); return () => obs.disconnect(); }, []); return visible; } function ShopPage({ coffees, onSelect }) { const [hovered, setHovered] = React.useState(null); return (
{/* Page header */}
Current Harvest — {new Date().getFullYear()}

The Origins

Four single-origin lots. Each sourced above 1,200 metres. Each roasted to the minimum necessary. Nothing added — everything earned by altitude, process, and the farmers who tend them.

{/* Product grid */}
{coffees.map((c, i) => { const isHovered = hovered === c.id; const isRight = i % 2 === 1; return (
onSelect(c)} onMouseEnter={() => setHovered(c.id)} onMouseLeave={() => setHovered(null)} style={{ padding: '56px 48px', borderRight: !isRight ? '1px solid rgba(255,255,255,0.08)' : 'none', borderBottom: i < 2 ? '1px solid rgba(255,255,255,0.08)' : 'none', cursor: 'crosshair', background: isHovered ? '#ffffff' : 'transparent', transition: 'background 0.45s cubic-bezier(0.25,0.46,0.45,0.94)', animation: `appleReveal 0.8s cubic-bezier(0.25,0.46,0.45,0.94) ${i * 0.12}s both`, position: 'relative', overflow: 'hidden', }} > {/* Large ghost index */} {/* Content */}
{c.process} · {c.altitude.toLocaleString()}m

{c.name}

{c.region} · {c.origin}
{/* Notes */}
{c.notes.map((n, ni) => ( {n} ))}
{/* Price + CTA */}
FROM
${c.prices['100g']}
SELECT
); })}
{/* Shipping note */}
{[ ['FREE SHIPPING', 'On all orders over $120'], ['ROASTED TO ORDER', 'Dispatched within 48 hours'], ['FRESHNESS WINDOW', 'Best 10–28 days post-roast'], ].map(([title, sub]) => (
{title}
{sub}
))}
); } Object.assign(window, { ShopPage });