/* ============================================================ WorkStay — shared UI components exports: NavBar, FooterBar, Btn, Badge, Pill, WorkScore, MetricBar, RadialStat, SectionHead, Stagger ============================================================ */ const { useState, useEffect, useRef } = React; /* ---------------- Buttons ---------------- */ function Btn({ children, variant = 'primary', size = 'md', icon, onClick, style = {}, type }) { const [hov, setHov] = useState(false); const sizes = { sm: { padding: '9px 16px', fontSize: 13 }, md: { padding: '13px 22px', fontSize: 14.5 }, lg: { padding: '17px 30px', fontSize: 16 }, }; const base = { display: 'inline-flex', alignItems: 'center', gap: 9, borderRadius: 999, fontFamily: 'var(--sans)', fontWeight: 500, letterSpacing: '0.01em', border: '1px solid transparent', transition: 'all .35s cubic-bezier(.16,1,.3,1)', whiteSpace: 'nowrap', ...sizes[size], }; const variants = { primary: { background: hov ? 'var(--violet-soft)' : 'var(--violet)', color: '#fff', boxShadow: hov ? '0 14px 40px -10px var(--violet-glow)' : '0 8px 24px -12px var(--violet-glow)', transform: hov ? 'translateY(-2px)' : 'none' }, glass: { background: hov ? 'rgba(255,255,255,0.14)' : 'rgba(255,255,255,0.07)', color: 'var(--text)', borderColor: 'var(--line-2)', backdropFilter: 'blur(20px)', transform: hov ? 'translateY(-2px)' : 'none' }, ghost: { background: 'transparent', color: hov ? 'var(--text)' : 'var(--text-dim)', borderColor: 'transparent' }, line: { background: 'transparent', color: 'var(--text)', borderColor: hov ? 'var(--text-dim)' : 'var(--line-2)', transform: hov ? 'translateY(-2px)' : 'none' }, }; return ( ); } /* ---------------- Badge / Pill ---------------- */ function Badge({ icon, children, glow = false, style = {} }) { return ( {icon && } {children} ); } function Pill({ children, active = false, onClick, icon }) { const [hov, setHov] = useState(false); return ( ); } /* ---------------- WorkScore radial ring ---------------- */ function WorkScore({ value = 92, size = 132, label = 'Work Score', stroke = 8 }) { const r = (size - stroke) / 2; const c = 2 * Math.PI * r; const [shown, setShown] = useState(0); useEffect(() => { let raf; const t0 = performance.now(); const dur = 1400; const tick = (t) => { const k = Math.min(1, (t - t0) / dur); const e = 1 - Math.pow(1 - k, 3); setShown(Math.round(value * e)); if (k < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [value]); return (
{shown}
{label}
); } /* ---------------- Metric bar ---------------- */ function MetricBar({ label, value, icon, suffix = '', delay = 0 }) { const ref = useRef(null); const [vis, setVis] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const ob = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVis(true); ob.disconnect(); } }, { threshold: 0.4 }); ob.observe(el); return () => ob.disconnect(); }, []); return (
{icon && }{label} {value}{suffix}
); } /* ---------------- Section head ---------------- */ function SectionHead({ eyebrow, title, sub, align = 'left', style = {} }) { return (
{eyebrow &&
{eyebrow}
}

{title}

{sub &&

{sub}

}
); } /* ---------------- Scroll-reveal wrapper ---------------- */ function Reveal({ children, delay = 0, y = 24, style = {} }) { const ref = useRef(null); const [vis, setVis] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const ob = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVis(true); ob.disconnect(); } }, { threshold: 0.15 }); ob.observe(el); return () => ob.disconnect(); }, []); return (
{children}
); } /* ---------------- NavBar ---------------- */ function NavBar({ route, go, onWish, wishCount = 0 }) { const [scrolled, setScrolled] = useState(false); useEffect(() => { const h = () => setScrolled(window.scrollY > 24); window.addEventListener('scroll', h, { passive: true }); h(); return () => window.removeEventListener('scroll', h); }, []); const links = [['explore', 'Explore'], ['intelligence', 'Workspace Intelligence'], ['operators', 'Operators']]; return (
go('explore')}>Find a stay
); } function Logo({ size = 30 }) { return ( W ); } /* ---------------- Footer ---------------- */ function FooterBar({ go }) { const cols = [ ['Platform', ['Explore stays', 'Workspace Intelligence', 'Become an Operator', 'Focus Score']], ['Company', ['Our story', 'Standards', 'Careers', 'Press']], ['Support', ['Help center', 'Trust & safety', 'Cancellation', 'Contact']], ]; return ( ); } Object.assign(window, { Btn, Badge, Pill, WorkScore, MetricBar, SectionHead, Reveal, NavBar, FooterBar, Logo });