/* Sections — DCAF microsite. Each section is a stand-alone React component that reads from props.t (translated content tree) and props.lang. */ const { useState, useEffect, useRef } = React; // ─── DCAF logo (inline) ─── function DCAFLogo({ height = 18, color = "currentColor" }) { return ( ); } // ─── Reveal on scroll ─── function Reveal({ children, delay = 0, as = "div", className = "" }) { const ref = useRef(null); const [shown, setShown] = useState(false); useEffect(() => { if (!ref.current) return; let done = false; const mark = () => { if (!done) { done = true; setShown(true); } }; // Safety: if IntersectionObserver doesn't fire (some preview iframes), // show after a short delay anyway. The cascade still feels staggered. const fallback = setTimeout(mark, 350); let obs; try { obs = new IntersectionObserver(([e]) => { if (e.isIntersecting) { mark(); obs.disconnect(); } }, { threshold: 0.05, rootMargin: '0px 0px -10% 0px' }); obs.observe(ref.current); } catch (e) { mark(); } return () => { clearTimeout(fallback); if (obs) obs.disconnect(); }; }, []); const Tag = as; return {children}; } const ArrowR = ({ size = 14 }) => ( ); // ─── Header ─── function SiteHeader({ t, lang, setLang, theme }) { const [scrolled, setScrolled] = useState(false); const [onPaper, setOnPaper] = useState(false); useEffect(() => { const onScroll = () => { setScrolled(window.scrollY > 40); // detect background of element under header center const el = document.elementFromPoint(window.innerWidth / 2, 60); if (el) { const closest = el.closest('.facility, .enquiry'); setOnPaper(!!closest); } }; onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); return (
{['en','ar','ru','zh'].map(l => ( ))}
{t.cta.quote}
); } // ─── Hero ─── function Hero({ t, lang, heroImg }) { const dir = lang === 'ar' ? 'rtl' : 'ltr'; return (
DCAF facility at Dubai South
Live · 24h ops · OMDW
Position {t.hero.coords[0]}
{t.hero.coords[1]}
{t.hero.coords[2]}
● {t.hero.bearing}
{t.hero.eyebrow}

{t.hero.h1a}{t.hero.h1b}{t.hero.h1c}

{t.hero.sub}

{t.hero.cta1} {t.hero.cta2}
scroll
); } // ─── Ticker ─── function Ticker({ t }) { // duplicate so the loop is seamless const items = [...t.ticker, ...t.ticker, ...t.ticker]; return (
{items.map((line, i) => ( {line} ))}
); } // ─── JV strip ─── function JVStrip({ t, lang }) { return (
{t.jv.left} {t.jv.leftEm}
"{t.jv.center}"
{t.jv.right} {t.jv.rightEm}
); } // ─── Services ─── function Services({ t }) { return (
{t.services.eyebrow}

{t.services.h2a}{t.services.h2b}{t.services.h2c}

{t.services.lede}

{t.services.list.map((s, i) => (
{s.title}
/ {s.num} {s.tag}

{s.title}

{s.body}

{s.link}
))}
); } // ─── Facility specs ─── function Facility({ t }) { return (
{t.facility.eyebrow}

{t.facility.h2}

{t.facility.lede}

DCAF hangar
{t.facility.specs.map((s, i) => (
/ {s.num} {s.label}{s.desc} {s.value}{s.unit.includes('m²') ? '' : ''} {s.unit}
))}
{t.facility.chips.map((c, i) => ( {c} ))}
); } // ─── Charter fleet ─── function Charter({ t }) { const [active, setActive] = useState('g650'); return (
{t.charter.eyebrow}

{t.charter.h2}

{t.charter.lede}

{t.charter.fleet.map((f, i) => (
setActive(f.id)}>
{f.img ? {f.name} : ( RENDER · {f.id.toUpperCase()} )}
{f.tag}

{f.name}

{f.range}
PAX{f.pax}
BEDS{f.beds}
WI-FI{f.wifi}
))}
{t.charter.note}
); } // ─── Credentials ─── function Credentials({ t }) { return (
{t.credentials.eyebrow}

{t.credentials.h2a}{t.credentials.h2b}{t.credentials.h2c}

{t.credentials.lede}

{t.credentials.quoteSrc}

"{t.credentials.quote}"

{t.credentials.badges.map((b, i) => (
{b.num}{b.numEm && {b.numEm}}
{b.title}{b.sub}
))}
); } // ─── Enquiry (multi-step form) ─── function Enquiry({ t, lang }) { const [step, setStep] = useState(0); const [submitted, setSubmitted] = useState(false); const [data, setData] = useState({ service: '', name: '', company: '', email: '', phone: '', aircraft: '', from: 'OMDW', to: '', date: '', pax: '', message: '' }); const L = t.enquiry.labels; const set = (k, v) => setData(d => ({ ...d, [k]: v })); const canNext = () => { if (step === 0) return !!data.service; if (step === 1) return data.from && data.to && data.date; if (step === 2) return data.name && data.email; return false; }; const ref = String(Math.floor(100000 + Math.random() * 900000)); return (
{t.enquiry.eyebrow}

{t.enquiry.h2a}{t.enquiry.h2b}.

{t.enquiry.lede}

{t.enquiry.contact.map((c, i) => (
{c.k} {c.v}
))}
Direct response
Our 24-hour Dubai desk handles every enquiry — no offshore call centre, no IVR. Average first response is under an hour.
{submitted ? (

{t.enquiry.success.h}

{t.enquiry.success.p}DCAF-{ref}

) : (
{t.enquiry.steps.map((s, i) => ( ))}
{step === 0 && (
{t.enquiry.svcOptions.map(o => ( ))}
)} {step === 1 && (
set('aircraft', e.target.value)} placeholder="G650 / Global 7500 / …" />
set('from', e.target.value)} placeholder="OMDW" />
set('to', e.target.value)} placeholder="EGLF / LFPB" />
set('date', e.target.value)} placeholder="14 Jun 2026" />
set('pax', e.target.value)} placeholder="6" />
)} {step === 2 && (
set('name', e.target.value)} />
set('company', e.target.value)} />
set('email', e.target.value)} />
set('phone', e.target.value)} placeholder="+971 …" />