// ─────────────────────────────────────────────────────────────────────────
// Meridian — Dashboard / Overview (wired to live API)
// KPIs + equity curve + Hermes headline signals + allocation donut with
// concentration warnings + catalyst runway + movers + latest research.
//
// Robustness contract (all data is live; fields can be missing/degraded):
//   • One consistent portfolio total, anchored to account.total_value and
//     falling back to Σ(position value) + cash when the balance feed is 0.
//   • Allocation % is ALWAYS recomputed against that single total, so the
//     slices (positions + cash) reconcile to the headline portfolio value.
//   • Every division is guarded; missing numbers render a clean "—" instead of
//     NaN / undefined / a misleading "0,00 €".
//   • Positions are sorted by value (desc) consistently across sections.
// ─────────────────────────────────────────────────────────────────────────

// Local guards — fmt.* coerce non-finite → 0, which is wrong for genuinely
// absent values. These keep "missing" visually distinct ("—").
const _isNum = (v) => typeof v === "number" && isFinite(v);
const _num0 = (v) => (_isNum(v) ? v : 0);
const _ratio = (a, b) => (_isNum(a) && _isNum(b) && b !== 0 ? a / b : null);
const dEur  = (v) => (_isNum(v) ? fmt.eur(v) : "—");
const dEur0 = (v) => (_isNum(v) ? fmt.eur0(v) : "—");
const dSEur = (v) => (_isNum(v) ? fmt.sEur(v) : "—");
const dPct  = (v) => (_isNum(v) ? fmt.pct(v) : "—");
const dAaqs = (v) => (_isNum(v) ? (Number.isInteger(v) ? String(v) : fmt.num(v)) : "—");

function KpiCard({ label, value, sub, tone, loading }) {
  return (
    <div className="card">
      <div className="kpi-label">{label}</div>
      {loading
        ? <div style={{ marginTop: 9 }}><Skel w="60%" h={26} /></div>
        : <div className="kpi-value" style={{ marginTop: 7, color: tone === "pos" ? "var(--pos-fg)" : tone === "neg" ? "var(--neg-fg)" : "var(--fg-1)" }}>{value == null ? "—" : value}</div>}
      {sub && !loading && <div style={{ marginTop: 5 }}>{sub}</div>}
    </div>
  );
}

const EQ_RANGES = [["ytd", "YTD"], ["1y", "1 J"], ["max", "Max"]];

function Dashboard({ theme, onNavigate, onOpenStock, store }) {
  const [range, setRange] = useState("ytd");
  const [allocBy, setAllocBy] = useState("position");

  const data = store.data;
  const loading = store.loading;
  const s = data && data.summary;

  const equity = useAsync(() => API.equityCurve(range === "max" ? undefined : range).then((r) => (r.points || []).map((p) => ({ t: p.date, v: p.value }))), [range]);
  const catalysts = useAsync(() => API.calendar(10).catch(() => null), []);
  const latestResearch = useAsync(async () => {
    const [pp, cc] = await Promise.all([API.papers(8).catch(() => ({ papers: [] })), API.calls(8).catch(() => ({ calls: [] }))]);
    const items = [
      ...(pp.papers || []).map((p) => API.map.mapResearch(p, "analysis", data && data.recBySym)),
      ...(cc.calls || []).map((c) => API.map.mapResearch(c, "call", data && data.recBySym)),
    ];
    return items.sort((a, b) => new Date(b.date) - new Date(a.date)).slice(0, 4);
  }, [!!data]);

  // Positions, sorted once by value (desc) for consistent ordering everywhere.
  const positionsSorted = useMemo(
    () => (data ? [...data.positions].sort((a, b) => _num0(b.value) - _num0(a.value) || String(a.name || "").localeCompare(String(b.name || ""))) : []),
    [data],
  );

  // One reconciled set of totals. Anchor to the account figures; fall back to
  // position-derived sums so the headline value never disagrees with the rows.
  const sums = useMemo(() => {
    const equityFromPos = positionsSorted.reduce((acc, p) => acc + _num0(p.value), 0);
    const cash = s && _isNum(s.cash) ? s.cash : 0;
    const acctTotal = s && _isNum(s.totalValue) ? s.totalValue : 0;
    const acctEquity = s && _isNum(s.equityValue) ? s.equityValue : 0;
    const equity = acctEquity > 0 ? acctEquity : equityFromPos;
    const total = acctTotal > 0 ? acctTotal : equity + cash;
    return { equity, equityFromPos, cash, total };
  }, [positionsSorted, s]);

  const allocSlices = useMemo(() => {
    if (!data) return [];
    if (allocBy === "sector") {
      return (data.sectors || []).map((x, i) => {
        const r = _ratio(x.value, sums.total);
        return { label: x.name, value: _num0(x.value), pct: r != null ? r * 100 : _num0(x.pct), color: CHART_PALETTE[i % CHART_PALETTE.length] };
      });
    }
    // Recompute % against the single reconciled total so slices + cash → 100 %.
    const slices = positionsSorted.map((p, i) => ({
      label: p.name, value: _num0(p.value),
      pct: (_ratio(p.value, sums.total) || 0) * 100,
      color: CHART_PALETTE[i % CHART_PALETTE.length],
    }));
    if (sums.cash > 0) slices.push({ label: "Cash", value: sums.cash, pct: (_ratio(sums.cash, sums.total) || 0) * 100, color: "var(--border-2)" });
    return slices;
  }, [allocBy, data, positionsSorted, sums]);

  // Concentration warnings recomputed from the SAME reconciled %, so they always
  // agree with the allocation shown above (and don't depend on backend `alloc`
  // unit assumptions).
  const concentration = useMemo(() => {
    if (!data) return [];
    const out = [];
    for (const x of data.sectors || []) {
      const p = (_ratio(x.value, sums.total) || 0) * 100;
      if (p > 30) out.push({ label: x.name, pct: p, note: `${x.name} exceeds your 30 % single-sector target.` });
    }
    for (const p of positionsSorted) {
      const a = (_ratio(p.value, sums.total) || 0) * 100;
      if (a > 10) out.push({ label: p.name, pct: a, note: `Single position above 10 %${_isNum(p.fvGap) && p.fvGap < -8 ? " — and trading above fair value" : ""}. Consider trimming.` });
    }
    return out.slice(0, 4);
  }, [data, positionsSorted, sums]);

  // Hermes headline — one buy, one sell, one other, drawn from live signals
  const headline = useMemo(() => {
    if (!data) return [];
    const withWhy = positionsSorted.filter((p) => p.why);
    const buys = withWhy.filter((p) => p.signal === "buy").sort((a, b) => _num0(b.fvGap) - _num0(a.fvGap));
    const sells = withWhy.filter((p) => p.signal === "sell").sort((a, b) => _num0(a.fvGap) - _num0(b.fvGap));
    const holds = withWhy.filter((p) => p.signal === "hold");
    const picks = [];
    if (buys[0]) picks.push(buys[0]);
    if (sells[0]) picks.push(sells[0]);
    const rest = [...buys.slice(1), ...holds].filter((p) => !picks.includes(p));
    if (rest[0]) picks.push(rest[0]);
    return picks.slice(0, 3);
  }, [data, positionsSorted]);

  if (store.error) {
    return <div className="rise"><PageHeader title="Overview" /><Card><EmptyState icon="cloud-off" title="Couldn't load your portfolio" body={store.error.message} action={<button className="btn btn-sm" onClick={store.reload}><Icon name="refresh-cw" size={13} /> Retry</button>} /></Card></div>;
  }

  // Best & worst by unrealised P/L %.
  const movers = useMemo(
    () => (data ? [...positionsSorted].sort((a, b) => _num0(b.plPct) - _num0(a.plPct)) : []),
    [data, positionsSorted],
  );

  const cashPctOfTotal = (_ratio(sums.cash, sums.total) || 0) * 100;

  return (
    <div className="rise">
      <PageHeader
        title="Overview"
        subtitle={s ? `${fmt.date((s.asOf) || new Date())} · everything in EUR` : "Loading your cockpit…"}
        actions={<>
          <button className="btn btn-sm" onClick={() => onNavigate("recommendations")}><Icon name="git-fork" size={13} /> All signals</button>
          <button className="btn btn-sm btn-primary" onClick={() => onNavigate("portfolio")}><Icon name="wallet" size={13} style={{ color: "#fff" }} /> Portfolio</button>
        </>}
      />

      {/* KPI strip */}
      <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(190px, 1fr))", gap: 16, marginBottom: 16 }}>
        <KpiCard loading={loading} label="Portfolio value" value={data ? dEur(sums.total) : "—"}
          sub={data && <span className="muted" style={{ fontSize: 12.5 }}>{positionsSorted.length} holdings</span>} />
        <KpiCard loading={loading} label="Total return" value={s ? dSEur(s.unrealized) : "—"} tone={s && _num0(s.unrealized) >= 0 ? "pos" : "neg"}
          sub={s && <Row gap={8}><Delta value={_num0(s.unrealizedPct)} kind="pct" size={12.5} showArrow={false} /><span className="muted" style={{ fontSize: 12.5 }}>unrealised</span></Row>} />
        <KpiCard loading={loading} label="Realized YTD" value={s ? dSEur(s.realizedYtd) : "—"} tone={s && _num0(s.realizedYtd) >= 0 ? "pos" : "neg"}
          sub={<span className="muted" style={{ fontSize: 12.5 }}>booked gains</span>} />
        <KpiCard loading={loading} label="Cash" value={data ? dEur(sums.cash) : "—"}
          sub={data && <span className="muted" style={{ fontSize: 12.5 }}>{dPct(cashPctOfTotal)} of portfolio</span>} />
      </div>

      {/* main 2-column grid */}
      <div style={{ display: "grid", gridTemplateColumns: "minmax(0, 1.62fr) minmax(0, 1fr)", gap: 16, alignItems: "start" }} className="dash-grid">
        {/* LEFT */}
        <Col gap={16} style={{ minWidth: 0 }}>
          {/* Equity curve hero */}
          <Card>
            <Row justify="space-between" align="flex-start" wrap style={{ gap: 12 }}>
              <div>
                <div className="kpi-label">Portfolio value</div>
                <Row gap={12} align="baseline" style={{ marginTop: 6 }}>
                  <span style={{ fontSize: 32, fontWeight: 600, letterSpacing: "-0.025em", fontVariantNumeric: "tabular-nums" }}>{data ? dEur(sums.total) : "—"}</span>
                  {s && <Delta value={_num0(s.unrealizedPct)} kind="pct" size={14} />}
                </Row>
                {s && <div className="muted" style={{ fontSize: 12.5, marginTop: 3 }}>{dSEur(s.unrealized)} unrealised · {dSEur(s.realizedYtd)} realised YTD</div>}
              </div>
              <div className="segmented">
                {EQ_RANGES.map(([r, l]) => <button key={r} className={range === r ? "active" : ""} onClick={() => setRange(r)}>{l}</button>)}
              </div>
            </Row>
            <div style={{ marginTop: 14, minHeight: 250 }}>
              {equity.loading ? <div style={{ height: 250, display: "grid", placeItems: "center" }}><Icon name="loader" size={18} className="spin" style={{ color: "var(--fg-3)" }} /></div>
                : (equity.data && equity.data.length > 1)
                  ? <LineChart data={equity.data} theme={theme} height={250} color="var(--sage-600)" />
                  : <EmptyState icon="line-chart" title="No equity history yet" body="The curve fills in as daily snapshots accumulate." />}
            </div>
          </Card>

          {/* Hermes signals */}
          <Card>
            <SectionTitle icon="sparkles" action={<button className="btn btn-sm btn-ghost" onClick={() => onNavigate("hermes")}>Ask Hermes <Icon name="arrow-right" size={12} /></button>}>Hermes says</SectionTitle>
            {loading ? <Row gap={12}><Skel h={120} /><Skel h={120} /><Skel h={120} /></Row>
              : headline.length === 0
                ? <EmptyState icon="sparkles" title="No signals yet" body="Run a portfolio refresh and the recommendations engine to populate calls." />
                : <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(210px, 1fr))", gap: 12 }}>
                    {headline.map((sig) => (
                      <button key={sig.t || sig.isin || sig.name} onClick={() => sig.t && onOpenStock(sig.t)} className="card-hover" style={{ textAlign: "left", background: "var(--bg-sunken)", border: "1px solid var(--border-1)", borderRadius: "var(--radius-3)", padding: 14, cursor: sig.t ? "pointer" : "default" }}>
                        <Row justify="space-between" align="flex-start">
                          <Row gap={10} style={{ minWidth: 0 }}>
                            <StockLogo isin={sig.isin} ticker={sig.ticker || sig.t} name={sig.name} size={30} />
                            <div style={{ minWidth: 0 }}>
                              <div style={{ fontWeight: 600, fontSize: 14.5, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{sig.name || sig.t || "—"}</div>
                              <div className="mono muted" style={{ fontSize: 11, marginTop: 1 }}>{sig.t || "—"}</div>
                            </div>
                          </Row>
                          <SignalBadge signal={sig.signal} />
                        </Row>
                        <Row gap={6} wrap style={{ marginTop: 10, fontSize: 11.5 }}>
                          {_isNum(sig.aaqs) && <span className="chip chip-tinted">AAQS {dAaqs(sig.aaqs)}</span>}
                          {_isNum(sig.fvGap) && <span className="chip chip-tinted" style={{ color: sig.fvGap >= 0 ? "var(--pos-fg)" : "var(--neg-fg)" }}>{fmt.sPct(sig.fvGap)} vs fair</span>}
                        </Row>
                        <p className="muted" style={{ fontSize: 12.5, lineHeight: 1.5, margin: "10px 0 0", display: "-webkit-box", WebkitLineClamp: 3, WebkitBoxOrient: "vertical", overflow: "hidden" }}>{sig.why}</p>
                      </button>
                    ))}
                  </div>}
          </Card>

          {/* Catalyst runway */}
          <Card>
            <SectionTitle icon="calendar-clock" action={<span className="muted" style={{ fontSize: 12 }}>next 10 trading days</span>}>Catalyst runway</SectionTitle>
            <CatalystRunway data={catalysts.data} loading={catalysts.loading} onOpenStock={onOpenStock} positions={data && data.positions} />
          </Card>

          {/* Top movers */}
          <Card>
            <SectionTitle icon="trending-up" action={<button className="btn btn-sm btn-ghost" onClick={() => onNavigate("portfolio")}>Portfolio <Icon name="arrow-right" size={12} /></button>}>Best & worst (unrealised)</SectionTitle>
            {loading ? <Col gap={8}>{[0,1,2,3].map(i => <Skel key={i} h={36} />)}</Col>
              : movers.length === 0 ? <EmptyState icon="inbox" title="No positions yet" />
              : <Col gap={2}>
                  {[...movers.slice(0, 3), ...movers.slice(-2).reverse()].filter((v, i, a) => a.indexOf(v) === i).slice(0, 5).map((p, i) => (
                    <button key={p.t || p.isin || p.name || i} onClick={() => p.t && onOpenStock(p.t)} style={{ display: "flex", alignItems: "center", gap: 12, padding: "9px 8px", background: "transparent", border: 0, borderTop: i ? "1px solid var(--border-1)" : 0, width: "100%", textAlign: "left", cursor: p.t ? "pointer" : "default", borderRadius: 8 }} className="mover-row">
                      <StockLogo isin={p.isin} ticker={p.ticker || p.t} name={p.name} size={30} />
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontWeight: 600, fontSize: 14, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{p.name || p.t || "—"}</div>
                        <div className="mono muted" style={{ fontSize: 11 }}>{p.t || "—"}{_isNum(p.price) && p.price > 0 ? ` · ${fmt.eur(p.price)}` : ""}{_isNum(p.aaqs) ? ` · AAQS ${dAaqs(p.aaqs)}` : ""}</div>
                      </div>
                      <Sparkline points={[_num0(p.avg), _num0(p.price)]} up={_num0(p.plPct) >= 0} width={64} height={24} />
                      <div style={{ textAlign: "right", minWidth: 96 }}>
                        <Delta value={_num0(p.plPct)} kind="pct" size={13} style={{ justifyContent: "flex-end" }} />
                        <div className="mono" style={{ fontSize: 11.5, color: _num0(p.pl) >= 0 ? "var(--pos-fg)" : "var(--neg-fg)" }}>{dSEur(p.pl)}</div>
                      </div>
                    </button>
                  ))}
                </Col>}
          </Card>
        </Col>

        {/* RIGHT RAIL */}
        <Col gap={16} style={{ minWidth: 0 }}>
          {/* Allocation */}
          <Card>
            <Row justify="space-between" style={{ marginBottom: 8 }}>
              <h2 style={{ fontSize: "var(--fs-lg)", fontWeight: 600, letterSpacing: "var(--tracking-tight)", margin: 0 }}>Allocation</h2>
              <div className="segmented">
                <button className={allocBy === "position" ? "active" : ""} onClick={() => setAllocBy("position")}>Position</button>
                <button className={allocBy === "sector" ? "active" : ""} onClick={() => setAllocBy("sector")}>Sector</button>
              </div>
            </Row>
            {loading ? <div style={{ height: 208, display: "grid", placeItems: "center" }}><Icon name="loader" size={18} className="spin" style={{ color: "var(--fg-3)" }} /></div>
              : allocSlices.length === 0 ? <EmptyState icon="pie-chart" title="No holdings" />
              : <>
                <Donut slices={allocSlices} theme={theme} height={208} centerLabel="Invested" centerValue={dEur0(sums.equity)} />
                <Col gap={2} style={{ marginTop: 14 }}>
                  {allocSlices.slice(0, 6).map((sl, i) => (
                    <Row key={(sl.label || "") + i} justify="space-between" style={{ fontSize: 12.5, padding: "4px 0", gap: 10 }}>
                      <Row gap={8} style={{ minWidth: 0, flex: 1 }}><span style={{ width: 9, height: 9, borderRadius: 3, background: sl.color, flexShrink: 0 }} /><span style={{ color: "var(--fg-1)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", minWidth: 0 }}>{sl.label || "—"}</span></Row>
                      <span className="mono muted" style={{ whiteSpace: "nowrap", flexShrink: 0 }}>{dPct(sl.pct)}</span>
                    </Row>
                  ))}
                  {allocSlices.length > 6 && <div className="muted" style={{ fontSize: 11.5, paddingTop: 2 }}>+{allocSlices.length - 6} more</div>}
                </Col>
              </>}

            {/* Concentration warnings */}
            {concentration.length > 0 && (
              <Col gap={8} style={{ marginTop: 14, paddingTop: 14, borderTop: "1px solid var(--border-1)" }}>
                {concentration.map((c, i) => (
                  <Row key={i} gap={9} align="flex-start" style={{ background: "var(--mixed-soft)", border: "1px solid var(--mixed-border)", borderRadius: "var(--radius-3)", padding: "9px 11px" }}>
                    <Icon name="alert-triangle" size={14} style={{ color: "var(--mixed-accent)", marginTop: 1, flexShrink: 0 }} />
                    <div style={{ fontSize: 12, lineHeight: 1.45 }}>
                      <span style={{ fontWeight: 600 }}>{c.label} {dPct(c.pct)}</span>
                      <span className="muted"> — {c.note}</span>
                    </div>
                  </Row>
                ))}
              </Col>
            )}
          </Card>

          {/* Latest research */}
          <Card>
            <SectionTitle icon="library" action={<button className="btn btn-sm btn-ghost" onClick={() => onNavigate("research")}>All <Icon name="arrow-right" size={12} /></button>}>Latest research</SectionTitle>
            {latestResearch.loading ? <Col gap={8}>{[0,1,2].map(i => <Skel key={i} h={40} />)}</Col>
              : (!latestResearch.data || latestResearch.data.length === 0)
                ? <EmptyState icon="file-search" title="No research yet" body="Ingest analyses & calls, or upload a transcript in Research." />
                : <Col gap={2}>
                    {latestResearch.data.map((r, i) => (
                      <button key={r.id || i} onClick={() => onNavigate("research")} style={{ display: "flex", gap: 11, alignItems: "flex-start", padding: "11px 8px", background: "transparent", border: 0, borderTop: i ? "1px solid var(--border-1)" : 0, width: "100%", textAlign: "left", cursor: "pointer", borderRadius: 8 }} className="mover-row">
                        <div style={{ width: 30, height: 30, borderRadius: 8, background: "var(--bg-tint)", display: "grid", placeItems: "center", flexShrink: 0, color: "var(--fg-2)" }}>
                          <Icon name={r.type === "analysis" ? "file-text" : "headphones"} size={14} />
                        </div>
                        <div style={{ flex: 1, minWidth: 0 }}>
                          <div style={{ fontWeight: 600, fontSize: 13.5, lineHeight: 1.3 }}>{r.title}</div>
                          <Row gap={6} wrap style={{ marginTop: 3, fontSize: 11.5, color: "var(--fg-3)" }}>
                            <span>{r.source}</span><span>·</span><span>{fmt.date(r.date)}</span>
                            {r.inPortfolio && <span className="chip chip-tinted" style={{ fontSize: 10, padding: "0 7px", color: "var(--sage-700)" }}>In portfolio</span>}
                          </Row>
                        </div>
                      </button>
                    ))}
                  </Col>}
          </Card>
        </Col>
      </div>
    </div>
  );
}

/* ── Catalyst runway ──────────────────────────────────────────────────────── */
function CatalystRunway({ data, loading, onOpenStock, positions }) {
  const kindStyle = {
    earnings: { c: "var(--sage-600)", bg: "var(--signal-bg)", icon: "bar-chart-2" },
    dividend: { c: "var(--mixed-accent)", bg: "var(--mixed-bg)", icon: "coins" },
    macro:    { c: "var(--sky-accent)", bg: "var(--sky-bg)", icon: "landmark" },
  };
  const impactDot = { high: "var(--neg-fg)", medium: "var(--mixed-accent)", low: "var(--fg-3)" };

  if (loading) return <Row gap={12}>{[0,1,2,3].map(i => <Skel key={i} w={180} h={120} />)}</Row>;
  if (!data || !data.events || data.events.length === 0) {
    return <EmptyState icon="calendar-off" title="No upcoming catalysts" body="No earnings, ex-dividends or macro events in the next 10 trading days." />;
  }
  const tickerByIsin = {};
  (positions || []).forEach((p) => { if (p.isin) tickerByIsin[p.isin] = p.t; });
  const cats = data.events.map((ev) => API.map.mapCatalyst(ev, tickerByIsin));
  const byDate = {};
  cats.forEach((c) => { (byDate[c.date] = byDate[c.date] || []).push(c); });
  const days = Object.keys(byDate).sort();

  return (
    <div className="scroll-nice" style={{ overflowX: "auto", paddingBottom: 6, margin: "0 -4px" }}>
      <div style={{ display: "flex", gap: 12, padding: "0 4px", minWidth: "min-content" }}>
        {days.map((d) => {
          const dt = new Date(d);
          return (
            <div key={d} style={{ width: 188, flexShrink: 0 }}>
              <Row gap={6} align="baseline" style={{ paddingBottom: 8, borderBottom: "2px solid var(--border-1)", marginBottom: 10 }}>
                <span style={{ fontWeight: 600, fontSize: 13 }}>{fmt.wday(dt)}</span>
                <span className="mono muted" style={{ fontSize: 12 }}>{fmt.dateS(dt)}</span>
              </Row>
              <Col gap={8}>
                {byDate[d].map((c, i) => {
                  const ks = kindStyle[c.kind] || kindStyle.macro;
                  return (
                    <div key={i} onClick={() => c.ticker && onOpenStock(c.ticker)} style={{ background: "var(--bg-sunken)", border: "1px solid var(--border-1)", borderLeft: `3px solid ${ks.c}`, borderRadius: "var(--radius-2)", padding: "9px 11px", cursor: c.ticker ? "pointer" : "default" }} className={c.ticker ? "card-hover" : ""}>
                      <Row justify="space-between" align="center" style={{ marginBottom: 5 }}>
                        <Row gap={5}><Icon name={ks.icon} size={12} style={{ color: ks.c }} /><span className="caps" style={{ color: ks.c, fontSize: 10 }}>{c.tag}</span></Row>
                        <span className="dot" style={{ background: impactDot[c.impact], width: 6, height: 6 }} title={c.impact + " impact"} />
                      </Row>
                      <div style={{ fontSize: 13, fontWeight: 600, lineHeight: 1.3 }}>{c.title}</div>
                      {c.note && <div className="muted" style={{ fontSize: 11.5, lineHeight: 1.4, marginTop: 4 }}>{c.note}</div>}
                    </div>
                  );
                })}
              </Col>
            </div>
          );
        })}
      </div>
    </div>
  );
}

Object.assign(window, { Dashboard, KpiCard, CatalystRunway });
