// detail-screens.jsx — 5 destination pages for the home shortcuts
// Sticks tightly to the visual system established in screens.jsx
// (white canvas, coral accents, pill CTAs, section labels, RTL).

const { I, Pill, CircleIcon, AvatarPair, SectionLabel, TabBar, RowMenu } = window;

// QuickNav — pill of glass shortcuts on every sub-screen so user can hop without going home
function QuickNav({ current }) {
  const items = [
    { id: "planning",  label: "תכנון",   svg: I.map(16, "currentColor") },
    { id: "map",       label: "מפה",     svg: I.compass(16, "currentColor") },
    { id: "reminders", label: "תזכורות", svg: I.bell(16, "currentColor") },
    { id: "docs",      label: "מסמכים",  svg: I.bookmark(16, "currentColor") },
    { id: "packing",   label: "אריזה",   svg: I.bag(16, "currentColor") },
  ].filter(x => x.id !== current);
  return (
    <div className="no-scrollbar" style={{
      display: "flex", gap: 8, overflowX: "auto",
      padding: "12px 20px 6px",
    }}>
      {items.map(it => (
        <div key={it.id} data-nav={it.id} data-nav-replace="1" className="lg-chip" style={{
          flexShrink: 0, padding: "8px 14px", borderRadius: 9999,
          display: "inline-flex", alignItems: "center", gap: 6,
          fontSize: 13, fontWeight: 500, cursor: "pointer",
          color: "var(--ink)",
        }}>
          {it.svg}
          <span>{it.label}</span>
        </div>
      ))}
    </div>
  );
}
window.QuickNav = QuickNav;

/* ─────────────────────────── 6) MAP ─────────────────────────── */

function MapScreen() {
  const { useStore, A } = window;
  const { trip, memories, mapFilter, spots } = useStore(s => ({
    trip: s.trip, memories: s.memories || [], mapFilter: s.mapFilter, spots: s.spots || [],
  }));
  const mapRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const layerRef = React.useRef(null);
  const fitBoundsKeyRef = React.useRef(null);

  const destinations = (trip.destinations || []).map(d => ({
    type: "destination",
    name: window.destName(d),
    coords: window.destCoords(d),
  })).filter(d => d.coords);

  const memMarkers = memories
    .filter(m => m.locationCoords && m.locationCoords.lat != null)
    .map(m => ({ type: "memory", id: m.id, name: m.title, sub: m.location || "", coords: m.locationCoords }));

  const spotMarkers = spots
    .filter(sp => sp.lat != null && sp.lng != null)
    .map(sp => {
      const meta = (window.spotCatMeta && window.spotCatMeta(sp.cat)) || { color: "#34c759", icon: "📍" };
      return {
        type: "spot", id: sp.id, name: sp.title, sub: sp.dest || sp.sub || "",
        coords: { lat: sp.lat, lng: sp.lng }, color: meta.color, glyph: meta.icon,
      };
    });

  const _all = [...destinations, ...memMarkers, ...spotMarkers];
  const allMarkers = (() => {
    const f = mapFilter || "הכל";
    if (f === "יעדים") return destinations;
    if (f === "זיכרונות") return memMarkers;
    if (f === "מקומות") return spotMarkers;
    return _all;
  })();

  // Init Leaflet map — destroy on unmount so re-entry rebuilds cleanly.
  // Without this cleanup, navigating away and back leaves the old map instance
  // pointing at a destroyed DOM node → silent breakage on second visit.
  React.useEffect(() => {
    if (!containerRef.current || mapRef.current || !window.L) return;
    const firstDest = destinations[0] && destinations[0].coords;
    const start = allMarkers[0]?.coords || firstDest || { lat: 13.7563, lng: 100.5018 };
    const map = window.L.map(containerRef.current, { zoomControl: false, attributionControl: false }).setView([start.lat, start.lng], allMarkers.length > 0 ? 5 : 4);
    window.L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19 }).addTo(map);
    window.L.control.zoom({ position: "bottomleft" }).addTo(map);
    mapRef.current = map;
    const t = setTimeout(() => map.invalidateSize(), 100);
    return () => {
      clearTimeout(t);
      try {
        if (layerRef.current) { layerRef.current.remove(); layerRef.current = null; }
        map.remove();
      } catch (e) { console.warn("[MapScreen] leaflet teardown:", e); }
      mapRef.current = null;
    };
  }, []);

  // Stable hash of marker shape — recomputes only when allMarkers identity
  // changes, not on every parent render. Used as the marker-re-render dep.
  const _markerHash = React.useMemo(
    () => allMarkers.map(m => `${m.type}|${m.coords.lat.toFixed(5)},${m.coords.lng.toFixed(5)}|${m.name}|${m.sub || ""}`).join("§"),
    [allMarkers]
  );

  // Re-render markers when data changes. Clusters nearby pins on low zoom.
  React.useEffect(() => {
    if (!mapRef.current || !window.L) return;
    const map = mapRef.current;
    const esc = (s) => String(s == null ? "" : s)
      .replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;").replace(/'/g, "&#39;");

    // Group markers whose projected pixel positions are within CLUSTER_PX of
    // each other at the current zoom. At high zoom (>= 13) clusters dissolve
    // so individual pins show again.
    const CLUSTER_PX = 48;
    function buildClusters() {
      const zoom = map.getZoom();
      if (zoom >= 13 || allMarkers.length < 4) return allMarkers.map(m => ({ kind: "single", m }));
      const items = allMarkers.map(m => ({
        m,
        pt: map.project([m.coords.lat, m.coords.lng], zoom),
        used: false,
      }));
      const out = [];
      for (let i = 0; i < items.length; i++) {
        if (items[i].used) continue;
        const seed = items[i];
        seed.used = true;
        const group = [seed.m];
        for (let j = i + 1; j < items.length; j++) {
          if (items[j].used) continue;
          const dx = items[j].pt.x - seed.pt.x;
          const dy = items[j].pt.y - seed.pt.y;
          if (Math.hypot(dx, dy) <= CLUSTER_PX) { items[j].used = true; group.push(items[j].m); }
        }
        out.push(group.length === 1 ? { kind: "single", m: group[0] } : { kind: "cluster", group });
      }
      return out;
    }

    function renderLayer() {
      if (layerRef.current) layerRef.current.remove();
      const group = window.L.layerGroup().addTo(map);
      layerRef.current = group;
      const items = buildClusters();
      for (const it of items) {
        if (it.kind === "cluster") {
          const lats = it.group.map(g => g.coords.lat);
          const lngs = it.group.map(g => g.coords.lng);
          const center = {
            lat: lats.reduce((a, b) => a + b, 0) / lats.length,
            lng: lngs.reduce((a, b) => a + b, 0) / lngs.length,
          };
          const count = it.group.length;
          const size = count > 9 ? 48 : 40;
          const icon = window.L.divIcon({
            className: "ma-pin",
            html: `<div style="
              width:${size}px;height:${size}px;border-radius:9999px;
              background:linear-gradient(135deg, #ff7a8b, var(--coral));
              border:2.5px solid #fff;box-shadow:0 8px 20px -4px rgba(255,78,100,.55);
              display:flex;align-items:center;justify-content:center;color:#fff;
              font-size:${count > 9 ? 16 : 15}px;font-weight:700;font-family:inherit;
            ">${count}</div>`,
            iconSize: [size, size], iconAnchor: [size / 2, size / 2],
          });
          const marker = window.L.marker([center.lat, center.lng], { icon }).addTo(group);
          marker.on("click", () => {
            // Zoom into the cluster bounds
            const bounds = window.L.latLngBounds(it.group.map(g => [g.coords.lat, g.coords.lng]));
            map.fitBounds(bounds, { padding: [60, 60], maxZoom: 15 });
          });
          continue;
        }
        const m = it.m;
        // bg + glyph are interpolated INTO HTML strings by Leaflet.divIcon, so
        // sanitize before injection. bg goes into a `background:` CSS rule —
        // accept only `var(--…)` or a hex literal. glyph goes inside <span> —
        // escape HTML special chars. Defends against a malicious m.color /
        // m.glyph entering via share-import or a future sync regression.
        const rawBg = m.type === "destination" ? "var(--coral)" : m.type === "spot" ? (m.color || "#34c759") : "var(--action)";
        const bg = /^(var\(--[\w-]+\)|#[0-9a-fA-F]{3,8})$/.test(rawBg) ? rawBg : "#34c759";
        const rawGlyph = m.type === "destination" ? "★" : m.type === "spot" ? (m.glyph || "📍") : "♥";
        const glyph = esc(rawGlyph);
        const icon = window.L.divIcon({
          className: "ma-pin",
          html: `<div style="
            width:32px;height:32px;border-radius:50% 50% 50% 0;transform:rotate(-45deg);
            background:${bg};
            border:2px solid #fff;box-shadow:0 6px 14px rgba(0,0,0,.3);
            display:flex;align-items:center;justify-content:center;color:#fff;
            font-size:14px;font-weight:700;font-family:inherit;
          "><span style="transform:rotate(45deg);">${glyph}</span></div>`,
          iconSize: [32, 32], iconAnchor: [16, 32], popupAnchor: [0, -32],
        });
        const marker = window.L.marker([m.coords.lat, m.coords.lng], { icon }).addTo(group);
        marker.bindPopup(`<div dir="rtl" style="font-family:inherit;direction:rtl;text-align:right;min-width:160px"><div style="font-weight:600;font-size:14px;margin-bottom:4px">${esc(m.name)}</div>${m.sub ? `<div style="color:#6a6a72;font-size:12px">${esc(m.sub)}</div>` : ""}</div>`);
      }
    }

    renderLayer();
    // Above the cluster-dissolve threshold (>=13) all markers are singles —
    // re-rendering on every zoom step thrashes the DOM for no visible change.
    // Only re-render when zoom transitions between "clustered" and "singles",
    // or when staying in the clustered range (where pixel projection differs
    // per zoom step).
    let prevWasSingles = map.getZoom() >= 13;
    const safeRender = () => {
      if (!mapRef.current) return;
      const z = map.getZoom();
      const nowSingles = z >= 13;
      if (nowSingles && prevWasSingles) return; // both above threshold — clusters unchanged
      prevWasSingles = nowSingles;
      renderLayer();
    };
    map.on("zoomend", safeRender);

    // Dedup fitBounds — only re-fit when the marker-set positions actually
    // changed, so tapping filter chips that produce the same set doesn't
    // trigger a distracting re-zoom animation every time.
    const boundsKey = allMarkers.map(m => `${m.coords.lat.toFixed(4)},${m.coords.lng.toFixed(4)}`).sort().join("|");
    if (allMarkers.length > 1 && fitBoundsKeyRef.current !== boundsKey) {
      const bounds = window.L.latLngBounds(allMarkers.map(m => [m.coords.lat, m.coords.lng]));
      map.fitBounds(bounds, { padding: [40, 40] });
      fitBoundsKeyRef.current = boundsKey;
    } else if (allMarkers.length === 1 && fitBoundsKeyRef.current !== boundsKey) {
      map.setView([allMarkers[0].coords.lat, allMarkers[0].coords.lng], 11);
      fitBoundsKeyRef.current = boundsKey;
    }
    return () => { map.off("zoomend", safeRender); };
    // Memoized hash — was O(n) string alloc on every render (including
    // unrelated Store.set ticks). React.useMemo recomputes only when
    // allMarkers itself changes identity, which happens far less often.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [_markerHash]);

  const recenter = () => {
    if (!navigator.geolocation || !mapRef.current) return;
    A.showToast("מאתר מיקום...", "info");
    navigator.geolocation.getCurrentPosition(
      pos => {
        // The user may have navigated away during GPS acquisition (3-8s on cellular).
        // mapRef.current goes null on unmount; setView on a destroyed map throws.
        if (!mapRef.current) return;
        mapRef.current.setView([pos.coords.latitude, pos.coords.longitude], 14);
      },
      (err) => {
        if (!mapRef.current) return;
        const msg = err && err.code === 1 ? "צריך לאשר גישה למיקום" : "לא ניתן לאתר מיקום";
        A.showToast(msg, "error");
      },
      { enableHighAccuracy: true, timeout: 8000 }
    );
  };

  return (
    <div dir="rtl" style={{ background: "var(--canvas)", color: "var(--ink)", minHeight: "100%", position: "relative", paddingBottom: 96, display: "flex", flexDirection: "column" }}>
      {/* header */}
      <div style={{ padding: "4px 20px 12px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <CircleIcon size={36} data-nav="back" style={{ cursor: "pointer" }}>{I.back(18)}</CircleIcon>
        <div style={{ fontSize: 16, fontWeight: 600 }}>מפת הטיול</div>
        <div style={{ width: 36, height: 36 }} />
      </div>

      <QuickNav current="map" />

      {/* Map filter chips */}
      <div className="no-scrollbar" style={{ display: "flex", gap: 8, overflowX: "auto", padding: "0 20px 12px" }}>
        {[
          { k: "הכל",       count: _all.length },
          { k: "יעדים",     count: destinations.length },
          { k: "מקומות",    count: spotMarkers.length },
          { k: "זיכרונות",  count: memMarkers.length },
        ].map(f => {
          const active = (mapFilter || "הכל") === f.k;
          return (
            <div key={f.k} onClick={() => A.setMapFilter(f.k)} className={`lg-chip ${active ? "is-active" : ""}`} style={{
              flexShrink: 0, padding: "8px 14px", borderRadius: 9999,
              fontSize: 12, fontWeight: 500, cursor: "pointer",
            }}>{f.k} ({f.count})</div>
          );
        })}
      </div>

      {/* real Leaflet map */}
      <div style={{
        margin: "0 20px", borderRadius: 18, overflow: "hidden",
        position: "relative", height: 420, background: "#e4e8ec",
      }}>
        <div ref={containerRef} style={{ position: "absolute", inset: 0 }} />
        <button onClick={recenter} className="lg-light" style={{
          position: "absolute", bottom: 14, insetInlineEnd: 14, zIndex: 1000,
          width: 44, height: 44, borderRadius: 9999, border: "none", cursor: "pointer",
          display: "flex", alignItems: "center", justifyContent: "center",
        }}>
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none">
            <circle cx="12" cy="12" r="3" fill="var(--action)"/>
            <circle cx="12" cy="12" r="9" stroke="var(--action)" strokeWidth="1.5" fill="none"/>
            <path d="M12 2v3M12 19v3M2 12h3M19 12h3" stroke="var(--action)" strokeWidth="1.5" strokeLinecap="round"/>
          </svg>
        </button>
        {allMarkers.length === 0 && (
          <div className="lg-light" style={{
            position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", zIndex: 1000,
            padding: "14px 22px", borderRadius: 14, fontSize: 13, textAlign: "center", maxWidth: 240,
          }}>
            עדיין אין מיקומים על המפה.<br/>
            <span style={{ color: "var(--muted)", fontSize: 12 }}>הוסיפי יעדים או זיכרונות עם מיקום</span>
          </div>
        )}
      </div>

      {/* legend / list */}
      <div style={{ padding: "18px 20px 0" }}>
        <SectionLabel>{allMarkers.length} מיקומים</SectionLabel>
        <div style={{ marginTop: 10 }}>
          {allMarkers.map((m, i) => (
            <div key={i} onClick={() => mapRef.current?.setView([m.coords.lat, m.coords.lng], 13)} style={{
              display: "flex", alignItems: "center", gap: 12,
              padding: "12px 0", cursor: "pointer",
              borderBottom: i < allMarkers.length - 1 ? "1px solid var(--hairline-soft)" : "none",
            }}>
              <div style={{
                width: 32, height: 32, borderRadius: 9999,
                background: m.type === "destination" ? "var(--coral)" : m.type === "spot" ? (m.color || "#34c759") : "var(--action)",
                color: "#fff", display: "flex", alignItems: "center", justifyContent: "center",
                fontSize: 14, fontWeight: 700, flexShrink: 0,
              }}>{m.type === "destination" ? "★" : m.type === "spot" ? (m.glyph || "📍") : "♥"}</div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 14, fontWeight: 600, letterSpacing: "-.01em" }}>{m.name}</div>
                <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 2 }}>
                  {m.type === "destination" ? "יעד" : m.type === "spot" ? (m.sub || "מקום") : (m.sub || "זיכרון")}
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>

    </div>
  );
}

/* ─────────────────────────── 7) REMINDERS ─────────────────────────── */

function RemindersScreen() {
  const { useStore, A } = window;
  const reminders = useStore(s => s.reminders);
  const SECTION_ORDER = ["דחוף", "השבוע", "החודש"];
  // Single-pass aggregation: every count the screen needs (sections, hero
  // totals, stats strip) derived from one walk over the reminders array.
  // Previously the render body invoked 7 separate .filter() chains per render
  // — flagged as HIGH by the perf audit. Memoized so unrelated Store.set
  // notifications (toasts, sheets) don't recompute.
  const agg = React.useMemo(() => {
    const sectionMap = new Map(SECTION_ORDER.map(t => [t, []]));
    let total = 0, done = 0, urgent = 0;
    let flight = 0, payment = 0, general = 0;
    for (const r of reminders) {
      total++;
      if (r.done) done++;
      if (!r.done && r.urgent) urgent++;
      if (sectionMap.has(r.section)) sectionMap.get(r.section).push(r);
      if (!r.done) {
        const t = (r.title || "").toLowerCase();
        // Broadened classifier — was matching only "טיסה"/"תשלום". Users wrote
        // "טיסות", "מקדמה", "flight", and got bucketed as "כללי" silently.
        if (t.includes("טיס") || t.includes("flight") || t.includes("airline") || t.includes("שדה תעופה")) flight++;
        else if (t.includes("תשלום") || t.includes("חיוב") || t.includes("מקדמה") || t.includes("payment") || t.includes("invoice")) payment++;
        else general++;
      }
    }
    const sections = SECTION_ORDER
      .map(title => {
        const items = sectionMap.get(title) || [];
        return { title, count: items.filter(it => !it.done).length, items };
      })
      .filter(sec => sec.items.length > 0);
    return { sections, total, done, urgent, flight, payment, general };
  }, [reminders]);
  const sections = agg.sections;

  return (
    <div dir="rtl" style={{ background: "var(--canvas)", color: "var(--ink)", minHeight: "100%", position: "relative", paddingBottom: 100 }}>
      {/* header */}
      <div style={{ padding: "4px 20px 12px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <CircleIcon size={36} data-nav="back" style={{ cursor: "pointer" }}>{I.back(18)}</CircleIcon>
        <div style={{ fontSize: 16, fontWeight: 600 }}>תזכורות</div>
        <CircleIcon size={36} onClick={() => A.addReminder()} className="lg-coral" style={{ cursor: "pointer" }}>{I.plus(20, "#fff")}</CircleIcon>
      </div>

      <QuickNav current="reminders" />

      {/* gradient progress hero — at-a-glance status before the buckets */}
      {(() => {
        const total = agg.total;
        const done = agg.done;
        const urgent = agg.urgent;
        const pct = total > 0 ? Math.round((done / total) * 100) : 0;
        return (
          <div style={{
            margin: "0 20px 14px", borderRadius: 18, padding: "16px 18px",
            background: urgent > 0
              ? "linear-gradient(135deg, #ff7a8b 0%, var(--coral) 55%, #c64b78 100%)"
              : "linear-gradient(135deg, #4ec5c2 0%, #1a6e8f 60%, #6bb6e0 100%)",
            color: "#fff", position: "relative", overflow: "hidden",
            boxShadow: "0 12px 28px -10px rgba(0,0,0,.32)",
          }}>
            <div className="aurora aurora-soft" aria-hidden="true" />
            <div style={{ position: "relative" }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: ".14em", opacity: .9 }}>{urgent > 0 ? `${urgent} דחופות פתוחות` : "סטטוס תזכורות"}</div>
                <div aria-hidden="true">{urgent > 0 ? BUDGET_ICONS.warn(26, "#fff") : BUDGET_ICONS.check(26, "#fff")}</div>
              </div>
              <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
                <div className="ltr-inline" style={{ fontSize: 36, fontWeight: 800, letterSpacing: "-.03em", textShadow: "0 1px 8px rgba(0,0,0,.18)" }}>{done}/{total}</div>
                <div style={{ fontSize: 13, opacity: .92 }}>{total === 0 ? "אין תזכורות עדיין" : `${pct}% הושלמו`}</div>
              </div>
              {total > 0 && (
                <div style={{ height: 6, borderRadius: 9999, background: "rgba(255,255,255,.25)", marginTop: 10, position: "relative", overflow: "hidden" }}>
                  <div style={{ position: "absolute", inset: 0, width: `${pct}%`, background: "#fff", borderRadius: 9999, transition: "width .35s ease" }} />
                </div>
              )}
            </div>
          </div>
        );
      })()}

      {/* stats strip — typed reminder buckets */}
      <div style={{ padding: "0 20px 18px", display: "flex", gap: 10 }}>
        {[
          { n: agg.flight,  label: "טיסה",     bg: "var(--coral-soft)",    fg: "var(--coral-press)",
            icon: BUDGET_ICONS.plane(14, "currentColor") },
          { n: agg.payment, label: "תשלומים",  bg: "rgba(10,102,214,.10)", fg: "var(--action-press)",
            icon: BUDGET_ICONS.money(14, "currentColor") },
          { n: agg.general, label: "כללי",      bg: "var(--surface-soft)",  fg: "var(--ink)",
            icon: BUDGET_ICONS.bell(14, "currentColor") },
        ].map((s, i) => (
          <div key={i} style={{
            flex: 1, padding: "12px 14px", borderRadius: 12, background: s.bg,
          }}>
            <div style={{ display: "flex", alignItems: "center", gap: 6, color: s.fg }}>
              {s.icon}
              <span style={{ fontSize: 11, fontWeight: 600, letterSpacing: ".06em", textTransform: "uppercase", opacity: .85 }}>{s.label}</span>
            </div>
            <div style={{ fontSize: 24, fontWeight: 600, color: s.fg, letterSpacing: "-.02em", marginTop: 4 }}>{s.n}</div>
          </div>
        ))}
      </div>

      {/* sections */}
      <div style={{ padding: "0 20px" }}>
        {sections.map((sec, i) => (
          <div key={i} style={{ marginBottom: 22 }}>
            <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", marginBottom: 8 }}>
              <SectionLabel>{sec.title}</SectionLabel>
              <span style={{ fontSize: 11, color: "var(--muted-soft)", fontFamily: "var(--mono)" }}>{sec.count} פתוחות</span>
            </div>
            <div style={{
              borderRadius: 14, background: "var(--canvas)",
              border: "1px solid var(--hairline)", overflow: "hidden",
            }}>
              {sec.items.map((it, j) => (
                <div key={it.id}
                  onClick={(e) => {
                    // Don't toggle if the tap originated inside the row's
                    // overflow menu — fixes the long-standing "menu tap also
                    // ticks the reminder" race.
                    if (e.target.closest && e.target.closest("[data-rowmenu]")) return;
                    A.toggleReminder(it.id);
                  }}
                  style={{
                  padding: "13px 14px", display: "flex", alignItems: "center", gap: 12,
                  borderBottom: j < sec.items.length - 1 ? "1px solid var(--hairline-soft)" : "none",
                  background: it.urgent ? "var(--coral-soft)" : "transparent",
                  opacity: it.done ? .55 : 1,
                  cursor: "pointer", minHeight: 44,
                  borderInlineStart: it.color ? `4px solid ${it.color}` : "none",
                }}>
                  {/* checkbox */}
                  <div style={{
                    width: 22, height: 22, borderRadius: 9999, flexShrink: 0,
                    background: it.done ? "var(--ink)" : "transparent",
                    border: it.done ? "1px solid var(--ink)" : "1.5px solid " + (it.urgent ? "var(--coral)" : "var(--border-strong, #c1c1c1)"),
                    display: "flex", alignItems: "center", justifyContent: "center",
                  }}>
                    {it.done && <svg width="12" height="12" viewBox="0 0 24 24" fill="none"><path d="M5 12l5 5 9-11" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{
                      fontSize: 14, fontWeight: 500, letterSpacing: "-.01em",
                      textDecoration: it.done ? "line-through" : "none",
                    }}>{it.title}</div>
                    <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 2 }}>{it.sub}</div>
                  </div>
                  <div style={{
                    fontSize: 11, fontWeight: 600, letterSpacing: ".04em",
                    color: it.urgent ? "var(--coral-press)" : "var(--muted)",
                    textTransform: "uppercase", whiteSpace: "nowrap",
                  }}>{it.when}</div>
                  {RowMenu && (
                    <RowMenu
                      label={it.title}
                      collection="reminders"
                      itemId={it.id}
                      onEdit={() => A.editReminder(it.id)}
                      onDelete={() => A.deleteReminder(it.id)}
                    />
                  )}
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>

    </div>
  );
}

/* ─────────────────────────── 8) BUDGET ─────────────────────────── */

// Compact 3-way currency calculator (ILS ↔ USD ↔ THB).
// Live rates from open.er-api.com (free, no key), static fallback.
function CurrencyMini() {
  const STATIC_RATES = { ILS: 1, USD: 0.27, THB: 9.5 };
  // Share the global rates slice — pull-to-refresh / A.fetchRates updates ONE
  // source instead of two diverging caches. Was: this component kept its own
  // fetch + state and got out of sync with BudgetScreen after refresh.
  const storeRates = window.useStore(s => s.rates);
  const rates = (storeRates && (storeRates.USD || storeRates.THB))
    ? { ILS: 1, USD: storeRates.USD || STATIC_RATES.USD, THB: storeRates.THB || STATIC_RATES.THB }
    : STATIC_RATES;
  const ratesUpdated = window.useStore(s => s.ratesUpdated);
  const [edited, setEdited] = React.useState({ cur: "ILS", raw: "100" });

  // Derive all 3 display values from the single edited field.
  const fmtFor = (cur, x) => {
    if (!isFinite(x)) return "";
    const decimals = cur === "USD" ? 2 : 0;
    return x.toLocaleString("en-US", { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
  };
  const cleanRaw = String(edited.raw || "").replace(/,/g, "");
  const n = parseFloat(cleanRaw);
  const ils = isFinite(n) ? n / (rates[edited.cur] || 1) : NaN;
  const vals = { ILS: "", USD: "", THB: "" };
  ["ILS", "USD", "THB"].forEach(cur => {
    if (cur === edited.cur) vals[cur] = edited.raw;
    else vals[cur] = isFinite(ils) ? fmtFor(cur, ils * (rates[cur] || 1)) : "";
  });

  const onChange = (cur, raw) => {
    const cleaned = raw.replace(/[^\d.,]/g, "");
    setEdited({ cur, raw: cleaned });
  };

  const CFG = [
    { cur: "ILS", symbol: "₪", label: "שקל" },
    { cur: "USD", symbol: "$", label: "דולר" },
    { cur: "THB", symbol: "฿", label: "באט" },
  ];

  const lastUpdated = (() => {
    if (!ratesUpdated) return null;
    const mins = Math.round((Date.now() - ratesUpdated) / 60000);
    if (mins < 1) return "עכשיו";
    if (mins < 60) return `לפני ${mins} ד׳`;
    const hrs = Math.round(mins / 60);
    if (hrs < 24) return `לפני ${hrs} ש׳`;
    const days = Math.round(hrs / 24);
    return `לפני ${days} י׳`;
  })();

  return (
    <div style={{
      margin: "0 20px 14px", borderRadius: 18, padding: 16,
      background: "var(--parchment)",
      border: "1px solid var(--hairline)",
    }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 12 }}>
        <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: ".12em", color: "var(--muted)" }}>
          מחשבון המרה
        </div>
        {lastUpdated && (
          <div style={{
            display: "inline-flex", alignItems: "center", gap: 6,
            padding: "3px 9px", borderRadius: 9999,
            background: "rgba(0,0,0,.04)",
            fontSize: 10, fontWeight: 600, color: "var(--muted)",
            letterSpacing: ".02em",
          }}>
            <span style={{ width: 5, height: 5, borderRadius: 9999, background: "#0a7a3d" }} />
            עודכן {lastUpdated}
          </div>
        )}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 8 }}>
        {CFG.map(({ cur, symbol, label }) => {
          const isActive = edited.cur === cur;
          return (
            <label key={cur} style={{
              display: "flex", flexDirection: "column", gap: 6,
              borderRadius: 14, padding: "12px 12px 10px",
              background: isActive ? "#fff" : "rgba(255,255,255,.55)",
              border: isActive ? "1.5px solid var(--coral)" : "1px solid var(--hairline)",
              boxShadow: isActive ? "0 6px 18px -6px rgba(255,78,100,.32)" : "none",
              cursor: "text",
              transition: "background .15s ease, border-color .15s ease, box-shadow .2s ease",
            }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
                <div style={{
                  width: 26, height: 26, borderRadius: 9999,
                  background: isActive ? "var(--coral)" : "rgba(0,0,0,.05)",
                  color: isActive ? "#fff" : "var(--ink)",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  fontSize: 13, fontWeight: 700,
                  transition: "background .15s ease, color .15s ease",
                }}>
                  <span className="ltr-inline">{symbol}</span>
                </div>
                <div style={{
                  fontSize: 10, fontWeight: 600, color: "var(--muted)",
                  letterSpacing: ".03em",
                }}>{label}</div>
              </div>
              <input
                type="text"
                inputMode="decimal"
                value={vals[cur]}
                onChange={e => onChange(cur, e.target.value)}
                onFocus={e => { setEdited(p => p.cur === cur ? p : { cur, raw: vals[cur] || "" }); e.target.select(); }}
                className="ltr-inline num-tabular"
                aria-label={`סכום ב${label}`}
                style={{
                  border: "none", background: "transparent", outline: "none",
                  fontSize: 22, fontWeight: 700, color: "var(--ink)",
                  padding: 0, width: "100%", direction: "ltr",
                  letterSpacing: "-.02em",
                  fontFeatureSettings: '"tnum" 1, "lnum" 1',
                }}
              />
            </label>
          );
        })}
      </div>
    </div>
  );
}

// Domain icon set for budget wallets + categories. Stroke-based, matches the
// rest of the app's I.* icon vocabulary. Emoji glyphs caused render
// inconsistency across iOS / Safari / Chrome; SVG resolves that.
// Material-style FILLED silhouettes — single brand language for every
// gradient card, wallet tile, category row, and section hero across the
// app. No strokes: filled shapes read better on coral/blue gradients and
// match the polish of the plane icon the user signed off on.
const BUDGET_ICONS = {
  plane:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M21 16v-2l-8-5V3.5a1.5 1.5 0 0 0-3 0V9l-8 5v2l8-2.5V19l-2 1.5V22l3.5-1 3.5 1v-1.5L13 19v-5.5L21 16z" fill={c}/></svg>,
  hotel:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M3 21V8h8v3h10v10H3zm3-2h3v-2H6v2zm5 0h3v-2h-3v2zm5 0h3v-2h-3v2zM7 13a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z" fill={c}/></svg>,
  shield:   (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M12 2 3 5v6c0 5.5 3.8 10.7 9 12 5.2-1.3 9-6.5 9-12V5l-9-3zm-1.3 14.3-3.4-3.4 1.4-1.4 2 2 5-5 1.4 1.4-6.4 6.4z" fill={c}/></svg>,
  fork:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M8 3v8a2 2 0 0 0 2 2v8h2v-8a2 2 0 0 0 2-2V3h-1v6h-1V3h-1v6h-1V3H8zm9 0c-1.5 2-2 4-2 6s.5 4 2 4v8h2V3h-2z" fill={c}/></svg>,
  taxi:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M19.4 7.6 18 4H6L4.6 7.6A2 2 0 0 0 3 9.5V17h2v2h2v-2h10v2h2v-2h2V9.5a2 2 0 0 0-1.6-1.9zM6.5 8h11l1 3h-13l1-3zM6 15a1.2 1.2 0 1 1 0-2.4A1.2 1.2 0 0 1 6 15zm12 0a1.2 1.2 0 1 1 0-2.4 1.2 1.2 0 0 1 0 2.4zM9 5h6v1.5H9V5z" fill={c}/></svg>,
  bag:      (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M19 7h-3V5a4 4 0 0 0-8 0v2H5l-1 14h16l-1-14zm-9-2a2 2 0 0 1 4 0v2h-4V5z" fill={c}/></svg>,
  ticket:   (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M22 10V8a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2v2a2 2 0 0 1 0 4v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a2 2 0 0 1 0-4zm-8 7h-2v-2h2v2zm0-3h-2v-2h2v2zm0-3h-2V9h2v2zm0-3h-2V7h2v1z" fill={c}/></svg>,
  gift:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M20 6h-2.2c.13-.31.2-.65.2-1a3 3 0 0 0-5.5-1.65L12 4l-.5-.65A3 3 0 0 0 6 5c0 .35.07.69.2 1H4v4h1v11h14V10h1V6zm-5-1a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM9 4a1 1 0 0 1 2 0v1H9V4zm2 15H6v-7h5v7zm2-7h5v7h-5v-7zM6 10V8h12v2H6z" fill={c}/></svg>,
  coin:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm1 16h-2v-1H8v-2h5v-1H9V9h2V7h2v2h3v2h-5v1h4v4h-2v2z" fill={c}/></svg>,
  cart:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M7 18a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm10 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4zM2 2v2h2l3.6 7.6L6 14.5c-.16.3-.25.6-.25 1 0 1.1.9 2 2 2h12v-2H8.4a.25.25 0 0 1-.2-.4l1-1.6h7.5c.7 0 1.4-.4 1.7-1l3.6-6.5H5.2L4.3 2H2z" fill={c}/></svg>,
  shirt:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M16 3l-2 2h-4L8 3 4 5l2 4 2-.5V21h8V8.5L18 9l2-4-4-2z" fill={c}/></svg>,
  lotion:   (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M9 2h6v3l1 3v12a2 2 0 0 1-2 2H10a2 2 0 0 1-2-2V8l1-3V2zm1 9v3h4v-3h-4z" fill={c}/></svg>,
  plug:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M16 7V3h-2v4h-4V3H8v4H6v4a6 6 0 0 0 5 5.9V22h2v-5.1A6 6 0 0 0 18 11V7h-2z" fill={c}/></svg>,
  pill:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M19.5 4.5a5 5 0 0 0-7 0l-8 8a5 5 0 0 0 7 7l8-8a5 5 0 0 0 0-7zm-8.5 11-2.5-2.5 4-4L15 11.5l-4 4z" fill={c}/></svg>,
  doc:      (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M14 2H6v20h14V8l-6-6zm0 1.5L18.5 8H14V3.5zM8 12h8v2H8v-2zm0 4h8v2H8v-2zm0-8h4v2H8V8z" fill={c}/></svg>,
  backpack: (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M9 2v2c-2 0-3 1.5-3 3v14h12V7c0-1.5-1-3-3-3V2h-2v2h-2V2H9zm-1 9h8v2H9v-2zm0 4h8v2H9v-2z" fill={c}/></svg>,
  money:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M21 6H3a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2zm-9 9a3 3 0 1 1 0-6 3 3 0 0 1 0 6z" fill={c}/></svg>,
  warn:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M12 2 1 21h22L12 2zm1 16h-2v-2h2v2zm0-4h-2V9h2v5z" fill={c}/></svg>,
  trophy:   (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M19 5V3H5v2H1v3a4 4 0 0 0 4 4h.4A6 6 0 0 0 10 15.9V18H7v3h10v-3h-3v-2.1A6 6 0 0 0 18.6 12H19a4 4 0 0 0 4-4V5h-4zM3 8V7h2v3a2 2 0 0 1-2-2zm18 0a2 2 0 0 1-2 2V7h2v1z" fill={c}/></svg>,
  users:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8zm0 2c-3.3 0-7 1.7-7 5v3h14v-3c0-3.3-3.7-5-7-5zm10-2a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm.5 2H17.5c1.2 1.1 2.5 2.7 2.5 4.5V21h4v-3c0-2.7-3-5-4-5z" fill={c}/></svg>,
  sparkle:  (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M12 2 14 9 22 12 14 15 12 22 10 15 2 12 10 9 12 2z" fill={c}/></svg>,
  photo:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M4 5h3l2-2h6l2 2h3a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V7a2 2 0 0 1 2-2zm8 13a5 5 0 1 0 0-10 5 5 0 0 0 0 10zm0-2a3 3 0 1 1 0-6 3 3 0 0 1 0 6z" fill={c}/></svg>,
  check:    (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zm-1.3 14.3L5.4 11l1.4-1.4 3.9 3.9 7.5-7.5L19.6 7.4l-8.9 8.9z" fill={c}/></svg>,
  bell:     (s = 18, c = "currentColor") => <svg width={s} height={s} viewBox="0 0 24 24"><path d="M12 22a2 2 0 0 0 2-2h-4a2 2 0 0 0 2 2zm6-6V11a6 6 0 1 0-12 0v5l-2 2v1h16v-1l-2-2z" fill={c}/></svg>,
};
window.BUDGET_ICONS = BUDGET_ICONS;

function BudgetScreen() {
  const { useStore, A, convertAmount, CURRENCY_SYMBOL } = window;
  const { expenses, stays, trip, rates } = useStore(s => ({
    expenses: s.expenses, stays: s.stays, trip: s.trip, rates: s.rates,
  }));

  const localCur = trip.localCurrency;
  const hasLocal = !!localCur && localCur !== "ILS";

  const CAT_DEFS = [
    { label: "טיסות",     color: "#ff4e64", iconKey: "plane",  group: "serious" },
    { label: "לינה",      color: "#0A66D6", iconKey: "hotel",  group: "serious" },
    { label: "ביטוח",     color: "#0a52a6", iconKey: "shield", group: "serious" },
    { label: "אוכל",      color: "#0a7a3d", iconKey: "fork",   group: "daily" },
    { label: "תחבורה",    color: "#7c5fbc", iconKey: "taxi",   group: "daily" },
    { label: "קניות",     color: "#d56ba3", iconKey: "bag",    group: "daily" },
    { label: "אטרקציות",  color: "#b56b3a", iconKey: "ticket", group: "exp" },
    { label: "מתנות",     color: "#e0973a", iconKey: "gift",   group: "gifts" },
    { label: "אחר",       color: "var(--muted-soft)", iconKey: "coin", group: "other" },
  ];

  // Pure expense-row classifier — also used by the expense list render below,
  // not just the memoized aggregations.
  const expCat = (e) => e.cat || (e.s ? e.s.split("·")[0].trim() : "אחר");

  // All expense/stay aggregations recompute only when expenses/stays/rates
  // change — unrelated state updates (toast, sheets, filters) no longer cost
  // O(N) over the full ledger on each render.
  const { catSums, totalSpentIls, walletTotals, splits } = React.useMemo(() => {
    const expIls = (e) => convertAmount(Number(e.a) || 0, e.cur || "ILS", "ILS", rates);
    function parseCostStr(s) {
      if (!s) return 0;
      const m = String(s).match(/(-?\d+(?:[.,]\d+)?)/);
      if (!m) return 0;
      const n = parseFloat(m[1].replace(",", "."));
      if (isNaN(n)) return 0;
      const cur = /\$/.test(s) ? "USD" : /฿/.test(s) ? "THB" : /€/.test(s) ? "EUR" : /£/.test(s) ? "GBP" : "ILS";
      return convertAmount(n, cur, "ILS", rates);
    }
    const catSums = {};
    const splits = {};
    expenses.forEach(e => {
      const cat = expCat(e);
      const ils = expIls(e);
      catSums[cat] = (catSums[cat] || 0) + ils;
      const who = e.by || "—";
      splits[who] = (splits[who] || 0) + ils;
    });
    const staysIls = stays.reduce((sum, st) => sum + (st.paid ? parseCostStr(st.cost) : 0), 0);
    if (staysIls > 0) catSums["לינה"] = (catSums["לינה"] || 0) + staysIls;
    const totalSpentIls = Object.values(catSums).reduce((a, b) => a + b, 0);
    const walletTotals = { serious: 0, daily: 0, exp: 0, gifts: 0, other: 0 };
    CAT_DEFS.forEach(d => { walletTotals[d.group] += catSums[d.label] || 0; });
    return { catSums, totalSpentIls, walletTotals, splits };
  }, [expenses, stays, rates]);

  const totalBudgetIls = trip.totalBudget || 0;
  const pct = totalBudgetIls > 0 ? Math.min(100, Math.round((totalSpentIls / totalBudgetIls) * 100)) : 0;

  const fmt = n => "₪" + Math.round(Number(n) || 0).toLocaleString();
  const fmtLocal = n => {
    if (!hasLocal) return "";
    const local = convertAmount(n, "ILS", localCur, rates);
    return (CURRENCY_SYMBOL[localCur] || localCur) + Math.round(local).toLocaleString();
  };

  return (
    <div dir="rtl" className="ma-bg-screen ma-bg-home" style={{ background: "var(--canvas)", color: "var(--ink)", minHeight: "100%", position: "relative", paddingBottom: 100, overflow: "hidden" }}>
      {/* header */}
      <div style={{ padding: "4px 20px 18px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <img src="assets/logo.png?v=69" alt="MA-Trip" style={{ height: 48, width: "auto", display: "block", filter: "saturate(1.18) drop-shadow(0 6px 14px rgba(0,0,0,.32))" }} />
        <div style={{ fontSize: 16, fontWeight: 600 }}>תקציב</div>
        <CircleIcon size={36} onClick={() => A.addExpense()} className="lg-coral" style={{ cursor: "pointer" }}>{I.plus(20, "#fff")}</CircleIcon>
      </div>

      <QuickNav current="budget" />

      {/* compact currency converter — ILS ↔ USD ↔ THB, type once → see all */}
      <CurrencyMini />

      {/* live insights hero — replaces the old parchment card (was a visual
          duplicate of the slider). Tap on the budget slide opens the budget
          editor when none is set yet. */}
      {window.InsightSlider && (() => {
        const remaining = Math.max(0, totalBudgetIls - totalSpentIls);
        const over = totalSpentIls - totalBudgetIls;
        const topCat = Object.entries(catSums).sort((a, b) => b[1] - a[1])[0];
        const splitRows = Object.entries(splits).filter(([, v]) => v > 0);
        const slides = [];
        if (totalBudgetIls > 0) {
          slides.push({
            eyebrow: "תקציב",
            title: remaining > 0 ? `נשארו ${fmt(remaining)}` : `חרגנו ${fmt(over)}`,
            body: `${pct}% מהתקציב נוצל · ${fmt(totalSpentIls)}${hasLocal ? " ≈ " + fmtLocal(totalSpentIls) : ""}`,
            icon: remaining > 0 ? BUDGET_ICONS.money(28, "#fff") : BUDGET_ICONS.warn(28, "#fff"),
            gradient: remaining > 0
              ? "linear-gradient(135deg, #ffd57a 0%, #f6a345 40%, #e0973a 100%)"
              : "linear-gradient(135deg, #ff7a8b 0%, var(--coral) 55%, #c64b78 100%)",
            onTap: () => A.editTotalBudget(),
          });
        } else {
          slides.push({
            eyebrow: "תקציב",
            title: "טרם הוגדר תקציב",
            body: "הקישו כדי להגדיר את התקציב הכולל לטיול",
            icon: BUDGET_ICONS.money(28, "#fff"),
            gradient: "linear-gradient(135deg, #ffd57a 0%, #f6a345 40%, #e0973a 100%)",
            onTap: () => A.editTotalBudget(),
          });
        }
        if (topCat) {
          slides.push({
            eyebrow: "הוצאה מובילה",
            title: topCat[0],
            body: `${fmt(topCat[1])} (${totalSpentIls > 0 ? Math.round(topCat[1] / totalSpentIls * 100) : 0}% מסך ההוצאות)`,
            icon: BUDGET_ICONS.trophy(28, "#fff"),
            gradient: "linear-gradient(135deg, #4ec5c2 0%, #1a6e8f 60%, #6bb6e0 100%)",
          });
        }
        if (splitRows.length > 0) {
          slides.push({
            eyebrow: "חלוקה לפי מי שילם",
            title: splitRows.map(([who, sum]) => `${who} ${fmt(sum)}`).join(" · "),
            body: `${expenses.length} הוצאות בסך הכל`,
            icon: BUDGET_ICONS.users(28, "#fff"),
            gradient: "linear-gradient(135deg, #af52de 0%, #7c5fbc 60%, #d56ba3 100%)",
          });
        }
        slides.push({
          eyebrow: stays.length > 0 ? "לינה" : "טיפ",
          title: stays.length > 0
            ? `${stays.filter(s => s.paid).length}/${stays.length} הזמנות שולמו`
            : "הוסיפו תקציב יומי",
          body: stays.length > 0
            ? "השלימו תשלום חסר מהמסך למטה"
            : `${(trip.days || 0) > 0 ? "כ-" + fmt(Math.round(totalBudgetIls / Math.max(1, trip.days || 0))) + " ליום" : "פיצול לפי ימים עוזר במעקב"}`,
          icon: BUDGET_ICONS.hotel(28, "#fff"),
          gradient: "linear-gradient(135deg, #ff7a8b 0%, var(--coral) 50%, #ff7e5f 100%)",
        });
        return <window.InsightSlider items={slides} height={160} />;
      })()}

      {/* stays / lodging bookings */}
      <div style={{ padding: "20px 20px 0" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 10 }}>
          <SectionLabel>הזמנות לינה</SectionLabel>
          <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
            <span style={{ fontSize: 11, color: "var(--muted)", fontFamily: "var(--mono)" }}>
              {stays.filter(s => s.paid).length}/{stays.length} שולמו
            </span>
            <button onClick={() => A.addStay()} style={{
              fontSize: 13, color: "var(--action)", background: "transparent",
              border: "none", cursor: "pointer", fontFamily: "inherit", padding: 0, fontWeight: 600,
            }}>+ לינה</button>
          </div>
        </div>
        <div style={{ borderRadius: 14, background: "var(--canvas)", border: "1px solid var(--hairline)", overflow: "hidden" }}>
          {stays.map((s, i) => (
            <div key={s.id} onClick={() => A.toggleStayPaid(s.id)} style={{
              padding: "13px 14px", display: "flex", alignItems: "center", gap: 12,
              borderBottom: i < stays.length - 1 ? "1px solid var(--hairline-soft)" : "none",
              cursor: "pointer", opacity: s.paid ? .6 : 1,
              borderInlineStart: s.color ? `4px solid ${s.color}` : "none",
            }}>
              <div style={{
                width: 22, height: 22, borderRadius: 6, flexShrink: 0,
                background: s.paid ? "var(--coral)" : "transparent",
                border: s.paid ? "1px solid var(--coral)" : "1.5px solid var(--border-strong, #c1c1c1)",
                display: "flex", alignItems: "center", justifyContent: "center",
              }}>
                {s.paid && <svg width="12" height="12" viewBox="0 0 24 24" fill="none"><path d="M5 12l5 5 9-11" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
              </div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 14, fontWeight: 600, letterSpacing: "-.01em", textDecoration: s.paid ? "line-through" : "none" }}>
                  {s.destination}{s.note ? ` · ${s.note}` : ""}
                </div>
                <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 2 }}>
                  {s.nights} לילות · <span className="ltr-inline">{s.cost}</span>
                  {s.paymentDue && !s.paid && (
                    <> · <span style={{ color: "var(--coral-press)", fontWeight: 600 }}>תשלום עד {s.paymentDue}</span></>
                  )}
                </div>
              </div>
              {RowMenu && (
                <RowMenu
                  label={s.destination + (s.note ? " · " + s.note : "")}
                  collection="stays"
                  itemId={s.id}
                  onEdit={() => A.editStay(s.id)}
                  onDelete={() => A.deleteStay(s.id)}
                />
              )}
            </div>
          ))}
        </div>
      </div>

      {/* budget pots — wallets derived from real expenses */}
      <div style={{ padding: "20px 20px 0" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 10 }}>
          <SectionLabel>ארנקי תקציב</SectionLabel>
          <span style={{ fontSize: 11, color: "var(--muted)", fontFamily: "var(--mono)" }}>
            {expenses.length} הוצאות
          </span>
        </div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 10 }}>
          {[
            { key: "serious", label: "רציני",   sub: "טיסות · לינה · ביטוח",         bar: "linear-gradient(90deg, #0A66D6, #4FA3FF)", ic: "rgba(10,102,214,.12)", icFg: "#0A66D6", icon: "plane" },
            { key: "daily",   label: "יום־יום", sub: "אוכל · תחבורה · קניות",          bar: "linear-gradient(90deg, var(--coral), #ff7e5f)", ic: "var(--coral-soft)", icFg: "var(--coral-press)", icon: "cart" },
            { key: "exp",     label: "חוויות",  sub: "אטרקציות · סיורים · כרטיסים",   bar: "linear-gradient(90deg, #0a7a3d, #5ab57a)", ic: "rgba(10,122,61,.12)", icFg: "#0a7a3d", icon: "ticket" },
            { key: "gifts",   label: "מתנות",   sub: "מזכרות · משלוחים הביתה",         bar: "linear-gradient(90deg, #e0973a, #ffcc66)", ic: "rgba(224,151,58,.14)", icFg: "#a86a18", icon: "gift" },
          ].map((p, i) => {
            const spent = walletTotals[p.key] || 0;
            const ratio = totalSpentIls > 0 ? spent / totalSpentIls : 0;
            const pctOfTotal = Math.round(ratio * 100);
            const barPct = totalBudgetIls > 0 ? Math.min(100, (spent / totalBudgetIls) * 100) : 0;
            return (
              <div key={i} style={{
                padding: "14px 14px 16px", borderRadius: 14,
                background: "var(--canvas)", border: "1px solid var(--hairline)",
              }}>
                <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
                  <div style={{
                    width: 34, height: 34, borderRadius: 10,
                    background: p.ic, color: p.icFg,
                    display: "flex", alignItems: "center", justifyContent: "center",
                  }}>{BUDGET_ICONS[p.icon](18, p.icFg)}</div>
                  <div className="ltr-inline" style={{ fontSize: 11, fontWeight: 600, color: "var(--muted)" }}>{pctOfTotal}%</div>
                </div>
                <div style={{ fontSize: 14, fontWeight: 600, letterSpacing: "-.01em", marginTop: 10 }}>{p.label}</div>
                <div style={{ fontSize: 11, color: "var(--muted)", marginTop: 2, lineHeight: 1.35 }}>{p.sub}</div>
                <div style={{ fontSize: 13, marginTop: 8 }}>
                  <span className="ltr-inline" style={{ fontWeight: 700 }}>{fmt(spent)}</span>
                  {hasLocal && (
                    <span className="ltr-inline" style={{ color: "var(--muted)", fontFamily: "var(--mono)", fontSize: 11, marginInlineStart: 6 }}>
                      ≈ {fmtLocal(spent)}
                    </span>
                  )}
                </div>
                <div style={{ height: 5, borderRadius: 9999, background: "var(--surface-soft)", marginTop: 8, overflow: "hidden" }}>
                  <div style={{ height: "100%", width: `${barPct}%`, background: p.bar, borderRadius: 9999, transition: "width .3s ease" }} />
                </div>
              </div>
            );
          })}
        </div>
      </div>

      {/* categories — derived from real expenses */}
      <div style={{ padding: "22px 20px 0" }}>
        <div className="lg-bento" style={{ padding: "18px 18px 16px" }}>
        <SectionLabel>קטגוריות</SectionLabel>
        <div style={{ marginTop: 12, display: "grid", gap: 14 }}>
          {CAT_DEFS.filter(c => (catSums[c.label] || 0) > 0 || c.label === "אחר").map((c, i) => {
            const spent = catSums[c.label] || 0;
            const p = totalSpentIls > 0 ? Math.round((spent / totalSpentIls) * 100) : 0;
            return (
              <div key={i}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline", marginBottom: 6 }}>
                  <div style={{ fontSize: 14, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 }}>
                    <span style={{
                      width: 24, height: 24, borderRadius: 7,
                      background: `${c.color}1a`, color: c.color,
                      display: "inline-flex", alignItems: "center", justifyContent: "center",
                      flexShrink: 0,
                    }}>{BUDGET_ICONS[c.iconKey] ? BUDGET_ICONS[c.iconKey](14, c.color) : null}</span>
                    <span>{c.label}</span>
                  </div>
                  <div style={{ fontSize: 12, color: "var(--muted)" }}>
                    <span className="ltr-inline" style={{ color: "var(--ink)", fontWeight: 600 }}>{fmt(spent)}</span>
                    {hasLocal && spent > 0 && (
                      <span className="ltr-inline" style={{ fontFamily: "var(--mono)", marginInlineStart: 6 }}> ≈ {fmtLocal(spent)}</span>
                    )}
                  </div>
                </div>
                <div style={{ height: 6, borderRadius: 9999, background: "var(--surface-soft)", position: "relative", overflow: "hidden" }}>
                  <div style={{ position: "absolute", inset: 0, width: `${p}%`, background: c.color, borderRadius: 9999, transition: "width .3s ease" }} />
                </div>
              </div>
            );
          })}
        </div>
        </div>
      </div>

      {/* recent */}
      <div style={{ padding: "22px 20px 0" }}>
        <div className="lg-bento" style={{ padding: "18px 18px 4px" }}>
        <div style={{ display: "flex", justifyContent: "space-between", alignItems: "baseline" }}>
          <SectionLabel>הוצאות אחרונות</SectionLabel>
        </div>
        <div style={{ marginTop: 10 }}>
          {expenses.length === 0 && (
            <div style={{ padding: "20px 0", fontSize: 13, color: "var(--muted)", textAlign: "center" }}>
              עוד אין הוצאות. הקישו על + למעלה כדי להוסיף.
            </div>
          )}
          {expenses.slice(0, 8).map((r, i) => {
            const cat = expCat(r);
            const def = CAT_DEFS.find(c => c.label === cat) || CAT_DEFS[CAT_DEFS.length - 1];
            const cur = r.cur || "ILS";
            const sym = CURRENCY_SYMBOL[cur] || cur;
            const ils = convertAmount(r.a, cur, "ILS", rates);
            return (
              <div key={r.id} style={{
                padding: "12px 0", display: "flex", alignItems: "center", gap: 12,
                borderBottom: i < Math.min(expenses.length, 8) - 1 ? "1px solid var(--hairline-soft)" : "none",
                borderInlineStart: r.color ? `4px solid ${r.color}` : "none",
                paddingInlineStart: r.color ? 10 : 0,
              }}>
                <div style={{
                  width: 38, height: 38, borderRadius: 10,
                  background: def.color + "1f", color: def.color,
                  display: "flex", alignItems: "center", justifyContent: "center",
                  flexShrink: 0,
                }}>{BUDGET_ICONS[def.iconKey] ? BUDGET_ICONS[def.iconKey](20, def.color) : null}</div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 14, fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{r.t}</div>
                  <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 2 }}>
                    {cat} · שילם {r.by}
                    {r.updatedBy && <span style={{ opacity: .8 }}> · עודכן ע״י {window.userName(r.updatedBy)}</span>}
                  </div>
                </div>
                <div style={{ textAlign: "end", minWidth: 0 }}>
                  <div className="ltr-inline" style={{ fontSize: 15, fontWeight: 700 }}>{sym + Number(r.a).toLocaleString(undefined, { maximumFractionDigits: 2 })}</div>
                  {cur !== "ILS" && (
                    <div className="ltr-inline" style={{ fontSize: 11, color: "var(--muted)", fontFamily: "var(--mono)", marginTop: 2 }}>≈ ₪{Math.round(ils).toLocaleString()}</div>
                  )}
                </div>
                {RowMenu && (
                  <RowMenu
                    label={r.t}
                    collection="expenses"
                    itemId={r.id}
                    onEdit={() => A.editExpense(r.id)}
                    onDelete={() => A.deleteExpense(r.id)}
                  />
                )}
              </div>
            );
          })}
        </div>
        </div>
      </div>

    </div>
  );
}

/* ─────────────────────────── 9) DOCUMENTS ─────────────────────────── */

function DocumentsScreen() {
  const { useStore, A } = window;
  const { docTab, documents } = useStore(s => ({ docTab: s.docTab, documents: s.documents || [] }));
  const galleryRef = React.useRef(null);
  const cameraRef = React.useRef(null);

  const handleFile = (e) => {
    const f = e.target.files && e.target.files[0];
    if (f) A.addDocumentScan(f);
    e.target.value = "";
  };

  // Prefer the native picker (real iOS Camera/Photos sheet) when running in Capacitor.
  // Falls back to the hidden web <input> so PWA users still work.
  const pickViaNativeOrWeb = async (source) => {
    if (window.MaNative && window.MaNative.isNative) {
      try {
        const file = await window.MaNative.pickPhoto({ source });
        if (file) A.addDocumentScan(file);
        return;
      } catch (e) { console.warn("[MaNative] doc pick failed, falling back:", e); }
    }
    (source === "camera" ? cameraRef : galleryRef).current?.click();
  };

  return (
    <div dir="rtl" style={{ background: "var(--canvas)", color: "var(--ink)", minHeight: "100%", position: "relative", paddingBottom: 110 }}>
      {/* hidden inputs — capture="environment" opens rear camera on mobile */}
      <input ref={cameraRef} type="file" accept="image/*" capture="environment" style={{ display: "none" }} onChange={handleFile} />
      <input ref={galleryRef} type="file" accept="image/*,application/pdf" style={{ display: "none" }} onChange={handleFile} />

      {/* header */}
      <div style={{ padding: "4px 20px 12px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <CircleIcon size={36} data-nav="back" aria-label="חזרה">{I.back(18)}</CircleIcon>
        <div style={{ fontSize: 16, fontWeight: 600 }}>מסמכים</div>
        <CircleIcon size={36} className="lg-coral" onClick={() => pickViaNativeOrWeb("camera")} aria-label="סריקת מסמך">
          {I.camera(18, "#fff")}
        </CircleIcon>
      </div>

      <QuickNav current="docs" />

      {/* gradient hero — total docs + recently added */}
      {(() => {
        const total = (documents || []).length;
        const latest = (documents || [])[0];
        return (
          <div style={{
            margin: "0 20px 14px", borderRadius: 18, padding: "16px 18px",
            background: "linear-gradient(135deg, #ffd57a 0%, #f6a345 40%, #e0973a 100%)",
            color: "#fff", position: "relative", overflow: "hidden",
            boxShadow: "0 12px 28px -10px rgba(224,151,58,.4)",
          }}>
            <div className="aurora aurora-soft" aria-hidden="true" />
            <div style={{ position: "relative" }}>
              <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
                <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: ".14em", opacity: .95 }}>מסמכים</div>
                <div aria-hidden="true">{BUDGET_ICONS.doc(26, "#fff")}</div>
              </div>
              <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
                <div className="ltr-inline" style={{ fontSize: 36, fontWeight: 800, letterSpacing: "-.03em", textShadow: "0 1px 8px rgba(0,0,0,.18)" }}>{total}</div>
                <div style={{ fontSize: 13, opacity: .95 }}>{total === 0 ? "ארכבו דרכון, ביטוח, אישורים" : `${total === 1 ? "מסמך" : "מסמכים"} נשמרים מוצפנים`}</div>
              </div>
              {latest && (
                <div style={{ fontSize: 11, marginTop: 8, opacity: .9 }}>אחרון: {latest.title || "ללא כותרת"}</div>
              )}
            </div>
          </div>
        );
      })()}

      {/* Action row — Scan + Upload */}
      <div style={{ padding: "0 20px 14px", display: "flex", gap: 10 }}>
        <Pill kind="coral" onClick={() => pickViaNativeOrWeb("camera")} style={{ flex: 1 }}>
          {I.camera(16, "#fff")} סרקי מסמך
        </Pill>
        <Pill kind="secondary" onClick={() => pickViaNativeOrWeb("gallery")} style={{ flex: 1, color: "var(--ink)" }}>
          {I.plus(16, "var(--action)")} העלי מהגלריה
        </Pill>
      </div>

      {/* Documents grid or empty state */}
      {documents.length === 0 ? (
        <div className="lg-light" style={{
          margin: "0 20px", borderRadius: 18, padding: "32px 22px",
          textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 14,
        }}>
          <div style={{ width: 56, height: 56, borderRadius: 9999, display: "flex", alignItems: "center", justifyContent: "center", background: "var(--coral-soft)" }}>
            {I.camera(26, "var(--coral)")}
          </div>
          <div style={{ fontSize: 17, fontWeight: 600 }}>עדיין אין מסמכים</div>
          <div style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.5 }}>
            סרקי דרכון, כרטיסי טיסה ואישורי לינה — הכל יישמר כאן
          </div>
        </div>
      ) : (
        <div style={{ padding: "0 20px", display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          {documents.map(d => (
            <div key={d.id} className="lg-light" style={{
              borderRadius: 14, overflow: "hidden", padding: 8,
            }}>
              <div onClick={async () => {
                // Resolve to a Blob and open via blob: URL — never interpolate
                // the resolved src into raw HTML (was an XSS sink for crafted
                // share-imported `image` fields with `javascript:` or `"onerror=`).
                let blob = null;
                if (window.MaMedia && window.MaMedia.getBlob && window.MaMedia.isRef && window.MaMedia.isRef(d.image)) {
                  blob = await window.MaMedia.getBlob(d.image);
                } else if (typeof d.image === "string" && (d.image.startsWith("data:image/") || d.image.startsWith("blob:"))) {
                  try { blob = await (await fetch(d.image)).blob(); } catch (e) { console.warn("[doc preview fetch]", d.id, e); }
                }
                if (!blob) { window.A.showToast("התמונה אינה זמינה", "error"); return; }
                const url = URL.createObjectURL(blob);
                const w = window.open(url, "_blank", "noopener");
                if (!w) window.A.showToast("חסום ע\"י החוסם פופאפ", "error");
                setTimeout(() => { try { URL.revokeObjectURL(url); } catch (e) {} }, 30000);
              }} style={{
                position: "relative", height: 130, borderRadius: 10, overflow: "hidden",
                background: "#000", cursor: "pointer",
              }}>
                <window.MediaImg src={d.image} alt={d.title || "מסמך"} style={{ width: "100%", height: "100%", objectFit: "cover" }} />
              </div>
              <div style={{ marginTop: 8, display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: 13, fontWeight: 600, letterSpacing: "-.01em", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{d.title}</div>
                  <div style={{ fontSize: 11, color: "var(--muted)", marginTop: 2 }}>{d.addedAt}</div>
                </div>
                <button onClick={() => A._confirmDelete(d.title, () => A.deleteDocument(d.id))} style={{
                  background: "transparent", border: "none", padding: 4, cursor: "pointer", color: "var(--muted)",
                }} aria-label="מחק">{I.close(14, "currentColor")}</button>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

/* ─────────────────────────── 10) PACKING ─────────────────────────── */

const PACKING_CAT_META = {
  "בגדים":   { gradient: "linear-gradient(135deg, #ff7a8b 0%, var(--coral) 55%, #c64b78 100%)", iconKey: "shirt",    color: "var(--coral)" },
  "טיפוח":   { gradient: "linear-gradient(135deg, #af52de 0%, #d56ba3 60%, #ff8eaf 100%)",      iconKey: "lotion",   color: "#af52de" },
  "חשמל":    { gradient: "linear-gradient(135deg, #4ec5c2 0%, #1a6e8f 60%, #6bb6e0 100%)",      iconKey: "plug",     color: "#1a6e8f" },
  "תרופות":  { gradient: "linear-gradient(135deg, #34c759 0%, #0a7a3d 60%, #5ab57a 100%)",      iconKey: "pill",     color: "#0a7a3d" },
  "מסמכים":  { gradient: "linear-gradient(135deg, #ffd57a 0%, #f6a345 40%, #e0973a 100%)",      iconKey: "doc",      color: "#e0973a" },
  "אחר":     { gradient: "linear-gradient(135deg, #94a3b8 0%, #475569 60%, #1f2937 100%)",      iconKey: "backpack", color: "#475569" },
};
function packingCatMeta(title) {
  return PACKING_CAT_META[title] || { gradient: "linear-gradient(135deg, #94a3b8 0%, #475569 60%, #1f2937 100%)", iconKey: "backpack", color: "#475569" };
}

function PackingScreen() {
  const { useStore, A } = window;
  const state = useStore(s => ({ packing: s.packing, categories: s.packingCategories, totals: s.packingTotals }));
  const defaultCats = ["בגדים", "טיפוח", "חשמל", "תרופות", "מסמכים", "אחר"];
  // Single-pass grouping. Was O(cats × items) per render with 4 separate
  // .filter() chains for totals — flagged HIGH by the perf audit. Now one
  // walk over `state.packing` builds the per-category lists, the
  // total counter, and the packed counter in a single pass; memoized so
  // unrelated Store.set notifications don't recompute.
  const { cats, totalItems, packedItems, overallPct } = React.useMemo(() => {
    const catList = state.categories.length ? state.categories : defaultCats;
    const byCat = new Map(catList.map(c => [c, { packed: 0, items: [] }]));
    let total = 0, packed = 0;
    for (const p of state.packing) {
      total++;
      if (p.done) packed++;
      const bucket = byCat.get(p.cat);
      if (bucket) {
        bucket.items.push(p);
        if (p.done) bucket.packed++;
      }
    }
    const out = catList.map(title => {
      const b = byCat.get(title) || { packed: 0, items: [] };
      // Total uses the real item count whenever items exist. The stale baseline
      // in state.totals[title] (a per-category "target") was shadowing the real
      // count for users who built smaller lists, showing "2/8" forever.
      const total = b.items.length > 0 ? b.items.length : (state.totals[title] || 4);
      return { title, packed: b.packed, total, items: b.items };
    });
    return {
      cats: out, totalItems: total, packedItems: packed,
      overallPct: total ? Math.round((packed / total) * 100) : 0,
    };
  }, [state.packing, state.categories, state.totals]);

  return (
    <div dir="rtl" style={{ background: "var(--canvas)", color: "var(--ink)", minHeight: "100%", position: "relative", paddingBottom: 110 }}>
      {/* header */}
      <div style={{ padding: "4px 20px 14px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <CircleIcon size={36} data-nav="back" style={{ cursor: "pointer" }}>{I.back(18)}</CircleIcon>
        <div style={{ fontSize: 16, fontWeight: 600 }}>רשימת אריזה</div>
        <div style={{ width: 36, height: 36 }} />
      </div>

      <QuickNav current="packing" />

      {/* progress hero — gradient card matching the rest of the system */}
      <div style={{ margin: "0 20px", borderRadius: 18, padding: "18px 20px",
        background: "linear-gradient(135deg, #ff7a8b 0%, var(--coral) 50%, #ff7e5f 100%)",
        color: "#fff", position: "relative", overflow: "hidden",
        boxShadow: "0 12px 28px -10px rgba(255,78,100,.4)",
      }}>
        <div className="aurora aurora-soft" aria-hidden="true" />
        <div style={{ position: "relative" }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
            <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: ".14em", opacity: .9 }}>התקדמות אריזה</div>
            <div aria-hidden="true">{BUDGET_ICONS.backpack(28, "#fff")}</div>
          </div>
          <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginTop: 6 }}>
            <div className="ltr-inline" style={{ fontSize: 42, fontWeight: 800, letterSpacing: "-.03em", textShadow: "0 1px 8px rgba(0,0,0,.18)" }}>{overallPct}%</div>
            <div style={{ fontSize: 13, opacity: .92 }}>{packedItems} מתוך {totalItems} פריטים</div>
          </div>
          <div style={{ height: 8, borderRadius: 9999, background: "rgba(255,255,255,.25)", marginTop: 12, position: "relative", overflow: "hidden" }}>
            <div style={{ position: "absolute", inset: 0, width: `${overallPct}%`, background: "#fff", borderRadius: 9999, transition: "width .35s ease" }} />
          </div>
          <div style={{ marginTop: 10, fontSize: 11, opacity: .92 }}>
            {cats.length} קטגוריות · <span className="ltr-inline">{totalItems - packedItems}</span> פריטים נותרו לאריזה
          </div>
        </div>
      </div>

      {/* categories — each cat wrapped in a glass card with a gradient header */}
      <div style={{ padding: "20px 20px 0" }}>
        {cats.map((c, i) => {
          const meta = packingCatMeta(c.title);
          const catPct = c.total > 0 ? Math.round((c.packed / c.total) * 100) : 0;
          return (
          <div key={i} className="lg-bento" style={{ marginBottom: 14, padding: 0, overflow: "hidden" }}>
            {/* gradient header strip */}
            <div style={{
              padding: "12px 14px",
              background: meta.gradient,
              color: "#fff",
              display: "flex", alignItems: "center", justifyContent: "space-between",
              gap: 12,
              position: "relative", overflow: "hidden",
            }}>
              <div className="aurora aurora-soft" aria-hidden="true" />
              <div style={{ position: "relative", display: "flex", alignItems: "center", gap: 10 }}>
                <div style={{
                  width: 32, height: 32, borderRadius: 10,
                  background: "rgba(255,255,255,.22)",
                  display: "flex", alignItems: "center", justifyContent: "center",
                }} aria-hidden="true">{BUDGET_ICONS[meta.iconKey] ? BUDGET_ICONS[meta.iconKey](18, "#fff") : null}</div>
                <div>
                  <div style={{ fontSize: 15, fontWeight: 700, letterSpacing: "-.01em", textShadow: "0 1px 4px rgba(0,0,0,.15)" }}>{c.title}</div>
                  <div className="ltr-inline" style={{ fontSize: 11, opacity: .9, marginTop: 2 }}>{c.packed}/{c.total} · {catPct}%</div>
                </div>
              </div>
              <button onClick={() => A.addPacking(c.title)} aria-label={`הוסף פריט ל${c.title}`} style={{
                position: "relative",
                width: 44, height: 44, borderRadius: 9999, border: "none",
                background: "rgba(255,255,255,.25)", color: "#fff",
                fontSize: 22, lineHeight: 1, cursor: "pointer", fontFamily: "inherit",
                display: "flex", alignItems: "center", justifyContent: "center",
              }}>+</button>
            </div>
            {c.items.length === 0 ? (
              <button onClick={() => A.addPacking(c.title)} style={{
                width: "100%", padding: "14px 16px",
                background: "transparent", border: "none",
                color: meta.color,
                fontSize: 13, fontWeight: 600, letterSpacing: "-.01em",
                cursor: "pointer", textAlign: "start", fontFamily: "inherit",
              }}>
                + הוסף פריטים לקטגוריה
              </button>
            ) : (
              <div>
                {c.items.map((it, j) => (
                  <div key={it.id}
                    onClick={(e) => {
                      if (e.target.closest && e.target.closest("[data-rowmenu]")) return;
                      A.togglePacking(it.id);
                    }}
                    style={{
                    padding: "11px 14px", display: "flex", alignItems: "center", gap: 12,
                    borderBottom: j < c.items.length - 1 ? "1px solid var(--hairline-soft)" : "none",
                    cursor: "pointer", minHeight: 44,
                    borderInlineStart: it.color ? `4px solid ${it.color}` : "none",
                  }}>
                    <div style={{
                      width: 22, height: 22, borderRadius: 9999, flexShrink: 0,
                      background: it.done ? "var(--coral)" : "transparent",
                      border: it.done ? "1px solid var(--coral)" : "1.5px solid var(--border-strong, #c1c1c1)",
                      display: "flex", alignItems: "center", justifyContent: "center",
                    }}>
                      {it.done && <svg width="12" height="12" viewBox="0 0 24 24" fill="none"><path d="M5 12l5 5 9-11" stroke="#fff" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                    </div>
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{
                        fontSize: 14, fontWeight: 500, letterSpacing: "-.01em",
                        textDecoration: it.done ? "line-through" : "none",
                        color: it.done ? "var(--muted)" : "var(--ink)",
                      }}>
                        {it.name}
                        {it.qty > 1 && <span className="ltr-inline" style={{ color: "var(--muted)", fontWeight: 400, marginInlineStart: 6 }}>×{it.qty}</span>}
                      </div>
                    </div>
                    {it.who && (
                      <div style={{
                        width: 22, height: 22, borderRadius: 9999,
                        background: it.who === "מ" ? "linear-gradient(135deg, #ff8a5b, #c9446a)" : "linear-gradient(135deg, #6fa0c8, #2d4d70)",
                        color: "#fff", fontSize: 11, fontWeight: 600,
                        display: "flex", alignItems: "center", justifyContent: "center",
                      }}>{it.who}</div>
                    )}
                    {RowMenu && (
                      <RowMenu
                        label={it.name}
                        collection="packing"
                        itemId={it.id}
                        onEdit={() => A.editPacking(it.id)}
                        onDelete={() => A.deletePacking(it.id)}
                      />
                    )}
                  </div>
                ))}
              </div>
            )}
          </div>
          );
        })}
      </div>

      {/* FAB */}
      <div style={{ position: "sticky", bottom: 100, marginTop: 16, display: "flex", justifyContent: "center", pointerEvents: "none", zIndex: 30 }}>
        <Pill kind="primary" onClick={() => A.addPackingFAB()} style={{ height: 50, padding: "0 28px", fontSize: 15, boxShadow: "0 10px 30px rgba(10,102,214,.35)", pointerEvents: "auto" }}>
          {I.plus(18, "#fff")} הוסף פריט
        </Pill>
      </div>

    </div>
  );
}

/* ─────────────────────────── 11) PLANNING (browse saved places) ─────────────────────────── */

function PlanningScreen() {
  const { useStore, A } = window;
  const { trip, places, planningDest, savedPlaceIds } = useStore(s => ({
    trip: s.trip,
    places: s.places,
    planningDest: s.planningDest,
    savedPlaceIds: s.savedPlaceIds,
  }));
  const destList = (trip.destinations || []).map(d => window.destName(d));
  const dest = planningDest || destList[0] || "";
  const filteredPlaces = places.filter(p => p.dest === dest);
  const CATEGORY_LABELS = {
    beach:  "חופים",
    view:   "תצפיות",
    market: "שווקים",
    food:   "אוכל",
    todo:   "אטרקציות",
    sunset: "שקיעות",
    temple: "מקדשים",
  };
  const CATEGORY_ORDER = ["beach", "view", "todo", "market", "food", "temple", "sunset"];
  const PH_BY_CAT = {
    beach:  "ph-thai-beach",
    view:   "ph-thai-jungle",
    market: "ph-thai-market",
    food:   "ph-thai-food",
    todo:   "ph-thai-sunset",
    sunset: "ph-thai-sunset",
    temple: "ph-thai-temple",
  };
  const groups = {};
  filteredPlaces.forEach(p => {
    if (!groups[p.cat]) groups[p.cat] = [];
    groups[p.cat].push(p);
  });
  const sortedCats = CATEGORY_ORDER.filter(c => groups[c] && groups[c].length > 0);

  return (
    <div dir="rtl" style={{ background: "var(--canvas)", color: "var(--ink)", minHeight: "100%", position: "relative", paddingBottom: 110 }}>
      {/* header */}
      <div style={{ padding: "4px 20px 12px", display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <CircleIcon size={36} data-nav="back" style={{ cursor: "pointer" }}>{I.back(18)}</CircleIcon>
        <div style={{ fontSize: 16, fontWeight: 600 }}>תכנון</div>
        <CircleIcon size={36} className="lg-coral" onClick={() => A.addDestination()} style={{ cursor: "pointer" }}>{I.plus(20, "#fff")}</CircleIcon>
      </div>

      <QuickNav current="planning" />

      {trip.destinations.length === 0 && (
        <div className="lg-light" style={{
          margin: "0 20px 16px", borderRadius: 18, padding: "32px 22px",
          textAlign: "center", display: "flex", flexDirection: "column", alignItems: "center", gap: 14,
        }}>
          <div style={{ width: 56, height: 56, borderRadius: 9999, display: "flex", alignItems: "center", justifyContent: "center", background: "var(--coral-soft)" }}>
            {I.pin(26, "var(--coral)")}
          </div>
          <div style={{ fontSize: 17, fontWeight: 600 }}>אין יעדים עדיין</div>
          <div style={{ fontSize: 13, color: "var(--muted)", lineHeight: 1.5 }}>הוסיפי יעד ראשון כדי להתחיל לתכנן</div>
          <Pill kind="coral" onClick={() => A.addDestination()}>{I.plus(16, "#fff")} הוסיפי יעד</Pill>
        </div>
      )}

      {/* destination filter */}
      {destList.length > 0 && (
      <div className="no-scrollbar" style={{ display: "flex", gap: 8, overflowX: "auto", padding: "0 20px 14px" }}>
        {destList.map(d => {
          const active = d === dest;
          const count = places.filter(p => p.dest === d).length;
          return (
            <div key={d} onClick={() => A.setPlanningDest(d)} className={`lg-chip ${active ? "is-active" : ""}`} style={{
              flexShrink: 0, padding: "9px 16px", borderRadius: 9999,
              display: "inline-flex", alignItems: "center", gap: 6,
              fontSize: 13, fontWeight: 500, letterSpacing: "-.01em",
              cursor: "pointer",
            }}>
              <span>{d}</span>
              <span style={{
                fontSize: 10, fontFamily: "var(--mono)", fontWeight: 600,
                padding: "1px 6px", borderRadius: 9999,
                background: active ? "rgba(255,255,255,.22)" : "var(--surface-soft)",
                color: active ? "#fff" : "var(--muted)",
              }}>{count}</span>
            </div>
          );
        })}
      </div>
      )}

      {/* destination summary line */}
      <div style={{ padding: "0 20px 8px" }}>
        <div style={{ fontSize: 22, fontWeight: 600, letterSpacing: "-.02em", display: "flex", alignItems: "center", gap: 6 }}>
          {I.pin(16, "var(--coral)")} {dest}
        </div>
        <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 2 }}>
          {filteredPlaces.length} מקומות · {sortedCats.length} קטגוריות
          {savedPlaceIds.length > 0 && (
            <> · <span style={{ color: "var(--coral)" }}>{filteredPlaces.filter(p => savedPlaceIds.includes(p.id)).length} שמורים</span></>
          )}
        </div>
      </div>

      {/* empty state */}
      {filteredPlaces.length === 0 && (
        <div style={{
          margin: "20px 20px 0", padding: "40px 20px",
          border: "1px dashed var(--hairline)", borderRadius: 14, textAlign: "center",
          fontSize: 13, color: "var(--muted)",
        }}>
          עוד אין מקומות ל{dest}.
        </div>
      )}

      {/* category sections */}
      <div style={{ marginTop: 10 }}>
        {sortedCats.map((cat) => {
          const items = groups[cat];
          const ph = PH_BY_CAT[cat] || "ph-beach";
          return (
            <div key={cat} style={{ marginBottom: 22 }}>
              <div style={{ padding: "0 20px", display: "flex", alignItems: "baseline", justifyContent: "space-between", marginBottom: 10 }}>
                <div style={{ fontSize: 16, fontWeight: 600, letterSpacing: "-.01em" }}>{CATEGORY_LABELS[cat] || cat}</div>
                <span style={{ fontSize: 12, color: "var(--muted)", fontFamily: "var(--mono)" }}>{items.length}</span>
              </div>
              <div className="no-scrollbar" style={{
                display: "flex", gap: 10, overflowX: "auto",
                padding: "0 20px 4px",
              }}>
                {items.map((p) => {
                  const isSaved = savedPlaceIds.includes(p.id);
                  return (
                    <div key={p.id} style={{ flexShrink: 0, width: 168 }}>
                      <div className={`ph ${ph}`} style={{
                        width: 168, height: 168, borderRadius: 12,
                        position: "relative",
                      }}>
                        <div onClick={() => A.toggleSavedPlace(p.id)} style={{
                          position: "absolute", top: 8, insetInlineEnd: 8,
                          width: 32, height: 32, borderRadius: 9999,
                          background: "rgba(0,0,0,.36)", backdropFilter: "blur(6px)",
                          display: "flex", alignItems: "center", justifyContent: "center",
                          cursor: "pointer",
                        }}>
                          {isSaved ? I.heartF(15, "var(--coral)") : I.heart(15, "#fff")}
                        </div>
                      </div>
                      <div style={{ marginTop: 8 }}>
                        <div style={{ fontSize: 13, fontWeight: 600, letterSpacing: "-.01em", lineHeight: 1.25 }}>{p.title}</div>
                        {p.info && <div style={{ fontSize: 11, color: "var(--muted)", marginTop: 3, lineHeight: 1.35 }}>{p.info}</div>}
                        {p.title_en && !p.info && <div style={{ fontSize: 11, color: "var(--muted)", marginTop: 3, fontFamily: "var(--mono)" }}>{p.title_en}</div>}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>

    </div>
  );
}

/* ─────────────────────────── exports ─────────────────────────── */
Object.assign(window, {
  MapScreen, RemindersScreen, BudgetScreen, DocumentsScreen, PackingScreen,
  PlanningScreen,
});
