// vaultlog.jsx - the core asset register (exported to window)
const { useState: useStateV, useMemo: useMemoV } = React;

const FILTERS = [
  { id: "all", label: "All assets" },
  { id: "uninsured", label: "Uninsured" },
  { id: "partial", label: "Underinsured" },
  { id: "lapsing", label: "Lapsing" },
  { id: "insured", label: "Insured" },
];

function VaultLog({ assets, profile, userName = "Adebola", tweaks, onInsure, onBundle, addToast, flashId, onChat, onUpdate, onDelete, onLayout, openAssetId, onAssetOpened, plan, statementsOK, onUpgrade }) {
  const D = window.VC_DATA;
  const { t } = window.useT();
  const layout = tweaks.vaultLayout || "cards";
  const [filter, setFilter] = useStateV("all");
  // Track the open asset by ID, not by object reference — so the detail always
  // reflects the LATEST asset data after an edit/save (an object snapshot would
  // go stale and show pre-save values on reopen).
  const [openId, setOpenId] = useStateV(null);
  const open = openId != null ? (assets.find((a) => a.id === openId) || null) : null;
  const [query, setQuery] = useStateV("");
  // Cross-category bundle selection: tick assets from anywhere, then insure them
  // together as one bundle.
  const [selMode, setSelMode] = useStateV(false);
  const [sel, setSel] = useStateV({});
  const [exportOpen, setExportOpen] = useStateV(false);

  // Deep-link from a notification: open the referenced asset's detail.
  React.useEffect(() => {
    if (!openAssetId) return;
    const a = assets.find((x) => x.id === openAssetId);
    if (a) setOpenId(a.id);
    onAssetOpened && onAssetOpened();
  }, [openAssetId]);

  const p = useMemoV(() => D.portfolio(assets), [assets]);
  const filtered = assets.filter((a) =>
    (filter === "all" || a.status === filter) &&
    (!query || a.name.toLowerCase().includes(query.toLowerCase()))
  );

  const toggleSel = (a) => {
    if (a.status === "insured") { addToast && addToast(`${a.name} is already insured`, "check"); return; }
    setSel((s) => ({ ...s, [a.id]: !s[a.id] }));
  };
  const selectedAssets = assets.filter((a) => sel[a.id] && a.status !== "insured");
  const enterSelect = () => { setSelMode(true); setSel({}); };
  const exitSelect = () => { setSelMode(false); setSel({}); };
  const moveSelected = (category) => {
    selectedAssets.forEach((a) => onUpdate && onUpdate(a.id, { category }));
    addToast && addToast(`Moved ${selectedAssets.length} to ${D.CATEGORIES[category]?.label || category}`, "layers");
    exitSelect();
  };
  const bundleSelected = () => {
    if (selectedAssets.length < 1) return;
    const list = selectedAssets.slice();
    exitSelect();
    onBundle && onBundle(list); // Insure accepts an array -> opens in bundle mode
  };

  // Card/row click: select in select-mode, otherwise open the detail drawer.
  const onCardClick = (a) => (selMode ? toggleSel(a) : setOpenId(a.id));
  const viewProps = { onOpen: onCardClick, flashId, selMode, sel };

  return (
    <div className="vc-page" style={{ padding: "28px 34px 60px", maxWidth: 1280, margin: "0 auto" }}>
      {/* HEADER */}
      <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 20, flexWrap: "wrap" }}>
        <div>
          <h1 className="display" style={{ fontSize: 32, display: "flex", alignItems: "center", gap: 12 }}>
            <span style={{ display: "grid", placeItems: "center", width: 40, height: 40, borderRadius: 13, background: "var(--accent-soft)", color: "var(--accent)" }}><window.Icon name="vault" size={22} /></span>
            VaultLog
          </h1>
          <p style={{ color: "var(--text-muted)", fontSize: 15, marginTop: 8 }}>{profile === "business" ? t("vl.subtitleBiz") : t("vl.subtitlePersonal")} {t("vl.subtitleTail")}</p>
        </div>
        <div style={{ display: "flex", gap: 10, flexWrap: "wrap" }}>
          <window.Btn variant="outline" icon="shield" onClick={() => onChat("Move my existing policy to VaultCova")}>{t("vl.moveExisting")}</window.Btn>
          <window.Btn variant="outline" icon="download" onClick={() => setExportOpen(true)}>{t("vl.schedule")}</window.Btn>
          <window.Btn icon="sparkle" onClick={() => onChat("Add an asset to my VaultLog")}>{t("vl.addChat")}</window.Btn>
        </div>
      </div>

      {/* PORTFOLIO SUMMARY — flips between the live figures and the growth chart */}
      <PortfolioPanel assets={assets} p={p} onChat={onChat} t={t} />

      {/* CONTROLS */}
      <div style={{ display: "flex", alignItems: "center", gap: 12, marginTop: 22, flexWrap: "wrap" }}>
        <StatusFilter assets={assets} filter={filter} setFilter={setFilter} t={t} />
        <div style={{ marginLeft: "auto", display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
          <button onClick={selMode ? exitSelect : enterSelect} className="vc-focus" style={{
            display: "inline-flex", alignItems: "center", gap: 7, padding: "8px 14px", borderRadius: 999, fontSize: 13.5, fontWeight: 700,
            background: selMode ? "var(--accent)" : "var(--surface)", color: selMode ? "#fff" : "var(--text-muted)",
            border: "1px solid " + (selMode ? "var(--accent)" : "var(--border)"), transition: "all .2s",
          }}><window.Icon name="layers" size={15} />{selMode ? "Done selecting" : "Select to bundle"}</button>
          <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "8px 14px", borderRadius: 999, background: "var(--surface)", border: "1px solid var(--border)" }}>
            <window.Icon name="search" size={16} style={{ color: "var(--text-faint)" }} />
            <input value={query} onChange={(e) => setQuery(e.target.value)} placeholder={t("common.search")} style={{ border: "none", outline: "none", background: "transparent", fontSize: 14, color: "var(--text)", width: 130 }} />
          </div>
          <LayoutToggle layout={layout} onLayout={onLayout} />
        </div>
      </div>

      {selMode && (
        <div style={{ marginTop: 14, padding: "10px 14px", borderRadius: 12, background: "var(--accent-soft)", color: "var(--accent-ink)", fontSize: 13, fontWeight: 600, display: "flex", alignItems: "center", gap: 8 }}>
          <window.Icon name="sparkle" size={15} />Tick assets from any category, then bundle them into one policy (or move them together). Insured assets can't be re-bundled.
        </div>
      )}

      {/* BODY */}
      <div style={{ marginTop: 20, paddingBottom: selMode ? 84 : 0 }}>
        {layout === "cards" && <CardsView assets={filtered} {...viewProps} />}
        {layout === "table" && <TableView assets={filtered} {...viewProps} />}
        {layout === "board" && <BoardView assets={assets} {...viewProps} query={query} />}
        {layout === "category" && <CategoryView assets={filtered} {...viewProps} onMove={onUpdate} query={query} />}
        {layout === "places" && <PlacesView assets={assets} {...viewProps} query={query} onBundle={onBundle} onUpdate={onUpdate} profile={profile} addToast={addToast} />}
        {filtered.length === 0 && layout !== "board" && layout !== "category" && layout !== "places" && (
          <div style={{ textAlign: "center", padding: 60, color: "var(--text-faint)" }}>
            <window.Icon name="vault" size={40} style={{ opacity: 0.4 }} />
            <p style={{ marginTop: 12, fontSize: 15 }}>No assets here. Try <button onClick={() => onChat("Add my car worth ₦52M")} style={{ color: "var(--accent)", fontWeight: 700 }}>adding one via chat</button>.</p>
          </div>
        )}
      </div>

      {selMode && <BundleBar assets={selectedAssets} onBundle={bundleSelected} onMove={moveSelected} onClear={exitSelect} />}

      <AssetDetail key={open ? open.id : "none"} asset={open} allAssets={assets} onClose={() => setOpenId(null)} onInsure={onInsure} onBundle={onBundle} onChat={onChat} profile={profile} userName={userName} onUpdate={onUpdate} onDelete={onDelete} statementsOK={statementsOK} onUpgrade={onUpgrade} />

      {exportOpen && <ExportSheet assets={assets} profile={profile} userName={userName} statementsOK={statementsOK} onUpgrade={onUpgrade} addToast={addToast} onClose={() => setExportOpen(false)} />}
    </div>
  );
}

/* ---- EXPORT SHEET — options for the schedule-of-assets PDF ----
   Collects: group-by-location, and (paid) a value statement for a chosen period.
   A centered modal that rises on open; never covers the whole screen on mobile. */
function ExportSheet({ assets, profile, userName, statementsOK, onUpgrade, addToast, onClose }) {
  const isMobile = window.useIsMobile ? window.useIsMobile() : false;
  const hasPlaces = assets.some((a) => (a.place || "").trim());
  const trackedCount = assets.filter((a) => a.trackValue).length;
  const [closing, setClosing] = useStateV(false);
  const [byLocation, setByLocation] = useStateV(hasPlaces);
  const [withStatement, setWithStatement] = useStateV(!!statementsOK && trackedCount > 0);
  const [period, setPeriod] = useStateV("12m"); // 12m | ytd | 3m | all | custom
  const [from, setFrom] = useStateV("");
  const [to, setTo] = useStateV("");
  const [busy, setBusy] = useStateV(false);
  const doClose = () => { if (closing) return; setClosing(true); setTimeout(() => onClose && onClose(), 260); };

  const range = () => {
    const now = new Date();
    const iso = (d) => d.toISOString();
    if (period === "custom") return { from: from ? new Date(from).toISOString() : null, to: to ? new Date(to + "T23:59:59").toISOString() : iso(now) };
    if (period === "all") return { from: null, to: iso(now) };
    if (period === "ytd") return { from: iso(new Date(now.getFullYear(), 0, 1)), to: iso(now) };
    const days = period === "3m" ? 92 : 365;
    return { from: iso(new Date(now.getTime() - days * 864e5)), to: iso(now) };
  };

  const download = async () => {
    if (busy) return;
    setBusy(true);
    try {
      const r = range();
      let statement = null;
      if (withStatement && statementsOK) {
        const qs = new URLSearchParams();
        if (r.from) qs.set("from", r.from);
        if (r.to) qs.set("to", r.to);
        const res = await fetch("/api/valuations?" + qs.toString());
        if (res.ok) { const d = await res.json(); statement = d.assets; }
        else if (res.status === 402) { addToast && addToast("Value statements are on Pro & Business.", "lock"); }
      }
      await window.downloadSchedule(assets, profile, userName, undefined, { byLocation, statement, from: r.from, to: r.to });
      addToast && addToast(statement && statement.length ? "Value statement downloaded" : "Schedule downloaded", "check");
      doClose();
    } catch (e) {
      addToast && addToast("Could not generate the PDF", "alert");
      setBusy(false);
    }
  };

  const periods = [["12m", "Last 12 months"], ["ytd", "This year"], ["3m", "Last 3 months"], ["all", "All time"], ["custom", "Custom"]];

  const overlay = (
    <div onClick={doClose} style={{ position: "fixed", inset: 0, zIndex: 1000, display: "flex", justifyContent: "center", alignItems: isMobile ? "flex-end" : "center", background: "rgba(8,9,26,.5)", backdropFilter: "blur(6px)", WebkitBackdropFilter: "blur(6px)", animation: closing ? "vc-fade-out .24s var(--ease) both" : "vc-fade-in .28s var(--ease) both", padding: isMobile ? 0 : 20 }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: isMobile ? "100%" : 440, maxWidth: "100%", maxHeight: isMobile ? "90dvh" : "88vh", overflowY: "auto", background: "var(--surface)",
        borderRadius: isMobile ? "24px 24px 0 0" : 22, boxShadow: "var(--shadow-xl)", border: "1px solid var(--border)",
        animation: closing ? (isMobile ? "vc-sheet-down .26s cubic-bezier(.4,0,1,1) both" : "vc-fade-out .24s both") : (isMobile ? "vc-sheet-rise .4s cubic-bezier(.16,1,.3,1) both" : "vc-pop-in .34s cubic-bezier(.16,1,.3,1) both"),
      }}>
        <style>{`@keyframes vc-pop-in{from{transform:translateY(14px) scale(.97);opacity:0}to{transform:none;opacity:1}}@keyframes vc-sheet-rise{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes vc-sheet-down{from{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes vc-fade-out{from{opacity:1}to{opacity:0}}`}</style>
        {isMobile && <div onClick={doClose} style={{ display: "grid", placeItems: "center", padding: "10px 0 2px", cursor: "pointer" }}><span style={{ width: 42, height: 5, borderRadius: 999, background: "var(--border-strong)" }} /></div>}
        <div style={{ padding: 22 }}>
          <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12 }}>
            <div>
              <h2 className="display" style={{ fontSize: 20 }}>Export schedule</h2>
              <p style={{ fontSize: 13, color: "var(--text-muted)", marginTop: 4 }}>A branded PDF of your {assets.length} asset{assets.length === 1 ? "" : "s"} and cover.</p>
            </div>
            <button onClick={doClose} style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 9, background: "var(--surface-2)", color: "var(--text-muted)", border: "1px solid var(--border)" }}><window.Icon name="close" size={16} /></button>
          </div>

          {/* group by location */}
          {hasPlaces && (
            <ExportRow icon="map" title="Group by location" desc="A section per site, each with its own subtotal." on={byLocation} onToggle={() => setByLocation((v) => !v)} />
          )}

          {/* value statement (paid) */}
          <div style={{ marginTop: 14, padding: 14, borderRadius: 14, border: "1px solid " + (withStatement ? "var(--accent)" : "var(--border)"), background: withStatement ? "var(--accent-soft)" : "var(--surface-2)" }}>
            <div style={{ display: "flex", alignItems: "flex-start", gap: 11 }}>
              <span style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 10, background: "var(--surface)", color: "var(--accent)" }}><window.Icon name="trend" size={17} /></span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                  <span style={{ fontSize: 14, fontWeight: 700 }}>Include value statement</span>
                  {!statementsOK && <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 10.5, fontWeight: 700, color: "var(--accent)", padding: "2px 7px", borderRadius: 999, background: "var(--surface)" }}><window.Icon name="lock" size={11} />Pro</span>}
                </div>
                <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 2 }}>
                  {trackedCount > 0 ? `How ${trackedCount} tracked item${trackedCount === 1 ? "" : "s"} changed in value, like a bank statement.` : "Turn on value tracking for stock to build a statement."}
                </div>
              </div>
              {statementsOK
                ? <VLToggle on={withStatement} onClick={() => setWithStatement((v) => !v)} disabled={trackedCount === 0} />
                : <window.Btn size="sm" onClick={() => { doClose(); onUpgrade && onUpgrade(); }}>Upgrade</window.Btn>}
            </div>

            {withStatement && statementsOK && (
              <div style={{ marginTop: 12 }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em", marginBottom: 8 }}>Statement period</div>
                <div style={{ display: "flex", gap: 7, flexWrap: "wrap" }}>
                  {periods.map(([id, label]) => (
                    <button key={id} onClick={() => setPeriod(id)} style={{ padding: "7px 12px", borderRadius: 999, fontSize: 12.5, fontWeight: 700, cursor: "pointer", background: period === id ? "var(--accent)" : "var(--surface)", color: period === id ? "#fff" : "var(--text-muted)", border: "1px solid " + (period === id ? "var(--accent)" : "var(--border)") }}>{label}</button>
                  ))}
                </div>
                {period === "custom" && (
                  <div style={{ display: "flex", gap: 10, marginTop: 10, flexWrap: "wrap" }}>
                    <label style={{ flex: 1, minWidth: 130, fontSize: 12, color: "var(--text-muted)" }}>From<input type="date" value={from} onChange={(e) => setFrom(e.target.value)} style={{ display: "block", width: "100%", marginTop: 4, padding: "9px 11px", borderRadius: 10, border: "1.5px solid var(--border)", background: "var(--surface)", color: "var(--text)", fontSize: 13.5, outline: "none" }} /></label>
                    <label style={{ flex: 1, minWidth: 130, fontSize: 12, color: "var(--text-muted)" }}>To<input type="date" value={to} onChange={(e) => setTo(e.target.value)} style={{ display: "block", width: "100%", marginTop: 4, padding: "9px 11px", borderRadius: 10, border: "1.5px solid var(--border)", background: "var(--surface)", color: "var(--text)", fontSize: 13.5, outline: "none" }} /></label>
                  </div>
                )}
              </div>
            )}
          </div>

          <window.Btn size="lg" icon="download" style={{ width: "100%", justifyContent: "center", marginTop: 18 }} onClick={download} disabled={busy}>{busy ? "Preparing…" : "Download PDF"}</window.Btn>
        </div>
      </div>
    </div>
  );
  return window.ReactDOM.createPortal(overlay, document.body);
}

function ExportRow({ icon, title, desc, on, onToggle }) {
  return (
    <div style={{ marginTop: 14, padding: 14, borderRadius: 14, border: "1px solid " + (on ? "var(--accent)" : "var(--border)"), background: on ? "var(--accent-soft)" : "var(--surface-2)", display: "flex", alignItems: "center", gap: 11 }}>
      <span style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 10, background: "var(--surface)", color: "var(--accent)" }}><window.Icon name={icon} size={17} /></span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 14, fontWeight: 700 }}>{title}</div>
        <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 2 }}>{desc}</div>
      </div>
      <VLToggle on={on} onClick={onToggle} />
    </div>
  );
}

function VLToggle({ on, onClick, disabled }) {
  return (
    <button onClick={disabled ? undefined : onClick} disabled={disabled} style={{ flexShrink: 0, width: 46, height: 27, borderRadius: 999, background: on ? "var(--accent)" : "var(--surface-3)", position: "relative", transition: "background .25s", opacity: disabled ? 0.5 : 1, cursor: disabled ? "default" : "pointer", border: "none" }}>
      <span style={{ position: "absolute", top: 3, left: on ? 22 : 3, width: 21, height: 21, borderRadius: 999, background: "#fff", boxShadow: "var(--shadow-sm)", transition: "left .25s var(--ease)" }} />
    </button>
  );
}

/* ---- summary sub-components ---- */
function Stat({ label, value, c, big }) {
  return (
    <div>
      <div style={{ fontSize: 12.5, color: "var(--text-faint)", fontWeight: 600 }}>{label}</div>
      <div className="display mono" style={{ fontSize: big ? "clamp(17px, 5.2cqi, 30px)" : "clamp(14px, 3.8cqi, 20px)", color: c || "var(--text)", marginTop: 2, whiteSpace: "nowrap" }}>{value}</div>
    </div>
  );
}
function GapCard({ p, onInsure }) {
  const has = p.gap > 0;
  const { t } = window.useT();
  return (
    <div className="vc-gap-wide" style={{ padding: 16, borderRadius: 16, background: has ? "var(--danger-soft)" : "var(--success-soft)", border: "1px solid " + (has ? "var(--danger)" : "var(--success)"), display: "flex", flexDirection: "column", minWidth: 0 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 12.5, fontWeight: 700, color: has ? "var(--danger)" : "var(--success)" }}>
        <window.Icon name={has ? "alert" : "check"} size={15} />{has ? t("vl.coverageGap") : t("vl.fullyProtected")}
      </div>
      {/* Full-precision figure so users see the exact exposure; the font scales
          with the card (wide on desktop, full-row on mobile) and never wraps. */}
      <div className="display mono" style={{ fontSize: "clamp(15px, 4.6cqi, 26px)", marginTop: 6, color: has ? "var(--danger)" : "var(--success)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{window.VC_DATA.fmtFull(p.gap)}</div>
      {has && <button onClick={onInsure} style={{ marginTop: 8, alignSelf: "flex-start", fontSize: 12.5, fontWeight: 700, color: "var(--danger)", display: "inline-flex", alignItems: "center", gap: 5 }}>{t("vl.closeIt")} <window.Icon name="arrowR" size={13} /></button>}
    </div>
  );
}
function MiniStat({ icon, label, value, sub }) {
  return (
    <div style={{ padding: 16, borderRadius: 16, background: "var(--surface)", border: "1px solid var(--border)" }}>
      <span style={{ display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 10, background: "var(--accent-soft)", color: "var(--accent)" }}><window.Icon name={icon} size={17} /></span>
      <div className="display mono" style={{ fontSize: "clamp(14px, 4cqi, 22px)", marginTop: 10, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div>
      <div style={{ fontSize: 12, color: "var(--text-faint)", fontWeight: 600, marginTop: 2 }}>{label}</div>
      <div style={{ fontSize: 11.5, color: "var(--text-muted)", marginTop: 4 }}>{sub}</div>
    </div>
  );
}
function LayoutToggle({ layout, onLayout }) {
  const opts = [["cards", "grid", "Cards"], ["table", "list", "Table"], ["board", "layers", "By status"], ["category", "box", "By category"], ["places", "map", "By location"]];
  return (
    <div style={{ display: "flex", gap: 2, padding: 3, borderRadius: 12, background: "var(--surface-3)", border: "1px solid var(--border)" }}>
      {opts.map(([id, ic, label]) => (
        <button key={id} title={label + " view"} onClick={() => onLayout && onLayout(id)} className="vc-focus"
          style={{ display: "grid", placeItems: "center", width: 34, height: 30, borderRadius: 9, cursor: "pointer", background: layout === id ? "var(--surface)" : "transparent", color: layout === id ? "var(--accent)" : "var(--text-faint)", boxShadow: layout === id ? "var(--shadow-sm)" : "none" }}>
          <window.Icon name={ic} size={16} />
        </button>
      ))}
    </div>
  );
}

/* ---- selection tick (shown in select-to-bundle mode) ---- */
function SelTick({ on }) {
  return (
    <span style={{ display: "grid", placeItems: "center", width: 24, height: 24, borderRadius: 8, flexShrink: 0,
      background: on ? "var(--accent)" : "var(--surface)", border: "1.5px solid " + (on ? "var(--accent)" : "var(--border-strong)"), color: "#fff", transition: "all .15s" }}>
      {on && <window.Icon name="check" size={15} stroke={3} />}
    </span>
  );
}

/* ---- CARDS view ---- */
function CardsView({ assets, onOpen, flashId, selMode, sel }) {
  return (
    <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(280px,1fr))", gap: 14 }}>
      {assets.map((a, i) => <AssetCard key={a.id} a={a} onOpen={onOpen} i={i} flash={a.id === flashId} selMode={selMode} selected={!!(sel && sel[a.id])} />)}
    </div>
  );
}
function AssetCard({ a, onOpen, i, flash, selMode, selected }) {
  const D = window.VC_DATA;
  const [h, setH] = useStateV(false);
  const insured = a.status === "insured";
  const pct = a.replacement ? Math.min(1, a.sumInsured / a.replacement) : 0;
  return (
    <div onClick={() => onOpen(a)} onMouseEnter={() => setH(true)} onMouseLeave={() => setH(false)} style={{
      position: "relative", padding: 16, borderRadius: 18, background: "var(--surface)", border: "1px solid var(--border)", cursor: "pointer",
      boxShadow: h ? "var(--shadow-lg)" : "var(--shadow-sm)", transform: h ? "translateY(-3px)" : "none", transition: "all .25s var(--ease)",
      animation: flash ? "vc-drop-in .7s var(--ease) both" : `vc-fade-up .5s var(--ease) ${i * 0.04}s both`,
      outline: (selMode && selected) ? "2px solid var(--accent)" : flash ? "2px solid var(--accent)" : "none",
      opacity: selMode && insured ? 0.55 : 1,
    }}>
      {selMode && <div style={{ position: "absolute", top: 10, left: 10, zIndex: 2 }}><SelTick on={selected} /></div>}
      <div style={{ display: "flex", alignItems: "flex-start", gap: 12 }}>
        <window.AssetThumb category={a.category} size={52} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontWeight: 700, fontSize: 15, lineHeight: 1.25 }}>{a.name}</div>
          <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 3 }}>{D.CATEGORIES[a.category]?.label}</div>
        </div>
        <window.StatusBadge status={a.status} size="sm" />
      </div>
      <div style={{ display: "flex", justifyContent: "space-between", marginTop: 16, marginBottom: 8 }}>
        <div><div style={{ fontSize: 11, color: "var(--text-faint)", fontWeight: 600 }}>Replacement</div><div className="mono" style={{ fontSize: 15, fontWeight: 700 }}>{D.fmtNaira(a.replacement)}</div></div>
        <div style={{ textAlign: "right" }}><div style={{ fontSize: 11, color: "var(--text-faint)", fontWeight: 600 }}>Insured</div><div className="mono" style={{ fontSize: 15, fontWeight: 700, color: a.sumInsured ? "var(--success)" : "var(--danger)" }}>{D.fmtNaira(a.sumInsured)}</div></div>
      </div>
      <window.CoverageBar insured={a.sumInsured} replacement={a.replacement} />
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 12, fontSize: 12 }}>
        {a.bundle ? <span style={{ display: "inline-flex", alignItems: "center", gap: 5, color: "var(--accent-ink)", fontWeight: 600 }}><window.Icon name="layers" size={13} />Bundled</span> : <span style={{ color: "var(--text-faint)" }}>Individual</span>}
        <span style={{ display: "inline-flex", alignItems: "center", gap: 4, color: h ? "var(--accent)" : "var(--text-faint)", fontWeight: 600 }}>Details <window.Icon name="arrowR" size={13} /></span>
      </div>
    </div>
  );
}

/* ---- TABLE view ---- */
/* ---- status filter as a single clean dropdown (replaces the wrapping pill row).
   One compact control shows the active filter + count; the menu lists each
   status with its colour dot and live count. Far calmer than five pills, and it
   never wraps on mobile. ---- */
function StatusFilter({ assets, filter, setFilter, t }) {
  const [open, setOpen] = useStateV(false);
  const D = window.VC_DATA;
  const countFor = (id) => (id === "all" ? assets.length : assets.filter((a) => a.status === id).length);
  const dot = (id) => (id === "all" ? "var(--text-muted)" : (D.STATUS_META[id]?.color || "var(--text-muted)"));
  const cur = FILTERS.find((f) => f.id === filter) || FILTERS[0];
  return (
    <div style={{ position: "relative" }}>
      <button onClick={() => setOpen((o) => !o)} className="vc-focus" style={{
        display: "inline-flex", alignItems: "center", gap: 9, padding: "9px 14px", borderRadius: 12, fontSize: 13.5, fontWeight: 600,
        background: "var(--surface)", color: "var(--text)", border: "1px solid var(--border)", boxShadow: "var(--shadow-sm)",
      }}>
        <span style={{ width: 8, height: 8, borderRadius: 999, background: dot(cur.id), flexShrink: 0 }} />
        <span className="vc-nowrap">{t("vl.filter." + cur.id)}</span>
        <span className="mono" style={{ fontSize: 12, color: "var(--text-faint)" }}>{countFor(cur.id)}</span>
        <window.Icon name="chevD" size={15} style={{ color: "var(--text-faint)", transform: open ? "rotate(180deg)" : "none", transition: "transform .2s" }} />
      </button>
      {open && (
        <>
          <div onClick={() => setOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 40 }} />
          <div style={{ position: "absolute", top: "calc(100% + 6px)", left: 0, zIndex: 41, minWidth: 210, padding: 6, borderRadius: 14, background: "var(--surface)", border: "1px solid var(--border)", boxShadow: "var(--shadow-xl)", animation: "vc-fade-up .18s var(--ease) both" }}>
            {FILTERS.map((f) => {
              const active = filter === f.id;
              return (
                <button key={f.id} onClick={() => { setFilter(f.id); setOpen(false); }} style={{
                  width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "9px 11px", borderRadius: 9, fontSize: 13.5, fontWeight: 600, textAlign: "left",
                  background: active ? "var(--accent-soft)" : "transparent", color: active ? "var(--accent-ink)" : "var(--text-muted)",
                }} onMouseEnter={(e) => { if (!active) e.currentTarget.style.background = "var(--surface-2)"; }} onMouseLeave={(e) => { if (!active) e.currentTarget.style.background = "transparent"; }}>
                  <span style={{ width: 8, height: 8, borderRadius: 999, background: dot(f.id), flexShrink: 0 }} />
                  <span className="vc-nowrap" style={{ flex: 1 }}>{t("vl.filter." + f.id)}</span>
                  <span className="mono" style={{ fontSize: 12, color: "var(--text-faint)" }}>{countFor(f.id)}</span>
                </button>
              );
            })}
          </div>
        </>
      )}
    </div>
  );
}

function TableView({ assets, onOpen, flashId, selMode, sel }) {
  const D = window.VC_DATA;
  // Desktop: a dense 5-column table. On a narrow column the row re-templates into
  // a card (see .vc-trow container query in styles.css) so nothing is crushed.
  const cols = "2.2fr 1fr 1fr 1.4fr auto";
  return (
    <div style={{ borderRadius: 18, border: "1px solid var(--border)", overflow: "hidden", background: "var(--surface)" }}>
      <div className="vc-table-head" style={{ display: "grid", gridTemplateColumns: cols, gap: 12, padding: "13px 18px", background: "var(--surface-2)", fontSize: 12, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em" }}>
        <span>Asset</span><span>Replacement</span><span>Insured</span><span>Coverage</span><span>Status</span>
      </div>
      {assets.map((a) => (
        <div key={a.id} className="vc-trow" onClick={() => onOpen(a)} style={{
          display: "grid", gridTemplateColumns: cols, gap: 12, padding: "13px 18px", alignItems: "center", cursor: "pointer",
          borderTop: "1px solid var(--border)", transition: "background .15s",
          animation: a.id === flashId ? "vc-drop-in .7s var(--ease) both" : "none", background: a.id === flashId ? "var(--accent-soft)" : "transparent",
          opacity: selMode && a.status === "insured" ? 0.55 : 1,
        }} onMouseEnter={(e) => e.currentTarget.style.background = "var(--surface-2)"} onMouseLeave={(e) => e.currentTarget.style.background = a.id === flashId ? "var(--accent-soft)" : "transparent"}>
          <div className="vc-tc-asset" style={{ display: "flex", alignItems: "center", gap: 11, minWidth: 0 }}>
            {selMode && <SelTick on={!!(sel && sel[a.id])} />}
            <window.AssetThumb category={a.category} size={38} radius={10} />
            <div style={{ minWidth: 0 }}>
              <div style={{ fontWeight: 700, fontSize: 14 }}>{a.name}</div>
              <div style={{ fontSize: 11.5, color: "var(--text-faint)" }}>{D.CATEGORIES[a.category]?.label}{a.bundle ? " · Bundled" : ""}</div>
            </div>
          </div>
          <span className="vc-tc-repl mono" style={{ fontSize: 14, fontWeight: 600, whiteSpace: "nowrap" }}>{D.fmtNaira(a.replacement)}</span>
          <span className="vc-tc-ins mono" style={{ fontSize: 14, fontWeight: 600, whiteSpace: "nowrap", color: a.sumInsured ? "var(--success)" : "var(--danger)" }}>{D.fmtNaira(a.sumInsured)}</span>
          <div className="vc-tc-cov" style={{ paddingRight: 8 }}><window.CoverageBar insured={a.sumInsured} replacement={a.replacement} height={8} /></div>
          <span className="vc-tc-status"><window.StatusBadge status={a.status} size="sm" /></span>
        </div>
      ))}
    </div>
  );
}

/* ---- small board card, shared by status + category boards ---- */
function BoardCard({ a, onOpen, flashId, selMode, sel }) {
  const D = window.VC_DATA;
  const selected = !!(sel && sel[a.id]);
  return (
    <div onClick={() => onOpen(a)} style={{ position: "relative", padding: 12, borderRadius: 14, background: "var(--surface)", border: "1px solid var(--border)", cursor: "pointer", boxShadow: "var(--shadow-sm)", animation: a.id === flashId ? "vc-drop-in .7s var(--ease) both" : "none", outline: (selMode && selected) || a.id === flashId ? "2px solid var(--accent)" : "none", opacity: selMode && a.status === "insured" ? 0.55 : 1 }}>
      {selMode && <div style={{ position: "absolute", top: 8, right: 8 }}><SelTick on={selected} /></div>}
      <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
        <window.AssetThumb category={a.category} size={34} radius={9} />
        <div style={{ fontWeight: 700, fontSize: 13, lineHeight: 1.2 }}>{a.name}</div>
      </div>
      <div style={{ marginTop: 10 }}><window.CoverageBar insured={a.sumInsured} replacement={a.replacement} height={7} /></div>
      <div className="mono" style={{ fontSize: 11.5, color: "var(--text-muted)", marginTop: 7 }}>{D.fmtNaira(a.sumInsured)} / {D.fmtNaira(a.replacement)}</div>
    </div>
  );
}

/* ---- BOARD view (by status) ---- */
function BoardView({ assets, onOpen, flashId, query, selMode, sel }) {
  const cols = [
    { id: "uninsured", label: "Uninsured", color: "var(--danger)" },
    { id: "partial", label: "Underinsured", color: "var(--warning)" },
    { id: "lapsing", label: "Lapsing soon", color: "var(--warning)" },
    { id: "insured", label: "Fully insured", color: "var(--success)" },
  ];
  return (
    <div className="vc-grid-2" style={{ display: "grid", gridTemplateColumns: "repeat(4,1fr)", gap: 14, alignItems: "start" }}>
      {cols.map((c) => {
        const items = assets.filter((a) => a.status === c.id && (!query || a.name.toLowerCase().includes(query.toLowerCase())));
        return (
          <div key={c.id} style={{ background: "var(--surface-2)", borderRadius: 18, padding: 12, border: "1px solid var(--border)" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "4px 6px 12px" }}>
              <span style={{ width: 9, height: 9, borderRadius: 9, background: c.color }} />
              <span style={{ fontWeight: 700, fontSize: 13.5 }}>{c.label}</span>
              <span style={{ marginLeft: "auto", fontSize: 12, color: "var(--text-faint)", fontWeight: 700 }}>{items.length}</span>
            </div>
            <div style={{ display: "grid", gap: 10 }}>
              {items.map((a) => <BoardCard key={a.id} a={a} onOpen={onOpen} flashId={flashId} selMode={selMode} sel={sel} />)}
              {items.length === 0 && <div style={{ textAlign: "center", padding: 18, fontSize: 12, color: "var(--text-faint)" }}>-</div>}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ---- CATEGORY view (auto-grouped by kind) ---- */
// Assets are auto-categorised when Cova adds them; this groups the vault by kind
// so you can see everything of a type together. In select mode you can tick items
// across these groups and bundle or move them.
function CategoryView({ assets, onOpen, flashId, query, selMode, sel }) {
  const D = window.VC_DATA;
  const q = (query || "").toLowerCase();
  const shown = assets.filter((a) => !q || a.name.toLowerCase().includes(q));
  // Preserve the canonical category order, only render kinds that have assets.
  const order = Object.keys(D.CATEGORIES);
  const groups = order
    .map((cat) => ({ cat, items: shown.filter((a) => a.category === cat) }))
    .filter((g) => g.items.length > 0);

  if (groups.length === 0) {
    return <div style={{ textAlign: "center", padding: 60, color: "var(--text-faint)" }}><window.Icon name="box" size={40} style={{ opacity: 0.4 }} /><p style={{ marginTop: 12, fontSize: 15 }}>No assets to group yet.</p></div>;
  }
  return (
    <div style={{ display: "grid", gap: 18 }}>
      {groups.map(({ cat, items }) => {
        const value = items.reduce((s, a) => s + (a.replacement || 0), 0);
        return (
          <div key={cat}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
              <span style={{ display: "grid", placeItems: "center", width: 30, height: 30, borderRadius: 9, background: `oklch(0.94 0.05 ${D.CATEGORIES[cat]?.hue || 260})`, color: `oklch(0.45 0.15 ${D.CATEGORIES[cat]?.hue || 260})` }}><window.Icon name={D.CATEGORIES[cat]?.glyph || "box"} size={17} /></span>
              <span className="display" style={{ fontSize: 17 }}>{D.CATEGORIES[cat]?.label || cat}</span>
              <span style={{ fontSize: 12, fontWeight: 700, color: "var(--text-faint)", padding: "2px 9px", borderRadius: 999, background: "var(--surface-3)" }}>{items.length}</span>
              <span className="mono" style={{ marginLeft: "auto", fontSize: 13, color: "var(--text-muted)" }}>{D.fmtNaira(value)}</span>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(280px,1fr))", gap: 14 }}>
              {items.map((a, i) => <AssetCard key={a.id} a={a} onOpen={onOpen} i={i} flash={a.id === flashId} selMode={selMode} selected={!!(sel && sel[a.id])} />)}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/* ---- PLACES view (grouped by named site/location) ---- */
// A "place" is a named site — a home, shop, warehouse or office at a location.
// Assets carry a `place`; this groups them so a whole site (building + contents)
// can be insured as one bundle, and several sites can roll up into one master
// policy. Assets with no place sit under "Not assigned to a place yet".
function PlacesView({ assets, onOpen, flashId, selMode, sel, query, onBundle, onUpdate, profile, addToast }) {
  const D = window.VC_DATA;
  const q = (query || "").toLowerCase();
  const shown = assets.filter((a) => !q || a.name.toLowerCase().includes(q) || (a.place || "").toLowerCase().includes(q));
  const placed = shown.filter((a) => (a.place || "").trim());
  const unplaced = shown.filter((a) => !(a.place || "").trim());

  // group placed assets by their place name (preserve first-seen order, sort by value)
  const map = new Map();
  placed.forEach((a) => { const k = a.place.trim(); if (!map.has(k)) map.set(k, []); map.get(k).push(a); });
  const places = [...map.entries()]
    .map(([name, items]) => ({ name, items, value: items.reduce((s, a) => s + (a.replacement || 0), 0) }))
    .sort((x, y) => y.value - x.value);

  const [openSet, setOpenSet] = useStateV(() => ({}));
  const [siteSel, setSiteSel] = useStateV({}); // place name -> selected for master policy
  const toggleOpen = (n) => setOpenSet((s) => ({ ...s, [n]: !s[n] }));
  const toggleSite = (n) => setSiteSel((s) => ({ ...s, [n]: !s[n] }));

  const uninsuredOf = (items) => items.filter((a) => a.status !== "insured");
  const insureSite = (items) => {
    const list = uninsuredOf(items);
    if (!list.length) { addToast && addToast("Everything at this place is already insured", "check"); return; }
    onBundle && onBundle(list); // opens Insure in bundle mode
  };
  const selectedSites = places.filter((p) => siteSel[p.name]);
  const masterInsure = () => {
    const list = selectedSites.flatMap((p) => uninsuredOf(p.items));
    if (!list.length) { addToast && addToast("The selected sites are already fully insured", "check"); return; }
    onBundle && onBundle(list);
  };

  if (places.length === 0 && unplaced.length === 0) {
    return <div style={{ textAlign: "center", padding: 60, color: "var(--text-faint)" }}><window.Icon name="map" size={40} style={{ opacity: 0.4 }} /><p style={{ marginTop: 12, fontSize: 15 }}>No assets yet.</p></div>;
  }

  return (
    <div style={{ display: "grid", gap: 16, paddingBottom: selectedSites.length ? 80 : 0 }}>
      {places.length > 1 && (
        <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "10px 14px", borderRadius: 12, background: "var(--accent-soft)", color: "var(--accent-ink)", fontSize: 13, fontWeight: 600 }}>
          <window.Icon name="sparkle" size={15} />Tick sites to insure them together as one master policy across locations.
        </div>
      )}

      {places.map(({ name, items, value }) => {
        const insured = items.filter((a) => a.status === "insured").length;
        const allInsured = insured === items.length;
        const open = !!openSet[name];
        const picked = !!siteSel[name];
        return (
          <div key={name} style={{ borderRadius: 18, background: "var(--surface)", border: "1px solid " + (picked ? "var(--accent)" : "var(--border)"), boxShadow: "var(--shadow-sm)", overflow: "hidden" }}>
            <div style={{ display: "flex", alignItems: "center", gap: 12, padding: 16, flexWrap: "wrap" }}>
              {places.length > 1 && (
                <button onClick={() => toggleSite(name)} title="Add to master policy" style={{ display: "grid", placeItems: "center", width: 22, height: 22, borderRadius: 6, flexShrink: 0, border: "2px solid " + (picked ? "var(--accent)" : "var(--border)"), background: picked ? "var(--accent)" : "transparent", color: "#fff" }}>
                  {picked && <window.Icon name="check" size={13} stroke={3} />}
                </button>
              )}
              {/* flex-basis 220px: on narrow screens the row wraps the Insure button
                  to its own line instead of crushing the site name to one letter */}
              <button onClick={() => toggleOpen(name)} style={{ display: "flex", alignItems: "center", gap: 12, flex: "1 1 220px", textAlign: "left", minWidth: 0 }}>
                <span style={{ display: "grid", placeItems: "center", width: 38, height: 38, borderRadius: 11, flexShrink: 0, background: "var(--accent-soft)", color: "var(--accent)" }}><window.Icon name="home" size={19} /></span>
                <div style={{ minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    <span className="display" style={{ fontSize: 16.5, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{name}</span>
                    <window.Icon name="chevD" size={15} style={{ color: "var(--text-faint)", transform: open ? "rotate(180deg)" : "none", transition: "transform .25s", flexShrink: 0 }} />
                  </div>
                  <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 1 }}>
                    {items.length} item{items.length > 1 ? "s" : ""} · <span className="mono">{D.fmtNaira(value)}</span> · {allInsured ? "fully insured" : `${insured}/${items.length} insured`}
                  </div>
                </div>
              </button>
              <window.Btn size="sm" variant={allInsured ? "outline" : "primary"} icon="shield" onClick={() => insureSite(items)} style={{ whiteSpace: "nowrap", flexShrink: 0 }}>
                {allInsured ? "Insured" : "Insure site & contents"}
              </window.Btn>
            </div>
            {open && (
              <div style={{ padding: "0 16px 16px", display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(260px,1fr))", gap: 12, borderTop: "1px solid var(--border)", paddingTop: 14 }}>
                {items.map((a, i) => <AssetCard key={a.id} a={a} onOpen={onOpen} i={i} flash={a.id === flashId} selMode={selMode} selected={!!(sel && sel[a.id])} />)}
              </div>
            )}
          </div>
        );
      })}

      {unplaced.length > 0 && (
        <div style={{ borderRadius: 18, background: "var(--surface-2)", border: "1px dashed var(--border)", padding: 16 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 12 }}>
            <span style={{ display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 10, background: "var(--surface-3)", color: "var(--text-faint)" }}><window.Icon name="box" size={17} /></span>
            <div>
              <div className="display" style={{ fontSize: 15.5 }}>Not assigned to a place yet</div>
              <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>Open an item to give it a place, or ask Cova to group them into a home, office or warehouse.</div>
            </div>
            <span style={{ marginLeft: "auto", fontSize: 12, fontWeight: 700, color: "var(--text-faint)", padding: "2px 9px", borderRadius: 999, background: "var(--surface-3)" }}>{unplaced.length}</span>
          </div>
          <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(260px,1fr))", gap: 12 }}>
            {unplaced.map((a, i) => <AssetCard key={a.id} a={a} onOpen={onOpen} i={i} flash={a.id === flashId} selMode={selMode} selected={!!(sel && sel[a.id])} />)}
          </div>
        </div>
      )}

      {selectedSites.length > 0 && (
        <div style={{ position: "fixed", left: 0, right: 0, bottom: 22, display: "flex", justifyContent: "center", zIndex: 150, pointerEvents: "none" }}>
          <div style={{ pointerEvents: "auto", display: "flex", alignItems: "center", gap: 14, padding: "12px 16px", borderRadius: 16, background: "var(--surface)", border: "1px solid var(--border)", boxShadow: "var(--shadow-lg)", maxWidth: "92%" }}>
            <div style={{ display: "flex", flexDirection: "column" }}>
              <span style={{ fontSize: 13.5, fontWeight: 700 }}>{selectedSites.length} site{selectedSites.length > 1 ? "s" : ""} selected</span>
              <span className="mono" style={{ fontSize: 11.5, color: "var(--text-muted)" }}>{D.fmtNaira(selectedSites.reduce((s, p) => s + p.value, 0))} total</span>
            </div>
            <window.Btn icon="layers" onClick={masterInsure} style={{ whiteSpace: "nowrap" }}>Insure as one master policy</window.Btn>
            <button onClick={() => setSiteSel({})} style={{ fontSize: 13, fontWeight: 700, color: "var(--text-muted)", padding: "6px 10px" }}>Clear</button>
          </div>
        </div>
      )}
    </div>
  );
}

/* ---- PORTFOLIO PANEL — the hero card at the top of VaultLog ----
   One card, two faces: the live figures ("Now") and how the vault has grown over
   time ("Growth"). A small toggle flips between them, and until the user takes
   control it also AUTO-cycles every few seconds so the growth story surfaces on
   its own. Growth only appears once there are at least two logged assets (before
   that there's no curve to draw), so single-asset vaults just see the figures. */
function PortfolioPanel({ assets, p, onChat, t }) {
  const [face, setFace] = useStateV("summary"); // summary | growth
  const [locked, setLocked] = useStateV(false);  // user tapped the toggle -> stop auto-cycling
  const canGrow = assets.length >= 2;

  React.useEffect(() => {
    if (locked || !canGrow) return undefined;
    const id = setInterval(() => setFace((f) => (f === "summary" ? "growth" : "summary")), 7000);
    return () => clearInterval(id);
  }, [locked, canGrow]);
  React.useEffect(() => { if (!canGrow && face !== "summary") setFace("summary"); }, [canGrow]);

  const pick = (f) => { setLocked(true); setFace(f); };
  const showGrowth = canGrow && face === "growth";

  return (
    <div style={{ position: "relative", marginTop: 24, padding: 24, borderRadius: 24, background: "linear-gradient(135deg, var(--surface), var(--surface-2))", border: "1px solid var(--border)", boxShadow: "var(--shadow-sm)", overflow: "hidden" }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginBottom: 18 }}>
        <div style={{ fontSize: 12.5, color: "var(--text-faint)", fontWeight: 700, textTransform: "uppercase", letterSpacing: ".03em" }}>{showGrowth ? "Vault value over time" : "Portfolio at a glance"}</div>
        {canGrow && (
          <div style={{ display: "flex", gap: 2, padding: 3, borderRadius: 11, background: "var(--surface-3)", border: "1px solid var(--border)", flexShrink: 0 }}>
            {[["summary", "Now", "vault"], ["growth", "Growth", "growth"]].map(([id, lbl, ic]) => (
              <button key={id} onClick={() => pick(id)} className="vc-focus" title={id === "growth" ? "How your vault has grown" : "Current figures"}
                style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "6px 11px", borderRadius: 8, fontSize: 12.5, fontWeight: 700, cursor: "pointer", background: face === id ? "var(--surface)" : "transparent", color: face === id ? "var(--accent)" : "var(--text-faint)", boxShadow: face === id ? "var(--shadow-sm)" : "none", transition: "color .2s" }}>
                <window.Icon name={ic} size={14} />{lbl}
              </button>
            ))}
          </div>
        )}
      </div>
      <div key={face} style={{ animation: "vc-face-in .45s var(--ease) both" }}>
        {showGrowth ? <GrowthFace assets={assets} onChat={onChat} /> : <SummaryFace p={p} assets={assets} onChat={onChat} t={t} />}
      </div>
    </div>
  );
}

/* The live-figures face: coverage ring, headline totals, gap + mini-stats. */
function SummaryFace({ p, assets, onChat, t }) {
  const D = window.VC_DATA;
  return (
    <div className="vc-grid" style={{ display: "grid", gridTemplateColumns: "auto 1fr", gap: 24, alignItems: "center" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 22, flexWrap: "wrap" }}>
        <window.CoverageRing pct={p.covered} size={128} sub="covered" />
        <div style={{ display: "grid", gap: 14 }}>
          <Stat label={t("vl.totalReplacement")} value={D.fmtFull(p.replacement)} big />
          <Stat label={t("vl.sumInsured")} value={D.fmtFull(p.insured)} c="var(--success)" />
        </div>
      </div>
      <div className="vc-grid-2" style={{ display: "grid", gridTemplateColumns: "repeat(2,1fr)", gap: 12, alignContent: "start" }}>
        <GapCard p={p} onInsure={() => onChat("Close my coverage gap")} />
        <MiniStat icon="layers" label={t("vl.activeAssets")} value={assets.length} sub={t("vl.fullyCovered", { n: p.counts.insured })} />
        <MiniStat icon="quote" label={t("vl.annualPremium")} value={D.fmtFull(p.premium)} sub={t("vl.acrossPolicies")} />
      </div>
    </div>
  );
}

/* ---- GROWTH face (how the vault grows over time) ----
   No dedicated valuation-history table exists yet (an asset stores only its
   CURRENT value), so this reconstructs the growth curve from each asset's
   createdAt: the vault total steps up on the day every item was logged. It plots
   the cumulative replacement value (total vault worth) against the cumulative
   sum-insured (what's actually covered) so the widening gap between the two lines
   reads as "uninsured value you've added". The 12-month time-weighted average is
   surfaced too, since that's the figure VaultCova prefers for annual stock cover. */
function GrowthFace({ assets, onChat }) {
  const D = window.VC_DATA;
  const isMobile = window.useIsMobile ? window.useIsMobile() : false;

  const model = useMemoV(() => {
    const now = Date.now();
    // Order by when each asset entered the vault. Guest/optimistic adds without a
    // stored createdAt are treated as "today" so they land at the tip of the curve.
    const dated = assets
      .map((a) => ({ a, t: a.createdAt ? new Date(a.createdAt).getTime() : now }))
      .filter((x) => !isNaN(x.t))
      .sort((x, y) => x.t - y.t);
    if (dated.length === 0) return null;

    // Cumulative step points: one per add, carrying running totals.
    let cumRepl = 0, cumIns = 0;
    const pts = dated.map(({ a, t }, i) => {
      cumRepl += a.replacement || 0;
      cumIns += a.sumInsured || 0;
      return { t, repl: cumRepl, ins: cumIns, name: a.name, add: a.replacement || 0, idx: i };
    });
    const firstT = pts[0].t;
    // Seed a zero baseline just before the first add so the curve rises from 0.
    const baseT = firstT - Math.max((now - firstT) * 0.04, 36e5);
    const series = [{ t: baseT, repl: 0, ins: 0 }, ...pts];

    // Time-weighted average of total vault value over the trailing 12 months,
    // measured from when logging actually began (never averaging in empty months
    // before the first asset) — a close proxy for annual stock value.
    const winStart = Math.max(firstT, now - 365 * 864e5);
    let area = 0;
    for (let i = 0; i < pts.length; i++) {
      const segStart = Math.max(pts[i].t, winStart);
      const segEnd = i + 1 < pts.length ? pts[i + 1].t : now;
      if (segEnd > segStart) area += pts[i].repl * (segEnd - segStart);
    }
    const winDur = now - winStart;
    const avg12 = winDur > 0 ? area / winDur : cumRepl;

    // Growth over the last year (value added in the trailing 12 months).
    const yearAgo = now - 365 * 864e5;
    const priorRepl = pts.filter((p) => p.t <= yearAgo).reduce((s, p) => Math.max(s, p.repl), 0);
    const grownYear = cumRepl - priorRepl;

    return { pts, series, firstT, now, cumRepl, cumIns, avg12, grownYear, count: pts.length };
  }, [assets]);

  if (!model) return null; // PortfolioPanel only mounts this when there's a curve to draw

  // Draw the full history; extend the curve to "now" so the tip shows the live total.
  let drawn = model.series.slice();
  if (drawn.length < 2) drawn = model.series.slice(-2);
  const tail = drawn[drawn.length - 1];
  const view = tail.t < model.now ? [...drawn, { t: model.now, repl: tail.repl, ins: tail.ins }] : drawn;

  // ---- SVG geometry ----
  // The viewBox aspect ratio is chosen per device: a short-wide chart reads well
  // on desktop, a taller one keeps mobile legible (the SVG scales uniformly, so
  // aspect ratio is what controls the rendered height).
  const W = isMobile ? 360 : 720, H = isMobile ? 200 : 150, padL = 8, padR = 8, padT = 16, padB = 22;
  const t0 = view[0].t, t1 = view[view.length - 1].t || (t0 + 1);
  const maxV = Math.max(1, ...view.map((p) => p.repl));
  const x = (t) => padL + ((t - t0) / Math.max(1, t1 - t0)) * (W - padL - padR);
  const y = (v) => padT + (1 - v / maxV) * (H - padT - padB);
  const linePath = (key) => view.map((p, i) => `${i ? "L" : "M"}${x(p.t).toFixed(1)} ${y(p[key]).toFixed(1)}`).join(" ");
  const areaPath = `${linePath("repl")} L${x(t1).toFixed(1)} ${y(0).toFixed(1)} L${x(t0).toFixed(1)} ${y(0).toFixed(1)} Z`;
  const fmtDate = (t) => new Date(t).toLocaleDateString("en-NG", { day: "numeric", month: "short", year: "2-digit" });

  const coveredPct = model.cumRepl > 0 ? Math.round((model.cumIns / model.cumRepl) * 100) : 0;
  const grew = model.grownYear > 0;

  return (
    <div style={{ display: "grid", gap: 16 }}>
      {/* headline: current value + growth chip + legend */}
      <div style={{ display: "flex", alignItems: "flex-end", justifyContent: "space-between", gap: 14, flexWrap: "wrap" }}>
        <div>
          <div className="display mono" style={{ fontSize: isMobile ? 26 : 32, lineHeight: 1 }}>{D.fmtFull(model.cumRepl)}</div>
          {grew && (
            <div style={{ display: "inline-flex", alignItems: "center", gap: 6, marginTop: 8, padding: "3px 9px", borderRadius: 999, background: "var(--success-soft)", color: "var(--success)", fontSize: 12.5, fontWeight: 700 }}>
              <window.Icon name="trend" size={14} />+{D.fmtNaira(model.grownYear)} in the last 12 months
            </div>
          )}
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap", fontSize: 12.5 }}>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, color: "var(--text-muted)" }}><span style={{ width: 10, height: 3, borderRadius: 2, background: "var(--accent)" }} />Total worth</span>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, color: "var(--text-muted)" }}><span style={{ width: 10, height: 3, borderRadius: 2, background: "var(--success)" }} />Insured</span>
        </div>
      </div>

      <div>
        <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: "block", overflow: "visible" }}>
          <defs>
            <linearGradient id="vc-growth-fill" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor="var(--accent)" stopOpacity="0.28" />
              <stop offset="100%" stopColor="var(--accent)" stopOpacity="0.02" />
            </linearGradient>
          </defs>
          {[0.5, 1].map((f) => (
            <line key={f} x1={padL} x2={W - padR} y1={y(maxV * f)} y2={y(maxV * f)} stroke="var(--border)" strokeWidth="1" strokeDasharray="3 5" vectorEffect="non-scaling-stroke" />
          ))}
          <path d={areaPath} fill="url(#vc-growth-fill)" />
          <path d={linePath("repl")} fill="none" stroke="var(--accent)" strokeWidth="2.5" strokeLinejoin="round" strokeLinecap="round" vectorEffect="non-scaling-stroke" />
          <path d={linePath("ins")} fill="none" stroke="var(--success)" strokeWidth="2" strokeLinejoin="round" strokeLinecap="round" strokeDasharray="1 5" vectorEffect="non-scaling-stroke" />
          {view.filter((pt) => pt.repl > 0 && pt.name).map((pt, i) => (
            <circle key={i} cx={x(pt.t)} cy={y(pt.repl)} r="3" fill="var(--surface)" stroke="var(--accent)" strokeWidth="2" vectorEffect="non-scaling-stroke">
              <title>{pt.name} · {fmtDate(pt.t)} · {D.fmtNaira(pt.repl)} total</title>
            </circle>
          ))}
          <circle cx={x(t1)} cy={y(view[view.length - 1].repl)} r="4.5" fill="var(--accent)" stroke="var(--surface)" strokeWidth="2" vectorEffect="non-scaling-stroke" />
        </svg>
        <div style={{ display: "flex", justifyContent: "space-between", fontSize: 11.5, color: "var(--text-faint)", marginTop: 2 }}>
          <span>{fmtDate(t0)}</span><span>Today</span>
        </div>
      </div>

      {/* compact stat strip — the 12-month average is the figure VaultCova insures on */}
      <div style={{ display: "grid", gridTemplateColumns: isMobile ? "1fr 1fr" : "repeat(3,1fr)", gap: 12 }}>
        <GrowthStat icon="clock" label="12-month average" value={D.fmtFull(model.avg12)} sub="valued for annual cover" accent />
        <GrowthStat icon="shield" label="Currently insured" value={D.fmtFull(model.cumIns)} sub={`${coveredPct}% of vault value`} />
        <GrowthStat icon="vault" label="First logged" value={fmtDate(model.firstT)} sub={`${model.count} assets · ${Math.max(1, Math.round((model.now - model.firstT) / 864e5))} days`} />
      </div>
    </div>
  );
}

function GrowthStat({ icon, label, value, sub, accent }) {
  return (
    <div style={{ padding: 14, borderRadius: 14, background: accent ? "var(--accent-soft)" : "var(--surface)", border: "1px solid " + (accent ? "var(--accent)" : "var(--border)"), minWidth: 0 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 7, fontSize: 12, fontWeight: 700, color: accent ? "var(--accent)" : "var(--text-faint)" }}>
        <window.Icon name={icon} size={15} />{label}
      </div>
      <div className="display mono" style={{ fontSize: "clamp(15px, 4.4cqi, 20px)", marginTop: 6, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div>
      <div style={{ fontSize: 11.5, color: "var(--text-muted)", marginTop: 2 }}>{sub}</div>
    </div>
  );
}

/* ---- BUNDLE BAR (cross-category selection actions) ---- */
function BundleBar({ assets, onBundle, onMove, onClear }) {
  const D = window.VC_DATA;
  const [moveOpen, setMoveOpen] = useStateV(false);
  const n = assets.length;
  const value = assets.reduce((s, a) => s + (a.replacement || 0), 0);
  const cats = Object.keys(D.CATEGORIES);
  return (
    <div style={{ position: "fixed", left: 0, right: 0, bottom: 22, display: "flex", justifyContent: "center", zIndex: 150, pointerEvents: "none" }}>
      <div style={{ pointerEvents: "auto", display: "flex", alignItems: "center", gap: 14, padding: "12px 16px", borderRadius: 16, background: "var(--surface)", border: "1px solid var(--border)", boxShadow: "var(--shadow-lg)", maxWidth: "92%" }}>
        <div style={{ display: "flex", flexDirection: "column" }}>
          <span style={{ fontSize: 13.5, fontWeight: 700 }}>{n} selected</span>
          <span className="mono" style={{ fontSize: 11.5, color: "var(--text-muted)" }}>{D.fmtNaira(value)} total</span>
        </div>
        <div style={{ position: "relative" }}>
          <window.Btn variant="outline" icon="box" onClick={() => setMoveOpen((o) => !o)} disabled={!n} style={{ whiteSpace: "nowrap" }}>Move to…</window.Btn>
          {moveOpen && n > 0 && (
            <div style={{ position: "absolute", bottom: "calc(100% + 8px)", left: 0, minWidth: 190, padding: 6, borderRadius: 12, background: "var(--surface)", border: "1px solid var(--border)", boxShadow: "var(--shadow-lg)", display: "grid", gap: 2 }}>
              {cats.map((c) => (
                <button key={c} onClick={() => { setMoveOpen(false); onMove(c); }} style={{ display: "flex", alignItems: "center", gap: 9, padding: "8px 10px", borderRadius: 9, fontSize: 13.5, fontWeight: 600, color: "var(--text)", textAlign: "left" }}
                  onMouseEnter={(e) => e.currentTarget.style.background = "var(--surface-2)"} onMouseLeave={(e) => e.currentTarget.style.background = "transparent"}>
                  <window.Icon name={D.CATEGORIES[c]?.glyph || "box"} size={15} style={{ color: "var(--text-faint)" }} />{D.CATEGORIES[c]?.label || c}
                </button>
              ))}
            </div>
          )}
        </div>
        <window.Btn icon="layers" onClick={onBundle} disabled={!n} style={{ whiteSpace: "nowrap" }}>Insure {n > 1 ? `${n} as a bundle` : "as cover"}</window.Btn>
        <button onClick={onClear} title="Cancel" style={{ display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 999, color: "var(--text-faint)" }}><window.Icon name="close" size={17} /></button>
      </div>
    </div>
  );
}

/* ---- ASSET DETAIL drawer ---- */
function AssetDetail({ asset, allAssets, onClose, onInsure, onBundle, onChat, profile, userName = "Adebola", onUpdate, onDelete, statementsOK, onUpgrade }) {
  const D = window.VC_DATA;
  const isMobile = window.useIsMobile ? window.useIsMobile() : false;
  // Closing plays the drop/slide-out before the parent unmounts us, so the
  // sheet animates out instead of blinking away. confirmDel gates the delete.
  const [closing, setClosing] = useStateV(false);
  const [confirmDel, setConfirmDel] = useStateV(false);
  // Rename: allowed until cover is bound (an insured asset is named on its
  // policy/certificate, so the server also locks it with a 409).
  const [editingName, setEditingName] = useStateV(false);
  const [nameVal, setNameVal] = useStateV((asset && asset.name) || "");
  const doClose = () => { if (closing) return; setClosing(true); setTimeout(() => onClose && onClose(), 300); };
  const doDelete = () => { if (closing) return; setClosing(true); setTimeout(() => { onDelete && asset && onDelete(asset.id); onClose && onClose(); }, 280); };
  // Hero photo: the asset's latest uploaded photo document; the pill uploads one.
  const [heroUrl, setHeroUrl] = useStateV(null);
  const [heroBusy, setHeroBusy] = useStateV(false);
  const persisted = asset && asset.id && !String(asset.id).startsWith("VA-");
  React.useEffect(() => {
    setHeroUrl(null);
    if (!persisted) return;
    fetch("/api/documents?assetId=" + asset.id).then((r) => r.json()).then((d) => {
      const photo = d && Array.isArray(d.items) && d.items.find((x) => x.kind === "photo" && x.dataUrl && x.mime && x.mime.indexOf("image/") === 0);
      if (photo) setHeroUrl(photo.dataUrl);
    }).catch(() => {});
  }, [persisted, asset && asset.id]);
  const pickHero = (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!file || !persisted || file.size > 6 * 1024 * 1024) return;
    const reader = new FileReader();
    reader.onload = () => {
      setHeroBusy(true);
      fetch("/api/documents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ assetId: asset.id, kind: "photo", name: file.name, dataUrl: reader.result }) })
        .then((r) => r.json())
        .then((doc) => { if (doc && doc.dataUrl) setHeroUrl(doc.dataUrl); else if (doc && doc.id) setHeroUrl(reader.result); })
        .catch(() => {})
        .finally(() => setHeroBusy(false));
    };
    reader.readAsDataURL(file);
  };
  if (!asset) return null;
  const a = asset;
  const pct = a.replacement ? Math.min(1, a.sumInsured / a.replacement) : 0;
  const gap = Math.max(0, a.replacement - a.sumInsured);
  const nameLocked = a.status !== "uninsured"; // bound to a policy → name is fixed
  const saveName = () => {
    const v = nameVal.trim();
    if (v && v !== a.name) onUpdate && onUpdate(a.id, { name: v });
    setEditingName(false);
  };
  // Portalled to <body> so it escapes the app column's containing block
  // (main.vc-main has container-type, which traps even fixed children) and always
  // covers the full viewport. Desktop: a right-side drawer. Mobile: a clean
  // near-full-height bottom sheet with a rounded top and a grab handle.
  const overlay = (
    <div onClick={doClose} style={{ position: "fixed", inset: 0, zIndex: 1000, display: "flex", justifyContent: isMobile ? "stretch" : "flex-end", alignItems: isMobile ? "flex-end" : "stretch", background: "rgba(8,9,26,.5)", backdropFilter: "blur(6px)", WebkitBackdropFilter: "blur(6px)", animation: closing ? "vc-fade-out .28s var(--ease) both" : "vc-fade-in .3s var(--ease) both" }}>
      <div onClick={(e) => e.stopPropagation()} style={isMobile
        ? { width: "100%", maxHeight: "calc(88dvh - env(safe-area-inset-top))", marginTop: "env(safe-area-inset-top)", display: "flex", flexDirection: "column", overflow: "hidden", background: "var(--surface)", borderRadius: "24px 24px 0 0", boxShadow: "0 -18px 50px -12px rgba(8,9,26,.45)", animation: closing ? "vc-sheet-down .3s cubic-bezier(.4,.0,1,1) both" : "vc-sheet-rise .42s cubic-bezier(.16,1,.3,1) both" }
        : { width: 460, maxWidth: "92%", height: "100%", overflowY: "auto", background: "var(--surface)", borderLeft: "1px solid var(--border)", boxShadow: "var(--shadow-xl)", animation: closing ? "vc-slide-r-out .28s var(--ease) both" : "vc-slide-r .4s var(--ease) both" }}>
        <style>{`@keyframes vc-slide-r{from{transform:translateX(40px);opacity:.5}to{transform:none;opacity:1}}@keyframes vc-slide-r-out{from{transform:none;opacity:1}to{transform:translateX(40px);opacity:0}}@keyframes vc-sheet-rise{from{transform:translateY(100%)}to{transform:translateY(0)}}@keyframes vc-sheet-down{from{transform:translateY(0)}to{transform:translateY(100%)}}@keyframes vc-fade-out{from{opacity:1}to{opacity:0}}`}</style>
        {isMobile && <div onClick={doClose} style={{ flexShrink: 0, display: "grid", placeItems: "center", padding: "10px 0 6px", cursor: "pointer" }}><span style={{ width: 42, height: 5, borderRadius: 999, background: "var(--border-strong)" }} /></div>}
        <div style={isMobile ? { overflowY: "auto", flex: 1, minHeight: 0 } : { display: "contents" }}>
        {/* asset photo: the user's upload if any, otherwise an animated cover
            that reads as the category (house, car, phone, …) until they add one */}
        <div style={{ position: "relative", height: 180, background: heroUrl ? `url(${heroUrl}) center/cover` : "var(--surface-3)", display: "grid", placeItems: "center" }}>
          {!heroUrl && <window.AssetCoverArt category={a.category} height={180} />}
          <div style={{ position: "absolute", inset: 0, background: "linear-gradient(160deg, transparent, rgba(0,0,0,.35))" }} />
          {persisted && (
            <label className="mono" style={{ position: "relative", color: "rgba(255,255,255,.9)", fontSize: 12, padding: "6px 12px", borderRadius: 999, background: "rgba(0,0,0,.35)", backdropFilter: "blur(4px)", cursor: "pointer" }}>
              {heroBusy ? "uploading…" : heroUrl ? "change photo" : "add asset photo"}
              <input type="file" accept="image/*" onChange={pickHero} style={{ display: "none" }} disabled={heroBusy} />
            </label>
          )}
          <button onClick={doClose} style={{ position: "absolute", top: 16, right: 16, display: "grid", placeItems: "center", width: 36, height: 36, borderRadius: 999, background: "rgba(0,0,0,.35)", color: "#fff", backdropFilter: "blur(6px)" }}><window.Icon name="close" size={18} /></button>
          <div style={{ position: "absolute", left: 18, bottom: 16 }}><window.StatusBadge status={a.status} /></div>
        </div>

        <div style={{ padding: 22 }}>
          {editingName ? (
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <input autoFocus value={nameVal} maxLength={120}
                onChange={(e) => setNameVal(e.target.value)}
                onKeyDown={(e) => { if (e.key === "Enter") saveName(); if (e.key === "Escape") { setNameVal(a.name); setEditingName(false); } }}
                className="display"
                style={{ flex: 1, minWidth: 0, fontSize: 21, fontWeight: 700, padding: "5px 11px", borderRadius: 11, border: "1.5px solid var(--accent)", background: "var(--surface-2)", color: "var(--text)", outline: "none" }} />
              <button onClick={saveName} title="Save name" style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 9, background: "var(--accent)", color: "#fff", border: "none", cursor: "pointer" }}><window.Icon name="check" size={17} /></button>
              <button onClick={() => { setNameVal(a.name); setEditingName(false); }} title="Cancel" style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 34, height: 34, borderRadius: 9, background: "var(--surface-2)", color: "var(--text-muted)", border: "1px solid var(--border)", cursor: "pointer" }}><window.Icon name="close" size={16} /></button>
            </div>
          ) : (
            <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
              <h2 className="display" style={{ fontSize: 23, minWidth: 0, overflowWrap: "anywhere" }}>{a.name}</h2>
              {!nameLocked
                ? <button onClick={() => { setNameVal(a.name); setEditingName(true); }} title="Rename asset" style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 30, height: 30, borderRadius: 8, background: "var(--surface-2)", color: "var(--text-faint)", border: "1px solid var(--border)", cursor: "pointer" }}><window.Icon name="edit" size={15} /></button>
                : <span title="Name is locked once cover is bound" style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 30, height: 30, borderRadius: 8, color: "var(--text-faint)" }}><window.Icon name="lock" size={14} /></span>}
            </div>
          )}
          <p style={{ fontSize: 13.5, color: "var(--text-muted)", marginTop: 5 }}>{a.note ? a.note + " · " : ""}<span className="mono" title="Asset reference">{D.assetRef(a, allAssets)}</span></p>

          {/* value breakdown */}
          <div style={{ marginTop: 20, padding: 16, borderRadius: 16, background: "var(--surface-2)", border: "1px solid var(--border)" }}>
            <div style={{ display: "flex", justifyContent: "space-between", gap: 14, marginBottom: 12 }}>
              <ValueCol label="Purchase price" value={D.fmtFull(a.purchase)} />
              <ValueCol label="Replacement cost" value={D.fmtFull(a.replacement)} accent />
            </div>
            <window.CoverageBar insured={a.sumInsured} replacement={a.replacement} />
            <div style={{ display: "flex", justifyContent: "space-between", marginTop: 10, fontSize: 13 }}>
              <span style={{ color: "var(--text-muted)" }}>Insured for <b className="mono" style={{ color: "var(--text)" }}>{D.fmtFull(a.sumInsured)}</b></span>
              {gap > 0 ? <span style={{ color: "var(--danger)", fontWeight: 700 }}>{D.fmtFull(gap)} exposed</span> : <span style={{ color: "var(--success)", fontWeight: 700 }}>Fully covered</span>}
            </div>
          </div>

          {/* category: auto-assigned, movable any time (organisational) */}
          <CategorySection a={a} onUpdate={onUpdate} />

          {/* valuation: actual vs replacement, and which is the sum insured */}
          <ValuationSection a={a} onUpdate={onUpdate} />

          {/* value tracking (stock/declaration cover): toggle + statement */}
          <ValueTrackingRow a={a} onUpdate={onUpdate} statementsOK={statementsOK} onUpgrade={onUpgrade} onClose={onClose} />

          {/* identification, optional, editable any time */}
          <IdentitySection a={a} onUpdate={onUpdate} />

          {/* policy info */}
          {a.insurer ? (
            <div style={{ marginTop: 14, display: "grid", gap: 10 }}>
              <DetailRow icon="shield" k="Insurer" v={a.insurer} />
              <DetailRow icon="quote" k="Premium" v={D.fmtFull(a.premium) + " / yr"} />
              <DetailRow icon="clock" k="Renews" v={a.renews || "-"} />
              {a.bundle && <DetailRow icon="layers" k="Bundle" v={(D.BUNDLES[profile]||[]).find(b=>b.id===a.bundle)?.name || a.bundle} />}
            </div>
          ) : (
            <div style={{ marginTop: 14, padding: 14, borderRadius: 14, background: "var(--danger-soft)", border: "1px solid var(--danger)", display: "flex", gap: 11, alignItems: "center" }}>
              <window.Icon name="alert" size={20} style={{ color: "var(--danger)" }} />
              <div style={{ fontSize: 13.5, color: "var(--danger)", fontWeight: 600 }}>This asset is uninsured. {D.fmtFull(a.replacement)} of value is fully exposed.</div>
            </div>
          )}

          {/* documents, uploadable + Cova reads them */}
          <DocumentsSection asset={a} />

          {/* actions */}
          <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: 22 }}>
            {gap > 0 && <window.Btn size="lg" icon="shield" style={{ justifyContent: "center" }} onClick={() => { onClose(); onInsure(a); }}>Insure this asset</window.Btn>}
            {a.insurer && (
              <window.Btn variant="soft" icon="download" style={{ justifyContent: "center" }} onClick={() => window.downloadCertificate([a], profile, userName)}>Download policy certificate</window.Btn>
            )}
            <div style={{ display: "flex", gap: 10 }}>
              <window.Btn variant="outline" icon="layers" style={{ flex: 1, justifyContent: "center" }} onClick={() => { onClose(); onBundle(a); }}>{a.bundle ? "Manage bundle" : "Add to bundle"}</window.Btn>
              {/* Claims only make sense on insured assets — uninsured assets get "Insure this asset" above. */}
              {a.insurer && <window.Btn variant="outline" icon="claim" style={{ flex: 1, justifyContent: "center" }} onClick={() => { onClose(); onChat(`Start a claim for ${a.name}`); }}>File claim</window.Btn>}
            </div>

            {/* Delete — only for assets with no cover bound to them. Insured/partial
                assets are protected (the server also blocks them with a 409). */}
            {onDelete && a.status !== "insured" && a.status !== "partial" && (
              confirmDel ? (
                <div style={{ marginTop: 4, padding: 14, borderRadius: 14, background: "var(--danger-soft)", border: "1px solid var(--danger)" }}>
                  <div style={{ fontSize: 13.5, fontWeight: 700, color: "var(--danger)" }}>Delete “{a.name}” permanently?</div>
                  <div style={{ fontSize: 12.5, color: "var(--text-muted)", margin: "3px 0 12px" }}>It's removed from your VaultLog and this can't be undone.</div>
                  <div style={{ display: "flex", gap: 10 }}>
                    <button onClick={() => setConfirmDel(false)} style={{ flex: 1, padding: "10px 0", borderRadius: 11, fontSize: 13.5, fontWeight: 700, background: "var(--surface-2)", color: "var(--text)", border: "1.5px solid var(--border)", cursor: "pointer" }}>Cancel</button>
                    <button onClick={doDelete} style={{ flex: 1, padding: "10px 0", borderRadius: 11, fontSize: 13.5, fontWeight: 700, background: "var(--danger)", color: "#fff", border: "none", cursor: "pointer" }}>Delete asset</button>
                  </div>
                </div>
              ) : (
                <button onClick={() => setConfirmDel(true)} style={{ marginTop: 2, width: "100%", display: "flex", alignItems: "center", justifyContent: "center", gap: 8, padding: "11px 0", borderRadius: 12, fontSize: 13.5, fontWeight: 700, color: "var(--danger)", background: "transparent", border: "1.5px solid var(--border)", cursor: "pointer" }}>
                  <window.Icon name="trash" size={16} /> Delete asset
                </button>
              )
            )}
          </div>
        </div>
        </div>
      </div>
    </div>
  );
  return window.ReactDOM.createPortal(overlay, document.body);
}
function ValueCol({ label, value, accent }) {
  // Two full-precision figures sit side by side here. The detail sheet is
  // portalled to <body>, outside main.vc-main's container context, so cqi
  // isn't available — size with vw. The coefficient is tuned so a 13-digit
  // billions figure still fits its half-column at 320px (the narrowest phone).
  return <div style={{ minWidth: 0 }}><div style={{ fontSize: 11.5, color: "var(--text-faint)", fontWeight: 600 }}>{label}</div><div className="display mono" style={{ fontSize: "clamp(13px, 4vw, 19px)", marginTop: 3, color: accent ? "var(--accent)" : "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{value}</div></div>;
}

// Move an asset to another category. Cova auto-categorises on add, but you can
// recategorise any time, it's organisational (grouping + which cover Cova
// suggests), so it stays available even after the asset is insured.
function CategorySection({ a, onUpdate }) {
  const D = window.VC_DATA;
  const cats = Object.keys(D.CATEGORIES);
  const change = (cat) => { if (cat !== a.category && onUpdate) onUpdate(a.id, { category: cat }); };
  return (
    <div style={{ marginTop: 18 }}>
      <div style={{ fontSize: 12.5, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em", marginBottom: 10 }}>Category</div>
      <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
        {cats.map((c) => {
          const on = a.category === c;
          return (
            <button key={c} onClick={() => change(c)} style={{
              display: "inline-flex", alignItems: "center", gap: 6, padding: "8px 12px", borderRadius: 999, fontSize: 12.5, fontWeight: 700, cursor: "pointer",
              background: on ? "var(--accent)" : "var(--surface-2)", color: on ? "#fff" : "var(--text-muted)",
              border: "1.5px solid " + (on ? "var(--accent)" : "var(--border)"), transition: "all .15s",
            }}><window.Icon name={D.CATEGORIES[c]?.glyph || "box"} size={14} />{D.CATEGORIES[c]?.label || c}</button>
          );
        })}
      </div>
    </div>
  );
}

// Upload receipts / photos / documents. Cova reads images (Claude vision) and
// shows what it saw, which strengthens the assessment.
function DocumentsSection({ asset }) {
  const [docs, setDocs] = useStateV([]);
  const [busy, setBusy] = useStateV("");
  const [err, setErr] = useStateV(null);
  const persisted = asset && asset.id && !String(asset.id).startsWith("VA-");

  React.useEffect(() => {
    if (!persisted) return;
    fetch("/api/documents?assetId=" + asset.id).then((r) => r.json()).then((d) => { if (d && Array.isArray(d.items)) setDocs(d.items); }).catch(() => {});
  }, [persisted, asset && asset.id]);

  const pick = (kind) => (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!file) return;
    if (file.size > 6 * 1024 * 1024) { setErr("File too large (max 6MB)."); return; }
    const reader = new FileReader();
    reader.onload = () => {
      setBusy(kind); setErr(null);
      fetch("/api/documents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ assetId: asset.id, kind, name: file.name, dataUrl: reader.result }) })
        .then((r) => r.json())
        .then((doc) => { if (doc && doc.id) setDocs((xs) => [doc, ...xs]); else setErr((doc && doc.error) || "Upload failed"); })
        .catch(() => setErr("Upload failed"))
        .finally(() => setBusy(""));
    };
    reader.readAsDataURL(file);
  };
  const remove = (id) => { fetch("/api/documents/" + id, { method: "DELETE" }); setDocs((xs) => xs.filter((d) => d.id !== id)); };

  const KINDS = [["receipt", "Receipt", "doc"], ["photo", "Photo", "eye"], ["doc", "Document", "doc"]];

  return (
    <div style={{ marginTop: 18 }}>
      <div style={{ fontSize: 12.5, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em", marginBottom: 10 }}>Documents & photos</div>
      {!persisted && <p style={{ fontSize: 12.5, color: "var(--text-faint)" }}>Add this asset to your vault first, then you can upload receipts and photos.</p>}
      {persisted && (
        <>
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
            {KINDS.map(([kind, label, ic]) => (
              <label key={kind} style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "8px 12px", borderRadius: 11, background: "var(--surface-2)", border: "1.5px dashed var(--border-strong)", fontSize: 12.5, fontWeight: 600, color: "var(--text-muted)", cursor: "pointer" }}>
                <window.Icon name={busy === kind ? "clock" : ic} size={15} />{busy === kind ? "Cova is reading…" : "Add " + label}
                <input type="file" accept={kind === "doc" ? "image/*,application/pdf" : "image/*"} onChange={pick(kind)} style={{ display: "none" }} disabled={!!busy} />
              </label>
            ))}
          </div>
          {err && <p style={{ fontSize: 12, color: "var(--danger)", marginTop: 8 }}>{err}</p>}
          <div style={{ display: "grid", gap: 8, marginTop: 10 }}>
            {docs.length === 0 && <p style={{ fontSize: 12.5, color: "var(--text-faint)" }}>No documents yet. Upload a receipt or photo and Cova will read it.</p>}
            {docs.map((d) => (
              <div key={d.id} style={{ display: "flex", gap: 11, padding: 11, borderRadius: 12, background: "var(--surface-2)", border: "1px solid var(--border)" }}>
                {d.dataUrl && d.mime && d.mime.indexOf("image/") === 0
                  ? <img src={d.dataUrl} alt={d.name} style={{ width: 46, height: 46, borderRadius: 9, objectFit: "cover", flexShrink: 0 }} />
                  : <span style={{ flexShrink: 0, display: "grid", placeItems: "center", width: 46, height: 46, borderRadius: 9, background: "var(--surface-3)", color: "var(--text-faint)" }}><window.Icon name="doc" size={20} /></span>}
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                    <span style={{ fontSize: 10.5, fontWeight: 700, textTransform: "uppercase", color: "var(--accent)" }}>{d.kind}</span>
                    <span style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{d.name}</span>
                  </div>
                  {d.covaRead
                    ? <div style={{ fontSize: 12, color: "var(--text-muted)", marginTop: 3, lineHeight: 1.45 }}><b style={{ color: "var(--accent)" }}>Cova read:</b> {d.covaRead}</div>
                    : <div style={{ fontSize: 11.5, color: "var(--text-faint)", marginTop: 3 }}>Stored. Cova reads images when its engine is connected.</div>}
                </div>
                <button onClick={() => remove(d.id)} style={{ flexShrink: 0, fontSize: 11.5, fontWeight: 700, color: "var(--danger)" }}>Remove</button>
              </div>
            ))}
          </div>
        </>
      )}
    </div>
  );
}

// Actual cost vs replacement cost, and which one is the sum insured. Cova
// recommends replacement cost and explains how to value it. Locked once cover
// is bound (the price is fixed at bind); descriptive info stays editable.
// Money input that reads naturally: digits grouped with commas as you type
// (₦18,000,000), plus shorthand like "18m" or "250k". Defined at MODULE level (not
// inside ValuationSection) so it keeps a stable identity across renders — a
// render-nested component would remount the <input> on every keystroke, which is
// what made typing feel laggy and drop the cursor.
function VLMoneyField({ label, val, set, accent, locked }) {
  const D = window.VC_DATA;
  return (
    <label style={{ display: "block", minWidth: 0 }}>
      <span style={{ fontSize: 12, fontWeight: 600, color: "var(--text-muted)" }}>{label}</span>
      <div style={{ display: "flex", alignItems: "center", gap: 6, marginTop: 5, padding: "0 12px", borderRadius: 11, background: locked ? "var(--surface-3)" : "var(--surface-2)", border: "1.5px solid var(--border)", opacity: locked ? 0.75 : 1, minWidth: 0 }}>
        <span style={{ color: "var(--text-faint)", fontWeight: 700, flexShrink: 0 }}>₦</span>
        <input value={val ? Number(val).toLocaleString("en-NG") : ""}
          onChange={(e) => {
            const raw = e.target.value.trim();
            const parsed = /[kmb]/i.test(raw) && window.parseAmount ? window.parseAmount(raw) : null;
            set(parsed ? String(parsed) : raw.replace(/[^0-9]/g, ""));
          }}
          inputMode="numeric" disabled={locked} placeholder="0"
          style={{ flex: 1, minWidth: 0, border: "none", outline: "none", background: "transparent", padding: "10px 0", fontSize: 15, fontWeight: 700, color: accent ? "var(--accent)" : "var(--text)" }} />
      </div>
      <span style={{ fontSize: 11, color: "var(--text-faint)" }}>{val ? D.fmtNaira(Number(val)) : "e.g. 18m or 18,000,000"}</span>
    </label>
  );
}

function ValuationSection({ a, onUpdate }) {
  const D = window.VC_DATA;
  // Value normally locks once cover is bound — EXCEPT for value-tracked assets
  // (stock under a declaration policy is re-declared while insured), which stay
  // editable so each change is snapshotted into the statement.
  const bound = a.status === "insured" || a.status === "partial";
  const locked = bound && !a.trackValue;
  const [actual, setActual] = useStateV(String(a.purchaseValue || ""));
  const [replacement, setReplacement] = useStateV(String(a.replacementValue || ""));
  const [basis, setBasis] = useStateV(a.sumInsuredBasis || "replacement");
  const [note, setNote] = useStateV("");
  const [saved, setSaved] = useStateV(false);
  const replChanged = Number(replacement) !== (a.replacementValue || 0);
  const dirty = Number(actual) !== (a.purchaseValue || 0) || replChanged || basis !== (a.sumInsuredBasis || "replacement");
  const chosen = basis === "actual" ? Number(actual) : Number(replacement);
  const save = () => {
    if (onUpdate) onUpdate(a.id, { purchase: Number(actual) || 0, replacement: Number(replacement) || 0, sumInsuredBasis: basis, ...(a.trackValue && replChanged && note.trim() ? { valueNote: note.trim() } : {}) });
    setNote(""); setSaved(true); setTimeout(() => setSaved(false), 1800);
  };

  return (
    <div style={{ marginTop: 18 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
        <span style={{ fontSize: 12.5, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em" }}>Valuation & sum insured</span>
        {locked && <span style={{ display: "inline-flex", alignItems: "center", gap: 4, fontSize: 11, fontWeight: 700, color: "var(--text-muted)", padding: "2px 8px", borderRadius: 999, background: "var(--surface-3)" }}><window.Icon name="shield" size={12} />Locked after cover</span>}
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(150px, 1fr))", gap: 10 }}>
        <VLMoneyField label="Actual cost (what you paid)" val={actual} set={setActual} locked={locked} />
        <VLMoneyField label="Replacement cost (to replace today)" val={replacement} set={setReplacement} accent locked={locked} />
      </div>

      <div style={{ marginTop: 12 }}>
        <span style={{ fontSize: 12, fontWeight: 600, color: "var(--text-muted)" }}>Insure at</span>
        <div style={{ display: "flex", gap: 8, marginTop: 6 }}>
          {[["replacement", "Replacement", "Recommended"], ["actual", "Actual cost", null]].map(([id, label, tag]) => (
            <button key={id} onClick={() => !locked && setBasis(id)} disabled={locked} style={{
              flex: 1, padding: "10px", borderRadius: 11, fontSize: 13, fontWeight: 700, cursor: locked ? "default" : "pointer", textAlign: "left",
              background: basis === id ? "var(--accent)" : "var(--surface-2)", color: basis === id ? "#fff" : "var(--text-muted)",
              border: "1.5px solid " + (basis === id ? "var(--accent)" : "var(--border)"),
            }}>{label}{tag && <span style={{ display: "block", fontSize: 10, fontWeight: 600, opacity: 0.85, marginTop: 1 }}>{tag}</span>}</button>
          ))}
        </div>
      </div>

      <div style={{ marginTop: 12, padding: "11px 13px", borderRadius: 12, background: "var(--accent-soft)", display: "flex", gap: 10 }}>
        <span style={{ flexShrink: 0, color: "var(--accent)" }}><window.Icon name="sparkle" size={16} /></span>
        <div style={{ fontSize: 12.5, color: "var(--accent-ink)", lineHeight: 1.5 }}>
          <b>Cova tip:</b> insure at <b>replacement cost</b>, what it costs to buy the same (or equivalent) item new today, so a claim fully restores you. Actual cost can leave you short as prices rise. To value it: check the current retail/market price for the same model, then add delivery and installation.
        </div>
      </div>

      {/* Reason for change — becomes the statement line's note (tracked assets). */}
      {!locked && a.trackValue && replChanged && (
        <input value={note} onChange={(e) => setNote(e.target.value)} maxLength={80} placeholder="Reason for the change (e.g. restocked, sold 120 units)"
          style={{ width: "100%", marginTop: 12, padding: "10px 12px", borderRadius: 11, border: "1.5px solid var(--border)", background: "var(--surface-2)", color: "var(--text)", fontSize: 13, outline: "none" }} />
      )}

      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginTop: 12, gap: 10, flexWrap: "wrap" }}>
        <span style={{ fontSize: 12.5, color: "var(--text-muted)" }}>Sum insured: <b className="mono" style={{ color: "var(--text)" }}>{D.fmtNaira(chosen || 0)}</b></span>
        {!locked && (
          <button onClick={save} disabled={!dirty} style={{ padding: "8px 16px", borderRadius: 11, fontSize: 13, fontWeight: 700, background: dirty ? "var(--accent)" : "var(--surface-3)", color: dirty ? "#fff" : "var(--text-faint)", cursor: dirty ? "pointer" : "default" }}>{saved ? "Saved ✓" : a.trackValue && replChanged ? "Record new value" : "Save valuation"}</button>
        )}
      </div>
    </div>
  );
}

// Value tracking for an asset — the toggle (auto-on for stock) plus, once on, a
// compact value statement (paid) so the history is visible in-app, not only in
// the exported PDF.
function ValueTrackingRow({ a, onUpdate, statementsOK, onUpgrade, onClose }) {
  const D = window.VC_DATA;
  const persisted = a && a.id && !String(a.id).startsWith("VA-");
  const [hist, setHist] = useStateV(null);
  const [loading, setLoading] = useStateV(false);
  const toggle = () => { if (onUpdate) onUpdate(a.id, { trackValue: !a.trackValue }); };

  React.useEffect(() => {
    if (!a.trackValue || !statementsOK || !persisted) { setHist(null); return; }
    setLoading(true);
    fetch("/api/valuations?from=2000-01-01").then((r) => (r.ok ? r.json() : null)).then((d) => {
      if (!d) { setHist(null); return; }
      const mine = (d.assets || []).find((x) => x.id === a.id);
      setHist(mine || { lines: [], opening: a.replacementValue || 0, avg12: a.replacementValue || 0, current: a.replacementValue || 0 });
    }).catch(() => setHist(null)).finally(() => setLoading(false));
  }, [a.trackValue, a.id, statementsOK, a.replacementValue]);

  const fmtDay = (iso) => { const d = new Date(iso); return isNaN(d) ? "" : d.toLocaleDateString("en-NG", { day: "numeric", month: "short", year: "2-digit" }); };
  let rows = [];
  if (hist) { let prev = hist.opening; (hist.lines || []).forEach((ln) => { rows.push({ ...ln, change: ln.value - prev }); prev = ln.value; }); }
  const recent = rows.slice().reverse().slice(0, 5);

  return (
    <div style={{ marginTop: 18 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 12.5, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em" }}>Value tracking</div>
          <div style={{ fontSize: 12.5, color: "var(--text-muted)", marginTop: 3 }}>Keep a value history for this item (best for stock). Insured on its 12-month average.</div>
        </div>
        <VLToggle on={!!a.trackValue} onClick={toggle} />
      </div>

      {a.trackValue && !statementsOK && (
        <div style={{ marginTop: 12, padding: 13, borderRadius: 13, background: "var(--accent-soft)", display: "flex", alignItems: "center", gap: 11 }}>
          <span style={{ flexShrink: 0, color: "var(--accent)" }}><window.Icon name="lock" size={16} /></span>
          <div style={{ flex: 1, fontSize: 12.5, color: "var(--accent-ink)", lineHeight: 1.5 }}>Changes are being recorded. See the full statement + period export on <b>Pro</b> or <b>Business</b>.</div>
          <window.Btn size="sm" onClick={() => { onClose && onClose(); onUpgrade && onUpgrade(); }}>Upgrade</window.Btn>
        </div>
      )}

      {a.trackValue && statementsOK && (
        <div style={{ marginTop: 12, borderRadius: 14, border: "1px solid var(--border)", overflow: "hidden" }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "11px 14px", background: "var(--accent-soft)" }}>
            <span style={{ fontSize: 12, fontWeight: 700, color: "var(--accent)" }}>12-month average</span>
            <span className="display mono" style={{ fontSize: 15, color: "var(--accent-ink)" }}>{hist ? D.fmtFull(hist.avg12) : "—"}</span>
          </div>
          {loading && <div style={{ padding: 14, fontSize: 12.5, color: "var(--text-faint)" }}>Loading statement…</div>}
          {!loading && recent.length === 0 && <div style={{ padding: 14, fontSize: 12.5, color: "var(--text-faint)" }}>No value changes recorded yet. Update the replacement cost above to add the first entry.</div>}
          {!loading && recent.map((ln, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 14px", borderTop: "1px solid var(--border)" }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{ln.note || (ln.source === "baseline" ? "Baseline" : "Revaluation")}</div>
                <div style={{ fontSize: 11.5, color: "var(--text-faint)" }}>{fmtDay(ln.at)}</div>
              </div>
              <div style={{ textAlign: "right", flexShrink: 0 }}>
                <div className="mono" style={{ fontSize: 13, fontWeight: 700 }}>{D.fmtNaira(ln.value)}</div>
                {ln.change !== 0 && <div className="mono" style={{ fontSize: 11, fontWeight: 700, color: ln.change > 0 ? "var(--success)" : "var(--danger)" }}>{ln.change > 0 ? "+" : ""}{D.fmtNaira(ln.change)}</div>}
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// Optional identity Cova nudges for when an asset is added, brand, condition,
// location (esp. property), reference. Always editable here, any time.
function IdentitySection({ a, onUpdate }) {
  const isProperty = a.category === "property";
  const [brand, setBrand] = useStateV(a.brand || "");
  const [condition, setCondition] = useStateV(a.condition || "");
  const [location, setLocation] = useStateV(a.location || "");
  const [serial, setSerial] = useStateV(a.serial || "");
  const [place, setPlace] = useStateV(a.place || "");
  const [saved, setSaved] = useStateV(false);

  const dirty = brand !== (a.brand || "") || condition !== (a.condition || "") || location !== (a.location || "") || serial !== (a.serial || "") || place !== (a.place || "");
  const empty = !a.brand && !a.condition && !a.location && !a.serial && !a.place;

  const save = () => {
    onUpdate && onUpdate(a.id, { brand, condition, location, serial, place });
    setSaved(true); setTimeout(() => setSaved(false), 1800);
  };

  return (
    <div style={{ marginTop: 18 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 10 }}>
        <span style={{ fontSize: 12.5, fontWeight: 700, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: ".03em" }}>Identification</span>
        {empty && <span style={{ fontSize: 11, fontWeight: 600, color: "var(--accent)", padding: "2px 8px", borderRadius: 999, background: "var(--accent-soft)" }}>Optional, helps at claim time</span>}
      </div>
      <div style={{ display: "grid", gap: 10 }}>
        <IdField label={isProperty ? "Type / title" : "Brand / make"} value={brand} onChange={setBrand} placeholder={isProperty ? "e.g. Detached duplex" : "e.g. Toyota, Rolex"} icon="box" />
        <div>
          <span style={{ fontSize: 12.5, fontWeight: 600, color: "var(--text-muted)" }}>Condition</span>
          <div style={{ display: "flex", gap: 8, marginTop: 6 }}>
            {[["new", "Brand new"], ["used", "Second-hand"]].map(([id, label]) => (
              <button key={id} onClick={() => setCondition(condition === id ? "" : id)} style={{
                flex: 1, padding: "9px 10px", borderRadius: 11, fontSize: 13, fontWeight: 600, cursor: "pointer",
                background: condition === id ? "var(--accent)" : "var(--surface-2)", color: condition === id ? "#fff" : "var(--text-muted)",
                border: "1.5px solid " + (condition === id ? "var(--accent)" : "var(--border)"),
              }}>{label}</button>
            ))}
          </div>
        </div>
        <IdField label={isProperty ? "Address" : "Where it's kept"} value={location} onChange={setLocation} placeholder={isProperty ? "Street, area, city" : "e.g. Home garage"} icon="map" />
        <IdField label="Place / site" value={place} onChange={setPlace} placeholder="e.g. My Lekki Home, Ikeja Warehouse" icon="home" />
        <IdField label={a.category === "vehicle" ? "Plate / VIN" : "Serial / reference"} value={serial} onChange={setSerial} placeholder="Optional reference" icon="doc" />
      </div>
      <button onClick={save} disabled={!dirty} style={{
        marginTop: 12, width: "100%", padding: "10px 0", borderRadius: 12, fontSize: 13.5, fontWeight: 700,
        background: dirty ? "var(--accent)" : "var(--surface-3)", color: dirty ? "#fff" : "var(--text-faint)",
        cursor: dirty ? "pointer" : "default", transition: "all .2s",
      }}>{saved ? "Saved ✓" : "Save details"}</button>
    </div>
  );
}

function IdField({ label, value, onChange, placeholder, icon }) {
  const [f, setF] = useStateV(false);
  return (
    <label style={{ display: "block" }}>
      <span style={{ fontSize: 12.5, fontWeight: 600, color: "var(--text-muted)" }}>{label}</span>
      <div style={{ display: "flex", alignItems: "center", gap: 9, marginTop: 5, padding: "0 12px", borderRadius: 11, background: "var(--surface-2)", border: "1.5px solid " + (f ? "var(--accent)" : "var(--border)"), transition: "border-color .2s" }}>
        <window.Icon name={icon} size={15} style={{ color: f ? "var(--accent)" : "var(--text-faint)" }} />
        <input value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder} onFocus={() => setF(true)} onBlur={() => setF(false)}
          style={{ flex: 1, border: "none", outline: "none", background: "transparent", padding: "10px 0", fontSize: 13.5, color: "var(--text)" }} />
      </div>
    </label>
  );
}
function DetailRow({ icon, k, v }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 11, padding: "9px 0", borderBottom: "1px solid var(--border)" }}>
      <window.Icon name={icon} size={17} style={{ color: "var(--accent)" }} />
      <span style={{ fontSize: 13.5, color: "var(--text-muted)" }}>{k}</span>
      <span style={{ marginLeft: "auto", fontSize: 13.5, fontWeight: 700 }}>{v}</span>
    </div>
  );
}

Object.assign(window, { VaultLog });
