// Decorative SVG components — floral corners, hearts, timeline icons // All in hand-drawn "watercolor" line style, matching the sello aesthetic. const FloralSpray = ({ className = '', style = {}, ...rest }) => ( {/* Stems */} {/* Leaves */} {/* Main flowers */} {[0, 60, 120, 180, 240, 300].map(a => ( ))} {[0, 60, 120, 180, 240, 300].map(a => ( ))} {[0, 60, 120, 180, 240, 300].map(a => ( ))} {/* Small buds */} ); const Heart = ({ size = 14, color = 'currentColor' }) => ( ); const Divider = () => (
); // Timeline icons — simplified line drawings in the reference style const IconBus = () => ( ); const IconRings = () => ( ); const IconCocktail = () => ( ); const IconPlate = () => ( ); const IconCake = () => ( ); const IconMic = () => ( ); const IconFood = () => ( ); const IconDisco = () => ( ); const TimelineIcon = ({ kind }) => { const map = { bus: IconBus, rings: IconRings, cocktail: IconCocktail, plate: IconPlate, cake: IconCake, mic: IconMic, food: IconFood, disco: IconDisco }; const C = map[kind] || IconBus; return ; }; // Falling petals background const Petals = ({ count = 18 }) => { const petals = Array.from({ length: count }, (_, i) => { const left = Math.random() * 100; const size = 6 + Math.random() * 10; const duration = 18 + Math.random() * 18; const delay = -Math.random() * duration; const hue = Math.random() > 0.5 ? '#b8c0d8' : '#c9c0d8'; return { left, size, duration, delay, hue, i }; }); return (
{petals.map(p => ( ))}
); }; // Ornate top frame wreath (simplified version of the sello) const Wreath = ({ size = 340 }) => ( {/* Gold geometric frame — hexagonal */} {/* Bottom floral cluster */} {/* Big center flower */} {[0, 45, 90, 135, 180, 225, 270, 315].map(a => ( ))} {/* Side flowers */} {[0, 60, 120, 180, 240, 300].map(a => ( ))} {[0, 60, 120, 180, 240, 300].map(a => ( ))} {/* Buds */} {[0, 72, 144, 216, 288].map(a => ( ))} {[0, 72, 144, 216, 288].map(a => ( ))} {/* Leaves */} {/* Top small flowers */} {[0, 60, 120, 180, 240, 300].map(a => ( ))} {[0, 60, 120, 180, 240, 300].map(a => ( ))} ); // Reveal-on-scroll helpers (used by Story photos) const useReveal = (threshold = 0.15) => { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { el.classList.add('in'); io.disconnect(); } }, { threshold }); io.observe(el); return () => io.disconnect(); }, [threshold]); return ref; }; const Reveal = ({ children, className = '', style, as: Tag = 'div' }) => { const ref = useReveal(); return {children}; }; const MaskReveal = ({ children, className = '', style, onClick }) => { const ref = useReveal(); return
{children}
; }; // Parallax: every element with [data-parallax="0.3"] moves at scroll * factor const useParallax = () => { React.useEffect(() => { const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches; if (reduced) return; let raf = 0; const update = () => { const els = document.querySelectorAll('[data-parallax]'); const sy = window.pageYOffset || document.documentElement.scrollTop; const vh = window.innerHeight; els.forEach((el) => { const factor = parseFloat(el.dataset.parallax) || 0; const rect = el.getBoundingClientRect(); const elementCenter = rect.top + rect.height / 2; const fromCenter = elementCenter - vh / 2; const offset = -fromCenter * factor; el.style.transform = `translate3d(0, ${offset.toFixed(1)}px, 0)`; }); }; const onScroll = () => { if (raf) return; raf = requestAnimationFrame(() => { update(); raf = 0; }); }; window.addEventListener('scroll', onScroll, { passive: true }); window.addEventListener('resize', onScroll); update(); return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); }; }, []); }; Object.assign(window, { FloralSpray, Heart, Divider, TimelineIcon, Petals, Wreath, Reveal, MaskReveal, useParallax });