{"id":11,"date":"2025-12-20T22:14:45","date_gmt":"2025-12-20T22:14:45","guid":{"rendered":"https:\/\/boat.wesolution.fr\/?page_id=11"},"modified":"2026-01-03T12:09:01","modified_gmt":"2026-01-03T12:09:01","slug":"calculateur-dautonomie","status":"publish","type":"page","link":"https:\/\/boat.wesolution.fr\/","title":{"rendered":"Calculateur d&rsquo;autonomie"},"content":{"rendered":"\n<!doctype html>\n<html lang=\"fr\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>Calculateur d&rsquo;autonomie \u2014 Donn\u00e9es d&rsquo;entr\u00e9e<\/title>\n\n  <style>\n    :root{\n      --bg: #0b1220;\n      --card: rgba(255,255,255,0.06);\n      --card2: rgba(255,255,255,0.08);\n      --stroke: rgba(255,255,255,0.12);\n      --text: rgba(255,255,255,0.92);\n      --muted: rgba(255,255,255,0.62);\n      --accent: #7dd3fc;\n      --accent2:#a78bfa;\n      --ok:#34d399;\n      --radius: 18px;\n      --shadow: 0 18px 60px rgba(0,0,0,0.45);\n    }\n\n    *{ box-sizing: border-box; }\n    body{\n      margin:0;\n      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\",\"Segoe UI Emoji\";\n      background: radial-gradient(1200px 600px at 15% 10%, rgba(125,211,252,0.18), transparent 55%),\n                  radial-gradient(900px 500px at 85% 25%, rgba(167,139,250,0.16), transparent 55%),\n                  radial-gradient(800px 600px at 50% 90%, rgba(52,211,153,0.10), transparent 60%),\n                  var(--bg);\n      color: var(--text);\n      min-height: 100vh;\n      padding: 28px;\n    }\n\n    .wrap{ max-width: 1180px; margin: 0 auto; }\n\n    .header{\n      display:flex;\n      align-items:flex-end;\n      justify-content:space-between;\n      gap: 16px;\n      margin-bottom: 18px;\n    }\n\n    .title{ display:flex; flex-direction:column; gap:6px; }\n    .title h1{\n      margin:0;\n      font-size: clamp(22px, 2.5vw, 32px);\n      letter-spacing: -0.02em;\n      line-height: 1.1;\n    }\n    .title p{ margin:0; color: var(--muted); font-size: 14px; }\n\n    .pill{\n      display:inline-flex;\n      align-items:center;\n      gap:10px;\n      padding:10px 12px;\n      background: linear-gradient(90deg, rgba(125,211,252,0.14), rgba(167,139,250,0.12));\n      border: 1px solid var(--stroke);\n      border-radius: 999px;\n      color: var(--text);\n      font-size: 13px;\n      backdrop-filter: blur(10px);\n      box-shadow: 0 10px 28px rgba(0,0,0,0.25);\n      white-space: nowrap;\n    }\n    .dot{\n      width:10px;height:10px;border-radius:50%;\n      background: var(--ok);\n      box-shadow: 0 0 0 6px rgba(52,211,153,0.14);\n      flex:0 0 auto;\n    }\n\n    \/* \u2705 50% \/ 50% pour la zone du haut (jusqu\u2019\u00e0 \u201c\u00c9tat de la coque\u201d) *\/\n    .gridTop{\n      display:grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 18px;\n      align-items: stretch;\n    }\n\n    \/* \u2705 pleine largeur pour la vitesse *\/\n    .fullRow{\n      margin-top: 18px;\n    }\n\n    .card{\n      background: linear-gradient(180deg, var(--card), rgba(255,255,255,0.04));\n      border: 1px solid var(--stroke);\n      border-radius: var(--radius);\n      box-shadow: var(--shadow);\n      overflow: hidden;\n      position: relative;\n      min-width: 0;\n    }\n\n    .card .inner{ padding: 18px; }\n\n    .card h2{\n      margin:0 0 12px 0;\n      font-size: 16px;\n      letter-spacing: -0.01em;\n      color: rgba(255,255,255,0.92);\n    }\n\n    .form{ display:grid; gap: 14px; }\n\n    .field{\n      display:grid;\n      gap: 8px;\n      padding: 14px;\n      border-radius: 14px;\n      background: rgba(255,255,255,0.04);\n      border: 1px solid rgba(255,255,255,0.10);\n    }\n\n    .labelRow{\n      display:flex;\n      align-items:center;\n      justify-content:space-between;\n      gap:10px;\n    }\n\n    label{ font-size: 13px; color: rgba(255,255,255,0.86); }\n\n    select, input[type=\"range\"]{ width: 100%; }\n\n    select{\n      appearance: none;\n      background: rgba(0,0,0,0.18);\n      color: var(--text);\n      border: 1px solid rgba(255,255,255,0.14);\n      border-radius: 12px;\n      padding: 12px 12px;\n      outline: none;\n      font-size: 14px;\n    }\n    select:focus{\n      border-color: rgba(125,211,252,0.55);\n      box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n    }\n\n    .selectWrap{ position: relative; }\n    .selectWrap:after{\n      content:\"\u25be\";\n      position:absolute;\n      right:12px;\n      top:50%;\n      transform: translateY(-50%);\n      color: rgba(255,255,255,0.65);\n      pointer-events:none;\n      font-size: 14px;\n    }\n\n    .badge{\n      font-size: 12px;\n      padding: 6px 10px;\n      border-radius: 999px;\n      border: 1px solid rgba(255,255,255,0.14);\n      background: rgba(0,0,0,0.20);\n      color: rgba(255,255,255,0.88);\n      white-space: nowrap;\n    }\n\n    input[type=\"range\"]{\n      -webkit-appearance: none;\n      appearance: none;\n      height: 6px;\n      border-radius: 999px;\n      background: linear-gradient(90deg, rgba(125,211,252,0.85), rgba(167,139,250,0.75));\n      outline:none;\n    }\n    input[type=\"range\"]::-webkit-slider-thumb{\n      -webkit-appearance: none;\n      appearance: none;\n      width: 18px;\n      height: 18px;\n      border-radius: 50%;\n      background: rgba(255,255,255,0.92);\n      border: 2px solid rgba(0,0,0,0.35);\n      box-shadow: 0 10px 25px rgba(0,0,0,0.35);\n      cursor: pointer;\n    }\n    input[type=\"range\"]::-moz-range-thumb{\n      width: 18px;\n      height: 18px;\n      border-radius: 50%;\n      background: rgba(255,255,255,0.92);\n      border: 2px solid rgba(0,0,0,0.35);\n      box-shadow: 0 10px 25px rgba(0,0,0,0.35);\n      cursor: pointer;\n    }\n\n    \/* Vitesse (pleine largeur) *\/\n    .speedGrid{\n      display:grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 10px;\n      align-items:end;\n    }\n\n    \/* \u2705 Image card: elle s\u2019\u00e9tire exactement \u00e0 la hauteur de la carte de gauche *\/\n    .imageCard{\n      display:flex;\n      flex-direction:column;\n      min-height: 100%;\n    }\n\n    \/* \u2705 Plus de min-height \u201c\u00e9norme\u201d ici : la hauteur vient de la grille *\/\n    .hero{\n      flex: 1;\n      background:\n        radial-gradient(700px 240px at 20% 10%, rgba(125,211,252,0.20), transparent 60%),\n        radial-gradient(700px 240px at 80% 25%, rgba(167,139,250,0.18), transparent 60%),\n        rgba(255,255,255,0.03);\n      border-top: 1px solid rgba(255,255,255,0.10);\n      overflow:hidden;\n      display:flex;\n      align-items:center;\n      justify-content:center;\n      padding: 14px;\n    }\n\n    .hero img{\n      width: 100%;\n      height: 100%;\n      object-fit: contain;\n      object-position: center;\n      border-radius: 14px;\n      border: 1px solid rgba(255,255,255,0.12);\n      display:none;\n      transform: scale(1.04);\n    }\n\n    .placeholder{\n      border: 1px dashed rgba(255,255,255,0.22);\n      border-radius: 14px;\n      width: 100%;\n      height: 100%;\n      display:flex;\n      align-items:center;\n      justify-content:center;\n      color: rgba(255,255,255,0.70);\n      background: rgba(0,0,0,0.16);\n      text-align:center;\n      padding: 16px;\n      min-height: 180px;\n    }\n    .placeholder strong{\n      display:block;\n      color: rgba(255,255,255,0.92);\n      font-size: 14px;\n      margin-bottom: 6px;\n    }\n    .placeholder span{\n      display:block;\n      font-size: 12px;\n      color: var(--muted);\n      line-height: 1.45;\n    }\n\n    .actions{\n      display:flex;\n      gap: 10px;\n      margin-top: 14px;\n      flex-wrap:wrap;\n    }\n    .btn{\n      border: 1px solid rgba(255,255,255,0.14);\n      background: rgba(0,0,0,0.20);\n      color: rgba(255,255,255,0.92);\n      padding: 10px 12px;\n      border-radius: 12px;\n      cursor:pointer;\n      font-size: 13px;\n      transition: transform .05s ease, border-color .2s ease, background .2s ease;\n      user-select:none;\n    }\n    .btn:hover{\n      border-color: rgba(125,211,252,0.55);\n      background: rgba(125,211,252,0.10);\n    }\n    .btn:active{ transform: translateY(1px); }\n\n    @media (max-width: 980px){\n      body{ padding: 18px; }\n      .gridTop{ grid-template-columns: 1fr; }\n      .speedGrid{ grid-template-columns: 1fr; }\n      .hero{ min-height: 260px; }\n    }\n  <\/style>\n<\/head>\n\n<body>\n  <div class=\"wrap\">\n    <div class=\"header\">\n      <div class=\"title\">\n        <h1>Calculateur d\u2019autonomie \u2014 Donn\u00e9es d\u2019entr\u00e9e<\/h1>\n        <p>Renseigne le bateau + l\u2019\u00e9tat r\u00e9el (h\u00e9lice \/ coque) et une vitesse.<\/p>\n      <\/div>\n\n      <div class=\"pill\">\n        <span class=\"dot\"><\/span>\n        <span>Mode d\u00e9mo \u2014 aucune donn\u00e9e envoy\u00e9e<\/span>\n      <\/div>\n    <\/div>\n\n    <!-- \u2705 LIGNE DU HAUT : jusqu\u2019\u00e0 \u201c\u00c9tat de la coque\u201d + image \u00e0 droite -->\n    <div class=\"gridTop\">\n      <section class=\"card\">\n        <div class=\"inner\">\n          <h2>1) Param\u00e8tres utilisateur<\/h2>\n\n          <div class=\"form\">\n            <div class=\"field\">\n              <div class=\"labelRow\">\n                <label for=\"boatBrand\">Marque<\/label>\n              <\/div>\n              <div class=\"selectWrap\">\n                <select id=\"boatBrand\" disabled>\n                  <option>Chargement\u2026<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n\n            <div class=\"field\">\n              <div class=\"labelRow\">\n                <label for=\"boatModel\">Mod\u00e8le du bateau<\/label>\n              <\/div>\n              <div class=\"selectWrap\">\n                <select id=\"boatModel\" disabled>\n                  <option>Choisis une marque\u2026<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n\n            <div class=\"field\">\n              <div class=\"labelRow\">\n                <label for=\"propWear\">Usure de l\u2019h\u00e9lice<\/label>\n                <span class=\"badge\"><span id=\"propWearVal\">0<\/span>%<\/span>\n              <\/div>\n              <input id=\"propWear\" type=\"range\" min=\"0\" max=\"100\" value=\"0\" \/>\n            <\/div>\n\n            <div class=\"field\">\n              <div class=\"labelRow\">\n                <label for=\"hullState\">\u00c9tat de la coque (antifouling)<\/label>\n              <\/div>\n              <div class=\"selectWrap\">\n                <select id=\"hullState\">\n                  <option value=\"antifouling_yes\" selected>Oui (traitement antifouling)<\/option>\n                  <option value=\"antifouling_no\">Non (pas d\u2019antifouling)<\/option>\n                <\/select>\n              <\/div>\n            <\/div>\n\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <aside class=\"card imageCard\">\n        <div class=\"inner\">\n          <h2>2) Visuel \/ Sch\u00e9ma du bateau<\/h2>\n        <\/div>\n\n        <div class=\"hero\">\n          <img id=\"boatImage\" alt=\"Bateau\">\n          <div class=\"placeholder\" id=\"imgPlaceholder\">\n            <div>\n              <strong>Image du bateau (\u00e0 ajouter)<\/strong>\n              <span>Tu mettras l\u2019URL plus tard.<\/span>\n            <\/div>\n          <\/div>\n        <\/div>\n      <\/aside>\n    <\/div>\n\n    <!-- \u2705 LIGNE DU BAS : vitesse pleine largeur -->\n    <section class=\"card fullRow\">\n      <div class=\"inner\">\n        <h2>3) Vitesse<\/h2>\n\n        <div class=\"form\">\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label>Vitesse du bateau<\/label>\n              <span class=\"badge\"><span id=\"speedVal\">10<\/span> <span id=\"speedUnitLabel\">kn<\/span><\/span>\n            <\/div>\n\n            <div class=\"speedGrid\">\n              <div>\n                <div class=\"selectWrap\">\n                  <select id=\"speedUnit\">\n                    <option value=\"kn\" selected>Noeuds (kn)<\/option>\n                    <option value=\"kmh\">km\/h<\/option>\n                  <\/select>\n                <\/div>\n              <\/div>\n\n              <div>\n                <div class=\"selectWrap\">\n                  <select id=\"speedValue\"><\/select>\n                <\/div>\n              <\/div>\n            <\/div>\n\n            <div class=\"actions\">\n              <button class=\"btn\" id=\"btnReset\" type=\"button\">R\u00e9initialiser<\/button>\n            <\/div>\n          <\/div>\n        <\/div>\n\n      <\/div>\n    <\/section>\n\n  <\/div>\n\n  <script>\n    const AJAX_URL = \"\/wp-admin\/admin-ajax.php\";\n\n    async function ajaxGet(action, params = {}) {\n      const url = new URL(AJAX_URL, window.location.origin);\n      url.searchParams.set(\"action\", action);\n      Object.entries(params).forEach(([k,v]) => url.searchParams.set(k, v));\n      const res = await fetch(url.toString(), { cache: \"no-store\" });\n      return res.json();\n    }\n\n    \/\/ Remplir la liste vitesse 1..30\n    const speedValueEl = document.getElementById(\"speedValue\");\n    for (let i = 1; i <= 30; i++) {\n      const opt = document.createElement(\"option\");\n      opt.value = String(i);\n      opt.textContent = String(i);\n      if (i === 10) opt.selected = true;\n      speedValueEl.appendChild(opt);\n    }\n\n    const els = {\n      boatBrand: document.getElementById(\"boatBrand\"),\n      boatModel: document.getElementById(\"boatModel\"),\n      propWear: document.getElementById(\"propWear\"),\n      propWearVal: document.getElementById(\"propWearVal\"),\n      hullState: document.getElementById(\"hullState\"),\n      speedUnit: document.getElementById(\"speedUnit\"),\n      speedValue: document.getElementById(\"speedValue\"),\n      speedVal: document.getElementById(\"speedVal\"),\n      speedUnitLabel: document.getElementById(\"speedUnitLabel\"),\n      boatImage: document.getElementById(\"boatImage\"),\n      imgPlaceholder: document.getElementById(\"imgPlaceholder\"),\n      btnReset: document.getElementById(\"btnReset\"),\n    };\n\n    function updateBoatImageByModel(model) {\n      const m = (model || \"\").toLowerCase().trim();\n      if (m === \"ariston\") {\n        setBoatImage(\"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/ariston.png\");\n      } else if (m === \"solo ruff 24\") {\n        setBoatImage(\"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/solo_ruff_24.png\");\n      }\n    }\n\n    function updateUI(){\n      const brand = els.boatBrand.value || \"\";\n      const model = els.boatModel.value || \"\";\n      const wear = Number(els.propWear.value);\n      const hull = els.hullState.value;\n      const unit = els.speedUnit.value;\n      const speed = Number(els.speedValue.value);\n\n      els.propWearVal.textContent = String(wear);\n      els.speedVal.textContent = String(speed);\n      els.speedUnitLabel.textContent = unit;\n\n      updateBoatImageByModel(model);\n\n      window.BOAT_INPUTS = {\n        brand,\n        model,\n        propeller_wear_percent: wear,\n        hull_antifouling: (hull === \"antifouling_yes\"),\n        speed_value: speed,\n        speed_unit: unit\n      };\n    }\n\n    window.setBoatImage = function(url){\n      if (!url) return;\n      els.boatImage.src = url;\n      els.boatImage.style.display = \"block\";\n      els.imgPlaceholder.style.display = \"none\";\n    };\n\n    async function loadBrandsAndFirstModels(){\n      els.boatBrand.disabled = true;\n      els.boatModel.disabled = true;\n\n      const brandsResp = await ajaxGet(\"boats_get_brands\");\n      if (!brandsResp || !brandsResp.ok) {\n        els.boatBrand.innerHTML = \"<option>Erreur chargement marques<\/option>\";\n        return;\n      }\n\n      els.boatBrand.innerHTML = \"\";\n      brandsResp.brands.forEach(b => {\n        const o = document.createElement(\"option\");\n        o.value = b;\n        o.textContent = b;\n        els.boatBrand.appendChild(o);\n      });\n\n      els.boatBrand.disabled = false;\n\n      const firstBrand = els.boatBrand.value;\n      await loadModelsForBrand(firstBrand);\n    }\n\n    async function loadModelsForBrand(brand){\n      els.boatModel.disabled = true;\n      els.boatModel.innerHTML = \"<option>Chargement\u2026<\/option>\";\n\n      const modelsResp = await ajaxGet(\"boats_get_models\", { brand });\n      if (!modelsResp || !modelsResp.ok) {\n        els.boatModel.innerHTML = \"<option>Erreur chargement mod\u00e8les<\/option>\";\n        return;\n      }\n\n      els.boatModel.innerHTML = \"\";\n      modelsResp.models.forEach(m => {\n        const o = document.createElement(\"option\");\n        o.value = m;\n        o.textContent = m;\n        els.boatModel.appendChild(o);\n      });\n\n      els.boatModel.disabled = false;\n      updateUI();\n    }\n\n    [\"change\",\"input\"].forEach(evt => {\n      els.boatModel.addEventListener(evt, updateUI);\n      els.propWear.addEventListener(evt, updateUI);\n      els.hullState.addEventListener(evt, updateUI);\n      els.speedUnit.addEventListener(evt, updateUI);\n      els.speedValue.addEventListener(evt, updateUI);\n    });\n\n    els.boatBrand.addEventListener(\"change\", async () => {\n      await loadModelsForBrand(els.boatBrand.value);\n    });\n\n    els.btnReset.addEventListener(\"click\", async () => {\n      els.propWear.value = \"0\";\n      els.hullState.value = \"antifouling_yes\";\n      els.speedUnit.value = \"kn\";\n      els.speedValue.value = \"10\";\n      await loadBrandsAndFirstModels();\n      updateUI();\n    });\n\n    (async function init(){\n      await loadBrandsAndFirstModels();\n      updateUI();\n    })();\n  <\/script>\n<\/body>\n<\/html>\n\n\n\n<!doctype html>\n<html lang=\"fr\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>Environnement de navigation<\/title>\n\n  <style>\n    :root{\n      --bg: #0b1220;\n      --card: rgba(255,255,255,0.06);\n      --stroke: rgba(255,255,255,0.12);\n      --text: rgba(255,255,255,0.92);\n      --muted: rgba(255,255,255,0.62);\n      --accent: #7dd3fc;\n      --accent2:#a78bfa;\n      --ok:#34d399;\n      --radius: 18px;\n      --shadow: 0 18px 60px rgba(0,0,0,0.45);\n    }\n\n    *{ box-sizing: border-box; }\n    body{\n      margin:0;\n      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\",\"Segoe UI Emoji\";\n      background: radial-gradient(1200px 600px at 15% 10%, rgba(125,211,252,0.18), transparent 55%),\n                  radial-gradient(900px 500px at 85% 25%, rgba(167,139,250,0.16), transparent 55%),\n                  radial-gradient(800px 600px at 50% 90%, rgba(52,211,153,0.10), transparent 60%),\n                  var(--bg);\n      color: var(--text);\n      min-height: 100vh;\n      padding: 28px;\n    }\n\n    .wrap{\n      max-width: 1180px;\n      margin: 0 auto;\n    }\n\n    .header{\n      display:flex;\n      align-items:flex-end;\n      justify-content:space-between;\n      gap: 16px;\n      margin-bottom: 18px;\n    }\n\n    .title{\n      display:flex;\n      flex-direction:column;\n      gap:6px;\n    }\n    .title h1{\n      margin:0;\n      font-size: clamp(22px, 2.5vw, 32px);\n      letter-spacing: -0.02em;\n      line-height: 1.1;\n    }\n    .title p{\n      margin:0;\n      color: var(--muted);\n      font-size: 14px;\n    }\n\n    .pill{\n      display:inline-flex;\n      align-items:center;\n      gap:10px;\n      padding:10px 12px;\n      background: linear-gradient(90deg, rgba(125,211,252,0.14), rgba(167,139,250,0.12));\n      border: 1px solid var(--stroke);\n      border-radius: 999px;\n      color: var(--text);\n      font-size: 13px;\n      backdrop-filter: blur(10px);\n      box-shadow: 0 10px 28px rgba(0,0,0,0.25);\n      white-space: nowrap;\n    }\n    .dot{\n      width:10px;height:10px;border-radius:50%;\n      background: var(--ok);\n      box-shadow: 0 0 0 6px rgba(52,211,153,0.14);\n      flex:0 0 auto;\n    }\n\n    .card{\n      background: linear-gradient(180deg, var(--card), rgba(255,255,255,0.04));\n      border: 1px solid var(--stroke);\n      border-radius: var(--radius);\n      box-shadow: var(--shadow);\n      overflow: hidden;\n      position: relative;\n    }\n\n    .card .inner{\n      padding: 18px;\n    }\n\n    .card h2{\n      margin:0 0 12px 0;\n      font-size: 16px;\n      letter-spacing: -0.01em;\n      color: rgba(255,255,255,0.92);\n    }\n\n    .form{\n      display:grid;\n      gap: 14px;\n    }\n\n    .field{\n      display:grid;\n      gap: 8px;\n      padding: 14px;\n      border-radius: 14px;\n      background: rgba(255,255,255,0.04);\n      border: 1px solid rgba(255,255,255,0.10);\n    }\n\n    .labelRow{\n      display:flex;\n      align-items:center;\n      justify-content:space-between;\n      gap:10px;\n    }\n\n    label{\n      font-size: 13px;\n      color: rgba(255,255,255,0.86);\n    }\n\n    .hint{\n      font-size: 12px;\n      color: var(--muted);\n    }\n\n    select, input[type=\"range\"]{ width: 100%; }\n\n    select{\n      appearance: none;\n      background: rgba(0,0,0,0.18);\n      color: var(--text);\n      border: 1px solid rgba(255,255,255,0.14);\n      border-radius: 12px;\n      padding: 12px 12px;\n      outline: none;\n      font-size: 14px;\n    }\n    select:focus{\n      border-color: rgba(125,211,252,0.55);\n      box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n    }\n\n    .selectWrap{ position: relative; }\n    .selectWrap:after{\n      content:\"\u25be\";\n      position:absolute;\n      right:12px;\n      top:50%;\n      transform: translateY(-50%);\n      color: rgba(255,255,255,0.65);\n      pointer-events:none;\n      font-size: 14px;\n    }\n\n    \/* Slider *\/\n    .badge{\n      font-size: 12px;\n      padding: 6px 10px;\n      border-radius: 999px;\n      border: 1px solid rgba(255,255,255,0.14);\n      background: rgba(0,0,0,0.20);\n      color: rgba(255,255,255,0.88);\n      white-space: nowrap;\n    }\n\n    input[type=\"range\"]{\n      -webkit-appearance: none;\n      appearance: none;\n      height: 6px;\n      border-radius: 999px;\n      background: linear-gradient(90deg, rgba(125,211,252,0.85), rgba(167,139,250,0.75));\n      outline:none;\n    }\n    input[type=\"range\"]::-webkit-slider-thumb{\n      -webkit-appearance: none;\n      appearance: none;\n      width: 18px; height: 18px;\n      border-radius: 50%;\n      background: rgba(255,255,255,0.92);\n      border: 2px solid rgba(0,0,0,0.35);\n      box-shadow: 0 10px 25px rgba(0,0,0,0.35);\n      cursor: pointer;\n    }\n    input[type=\"range\"]::-moz-range-thumb{\n      width: 18px; height: 18px;\n      border-radius: 50%;\n      background: rgba(255,255,255,0.92);\n      border: 2px solid rgba(0,0,0,0.35);\n      box-shadow: 0 10px 25px rgba(0,0,0,0.35);\n      cursor: pointer;\n    }\n\n    .mini{\n      font-size: 12px;\n      color: var(--muted);\n      margin-top: -2px;\n    }\n\n    .actions{\n      display:flex;\n      gap: 10px;\n      margin-top: 6px;\n      flex-wrap:wrap;\n    }\n    .btn{\n      border: 1px solid rgba(255,255,255,0.14);\n      background: rgba(0,0,0,0.20);\n      color: rgba(255,255,255,0.92);\n      padding: 10px 12px;\n      border-radius: 12px;\n      cursor:pointer;\n      font-size: 13px;\n      transition: transform .05s ease, border-color .2s ease, background .2s ease;\n      user-select:none;\n    }\n    .btn:hover{\n      border-color: rgba(125,211,252,0.55);\n      background: rgba(125,211,252,0.10);\n    }\n    .btn:active{ transform: translateY(1px); }\n    .btnPrimary{\n      background: linear-gradient(90deg, rgba(125,211,252,0.22), rgba(167,139,250,0.20));\n      border-color: rgba(255,255,255,0.18);\n    }\n\n    .riverOnly{ display:none; }\n    .riverOnly.show{ display:grid; }\n\n    @media (max-width: 980px){\n      body{ padding: 18px; }\n    }\n  <\/style>\n<\/head>\n\n<body>\n  <div class=\"wrap\">\n    <div class=\"header\">\n      <div class=\"title\">\n        <h1>Donn\u00e9es d\u2019entr\u00e9e \u2014 Environnement de navigation<\/h1>\n        <p>Quelques choix simples pour adapter les hypoth\u00e8ses (courant, m\u00e9t\u00e9o, style de conduite).<\/p>\n      <\/div>\n\n      <div class=\"pill\">\n        <span class=\"dot\"><\/span>\n        <span>Mode d\u00e9mo \u2014 aucune donn\u00e9e envoy\u00e9e<\/span>\n      <\/div>\n    <\/div>\n\n    <section class=\"card\">\n      <div class=\"inner\">\n        <h2>Param\u00e8tres environnement<\/h2>\n\n        <div class=\"form\">\n          <!-- Type de navigation -->\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"navType\">Type de navigation<\/label>\n              <span class=\"hint\">Liste d\u00e9roulante<\/span>\n            <\/div>\n            <div class=\"selectWrap\">\n              <select id=\"navType\">\n                <option value=\"inland\" selected>Lac \/ Fleuve<\/option>\n                <option value=\"sea\">Mer \/ Oc\u00e9an<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n\n          <!-- Type de conduite -->\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"driveType\">Type de conduite<\/label>\n              <span class=\"hint\">Liste d\u00e9roulante<\/span>\n            <\/div>\n            <div class=\"selectWrap\">\n              <select id=\"driveType\">\n                <option value=\"leisure\" selected>Navigation de plaisance<\/option>\n                <option value=\"pro\">Navigation professionnelle<\/option>\n                <option value=\"sport\">Conduite sportive<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n\n          <!-- M\u00e9t\u00e9o -->\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"meteo\">Conditions m\u00e9t\u00e9o habituelles<\/label>\n              <span class=\"hint\">Liste d\u00e9roulante<\/span>\n            <\/div>\n            <div class=\"selectWrap\">\n              <select id=\"meteo\">\n                <option value=\"fair\" selected>Navigation par beau temps<\/option>\n                <option value=\"degraded\">D\u00e9grad\u00e9es (vent, houle)<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n\n          <!-- Courant (fleuve uniquement) -->\n          <div class=\"field riverOnly\" id=\"riverBlock\">\n            <div class=\"labelRow\">\n              <label for=\"currentSpeed\">Vitesse du courant (fleuve)<\/label>\n              <span class=\"badge\"><span id=\"currentVal\">0.0<\/span> kn<\/span>\n            <\/div>\n            <input id=\"currentSpeed\" type=\"range\" min=\"0\" max=\"6\" step=\"0.1\" value=\"0\" \/>\n            <div class=\"mini\">Affich\u00e9 uniquement si \u201cLac \/ Fleuve\u201d.<\/div>\n          <\/div>\n\n          <div class=\"actions\">\n            <button class=\"btn btnPrimary\" id=\"btnContinue\">Continuer (\u00e9tape suivante)<\/button>\n            <button class=\"btn\" id=\"btnReset\">R\u00e9initialiser<\/button>\n            <button class=\"btn\" id=\"btnExport\">Exporter JSON<\/button>\n          <\/div>\n        <\/div>\n      <\/div>\n    <\/section>\n  <\/div>\n\n  <script>\n    const els = {\n      navType: document.getElementById(\"navType\"),\n      driveType: document.getElementById(\"driveType\"),\n      meteo: document.getElementById(\"meteo\"),\n      riverBlock: document.getElementById(\"riverBlock\"),\n      currentSpeed: document.getElementById(\"currentSpeed\"),\n      currentVal: document.getElementById(\"currentVal\"),\n\n      btnReset: document.getElementById(\"btnReset\"),\n      btnExport: document.getElementById(\"btnExport\"),\n      btnContinue: document.getElementById(\"btnContinue\"),\n    };\n\n    function toggleRiver(){\n      const isInland = els.navType.value === \"inland\";\n      els.riverBlock.classList.toggle(\"show\", isInland);\n      if (!isInland){\n        els.currentSpeed.value = \"0\";\n        els.currentVal.textContent = \"0.0\";\n      }\n    }\n\n    function updateUI(){\n      toggleRiver();\n      const navType = els.navType.value;      \/\/ inland | sea\n      const driveType = els.driveType.value;  \/\/ leisure | pro | sport\n      const meteo = els.meteo.value;          \/\/ fair | degraded\n      const currentKn = Number(els.currentSpeed.value);\n\n      els.currentVal.textContent = currentKn.toFixed(1);\n\n      \/\/ Export simple pour brancher la suite\n      window.NAV_ENV = {\n        navigation_type: navType,\n        driving_style: driveType,\n        typical_weather: meteo,\n        river_current_kn: (navType === \"inland\") ? currentKn : 0\n      };\n    }\n\n    [\"change\",\"input\"].forEach(evt => {\n      els.navType.addEventListener(evt, updateUI);\n      els.driveType.addEventListener(evt, updateUI);\n      els.meteo.addEventListener(evt, updateUI);\n      els.currentSpeed.addEventListener(evt, updateUI);\n    });\n\n    els.btnReset.addEventListener(\"click\", () => {\n      els.navType.value = \"inland\";\n      els.driveType.value = \"leisure\";\n      els.meteo.value = \"fair\";\n      els.currentSpeed.value = \"0\";\n      updateUI();\n    });\n\n    els.btnExport.addEventListener(\"click\", async () => {\n      updateUI();\n      const data = window.NAV_ENV;\n\n      try {\n        await navigator.clipboard.writeText(JSON.stringify(data, null, 2));\n        alert(\"JSON copi\u00e9 dans le presse-papier \u2705\");\n      } catch (e) {\n        alert(\"Voici le JSON (copie manuelle) :\\n\\n\" + JSON.stringify(data, null, 2));\n      }\n    });\n\n    els.btnContinue.addEventListener(\"click\", () => {\n      updateUI();\n      alert(\"OK \u2705 Environnement enregistr\u00e9.\");\n    });\n\n    updateUI();\n  <\/script>\n<\/body>\n<\/html>\n\n\n\n<!doctype html>\n<html lang=\"fr\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>Calculateur d&rsquo;autonomie \u2014 Trajet<\/title>\n\n  <!-- Leaflet + Draw -->\n  <link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\">\n  <link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet-draw@1.0.4\/dist\/leaflet.draw.css\">\n  <script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\n  <script src=\"https:\/\/unpkg.com\/leaflet-draw@1.0.4\/dist\/leaflet.draw.js\"><\/script>\n\n  <style>\n    :root{\n      --bg: #0b1220;\n      --card: rgba(255,255,255,0.06);\n      --card2: rgba(255,255,255,0.08);\n      --stroke: rgba(255,255,255,0.12);\n      --text: rgba(255,255,255,0.92);\n      --muted: rgba(255,255,255,0.62);\n      --accent: #7dd3fc;\n      --accent2:#a78bfa;\n      --ok:#34d399;\n      --radius: 18px;\n      --shadow: 0 18px 60px rgba(0,0,0,0.45);\n    }\n\n    *{ box-sizing:border-box; }\n    body{\n      margin:0;\n      font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial;\n      background: radial-gradient(1200px 600px at 15% 10%, rgba(125,211,252,0.18), transparent 55%),\n                  radial-gradient(900px 500px at 85% 25%, rgba(167,139,250,0.16), transparent 55%),\n                  radial-gradient(800px 600px at 50% 90%, rgba(52,211,153,0.10), transparent 60%),\n                  var(--bg);\n      color: var(--text);\n      min-height:100vh;\n      padding: 28px;\n    }\n\n    .wrap{ max-width: 1180px; margin: 0 auto; }\n\n    .header{\n      display:flex;\n      align-items:flex-end;\n      justify-content:space-between;\n      gap: 16px;\n      margin-bottom: 18px;\n    }\n\n    .title{ display:flex; flex-direction:column; gap:6px; }\n    .title h1{\n      margin:0;\n      font-size: clamp(22px, 2.5vw, 32px);\n      letter-spacing: -0.02em;\n      line-height: 1.1;\n    }\n    .title p{ margin:0; color: var(--muted); font-size: 14px; }\n\n    .pill{\n      display:inline-flex;\n      align-items:center;\n      gap:10px;\n      padding:10px 12px;\n      background: linear-gradient(90deg, rgba(125,211,252,0.14), rgba(167,139,250,0.12));\n      border: 1px solid var(--stroke);\n      border-radius: 999px;\n      color: var(--text);\n      font-size: 13px;\n      backdrop-filter: blur(10px);\n      box-shadow: 0 10px 28px rgba(0,0,0,0.25);\n      white-space: nowrap;\n    }\n    .dot{\n      width:10px;height:10px;border-radius:50%;\n      background: var(--ok);\n      box-shadow: 0 0 0 6px rgba(52,211,153,0.14);\n      flex:0 0 auto;\n    }\n\n    \/* \u2705 50% \/ 50% *\/\n    .grid{\n      display:grid;\n      grid-template-columns: 1fr 1fr;\n      gap: 18px;\n      align-items: stretch;\n    }\n\n    .card{\n      background: linear-gradient(180deg, var(--card), rgba(255,255,255,0.04));\n      border: 1px solid var(--stroke);\n      border-radius: var(--radius);\n      box-shadow: var(--shadow);\n      overflow: hidden;\n      position: relative;\n      min-width: 0;\n    }\n\n    .inner{ padding: 18px; }\n\n    h2{\n      margin:0 0 12px 0;\n      font-size: 16px;\n      letter-spacing: -0.01em;\n      color: rgba(255,255,255,0.92);\n    }\n\n    .field{\n      display:grid;\n      gap: 10px;\n      padding: 14px;\n      border-radius: 14px;\n      background: rgba(255,255,255,0.04);\n      border: 1px solid rgba(255,255,255,0.10);\n      margin-bottom: 12px;\n    }\n\n    label{ font-size: 13px; color: rgba(255,255,255,0.86); }\n\n    .row{ display:flex; gap:10px; align-items:center; }\n\n    input[type=\"text\"]{\n      width:100%;\n      appearance:none;\n      background: rgba(0,0,0,0.18);\n      color: var(--text);\n      border: 1px solid rgba(255,255,255,0.14);\n      border-radius: 12px;\n      padding: 12px 12px;\n      outline: none;\n      font-size: 14px;\n      min-width: 0;\n    }\n    input[type=\"text\"]:focus{\n      border-color: rgba(125,211,252,0.55);\n      box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n    }\n\n    .btn{\n      border: 1px solid rgba(255,255,255,0.14);\n      background: rgba(0,0,0,0.20);\n      color: rgba(255,255,255,0.92);\n      padding: 10px 12px;\n      border-radius: 12px;\n      cursor:pointer;\n      font-size: 13px;\n      transition: transform .05s ease, border-color .2s ease, background .2s ease;\n      user-select:none;\n      white-space:nowrap;\n    }\n    .btn:hover{\n      border-color: rgba(125,211,252,0.55);\n      background: rgba(125,211,252,0.10);\n    }\n    .btn:active{ transform: translateY(1px); }\n\n    .hint{ font-size: 12px; color: var(--muted); line-height: 1.45; margin: 0; }\n\n    .result{ display:grid; grid-template-columns: 1fr; gap: 10px; }\n\n    .kpi{\n      padding: 12px;\n      border-radius: 14px;\n      background: rgba(255,255,255,0.04);\n      border: 1px solid rgba(255,255,255,0.10);\n      display:flex;\n      justify-content:space-between;\n      align-items:baseline;\n      gap: 10px;\n    }\n    .kpi .k{ font-size: 12px; color: var(--muted); }\n    .kpi .v{\n      font-size: 16px;\n      color: rgba(255,255,255,0.92);\n      font-weight: 600;\n      letter-spacing: -0.01em;\n    }\n\n    \/* MAP CARD *\/\n    .mapShell{ display:flex; flex-direction:column; height: 100%; min-width: 0; }\n    .mapHeader{\n      padding: 14px 18px;\n      border-bottom: 1px solid rgba(255,255,255,0.10);\n      display:flex;\n      justify-content:space-between;\n      align-items:center;\n      gap: 10px;\n    }\n    .mapHeader .mini{ font-size: 12px; color: var(--muted); }\n\n    #boatLeafletMap{\n      height: 420px;\n      border-top: 1px solid rgba(255,255,255,0.10);\n    }\n\n    @media (max-width: 980px){\n      body{ padding: 18px; }\n      .grid{ grid-template-columns: 1fr; }\n      #boatLeafletMap{ height: 360px; }\n    }\n  <\/style>\n<\/head>\n\n<body>\n  <div class=\"wrap\">\n\n    <div class=\"header\">\n      <div class=\"title\">\n        <h1>Calculateur d\u2019autonomie \u2014 Trac\u00e9 du trajet<\/h1>\n        <p>Choisis un port, trace une ligne sur la carte, r\u00e9cup\u00e8re la distance (km) + une conso (kW).<\/p>\n      <\/div>\n\n      <div class=\"pill\">\n        <span class=\"dot\"><\/span>\n        <span>Exports : <code>window.TRIP_KM<\/code> \/ <code>window.TRIP_KW<\/code><\/span>\n      <\/div>\n    <\/div>\n\n    <div class=\"grid\">\n\n      <section class=\"card\">\n        <div class=\"inner\">\n          <h2>1) Port + R\u00e9sultat<\/h2>\n\n          <div class=\"field\">\n            <label for=\"boatSearchPort\">Port de d\u00e9part<\/label>\n            <div class=\"row\">\n              <input id=\"boatSearchPort\" type=\"text\" placeholder=\"Ex : Port de Nice\" \/>\n              <button class=\"btn\" id=\"boatSearchBtn\" type=\"button\">Rechercher<\/button>\n            <\/div>\n            <p class=\"hint\">La carte se centre automatiquement sur le meilleur r\u00e9sultat.<\/p>\n          <\/div>\n\n          <div class=\"field\">\n            <label>2) Itin\u00e9raire<\/label>\n            <p class=\"hint\">Utilise l\u2019outil <b>ligne<\/b> \u00e0 gauche de la carte. Tu peux modifier ou supprimer.<\/p>\n          <\/div>\n\n          <div class=\"field\">\n            <label>3) R\u00e9sultat<\/label>\n            <div class=\"result\">\n              <div class=\"kpi\">\n                <div class=\"k\">Distance<\/div>\n                <div class=\"v\"><span id=\"boatKm\">0.00<\/span> km<\/div>\n              <\/div>\n              <div class=\"kpi\">\n                <div class=\"k\">Consommation<\/div>\n                <div class=\"v\"><span id=\"boatKw\">0.0<\/span> kW<\/div>\n              <\/div>\n            <\/div>\n\n            <input type=\"hidden\" id=\"boatKmField\" value=\"0.00\">\n            <input type=\"hidden\" id=\"boatKwField\" value=\"0.0\">\n          <\/div>\n        <\/div>\n      <\/section>\n\n      <aside class=\"card mapShell\">\n        <div class=\"mapHeader\">\n          <div>\n            <div style=\"font-weight:600;\">Carte<\/div>\n            <div class=\"mini\">Trace avec l\u2019outil ligne (Leaflet Draw)<\/div>\n          <\/div>\n          <div class=\"mini\">Zoom : molette \/ doigts<\/div>\n        <\/div>\n        <div id=\"boatLeafletMap\"><\/div>\n      <\/aside>\n\n    <\/div>\n  <\/div>\n\n  <script>\n    (function(){\n      \/\/ \u2705 Guard par conteneur (pas global) -> OK m\u00eame si 2 cartes sur la page\n      const mapEl = document.getElementById(\"boatLeafletMap\");\n      if (!mapEl) return;\n      if (mapEl.dataset.inited === \"1\") return;\n      mapEl.dataset.inited = \"1\";\n\n      function setKm(km){\n        const txt = Number(km).toFixed(2);\n        document.getElementById(\"boatKm\").textContent = txt;\n        document.getElementById(\"boatKmField\").value = txt;\n        window.TRIP_KM = Number(km);\n      }\n\n      function setKw(kw){\n        const txt = (Math.round(Number(kw) * 10) \/ 10).toFixed(1);\n        document.getElementById(\"boatKw\").textContent = txt;\n        document.getElementById(\"boatKwField\").value = txt;\n        window.TRIP_KW = Number(kw);\n      }\n\n      function computeKwFromKm(km){\n        return km * 2.0;\n      }\n\n      window.TRIP_KM = window.TRIP_KM || 0;\n      window.TRIP_KW = window.TRIP_KW || 0;\n\n      const map = L.map(\"boatLeafletMap\").setView([46.2, 2.2], 6);\n      L.tileLayer(\"https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\n        maxZoom: 19,\n        attribution: \"&copy; OpenStreetMap\"\n      }).addTo(map);\n\n      const drawnItems = new L.FeatureGroup();\n      map.addLayer(drawnItems);\n\n      const drawControl = new L.Control.Draw({\n        edit: { featureGroup: drawnItems },\n        draw: {\n          polyline: true,\n          polygon: false,\n          rectangle: false,\n          circle: false,\n          circlemarker: false,\n          marker: false\n        }\n      });\n      map.addControl(drawControl);\n\n      function kmFromLatLngs(latlngs){\n        let meters = 0;\n        for (let i = 1; i < latlngs.length; i++){\n          meters += latlngs[i-1].distanceTo(latlngs[i]);\n        }\n        return meters \/ 1000;\n      }\n\n      function updateFromLayer(layer){\n        const km = kmFromLatLngs(layer.getLatLngs());\n        setKm(km);\n        setKw(computeKwFromKm(km));\n      }\n\n      map.on(L.Draw.Event.CREATED, function(e){\n        drawnItems.clearLayers();\n        drawnItems.addLayer(e.layer);\n        updateFromLayer(e.layer);\n      });\n\n      map.on(\"draw:edited\", function(e){\n        e.layers.eachLayer(layer => updateFromLayer(layer));\n      });\n\n      map.on(\"draw:deleted\", function(){\n        setKm(0);\n        setKw(0);\n      });\n\n      \/\/ \u2705 Marker local (pas window.__PORT_MARKER)\n      let portMarker = null;\n\n      async function searchPort(){\n        const query = document.getElementById(\"boatSearchPort\").value.trim();\n        if (!query) return;\n\n        const url = \"https:\/\/nominatim.openstreetmap.org\/search?format=json&q=\" + encodeURIComponent(query);\n\n        try{\n          const res = await fetch(url, { headers: { \"Accept-Language\":\"fr\" } });\n          const data = await res.json();\n          if (!data.length){\n            alert(\"Port non trouv\u00e9\");\n            return;\n          }\n          const lat = parseFloat(data[0].lat);\n          const lon = parseFloat(data[0].lon);\n          map.setView([lat, lon], 12);\n\n          if (portMarker) map.removeLayer(portMarker);\n          portMarker = L.marker([lat, lon]).addTo(map)\n            .bindPopup(data[0].display_name)\n            .openPopup();\n\n        }catch(e){\n          alert(\"Erreur lors de la recherche\");\n        }\n      }\n\n      document.getElementById(\"boatSearchBtn\").addEventListener(\"click\", searchPort);\n      document.getElementById(\"boatSearchPort\").addEventListener(\"keydown\", function(e){\n        if (e.key === \"Enter\") searchPort();\n      });\n\n      setTimeout(() => map.invalidateSize(), 300);\n    })();\n  <\/script>\n<\/body>\n<\/html>\n\n\n\n\n<!-- \u2705 BLOC BATTERIE (COLLER TEL QUEL DANS 1 SEUL BLOC WP:HTML) -->\n\n<style>\n  :root{\n    --bg: #0b1220;\n    --card: rgba(255,255,255,0.06);\n    --stroke: rgba(255,255,255,0.12);\n    --text: rgba(255,255,255,0.92);\n    --muted: rgba(255,255,255,0.62);\n    --radius: 18px;\n    --shadow: 0 18px 60px rgba(0,0,0,0.45);\n  }\n\n  \/* \u2705 wrap identique aux autres blocs *\/\n  .wrap1180{\n    max-width: 1180px;\n    margin: 18px auto 0;\n  }\n\n  \/* \u2705 style carte identique *\/\n  .card{\n    background: linear-gradient(180deg, var(--card), rgba(255,255,255,0.04));\n    border: 1px solid var(--stroke);\n    border-radius: var(--radius);\n    box-shadow: var(--shadow);\n    overflow: hidden;\n    position: relative;\n    min-width: 0;\n    color: var(--text);\n  }\n  .card .inner{ padding: 18px; }\n  .card h2{\n    margin:0 0 12px 0;\n    font-size: 16px;\n    letter-spacing: -0.01em;\n    color: rgba(255,255,255,0.92);\n  }\n\n  .form{ display:grid; gap: 14px; }\n  .field{\n    display:grid;\n    gap: 8px;\n    padding: 14px;\n    border-radius: 14px;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.10);\n  }\n  .labelRow{\n    display:flex;\n    align-items:center;\n    justify-content:space-between;\n    gap:10px;\n  }\n  label{ font-size: 13px; color: rgba(255,255,255,0.86); }\n\n  .badge{\n    font-size: 12px;\n    padding: 6px 10px;\n    border-radius: 999px;\n    border: 1px solid rgba(255,255,255,0.14);\n    background: rgba(0,0,0,0.20);\n    color: rgba(255,255,255,0.88);\n    white-space: nowrap;\n  }\n\n  .selectWrap{ position: relative; }\n  .selectWrap:after{\n    content:\"\u25be\";\n    position:absolute;\n    right:12px;\n    top:50%;\n    transform: translateY(-50%);\n    color: rgba(255,255,255,0.65);\n    pointer-events:none;\n    font-size: 14px;\n  }\n\n  select{\n    width:100%;\n    appearance: none;\n    background: rgba(0,0,0,0.18);\n    color: var(--text);\n    border: 1px solid rgba(255,255,255,0.14);\n    border-radius: 12px;\n    padding: 12px 12px;\n    outline: none;\n    font-size: 14px;\n  }\n  select:focus{\n    border-color: rgba(125,211,252,0.55);\n    box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n  }\n\n  .batteryInput{\n    width:100%;\n    appearance:none;\n    background: rgba(0,0,0,0.18);\n    color: var(--text);\n    border: 1px solid rgba(255,255,255,0.14);\n    border-radius: 12px;\n    padding: 12px 12px;\n    outline: none;\n    font-size: 14px;\n  }\n  .batteryInput:focus{\n    border-color: rgba(125,211,252,0.55);\n    box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n  }\n\n  \/* \u2705 grille 50\/50 (comme tes autres sections) *\/\n  .batteryGrid{\n    display:grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 18px;\n    align-items: stretch;\n  }\n\n  .batteryImageCard{ display:flex; flex-direction:column; min-height: 100%; }\n\n  .batteryHero{\n    flex: 1;\n    background:\n      radial-gradient(700px 240px at 20% 10%, rgba(125,211,252,0.20), transparent 60%),\n      radial-gradient(700px 240px at 80% 25%, rgba(167,139,250,0.18), transparent 60%),\n      rgba(255,255,255,0.03);\n    border-top: 1px solid rgba(255,255,255,0.10);\n    overflow:hidden;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    padding: 14px;\n    min-height: 260px;\n  }\n\n  .batteryHero img{\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n    object-position: center;\n    border-radius: 14px;\n    border: 1px solid rgba(255,255,255,0.12);\n    display:block;\n    transform: scale(1.04);\n  }\n\n  .madeRow{\n    display:flex;\n    align-items:center;\n    gap: 10px;\n  }\n  .flagBox{\n    width: 34px;\n    height: 24px;\n    border-radius: 6px;\n    border: 1px solid rgba(255,255,255,0.14);\n    overflow:hidden;\n    box-shadow: 0 10px 25px rgba(0,0,0,0.25);\n    flex: 0 0 auto;\n    background: rgba(0,0,0,0.20);\n  }\n  .madeText{\n    color: var(--muted);\n    font-size: 12px;\n    line-height: 1.35;\n  }\n\n  @media (max-width: 980px){\n    .batteryGrid{ grid-template-columns: 1fr; }\n    .batteryHero{ min-height: 240px; }\n  }\n<\/style>\n\n<div class=\"wrap1180\">\n  <section class=\"card\" id=\"batteryBlock\">\n    <div class=\"inner\">\n      <h2>4) Batterie<\/h2>\n\n      <div class=\"batteryGrid\">\n\n        <!-- LEFT -->\n        <div class=\"form\">\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"batteryBrand\">Marque de batterie<\/label>\n            <\/div>\n            <div class=\"selectWrap\">\n              <select id=\"batteryBrand\">\n                <option value=\"cts\" selected>CTS<\/option>\n                <option value=\"tyva\">TYVA<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"batteryPower\">Puissance de la batterie (kW)<\/label>\n            <\/div>\n            <input id=\"batteryPower\" class=\"batteryInput\" type=\"number\" step=\"0.1\" min=\"0\" placeholder=\"Ex : 80\">\n          <\/div>\n\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"batteryPrice\">Tarif (\u20ac)<\/label>\n            <\/div>\n            <input id=\"batteryPrice\" class=\"batteryInput\" type=\"number\" step=\"1\" min=\"0\" placeholder=\"Ex : 35000\">\n          <\/div>\n\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label>Fabrication<\/label>\n              <span class=\"badge\" id=\"batteryMadeBadge\">Chine<\/span>\n            <\/div>\n\n            <div class=\"madeRow\">\n              <div id=\"batteryFlag\" class=\"flagBox\" aria-hidden=\"true\"><\/div>\n              <div class=\"madeText\" id=\"batteryMadeText\">CTS \u2192 Chine<\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- RIGHT -->\n        <aside class=\"card batteryImageCard\">\n          <div class=\"inner\">\n            <h2>Visuel batterie<\/h2>\n          <\/div>\n\n          <div class=\"batteryHero\">\n            <img id=\"batteryImage\" alt=\"Batterie\">\n          <\/div>\n        <\/aside>\n\n      <\/div>\n    <\/div>\n  <\/section>\n<\/div>\n\n<script>\n  (function(){\n    \/\/ \u2705 \u00e9vite double init WP\n    if (window.__BATTERY_BLOCK_INITED) return;\n    window.__BATTERY_BLOCK_INITED = true;\n\n    const URLS = {\n      cts: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/cts.png\",\n      tyva: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/tyva.png\"\n    };\n\n    const els = {\n      brand: document.getElementById(\"batteryBrand\"),\n      power: document.getElementById(\"batteryPower\"),\n      price: document.getElementById(\"batteryPrice\"),\n      img: document.getElementById(\"batteryImage\"),\n      badge: document.getElementById(\"batteryMadeBadge\"),\n      txt: document.getElementById(\"batteryMadeText\"),\n      flag: document.getElementById(\"batteryFlag\")\n    };\n\n    function flagFrance(){\n      return `\n        <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 3 2\" preserveAspectRatio=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n          <rect width=\"1\" height=\"2\" x=\"0\" y=\"0\" fill=\"#0055A4\"><\/rect>\n          <rect width=\"1\" height=\"2\" x=\"1\" y=\"0\" fill=\"#FFFFFF\"><\/rect>\n          <rect width=\"1\" height=\"2\" x=\"2\" y=\"0\" fill=\"#EF4135\"><\/rect>\n        <\/svg>`;\n    }\n\n    function flagChina(){\n      return `\n        <svg width=\"100%\" height=\"100%\" viewBox=\"0 0 30 20\" preserveAspectRatio=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n          <rect width=\"30\" height=\"20\" fill=\"#DE2910\"><\/rect>\n          <polygon fill=\"#FFDE00\" points=\"5,2 6.2,5.6 10,5.6 6.9,7.8 8.1,11.4 5,9.2 1.9,11.4 3.1,7.8 0,5.6 3.8,5.6\"><\/polygon>\n        <\/svg>`;\n    }\n\n    function update(){\n      const brand = (els.brand.value || \"\").toLowerCase().trim();\n\n      \/\/ image\n      els.img.src = URLS[brand] || \"\";\n\n      \/\/ fabrication + drapeau\n      if (brand === \"tyva\") {\n        els.badge.textContent = \"France\";\n        els.txt.textContent = \"TYVA \u2192 France\";\n        els.flag.innerHTML = flagFrance();\n      } else {\n        els.badge.textContent = \"Chine\";\n        els.txt.textContent = \"CTS \u2192 Chine\";\n        els.flag.innerHTML = flagChina();\n      }\n\n      \/\/ export pour la suite si besoin\n      window.BATTERY_INPUTS = {\n        brand,\n        power_kw: Number(els.power.value || 0),\n        price_eur: Number(els.price.value || 0),\n        made_in: (brand === \"tyva\") ? \"France\" : \"Chine\",\n        image_url: URLS[brand] || \"\"\n      };\n    }\n\n    [\"change\",\"input\"].forEach(evt => {\n      els.brand.addEventListener(evt, update);\n      els.power.addEventListener(evt, update);\n      els.price.addEventListener(evt, update);\n    });\n\n    update();\n  })();\n<\/script>\n\n\n\n<!-- \u2705 BLOC MOTEUR (COLLER TEL QUEL DANS 1 SEUL BLOC WP:HTML) -->\n\n<style>\n  :root{\n    --bg: #0b1220;\n    --card: rgba(255,255,255,0.06);\n    --stroke: rgba(255,255,255,0.12);\n    --text: rgba(255,255,255,0.92);\n    --muted: rgba(255,255,255,0.62);\n    --radius: 18px;\n    --shadow: 0 18px 60px rgba(0,0,0,0.45);\n  }\n\n  \/* \u2705 wrap identique aux autres blocs *\/\n  .wrap1180{\n    max-width: 1180px;\n    margin: 18px auto 0;\n  }\n\n  \/* \u2705 style carte identique *\/\n  .card{\n    background: linear-gradient(180deg, var(--card), rgba(255,255,255,0.04));\n    border: 1px solid var(--stroke);\n    border-radius: var(--radius);\n    box-shadow: var(--shadow);\n    overflow: hidden;\n    position: relative;\n    min-width: 0;\n    color: var(--text);\n  }\n  .card .inner{ padding: 18px; }\n  .card h2{\n    margin:0 0 12px 0;\n    font-size: 16px;\n    letter-spacing: -0.01em;\n    color: rgba(255,255,255,0.92);\n  }\n\n  .form{ display:grid; gap: 14px; }\n  .field{\n    display:grid;\n    gap: 8px;\n    padding: 14px;\n    border-radius: 14px;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.10);\n  }\n  .labelRow{\n    display:flex;\n    align-items:center;\n    justify-content:space-between;\n    gap:10px;\n  }\n  label{ font-size: 13px; color: rgba(255,255,255,0.86); }\n\n  .selectWrap{ position: relative; }\n  .selectWrap:after{\n    content:\"\u25be\";\n    position:absolute;\n    right:12px;\n    top:50%;\n    transform: translateY(-50%);\n    color: rgba(255,255,255,0.65);\n    pointer-events:none;\n    font-size: 14px;\n  }\n\n  select{\n    width:100%;\n    appearance: none;\n    background: rgba(0,0,0,0.18);\n    color: var(--text);\n    border: 1px solid rgba(255,255,255,0.14);\n    border-radius: 12px;\n    padding: 12px 12px;\n    outline: none;\n    font-size: 14px;\n  }\n  select:focus{\n    border-color: rgba(125,211,252,0.55);\n    box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n  }\n\n  .motorInput{\n    width:100%;\n    appearance:none;\n    background: rgba(0,0,0,0.18);\n    color: var(--text);\n    border: 1px solid rgba(255,255,255,0.14);\n    border-radius: 12px;\n    padding: 12px 12px;\n    outline: none;\n    font-size: 14px;\n  }\n  .motorInput:focus{\n    border-color: rgba(125,211,252,0.55);\n    box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n  }\n\n  \/* \u2705 grille 50\/50 *\/\n  .motorGrid{\n    display:grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 18px;\n    align-items: stretch;\n  }\n\n  .motorImageCard{ display:flex; flex-direction:column; min-height: 100%; }\n\n  .motorHero{\n    flex: 1;\n    background:\n      radial-gradient(700px 240px at 20% 10%, rgba(125,211,252,0.20), transparent 60%),\n      radial-gradient(700px 240px at 80% 25%, rgba(167,139,250,0.18), transparent 60%),\n      rgba(255,255,255,0.03);\n    border-top: 1px solid rgba(255,255,255,0.10);\n    overflow:hidden;\n    display:flex;\n    align-items:center;\n    justify-content:center;\n    padding: 14px;\n    min-height: 260px;\n  }\n\n  .motorHero img{\n    width: 100%;\n    height: 100%;\n    object-fit: contain;\n    object-position: center;\n    border-radius: 14px;\n    border: 1px solid rgba(255,255,255,0.12);\n    display:block;\n    transform: scale(1.04);\n  }\n\n  @media (max-width: 980px){\n    .motorGrid{ grid-template-columns: 1fr; }\n    .motorHero{ min-height: 240px; }\n  }\n<\/style>\n\n<div class=\"wrap1180\">\n  <section class=\"card\" id=\"motorBlock\">\n    <div class=\"inner\">\n      <h2>5) Moteur<\/h2>\n\n      <div class=\"motorGrid\">\n\n        <!-- LEFT -->\n        <div class=\"form\">\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"motorBrand\">Marque de moteur<\/label>\n            <\/div>\n            <div class=\"selectWrap\">\n              <select id=\"motorBrand\">\n                <option value=\"bosch\" selected>Bosch<\/option>\n                <option value=\"torqeedo\">Torqeedo<\/option>\n                <option value=\"kongsberg\">Kongsberg<\/option>\n                <option value=\"evoy\">Evoy<\/option>\n                <option value=\"molabo\">Molabo<\/option>\n                <option value=\"autre\">Autre<\/option>\n              <\/select>\n            <\/div>\n          <\/div>\n\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"motorPower\">Puissance du moteur (kW)<\/label>\n            <\/div>\n            <input id=\"motorPower\" class=\"motorInput\" type=\"number\" step=\"0.1\" min=\"0\" placeholder=\"Ex : 130\">\n          <\/div>\n\n          <div class=\"field\">\n            <div class=\"labelRow\">\n              <label for=\"motorPrice\">Tarif (\u20ac)<\/label>\n            <\/div>\n            <input id=\"motorPrice\" class=\"motorInput\" type=\"number\" step=\"1\" min=\"0\" placeholder=\"Ex : 45000\">\n          <\/div>\n        <\/div>\n\n        <!-- RIGHT -->\n        <aside class=\"card motorImageCard\">\n          <div class=\"inner\">\n            <h2>Visuel moteur<\/h2>\n          <\/div>\n\n          <div class=\"motorHero\">\n            <!-- image par d\u00e9faut -->\n            <img decoding=\"async\" id=\"motorImage\" alt=\"Moteur\" src=\"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\">\n          <\/div>\n        <\/aside>\n\n      <\/div>\n    <\/div>\n  <\/section>\n<\/div>\n\n<script>\n  (function(){\n    if (window.__MOTOR_BLOCK_INITED) return;\n    window.__MOTOR_BLOCK_INITED = true;\n\n    \/\/ \u2705 Mets tes URLs ici quand tu les auras\n    const URLS = {\n      bosch: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\",\n      torqeedo: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\",\n      kongsberg: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\",\n      evoy: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\",\n      molabo: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\",\n      autre: \"http:\/\/boat.wesolution.fr\/wp-content\/uploads\/2025\/12\/motor_placeholder.png\"\n    };\n\n    const els = {\n      brand: document.getElementById(\"motorBrand\"),\n      power: document.getElementById(\"motorPower\"),\n      price: document.getElementById(\"motorPrice\"),\n      img: document.getElementById(\"motorImage\")\n    };\n\n    function update(){\n      const brand = (els.brand.value || \"\").toLowerCase().trim();\n      els.img.src = URLS[brand] || URLS.autre;\n\n      window.MOTOR_INPUTS = {\n        brand,\n        power_kw: Number(els.power.value || 0),\n        price_eur: Number(els.price.value || 0),\n        image_url: els.img.src\n      };\n    }\n\n    [\"change\",\"input\"].forEach(evt => {\n      els.brand.addEventListener(evt, update);\n      els.power.addEventListener(evt, update);\n      els.price.addEventListener(evt, update);\n    });\n\n    update();\n  })();\n<\/script>\n\n\n\n<!-- \u2705 MODULE \"CHANTIER NAVAL\" (recherche + s\u00e9lection sur carte) -->\n<!-- Colle tout dans UN seul bloc WordPress \"HTML personnalis\u00e9\" -->\n\n<link rel=\"stylesheet\" href=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css\">\n<script src=\"https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js\"><\/script>\n\n<style>\n  :root{\n    --bg: #0b1220;\n    --card: rgba(255,255,255,0.06);\n    --stroke: rgba(255,255,255,0.12);\n    --text: rgba(255,255,255,0.92);\n    --muted: rgba(255,255,255,0.62);\n    --radius: 18px;\n    --shadow: 0 18px 60px rgba(0,0,0,0.45);\n  }\n\n  \/* \u2705 m\u00eame largeur que tes autres blocs *\/\n  .wrap1180{\n    max-width: 1180px;\n    margin: 18px auto 0;\n  }\n\n  .card{\n    background: linear-gradient(180deg, var(--card), rgba(255,255,255,0.04));\n    border: 1px solid var(--stroke);\n    border-radius: var(--radius);\n    box-shadow: var(--shadow);\n    overflow: hidden;\n    position: relative;\n    min-width: 0;\n    color: var(--text);\n  }\n  .card .inner{ padding: 18px; }\n  .card h2{\n    margin:0 0 12px 0;\n    font-size: 16px;\n    letter-spacing: -0.01em;\n    color: rgba(255,255,255,0.92);\n  }\n\n  .grid{\n    display:grid;\n    grid-template-columns: 1fr 1fr;  \/* \u2705 moiti\u00e9\/moiti\u00e9 *\/\n    gap: 18px;\n    align-items: stretch;\n  }\n\n  .field{\n    display:grid;\n    gap: 10px;\n    padding: 14px;\n    border-radius: 14px;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.10);\n    margin-bottom: 12px;\n  }\n  label{\n    font-size: 13px;\n    color: rgba(255,255,255,0.86);\n  }\n\n  .row{\n    display:flex;\n    gap:10px;\n    align-items:center;\n  }\n  input[type=\"text\"]{\n    width:100%;\n    min-width:0;\n    appearance:none;\n    background: rgba(0,0,0,0.18);\n    color: var(--text);\n    border: 1px solid rgba(255,255,255,0.14);\n    border-radius: 12px;\n    padding: 12px 12px;\n    outline: none;\n    font-size: 14px;\n  }\n  input[type=\"text\"]:focus{\n    border-color: rgba(125,211,252,0.55);\n    box-shadow: 0 0 0 4px rgba(125,211,252,0.14);\n  }\n\n  .btn{\n    border: 1px solid rgba(255,255,255,0.14);\n    background: rgba(0,0,0,0.20);\n    color: rgba(255,255,255,0.92);\n    padding: 10px 12px;\n    border-radius: 12px;\n    cursor:pointer;\n    font-size: 13px;\n    transition: transform .05s ease, border-color .2s ease, background .2s ease;\n    user-select:none;\n    white-space:nowrap;\n  }\n  .btn:hover{\n    border-color: rgba(125,211,252,0.55);\n    background: rgba(125,211,252,0.10);\n  }\n  .btn:active{ transform: translateY(1px); }\n\n  .hint{\n    font-size: 12px;\n    color: var(--muted);\n    line-height: 1.45;\n    margin: 0;\n  }\n\n  .resultBox{\n    display:grid;\n    gap: 10px;\n  }\n  .kpi{\n    padding: 12px;\n    border-radius: 14px;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.10);\n    display:flex;\n    justify-content:space-between;\n    align-items:baseline;\n    gap: 12px;\n  }\n  .kpi .k{ font-size: 12px; color: var(--muted); }\n  .kpi .v{\n    font-size: 14px;\n    color: rgba(255,255,255,0.92);\n    font-weight: 600;\n    text-align:right;\n  }\n\n  .list{\n    display:grid;\n    gap: 10px;\n    max-height: 340px;\n    overflow:auto;\n    padding-right: 4px;\n  }\n  .yardItem{\n    padding: 12px;\n    border-radius: 14px;\n    background: rgba(255,255,255,0.04);\n    border: 1px solid rgba(255,255,255,0.10);\n    cursor:pointer;\n    transition: border-color .2s ease, background .2s ease, transform .05s ease;\n  }\n  .yardItem:hover{\n    border-color: rgba(125,211,252,0.45);\n    background: rgba(125,211,252,0.08);\n  }\n  .yardItem:active{ transform: translateY(1px); }\n\n  .yardName{\n    font-weight: 650;\n    font-size: 13px;\n    margin-bottom: 6px;\n    color: rgba(255,255,255,0.92);\n  }\n  .yardAddr{\n    font-size: 12px;\n    color: var(--muted);\n    line-height: 1.35;\n  }\n  .yardMeta{\n    margin-top: 8px;\n    font-size: 12px;\n    color: rgba(255,255,255,0.78);\n    display:flex;\n    gap: 10px;\n    flex-wrap:wrap;\n  }\n  .pillMini{\n    padding: 5px 9px;\n    border-radius: 999px;\n    border: 1px solid rgba(255,255,255,0.14);\n    background: rgba(0,0,0,0.18);\n  }\n\n  .mapShell{\n    display:flex;\n    flex-direction:column;\n    height: 100%;\n    min-width: 0;\n  }\n  .mapHeader{\n    padding: 14px 18px;\n    border-bottom: 1px solid rgba(255,255,255,0.10);\n    display:flex;\n    justify-content:space-between;\n    align-items:center;\n    gap: 10px;\n  }\n  .mapHeader .mini{\n    font-size: 12px;\n    color: var(--muted);\n  }\n\n  #yardMap{\n    height: 520px;\n    border-top: 1px solid rgba(255,255,255,0.10);\n  }\n\n  @media (max-width: 980px){\n    .grid{ grid-template-columns: 1fr; }\n    #yardMap{ height: 420px; }\n  }\n<\/style>\n\n<div class=\"wrap1180\">\n  <section class=\"card\" id=\"yardModule\">\n    <div class=\"inner\">\n      <h2>6) Chantier naval<\/h2>\n\n      <div class=\"grid\">\n\n        <!-- LEFT -->\n        <div>\n          <div class=\"field\">\n            <label for=\"yardCity\">Ville \/ zone<\/label>\n            <div class=\"row\">\n              <input id=\"yardCity\" type=\"text\" placeholder=\"Ex : Saint-Tropez, La Rochelle, Nice...\" \/>\n              <button class=\"btn\" id=\"yardSearchBtn\" type=\"button\">Rechercher<\/button>\n            <\/div>\n            <p class=\"hint\">On cherche d\u2019abord la ville, puis les chantiers navals autour (OpenStreetMap).<\/p>\n          <\/div>\n\n          <div class=\"field\">\n            <label>R\u00e9sultat s\u00e9lectionn\u00e9<\/label>\n\n            <div class=\"resultBox\">\n              <div class=\"kpi\">\n                <div class=\"k\">Nom<\/div>\n                <div class=\"v\" id=\"yardSelName\">\u2014<\/div>\n              <\/div>\n              <div class=\"kpi\">\n                <div class=\"k\">Adresse<\/div>\n                <div class=\"v\" id=\"yardSelAddr\">\u2014<\/div>\n              <\/div>\n              <div class=\"kpi\">\n                <div class=\"k\">Coordonn\u00e9es<\/div>\n                <div class=\"v\"><span id=\"yardSelLat\">\u2014<\/span>, <span id=\"yardSelLon\">\u2014<\/span><\/div>\n              <\/div>\n            <\/div>\n\n            <!-- \u2705 export (si tu veux l'utiliser ailleurs) -->\n            <input type=\"hidden\" id=\"yardNameField\" value=\"\">\n            <input type=\"hidden\" id=\"yardAddrField\" value=\"\">\n            <input type=\"hidden\" id=\"yardLatField\" value=\"\">\n            <input type=\"hidden\" id=\"yardLonField\" value=\"\">\n          <\/div>\n\n          <div class=\"field\">\n            <label>Liste (clique pour s\u00e9lectionner)<\/label>\n            <div class=\"list\" id=\"yardList\">\n              <div class=\"hint\" id=\"yardListHint\">Fais une recherche pour afficher des chantiers.<\/div>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- RIGHT -->\n        <aside class=\"card mapShell\">\n          <div class=\"mapHeader\">\n            <div>\n              <div style=\"font-weight:600;\">Carte<\/div>\n              <div class=\"mini\">Clique un marqueur pour s\u00e9lectionner le chantier.<\/div>\n            <\/div>\n            <div class=\"mini\">Rayon : <span id=\"yardRadiusLabel\">10<\/span> km<\/div>\n          <\/div>\n          <div id=\"yardMap\"><\/div>\n        <\/aside>\n\n      <\/div>\n    <\/div>\n  <\/section>\n<\/div>\n\n<script>\n  (function(){\n    if (window.__YARD_MODULE_INITED) return;\n    window.__YARD_MODULE_INITED = true;\n\n    \/\/ ====== Config ======\n    const SEARCH_RADIUS_KM = 10; \/\/ ajuste si tu veux 5 \/ 20 etc.\n    document.getElementById(\"yardRadiusLabel\").textContent = String(SEARCH_RADIUS_KM);\n\n    \/\/ ====== Map init ======\n    const map = L.map(\"yardMap\").setView([46.2, 2.2], 6);\n    L.tileLayer(\"https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png\", {\n      maxZoom: 19,\n      attribution: \"&copy; OpenStreetMap\"\n    }).addTo(map);\n\n    const markersLayer = L.layerGroup().addTo(map);\n    let cityMarker = null;\n    let radiusCircle = null;\n\n    \/\/ DOM\n    const els = {\n      city: document.getElementById(\"yardCity\"),\n      btn: document.getElementById(\"yardSearchBtn\"),\n      list: document.getElementById(\"yardList\"),\n      listHint: document.getElementById(\"yardListHint\"),\n\n      selName: document.getElementById(\"yardSelName\"),\n      selAddr: document.getElementById(\"yardSelAddr\"),\n      selLat: document.getElementById(\"yardSelLat\"),\n      selLon: document.getElementById(\"yardSelLon\"),\n\n      fName: document.getElementById(\"yardNameField\"),\n      fAddr: document.getElementById(\"yardAddrField\"),\n      fLat: document.getElementById(\"yardLatField\"),\n      fLon: document.getElementById(\"yardLonField\"),\n    };\n\n    \/\/ Helpers\n    function setSelected(y){\n      const name = y.name || \"\u2014\";\n      const addr = y.address || \"\u2014\";\n      const lat = (typeof y.lat === \"number\") ? y.lat : null;\n      const lon = (typeof y.lon === \"number\") ? y.lon : null;\n\n      els.selName.textContent = name;\n      els.selAddr.textContent = addr;\n      els.selLat.textContent = (lat !== null) ? lat.toFixed(6) : \"\u2014\";\n      els.selLon.textContent = (lon !== null) ? lon.toFixed(6) : \"\u2014\";\n\n      els.fName.value = name;\n      els.fAddr.value = addr;\n      els.fLat.value = (lat !== null) ? String(lat) : \"\";\n      els.fLon.value = (lon !== null) ? String(lon) : \"\";\n\n      \/\/ export global\n      window.YARD_SELECTED = { name, address: addr, lat, lon };\n    }\n\n    function clearList(){\n      els.list.innerHTML = \"\";\n    }\n\n    function makeItem(y){\n      const div = document.createElement(\"div\");\n      div.className = \"yardItem\";\n      div.innerHTML = `\n        <div class=\"yardName\">${escapeHtml(y.name || \"Chantier naval\")}<\/div>\n        <div class=\"yardAddr\">${escapeHtml(y.address || \"\")}<\/div>\n        <div class=\"yardMeta\">\n          <span class=\"pillMini\">${(y.dist_km ?? 0).toFixed(1)} km<\/span>\n          ${y.phone ? `<span class=\"pillMini\">\u260e ${escapeHtml(y.phone)}<\/span>` : \"\"}\n          ${y.website ? `<span class=\"pillMini\">\ud83c\udf10 site<\/span>` : \"\"}\n        <\/div>\n      `;\n      div.addEventListener(\"click\", () => {\n        setSelected(y);\n        map.setView([y.lat, y.lon], 14);\n      });\n      return div;\n    }\n\n    function escapeHtml(s){\n      return String(s || \"\")\n        .replaceAll(\"&\",\"&amp;\")\n        .replaceAll(\"<\",\"&lt;\")\n        .replaceAll(\">\",\"&gt;\")\n        .replaceAll('\"',\"&quot;\")\n        .replaceAll(\"'\",\"&#039;\");\n    }\n\n    function haversineKm(lat1, lon1, lat2, lon2){\n      const toRad = d => (d * Math.PI) \/ 180;\n      const R = 6371;\n      const dLat = toRad(lat2 - lat1);\n      const dLon = toRad(lon2 - lon1);\n      const a =\n        Math.sin(dLat\/2)*Math.sin(dLat\/2) +\n        Math.cos(toRad(lat1))*Math.cos(toRad(lat2)) *\n        Math.sin(dLon\/2)*Math.sin(dLon\/2);\n      const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));\n      return R * c;\n    }\n\n    \/\/ ====== API calls (OpenStreetMap) ======\n    async function geocodeCity(q){\n      const url = \"https:\/\/nominatim.openstreetmap.org\/search?format=json&limit=1&q=\" + encodeURIComponent(q);\n      const res = await fetch(url, { headers: { \"Accept-Language\":\"fr\" } });\n      const data = await res.json();\n      if (!data || !data.length) return null;\n      return {\n        lat: parseFloat(data[0].lat),\n        lon: parseFloat(data[0].lon),\n        label: data[0].display_name\n      };\n    }\n\n    async function searchYardsAround(lat, lon, radiusKm){\n      \/\/ Overpass query : chantiers navals \/ boatyard \/ shipyard autour du centre\n      const radiusM = Math.round(radiusKm * 1000);\n\n      const query = `\n        [out:json][timeout:25];\n        (\n          nwr(around:${radiusM},${lat},${lon})[\"industrial\"=\"shipyard\"];\n          nwr(around:${radiusM},${lat},${lon})[\"man_made\"=\"shipyard\"];\n          nwr(around:${radiusM},${lat},${lon})[\"craft\"=\"boatbuilder\"];\n          nwr(around:${radiusM},${lat},${lon})[\"leisure\"=\"marina\"][\"service:boat:repair\"=\"yes\"];\n          nwr(around:${radiusM},${lat},${lon})[\"name\"][\"boat:repair\"=\"yes\"];\n        );\n        out center tags;\n      `;\n\n      const url = \"https:\/\/overpass-api.de\/api\/interpreter\";\n      const res = await fetch(url, {\n        method: \"POST\",\n        headers: { \"Content-Type\":\"text\/plain;charset=UTF-8\" },\n        body: query\n      });\n      const json = await res.json();\n      const elements = json.elements || [];\n\n      \/\/ Normalize\n      const yards = elements.map(el => {\n        const t = el.tags || {};\n        const yLat = (el.type === \"node\") ? el.lat : (el.center ? el.center.lat : null);\n        const yLon = (el.type === \"node\") ? el.lon : (el.center ? el.center.lon : null);\n\n        const name = t.name || t.operator || \"Chantier naval\";\n        const addrParts = [\n          t[\"addr:housenumber\"],\n          t[\"addr:street\"],\n          t[\"addr:postcode\"],\n          t[\"addr:city\"]\n        ].filter(Boolean).join(\" \").trim();\n\n        const address = addrParts || t[\"addr:full\"] || t[\"contact:address\"] || \"\";\n\n        const phone = t[\"contact:phone\"] || t.phone || \"\";\n        const website = t[\"contact:website\"] || t.website || \"\";\n\n        const dist_km = (yLat && yLon) ? haversineKm(lat, lon, yLat, yLon) : 999;\n\n        return {\n          id: el.type + \":\" + el.id,\n          name,\n          address,\n          lat: yLat,\n          lon: yLon,\n          phone,\n          website,\n          dist_km\n        };\n      }).filter(y => y.lat && y.lon);\n\n      \/\/ sort by distance, keep best\n      yards.sort((a,b) => a.dist_km - b.dist_km);\n      return yards.slice(0, 25);\n    }\n\n    \/\/ ====== Render markers\/list ======\n    function renderYards(yards){\n      clearList();\n      markersLayer.clearLayers();\n\n      if (!yards.length){\n        els.list.innerHTML = `<div class=\"hint\">Aucun chantier trouv\u00e9 dans le rayon. Essaie une autre ville ou augmente le rayon.<\/div>`;\n        return;\n      }\n\n      yards.forEach(y => {\n        \/\/ list item\n        els.list.appendChild(makeItem(y));\n\n        \/\/ marker\n        const m = L.marker([y.lat, y.lon]).addTo(markersLayer);\n        const popup = `\n          <b>${escapeHtml(y.name)}<\/b><br>\n          ${escapeHtml(y.address || \"\")}<br>\n          <span style=\"opacity:.8\">${y.dist_km.toFixed(1)} km<\/span><br><br>\n          <button id=\"pick_${y.id.replace(\":\",\"_\")}\" style=\"\n            border:1px solid rgba(255,255,255,0.25);\n            background: rgba(0,0,0,0.35);\n            color: white;\n            padding: 6px 10px;\n            border-radius: 10px;\n            cursor:pointer;\n          \">Choisir ce chantier<\/button>\n        `;\n        m.bindPopup(popup);\n\n        m.on(\"popupopen\", () => {\n          const btn = document.getElementById(\"pick_\" + y.id.replace(\":\",\"_\"));\n          if (btn) btn.onclick = () => {\n            setSelected(y);\n            m.closePopup();\n          };\n        });\n\n        m.on(\"click\", () => setSelected(y));\n      });\n\n      \/\/ preselect first\n      setSelected(yards[0]);\n    }\n\n    function drawCityStuff(lat, lon){\n      if (cityMarker) map.removeLayer(cityMarker);\n      if (radiusCircle) map.removeLayer(radiusCircle);\n\n      cityMarker = L.marker([lat, lon]).addTo(map);\n      radiusCircle = L.circle([lat, lon], {\n        radius: SEARCH_RADIUS_KM * 1000\n      }).addTo(map);\n\n      map.setView([lat, lon], 12);\n    }\n\n    \/\/ ====== main search ======\n    async function runSearch(){\n      const q = els.city.value.trim();\n      if (!q) return;\n\n      els.btn.disabled = true;\n      els.btn.textContent = \"Recherche...\";\n      clearList();\n      els.list.innerHTML = `<div class=\"hint\">Recherche en cours\u2026<\/div>`;\n\n      try{\n        const city = await geocodeCity(q);\n        if (!city){\n          els.list.innerHTML = `<div class=\"hint\">Ville introuvable. Essaie \u201cPort de \u2026\u201d ou une commune voisine.<\/div>`;\n          return;\n        }\n\n        drawCityStuff(city.lat, city.lon);\n\n        const yards = await searchYardsAround(city.lat, city.lon, SEARCH_RADIUS_KM);\n        renderYards(yards);\n\n        \/\/ Fix Leaflet sizing if WP layout reflow\n        setTimeout(() => map.invalidateSize(), 200);\n\n      }catch(e){\n        els.list.innerHTML = `<div class=\"hint\">Erreur de recherche. R\u00e9essaie dans quelques secondes.<\/div>`;\n      }finally{\n        els.btn.disabled = false;\n        els.btn.textContent = \"Rechercher\";\n      }\n    }\n\n    els.btn.addEventListener(\"click\", runSearch);\n    els.city.addEventListener(\"keydown\", (e) => {\n      if (e.key === \"Enter\") runSearch();\n    });\n\n    \/\/ Export global par d\u00e9faut\n    window.YARD_SELECTED = { name:\"\", address:\"\", lat:null, lon:null };\n\n    \/\/ Leaflet sizing safeguard\n    setTimeout(() => map.invalidateSize(), 250);\n  })();\n<\/script>\n\n\n\n<style>\n  .wrap1180{\n    max-width: 1180px;\n    margin: 18px auto 0;\n  }\n\n  .card{\n    background: linear-gradient(180deg, rgba(255,255,255,0.06), rgba(255,255,255,0.04));\n    border: 1px solid rgba(255,255,255,0.12);\n    border-radius: 18px;\n    box-shadow: 0 18px 60px rgba(0,0,0,0.45);\n    overflow: hidden;\n    color: rgba(255,255,255,0.92);\n  }\n\n  .inner{ padding:18px; }\n\n  h2{ margin:0 0 10px 0; font-size:16px; }\n\n  .hint{\n    font-size:12px;\n    color:rgba(255,255,255,0.62);\n    margin: 0 0 14px 0;\n  }\n\n  .field{\n    display:grid;\n    gap:8px;\n    padding:14px;\n    border-radius:14px;\n    background:rgba(255,255,255,0.04);\n    border:1px solid rgba(255,255,255,0.10);\n    margin-bottom:12px;\n  }\n\n  .row2{\n    display:flex;\n    justify-content:space-between;\n    align-items:center;\n    gap:10px;\n  }\n\n  label{ font-size:13px; }\n\n  .badge{\n    font-size:12px;\n    padding:6px 10px;\n    border-radius:999px;\n    border:1px solid rgba(255,255,255,0.14);\n    background:rgba(0,0,0,0.20);\n    white-space: nowrap;\n  }\n\n  .moneyInput{\n    width:100%;\n    background:rgba(0,0,0,0.18);\n    color:#fff;\n    border:1px solid rgba(255,255,255,0.14);\n    border-radius:12px;\n    padding:12px;\n    font-size:14px;\n  }\n  .moneyInput:focus{\n    outline:none;\n    border-color:rgba(125,211,252,0.55);\n    box-shadow:0 0 0 4px rgba(125,211,252,0.14);\n  }\n\n  .totalRow{\n    margin-top: 14px;\n    padding: 14px;\n    border-radius: 14px;\n    background: linear-gradient(90deg, rgba(125,211,252,0.12), rgba(167,139,250,0.10));\n    border: 1px solid rgba(125,211,252,0.35);\n    display:flex;\n    justify-content:space-between;\n    align-items:center;\n    font-weight: 600;\n    font-size: 18px;\n  }\n\n  .actions{\n    display:flex;\n    gap:10px;\n    margin-top:14px;\n    flex-wrap: wrap;\n  }\n\n  .btn{\n    padding:10px 14px;\n    border-radius:12px;\n    border:1px solid rgba(255,255,255,0.14);\n    background:rgba(0,0,0,0.20);\n    color:#fff;\n    cursor:pointer;\n  }\n  .btn:hover{\n    background:rgba(125,211,252,0.12);\n    border-color:rgba(125,211,252,0.55);\n  }\n<\/style>\n\n<div class=\"wrap1180\">\n  <section class=\"card\">\n    <div class=\"inner\">\n      <h2>7) Bilan financier<\/h2>\n      <p class=\"hint\">Saisis les co\u00fbts ci-dessous. Le total se calcule automatiquement. (Export dispo dans <code>window.FINANCIAL_SUMMARY<\/code>)<\/p>\n\n      <!-- \u2705 4 CHAMPS UNIQUES (plus de doublon) -->\n      <div class=\"field\">\n        <div class=\"row2\">\n          <label for=\"cBattery\">Co\u00fbt de la batterie (\u20ac)<\/label>\n          <span class=\"badge\" id=\"bBattery\">0 \u20ac<\/span>\n        <\/div>\n        <input id=\"cBattery\" class=\"moneyInput\" type=\"number\" inputmode=\"decimal\" step=\"1\" min=\"0\" placeholder=\"Ex : 35000\">\n      <\/div>\n\n      <div class=\"field\">\n        <div class=\"row2\">\n          <label for=\"cMotor\">Co\u00fbt de la motorisation (\u20ac)<\/label>\n          <span class=\"badge\" id=\"bMotor\">0 \u20ac<\/span>\n        <\/div>\n        <input id=\"cMotor\" class=\"moneyInput\" type=\"number\" inputmode=\"decimal\" step=\"1\" min=\"0\" placeholder=\"Ex : 22000\">\n      <\/div>\n\n      <div class=\"field\">\n        <div class=\"row2\">\n          <label for=\"cStudy\">Co\u00fbt d\u2019\u00e9tude &#038; homologation (\u20ac)<\/label>\n          <span class=\"badge\" id=\"bStudy\">0 \u20ac<\/span>\n        <\/div>\n        <input id=\"cStudy\" class=\"moneyInput\" type=\"number\" inputmode=\"decimal\" step=\"1\" min=\"0\" placeholder=\"Ex : 12000\">\n      <\/div>\n\n      <div class=\"field\">\n        <div class=\"row2\">\n          <label for=\"cAssembly\">Co\u00fbt du montage (\u20ac)<\/label>\n          <span class=\"badge\" id=\"bAssembly\">0 \u20ac<\/span>\n        <\/div>\n        <input id=\"cAssembly\" class=\"moneyInput\" type=\"number\" inputmode=\"decimal\" step=\"1\" min=\"0\" placeholder=\"Ex : 8000\">\n      <\/div>\n\n      <!-- \u2705 TOTAL UNIQUE -->\n      <div class=\"totalRow\">\n        <span>Co\u00fbt total<\/span>\n        <span id=\"lTotal\">0 \u20ac<\/span>\n      <\/div>\n\n      <div class=\"actions\">\n        <button class=\"btn\" id=\"finReset\" type=\"button\">R\u00e9initialiser<\/button>\n        <button class=\"btn\" id=\"finCopy\" type=\"button\">Copier JSON<\/button>\n      <\/div>\n    <\/div>\n  <\/section>\n<\/div>\n\n<script>\n(function(){\n  if (window.__FIN_BLOCK_INITED) return;\n  window.__FIN_BLOCK_INITED = true;\n\n  const el = (id) => document.getElementById(id);\n  const f = {\n    b: el(\"cBattery\"),\n    m: el(\"cMotor\"),\n    s: el(\"cStudy\"),\n    a: el(\"cAssembly\"),\n    bb: el(\"bBattery\"),\n    bm: el(\"bMotor\"),\n    bs: el(\"bStudy\"),\n    ba: el(\"bAssembly\"),\n    total: el(\"lTotal\"),\n  };\n\n  const eur = (v) => (v||0).toLocaleString(\"fr-FR\", {\n    style:\"currency\",\n    currency:\"EUR\",\n    maximumFractionDigits:0\n  });\n\n  function upd(){\n    const b = +f.b.value || 0;\n    const m = +f.m.value || 0;\n    const s = +f.s.value || 0;\n    const a = +f.a.value || 0;\n    const t = b + m + s + a;\n\n    f.bb.textContent = eur(b);\n    f.bm.textContent = eur(m);\n    f.bs.textContent = eur(s);\n    f.ba.textContent = eur(a);\n    f.total.textContent = eur(t);\n\n    window.FINANCIAL_SUMMARY = {\n      battery: b,\n      motor: m,\n      study: s,\n      assembly: a,\n      total: t\n    };\n  }\n\n  [\"input\",\"change\"].forEach(evt => {\n    f.b.addEventListener(evt, upd);\n    f.m.addEventListener(evt, upd);\n    f.s.addEventListener(evt, upd);\n    f.a.addEventListener(evt, upd);\n  });\n\n  el(\"finReset\").addEventListener(\"click\", () => {\n    f.b.value = \"\";\n    f.m.value = \"\";\n    f.s.value = \"\";\n    f.a.value = \"\";\n    upd();\n  });\n\n  el(\"finCopy\").addEventListener(\"click\", async () => {\n    upd();\n    try{\n      await navigator.clipboard.writeText(JSON.stringify(window.FINANCIAL_SUMMARY, null, 2));\n      alert(\"JSON copi\u00e9 \u2705\");\n    }catch(e){\n      alert(\"Copie impossible. JSON :\\n\\n\" + JSON.stringify(window.FINANCIAL_SUMMARY, null, 2));\n    }\n  });\n\n  upd();\n})();\n<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Calculateur d&rsquo;autonomie \u2014 Donn\u00e9es d&rsquo;entr\u00e9e Calculateur d\u2019autonomie \u2014 Donn\u00e9es d\u2019entr\u00e9e Renseigne le bateau + l\u2019\u00e9tat r\u00e9el (h\u00e9lice \/ coque) et une vitesse. Mode d\u00e9mo \u2014 aucune donn\u00e9e envoy\u00e9e 1) Param\u00e8tres utilisateur Marque Chargement\u2026 Mod\u00e8le du bateau Choisis une marque\u2026 Usure de l\u2019h\u00e9lice 0% \u00c9tat de la coque (antifouling) Oui (traitement antifouling)Non (pas d\u2019antifouling) 2) Visuel [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-11","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/pages\/11","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/comments?post=11"}],"version-history":[{"count":42,"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/pages\/11\/revisions"}],"predecessor-version":[{"id":80,"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/pages\/11\/revisions\/80"}],"wp:attachment":[{"href":"https:\/\/boat.wesolution.fr\/index.php\/wp-json\/wp\/v2\/media?parent=11"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}