// Reusable components // Persera symbol — P with extending strokes const PerseraMark = ({ size = 28, color = "currentColor" }) => ( ); // Persera lockup const Wordmark = ({ size = 18, color = "currentColor" }) => ( ); // Abstract gradient field — stock-style placeholder imagery const Field = ({ palette = "blue", label, style = {}, className = "", children }) => { const palettes = { blue: 'radial-gradient(ellipse 80% 60% at 30% 30%, #688A9B 0%, #3C515D 45%, #253038 100%)', blueWarm: 'radial-gradient(ellipse 70% 80% at 70% 20%, #DAD9D4 0%, #688A9B 50%, #3C515D 100%)', paper: 'radial-gradient(ellipse 100% 80% at 50% 100%, #688A9B 0%, #DAD9D4 60%, #EFEAE0 100%)', deep: 'radial-gradient(ellipse 80% 100% at 20% 80%, #3C515D 0%, #253038 70%, #1A2228 100%)', sand: 'radial-gradient(ellipse 80% 60% at 80% 30%, #EFEAE0 0%, #DAD9D4 50%, #688A9B 100%)', smoke: 'linear-gradient(135deg, #626364 0%, #3C515D 60%, #253038 100%)', glow: 'radial-gradient(ellipse 60% 40% at 50% 50%, rgba(218,217,212,0.6), rgba(104,138,155,0.4) 40%, #253038 100%)', }; return (
{/* Subtle grain */}
{/* Soft noise via SVG */} {children} {label && {label}}
); }; // Split text into chars/lines for animation const SplitText = ({ children, className = "" }) => ( {children} ); // Reveal observer hook const useReveal = () => { React.useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { threshold: 0.15, rootMargin: '0px 0px -10% 0px' }); els.forEach(el => io.observe(el)); return () => io.disconnect(); }, []); }; const useScrolled = () => { const [scrolled, setScrolled] = React.useState(false); React.useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 60); window.addEventListener('scroll', onScroll, { passive: true }); onScroll(); return () => window.removeEventListener('scroll', onScroll); }, []); return scrolled; }; Object.assign(window, { PerseraMark, Wordmark, Field, SplitText, useReveal, useScrolled });