/* ── DominiTrip UI Primitives ── */ /* ── Scroll Animation Hook ── */ function useScrollReveal(ref) { const [visible, setVisible] = React.useState(false); React.useEffect(() => { if (!ref.current) return; const obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { setVisible(true); obs.disconnect(); } }, { threshold: 0.12 }); obs.observe(ref.current); return () => obs.disconnect(); }, [ref]); return visible; } function AnimatedSection({ children, className = '', style = {}, delay = 0 }) { const ref = React.useRef(null); const visible = useScrollReveal(ref); return (
{children}
); } /* ── Section Header ── */ function SectionHeader({ tag, title, subtitle, align = 'center', light = false }) { const sectionHeaderStyles = { wrapper: { textAlign: align, marginBottom: 48, maxWidth: 680, marginLeft: align === 'center' ? 'auto' : 0, marginRight: align === 'center' ? 'auto' : 0 }, tag: { display: 'inline-block', fontFamily: 'var(--font-display)', fontSize: 13, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.12em', color: light ? 'rgba(255,255,255,0.7)' : 'var(--primary)', background: light ? 'rgba(255,255,255,0.12)' : 'var(--primary-glow)', padding: '6px 16px', borderRadius: 100, marginBottom: 14, }, title: { fontSize: 'clamp(28px, 4.5vw, 44px)', fontWeight: 800, lineHeight: 1.12, color: light ? '#fff' : 'var(--text)', marginBottom: subtitle ? 14 : 0, }, subtitle: { fontSize: 17, color: light ? 'rgba(255,255,255,0.75)' : 'var(--text-secondary)', lineHeight: 1.6, maxWidth: 560, margin: align === 'center' ? '0 auto' : 0 }, }; return (
{tag && {tag}}

{title}

{subtitle &&

{subtitle}

}
); } /* ── Button ── */ function Btn({ children, variant = 'primary', size = 'md', onClick, style = {}, icon, full }) { const [hovered, setHovered] = React.useState(false); const base = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8, fontFamily: 'var(--font-display)', fontWeight: 600, cursor: 'pointer', border: 'none', borderRadius: 'var(--radius-sm)', transition: 'all 0.25s ease', textDecoration: 'none', whiteSpace: 'nowrap', width: full ? '100%' : 'auto', }; const sizes = { sm: { fontSize: 13, padding: '8px 18px' }, md: { fontSize: 15, padding: '12px 28px' }, lg: { fontSize: 17, padding: '16px 36px' }, }; const variants = { primary: { background: 'var(--primary)', color: 'var(--text-on-primary)', transform: hovered ? 'translateY(-2px)' : 'none', boxShadow: hovered ? '0 8px 24px var(--primary-glow)' : 'none' }, accent: { background: 'var(--accent)', color: '#fff', transform: hovered ? 'translateY(-2px)' : 'none', boxShadow: hovered ? '0 8px 24px rgba(251,146,60,0.3)' : 'none' }, outline: { background: 'transparent', color: 'var(--primary)', border: '2px solid var(--primary)', transform: hovered ? 'translateY(-2px)' : 'none' }, ghost: { background: hovered ? 'var(--primary-glow)' : 'transparent', color: 'var(--primary)' }, white: { background: '#fff', color: 'var(--primary)', transform: hovered ? 'translateY(-2px)' : 'none', boxShadow: hovered ? '0 8px 24px rgba(0,0,0,0.15)' : 'none' }, whatsapp: { background: '#25D366', color: '#fff', transform: hovered ? 'translateY(-2px)' : 'none', boxShadow: hovered ? '0 8px 24px rgba(37,211,102,0.35)' : 'none' }, }; return ( ); } /* ── Wave Divider ── */ function WaveDivider({ flip, color, style = {} }) { return (
); } /* ── Badge ── */ function Badge({ children, style = {} }) { return ( {children} ); } /* ── Star Rating ── */ function Stars({ rating = 5, size = 16 }) { return (
{Array.from({ length: 5 }, (_, i) => ( ))}
); } /* ── Card ── */ function Card({ children, style = {}, onClick, hoverable = true }) { const [hovered, setHovered] = React.useState(false); return (
setHovered(true)} onMouseLeave={() => setHovered(false)} style={{ background: 'var(--surface)', borderRadius: 'var(--radius)', boxShadow: hovered && hoverable ? 'var(--card-shadow-hover)' : 'var(--card-shadow)', transition: 'all 0.35s cubic-bezier(0.22,1,0.36,1)', transform: hovered && hoverable ? 'translateY(-4px)' : 'none', overflow: 'hidden', cursor: onClick ? 'pointer' : 'default', ...style, }}>{children}
); } /* ── SVG Icons ── */ const Icons = { plane: , van: , hotel: , users: , clock: , wifi: , shield: , globe: , drink: , heart: , phone: , chevDown: , mapPin: , close: , menu: , calendar: , check: , arrowRight: , car: , tag: , }; /* ── Currency Formatter ── */ const CAD_RATE = 1.37; function formatPrice(usd, currency = 'USD') { if (currency === 'CAD') return `C$${Math.round(usd * CAD_RATE)}`; return `$${usd}`; } function formatPriceLabel(usd, currency = 'USD') { if (currency === 'CAD') return `C$${Math.round(usd * CAD_RATE)}`; return `$${usd}`; } /* ── Currency Toggle Pill ── */ function CurrencyToggle({ currency, onChange }) { const active = { background: 'var(--primary)', color: 'var(--text-on-primary)' }; const inactive = { background: 'transparent', color: 'var(--text-secondary)' }; const btnBase = { padding: '4px 12px', borderRadius: 100, border: 'none', cursor: 'pointer', fontFamily: 'var(--font-display)', fontSize: 12, fontWeight: 700, transition: 'all 0.2s', }; return (
); } /* ── Export to window ── */ Object.assign(window, { useScrollReveal, AnimatedSection, SectionHeader, Btn, WaveDivider, Badge, Stars, Card, Icons, formatPrice, formatPriceLabel, CurrencyToggle, CAD_RATE, });