/* BoatsFun — Page demande de réservation V1 (checkout-combine.html)
   Réservation simple via ?boat=slug, ou combinée via ?slugs=...&region=...&date=...&slot=...
   Affiche le récap des bateaux + totaux ESTIMÉS (aucun paiement), le mode d'opération,
   les services sur demande et un formulaire "Vos informations".
   CTA "Envoyer la demande de réservation" → handleReservationRequestSubmit :
   regroupe la demande dans un objet et fait un console.log (Zapier/email à brancher post-V1). */

(function () {
  const { I } = window.BF;
  const { BOATS, bySlug, blockTotal, depositFor, TIME_SLOTS } = window.BFBoats;
  const { useState, useMemo } = React;

  const COMBINE_KEY = "bf.combine.selection";

  // Images temporaires (Unsplash) — style nautique / premium / lifestyle.
  // À remplacer par les visuels finaux dans assets/ quand fournis.
  const SERVICES = [
    { id: "crew",      title: "Équipage à bord",                          desc: "Personnel de bord supplémentaire pour accompagner votre sortie.",                  img: "https://images.unsplash.com/photo-1540541338287-41700207dee6?w=800&q=80&auto=format&fit=crop" },
    { id: "traiteur",  title: "Service traiteur",                         desc: "Repas préparés, bouchées ou service complet selon votre événement.",               img: "https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=800&q=80&auto=format&fit=crop" },
    { id: "dj",        title: "DJ privé",                                 desc: "Un DJ pour créer une ambiance festive directement à bord.",                        img: "https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800&q=80&auto=format&fit=crop" },
    { id: "barman",    title: "Barman privé",                             desc: "Service de cocktails et boissons préparées pendant la sortie.",                     img: "https://images.unsplash.com/photo-1514362545857-3bc16c4c7d1b?w=800&q=80&auto=format&fit=crop" },
    { id: "transport", title: "Transport privé / limousine / party bus",  desc: "Transport premium pour arriver à la marina sans stress.",                          img: "https://images.unsplash.com/photo-1503376780353-7e6692767b70?w=800&q=80&auto=format&fit=crop" },
    { id: "media",     title: "Photos & vidéo souvenir",                  desc: "Capturez votre sortie avec du contenu photo ou vidéo professionnel.",               img: "https://images.unsplash.com/photo-1492144534655-ae79c964c9d7?w=800&q=80&auto=format&fit=crop" },
  ];

  // Critères d'admissibilité pour l'option « opérer le bateau soi-même » (tous requis).
  const ELIG_ITEMS = [
    { k: "competence", label: "Je confirme avoir une preuve de compétence valide pour opérer une embarcation motorisée." },
    { k: "experience", label: "Je confirme avoir au moins 2 ans d'expérience de navigation pertinente." },
    { k: "similaire",  label: "J'ai déjà opéré un bateau de taille similaire." },
    { k: "manoeuvres", label: "Je suis à l'aise avec les manœuvres, l'accostage, les règles de navigation et les conditions météo." },
    { k: "documents",  label: "Je comprends que BoatsFun peut demander des documents justificatifs avant d'approuver ma demande." },
  ];

  function readParams() {
    if (typeof window === "undefined") return { slugs: [], boat: "", region: "", date: "", timeSlot: "" };
    const p = new URLSearchParams(window.location.search);
    const slugsParam = p.get("slugs") || "";
    const slugs = slugsParam.split(",").map((s) => s.trim()).filter(Boolean);
    return {
      slugs,
      boat:     (p.get("boat") || "").trim(),  // réservation simple depuis une fiche bateau
      region:   p.get("region") || "",
      date:     p.get("date")   || "",
      timeSlot: p.get("slot")   || ""
    };
  }

  function readFallback() {
    try {
      const raw = localStorage.getItem(COMBINE_KEY);
      if (!raw) return null;
      return JSON.parse(raw);
    } catch (e) { return null; }
  }

  // Formatte les zones de navigation communes entre les bateaux sélectionnés
  function commonZones(boats) {
    if (!boats.length) return [];
    const set = new Set(boats[0].compatibleMeetingZones || []);
    boats.slice(1).forEach((b) => {
      const z = new Set(b.compatibleMeetingZones || []);
      for (const v of Array.from(set)) if (!z.has(v)) set.delete(v);
    });
    return Array.from(set);
  }

  function formatDate(iso) {
    if (!iso) return "Non renseignée";
    try {
      const d = new Date(iso + "T00:00:00");
      return d.toLocaleDateString("fr-CA", { weekday: "long", day: "numeric", month: "long", year: "numeric" });
    } catch (e) { return iso; }
  }

  const styles = `
.bf .cc-hero { background:linear-gradient(180deg, #002244 0%, #003a66 100%); color:#fff; padding:56px 0 48px; }
.bf .cc-hero .eyebrow { color:var(--teal); }
.bf .cc-hero h1 { font-family:'Poppins', sans-serif; font-size:42px; line-height:1.1; font-weight:800; color:#fff; margin:12px 0 12px; letter-spacing:-1.1px; }
.bf .cc-hero p.lead { font-size:16.5px; line-height:1.55; color:rgba(255,255,255,.82); max-width:680px; }

.bf .cc-wrap { padding:48px 0 var(--section-pad); }
.bf .cc-grid { display:grid; grid-template-columns: 1.5fr 1fr; gap:28px; align-items:start; }

/* Cartes bateaux */
.bf .cc-section { background:#fff; border:1px solid var(--hair); border-radius:18px; padding:24px 26px; }
.bf .cc-section h2 { font-family:'Poppins', sans-serif; font-size:22px; font-weight:700; color:var(--navy); margin-bottom:18px; letter-spacing:-.4px; }
.bf .cc-boats { display:flex; flex-direction:column; gap:16px; }
.bf .cc-boat { display:grid; grid-template-columns: 120px 1fr auto; gap:16px; align-items:center; padding:12px; border:1px solid var(--hair); border-radius:14px; transition:border-color .15s; }
.bf .cc-boat:hover { border-color:var(--navy); }
.bf .cc-boat-img { width:120px; height:90px; border-radius:10px; overflow:hidden; background:var(--sea-mist); }
.bf .cc-boat-img img { width:100%; height:100%; object-fit:cover; display:block; }
.bf .cc-boat-info { min-width:0; }
.bf .cc-boat-info h3 { font-family:'Poppins', sans-serif; font-size:16.5px; font-weight:700; color:var(--navy); line-height:1.25; letter-spacing:-.2px; margin-bottom:4px; }
.bf .cc-boat-info .ccb-type { display:inline-block; background:var(--sea-mist); color:var(--navy); font-family:'Inter', sans-serif; font-size:11.5px; font-weight:600; padding:2px 9px; border-radius:999px; margin-right:6px; }
.bf .cc-boat-info .ccb-meta { font-size:13px; color:var(--body); margin-top:6px; line-height:1.4; }
.bf .cc-boat-info .ccb-meta svg { width:13px; height:13px; vertical-align:-2px; margin-right:4px; color:var(--teal); }
.bf .cc-boat-price { text-align:right; }
.bf .cc-boat-price .pp { font-family:'Poppins', sans-serif; font-size:18px; font-weight:700; color:var(--navy); }
.bf .cc-boat-price .pd { font-size:12px; color:var(--muted); margin-top:3px; }

/* Sidebar : récap + paiement */
.bf .cc-side { display:flex; flex-direction:column; gap:18px; position:sticky; top:96px; }
.bf .cc-summary { background:#fff; border:1px solid var(--hair); border-radius:18px; padding:24px 26px; }
.bf .cc-summary h2 { font-family:'Poppins', sans-serif; font-size:20px; font-weight:700; color:var(--navy); margin-bottom:14px; }
.bf .cc-summary .ccs-row { display:flex; justify-content:space-between; padding:9px 0; border-bottom:1px dashed var(--hair); font-size:14px; color:var(--body); }
.bf .cc-summary .ccs-row:last-of-type { border-bottom:none; }
.bf .cc-summary .ccs-row b { color:var(--navy); font-weight:600; }
.bf .cc-summary .ccs-totals { margin-top:18px; padding-top:18px; border-top:2px solid var(--hair); }
.bf .cc-summary .ccs-tot { display:flex; justify-content:space-between; align-items:baseline; margin-bottom:8px; font-size:14.5px; color:var(--body); }
.bf .cc-summary .ccs-tot.is-big { font-family:'Poppins', sans-serif; font-size:22px; font-weight:700; color:var(--navy); margin-bottom:0; }
.bf .cc-summary .ccs-tot .amount { color:var(--navy); font-weight:700; }
.bf .cc-summary .ccs-tot.is-big .amount { color:var(--orange); }
.bf .cc-summary .ccs-cta { width:100%; margin-top:18px; background:var(--orange); border:none; color:#fff; font-family:'Inter', sans-serif; font-weight:600; font-size:15px; padding:15px 24px; border-radius:999px; cursor:pointer; box-shadow:var(--cta-shadow-orange); transition:all .2s; display:inline-flex; align-items:center; justify-content:center; gap:8px; }
.bf .cc-summary .ccs-cta:hover { background:#FF9420; transform:translateY(-2px); box-shadow:var(--cta-shadow-orange-hover); }
.bf .cc-summary .ccs-cta:disabled { opacity:.5; cursor:not-allowed; transform:none; }

.bf .cc-note { background:var(--sea-mist); border-left:3px solid var(--teal); border-radius:10px; padding:14px 16px; font-size:13.5px; color:var(--body); line-height:1.55; }
.bf .cc-note strong { color:var(--navy); display:block; margin-bottom:4px; font-size:13.5px; }

.bf .cc-empty { text-align:center; padding:80px 24px; background:#fff; border:1px dashed var(--hair); border-radius:18px; }
.bf .cc-empty h2 { font-family:'Poppins', sans-serif; font-size:22px; color:var(--navy); margin-bottom:8px; }
.bf .cc-empty p { color:var(--body); margin-bottom:18px; }

@media (max-width: 1024px) {
  .bf .cc-grid { grid-template-columns: 1fr; }
  .bf .cc-side { position:static; }
}
@media (max-width: 860px) {
  .bf .cc-hero h1 { font-size:30px; }
  .bf .cc-section, .bf .cc-summary { padding:18px 18px; }
  .bf .cc-boat { grid-template-columns: 90px 1fr; }
  .bf .cc-boat-img { width:90px; height:75px; }
  .bf .cc-boat-price { grid-column:1 / -1; text-align:left; padding-top:6px; border-top:1px dashed var(--hair); }
}

/* Add-ons */
.bf .cc-left { display:flex; flex-direction:column; gap:18px; }
.bf .cc-addons-sec { background:#fff; border:1px solid var(--hair); border-radius:18px; padding:24px 26px; }
.bf .cc-addons-sec h2 { font-family:'Poppins', sans-serif; font-size:22px; font-weight:700; color:var(--navy); margin:0 0 4px; letter-spacing:-.4px; }
.bf .cc-addons-sub { font-size:14px; color:var(--muted); margin:0 0 20px; }
.bf .cc-addons-grid { display:grid; grid-template-columns:repeat(3,1fr); gap:16px; }
.bf .cc-addon-card { border:1.5px solid var(--hair); border-radius:16px; padding:0; cursor:pointer; transition:border-color .2s, box-shadow .25s, transform .2s; text-align:left; background:#fff; width:100%; box-sizing:border-box; overflow:hidden; display:flex; flex-direction:column; }
.bf .cc-addon-card:hover { border-color:var(--teal); box-shadow:0 10px 26px rgba(0,34,68,.13); transform:translateY(-3px); }
.bf .cc-addon-card.is-active { border-color:var(--teal); box-shadow:0 10px 26px rgba(0,181,226,.22); }
.bf .cc-addon-media { position:relative; width:100%; aspect-ratio:16 / 10; background:linear-gradient(135deg,#002244,#00B5E2); overflow:hidden; }
.bf .cc-addon-media img { width:100%; height:100%; object-fit:cover; display:block; transition:transform .4s ease; }
.bf .cc-addon-card:hover .cc-addon-media img { transform:scale(1.05); }
.bf .cc-addon-media::after { content:""; position:absolute; inset:0; background:linear-gradient(180deg, rgba(0,34,68,0) 50%, rgba(0,34,68,.30)); pointer-events:none; }
.bf .cc-addon-badge { position:absolute; top:10px; left:10px; z-index:1; display:inline-flex; align-items:center; gap:5px; background:var(--teal); color:#fff; font-size:11px; font-weight:700; letter-spacing:.2px; padding:5px 10px; border-radius:999px; box-shadow:0 4px 12px rgba(0,34,68,.25); }
.bf .cc-addon-body { padding:14px 16px 16px; display:flex; flex-direction:column; flex:1; }
.bf .cc-addon-title { font-family:'Poppins', sans-serif; font-size:14.5px; font-weight:700; color:var(--navy); margin:0 0 6px; line-height:1.25; }
.bf .cc-addon-desc { font-size:12.5px; color:var(--body); line-height:1.5; margin:0 0 14px; flex:1; }
.bf .cc-addon-footer { display:flex; justify-content:space-between; align-items:center; margin-top:auto; }
.bf .cc-addon-price { font-size:12px; color:var(--muted); font-style:italic; }
.bf .cc-addon-toggle { width:32px; height:32px; border-radius:50%; border:1.5px solid rgba(0,34,68,.18); background:#fff; color:var(--navy); font-size:19px; line-height:1; cursor:pointer; display:inline-flex; align-items:center; justify-content:center; transition:all .18s; padding:0; flex-shrink:0; }
.bf .cc-addon-card:hover .cc-addon-toggle { border-color:var(--teal); color:var(--teal); }
.bf .cc-addon-card.is-active .cc-addon-toggle { background:var(--teal); border-color:var(--teal); color:#fff; font-size:15px; font-weight:700; }
.bf .cc-addons-note { margin-top:14px; background:var(--sea-mist); border-left:3px solid var(--teal); border-radius:10px; padding:12px 14px; font-size:12.5px; color:var(--body); line-height:1.55; }
.bf .cc-addons-sum { margin-top:14px; padding-top:14px; border-top:1px dashed var(--hair); }
.bf .cc-addons-sum-hd { font-size:13px; font-weight:700; color:var(--navy); margin-bottom:6px; }
.bf .cc-addons-sum-row { display:flex; justify-content:space-between; font-size:13px; color:var(--body); padding:3px 0; }
.bf .cc-addons-sum-row .sum-muted { color:var(--muted); }
@media (max-width:960px) { .bf .cc-addons-grid { grid-template-columns:repeat(2,1fr); } }
@media (max-width:600px) { .bf .cc-addons-grid { grid-template-columns:1fr; } .bf .cc-addons-sec { padding:18px; } }
`;

  const opStyles = `
/* Mode d'opération */
.bf .cc-op-sec { background:#fff; border:1px solid var(--hair); border-radius:18px; padding:24px 26px; }
.bf .cc-op-sec h2 { font-family:'Poppins', sans-serif; font-size:22px; font-weight:700; color:var(--navy); margin:0 0 4px; letter-spacing:-.4px; }
.bf .cc-op-sub { font-size:14px; color:var(--muted); margin:0 0 20px; }
.bf .cc-op-cards { display:grid; grid-template-columns:repeat(2,1fr); gap:14px; }
.bf .cc-op-card { position:relative; text-align:left; border:1.5px solid var(--hair); border-radius:14px; padding:18px; cursor:pointer; background:#fff; width:100%; box-sizing:border-box; transition:border-color .2s, background .2s, box-shadow .2s; }
.bf .cc-op-card:hover { border-color:var(--teal); box-shadow:0 4px 16px rgba(0,181,226,.12); }
.bf .cc-op-card.is-active { border-color:var(--teal); background:rgba(0,181,226,.07); }
.bf .cc-op-card-head { display:flex; align-items:center; justify-content:space-between; gap:10px; margin-bottom:8px; }
.bf .cc-op-title { font-family:'Poppins', sans-serif; font-size:15.5px; font-weight:700; color:var(--navy); margin:0; line-height:1.25; }
.bf .cc-op-badge { font-family:'Inter', sans-serif; font-size:11px; font-weight:700; padding:3px 10px; border-radius:999px; white-space:nowrap; flex-shrink:0; }
.bf .cc-op-badge.is-reco { background:var(--teal); color:#fff; }
.bf .cc-op-badge.is-valid { background:var(--sea-mist); color:var(--navy); border:1px solid var(--hair); }
.bf .cc-op-text { font-size:13px; color:var(--body); line-height:1.55; margin:0 0 10px; }
.bf .cc-op-mention { font-size:12px; color:var(--muted); font-style:italic; margin:0; }

/* Sous-section admissibilité opérateur */
.bf .cc-elig { margin-top:18px; padding-top:18px; border-top:1px dashed var(--hair); }
.bf .cc-elig h3 { font-family:'Poppins', sans-serif; font-size:16px; font-weight:700; color:var(--navy); margin:0 0 8px; }
.bf .cc-elig-intro { font-size:13px; color:var(--body); line-height:1.6; margin:0 0 16px; }
.bf .cc-elig-list { display:flex; flex-direction:column; gap:10px; margin-bottom:18px; }
.bf .cc-elig-check { display:flex; align-items:flex-start; gap:10px; font-size:13.5px; color:var(--navy); line-height:1.45; cursor:pointer; }
.bf .cc-elig-check input { width:18px; height:18px; margin-top:1px; accent-color:var(--teal); cursor:pointer; flex-shrink:0; }
.bf .cc-fields { display:grid; grid-template-columns:1fr 1fr; gap:14px; }
.bf .cc-field { display:flex; flex-direction:column; gap:5px; }
.bf .cc-field.is-full { grid-column:1 / -1; }
.bf .cc-field label { font-size:12.5px; font-weight:600; color:var(--navy); }
.bf .cc-field input, .bf .cc-field textarea { font-family:'Inter', sans-serif; font-size:14px; color:var(--navy); border:1.5px solid var(--hair); border-radius:10px; padding:10px 12px; background:#fff; width:100%; box-sizing:border-box; }
.bf .cc-field input:focus, .bf .cc-field textarea:focus { outline:none; border-color:var(--teal); }
.bf .cc-field textarea { resize:vertical; min-height:64px; }
.bf .cc-uploads { display:grid; grid-template-columns:1fr 1fr; gap:14px; margin-top:14px; }
.bf .cc-upload { border:1.5px dashed var(--hair); border-radius:12px; padding:18px 14px; text-align:center; color:var(--muted); font-size:13px; background:var(--sea-mist); line-height:1.4; }
.bf .cc-upload .cc-upload-ic { font-size:22px; display:block; margin-bottom:6px; }
.bf .cc-upload .cc-upload-soon { display:inline-block; margin-top:8px; font-size:11px; font-weight:700; color:var(--navy); background:#fff; border:1px solid var(--hair); border-radius:999px; padding:2px 8px; }

/* Note légale + avertissement */
.bf .cc-op-legal { margin-top:18px; background:var(--sea-mist); border-left:3px solid var(--navy); border-radius:10px; padding:14px 16px; font-size:12.5px; color:var(--body); line-height:1.6; }
.bf .cc-op-warning { margin-top:14px; display:flex; align-items:flex-start; gap:10px; background:#fff; border:1.5px solid var(--orange); border-radius:10px; padding:14px 16px; font-size:13.5px; color:var(--navy); font-weight:600; line-height:1.5; }
.bf .cc-op-warning .cc-warn-ic { flex-shrink:0; font-size:16px; line-height:1.3; }
.bf .cc-cta-hint { margin-top:10px; font-size:12px; color:var(--muted); text-align:center; line-height:1.5; }
@media (max-width: 860px) { .bf .cc-op-sec { padding:18px 18px; } }
@media (max-width: 600px) { .bf .cc-op-cards { grid-template-columns:1fr; } .bf .cc-fields { grid-template-columns:1fr; } .bf .cc-uploads { grid-template-columns:1fr; } }

/* ===== Vos informations (demande de réservation V1) ===== */
.bf .cc-info-sec { background:#fff; border:1px solid var(--hair); border-radius:18px; padding:24px 26px; }
.bf .cc-info-sec h2 { font-family:'Poppins', sans-serif; font-size:22px; font-weight:700; color:var(--navy); margin:0 0 4px; letter-spacing:-.4px; }
.bf .cc-req { color:var(--orange); }
.bf .cc-field.is-error input, .bf .cc-field.is-error select, .bf .cc-field.is-error textarea { border-color:#E0322F; background:#FFF6F5; }
.bf .cc-summary .ccs-estimate-note { margin:10px 0 0; font-size:11.5px; color:var(--muted); line-height:1.45; }
.bf .cc-summary .ccs-cta.is-inactive { opacity:.6; box-shadow:none; }
.bf .cc-summary .ccs-cta.is-inactive:hover { background:var(--orange); transform:none; box-shadow:none; }

/* ===== Confirmation d'envoi ===== */
.bf .cc-success { text-align:center; max-width:640px; margin:0 auto; background:#fff; border:1px solid var(--hair); border-radius:22px; padding:48px 32px; box-shadow:0 20px 46px -32px rgba(0,34,68,.4); }
.bf .cc-success-ic { width:72px; height:72px; border-radius:50%; background:rgba(0,181,226,.12); color:var(--teal); display:inline-flex; align-items:center; justify-content:center; font-size:34px; font-weight:800; margin-bottom:20px; }
.bf .cc-success h2 { font-family:'Poppins', sans-serif; font-size:26px; color:var(--navy); margin:0 0 12px; letter-spacing:-.5px; }
.bf .cc-success > p { font-size:15px; color:var(--body); line-height:1.6; margin:0 auto 26px; max-width:520px; }
.bf .cc-success-recap { text-align:left; background:var(--sea-mist); border-radius:14px; padding:18px 20px; margin-bottom:26px; }
.bf .cc-success-recap .sr-row { display:flex; justify-content:space-between; gap:16px; padding:8px 0; font-size:14px; color:var(--body); border-bottom:1px dashed var(--hair); }
.bf .cc-success-recap .sr-row:last-child { border-bottom:none; }
.bf .cc-success-recap .sr-row b { color:var(--navy); font-weight:600; text-align:right; }
.bf .cc-success-actions { display:flex; gap:12px; justify-content:center; flex-wrap:wrap; }
.bf .cc-success-actions a { display:inline-flex; align-items:center; justify-content:center; padding:13px 24px; border-radius:999px; font-family:'Inter', sans-serif; font-weight:600; font-size:14.5px; text-decoration:none; transition:transform .2s, background .2s, color .2s, box-shadow .2s; }
.bf .cc-success-actions .is-primary { background:var(--orange); color:#fff; box-shadow:var(--cta-shadow-orange); }
.bf .cc-success-actions .is-primary:hover { background:#FF9420; transform:translateY(-2px); }
.bf .cc-success-actions .is-secondary { background:#fff; color:var(--navy); border:1.5px solid rgba(0,34,68,.22); }
.bf .cc-success-actions .is-secondary:hover { background:var(--navy); color:#fff; }
@media (max-width: 600px) { .bf .cc-info-sec { padding:18px 18px; } .bf .cc-success { padding:36px 20px; } }
`;

  function BoatRow({ b }) {
    return (
      <div className="cc-boat">
        <div className="cc-boat-img"><img src={b.img} alt={b.name} loading="lazy" /></div>
        <div className="cc-boat-info">
          <h3>{b.name}</h3>
          <span className="ccb-type">{b.type}</span>
          <span className="ccb-type">{b.pers} pers.</span>
          <div className="ccb-meta">
            <I.pin />{b.region} · {b.sector}{b.marina ? ` · ${b.marina}` : ""}
          </div>
        </div>
        <div className="cc-boat-price">
          <div className="pp">{blockTotal(b).toLocaleString("fr-CA")} $</div>
          <div className="pd">Bloc 4h · Dépôt {depositFor(b).toLocaleString("fr-CA")} $</div>
        </div>
      </div>
    );
  }

  function AddonCard({ addon, selected, onToggle }) {
    return (
      <button
        type="button"
        className={"cc-addon-card" + (selected ? " is-active" : "")}
        onClick={() => onToggle(addon.id)}
        aria-pressed={selected ? "true" : "false"}
      >
        <div className="cc-addon-media">
          <img
            src={addon.img}
            alt={addon.title}
            loading="lazy"
            onError={(e) => { e.currentTarget.style.display = "none"; }}
          />
          {selected && <span className="cc-addon-badge">Ajouté ✓</span>}
        </div>
        <div className="cc-addon-body">
          <div className="cc-addon-title">{addon.title}</div>
          <p className="cc-addon-desc">{addon.desc}</p>
          <div className="cc-addon-footer">
            <span className="cc-addon-price">Prix sur demande</span>
            <span className="cc-addon-toggle" aria-hidden="true">{selected ? "✓" : "+"}</span>
          </div>
        </div>
      </button>
    );
  }

  function BFPageCheckoutCombine() {
    // 1) Priorité URL params, fallback localStorage (cas redirection avec sélection vide)
    const url = useMemo(readParams, []);
    const fallback = useMemo(() => (url.slugs.length === 0 ? readFallback() : null), [url]);
    const [selectedServices, setSelectedServices] = useState([]);
    const toggleService = (id) => setSelectedServices(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);

    // Mode d'opération : "" (à choisir) | "captain" | "self"
    const [operationMode, setOperationMode] = useState("");
    const [elig, setElig] = useState({});
    const toggleElig = (k) => setElig(prev => ({ ...prev, [k]: !prev[k] }));
    const [certType, setCertType] = useState("");
    const [expYears, setExpYears] = useState("");
    const [expDesc, setExpDesc] = useState("");

    const allEligChecked = ELIG_ITEMS.every(it => elig[it.k]);
    const selfNeedsCaptain = operationMode === "self" && !allEligChecked;
    // Mode d'opération valide : capitaine demandé, ou auto-opération avec tous les critères cochés.
    const operationReady = operationMode === "captain" || (operationMode === "self" && allEligChecked);

    // Réservation simple si ?boat=slug fourni, sinon réservation combinée (≥ 2 bateaux)
    const isSingle = !!url.boat;
    const slugs = isSingle
      ? [url.boat]
      : (url.slugs.length > 0 ? url.slugs : (fallback?.slugs || []));
    const region = url.region || fallback?.ctx?.region || "";

    const boats = slugs.map(bySlug).filter(Boolean);
    const totalPrice   = boats.reduce((sum, b) => sum + blockTotal(b), 0);
    const totalDeposit = boats.reduce((sum, b) => sum + depositFor(b), 0);
    const navigationZones = Array.from(new Set(boats.map((b) => b.navigationZone).filter(Boolean)));
    const zones = commonZones(boats);

    const boatNotFound = isSingle && boats.length === 0;        // ?boat= invalide
    const ready = isSingle ? boats.length >= 1 : boats.length >= 2;

    // 2) Détails de sortie éditables (pré-remplis depuis l'URL / la sélection)
    const [date, setDate]         = useState(url.date     || fallback?.ctx?.date     || "");
    const [timeSlot, setTimeSlot] = useState(url.timeSlot || fallback?.ctx?.timeSlot || "");
    const [guests, setGuests]     = useState("");

    // 3) Informations client (requises pour envoyer la demande)
    const [firstName, setFirstName] = useState("");
    const [lastName, setLastName]   = useState("");
    const [email, setEmail]         = useState("");
    const [phone, setPhone]         = useState("");
    const [message, setMessage]     = useState("");

    const [triedSubmit, setTriedSubmit] = useState(false);
    const [submitted, setSubmitted]     = useState(false);

    const maxGuests = boats.length ? (isSingle ? boats[0].pers : boats.reduce((s, b) => s + (b.pers || 0), 0)) : 0;
    const slotOptions = (isSingle && boats[0] && boats[0].timeSlots && boats[0].timeSlots.length)
      ? boats[0].timeSlots
      : (TIME_SLOTS || []);

    const emailValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
    const formValid = !!(
      firstName.trim() && lastName.trim() && emailValid &&
      phone.trim() && date && timeSlot && String(guests).trim() && Number(guests) >= 1
    );
    // Envoi autorisé : mode d'opération valide ET informations client complètes.
    const canSubmit = operationReady && formValid;

    // V1 : regroupe toute la demande dans un objet propre. Aucun paiement, aucun
    // appel API. Branchement Zapier / email à faire plus tard (voir handoff).
    const handleReservationRequestSubmit = (e) => {
      if (e && e.preventDefault) e.preventDefault();
      setTriedSubmit(true);
      if (!canSubmit) return;

      const reservationRequest = {
        typeDemande: isSingle ? "reservation-simple" : "reservation-combinee",
        bateau: isSingle
          ? { slug: boats[0].slug, nom: boats[0].name, type: boats[0].type, secteur: boats[0].sector, marina: boats[0].marina }
          : boats.map((b) => ({ slug: b.slug, nom: b.name, type: b.type, secteur: b.sector, marina: b.marina })),
        date,
        plageHoraire: timeSlot,
        nombrePersonnes: Number(guests),
        modeOperation: operationMode, // "captain" | "self"
        operateurAutonome: operationMode === "self"
          ? { criteres: { ...elig }, typeCertificat: certType, anneesExperience: expYears, descriptionExperience: expDesc }
          : null,
        servicesAdditionnels: selectedServices
          .map((id) => (SERVICES.find((s) => s.id === id) || {}).title)
          .filter(Boolean),
        client: {
          prenom: firstName.trim(),
          nom: lastName.trim(),
          email: email.trim(),
          telephone: phone.trim()
        },
        message: message.trim(),
        totalEstime: totalPrice || null,
        depotEstime: totalDeposit || null,
        soumisLe: new Date().toISOString()
      };

      // TODO (post-V1) : envoyer reservationRequest vers Zapier / email au lieu du console.log.
      console.log("[BoatsFun] Demande de réservation V1 :", reservationRequest);

      setSubmitted(true);
      if (typeof window !== "undefined" && window.scrollTo) {
        window.scrollTo({ top: 0, behavior: "smooth" });
      }
    };

    return (
      <div className="bf" data-screen-label="Checkout combiné">
        <style>{styles + opStyles}</style>

        <window.BFHeader />

        <section className="cc-hero">
          <div className="container">
            <span className="eyebrow">{isSingle ? "Réservation" : "Réservation combinée"}</span>
            <h1>{isSingle ? "Réservez votre sortie" : "Vos bateaux pour une sortie coordonnée"}</h1>
            <p className="lead">{isSingle
              ? "Vérifiez les détails de votre sortie, ajoutez vos services sur demande et envoyez votre demande de réservation."
              : "Vous réservez plusieurs bateaux dans le même secteur pour une sortie coordonnée. Dépôt 100 % remboursable si l'un des propriétaires refuse."}</p>
          </div>
        </section>

        <main className="container cc-wrap">
          {submitted ? (
            <div className="cc-success">
              <div className="cc-success-ic" aria-hidden="true">✓</div>
              <h2>Demande de réservation envoyée</h2>
              <p>Votre demande de réservation a été envoyée. L'équipe BoatsFun vous contactera rapidement pour confirmer la disponibilité.</p>
              <div className="cc-success-recap">
                <div className="sr-row"><span>{isSingle ? "Bateau" : "Bateaux"}</span><b>{isSingle ? (boats[0] && boats[0].name) : boats.map((b) => b.name).join(", ")}</b></div>
                <div className="sr-row"><span>Date</span><b>{formatDate(date)}</b></div>
                <div className="sr-row"><span>Plage horaire</span><b>{timeSlot || "—"}</b></div>
                <div className="sr-row"><span>Personnes</span><b>{guests || "—"}</b></div>
                <div className="sr-row"><span>Mode d'opération</span><b>{operationMode === "captain" ? "Capitaine demandé" : "Opération autonome — sur validation"}</b></div>
                <div className="sr-row"><span>Contact</span><b>{firstName} {lastName}</b></div>
              </div>
              <div className="cc-success-actions">
                <a className="is-primary" href="/bateaux">Voir d'autres bateaux</a>
                <a className="is-secondary" href="/">Retour à l'accueil</a>
              </div>
            </div>
          ) : !ready ? (
            <div className="cc-empty">
              {boatNotFound ? (
                <React.Fragment>
                  <h2>Bateau introuvable</h2>
                  <p>Bateau introuvable. Retournez au catalogue pour choisir un bateau.</p>
                  <a className="btn-outline" href="/bateaux">Retour au catalogue</a>
                </React.Fragment>
              ) : (
                <React.Fragment>
                  <h2>Aucune sélection trouvée</h2>
                  <p>Retournez au catalogue, activez le mode <b>Combiner plusieurs bateaux</b> et sélectionnez au moins 2 bateaux compatibles.</p>
                  <a className="btn-outline" href="/bateaux?combine=1">Retour au catalogue</a>
                </React.Fragment>
              )}
            </div>
          ) : (
            <div className="cc-grid">
              {/* Colonne gauche : bateaux + add-ons */}
              <div className="cc-left">
                <div className="cc-section">
                  <h2>{isSingle ? "Votre bateau" : `${boats.length} bateaux sélectionnés`}</h2>
                  <div className="cc-boats">
                    {boats.map((b) => <BoatRow key={b.slug} b={b} />)}
                  </div>
                </div>

                <div className="cc-op-sec">
                  <h2>Comment souhaitez-vous organiser votre sortie ?</h2>
                  <p className="cc-op-sub">Choisissez comment votre bateau sera opéré pendant la sortie.</p>
                  <div className="cc-op-cards">
                    <button
                      type="button"
                      className={"cc-op-card" + (operationMode === "captain" ? " is-active" : "")}
                      aria-pressed={operationMode === "captain" ? "true" : "false"}
                      onClick={() => setOperationMode("captain")}
                    >
                      <div className="cc-op-card-head">
                        <h3 className="cc-op-title">Demander un capitaine</h3>
                        <span className="cc-op-badge is-reco">Recommandé</span>
                      </div>
                      <p className="cc-op-text">Profitez de votre sortie sans gérer la navigation. Cette option est recommandée pour la majorité des groupes.</p>
                      <p className="cc-op-mention">Prix à confirmer selon le bateau, la durée et la disponibilité.</p>
                    </button>

                    <button
                      type="button"
                      className={"cc-op-card" + (operationMode === "self" ? " is-active" : "")}
                      aria-pressed={operationMode === "self" ? "true" : "false"}
                      onClick={() => setOperationMode("self")}
                    >
                      <div className="cc-op-card-head">
                        <h3 className="cc-op-title">Opérer le bateau moi-même</h3>
                        <span className="cc-op-badge is-valid">Sur validation</span>
                      </div>
                      <p className="cc-op-text">Disponible uniquement si vous répondez aux critères d'expérience, de compétence et de validation requis.</p>
                      <p className="cc-op-mention">Documents et informations additionnels requis.</p>
                    </button>
                  </div>

                  {operationMode === "self" && (
                    <div className="cc-elig">
                      <h3>Vérification d'admissibilité opérateur</h3>
                      <p className="cc-elig-intro">Pour des raisons de sécurité, d'assurance et de conformité, les demandes sans capitaine sont sujettes à validation. BoatsFun peut refuser une demande autonome si les documents ou l'expérience fournis ne sont pas suffisants.</p>

                      <div className="cc-elig-list">
                        {ELIG_ITEMS.map(it => (
                          <label key={it.k} className="cc-elig-check">
                            <input type="checkbox" checked={!!elig[it.k]} onChange={() => toggleElig(it.k)} />
                            <span>{it.label}</span>
                          </label>
                        ))}
                      </div>

                      <div className="cc-fields">
                        <div className="cc-field">
                          <label htmlFor="cc-cert">Type de preuve de compétence / certificat</label>
                          <input id="cc-cert" type="text" value={certType} onChange={(e) => setCertType(e.target.value)} placeholder="Ex. CCEP, carte de compétence d'opérateur…" />
                        </div>
                        <div className="cc-field">
                          <label htmlFor="cc-years">Nombre d'années d'expérience</label>
                          <input id="cc-years" type="number" min="0" value={expYears} onChange={(e) => setExpYears(e.target.value)} placeholder="Ex. 3" />
                        </div>
                        <div className="cc-field is-full">
                          <label htmlFor="cc-desc">Description courte de l'expérience nautique</label>
                          <textarea id="cc-desc" value={expDesc} onChange={(e) => setExpDesc(e.target.value)} placeholder="Types de bateaux opérés, plans d'eau fréquentés, fréquence de navigation…" />
                        </div>
                      </div>

                      <div className="cc-uploads">
                        <div className="cc-upload">
                          <span className="cc-upload-ic">📄</span>
                          Preuve de compétence
                          <span className="cc-upload-soon">Téléversement bientôt disponible</span>
                        </div>
                        <div className="cc-upload">
                          <span className="cc-upload-ic">🪪</span>
                          Pièce d'identité
                          <span className="cc-upload-soon">Téléversement bientôt disponible</span>
                        </div>
                      </div>

                      {selfNeedsCaptain && (
                        <div className="cc-op-warning">
                          <span className="cc-warn-ic" aria-hidden="true">⚠️</span>
                          <span>Pour continuer, veuillez choisir « Demander un capitaine » ou compléter tous les critères d'admissibilité pour une demande autonome.</span>
                        </div>
                      )}
                    </div>
                  )}

                  <div className="cc-op-legal">
                    Le choix d'opérer le bateau soi-même ne garantit pas l'approbation de la réservation. BoatsFun, le propriétaire ou leurs représentants peuvent demander des documents additionnels, refuser une demande autonome ou recommander un capitaine selon le bateau, l'expérience déclarée, la météo, le lieu de départ ou les exigences applicables.
                  </div>
                </div>

                <div className="cc-addons-sec">
                  <h2>Services additionnels</h2>
                  <p className="cc-addons-sub">Tous sur demande — prix à confirmer.</p>
                  <div className="cc-addons-grid">
                    {SERVICES.map(a => (
                      <AddonCard key={a.id} addon={a} selected={selectedServices.includes(a.id)} onToggle={toggleService} />
                    ))}
                  </div>
                  <div className="cc-addons-note">
                    Ces services sont sur demande. Notre équipe confirmera la disponibilité et le tarif après la demande de réservation.
                  </div>
                </div>

                <div className="cc-info-sec">
                  <h2>Vos informations</h2>
                  <p className="cc-op-sub">Pour vous recontacter et confirmer la disponibilité. Aucun paiement n'est requis à cette étape.</p>
                  <div className="cc-fields">
                    <div className={"cc-field" + (triedSubmit && !firstName.trim() ? " is-error" : "")}>
                      <label htmlFor="cc-fn">Prénom <span className="cc-req">*</span></label>
                      <input id="cc-fn" type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} placeholder="Ex. Camille" autoComplete="given-name" />
                    </div>
                    <div className={"cc-field" + (triedSubmit && !lastName.trim() ? " is-error" : "")}>
                      <label htmlFor="cc-ln">Nom <span className="cc-req">*</span></label>
                      <input id="cc-ln" type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} placeholder="Ex. Tremblay" autoComplete="family-name" />
                    </div>
                    <div className={"cc-field" + (triedSubmit && !emailValid ? " is-error" : "")}>
                      <label htmlFor="cc-email">Email <span className="cc-req">*</span></label>
                      <input id="cc-email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="vous@exemple.com" autoComplete="email" />
                    </div>
                    <div className={"cc-field" + (triedSubmit && !phone.trim() ? " is-error" : "")}>
                      <label htmlFor="cc-phone">Téléphone <span className="cc-req">*</span></label>
                      <input id="cc-phone" type="tel" value={phone} onChange={(e) => setPhone(e.target.value)} placeholder="514 000 0000" autoComplete="tel" />
                    </div>
                    <div className={"cc-field" + (triedSubmit && !date ? " is-error" : "")}>
                      <label htmlFor="cc-date">Date souhaitée <span className="cc-req">*</span></label>
                      <input id="cc-date" type="date" value={date} onChange={(e) => setDate(e.target.value)} />
                    </div>
                    <div className={"cc-field" + (triedSubmit && !timeSlot ? " is-error" : "")}>
                      <label htmlFor="cc-slot">Plage horaire <span className="cc-req">*</span></label>
                      <select id="cc-slot" value={timeSlot} onChange={(e) => setTimeSlot(e.target.value)}>
                        <option value="">Choisir…</option>
                        {slotOptions.map((s) => <option key={s} value={s}>{s}</option>)}
                      </select>
                    </div>
                    <div className={"cc-field" + (triedSubmit && !(String(guests).trim() && Number(guests) >= 1) ? " is-error" : "")}>
                      <label htmlFor="cc-guests">Nombre de personnes <span className="cc-req">*</span></label>
                      <input id="cc-guests" type="number" min="1" max={maxGuests || undefined} value={guests} onChange={(e) => setGuests(e.target.value)} placeholder={maxGuests ? `Max ${maxGuests}` : "Ex. 6"} />
                    </div>
                    <div className="cc-field is-full">
                      <label htmlFor="cc-msg">Message (optionnel)</label>
                      <textarea id="cc-msg" value={message} onChange={(e) => setMessage(e.target.value)} placeholder="Occasion spéciale, questions, besoins particuliers…" />
                    </div>
                  </div>
                  {triedSubmit && !formValid && (
                    <div className="cc-op-warning">
                      <span className="cc-warn-ic" aria-hidden="true">⚠️</span>
                      <span>Veuillez remplir tous les champs requis (*) avec une adresse email valide.</span>
                    </div>
                  )}
                </div>
              </div>

              {/* Colonne droite : récap + demande */}
              <aside className="cc-side">
                <div className="cc-summary">
                  <h2>{isSingle ? "Votre réservation" : "Votre sortie"}</h2>
                  {isSingle ? (
                    <React.Fragment>
                      <div className="ccs-row"><span>Bateau</span><b>{boats[0].name}</b></div>
                      {date && <div className="ccs-row"><span>Date</span><b>{formatDate(date)}</b></div>}
                      {timeSlot && <div className="ccs-row"><span>Plage horaire</span><b>{timeSlot}</b></div>}
                      {guests && <div className="ccs-row"><span>Personnes</span><b>{guests}</b></div>}
                      <div className="ccs-row"><span>Durée</span><b>{boats[0].durationHours || 4} heures</b></div>
                    </React.Fragment>
                  ) : (
                    <React.Fragment>
                      <div className="ccs-row"><span>Date</span><b>{formatDate(date)}</b></div>
                      <div className="ccs-row"><span>Plage horaire</span><b>{timeSlot || "Non renseignée"}</b></div>
                      <div className="ccs-row"><span>Région</span><b>{region || boats[0]?.region || "—"}</b></div>
                      <div className="ccs-row"><span>Zone de navigation</span><b>{navigationZones.join(", ") || "—"}</b></div>
                      {zones.length > 0 && (
                        <div className="ccs-row"><span>Point de rendez-vous</span><b>{zones[0].replace(/-/g, " ")}</b></div>
                      )}
                    </React.Fragment>
                  )}
                  <div className="ccs-row">
                    <span>Mode d'opération</span>
                    <b>{operationMode === "captain" ? "Capitaine demandé" : operationMode === "self" ? "Opération autonome — sur validation" : "À choisir"}</b>
                  </div>

                  <div className="cc-addons-sum">
                    <div className="cc-addons-sum-hd">Services demandés</div>
                    {selectedServices.length > 0 ? (
                      selectedServices.map(id => {
                        const a = SERVICES.find(s => s.id === id);
                        return (
                          <div key={id} className="cc-addons-sum-row">
                            <span>{a.title}</span>
                            <span className="sum-muted">Sur demande</span>
                          </div>
                        );
                      })
                    ) : (
                      <div className="cc-addons-sum-row"><span className="sum-muted">Aucun service additionnel sélectionné.</span></div>
                    )}
                  </div>

                  <div className="ccs-totals">
                    <div className="ccs-tot"><span>{isSingle ? "Prix estimé du bloc (4h)" : "Prix total estimé"}</span><span className="amount">{totalPrice} $</span></div>
                    <div className="ccs-tot is-big"><span>{isSingle ? "Dépôt estimé (25 %)" : "Dépôt total estimé"}</span><span className="amount">{totalDeposit} $</span></div>
                    <p className="ccs-estimate-note">Estimations hors taxes. Aucun montant n'est prélevé maintenant.</p>
                  </div>

                  <button type="button" className={"ccs-cta" + (canSubmit ? "" : " is-inactive")} onClick={handleReservationRequestSubmit} aria-disabled={canSubmit ? "false" : "true"}>
                    Envoyer la demande de réservation <I.arrowR width="18" />
                  </button>
                  {!canSubmit && (
                    <p className="cc-cta-hint">{!operationReady
                      ? (operationMode === "self"
                          ? "Cochez tous les critères d'admissibilité, ou demandez un capitaine, pour continuer."
                          : "Choisissez un mode d'opération pour continuer.")
                      : "Complétez vos informations pour envoyer la demande."}</p>
                  )}
                </div>

                <div className="cc-note">
                  <strong>Aucun paiement maintenant</strong>
                  {isSingle
                    ? "L'envoi de cette demande est gratuit et ne réserve pas encore le bateau. Les montants ci-dessus sont des estimations. L'équipe BoatsFun confirme d'abord la disponibilité ; le dépôt et le solde sont traités plus tard, selon les conditions de réservation."
                    : "L'envoi de cette demande est gratuit et ne réserve pas encore les bateaux. Les montants ci-dessus sont des estimations. L'équipe BoatsFun confirme d'abord la disponibilité auprès de chaque propriétaire ; le dépôt et le solde sont traités plus tard, selon les conditions de réservation."}
                </div>
              </aside>
            </div>
          )}
        </main>

        <window.BFFooter />
      </div>
    );
  }

  window.BFPageCheckoutCombine = BFPageCheckoutCombine;
})();
