const { useState, useMemo } = React;
const {
  IconSearch, IconChevDown, IconClock, IconRefresh, IconInfo,
} = window.Icons;

// ─── Service icon glyphs (rendered inside circular nodes)
function SvcIcon({ kind, size = 16 }) {
  const p = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 1.6, strokeLinecap: "round", strokeLinejoin: "round" };
  switch (kind) {
    case "money":   return <svg {...p}><path d="M12 4v16M8 8h6a2 2 0 1 1 0 4H10a2 2 0 1 0 0 4h6"/></svg>;
    case "server":  return <svg {...p}><rect x="4" y="5" width="16" height="6" rx="1.5"/><rect x="4" y="13" width="16" height="6" rx="1.5"/><circle cx="7" cy="8" r=".5" fill="currentColor"/><circle cx="7" cy="16" r=".5" fill="currentColor"/></svg>;
    case "globe":   return <svg {...p}><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18"/></svg>;
    case "pulse":   return <svg {...p}><path d="M3 12h4l3-7 4 14 3-7h4"/></svg>;
    case "image":   return <svg {...p}><rect x="3" y="5" width="18" height="14" rx="1.5"/><circle cx="9" cy="10" r="1.5"/><path d="m3 17 5-5 4 4 3-3 6 6"/></svg>;
    case "speaker": return <svg {...p}><path d="M5 9v6h4l5 4V5L9 9z"/><path d="M17 8a5 5 0 0 1 0 8"/></svg>;
    case "doc":     return <svg {...p}><path d="M6 3h9l4 4v14H6z"/><path d="M14 3v5h5M9 13h7M9 17h5"/></svg>;
    case "chip":    return <svg {...p}><rect x="6" y="6" width="12" height="12" rx="1.5"/><path d="M9 9h6v6H9zM12 3v3M12 18v3M3 12h3M18 12h3"/></svg>;
    case "monitor": return <svg {...p}><rect x="3" y="4" width="18" height="12" rx="1.5"/><path d="M8 20h8M12 16v4"/></svg>;
    case "cart":    return <svg {...p}><path d="M3 4h2l3 12h11l2-8H6"/><circle cx="9" cy="20" r="1.5"/><circle cx="17" cy="20" r="1.5"/></svg>;
    case "mail":    return <svg {...p}><rect x="3" y="5" width="18" height="14" rx="1.5"/><path d="m3 7 9 7 9-7"/></svg>;
    case "truck":   return <svg {...p}><rect x="2" y="7" width="12" height="9" rx="1"/><path d="M14 10h4l3 3v3h-7z"/><circle cx="7" cy="18" r="1.5"/><circle cx="17" cy="18" r="1.5"/></svg>;
    case "sliders": return <svg {...p}><path d="M5 4v6M5 14v6M12 4v3M12 11v9M19 4v9M19 17v3M3 10h4M10 7h4M17 13h4"/></svg>;
    default:        return <svg {...p}><circle cx="12" cy="12" r="6"/></svg>;
  }
}

// ─── Service map graph (horizontal layout, x,y in px)
const SERVICES = [
  // Sources (col 0, left side)
  { id: "accounting",     label: "accounting",     icon: "money",   req: "5.3K",   col: 0, row: 1.0, status: "ok" },
  { id: "fraud-detection",label: "fraud-detection",icon: "server",  req: "120",    col: 0, row: 2.2, status: "critical" },
  { id: "frontend-proxy", label: "frontend-proxy", icon: "globe",   req: "98.7K",  col: 1, row: 3.8, status: "ok" },
  { id: "load-generator", label: "load-generator", icon: "pulse",   req: "112.7K", col: 0, row: 4.4, status: "ok" },
  { id: "image-provider", label: "image-provider", icon: "image",   req: "8.6K",   col: 0, row: 5.6, status: "ok" },

  // flagd (downstream of multiple — show two instances)
  { id: "flagd-fraud",    label: "flagd",          icon: "sliders", req: "120",    col: 1, row: 2.2, status: "ok",  flagd: true },
  { id: "flagd-load",     label: "flagd",          icon: "sliders", req: "14.0K",  col: 1, row: 5.0, status: "ok",  flagd: true },

  // Frontend hub
  { id: "frontend",       label: "frontend",       icon: "monitor", req: "100.1K", col: 2, row: 4.0, status: "ok" },

  // Downstream services from frontend (col 3 right side)
  { id: "ad",             label: "ad",             icon: "speaker", req: "8.5K",   col: 3, row: 0.6, status: "ok" },
  { id: "product-catalog",label: "product-catalog",icon: "doc",     req: "82.5K",  col: 3, row: 1.6, status: "ok" },
  { id: "recommendation", label: "recommendation", icon: "chip",    req: "7.8K",   col: 3, row: 2.6, status: "ok" },
  { id: "payment",        label: "payment",        icon: "money",   req: "5.3K",   col: 3, row: 3.7, status: "ok" },
  { id: "cart",           label: "cart",           icon: "cart",    req: "10.6K",  col: 3, row: 4.5, status: "ok" },
  { id: "email",          label: "email",          icon: "mail",    req: "5.3K",   col: 3, row: 5.3, status: "ok" },
  { id: "checkout",       label: "checkout",       icon: "cart",    req: "5.3K",   col: 3, row: 6.0, status: "ok" },
  { id: "shipping",       label: "shipping",       icon: "truck",   req: "10.6K",  col: 3, row: 6.8, status: "ok" },
  { id: "currency",       label: "currency",       icon: "money",   req: "14.6K",  col: 3, row: 7.6, status: "ok" },

  // Downstream of right-side services
  { id: "flagd-ad",       label: "flagd",          icon: "sliders", req: "120",    col: 4, row: 0.6, status: "ok",  flagd: true },
  { id: "flagd-reco",     label: "flagd",          icon: "sliders", req: "120",    col: 4, row: 2.6, status: "ok",  flagd: true },
  { id: "pc-2",           label: "product-cat",    icon: "doc",     req: "7.8K",   col: 4, row: 1.6, status: "ok" },
  { id: "pc-3",           label: "product-cat",    icon: "doc",     req: "9.3K",   col: 4, row: 4.8, status: "ok" },
  { id: "flagd-cart",     label: "flagd",          icon: "sliders", req: "5.3K",   col: 4, row: 8.2, status: "ok",  flagd: true },
  { id: "cart-down",      label: "cart",           icon: "cart",    req: "39.8K",  col: 3, row: 8.2, status: "ok" },
];

const EDGES = [
  // accounting goes to virtual root only (no downstream)
  ["fraud-detection", "flagd-fraud"],
  ["load-generator", "frontend-proxy"],
  ["load-generator", "flagd-load"],
  ["frontend-proxy", "frontend"],
  ["frontend", "ad"],
  ["frontend", "product-catalog"],
  ["frontend", "recommendation"],
  ["frontend", "checkout"],
  ["frontend", "cart"],
  ["ad", "flagd-ad"],
  ["recommendation", "flagd-reco"],
  ["product-catalog", "pc-2"],
  ["checkout", "payment"],
  ["checkout", "email"],
  ["checkout", "shipping"],
  ["checkout", "currency"],
  ["checkout", "pc-3"],
  ["cart-down", "flagd-cart"],
];

// Service details (Recent Traces, error rate, etc.)
const SERVICE_DETAILS = {
  "fraud-detection": {
    status: "critical",
    requestsMin: 120,
    rateChangeIn: 0, rateChangeOut: 120,
    errorRate: "100.00%",
    p50: "N/A", p95: "N/A", p99: "N/A",
    upstream: [],
    downstream: [{ name: "flagd", status: "critical" }],
    recentTraces: [
      { id: "2d5cd943...70f5", dur: "3.10s", spans: 2, status: "ok", time: "10:36:06 PM" },
      { id: "4d2e70b4...5940", dur: "93.46s", spans: 2, status: "ok", time: "10:34:52 PM" },
      { id: "5595dfa3...d3e8", dur: "48.43s", spans: 2, status: "ok", time: "10:34:41 PM" },
      { id: "c2e070c7...080b", dur: "12.84s", spans: 2, status: "ok", time: "10:33:18 PM" },
      { id: "a73fc011...4ee2", dur: "8.21s",  spans: 2, status: "ok", time: "10:32:55 PM" },
    ],
  },
  "frontend": {
    status: "ok",
    requestsMin: "100.1K",
    rateChangeIn: 100100, rateChangeOut: 100100,
    errorRate: "0.12%",
    p50: "48ms", p95: "210ms", p99: "640ms",
    upstream: [{ name: "frontend-proxy", status: "ok" }],
    downstream: [
      { name: "ad", status: "ok" }, { name: "product-catalog", status: "ok" },
      { name: "recommendation", status: "ok" }, { name: "checkout", status: "ok" },
      { name: "cart", status: "ok" },
    ],
    recentTraces: [
      { id: "8f3e4b29...1c4a", dur: "182ms", spans: 8, status: "ok", time: "10:36:18 PM" },
      { id: "ab73e119...88d2", dur: "94ms",  spans: 6, status: "ok", time: "10:36:14 PM" },
      { id: "ddef34a1...0091", dur: "421ms", spans: 9, status: "ok", time: "10:36:10 PM" },
    ],
  },
  "checkout": {
    status: "ok",
    requestsMin: "5.3K",
    rateChangeIn: 5300, rateChangeOut: 26500,
    errorRate: "0.04%",
    p50: "230ms", p95: "780ms", p99: "1.4s",
    upstream: [{ name: "frontend", status: "ok" }],
    downstream: [
      { name: "payment", status: "ok" }, { name: "email", status: "ok" },
      { name: "shipping", status: "ok" }, { name: "currency", status: "ok" },
    ],
    recentTraces: [
      { id: "f1ce7e2c...44a8", dur: "1.21s", spans: 12, status: "ok", time: "10:36:08 PM" },
      { id: "0c4a9133...77fe", dur: "920ms", spans: 11, status: "ok", time: "10:36:01 PM" },
    ],
  },
};
const DEFAULT_DETAIL = "fraud-detection";

const COL_X = [180, 380, 580, 780, 1010];
const ROW_H = 76;

function nodeXY(svc) {
  return [COL_X[svc.col], 80 + svc.row * ROW_H];
}

function edgePath(from, to) {
  const [x1, y1] = nodeXY(from);
  const [x2, y2] = nodeXY(to);
  const mx = (x1 + x2) / 2;
  return `M${x1} ${y1} C ${mx} ${y1}, ${mx} ${y2}, ${x2} ${y2}`;
}

function ServiceNode({ svc, selected, onClick }) {
  const [x, y] = nodeXY(svc);
  const ringColor = svc.status === "critical" ? "var(--err)" : svc.flagd ? "var(--warn)" : "var(--ok)";
  const isPlaceholder = false;
  return (
    <g style={{ cursor: "pointer" }} onClick={() => onClick(svc.id)}>
      {selected && (
        <circle cx={x} cy={y} r="22" fill="var(--accent-bg)" stroke="var(--accent)" strokeWidth="1.5" strokeDasharray="3 2"/>
      )}
      <circle cx={x} cy={y} r="14" fill="var(--surface)" stroke={ringColor} strokeWidth="2"/>
      <g transform={`translate(${x - 8} ${y - 8})`} style={{ color: ringColor }}>
        <SvcIcon kind={svc.icon} size={16}/>
      </g>
      <text x={x + 22} y={y - 2} fontSize="12" fontWeight="500" fill="var(--ink)">{svc.label}</text>
      <text x={x + 22} y={y + 12} fontSize="10.5" fill="var(--muted)" fontFamily="JetBrains Mono">{svc.req} req</text>
    </g>
  );
}

function ServiceMap({ selected, onSelect }) {
  const W = 1200, H = 740;
  return (
    <svg width="100%" height="100%" viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="xMinYMin meet"
         style={{ display: "block" }}>
      {/* Background grid */}
      <defs>
        <pattern id="dotgrid" width="24" height="24" patternUnits="userSpaceOnUse">
          <circle cx="0.5" cy="0.5" r="0.5" fill="var(--border)" />
        </pattern>
      </defs>
      <rect width={W} height={H} fill="url(#dotgrid)" opacity=".5"/>

      {/* Edges */}
      <g stroke="var(--border-2)" strokeWidth="1.5" fill="none">
        {EDGES.map(([fromId, toId], i) => {
          const from = SERVICES.find(s => s.id === fromId);
          const to = SERVICES.find(s => s.id === toId);
          if (!from || !to) return null;
          return <path key={i} d={edgePath(from, to)} opacity=".8" />;
        })}
      </g>

      {/* Nodes */}
      {SERVICES.map(s => (
        <ServiceNode key={s.id} svc={s} selected={selected === s.id} onClick={onSelect} />
      ))}
    </svg>
  );
}

// ─── Detail panel
function DetailPanel({ serviceId, onClose, onOpenTrace }) {
  const svc = SERVICES.find(s => s.id === serviceId);
  const d = SERVICE_DETAILS[serviceId] || SERVICE_DETAILS[DEFAULT_DETAIL];
  if (!svc) return null;
  const isCrit = d.status === "critical";

  return (
    <aside style={{
      width: 360, flexShrink: 0,
      borderLeft: "1px solid var(--border)",
      background: "var(--surface)",
      display: "flex", flexDirection: "column", minHeight: 0,
    }}>
      <div style={{
        padding: "12px 14px", borderBottom: "1px solid var(--border)",
        display: "flex", alignItems: "center", gap: 8,
      }}>
        <span style={{ fontSize: 14, fontWeight: 600, color: "var(--ink)" }}>{svc.label}</span>
        <span className="mono pill" style={{
          background: isCrit ? "var(--err-bg)" : "var(--go-bg)",
          color: isCrit ? "var(--err)" : "var(--go)",
        }}>
          <span style={{ width: 5, height: 5, borderRadius: "50%", background: "currentColor", display: "inline-block" }}/>
          {isCrit ? "Critical" : "OK"}
        </span>
        <div style={{ flex: 1 }} />
        <button className="btn-primary" style={{
          background: "var(--accent)", borderColor: "var(--accent)", height: 26, padding: "0 10px", fontSize: 11,
        }}>
          <IconSearch size={12} /> Show telemetry
        </button>
        <button className="icon-btn" onClick={onClose} title="Close" style={{ marginLeft: 2 }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"><path d="M6 6l12 12M18 6 6 18"/></svg>
        </button>
      </div>

      <div style={{ overflowY: "auto", flex: 1 }}>
        {/* Top stats row */}
        <div style={{ padding: "12px 14px", display: "grid", gridTemplateColumns: "1.4fr .8fr .8fr", gap: 8 }}>
          <Stat label="Requests" value={d.requestsMin} unit="/min" accent />
          <Stat icon="in" value={d.rateChangeIn} />
          <Stat icon="out" value={d.rateChangeOut} />
        </div>
        <div style={{ padding: "0 14px 12px", display: "grid", gridTemplateColumns: "1fr 1fr", gap: 8 }}>
          <Stat label="Error Rate" value={d.errorRate} crit={isCrit} />
          <Stat label="P95 Latency" value={d.p95} />
          <Stat label="P50" value={d.p50} small />
          <Stat label="P99" value={d.p99} small />
        </div>

        {/* Upstream / Downstream */}
        <Section title={`Upstream Services (${d.upstream.length})`}>
          {d.upstream.length === 0
            ? <div style={{ textAlign: "center", fontSize: 12, color: "var(--muted)", padding: "10px 0" }}>No upstream dependencies</div>
            : d.upstream.map((s, i) => <DepRow key={i} {...s} />)
          }
        </Section>

        <Section title={`Downstream Services (${d.downstream.length})`}>
          {d.downstream.map((s, i) => <DepRow key={i} {...s} />)}
        </Section>

        <Section title={`Recent Traces (${d.recentTraces.length})`}>
          {d.recentTraces.map((t, i) => (
            <TraceRow key={i} {...t} onOpen={onOpenTrace} />
          ))}
        </Section>
      </div>
    </aside>
  );
}

function Stat({ label, value, unit, accent, crit, small, icon }) {
  return (
    <div style={{
      padding: "8px 10px", borderRadius: 6,
      background: accent ? "var(--accent-bg)" : crit ? "var(--err-bg)" : "var(--canvas)",
      border: `1px solid ${accent ? "var(--accent-line)" : crit ? "rgba(220,64,64,.18)" : "var(--border)"}`,
      display: "flex", flexDirection: "column", gap: 1,
    }}>
      {label && (
        <span style={{ fontSize: 10, color: crit ? "var(--err)" : "var(--muted)", fontWeight: 500 }}>
          {crit && "⚠ "}{label}{accent && ":"}
        </span>
      )}
      <div style={{ display: "flex", alignItems: "baseline", gap: 4 }}>
        {icon === "in"  && <span style={{ color: "var(--accent)", fontFamily: "monospace" }}>→</span>}
        <span className="mono" style={{
          fontSize: small ? 14 : 16, fontWeight: 600,
          color: accent ? "var(--accent)" : crit ? "var(--err)" : "var(--ink)",
        }}>{value}</span>
        {unit && <span className="mono" style={{ fontSize: 10, color: "var(--muted)" }}>{unit}</span>}
        {icon === "out" && <span style={{ color: "var(--accent)", fontFamily: "monospace", marginLeft: "auto" }}>→</span>}
      </div>
    </div>
  );
}

function Section({ title, children }) {
  return (
    <div style={{ padding: "8px 14px", borderTop: "1px solid var(--border)" }}>
      <div style={{ fontSize: 12, fontWeight: 600, color: "var(--ink)", marginBottom: 6 }}>{title}</div>
      {children}
    </div>
  );
}

function DepRow({ name, status }) {
  const isCrit = status === "critical";
  return (
    <div style={{
      display: "flex", alignItems: "center",
      padding: "6px 10px", marginBottom: 4,
      background: "var(--canvas)", border: "1px solid var(--border)", borderRadius: 5,
    }}>
      <span style={{ fontSize: 12, color: "var(--ink)" }}>{name}</span>
      <div style={{ flex: 1 }}/>
      <span className="pill" style={{
        background: isCrit ? "var(--err-bg)" : "var(--go-bg)",
        color: isCrit ? "var(--err)" : "var(--go)",
      }}>
        <span style={{ width: 5, height: 5, borderRadius: "50%", background: "currentColor", display: "inline-block" }}/>
        {isCrit ? "Critical" : "OK"}
      </span>
    </div>
  );
}

function TraceRow({ id, dur, spans, status, time, onOpen }) {
  return (
    <div onClick={onOpen} style={{
      display: "flex", flexDirection: "column", gap: 4,
      padding: "8px 10px", marginBottom: 4,
      background: "var(--canvas)", border: "1px solid var(--border)", borderRadius: 5,
      cursor: onOpen ? "pointer" : "default",
    }}
    onMouseEnter={e => { if (onOpen) e.currentTarget.style.background = "var(--accent-bg)"; }}
    onMouseLeave={e => { if (onOpen) e.currentTarget.style.background = "var(--canvas)"; }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <span className="mono" style={{ fontSize: 12, color: "var(--accent)", fontWeight: 500 }}>{id}</span>
        <span className="pill" style={{
          background: "var(--go-bg)", color: "var(--go)",
        }}>
          <svg width="9" height="9" viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.2 4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4z"/></svg>
          OK
        </span>
      </div>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <div style={{ display: "flex", gap: 10 }}>
          <span style={{ fontSize: 11, color: "var(--muted)", display: "inline-flex", alignItems: "center", gap: 3 }}>
            <IconClock size={10}/> {dur}
          </span>
          <span style={{ fontSize: 11, color: "var(--muted)", display: "inline-flex", alignItems: "center", gap: 3 }}>
            <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="12" cy="18" r="2"/><path d="M7 8l4 8M17 8l-4 8"/></svg>
            {spans} spans
          </span>
        </div>
        <span className="mono" style={{ fontSize: 10, color: "var(--muted)" }}>{time}</span>
      </div>
    </div>
  );
}

// ─── Traces toolbar
function TracesToolbar({ view, onView, layout, onLayout }) {
  return (
    <div style={{
      height: 52, flexShrink: 0,
      display: "flex", alignItems: "center", gap: 10,
      padding: "0 16px",
      background: "var(--canvas)",
      borderBottom: "1px solid var(--border)",
    }}>
      <div style={{
        display: "flex", alignItems: "center",
        background: "var(--surface)", border: "1px solid var(--border)",
        borderRadius: 6, padding: "0 4px",
      }}>
        <button className="icon-btn" style={{ width: 28, height: 28 }}><IconSearch size={14}/></button>
        <button className="icon-btn active" style={{
          width: 28, height: 28,
          background: "var(--accent-dark)", color: "white", borderRadius: 5,
        }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6">
            <circle cx="6" cy="6" r="2.5"/><circle cx="18" cy="6" r="2.5"/><circle cx="12" cy="18" r="2.5"/>
            <path d="M7.5 7.5 11 16.5M16.5 7.5 13 16.5"/>
          </svg>
        </button>
      </div>

      <button className="btn" style={{ minWidth: 180, justifyContent: "space-between" }}>
        <span style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M4 10h16M10 4v16"/></svg>
          otel_demo
        </span>
        <IconChevDown size={12} />
      </button>

      <div style={{
        display: "flex", alignItems: "center", gap: 8,
        background: "var(--surface)", border: "1px solid var(--border)",
        borderRadius: 6, padding: "0 12px", height: 32, minWidth: 240,
      }}>
        <IconSearch size={13} style={{ color: "var(--muted)" }}/>
        <input placeholder="Search services..." style={{
          border: "none", background: "transparent", outline: "none",
          fontSize: 12, color: "var(--ink)", flex: 1, fontFamily: "inherit",
        }}/>
      </div>

      <div style={{ flex: 1 }} />

      <button className="btn" style={{ height: 32 }}>
        <IconClock size={13}/>
        <span>Past 1 Hour</span>
        <IconChevDown size={12} />
      </button>
      <button className="btn" style={{ height: 32 }}>
        <IconRefresh size={13}/>
        <span>Refresh</span>
      </button>

      {/* Tree/Graph toggle */}
      <div style={{
        display: "flex", border: "1px solid var(--border)", borderRadius: 6, overflow: "hidden",
        background: "var(--surface)",
      }}>
        {["tree", "graph"].map(k => (
          <button key={k} onClick={() => onView(k)} style={{
            border: "none", padding: "6px 14px", height: 32,
            fontSize: 12, fontWeight: view === k ? 600 : 500,
            background: view === k ? "var(--accent)" : "transparent",
            color: view === k ? "white" : "var(--ink-2)",
            cursor: "pointer",
          }}>
            {k === "tree" ? "Tree View" : "Graph View"}
          </button>
        ))}
      </div>

      <button className="btn" style={{ height: 32, minWidth: 130, justifyContent: "space-between" }}>
        <span style={{ textTransform: "capitalize" }}>{layout}</span>
        <IconChevDown size={12} />
      </button>
    </div>
  );
}

// ─── Main
function TracesScreen({ onOpenTrace }) {
  const [selected, setSelected] = useState(DEFAULT_DETAIL);
  const [view, setView] = useState("tree");
  const [layout, setLayout] = useState("Horizontal");

  return (
    <div style={{
      flex: 1, display: "flex", flexDirection: "column", minHeight: 0,
      background: "var(--bg)",
    }}>
      <TracesToolbar view={view} onView={setView} layout={layout} onLayout={setLayout} />
      <div style={{ flex: 1, display: "flex", minHeight: 0 }}>
        <div style={{
          flex: 1, minWidth: 0, overflow: "auto",
          background: "var(--surface)",
          margin: 12, marginRight: selected ? 0 : 12,
          border: "1px solid var(--border)", borderRadius: 6,
        }}>
          <ServiceMap selected={selected} onSelect={setSelected} />
        </div>
        {selected && <DetailPanel serviceId={selected}
                                  onClose={() => setSelected(null)}
                                  onOpenTrace={onOpenTrace} />}
      </div>
    </div>
  );
}

window.TracesScreen = TracesScreen;
