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