import React, { useState, useMemo } from "react";
import {
Compass, MapPin, CalendarDays, Users, Tent, Car, Plane, Send,
Download, MessageCircle, SlidersHorizontal, CreditCard, Check, X,
ChevronRight, Sparkles, Baby, Globe, Footprints, Waves
} from "lucide-react";
/* ============================================================
WildTrail Tanzania — Build My Safari + Instant Quote (prototype)
Sample rates verified against 2025/26 TANAPA & NCAA fee guidance.
All rates are editable in the "Rates & Margin" panel — in production
these are pulled from the supplier portal / admin database.
============================================================ */
const WHATSAPP = "255747108883";
const EMAIL = "info@reservationshub.org";
// destType: 'park' (game drives) | 'trek' | 'beach' | 'hub'
const DESTINATIONS = [
{ id: "tarangire", name: "Tarangire", region: "Northern", type: "park", order: 2, baseNights: 1, feeHigh: 50, feeLow: 45, childFee: 15, interior: false },
{ id: "manyara", name: "Lake Manyara", region: "Northern", type: "park", order: 3, baseNights: 1, feeHigh: 50, feeLow: 45, childFee: 15, interior: false },
{ id: "serengeti", name: "Serengeti", region: "Northern", type: "park", order: 4, baseNights: 2, feeHigh: 70, feeLow: 60, childFee: 20, interior: true },
{ id: "ngorongoro", name: "Ngorongoro Crater", region: "Northern", type: "park", order: 5, baseNights: 1, feeHigh: 70.8,feeLow: 70.8, childFee: 23.6, interior: true, craterFee: 295 },
{ id: "arushanp", name: "Arusha N.P.", region: "Northern", type: "park", order: 1, baseNights: 1, feeHigh: 50, feeLow: 45, childFee: 15, interior: false },
{ id: "kilimanjaro",name: "Kilimanjaro Trek", region: "Northern", type: "trek", order: 0, baseNights: 6, trekPPPD: 240 },
{ id: "nyerere", name: "Nyerere (Selous)", region: "Southern", type: "park", order: 6, baseNights: 2, feeHigh: 60, feeLow: 50, childFee: 20, interior: false },
{ id: "ruaha", name: "Ruaha", region: "Southern", type: "park", order: 7, baseNights: 2, feeHigh: 30, feeLow: 30, childFee: 10, interior: false },
{ id: "mikumi", name: "Mikumi", region: "Southern", type: "park", order: 8, baseNights: 1, feeHigh: 30, feeLow: 30, childFee: 10, interior: false },
{ id: "zanzibar", name: "Zanzibar", region: "Coast", type: "beach", order: 9, baseNights: 3 },
];
const STYLES = ["Budget", "Mid-Range", "Luxury", "Ultra-Luxury"];
const ACCOM = ["Camping", "Lodge", "Tented Camp", "Luxury Lodge"];
const TRANSPORT = ["Shared Safari", "Private Safari", "Fly-In Safari"];
const DEFAULT_RATES = {
// per person per night (game lodges) by style
lodge: { Budget: 90, "Mid-Range": 180, Luxury: 450, "Ultra-Luxury": 950 },
// per person per night (beach resorts) by style
beach: { Budget: 70, "Mid-Range": 150, Luxury: 380, "Ultra-Luxury": 800 },
seasonMult: { Low: 0.85, High: 1.0, Peak: 1.25 },
vehiclePerDay: 230, // private Land Cruiser incl. driver-guide & fuel
sharedPerPersonDay: 75, // seat-in-vehicle shared safari
flightPerSector: 110, // domestic / bush flight per person per sector
concessionPPPN: 59, // park-interior concession (luxury tiers)
transfersFlat: 70, // airport transfers per group
vatParkFees: 0.18, // 18% VAT on park fees
margin: 20, // WildTrail margin %
depositPct: 30,
};
const seasonFor = (dateStr) => {
const m = dateStr ? new Date(dateStr + "T00:00").getMonth() + 1 : new Date().getMonth() + 1;
if ([7, 8, 9, 10, 12].includes(m)) return "Peak";
if ([6, 11, 1, 2].includes(m)) return "High";
return "Low";
};
const usd = (n) => "$" + Math.round(n).toLocaleString("en-US");
export default function App() {
const [view, setView] = useState("build"); // build | quote
const [showRates, setShowRates] = useState(false);
const [showDeposit, setShowDeposit] = useState(false);
const [rates, setRates] = useState(DEFAULT_RATES);
const [form, setForm] = useState({
arrival: "",
departure: "",
adults: 2,
children: 0,
nationality: "Non-resident",
style: "Mid-Range",
accom: "Lodge",
transport: "Private Safari",
dests: ["tarangire", "serengeti", "ngorongoro"],
});
const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));
const toggleDest = (id) =>
setForm((f) => ({
...f,
dests: f.dests.includes(id) ? f.dests.filter((d) => d !== id) : [...f.dests, id],
}));
const trip = useMemo(() => buildTrip(form, rates), [form, rates]);
return (
{/* Header */}
WildTrail Tanzania
Where every trail tells a story
setShowRates(true)} className="hov">
Rates & Margin
{view === "build" ? (
setView("quote")}
/>
) : (
setView("build")}
onDeposit={() => setShowDeposit(true)}
/>
)}
Prototype · {EMAIL} · WhatsApp +{WHATSAPP}
Sample rates — configure in the admin panel
{showRates && setShowRates(false)} />}
{showDeposit && setShowDeposit(false)} />}
);
}
/* ----------------------------- BUILD VIEW ----------------------------- */
function BuildView({ form, set, toggleDest, trip, onGenerate }) {
const grouped = ["Northern", "Southern", "Coast"];
return (
} title="Travel information">
set("arrival", e.target.value)} style={S.input} />
set("departure", e.target.value)} style={S.input} />
set("adults", v)} icon={ } />
set("children", v)} icon={ } />
{["Non-resident", "Resident / Expat", "East African"].map((n) => (
set("nationality", n)}
style={{ ...S.segBtn, ...(form.nationality === n ? S.segOn : {}) }} className="hov">{n}
))}
} title="Safari style">
set("style", v)} />
set("accom", v)} small />
set("transport", v)} small
icons={{ "Shared Safari": , "Private Safari": , "Fly-In Safari": }} />
} title="Destinations">
{grouped.map((g) => (
{g} circuit
{DESTINATIONS.filter((d) => d.region === g).map((d) => {
const on = form.dests.includes(d.id);
return (
toggleDest(d.id)} className="hov"
style={{ ...S.chip, ...(on ? S.chipOn : {}) }}>
{d.type === "beach" ? : d.type === "trek" ? : }
{d.name}
{on && }
);
})}
))}
{/* live preview */}
Estimated total
{usd(trip.total)}
{usd(trip.perPerson)} per person · {trip.season} season
{trip.totalDays} days
{form.adults + form.children} {form.adults + form.children === 1 ? "traveller" : "travellers"}
{form.style}
Generate my itinerary & quote
Instant — no email wait. {trip.warning && {trip.warning} }
);
}
/* ----------------------------- QUOTE VIEW ----------------------------- */
function QuoteView({ form, trip, rates, onBack, onDeposit }) {
const pax = form.adults + form.children;
const summary = `${trip.totalDays}-day ${form.style} Tanzania safari for ${pax} (${trip.dests.map((d) => d.name).join(", ")}). Estimated total ${usd(trip.total)} / ${usd(trip.perPerson)} pp.`;
const wa = `https://wa.me/${WHATSAPP}?text=${encodeURIComponent("Hi WildTrail Tanzania! I built this safari on your site:\n\n" + summary + "\n\nCan we discuss?")}`;
const mail = `mailto:${EMAIL}?subject=${encodeURIComponent("Safari booking request — " + trip.totalDays + " days")}&body=${encodeURIComponent("Hello WildTrail Tanzania,\n\nI'd like to request this safari:\n\n" + summary + "\n\nTravel dates: " + (form.arrival || "TBC") + " to " + (form.departure || "TBC") + "\nName:\nContact:\n")}`;
return (
← Edit safari
WildTrail Tanzania
Tailor-made safari proposal
{trip.totalDays}-day {form.style} safari
{form.arrival || "Dates TBC"} → {form.departure || ""}
{form.adults} adult{form.adults > 1 ? "s" : ""}{form.children ? `, ${form.children} child${form.children > 1 ? "ren" : ""}` : ""} · {trip.season} season
Total package price
{usd(trip.total)}
Per person {usd(trip.perPerson)}
Deposit ({rates.depositPct}%) {usd(trip.deposit)}
{/* itinerary */}
Day-by-day itinerary
{trip.itinerary.map((d, i) => (
{d.day}
{d.title}
{d.note &&
{d.note}
}
))}
{/* breakdown + incl/excl */}
Price breakdown
{trip.breakdown.map((b, i) => (
{b.label} {usd(b.amount)}
))}
Subtotal {usd(trip.subtotal)}
WildTrail service & margin ({rates.margin}%) {usd(trip.total - trip.subtotal)}
Total {usd(trip.total)}
What's included
{["Accommodation as specified", "All park & conservation fees", "Professional driver-guide", "4×4 safari vehicle / transfers", "Drinking water on safari", "Game drives per itinerary"].map((x) => (
{x}
))}
Excluded
{["International flights", "Tanzania visa", "Travel insurance", "Tips & gratuities", "Personal expenses"].map((x) => (
{x}
))}
A {rates.depositPct}% deposit confirms your booking; balance due 45 days before arrival. Quote valid 14 days, subject to availability.
Prices in USD per the above party size and dates. Bank & payment details provided on the confirmation invoice.
{/* action buttons */}
);
}
/* ----------------------------- PANELS ----------------------------- */
function RatesPanel({ rates, setRates, onClose }) {
const upd = (path, val) => {
const v = parseFloat(val);
setRates((r) => {
const n = { ...r, lodge: { ...r.lodge }, beach: { ...r.beach } };
if (path[0] === "lodge" || path[0] === "beach") n[path[0]][path[1]] = isNaN(v) ? 0 : v;
else n[path[0]] = isNaN(v) ? 0 : v;
return n;
});
};
return (
e.stopPropagation()}>
Rates & Margin
Admin · drives every quote
In production these sync from the supplier portal & TANAPA. Change a number and reopen a quote to see it recalculate.
{STYLES.map((s) => upd(["lodge", s], v)} />)}
{STYLES.map((s) => upd(["beach", s], v)} />)}
upd(["vehiclePerDay"], v)} />
upd(["sharedPerPersonDay"], v)} />
upd(["flightPerSector"], v)} />
setRates((r) => ({ ...r, vatParkFees: (parseFloat(v) || 0) / 100 }))} />
WildTrail margin {rates.margin}%
setRates((r) => ({ ...r, margin: parseInt(e.target.value) }))} style={{ width: "100%", accentColor: "var(--ochre)" }} />
5% 40%
);
}
function DepositModal({ trip, rates, onClose }) {
return (
e.stopPropagation()}>
Secure deposit
{rates.depositPct}% confirms your dates. In production this opens your gateway —
Flutterwave, Pesapal, DPO, Selcom or card.
{usd(trip.deposit)}
{["Flutterwave", "Pesapal", "DPO Pay", "Visa", "Mastercard", "PayPal"].map((g) => (
{g}
))}
Close demo
);
}
/* ----------------------------- SMALL PARTS ----------------------------- */
const Section = ({ icon, title, children }) => (
);
const Field = ({ label, children }) => (
);
const Pills = ({ options, value, onPick, small, icons }) => (
{options.map((o) => (
onPick(o)} className="hov"
style={{ ...(small ? S.pillSm : S.pill), ...(value === o ? S.pillOn : {}) }}>
{icons && icons[o]} {o}
))}
);
const Stepper = ({ value, onChange, min = 0, icon }) => (
{icon}
onChange(Math.max(min, value - 1))}>–
{value}
onChange(value + 1)}>+
);
const RateBlock = ({ title, children }) => (
);
const RateInput = ({ label, value, onChange }) => (
{label}
onChange(e.target.value)} style={S.rateInput} />
);
/* ----------------------------- ENGINE ----------------------------- */
function buildTrip(form, rates) {
const pax = form.adults + form.children;
const season = seasonFor(form.arrival);
const seasonMult = rates.seasonMult[season];
// total days
let totalDays;
let warning = "";
if (form.arrival && form.departure) {
const a = new Date(form.arrival + "T00:00"), d = new Date(form.departure + "T00:00");
totalDays = Math.round((d - a) / 86400000) + 1;
}
const selected = DESTINATIONS.filter((x) => form.dests.includes(x.id)).sort((p, q) => p.order - q.order);
const sumBase = selected.reduce((s, x) => s + x.baseNights, 0);
if (!totalDays || totalDays < 2) { totalDays = sumBase + 2; warning = "Add dates for an exact quote."; }
const nights = totalDays - 1; // sleeps
let safariNights = nights - 1; // minus arrival night in Arusha
if (safariNights < 0) safariNights = 0;
// allocate nights across destinations (1 each, extras to higher baseNights)
const alloc = {};
selected.forEach((d) => (alloc[d.id] = 0));
let left = safariNights;
selected.forEach((d) => { if (left > 0) { alloc[d.id] = 1; left--; } });
// distribute remaining by baseNights weight (Serengeti etc. get more)
const order = [...selected].sort((p, q) => q.baseNights - p.baseNights);
let i = 0;
while (left > 0 && order.length) {
const d = order[i % order.length];
if (alloc[d.id] < d.baseNights + 2) { alloc[d.id]++; left--; }
i++;
if (i > 200) break;
}
// ---- build itinerary ----
const itinerary = [{ day: 1, title: "Arrival in Arusha", note: "Airport pickup & overnight; safari briefing." }];
let dayN = 1;
selected.forEach((d) => {
for (let k = 0; k < alloc[d.id]; k++) {
dayN++;
let title = d.name, note = "";
if (d.type === "park") { title = `${d.name} — game drives`; note = k === 0 ? "Full day exploring with your guide." : "Continue exploring the ecosystem."; }
else if (d.type === "trek") { title = `${d.name} — ascent`; note = "Guided trekking with full mountain crew."; }
else if (d.type === "beach") { title = `${d.name} — beach & leisure`; note = "Relax on the coast; optional excursions."; }
itinerary.push({ day: dayN, title, note });
}
});
itinerary.push({ day: totalDays, title: "Departure", note: "Transfer to the airport for your flight home." });
// ---- costs ----
const breakdown = [];
// accommodation: arrival night + park/beach nights (trek nights are in the trek package)
const trekNights = selected.filter((d) => d.type === "trek").reduce((s, d) => s + alloc[d.id], 0);
const beachNights = selected.filter((d) => d.type === "beach").reduce((s, d) => s + alloc[d.id], 0);
const lodgeNights = nights - trekNights - beachNights; // includes arrival night
const lodgeRate = rates.lodge[form.style];
const beachRate = rates.beach[form.style];
const accomLodge = lodgeNights * lodgeRate * seasonMult * pax;
const accomBeach = beachNights * beachRate * seasonMult * pax;
if (accomLodge > 0) breakdown.push({ label: `Lodge / camp · ${lodgeNights} nt × ${pax} pax`, amount: accomLodge });
if (accomBeach > 0) breakdown.push({ label: `Beach resort · ${beachNights} nt × ${pax} pax`, amount: accomBeach });
// park fees (per night-in-park as a park day) + VAT, EAC/resident discounted
const natFactor = form.nationality === "East African" ? 0.15 : form.nationality === "Resident / Expat" ? 0.5 : 1;
let parkFeesRaw = 0, craterFees = 0, concession = 0;
selected.filter((d) => d.type === "park").forEach((d) => {
const fee = season === "Low" ? d.feeLow : d.feeHigh;
parkFeesRaw += alloc[d.id] * (fee * form.adults + (d.childFee || 0) * form.children) * natFactor;
if (d.craterFee) craterFees += d.craterFee * Math.max(1, Math.ceil(pax / 6));
if (d.interior && (form.style === "Luxury" || form.style === "Ultra-Luxury"))
concession += alloc[d.id] * rates.concessionPPPN * pax;
});
const parkFees = parkFeesRaw * (1 + rates.vatParkFees);
if (parkFees > 0) breakdown.push({ label: "Park & conservation fees (incl. VAT)", amount: parkFees });
if (craterFees > 0) breakdown.push({ label: "Ngorongoro crater service fee", amount: craterFees });
if (concession > 0) breakdown.push({ label: "Park-interior concession fees", amount: concession });
// transport
const parkNights = selected.filter((d) => d.type === "park").reduce((s, d) => s + alloc[d.id], 0);
const landDays = totalDays - beachNights - trekNights; // vehicle present for the land safari
let transport = 0;
const clusters = selected.filter((d) => d.type === "park").length;
if (form.transport === "Private Safari") {
transport = Math.max(landDays, 1) * rates.vehiclePerDay;
breakdown.push({ label: `Private 4×4 · ${Math.max(landDays, 1)} days`, amount: transport });
} else if (form.transport === "Shared Safari") {
transport = Math.max(landDays, 1) * rates.sharedPerPersonDay * pax;
breakdown.push({ label: `Shared safari · ${Math.max(landDays, 1)} days × ${pax}`, amount: transport });
} else {
const sectors = Math.max(clusters, 1) + (beachNights > 0 ? 1 : 0);
const flights = sectors * rates.flightPerSector * pax;
const ground = parkNights * rates.vehiclePerDay * 0.7;
transport = flights + ground;
breakdown.push({ label: `Bush flights · ${sectors} sectors × ${pax}`, amount: flights });
if (ground > 0) breakdown.push({ label: "In-park game-drive vehicle", amount: ground });
}
// beach transfer flight
if (beachNights > 0 && form.transport !== "Fly-In Safari") {
const znz = rates.flightPerSector * pax;
transport += znz;
breakdown.push({ label: `Zanzibar flight · ${pax} pax`, amount: znz });
}
// trek package
const trekCost = selected.filter((d) => d.type === "trek").reduce((s, d) => s + alloc[d.id] * (d.trekPPPD || 0) * pax, 0);
if (trekCost > 0) breakdown.push({ label: "Kilimanjaro trek package", amount: trekCost });
// transfers
breakdown.push({ label: "Airport transfers", amount: rates.transfersFlat });
const subtotal = breakdown.reduce((s, b) => s + b.amount, 0);
const total = subtotal * (1 + rates.margin / 100);
const perPerson = total / pax;
const deposit = total * (rates.depositPct / 100);
return { totalDays, nights, season, itinerary, breakdown, subtotal, total, perPerson, deposit, dests: selected, warning };
}
/* ----------------------------- STYLES ----------------------------- */
const CSS = `
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=Hanken+Grotesk:wght@400;500;600;700&display=swap');
:root{
--bg:#f5efe3; --paper:#fdfaf3; --ink:#2c2419; --muted:#6b5d4c;
--line:#e6dcc8; --ochre:#bb5e2f; --ochre-d:#9d4d24; --green:#4a5d36; --gold:#b8902e;
--display:'Fraunces',serif; --body:'Hanken Grotesk',sans-serif;
}
*{box-sizing:border-box;}
.hov{transition:all .15s ease;cursor:pointer;}
.hov:hover{filter:brightness(0.97);transform:translateY(-1px);}
.cta{transition:all .18s ease;cursor:pointer;}
.cta:hover{transform:translateY(-2px);box-shadow:0 10px 24px rgba(155,77,36,.28);}
input[type=date]::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.6;}
@media print{
.no-print{display:none !important;}
body{background:#fff !important;}
#proposal{box-shadow:none !important;border:none !important;}
}
@media (max-width:760px){
.bgrid{grid-template-columns:1fr !important;}
.tcol{grid-template-columns:1fr !important;}
}
`;
const S = {
shell: { fontFamily: "var(--body)", background: "var(--bg)", color: "var(--ink)", minHeight: "100vh", padding: "0 0 40px" },
header: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: "18px 22px", borderBottom: "1px solid var(--line)", background: "var(--paper)", position: "sticky", top: 0, zIndex: 10 },
brandWrap: { display: "flex", alignItems: "center", gap: 12 },
logoMark: { width: 40, height: 40, borderRadius: 12, background: "linear-gradient(135deg,var(--ochre),var(--ochre-d))", color: "#fff", display: "flex", alignItems: "center", justifyContent: "center", boxShadow: "0 4px 12px rgba(155,77,36,.25)" },
brand: { fontFamily: "var(--display)", fontSize: 21, fontWeight: 600, letterSpacing: "-0.01em", lineHeight: 1 },
tag: { fontSize: 11.5, color: "var(--muted)", letterSpacing: ".02em", marginTop: 3 },
ghostBtn: { display: "flex", alignItems: "center", gap: 7, background: "transparent", border: "1px solid var(--line)", color: "var(--ink)", padding: "9px 14px", borderRadius: 10, fontSize: 13, fontWeight: 600, fontFamily: "var(--body)" },
main: { maxWidth: 1080, margin: "0 auto", padding: "26px 22px" },
buildGrid: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 },
card: { background: "var(--paper)", border: "1px solid var(--line)", borderRadius: 16, padding: 18 },
cardHead: { fontFamily: "var(--display)", fontSize: 16, fontWeight: 600, display: "flex", alignItems: "center", gap: 9, marginBottom: 14 },
cardIcon: { color: "var(--ochre)", display: "flex" },
fieldLabel: { fontSize: 12, fontWeight: 600, color: "var(--muted)", marginBottom: 6, textTransform: "uppercase", letterSpacing: ".04em" },
row2: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 },
input: { width: "100%", padding: "10px 12px", border: "1px solid var(--line)", borderRadius: 10, fontSize: 14, fontFamily: "var(--body)", background: "#fff", color: "var(--ink)" },
segment: { display: "flex", gap: 6, background: "#f0e8d8", padding: 4, borderRadius: 11 },
segBtn: { flex: 1, padding: "8px 6px", border: "none", background: "transparent", borderRadius: 8, fontSize: 12, fontWeight: 600, color: "var(--muted)", fontFamily: "var(--body)" },
segOn: { background: "#fff", color: "var(--ink)", boxShadow: "0 1px 4px rgba(0,0,0,.08)" },
chipWrap: { display: "flex", flexWrap: "wrap", gap: 8 },
chip: { display: "flex", alignItems: "center", gap: 6, padding: "9px 13px", border: "1px solid var(--line)", borderRadius: 999, background: "#fff", fontSize: 13, fontWeight: 600, color: "var(--ink)", fontFamily: "var(--body)" },
chipOn: { background: "var(--green)", borderColor: "var(--green)", color: "#fff" },
groupLabel: { fontSize: 11, fontWeight: 700, color: "var(--muted)", textTransform: "uppercase", letterSpacing: ".06em", marginBottom: 7 },
pill: { padding: "11px 16px", border: "1.5px solid var(--line)", borderRadius: 12, background: "#fff", fontSize: 14, fontWeight: 600, color: "var(--ink)", fontFamily: "var(--body)", display: "flex", alignItems: "center", gap: 6 },
pillSm: { padding: "8px 13px", border: "1.5px solid var(--line)", borderRadius: 10, background: "#fff", fontSize: 13, fontWeight: 600, color: "var(--ink)", fontFamily: "var(--body)", display: "flex", alignItems: "center", gap: 6 },
pillOn: { borderColor: "var(--ochre)", background: "#fbf1e9", color: "var(--ochre-d)" },
stepper: { display: "flex", alignItems: "center", gap: 10, border: "1px solid var(--line)", borderRadius: 10, padding: "6px 12px", background: "#fff" },
stepBtn: { width: 28, height: 28, borderRadius: 8, border: "1px solid var(--line)", background: "#faf6ee", fontSize: 18, lineHeight: 1, color: "var(--ink)", fontFamily: "var(--body)" },
stepVal: { flex: 1, textAlign: "center", fontSize: 16, fontWeight: 700 },
previewCard: { background: "linear-gradient(160deg,#fdfaf3,#f6eee0)", border: "1px solid var(--line)", borderRadius: 16, padding: 20, boxShadow: "0 8px 24px rgba(64,48,28,.07)" },
previewTop: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 16, gap: 12 },
previewKicker: { fontSize: 11, fontWeight: 700, color: "var(--muted)", textTransform: "uppercase", letterSpacing: ".06em" },
previewPrice: { fontFamily: "var(--display)", fontSize: 40, fontWeight: 600, lineHeight: 1.05, color: "var(--ink)", margin: "2px 0" },
previewPP: { fontSize: 13, color: "var(--muted)" },
previewMeta: { display: "flex", flexDirection: "column", gap: 4, alignItems: "flex-end", fontSize: 12.5, fontWeight: 600, color: "var(--muted)" },
cta: { width: "100%", padding: "15px", background: "linear-gradient(135deg,var(--ochre),var(--ochre-d))", color: "#fff", border: "none", borderRadius: 13, fontSize: 16, fontWeight: 700, fontFamily: "var(--body)", display: "flex", alignItems: "center", justifyContent: "center", gap: 8 },
previewNote: { fontSize: 12, color: "var(--muted)", textAlign: "center", marginTop: 10 },
quoteHead: { marginBottom: 14 },
backBtn: { background: "transparent", border: "1px solid var(--line)", padding: "9px 14px", borderRadius: 10, fontSize: 13, fontWeight: 600, fontFamily: "var(--body)", color: "var(--ink)" },
proposal: { background: "var(--paper)", border: "1px solid var(--line)", borderRadius: 18, padding: 28, boxShadow: "0 10px 30px rgba(64,48,28,.08)" },
propHeader: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", paddingBottom: 18, borderBottom: "2px solid var(--line)", gap: 16, flexWrap: "wrap" },
propMeta: { textAlign: "right", fontSize: 13, color: "var(--muted)", lineHeight: 1.7 },
priceBanner: { display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 16, background: "linear-gradient(135deg,var(--green),#3a4a2b)", color: "#fff", borderRadius: 14, padding: "20px 24px", margin: "20px 0" },
bannerKicker: { fontSize: 11.5, textTransform: "uppercase", letterSpacing: ".08em", opacity: 0.8 },
bannerPrice: { fontFamily: "var(--display)", fontSize: 42, fontWeight: 600, lineHeight: 1.05 },
bannerSplit: { display: "flex", gap: 28 },
bannerLabel: { display: "block", fontSize: 11.5, opacity: 0.8, textTransform: "uppercase", letterSpacing: ".05em" },
bannerVal: { display: "block", fontSize: 22, fontWeight: 700, fontFamily: "var(--display)" },
h3: { fontFamily: "var(--display)", fontSize: 18, fontWeight: 600, margin: "20px 0 12px", color: "var(--ink)" },
timeline: { display: "flex", flexDirection: "column" },
dayRow: { display: "grid", gridTemplateColumns: "44px 2px 1fr", gap: 14, paddingBottom: 16, alignItems: "start" },
dayNum: { width: 44, height: 44, borderRadius: 11, background: "#fbf1e9", color: "var(--ochre-d)", fontFamily: "var(--display)", fontWeight: 700, fontSize: 18, display: "flex", alignItems: "center", justifyContent: "center", border: "1px solid #f0ddcc" },
dayLine: { background: "var(--line)", width: 2, height: "100%", borderRadius: 2, marginLeft: 0 },
dayTitle: { fontWeight: 700, fontSize: 15 },
dayNote: { fontSize: 13, color: "var(--muted)", marginTop: 2 },
twoCol: { display: "grid", gridTemplateColumns: "1.1fr 1fr", gap: 28 },
breakdown: { border: "1px solid var(--line)", borderRadius: 12, overflow: "hidden" },
bRow: { display: "flex", justifyContent: "space-between", padding: "11px 14px", fontSize: 13.5, borderBottom: "1px solid var(--line)" },
bSub: { background: "#faf6ee", fontWeight: 600 },
bTotal: { background: "var(--ink)", color: "#fff", fontWeight: 700, fontSize: 16, fontFamily: "var(--display)", borderBottom: "none" },
list: { listStyle: "none", padding: 0, margin: 0, display: "flex", flexDirection: "column", gap: 8 },
li: { display: "flex", alignItems: "center", gap: 9, fontSize: 13.5 },
terms: { marginTop: 22, paddingTop: 16, borderTop: "1px solid var(--line)", fontSize: 12, color: "var(--muted)", lineHeight: 1.6 },
actions: { display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 12, marginTop: 18 },
actBtn: { display: "flex", alignItems: "center", justifyContent: "center", gap: 8, padding: "14px", borderRadius: 12, fontSize: 14, fontWeight: 700, fontFamily: "var(--body)", textDecoration: "none", border: "none" },
actPrimary: { background: "linear-gradient(135deg,var(--ochre),var(--ochre-d))", color: "#fff" },
actDark: { background: "var(--ink)", color: "#fff" },
actGold: { background: "var(--gold)", color: "#fff" },
actWa: { background: "#1faf54", color: "#fff" },
footer: { maxWidth: 1080, margin: "26px auto 0", padding: "16px 22px 0", borderTop: "1px solid var(--line)", display: "flex", justifyContent: "space-between", fontSize: 12, color: "var(--muted)", flexWrap: "wrap", gap: 8 },
overlay: { position: "fixed", inset: 0, background: "rgba(40,30,18,.45)", backdropFilter: "blur(3px)", zIndex: 50, display: "flex", justifyContent: "flex-end" },
drawer: { width: "min(420px,94vw)", height: "100%", background: "var(--paper)", boxShadow: "-10px 0 40px rgba(0,0,0,.2)", display: "flex", flexDirection: "column" },
drawerHead: { display: "flex", justifyContent: "space-between", alignItems: "center", padding: 20, borderBottom: "1px solid var(--line)" },
drawerBody: { padding: 20, overflowY: "auto" },
iconBtn: { width: 34, height: 34, borderRadius: 9, border: "1px solid var(--line)", background: "#fff", display: "flex", alignItems: "center", justifyContent: "center", color: "var(--ink)" },
rateNote: { fontSize: 12.5, color: "var(--muted)", background: "#faf3e6", border: "1px solid var(--line)", borderRadius: 10, padding: "10px 12px", marginBottom: 18, lineHeight: 1.5 },
rateInputWrap: { display: "flex", flexDirection: "column", gap: 3 },
rateInput: { padding: "8px 10px", border: "1px solid var(--line)", borderRadius: 8, fontSize: 14, fontFamily: "var(--body)", background: "#fff", color: "var(--ink)" },
marginBox: { background: "#fbf1e9", border: "1px solid #f0ddcc", borderRadius: 12, padding: 16, marginTop: 8 },
modal: { width: "min(440px,92vw)", margin: "auto", background: "var(--paper)", borderRadius: 18, padding: 26, textAlign: "center", boxShadow: "0 20px 60px rgba(0,0,0,.3)" },
depositAmt: { fontFamily: "var(--display)", fontSize: 44, fontWeight: 600, color: "var(--green)", margin: "16px 0 4px" },
gateways: { display: "flex", flexWrap: "wrap", gap: 7, justifyContent: "center", marginTop: 8 },
gwChip: { fontSize: 11.5, fontWeight: 600, padding: "5px 10px", borderRadius: 999, background: "#f0e8d8", color: "var(--muted)" },
};