// Shared components for Little Bloom Photo Studio // All components attached to window for cross-file access. const { useState, useEffect, useRef } = React; /* ============ Photo URLs (client studio assets) ============ */ const PHOTOS = { hero: "assets/NG5A0131.jpg", // full boho daybed — hero portrait maternity1: "assets/NG5A0133.jpg", // daybed wider shot maternity2: "assets/NG5A0134.jpg", // daybed + window light detail family1: "assets/NG5A0139.jpg", // cream sofa / macramé family2: "assets/NG5A0141.jpg", // sofa wide with windows newborn1: "assets/NG5A0138.jpg", // antique bassinet by windows newborn2: "assets/NG5A0136-2.jpg", // pampas arrangement close couple1: "assets/NG5A0142.jpg", // macramé wall detail couple2: "assets/NG5A0141.jpg", portrait1: "assets/NG5A0135.jpg", // daybed angled portrait2: "assets/NG5A0134.jpg", portrait3: "assets/NG5A0133.jpg", branding1: "assets/NG5A0141.jpg", // bright minimal sofa branding2: "assets/NG5A0139.jpg", studio1: "assets/NG5A0131.jpg", studio2: "assets/NG5A0139.jpg", // sofa/macramé — CTA background studio3: "assets/NG5A0133.jpg", flowers: "assets/NG5A0136.jpg", // pampas arrangement full flowers2: "assets/NG5A0136-2.jpg", child1: "assets/NG5A0137.jpg", // pampas wall wide child2: "assets/NG5A0139.jpg", photographer:"assets/NG5A0134.jpg", // studio detail (daybed + window) light: "assets/NG5A0138.jpg", // bassinet / window light snackbar: "assets/NG5A0144.jpg", // snack bar amenity }; window.PHOTOS = PHOTOS; /* ============ Floral Wreath SVG (mini decoration) ============ */ function Wreath({ size = 80, color, half = false }) { const c = color || "var(--lb-tan)"; // Stylized minimal wreath - delicate botanical line art return ( {/* outer ring as guideline (invisible) — draw sprigs around */} {/* Left sprig */} {/* Top sprig */} {!half && ( <> {/* Right sprig */} {/* Bottom sprig */} )} ); } /* ============ Logo (uses extracted PNG) ============ */ function Logo({ size = 56, variant = "full" }) { // variant: "full" (full wreath logo), "text" (just "Little Bloom"), "monogram" (LB) if (variant === "text") { return (
Little Bloom PHOTO  STUDIO
); } return Little Bloom Photo Studio; } /* ============ Nav ============ */ function Nav({ page, setPage, dark = false }) { const [scrolled, setScrolled] = useState(false); const [open, setOpen] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 60); onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); const items = [ { id: 'home', label: 'Home' }, { id: 'about', label: 'About' }, { id: 'booking', label: 'Booking & Memberships' }, ]; const onSolid = scrolled || open; const color = onSolid ? 'var(--lb-brown)' : (dark ? 'var(--lb-cream-3)' : 'var(--lb-brown)'); const bg = onSolid ? 'rgba(251,247,236,0.92)' : 'transparent'; const go = (id) => { setPage(id); setOpen(false); window.scrollTo({ top: 0, behavior: 'instant' }); }; return (
{/* Mobile sheet */} {open && (
{items.map(it => ( ))}
)}
); } /* ============ Footer ============ */ function Footer({ setPage }) { return ( ); } /* ============ Marquee strip ============ */ function Marquee({ items, speed = 60 }) { // duplicate items for seamless scroll const arr = [...items, ...items]; return (
{arr.map((t, i) => ( · {t} ))}
); } /* ============ Reveal-on-scroll wrapper ============ */ function Reveal({ children, delay = 0, as: As = 'div', ...rest }) { const ref = useRef(null); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { setTimeout(() => e.target.classList.add('in'), delay); io.unobserve(e.target); } }); }, { threshold: 0.12 }); io.observe(el); return () => io.disconnect(); }, [delay]); return {children}; } /* Export to window */ Object.assign(window, { Wreath, Logo, Nav, Footer, Marquee, Reveal, PHOTOS });