// =====================================================
// PAGE — Explorer les escales (carte interactive)
// =====================================================

// ---------------------------------------------------------------
// Maritime route helpers
// ---------------------------------------------------------------

// Waypoints to route around land — each point is verified to be in open ocean
const MARITIME_BYPASSES = {
  // Canal du Mozambique : côte ouest Mahajanga → Morondava
  "mahajanga|morondava":      [[-15.8,44.5],[-17.5,43.5],[-19.5,43.5]],
  "morondava|mahajanga":      [[-19.5,43.5],[-17.5,43.5],[-15.8,44.5]],
  // Canal du Mozambique : Morondava → Toliara (rester offshore)
  "morondava|toliara":        [[-21.5,43.0],[-23.0,43.0]],
  "toliara|morondava":        [[-23.0,43.0],[-21.5,43.0]],
  // Contournement Cap Sainte-Marie (pointe sud) : SW → SE
  "toliara|fort-dauphin":     [[-24.5,43.0],[-26.3,44.5],[-26.3,46.5],[-25.2,47.8]],
  "fort-dauphin|toliara":     [[-25.2,47.8],[-26.3,46.5],[-26.3,44.5],[-24.5,43.0]],
  // Contournement Cap d'Ambre (pointe nord)
  "nosy-be|antsiranana":      [[-12.5,48.0],[-11.7,49.0],[-11.8,49.6]],
  "antsiranana|nosy-be":      [[-11.8,49.6],[-11.7,49.0],[-12.5,48.0]],
  // Contournement presqu'île Masoala (côte NE)
  "antsiranana|sainte-marie": [[-13.0,50.2],[-15.0,50.8]],
  "sainte-marie|antsiranana": [[-15.0,50.8],[-13.0,50.2]],
  // Côte est Mahajanga → toutes destinations est
  "mahajanga|toamasina":      [[-15.5,48.5],[-17.0,50.0]],
  "toamasina|mahajanga":      [[-17.0,50.0],[-15.5,48.5]],
  // Péninsule Masoala — Maroantsetra ↔ Antalaha (côtés opposés, contournement par Cap Est au sud)
  "maroantsetra|antalaha":    [[-16.0,50.5],[-14.8,50.9]],
  "antalaha|maroantsetra":    [[-14.8,50.9],[-16.0,50.5]],
  // Péninsule Masoala — Antalaha ↔ Sainte-Marie (côte est, au large)
  "antalaha|sainte-marie":    [[-15.8,51.0],[-16.8,50.6]],
  "sainte-marie|antalaha":    [[-16.8,50.6],[-15.8,51.0]],
  // Baie d'Antongil — Sainte-Marie ↔ Maroantsetra (point d'entrée au large)
  "sainte-marie|maroantsetra":[[-16.0,50.3]],
  "maroantsetra|sainte-marie":[[-16.0,50.3]],
};

function buildMaritimeCoords(stops) {
  const coords = [];
  for (let i = 0; i < stops.length; i++) {
    coords.push([stops[i].lat, stops[i].lng]);
    if (i < stops.length - 1) {
      const key = `${stops[i].id}|${stops[i+1].id}`;
      (MARITIME_BYPASSES[key] || []).forEach(wp => coords.push(wp));
    }
  }
  return coords;
}

function catmullRomSpline(pts, steps) {
  if (pts.length < 2) return pts;
  const result = [];
  const p = [pts[0], ...pts, pts[pts.length - 1]];
  for (let i = 1; i < p.length - 2; i++) {
    for (let j = 0; j < steps; j++) {
      const t = j / steps, t2 = t * t, t3 = t2 * t;
      result.push([
        0.5 * ((2*p[i][0]) + (p[i+1][0]-p[i-1][0])*t + (2*p[i-1][0]-5*p[i][0]+4*p[i+1][0]-p[i+2][0])*t2 + (3*p[i][0]-p[i-1][0]-3*p[i+1][0]+p[i+2][0])*t3),
        0.5 * ((2*p[i][1]) + (p[i+1][1]-p[i-1][1])*t + (2*p[i-1][1]-5*p[i][1]+4*p[i+1][1]-p[i+2][1])*t2 + (3*p[i][1]-p[i-1][1]-3*p[i+1][1]+p[i+2][1])*t3)
      ]);
    }
  }
  result.push(pts[pts.length - 1]);
  return result;
}

function bearingBetween(from, to) {
  const r = Math.PI / 180;
  const la1 = from[0] * r, la2 = to[0] * r, dl = (to[1] - from[1]) * r;
  const y = Math.sin(dl) * Math.cos(la2);
  const x = Math.cos(la1) * Math.sin(la2) - Math.sin(la1) * Math.cos(la2) * Math.cos(dl);
  return (Math.atan2(y, x) * 180 / Math.PI + 360) % 360;
}

const MOBILE_MAP_POINTS = {
  "antsiranana":  { x: 53, y: 11, side: "right" },
  "nosy-be":      { x: 40, y: 18, side: "left" },
  "antalaha":     { x: 68, y: 27, side: "right" },
  "maroantsetra": { x: 66, y: 36, side: "right" },
  "sainte-marie": { x: 72, y: 48, side: "right" },
  "toamasina":    { x: 63, y: 58, side: "right" },
  "mahajanga":    { x: 30, y: 38, side: "left" },
  "morondava":    { x: 29, y: 62, side: "left" },
  "toliara":      { x: 38, y: 78, side: "left" },
  "fort-dauphin": { x: 57, y: 86, side: "right" }
};

function makeBoatIcon(angle) {
  const a = (angle - 90 + 360) % 360;
  return L.divIcon({
    className: "boat-mk",
    html: `<div style="transform:rotate(${a}deg);width:40px;height:40px;display:flex;align-items:center;justify-content:center;">
      <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
        <!-- Halo -->
        <circle cx="20" cy="20" r="18" fill="white" fill-opacity="0.95" stroke="#5BB4D4" stroke-width="1.5" stroke-opacity="0.7"/>
        <!-- Coque (bow à droite) -->
        <path d="M4 25 L4 29 L28 29 L35 25.5 L28 22 L4 22 Z" fill="#1A2A4F"/>
        <!-- Ligne de flottaison -->
        <line x1="5" y1="25" x2="35" y2="25" stroke="#5BB4D4" stroke-width="0.9" stroke-opacity="0.85"/>
        <!-- Superstructure principale (3 ponts) -->
        <rect x="5" y="16" width="23" height="6" rx="0.5" fill="white" stroke="#1A2A4F" stroke-width="0.6"/>
        <line x1="5" y1="18" x2="28" y2="18" stroke="#C8D4E8" stroke-width="0.5"/>
        <line x1="5" y1="20" x2="28" y2="20" stroke="#C8D4E8" stroke-width="0.5"/>
        <!-- Passerelle de navigation -->
        <rect x="16" y="11" width="11" height="5" rx="0.5" fill="white" stroke="#1A2A4F" stroke-width="0.6"/>
        <!-- Cheminée (couleur d'accent) -->
        <rect x="19" y="6.5" width="5" height="5.5" rx="1.5" fill="#E85A1F"/>
        <rect x="18.5" y="5.5" width="6" height="1.8" rx="0.9" fill="#C04010"/>
        <!-- Hublots sur la coque -->
        <circle cx="9"  cy="25.5" r="1" fill="white"/>
        <circle cx="13" cy="25.5" r="1" fill="white"/>
        <circle cx="17" cy="25.5" r="1" fill="white"/>
        <circle cx="21" cy="25.5" r="1" fill="white"/>
        <!-- Vague d'étrave -->
        <path d="M29 27 Q33 25.5 35 25.5" stroke="#5BB4D4" stroke-width="0.9" fill="none" stroke-opacity="0.7"/>
      </svg>
    </div>`,
    iconSize: [40, 40],
    iconAnchor: [20, 20]
  });
}

const ExplorerPage = ({ navigate, itinIds = null }) => {
  const [selected, setSelected] = React.useState(null);
  const [drawerTab, setDrawerTab] = React.useState("decouvrir");
  const [mobileQuery, setMobileQuery] = React.useState("");
  const [mapReady, setMapReady] = React.useState(false);
  const mapRef = React.useRef(null);
  const leafletMap = React.useRef(null);
  const markers = React.useRef({});
  const routeLayer = React.useRef(null);
  const animFrameRef = React.useRef(null);

  const matches = ESCALES.map(e => e.id);

  // Initialise Leaflet map once
  React.useEffect(() => {
    if (!mapRef.current || leafletMap.current) return;
    const map = L.map(mapRef.current, {
      center: [-20, 47],
      zoom: 5,
      zoomControl: true,
      attributionControl: false,
      preferCanvas: false
    });
    L.tileLayer("https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png", {
      subdomains: "abcd", maxZoom: 18
    }).addTo(map);

    ESCALES.forEach(e => {
      const mk = L.marker([e.lat, e.lng], {
        icon: buildIcon(e, false),
        zIndexOffset: 100
      }).addTo(map);
      mk.on("click", () => setSelected(e.id));
      markers.current[e.id] = mk;
    });

    leafletMap.current = map;
    setMapReady(true);
    setTimeout(() => {
      map.invalidateSize({ animate: false });
      map.fitBounds(L.latLngBounds(ESCALES.map(e => [e.lat, e.lng])), {
        padding: [28, 28],
        maxZoom: 5
      });
    }, 120);
  }, []);

  // Update marker icons on selection change + dim non-route escales
  React.useEffect(() => {
    const routeSet = new Set(itinIds || []);
    ESCALES.forEach(e => {
      const mk = markers.current[e.id];
      if (!mk) return;
      const isDimmed = itinIds && itinIds.length > 0 && !routeSet.has(e.id);
      mk.setIcon(buildIcon(e, selected === e.id, isDimmed));
    });
  }, [selected, JSON.stringify(itinIds)]);

  // On selection: invalidate map size (grid resized) then fit to show all ports
  React.useEffect(() => {
    if (!leafletMap.current) return;
    const map = leafletMap.current;
    // Wait for CSS transition to finish before recalculating size
    const t = setTimeout(() => {
      map.invalidateSize({ animate: false });
      if (selected) {
        // Zoom to show all Madagascar so other ports stay clickable
        map.flyTo([-20, 46.5], 5, { duration: 0.5 });
      }
    }, 320);
    return () => clearTimeout(t);
  }, [selected]);

  // Draw maritime route polyline + animated boat
  React.useEffect(() => {
    if (!mapReady || !leafletMap.current) return;
    const map = leafletMap.current;
    if (animFrameRef.current) { clearTimeout(animFrameRef.current); animFrameRef.current = null; }
    if (routeLayer.current) { map.removeLayer(routeLayer.current); routeLayer.current = null; }
    if (!itinIds || itinIds.length < 2) return;
    const stops = itinIds.map(id => ESCALES.find(e => e.id === id)).filter(Boolean);
    if (stops.length < 2) return;

    const coords = buildMaritimeCoords(stops);
    const smoothPts = catmullRomSpline(coords, 30);
    const fg = L.featureGroup();

    L.polyline(smoothPts, { color: "#E85A1F", weight: 2.5, dashArray: "8 5", opacity: 0.85 }).addTo(fg);

    const boatMk = L.marker(smoothPts[0], {
      icon: makeBoatIcon(bearingBetween(smoothPts[0], smoothPts[1])),
      zIndexOffset: 1000
    }).addTo(fg);

    let idx = 0;
    function tick() {
      idx = (idx + 1) % smoothPts.length;
      const pos = smoothPts[idx];
      const nxt = smoothPts[(idx + 1) % smoothPts.length];
      boatMk.setLatLng(pos);
      boatMk.setIcon(makeBoatIcon(bearingBetween(pos, nxt)));
      animFrameRef.current = setTimeout(tick, 40);
    }
    animFrameRef.current = setTimeout(tick, 40);

    fg.addTo(map);
    routeLayer.current = fg;
    map.fitBounds(L.latLngBounds(smoothPts), { padding: [60, 60], maxZoom: 7 });
    return () => { if (animFrameRef.current) { clearTimeout(animFrameRef.current); animFrameRef.current = null; } };
  }, [mapReady, JSON.stringify(itinIds)]);

  const itinMeta = itinIds && ITINERAIRES.find(it =>
    it.escales.length === itinIds.length && it.escales.every((id, i) => id === itinIds[i])
  );

  const escale = ESCALES.find(e => e.id === selected);
  const filteredEscales = React.useMemo(() => {
    const q = mobileQuery.trim().toLowerCase();
    if (!q) return ESCALES;
    return ESCALES.filter(e => [
      e.nom,
      e.nomAlt,
      e.facade,
      e.typeAccueil,
      e.segment
    ].some(value => String(value || "").toLowerCase().includes(q)));
  }, [mobileQuery]);

  const handleSelect = (id) => {
    setSelected(id);
    setDrawerTab("decouvrir");
    if (window.matchMedia && window.matchMedia("(max-width: 860px)").matches) {
      setTimeout(() => {
        document.querySelector(".escale-drawer")?.scrollIntoView({ block: "start", behavior: "smooth" });
      }, 120);
    }
  };

  return (
    <main className="app-main">
      <h1 className="sr-only">Carte interactive des escales croisières de Madagascar</h1>
      <div className={`explorer-shell ${selected ? "has-selection" : ""}`}>
        <div className="explorer-map-wrap">
          <div className="explorer-map" ref={mapRef}></div>
          <div className="mobile-static-map" aria-label="Carte simplifiée des escales de Madagascar">
            <div className="msm-island" aria-hidden="true"></div>
            <div className="msm-note">
              <Icon name="map" size={13}/> Carte simplifiée · touchez un point
            </div>
            {ESCALES.map(e => {
              const p = MOBILE_MAP_POINTS[e.id] || { x: 50, y: 50, side: "right" };
              return (
                <button
                  key={e.id}
                  className={`msm-port ${selected === e.id ? "active" : ""} ${p.side === "left" ? "west" : "east"}`}
                  style={{ left: `${p.x}%`, top: `${p.y}%`, "--port-color": e.couleur }}
                  onClick={() => handleSelect(e.id)}
                >
                  <span className="msm-dot"></span>
                  <span className="msm-label">{e.nom}</span>
                </button>
              );
            })}
          </div>
          {itinIds && itinIds.length > 0 && (
            <div className="route-banner">
              <Icon name="map" size={13}/>
              <span>{itinMeta ? itinMeta.nom : `${itinIds.length} escales`} · route maritime tracée</span>
              <button className="rb-back" onClick={() => navigate("#/inspirations")} title="Retour aux inspirations">
                <Icon name="arrow-left" size={12}/> Inspirations
              </button>
              <button className="rb-close" onClick={() => navigate("#/explorer")} title="Effacer la route">
                <Icon name="x" size={12}/>
              </button>
            </div>
          )}
        </div>

        <section className="mobile-map-panel" aria-labelledby="mobile-map-title">
          <div className="mmp-head">
            <span className="h-eyebrow">Carte interactive</span>
            <h2 id="mobile-map-title">Choisir une escale</h2>
            <p>Touchez un point sur la carte ou sélectionnez directement une escale dans la liste.</p>
          </div>
          <label className="mobile-port-search">
            <Icon name="search" size={15}/>
            <input
              type="search"
              value={mobileQuery}
              onChange={e => setMobileQuery(e.target.value)}
              placeholder="Rechercher une escale"
              aria-label="Rechercher une escale"
            />
          </label>
          <div className="mobile-port-list" aria-label="Liste des escales">
            {filteredEscales.map(e => (
              <button
                key={e.id}
                className={`mobile-port-btn ${selected === e.id ? "active" : ""}`}
                onClick={() => handleSelect(e.id)}
              >
                <span className="mp-dot" style={{ background: e.couleur }}/>
                <span className="mp-copy">
                  <span className="mp-name">{e.nom}</span>
                  <span className="mp-meta">{e.facade} · {e.typeAccueil.split("·")[0].trim()}</span>
                </span>
                <Icon name="chevron-right" size={14}/>
              </button>
            ))}
            {filteredEscales.length === 0 && (
              <div className="mobile-port-empty">Aucune escale ne correspond à cette recherche.</div>
            )}
          </div>
        </section>

        {/* Drawer / bottom-sheet */}
        <aside className="escale-drawer">
          {/* Handle mobile — affordance de glissement */}
          <div className="bs-handle">
            <div className="bs-pill"/>
            {!escale && (
              <span className="bs-hint">
                <Icon name="map" size={12}/> 10 escales · Touchez un marqueur
              </span>
            )}
            {escale && (
              <span className="bs-hint bs-hint--port">
                <span className="bs-dot" style={{ background: escale.couleur }}/>{escale.nom}
              </span>
            )}
          </div>

          {!escale && <DrawerEmpty itinIds={itinIds}/>}
          {escale && (
            <DrawerEscale
              escale={escale}
              tab={drawerTab}
              onTab={setDrawerTab}
              onClose={() => setSelected(null)}
              onSwitch={handleSelect}
              onOpenFiche={() => navigate(`#/escale/${escale.id}`)}
            />
          )}
        </aside>
      </div>

    </main>
  );
};

// ---------------------------------------------------------------
// Marker icon factory — label avec nom du port
// ---------------------------------------------------------------
function buildIcon(escale, active, dimmed) {
  const cls = `mk-label ${active ? "is-active" : ""} ${dimmed ? "is-dimmed" : ""}`;
  const rings = active
    ? `<span class="mk-pulse" style="background:${escale.couleur}"></span>
       <span class="mk-pulse mk-pulse--2" style="background:${escale.couleur}"></span>
       <span class="mk-pulse mk-pulse--3" style="background:${escale.couleur}"></span>`
    : "";
  const html = `<div class="${cls}">
    <span class="mk-dot-wrap">${rings}<span class="mk-dot" style="background:${escale.couleur}"></span></span>
    <span class="mk-name">${escale.nom}</span>
  </div>`;
  return L.divIcon({
    className: "escale-mk",
    html,
    iconSize: [160, 30],
    iconAnchor: [10, 15]
  });
}

// ---------------------------------------------------------------
// Drawer empty state
// ---------------------------------------------------------------
const DrawerEmpty = ({ itinIds }) => (
  <div className="drawer-empty">
    <svg width="72" height="72" viewBox="0 0 64 64" fill="none" className="e-icon">
      <circle cx="32" cy="32" r="28" stroke="currentColor" strokeOpacity="0.25" strokeWidth="1"/>
      <circle cx="32" cy="32" r="20" stroke="currentColor" strokeOpacity="0.45" strokeWidth="1"/>
      <path d="M32 8 L34 30 L56 32 L34 34 L32 56 L30 34 L8 32 L30 30 Z" fill="currentColor" opacity="0.85"/>
    </svg>
    {itinIds && itinIds.length > 0 ? (
      <>
        <h3 className="h-card" style={{ marginTop: 4 }}>Route tracée sur la carte</h3>
        <p>La route en pointillé relie les {itinIds.length} escales de l'itinéraire. Cliquez sur un marqueur pour ouvrir la fiche d'une escale.</p>
      </>
    ) : (
      <>
        <h3 className="h-card" style={{ marginTop: 4 }}>Sélectionnez une escale</h3>
        <p>
          Cliquez sur l'un des dix points de la carte pour ouvrir une fiche
          synthétique avec photos, données opérationnelles et excursions.
        </p>
      </>
    )}
  </div>
);

// ---------------------------------------------------------------
// Drawer — Escale preview (panel)
// ---------------------------------------------------------------
const DrawerEscale = ({ escale, tab, onTab, onClose, onSwitch, onOpenFiche }) => {
  const hasPhotos = !!(window.PHOTOS && window.PHOTOS[escale.id]);
  return (
    <div className="drawer-content fade-in" key={escale.id}>
      {/* Port switcher — navigation rapide entre escales */}
      <div className="port-switcher">
        <div className="ps-label">Changer d'escale</div>
        <div className="ps-list">
          {ESCALES.map(e => (
            <button
              key={e.id}
              className={`ps-btn ${e.id === escale.id ? "active" : ""}`}
              onClick={() => onSwitch(e.id)}
              title={e.nom}
            >
              <span className="ps-dot" style={{ background: e.couleur }}/>
              <span className="ps-name">{e.nom}</span>
            </button>
          ))}
        </div>
      </div>

      <div className="drawer-hero">
        <button className="dh-close" onClick={onClose} aria-label="Fermer le panneau">
          <Icon name="x" size={16}/>
        </button>
        <EscaleVisual escale={escale} placeholderText="Photo officielle en cours de sélection"/>
        <div className="drawer-hero-title">
          <div className="d-num">Fiche N°{String(escale.num).padStart(2,"0")} · {escale.facade}</div>
          <h2>{escale.nom}</h2>
          <div className="d-sub">{escale.nomAlt}</div>
        </div>
      </div>

      <div className="drawer-body">
        <button className="drawer-close-inline" onClick={onClose} aria-label="Fermer le panneau">
          <Icon name="x" size={16}/>
        </button>
        <div className="d-accroche">{escale.accroche}</div>

        <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
          <FiabilityBadge niveau={escale.fiabilite}/>
          <FacadeBadge facade={escale.facade}/>
        </div>

        {/* Tabs */}
        <div className="tabs">
          <button className={`tab ${tab === "decouvrir" ? "active" : ""}`} onClick={() => onTab("decouvrir")}>Découvrir</button>
          <button className={`tab ${tab === "operateurs" ? "active" : ""}`} onClick={() => onTab("operateurs")}>Infos opérateurs</button>
        </div>

        {tab === "decouvrir" && (
          <div key="decouvrir" className="col tab-content" style={{ gap: 14 }}>
            <p style={{ fontSize: 14, color: "var(--c-ink-soft)", lineHeight: 1.6, margin: 0 }}>{escale.description}</p>

            <div>
              <div className="section-mini-title"><Icon name="leaf" size={12} stroke={2.2}/>Expériences emblématiques</div>
              <ul className="excu-list">
                {escale.excursions.slice(0, 5).map((ex, i) => (
                  <li key={i}>
                    <span className="ex-bullet">→</span>
                    <span>
                      <span className="ex-name">{ex.nom}</span> — <span style={{ color: "var(--c-ink-soft)" }}>{ex.desc}</span>
                    </span>
                  </li>
                ))}
              </ul>
            </div>

            <div>
              <div className="section-mini-title"><Icon name="calendar" size={12} stroke={2.2}/>Saisonnalité</div>
              <SeasonBar months={escale.saisonOpti}/>
              <div className="note" style={{ marginTop: 6 }}>{escale.saisonnalite}</div>
            </div>
          </div>
        )}

        {tab === "operateurs" && (
          <div key="operateurs" className="col tab-content" style={{ gap: 14 }}>
            <div className="fact-grid">
              <Fact label="Mode d'accueil"        value={escale.typeAccueil.split("·")[0].trim()} mood="info"/>
              <Fact label="Quai / mouillage"      value={escale.quai}/>
              <Fact label="Profondeur"            value={escale.profondeur}/>
              <Fact label="Pilotage"              value={escale.pilotage} mood={statusMood(escale.pilotage)}/>
              <Fact label="Remorqueurs"           value={escale.remorqueurs} mood={statusMood(escale.remorqueurs)}/>
              <Fact label="Terminal passagers"    value={escale.terminal} mood={statusMood(escale.terminal)}/>
              <Fact label="Aéroport proche"       value={escale.aeroport}/>
              <Fact label="Segment recommandé"    value={escale.segment}/>
            </div>

            <div>
              <div className="section-mini-title"><Icon name="alert" size={12} stroke={2.2}/>Contrainte principale</div>
              <p style={{ fontSize: 13, color: "var(--c-ink-soft)", lineHeight: 1.55, margin: "8px 0 0" }}>{escale.contrainte}</p>
            </div>

            <div>
              <div className="section-mini-title"><Icon name="info" size={12} stroke={2.2}/>À confirmer</div>
              <ul style={{ margin: "8px 0 0", paddingLeft: 18, fontSize: 13, color: "var(--c-ink-soft)" }}>
                {escale.pointsAConfirmer.map((p,i) => <li key={i} style={{ padding: "3px 0" }}>{p}</li>)}
              </ul>
            </div>
          </div>
        )}

        <button className="btn btn-primary" style={{ width: "100%", justifyContent: "center", marginTop: 4 }} onClick={onOpenFiche}>
          Voir la fiche complète <Icon name="arrow-right" size={14}/>
        </button>

        {/* Mini gallery */}
        <div className="drawer-gallery">
          <div className="section-mini-title" style={{ marginBottom: 8 }}>
            <Icon name="info" size={12} stroke={2.2}/>
            Galerie
          </div>
          <div className="mini-gallery">
            {[null,0,1,2].map((photoIndex, i) => (
              <div className="mg-thumb" key={i}>
                <EscaleVisual escale={escale} placeholderText={null} showLabel={false} photoIndex={photoIndex}/>
              </div>
            ))}
          </div>
          {!hasPhotos && (
            <div className="note" style={{ marginTop: 6 }}>Visuel à intégrer prochainement · crédits photo à compléter.</div>
          )}
        </div>
      </div>
    </div>
  );
};

const SeasonBar = ({ months }) => {
  const set = new Set((months || []).map(m => m.toLowerCase()));
  return (
    <div className="season-bar" style={{ marginTop: 6 }}>
      {MONTHS_FR.map((m, i) => (
        <div className={`season-cell ${set.has(m.toLowerCase()) ? "on" : ""}`} key={i}>{m}</div>
      ))}
    </div>
  );
};

const Fact = ({ label, value, mood }) => (
  <div className={`fact-tile ${mood || ""}`}>
    <div className="ft-lbl">{label}</div>
    <div className="ft-val">{value}</div>
  </div>
);

function statusMood(v) {
  if (!v) return "no";
  const s = v.toLowerCase();
  if (s.includes("obligatoire") || s.includes("disponible") || s.includes("certifié") || s.includes("permanence")) return "ok";
  if (s.includes("sur demande") || s.includes("optionnel") || s.includes("réquisition") || s.includes("à confirmer")) return "warn";
  if (s.includes("non") || s.includes("absent") || s.includes("aucun") || s.includes("minimal") || s.includes("limit")) return "no";
  return "";
}

// ---------------------------------------------------------------
// Synthetic card (below map)
// ---------------------------------------------------------------
const SyntheticCard = ({ escale, navigate, onPreview }) => {
  return (
    <div className="escale-card" onClick={() => navigate(`#/escale/${escale.id}`)}>
      <div className="ec-visual">
        <EscaleVisual escale={escale} placeholderText={null}/>
        <div className="ec-num">{String(escale.num).padStart(2,"0")}</div>
        <div className="ec-fiab"><FiabilityBadge niveau={escale.fiabilite} small/></div>
      </div>
      <div className="ec-body">
        <div className="ec-title">
          <h3>{escale.nom}</h3>
          <span className="ec-alt">{escale.nomAlt}</span>
        </div>
        <div className="ec-tags">
          <FacadeBadge facade={escale.facade}/>
          <span className="badge">{escale.typeAccueil.split("·")[0].trim()}</span>
        </div>
        <div className="ec-desc">{escale.accroche}</div>
        <div className="col" style={{ gap: 6 }}>
          <div className="section-mini-title" style={{ fontSize: 10, paddingBottom: 4, borderBottom: 0 }}>
            <Icon name="leaf" size={10} stroke={2.2}/>3 expériences clés
          </div>
          <ul style={{ margin: 0, paddingLeft: 14, fontSize: 12.5, color: "var(--c-ink-soft)" }}>
            {escale.excursions.slice(0,3).map((ex,i) => <li key={i} style={{ padding: "2px 0" }}>{ex.nom}</li>)}
          </ul>
        </div>
        <div className="ec-foot">
          <span className="note" style={{ fontSize: 11 }}>{escale.segment.split("·")[0]}</span>
          <button className="btn btn-link btn-sm" style={{ padding: 0 }} onClick={(ev) => { ev.stopPropagation(); navigate(`#/escale/${escale.id}`); }}>
            Découvrir <Icon name="arrow-right" size={12}/>
          </button>
        </div>
      </div>
    </div>
  );
};

// ---------------------------------------------------------------
// Page — Les escales (grille des 10 cartes synthétiques)
// ---------------------------------------------------------------
const EscalesPage = ({ navigate }) => (
  <main className="app-main">
    <section className="section">
      <div className="container container-wide">
        <div className="section-head" style={{ marginBottom: 28, display: "flex", flexDirection: "row", justifyContent: "space-between", alignItems: "flex-end", flexWrap: "wrap", gap: 16, maxWidth: "none" }}>
          <div style={{ maxWidth: 520 }}>
            <span className="h-eyebrow">Les dix escales</span>
            <h1 className="h-section" style={{ marginTop: 8 }}>Cartes <em>synthétiques</em></h1>
          </div>
          <div style={{ display: "flex", flexDirection: "column", alignItems: "flex-end", gap: 10 }}>
            <div className="note" style={{ maxWidth: 360, textAlign: "right" }}>
              Cliquez sur une carte pour ouvrir la fiche détaillée de l'escale.
            </div>
            <button className="btn btn-ghost btn-sm" onClick={() => navigate("#/explorer")}>
              <Icon name="map" size={13}/> Voir la carte interactive
            </button>
          </div>
        </div>

        <div className="escales-card-grid">
          {ESCALES.map(e => (
            <SyntheticCard key={e.id} escale={e} navigate={navigate} onPreview={() => navigate(`#/escale/${e.id}`)}/>
          ))}
        </div>
      </div>
    </section>
  </main>
);

window.ExplorerPage = ExplorerPage;
window.EscalesPage = EscalesPage;
window.statusMood = statusMood;
window.SeasonBar = SeasonBar;
window.Fact = Fact;
