/* simple/core.jsx — the redesigned app's spine.
   On web, proposal review lives in the Briefs two-pane reading card (see
   WebBriefs), so there is no ConfirmSheet primitive here — only the shared
   money rows and the agent card library rendered inline in the Yoshi thread.
   Each card replaces a killed screen (Studio, Trade browse, Briefs). */

/* ---------- shared money rows ---------------------------------------------- */
const LegRow = ({ leg, last }) => {
  const [tag, label, detail, amount] = leg;
  const tagColor = tag === "SELL" ? "var(--signal-neg)" : tag === "BUY" ? "var(--accent-pos)" : "var(--ink-2)";
  // newer proposals put a security object in the label slot ({ ticker, price, change, name, valueLabel })
  const sec = label && typeof label === "object" ? label : null;
  return (
    <div style={{ display: "grid", gridTemplateColumns: "62px 1fr auto", gap: 8, alignItems: "start", padding: "9px 11px", borderBottom: last ? "none" : "1px dashed var(--rule)" }}>
      <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 700, letterSpacing: "0.08em", color: tagColor, paddingTop: 2 }}>{tag}</span>
      <div style={{ minWidth: 0 }}>
        {sec ? (
          <>
            <div style={{ display: "flex", alignItems: "baseline", gap: 6, flexWrap: "wrap" }}>
              <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600 }}>{sec.ticker}</span>
              {sec.price ? <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{sec.price}</span> : null}
              {sec.change ? <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(10), color: sec.changeTone === "neg" ? "var(--signal-neg)" : "var(--accent-pos)" }}>{sec.change}</span> : null}
            </div>
            {(sec.name || sec.valueLabel) ? <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{[sec.name, sec.valueLabel].filter(Boolean).join(" · ")}</div> : null}
          </>
        ) : (
          <>
            <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 500 }}>{label}</div>
            {detail ? <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>{detail}</div> : null}
          </>
        )}
      </div>
      <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: "var(--ink)", whiteSpace: "nowrap" }}>{amount}</span>
    </div>
  );
};

const StatCell = ({ label, value, color = "var(--ink)", border }) => (
  <div style={{ padding: "11px 13px", borderLeft: border ? "1px solid var(--rule)" : "none" }}>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 600, letterSpacing: "0.1em", textTransform: "uppercase", color: "var(--ink-3)" }}>{label}</div>
    <div style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(15), fontWeight: 500, color, marginTop: 3, fontVariantNumeric: "tabular-nums" }}>{value}</div>
  </div>
);

/* One receipt language for every instruction. Manual queued orders, transfers,
   and approved briefs all pass through this model before Activity takes over. */
const outcomeTicker = (row) => {
  if (row.ticker) return row.ticker;
  const match = String(row.title || "").match(/\b(?:Buy|Sell)\s+([A-Z]{1,6})\b/i);
  return match ? match[1].toUpperCase() : null;
};
const outcomeIsTrade = (row) => ["BUY", "SELL"].includes(String(row.tag || "").toUpperCase());
const outcomeAmount = (row) => Number(String(row.amt || "").replace(/[^\d.]/g, "")) || 0;
const outcomeNet = (row) => {
  const raw = String(row.amt || "");
  const value = outcomeAmount(row);
  const tag = String(row.tag || "").toUpperCase();
  if (tag === "BUY" || tag === "PAY") return -value;
  if (tag === "SELL" || tag === "PULL") return value;
  if (/[-−]/.test(raw)) return -value;
  if (/\+/.test(raw)) return value;
  return 0;
};
const transactionRowsForBrief = (action) => {
  if (action.series) return action.series;
  if (action.kind === "trade" && action.legs) {
    const account = action.legs.find((leg) => leg[0] === "ACCOUNT" || leg[0] === "FROM");
    return action.legs.filter((leg) => ["BUY", "SELL"].includes(leg[0])).map((leg) => {
      const security = leg[1] && typeof leg[1] === "object" ? leg[1] : null;
      const ticker = security ? security.ticker : String(leg[1] || "");
      return { tag: leg[0], ticker, title: `${leg[0] === "BUY" ? "Buy" : "Sell"} ${ticker}`, sub: [security && security.name, security && security.valueLabel, account && account[1]].filter(Boolean).join(" · "), amt: leg[3] };
    });
  }
  return [{
    tag: action.kind === "trade" ? "BUY" : action.kind === "automation" ? "RULE" : "MOVE",
    title: action.title,
    sub: action.rail || action.settles || "Submitted to Yoshi",
    amt: action.amount || (action.net ? usd(Math.abs(action.net)) : ""),
  }];
};
const briefOutcomeItems = (action) => {
  const rows = transactionRowsForBrief(action);
  return rows.map((row) => {
    const trade = outcomeIsTrade(row) || (!action.series && action.kind === "trade");
    const scheduled = !action.series && action.kind === "automation";
    const executed = trade && MARKET_OPEN;
    const ticker = outcomeTicker(row);
    const quote = ticker && MARKET.find((security) => security.ticker === ticker);
    const submittedDetail = [row.amt, row.sub].filter(Boolean).join(" · ");
    const detail = executed
      ? [row.amt, quote ? `Executed at ${usd(quote.last)}` : "Market order executed"].filter(Boolean).join(" · ")
      : trade
        ? [row.amt, "Queued for tomorrow at market open"].filter(Boolean).join(" · ")
        : [row.amt, row.sub].filter(Boolean).join(" · ");
    return { ...row, submittedDetail, detail, status: scheduled ? "Scheduled" : executed ? "Executed" : "In motion", trade };
  });
};
const transactionStatusForTracker = (item) => {
  const trade = item.category === "Trade";
  const scheduled = item.kind === "recurring";
  const amountValue = Math.abs(item.amount || item.net || 0);
  const amount = amountValue ? usd(amountValue) : "";
  return {
    title: scheduled ? "Automation scheduled" : `${trade ? "Trade" : "Transfer"} submitted`,
    summary: scheduled
      ? "Your recurring transfer is scheduled. You can follow its next run in Activity."
      : trade
      ? "Your one-time order is queued for tomorrow at market open."
      : "Your transfer is processing. You can follow it in Activity.",
    items: [{ title: item.title, submittedDetail: [amount, item.rail || item.acct].filter(Boolean).join(" · "), detail: [amount, item.rail || item.acct, item.chip].filter(Boolean).join(" · "), status: scheduled ? "Scheduled" : "In motion" }],
  };
};
const transactionStatusForBrief = (action) => {
  const items = briefOutcomeItems(action);
  const executed = items.filter((item) => item.status === "Executed").length;
  const inMotion = items.filter((item) => item.status === "In motion").length;
  const scheduled = items.filter((item) => item.status === "Scheduled").length;
  const summary = [
    executed ? `${executed} executed` : "",
    inMotion ? `${inMotion} ${inMotion === 1 ? "is" : "are"} in motion` : "",
    scheduled ? `${scheduled} scheduled` : "",
  ].filter(Boolean).join(". ") + ".";
  const outcomeTitle = executed === 1 && items.length === 1
    ? "Trade confirmed!"
    : executed === items.length
      ? "Transactions confirmed"
      : "Transactions updated";
  return {
    title: items.length > 1 ? "Transactions submitted" : action.kind === "trade" ? "Trade submitted" : action.kind === "automation" ? "Automation scheduled" : "Transfer submitted",
    submittedSummary: items.length === 1 ? "Your transaction was submitted." : `${items.length} transactions were submitted.`,
    outcomeTitle,
    summary,
    items,
  };
};
const seriesInflightItem = (row) => {
  const trade = outcomeIsTrade(row);
  if (trade && MARKET_OPEN) return null;
  const net = outcomeNet(row);
  return {
    icon: trade ? "trade" : "swap", title: row.title, category: trade ? "Trade" : "Transfer",
    kind: "once", state: trade ? "queued" : "pending", agent: false,
    when: trade ? "Tomorrow · market open" : "Processing",
    chip: trade ? "Queued · tomorrow 9:30 AM" : "In motion · processing",
    net, amount: outcomeAmount(row), scope: "yoshi", rail: trade ? "Market order" : "Brief transaction", acct: row.sub,
    cancelNote: trade ? "Cancellation is best efforts before market open." : undefined,
    steps: trade
      ? [["Submitted", "Just now"], ["Queued for market open", "You are here"], ["Executes", "Tomorrow · 9:30 AM"]]
      : [["Submitted", "Just now"], ["Processing", "You are here"], ["Completes", "Soon"]],
    stepAt: 1, settleMs: trade ? 30000 : 25000,
    settle: { icon: trade ? "trade" : "swap", title: row.title, detail: row.sub, category: trade ? "Investment" : "Transfer", accountScope: "yoshi", net, amount: outcomeAmount(row) },
  };
};
const automationInflightItem = (action) => ({
  icon: "clock", title: action.title, category: "Automation", kind: "recurring", state: "scheduled", agent: true,
  when: action.settles || "Next run", chip: `Scheduled · ${action.settles || "next run"}`,
  net: action.net || 0, amount: outcomeAmount({ amt: action.amount }), scope: "yoshi", acct: "Yoshi automation",
});
const briefExecutedRows = (action) => transactionRowsForBrief(action)
  .filter((row) => outcomeIsTrade(row) && MARKET_OPEN)
  .map((row) => {
    const ticker = outcomeTicker(row);
    const quote = ticker && MARKET.find((security) => security.ticker === ticker);
    return {
      icon: "trade", title: String(row.title).replace(/^Buy\b/i, "Bought").replace(/^Sell\b/i, "Sold"),
      detail: [row.amt, quote ? `Executed at ${usd(quote.last)}` : "Market order executed", row.sub].filter(Boolean).join(" · "),
      category: "Investment", accountScope: "yoshi", net: outcomeNet(row), by: action.agent,
    };
  });
const TransactionSubmissionScreen = ({ status, onDone }) => {
  const [phase, setPhase] = useState("submitted");
  const hasExecutions = status.items.some((item) => item.status === "Executed");
  const showingExecution = phase === "executed";
  useEffect(() => {
    setPhase("submitted");
    const executionTimer = hasExecutions ? setTimeout(() => setPhase("executed"), 1400) : null;
    const doneTimer = setTimeout(onDone, hasExecutions ? 3800 : 2200);
    return () => {
      if (executionTimer) clearTimeout(executionTimer);
      clearTimeout(doneTimer);
    };
  }, [status]);
  const destinationCopy = status.origin === "brief" ? "Opening next brief…" : "Opening Activity…";
  return (
    <div key={phase} className="push-enter" data-screen-label={showingExecution ? status.outcomeTitle : status.title} style={{ flex: 1, minHeight: 0, width: "100%", background: "var(--bg)", display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "28px 24px", boxSizing: "border-box" }}>
      <div style={{ position: "relative", width: 104, height: 104, flex: "none" }}>
        <div style={{ position: "absolute", inset: 0, borderRadius: "44% 56% 48% 52% / 53% 42% 58% 47%", background: "var(--accent)", opacity: 0.22, filter: "blur(12px)", animation: "yo-blob-morph 5s ease-in-out infinite, yo-blob-breathe 2.8s ease-in-out infinite" }} />
        <div style={{ position: "absolute", inset: 13, borderRadius: 999, border: "1.5px solid var(--accent)", background: "var(--bg-card)", display: "grid", placeItems: "center", animation: "yo-blob-breathe 1.4s ease-in-out infinite" }}>
          {showingExecution
            ? <Icon name="check" size={31} color="var(--accent)" stroke={1.8} />
            : <span style={{ width: 14, height: 14, borderRadius: 999, background: "var(--accent)", boxShadow: "0 0 0 7px color-mix(in srgb, var(--accent) 18%, transparent)", animation: "yo-blob-breathe 1.1s ease-in-out infinite" }} />}
        </div>
      </div>
      <Eyebrow color="var(--accent)" style={{ marginTop: 22 }}>{showingExecution ? "Executed" : "Submitted"}</Eyebrow>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(25), fontWeight: 600, letterSpacing: "-0.028em", textAlign: "center", marginTop: 7 }}>{showingExecution ? status.outcomeTitle : status.title}</div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), color: "var(--ink-2)", lineHeight: 1.5, textAlign: "center", maxWidth: 420, marginTop: 7 }}>{showingExecution ? status.summary : (status.submittedSummary || status.summary)}</div>
      <div data-transaction-outcomes style={{ width: "100%", maxWidth: 480, border: "1px solid var(--rule-2)", borderRadius: 12, overflow: "hidden", marginTop: 20 }}>
        {status.items.map((item, index) => (
          <div key={`${item.title}-${index}`} style={{ display: "grid", gridTemplateColumns: "minmax(0, 1fr) auto", gap: 12, alignItems: "center", padding: "11px 12px", borderTop: index ? "1px solid var(--rule)" : "none" }}>
            <span style={{ minWidth: 0 }}>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600 }}>{item.title}</span>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", lineHeight: 1.4, marginTop: 2 }}>{phase === "executed" ? item.detail : item.submittedDetail}</span>
            </span>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 700, letterSpacing: "0.05em", textTransform: "uppercase", color: showingExecution && item.status === "Executed" ? "var(--accent-pos)" : "var(--accent)", whiteSpace: "nowrap" }}>{phase === "executed" ? item.status : "Submitted"}</span>
          </div>
        ))}
      </div>
      <div style={{ display: "flex", alignItems: "center", gap: 7, fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 16 }}><Icon name="clock" size={13} color="var(--ink-3)" /> {showingExecution || !hasExecutions ? destinationCopy : "Waiting for execution…"}</div>
    </div>
  );
};

/* multi-transaction series — the brief shows the legs for review only.
   The footer owns the single confirmation decision for the whole series. */
const seriesReviewTag = (item, index) => item.tag || String(index + 1);
const seriesReviewTone = (tag) => tag === "BUY" || tag === "PULL" || tag === "MOVE" ? "var(--accent-pos)" : tag === "PAY" ? "var(--signal-neg)" : "var(--ink-2)";
const SeriesReviewRows = ({ items }) => {
  return (
    <div style={{ marginTop: 2 }}>
      <div data-series-review="proposal-table" style={{ display: "inline-block", maxWidth: "100%", border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden" }}>
        {items.map((s, i) => (
          <div key={i} style={{ display: "grid", gridTemplateColumns: "64px minmax(0, 1fr) auto", gap: 10, alignItems: "start", padding: "10px 10px", borderBottom: i === items.length - 1 ? "none" : "1px dashed var(--rule)" }}>
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(9.5), fontWeight: 700, letterSpacing: "0.08em", color: seriesReviewTone(seriesReviewTag(s, i)), paddingTop: 2 }}>{seriesReviewTag(s, i)}</span>
            <span style={{ minWidth: 0 }}>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, lineHeight: 1.35 }}>{s.title}</span>
              <span style={{ display: "block", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2, lineHeight: 1.4 }}>{s.sub}</span>
            </span>
            <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), fontWeight: 500, whiteSpace: "nowrap", color: (s.amt || "").startsWith("+") ? "var(--accent-pos)" : "var(--ink)" }}>{s.amt}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

/* ============================================================
   Agent cards · rendered inline in the Yoshi thread (and on Home
   for pending proposals). Terminal or actionable — a card answers
   the question or carries ≤2 buttons that lead to the brief's review.
   ============================================================ */

/* from→to route + amount pulled from a proposal's legs, so the card can state
   exactly what will happen instead of a vague "Net". */
const propRoute = (p) => {
  const find = (t) => p.legs && p.legs.find((l) => l[0] === t);
  const from = find("FROM"), to = find("TO") || find("PAY");
  const parts = [];
  if (from) parts.push("From " + from[1]);
  if (to) parts.push((from ? "to " : "To ") + to[1]);
  return parts.join(" ");
};
/* transfer direction — the card's sign means "impact on your Yoshi balance":
   external → Yoshi shows +, Yoshi → external shows −, and Yoshi → Yoshi shows
   unsigned (nothing enters or leaves; the money just changes rooms).
   Yoshi-side detection: the label says Yoshi, or carries a Yoshi account mask
   (legs write the same account as "Yoshi Cash ••8841" or just "Cash ••8841"). */
const YOSHI_MASKS = ["8841", "2207", "7731", "4410"];
const isYoshiAcct = (label) => typeof label === "string" && (/yoshi/i.test(label) || YOSHI_MASKS.some((m) => label.includes("••" + m)));
const transferDirection = (p) => {
  if (p.kind !== "transfer" || !p.legs) return null;
  const from = p.legs.find((x) => x[0] === "FROM");
  const to = p.legs.find((x) => x[0] === "TO") || p.legs.find((x) => x[0] === "PAY");
  if (!from || !to) return null;
  const f = isYoshiAcct(from[1]), t = isYoshiAcct(to[1]);
  return f && t ? "internal" : t ? "in" : f ? "out" : null;
};
const propAmount = (p) => {
  const dir = transferDirection(p);
  if (dir) {
    const from = p.legs.find((x) => x[0] === "FROM");
    const mag = p.amount || (from && from[3] ? from[3].replace(/^[-−+]\s*/, "") : null);
    if (mag) return dir === "in" ? "+" + mag : dir === "out" ? "−" + mag : mag;
  }
  const l = p.legs && (p.legs.find((x) => x[0] === "FROM") || p.legs.find((x) => x[0] === "PAY") || p.legs.find((x) => x[0] === "TO"));
  const cell = l && l[3];
  return (cell && /\d/.test(cell)) ? cell : (p.net ? usd(Math.abs(p.net)) : null);
};

/* proposal card — the only thing allowed to "pool", and it pools on Home */
const PCard = ({ p, onReview, compact }) => {
  const route = propRoute(p);
  const amount = propAmount(p);
  const dir = transferDirection(p);
  const theme = useTheme();
  const inkBrand = theme === "bone" ? "brightness(0)" : "brightness(0) invert(1)";
  return (
    <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)" }}>
      <div style={{ padding: "13px 14px 14px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
          {/* identity stays with the sender: Yoshi's accent dot for Yoshi work
              (incl. rule-fired), the agent's own brand mark for external agents */}
          {p.agentIcon && typeof AGENT_ICON !== "undefined" && AGENT_ICON[p.agentIcon] ? (
            <img src={AGENT_ICON[p.agentIcon]} alt="" style={{ width: 12, height: 12, objectFit: "contain", filter: inkBrand, opacity: 0.8, flex: "none" }} />
          ) : (
            <span style={{ width: 6, height: 6, borderRadius: 999, flex: "none", background: "var(--accent)" }} />
          )}
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10), fontWeight: 600, color: "var(--ink-3)", letterSpacing: "0.04em" }}>{p.source ? `${p.source.name} · asked first` : p.agent}</span>
        </div>
        {/* explicit action + amount */}
        <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginTop: 7 }}>
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(16), fontWeight: 600, letterSpacing: "-0.02em", minWidth: 0 }}>{p.title}</span>
          {amount && <span style={{ marginLeft: "auto", fontFamily: "var(--f-mono)", fontSize: typeSize(15), fontWeight: 600, color: dir === "in" ? "var(--accent-pos)" : "var(--ink)", whiteSpace: "nowrap" }}>{amount}</span>}
        </div>
        {/* details: from where to where */}
        {route && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), color: "var(--ink-2)", marginTop: 5, lineHeight: 1.45 }}>{route}</div>}
        {!compact && p.why && <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-3)", marginTop: 6, lineHeight: 1.5 }}>{p.why}</div>}
        <Btn full onClick={onReview} style={{ marginTop: 12 }}>Review <Icon name="arrow" size={16} color="var(--accent-ink)" /></Btn>
      </div>
    </div>
  );
};

/* chat proposal card — compact, matches main's InlineProposal: a "Proposal"
   eyebrow (accent), the title, the net + a settle/starts label, and a small
   Review button. No description paragraph — that lives in the brief you Review. */
const ChatProposal = ({ p, onReview, done }) => (
  <div style={{ width: "min(184px, 100%)", border: "1px solid " + (done ? "var(--rule)" : "var(--accent)"), background: "var(--bg-card)", borderRadius: 12, boxSizing: "border-box" }}>
    <div style={{ padding: "8px 10px 10px", display: "flex", flexDirection: "column" }}>
      <Eyebrow color={done ? "var(--ink-3)" : "var(--accent)"}>{done ? "● Executed" : "Proposal"}</Eyebrow>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), fontWeight: 600, letterSpacing: "-0.012em", marginTop: 4, lineHeight: 1.2 }}>{p.title}</div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 5, marginTop: 5 }}>
        <Money value={p.net} size={11.5} sign color={p.net >= 0 ? "var(--accent-pos)" : "var(--ink)"} dim="var(--ink-3)" />
        <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(8.5), color: "var(--ink-3)", marginLeft: "auto" }}>{p.settlesVerb || "settles"} {p.settles}</span>
      </div>
      {!done && <Btn full size="sm" onClick={onReview} style={{ marginTop: 9 }}>Review</Btn>}
    </div>
  </div>
);

/* quote card — replaces the Trade browse surface. Ask about a ticker, get
   the price and the two buttons that matter. */
const QuoteCard = ({ id, nav }) => {
  const m = MARKET.find((x) => x.id === id || x.ticker === String(id).toUpperCase());
  if (!m) return null;
  const h = securityHolding(m.id);
  const data = series(m.id.length * 7 + 13, 34, m.last * (1 - m.dch / 100), m.last, 0.012);
  return (
    <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)" }}>
      <button className="press" onClick={() => nav.security(m.id)} style={{ width: "100%", textAlign: "left", background: "none", border: "none", padding: "13px 14px 4px", cursor: "pointer" }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
          <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(13), fontWeight: 700, color: "var(--accent)" }}>{m.ticker}</span>
          <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12), color: "var(--ink-3)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{m.name}</span>
        </div>
        <div style={{ display: "flex", alignItems: "baseline", gap: 9, marginTop: 6 }}>
          <Money value={m.last} size={20} weight={600} />
          <Delta pct={m.dch} size={12} showAbs={false} />
          {h && <span style={{ marginLeft: "auto", fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)" }}>You hold {compactUsd(h.value)}</span>}
        </div>
      </button>
      <div style={{ padding: "2px 8px 0" }}>
        <Chart data={data} height={56} showAxisLabels={false} baseline={false} accent={m.dch >= 0 ? "var(--accent-pos)" : "var(--signal-neg)"} />
      </div>
      <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8, padding: "10px 12px 12px" }}>
        <Btn onClick={() => nav.sheet({ type: "trade", id: m.id, side: "BUY" })} style={{ padding: "10px" }}>Buy</Btn>
        <Btn kind="ghost" onClick={() => nav.sheet({ type: "trade", id: m.id, side: "SELL" })} style={{ padding: "10px" }}>Sell</Btn>
      </div>
    </div>
  );
};

/* comparison card — replaces Studio's analyze workspace. Rendered on request. */
const CompareCard = ({ aLabel = "Your portfolio", bLabel = "S&P 500", aRet = 18.9, bRet = 14.2 }) => {
  const A = series(7, 40, 100, 100 + aRet, 0.02);
  const B = series(8, 40, 100, 100 + bRet, 0.014);
  const all = [...A, ...B];
  const min = Math.min(...all), max = Math.max(...all), R = max - min || 1;
  const W = 320, H = 96;
  const path = (d) => d.map((v, i) => `${i === 0 ? "M" : "L"}${((i / (d.length - 1)) * W).toFixed(1)},${(8 + (1 - (v - min) / R) * (H - 16)).toFixed(1)}`).join(" ");
  return (
    <div style={{ border: "1px solid var(--rule)", borderRadius: 12, overflow: "hidden", background: "var(--bg-card)", padding: "13px 14px" }}>
      <Eyebrow>Past year · indexed</Eyebrow>
      <svg width="100%" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ display: "block", marginTop: 10 }}>
        <line x1="0" y1={H - 8} x2={W} y2={H - 8} stroke="var(--rule)" strokeWidth="1" strokeDasharray="2 3" vectorEffect="non-scaling-stroke" />
        <path d={path(B)} fill="none" stroke="var(--ink-3)" strokeWidth="1.4" vectorEffect="non-scaling-stroke" />
        <path d={path(A)} fill="none" stroke="var(--accent)" strokeWidth="1.6" vectorEffect="non-scaling-stroke" />
      </svg>
      <div style={{ display: "flex", gap: 16, marginTop: 10 }}>
        {[[aLabel, aRet, "var(--accent)"], [bLabel, bRet, "var(--ink-3)"]].map(([l, r, c]) => (
          <span key={l} style={{ display: "inline-flex", alignItems: "center", gap: 6 }}>
            <span style={{ width: 14, height: 2, background: c, flex: "none" }} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-2)" }}>{l}</span>
            <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(11), fontWeight: 600, color: c === "var(--accent)" ? "var(--accent-pos)" : "var(--ink-2)" }}>{pct(r)}</span>
          </span>
        ))}
      </div>
    </div>
  );
};

/* insight card — replaces the Briefs "insights" tab. Pushed, never pooled. */
const InsightChip = ({ label, stat, read }) => (
  <div style={{ border: "1px solid var(--rule)", borderRadius: 12, background: "var(--bg-card)", padding: "13px 14px" }}>
    <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
      <Eyebrow>{label}</Eyebrow>
      <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(16), fontWeight: 600, color: "var(--accent-pos)" }}>{stat}</span>
    </div>
    <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(12.5), color: "var(--ink-2)", marginTop: 6, lineHeight: 1.5 }}>{read}</div>
  </div>
);

/* document card — replaces the Docs hub. Ask, receive, done. */
const DocCard = ({ name, sub, flash }) => (
  <div style={{ border: "1px solid var(--rule)", borderRadius: 12, background: "var(--bg-card)", padding: "13px 14px", display: "flex", alignItems: "center", gap: 12 }}>
    <span style={{ width: 34, height: 34, flex: "none", border: "1px solid var(--rule-2)", borderRadius: 9, display: "grid", placeItems: "center", color: "var(--ink-2)" }}><Icon name="doc" size={17} /></span>
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13.5), fontWeight: 600 }}>{name}</div>
      <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(11), color: "var(--ink-3)", marginTop: 2 }}>{sub}</div>
    </div>
    <Btn kind="quiet" onClick={() => flash && flash("Sent to your email")} style={{ padding: "9px 14px" }}>Share</Btn>
  </div>
);

/* automations card — replaces the automations tab. The agent manages the
   rules; this is the glanceable list, conversational management. */
const AutosCard = ({ flash }) => (
  <div style={{ border: "1px solid var(--rule)", borderRadius: 12, background: "var(--bg-card)", overflow: "hidden" }}>
    {AUTOMATIONS.slice(0, 5).map((au, i) => (
      <div key={au.id} style={{ display: "grid", gridTemplateColumns: "1fr auto", gap: 10, alignItems: "center", padding: "11px 14px", borderTop: i ? "1px solid var(--rule)" : "none" }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
            <span style={{ width: 5, height: 5, borderRadius: 999, flex: "none", background: au.status === "active" ? "var(--accent)" : "var(--ink-3)" }} />
            <span style={{ fontFamily: "var(--f-display)", fontSize: typeSize(13), fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{au.name}</span>
          </div>
          <div style={{ fontFamily: "var(--f-display)", fontSize: typeSize(10.5), color: "var(--ink-3)", marginTop: 2 }}>{au.cadence}</div>
        </div>
        <span style={{ fontFamily: "var(--f-mono)", fontSize: typeSize(12), color: au.measure.startsWith("+") ? "var(--accent-pos)" : "var(--ink-2)" }}>{au.measure}</span>
      </div>
    ))}
    <div style={{ padding: "10px 14px", borderTop: "1px solid var(--rule)", fontFamily: "var(--f-display)", fontSize: typeSize(11.5), color: "var(--ink-3)", lineHeight: 1.45 }}>
      Tell me to pause, change, or add a rule — in plain words.
    </div>
  </div>
);

Object.assign(window, { LegRow, StatCell, PCard, QuoteCard, CompareCard, InsightChip, DocCard, AutosCard, TransactionSubmissionScreen, transactionStatusForTracker, transactionStatusForBrief, briefOutcomeItems, transactionRowsForBrief, seriesInflightItem, automationInflightItem, briefExecutedRows });
