// Foresight — full-bleed financial projection prototype.
// Live recompute, scenario overlay, click-year drill, add-event modal, tweaks.

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

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "indigo",
  "density": "comfortable",
  "chartStyle": "band",
  "showBands": true,
  "numberFormat": "compact",
  "compareScenario": "none"
}/*EDITMODE-END*/;

const THEMES = JSON.parse(document.getElementById('theme-config').textContent).themes;

// ─── Helpers ──────────────────────────────────────────────────────────────
const formatN = (n, mode) =>
  mode === 'full' ? fmt.usdLong(n) : fmt.usd(n);

function useTheme(themeKey) {
  const t = THEMES[themeKey] || THEMES.indigo;
  useEffect(() => {
    const r = document.documentElement.style;
    r.setProperty('--bg', t.bg);
    r.setProperty('--ink', t.ink);
  }, [t]);
  return t;
}

// ─── Top-level component ──────────────────────────────────────────────────
function Foresight() {
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const theme = useTheme(tweaks.theme);
  const compact = tweaks.density === 'compact';

  const [state, setState] = useState(() => JSON.parse(JSON.stringify(DEFAULT_STATE)));
  const [selected, setSelected] = useState(state.horizon);
  const [hover, setHover] = useState(null);
  const [activeScenario, setActiveScenario] = useState('baseline');
  const [showAddEvent, setShowAddEvent] = useState(false);
  const [showMonarchPanel, setShowMonarchPanel] = useState(false);

  // Apply active scenario preset
  const activeState = useMemo(() => SCENARIO_PRESETS[activeScenario](state), [state, activeScenario]);
  const data = useMemo(() => project(activeState), [activeState]);
  const compareState = useMemo(
    () => tweaks.compareScenario !== 'none' ? SCENARIO_PRESETS[tweaks.compareScenario](state) : null,
    [state, tweaks.compareScenario]
  );
  const compareData = useMemo(() => compareState ? project(compareState) : null, [compareState]);

  const cur = data[0];
  const end = data[data.length - 1];
  const sel = data[selected];

  const setAssumption = (k, v) => setState((s) => ({ ...s, assumptions: { ...s.assumptions, [k]: v } }));
  const addEvent = (template, year, label, extras) => {
    const id = 'e' + Date.now();
    setState((s) => ({
      ...s,
      events: [...s.events, {
        id, year, age: PROFILE.age + year, kind: template.kind, label,
        ...template.defaults, ...extras,
      }].sort((a, b) => a.year - b.year),
    }));
    setShowAddEvent(false);
  };
  const removeEvent = (id) => setState((s) => ({ ...s, events: s.events.filter((e) => e.id !== id) }));

  const P = compact
    ? { pad: 14, gap: 10, statPad: '12px 14px', cardPad: 12, headPad: '10px 12px' }
    : { pad: 22, gap: 14, statPad: '16px 18px', cardPad: 16, headPad: '12px 16px' };

  return (
    <div style={{ minHeight: '100vh', background: theme.bg, color: theme.ink,
      padding: P.pad, display: 'flex', flexDirection: 'column', gap: P.gap }}>
      <TopBar theme={theme} active={activeScenario} setActive={setActiveScenario}
        onMonarch={() => setShowMonarchPanel(true)} />

      <KPIs theme={theme} P={P} cur={cur} end={end} state={activeState} fmtN={(n) => formatN(n, tweaks.numberFormat)} />

      <ChartPanel theme={theme} P={P} compact={compact}
        data={data} compareData={compareData} compareLabel={tweaks.compareScenario}
        selected={selected} setSelected={setSelected}
        hover={hover} setHover={setHover}
        chartStyle={tweaks.chartStyle} showBands={tweaks.showBands}
        fmtN={(n) => formatN(n, tweaks.numberFormat)} />

      <div style={{ display: 'grid', gridTemplateColumns: '1.1fr 1fr 1.1fr', gap: P.gap }}>
        <AssumptionsPanel theme={theme} P={P} state={state} setAssumption={setAssumption} />
        <EventsPanel theme={theme} P={P} state={state} onAdd={() => setShowAddEvent(true)} onRemove={removeEvent} />
        <BreakdownPanel theme={theme} P={P} sel={sel} selected={selected}
          horizon={state.horizon} setSelected={setSelected}
          fmtN={(n) => formatN(n, tweaks.numberFormat)} />
      </div>

      <LedgerPanel theme={theme} P={P} cur={cur} end={end} horizon={state.horizon}
        fmtN={(n) => formatN(n, tweaks.numberFormat)} />

      {showAddEvent && (
        <AddEventModal theme={theme} horizon={state.horizon}
          onClose={() => setShowAddEvent(false)} onAdd={addEvent} />
      )}
      {showMonarchPanel && (
        <MonarchPanel theme={theme} onClose={() => setShowMonarchPanel(false)} />
      )}

      <TweaksPanel title="Tweaks">
        <TweakSection label="Appearance">
          <TweakSelect label="Color theme" value={tweaks.theme}
            options={[
              { value: 'indigo', label: 'Indigo' },
              { value: 'emerald', label: 'Emerald' },
              { value: 'charcoal', label: 'Charcoal' },
              { value: 'sunset', label: 'Sunset' },
              { value: 'dark', label: 'Dark' },
            ]} onChange={(v) => setTweak('theme', v)} />
          <TweakRadio label="Density" value={tweaks.density}
            options={[{ value: 'comfortable', label: 'Cozy' }, { value: 'compact', label: 'Compact' }]}
            onChange={(v) => setTweak('density', v)} />
          <TweakSelect label="Number format" value={tweaks.numberFormat}
            options={[{ value: 'compact', label: 'Compact ($1.2M)' }, { value: 'full', label: 'Full ($1,234,567)' }]}
            onChange={(v) => setTweak('numberFormat', v)} />
        </TweakSection>
        <TweakSection label="Chart">
          <TweakSelect label="Style" value={tweaks.chartStyle}
            options={[
              { value: 'band',    label: 'Smooth area' },
              { value: 'dotted',  label: 'Dotted band' },
              { value: 'step',    label: 'Stepped' },
            ]}
            onChange={(v) => setTweak('chartStyle', v)} />
          <TweakToggle label="Confidence bands" value={tweaks.showBands}
            onChange={(v) => setTweak('showBands', v)} />
          <TweakSelect label="Compare scenario" value={tweaks.compareScenario}
            options={[
              { value: 'none',        label: 'None' },
              { value: 'aggressive',  label: 'Aggressive savings (34%)' },
              { value: 'earlyRetire', label: 'Retire at 52' },
            ]} onChange={(v) => setTweak('compareScenario', v)} />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

// ─── Top bar ──────────────────────────────────────────────────────────────
function TopBar({ theme, active, setActive, onMonarch }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 18 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 26, height: 26, borderRadius: 7, background: theme.accent,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: '#fff', fontWeight: 700, fontSize: 14 }}>F</div>
          <span style={{ fontSize: 16, fontWeight: 600, letterSpacing: -0.2 }}>Foresight</span>
        </div>
        <div style={{ display: 'flex', gap: 4, padding: 3, background: theme.panel,
          border: `1px solid ${theme.rule}`, borderRadius: 8 }}>
          {[
            { k: 'baseline',    label: 'Baseline' },
            { k: 'aggressive',  label: 'Aggressive savings' },
            { k: 'earlyRetire', label: 'Retire at 52' },
          ].map((s) => (
            <button key={s.k} onClick={() => setActive(s.k)} style={{
              padding: '6px 12px', border: 0, borderRadius: 5, fontSize: 12, cursor: 'pointer',
              background: s.k === active ? theme.accent : 'transparent',
              color: s.k === active ? '#fff' : theme.ink2, fontWeight: 500,
              fontFamily: 'inherit', transition: 'background .12s',
            }}>{s.label}</button>
          ))}
        </div>
      </div>
      <div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
        <MonarchBadge theme={theme} onClick={onMonarch} />
        <span style={{ fontSize: 13, color: theme.ink2 }}>{PROFILE.name}</span>
        <div style={{ width: 30, height: 30, borderRadius: 99, background: theme.accentSoft,
          color: theme.accent, fontWeight: 600, fontSize: 11, display: 'flex',
          alignItems: 'center', justifyContent: 'center' }}>AM</div>
      </div>
    </div>
  );
}

function MonarchBadge({ theme, onClick }) {
  return (
    <button onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 8,
      padding: '5px 10px 5px 8px', borderRadius: 6,
      background: theme.accentSoft, border: `1px solid ${theme.accent}33`,
      fontSize: 11.5, color: theme.accent, fontWeight: 500, cursor: 'pointer',
      fontFamily: 'inherit', transition: 'background .12s',
    }}>
      <span style={{ width: 6, height: 6, borderRadius: 99, background: '#0e9f6e',
        boxShadow: '0 0 0 3px #0e9f6e22' }} />
      Synced from Monarch · 4m ago
    </button>
  );
}

// ─── KPIs ─────────────────────────────────────────────────────────────────
function KPIs({ theme, P, cur, end, state, fmtN }) {
  const debtFree = (() => {
    return null; // computed via data, but kept simple here
  })();
  const items = [
    { label: 'NET WORTH · TODAY', value: fmtN(cur.median),
      sub: `+ ${fmt.usd(end.median - cur.median)} over 20y`, subColor: '#0e9f6e' },
    { label: `PROJECTED · AGE ${end.age}`, value: fmtN(end.median),
      sub: `80% likely ${fmt.usd(end.p10)} – ${fmt.usd(end.p90)}`, subColor: theme.ink3 },
    { label: 'TOTAL ASSETS', value: fmtN(cur.assets),
      sub: `${fmt.pct(1 - state.accounts.home.value / cur.assets)} liquid`, subColor: theme.ink3 },
    { label: 'TOTAL LIABILITIES', value: fmtN(cur.debts),
      sub: 'Mortgage · auto · student', subColor: theme.ink3 },
  ];
  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: P.gap }}>
      {items.map((it) => (
        <div key={it.label} style={{ padding: P.statPad, background: theme.panel,
          borderRadius: 10, border: `1px solid ${theme.rule}` }}>
          <div style={{ fontSize: 10.5, color: theme.ink3, fontWeight: 600, letterSpacing: 0.6 }}>
            {it.label}
          </div>
          <div style={{ fontSize: 28, fontWeight: 600, marginTop: 6, letterSpacing: -0.5, lineHeight: 1.1 }}>
            {it.value}
          </div>
          <div style={{ fontSize: 11.5, color: it.subColor, marginTop: 6,
            fontFamily: '"Geist Mono", monospace' }}>{it.sub}</div>
        </div>
      ))}
    </div>
  );
}

// ─── Chart panel ──────────────────────────────────────────────────────────
function ChartPanel({ theme, P, compact, data, compareData, compareLabel,
                      selected, setSelected, hover, setHover, chartStyle, showBands, fmtN }) {
  const containerRef = useRef(null);
  const [width, setWidth] = useState(1400);
  useEffect(() => {
    if (!containerRef.current) return;
    const ro = new ResizeObserver((e) => setWidth(e[0].contentRect.width));
    ro.observe(containerRef.current);
    return () => ro.disconnect();
  }, []);

  return (
    <div style={{ background: theme.panel, borderRadius: 10, border: `1px solid ${theme.rule}` }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: P.headPad, borderBottom: `1px solid ${theme.rule}` }}>
        <div>
          <div style={{ fontSize: 13, fontWeight: 600 }}>Net worth · 2026 → 2046</div>
          <div style={{ fontSize: 11, color: theme.ink3, marginTop: 2 }}>
            Click any year on the chart for a breakdown · hover for details
          </div>
        </div>
        <div style={{ display: 'flex', gap: 16, fontSize: 11, color: theme.ink2, alignItems: 'center' }}>
          <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
            <span style={{ width: 14, height: 2, background: theme.accent }} /> Median
          </span>
          {showBands && (
            <>
              <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ width: 14, height: 8, background: `${theme.accent}33`, borderRadius: 1 }} /> 50% range
              </span>
              <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                <span style={{ width: 14, height: 8, background: `${theme.accent}1a`, borderRadius: 1 }} /> 80% range
              </span>
            </>
          )}
          {compareData && (
            <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
              <span style={{ width: 14, height: 0, borderTop: `2px dashed ${theme.ink2}` }} />
              {compareLabel === 'aggressive' ? 'Aggressive savings' : 'Retire at 52'}
            </span>
          )}
        </div>
      </div>
      <div ref={containerRef} style={{ padding: '8px 8px 4px' }}>
        <Chart theme={theme} data={data} compareData={compareData}
          width={width - 16} height={compact ? 280 : 340}
          selected={selected} setSelected={setSelected}
          hover={hover} setHover={setHover}
          chartStyle={chartStyle} showBands={showBands} fmtN={fmtN} />
      </div>
    </div>
  );
}

function Chart({ theme, data, compareData, width, height,
                 selected, setSelected, hover, setHover, chartStyle, showBands, fmtN }) {
  const W = Math.max(400, width), H = height;
  const Pad = { l: 64, r: 24, t: 22, b: 36 };
  const innerW = W - Pad.l - Pad.r, innerH = H - Pad.t - Pad.b;
  const xs = (i) => Pad.l + (i / (data.length - 1)) * innerW;
  const yMaxA = Math.max(...data.map((d) => d.p90));
  const yMaxB = compareData ? Math.max(...compareData.map((d) => d.median)) : 0;
  const yMax = Math.max(yMaxA, yMaxB) * 1.05;
  const yMin = Math.min(0, ...data.map((d) => d.p10));
  const ys = (v) => Pad.t + innerH - ((v - yMin) / (yMax - yMin)) * innerH;

  const linePath = (arr, key) => {
    if (chartStyle === 'step') {
      let d = '';
      arr.forEach((p, i) => {
        const x = xs(i), y = ys(p[key]);
        if (i === 0) d += `M ${x.toFixed(1)} ${y.toFixed(1)}`;
        else d += ` H ${x.toFixed(1)} V ${y.toFixed(1)}`;
      });
      return d;
    }
    return arr.map((p, i) => `${i === 0 ? 'M' : 'L'} ${xs(i).toFixed(1)} ${ys(p[key]).toFixed(1)}`).join(' ');
  };

  const areaPath = (lo, hi) => {
    const top = data.map((d, i) => `${i === 0 ? 'M' : 'L'} ${xs(i).toFixed(1)} ${ys(d[hi]).toFixed(1)}`).join(' ');
    const bot = data.slice().reverse().map((d, j) => {
      const i = data.length - 1 - j;
      return `L ${xs(i).toFixed(1)} ${ys(d[lo]).toFixed(1)}`;
    }).join(' ');
    return `${top} ${bot} Z`;
  };

  const ticks = [];
  const step = yMax > 4e6 ? 2e6 : yMax > 2e6 ? 1e6 : 5e5;
  for (let v = 0; v <= yMax; v += step) ticks.push(v);

  const tipIdx = hover != null ? hover : selected;
  const tip = data[tipIdx];

  return (
    <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', height: H, display: 'block' }}>
      <defs>
        <linearGradient id="fGrad90" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor={theme.accent} stopOpacity="0.12" />
          <stop offset="100%" stopColor={theme.accent} stopOpacity="0" />
        </linearGradient>
        <linearGradient id="fGrad75" x1="0" x2="0" y1="0" y2="1">
          <stop offset="0%" stopColor={theme.accent} stopOpacity="0.24" />
          <stop offset="100%" stopColor={theme.accent} stopOpacity="0.04" />
        </linearGradient>
        <pattern id="fDots" x="0" y="0" width="6" height="6" patternUnits="userSpaceOnUse">
          <circle cx="1" cy="1" r="1" fill={theme.accent} fillOpacity="0.15" />
        </pattern>
      </defs>

      {ticks.map((v) => (
        <g key={v}>
          <line x1={Pad.l} x2={W - Pad.r} y1={ys(v)} y2={ys(v)} stroke={theme.ruleSoft} />
          <text x={Pad.l - 8} y={ys(v) + 3} textAnchor="end"
            style={{ fontFamily: '"Geist Mono", monospace', fontSize: 10, fill: theme.ink3 }}>
            {fmt.usd(v)}
          </text>
        </g>
      ))}

      {showBands && (
        <>
          {chartStyle === 'dotted' ? (
            <>
              <path d={areaPath('p10', 'p90')} fill="url(#fDots)" />
              <path d={areaPath('p25', 'p75')} fill="url(#fDots)" />
            </>
          ) : (
            <>
              <path d={areaPath('p10', 'p90')} fill="url(#fGrad90)" />
              <path d={areaPath('p25', 'p75')} fill="url(#fGrad75)" />
            </>
          )}
        </>
      )}

      {compareData && (
        <path d={linePath(compareData, 'median')} fill="none"
          stroke={theme.ink2} strokeWidth="1.5" strokeDasharray="5 4" opacity="0.7" />
      )}

      <path d={linePath(data, 'median')} fill="none"
        stroke={theme.accent} strokeWidth="2.25" strokeLinejoin="round" />

      {/* Event markers */}
      {data.flatMap((d) => d.events.map((e) => (
        <g key={e.id}>
          <line x1={xs(d.year)} x2={xs(d.year)} y1={Pad.t} y2={H - Pad.b}
            stroke={theme.ink2} strokeWidth="1" strokeDasharray="3 3" opacity="0.35" />
          <rect x={xs(d.year) - 56} y={Pad.t - 2} width="112" height="20" rx="10" fill={theme.ink} />
          <text x={xs(d.year)} y={Pad.t + 12} textAnchor="middle"
            style={{ fontSize: 10.5, fill: theme.bg, fontWeight: 600, letterSpacing: 0.2 }}>
            {(e.kind === 'retire' ? '◐ ' : e.kind === 'job' ? '↑ ' : e.kind === 'home' ? '⌂ ' : '✶ ') + e.label}
          </text>
        </g>
      )))}

      {hover != null && hover !== selected && (
        <line x1={xs(hover)} x2={xs(hover)} y1={Pad.t} y2={H - Pad.b}
          stroke={theme.ink2} strokeWidth="1" opacity="0.3" />
      )}
      {selected != null && (
        <g>
          <line x1={xs(selected)} x2={xs(selected)} y1={Pad.t} y2={H - Pad.b}
            stroke={theme.accent} strokeWidth="1" />
          <circle cx={xs(selected)} cy={ys(data[selected].median)} r="6"
            fill={theme.panel} stroke={theme.accent} strokeWidth="2.5" />
        </g>
      )}

      {data.map((d, i) => (i % 5 === 0 || i === data.length - 1) && (
        <text key={i} x={xs(i)} y={H - 14} textAnchor="middle"
          style={{ fontFamily: '"Geist Mono", monospace', fontSize: 10, fill: theme.ink3 }}>
          {2026 + d.year}
        </text>
      ))}

      {/* Hover tooltip */}
      {tipIdx != null && (
        <g style={{ pointerEvents: 'none' }}>
          {(() => {
            const tx = xs(tipIdx);
            const flip = tx > W * 0.65;
            const tw = 168, th = 92;
            const x0 = flip ? tx - tw - 10 : tx + 10;
            const y0 = Pad.t + 6;
            return (
              <>
                <rect x={x0} y={y0} width={tw} height={th} rx="6"
                  fill={theme.panel} stroke={theme.rule} strokeWidth="1"
                  filter="drop-shadow(0 6px 16px rgba(15,23,42,0.12))" />
                <text x={x0 + 12} y={y0 + 18} style={{ fontSize: 10, fill: theme.ink3, fontWeight: 600, letterSpacing: 0.5 }}>
                  {(2026 + tip.year)} · AGE {tip.age}
                </text>
                <text x={x0 + 12} y={y0 + 40} style={{ fontSize: 18, fill: theme.ink, fontWeight: 600 }}>
                  {fmtN(tip.median)}
                </text>
                <text x={x0 + 12} y={y0 + 58} style={{ fontSize: 10, fill: theme.ink2, fontFamily: '"Geist Mono", monospace' }}>
                  P10 {fmt.usd(tip.p10)} · P90 {fmt.usd(tip.p90)}
                </text>
                <text x={x0 + 12} y={y0 + 78} style={{ fontSize: 10, fill: tip.retired ? '#d99000' : '#0e9f6e', fontWeight: 600 }}>
                  {tip.retired ? '◐ Retired' : tip.paused ? '✦ Sabbatical' : '● Working'}
                </text>
              </>
            );
          })()}
        </g>
      )}

      {data.map((d, i) => (
        <rect key={i} x={xs(i) - innerW / data.length / 2} y={Pad.t}
          width={innerW / data.length} height={innerH}
          fill="transparent" style={{ cursor: 'pointer' }}
          onClick={() => setSelected(i)}
          onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)} />
      ))}
    </svg>
  );
}

// ─── Assumptions panel ────────────────────────────────────────────────────
function AssumptionsPanel({ theme, P, state, setAssumption }) {
  return (
    <Panel theme={theme} P={P} title="Assumptions"
      action={<span style={{ fontSize: 11, color: theme.ink3 }}>recomputes live</span>}>
      <div style={{ display: 'flex', flexDirection: 'column' }}>
        <AssumptionRow theme={theme} label="Investment return" value={state.assumptions.returnRate}
          format={(v) => `${(v*100).toFixed(1)}%`} min={0.02} max={0.12} step={0.005}
          onChange={(v) => setAssumption('returnRate', v)} />
        <AssumptionRow theme={theme} label="Salary growth" value={state.assumptions.salaryGrowth}
          format={(v) => `${(v*100).toFixed(1)}%`} min={0} max={0.08} step={0.005}
          onChange={(v) => setAssumption('salaryGrowth', v)} />
        <AssumptionRow theme={theme} label="Inflation" value={state.assumptions.inflation}
          format={(v) => `${(v*100).toFixed(1)}%`} min={0} max={0.08} step={0.005}
          onChange={(v) => setAssumption('inflation', v)} />
        <AssumptionRow theme={theme} label="Savings rate" value={state.assumptions.savingsRate}
          format={(v) => `${Math.round(v*100)}%`} min={0.05} max={0.5} step={0.01}
          onChange={(v) => setAssumption('savingsRate', v)} />
        <AssumptionRow theme={theme} label="Home appreciation" value={state.assumptions.homeAppreciation}
          format={(v) => `${(v*100).toFixed(1)}%`} min={0} max={0.08} step={0.005}
          onChange={(v) => setAssumption('homeAppreciation', v)} />
      </div>
    </Panel>
  );
}

function AssumptionRow({ theme, label, value, format, min, max, step, onChange }) {
  const pct = (value - min) / (max - min);
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 0',
      borderBottom: `1px solid ${theme.ruleSoft}` }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 12, color: theme.ink, fontWeight: 500 }}>{label}</div>
        <div style={{ position: 'relative', height: 4, background: theme.ruleSoft,
          borderRadius: 99, marginTop: 6 }}>
          <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0,
            width: `${pct * 100}%`, background: theme.accent, borderRadius: 99 }} />
          <div style={{ position: 'absolute', left: `calc(${pct * 100}% - 6px)`, top: -4,
            width: 12, height: 12, background: theme.panel, border: `2px solid ${theme.accent}`,
            borderRadius: 99 }} />
        </div>
      </div>
      <Stepper theme={theme} value={value} format={format} min={min} max={max} step={step} onChange={onChange} />
    </div>
  );
}

function Stepper({ theme, value, format, min, max, step, onChange }) {
  return (
    <div style={{ display: 'inline-flex', alignItems: 'stretch', borderRadius: 6,
      border: `1px solid ${theme.rule}`, overflow: 'hidden', background: theme.panel }}>
      <button onClick={() => onChange(Math.max(min, +(value - step).toFixed(4)))}
        style={{ width: 24, border: 0, background: theme.subtleBg, color: theme.ink2,
          cursor: 'pointer', fontSize: 14, fontFamily: 'inherit' }}>−</button>
      <div style={{ minWidth: 56, textAlign: 'center', fontFamily: '"Geist Mono", monospace',
        fontSize: 12, padding: '5px 0', color: theme.ink, fontWeight: 500 }}>{format(value)}</div>
      <button onClick={() => onChange(Math.min(max, +(value + step).toFixed(4)))}
        style={{ width: 24, border: 0, background: theme.subtleBg, color: theme.ink2,
          cursor: 'pointer', fontSize: 14, fontFamily: 'inherit' }}>+</button>
    </div>
  );
}

// ─── Events panel ─────────────────────────────────────────────────────────
function EventsPanel({ theme, P, state, onAdd, onRemove }) {
  return (
    <Panel theme={theme} P={P} title="Life events"
      action={
        <button onClick={onAdd} style={{
          background: theme.accentSoft, color: theme.accent, border: 0,
          borderRadius: 6, padding: '5px 11px', fontSize: 11.5, fontWeight: 600,
          cursor: 'pointer', fontFamily: 'inherit',
        }}>+ Add event</button>
      }>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
        {state.events.map((e) => (
          <div key={e.id} style={{
            display: 'flex', alignItems: 'center', gap: 10, padding: 10,
            background: theme.subtleBg, borderRadius: 7, border: `1px solid ${theme.ruleSoft}`,
          }}>
            <div style={{
              width: 30, height: 30, borderRadius: 7,
              background: e.kind === 'retire' ? '#fff3e0' : theme.accentSoft,
              color: e.kind === 'retire' ? '#d68a1f' : theme.accent,
              display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 14, fontWeight: 600,
            }}>{eventIcon(e.kind)}</div>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: theme.ink }}>{e.label}</div>
              <div style={{ fontFamily: '"Geist Mono", monospace', fontSize: 10.5,
                color: theme.ink3, marginTop: 2 }}>
                {2026 + e.year} · age {e.age} · year {e.year}
              </div>
            </div>
            <button onClick={() => onRemove(e.id)} style={{
              background: 'transparent', border: 0, color: theme.ink3,
              cursor: 'pointer', fontSize: 16, padding: 4, fontFamily: 'inherit',
              borderRadius: 4,
            }}>×</button>
          </div>
        ))}
        <button onClick={onAdd} style={{
          padding: 10, borderRadius: 7, border: `1px dashed ${theme.rule}`,
          background: 'transparent', color: theme.ink3, fontSize: 11.5, cursor: 'pointer',
          fontFamily: 'inherit', textAlign: 'center',
        }}>+ Buy a home · Have a child · Sabbatical · Windfall</button>
      </div>
    </Panel>
  );
}

function eventIcon(kind) {
  return { retire: '◐', job: '↑', home: '⌂', car: '⚯', child: '✶', sabbatical: '✦', windfall: '✱' }[kind] || '·';
}

// ─── Breakdown panel ──────────────────────────────────────────────────────
function BreakdownPanel({ theme, P, sel, selected, horizon, setSelected, fmtN }) {
  const segments = [
    { label: 'Retirement', value: sel.retirement, color: theme.accent },
    { label: 'Brokerage', value: sel.brokerage, color: shade(theme.accent, 0.25) },
    { label: 'Home equity', value: Math.max(0, sel.home - sel.mortgage), color: shade(theme.accent, 0.5) },
    { label: 'Cash', value: sel.cash, color: shade(theme.accent, 0.75) },
  ];
  const total = segments.reduce((a, b) => a + b.value, 0);
  const debts = sel.mortgage + sel.autoLoan + sel.studentLoan;
  return (
    <Panel theme={theme} P={P} title={`Year breakdown · ${2026 + sel.year}`}
      action={
        <div style={{ display: 'flex', gap: 4 }}>
          <button onClick={() => setSelected(Math.max(0, selected - 1))}
            style={navBtn(theme)}>‹</button>
          <button onClick={() => setSelected(Math.min(horizon, selected + 1))}
            style={navBtn(theme)}>›</button>
        </div>
      }>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline' }}>
        <div>
          <div style={{ fontSize: 11, color: theme.ink3 }}>Net worth · age {sel.age}</div>
          <div style={{ fontSize: 26, fontWeight: 600, letterSpacing: -0.4, marginTop: 2 }}>
            {fmtN(sel.median)}
          </div>
          <div style={{ fontSize: 11, color: theme.ink3, marginTop: 4, fontFamily: '"Geist Mono", monospace' }}>
            band {fmt.usd(sel.p10)} – {fmt.usd(sel.p90)}
          </div>
        </div>
        <span style={{
          fontFamily: '"Geist Mono", monospace', fontSize: 10, padding: '4px 9px',
          borderRadius: 99,
          background: sel.retired ? '#fff3e0' : sel.paused ? '#fef9c3' : '#e8f6ee',
          color: sel.retired ? '#d99000' : sel.paused ? '#a16207' : '#0e9f6e',
          fontWeight: 600, letterSpacing: 0.3,
        }}>
          {sel.retired ? '◐ RETIRED' : sel.paused ? '✦ SABBATICAL' : '● WORKING'}
        </span>
      </div>
      <div style={{ height: 12 }} />
      <div style={{ display: 'flex', height: 10, borderRadius: 99, overflow: 'hidden',
        background: theme.ruleSoft }}>
        {segments.map((s) => (
          <div key={s.label} title={`${s.label}: ${fmt.usd(s.value)}`}
            style={{ width: `${(s.value / total) * 100}%`, background: s.color,
              transition: 'width .3s' }} />
        ))}
      </div>
      <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 6 }}>
        {segments.map((s) => (
          <div key={s.label} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
            <span style={{ width: 8, height: 8, background: s.color, borderRadius: 2 }} />
            <span style={{ flex: 1, color: theme.ink2 }}>{s.label}</span>
            <span style={{ fontFamily: '"Geist Mono", monospace', fontSize: 11.5,
              color: theme.ink, fontWeight: 500 }}>{fmtN(s.value)}</span>
          </div>
        ))}
        <div style={{ height: 1, background: theme.ruleSoft, margin: '4px 0' }} />
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12 }}>
          <span style={{ width: 8, height: 8, background: '#e0473a', borderRadius: 2 }} />
          <span style={{ flex: 1, color: theme.ink2 }}>Liabilities</span>
          <span style={{ fontFamily: '"Geist Mono", monospace', fontSize: 11.5,
            color: '#e0473a', fontWeight: 500 }}>−{fmtN(debts)}</span>
        </div>
      </div>
    </Panel>
  );
}

const navBtn = (theme) => ({
  width: 24, height: 24, border: `1px solid ${theme.rule}`, borderRadius: 5,
  background: theme.panel, cursor: 'pointer', color: theme.ink2, fontFamily: 'inherit',
  fontSize: 14, lineHeight: 1,
});

// ─── Ledger panel ─────────────────────────────────────────────────────────
function LedgerPanel({ theme, P, cur, end, horizon, fmtN }) {
  const rows = [
    { k: 'Retirement (401k + IRA)', a: cur.retirement, b: end.retirement, kind: 'asset' },
    { k: 'Brokerage', a: cur.brokerage, b: end.brokerage, kind: 'asset' },
    { k: 'Primary residence', a: cur.home, b: end.home, kind: 'asset' },
    { k: 'Cash + HYSA', a: cur.cash, b: end.cash, kind: 'asset' },
    { k: 'Mortgage', a: -cur.mortgage, b: -end.mortgage, kind: 'debt' },
    { k: 'Auto loan', a: -cur.autoLoan, b: -end.autoLoan, kind: 'debt' },
    { k: 'Student loans', a: -cur.studentLoan, b: -end.studentLoan, kind: 'debt' },
  ];
  return (
    <Panel theme={theme} P={P} title="Assets & Debts" action={
      <span style={{ fontSize: 11, color: theme.ink3 }}>{rows.length} accounts · Monarch</span>
    }>
      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: 12.5 }}>
        <thead>
          <tr style={{ color: theme.ink3, fontSize: 10.5, letterSpacing: 0.6, fontWeight: 600 }}>
            <th style={{ textAlign: 'left', padding: '6px 0', width: '34%' }}>ACCOUNT</th>
            <th style={{ textAlign: 'left', padding: '6px 0', width: '14%' }}>TYPE</th>
            <th style={{ textAlign: 'right', padding: '6px 0' }}>TODAY</th>
            <th style={{ textAlign: 'right', padding: '6px 0' }}>YEAR {horizon}</th>
            <th style={{ textAlign: 'right', padding: '6px 0' }}>Δ</th>
            <th style={{ textAlign: 'right', padding: '6px 0', width: '22%' }}>TRAJECTORY</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((r) => (
            <tr key={r.k} style={{ borderTop: `1px solid ${theme.ruleSoft}` }}>
              <td style={{ padding: '9px 0', color: theme.ink }}>{r.k}</td>
              <td style={{ padding: '9px 0', color: theme.ink3, fontSize: 11.5 }}>
                <span style={{ padding: '2px 7px', borderRadius: 99,
                  background: r.kind === 'asset' ? '#e8f6ee' : '#fde8e6',
                  color: r.kind === 'asset' ? '#0e9f6e' : '#e0473a',
                  fontSize: 10.5, fontWeight: 600, letterSpacing: 0.3 }}>
                  {r.kind === 'asset' ? 'ASSET' : 'DEBT'}
                </span>
              </td>
              <td style={{ padding: '9px 0', textAlign: 'right', fontFamily: '"Geist Mono", monospace',
                color: theme.ink2 }}>{fmtN(r.a)}</td>
              <td style={{ padding: '9px 0', textAlign: 'right', fontFamily: '"Geist Mono", monospace',
                color: theme.ink, fontWeight: 500 }}>{fmtN(r.b)}</td>
              <td style={{ padding: '9px 0', textAlign: 'right', fontFamily: '"Geist Mono", monospace',
                color: r.b - r.a >= 0 ? '#0e9f6e' : '#e0473a', fontWeight: 600 }}>
                {r.b - r.a >= 0 ? '+' : '−'}{fmt.usd(Math.abs(r.b - r.a))}
              </td>
              <td style={{ padding: '9px 0', textAlign: 'right' }}>
                <MiniTrend theme={theme} from={r.a} to={r.b} kind={r.kind} />
              </td>
            </tr>
          ))}
          <tr style={{ borderTop: `2px solid ${theme.accent}` }}>
            <td style={{ padding: '11px 0', fontWeight: 700 }}>NET WORTH</td>
            <td />
            <td style={{ padding: '11px 0', textAlign: 'right', fontFamily: '"Geist Mono", monospace',
              fontWeight: 700, color: theme.accent }}>{fmtN(cur.median)}</td>
            <td style={{ padding: '11px 0', textAlign: 'right', fontFamily: '"Geist Mono", monospace',
              fontWeight: 700, color: theme.accent }}>{fmtN(end.median)}</td>
            <td style={{ padding: '11px 0', textAlign: 'right', fontFamily: '"Geist Mono", monospace',
              fontWeight: 700, color: '#0e9f6e' }}>
              +{fmt.usd(end.median - cur.median)}
            </td>
            <td />
          </tr>
        </tbody>
      </table>
    </Panel>
  );
}

function MiniTrend({ theme, from, to, kind }) {
  const w = 88, h = 22;
  // Generate a soft curve from->to
  const steps = 20;
  const pts = Array.from({ length: steps + 1 }, (_, i) => {
    const t = i / steps;
    const v = from + (to - from) * (t * t * (3 - 2 * t));
    return v;
  });
  const min = Math.min(...pts), max = Math.max(...pts);
  const rng = max - min || 1;
  const xs = (i) => (i / steps) * w;
  const ys = (v) => h - 2 - ((v - min) / rng) * (h - 4);
  const d = pts.map((v, i) => `${i === 0 ? 'M' : 'L'} ${xs(i).toFixed(1)} ${ys(v).toFixed(1)}`).join(' ');
  const color = to - from >= 0 ? '#0e9f6e' : '#e0473a';
  return (
    <svg width={w} height={h} style={{ display: 'inline-block', verticalAlign: 'middle' }}>
      <path d={d} fill="none" stroke={color} strokeWidth="1.5" strokeLinejoin="round" />
    </svg>
  );
}

// ─── Add Event Modal ──────────────────────────────────────────────────────
function AddEventModal({ theme, horizon, onClose, onAdd }) {
  const [template, setTemplate] = useState(EVENT_TEMPLATES[0]);
  const [year, setYear] = useState(5);
  const [label, setLabel] = useState('');
  const [salaryDelta, setSalaryDelta] = useState(30000);
  const [cashCost, setCashCost] = useState(150000);
  const [debtAdded, setDebtAdded] = useState(600000);

  const submit = () => {
    const extras = {};
    if (template.kind === 'job') extras.salaryDelta = salaryDelta;
    if (template.kind === 'home' || template.kind === 'car') {
      extras.cashCost = cashCost;
      extras.debtAdded = debtAdded;
    }
    if (template.kind === 'windfall') extras.cashCost = -Math.abs(cashCost);
    onAdd(template, year, label || template.label, extras);
  };

  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(15,23,42,0.45)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100,
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: 560, background: theme.panel, borderRadius: 12, border: `1px solid ${theme.rule}`,
        padding: 24, color: theme.ink, boxShadow: '0 24px 80px rgba(15,23,42,0.25)',
      }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 16 }}>
          <div>
            <div style={{ fontSize: 18, fontWeight: 600 }}>Add life event</div>
            <div style={{ fontSize: 12, color: theme.ink3, marginTop: 4 }}>
              Pick a type and when it happens — we'll re-run the projection.
            </div>
          </div>
          <button onClick={onClose} style={{
            background: 'transparent', border: 0, color: theme.ink3,
            cursor: 'pointer', fontSize: 22, fontFamily: 'inherit', padding: 0,
          }}>×</button>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8, marginBottom: 18 }}>
          {EVENT_TEMPLATES.map((t) => (
            <button key={t.kind} onClick={() => setTemplate(t)} style={{
              padding: '12px 10px', textAlign: 'left', cursor: 'pointer',
              background: template.kind === t.kind ? theme.accentSoft : theme.subtleBg,
              border: `1px solid ${template.kind === t.kind ? theme.accent : theme.rule}`,
              borderRadius: 8, fontFamily: 'inherit', color: theme.ink,
            }}>
              <div style={{ fontSize: 18, color: theme.accent }}>{t.icon}</div>
              <div style={{ fontSize: 11.5, fontWeight: 500, marginTop: 4, lineHeight: 1.3 }}>{t.label}</div>
            </button>
          ))}
        </div>
        <Field theme={theme} label="When">
          <input type="range" min={1} max={horizon} value={year}
            onChange={(e) => setYear(parseInt(e.target.value))}
            style={{ flex: 1, accentColor: theme.accent }} />
          <div style={{ width: 96, textAlign: 'right', fontFamily: '"Geist Mono", monospace', fontSize: 12 }}>
            year {year} · age {PROFILE.age + year}
          </div>
        </Field>
        <Field theme={theme} label="Label">
          <input value={label} onChange={(e) => setLabel(e.target.value)}
            placeholder={template.label}
            style={inputStyle(theme)} />
        </Field>
        {template.kind === 'job' && (
          <Field theme={theme} label="Salary change">
            <input type="number" value={salaryDelta} onChange={(e) => setSalaryDelta(parseInt(e.target.value) || 0)}
              style={inputStyle(theme)} />
            <span style={{ fontSize: 11, color: theme.ink3 }}>$/yr (negative = pay cut)</span>
          </Field>
        )}
        {(template.kind === 'home' || template.kind === 'car') && (
          <>
            <Field theme={theme} label="Down payment / cash">
              <input type="number" value={cashCost} onChange={(e) => setCashCost(parseInt(e.target.value) || 0)}
                style={inputStyle(theme)} />
            </Field>
            <Field theme={theme} label="Loan added">
              <input type="number" value={debtAdded} onChange={(e) => setDebtAdded(parseInt(e.target.value) || 0)}
                style={inputStyle(theme)} />
            </Field>
          </>
        )}
        {template.kind === 'windfall' && (
          <Field theme={theme} label="Amount received">
            <input type="number" value={cashCost} onChange={(e) => setCashCost(parseInt(e.target.value) || 0)}
              style={inputStyle(theme)} />
          </Field>
        )}
        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 18 }}>
          <button onClick={onClose} style={{
            padding: '9px 16px', borderRadius: 7, border: `1px solid ${theme.rule}`,
            background: theme.panel, color: theme.ink2, fontFamily: 'inherit', fontSize: 13, cursor: 'pointer',
          }}>Cancel</button>
          <button onClick={submit} style={{
            padding: '9px 16px', borderRadius: 7, border: 0,
            background: theme.accent, color: '#fff', fontFamily: 'inherit', fontSize: 13,
            fontWeight: 600, cursor: 'pointer',
          }}>Add to timeline</button>
        </div>
      </div>
    </div>
  );
}

function Field({ theme, label, children }) {
  return (
    <div style={{ marginBottom: 12 }}>
      <div style={{ fontSize: 11, color: theme.ink3, fontWeight: 600,
        letterSpacing: 0.5, marginBottom: 6 }}>{label.toUpperCase()}</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>{children}</div>
    </div>
  );
}

const inputStyle = (theme) => ({
  flex: 1, padding: '8px 11px', border: `1px solid ${theme.rule}`,
  borderRadius: 6, background: theme.panel, color: theme.ink,
  fontFamily: 'inherit', fontSize: 13, outline: 'none',
});

// ─── Monarch Panel ────────────────────────────────────────────────────────
function MonarchPanel({ theme, onClose }) {
  const accounts = [
    { name: 'Fidelity 401(k)',     inst: 'Fidelity',         bal:  244900, kind: 'asset' },
    { name: 'Fidelity Brokerage',  inst: 'Fidelity',         bal:  128400, kind: 'asset' },
    { name: 'Chase Total Checking',inst: 'Chase',            bal:   12800, kind: 'asset' },
    { name: 'Marcus HYSA',         inst: 'Goldman Sachs',    bal:   30000, kind: 'asset' },
    { name: 'Redfin Now Mortgage', inst: 'Rocket Mortgage',  bal: -535200, kind: 'debt' },
    { name: 'Honda Financial',     inst: 'Honda Financial',  bal:  -13900, kind: 'debt' },
    { name: 'SoFi Student Loan',   inst: 'SoFi',             bal:  -18400, kind: 'debt' },
  ];
  return (
    <div onClick={onClose} style={{
      position: 'fixed', inset: 0, background: 'rgba(15,23,42,0.45)',
      display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 100,
    }}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: 560, maxHeight: '80vh', background: theme.panel, borderRadius: 12,
        border: `1px solid ${theme.rule}`, padding: 24, color: theme.ink,
        boxShadow: '0 24px 80px rgba(15,23,42,0.25)', overflow: 'auto',
      }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',
          marginBottom: 18 }}>
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              <span style={{ width: 8, height: 8, borderRadius: 99, background: '#0e9f6e',
                boxShadow: '0 0 0 4px #0e9f6e22' }} />
              <div style={{ fontSize: 18, fontWeight: 600 }}>Connected to Monarch</div>
            </div>
            <div style={{ fontSize: 12, color: theme.ink3, marginTop: 4 }}>
              Last sync 4 minutes ago · {accounts.length} accounts
            </div>
          </div>
          <button onClick={onClose} style={{
            background: 'transparent', border: 0, color: theme.ink3,
            cursor: 'pointer', fontSize: 22, fontFamily: 'inherit', padding: 0,
          }}>×</button>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
          {accounts.map((a) => (
            <div key={a.name} style={{
              display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px',
              background: theme.subtleBg, borderRadius: 7, border: `1px solid ${theme.ruleSoft}`,
            }}>
              <div style={{ width: 30, height: 30, borderRadius: 7, background: theme.panel,
                border: `1px solid ${theme.rule}`, display: 'flex', alignItems: 'center',
                justifyContent: 'center', color: theme.ink2, fontWeight: 700, fontSize: 12 }}>
                {a.inst[0]}
              </div>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 13, fontWeight: 500 }}>{a.name}</div>
                <div style={{ fontSize: 11, color: theme.ink3, marginTop: 1 }}>{a.inst}</div>
              </div>
              <div style={{ fontFamily: '"Geist Mono", monospace', fontSize: 13,
                color: a.kind === 'asset' ? theme.ink : '#e0473a', fontWeight: 600 }}>
                {a.bal < 0 ? '−' : ''}${Math.abs(a.bal).toLocaleString('en-US')}
              </div>
            </div>
          ))}
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center',
          marginTop: 16, paddingTop: 14, borderTop: `1px solid ${theme.rule}` }}>
          <div style={{ fontSize: 12, color: theme.ink3 }}>
            We re-sync every hour. Trigger now?
          </div>
          <button style={{
            padding: '7px 14px', borderRadius: 7, border: 0,
            background: theme.accent, color: '#fff', fontFamily: 'inherit', fontSize: 12,
            fontWeight: 600, cursor: 'pointer',
          }}>↻ Re-sync now</button>
        </div>
      </div>
    </div>
  );
}

// ─── Panel shell ──────────────────────────────────────────────────────────
function Panel({ theme, P, title, action, children }) {
  return (
    <div style={{ background: theme.panel, borderRadius: 10, border: `1px solid ${theme.rule}`,
      display: 'flex', flexDirection: 'column' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: P.headPad, borderBottom: `1px solid ${theme.rule}` }}>
        <span style={{ fontSize: 13, fontWeight: 600 }}>{title}</span>
        {action}
      </div>
      <div style={{ padding: P.cardPad, flex: 1 }}>{children}</div>
    </div>
  );
}

// ─── Color helpers ────────────────────────────────────────────────────────
function shade(hex, mix) {
  // Lighten a hex color by mixing with white. mix=0 → original, 1 → white.
  const h = hex.replace('#', '');
  const r = parseInt(h.slice(0, 2), 16), g = parseInt(h.slice(2, 4), 16), b = parseInt(h.slice(4, 6), 16);
  const f = (c) => Math.round(c + (255 - c) * mix);
  return `rgb(${f(r)}, ${f(g)}, ${f(b)})`;
}

window.Foresight = Foresight;
