// Shared components — Nav, Footer, Buttons, Icons, FAB, Modal const { useState, useEffect, useRef, useMemo } = React; // ---------- Icons ---------- const Icon = ({ name, size = 20 }) => { const s = size; const paths = { arrow: , phone: , wa: <>, mail: <>, pin: <>, calendar: <>, play: , check: , classroom: <>, art: <>, music: <>, book: <>, media: <>, field: <>, tree: <>, gym: <>, star: , globe: <>, leaf: <>, shield: , sparkle: <>, sun: <>, heart: , close: <>, menu: <>, chev: , chevDown: , instagram: <>, facebook: , }; return ( {paths[name] || null} ); }; // ---------- Brand mark (mini-crest as SVG fallback so it always renders) ---------- const BrandMark = ({ size = 44 }) => ( Odyssey Nursery ); // ---------- Locale switch ---------- const LocaleSwitch = ({ locale, onChange }) => (
{["EN", "AR", "RU", "ZH"].map(l => ( ))}
); // ---------- Nav ---------- const Nav = ({ route, navigate, locale, setLocale, onBookTour }) => { const items = [ ["home", "Home"], ["curriculum", "Curriculum"], ["programmes", "Programmes"], ["facilities", "Facilities"], ["branches", "Branches"], ["admissions", "Admissions"], ["about", "About"] ]; return (
); }; // ---------- WhatsApp FAB ---------- const Fab = () => ( Chat on WhatsApp ); // ---------- Mobile sticky CTA ---------- const MobileCta = ({ onBookTour }) => (
Call
); // ---------- Modal ---------- const Modal = ({ open, onClose, children }) => { useEffect(() => { if (!open) return; const onKey = (e) => e.key === "Escape" && onClose(); document.addEventListener("keydown", onKey); return () => document.removeEventListener("keydown", onKey); }, [open, onClose]); if (!open) return null; return (
e.stopPropagation()}> {children}
); }; // ---------- Footer ---------- const Footer = ({ navigate }) => { const D = window.OdysseyData; return ( ); }; // ---------- Tour booking form (multi-step) ---------- const TourForm = ({ onClose, presetBranch }) => { const D = window.OdysseyData; const [step, setStep] = useState(0); const [done, setDone] = useState(false); const [data, setData] = useState({ branch: presetBranch || "", age: "", name: "", phone: "", email: "", childAge: "", when: "", msg: "" }); const update = (k, v) => setData(d => ({ ...d, [k]: v })); const steps = [ { title: "Which branch?", help: "Pick the campus that's most convenient." }, { title: "About your child", help: "We'll match the right age group and key person." }, { title: "Your contact", help: "We reply within one working day." } ]; const next = () => setStep(s => Math.min(s + 1, 2)); const back = () => setStep(s => Math.max(s - 1, 0)); const submit = () => setDone(true); if (done) { return (

Tour request received

Thank you, {data.name || "friend"}. A member of the Odyssey admissions team will reply within one working day.

); } const canNext = (step === 0 && data.branch) || (step === 1 && data.age) || (step === 2 && data.name && data.phone); return (
Book a Tour · Step {step + 1} of 3
{[0, 1, 2].map(i => (
))}

{steps[step].title}

{steps[step].help}

{step === 0 && (
{D.branches.map(b => ( ))}
)} {step === 1 && (
{D.ageGroups.map(a => ( ))}
update("childAge", e.target.value)} />
)} {step === 2 && (
update("name", e.target.value)} placeholder="Full name" />
update("phone", e.target.value)} placeholder="+971 ..." />
update("email", e.target.value)} placeholder="you@example.com" />
update("when", e.target.value)} placeholder="Weekday morning, this Thursday, etc." />
)}
{step < 2 ? ( ) : ( )}
); }; Object.assign(window, { Icon, BrandMark, Nav, Footer, Fab, MobileCta, Modal, TourForm });