/* SAFRI โ main app. All text/links read from window.SAFRI_DATA (injected by WordPress). */
const { useState: useS, useEffect: useE, useRef: useR } = React;
// Shorthand accessors
const _D = window.SAFRI_DATA || {};
const _S = _D.sections || {};
const _H = _D.hero || {};
const _N = _D.nav || {};
const _F = _D.footer || {};
// Logo: read data-src from the PHP-rendered hidden img (exact string, no browser URL resolution).
// Falls back to SAFRI_DATA.logoUrl which is always an absolute URL from PHP.
const LOGO_URL = (() => {
const el = document.getElementById('safri-logo-el');
if (el && el.dataset && el.dataset.src) return el.dataset.src;
return _D.logoUrl || '';
})();
/* ---------- Nav ---------- */
function Nav(){
const [open, setOpen] = useS(false);
const close = () => setOpen(false);
const links = _D.menu || [
{ label:"Tours", href:"#tours" },
{ label:"Rent a Car", href:"#rent" },
{ label:"Umrah", href:"#umrah" },
{ label:"Reviews", href:"#reviews" },
{ label:"Contact", href:"#contact" },
];
const ctaText = _N.ctaText || "Plan my trip";
return (
{ctaText}
setOpen(o => !o)} aria-label="menu">
);
}
/* ---------- Hero ---------- */
function Hero(){
const eyebrow = _H.eyebrow || "Lahore-based ยท serving all of Pakistan ยท since 2018";
const line1 = _H.line1 || "From Safri";
const line2 = _H.line2 || "to Sukoon.";
const sub = _H.sub || "A Lahore travel agency turning Pakistan's northern peaks and Islam's holiest cities into seamless, worry-free journeys.";
const btn1Text = _H.btn1Text || "Start the journey";
const btn1Href = _H.btn1Href || "#tours";
const btn2Text = _H.btn2Text || "Talk to a human";
return (
{eyebrow}
{line1.replace(/ (\S+)$/, " ")}{line1.trim().split(" ").pop()}
{line2.split(" ")[0]} {line2.split(" ").slice(1).join(" ")}
{sub}
{STATS.slice(0, 3).map((s, i) => (
{s.n} {s.l}
))}
Scroll the story
);
}
/* ---------- Section header ---------- */
function Head({ eyebrow, title, sub, id, center }){
return (
{eyebrow}
{title}
{sub &&
{sub}
}
);
}
/* ---------- Tours ---------- */
function Tours(){
const sc = _S.tours || {};
return (
{(sc.heading || "The north, refined year after year.").replace(/, refined.*/, "")}, refined year after year. >}
sub={sc.sub}
/>
);
}
/* ---------- Rent a Car ---------- */
function Rent(){
const sc = _S.rent || {};
return (
01
{sc.mediaHeadline || "Mountain-graded fleet, driver included."}
{VEHICLES.map((v, i) => (
{v.name} ยท {v.seats} seats
))}
{sc.tag || "Rent a Car"}
{sc.heading || "Sedans to Land Cruisers โ no surprises."}
{sc.sub}
{sc.feat1Title || "Background-checked drivers"} {sc.feat1Desc}
{sc.feat2Title || "4ร4s graded for the Karakoram"} {sc.feat2Desc}
{sc.feat3Title || "All-in daily rate"} {sc.feat3Desc}
{sc.btnText || "Get a vehicle quote"}
);
}
/* ---------- Custom tours ---------- */
function Custom(){
const sc = _S.custom || {};
return (
02
{sc.mediaHeadline || "A Hunza honeymoon. A family Umrah. A solo reset."}
Tailored itinerary
Reply within 24h
Free, no obligation
{sc.tag || "Custom Tours"}
{sc.heading || "Tell us the dream. We'll draft it."}
{sc.sub}
{sc.feat1 || "Built around your group, pace and budget."}
{sc.feat2 || "Nine in-house consultants who know every route."}
{sc.feat3 || "One WhatsApp thread from first idea to safe return."}
{sc.btnText || "Share my brief"}
);
}
/* ---------- Umrah ---------- */
function Umrah(){
const sc = _S.umrah || {};
return (
Pilgrimage, {(sc.heading || "Pilgrimage, perfected.").split(",")[1]?.trim() || "perfected."} >}
sub={sc.sub}
/>
{UMRAH.map((p, i) => (
{p.feature &&
Most chosen }
{p.name}
{p.tier} ยท {p.nights} ยท all-inclusive
{p.pts.map((x, j) => (
{x}
))}
Enquire on WhatsApp
))}
);
}
/* ---------- Why + stats ---------- */
function Why(){
const sc = _S.why || {};
return (
Trusted by {(sc.heading || "Trusted by 12,000+ travellers.").match(/[\d,+โ
]+/)?.[0] || "12,000+"} travellers.>}
sub={sc.sub}
/>
{WHY.map((w, i) => {
const Ic = Icons[w.icon] || Icons.star;
return (
);
})}
{STATS.map((s, i) => (
{s.n} {s.l}
))}
);
}
/* ---------- Reviews ---------- */
function Reviews(){
const [i, setI] = useS(0);
const [paused, pause] = useS(false);
const n = REVIEWS.length;
const sc = _S.reviews || {};
useE(() => {
if (paused) return;
const id = setInterval(() => setI(p => (p + 1) % n), 5200);
return () => clearInterval(id);
}, [paused, n]);
const go = d => setI(p => (p + d + n) % n);
return (
Real journeys, {(sc.heading || "Real journeys, real words.").split(", ")[1] || "real words."} >}
/>
pause(true)} onMouseLeave={() => pause(false)}>
go(-1)} aria-label="previous">
{REVIEWS.map((r, k) => (
{Array.from({length:5}).map((_, s) => )}
{r.text}
{r.initials}
{r.name} {r.trip}
))}
go(1)} aria-label="next">
{REVIEWS.map((_, k) => (
setI(k)} aria-label={"review " + (k + 1)}>
))}
);
}
/* ---------- How it works ---------- */
function How(){
const sc = _S.how || {};
return (
Four steps to {(sc.heading || "Four steps to show up & travel.").split("to ")[1] || "show up & travel."} >}
/>
{STEPS.map((s, i) => (
))}
);
}
/* ---------- CTA band ---------- */
function CTABand(){
const sc = _S.cta || {};
const phoneHref = _D.phoneHref || "tel:+923391172374";
return (
);
}
/* ---------- Footer ---------- */
function Footer(){
const year = _D.year || new Date().getFullYear();
const email = _D.email || "contact@safritravels.com";
const address = _D.address || "Cavalry Ground, near Iceland Khalid Masjid, Lahore 54000";
const phone = _D.phone || "+92 339 117 2374";
const phoneHref = _D.phoneHref || "tel:+923391172374";
return (
);
}
/* ---------- Story rail ---------- */
function StoryRail(){
const beats = [["Drive","#top"],["Mountains","#tours"],["Road","#rent"],["Haram","#umrah"],["Home","#contact"]];
const [active, setActive] = useS(0);
useE(() => {
const id = setInterval(() => { if (window.SafriBG) setActive(Math.round(window.SafriBG.progress)); }, 200);
return () => clearInterval(id);
}, []);
return (
{beats.map(([l, h], i) => (
{l}
))}
);
}
/* ---------- Decorative road car ---------- */
function RoadCar(){
const carRef = useR(null);
useE(() => {
let raf, cur = 0;
const tick = () => {
const max = document.documentElement.scrollHeight - window.innerHeight;
const target = max > 0 ? Math.min(1, Math.max(0, window.scrollY / max)) : 0;
cur += (target - cur) * 0.08;
if (carRef.current) {
const y = 24 + cur * (window.innerHeight - 110);
const tilt = Math.max(-12, Math.min(12, (target - cur) * 120));
carRef.current.style.transform = `translateY(${y}px) rotate(${tilt}deg)`;
}
raf = requestAnimationFrame(tick);
};
tick();
return () => cancelAnimationFrame(raf);
}, []);
return (
);
}
/* ---------- App root ---------- */
function App(){
return (
<>
>
);
}
ReactDOM.createRoot(document.getElementById("app")).render( );