/* ─────────────────────────────────────────────────────────
   components.jsx — reusable bits for the dashboard
   Export each to window so the main app.jsx can use them.
   ───────────────────────────────────────────────────────── */

const { useState, useMemo, useRef, useEffect } = React;

/* ───────── format helpers ───────── */

const fmtUSD = (n, opts = {}) => {
  const { showSign = false, decimals = 2 } = opts;
  const sign = n > 0 ? "+" : n < 0 ? "−" : "";
  const abs = Math.abs(n).toLocaleString("en-US", {
    minimumFractionDigits: decimals,
    maximumFractionDigits: decimals,
  });
  return (showSign ? sign : (n < 0 ? "−" : "")) + "$" + abs;
};

const fmtPct = (n, decimals = 1) => {
  const sign = n > 0 ? "+" : n < 0 ? "−" : "";
  return sign + Math.abs(n).toFixed(decimals) + "%";
};

const fmtDateShort = (iso) => {
  const d = new Date(iso + "T12:00:00");
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
};

const pnlClass = (n) => (n > 0 ? "pos" : n < 0 ? "neg" : "");

/* ───────── Tiny sparkline ───────── */

function Sparkline({ values, color = "var(--text-2)", height = 28, fill = false }) {
  if (!values || values.length === 0) return null;
  const min = Math.min(...values);
  const max = Math.max(...values);
  const W = 100, H = 30;
  const span = max - min || 1;
  const pts = values.map((v, i) => {
    const x = (i / (values.length - 1)) * W;
    const y = H - ((v - min) / span) * H;
    return [x, y];
  });
  const d = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(2) + "," + p[1].toFixed(2)).join(" ");
  const dFill = d + ` L${W},${H} L0,${H} Z`;
  return (
    <svg className="sparkline" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" style={{ height }}>
      {fill && <path d={dFill} fill={color} opacity="0.12" />}
      <path d={d} fill="none" stroke={color} strokeWidth="1.25" vectorEffect="non-scaling-stroke" />
    </svg>
  );
}

/* ───────── ET clock + market status hook ─────────
   Mirrors the timing_optimizer reference:
   - 00:00–04:00 ET → AFTER-HRS
   - 04:00–09:30 ET → PRE-MKT
   - 09:30–16:00 ET → OPEN
   - 16:00–24:00 ET → AFTER-HRS
   Updates every second.
   ─────────────────────────────────────────────────── */

function getETParts(d = new Date()) {
  // Use Intl to reliably read NY time regardless of viewer's local tz
  const fmt = new Intl.DateTimeFormat("en-US", {
    timeZone: "America/New_York",
    hour: "2-digit", minute: "2-digit", second: "2-digit",
    hour12: false, weekday: "short",
    year: "numeric", month: "2-digit", day: "2-digit",
  });
  const parts = fmt.formatToParts(d);
  const get = k => parts.find(p => p.type === k)?.value;
  const h = parseInt(get("hour"), 10) % 24;
  const m = parseInt(get("minute"), 10);
  const s = parseInt(get("second"), 10);
  const weekday = get("weekday");          // "Mon" "Tue" ...
  const isoDate = `${get("year")}-${get("month")}-${get("day")}`;
  return { h, m, s, weekday, isoDate, totalMin: h * 60 + m };
}

function getMarketStatus(parts) {
  const { totalMin, weekday } = parts;
  const isWeekend = weekday === "Sat" || weekday === "Sun";
  if (isWeekend) return { label: "WEEKEND", color: "var(--text-3)", isAfterHours: true, isOpen: false };
  if (totalMin < 240)               return { label: "AFTER-HRS", color: "var(--warn)",    isAfterHours: true,  isOpen: false };
  if (totalMin < 570)               return { label: "PRE-MKT",   color: "#FF8A3D",        isAfterHours: false, isOpen: false };
  if (totalMin < 960)               return { label: "OPEN",      color: "var(--pos)",     isAfterHours: false, isOpen: true  };
  return                                   { label: "AFTER-HRS", color: "var(--warn)",    isAfterHours: true,  isOpen: false };
}

function useETClock() {
  const [now, setNow] = React.useState(() => new Date());
  React.useEffect(() => {
    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, []);
  const parts = getETParts(now);
  const status = getMarketStatus(parts);
  return { parts, status, now };
}

function fmtETTime12(parts) {
  const { h, m, s } = parts;
  const period = h >= 12 ? "PM" : "AM";
  const h12 = ((h + 11) % 12) + 1;
  const pad = n => String(n).padStart(2, "0");
  return `${h12}:${pad(m)}:${pad(s)} ${period}`;
}

/* Next-trading-day label, used in the hero when after hours.
   - Friday after hours → Monday
   - Sat/Sun → Monday
   - Other days after hours → tomorrow's weekday + date */
function nextTradingDayLabel(parts) {
  // Walk forward at least 1 calendar day, skip Sat/Sun
  const base = new Date(parts.isoDate + "T12:00:00Z");
  base.setUTCDate(base.getUTCDate() + 1);
  while ([0, 6].includes(base.getUTCDay())) base.setUTCDate(base.getUTCDate() + 1);
  return base.toLocaleDateString("en-US", { weekday: "long", month: "long", day: "numeric", timeZone: "UTC" });
}

/* ───────── Logo variants ───────── */

function LogoVariant({ variant = "candlesticks" }) {
  if (variant === "candlesticks") {
    return (
      <span className="logo logo-candle">
        <svg viewBox="0 0 56 32" className="logo-mark" aria-hidden="true">
          {/* Three OHLC candlesticks, last one a tall green winner */}
          <line x1="8"  y1="6"  x2="8"  y2="26" stroke="var(--text-2)" strokeWidth="1" />
          <rect x="5"  y="14" width="6" height="9" fill="var(--neg)" />
          <line x1="22" y1="9"  x2="22" y2="28" stroke="var(--text-2)" strokeWidth="1" />
          <rect x="19" y="13" width="6" height="7" fill="var(--neg)" />
          <line x1="36" y1="3"  x2="36" y2="22" stroke="var(--text-1)" strokeWidth="1" />
          <rect x="33" y="6"  width="6" height="14" fill="var(--pos)" />
          {/* Arrow lifting off the winner */}
          <path d="M 44 8 L 51 3 M 51 3 L 47 3 M 51 3 L 51 7"
                stroke="var(--pos)" strokeWidth="1.5" fill="none" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
        <span className="logo-word">
          <span className="lw-strong">TRADING</span>
          <span className="lw-accent">.OPT</span>
        </span>
      </span>
    );
  }

  if (variant === "monogram") {
    return (
      <span className="logo logo-mono">
        <svg viewBox="0 0 44 44" className="logo-mark" aria-hidden="true">
          <rect x="1" y="1" width="42" height="42" rx="2"
                fill="var(--pos)" fillOpacity="0.08" stroke="var(--pos)" strokeWidth="1.2" />
          <text x="22" y="22" textAnchor="middle" dominantBaseline="central"
                fontFamily="JetBrains Mono, monospace" fontSize="20" fontWeight="700"
                fill="var(--text-1)" letterSpacing="-1">tO</text>
          <line x1="6" y1="36" x2="38" y2="36" stroke="var(--text-3)" strokeWidth="0.6" strokeDasharray="1 2" />
          <circle cx="38" cy="36" r="2" fill="var(--pos)" />
        </svg>
        <span className="logo-word">
          <span className="lw-strong">trading-optimizer</span>
        </span>
      </span>
    );
  }

  if (variant === "ticker") {
    return (
      <span className="logo logo-ticker">
        <span className="logo-chip">
          <span className="chip-dollar">$</span>
          <span className="chip-sym">TO</span>
          <svg viewBox="0 0 14 10" className="chip-arrow" aria-hidden="true">
            <path d="M 1 9 L 6 4 L 9 6 L 13 1"
                  fill="none" stroke="currentColor" strokeWidth="1.5"
                  strokeLinecap="round" strokeLinejoin="round" />
          </svg>
        </span>
        <span className="logo-word">
          <span className="lw-strong">TRADING</span>
          <span className="lw-muted">OPTIMIZER</span>
        </span>
      </span>
    );
  }

  if (variant === "target") {
    return (
      <span className="logo logo-target">
        <svg viewBox="0 0 32 32" className="logo-mark" aria-hidden="true">
          <circle cx="16" cy="16" r="14" fill="none" stroke="var(--text-3)" strokeWidth="0.8" />
          <circle cx="16" cy="16" r="9"  fill="none" stroke="var(--text-2)" strokeWidth="0.8" />
          <circle cx="16" cy="16" r="4"  fill="none" stroke="var(--pos)"    strokeWidth="1.2" />
          <circle cx="16" cy="16" r="1.6" fill="var(--pos)" />
          {/* Crosshair ticks */}
          <line x1="16" y1="0"  x2="16" y2="4"  stroke="var(--text-3)" strokeWidth="0.8" />
          <line x1="16" y1="28" x2="16" y2="32" stroke="var(--text-3)" strokeWidth="0.8" />
          <line x1="0"  y1="16" x2="4"  y2="16" stroke="var(--text-3)" strokeWidth="0.8" />
          <line x1="28" y1="16" x2="32" y2="16" stroke="var(--text-3)" strokeWidth="0.8" />
        </svg>
        <span className="logo-word">
          <span className="lw-bracket">/</span>
          <span className="lw-strong">OPTIMIZER</span>
          <span className="lw-bracket">/</span>
        </span>
      </span>
    );
  }

  if (variant === "network") {
    return (
      <span className="logo logo-network">
        <svg viewBox="0 0 40 32" className="logo-mark" aria-hidden="true">
          {/* Constellation of trade-day nodes connected by a path */}
          <line x1="4"  y1="22" x2="14" y2="16" stroke="var(--text-3)" strokeWidth="0.8" />
          <line x1="14" y1="16" x2="22" y2="20" stroke="var(--text-3)" strokeWidth="0.8" />
          <line x1="22" y1="20" x2="30" y2="10" stroke="var(--text-3)" strokeWidth="0.8" />
          <line x1="30" y1="10" x2="36" y2="6"  stroke="var(--pos)"    strokeWidth="1" />
          <circle cx="4"  cy="22" r="2"  fill="var(--text-2)" />
          <circle cx="14" cy="16" r="2"  fill="var(--text-2)" />
          <circle cx="22" cy="20" r="2"  fill="var(--text-2)" />
          <circle cx="30" cy="10" r="2"  fill="var(--text-1)" />
          <circle cx="36" cy="6"  r="2.6" fill="var(--pos)" />
          <circle cx="36" cy="6"  r="5"   fill="var(--pos)" fillOpacity="0.18" />
        </svg>
        <span className="logo-word">
          <span className="lw-strong">trading</span>
          <span className="lw-dot">·</span>
          <span className="lw-muted">optimizer</span>
        </span>
      </span>
    );
  }

  if (variant === "bracket") {
    return (
      <span className="logo logo-bracket">
        <span className="brk left">[</span>
        <span className="brk-stack">
          <span className="brk-top mono">10:45 → 13:00</span>
          <span className="brk-bot">TRADING OPTIMIZER</span>
        </span>
        <span className="brk right">]</span>
      </span>
    );
  }

  return null;
}

Object.assign(window, { LogoVariant });

/* ───────── Account switcher tabs ───────── */

function AccountTabs({ accounts, selected, onSelect }) {
  return (
    <div className="account-tabs">
      <div className="account-tabs-label mono">Accounts</div>
      <div className="account-tabs-row" role="tablist" aria-label="Switch account">
        {accounts.map((acct, i) => {
          const isOn = selected === i;
          const dayPos = acct.account.day_pnl_dollars >= 0;
          return (
            <button
              key={acct.id}
              role="tab"
              aria-selected={isOn}
              className={"account-tab" + (isOn ? " on" : "")}
              onClick={() => onSelect(i)}
              style={isOn ? { "--accent": acct.accentColor } : undefined}
            >
              <div className="account-tab-top">
                <span className="account-tab-name">{acct.label}</span>
                <span className="account-tab-id mono">{acct.alpacaAccountId}</span>
              </div>
              <div className="account-tab-mid">
                <span className="account-tab-strat" style={{ color: acct.accentColor }}>{acct.strategyKindLabel}</span>
              </div>
              <div className="account-tab-bot">
                <span className="mono account-tab-port">
                  ${acct.account.portfolio_value.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 0 })}
                </span>
                <span className={"mono account-tab-day " + (dayPos ? "value-pos" : "value-neg")}>
                  {fmtUSD(acct.account.day_pnl_dollars, { showSign: true })}
                </span>
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

function Topbar({ botHealth, asOf, clock, logoVariant = "candlesticks" }) {
  const statusDot =
    botHealth.last_cron_status === "ok" ? "dot" :
    botHealth.last_cron_status === "warn" ? "dot warn" : "dot neg";
  const timeStr = clock ? fmtETTime12(clock.parts) : "—";
  const status = clock?.status;
  return (
    <header className="topbar">
      <div className="brand">
        <a href="#" className="brand-logo" aria-label="trading-optimizer home">
          <LogoVariant variant={logoVariant} />
        </a>
        <span className="bot-pill" data-tip="EC2 instance is running and the daily cron has fired successfully.">
          <span className={statusDot}></span>
          <span className="bot-pill-label">bot live</span>
        </span>
      </div>
      <div className="topbar-meta">
        <div className="clock-stack">
          <div className="clock-box">
            <div className="clock-label">Current Time</div>
            <div className="clock-val mono">
              {timeStr} <span className="clock-tz-inline">ET</span>
            </div>
          </div>
          <div className="clock-box">
            <div className="clock-label">Market Hours</div>
            <div className="clock-val mono clock-status" style={{ color: status?.color }}>
              {status?.label || "—"}
            </div>
          </div>
        </div>
      </div>
    </header>
  );
}

/* ───────── Hero / TODAY ───────── */

function TodayHero({ plan, account, streakHistory, currentStreak, botHealth, equitySpark, asOf, acct, clock }) {
  const isStrategyB = acct && acct.strategyKind === "B";
  // Variable central content per state
  let badge = null;
  let ticker = plan.ticker || "—";
  let tickerCls = "tile-hero-ticker";
  let context = null;

  if (plan.state === "holding") {
    badge = <span className="badge live"><span className="dot" style={{ background: "var(--neutral)", boxShadow: "0 0 0 3px var(--neutral-soft)" }}></span> Live · Holding</span>;
    const h = Math.floor(plan.closes_in_minutes / 60);
    const m = plan.closes_in_minutes % 60;
    context = (
      <div className="tile-hero-context">
        Buy <strong>{plan.buy_time}</strong> <span className="muted">·</span> Sell <strong>{plan.sell_time}</strong>
        <span className="muted"> · closes in {h}h {m}m</span>
      </div>
    );
  } else if (plan.state === "closed") {
    const won = (plan.realized_pnl ?? 0) >= 0;
    badge = <span className={won ? "badge pos" : "badge neg"}>Closed · {won ? "win" : "loss"} settled</span>;
    context = (
      <div className="tile-hero-context">
        Bought <strong>{plan.buy_time}</strong> <span className="muted">·</span> Sold <strong>{plan.sell_time}</strong>
        <span className="muted"> · day complete</span>
      </div>
    );
  } else if (plan.state === "pre_market") {
    badge = <span className="badge mute">Pre-market · queued</span>;
    const h = Math.floor(plan.fires_in_minutes / 60);
    const m = plan.fires_in_minutes % 60;
    context = (
      <div className="tile-hero-context">
        Scanner picked <strong>{plan.ticker}</strong> · order fires in {h}h {m}m at <strong>{plan.buy_time}</strong>
      </div>
    );
  } else if (plan.state === "no_trade") {
    ticker = "NO TRADE";
    tickerCls = "tile-hero-ticker no-trade";
    badge = <span className="badge mute">Scanner · no qualifying mover</span>;
    context = (
      <div className="tile-hero-context"><span className="muted">{plan.reason}</span></div>
    );
  } else if (plan.state === "weekend") {
    ticker = "MARKETS CLOSED";
    tickerCls = "tile-hero-ticker no-trade";
    badge = <span className="badge mute">Weekend · cron paused</span>;
    const h = Math.floor(plan.next_fire_in_minutes / 60);
    const m = plan.next_fire_in_minutes % 60;
    context = (
      <div className="tile-hero-context">
        Next cron fires <strong>{plan.next_fire_at}</strong> <span className="muted">· in {h}h {m}m</span>
      </div>
    );
  } else if (plan.state === "next_day") {
    ticker = "AWAITING SCANNER";
    tickerCls = "tile-hero-ticker no-trade";
    badge = <span className="badge mute">After-hours · tomorrow's plan</span>;
    context = (
      <div className="next-plan-details">
        <div className="next-plan-row">
          <div className="next-plan-field">
            <div className="np-label">Stake</div>
            <div className="np-value mono">${plan.stake.toLocaleString()}</div>
            <div className="np-sub">{plan.stake_basis}</div>
          </div>
          <div className="next-plan-field">
            <div className="np-label">Buy</div>
            <div className="np-value mono">{plan.buy_time}</div>
            <div className="np-sub">market order</div>
          </div>
          <div className="next-plan-field">
            <div className="np-label">Sell</div>
            <div className="np-value mono">{plan.sell_time}</div>
            <div className="np-sub">market order</div>
          </div>
          <div className="next-plan-field next-plan-formula">
            <div className="np-label">Formula in effect</div>
            <div className="np-value mono" style={{ fontSize: 14 }}>{plan.formula}</div>
            <div className="np-sub">{isStrategyB ? "always-on compounding" : "win × 1.5 / loss = port × 2%"}</div>
          </div>
        </div>
        <div className="next-plan-note">{plan.stake_explanation} {plan.note}</div>
      </div>
    );
  }

  const isActive = plan.state === "holding" || plan.state === "closed" || plan.state === "pre_market";
  const showStake = isActive || plan.state === "next_day";

  // Today P&L value (unrealized or realized; 0 otherwise)
  const todayPnl = plan.unrealized_pnl ?? plan.realized_pnl ?? 0;
  const asOfDate = asOf ? new Date(asOf) : new Date();

  return (
    <section className="section">
      <div className="section-head">
        <div>
          <div className="section-label">{plan.state === "next_day" ? "Today's trading day" : "Today"}</div>
          <h2 className="section-title">
            {(() => {
              const d = clock ? new Date(clock.parts.isoDate + "T12:00:00Z") : asOfDate;
              const opts = { weekday: "long", month: "long", day: "numeric", timeZone: clock ? "UTC" : undefined };
              return d.toLocaleDateString("en-US", opts);
            })()}
          </h2>
        </div>
        <div className="section-meta">
          {plan.state === "next_day"
            ? `cron fires next at 09:20 ET`
            : `cron @ 09:20 ET · last fire ${botHealth.last_cron_at}`}
        </div>
      </div>

      <div className={"hero" + (plan.state === "next_day" ? " hero-next" : "")}>
        {/* Dominant tile */}
        <div className="tile tile-hero">
          <div className="tile-hero-row">
            <div>{badge}</div>
            <div style={{ color: "var(--text-3)" }} className="mono">
              {isActive ? plan.formula : ""}
            </div>
          </div>
          <div>
            <h1 className={tickerCls}>{ticker}</h1>
            {plan.ticker_long && isActive && (
              <div style={{ color: "var(--text-2)", fontSize: 14, marginTop: 6, fontFamily: "var(--mono)" }}>
                {plan.ticker_long}
              </div>
            )}
            {plan.ticker_sector && isActive && (
              <div style={{
                color: "var(--text-3)", fontSize: 11, marginTop: 4,
                letterSpacing: "0.06em", textTransform: "uppercase",
                fontFamily: "var(--sans)"
              }}>
                {plan.ticker_sector}{plan.ticker_exchange ? ` · ${plan.ticker_exchange}` : ""}
                {plan.ticker_industry ? <span style={{opacity: 0.6}}>{` · ${plan.ticker_industry}`}</span> : ""}
              </div>
            )}
          </div>
          <div className="tile-hero-meta">
            {context}
            {plan.state === "holding" && (
              <div style={{ textAlign: "right" }}>
                <div className="mono" style={{ color: "var(--text-3)", fontSize: 11, letterSpacing: "0.04em", textTransform: "uppercase" }}>Last · Bought</div>
                <div className="mono" style={{ fontSize: 20, marginTop: 4 }}>
                  ${plan.last_price.toFixed(2)} <span style={{ color: "var(--text-3)" }}>/ ${plan.bought_at.toFixed(2)}</span>
                </div>
              </div>
            )}
            {plan.state === "closed" && (
              <div style={{ textAlign: "right" }}>
                <div className="mono" style={{ color: "var(--text-3)", fontSize: 11, letterSpacing: "0.04em", textTransform: "uppercase" }}>Sell · Buy · Shares</div>
                <div className="mono" style={{ fontSize: 20, marginTop: 4 }}>
                  ${plan.sell_price.toFixed(2)} <span style={{ color: "var(--text-3)" }}>/ ${plan.buy_price.toFixed(2)}</span>
                  <span style={{ color: "var(--text-3)" }}> · {plan.shares}sh</span>
                </div>
              </div>
            )}
          </div>
        </div>

        {/* Supporting tiles */}
        <div className="tile-grid">
          {/* Stake tile (today) OR Today's trade tile (next_day) */}
          {plan.state === "next_day" && plan.today_settled ? (
            <div className="tile">
              <div className="tile-label">
                <span data-tip="The trade the bot executed today.&#10;Buy + sell submitted as market orders at the scheduled times.&#10;Realized P&L shown in the next tile.">Today's trade</span>
                <span className="mono" style={{ color: "var(--text-3)" }}>{plan.today_settled.ticker}</span>
              </div>
              <div className="trade-prices">
                <div className="trade-side">
                  <div className="np-label">Buy</div>
                  <div className="mono trade-px">${plan.today_settled.buy_price.toFixed(2)}</div>
                  <div className="np-sub mono">{plan.today_settled.buy_time}</div>
                </div>
                <div className="trade-arrow" aria-hidden="true">→</div>
                <div className="trade-side">
                  <div className="np-label">Sell</div>
                  <div className="mono trade-px">${plan.today_settled.sell_price.toFixed(2)}</div>
                  <div className="np-sub mono">{plan.today_settled.sell_time}</div>
                </div>
              </div>
              <div className="tile-sub">
                {plan.today_settled.shares.toLocaleString()} shares of <span style={{ color: "var(--text-1)" }}>{plan.today_settled.ticker}</span>
              </div>
            </div>
          ) : (
            <div className="tile">
              <div className="tile-label">
                <span data-tip={isStrategyB
                  ? "Strategy B reinvests the ENTIRE portfolio every day.\nStake = yesterday's closing portfolio value.\nWins + losses compound exponentially — no fixed dollar amount."
                  : "Dollar amount the bot deployed for today's trade.&#10;Scales by the streak formula in effect:&#10;• base ($15,000)&#10;• win streak → prevStake × 1.5&#10;• loss streak → portfolio × 2%"}>
                  Stake deployed
                </span>
                {showStake && plan.stake_basis && (
                  <span className="mono" style={{ color: "var(--text-3)" }}>{plan.stake_basis}</span>
                )}
              </div>
              <div className="tile-value">
                {showStake ? "$" + plan.stake.toLocaleString() : <span style={{ color: "var(--text-3)" }}>—</span>}
              </div>
              <div className="tile-sub">
                {showStake
                  ? (plan.stake_explanation || "Stake set from current streak formula.")
                  : "No position deployed today."}
              </div>
            </div>
          )}

          <div className="tile">
            <div className="tile-label">
              <span data-tip={isStrategyB
                ? "Strategy B has no streak formula — stake is always the full portfolio.\nThe W/L bar still shows the last 8 outcomes for reference."
                : "Current consecutive win (W) or loss (L) streak.&#10;Resets to 0 on the day after any opposite-result close.&#10;The W/L bar shows the last 8 outcomes, oldest left."}>Streak</span>
              <span className="mono" style={{ color: "var(--text-3)" }}>last 8</span>
            </div>
            <div className="tile-value">
              {isStrategyB
                ? <span style={{ color: "var(--text-2)" }}>—<span style={{ fontSize: 16, color: "var(--text-3)", marginLeft: 6 }}>n/a</span></span>
                : currentStreak.count === 0
                  ? <span style={{ color: "var(--text-2)" }}>0<span style={{ fontSize: 16, color: "var(--text-3)", marginLeft: 6 }}>reset</span></span>
                  : <span className={currentStreak.kind === "W" ? "value-pos" : "value-neg"}>
                      {currentStreak.count}{currentStreak.kind}
                    </span>}
            </div>
            <div className="streak" aria-hidden="true">
              {streakHistory.map((s, i) => <span key={i} className={s === "W" ? "w" : "l"}></span>)}
            </div>
            {!isStrategyB && currentStreak.count === 0 && (
              <div className="tile-sub" style={{ marginTop: 8 }}>
                broken 5/15 from <span className="mono value-pos">4W</span> by CSCO loss
              </div>
            )}
            {isStrategyB && (
              <div className="tile-sub" style={{ marginTop: 8 }}>
                Strategy B always deploys full portfolio — no streak formula.
              </div>
            )}
          </div>

          <div className="tile">
            <div className="tile-label">
              <span data-tip={plan.state === "next_day"
                ? "Realized P&L from today's trade — fully settled.\nStreak + tomorrow's stake have already been updated from this result."
                : "Today's profit/loss in dollars.&#10;Unrealized while the position is open (marks every minute).&#10;Realized after the 13:00 ET sell lands."}>
                {plan.state === "next_day" ? "Today's result" : "Today P&L"}
              </span>
              {plan.state === "next_day" && plan.today_settled && (
                <span className="mono" style={{ color: "var(--text-3)" }}>{plan.today_settled.ticker}</span>
              )}
            </div>
            {plan.state === "next_day" && plan.today_settled
              ? (() => {
                  const pnl = plan.today_settled.realized_pnl;
                  const pct = plan.today_settled.realized_pnl_pct;
                  const cls = pnl > 0 ? "value-pos" : pnl < 0 ? "value-neg" : "value-neutral";
                  return (
                    <>
                      <div className={"tile-value " + cls}>{fmtUSD(pnl, { showSign: true })}</div>
                      <div className="tile-sub">
                        <span className={cls + " mono"}>{fmtPct(pct, 2)}</span>
                        <span style={{ color: "var(--text-3)" }}> · settled at 13:00 ET · {plan.today_settled.shares}sh of {plan.today_settled.ticker}</span>
                      </div>
                    </>
                  );
                })()
              : (
                <>
                  <div className={"tile-value " + (todayPnl > 0 ? "value-pos" : todayPnl < 0 ? "value-neg" : "value-neutral")}>
                    {isActive ? fmtUSD(todayPnl, { showSign: true }) : <span style={{ color: "var(--text-3)" }}>$0.00</span>}
                  </div>
                  <div className="tile-sub">
                    {plan.state === "holding" ? "unrealized · marks every tick"
                      : plan.state === "closed" ? "realized · settled"
                      : "no trade today"}
                  </div>
                </>
              )}
          </div>

          <div className="tile">
            <div className="tile-label"><span data-tip="Total account value at the latest refresh.&#10;= cash + open position market value.&#10;Spark line shows the trailing 15 trading days.">Portfolio value</span></div>
            <div className="tile-value">
              ${account.portfolio_value.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
            </div>
            <Sparkline values={equitySpark} color="var(--pos)" fill={true} />
          </div>

          <div className="tile">
            <div className="tile-label"><span data-tip="Time the daily_runner cron last fired successfully on EC2.&#10;Fires every weekday at 09:20 ET.&#10;Backup is the latest AWS Backup recovery point.">Last cron fire</span></div>
            <div className="tile-value" style={{ fontSize: "var(--t-20)" }}>
              <span className="dot" style={{ display: "inline-block", marginRight: 8, verticalAlign: "middle" }}></span>
              {botHealth.last_cron_at}
            </div>
            <div className="tile-sub">
              status <span style={{ color: "var(--pos)" }}>OK</span> · {botHealth.consecutive_successful_fires} successful in a row · backup {botHealth.backup_age_hours}h old
            </div>
          </div>

          <div className="tile">
            <div className="tile-label"><span data-tip="Positions currently held by the bot.&#10;Strategy holds at most one position at a time — buy at 10:45 ET, sell at 13:00 ET.">Open positions</span></div>
            <div className="tile-value">
              {account.open_positions_count === 0
                ? <span style={{ color: "var(--text-3)" }}>0</span>
                : account.open_positions_count}
              {account.open_positions_count > 0 && plan.ticker && (
                <span className="mono" style={{ fontSize: 14, color: "var(--text-2)", marginLeft: 10 }}>
                  · {plan.ticker}
                </span>
              )}
            </div>
            <div className="tile-sub">
              cash ${account.cash.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 0 })} available
            </div>
          </div>
        </div>
      </div>
    </section>
  );
}

/* ───────── Stats row ───────── */

function StatsRow({ perf }) {
  return (
    <div className="stats">
      <div className="stat">
        <div className="label">
          <span data-tip="Cumulative percent return on starting equity since the window began.&#10;= (current portfolio − starting balance) / starting balance.">Total return</span>
          <span className="mono" style={{ color: "var(--text-3)" }}>since 2026-03-31</span>
        </div>
        <div className={"big " + (perf.totalReturnDollars > 0 ? "value-pos" : "value-neg")}>
          {fmtPct(perf.totalReturnPct, 2)}
        </div>
        <div className="sub">
          <span className={perf.totalReturnDollars > 0 ? "value-pos" : "value-neg"}>
            {fmtUSD(perf.totalReturnDollars, { showSign: true })}
          </span>
          <span style={{ color: "var(--text-3)" }}> &nbsp;on ${perf.startingBalance.toLocaleString()} starting equity</span>
        </div>
      </div>

      <div className="stat">
        <div className="label"><span data-tip="Percent of trades that closed positive.&#10;Sharpe is the annualized risk-adjusted return (>1 is good, >2 is excellent).">Win rate</span></div>
        <div className="big">{perf.winRate.toFixed(1)}<span style={{ fontSize: "var(--t-28)", color: "var(--text-2)" }}>%</span></div>
        <div className="sub">
          <span style={{ color: "var(--pos)" }}>{perf.wins}W</span>
          <span style={{ color: "var(--text-3)" }}> / </span>
          <span style={{ color: "var(--neg)" }}>{perf.losses}L</span>
          <span style={{ color: "var(--text-3)" }}> &nbsp;of {perf.totalTrades} trades · Sharpe {perf.sharpe.toFixed(1)}</span>
        </div>
      </div>

      <div className="stat">
        <div className="label"><span data-tip="Largest single-day gain and largest single-day loss across the window.&#10;Worst day usually correlates with the largest deployed stake (post-streak compounding).">Best · Worst day</span></div>
        <div className="split">
          <div className="col">
            <div className="k">Best</div>
            <div className="v value-pos">{fmtUSD(perf.bestDay.pnl_dollars, { showSign: true })}</div>
            <div className="mono" style={{ color: "var(--text-3)", fontSize: 11, marginTop: 4 }}>
              {perf.bestDay.ticker} · {fmtDateShort(perf.bestDay.date)}
            </div>
          </div>
          <div className="col">
            <div className="k">Worst</div>
            <div className="v value-neg">{fmtUSD(perf.worstDay.pnl_dollars, { showSign: true })}</div>
            <div className="mono" style={{ color: "var(--text-3)", fontSize: 11, marginTop: 4 }}>
              {perf.worstDay.ticker} · {fmtDateShort(perf.worstDay.date)}
            </div>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ───────── Main chart — tabbed (Strategy-A-style) ─────────
   Tabs: Portfolio Value · Daily P&L ($) · Daily P&L (%) · Cumulative P&L
   Range: 3D · 7D · 14D · 1M · All
   ─────────────────────────────────────────────────────────── */

const TAB_DEFS = [
  { id: "portfolio",  label: "Portfolio Value",
    tip: "Account value at each daily close.\nThe headline strategy curve — answers \"is this making money over time?\"" },
  { id: "daily",      label: "Daily P&L ($)",
    tip: "Per-day realized profit/loss in dollars.\nBars colored green positive, red negative." },
  { id: "dailypct",   label: "Daily P&L (%)",
    tip: "Per-day P&L as a percent of the stake deployed that day.\nNormalizes for variable position sizing — apples-to-apples across streak-scaled days." },
  { id: "cumulative", label: "Cumulative P&L",
    tip: "Running sum of daily P&L from the start of the window.\nSame trajectory as Portfolio Value minus starting balance." },
];

const RANGE_DEFS = [
  { id: "3D",  label: "3D",  days: 3 },
  { id: "7D",  label: "7D",  days: 7 },
  { id: "14D", label: "14D", days: 14 },
  { id: "1M",  label: "1M",  days: 22 },
  { id: "3M",  label: "3M",  days: 66 },
  { id: "6M",  label: "6M",  days: 126 },
  { id: "1Y",  label: "1Y",  days: 252 },
  { id: "5Y",  label: "5Y",  days: 1260 },
  { id: "All", label: "All", days: Infinity },
];

function MainChart({ equityCurve, dailyPnl, tradeByDate, tickerMeta, accentColor = "#00E5A0" }) {
  const [tab, setTab] = useState("portfolio");
  // View window expressed as data-array indices [start, end). Range pills are
  // shortcuts that snap this window; pan/pinch/wheel mutate it directly.
  const total = equityCurve.length;
  const defaultDays = Math.min(35, total);
  const [view, setView] = useState({ start: total - defaultDays, end: total });
  const [isDragging, setIsDragging] = useState(false);

  // Refs for pointer interaction (mutating per-event without re-rendering)
  const bodyRef = useRef(null);
  const pointersRef = useRef(new Map());
  const dragRef = useRef(null);
  const pinchRef = useRef(null);

  function snapToRange(rangeId) {
    const def = RANGE_DEFS.find(r => r.id === rangeId);
    if (!def) return;
    const N = !isFinite(def.days) ? total : Math.min(def.days, total);
    setView({ start: total - N, end: total });
  }

  function clampWindow(start, end) {
    let s = start, e = end;
    if (s < 0)     { e -= s; s = 0; }
    if (e > total) { s -= (e - total); e = total; }
    s = Math.max(0, Math.round(s));
    e = Math.min(total, Math.round(e));
    if (e - s < 2) e = Math.min(total, s + 2);
    return { start: s, end: e };
  }

  // Which range pill (if any) matches the current window exactly + is end-anchored
  const activeRange = (() => {
    if (view.end !== total) return null;
    const win = view.end - view.start;
    for (const r of RANGE_DEFS) {
      const target = !isFinite(r.days) ? total : Math.min(r.days, total);
      if (win === target) return r.id;
    }
    return null;
  })();

  /* ── Pointer interaction handlers (mouse + touch + pen via Pointer Events) ── */

  function onPointerDown(e) {
    bodyRef.current?.setPointerCapture?.(e.pointerId);
    pointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
    const n = pointersRef.current.size;
    if (n === 1) {
      const rect = bodyRef.current.getBoundingClientRect();
      dragRef.current = { startX: e.clientX, startView: { ...view }, width: rect.width };
      pinchRef.current = null;
    } else if (n === 2) {
      const [a, b] = [...pointersRef.current.values()];
      pinchRef.current = {
        startDist: Math.hypot(a.x - b.x, a.y - b.y),
        startMidX: (a.x + b.x) / 2,
        startView: { ...view },
        rectLeft: bodyRef.current.getBoundingClientRect().left,
        width: bodyRef.current.getBoundingClientRect().width,
      };
      dragRef.current = null;
    }
    setIsDragging(true);
  }

  function onPointerMove(e) {
    if (!pointersRef.current.has(e.pointerId)) return;
    pointersRef.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
    const n = pointersRef.current.size;
    if (n === 1 && dragRef.current) {
      const { startX, startView, width } = dragRef.current;
      const dx = e.clientX - startX;
      const win = startView.end - startView.start;
      const dIdx = -(dx / width) * win;
      setView(clampWindow(startView.start + dIdx, startView.end + dIdx));
    } else if (n === 2 && pinchRef.current) {
      const [a, b] = [...pointersRef.current.values()];
      const dist = Math.hypot(a.x - b.x, a.y - b.y);
      const { startDist, startMidX, startView, rectLeft, width } = pinchRef.current;
      const scale = startDist / Math.max(1, dist); // >1 zooms out, <1 zooms in
      const startWin = startView.end - startView.start;
      const newWin = Math.max(2, Math.min(total, startWin * scale));
      // Anchor zoom around the midpoint of the two fingers (in data-index space)
      const midT = (startMidX - rectLeft) / width;
      const midIdx = startView.start + midT * startWin;
      setView(clampWindow(midIdx - midT * newWin, midIdx + (1 - midT) * newWin));
    }
  }

  function onPointerUp(e) {
    if (pointersRef.current.has(e.pointerId)) {
      pointersRef.current.delete(e.pointerId);
    }
    if (pointersRef.current.size === 0) {
      dragRef.current = null;
      pinchRef.current = null;
      setIsDragging(false);
    } else if (pointersRef.current.size === 1) {
      // Transition from pinch back to drag — restart drag anchor
      const [pid] = [...pointersRef.current.keys()];
      const pt = pointersRef.current.get(pid);
      const rect = bodyRef.current.getBoundingClientRect();
      dragRef.current = { startX: pt.x, startView: { ...view }, width: rect.width };
      pinchRef.current = null;
    }
  }

  function onWheel(e) {
    e.preventDefault();
    const rect = bodyRef.current.getBoundingClientRect();
    const cursorT = Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width));
    const win = view.end - view.start;
    const factor = e.deltaY > 0 ? 1.18 : 1 / 1.18;
    const newWin = Math.max(2, Math.min(total, win * factor));
    const cursorIdx = view.start + cursorT * win;
    setView(clampWindow(cursorIdx - cursorT * newWin, cursorIdx + (1 - cursorT) * newWin));
  }

  // Suppress default browser wheel scrolling over the chart, but only while
  // the user is actually hovering us. Attach as non-passive so preventDefault sticks.
  useEffect(() => {
    const el = bodyRef.current;
    if (!el) return;
    const handler = (e) => onWheel(e);
    el.addEventListener("wheel", handler, { passive: false });
    return () => el.removeEventListener("wheel", handler);
  }, [view.start, view.end, total]);

  /* ── Derived data slices for current view window ── */

  const eqSlice = equityCurve.slice(view.start, view.end);
  const pnlSlice = dailyPnl.slice(view.start, view.end);

  const pnlPctSlice = pnlSlice.map(d => {
    const tr = tradeByDate[d.date];
    const stake = (tr && tr.stake_dollars) || 15000;
    return { date: d.date, pnl_dollars: d.pnl_dollars, pct: stake ? (d.pnl_dollars / stake) * 100 : 0, stake };
  });
  const cumulativeSlice = (() => {
    let sum = 0;
    return pnlSlice.map(d => { sum += d.pnl_dollars; return { date: d.date, cumulative: sum }; });
  })();

  const lastEq = eqSlice[eqSlice.length - 1]?.portfolio_value;
  const firstEq = eqSlice[0]?.portfolio_value;

  // ─── Sub-line under tabs: summary of current tab+range ───
  let summaryLeft = "";
  let summaryRight = "";
  if (tab === "portfolio") {
    summaryLeft = `${eqSlice.length} trading days · daily close`;
    summaryRight = `$${firstEq?.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })} → $${lastEq?.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
  } else if (tab === "daily") {
    const wins = pnlSlice.filter(d => d.pnl_dollars > 0).length;
    const losses = pnlSlice.filter(d => d.pnl_dollars < 0).length;
    const net = pnlSlice.reduce((a, d) => a + d.pnl_dollars, 0);
    summaryLeft = `${pnlSlice.length} days · ${wins}W / ${losses}L`;
    summaryRight = `net ${fmtUSD(net, { showSign: true })}`;
  } else if (tab === "dailypct") {
    const winRows = pnlPctSlice.filter(d => d.pct > 0);
    const lossRows = pnlPctSlice.filter(d => d.pct < 0);
    const avgWin  = winRows.length  ? winRows.reduce((a, d) => a + d.pct, 0) / winRows.length  : 0;
    const avgLoss = lossRows.length ? lossRows.reduce((a, d) => a + d.pct, 0) / lossRows.length : 0;
    summaryLeft = `avg win ${fmtPct(avgWin, 2)} · avg loss ${fmtPct(avgLoss, 2)}`;
    summaryRight = `% of daily stake deployed`;
  } else if (tab === "cumulative") {
    const t = cumulativeSlice[cumulativeSlice.length - 1]?.cumulative ?? 0;
    summaryLeft = `running sum · ${cumulativeSlice.length} days`;
    summaryRight = `total ${fmtUSD(t, { showSign: true })}`;
  }

  // ─── Choose data + renderer per tab ───
  let chartEl = null;
  if (tab === "portfolio") {
    chartEl = <LineView
      data={eqSlice}
      yKey="portfolio_value"
      yFmt={v => "$" + (v / 1000).toFixed(1) + "k"}
      tooltipPrimary={v => "$" + v.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
      tooltipPrimaryLabel="EQUITY"
      tooltipDeltaLabel="Δ START"
      tradeByDate={tradeByDate}
      tickerMeta={tickerMeta}
      interactive={!isDragging}
      accentColor={accentColor}
    />;
  } else if (tab === "daily") {
    chartEl = <BarsView
      data={pnlSlice.map(d => ({ ...d, value: d.pnl_dollars }))}
      yFmt={v => (v < 0 ? "−" : "") + "$" + Math.abs(v / 1000).toFixed(1) + "k"}
      tooltipPrimary={v => fmtUSD(v, { showSign: true })}
      tooltipPrimaryLabel="P&L"
      tradeByDate={tradeByDate}
      tickerMeta={tickerMeta}
      interactive={!isDragging}
    />;
  } else if (tab === "dailypct") {
    chartEl = <BarsView
      data={pnlPctSlice.map(d => ({ ...d, value: d.pct }))}
      yFmt={v => (v > 0 ? "+" : v < 0 ? "−" : "") + Math.abs(v).toFixed(1) + "%"}
      tooltipPrimary={v => fmtPct(v, 2)}
      tooltipPrimaryLabel="P&L %"
      extraRows={(d) => [["STAKE", "$" + d.stake.toLocaleString()], ["P&L $", fmtUSD(d.pnl_dollars, { showSign: true })]]}
      tradeByDate={tradeByDate}
      tickerMeta={tickerMeta}
      interactive={!isDragging}
    />;
  } else if (tab === "cumulative") {
    chartEl = <LineView
      data={cumulativeSlice}
      yKey="cumulative"
      yFmt={v => (v < 0 ? "−" : "") + "$" + Math.abs(v / 1000).toFixed(1) + "k"}
      tooltipPrimary={v => fmtUSD(v, { showSign: true })}
      tooltipPrimaryLabel="CUM P&L"
      tradeByDate={tradeByDate}
      tickerMeta={tickerMeta}
      anchorAtZero={true}
      interactive={!isDragging}
      accentColor={accentColor}
    />;
  }

  return (
    <div className="chart-panel">
      <div className="chart-tabs" role="tablist">
        {TAB_DEFS.map(t => (
          <button
            key={t.id}
            role="tab"
            aria-selected={tab === t.id}
            data-tip={t.tip}
            className={"chart-tab no-underline " + (tab === t.id ? "active" : "")}
            onClick={() => setTab(t.id)}
          >
            {t.label}
          </button>
        ))}
        <div style={{ flex: 1 }} />
        <div className="chart-controls pill-row" role="tablist" aria-label="Range">
          {RANGE_DEFS.map(r => (
            <button
              key={r.id}
              role="tab"
              aria-selected={activeRange === r.id}
              className={activeRange === r.id ? "on" : ""}
              onClick={() => snapToRange(r.id)}
            >{r.label}</button>
          ))}
        </div>
      </div>

      <div className="chart-body">
        <div className="chart-meta-line">
          <span>{summaryLeft}</span>
          <span style={{ color: "var(--text-2)" }}>
            {summaryRight} <span style={{ color: "var(--text-3)" }}>· drag to pan · pinch / scroll to zoom</span>
          </span>
        </div>
        <div
          ref={bodyRef}
          className={"chart-interactive" + (isDragging ? " is-dragging" : "")}
          onPointerDown={onPointerDown}
          onPointerMove={onPointerMove}
          onPointerUp={onPointerUp}
          onPointerCancel={onPointerUp}
        >
          {chartEl}
        </div>
      </div>
    </div>
  );
}

/* ───────── Generic line view (Portfolio / Cumulative) ───────── */

function LineView({ data, yKey, yFmt, tooltipPrimary, tooltipPrimaryLabel, tooltipDeltaLabel,
                    tradeByDate, tickerMeta, anchorAtZero = false, height = 300, interactive = true,
                    accentColor = "#00E5A0" }) {
  const wrapRef = useRef(null);
  const [hover, setHover] = useState(null);
  const [width, setWidth] = useState(900);

  useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    const ro = new ResizeObserver(() => setWidth(el.clientWidth));
    ro.observe(el);
    setWidth(el.clientWidth);
    return () => ro.disconnect();
  }, []);

  useEffect(() => { if (!interactive) setHover(null); }, [interactive]);

  const pad = { l: 56, r: 16, t: 8, b: 28 };
  const W = width, H = height;
  const iw = W - pad.l - pad.r;
  const ih = H - pad.t - pad.b;

  if (!data || data.length === 0) return <div style={{ height }} />;

  const values = data.map(d => d[yKey]);
  let min = Math.min(...values);
  let max = Math.max(...values);
  if (anchorAtZero) {
    min = Math.min(0, min);
    max = Math.max(0, max);
  }
  const yPadV = (max - min) * 0.1 || 1;
  const yMin = min - yPadV;
  const yMax = max + yPadV;

  const x = i => pad.l + (data.length === 1 ? iw / 2 : (i / (data.length - 1)) * iw);
  const y = v => pad.t + ih - ((v - yMin) / (yMax - yMin)) * ih;

  const linePath = data.map((d, i) => (i === 0 ? "M" : "L") + x(i).toFixed(2) + "," + y(d[yKey]).toFixed(2)).join(" ");
  const areaPath = linePath + ` L${x(data.length - 1).toFixed(2)},${pad.t + ih} L${x(0).toFixed(2)},${pad.t + ih} Z`;

  const yTicks = 4;
  const yVals = Array.from({ length: yTicks + 1 }, (_, i) => yMin + ((yMax - yMin) * i) / yTicks);
  const xTickCount = Math.min(5, data.length);
  const xIdx = xTickCount <= 1 ? [0] :
    Array.from({ length: xTickCount }, (_, i) => Math.round((i * (data.length - 1)) / (xTickCount - 1)));

  // Color (green for non-negative trend / positive zero-anchor, red for negative)
  const lastVal = data[data.length - 1][yKey];
  const lineColor = anchorAtZero
    ? (lastVal >= 0 ? accentColor : "#FF5C6F")
    : accentColor;
  const isNeg = anchorAtZero && lastVal < 0;

  function onMove(e) {
    if (!interactive) return;
    const rect = e.currentTarget.getBoundingClientRect();
    const px = e.clientX - rect.left;
    const t = (px - pad.l) / iw;
    const i = Math.max(0, Math.min(data.length - 1, Math.round(t * (data.length - 1))));
    setHover({ i, x: x(i), y: y(data[i][yKey]) });
  }
  function onLeave() { setHover(null); }

  const startVal = data[0][yKey];
  const hovered = hover ? data[hover.i] : null;
  const hoveredTrade = hovered ? tradeByDate[hovered.date] : null;
  const hoveredMeta = hoveredTrade ? tickerMeta[hoveredTrade.ticker] : null;
  const tooltipDelta = hovered ? hovered[yKey] - startVal : 0;

  return (
    <div ref={wrapRef} style={{ position: "relative" }}>
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} width={W} height={H}
           onMouseMove={onMove} onMouseLeave={onLeave}>
        <defs>
          <linearGradient id={"lineFill_" + tooltipPrimaryLabel.replace(/\W/g, "")} x1="0" x2="0" y1="0" y2="1">
            <stop offset="0%" stopColor={isNeg ? "#FF5C6F" : accentColor} stopOpacity="0.22" />
            <stop offset="100%" stopColor={isNeg ? "#FF5C6F" : accentColor} stopOpacity="0" />
          </linearGradient>
        </defs>

        <g className="chart-grid">
          {yVals.map((v, i) => (
            <line key={i} x1={pad.l} x2={W - pad.r} y1={y(v)} y2={y(v)} />
          ))}
        </g>

        {/* zero baseline */}
        {anchorAtZero && yMin < 0 && yMax > 0 && (
          <line x1={pad.l} x2={W - pad.r} y1={y(0)} y2={y(0)} stroke="var(--text-3)" strokeDasharray="2 4" opacity="0.5" />
        )}

        <g className="chart-axis">
          {yVals.map((v, i) => (
            <text key={i} x={pad.l - 10} y={y(v) + 3} textAnchor="end">{yFmt(v)}</text>
          ))}
        </g>
        <g className="chart-axis">
          {xIdx.map((i, k) => (
            <text key={k} x={x(i)} y={H - 8} textAnchor="middle">{fmtDateShort(data[i].date)}</text>
          ))}
        </g>

        <path d={areaPath} fill={`url(#lineFill_${tooltipPrimaryLabel.replace(/\W/g, "")})`} />
        <path d={linePath} fill="none" stroke={lineColor} strokeWidth="1.5" />

        <circle cx={x(data.length - 1)} cy={y(lastVal)} r="3.5"
                fill={lineColor} stroke="var(--bg-base)" strokeWidth="2" />

        {hover && (
          <g>
            <line className="chart-crosshair" x1={hover.x} x2={hover.x} y1={pad.t} y2={pad.t + ih} />
            <circle cx={hover.x} cy={hover.y} r="4" fill={lineColor} stroke="var(--bg-base)" strokeWidth="2" />
          </g>
        )}
      </svg>

      {hover && (
        <div className="chart-tooltip" style={{
          left: Math.min(W - 240, Math.max(8, hover.x + 16)),
          top:  Math.max(8, hover.y - 60),
        }}>
          <div><span className="k">DATE</span><span className="v">{fmtDateShort(hovered.date)} · {new Date(hovered.date).getFullYear()}</span></div>
          <div><span className="k">{tooltipPrimaryLabel}</span><span className={anchorAtZero ? (hovered[yKey] >= 0 ? "pnl-pos" : "pnl-neg") : "v"}>{tooltipPrimary(hovered[yKey])}</span></div>
          {tooltipDeltaLabel && (
            <div><span className="k">{tooltipDeltaLabel}</span><span className={tooltipDelta >= 0 ? "pnl-pos" : "pnl-neg"}>{fmtUSD(tooltipDelta, { showSign: true })}</span></div>
          )}
          {hoveredTrade && (
            <div className="ent">
              <span className="name">{hoveredTrade.ticker}{hoveredMeta ? " · " + hoveredMeta.n : ""}</span>
              {hoveredMeta && <span className="sec">{hoveredMeta.s} · {hoveredMeta.x}</span>}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

/* ───────── Generic bars view (Daily $ / Daily %) ───────── */

function BarsView({ data, yFmt, tooltipPrimary, tooltipPrimaryLabel, extraRows,
                    tradeByDate, tickerMeta, height = 300, interactive = true }) {
  const wrapRef = useRef(null);
  const [width, setWidth] = useState(900);
  const [hover, setHover] = useState(null);

  useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    const ro = new ResizeObserver(() => setWidth(el.clientWidth));
    ro.observe(el);
    setWidth(el.clientWidth);
    return () => ro.disconnect();
  }, []);

  useEffect(() => { if (!interactive) setHover(null); }, [interactive]);

  const pad = { l: 56, r: 16, t: 14, b: 28 };
  const W = width, H = height;
  const iw = W - pad.l - pad.r;
  const ih = H - pad.t - pad.b;

  if (!data || data.length === 0) return <div style={{ height }} />;

  const max = Math.max(...data.map(d => Math.abs(d.value))) || 1;
  const zero = pad.t + ih / 2;
  const yScale = (ih / 2) / max;
  // With very dense data (1000+ bars in ~800px) bars can go sub-pixel; clamp to 1.
  const bw = Math.max(1, Math.min(20, (iw / data.length) - (data.length > 200 ? 0 : 2)));

  const xTickCount = Math.min(5, data.length);
  const xIdx = xTickCount <= 1 ? [0] :
    Array.from({ length: xTickCount }, (_, i) => Math.round((i * (data.length - 1)) / (xTickCount - 1)));

  const yTickVals = [max, max / 2, 0, -max / 2, -max];
  const yTickY = v => zero - v * yScale;

  const hovered = hover ? data[hover.i] : null;
  const hoveredTrade = hovered ? tradeByDate[hovered.date] : null;
  const hoveredMeta = hoveredTrade ? tickerMeta[hoveredTrade.ticker] : null;

  return (
    <div ref={wrapRef} style={{ position: "relative" }}>
      <svg className="chart-svg" viewBox={`0 0 ${W} ${H}`} width={W} height={H}>
        <g className="chart-grid">
          {yTickVals.map((v, i) => (
            <line key={i} x1={pad.l} x2={W - pad.r} y1={yTickY(v)} y2={yTickY(v)} />
          ))}
        </g>
        <line x1={pad.l} x2={W - pad.r} y1={zero} y2={zero} stroke="var(--text-3)" opacity="0.5" />

        <g className="chart-axis">
          {yTickVals.map((v, i) => (
            <text key={i} x={pad.l - 10} y={yTickY(v) + 3} textAnchor="end">{yFmt(v)}</text>
          ))}
        </g>
        <g className="chart-axis">
          {xIdx.map((i, k) => (
            <text key={k} x={pad.l + (data.length === 1 ? iw / 2 : (i / (data.length - 1)) * iw)} y={H - 8} textAnchor="middle">
              {fmtDateShort(data[i].date)}
            </text>
          ))}
        </g>

        {data.map((d, i) => {
          const xCenter = pad.l + (data.length === 1 ? iw / 2 : (i / (data.length - 1)) * iw);
          const x = xCenter - bw / 2;
          const v = d.value;
          const h = Math.abs(v) * yScale;
          const yTop = v >= 0 ? zero - h : zero;
          const fill = v >= 0 ? "var(--pos)" : "var(--neg)";
          const isHovered = hover && hover.i === i;
          return (
            <rect
              key={i}
              className="bar-bar"
              x={x} y={yTop} width={bw} height={Math.max(1, h)} fill={fill}
              opacity={hover ? (isHovered ? 1 : 0.5) : 1}
              onMouseEnter={() => interactive && setHover({ i, x: xCenter, yTop })}
              onMouseLeave={() => interactive && setHover(null)}
            />
          );
        })}

        {hover && (
          <line className="chart-crosshair" x1={hover.x} x2={hover.x} y1={pad.t} y2={pad.t + ih} />
        )}
      </svg>

      {hover && hovered && (
        <div className="chart-tooltip" style={{
          left: Math.min(W - 240, Math.max(8, hover.x + 14)),
          top:  Math.max(8, hover.yTop - 60),
        }}>
          <div><span className="k">DATE</span><span className="v">{fmtDateShort(hovered.date)}</span></div>
          <div><span className="k">{tooltipPrimaryLabel}</span><span className={hovered.value >= 0 ? "pnl-pos" : "pnl-neg"}>{tooltipPrimary(hovered.value)}</span></div>
          {extraRows && extraRows(hovered).map(([k, v], idx) => (
            <div key={idx}><span className="k">{k}</span><span className="v">{v}</span></div>
          ))}
          {hoveredTrade && (
            <div className="ent">
              <span className="name">{hoveredTrade.ticker}{hoveredMeta ? " · " + hoveredMeta.n : ""}</span>
              {hoveredMeta && <span className="sec">{hoveredMeta.s} · {hoveredMeta.x}</span>}
            </div>
          )}
        </div>
      )}
    </div>
  );
}

/* ───────── Recent trades ───────── */

function TradesTable({ trades, tickerMeta }) {
  return (
    <div className="trades" style={{ marginTop: "var(--s-6)" }}>
      <div className="trades-head">
        <div>
          <div className="chart-title">Recent trades</div>
          <div className="mono" style={{ color: "var(--text-3)", fontSize: 11, marginTop: 2 }}>
            last {trades.length} closed positions · newest first
          </div>
        </div>
        <div className="mono" style={{ color: "var(--text-3)", fontSize: 11 }}>read-only · source: alpaca</div>
      </div>
      <table className="trades-table">
        <thead>
          <tr>
            <th data-tip="Date the bot executed buy and sell.&#10;ET market days only — weekends + holidays skipped.">Date</th>
            <th data-tip="Stock symbol the scanner picked that morning.&#10;Top pre-market % gainer above the 4% threshold from the watchlist.">Ticker</th>
            <th data-tip="Full company name from the watchlist enrichment.&#10;Pulled from watchlist_large2_enriched.json (570 tickers, Twelve Data).">Company</th>
            <th data-tip="GICS sector classification.&#10;Useful for spotting whether wins/losses cluster in particular sectors.">Sector</th>
            <th className="num" data-tip="Dollar amount deployed for this trade.&#10;Driven by the streak formula in effect that day:&#10;• base ($15,000)&#10;• win streak → prevStake × 1.5&#10;• loss streak → portfolio × 2%">Stake</th>
            <th className="num" data-tip="Realized profit/loss in dollars.&#10;= Stake × (Sell − Buy) / Buy&#10;Green positive, red negative.">P&amp;L $</th>
            <th className="num" data-tip="Realized P&L as a percent of stake deployed.&#10;Normalizes outcome regardless of stake size.">P&amp;L %</th>
          </tr>
        </thead>
        <tbody>
          {trades.map((t, i) => {
            const meta = tickerMeta[t.ticker] || { n: t.ticker, s: "—", x: "" };
            return (
              <tr key={i}>
                <td className="date">{fmtDateShort(t.date)}</td>
                <td className="ticker">
                  <span style={{ color: "var(--text-1)" }}>{t.ticker}</span>
                  {meta.x && <span style={{ color: "var(--text-3)", marginLeft: 6, fontSize: 11 }}>· {meta.x}</span>}
                </td>
                <td style={{ color: "var(--text-2)", fontFamily: "var(--sans)", fontSize: 13 }}>{meta.n}</td>
                <td><span className="sector-pill">{meta.s}</span></td>
                <td className="num">${t.stake_dollars.toLocaleString()}</td>
                <td className={"num " + pnlClass(t.pnl_dollars)}>{fmtUSD(t.pnl_dollars, { showSign: true })}</td>
                <td className={"num " + pnlClass(t.pnl_dollars)}>{fmtPct(t.pnl_percent, 2)}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    </div>
  );
}

/* ───────── About / How it works ───────── */

function AboutSection({ strategy, acct }) {
  const isB = acct && acct.strategyKind === "B";
  const steps = [
    { time: "09:00", name: "Optimizer", desc: isB
        ? "No formula search for Strategy B — stake is always the full portfolio. Optimizer still picks buy/sell timing."
        : "Brute-force search over 14,700 combinations picks today's win/loss formula and buy/sell time." },
    { time: "09:20", name: "Scanner",   desc: "Pulls pre-market movers from a curated watchlist. Picks the top % gainer above 4%." },
    { time: strategy.buy_time,  name: "Buy",  desc: isB
        ? "Deploys the ENTIRE portfolio into the picked ticker. Today's stake = yesterday's close."
        : `Submits a market buy on Alpaca. Today's stake: $${strategy.base_stake_dollars.toLocaleString()} (base).` },
    { time: strategy.sell_time, name: "Sell", desc: "Closes the position at the chosen sell time. Realized P&L lands." },
    { time: "13:15", name: "Log",       desc: "Writes logs/YYYY-MM-DD.json. Updates streak state for tomorrow." },
    { time: "13:30", name: "Notify",    desc: "SNS email with the day's summary. CloudWatch records the run." },
  ];
  return (
    <section className="section">
      <div className="section-head">
        <div>
          <div className="section-label">How it works</div>
          <h2 className="section-title">
            {isB ? "Compound everything, every day." : "One stock, one day, one schedule."}
          </h2>
        </div>
        <div className="section-meta">read-only · alpaca · v0.1</div>
      </div>

      <div className="about">
        <div>
          {isB ? (
            <>
              <p>
                <strong>{acct.label}</strong> runs <strong>Strategy B — full compounding</strong>.
                Each day the bot deploys the <strong>entire portfolio balance</strong> into one stock
                rather than a fixed $15k stake. The trade execution is identical to Account 1, but the
                sizing changes every day.
              </p>
              <p>
                Both gains and losses compound exponentially. A 5% winning day on a $130,000 portfolio
                returns $6,500 — but a 7.9% losing day (like CSCO on 5/15) wipes out
                <span className="mono"> $10,200+</span> in one trade. There is no streak formula —
                stake is always 100% of available capital.
              </p>
              <p style={{ fontSize: 13, color: "var(--text-3)" }}>
                Backtest over the last {strategy.backtest_days} days returned
                {" "}<span className="mono value-pos">+{strategy.backtest_pnl_percent.toFixed(2)}%</span>
                {" "}at a {(strategy.backtest_win_rate * 100).toFixed(1)}% win rate.
              </p>
            </>
          ) : (
            <>
              <p>
                This bot trades <strong>one stock per day</strong> on Alpaca. Each morning a
                scanner pulls pre-market movers from a curated watchlist of 570 tickers and picks the top
                percent gainer above 4%. An optimizer decides what time to buy and sell based on what has
                worked historically.
              </p>
              <p>
                Stake size scales <strong>up after winning streaks</strong> and resets after a loss. The
                current win formula is <span className="mono" style={{ color: "var(--text-1)" }}>{strategy.win_formula_expression}</span>
                {" "}({strategy.win_formula_label}); the loss formula is
                {" "}<span className="mono" style={{ color: "var(--text-1)" }}>{strategy.loss_formula_expression}</span>
                {" "}({strategy.loss_formula_label}). Both were chosen by {strategy.chosen_via}.
              </p>
              <p style={{ fontSize: 13, color: "var(--text-3)" }}>
                Backtest over the last {strategy.backtest_days} days returned
                {" "}<span className="mono value-pos">+{strategy.backtest_pnl_percent.toFixed(2)}%</span>
                {" "}at a {(strategy.backtest_win_rate * 100).toFixed(1)}% win rate. The dashboard reads
                the daily logs the bot writes to disk after each run.
              </p>
            </>
          )}
          <div className="stack-line">
            <span>AWS EC2</span>
            <span>Alpaca Markets API</span>
            <span>Python 3.12</span>
            <span>cron @ 09:20 ET</span>
            <span>SNS email alerts</span>
            <span>CloudWatch</span>
            <span>github.com / trading-optimizer</span>
          </div>
        </div>

        <div className="flow">
          <div className="flow-row">
            {steps.map((s, i) => (
              <div className="flow-step" key={i}>
                <div className="flow-time">{s.time} ET</div>
                <div className="flow-name">{s.name}</div>
                <div className="flow-desc">{s.desc}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </section>
  );
}

/* ───────── Export ───────── */

Object.assign(window, {
  Sparkline, Topbar, TodayHero, StatsRow, MainChart, TradesTable, AboutSection,
  AccountTabs,
  fmtUSD, fmtPct, fmtDateShort, pnlClass,
});
