function NewsletterForm() { const [email, setEmail] = React.useState(''); const [done, setDone] = React.useState(false); const [err, setErr] = React.useState(''); function submit(e) { e.preventDefault(); if (!email.includes('@')) { setErr('Valid email required'); return; } window.UmbraDB?.saveSubscriber?.(email, 'homepage'); setDone(true); } if (done) return (
You're in the shadow now.
We'll be in touch — sparingly.
); return (
{ setEmail(e.target.value); setErr(''); }} placeholder="your@email.com" style={{ flex: 1, background: 'none', border: 'none', outline: 'none', fontFamily: "'Montserrat', sans-serif", fontSize: 12, color: '#ffffff', padding: '12px 0', letterSpacing: '0.04em', }} onFocus={e => e.target.closest('div').style.borderBottomColor = 'rgba(255,255,255,0.9)'} onBlur={e => e.target.closest('div').style.borderBottomColor = err ? 'rgba(255,255,255,0.7)' : 'rgba(255,255,255,0.25)'} />
{err &&
{err}
}
No spam. Unsubscribe any time.
); } function Nav({ cartCount, onLogoClick, onNavigate, page, onCartClick, onJoinClick }) { const links = [ { id: 'home', label: 'HOME' }, { id: 'shop', label: 'SHOP' }, { id: 'about', label: 'ABOUT' }, { id: 'ritual', label: 'RITUAL' }, ]; return (
{/* Logo + wordmark */} {/* Nav links */} {/* Cart + Join */}
); } function ProductRow({ coffee, onSelect, density }) { const [hovered, setHovered] = React.useState(false); const compact = density === 'compact'; return (
onSelect(coffee)} onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)} style={{ borderTop: '1px solid rgba(255,255,255,0.1)', padding: compact ? '26px 48px' : '38px 48px', cursor: 'crosshair', background: hovered ? '#ffffff' : 'transparent', transition: 'background 0.45s cubic-bezier(0.25,0.46,0.45,0.94)', display: 'grid', gridTemplateColumns: '56px 1fr auto', alignItems: 'center', gap: '32px', }} > {coffee.index}
{coffee.name}
{coffee.origin} · {coffee.process}
{coffee.altitude.toLocaleString()}m
altitude
); } function HomePage({ coffees, onSelect, tweaks }) { const [heroVisible, setHeroVisible] = React.useState(false); const [manifestoVisible, setManifestoVisible] = React.useState(false); const [mouse, setMouse] = React.useState({ x: 0, y: 0 }); const manifestoRef = React.useRef(null); React.useEffect(() => { const t = setTimeout(() => setHeroVisible(true), 80); const obs = new IntersectionObserver( ([e]) => { if (e.isIntersecting) setManifestoVisible(true); }, { threshold: 0.25 } ); if (manifestoRef.current) obs.observe(manifestoRef.current); return () => { clearTimeout(t); obs.disconnect(); }; }, []); function onMouseMove(e) { setMouse({ x: (e.clientX / window.innerWidth - 0.5) * 24, y: (e.clientY / window.innerHeight - 0.5) * 12, }); } const trans = 'opacity 1.1s ease, transform 1.1s cubic-bezier(0.16, 1, 0.3, 1)'; return (
{/* ── HERO ── */}
{/* Background roastery image — grayscale + parallax */}
{/* Dark vignette overlay — deepens toward edges */}
{/* Bottom fade into black */}
{/* Main UMBRA */}

UMBRA

{/* Subtitle */}
Specialty Coffee Roasters  ·  Makassar, Indonesia  ·  Est. 2021
{/* Thin divider line — fades in */}
{/* Scroll cue */}
SCROLL
{/* ── MANIFESTO ── */}
Manifesto
{[ '"Umbra: from Latin, the darkest shadow.', 'The shadow cast by a sun-grown cherry.', 'The shadow inside the cup.', 'We source where altitude writes the flavour —', 'and roast to the edge of darkness."', ].map((line, i) => ( {line} ))}
{/* ── PRODUCT GRID ── */}
Current Harvest — {new Date().getFullYear()}
4 Origins
{coffees.map((c, i) => (
))}
{/* ── NEWSLETTER ── */}
The Dispatch

Stay in
the shadow.

New arrivals, roast dates, and the occasional manifesto. Never frequent — always worth opening.

{/* ── FOOTER ── */}
); } Object.assign(window, { Nav, ProductRow, HomePage });