/* 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 (
);
}
// 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 });