// FRZN — shared UI (Nav, Drawer, Modal, Toast, Checkout, Tweaks, LangSwitcher)
const { useState: useStateUI, useEffect: useEffectUI, useRef: useRefUI } = React;
const LANGS = [
{ code: 'en', label: 'English' },
{ code: 'it', label: 'Italiano' },
{ code: 'fr', label: 'Français' },
{ code: 'de', label: 'Deutsch' },
{ code: 'es', label: 'Español' },
];
const LangSwitcher = ({ lang, onChange }) => {
const [open, setOpen] = useStateUI(false);
const ref = useRefUI(null);
useEffectUI(() => {
const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
document.addEventListener('mousedown', h);
return () => document.removeEventListener('mousedown', h);
}, []);
const cur = LANGS.find(l => l.code === lang);
return (
{LANGS.map(l => (
))}
);
};
const Nav = ({ t, lang, onLang, cartCount, onOpenCart, liveTemp, current }) => {
const [hidden, setHidden] = useStateUI(false);
const [mobileOpen, setMobileOpen] = useStateUI(false);
const lastY = useRefUI(0);
useEffectUI(() => {
const h = () => {
const y = window.scrollY;
setHidden(y > 200 && y > lastY.current);
lastY.current = y;
};
window.addEventListener('scroll', h, { passive: true });
return () => window.removeEventListener('scroll', h);
}, []);
useEffectUI(() => {
document.body.classList.toggle('mobile-open', mobileOpen);
}, [mobileOpen]);
return (
<>
{Array.from({ length: 3 }).map((_, i) => {t.urgency})}
>
);
};
const ThermalSwatch = () => (
);
const CartDrawer = ({ t, open, onClose, items, onQty, onRemove, onCheckout }) => {
const total = items.reduce((s, it) => s + it.price * it.qty, 0);
return (
<>
>
);
};
const Checkout = ({ t, open, items, onClose, onConfirm }) => {
const [pay, setPay] = useStateUI('card');
const total = items.reduce((s, it) => s + it.price * it.qty, 0);
return (
FRZN · {t.cart.checkout.toUpperCase()}
03 · PaymentSTEP 3/3
{['card','klarna','paypal'].map(p => (
))}
{pay === 'card' && (<>
>)}
{pay === 'klarna' &&
Klarna 3× — €{Math.round(total/3)} now · €{Math.round(total/3)} × 2
}
{pay === 'paypal' &&
You\u2019ll be redirected to PayPal to complete.
}
◎ SSL SECURE↩ 60-DAY RETURN✓ EU SHIPPING FREE♺ CARBON NEG.
ORDER · ED. 001
{items.map(it => (
{it.name} · {it.size}
{it.id} · QTY {it.qty}
€{it.price * it.qty}
))}
Subtotal€{total}
ShippingFREE
VAT included€{Math.round(total * 0.18)}
Total€{total}
);
};
const Modal = ({ open, title, body, onClose }) => (
e.stopPropagation()}>
{title}
{body}
);
const TweaksPanel = ({ visible, values, onChange, onClose }) => {
if (!visible) return null;
const ACCENTS = [
{ k:'thermal',hex:'#E8542A'},{k:'ice',hex:'#3B82F6'},{k:'mint',hex:'#00B39A'},
{ k:'ember',hex:'#C2410C'},{k:'xenon',hex:'#FFB800'},{k:'graphite',hex:'#0A0D10'},
];
return (
Tweaks
{[['glacial','Glacial'],['nordic','Nordic'],['clinical','Clinical']].map(([k,l]) => (
))}
{ACCENTS.map(a => (
{[['thermal','Thermal'],['vitality','Vitality'],['protocol','Protocol']].map(([k,l]) => (
))}
{[['display','Bricolage'],['editorial','Fraunces'],['grotesk','Archivo']].map(([k,l]) => (
))}
{[['default','Lab'],['editorial','Editorial'],['spec','Spec']].map(([k,l]) => (
))}
{[['compact','Compact'],['normal','Normal'],['spacious','Spacious']].map(([k,l]) => (
))}
);
};
const Toast = ({ msg, show }) => ({msg}
);
const StickyBuy = ({ t, onReserve, show }) => (
FRZN · ED. 001
From €140 · {t.hero.metaReleaseV}
);
Object.assign(window, { LANGS, Nav, LangSwitcher, CartDrawer, Checkout, Modal, TweaksPanel, Toast, StickyBuy, ThermalSwatch });