// Exit Planner · multi-target ladder builder per coin.

function Planner() {
  const { state, prices, setLadder } = useStore();
  const cur = state.settings.currency;
  const portfolio = React.useMemo(() => computePortfolio(state, prices), [state, prices]);
  const [activeCoinId, setActiveCoinId] = React.useState(portfolio.rows[0]?.coinId);

  React.useEffect(() => {
    if (!activeCoinId && portfolio.rows[0]) setActiveCoinId(portfolio.rows[0].coinId);
  }, [portfolio.rows.length]);

  const activeRow = portfolio.rows.find(r => r.coinId === activeCoinId);

  if (portfolio.rows.length === 0) {
    return (
      <div className="exos-page">
        <PageHead title="Exit Planner" sub="Pre-commit to selling at specific prices. Removes the in-the-moment decision." />
        <Card><EmptyState
          icon={<Icons.Planner size={22} />}
          title="No holdings to plan for"
          sub="Add coins on the Portfolio page, then come back to build your exit ladders."
        /></Card>
      </div>
    );
  }

  return (
    <div className="exos-page">
      <PageHead
        title="Exit Planner"
        sub="Pre-commit to selling at specific prices. Removes the in-the-moment decision."
      />

      {/* Coin selector tabs */}
      <div className="exos-coin-tabs">
        {portfolio.rows.map(r => {
          const l = state.ladders[r.coinId];
          const sumPct = l?.targets?.reduce((a,t)=>a+t.sellPercent,0) ?? 0;
          return (
            <button key={r.coinId}
              className={`exos-coin-tab ${activeCoinId === r.coinId ? "active" : ""}`}
              onClick={() => setActiveCoinId(r.coinId)}>
              <CoinIcon coinId={r.coinId} size={24} />
              <div style={{ textAlign: "left" }}>
                <div style={{ fontWeight: 500, fontSize: 13 }}>{r.meta.symbol}</div>
                <div className="mono" style={{ fontSize: 10, color: "var(--text-mute)" }}>
                  {sumPct > 0 ? `${sumPct}% planned` : "no plan"}
                </div>
              </div>
            </button>
          );
        })}
      </div>

      {activeRow && (
        <LadderEditor
          key={activeRow.id}
          holding={activeRow}
          ladder={state.ladders[activeRow.coinId]}
          onSave={(l) => setLadder(activeRow.coinId, l)}
          currency={cur}
          taxRate={state.settings.taxRate}
        />
      )}
    </div>
  );
}

function PageHead({ title, sub, right }) {
  return (
    <div className="exos-page-head">
      <div>
        <div className="exos-kicker mono">{title.toUpperCase()}</div>
        <h1 className="exos-h1">{title}</h1>
        <div className="exos-page-head-sub">{sub}</div>
      </div>
      {right}
    </div>
  );
}

function LadderEditor({ holding, ladder, onSave, currency, taxRate }) {
  const cur = currency;
  const safeLadder = ladder ?? { targets: [], moonBag: 100 };
  const computed = computeLadder(holding, safeLadder, holding.price, taxRate);
  const totalCost = holding.amount * holding.avgBuyPrice;
  const recovery = capitalRecovery(holding, holding.price);

  function addTarget() {
    const lastT = safeLadder.targets[safeLadder.targets.length - 1];
    const newPrice = lastT ? lastT.price * 1.5 : holding.price * 1.5;
    const newPct = 15;
    onSave({
      ...safeLadder,
      targets: [...safeLadder.targets, { id: rid(), price: Math.round(newPrice * 100) / 100, sellPercent: newPct }]
    });
  }
  function updateTarget(id, patch) {
    onSave({ ...safeLadder, targets: safeLadder.targets.map(t => t.id === id ? { ...t, ...patch } : t) });
  }
  function removeTarget(id) {
    onSave({ ...safeLadder, targets: safeLadder.targets.filter(t => t.id !== id) });
  }
  function loadPreset(preset) {
    let targets = [];
    if (preset === "conservative") {
      targets = [
        { id: rid(), price: holding.price * 1.5, sellPercent: 25 },
        { id: rid(), price: holding.price * 2.0, sellPercent: 30 },
        { id: rid(), price: holding.price * 3.0, sellPercent: 25 },
      ];
    } else if (preset === "moonbag") {
      targets = [
        { id: rid(), price: holding.price * 2.0, sellPercent: 20 },
        { id: rid(), price: holding.price * 4.0, sellPercent: 25 },
        { id: rid(), price: holding.price * 8.0, sellPercent: 30 },
      ];
    } else if (preset === "recover") {
      if (recovery?.possible) {
        targets = [
          { id: rid(), price: Math.round(holding.price * 100) / 100, sellPercent: Math.ceil(recovery.percentNeeded) },
          { id: rid(), price: holding.price * 2.0, sellPercent: 30 },
          { id: rid(), price: holding.price * 4.0, sellPercent: 30 },
        ];
      }
    }
    targets = targets.map(t => ({ ...t, price: Math.round(t.price * 100) / 100 }));
    onSave({ ...safeLadder, targets });
  }

  // moonBag derived from remainingPct
  const moonBag = computed.remainingPct;
  const overflow = computed.sumPct > 100;

  return (
    <div className="exos-planner-body">
      {/* Holding command bar */}
      <Card className="exos-planner-head" padding="p-none">
        <div className="exos-ph-left">
          <CoinIcon coinId={holding.coinId} size={56} />
          <div>
            <div className="exos-ph-name">{holding.meta.name}</div>
            <div className="exos-ph-meta">
              <span className="mono">{fmtNum(holding.amount, 4)} {holding.meta.symbol}</span>
              <span className="exos-ph-dot">·</span>
              <span>avg <span className="mono">{fmtCurrency(holding.avgBuyPrice, cur, { precise: holding.avgBuyPrice < 10 })}</span></span>
            </div>
          </div>
        </div>
        <div className="exos-ph-divider" />
        <div className="exos-ph-price">
          <div className="exos-label">Current price</div>
          <div className="exos-ph-price-num mono">
            <Ticker value={holding.price} format={v => fmtCurrency(v, cur, { precise: holding.price < 10 })} />
          </div>
        </div>
        <div className="exos-ph-divider" />
        <div className="exos-ph-stats">
          <PHStat label="Value" value={fmtCurrency(holding.value, cur, { compact: true })} />
          <PHStat label="Cost" value={fmtCurrency(totalCost, cur, { compact: true })} dim />
          <PHStat label="P/L"
            value={<span style={{ color: holding.pnl >= 0 ? "var(--gain)" : "var(--loss)" }}>{fmtPct(holding.pnlPct, { digits: 0 })}</span>}
          />
        </div>
      </Card>

      {/* You already won for THIS coin */}
      {recovery?.possible && (
        <Card className="exos-yaw-inline">
          <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
            <span style={{ display: "inline-flex", padding: 8, background: "var(--gain-soft)", borderRadius: 8, color: "var(--gain)" }}>
              <Icons.Trophy size={18} />
            </span>
            <div style={{ flex: 1, minWidth: 240 }}>
              <div style={{ fontWeight: 500 }}>You already won on {holding.meta.symbol}.</div>
              <div style={{ fontSize: 13, color: "var(--text-mute)" }}>
                Sell <span className="mono">{recovery.percentNeeded.toFixed(1)}%</span> at <span className="mono">{fmtCurrency(holding.price, cur, { precise: holding.price < 10 })}</span> to recover your full {fmtCurrency(totalCost, cur, { compact: true })} cost.
              </div>
            </div>
            <Button size="sm" variant="primary" onClick={() => loadPreset("recover")} icon={<Icons.Sparkle size={13} />}>Lock recovery in</Button>
          </div>
        </Card>
      )}

      <div className="exos-planner-grid">
        {/* Targets list */}
        <Card padding="p-none">
          <div style={{ padding: "14px 18px", borderBottom: "1px solid var(--line)", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
            <div>
              <div className="exos-label">Sell ladder</div>
              <div style={{ fontSize: 13, color: "var(--text-mute)", marginTop: 2 }}>Each target sells a % of your {holding.meta.symbol} when price hits.</div>
            </div>
            <div style={{ display: "flex", gap: 6 }}>
              <Button size="sm" variant="ghost" onClick={() => loadPreset("conservative")}>Conservative</Button>
              <Button size="sm" variant="ghost" onClick={() => loadPreset("moonbag")}>Moon bag</Button>
            </div>
          </div>

          {safeLadder.targets.length === 0 ? (
            <EmptyState
              icon={<Icons.Planner size={20} />}
              title="No exit targets yet"
              sub="Add targets to define when and how much you'll sell."
              action={<Button variant="primary" icon={<Icons.Plus size={14} />} onClick={addTarget}>Add first target</Button>}
            />
          ) : (
            <div className="exos-targets">
              {safeLadder.targets.map((t, i) => {
                const c = computed.targets[i];
                return (
                  <div key={t.id} className={`exos-target ${c.reached ? "reached" : ""}`}>
                    <div className="exos-target-idx">
                      <div className="exos-target-num mono">{i + 1}</div>
                      {c.reached && <Pill tone="gain" dot className="exos-target-pill">reached</Pill>}
                    </div>
                    <div className="exos-target-fields">
                      <div>
                        <div className="exos-label" style={{ marginBottom: 5 }}>Price target</div>
                        <NumInput value={t.price} onChange={v => updateTarget(t.id, { price: v || 0 })}
                          prefix={CURRENCY_SYMBOLS[cur]} />
                        <div style={{ fontSize: 11, color: "var(--text-mute)", marginTop: 4 }} className="mono">
                          {t.price > 0 && holding.price > 0 ? `${(t.price/holding.price).toFixed(2)}× from here` : ""}
                        </div>
                      </div>
                      <div>
                        <div className="exos-label" style={{ marginBottom: 5 }}>Sell amount</div>
                        <Slider value={t.sellPercent} onChange={v => updateTarget(t.id, { sellPercent: v })} min={1} max={100} format={v => v + "%"} />
                        <div className="exos-target-coins">
                          <NumInput
                            value={Math.round(holding.amount * t.sellPercent / 100 * 10000) / 10000}
                            onChange={v => {
                              if (v == null || isNaN(v)) return;
                              const pct = Math.max(1, Math.min(100, (v / holding.amount) * 100));
                              updateTarget(t.id, { sellPercent: Math.round(pct * 10) / 10 });
                            }}
                            suffix={holding.meta.symbol}
                            className="exos-target-coin-input"
                          />
                          <span className="mono exos-target-coin-aux">/ {fmtNum(holding.amount, 2)} held</span>
                        </div>
                      </div>
                    </div>
                    <div className="exos-target-out">
                      <div className="exos-label">Proceeds</div>
                      <div className="mono" style={{ fontWeight: 500, fontSize: 14 }}>{fmtCurrency(c.proceeds, cur, { compact: true })}</div>
                      <div className="mono" style={{ fontSize: 11, color: "var(--text-mute)" }}>net {fmtCurrency(c.net, cur, { compact: true })}</div>
                    </div>
                    <button className="exos-icon-btn" onClick={() => removeTarget(t.id)} title="Remove"><Icons.Trash size={14} /></button>
                  </div>
                );
              })}
              <div style={{ padding: 14, borderTop: "1px solid var(--line)", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
                <Button size="sm" variant="ghost" icon={<Icons.Plus size={14} />} onClick={addTarget}>Add target</Button>
                <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                  <span className="exos-label">Total sell:</span>
                  <span className="mono" style={{ fontSize: 14, fontWeight: 500, color: overflow ? "var(--loss)" : "var(--text)" }}>
                    {computed.sumPct}%
                  </span>
                  {overflow && <Pill tone="loss" dot>over 100%</Pill>}
                </div>
              </div>
            </div>
          )}
        </Card>

        {/* Summary side panel */}
        <div className="exos-col">
          <PlanQuality
            holding={holding}
            ladder={safeLadder}
            computed={computed}
            taxRate={taxRate}
            currency={cur}
            totalCost={totalCost}
          />

          <MoonBagStrip
            moonBag={moonBag}
            holding={holding}
          />
        </div>
      </div>
    </div>
  );
}

function MoonBagStrip({ moonBag, holding }) {
  const fullExit = moonBag === 0;
  const noPlan = moonBag === 100;
  const sellPct = 100 - moonBag;

  let bigLabel, bigNum, sub;
  if (fullExit) {
    bigLabel = "Strategy";
    bigNum = "Full exit";
    sub = "Plan sells your entire position. No bag.";
  } else if (noPlan) {
    bigLabel = "Moon bag";
    bigNum = "100%";
    sub = "Add targets to start carving the position.";
  } else {
    bigLabel = "Moon bag";
    bigNum = `${moonBag}%`;
    sub = <>≈ <span className="mono">{fmtNum(holding.amount * moonBag / 100, 4)}</span> {holding.meta.symbol} kept after plan</>;
  }

  return (
    <Card className={`exos-moonbag-strip ${fullExit ? "is-full-exit" : noPlan ? "is-no-plan" : ""}`} padding="p-none">
      <div className="exos-mb-left">
        <div className="exos-label">{bigLabel}</div>
        <div className={`exos-mb-num ${fullExit ? "" : "mono"}`}>{bigNum}</div>
        <div className="exos-mb-sub">{sub}</div>
      </div>
      <div className="exos-mb-bar">
        {sellPct > 0 && (
          <div className="exos-mb-bar-sell" style={{ width: `${sellPct}%` }}>
            {sellPct >= 22 && <span className="mono">SELL {sellPct}%</span>}
          </div>
        )}
        {moonBag > 0 && (
          <div className="exos-mb-bar-keep" style={{ width: `${moonBag}%` }}>
            {moonBag >= 22 && <span className="mono">HOLD {moonBag}%</span>}
          </div>
        )}
      </div>
    </Card>
  );
}

// ---------- Plan Quality panel
function planQuality(holding, ladder, computed, taxRate) {
  const checks = [];
  const sumPct = computed.sumPct;
  const moonBag = computed.remainingPct;
  const cost = holding.amount * holding.avgBuyPrice;
  const cur = holding.price;

  // 1. Coverage
  if (computed.targets.length === 0) {
    checks.push({ kind: "warn", text: "No exit targets defined", why: "Without targets, you're fully exposed to a reversal. Start with 2–3 prices." });
  } else if (sumPct > 100) {
    checks.push({ kind: "bad", text: `Plan sells ${sumPct}% · over 100%`, why: "You can't sell more than you own. Reduce one target." });
  } else if (sumPct < 30) {
    checks.push({ kind: "warn", text: `Only ${sumPct}% of position planned to sell`, why: "Most of your gains stay exposed if markets reverse." });
  } else if (sumPct >= 30 && sumPct <= 85) {
    checks.push({ kind: "good", text: `${sumPct}% planned · ${moonBag}% moon bag`, why: "Balanced · locks in gains while keeping upside exposure." });
  } else {
    checks.push({ kind: "warn", text: `${sumPct}% planned, only ${moonBag}% moon bag`, why: "Aggressive exit. Fine if you want max cash, leaves little for a cycle 2." });
  }

  // 2. Recovery · cumulative net vs cost
  if (computed.targets.length > 0) {
    let cumNet = 0, recIdx = -1;
    for (let i = 0; i < computed.targets.length; i++) {
      cumNet += computed.targets[i].net;
      if (cumNet >= cost) { recIdx = i; break; }
    }
    if (recIdx === -1) {
      checks.push({ kind: "warn", text: "Plan doesn't fully recover your cost basis",
        why: `Even if every target hits, net cash is ${fmtCurrency(computed.totalNet, "USD", { compact: true })} vs ${fmtCurrency(cost, "USD", { compact: true })} cost.` });
    } else if (recIdx === 0) {
      checks.push({ kind: "good", text: "Target #1 alone recovers your cost basis", why: "Everything after this is a free ride. This is the dream setup." });
    } else {
      checks.push({ kind: "good", text: `Cost recovered by target #${recIdx + 1}`, why: "Cumulative proceeds cover what you put in." });
    }
  }

  // 3. Spacing
  if (computed.targets.length >= 2) {
    const ratios = [];
    for (let i = 1; i < computed.targets.length; i++) {
      const a = computed.targets[i-1].price, b = computed.targets[i].price;
      if (a > 0) ratios.push(b / a);
    }
    if (ratios.length) {
      const minR = Math.min(...ratios);
      const maxR = Math.max(...ratios);
      const avg = ratios.reduce((a,r)=>a+r,0) / ratios.length;
      if (minR < 1.15) checks.push({ kind: "warn", text: "Two targets are very close in price", why: "They'll likely fill back-to-back. Consider merging them." });
      else if (maxR > 6) checks.push({ kind: "warn", text: "Big gap between targets", why: "If price retraces between them, later targets may never fill." });
      else checks.push({ kind: "good", text: `Targets well-spaced (${avg.toFixed(1)}× avg step)`, why: "Spread captures multiple price levels without crowding." });
    }
  }

  // 4. First-target realism
  if (computed.targets.length > 0 && cur > 0) {
    const firstMul = computed.targets[0].price / cur;
    if (firstMul > 5) checks.push({ kind: "warn", text: `First target is ${firstMul.toFixed(1)}× from current price`, why: "Aggressive. Consider an earlier mid-target to lock in something." });
    else if (firstMul < 1) checks.push({ kind: "good", text: "First target is already reached", why: "Either execute now or update the ladder." });
  }

  // 5. Reached
  const reachedN = computed.targets.filter(t => t.reached).length;
  if (reachedN > 0 && computed.targets.length > 0) {
    checks.push({ kind: "info", text: `${reachedN} target${reachedN > 1 ? "s" : ""} currently in the money`, why: "Time to actually execute, not just admire the plan." });
  }

  // 6. Score
  let score = 70;
  for (const c of checks) {
    if (c.kind === "good") score += 7;
    else if (c.kind === "warn") score -= 9;
    else if (c.kind === "bad") score -= 30;
  }
  score = Math.max(0, Math.min(100, Math.round(score)));
  const label = score >= 80 ? "Strong" : score >= 60 ? "Solid" : score >= 40 ? "Needs work" : "Risky";
  return { checks, score, label };
}

function PlanQuality({ holding, ladder, computed, taxRate, currency, totalCost }) {
  const { state } = useStore();
  const { checks, score, label } = React.useMemo(
    () => planQuality(holding, ladder, computed, taxRate),
    [holding, ladder, computed, taxRate]
  );
  const tone = score >= 80 ? "good" : score >= 60 ? "solid" : score >= 40 ? "warn" : "bad";
  const color = score >= 80 ? "var(--gain)" : score >= 60 ? "var(--accent)" : score >= 40 ? "var(--warn)" : "var(--loss)";
  const [coachText, setCoachText] = React.useState(null);
  const [loading, setLoading] = React.useState(false);

  async function askCoach() {
    if (!aiAvailable(state)) return;
    setLoading(true);
    try {
      const ctx = {
        coin: holding.meta.symbol,
        amountHeld: holding.amount,
        avgBuyPrice: holding.avgBuyPrice,
        currentPrice: holding.price,
        targets: computed.targets.map(t => ({ price: t.price, sellPercent: t.sellPercent, reached: t.reached })),
        moonBagPercent: computed.remainingPct,
        score, label,
        checks: checks.map(c => ({ kind: c.kind, text: c.text })),
      };
      const prompt = `You are a calm, direct crypto exit-strategy coach. Given this single coin's plan as JSON, write ONE short paragraph (max 80 words). Give your honest read on whether this is a good plan and what one thing to change. Be specific, never preachy. No emojis. No disclaimers. Do not use em dashes (—). Use periods and commas.\n\n${JSON.stringify(ctx)}`;
      const { text } = await aiComplete(prompt, state);
      setCoachText(text);
    } catch (e) {
      setCoachText(e?.message || "Coach unavailable right now.");
    } finally {
      setLoading(false);
    }
  }

  const multiple = totalCost > 0 ? computed.totalNet / totalCost : 0;

  return (
    <Card className={`exos-quality-hero tone-${tone}`} padding="p-none">
      {/* HERO · score + label */}
      <div className="exos-qh-top">
        <div>
          <div className="exos-label" style={{ color: "var(--text-mute)" }}>Plan quality</div>
          <div className="exos-qh-score mono" style={{ color }}>{score}<span>/100</span></div>
          <div className="exos-qh-label" style={{ color }}>{label.toUpperCase()}</div>
        </div>
        <PlanGaugeBig score={score} color={color} />
      </div>

      {/* OUTCOME · capital protected if plan executes */}
      <div className="exos-qh-outcome">
        <div className="exos-label">Capital protected</div>
        <div className="exos-qh-cash mono">
          {fmtCurrency(computed.totalNet, currency, { compact: true })}
        </div>
        <div className="exos-qh-cash-sub">
          <span className="mono" style={{ color: "var(--gain)" }}>{multiple.toFixed(2)}×</span>
          <span>your {fmtCurrency(totalCost, currency, { compact: true })} cost basis</span>
        </div>
        <div className="exos-qh-breakdown">
          <div>
            <div className="exos-qh-bd-label">Gross proceeds</div>
            <div className="mono exos-qh-bd-val">{fmtCurrency(computed.totalProceeds, currency, { compact: true })}</div>
          </div>
          <div className="exos-qh-bd-divider" />
          <div>
            <div className="exos-qh-bd-label">Tax reserve ({Math.round(taxRate*100)}%)</div>
            <div className="mono exos-qh-bd-val muted">−{fmtCurrency(computed.totalProceeds - computed.totalNet, currency, { compact: true })}</div>
          </div>
        </div>
      </div>

      {/* CHECKS */}
      <div className="exos-qh-checks">
        <div className="exos-label" style={{ marginBottom: 10 }}>Why this score</div>
        <div className="exos-checks">
          {checks.map((c, i) => <CheckItem key={i} {...c} />)}
        </div>
      </div>

      {aiAvailable(state) && (
        <div className="exos-qh-coach">
          <Button size="sm" variant="primary" icon={<Icons.Sparkle size={13} />} onClick={askCoach} disabled={loading}>
            {loading ? "Thinking…" : "Get Greed Coach read"}
          </Button>
          {coachText && <div className="exos-coach-text">{coachText}</div>}
        </div>
      )}
    </Card>
  );
}

function PlanGaugeBig({ score, color }) {
  const R = 36, C = 2 * Math.PI * R;
  const dash = (score / 100) * C;
  return (
    <svg width="84" height="84" viewBox="0 0 84 84">
      <circle cx="42" cy="42" r={R} stroke="var(--line)" strokeWidth="6" fill="none" />
      <circle cx="42" cy="42" r={R} stroke={color} strokeWidth="6" fill="none"
        strokeDasharray={`${dash} ${C - dash}`} strokeLinecap="round"
        transform="rotate(-90 42 42)" style={{ transition: "stroke-dasharray .6s cubic-bezier(.16,1,.3,1)" }} />
    </svg>
  );
}

function CheckItem({ kind, text, why }) {
  const [open, setOpen] = React.useState(false);
  const icons = {
    good: { Ic: Icons.Check, color: "var(--gain)" },
    warn: { Ic: Icons.Warning, color: "var(--warn)" },
    bad:  { Ic: Icons.Warning, color: "var(--loss)" },
    info: { Ic: Icons.Sparkle, color: "var(--accent)" },
  };
  const { Ic, color } = icons[kind] || icons.info;
  return (
    <div className={`exos-check ${kind}`} onClick={() => why && setOpen(o => !o)}>
      <span className="exos-check-ic" style={{ color }}><Ic size={13} /></span>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 13, lineHeight: 1.4 }}>{text}</div>
        {why && open && <div className="exos-check-why">{why}</div>}
      </div>
      {why && <span className="exos-check-toggle" data-open={open}>{open ? "−" : "+"}</span>}
    </div>
  );
}

function PlanGauge({ score, color }) {
  const R = 22, C = 2 * Math.PI * R;
  const dash = (score / 100) * C;
  return (
    <svg width="54" height="54" viewBox="0 0 54 54">
      <circle cx="27" cy="27" r={R} stroke="var(--line)" strokeWidth="4" fill="none" />
      <circle cx="27" cy="27" r={R} stroke={color} strokeWidth="4" fill="none"
        strokeDasharray={`${dash} ${C - dash}`} strokeLinecap="round"
        transform="rotate(-90 27 27)" style={{ transition: "stroke-dasharray .5s" }} />
    </svg>
  );
}

function Stat({ label, value }) {
  return (
    <div>
      <div className="exos-label">{label}</div>
      <div className="mono" style={{ fontSize: 15, fontWeight: 500, marginTop: 2 }}>{value}</div>
    </div>
  );
}

function PHStat({ label, value, dim }) {
  return (
    <div className="exos-ph-stat">
      <div className="exos-label">{label}</div>
      <div className={`mono exos-ph-stat-val ${dim ? "dim" : ""}`}>{value}</div>
    </div>
  );
}

const PLANNER_STYLES = `
.exos-coin-tabs {
  display: flex; gap: 8px; overflow-x: auto; padding-bottom: 12px; margin-bottom: 18px;
  scrollbar-width: thin;
}
.exos-coin-tab {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; border-radius: 10px;
  background: var(--bg-elev); border: 1px solid var(--line); cursor: pointer;
  transition: background .12s, border-color .12s;
  flex-shrink: 0; color: var(--text);
}
.exos-coin-tab:hover { background: var(--bg-elev-2); border-color: var(--line-strong); }
.exos-coin-tab.active { background: var(--accent-soft); border-color: var(--accent-line); }
.exos-coin-tab.active .exos-label { color: var(--accent); }

.exos-planner-body { display: flex; flex-direction: column; gap: 14px; }

/* ===== Holding command bar ===== */
.exos-planner-head {
  display: flex; align-items: stretch; overflow: hidden;
  background: linear-gradient(135deg, var(--bg-elev) 0%, var(--bg-elev-2) 100%);
}
.exos-ph-left { display: flex; align-items: center; gap: 14px; padding: 18px 22px; flex: 0 0 auto; min-width: 0; }
.exos-ph-name { font-size: 18px; font-weight: 500; letter-spacing: -0.01em; }
.exos-ph-meta { font-size: 12.5px; color: var(--text-mute); margin-top: 2px; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.exos-ph-dot { color: var(--line-strong); }
.exos-ph-divider { width: 1px; background: var(--line); align-self: stretch; }
.exos-ph-price { padding: 18px 24px; display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.exos-ph-price-num { font-size: 30px; font-weight: 500; letter-spacing: -0.02em; line-height: 1.05; color: var(--text); white-space: nowrap; }
.exos-ph-stats { display: flex; gap: 28px; padding: 18px 22px; flex: 1; min-width: 0; }
.exos-ph-stat { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.exos-ph-stat-val { font-size: 16px; font-weight: 500; letter-spacing: -0.005em; }
.exos-ph-stat-val.dim { color: var(--text-mute); }
@media (max-width: 1100px) {
  .exos-planner-head { flex-wrap: wrap; }
  .exos-ph-divider { display: none; }
  .exos-ph-left, .exos-ph-price, .exos-ph-stats { padding: 14px 18px; }
  .exos-ph-stats { width: 100%; border-top: 1px solid var(--line); }
}

/* ===== Plan Quality HERO ===== */
.exos-quality-hero {
  overflow: hidden;
  background: var(--bg-elev);
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
  position: relative;
}
.exos-quality-hero::before {
  content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--accent);
}
.exos-quality-hero.tone-good::before { background: var(--gain); }
.exos-quality-hero.tone-solid::before { background: var(--accent); }
.exos-quality-hero.tone-warn::before { background: var(--warn); }
.exos-quality-hero.tone-bad::before { background: var(--loss); }

.exos-qh-top {
  display: flex; justify-content: space-between; align-items: center;
  padding: 22px 22px 18px;
  background: linear-gradient(180deg, transparent, var(--bg-elev-2));
}
.exos-quality-hero.tone-good .exos-qh-top { background: linear-gradient(180deg, var(--gain-soft), transparent); }
.exos-quality-hero.tone-solid .exos-qh-top { background: linear-gradient(180deg, var(--accent-soft), transparent); }
.exos-quality-hero.tone-warn .exos-qh-top { background: linear-gradient(180deg, var(--warn-soft), transparent); }
.exos-quality-hero.tone-bad .exos-qh-top { background: linear-gradient(180deg, var(--loss-soft), transparent); }

.exos-qh-score { font-size: 52px; font-weight: 500; letter-spacing: -0.04em; line-height: 0.95; margin-top: 4px; }
.exos-qh-score span { font-size: 18px; color: var(--text-mute); margin-left: 4px; letter-spacing: -0.01em; }
.exos-qh-label { font-size: 10px; font-family: "Geist Mono", monospace; letter-spacing: 0.14em; margin-top: 6px; font-weight: 500; }

.exos-qh-outcome {
  padding: 18px 22px;
  border-top: 1px solid var(--line);
  background: var(--bg-elev);
}
.exos-qh-cash {
  font-size: 32px; font-weight: 500; letter-spacing: -0.02em; line-height: 1;
  margin-top: 6px; color: var(--text);
}
.exos-qh-cash-sub { display: flex; gap: 6px; align-items: center; font-size: 12.5px; color: var(--text-mute); margin-top: 6px; }
.exos-qh-breakdown {
  display: grid; grid-template-columns: 1fr 1px 1fr; gap: 12px;
  margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--line);
}
.exos-qh-bd-divider { background: var(--line); width: 1px; }
.exos-qh-bd-label { font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; margin-bottom: 4px; }
.exos-qh-bd-val { font-size: 14px; font-weight: 500; }
.exos-qh-bd-val.muted { color: var(--text-mute); font-weight: 400; }

.exos-qh-checks {
  padding: 16px 22px 18px;
  border-top: 1px solid var(--line);
  background: var(--bg-elev-2);
}
.exos-qh-coach {
  padding: 14px 22px 18px;
  border-top: 1px solid var(--line);
  background: var(--bg-elev-2);
}

/* ===== Moon bag strip ===== */
.exos-moonbag-strip { display: flex; align-items: stretch; overflow: hidden; min-height: 84px; }
.exos-mb-left {
  padding: 16px 18px;
  display: flex; flex-direction: column; gap: 4px;
  min-width: 170px;
  border-right: 1px solid var(--line);
  background: var(--bg-elev);
}
.exos-mb-num {
  font-size: 28px; font-weight: 500;
  letter-spacing: -0.02em; line-height: 1;
  margin-top: 2px;
  color: var(--text);
}
.exos-mb-sub { font-size: 11px; color: var(--text-mute); line-height: 1.5; margin-top: 4px; }
.exos-mb-bar { flex: 1; display: flex; background: var(--bg-elev-2); }
.exos-mb-bar-sell, .exos-mb-bar-keep {
  display: grid; place-items: center;
  font-size: 10px; letter-spacing: 0.12em; font-weight: 500;
  transition: width .45s cubic-bezier(.16,1,.3,1);
  overflow: hidden;
  white-space: nowrap;
}
.exos-mb-bar-sell { background: var(--accent); color: white; }
.exos-mb-bar-keep { background: transparent; color: var(--text-mute); }
.exos-moonbag-strip.is-full-exit .exos-mb-num { color: var(--warn); font-size: 22px; }
.exos-moonbag-strip.is-full-exit .exos-mb-bar-sell { background: var(--warn); }
.exos-moonbag-strip.is-no-plan .exos-mb-num { color: var(--loss); }
.exos-moonbag-strip.is-no-plan .exos-mb-bar-keep { background: var(--loss-soft); color: var(--loss); }

@media (max-width: 540px) {
  .exos-moonbag-strip { flex-direction: column; }
  .exos-mb-left { border-right: 0; border-bottom: 1px solid var(--line); min-width: 0; }
}

/* legacy moonbar · kept to avoid breaking anything still referencing it */
.exos-moonbar { display: flex; height: 28px; margin-top: 14px; border-radius: 6px; overflow: hidden; border: 1px solid var(--line); }
.exos-moonbar-fill { background: var(--accent); display: grid; place-items: center; color: white; font-size: 11px; transition: width .4s; }
.exos-moonbar-rest { background: var(--bg-elev-2); display: grid; place-items: center; flex: 1; color: var(--text-mute); font-size: 11px; }

.exos-yaw-inline { background: linear-gradient(90deg, var(--gain-soft) 0%, transparent 60%); }

.exos-planner-grid { display: grid; grid-template-columns: 1fr 320px; gap: 14px; }
@media (max-width: 1024px) { .exos-planner-grid { grid-template-columns: 1fr; } }

.exos-targets { display: flex; flex-direction: column; }
.exos-target {
  display: grid; grid-template-columns: 80px 1fr 140px 32px;
  gap: 18px; padding: 16px 18px;
  border-bottom: 1px solid var(--line); align-items: center;
}
.exos-target:last-child { border-bottom: 0; }
.exos-target.reached { background: var(--gain-soft); }
.exos-target-idx { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
.exos-target-num {
  width: 28px; height: 28px; border-radius: 8px;
  display: grid; place-items: center;
  background: var(--bg-elev-2); border: 1px solid var(--line);
  font-size: 13px; font-weight: 500;
}
.exos-target.reached .exos-target-num { background: var(--gain); color: white; border-color: var(--gain); }
.exos-target-pill { font-size: 9px; }
.exos-target-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.exos-target-out { text-align: right; }
@media (max-width: 760px) {
  .exos-target { grid-template-columns: 1fr; gap: 10px; }
  .exos-target-fields { grid-template-columns: 1fr 1fr; }
  .exos-target-out { text-align: left; }
}

.exos-summary-row { display: flex; justify-content: space-between; align-items: center; padding: 6px 0; font-size: 14px; }
.exos-rule { display: flex; gap: 8px; align-items: flex-start; font-size: 13px; color: var(--text-mute); padding: 5px 0; line-height: 1.45; }
.exos-rule svg { margin-top: 2px; flex-shrink: 0; }

.exos-moonbar { display: flex; height: 28px; margin-top: 14px; border-radius: 6px; overflow: hidden; border: 1px solid var(--line); }
.exos-moonbar-fill { background: var(--accent); display: grid; place-items: center; color: white; font-size: 11px; transition: width .4s; }
.exos-moonbar-rest { background: var(--bg-elev-2); display: grid; place-items: center; flex: 1; color: var(--text-mute); font-size: 11px; }

.exos-target-coins { display: flex; align-items: center; gap: 8px; margin-top: 8px; }
.exos-target-coin-input { flex: 1; max-width: 180px; }
.exos-target-coin-input .exos-input { height: 28px; }
.exos-target-coin-input input { font-size: 12px; }
.exos-target-coin-aux { font-size: 11px; color: var(--text-mute); white-space: nowrap; }

.exos-checks { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
.exos-check { display: flex; gap: 9px; padding: 8px 10px; border-radius: 8px; align-items: flex-start; cursor: pointer; transition: background .12s; background: var(--bg-elev-2); border: 1px solid var(--line); }
.exos-check:hover { background: var(--bg-hover); }
.exos-check.good { border-color: oklch(from var(--gain) l c h / 0.22); }
.exos-check.warn { border-color: oklch(from var(--warn) l c h / 0.28); }
.exos-check.bad  { border-color: oklch(from var(--loss) l c h / 0.32); background: var(--loss-soft); }
.exos-check.info { border-color: var(--accent-line); }
.exos-check-ic { margin-top: 2px; flex-shrink: 0; }
.exos-check-why { margin-top: 6px; font-size: 11.5px; color: var(--text-mute); line-height: 1.45; }
.exos-check-toggle { color: var(--text-dim); font-family: "Geist Mono", monospace; font-size: 14px; padding: 0 4px; user-select: none; }

.exos-coach-row { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.exos-coach-text { margin-top: 10px; padding: 10px 12px; background: var(--accent-soft); border-radius: 8px; font-size: 12.5px; line-height: 1.55; color: var(--text); border-left: 2px solid var(--accent); }
`;
(function(){ if(document.getElementById("exos-planner-styles")) return; const t=document.createElement("style"); t.id="exos-planner-styles"; t.textContent=PLANNER_STYLES; document.head.appendChild(t); })();

window.Planner = Planner;
