/* ─── Hoopooes sections ─────────────────────────────────────── All content comes from window.HOOPOE_* objects defined in index.html. Edit content there — not here. ───────────────────────────────────────────────────────────── */ /* ── Nav ──────────────────────────────────────────────────── */ function Nav({ onQuote }) { const [open, setOpen] = React.useState(false); const close = () => setOpen(false); React.useEffect(() => { document.body.style.overflow = open ? 'hidden' : ''; return () => { document.body.style.overflow = ''; }; }, [open]); const links = [ { href: '#about', label: 'About' }, { href: '#services', label: 'Services' }, { href: '#products', label: 'Products' }, { href: '#gallery', label: 'Gallery' }, { href: '#work', label: 'Case Studies' }, { href: '#faqs', label: 'FAQs' }, { href: '#contact', label: 'Contact' }, ]; return ( ); } /* ── Hero ─────────────────────────────────────────────────── */ function Hero({ onQuote, showMarquee }) { const H = window.HOOPOE_HERO || {}; const stats = H.stats || []; const marquee = H.marquee || []; return (

{H.tag1}
{H.tag2}
{H.tagAccent}

{H.subtitle}

{H.ctaSecondary}
{H.videoUrl && (
{H.videoHeading &&

{H.videoHeading}

}
)}
{stats.map((s, i) => (
{s.n}{s.suffix}
{s.label}
))}
{showMarquee && marquee.length > 0 && (
{[...Array(2)].map((_, k) => ( {marquee.map((m, i) => {m})} ))}
)}
); } /* ── About ────────────────────────────────────────────────── */ function About({ onQuote }) { const ref = useReveal(); const A = window.HOOPOE_ABOUT || {}; const Q = A.quoteCard || {}; return (
01 / About Us

{A.heading}

{A.aside}

{(A.paragraphs || []).map((p, i) =>

{p}

)}
); } /* ── Services ─────────────────────────────────────────────── */ const SERVICES = window.HOOPOE_SERVICES || []; const SERVICE_PLACEHOLDER_KEY = { carbon: 'carbon', rubber: 'rubber', carmat: 'carmat', asbestos: 'asbestos', eps: 'eps', acrylic: 'acrylic', mdf: 'mdf', aluminium: 'aluminium', stainless: 'stainless', cnc: 'cnc', industrial: 'industrial', }; function Services({ onQuote }) { return (
02 / Our Services

One workshop. Every CNC & fabrication need.

We offer one complete solution for all CNC works and customised fabrication — prototypes, industrial parts, decorative panels, signage, packaging, automotive accessories, architectural elements or bulk production.

{SERVICES.map(s => { const placeholderKey = SERVICE_PLACEHOLDER_KEY[s.id] || s.id; const Placeholder = SERVICE_IMAGES[placeholderKey]; return (
{s.image ? {s.title} : (Placeholder ? : null)}
{s.num}

{s.title}

{s.blurb}

{(s.tags || []).map(t => {t})}
); })}
); } /* ── Products (separate from Services) ────────────────────── */ function Products({ onQuote }) { const P = window.HOOPOE_PRODUCTS || {}; const items = P.items || []; if (items.length === 0) return null; return (
04 / Product Range

{P.heading || 'Our product range.'}

{P.aside}

{items.map((s, i) => (
{s.image ? {s.title} : null}
{String(i + 1).padStart(2, '0')}

{s.title}

{s.blurb}

{(s.tags || []).map(t => {t})}
))}
); } /* ── Design & Development Support ─────────────────────────── */ function DesignSupport() { const D = window.HOOPOE_DESIGN || {}; return (
05 / Design & Development

{D.heading}

{(D.paragraphs || []).map((p, i) => (

{p}

))}
{(D.steps || []).map((s) => (
{s.n}

{s.t}

{s.d}

))}
); } /* ── Why Choose Us ────────────────────────────────────────── */ function WhyChooseUs() { const W = window.HOOPOE_WHY || {}; return (
06 / Why Choose Us

{W.heading}

{W.aside}

{(W.items || []).map((w, i) => (

{w.t}

{w.d}

))}
); } /* ── Final CTA band ───────────────────────────────────────── */ function FinalCTA({ onQuote }) { const C = window.HOOPOE_CTA || {}; return (
{C.eyebrow}

{C.heading}

{C.body}

{C.secondary}
); } /* ── Case Studies ─────────────────────────────────────────── */ const CASES = window.HOOPOE_CASES || []; function CaseStudies({ count = 4 }) { const items = CASES.slice(0, count); return (
07 / Selected Work

Case studies, signed off and shipped.

A sample of projects across our four largest sectors. All numbers verified by client sign-off.

{items.map((c, i) => { const PlaceholderImg = CASE_IMAGES[i % CASE_IMAGES.length]; return (
{c.image ? {c.title} : }
{c.sector} {c.code} / 2026

{c.title}

{c.blurb}

{(c.stats || []).map((s, j) => (
{s.n}
{s.l}
))}
); })}
); } /* ── Clients ──────────────────────────────────────────────── */ const CLIENTS = window.HOOPOE_CLIENTS || []; function Clients() { const T = window.HOOPOE_TESTIMONIAL || {}; return (
08 / Clients & Partners

Trusted by teams across the Gulf.

From government primes to retail studios — long-term partnerships are how we work best.

{CLIENTS.map((c, i) => (
{c.logo ? ( {c.n} ) : (
{c.n} {c.s}
)}
))}
{T.quote && (
"{T.quote}"
{T.author}{T.company ? ` · ${T.company}` : ''} {T.reference}
)}
); } /* ── FAQs ─────────────────────────────────────────────────── */ const FAQS = window.HOOPOE_FAQS || []; function FAQs() { const [openIdx, setOpenIdx] = React.useState(0); return (
09 / FAQs

Common questions, answered straight.

Don't see your question? Ask in the contact form — most are answered within the hour during workshop hours.

{FAQS.map((f, i) => (
{f.a}
))}
); } /* ── Contact ──────────────────────────────────────────────── */ function Contact({ onQuote }) { const C = window.HOOPOE_CONTACT || {}; const [form, setForm] = React.useState({ name: '', email: '', phone: '', company: '', service: '', message: '', file: null, }); const [drag, setDrag] = React.useState(false); const [sent, setSent] = React.useState(false); const [sending, setSending] = React.useState(false); const [error, setError] = React.useState(''); const onDrop = (e) => { e.preventDefault(); setDrag(false); const file = e.dataTransfer?.files?.[0]; if (file) setForm(f => ({ ...f, file })); }; const onSubmit = async (e) => { e.preventDefault(); setError(''); setSending(true); try { const fd = new FormData(); fd.append('name', form.name); fd.append('email', form.email); fd.append('phone', form.phone); fd.append('company', form.company); fd.append('service', form.service); fd.append('message', form.message); if (form.file) fd.append('file', form.file); // "formAction" can be set in HOOPOE_CONTACT (index.html). Defaults to send.php const action = C.formAction || 'send.php'; const res = await fetch(action, { method: 'POST', body: fd }); let data = {}; try { data = await res.json(); } catch (_) {} if (res.ok && data.ok) { setSent(true); } else { setError(data.error || ('Could not send right now. Please email us directly at ' + (C.email || 'hello@hoopooes.ae') + '.')); } } catch (err) { setError('Could not connect to the mail service. Please email us directly at ' + (C.email || 'hello@hoopooes.ae') + '.'); } finally { setSending(false); } }; // Workshop address can contain "\n" for line breaks const workshopLines = (C.workshop || '').split('\n'); return (
10 / Get in Touch

{C.heading}

{C.blurb}

Workshop
{workshopLines.map((ln, i) => ( {ln}{i < workshopLines.length - 1 ?
: null}
))}
Phone
Email
Hours
{C.hours}
{sent ? (

Message received.

Thanks {form.name || '—'}. We'll reply to {form.email || 'your email'} within one business day.

) : ( <>
setForm(f => ({ ...f, name: e.target.value }))} placeholder="Your name"/>
setForm(f => ({ ...f, company: e.target.value }))} placeholder="Company"/>
setForm(f => ({ ...f, email: e.target.value }))} placeholder="you@company.com"/>
setForm(f => ({ ...f, phone: e.target.value }))} placeholder="+971 …"/>