// Decorative SVG components — floral corners, hearts, timeline icons
// All in hand-drawn "watercolor" line style, matching the sello aesthetic.
const FloralSpray = ({ className = '', style = {}, ...rest }) => (
);
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 }) => (
);
// 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 });