/* simple/tabs.jsx — the three surfaces.
   HOME: your money at Yoshi. 7 elements, no scrolling required.
   YOSHI: the interface. Research, trades, moves, automations, docs,
          support — all conversation, rendered as inline cards.
   ACTIVITY: the audit trail. Everything that touched money, one feed,
             tagged with who acted. */

/* ============================================================
   HOME
   ============================================================ */
const HOME_RANGES = ["1D", "1W", "1M", "6M", "YTD", "1Y", "ALL"];
const HOME_RETS = { "1D": DAY_PCT, "1W": 1.2, "1M": 3.1, "6M": 9.4, "YTD": 11.2, "1Y": 18.9, "ALL": 64.0 };
const homeSeries = (range) => series(101 + HOME_RANGES.indexOf(range) * 31, 36, NET_WORTH / (1 + HOME_RETS[range] / 100), NET_WORTH, range === "1D" ? 0.0016 : 0.006);

const AllocStrip = ({ nav }) => {
  const segs = [
    ["Cash", CASH_TOTAL, "var(--alloc-cash)"],
    ["Stocks", INVEST_TOTAL, "var(--alloc-invest)"],
    ["Crypto", CRYPTO_TOTAL, "var(--alloc-crypto)"],
  ];
  const total = CASH_TOTAL + INVEST_TOTAL + CRYPTO_TOTAL;
  // display-only: sits just above Move/Trade, so no tap target to avoid mis-taps
  return (
    <div>
      <div style={{ display: "flex", height: 6, gap: 2, marginBottom: 10, overflow: "hidden" }}>
        {segs.map(([l, v, c]) => <div key={l} style={{ background: c, width: `${(v / total) * 100}%` }} />)}
      </div>
      <div style={{ display: "flex", gap: 2 }}>
        {segs.map(([l, v, c]) => (
          <div key={l} style={{ width: `${(v / total) * 100}%`, display: "flex", flexDirection: "column", alignItems: "center" }}>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 5 }}>
              <span style={{ width: 7, height: 7, background: c, flex: "none" }} />
              <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(11), color: "var(--ink-2)" }}>{((v / total) * 100).toFixed(2)}%</span>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), fontWeight: 600, color: "var(--ink-2)" }}>{l}</span>
            </div>
            <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2 }}>{usd(v)}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

/* the pulse — where external context is allowed to speak, in Yoshi's voice.
   Section label sits above the card, matching "Needs you" and "Holdings". */
const PulseCard = ({ nav }) => {
  // the pulse IS the day's brief — register it so clicking opens the full read
  // in the Briefs UI (as the "Today's read" brief), like the other briefs
  const read = "Up " + perf(DAY_ABS, DAY_PCT) + " today, led by NVDA and BTC. Your weekly VTI buy ran this morning as scheduled, and your Yoshi cash keeps earning 3.30% APY across Cash and Reserve. One thing on my radar: your Chase Sapphire statement of $2,340.18 is due Friday — I can pay it in full so you owe zero interest. Beyond that, nothing needs your approval right now.";
  window.MORNING_BRIEF = { body: read, ask: ["Give me the full read", "Markets closed higher and you're up " + perf(DAY_ABS, DAY_PCT) + " — NVDA and BTC did most of the lifting. Your weekly VTI buy ran this morning and the cash sweep is on schedule. Your Chase Sapphire statement ($2,340.18) is due Friday — I can pay it from your Cash and keep you at zero interest. Want that?"] };
  return (
    <div>
      <div style={{ display: "flex", alignItems: "baseline" }}>
        <Eyebrow color="var(--accent)">The pulse</Eyebrow>
        <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>As of 9:41 AM</span>
      </div>
      <button className="press" onClick={() => nav.sheet({ type: "briefs", brief: "daily-brief" })}
        style={{ width: "100%", textAlign: "left", background: "var(--bg-card)", border: "1px solid var(--rule)", borderRadius: 12, padding: "12px 14px", cursor: "pointer", marginTop: 8 }}>
        <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink)", lineHeight: 1.55 }}>
          {read} Tap to open the full brief.
        </div>
      </button>
    </div>
  );
};

/* holdings — what you hold, with live day moves. Below the pulse so the
   number up top is the summary and this is the detail beneath it. */
const HoldingRow = ({ h, nav, first }) => (
  <button className="press" onClick={() => nav.security(h.id)} style={{ width: "100%", textAlign: "left", display: "grid", gridTemplateColumns: "38px 1fr auto", gap: 11, alignItems: "center", padding: "11px 14px", background: "none", border: "none", borderTop: first ? "none" : "1px solid var(--rule)", cursor: "pointer" }}>
    <span style={{ width: 38, height: 38, flex: "none", borderRadius: 9, background: "var(--bg-2)", display: "grid", placeItems: "center", fontFamily: "var(--f-mono)", fontSize: typeSize(11), fontWeight: 700, color: h.kind === "crypto" ? "var(--alloc-crypto)" : "var(--accent)" }}>{h.ticker.slice(0, 4)}</span>
    <div style={{ minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{h.name}</div>
      {/* value of the holding */}
      <div style={{ marginTop: 2 }}><Money value={h.value} size={11.5} color="var(--ink-3)" dim="var(--ink-3)" /></div>
    </div>
    {/* price of the stock today + change today */}
    <span style={{ textAlign: "right", whiteSpace: "nowrap" }}>
      <Money value={h.last} size={13.5} />
      <div style={{ marginTop: 2 }}><Delta abs={h.last * h.dch / 100} pct={h.dch} size={10.5} /></div>
    </span>
  </button>
);

const HoldingsList = ({ nav }) => {
  // GROUPS.*.items is snapshotted in data.jsx before accounts.jsx pushes its
  // EXTRA_HOLDINGS into the shared HOLDINGS array — so this stays the user's
  // actual held positions, not the wider tradeable universe.
  const rows = [...GROUPS.investments.items, ...GROUPS.crypto.items].sort((a, b) => b.value - a.value);
  return (
    <div>
      <button className="press" onClick={() => nav.sheet({ type: "trade", holdings: true })} style={{ width: "100%", display: "flex", alignItems: "center", gap: 6, background: "none", border: "none", padding: "10px 2px", cursor: "pointer" }}>
        <Eyebrow color="var(--accent)">Holdings</Eyebrow>
        <span style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 4, fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, color: "var(--accent)" }}>
          View all <Icon name="back" size={13} color="var(--accent)" style={{ transform: "scaleX(-1)" }} />
        </span>
      </button>
      <div style={{ marginTop: 4, border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)" }}>
        {rows.map((h, i) => <HoldingRow key={h.id} h={h} nav={nav} first={i === 0} />)}
      </div>
    </div>
  );
};

/* Manual fast paths — same centered icon + label as before, just more
   presence: a hairline border, a touch more height, and an accent-lit icon. */
const FastBtn = ({ icon, label, onClick }) => (
  <button className="press" onClick={onClick} style={{
    display: "flex", alignItems: "center", justifyContent: "center", gap: 8, width: "100%",
    padding: "15px 18px", background: "var(--bg-2)", border: "1px solid var(--accent)",
    borderRadius: 12, cursor: "pointer",
    fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "0.01em", color: "var(--accent)",
  }}>
    <Icon name={icon} size={17} color="var(--accent)" /> {label}
  </button>
);

const HomeTab = ({ nav, proposals, onReview, hideFastPaths }) => {
  const [range, setRange] = useState("1D");
  const [scrub, setScrub] = useState(null);
  const data = useMemo(() => homeSeries(range), [range]);
  const ret = HOME_RETS[range];
  const abs = NET_WORTH - NET_WORTH / (1 + ret / 100);
  const shown = proposals.slice(0, 1);
  const chartColor = ret >= 0 ? "var(--accent-pos)" : "var(--signal-neg)";
  // drag across the chart → read the value + change at that point in time
  const scrubbing = scrub != null && data[scrub] != null;
  const dispVal = scrubbing ? data[scrub] : NET_WORTH;
  const dispAbs = scrubbing ? data[scrub] - data[0] : abs;
  const dispPct = scrubbing ? ((data[scrub] - data[0]) / data[0]) * 100 : ret;
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      {/* header: hamburger (settings) · bell (briefs) — logo lives in the Yoshi tab */}
      <div style={{ flex: "none", display: "flex", alignItems: "center", padding: "6px 16px 2px", minHeight: 40 }}>
        <button className="press" onClick={() => nav.sheet({ type: "profile" })} aria-label="Menu" style={{ background: "none", border: "none", padding: 6, marginLeft: -6, color: "var(--ink)", display: "flex", cursor: "pointer" }}>
          <Icon name="menu" size={22} />
        </button>
        <span style={{ marginLeft: "auto" }}><BellButton nav={nav} /></span>
      </div>

      <div className="scroll" style={{ padding: "4px 18px 24px" }}>
        {/* 1+2 · the number and the day — tap to see the accounts behind it
             (isolated up here, away from Move/Trade, so no mis-taps) */}
        <button className="press" onClick={() => nav.sheet({ type: "accounts" })} style={{ display: "block", width: "100%", textAlign: "left", background: "none", border: "none", padding: 0, cursor: "pointer" }}>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
            <Eyebrow>Yoshi balance</Eyebrow>
            <Icon name="back" size={12} color="var(--ink-3)" style={{ transform: "scaleX(-1)" }} />
          </span>
          <div style={{ marginTop: 5, lineHeight: 1 }}><Money value={dispVal} size={38} weight={500} /></div>
          <div style={{ marginTop: 7, display: "flex", alignItems: "baseline", gap: 7 }}>
            <Delta abs={dispAbs} pct={dispPct} size={13} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)" }}>{scrubbing ? chartDateLabel(range, scrub, data.length) : (range === "1D" ? "Today" : range)}</span>
          </div>
        </button>

        {/* 3 · sparkline — drag across to scrub the value over time */}
        <div style={{ margin: "8px 0 0" }}>
          <Chart data={data} height={104} accent={chartColor} fillColor={chartColor} padY={4} scrub onScrub={(idx) => setScrub(idx)} range={range} yLabelFormatter={(n) => "$" + Math.round(n / 1000) + "k"} />
        </div>
        <div style={{ display: "flex", gap: 3, marginTop: 4 }}>
          {HOME_RANGES.map((r) => (
            <button key={r} className="press" onClick={() => { setRange(r); setScrub(null); }} style={{ flex: 1, padding: "3px 0", background: r === range ? "var(--bg-2)" : "transparent", border: "none", borderRadius: 7, fontFamily: "var(--f-mono)", fontSize: typeSize(12), fontWeight: 500, letterSpacing: "0.02em", color: r === range ? "var(--ink)" : "var(--ink-3)", cursor: "pointer", fontVariantNumeric: "tabular-nums" }}>{r}</button>
          ))}
        </div>

        {/* 4 · allocation strip — one account, three rails */}
        <div style={{ marginTop: 12 }}><AllocStrip nav={nav} /></div>

        {/* 5 · manual fast paths — the two doors, above the fold.
             Original TransferFlow / TradeSheet, unchanged. On web these live
             in the top bar instead, so the shell hides them here. */}
        {!hideFastPaths && (
          <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, marginTop: 16 }}>
            <FastBtn icon="swap" label="Move" onClick={() => nav.sheet({ type: "transfer" })} />
            <FastBtn icon="trade" label="Trade" onClick={() => nav.sheet({ type: "trade" })} />
          </div>
        )}

        {/* 6 · needs you — the hero when present, absent when not */}
        {proposals.length > 0 && (
          <div style={{ marginTop: 18 }}>
            <div style={{ display: "flex", alignItems: "baseline" }}>
              <Eyebrow color="var(--accent)">Needs you</Eyebrow>
              {proposals.length > 1 && (
                <button className="press" onClick={() => nav.sheet({ type: "briefs" })} style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 4, background: "none", border: "none", padding: "2px 0", cursor: "pointer", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, color: "var(--accent)" }}>
                  +{proposals.length - 1} more <Icon name="back" size={13} color="var(--accent)" style={{ transform: "scaleX(-1)" }} />
                </button>
              )}
            </div>
            <div style={{ display: "flex", flexDirection: "column", gap: 8, marginTop: 8 }}>
              {shown.map((p) => <PCard key={p.id} p={p} compact onReview={() => onReview(p)} />)}
            </div>
          </div>
        )}

        {/* 7 · the pulse */}
        <div style={{ marginTop: 18 }}><PulseCard nav={nav} /></div>

        {/* 8 · holdings — what you hold, live day moves, below the pulse */}
        <div style={{ marginTop: 18 }}><HoldingsList nav={nav} /></div>
      </div>
    </div>
  );
};

/* ============================================================
   YOSHI · the interface. The reply engine renders capability
   cards inline — each one a screen this redesign deleted.
   ============================================================ */
const YOSHI_QUICK = ["What's my full net worth?", "How am I doing vs the S&P?", "Move $2,000 to savings", "Show my automations"];

const yoshiReply = (t, ctx) => {
  const s = t.toLowerCase();
  if (/net worth|everything|total|full picture|external/.test(s)) return {
    t: `Everything together: ${usd(NET_WORTH + 8210.32 + 186400 + 12840 - LIAB_TOTAL)}. That's ${usd(NET_WORTH)} here at Yoshi, plus ${usd(8210.32 + 186400 + 12840)} across Chase, Schwab, and Coinbase, minus ${usd(LIAB_TOTAL)} of debt (the mortgage and two cards). Your biggest lever: the Schwab account is earning almost nothing in its cash sweep — want me to draft a consolidation?`,
  };
  if (/s&p|compare|vs |versus|how am i doing|performance/.test(s)) return {
    t: "You're ahead of the index this year — concentration in NVDA did most of that, which cuts both ways.",
    card: { type: "compare" },
  };
  if (/nvda|nvidia/.test(s)) return { t: securityTake("nvda").take, card: { type: "quote", id: "nvda" } };
  if (/bitcoin|btc/.test(s)) return { t: securityTake("btc").take, card: { type: "quote", id: "btc" } };
  if (/tsla|tesla/.test(s)) return { t: securityTake("tsla").take, card: { type: "quote", id: "tsla" } };
  if (/move|transfer|send/.test(s)) {
    const amt = parseFloat((s.match(/\$?([\d,]+(?:\.\d+)?)/) || [])[1]?.replace(/,/g, "")) || 2000;
    return {
      t: `Drafted. ${usd(amt)} from Cash to Reserve, instant, no fee. Approve when ready.`,
      proposal: {
        id: "pm_" + Date.now(), agent: "Yoshi agent", title: `Move ${usd(amt)} to Reserve`,
        why: `One-time instant transfer from Yoshi Cash ••8841 to Yoshi Reserve ••2207. Both earn 3.30% APY. This will not repeat.`,
        net: 0, settles: "In seconds", settlesVerb: "arrives", kind: "transfer",
        legs: [["FROM", "Yoshi Cash ••8841", `Balance ${usd(18420.55)}`, "−" + usd(amt)], ["TO", "Yoshi Reserve ••2207", `Balance ${usd(64000)}`, "+" + usd(amt)], ["TIMING", "Instant transfer", "Arrives in seconds", "$0.00 fee"]],
        activity: { icon: "swap", title: "Reserve", detail: `${usd(amt)} · Instant`, category: "Transfer", by: "Yoshi agent", net: 0, amount: amt },
      },
    };
  }
  if (/automat|rules|recurring/.test(s)) return { t: "Six rules running, all inside your limits. They've earned you $841.00 this quarter between interest, the VTI streak, and harvested losses.", card: { type: "autos" } };
  if (/spend|dining|budget/.test(s)) return { t: "Good news, actually:", card: { type: "insight", i: 0 } };
  if (/1099|tax|statement|document|doc/.test(s)) return { t: "Here you go — I can also send these straight to your accountant.", card: { type: "doc", name: "1099-B · 2025", sub: "Yoshi Securities · consolidated" } };
  if (/pay|sapphire|card|debt/.test(s)) return { t: "Your Sapphire statement is $2,340.18, due Friday at 21.99% APR. Paying in full from your Cash keeps you at zero interest — it's ready below.", proposal: PROPOSALS.find((p) => p.id === "p2") };
  if (/cash|idle|savings rate|yield/.test(s)) return { t: "Your cash is working: every Yoshi cash account earns 3.30% APY, and the buffer rule keeps your spendable Cash lean while the excess sits in your Reserve. One idea — $15,000.00 has sat past your buffer for 9 days; short-term Treasuries would add about $760.00 a year and stay liquid. Want the proposal?" };
  return { t: "On it. I can research anything you hold, move money, place trades, set up rules, pull documents, or just explain what's happening — all from right here. What do you want to do?" };
};

const YoshiCardFor = ({ card, nav, flash }) => {
  if (!card) return null;
  if (card.type === "quote") return <QuoteCard id={card.id} nav={nav} />;
  if (card.type === "compare") return <CompareCard />;
  if (card.type === "autos") return <AutosCard flash={flash} />;
  if (card.type === "insight") { const ins = BRIEF_INSIGHTS[card.i || 0]; return <InsightChip label={ins.label} stat={ins.stat} read={ins.read} />; }
  if (card.type === "doc") return <DocCard name={card.name} sub={card.sub} flash={flash} />;
  return null;
};

const YOSHI_SEED = [
  { from: "agent", t: "Morning. You're up " + perf(DAY_ABS, DAY_PCT) + " today — NVDA and BTC did most of it. One thing needs you: your stocks drifted past the mix you set, so I'd like to even things out.", time: "9:24" },
  { from: "user", t: "what would that do", time: "9:25" },
  { from: "agent", t: "Three trades: trim NVDA and BTC, add VOO. You'd come out about $1,153.00 ahead, done by tomorrow. Approve whenever you're ready.", time: "9:25", proposal: "p1" },
];

const YoshiTab = ({ nav, proposals, onReview, inject, onInjected, flash }) => {
  const [msgs, setMsgs] = useState(YOSHI_SEED);
  const [input, setInput] = useState("");
  const [typing, setTyping] = useState(false);
  const endRef = useRef(null);
  useEffect(() => { endRef.current && endRef.current.scrollIntoView({ behavior: "smooth" }); }, [msgs, typing]);
  useEffect(() => {
    if (!inject) return;
    setMsgs((m) => [...m, ...(inject.note ? [{ from: "user", t: inject.note, time: "now" }] : []), { from: "agent", t: inject.reply, time: "now" }]);
    onInjected();
  }, [inject]);

  const send = (text) => {
    const t = (text != null ? text : input).trim();
    if (!t) return;
    setInput("");
    setMsgs((m) => [...m, { from: "user", t, time: "now" }]);
    setTyping(true);
    setTimeout(() => {
      const r = yoshiReply(t);
      setTyping(false);
      setMsgs((m) => [...m, { from: "agent", t: r.t, time: "now", card: r.card, proposalObj: r.proposal }]);
    }, 700);
  };

  const findP = (idOrObj) => typeof idOrObj === "string" ? proposals.find((p) => p.id === idOrObj) || PROPOSALS.find((p) => p.id === idOrObj) : idOrObj;

  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      <div style={{ flex: "none", display: "flex", alignItems: "center", gap: 8, padding: "6px 20px 10px", borderBottom: "1px solid var(--rule)" }}>
        <Logo size={18} />
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.02em" }}>Yoshi</span>
        <button className="press" onClick={() => nav.tab("home")} aria-label="Close" style={{ marginLeft: "auto", background: "none", border: "none", padding: 0, color: "var(--ink-3)", display: "flex", cursor: "pointer" }}>
          <Icon name="close" size={19} />
        </button>
      </div>

      <div className="scroll" style={{ padding: "14px 16px 8px" }}>
        {msgs.map((m, i) => {
          const p = m.proposal ? findP(m.proposal) : m.proposalObj;
          return (
            <div key={i} style={{ marginBottom: 12 }}>
              <div style={{ display: "flex", justifyContent: m.from === "user" ? "flex-end" : "flex-start" }}>
                <div style={{ maxWidth: "86%", padding: "10px 13px", borderRadius: m.from === "user" ? "14px 14px 4px 14px" : "14px 14px 14px 4px", background: m.from === "user" ? "var(--ink)" : "var(--bg-card)", color: m.from === "user" ? "var(--bg)" : "var(--ink)", border: m.from === "user" ? "none" : "1px solid var(--rule)", fontFamily: "var(--f-display)", fontSize: typeSize(13.5), lineHeight: 1.5 }}>
                  {m.t}
                </div>
              </div>
              {p && <div style={{ marginTop: 8 }}><ChatProposal p={p} onReview={() => onReview(p)} /></div>}
              {m.card && <div style={{ marginTop: 8 }}><YoshiCardFor card={m.card} nav={nav} flash={flash} /></div>}
            </div>
          );
        })}
        {typing && (
          <div style={{ display: "inline-flex", gap: 4, padding: "12px 14px", background: "var(--bg-card)", border: "1px solid var(--rule)", borderRadius: "14px 14px 14px 4px" }}>
            {[0, 1, 2].map((i) => <span key={i} className="yo-mk-pulse" style={{ width: 5, height: 5, borderRadius: 999, background: "var(--ink-3)", animationDelay: `${i * 0.2}s` }} />)}
          </div>
        )}
        <div ref={endRef} />
      </div>

      <div style={{ flex: "none", padding: "6px 14px 10px" }}>
        <div className="hcar" style={{ display: "flex", gap: 7, overflowX: "auto", paddingBottom: 8 }}>
          {YOSHI_QUICK.map((qr) => (
            <button key={qr} className="press" onClick={() => send(qr)} style={{ flex: "none", padding: "8px 13px", borderRadius: 999, background: "transparent", border: "1px solid var(--rule-2)", color: "var(--ink-2)", fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 500, cursor: "pointer" }}>{qr}</button>
          ))}
        </div>
        <YoshiComposer value={input} onChange={setInput} onSend={() => send()} placeholder="Ask, move, trade, automate…" onAttach={(d) => send(`I attached ${d.name}`)} />
      </div>
    </div>
  );
};

/* ============================================================
   ACTIVITY · the audit trail. One feed, everything, tagged with
   who acted. The trust contract for an agent with authority.
   ============================================================ */
const ACT_FILTERS = ["All", "Yoshi", "External", "Card"];
// a credit-card transaction — detail carries the card, e.g. "Card ··4417"
const cardOf = (tx) => { const m = (tx.detail || "").match(/Card ··(\d{4})/); return m ? "··" + m[1] : null; };

/* scheduled items — automations, bills, and payday still to come */
const COMING_UP = [
  { id: "u1", icon: "trade", title: "Weekly investing · VTI", category: "Automation", agent: true, when: "Friday, Jun 5", net: -500.00, scope: "yoshi", autoId: "a1", rail: "Auto-invest" },
  { id: "u2", icon: "swap", title: "Pay Chase Sapphire", category: "Automation", agent: true, when: "Tuesday, Jun 2", net: -2340.18, scope: "yoshi", autoId: "a5", acct: "Cash → Chase Sapphire", rail: "ACH" },
  { id: "u3", icon: "down", title: "Acme Corp", category: "Income", agent: false, when: "Monday, Jun 15", net: 6240.00, scope: "external", acct: "Direct deposit" },
  { id: "u4", icon: "receipt", title: "Netflix", category: "Subscriptions", agent: false, when: "Thursday, Jun 25", net: -15.49, scope: "card", acct: "Card ··4417" },
];

const actorOf = (tx) => {
  if (tx.by) return tx.by;
  if (tx.autoId) { const a = AUTOMATIONS.find((x) => x.id === tx.autoId); return a ? a.name : "Yoshi"; }
  if (/Card ··/.test(tx.detail || "")) return "Card";
  if (tx.category === "Income") return "Deposit";
  return "You";
};
const isAgentActed = (tx) => !!tx.autoId || (tx.by && tx.by !== "You" && tx.by !== "Card");

const ActivityTab = ({ nav, extra }) => {
  const [filter, setFilter] = useState("All");
  const [q, setQ] = useState("");
  const rows = [...extra, ...TRANSACTIONS];
  const shown = rows.filter((tx) => {
    if (filter === "Yoshi" && tx.accountScope !== "yoshi") return false;
    if (filter === "External" && tx.accountScope !== "external") return false;
    if (filter === "Card" && !cardOf(tx)) return false;
    const s = q.trim().toLowerCase();
    return !s || (tx.title + " " + (tx.detail || "") + " " + (tx.category || "")).toLowerCase().includes(s);
  });
  // scheduled items match the same scopes, so Coming up follows the filter too
  const comingShown = COMING_UP.filter((item) => filter === "All" || item.scope === filter.toLowerCase());
  return (
    <div style={{ flex: 1, display: "flex", flexDirection: "column", minHeight: 0 }}>
      <div style={{ flex: "none", padding: "6px 20px 0" }}>
        <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(22), fontWeight: 700, letterSpacing: "-0.025em" }}>Activity</span>
        {/* search */}
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 12, padding: "9px 12px", background: "var(--bg-2)", border: "1px solid var(--rule-2)", borderRadius: 10 }}>
          <Icon name="search" size={15} color="var(--ink-3)" />
          <input value={q} onChange={(e) => setQ(e.target.value)} placeholder="Search activity" style={{ flex: 1, minWidth: 0, background: "transparent", border: "none", outline: "none", color: "var(--ink)", fontFamily: "var(--f-display)", fontSize: typeSize(13.5) }} />
        </div>
        <div className="hcar" style={{ display: "flex", gap: 7, overflowX: "auto", padding: "10px 0 10px" }}>
          {ACT_FILTERS.map((f) => (
            <button key={f} className="press" onClick={() => setFilter(f)} style={{ flex: "none", padding: "7px 13px", borderRadius: 999, background: f === filter ? "var(--ink)" : "transparent", color: f === filter ? "var(--bg)" : "var(--ink-2)", border: "1px solid " + (f === filter ? "var(--ink)" : "var(--rule-2)"), fontFamily: "var(--f-display)", fontSize: typeSize(11.5), fontWeight: 600, cursor: "pointer" }}>{f}</button>
          ))}
        </div>
      </div>
      <div className="scroll" style={{ borderTop: "1px solid var(--rule)" }}>
        {/* Coming up — scheduled automations, bills, payday (full view only) */}
        {!q && comingShown.length > 0 && (
          <>
            <div style={{ padding: "12px 20px 6px" }}><Eyebrow color="var(--accent)">Coming up</Eyebrow></div>
            {comingShown.map((item) => {
              const iCat = directionalCategory(item.category, item.net);
              const cSub = item.acct ? `${iCat} · ${item.acct}` : iCat;
              return (
              <button key={item.id} className="press" onClick={() => nav.sheet({ type: "txn", tx: { ...item, net: null, amount: Math.abs(item.net), tone: item.net < 0 ? "out" : "in", detail: item.acct } })} style={{ width: "100%", textAlign: "left", display: "grid", gridTemplateColumns: "30px 1fr auto", gap: 12, alignItems: "center", padding: "11px 20px", background: "none", border: "none", borderBottom: "1px solid var(--rule)", cursor: "pointer" }}>
                <span style={{ width: 30, height: 30, borderRadius: 9, background: "var(--bg-2)", display: "grid", placeItems: "center", color: item.agent ? "var(--accent)" : "var(--ink-2)" }}>
                  <Icon name={item.icon} size={15} stroke={1.5} />
                </span>
                <div style={{ minWidth: 0 }}>
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{item.title}</div>
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: item.agent ? "var(--accent)" : "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{item.agent ? "● " : ""}{cSub}</div>
                </div>
                <span style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                  <Money value={item.net} size={13} sign color={item.net > 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
                  <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2 }}>{item.when}</div>
                </span>
              </button>
            );})}
            <div style={{ padding: "12px 20px 6px", borderTop: "1px solid var(--rule)" }}><Eyebrow>Recent</Eyebrow></div>
          </>
        )}
        {shown.map((tx, i) => {
          const agent = isAgentActed(tx);
          // when it's today, show the time; otherwise the date
          const stamp = tx.when === "Today" ? (tx.time || "Today") : tx.when;
          // credit-card transactions call out the card; other external
          // transactions call out the account they touched, the same way
          const card = cardOf(tx);
          const acct = card ? `Card ${card}` : (tx.accountScope === "external" ? tx.acct : null);
          const tCat = directionalCategory(tx.category, tx.net);
          const sub = acct ? `${tCat} · ${acct}` : tCat;
          return (
            <button key={tx.id || "x" + i} className="press" onClick={() => nav.sheet({ type: "txn", tx })} style={{ width: "100%", textAlign: "left", display: "grid", gridTemplateColumns: "30px 1fr auto", gap: 12, alignItems: "center", padding: "11px 20px", background: "none", border: "none", borderBottom: "1px solid var(--rule)", cursor: "pointer" }}>
              <span style={{ width: 30, height: 30, borderRadius: 9, background: "var(--bg-2)", display: "grid", placeItems: "center", color: agent ? "var(--accent)" : "var(--ink-2)" }}>
                <Icon name={tx.icon || "receipt"} size={15} stroke={1.5} />
              </span>
              <div style={{ minWidth: 0 }}>
                {/* title = the transaction or the account; wraps to 2 lines so
                    'Transferred from X to Y' isn't cut off */}
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 500, lineHeight: 1.3, display: "-webkit-box", WebkitLineClamp: 2, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{tx.title}</div>
                {/* category under the title; automation-powered rows stay highlighted */}
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: agent ? "var(--accent)" : "var(--ink-3)", marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
                  {agent ? "● " : ""}{sub}
                </div>
              </div>
              {/* value on top, date/time beneath */}
              <span style={{ textAlign: "right", whiteSpace: "nowrap" }}>
                {tx.net !== 0
                  ? <Money value={tx.net} size={13} sign color={tx.net > 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
                  : tx.amount ? <Money value={tx.amount} size={13} color="var(--ink-2)" dim="var(--ink-3)" /> : <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), fontWeight: 500, color: "var(--ink-2)" }}>Done</span>}
                <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2 }}>{stamp}</div>
              </span>
            </button>
          );
        })}
        <div style={{ height: 20 }} />
      </div>
    </div>
  );
};

Object.assign(window, { HomeTab, YoshiTab, ActivityTab, AllocStrip, PulseCard, actorOf });
