/* Shared assets, icons, data and primitives for all BoatsFun homepage directions. */ // ============ ICONS (inline SVG, stroke based) ============ const I = { search: (p={}) => , shield: (p={}) => , spark: (p={}) => , tag: (p={}) => , fleet: (p={}) => , headset: (p={}) => , refund: (p={}) => , users: (p={}) => , ruler: (p={}) => , bolt: (p={}) => , arrow: (p={}) => , phone: (p={}) => , globe: (p={}) => , pin: (p={}) => , cal: (p={}) => , clock: (p={}) => , user: (p={}) => , star: (p={}) => , heart: (p={}) => , check: (p={}) => , mail: (p={}) => , sun: (p={}) => , wave: (p={}) => , anchor: (p={}) => , compass: (p={}) => , camera: (p={}) => , fb: (p={}) => , ig: (p={}) => , yt: (p={}) => , tt: (p={}) => , arrowR: (p={}) => , arrowL: (p={}) => , plus: (p={}) => , filter: (p={}) => , diamond: (p={}) => , }; // ============ BOAT DATA ============ const BOATS = [ { name: "Ponton Sun Tracker 20", type: "Ponton", loc: "Cannes, FR", pers: 10, ft: "20 ft", hp: "250 HP", price: 350, rating: 4.9, reviews: 28, badge: "NOUVEAU", img: "https://images.unsplash.com/photo-1605281317010-fe5ffe798166?w=1200&q=80&auto=format&fit=crop" }, { name: "Sea Ray Sundancer 320", type: "Cruiser", loc: "Saint-Tropez", pers: 8, ft: "32 ft", hp: "600 HP", price: 850, rating: 4.8, reviews: 41, badge: "TOP", img: "https://images.unsplash.com/photo-1567899378494-47b22a2ae96a?w=1200&q=80&auto=format&fit=crop" }, { name: "Boston Whaler 270", type: "Speedboat", loc: "Nice", pers: 9, ft: "27 ft", hp: "400 HP", price: 650, rating: 4.9, reviews: 19, badge: "NOUVEAU", img: "https://images.unsplash.com/photo-1542384557-0824d90731ee?w=1200&q=80&auto=format&fit=crop&sat=-10" }, { name: "Bayliner VR6", type: "Bowrider", loc: "Marseille", pers: 10, ft: "23 ft", hp: "250 HP", price: 450, rating: 4.7, reviews: 53, badge: null, img: "https://images.unsplash.com/photo-1473116763249-2faaef81ccda?w=1200&q=80&auto=format&fit=crop" }, { name: "Azimut 50 Fly", type: "Yacht", loc: "Monaco", pers: 12, ft: "50 ft", hp: "1000 HP", price: 1600, rating: 5.0, reviews: 12, badge: "PREMIUM", img: "https://images.unsplash.com/photo-1540541338287-41700207dee6?w=1200&q=80&auto=format&fit=crop" }, { name: "Beneteau Antares 8", type: "Cruiser", loc: "Antibes", pers: 6, ft: "26 ft", hp: "200 HP", price: 380, rating: 4.8, reviews: 34, badge: null, img: "https://images.unsplash.com/photo-1542384557-0824d90731ee?w=1200&q=80&auto=format&fit=crop" }, ]; const HERO_IMG = "https://images.unsplash.com/photo-1567899378494-47b22a2ae96a?w=1800&q=80&auto=format&fit=crop"; const LIFESTYLE_IMG = "https://images.unsplash.com/photo-1530541930197-ff16ac917b0e?w=1600&q=80&auto=format&fit=crop"; const EXP_IMGS = [ "https://images.unsplash.com/photo-1502920514313-52581002a659?w=900&q=80&auto=format&fit=crop", // people jumping "https://images.unsplash.com/photo-1518176258769-f227c798150e?w=900&q=80&auto=format&fit=crop", // aerial turquoise "https://images.unsplash.com/photo-1502082553048-f009c37129b9?w=900&q=80&auto=format&fit=crop", // sunset cockpit ]; const SUNSET_IMG = "https://images.unsplash.com/photo-1469854523086-cc02fe5d8800?w=1800&q=80&auto=format&fit=crop"; const COAST_IMG = "https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=1600&q=80&auto=format&fit=crop"; const DARK_IMG = "https://images.unsplash.com/photo-1502082553048-f009c37129b9?w=1800&q=80&auto=format&fit=crop"; const TESTIMONIALS = [ { name: "Thomas L.", loc: "Montréal, Canada", text: "Super service, bateau propre et en parfait état. On a passé une journée magique en famille.", rating: 5, img: "https://i.pravatar.cc/120?img=12" }, { name: "Sophie M.", loc: "Lyon, France", text: "J'ai fait une offre et j'ai obtenu un super prix. Le processus est simple et rapide.", rating: 5, img: "https://i.pravatar.cc/120?img=47" }, { name: "Marc D.", loc: "Nice, France", text: "Équipe professionnelle et très disponible. Je recommande à 100%, on reviendra l'été prochain.", rating: 5, img: "https://i.pravatar.cc/120?img=33" }, { name: "Elena R.", loc: "Barcelone, ES", text: "Le système d'offre m'a fait économiser 30%. Le propriétaire a été adorable. Expérience top.", rating: 5, img: "https://i.pravatar.cc/120?img=23" }, ]; const WHY = [ { icon: "shield", title: "Sécurisé", desc: "Paiement protégé et dépôt remboursable en cas de refus de l'offre." }, { icon: "bolt", title: "Simple", desc: "Réservez en quelques clics, ou faites une offre raisonnable en 60 sec." }, { icon: "tag", title: "Meilleur prix", desc: "Faites une offre et obtenez le tarif que vous voulez réellement payer." }, { icon: "fleet", title: "Grand choix", desc: "Ponton, Yacht, Cruiser, Speedboat, voilier — toutes les catégories." }, { icon: "headset", title: "Support 7/7", desc: "Une équipe humaine disponible pour répondre du lundi au dimanche." }, { icon: "refund", title: "Remboursement", desc: "Dépôt intégralement remboursé si aucune offre n'est acceptée." }, ]; const OFFER_STEPS = [ { n: 1, title: "Choisissez", desc: "un type de bateau, une date et une heure de sortie." }, { n: 2, title: "Proposez", desc: "votre budget — restez raisonnable pour maximiser vos chances." }, { n: 3, title: "Payez le dépôt", desc: "sécurisé pour valider votre offre auprès du propriétaire." }, { n: 4, title: "Confirmé", desc: "un propriétaire accepte votre offre et confirme la sortie." }, ]; const STATS = [ { v: "+500", l: "bateaux disponibles" }, { v: "+10K", l: "clients satisfaits" }, { v: "+20", l: "destinations" }, { v: "100%", l: "sécurisé" }, ]; // ============ TYPE: Star Row ============ function Stars({ n = 5, color = "#FF8200", size = 14 }) { return {Array.from({ length: n }, (_, i) => )} ; } // ============ TYPE: Boat image w/ photo fallback gradient ============ function Photo({ src, alt = "", ratio = "4/3", radius = 14, style = {}, children, gradient = "linear-gradient(135deg,#002244,#00B5E2)", overlay }) { return (
{src && {alt}} {overlay &&
} {children}
); } // ============ TYPE: Headers used across versions ============ const BRAND = { navy: "#002244", navyDark: "#001530", cyan: "#00B5E2", cyanDark: "#0091B5", cyanSoft: "#CCF1F9", orange: "#FF8200", orangeDark: "#E56F00", orangeSoft: "#FFE6CC", bg: "#F0F8FF", bgSoft: "#FFFFFF", bgStrong: "#E1ECF7", ink: "#111111", body: "#3A4A5A", muted: "#6B7A8C", hairline: "#D9E2EE", success: "#00A86B" }; // ============ NAVIGATION LOCALE vs PRODUCTION ============ // En production (boatsfun.com) le .htaccess gère les URLs propres : /bateaux // En local (localhost / 127.0.0.1 / file://) il n'y a pas de .htaccess, donc on // sert les fichiers .html directement : /bateaux.html // Les hrefs restent écrits en URL propre partout ; ce helper les adapte à l'exécution. const BF_IS_LOCAL = (typeof location !== "undefined") && (/^(localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])$/.test(location.hostname) || location.protocol === "file:"); function bfToLocalUrl(href) { if (!BF_IS_LOCAL || typeof href !== "string") return href; // Laisser passer : externes, ancres pures, mailto/tel, et chemins déjà relatifs (non absolus) if (/^(https?:|mailto:|tel:|#)/i.test(href)) return href; if (!href.startsWith("/")) return href; // Découper path / (query|hash) const m = href.match(/^([^?#]*)([?#].*)?$/); const path = m[1] || ""; const rest = m[2] || ""; if (path === "/" || path === "") return href; // racine → index.html servi par défaut if (/\.[a-z0-9]+$/i.test(path)) return href; // déjà une extension (fichier) return path + ".html" + rest; } // Navigation programmatique (remplace window.location.href = "/xxx") function bfNav(href) { window.location.href = bfToLocalUrl(href); } // Délégation globale : intercepte les clics sur les liens internes en local et // réécrit /bateaux → /bateaux.html sans modifier le JSX de chaque page. if (BF_IS_LOCAL && typeof document !== "undefined") { document.addEventListener("click", function (e) { if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; const a = e.target && e.target.closest ? e.target.closest("a[href]") : null; if (!a) return; const target = a.getAttribute("target"); if (target && target !== "_self") return; // _blank, etc. → comportement natif const raw = a.getAttribute("href"); const fixed = bfToLocalUrl(raw); if (fixed !== raw) { e.preventDefault(); window.location.href = fixed; } }, true); } window.BF = { I, BOATS, HERO_IMG, LIFESTYLE_IMG, EXP_IMGS, SUNSET_IMG, COAST_IMG, DARK_IMG, TESTIMONIALS, WHY, OFFER_STEPS, STATS, Stars, Photo, BRAND, isLocal: BF_IS_LOCAL, toLocalUrl: bfToLocalUrl, nav: bfNav };