// ============================================================
// mapa.jsx — MapLibre: /mapa, karta lokalizacji, wizard /zglos
// ============================================================

const ORTHO_URL = "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTOFOTOMAPA&STYLE=default&FORMAT=image/jpeg&TILEMATRIXSET=EPSG:3857&TILEMATRIX=EPSG:3857:{z}&TILEROW={y}&TILECOL={x}";
const BASE_STYLE = "https://tiles.openfreemap.org/styles/positron";

function locsGeojson(locs) {
  return {
    type: "FeatureCollection",
    features: locs.map((l) => ({
      type: "Feature",
      geometry: { type: "Point", coordinates: [l.lng, l.lat] },
      properties: { id: l.id, slug: l.slug, name: l.name, src: l.source, ms: l.ms || 0, votes: votesFor(l.id).total },
    })),
  };
}

// ---------- rdzeń mapy ----------
function useMaplibre(ref, opts) {
  const mapRef = React.useRef(null);
  const [ready, setReady] = React.useState(false);
  React.useEffect(() => {
    const map = new maplibregl.Map({
      container: ref.current,
      style: BASE_STYLE,
      center: opts.center || [21.7656, 49.6925],
      zoom: opts.zoom != null ? opts.zoom : 12,
      attributionControl: { compact: true },
      interactive: opts.interactive !== false,
      scrollZoom: opts.scrollZoom !== false,
      preserveDrawingBuffer: true,
    });
    mapRef.current = map;
    if (opts.nav !== false && opts.interactive !== false) map.addControl(new maplibregl.NavigationControl({ showCompass: false }), "top-left");
    map.on("load", () => {
      map.addSource("ortho", { type: "raster", tiles: [ORTHO_URL], tileSize: 256, attribution: "Geoportal.gov.pl / GUGiK" });
      map.addLayer({ id: "ortho", type: "raster", source: "ortho", layout: { visibility: "none" } });
      setReady(true);
    });
    return () => map.remove();
  }, []);
  return [mapRef, ready];
}

function setOrtho(map, on) {
  if (map && map.getLayer("ortho")) map.setLayoutProperty("ortho", "visibility", on ? "visible" : "none");
}

function BasemapToggle({ value, onChange, style }) {
  return (
    <div style={{ display: "flex", background: "var(--surface)", border: "1px solid var(--line-strong)", borderRadius: var_r(), overflow: "hidden", boxShadow: "var(--shadow-1)", ...style }}>
      {[["map", "Mapa"], ["ortho", "Ortofoto"]].map(([v, l]) => (
        <button key={v} onClick={() => onChange(v)} style={{
          border: "none", cursor: "pointer", padding: "7px 13px", fontSize: 12.5, fontWeight: 600,
          fontFamily: "var(--font-mono)", background: value === v ? "var(--ink)" : "transparent",
          color: value === v ? "#fff" : "var(--ink-2)",
        }}>{l}</button>
      ))}
    </div>
  );
}
function var_r() { return "4px"; }

// ---------- /mapa ----------
function PageMapa() {
  const containerRef = React.useRef(null);
  const [mapRef, ready] = useMaplibre(containerRef, { center: [21.765, 49.694], zoom: 12, scrollZoom: false });
  React.useEffect(() => {
    if (!ready) return;
    const pts = publishedLocations().filter((l) => l.commune === "Krosno");
    if (pts.length > 1) {
      const b = new maplibregl.LngLatBounds();
      pts.forEach((l) => b.extend([l.lng, l.lat]));
      mapRef.current.fitBounds(b, { padding: 70, maxZoom: 13.5, duration: 0 });
    }
  }, [ready]);
  const s = useStore();
  const [basemap, setBasemap] = React.useState("map");
  const [filters, setFilters] = React.useState({ admin: true, user: true });
  const [sort, setSort] = React.useState("votes");
  const [query, setQuery] = React.useState("");
  const [viewport, setViewport] = React.useState(null);
  const [geo, setGeo] = React.useState(null);
  const markersRef = React.useRef([]);

  const locs = publishedLocations().filter((l) =>
    (filters[l.source]) &&
    (!query.trim() || (l.commune + " " + l.name).toLowerCase().includes(query.trim().toLowerCase()))
  );

  // markery HTML
  React.useEffect(() => {
    if (!ready) return;
    const map = mapRef.current;
    markersRef.current.forEach((m) => m.remove());
    markersRef.current = locs.map((l) => {
      const el = document.createElement("div");
      el.className = l.source === "admin" ? "marker-concept" : "marker-proposal";
      el.innerHTML = "<div class='pin'></div>";
      el.title = l.name;
      el.addEventListener("click", () => navigate("/lokalizacja/" + l.slug));
      return new maplibregl.Marker({ element: el, anchor: "bottom" }).setLngLat([l.lng, l.lat]).addTo(map);
    });
  }, [ready, JSON.stringify(locs.map((l) => l.id)), s.myVotes.length]);

  React.useEffect(() => { if (ready) setOrtho(mapRef.current, basemap === "ortho"); }, [basemap, ready]);

  React.useEffect(() => {
    if (!ready) return;
    const map = mapRef.current;
    const upd = () => setViewport(map.getBounds());
    map.on("moveend", upd); upd();
    return () => map.off("moveend", upd);
  }, [ready]);

  const inView = locs.filter((l) => !viewport || viewport.contains([l.lng, l.lat]));
  const sorted = [...inView].sort((a, b) => {
    if (sort === "votes") return votesFor(b.id).total - votesFor(a.id).total;
    if (sort === "new") return (b.createdAt || "").localeCompare(a.createdAt || "");
    if (sort === "near" && geo) return haversine(geo[0], geo[1], a.lat, a.lng) - haversine(geo[0], geo[1], b.lat, b.lng);
    return 0;
  });

  const askGeo = () => {
    setSort("near");
    if (geo) return;
    if (navigator.geolocation) navigator.geolocation.getCurrentPosition(
      (p) => setGeo([p.coords.latitude, p.coords.longitude]),
      () => { setGeo([49.6925, 21.7656]); toast("Brak zgody na lokalizację — sortuję od centrum Krosna"); }
    );
    else setGeo([49.6925, 21.7656]);
  };

  return (
    <div className="mapa-shell">
      {/* panel boczny */}
      <aside className="mapa-aside">
        <div style={{ padding: "16px 18px 12px", borderBottom: "1px solid var(--line)", display: "flex", flexDirection: "column", gap: 10 }}>
          <input className="input" placeholder="Szukaj gminy lub lokalizacji…" value={query} onChange={(e) => setQuery(e.target.value)} />
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center" }}>
            <button className={"badge " + (filters.admin ? "blue" : "")} style={{ cursor: "pointer", border: filters.admin ? "1px solid transparent" : undefined }}
              onClick={() => setFilters({ ...filters, admin: !filters.admin })}>▲ koncepcje · {publishedLocations().filter((l) => l.source === "admin").length}</button>
            <button className={"badge " + (filters.user ? "green" : "")} style={{ cursor: "pointer" }}
              onClick={() => setFilters({ ...filters, user: !filters.user })}>○ propozycje · {publishedLocations().filter((l) => l.source === "user").length}</button>
            <select className="select" style={{ width: "auto", marginLeft: "auto", padding: "5px 8px", fontSize: 12.5 }} value={sort}
              onChange={(e) => e.target.value === "near" ? askGeo() : setSort(e.target.value)}>
              <option value="votes">najwięcej głosów</option>
              <option value="new">najnowsze</option>
              <option value="near">najbliżej mnie</option>
            </select>
          </div>
        </div>
        <div className="scroll-y" style={{ flex: 1, padding: "10px 12px", display: "flex", flexDirection: "column", gap: 8 }}>
          {sorted.length === 0 && <p className="small muted" style={{ padding: 12 }}>Brak lokalizacji w bieżącym widoku mapy — oddal mapę lub zmień filtry.</p>}
          {sorted.map((l) => {
            const v = votesFor(l.id);
            return (
              <Link key={l.id} to={"/lokalizacja/" + l.slug} className="card pad hover" style={{ textDecoration: "none", padding: 14, display: "flex", flexDirection: "column", gap: 7 }}>
                <div style={{ display: "flex", justifyContent: "space-between", gap: 10, alignItems: "flex-start" }}>
                  <b style={{ fontSize: 14.5, lineHeight: 1.3 }}>{l.name}</b>
                  {l.source === "admin"
                    ? <span className="badge blue" style={{ flex: "none" }}>koncepcja</span>
                    : <span className="badge green" style={{ flex: "none" }}>propozycja</span>}
                </div>
                <div className="small muted">{l.commune}{l.ms ? <span className="mono"> · MS {l.ms} · mp {l.mp}</span> : null}</div>
                <div style={{ display: "flex", gap: 10, alignItems: "center" }}>
                  <span className="stat-num" style={{ fontSize: 16 }}>{fmt(v.total)}</span>
                  <span className="small muted">głosów</span>
                  <span className="small" style={{ color: "var(--green)", fontFamily: "var(--font-mono)", fontSize: 12 }}>✓ {v.verified}</span>
                </div>
              </Link>
            );
          })}
        </div>
        <div style={{ padding: 14, borderTop: "1px solid var(--line)" }}>
          <Link to="/zglos" className="btn green block">➕ Zaproponuj lokalizację</Link>
        </div>
      </aside>

      {/* mapa */}
      <div className="mapa-canvas">
        <div ref={containerRef} style={{ position: "absolute", inset: 0 }}></div>
        <BasemapToggle value={basemap} onChange={setBasemap} style={{ position: "absolute", top: 12, right: 12, zIndex: 10 }} />
        <div style={{ position: "absolute", bottom: 22, left: 12, zIndex: 10, display: "flex", gap: 8, flexDirection: "column" }}>
          <div className="card" style={{ padding: "9px 13px", display: "flex", gap: 14, fontSize: 12, boxShadow: "var(--shadow-1)" }}>
            <span style={{ display: "flex", alignItems: "center", gap: 7 }}>
              <span style={{ width: 0, height: 0, borderLeft: "7px solid transparent", borderRight: "7px solid transparent", borderBottom: "11px solid var(--accent)" }}></span>
              koncepcja autorska
            </span>
            <span style={{ display: "flex", alignItems: "center", gap: 7 }}>
              <span style={{ width: 11, height: 11, borderRadius: 99, background: "#fff", border: "3px solid var(--green)" }}></span>
              propozycja mieszkańców
            </span>
          </div>
        </div>
      </div>
    </div>
  );
}

// ---------- karta lokalizacji ----------
function PageLokalizacja({ slug }) {
  useStore();
  const loc = locBySlug(slug);
  const miniRef = React.useRef(null);
  const [basemap, setBasemap] = React.useState("map");
  const [mapRef, ready] = useMaplibre(miniRef, { center: loc ? [loc.lng, loc.lat] : undefined, zoom: 15, interactive: false, nav: false });

  React.useEffect(() => {
    if (!ready || !loc) return;
    const el = document.createElement("div");
    el.className = loc.source === "admin" ? "marker-concept" : "marker-proposal";
    el.innerHTML = "<div class='pin'></div>";
    const m = new maplibregl.Marker({ element: el, anchor: "bottom" }).setLngLat([loc.lng, loc.lat]).addTo(mapRef.current);
    return () => m.remove();
  }, [ready]);
  React.useEffect(() => { if (ready) setOrtho(mapRef.current, basemap === "ortho"); }, [basemap, ready]);

  if (!loc) return <div className="page container section"><h1>Nie znaleziono lokalizacji</h1><Link to="/mapa" className="btn ghost" style={{ marginTop: 16 }}>← Wróć do mapy</Link></div>;
  const nearby = nearbyLocations(loc.lat, loc.lng, 3000, loc.id).filter((l) => l.status === "published");

  return (
    <div className="page" style={{ background: "var(--bg)" }}>
      <div className="container" style={{ padding: "28px 28px 64px" }}>
        <Link to="/mapa" className="small" style={{ color: "var(--ink-2)", textDecoration: "none" }}>← Mapa lokalizacji</Link>
        <div style={{ display: "grid", gridTemplateColumns: "minmax(0,7fr) minmax(0,5fr)", gap: 28, marginTop: 14, alignItems: "start" }}>
          {/* lewa kolumna */}
          <div style={{ display: "flex", flexDirection: "column", gap: 18 }}>
            <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
              <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                {loc.source === "admin" ? <span className="badge blue">▲ koncepcja autorska</span> : <span className="badge green">○ propozycja mieszkańców</span>}
                <span className="badge">{loc.commune}</span>
                {loc.landCat && <span className="badge">{LAND_CATS[loc.landCat]}</span>}
              </div>
              <h1 style={{ fontSize: 32 }}>{loc.name}</h1>
              <p className="small muted mono" style={{ fontSize: 12 }}>{loc.addressLabel} · {loc.lat.toFixed(5)}, {loc.lng.toFixed(5)}</p>
            </div>

            <div className="card" style={{ position: "relative", overflow: "hidden" }}>
              <div ref={miniRef} style={{ height: 320 }}></div>
              <BasemapToggle value={basemap} onChange={setBasemap} style={{ position: "absolute", top: 10, right: 10, zIndex: 10 }} />
            </div>

            {loc.source === "admin" && (
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }}>
                <div className="card pad">
                  <div className="kicker" style={{ fontSize: 10.5 }}>miejsca schronienia</div>
                  <div className="stat-num" style={{ fontSize: 34, marginTop: 6 }}>MS {fmt(loc.ms)}</div>
                </div>
                <div className="card pad">
                  <div className="kicker" style={{ fontSize: 10.5 }}>miejsca parkingowe</div>
                  <div className="stat-num" style={{ fontSize: 34, marginTop: 6 }}>mp {fmt(loc.mp)}</div>
                </div>
              </div>
            )}

            <div className="card pad">
              <div className="kicker" style={{ fontSize: 10.5, marginBottom: 8 }}>{loc.source === "admin" ? "opis koncepcji" : "dlaczego to miejsce — opis zgłaszającego"}</div>
              <p style={{ fontSize: 15, color: "var(--ink-2)" }}>{loc.desc}</p>
            </div>

            {loc.source === "admin" && (
              <div>
                <div className="kicker" style={{ fontSize: 10.5, marginBottom: 10 }}>wizualizacja przed / po</div>
                <BeforeAfter />
              </div>
            )}

            {nearby.length > 0 && (
              <div>
                <div className="kicker" style={{ fontSize: 10.5, marginBottom: 10 }}>podobne lokalizacje w okolicy (3 km)</div>
                <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                  {nearby.slice(0, 4).map((n) => (
                    <Link key={n.id} to={"/lokalizacja/" + n.slug} className="card hover" style={{ padding: "11px 14px", textDecoration: "none", display: "flex", justifyContent: "space-between", gap: 10, alignItems: "center" }}>
                      <span style={{ fontWeight: 600, fontSize: 14 }}>{n.name}</span>
                      <span className="small muted mono">{Math.round(n.dist)} m · {fmt(votesFor(n.id).total)} głosów</span>
                    </Link>
                  ))}
                </div>
              </div>
            )}
          </div>

          {/* prawa kolumna — głosowanie */}
          <div style={{ position: "sticky", top: "calc(var(--nav-h) + 16px)", display: "flex", flexDirection: "column", gap: 14 }}>
            <div className="card pad" style={{ display: "flex", flexDirection: "column", gap: 16 }}>
              <VoteCounter locId={loc.id} size="lg" />
              <VoteDistribution locId={loc.id} />
              <hr style={{ border: "none", borderTop: "1px solid var(--line)", margin: 0 }} />
              <div style={{ fontWeight: 600, fontSize: 16 }}>Jestem zainteresowany / popieram</div>
              <VoteFlow loc={loc} />
            </div>
            <button className="btn ghost sm" onClick={() => { navigator.clipboard && navigator.clipboard.writeText(location.href); toast("Link do lokalizacji skopiowany"); }}>
              Udostępnij kartę lokalizacji ↗
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}

// ---------- wizard /zglos ----------
function PageZglos() {
  const [step, setStep] = React.useState(1);
  const [pin, setPin] = React.useState(null);          // {lat,lng}
  const [zoomOk, setZoomOk] = React.useState(false);
  const [label, setLabel] = React.useState("");
  const [dup, setDup] = React.useState(null);
  const [form, setForm] = React.useState({ name: "", desc: "", cat: "parking" });
  const [email, setEmail] = React.useState("");
  const [rodo, setRodo] = React.useState(false);
  const [ts, setTs] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const [stage, setStage] = React.useState("wizard");  // wizard | sent | done
  const containerRef = React.useRef(null);
  const [mapRef, ready] = useMaplibre(containerRef, { center: [21.7656, 49.6925], zoom: 13.4 });
  const [zoom, setZoom] = React.useState(13.4);

  React.useEffect(() => {
    if (!ready) return;
    const map = mapRef.current;
    const upd = () => { setZoom(map.getZoom()); setZoomOk(map.getZoom() >= 15); };
    map.on("zoom", upd); map.on("moveend", upd); upd();
  }, [ready]);

  const placePin = () => {
    const c = mapRef.current.getCenter();
    const p = { lat: c.lat, lng: c.lng };
    setPin(p);
    const near = nearbyLocations(p.lat, p.lng, 150);
    setDup(near[0] || null);
    setLabel("lokalizacja wskazana na mapie");
    // reverse geocoding (best effort, fallback po 1.5 s)
    const ctrl = new AbortController();
    const t = setTimeout(() => ctrl.abort(), 1500);
    fetch(`https://nominatim.openstreetmap.org/reverse?lat=${p.lat}&lon=${p.lng}&format=json&zoom=17&accept-language=pl`, { signal: ctrl.signal })
      .then((r) => r.json())
      .then((d) => {
        clearTimeout(t);
        if (d && d.address) {
          const a = d.address;
          const parts = [a.road, a.house_number, a.suburb || a.neighbourhood, a.city || a.town || a.village].filter(Boolean);
          if (parts.length) setLabel(parts.join(", "));
        }
      })
      .catch(() => {});
  };

  const submit = () => {
    if (!/.+@.+\..+/.test(email)) return setErr("Podaj prawidłowy adres e-mail.");
    if (!rodo) return setErr("Zgoda jest wymagana.");
    if (!ts) return setErr("Potwierdź weryfikację antybotową.");
    setErr(null);
    setStage("sent");
  };

  const confirmEmail = () => {
    const id = "P" + Date.now();
    const prop = {
      id, slug: slugify(form.name) + "-" + id.slice(-4), source: "user", status: "pending",
      name: form.name, commune: "Krosno", lat: pin.lat, lng: pin.lng, landCat: form.cat,
      addressLabel: label, desc: form.desc, createdAt: new Date().toISOString().slice(0, 10), mine: true,
    };
    Store.set({
      myProposals: [...Store.get().myProposals, prop],
      myVotes: [...Store.get().myVotes, { locId: id, interest: "support", verified: false, email, ts: Date.now() }],
    });
    Store.audit("user", "proposal_submitted", "location", { name: form.name });
    setStage("done");
  };

  const descLen = form.desc.trim().length;

  if (stage === "sent") return (
    <div className="page container section" style={{ maxWidth: 560 }}>
      <div className="card pad" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div style={{ fontSize: 30 }}>✉</div>
        <h2 style={{ fontSize: 22 }}>Potwierdź zgłoszenie</h2>
        <p className="muted small">Wysłaliśmy link na <b>{email}</b>. Propozycja trafi na mapę po potwierdzeniu e-mail i akceptacji moderatora (zwykle do 48 h). Zgłoszenie liczy się jednocześnie jako Twój pierwszy głos.</p>
        <button className="btn ghost sm" style={{ borderStyle: "dashed" }} onClick={confirmEmail}>▸ Prototyp: symuluj kliknięcie linku w e-mailu</button>
      </div>
    </div>
  );

  if (stage === "done") return (
    <div className="page container section" style={{ maxWidth: 560 }}>
      <div className="card pad" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <span className="badge amber" style={{ alignSelf: "flex-start" }}>oczekuje na moderację</span>
        <h2 style={{ fontSize: 22 }}>Dziękujemy! Propozycja „{form.name}" przyjęta</h2>
        <p className="muted small">Moderator zweryfikuje zgłoszenie (SLA: 48 h). Status możesz śledzić w panelu „Moje głosy i propozycje". Twój głos poparcia został zapisany.</p>
        <div style={{ display: "flex", gap: 8 }}>
          <Link to="/moje" className="btn primary sm">Moje propozycje</Link>
          <Link to="/mapa" className="btn ghost sm">Wróć na mapę</Link>
          <Link to="/admin" className="btn quiet sm">Zobacz kolejkę moderacji (demo) →</Link>
        </div>
      </div>
    </div>
  );

  return (
    <div className="page container" style={{ padding: "28px 28px 64px", maxWidth: 980 }}>
      <span className="kicker green">propozycja mieszkańca</span>
      <h1 style={{ fontSize: 30, margin: "10px 0 6px" }}>Zaproponuj lokalizację</h1>
      <p className="muted small" style={{ marginBottom: 20 }}>Wskaż miejsce (np. istniejący parking naziemny, nieużytek), gdzie widziałbyś parking podziemny z funkcją schronu.</p>

      {/* pasek kroków */}
      <div style={{ display: "flex", gap: 0, marginBottom: 22, borderBottom: "1px solid var(--line)" }}>
        {["Wskaż miejsce", "Opisz", "Potwierdź"].map((t, i) => (
          <div key={i} style={{
            padding: "10px 18px 12px", fontSize: 13.5, fontWeight: 600,
            color: step === i + 1 ? "var(--ink)" : step > i + 1 ? "var(--green)" : "var(--ink-3)",
            borderBottom: step === i + 1 ? "2px solid var(--accent)" : "2px solid transparent", marginBottom: -1,
            display: "flex", gap: 8, alignItems: "center",
          }}>
            <span className="stat-num" style={{ fontSize: 12 }}>{step > i + 1 ? "✓" : "0" + (i + 1)}</span>{t}
          </div>
        ))}
      </div>

      {/* KROK 1 */}
      <div style={{ display: step === 1 ? "block" : "none" }}>
        <div className="card" style={{ position: "relative", overflow: "hidden" }}>
          <div ref={containerRef} style={{ height: 440 }}></div>
          {/* celownik */}
          <div style={{ position: "absolute", left: "50%", top: "50%", transform: "translate(-50%,-100%)", pointerEvents: "none", zIndex: 5 }}>
            <div style={{ width: 0, height: 0, borderLeft: "13px solid transparent", borderRight: "13px solid transparent", borderTop: "22px solid " + (zoomOk ? "var(--green)" : "var(--ink-3)"), filter: "drop-shadow(0 2px 3px rgba(0,0,0,0.4))" }}></div>
          </div>
          <div style={{ position: "absolute", top: 10, right: 10, zIndex: 10 }} className="card">
            <div style={{ padding: "8px 12px", fontSize: 12, fontFamily: "var(--font-mono)", color: zoomOk ? "var(--green)" : "var(--ink-2)" }}>
              {zoomOk ? "✓ przybliżenie OK — możesz wskazać miejsce" : `przybliż mapę, by wskazać precyzyjnie (zoom ${zoom.toFixed(1)} / 15)`}
            </div>
          </div>
        </div>
        {pin && dup && (
          <div className="card pad" style={{ marginTop: 14, borderColor: "var(--amber)", background: "var(--amber-soft)", display: "flex", flexDirection: "column", gap: 10 }}>
            <b style={{ fontSize: 14.5 }}>To miejsce już zgłoszono — {Math.round(dup.dist)} m stąd: „{dup.name}"</b>
            <p className="small muted">Zamiast dodawać duplikat, możesz poprzeć istniejącą lokalizację — głosy się nie rozproszą.</p>
            <div style={{ display: "flex", gap: 8 }}>
              <Link to={"/lokalizacja/" + dup.slug} className="btn accent sm">Poprzyj „{dup.name}" →</Link>
              <button className="btn quiet sm" onClick={() => setDup(null)}>To inne miejsce — kontynuuj</button>
            </div>
          </div>
        )}
        {pin && !dup && (
          <div className="card pad" style={{ marginTop: 14, display: "flex", gap: 14, alignItems: "center", flexWrap: "wrap" }}>
            <span className="badge green">✓ pinezka ustawiona</span>
            <span className="small mono muted">{pin.lat.toFixed(5)}, {pin.lng.toFixed(5)}</span>
            <span className="small muted" style={{ flex: 1 }}>{label}</span>
          </div>
        )}
        <div style={{ display: "flex", gap: 10, marginTop: 16 }}>
          {!pin || dup
            ? <button className="btn primary" disabled={!zoomOk} onClick={placePin}>{pin ? "Przestaw pinezkę na środek mapy" : "Wskaż środek mapy pinezką"}</button>
            : <React.Fragment>
                <button className="btn ghost" onClick={placePin}>Przestaw pinezkę</button>
                <button className="btn primary" onClick={() => setStep(2)}>Dalej: opisz miejsce →</button>
              </React.Fragment>}
        </div>
      </div>

      {/* KROK 2 */}
      {step === 2 && (
        <div style={{ maxWidth: 560, display: "flex", flexDirection: "column", gap: 16 }}>
          <div className="field">
            <label>Nazwa robocza *</label>
            <input className="input" placeholder="np. Parking przy ul. Okrzei" value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} />
          </div>
          <div className="field">
            <label>Dlaczego to miejsce? *</label>
            <textarea className="textarea" placeholder="Opisz, dlaczego ta lokalizacja ma sens — co tu jest teraz, komu służyłby parking i schron…" value={form.desc} onChange={(e) => setForm({ ...form, desc: e.target.value })}></textarea>
            <span className="hint" style={{ color: descLen > 0 && descLen < 50 ? "var(--red)" : undefined }}>{descLen} / 50–500 znaków</span>
          </div>
          <div className="field">
            <label>Kategoria terenu</label>
            <select className="select" value={form.cat} onChange={(e) => setForm({ ...form, cat: e.target.value })}>
              <option value="parking">parking naziemny</option><option value="wasteland">nieużytek</option>
              <option value="square">plac</option><option value="other">inne</option>
            </select>
          </div>
          <div className="field">
            <label>Zdjęcie <span className="muted" style={{ fontWeight: 400 }}>(opcjonalnie, max 5 MB — EXIF usuwany serwerowo)</span></label>
            <input className="input" type="file" accept="image/*" />
          </div>
          <div style={{ display: "flex", gap: 10 }}>
            <button className="btn ghost" onClick={() => setStep(1)}>← Wróć</button>
            <button className="btn primary" disabled={!form.name.trim() || descLen < 50 || descLen > 500} onClick={() => setStep(3)}>Dalej: potwierdź →</button>
          </div>
        </div>
      )}

      {/* KROK 3 */}
      {step === 3 && (
        <div style={{ maxWidth: 560, display: "flex", flexDirection: "column", gap: 16 }}>
          <div className="card pad" style={{ background: "var(--surface-2)", borderColor: "transparent", display: "flex", flexDirection: "column", gap: 6 }}>
            <b style={{ fontSize: 15 }}>{form.name}</b>
            <span className="small muted">{label} · {LAND_CATS[form.cat]}</span>
            <span className="small muted mono" style={{ fontSize: 11.5 }}>{pin && pin.lat.toFixed(5)}, {pin && pin.lng.toFixed(5)}</span>
          </div>
          <div className="field">
            <label>Adres e-mail *</label>
            <input className="input" type="email" placeholder="twoj@email.pl" value={email} onChange={(e) => setEmail(e.target.value)} />
            <span className="hint">Zgłoszenie liczy się jednocześnie jako Twój głos poparcia dla tej lokalizacji.</span>
          </div>
          <label className="checkrow">
            <input type="checkbox" checked={rodo} onChange={(e) => setRodo(e.target.checked)} />
            <span>Wyrażam zgodę na przetwarzanie danych w celu obsługi propozycji i badania zainteresowania; akceptuję <Link to="/regulamin" style={{ color: "var(--accent)" }}>regulamin</Link>. <b>*</b></span>
          </label>
          <Turnstile ok={ts} onOk={() => setTs(true)} />
          {err && <div className="badge red" style={{ alignSelf: "flex-start" }}>{err}</div>}
          <div style={{ display: "flex", gap: 10 }}>
            <button className="btn ghost" onClick={() => setStep(2)}>← Wróć</button>
            <button className="btn green" onClick={submit}>Wyślij propozycję</button>
          </div>
          <Disclaimer />
        </div>
      )}
    </div>
  );
}

Object.assign(window, { PageMapa, PageLokalizacja, PageZglos, useMaplibre, setOrtho, BasemapToggle });
