// Explorer — investor-facing dashboard. Built on the V4 visual
// language so the rest of the app can inherit the same conventions.
//
// What's carried forward from V4:
//   · Linear Default dark bg (lch 1.82) — controlled at the page level
//   · Mint accent #2CFE91 used sparingly: brand mark, live pulses,
//     primary action, positive deltas
//   · Geist Sans + Geist Mono. Mono ONLY for labels, IDs, amounts, deltas
//   · Tabular numerals everywhere numbers appear
//   · Hairline borders (rgba 255,255,255 / 0.06–0.10) — never heavy boxes
//   · Section title pattern: tiny mono caps label → big sans headline
//   · Activity row pattern: ✓ amount · location · type · time (from the
//     V4 ticker), here vertically stacked as a feed
//   · USNetworkMap as ambient structural element, not decoration
//
// Explicit non-goals (the look we're moving AWAY from):
//   · Heavy rounded cards with solid gray fills
//   · Chunky pill "INTEREST" chips in saturated green
//   · 5 KPI tiles each in their own bordered box
//   · Red/green tinted delta numbers that vibrate against the bg
//
// The whole page is rendered on a SCROLLING canvas at 1440 wide, so
// when embedded in the design-canvas focus mode it behaves like a real
// product page (Cmd+scroll), not a fixed slide.

// ─────────────────────────── Tokens ───────────────────────────
// Sole source of truth: brand/tokens.css. We hold one-letter local
// aliases purely for inline-style readability — every value still
// flows through the design tokens.

const EX_BG    = "var(--surface)";
const EX_INK   = "var(--text)";
const EX_BODY  = "var(--text-muted)";
const EX_MUTE  = "var(--text-subtle)";
const EX_FAINT = "var(--text-faint)";
const EX_GHOST = "var(--text-ghost)";
const EX_LINE  = "var(--line)";
const EX_LINE_S = "var(--line-strong)";
const EX_PANEL = "var(--surface-overlay)";
const EX_DOWN  = "var(--down)";
const EX_UP    = "var(--up)";
const EX_USDC  = "var(--usdc)";
const EX_BRAND = "var(--brand)";       // mint always — fills, dots, comets
const EX_BRAND_INK = "var(--brand-ink)"; // dark-green text for use ON mint surfaces

// ─────────────────────────── Activity type palette ───────────────────────────
// Domain-specific mapping (loan events → accent hues). The design system
// ships hues (--chart-1..7 plus chart-up/down/warn/default);
// this is where the app decides what each one means. Other apps will
// map their own domain (KYC states, risk tiers, etc.) onto the same hues.
//
//   Outflows  (money leaves Onrail) — green family
//     LOAN_FUNDING → mint   · GUC_DRAW → teal · REHAB_DRAW → sage
//   Inflows   (money returns)       — blue family
//     PRINCIPAL    → sky    · INTEREST → sky (paler via opacity at use site)
//   Anomalies (state changes)       — warm + cool
//     LATE_FEE     → amber  · EXTENSION → lilac

const TYPE_META = {
  LOAN_FUNDING: { label: "FUNDED",      color: "var(--chart-2)",  group: "out" },
  GUC_DRAW:     { label: "GUC DRAW",    color: "var(--chart-6)",  group: "out" },
  REHAB_DRAW:   { label: "REHAB DRAW",  color: "var(--chart-7)",  group: "out" },
  PRINCIPAL:    { label: "PRINCIPAL",   color: "var(--chart-1)",  group: "in"  },
  INTEREST:     { label: "INTEREST",    color: "var(--chart-10)", group: "in"  },
  LATE_FEE:     { label: "LATE FEE",    color: "var(--chart-3)",  group: "signal"  },
  EXTENSION:    { label: "EXTENDED",    color: "var(--chart-5)",  group: "neutral" }
};

// Map markers need the resolved hex, not "var(...)". This lookup mirrors
// TYPE_META.color with bare token names so getComputedStyle can resolve.
const TYPE_VAR = {
  LOAN_FUNDING: "--chart-2",
  GUC_DRAW:     "--chart-6",
  REHAB_DRAW:   "--chart-7",
  PRINCIPAL:    "--chart-1",
  INTEREST:     "--chart-10",
  LATE_FEE:     "--chart-3",
  EXTENSION:    "--chart-5"
};

// City → [lat, lng]. Vault is plotted at the US geographic centroid so
// the central node still anchors the map visually like USNetworkMap did.
const COORDS = {
  "Austin, TX": [30.27, -97.74],
  "Phoenix, AZ": [33.45, -112.07],
  "Seattle, WA": [47.61, -122.33],
  "Miami, FL": [25.76, -80.19],
  "Houston, TX": [29.76, -95.37],
  "San Diego, CA": [32.72, -117.16],
  "Los Angeles, CA": [34.05, -118.24],
  "Tampa, FL": [27.95, -82.46],
  "Charlotte, NC": [35.23, -80.84],
  "Denver, CO": [39.74, -104.99],
  "Newark, NJ": [40.74, -74.17],
  "Vault · Tranche A": [39.5, -98.35]
};

// ─────────────────────────── Theme toggle / header / footer ───────────
// All from lib/onrail-ui.jsx — useTheme, <Header>, <Footer>, <ThemeToggle>.

// ─────────────────────────── Page intro ───────────────────────────

function ExplorerIntro() {
  return (
    <div style={{ padding: "44px 64px 28px" }}>
      <div style={{ marginBottom: 18 }}>
        <Eyebrow tone="brand" rule>Live · Ethereum mainnet</Eyebrow>
      </div>
      <Display>Every event, on the rail.</Display>
      <Body style={{ margin: "12px 0 0", maxWidth: 640 }}>
        Real-time activity across the Onrail mortgage rail. Every loan,
        every payment, every draw, verified on-chain. Served from the
        index.
      </Body>
    </div>);

}

// ─────────────────────────── KPI strip ───────────────────────────
// Single hairline-bounded row, 5 cells, hairline dividers BETWEEN cells
// only (no top/bottom border on the cells themselves). One row of
// numbers, 1/5 the visual weight of stacked tiles.

const KPIS = [
  { label: "Total AUM",         value: "$1.99B", delta: "+4.2%",   deltaLabel: "vs. last month", up: true,  primary: true },
  { label: "Portfolio APY",     value: "9.12%",  delta: "+0.72pp", deltaLabel: "vs. last month", up: true },
  { label: "Weighted LTV",      value: "69.95%", delta: "−0.6pp",  deltaLabel: "vs. last month", up: true },
  { label: "Active loans",      value: "247",    delta: "+12",     deltaLabel: "new this month", up: true },
  { label: "Delinquency · 90d", value: "1.25%",  delta: "−0.04pp", deltaLabel: "vs. last month", up: true }
];

function ExplorerKPIs() {
  return (
    <KpiStrip>
      {KPIS.map((k) => {
        const dir = k.up === null ? "flat" : k.up ? "up" : "down";
        return (
          <KpiCell key={k.label}
            label={k.label}
            value={k.value}
            delta={k.delta}
            deltaLabel={k.deltaLabel}
            tone={dir}
            primary={k.primary} />
        );
      })}
    </KpiStrip>
  );
}

// ─────────────────────────── d3-geo activity map ───────────────────────────
// Renders real US states geometry from us-atlas TopoJSON (the same source
// the website uses) using d3-geo's geoAlbersUsa projection. Markers
// project lat/lng through the same projection so they align with the
// geometry. Theme tokens drive fills + strokes; no external API or key.

function ExplorerActivityMap({ theme = "dark" }) {
  const W = 960, H = 600;
  const [data, setData] = React.useState(null);
  const [projection, setProjection] = React.useState(null);
  const [error, setError] = React.useState(null);

  // Per-theme paint, matching the site's tokens:
  //   · state fill: subtle wash that defines landmass
  //   · county mesh: thinnest line, most translucent
  //   · state mesh: medium weight
  //   · nation outline: thickest, with a subtle mint glow on dark
  const paint = theme === "dark"
    ? {
        landFill:     "var(--surface-overlay)",
        countyStroke: "color-mix(in srgb, #6EE7B7 30%, transparent)",
        stateStroke:  "color-mix(in srgb, #6EE7B7 55%, transparent)",
        nationStroke: "color-mix(in srgb, #6EE7B7 45%, transparent)",
        nationGlow:   "drop-shadow(0 0 6px color-mix(in srgb, #6EE7B7 35%, transparent))",
      }
    : {
        landFill:     "var(--surface-overlay)",
        countyStroke: "color-mix(in srgb, #0F1115 8%, transparent)",
        stateStroke:  "color-mix(in srgb, #0F1115 18%, transparent)",
        nationStroke: "color-mix(in srgb, #0F1115 26%, transparent)",
        nationGlow:   "none",
      };

  // One-time topojson load. counties-10m carries states + counties +
  // nation in one file, so we draw all three layers from a single fetch.
  React.useEffect(() => {
    let alive = true;
    (async () => {
      try {
        if (!window.d3 || !window.topojson) throw new Error("d3/topojson not loaded");
        const r = await fetch("https://cdn.jsdelivr.net/npm/us-atlas@3/counties-10m.json");
        if (!r.ok) throw new Error("topojson fetch failed");
        const topo = await r.json();
        if (!alive) return;

        const statesFC   = window.topojson.feature(topo, topo.objects.states);
        const proj       = window.d3.geoAlbersUsa().fitSize([W, H], statesFC);
        const path       = window.d3.geoPath(proj);

        // Interior boundaries only — topojson.mesh with a filter excludes
        // the outer edges (we draw the nation outline separately).
        const countyMesh = window.topojson.mesh(topo, topo.objects.counties, (a, b) => a !== b);
        const stateMesh  = window.topojson.mesh(topo, topo.objects.states,   (a, b) => a !== b);
        const nation     = window.topojson.mesh(topo, topo.objects.nation);

        setData({
          states:       statesFC.features.map((f) => ({ id: f.id, d: path(f) })),
          countyMeshD:  path(countyMesh),
          stateMeshD:   path(stateMesh),
          nationD:      path(nation),
        });
        setProjection(() => proj);
      } catch (e) {
        if (alive) setError(e.message || "Map load failed");
      }
    })();
    return () => { alive = false; };
  }, []);

  const projectCity = React.useCallback((city) => {
    if (!projection) return null;
    const ll = COORDS[city];
    if (!ll) return null;
    const out = projection([ll[1], ll[0]]);
    return out ? { left: out[0], top: out[1] } : null;
  }, [projection]);

  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <svg
        viewBox={`0 0 ${W} ${H}`}
        preserveAspectRatio="xMidYMid meet"
        style={{ position: "absolute", inset: 0, width: "100%", height: "100%", display: "block" }}
        aria-hidden="true"
      >
        {data && (
          <g>
            {/* State fills — light atmospheric wash */}
            <g fill={paint.landFill} stroke="none">
              {data.states.map((s) => (
                <path key={s.id} d={s.d} />
              ))}
            </g>
            {/* County mesh — thinnest interior lines, vector-effect keeps
                them hairline regardless of SVG scale */}
            <path
              d={data.countyMeshD}
              fill="none"
              stroke={paint.countyStroke}
              strokeWidth="0.36"
              vectorEffect="non-scaling-stroke"
            />
            {/* State mesh — medium weight */}
            <path
              d={data.stateMeshD}
              fill="none"
              stroke={paint.stateStroke}
              strokeWidth="0.8"
              vectorEffect="non-scaling-stroke"
            />
            {/* Nation outline — thickest, with subtle glow on dark */}
            <path
              d={data.nationD}
              fill="none"
              stroke={paint.nationStroke}
              strokeWidth="0.9"
              vectorEffect="non-scaling-stroke"
              style={{ filter: paint.nationGlow }}
            />
          </g>
        )}
        {!data && !error && (
          <text x={W/2} y={H/2} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="11" fill={EX_MUTE} style={{ letterSpacing: 1.2, textTransform: "uppercase" }}>
            Loading map…
          </text>
        )}
        {error && (
          <text x={W/2} y={H/2} textAnchor="middle" fontFamily="var(--font-mono)" fontSize="11" fill={EX_DOWN}>
            {error}
          </text>
        )}

        {/* Markers — drawn INSIDE the svg so they share the geoAlbersUsa
            projection's letterbox (preserveAspectRatio=meet centers the
            content; HTML overlays sitting outside the svg would float
            into the empty bands and end up outside the map). */}
        {projection && ACTIVITY.map((row, i) => {
          const pos = projectCity(row.loc);
          if (!pos) return null;
          const varName = TYPE_VAR[row.type] || "--text";
          const fill = `var(${varName})`;
          return (
            <g
              key={i}
              transform={`translate(${pos.left} ${pos.top})`}
              style={{ pointerEvents: "auto", cursor: "default" }}
            >
              <title>{`${TYPE_META[row.type]?.label || row.type} · ${row.amt} · ${row.loc}`}</title>
              {/* Halo — same hue at low alpha. */}
              <rect x={-12} y={-12} width={24} height={24} rx={7} fill={fill} opacity={0.16} />
              {/* Glyph — solid rounded square. */}
              <rect x={-8} y={-8} width={16} height={16} rx={5} fill={fill} />
              {/* Inner white dot. */}
              <circle cx={0} cy={0} r={3} fill="#FFFFFF" opacity={0.96} />
            </g>
          );
        })}
      </svg>
    </div>
  );
}

// ─────────────────────────── Map card ───────────────────────────

function ExplorerMapCard({ theme = "dark" }) {
  return (
    <Panel overflow="hidden" style={{ height: 560 }}>
      <PanelHead flat>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
          <LiveDot size={6} />
          <Eyebrow tone="muted">Activity map</Eyebrow>
        </div>
        <ArrowLink href="#">View loans</ArrowLink>
      </PanelHead>
      {/* Map fills the body slot — padding stripped so the geometry runs
          edge-to-edge under the hairline. */}
      <PanelBody style={{ padding: 0, position: "relative" }}>
        <ExplorerActivityMap theme={theme} />
      </PanelBody>
    </Panel>
  );
}

// ─────────────────────────── Activity feed ───────────────────────────
// Mirrors the V4 ticker row pattern but vertically. Each row is a
// hairline-divided list item; the type is set in mono caps (no chunky
// pills), and only amount + key signal carry color.

const ACTIVITY = [
{ type: "LOAN_FUNDING", amt: "$483,200", loc: "Austin, TX", meta: "FIX & FLIP · 12mo · 8.9% APY", ago: "4m ago", primary: true },
{ type: "INTEREST", amt: "$12,408", loc: "Vault · Tranche A", meta: "247 loans · weighted 8.4%", ago: "12m ago" },
{ type: "REHAB_DRAW", amt: "$45,200", loc: "Phoenix, AZ", meta: "Loan #ON-0411 · disbursement 3 of 4", ago: "18m ago" },
{ type: "LOAN_FUNDING", amt: "$1,240,000", loc: "Seattle, WA", meta: "GROUND-UP · 24mo · 9.8% APY", ago: "22m ago", primary: true },
{ type: "PRINCIPAL", amt: "$87,500", loc: "Miami, FL", meta: "Loan #ON-0418 · partial payoff", ago: "41m ago" },
{ type: "GUC_DRAW", amt: "$16,959", loc: "Houston, TX", meta: "Loan #ON-0407 · operating costs", ago: "1h ago" },
{ type: "LATE_FEE", amt: "$1,250", loc: "San Diego, CA", meta: "Loan #ON-0384 · 8 days past due", ago: "1h ago" },
{ type: "INTEREST", amt: "$5,617", loc: "Los Angeles, CA", meta: "Loan #ON-0392 · monthly", ago: "2h ago" },
{ type: "EXTENSION", amt: "—", loc: "Tampa, FL", meta: "Loan #ON-0367 · term +3 months", ago: "2h ago" },
{ type: "LOAN_FUNDING", amt: "$612,500", loc: "Charlotte, NC", meta: "FIX & FLIP · 10mo · 9.0% APY", ago: "2h ago", primary: true },
{ type: "PRINCIPAL", amt: "$215,000", loc: "Denver, CO", meta: "Loan #ON-0341 · scheduled", ago: "3h ago" },
{ type: "INTEREST", amt: "$993", loc: "Newark, NJ", meta: "Loan #ON-0376 · monthly", ago: "4h ago" }];


// USDC mark — Circle's official two-tone treatment: blue disc + white $.
// The path draws an outer disc with negative-space subpaths (the $ + the
// two side arcs around it); rendered with even-odd fill rule, those
// subpaths become holes that reveal whatever sits behind. We put a white
// circle behind the path, so the result is always blue+white regardless
// of theme.

// USDCMark and ActivityRow — USDCMark is now imported from
// lib/onrail-ui.jsx (no longer redefined here). ActivityRow is
// app-specific and uses the lib's <Money> + <Signal> primitives.

// ─────────────────────────── Activity row ───────────────────────────

function ActivityRow({ row }) {
  const meta = TYPE_META[row.type] || { label: row.type, color: "var(--text)" };
  return (
    <ListItem>
      <div style={{ display: "flex", flexDirection: "column", gap: 4, minWidth: 0 }}>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
          <Money amount={row.amt} />
          <Tag color={meta.color}>{meta.label}</Tag>
          <Body as="span" size="sm" color="var(--text-muted)">{row.loc}</Body>
        </div>
        <Num size="sm" weight="regular" color="var(--text-faint)">
          {row.meta}
        </Num>
      </div>
      <Num size="sm" weight="regular" color="var(--text-subtle)" style={{ whiteSpace: "nowrap" }}>
        {row.ago}
      </Num>
    </ListItem>);

}

function ExplorerActivityFeed() {
  return (
    <Panel overflow="hidden" style={{ height: 560 }}>
      <PanelHead>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
          <LiveDot size={6} />
          <Eyebrow tone="muted">Recent activity</Eyebrow>
          <Dot />
          <Num size="sm" weight="regular" color="var(--text-subtle)">
            1,247 events
          </Num>
        </div>
        <ArrowLink href="transactions.html">View all</ArrowLink>
      </PanelHead>
      {/* Scrollable list — padding stripped so rows can run edge-to-edge
          and own their own hairlines / interior padding. */}
      <PanelBody style={{ padding: 0, overflowY: "auto" }}>
        {ACTIVITY.map((r, i) =>
          <ActivityRow key={i} row={r} />
        )}
      </PanelBody>
      <PanelFoot>
        <Eyebrow tone="faint">Showing 8 of 1,247</Eyebrow>
        <Num size="sm" weight="regular" color="var(--text-faint)">
          Updated 2s ago
        </Num>
      </PanelFoot>
    </Panel>
  );
}

// ─────────────────────────── Loan portfolio ───────────────────────────
// Below the map + activity grid. Uses primitives from lib/onrail-ui.jsx
// (Table, Chip, HashLink, Pagination) — no new visual logic in this file.

const LOANS = [
  { id: "CB-NY-AA01", type: "Bridge",       color: "var(--chart-1)", addr: "2554 Oak Ave, New York, NY 10001",       state: "NY", status: "up", statusText: "Active", upb: "$154,000",   rate: "8.22%",  ltv: 39 },
  { id: "PL-WA-AA02", type: "Fix & Flip",   color: "var(--chart-2)", addr: "1023 Sunset Ave, Seattle, WA 98101",     state: "WA", status: "up", statusText: "Active", upb: "$263,000",   rate: "11.93%", ltv: 72 },
  { id: "PL-CA-AA05", type: "Fix & Flip",   color: "var(--chart-2)", addr: "3001 Pine Blvd, Oakland, CA 94601",      state: "CA", status: "up", statusText: "Active", upb: "$146,000",   rate: "11.56%", ltv: 21 },
  { id: "SF-CA-AA01", type: "DSCR",         color: "var(--chart-5)", addr: "4550 Maple Dr, San Francisco, CA 94105", state: "CA", status: "up", statusText: "Active", upb: "$335,000",   rate: "9.66%",  ltv: 65 },
  { id: "CB-TX-AA02", type: "Bridge",       color: "var(--chart-1)", addr: "7803 Madison Way, Houston, TX 77001",    state: "TX", status: "up", statusText: "Active", upb: "$383,000",   rate: "10.50%", ltv: 49 },
  { id: "AC-GA-AA01", type: "Construction", color: "var(--chart-6)", addr: "5383 Madison Way, Atlanta, GA 30303",    state: "GA", status: "up", statusText: "Active", upb: "$167,000",   rate: "9.69%",  ltv: 25 },
  { id: "AC-FL-AA03", type: "Ground-Up",    color: "var(--chart-7)", addr: "4322 Washington St, Orlando, FL 32801",  state: "FL", status: "up", statusText: "Active", upb: "$227,000",   rate: "8.47%",  ltv: 39 },
  { id: "CB-FL-AA04", type: "Bridge",       color: "var(--chart-1)", addr: "8872 Hilltop Dr, Jacksonville, FL 32099", state: "FL", status: "up", statusText: "Active", upb: "$1,466,000", rate: "11.25%", ltv: 78 },
  { id: "SF-TX-AA02", type: "DSCR",         color: "var(--chart-5)", addr: "1693 Park Ave, Dallas, TX 75201",        state: "TX", status: "up", statusText: "Active", upb: "$1,824,000", rate: "8.17%",  ltv: 75 },
  { id: "PL-NY-AA01", type: "Fix & Flip",   color: "var(--chart-2)", addr: "5654 Washington St, New York, NY 10001", state: "NY", status: "up", statusText: "Active", upb: "$167,000",   rate: "8.94%",  ltv: 41 }
];

// Replaced by <MeterCell> from lib/onrail-ui.jsx — same shape, generic
// zones prop (chart-up < 70, chart-warn ≤ 90, chart-down > 90 by default).

function ExplorerLoanPortfolio() {
  const ALL = LOANS;

  return (
    <Panel overflow="hidden" style={{ margin: "32px 64px 0" }}>
      <PanelHead>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
          <Eyebrow tone="muted">Latest loans</Eyebrow>
          <Dot />
          <Num size="sm" weight="regular" color="var(--text-subtle)">
            {ALL.length} most recent
          </Num>
        </div>
        <ArrowLink href="loans.html">View all loans</ArrowLink>
      </PanelHead>
      {/* Table body — padding stripped so the .table chrome runs
          edge-to-edge under the head hairline. */}
      <PanelBody style={{ padding: 0, overflowX: "auto" }}>
        <Table>
          <thead><tr>
            <th data-sortable="true" aria-sort="ascending">
              Loan ID <TableSortArrow />
            </th>
            <th>Type</th>
            <th>Property</th>
            <th>Status</th>
            <th data-align="right" data-sortable="true">
              UPB <TableSortArrow />
            </th>
            <th data-align="right">Rate</th>
            <th data-align="right">LTV</th>
          </tr></thead>
          <tbody>
            {ALL.map((l) => (
              <tr key={l.id}>
                <td><HashLink value={l.id} href={`loan.html?id=${l.id}`} head={10} tail={0} /></td>
                <td><Chip color={l.color}>{l.type}</Chip></td>
                <td>
                  <Body as="span" size="sm" color={EX_BODY}>{l.addr}</Body>
                  <Num size="sm" weight="regular" color={EX_FAINT} style={{ marginLeft: 8 }}>
                    {l.state}
                  </Num>
                </td>
                <td><Chip color={`var(--${l.status})`}>{l.statusText}</Chip></td>
                <td data-align="right">
                  <Money amount={l.upb} size="sm" color="var(--text-muted)" />
                </td>
                <td data-align="right">
                  {l.rate === "—"
                    ? <Num size="sm" color={EX_FAINT}>—</Num>
                    : <Signal tone="up">{l.rate}</Signal>}
                </td>
                <td data-align="right"><MeterCell value={l.ltv} /></td>
              </tr>
            ))}
          </tbody>
        </Table>
      </PanelBody>
    </Panel>
  );
}

// ─────────────────────────── Page assembly ───────────────────────────

function Explorer() {
  const [theme, setTheme] = useTheme();
  return (
    <div
      data-theme={theme}
      style={{
        width: "100%", minHeight: "100%",
        background: EX_BG,
        color: EX_INK,
        fontFamily: "var(--font-sans)"
      }}>
      
      <Header
        label="Explorer"
        nav={[
          { label: "Overview",     href: "overview.html" },
          { label: "Portfolio",    href: "portfolio.html" },
          { label: "Transactions", href: "transactions.html" },
          { label: "Loans",        href: "loans.html" }
        ]}
        active="Overview"
        theme={theme}
        onThemeChange={setTheme}
        cta={{ label: "Request access" }}
      />
      <ExplorerIntro />
      <ExplorerKPIs />

      {/* Main grid — map + activity feed */}
      <div style={{
        margin: "32px 64px 0",
        display: "grid",
        gridTemplateColumns: "1.5fr 1fr",
        gap: 24
      }}>
        <ExplorerMapCard theme={theme} />
        <ExplorerActivityFeed />
      </div>

      <ExplorerLoanPortfolio />

      <Footer
        left={
          <Num size="sm" weight="regular" color="var(--text-faint)">
            © 2026 Onrail · Real-time index of on-chain events
          </Num>
        }
        right={
          <Num size="sm" weight="regular" color="var(--text-faint)" style={{
            display: "inline-flex", alignItems: "center", gap: 14,
          }}>
            <span>Block 312,840,219</span>
            <Dot />
            <span>RPC 32ms</span>
            <Dot />
            <span>Indexer healthy</span>
          </Num>
        }
      />
    </div>);

}

window.Explorer = Explorer;