// Shared blog components — Nav, Footer, eyebrow, and helpers const { useState: useStateBS, useEffect: useEffectBS, useRef: useRefBS } = React; const BS = { cream: '#fafaf8', black: '#0f0f0f', green: '#3d6b4f', greenDark: '#2d5239', greenLight: '#e8f0eb', greenLighter: '#f1f6f2', gray: '#6b6b6b', grayLight: '#f0efed', border: '#e2e0db', }; // ─── Wordmark ──────────────────────────────────────── function BlogWordmark() { return ( KeepAfter ); } // ─── Nav ───────────────────────────────────────────── function BlogNav({ active }) { const linkStyle = (isActive) => ({ fontFamily: '"DM Sans"', fontSize: 14, fontWeight: isActive ? 600 : 500, color: isActive ? BS.green : BS.black, textDecoration: 'none', position: 'relative', paddingBottom: 2, borderBottom: isActive ? `1.5px solid ${BS.green}` : '1.5px solid transparent', transition: 'color .2s ease, border-color .2s ease', }); return ( ); } // ─── Footer ────────────────────────────────────────── function BlogFooter() { return ( ); } function FCol({ title, items }) { return (
{title}
{items.map((it, i) => ( e.currentTarget.style.color = BS.green} onMouseLeave={e => e.currentTarget.style.color = BS.gray} >{it.label} ))}
); } // ─── Eyebrow ───────────────────────────────────────── function BlogEyebrow({ children, color = BS.green }) { return (
{children}
); } // ─── Reveal ─────────────────────────────────────────── // Pass-through wrapper. Content is always visible — entrance motion is // optional and applied via a CSS keyframe class that only enhances; if the // animation engine doesn't run, the content stays readable. function Reveal({ children, delay = 0, as = 'div', style = {}, ...rest }) { const Tag = as; return ( {children} ); } // ─── Image placeholder ─────────────────────────────── // Wraps with editorial styling. Drop a file onto it and the // placeholder swaps for the real photo (persisted across reloads). function EditorialImage({ id, ratio = '4 / 3', src = null, direction = 'Drop an image, or describe the shot', radius = 16, kicker = null, // optional badge top-left style = {}, className = '', }) { return (
{/* Dotted pattern — only visible when slot is empty (image-slot covers it once filled) */}
{src ? ( {direction} ) : ( )} {kicker && (
{kicker}
)}
); } Object.assign(window, { BS, BlogNav, BlogFooter, BlogWordmark, BlogEyebrow, Reveal, EditorialImage, });