/* global React, ReactDOM */
/* Shared site chrome: Icon, Header, Footer, BookingModal, helpers.
Exposed on window for every page. */
const NS = window.BlackLuxuryLimoDesignSystem_3e0f4d;
const { useState: useCS, useEffect: useCE } = React;
const PHONE = "(416) 555-0182";
const PHONE_HREF = "tel:+14165550182";
function Icon({ name, size = 18, color, style }) {
return ;
}
window.Icon = Icon;
/* ---- shared nav model ---- */
const NAV = [
{ label: "Home", href: "index.html" },
{ label: "Services", href: "services.html" },
{ label: "Fleet", href: "fleet.html" },
{ label: "Rates", href: "rates.html" },
{ label: "Airport", href: "airport-limo.html" },
{ label: "About", href: "about.html" },
{ label: "Contact", href: "contact.html" },
];
window.SITE_PHONE = PHONE;
window.SITE_PHONE_HREF = PHONE_HREF;
/* persist a quote so the Reserve flow can prefill */
function saveQuote(q) {
try { localStorage.setItem("bll_quote", JSON.stringify(q)); } catch (e) {}
}
function loadQuote() {
try { return JSON.parse(localStorage.getItem("bll_quote") || "null"); } catch (e) { return null; }
}
window.saveQuote = saveQuote;
window.loadQuote = loadQuote;
function Header({ active }) {
const { Button } = NS;
const [open, setOpen] = useCS(false);
return (
);
}
window.Header = Header;
/* section heading used across pages */
function SectionHead({ eyebrow, title, sub, align = "center" }) {
return (
{eyebrow}
{title}
{sub &&
{sub}
}
);
}
window.SectionHead = SectionHead;
/* compact page banner for interior pages */
function PageHero({ eyebrow, title, sub, img = "assets/img/city-dusk.jpg", crumbs }) {
return (
{crumbs && (
)}
{eyebrow &&
{eyebrow}}
{title}
{sub &&
{sub}
}
);
}
window.PageHero = PageHero;
function Footer() {
const { Button, TrustBar } = NS;
return (
);
}
window.Footer = Footer;
/* image disclaimer note reused near placeholder photography */
function ImageryNote({ style, text }) {
return (
{text || "Representative imagery — not the actual fleet. Real vehicle photos pending."}
);
}
window.ImageryNote = ImageryNote;
/* ---- shared FAQ accordion ---- */
function Accordion({ items }) {
const [open, setOpen] = useCS(0);
return (
{items.map((it, i) => {
const isOpen = open === i;
return (
{isOpen && (
{it.a}
)}
);
})}
);
}
window.Accordion = Accordion;
/* ---- airports we serve: partner logo strip ---- */
function AirportStrip({ title = "Serving Ontario's airports", showReview = true }) {
const logos = [
{ src: "assets/logo/pearson.png", alt: "Toronto Pearson International Airport", h: 72 },
{ src: "assets/logo/billy-bishop.png", alt: "Billy Bishop Toronto City Airport", h: 34 },
{ src: "assets/logo/waterloo.svg", alt: "Region of Waterloo International Airport", h: 54 },
];
return (
{title}
{logos.map((l) => (

))}
{showReview && (
4.9 ★
1,200+ reviews
)}
);
}
window.AirportStrip = AirportStrip;
/* ---- quick-quote modal (home hero) ---- */
function BookingModal({ open, onClose, quote }) {
const { Button, Badge } = NS;
if (!open) return null;
const q = quote || {};
const line = (label, value, mono) => (
{label}
{value}
);
return (
e.stopPropagation()} style={{ width: "480px", maxWidth: "100%", maxHeight: "90vh", overflow: "auto", background: "var(--surface-1)", border: "1px solid var(--border-gold)", borderRadius: "var(--r-xl)", boxShadow: "var(--shadow-xl)" }}>
Your Instant Quote
Luxury SUV · Escalade
{line("From", q.pickup || "YYZ — Pearson T1")}
{line("To", q.dropoff || "Downtown Toronto")}
{line("Pickup", (q.date || "Jun 24, 2026") + " · " + (q.time || "6:45 AM"), true)}
{line("Distance", "27 km · ~32 min", true)}
All-in fare from
$92.00
Includes meet & greet, flight tracking, taxes and gratuity. No surge pricing.
}>Reserve This Car
);
}
window.BookingModal = BookingModal;
/* ---- generic mount with lucide icon hydration ---- */
window.mountPage = function (element, activeHref) {
function go() {
if (!window.BlackLuxuryLimoDesignSystem_3e0f4d || !window.React || !window.ReactDOM) { setTimeout(go, 30); return; }
const root = document.getElementById("root");
ReactDOM.createRoot(root).render(element);
let obs, scheduled = false;
const refresh = () => {
if (!window.lucide) return;
if (obs) obs.disconnect();
window.lucide.createIcons();
if (obs) obs.observe(root, { childList: true, subtree: true });
};
const schedule = () => { if (scheduled) return; scheduled = true; requestAnimationFrame(() => { scheduled = false; refresh(); }); };
obs = new MutationObserver(schedule);
obs.observe(root, { childList: true, subtree: true });
setTimeout(refresh, 80);
}
go();
};