// Shared components for VIP Motors prototype
const { useState, useEffect, useRef, useMemo } = React;
// ── Logo ──────────────────────────────────────────────────────────
function Logo({ variant = "white", height = 28 }) {
const src = variant === "white" ? "media/logo-white.webp" : "media/logo-dark.webp";
return (
);
}
// ── Icons (inline minimal SVG) ────────────────────────────────────
function Icon({ name, size = 16 }) {
const s = size;
const stroke = { fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round" };
switch (name) {
case "arrow": return ;
case "arrow-up-right": return ;
case "close": return ;
case "phone": return ;
case "whatsapp": return ;
case "mail": return ;
case "pin": return ;
case "menu": return ;
case "filter": return ;
case "back": return ;
case "share": return ;
case "heart": return ;
case "check": return ;
case "globe": return ;
case "camera": return ;
default: return null;
}
}
// ── Navbar ────────────────────────────────────────────────────────
function Nav({ route, navigate, onEnquire, tweaks }) {
const [scrolled, setScrolled] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const [lang, setLang] = useState("EN");
const onPaper = route === "sell" || route === "contact";
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
const links = [
{ k: "inventory", label: "Inventory" },
{ k: "sell", label: "Sell / Trade-in" },
{ k: "finance", label: "Finance" },
{ k: "about", label: "About" },
{ k: "contact", label: "Contact" }
];
const bg = scrolled || onPaper
? (onPaper ? "rgba(246,245,243,0.92)" : "rgba(11,12,14,0.82)")
: "transparent";
const color = onPaper ? "var(--ink-on-paper)" : "var(--text)";
const borderB = scrolled || onPaper
? `1px solid ${onPaper ? "var(--rule)" : "var(--line)"}`
: "1px solid transparent";
return (
);
}
// ── Vehicle Card ──────────────────────────────────────────────────
function VehicleCard({ v, onOpen, onEnquire, paper }) {
return (
onOpen(v.slug)}>

{v.badges && v.badges.length > 0 && (
{v.badges.slice(0, 2).map(b => (
{b}
))}
)}
{v.year} · {v.body}
{v.make} {v.model}
{v.headline && (
{v.headline}
)}
Request price
);
}
// ── Modal ─────────────────────────────────────────────────────────
function Modal({ open, onClose, title, children }) {
useEffect(() => {
if (!open) return;
const onKey = (e) => e.key === "Escape" && onClose();
document.addEventListener("keydown", onKey);
document.body.style.overflow = "hidden";
return () => {
document.removeEventListener("keydown", onKey);
document.body.style.overflow = "";
};
}, [open, onClose]);
if (!open) return null;
return (
e.stopPropagation()}>
{children}
);
}
// ── Enquiry form ──────────────────────────────────────────────────
function EnquiryForm({ vehicle, type = "Enquire", onSubmitted }) {
const [data, setData] = useState({ name: "", phone: "", email: "", message: "" });
const [sent, setSent] = useState(false);
const update = (k) => (e) => setData({ ...data, [k]: e.target.value });
const onSubmit = (e) => {
e.preventDefault();
setSent(true);
setTimeout(() => onSubmitted && onSubmitted(), 1600);
};
if (sent) {
return (
Enquiry received.
A specialist will respond within one business day, typically by WhatsApp.
);
}
return (
);
}
// ── Footer ────────────────────────────────────────────────────────
function Footer({ navigate }) {
return (
);
}
// ── Quick rail ────────────────────────────────────────────────────
function QuickRail({ onEnquire }) {
return (
);
}
Object.assign(window, { Logo, Icon, Nav, VehicleCard, Modal, EnquiryForm, Footer, QuickRail });