/* ── DominiTrip App Shell ── */
const { useState, useEffect, useRef, useCallback } = React;
/* ── Navbar ── */
function Navbar({ onBook, scrolled, currency, onCurrencyChange }) {
const [menuOpen, setMenuOpen] = useState(false);
const links = [
{ label: 'Tours', href: '#tours' },
{ label: 'Transfers', href: '#transfers' },
{ label: 'FAQ', href: '#faq' },
{ label: 'Contact', href: '#footer' },
];
return (
DominiTrip
{/* Desktop Links */}
{links.map(l => (
{l.label}
))}
{Icons.phone}
+1 (809) 555-0123
Book Now
{/* Mobile menu toggle */}
setMenuOpen(!menuOpen)} style={{
display: 'none', background: 'none', border: 'none', color: scrolled ? 'var(--text)' : '#fff', cursor: 'pointer',
}} className="mobile-toggle">{menuOpen ? Icons.close : Icons.menu}
{/* Mobile dropdown */}
{menuOpen && (
)}
);
}
/* ── Hero ── */
function Hero({ onBook, onExploreTours, heroStyle }) {
return (
{/* Hero background image + gradient overlay */}
{/* Decorative waves */}
{/* Floating circles */}
Licensed & Insured · MITUR Registered · Free Cancellation
Skip the Tourist Traps.
See the Real Punta Cana.
No forced gift shops. No unmarked cars. No surprise fees. Just licensed transfers, curated tours, and bilingual drivers — trusted by US & Canadian travelers.
Book a Transfer
Explore Tours
{heroStyle === 'split' && (
Quick Book
{['Airport Pickup', 'Hotel Transfer', 'Tour Excursion'].map(s => (
e.currentTarget.style.background = 'rgba(255,255,255,0.2)'}
onMouseLeave={e => e.currentTarget.style.background = 'rgba(255,255,255,0.12)'}
>
{s} {Icons.arrowRight}
))}
)}
);
}
/* ── Booking Modal ── */
function BookingModal({ open, onClose, preselected }) {
const [step, setStep] = useState(1);
const [form, setForm] = useState({ type: 'tour', tour: '', pickup: '', dropoff: '', date: '', guests: 2, name: '', phone: '', promo: '' });
useEffect(() => {
if (preselected) {
if (preselected.isTransfer) {
setForm(f => ({ ...f, type: 'transfer', pickup: preselected.name }));
} else {
setForm(f => ({ ...f, type: 'tour', tour: preselected.name }));
}
}
}, [preselected]);
useEffect(() => { if (open) setStep(1); }, [open]);
if (!open) return null;
const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
const fieldStyle = {
width: '100%', padding: '12px 16px', borderRadius: 'var(--radius-sm)',
border: '2px solid var(--text-muted)', fontFamily: 'var(--font-body)', fontSize: 15,
background: 'var(--surface)', color: 'var(--text)', outline: 'none', transition: 'border-color 0.2s',
};
const labelStyle = { fontSize: 13, fontWeight: 600, fontFamily: 'var(--font-display)', color: 'var(--text)', marginBottom: 6, display: 'block' };
return (
{ if (e.target === e.currentTarget) onClose(); }}>
{step === 1 ? 'What do you need?' : step === 2 ? 'Trip Details' : step === 3 ? 'Your Info' : 'Confirmed!'}
{Icons.close}
{/* Progress */}
{[1, 2, 3].map(s => (
))}
{step === 1 && (
{['tour', 'transfer'].map(t => (
set('type', t)} style={{
padding: '20px 16px', borderRadius: 'var(--radius-sm)', cursor: 'pointer', textAlign: 'center',
border: form.type === t ? '2px solid var(--primary)' : '2px solid var(--text-muted)',
background: form.type === t ? 'var(--primary-glow)' : 'var(--surface)',
fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 15, color: 'var(--text)',
transition: 'all 0.2s',
}}>
{t === 'tour' ? Icons.mapPin : Icons.van}
{t === 'tour' ? 'Book a Tour' : 'Book a Transfer'}
))}
{form.type === 'tour' && (
Select Tour
set('tour', e.target.value)} style={fieldStyle}>
Choose a tour...
{TOURS.map(t => {t.name} — ${t.price} )}
)}
setStep(2)} icon={Icons.arrowRight}>Continue
)}
{step === 2 && (
{form.type === 'transfer' && (
<>
Pickup Location
set('pickup', e.target.value)} style={fieldStyle} />
Dropoff Location
set('dropoff', e.target.value)} style={fieldStyle} />
>
)}
Date
set('date', e.target.value)} style={fieldStyle} />
Guests
set('guests', Math.max(1, form.guests - 1))} style={{ width: 40, height: 40, borderRadius: 8, border: '2px solid var(--text-muted)', background: 'var(--surface)', cursor: 'pointer', fontSize: 20, fontWeight: 700, color: 'var(--text)' }}>−
{form.guests}
set('guests', Math.min(15, form.guests + 1))} style={{ width: 40, height: 40, borderRadius: 8, border: '2px solid var(--text-muted)', background: 'var(--surface)', cursor: 'pointer', fontSize: 20, fontWeight: 700, color: 'var(--text)' }}>+
setStep(1)}>Back
setStep(3)} icon={Icons.arrowRight}>Continue
)}
{step === 3 && (
)}
{step === 4 && (
Booking Confirmed!
We'll send you a confirmation via WhatsApp shortly. See you in Punta Cana!
Done
)}
);
}
/* ── WhatsApp Float ── */
function WhatsAppFloat() {
const [show, setShow] = useState(false);
useEffect(() => { setTimeout(() => setShow(true), 2000); }, []);
if (!show) return null;
return (
);
}
/* ── Footer ── */
function Footer() {
return (
);
}
/* ── Final CTA ── */
function FinalCTA({ onBook }) {
return (
Ready to Book with Confidence?
Licensed, insured, transparent — book direct and save vs. OTA prices.
Book Now
window.open('https://wa.me/18095550123', '_blank')}>WhatsApp Us
);
}
/* ── Main App ── */
function App() {
const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
const [scrolled, setScrolled] = useState(false);
const [bookingOpen, setBookingOpen] = useState(false);
const [preselected, setPreselected] = useState(null);
const [currency, setCurrency] = useState('USD');
useEffect(() => {
document.documentElement.setAttribute('data-theme', t.theme);
}, [t.theme]);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 60);
window.addEventListener('scroll', onScroll, { passive: true });
return () => window.removeEventListener('scroll', onScroll);
}, []);
const openBooking = useCallback((item) => {
setPreselected(item || null);
setBookingOpen(true);
}, []);
const scrollToTours = useCallback(() => {
document.getElementById('tours')?.scrollTo?.({ behavior: 'smooth' });
const el = document.getElementById('tours');
if (el) el.style.scrollMarginTop = '80px';
el?.scrollIntoView?.({ behavior: 'smooth', block: 'start' });
}, []);
return (
<>
openBooking()} scrolled={scrolled} currency={currency} onCurrencyChange={setCurrency} />
openBooking()} onExploreTours={scrollToTours} heroStyle={t.heroStyle} />
openBooking()} />
openBooking()} />
setBookingOpen(false)} preselected={preselected} />
setTweak('theme', v)} />
setTweak('heroStyle', v)} />
setTweak('cardStyle', v)} />
setCurrency(v)} />
>
);
}
/* ── Mount ── */
ReactDOM.createRoot(document.getElementById('root')).render( );