/* Shared components: HouseSymbol, Logo, Placeholder, Reveal, Marquee */ // House symbol — single chevron-arch derived from the Myfab logomark // Per brand: "The house symbol is our unique graphic device... versatile, can be rotated and scaled" function HouseSymbol({ size = 24, color = "currentColor", style = {}, className = "", rotation = 0 }) { return ( ); } // Solid filled chevron (one arch as a single solid shape) function HouseSymbolSolid({ size = 24, color = "currentColor", style = {}, className = "" }) { return ( ); } // Logomark — the official square mark with nested arches (recreated as SVG so it can be recolored) function LogoMark({ size = 28, color = "var(--cobalt)", style = {}, className = "" }) { return ( ); } // Logo — uses the actual brand asset (PNG) function Logo({ height = 28, variant = "blue", style = {}, className = "" }) { const src = variant === "white" ? "assets/logo-full-white.png" : "assets/logo-full-blue.png"; return ( Myfab ); } // Placeholder image slot function Placeholder({ label, ratio = "16/9", cobalt = false, style = {}, children, className = "" }) { return (
{children || {label}}
); } // Intersection-observer reveal wrapper function Reveal({ children, delay = 0, as: Tag = 'div', className = '', style = {} }) { const ref = React.useRef(null); const [shown, setShown] = React.useState(false); React.useEffect(() => { const el = ref.current; if (!el) return; const obs = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting) { setShown(true); obs.disconnect(); } }, { threshold: 0.12, rootMargin: '0px 0px -10% 0px' } ); obs.observe(el); return () => obs.disconnect(); }, []); return ( {children} ); } // Animated counter function Counter({ to, suffix = '', duration = 1600, className = '' }) { const ref = React.useRef(null); const [val, setVal] = React.useState(0); const [started, setStarted] = React.useState(false); React.useEffect(() => { const el = ref.current; if (!el) return; const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting && !started) { setStarted(true); const start = performance.now(); const tick = (t) => { const p = Math.min(1, (t - start) / duration); const eased = 1 - Math.pow(1 - p, 3); setVal(Math.round(to * eased)); if (p < 1) requestAnimationFrame(tick); }; requestAnimationFrame(tick); } }, { threshold: 0.5 }); obs.observe(el); return () => obs.disconnect(); }, [to, duration, started]); return {val}{suffix}; } // Decorative scattered house symbols — disabled per design request. function HouseDeco() { return null; } // Marquee row function Marquee({ items, sep = '·' }) { const seq = [...items, ...items]; return (
{seq.map((it, i) => (
{it}
))}
); } Object.assign(window, { HouseSymbol, HouseSymbolSolid, LogoMark, Logo, Placeholder, Reveal, Counter, HouseDeco, Marquee });