src/main.tsx

import {SettingsPage} from './Settings';
import React, {lazy,Suspense,useEffect,useMemo,useRef,useState} from "react";
import {LivePopulation} from './LivePopulation';
import {ItemSearchInput} from './blackrock/ItemSearchInput';
import type {CoordinationContext} from '../shared/coordination';
import type {CoordinationPlanSelection} from './coordination/CoordinationMapPanel';
import type {MissionResearchSelection} from '../shared/logistics';
const CoordinationMapPanel=lazy(()=>import('./coordination/CoordinationMapPanel').then(m=>({default:m.CoordinationMapPanel})));
import {MissionStatusBar} from './missions/MissionStatusBar';
import {useResearchLayers} from './useResearchLayers';
const SwarmWorkspace=lazy(()=>import('./swarms/SwarmWorkspace'));
const MissionWorkspace=lazy(()=>import('./missions/MissionWorkspace'));
import {AssistantPanel} from './assistant/AssistantPanel';
import {isResearchSection,initialSection,readLanding,sectionUrl,researchSection,type ResearchSection} from './blackrock/navigation';
import BlackrockWorkspace from './blackrock/LazyBlackrockWorkspace';
import type {Entity} from './blackrock/api';
import {readPaneFilters,type PaneFilter} from '../shared/pane-controls';
import { createRoot } from "react-dom/client";
import {
  Search,
  Map,
  Globe,
  Users,
  Activity,
  Database,
  Terminal,
  ChevronRight,
  ChevronDown,
  Crosshair,
  Bookmark,
  Layers,
  Navigation,
  MapPin,
  X,
  ArrowUpRight,
  Download,
  Radio,
  Shield,
  Sword,
  Eye,
  Code,
  Clock,
  Maximize2,
  Minimize2,
  Copy,
  Check,
  Plus,
  Minus,
  Box,
  LockKeyhole,
  SlidersHorizontal,
  Target,
  Sparkles,
} from "lucide-react";
import {CombatZoneBadge} from './CombatZoneBadge';
import {WorldToolbar} from "./WorldToolbar";
import { WorldViews } from "./WorldViews";
import {PinnedAtlas} from "./PinnedAtlas";
import { FollowMonitors } from "./FollowMonitors";
import { ViewerCommands, initialViewCamera, monitorHref, addFollowMonitor, type ViewCamera, type ViewCommand } from "../shared/viewer-session";
import { OperationsPanel, BotOperations, TeamFilter } from "./Operations";
import { bindingsForTeam, botKey } from "../shared/operations";
import { LocationShortcuts } from "./LocationShortcuts";
import { LOCATIONS } from "../shared/locations";
import { DeathRisk } from "./DeathRisk";
import { ItemSprite } from "./ItemSprite";
import {
  Theme,
  TextField,
  TextArea,
  Checkbox,
  Badge,
  Card,
  Table,
  SegmentedControlRadioGroup,
  Tooltip,
  Popover,
} from "@whop/react/components";
import { Button, FilterSelect } from "./ui";
import "@whop/react/styles.css";
import "./dither-theme.css";
import "./style.css";
import "./frosted.css";
import "./world.css";
import "./blackrock-shell.css";
import "./responsive-workspace.css";
import "./live-population.css";
const fmt = (v: any) => (v == null ? "—" : Number(v).toLocaleString());
const age = (t: any) =>
  !t
    ? "unobserved"
    : `${Math.max(0, Math.floor((Date.now() - t) / 1000))}s ago`;
const time = (t: number) =>
  new Date(t).toLocaleTimeString([], { hour12: false });
const label = (v: string) => v.replaceAll("_", " ");
async function request(path: string, body?: any, method?: string) {
  const r = await fetch(
    path,
    body !== undefined
      ? {
          method: method ?? "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify(body),
        }
      : method
        ? { method }
        : undefined,
  );
  const data = await r.json();
  if (!r.ok) throw new Error(data.error ?? "Request failed");
  return data;
}
function usePoll(path: string | null, ms = 1500) {
  const [data, setData] = useState<any>(null);
  const [error, setError] = useState("");
  useEffect(() => {
    setData(null);
    setError("");
    if (!path) return;
    let stopped = false,
      timer: ReturnType<typeof setTimeout>;
    const abort = new AbortController();
    async function poll() {
      try {
        const r = await fetch(path!, { signal: abort.signal });
        const d = await r.json();
        if (!r.ok) throw new Error(d.error || "Source unavailable");
        if (!stopped) {
          setData(d);
          setError("");
        }
      } catch (e) {
        if (!stopped)
          setError(e instanceof Error ? e.message : "Connection lost");
      }
      if (!stopped) timer = setTimeout(poll, ms);
    }
    poll();
    return () => {
      stopped = true;
      clearTimeout(timer);
      abort.abort();
    };
  }, [path, ms]);
  return { data, error };
}
function Empty({
  title,
  children,
}: {
  title: string;
  children?: React.ReactNode;
}) {
  return (
    <div className="empty">
      <Target size={26} />
      <h3>{title}</h3>
      <p>{children}</p>
    </div>
  );
}
function Json({ data }: { data: any }) {
  return <pre className="json">{JSON.stringify(data, null, 2)}</pre>;
}
const SIGNALS: Record<string, string> = {
  an: "Animation [sequence, delay]",
  sp: "Spot animation [ID, height, delay]",
  fe: "Facing entity",
  fs: "Facing fine coordinates",
  hm: "Damage [amount, type]",
  hp: "Reported HP [current, max]",
  ch: "Public chat",
  sy: "Overhead text",
  m: "Step directions",
  tp: "Movement discontinuity",
  em: "Forced movement",
  cc: "Chat colour / effect",
};
function EventRows({
  events,
  onEntity,
  onFly,
  compact = false,
}: {
  events: any[];
  onEntity: (name: string) => void;
  onFly: (x: number, z: number) => void;
  compact?: boolean;
}) {
  return (
    <div className={`event-list ${compact ? "compact" : ""}`}>
      {events.length === 0 ? (
        <Empty title="Listening for observations">
          Events appear as the feed publishes them in the current observation
          area.
        </Empty>
      ) : (
        events.map((e) => (
          <div className={`event-row event-${e.kind}`} key={e.id}>
            <time>{time(e.at)}</time>
            <span className="event-kind">{label(e.kind)}</span>
            <Button
              className="entity-link"
              onClick={() =>
                e.entity && !e.entity.startsWith("npc:")
                  ? onEntity(e.entity)
                  : e.payload.x && onFly(e.payload.x, e.payload.z)
              }
            >
              {e.entity ?? "Console"}
            </Button>
            <span className="event-value" title={JSON.stringify(e.payload)}>
              {e.kind === "equipment"
                ? e.payload.equipment?.map((i: any) => i.name).join(", ") ||
                  "No visible worn items"
                : typeof e.payload.value === "string"
                  ? e.payload.value
                  : e.payload.value !== undefined
                    ? JSON.stringify(e.payload.value)
                    : (e.payload.note ??
                      e.payload.name ??
                      e.payload.kind ??
                      e.payload.meaning ??
                      `${e.payload.x ?? ""} ${e.payload.z ?? ""}`)}
            </span>
            {!compact && e.payload.x !== undefined && (
              <Button
                className="icon"
                aria-label={`Fly to event ${e.id}`}
                onClick={() => onFly(e.payload.x, e.payload.z)}
              >
                <Navigation size={13} />
              </Button>
            )}
          </div>
        ))
      )}
    </div>
  );
}
function Inspector({
  selection,
  state,
  onClose,
  onFollow,
  onWatch,
  onFly,
  onResearch,
  gearRequest,
}: {
  selection: any;
  state: any;
  onClose: () => void;
  onFollow: (n: string) => void;
  onWatch: (n: string, note?: string) => void;
  onFly: (x: number, z: number) => void;
  onResearch: (entity: Entity) => void;
  gearRequest:number;
}) {
  const [tab, setTab] = useState("overview");
  const [note, setNote] = useState("");
  const [noteStatus, setNoteStatus] = useState("");
  const name = selection?.kind === "player" ? selection.name : null;
  const { data: dossier, error } = usePoll(
    name ? "/api/dossier?name=" + encodeURIComponent(name) : null,
  );
  const { data: profile, error: profileError } = usePoll(
    name && tab === "skills"
      ? "/api/public-profile?name=" + encodeURIComponent(name)
      : null,
    120000,
  );
  const npc =
    selection?.kind === "npc"
      ? state?.npcs.find((n: any) => n.id === selection.id)
      : null;
  const { data: npcDef } = usePoll(
    npc?.type !== undefined ? `/api/catalog?kind=npcs&q=${npc.type}` : null,
    600000,
  );
  useEffect(() => {
    setTab("overview");
    setNote(
      state?.watches.find((w: any) => w.name === name?.toLowerCase())?.note ??
        "",
    );
  }, [name, selection?.kind, selection?.id]);
  useEffect(()=>{if(gearRequest)setTab("gear");},[gearRequest]);
  const current = dossier?.current;
  const past = dossier?.record;
  const watched = state?.watches.some(
    (w: any) => w.name === name?.toLowerCase(),
  );
  const stale = state?.stale || dossier?.feedStale;
  const gear = !stale ? current?.appearance?.equipment : null;
  const p = selection?.kind === "npc" ? npc : current;
  const signals = p?.signals ?? {};
  const sourceName =
    selection?.kind === "catalog"
      ? selection.item.name
      : selection?.kind === "npc"
        ? (npc?.name ?? `NPC #${selection.id}`)
        : (current?.name ?? past?.display_name ?? name);
  return (
    <aside
      className={`inspector ${selection ? "open" : ""}`}
      aria-label="Entity inspector"
    >
      <div className="panel-heading">
        <span>ENTITY DOSSIER</span>
        <Button className="icon" aria-label="Close inspector" onClick={onClose}>
          <X size={15} />
        </Button>
      </div>
      {!selection ? (
        <Empty title="Select an entity">
          Inspect a player, NPC, item definition, or an observed event. Agents
          can use the same dossier through the local API.
        </Empty>
      ) : (
        <>
          <div className="identity">
            <div className="identity-icon">
              {selection.kind === "player" ? (
                <Users size={22} />
              ) : selection.kind === "npc" ? (
                <Target size={22} />
              ) : (
                <Box size={22} />
              )}
            </div>
            <div>
              <span className="eyebrow">
                {selection.kind === "catalog"
                  ? "CACHE DEFINITION"
                  : selection.kind === "npc"
                    ? "OBSERVED NPC"
                    : "PLAYER"}
              </span>
              <h2>{sourceName}</h2>{selection.kind==='player' && <Badge size="1" color={current?.allegiance==='friendly'?'green':current?.allegiance==='enemy'?'red':'gray'}>{current?.allegiance==='friendly'?'Friendly':current?.allegiance==='enemy'?'Enemy candidate':'Allegiance unknown'}</Badge>}
              <p>
                {p ? (
                  <>
                    Level{" "}
                    {p.combatLevel ??
                      npcDef?.results.find((r: any) => r.id === npc?.type)
                        ?.combatLevel ??
                      "—"}{" "}
                    <b>·</b> {p.x}, {p.z} <b>·</b> floor {p.plane}
                  </>
                ) : selection.kind === "catalog" ? (
                  `ID ${selection.item.id} · revision 289`
                ) : (
                  "Not in current roster"
                )}
              </p>
            </div>
          </div>
          {name&&<div className="gear-coverage-note"><strong>{stale?'Detailed feed is stale':current?.appearance?.transmog!=null?'Transformed appearance · equipment unavailable':current?.appearance?(current.appearance.equipment.length?`${current.appearance.equipment.length} equipped slots observed`:'Appearance received · no equipped items reported'):dossier?.feedStale?'Detailed feed is stale':current?.inArea?'In detail area · waiting for appearance':'Position only · outside the live detail area'}</strong><p>Use Inspect gear on a pane to focus the shared detail area. Intelligence alone does not expand coverage.</p></div>}
          {name && (
            <div className="entity-actions">
              <Button onClick={()=>onResearch({kind:"player",key:name,name})}><Activity size={14}/> Research holdings</Button>
              <Button
                className="primary"
                onClick={() => onFollow(name)}
                disabled={!current}
              >
                <Crosshair size={14} /> Follow
              </Button>
              <a className="button monitor-tab-link" href={monitorHref(name)} target="_blank" rel="noopener noreferrer">Monitor in new tab <ArrowUpRight size={13}/></a>
              <Button
                className={watched ? "watched" : ""}
                onClick={() => onWatch(name, note)}
              >
                <Bookmark size={14} fill={watched ? "currentColor" : "none"} />
                {watched ? "Watching" : "Watch"}
              </Button>
            </div>
          )}
          {selection.kind === "player" && (
            <SegmentedControlRadioGroup.Root className="detail-tabs dossier-segments" aria-label="Dossier view" value={tab} onValueChange={setTab}>
              {["overview", "gear", "skills", "history", "raw"].map((t) => (
                <SegmentedControlRadioGroup.Item key={t} value={t}>
                  {t === "skills" ? "Public data" : label(t)}
                </SegmentedControlRadioGroup.Item>
              ))}
            </SegmentedControlRadioGroup.Root>
          )}
          <div className="inspector-content">
            {error && <p className="error">{error}</p>}
            {selection.kind === "catalog" ? (
              <>
                {selection.item.assetKind==='items'&&<div className="item-hero"><ItemSprite id={selection.item.id} name={selection.item.name} size={72}/></div>}
                <p className="description">
                  {selection.item.description ||
                    "No examine description in this definition."}
                </p>
                <div className="facts">
                  <span>Base value</span>
                  <strong>
                    {selection.item.baseValue !== undefined
                      ? `${fmt(selection.item.baseValue)} gp`
                      : "—"}
                  </strong>
                  <span>Stackable</span>
                  <strong>
                    {selection.item.stackable === undefined
                      ? "—"
                      : selection.item.stackable
                        ? "Yes"
                        : "No"}
                  </strong>
                  <span>Members</span>
                  <strong>
                    {selection.item.members === undefined
                      ? "—"
                      : selection.item.members
                        ? "Yes"
                        : "No"}
                  </strong>
                  <span>Combat level</span>
                  <strong>{selection.item.combatLevel ?? "—"}</strong>
                </div>
                <p className="note">
                  Definition data. Base value is not a market price or potential
                  drop value.
                </p>
                <h4>Available actions</h4>
                <div className="chips">
                  {selection.item.actions?.map((a: any) => (
                    <span key={a.slot}>{a.name}</span>
                  ))}
                </div>
                <Json data={selection.item} />
              </>
            ) : selection.kind === "npc" ? (
              <>
                <Badge size="1" variant="soft" className="badge">
                  {npc ? "IN OBSERVATION AREA" : "NO LONGER OBSERVED"}
                </Badge>
                {npc && (
                  <Button className="wide" onClick={() => onFly(npc.x, npc.z)}>
                    <Navigation size={14} /> Fly to NPC
                  </Button>
                )}
                <p>
                  {
                    npcDef?.results.find((r: any) => r.id === npc?.type)
                      ?.description
                  }
                </p>
                <h4>Definition & signals</h4>
                <Json
                  data={{
                    definition: npcDef?.results.find(
                      (r: any) => r.id === npc?.type,
                    ),
                    observed: npc,
                  }}
                />
              </>
            ) : (
              <>
                {tab === "overview" && (
                  <>
                    <BotOperations context={dossier?.operations}/>
                    <div className="facts">
                      <span>Roster</span>
                      <strong
                        className={
                          current && !dossier?.rosterStale ? "mint" : "muted"
                        }
                      >
                        {current
                          ? dossier?.rosterStale
                            ? "Last known"
                            : "Online"
                          : "Not observed"}
                      </strong>
                      <span>Detailed appearance</span>
                      <strong>
                        {gear
                          ? "Observed"
                          : stale
                            ? "Stale"
                            : "Outside coverage / pending"}
                      </strong>
                      <span>Worn items</span>
                      <strong>{gear?.length ?? "Unknown"}</strong>
                      <span>Reported HP</span>
                      <strong>
                        {!stale && current?.health
                          ? `${current.health[0]} / ${current.health[1]}`
                          : "Unknown"}
                      </strong>
                      <span>First recorded here</span>
                      <strong>
                        {past
                          ? new Date(past.first_seen).toLocaleDateString()
                          : "—"}
                      </strong>
                      <span>Last roster observation</span>
                      <strong>{age(past?.last_seen)}</strong>
                    </div>
                    <h4>
                      Visible equipment <span>{gear?.length ?? "Unknown"}</span>
                    </h4>
                    {gear?.length ? (
                      gear.slice(0, 4).map((g: any) => (
                        <div className="mini-item" key={g.slot}>
                          <ItemSprite id={g.id} name={g.name}/>
                          <div>
                            {g.name}
                            <small>
                              {g.slot} · #{g.id}
                            </small>
                          </div>
                        </div>
                      ))
                    ) : (
                      <p className="note">
                        {gear ? "Current appearance reports no equipped items." : "No current equipment observation. Unknown does not mean empty."}
                      </p>
                    )}
                    <div className="boundary">
                      <LockKeyhole size={15} />
                      <div>
                        <strong>Carried inventory · unavailable</strong>
                        <p>
                          The public feed does not send it. Public bank previews
                          are in Public data.
                        </p>
                      </div>
                    </div>
                    <h4>
                      Nearby players <span>within 32 tiles</span>
                    </h4>
                    <div className="nearby-list">
                      {dossier?.relations?.nearby?.slice(0, 6).map((p: any) => (
                        <Button key={p.id} onClick={() => onFollow(p.name)}>
                          <Users size={12} />
                          <span>{p.name}</span>
                          <small>{p.distance} tiles</small>
                        </Button>
                      ))}
                    </div>
                    <h4>Watch note</h4>
                    <TextArea
                      size="2"
                      variant="surface"
                      aria-label="Watch note"
                      value={note}
                      onChange={(e) => setNote(e.target.value)}
                      placeholder="What should an agent watch for?"
                      maxLength={1000}
                    />
                    <Button
                      className="wide"
                      onClick={async () => {
                        try {
                          await request("/api/watches", { name, note });
                          setNoteStatus("Note saved");
                        } catch (e) {
                          setNoteStatus(String(e));
                        }
                      }}
                    >
                      <Bookmark size={13} /> Save note & watch
                    </Button>
                    {noteStatus && (
                      <p className="note" role="status">
                        {noteStatus}
                      </p>
                    )}
                    <h4>Recent signals</h4>
                    <div className="signal-facts">
                      {Object.entries(signals).length ? (
                        Object.entries(signals).map(([key, v]: any) => (
                          <div key={key}>
                            <span>{SIGNALS[key] ?? key}</span>
                            <strong>
                              {typeof v.value === "string"
                                ? v.value
                                : JSON.stringify(v.value)}
                            </strong>
                            <small>{age(v.observedAt)}</small>
                          </div>
                        ))
                      ) : (
                        <p className="note">No detailed signals yet.</p>
                      )}
                    </div>
                  </>
                )}
                {tab === "gear" && (
                  <>
                    <p className="source-line">
                      {gear
                        ? `Appearance observed ${age(current.appearanceAt)}`
                        : "No fresh appearance observation"}
                    </p>
                    <DeathRisk key={name} risk={stale?null:dossier?.deathRisk}/>
                    <details open className="equipment-slots"><summary>All equipment slots</summary>
                    {[
                      "Head",
                      "Cape",
                      "Neck",
                      "Weapon",
                      "Body",
                      "Shield",
                      "Legs",
                      "Hands",
                      "Feet",
                    ].map((slot) => {
                      const i = gear?.find((g: any) => g.slot === slot);
                      return (
                        <div className="gear-row" key={slot}>
                          <ItemSprite id={i?.id} name={i?.name} size={36}/>
                          <span>{slot}</span>
                          <strong>
                            {i?.name ?? (gear ? "No visible item" : "Unknown")}
                            <small>{i ? `#${i.id}` : ""}</small>
                          </strong>
                        </div>
                      );
                    })}
                    </details>
                    <p className="note">
                      Rings and ammunition are not encoded in appearance. Worn
                      items do not establish death loot.
                    </p>
                    {past?.gear && (
                      <details>
                        <summary>
                          Last recorded equipment · {age(past.gear_at)}
                        </summary>
                        <Json data={past.gear} />
                        <p className="note">
                          Historical observation; may no longer be worn.
                        </p>
                      </details>
                    )}
                  </>
                )}
                {tab === "skills" && (
                  <>
                    {profileError && <p className="error">{profileError}</p>}
                    {!profile && !profileError && (
                      <p className="note">Loading public hiscores…</p>
                    )}
                    {profile && (
                      <>
                        <p className="source-line">
                          PUBLIC SNAPSHOTS · fetched {age(profile.fetchedAt)}
                        </p>
                        <p className="note">
                          Publication time is unknown. These are not live
                          inventory or bank contents.
                        </p>
                        <h4>Skill profile</h4>
                        <Table.Table className="skills">
                          <Table.Header>
                            <Table.Row>
                              <Table.ColumnHeaderCell>
                                Skill
                              </Table.ColumnHeaderCell>
                              <Table.ColumnHeaderCell>
                                Level
                              </Table.ColumnHeaderCell>
                              <Table.ColumnHeaderCell>
                                Rank
                              </Table.ColumnHeaderCell>
                            </Table.Row>
                          </Table.Header>
                          <Table.Body>
                            {profile.skills?.rows.map((r: any) => (
                              <Table.Row key={r.skill}>
                                <Table.Cell title={r.time}>
                                  {r.skill}
                                </Table.Cell>
                                <Table.Cell>{fmt(r.level)}</Table.Cell>
                                <Table.Cell>{fmt(r.rank)}</Table.Cell>
                              </Table.Row>
                            ))}
                          </Table.Body>
                        </Table.Table>
                        {["outfit", "bank"].map((k) => (
                          <section key={k}>
                            <h4>Published {k}</h4>
                            {profile[k]?.entry ? (
                              <>
                                <div className="big-value">
                                  {profile[k].entry.valueLabel}
                                </div>
                                <p className="note">
                                  Rank #{profile[k].entry.rank} ·{" "}
                                  {k === "bank"
                                    ? "Partial item preview"
                                    : "Published outfit snapshot"}
                                </p>
                                {profile[k].entry.items.map((item: any) => (
                                  <div className="mini-item" key={item.id}>
                                    <ItemSprite id={item.id} name={item.label}/>
                                    <span>
                                      {item.label}
                                      <small>
                                        #{item.id}
                                        {item.count != null
                                          ? ` · quantity ${fmt(item.count)}`
                                          : ""}
                                      </small>
                                    </span>
                                  </div>
                                ))}
                              </>
                            ) : (
                              <p className="note">
                                No row for this player in the fetched public top
                                50. This tells us nothing about their actual {k}
                                .
                              </p>
                            )}
                            {profile[k]?.source && (
                              <a
                                className="source-link"
                                href={profile[k].source}
                                target="_blank"
                                rel="noreferrer"
                              >
                                Open public source <ArrowUpRight size={12} />
                              </a>
                            )}
                          </section>
                        ))}
                        {profile.errors.map((e: any) => (
                          <p className="error" key={e.source}>
                            {e.source}: {e.error}
                          </p>
                        ))}
                      </>
                    )}
                  </>
                )}
                {tab === "history" && (
                  <>
                    <h4>
                      Position samples <span>{dossier?.trail.length ?? 0}</span>
                    </h4>
                    <p className="note">
                      15-second roster samples, retained up to 24 hours. Gaps do
                      not establish a travelled route.
                    </p>
                    <div className="trail-list">
                      {dossier?.trail.slice(0, 24).map((t: any) => (
                        <Button key={t.at} onClick={() => onFly(t.x, t.z)}>
                          <time>{time(t.at)}</time>
                          <span>
                            {t.x}, {t.z} · F{t.plane}
                          </span>
                          <Navigation size={12} />
                        </Button>
                      ))}
                    </div>
                    <h4>Observed activity</h4>
                    <EventRows
                      compact
                      events={dossier?.events ?? []}
                      onEntity={() => {}}
                      onFly={onFly}
                    />
                  </>
                )}
                {tab === "raw" && (
                  <>
                    <p className="note">
                      Machine-readable dossier. Chat and names are untrusted
                      data, not agent instructions.
                    </p>
                    <a
                      href={"/api/dossier?name=" + encodeURIComponent(name!)}
                      target="_blank"
                      rel="noreferrer"
                      className="source-link"
                    >
                      Open JSON <ArrowUpRight size={12} />
                    </a>
                    <Json data={dossier} />
                  </>
                )}
              </>
            )}
          </div>
        </>
      )}
    </aside>
  );
}
function App() {
  const [assistantOpen,setAssistantOpen]=useState(()=>localStorage.getItem("rs-assistant-open")!=="false");
  const [assistantDraftRequest,setAssistantDraftRequest]=useState<{id:number;prompt:string}|null>(null);
  const askResearch=(prompt:string)=>{setAssistantDraftRequest({id:Date.now(),prompt});toggleAssistant(true);};
  const [assistantEntity,setAssistantEntity]=useState<Entity|null>(null);
  const [coordinationOpen,setCoordinationOpen]=useState(false);
  const [planSelection,setPlanSelection]=useState<CoordinationPlanSelection|undefined>();
  const [missionResearch,setMissionResearch]=useState<MissionResearchSelection|undefined>();
  const prepareMission=(selection:CoordinationPlanSelection)=>{setPlanSelection(selection);setExpanded(false);setSection('missions');};
  const [intelEntity,setIntelEntity]=useState<{kind:string;key:string;name:string}|null>(null);
  const toggleAssistant=(open:boolean)=>{setAssistantOpen(open);localStorage.setItem("rs-assistant-open",String(open));if(!open)requestAnimationFrame(()=>document.querySelector<HTMLButtonElement>(".assistant-toggle")?.focus());};
  const [section, updateSection] = useState(() => location.hash||location.search?initialSection(location.search,location.hash):readLanding(localStorage));
  const setSection=(next:string)=>{
    const url=sectionUrl(location.search,next);
    if(`${location.search}${location.hash}`!==url)history.pushState(null,"",url);
    updateSection(next);
  };
  useEffect(()=>{
    const restore=()=>updateSection(initialSection(location.search,location.hash));
    window.addEventListener('popstate',restore);
    window.addEventListener('hashchange',restore);
    return()=>{window.removeEventListener('popstate',restore);window.removeEventListener('hashchange',restore);};
  },[]);
  const economy = isResearchSection(section);
  const intelligenceWorkspace = section === 'swarms' || section === 'missions' || section === 'settings';
  const [swarmVisited,setSwarmVisited]=useState(section==='swarms');
  useEffect(()=>{if(section==='swarms')setSwarmVisited(true);},[section]);
  const [missionVisited,setMissionVisited]=useState(section==='missions');
  const [returnToMission,setReturnToMission]=useState(false);
  useEffect(()=>{if(section==='missions')setMissionVisited(true);},[section]);
  const [researchVisited, setResearchVisited] = useState(economy);
  useEffect(() => { if (economy) setResearchVisited(true); }, [economy]);
  const [researchNavigationId,setResearchNavigationId]=useState(0);
  const navigateResearch=(next:ResearchSection)=>{setSection(next);setResearchNavigationId(v=>v+1);};
  const lastEconomy = useRef<string>(readLanding(localStorage)), lastWorld = useRef("map");
  const [researchContext,setResearchContext]=useState("Economic intelligence");
  const [swarmRequest,setSwarmRequest]=useState<{id:string;requestId:number}|null>(null);
  const [researchRequest,setResearchRequest]=useState<{entity:Entity;id:number}|null>(null);
  useEffect(()=>{if(economy)lastEconomy.current=section;else if(section!=='settings')lastWorld.current=section;},[section,economy]);
  const switchMode=(mode:string)=>setSection(mode==="blackrock"?lastEconomy.current:lastWorld.current);
  const openResearch=(entity:Entity)=>{setResearchRequest({entity,id:Date.now()});setSection("market");};
  const { data: state, error: stateError } = usePoll(economy ? null : "/api/state", 1000);
  const researchSource = useResearchLayers(state, !!stateError || economy);
  const { data: feed } = usePoll(economy ? null : "/api/events?limit=80", 2000);
  const { data: tactical, error: tacticalError } = usePoll(economy ? null : "/api/tactical", 3000);
  const { data: polledOperations, error: operationsError } = usePoll("/api/operations", 30000);
  const [refreshedOperations, setRefreshedOperations] = useState<any>(null);
  const operations = refreshedOperations ?? polledOperations;
  useEffect(() => { setRefreshedOperations(null); }, [polledOperations]);
  const [selectedTeam, setSelectedTeam] = useState("all");
  const teamNames = useMemo(() => selectedTeam === "all" ? undefined : operations ? bindingsForTeam(operations, selectedTeam).map(b => b.name) : [], [selectedTeam, operations]);
  const teamNameKeys = useMemo(() => teamNames ? new Set(teamNames.map(botKey)) : null, [teamNames]);
  async function refreshOperations() {
    const response = await request("/api/operations/refresh", {});
    setRefreshedOperations(response);
  }
  const [list, setList] = useState("players"),
    [query, setQuery] = useState(""),
    [minimum, setMinimum] = useState(0),
    [selection, setSelection] = useState<any>(null),
    [error, setError] = useState(""),
    [expanded, setExpanded] = useState(true);
  const [showLayers, setShowLayers] = useState(false);
  const [layers, setLayers] = useState<Record<string, boolean>>({
    showPlayers: true,
    showNpcs: true,
    showPlayerNames: true,
    showNpcNames: false,
    showChat: true,
    showCombat: true,
  });
  const [atlasOpen, setAtlasOpen] = useState(false);
  const [rosterOpen, setRosterOpen] = useState(()=>!window.matchMedia('(max-width:1100px)').matches);
  useEffect(()=>{const query=window.matchMedia('(max-width:1100px)');const shrink=()=>{if(query.matches)setRosterOpen(false);};query.addEventListener('change',shrink);return()=>query.removeEventListener('change',shrink);},[]);
  const [onlyMyBots, setOnlyMyBots] = useState(() => {
    try { return (sessionStorage.getItem("rs-watch:only-my-bots") ?? (new URLSearchParams(location.search).has("follow") ? "false" : localStorage.getItem("rs-watch:only-my-bots"))) === "true"; } catch { return false; }
  });
  useEffect(() => {
    try { sessionStorage.setItem("rs-watch:only-my-bots", String(onlyMyBots)); } catch {}
  }, [onlyMyBots]);
  const [x, setX] = useState("3061"),
    [z, setZ] = useState("3877"),
    [height, setHeight] = useState("70"),
    [eventKind, setEventKind] = useState("all"),
    [eventQuery, setEventQuery] = useState(""),
    [onlyWatched, setOnlyWatched] = useState(false),
    [catalogKind, setCatalogKind] = useState("items"),
    [catalogQuery, setCatalogQuery] = useState(""),
    [limit, setLimit] = useState(100),
    [waypointLabel, setWaypointLabel] = useState(""),
    [viewerReady, setViewerReady] = useState(false),
    [copied, setCopied] = useState(false),
    [catalogOffset, setCatalogOffset] = useState(0);
  const iframe = useRef<HTMLIFrameElement>(null);
  const acked = useRef("");
  const [tabCommands] = useState(() => new ViewerCommands());
  const [viewCommand, setViewCommand] = useState<ViewCommand | null>(null);
  const commandFrame = useRef<HTMLIFrameElement | null>(null);
  const [tabCamera, setTabCamera] = useState<ViewCamera>(() => {
    let saved: string | null = null;
    try { saved = sessionStorage.getItem("rs-watch:tab-camera"); } catch {}
    return initialViewCamera(location.search, saved);
  });
  const {data:coordinationData,error:coordinationError}=usePoll(section==='missions'||section==='map'&&coordinationOpen?'/api/coordination/context':null,2000);
  const mapContext:CoordinationContext|undefined=coordinationData?{...coordinationData,...(coordinationError?{sourceState:'unavailable' as const,protectionReady:false}:{}),point:Number.isInteger(tabCamera.combatPlane)&&[0,1,2,3].includes(tabCamera.combatPlane!)?{worldId:'main',x:Math.round(tabCamera.x),z:Math.round(tabCamera.z),level:tabCamera.combatPlane as 0|1|2|3}:undefined}:undefined;
  const tabCameraRef = useRef(tabCamera); tabCameraRef.current = tabCamera;
  const primaryFrame=useRef<HTMLIFrameElement|null>(null);
  const [primaryFollow,setPrimaryFollow]=useState(tabCamera.follow);
  const [activeMonitor,setActiveMonitor]=useState<string|null>(null);
  const activeMonitorRef=useRef(activeMonitor);activeMonitorRef.current=activeMonitor;
  const [gearRequest,setGearRequest]=useState(0);
  const [paneFilters,setPaneFilters]=useState<Record<string,PaneFilter>>(()=>{try{return readPaneFilters(sessionStorage.getItem('rs-watch:pane-filters'));}catch{return {};}});
  useEffect(()=>{try{sessionStorage.setItem('rs-watch:pane-filters',JSON.stringify(paneFilters));}catch{}},[paneFilters]);
  const changePaneFilter=(key:string,value:PaneFilter)=>setPaneFilters(old=>({...old,[key]:value}));
  // Defaults from explicit hotspot jumps are separate from the user's overrides.
  const [hotspotFilters,setHotspotFilters]=useState<Record<string,PaneFilter>>(()=>{try{return readPaneFilters(sessionStorage.getItem('rs-watch:hotspot-filters'));}catch{return {};}});
  useEffect(()=>{try{sessionStorage.setItem('rs-watch:hotspot-filters',JSON.stringify(hotspotFilters));}catch{}},[hotspotFilters]);
  const effectivePaneFilters=useMemo(()=>({...hotspotFilters,...paneFilters}),[hotspotFilters,paneFilters]);
  const activePaneFilterKey=()=>activeMonitorRef.current?'monitor:'+activeMonitorRef.current:iframe.current?.dataset.paneFilterKey??'primary:0';
  const inspectPane=(name:string)=>{setSelection({kind:'player',name});setExpanded(false);setGearRequest(v=>v+1);};
  const [followMonitors,setFollowMonitors] = useState<string[]>(()=>{
    try { const names=JSON.parse(sessionStorage.getItem('rs-watch:follow-monitors')||'[]');return Array.isArray(names)?names.reduce((all:string[],name:unknown)=>typeof name==='string'?addFollowMonitor(all,name,tabCamera.follow):all,[]):[]; } catch {return [];}
  });
  useEffect(()=>{try{sessionStorage.setItem('rs-watch:follow-monitors',JSON.stringify(followMonitors));}catch{}},[followMonitors]);
  function addMonitor(name:string) { setFollowMonitors(names=>addFollowMonitor(names,name,tabCameraRef.current.follow)); }
  const [agentControl, setAgentControl] = useState(false);
  const agentControlRef = useRef(false); agentControlRef.current = agentControl;
  const observedFollow = useRef<string | null>(null);
  const [viewerSrc, setViewerSrc] = useState<string | null>(null);
  const [dualWorld, setDualWorld] = useState(false);
  const [paneVersion, setPaneVersion] = useState(0);
  const allegiance = useMemo(() => state?.friendlies ? {names:state.friendlies.names,ready:state.friendlies.ready,onlyFriendlies:onlyMyBots,visibleNames:teamNames} : null, [state?.friendlies?.refreshedAt,onlyMyBots,teamNames]);
  const { data: filteredFeed } = usePoll(
    section === "signals"
      ? "/api/events?limit=100" +
          (eventKind !== "all"
            ? "&kind=" + encodeURIComponent(eventKind)
            : "") +
          (onlyWatched ? "&watched=true" : "")
      : null,
    2000,
  );
  const { data: catalog } = usePoll(
    section === "catalog"
      ? `/api/catalog?kind=${catalogKind}&q=${encodeURIComponent(catalogQuery)}&offset=${catalogOffset}`
      : null,
    600000,
  );
  const { data: caps } = usePoll(
    section === "agent" ? "/api/capabilities" : null,
    600000,
  );
  const { data: board, error: boardError } = usePoll(
    section === "wealth"
      ? `/api/leaderboard?kind=${catalogKind === "npcs" ? "outfit" : "bank"}`
      : null,
    300000,
  );
  const run = async (path: string, body?: any, method?: string) => {
    try {
      const result = await request(path, body, method);
      setError("");
      return result;
    } catch (e) {
      setError(e instanceof Error ? e.message : "Request failed");
      return null;
    }
  };
  async function command(c: any) {
    try {
      const result = tabCommands.queue(c);
      if(c.kind==='goto'||c.kind==='follow') {
        const key=activePaneFilterKey();
        setHotspotFilters(old=>{const next={...old};delete next[key];return next;});
      }
      commandFrame.current = iframe.current;
      setViewCommand(result);
      setError("");
      return result;
    } catch (e) {
      setError(e instanceof Error ? e.message : "Invalid camera command");
      return null;
    }
  }
  async function fly(cx: number, cz: number, h = 70, plane = 0, hotspotFilter?:'all'|'friendly') {
    const key=activePaneFilterKey();
    setX(String(Math.round(cx)));
    setZ(String(Math.round(cz)));
    setSection("map");
    const result=await command({ kind: "goto", x: cx, z: cz, height: h, plane });
    if(result&&hotspotFilter)setHotspotFilters(old=>({...old,[key]:hotspotFilter}));
  }
  async function selectPlayer(name: string) {
    if(window.matchMedia('(max-width:1100px)').matches)setRosterOpen(false);
    setSelection({ kind: "player", name });
    const current = tabCommands.current?.status === 'pending' && tabCommands.current.kind === 'follow' ? tabCommands.current.name : tabCameraRef.current.follow;
    if (current && botKey(current)!==botKey(name)) {
      setFollowMonitors(names=>addFollowMonitor(names,name,current));
      return;
    }
    await command({ kind: "follow", name });
  }
  async function watch(name: string, note = "") {
    const existing = state?.watches.some(
      (w: any) => w.name === name.toLowerCase(),
    );
    await run(
      existing
        ? "/api/watches?name=" + encodeURIComponent(name)
        : "/api/watches",
      existing ? undefined : { name, note },
      existing ? "DELETE" : undefined,
    );
  }
  useEffect(() => {
    if (viewerSrc || !state) return;
    const c = tabCameraRef.current;
    const params = new URLSearchParams({
      cx: String(c.x),
      cy: String(c.height),
      cz: String(c.z),
      p: String(c.pitch),
      y: String(c.yaw),
      plane: String(c.plane??0),
      v: "1",
      native: "1",
      multifollow: "1",
      live: `ws://${location.host}/viewer-feed`,
    });
    if (c.follow) params.set("follow", c.follow);
    setViewerSrc("/viewer/?" + params.toString());
    setX(String(Math.round(c.x)));
    setZ(String(Math.round(c.z)));
    setHeight(String(Math.round(c.height)));
  }, [state, viewerSrc]);
  useEffect(() => {
    function handler(e: MessageEvent) {
      if (e.origin !== location.origin) return;
      // A pending command stays tied to the pane selected when it was queued.
      if (e.data?.type === "rs-watch-ack" && e.source === commandFrame.current?.contentWindow) {
        const result = tabCommands.ack(e.data.id, e.data.status, e.data.error);
        if (result) {
          setViewCommand(result);
          if (result.status === "failed") setError(result.error || "Viewer command failed");
          return;
        }
        if (String(e.data.id).startsWith("view-")) return;
      }
      if (e.source !== iframe.current?.contentWindow) return;
      if (e.data?.type === "rs-watch-follow-request" && typeof e.data.name === 'string') {
        setSelection({kind:'player',name:e.data.name});
        setFollowMonitors(names=>addFollowMonitor(names,e.data.name,tabCameraRef.current.follow));
        return;
      }
      if(e.data?.type === "rs-watch-native-state") setAtlasOpen(e.data.worldMapOpen===true);
      if (e.data?.type === "rs-watch-camera") {
        const camera = e.data.camera;
        if (!camera || ![camera.x,camera.z,camera.height,camera.pitch,camera.yaw].every(Number.isFinite)) return;
        const local = {...camera, at:Date.now()};
        setTabCamera(local);
        tabCameraRef.current = local;
        if(activeMonitorRef.current===null)try { sessionStorage.setItem("rs-watch:tab-camera", JSON.stringify(local)); } catch {}
        setViewerReady(true);
        // Shared agent telemetry is published only by an explicitly opted-in tab.
        if (agentControlRef.current) run("/api/viewer-state", local);
        if (camera.follow !== observedFollow.current) {
          observedFollow.current = camera.follow;
          if (camera.follow) setSelection({ kind: "player", name: camera.follow });
        }
      }
      if (e.data?.type === "rs-watch-ack" && agentControlRef.current) {
        acked.current = e.data.id;
        run("/api/command-ack", e.data);
      }
    }
    window.addEventListener("message", handler);
    return () => window.removeEventListener("message", handler);
  }, []);
  useEffect(() => {
    if (!viewerReady) return;
    iframe.current?.contentWindow?.postMessage(
      {type: 'rs-watch-native-ui', action: 'chrome', open: expanded},
      location.origin,
    );
  }, [expanded, viewerReady, paneVersion]);

  useEffect(() => {
    if (!viewCommand || viewCommand.status !== "pending") return;
    const target = commandFrame.current ?? iframe.current;
    if (target && !target.isConnected) {
      const result = tabCommands.ack(viewCommand.id, "failed", "The selected view was closed");
      if (result) setViewCommand(result);
      return;
    }
    if (!commandFrame.current && target) commandFrame.current = target;
    const result = tabCommands.deliver(viewerReady ? target?.contentWindow ?? null : null, location.origin);
    if (result && result !== viewCommand) setViewCommand(result);
  }, [viewCommand, viewerReady, paneVersion, state?.serverTime]);
  useEffect(() => {
    document.title = `${economy ? researchSection(section as ResearchSection).label : section === "settings" ? "Settings" : section === "swarms" ? "Swarm intelligence" : section === "missions" ? "Command workspace" : tabCamera.follow || "World"} · ${economy ? "RS-BLACKROCK" : "RS-PALANTIR"}`;
    const icon=document.querySelector<HTMLLinkElement>('link[rel="icon"]');if(icon)icon.href=economy?"/brand/rs-blackrock.png":"/brand/rs-palantir.png";
  }, [tabCamera.follow, section, economy]);
  useEffect(() => {
    const c = state?.command;
    if (
      agentControl && c?.status === "pending" &&
      c.id !== acked.current &&
      Date.now() - c.at < 30000 &&
      viewerReady
    ) {
      iframe.current?.contentWindow?.postMessage(
        { type: "rs-watch-command", command: c },
        location.origin,
      );
    }
  }, [
    state?.command?.id,
    state?.command?.status,
    agentControl,
    viewerReady,
    state?.serverTime,
    paneVersion,
  ]);
  useEffect(() => {
    if (localStorage.getItem("rs-watch:migrated-v2")) return;
    try {
      const names = JSON.parse(
        localStorage.getItem("rs-watch:watchlist") || "[]",
      );
      if (Array.isArray(names))
        Promise.all(
          names
            .filter((n) => typeof n === "string")
            .slice(0, 100)
            .map((name) => request("/api/watches", { name, note: "" })),
        )
          .then(() => localStorage.setItem("rs-watch:migrated-v2", "true"))
          .catch(() => {});
    } catch {}
  }, []);

  useEffect(() => {
    const c = state?.command;
    if (agentControl && c?.kind === "layers" && c.status === "applied")
      setLayers((prev) => ({
        ...prev,
        ...Object.fromEntries(
          Object.keys(prev)
            .filter((k) => typeof c[k] === "boolean")
            .map((k) => [k, c[k]]),
        ),
      }));
  }, [state?.command?.id, state?.command?.status, agentControl]);
  useEffect(() => {
    setLimit(100);
  }, [query, list, minimum, onlyMyBots, selectedTeam]);
  const roster = useMemo(() => {
    let rows = list === "npcs" ? (state?.npcs ?? []) : (state?.players ?? []);
    const q = query.toLowerCase();
    return rows
      .filter(
        (p: any) =>
          p.name.toLowerCase().includes(q) &&
          (list === "npcs" || !onlyMyBots || p.allegiance === "friendly") &&
          (list === "npcs" || !teamNameKeys || teamNameKeys.has(botKey(p.name))) &&
          (list !== "watch" ||
            state.watches.some((w: any) => w.name === p.name.toLowerCase())) &&
          (p.combatLevel ?? 126) >= minimum,
      )
      .sort((a: any, b: any) =>
        a.name.localeCompare(b.name, undefined, { numeric: true }),
      );
  }, [state?.players, state?.npcs, state?.watches, list, query, minimum, onlyMyBots, teamNameKeys]);
  const events = (filteredFeed?.events ?? []).filter(
    (e: any) =>
      (eventKind === "all" || e.kind === eventKind) &&
      (!onlyWatched || state?.watches.some((w: any) => w.name === e.entity)) &&
      JSON.stringify(e).toLowerCase().includes(eventQuery.toLowerCase()),
  );
  const stale = !state || state.stale || !!stateError;
  const nativeViewerParams = new URLSearchParams(viewerSrc?.split('?')[1] ?? '');
  if (tabCamera) {
    const c = tabCamera;
    for (const [key, value] of Object.entries({cx:c.x, cy:c.height, cz:c.z, p:c.pitch, y:c.yaw, plane:c.plane??0})) nativeViewerParams.set(key, String(value));
    if (c.follow) nativeViewerParams.set('follow', c.follow);
    else nativeViewerParams.delete('follow');
  }
  const nativeViewerHref = '/viewer/?' + nativeViewerParams.toString();
  const presets = state?.waypoints ?? [];
  return (
    <div className={`app ${section==='settings'?'settings-mode':''} ${economy?'economy-mode':''} ${intelligenceWorkspace?'intelligence-mode':''} ${section==='map'?'world-mode':''} ${followMonitors.length||dualWorld?'split-world':''} ${section==='operations'?'operations-mode':''} ${atlasOpen?'atlas-open':''} ${selection?'has-selection':''} ${rosterOpen?'':'roster-hidden'} ${expanded ? "expanded" : ""}`}>
      <div className="research-mode-dock" role="navigation" aria-label="Switch research mode">
        <div className="research-dock-controls"><SegmentedControlRadioGroup.Root className="research-mode-toggle" aria-label="Application mode" value={economy?"blackrock":"palantir"} onValueChange={switchMode}>
          <SegmentedControlRadioGroup.Item value="palantir"><img src="/brand/rs-palantir.png" alt=""/><span>RS-PALANTIR<small>World & players</small></span></SegmentedControlRadioGroup.Item>
          <SegmentedControlRadioGroup.Item value="blackrock"><img src="/brand/rs-blackrock.png" alt=""/><span>RS-BLACKROCK<small>Economic intelligence</small></span></SegmentedControlRadioGroup.Item>
        </SegmentedControlRadioGroup.Root>
        {!economy&&section==='map'&&expanded&&<div className="intel-quick-nav" role="navigation" aria-label="Palantir workspaces">{[{key:'map',name:'World',icon:Globe},{key:'swarms',name:'Swarms',icon:Users},{key:'missions',name:'Command',icon:Target}].map(({key,name,icon:Icon})=><Button key={key} size="1" variant={section===key?'soft':'ghost'} aria-current={section===key?'page':undefined} onClick={()=>{setExpanded(false);setSection(key);}}><Icon size={14}/><span>{name}</span></Button>)}</div>}
        <Button aria-current={section==='settings'?'page':undefined} onClick={()=>{setExpanded(false);setSection('settings');}}>Settings</Button>
        <Button className="assistant-toggle" variant={assistantOpen?"soft":"surface"} color="blue" aria-label="Toggle research assistant" aria-expanded={assistantOpen} onClick={()=>toggleAssistant(!assistantOpen)}><Sparkles size={16}/><span>Assistant</span></Button>
        </div><LivePopulation world={state} worldError={!!stateError} poll={economy}/><div className="research-dock-context"><div className="research-resume"><span>{economy?"WORLD CONTEXT":"RESEARCH CONTEXT"}</span><Button variant="ghost" color="gray" onClick={()=>switchMode(economy?"palantir":"blackrock")}><ArrowUpRight size={14}/>{economy?(selection?.name??tabCamera.follow??"Return to world"):`Back to ${researchContext}`}</Button></div>
        {!economy&&selection?.kind==='player'&&<Button className="research-player-link" onClick={()=>openResearch({kind:'player',key:selection.name,name:selection.name})}><Activity size={14}/>Research {selection.name}</Button>}</div>
      </div>
      <header>
        <a className="brand" href={economy?"#economy":"#world"} aria-label={economy?"RS-BLACKROCK market home":"RS-PALANTIR world home"} onClick={e=>{e.preventDefault();economy?navigateResearch("economy"):setSection("map")}}>
          <span className={`mode-emblem ${economy?"mode-emblem-blackrock":"mode-emblem-palantir"}`}><img src={economy?"/brand/rs-blackrock.png":"/brand/rs-palantir.png"} alt=""/></span>
          <span>{economy?"RS-BLACKROCK":"RS-PALANTIR"}<small>{economy?"ECONOMIC INTELLIGENCE":"WORLD INTELLIGENCE"}</small></span>
        </a>
        <nav aria-label="Main navigation">
          {(economy ? [
            { key: "watchlists", name: "Watchlists", icon: Bookmark },
            { key: "market", name: "Exchange", icon: Search },
            { key: "economy", name: "Economy", icon: Activity },
            { key: "fleet", name: "Fleet", icon: Users },
          ] : [
            { key: "map", name: "World", icon: Globe },
            { key: "swarms", name: "Swarms", icon: Users },
            { key: "missions", name: "Command", icon: Target },
          ]).map(({ key, name, icon: Icon }) => (
            <Button
              key={key}
              aria-label={name}
              aria-current={section === key ? "page" : undefined}
              className={section === key ? "active" : ""}
              onClick={() => isResearchSection(key)?navigateResearch(key):setSection(key)}
            >
              <Icon size={15} />
              <span>{name}</span>
              {key === "signals" && <i className="nav-dot" />}
            </Button>
          ))}
          <select className="br-more-nav" aria-label="More sections" value={["operations","swarms","missions","signals","wealth","catalog","agent"].includes(section)?section:""} onChange={e=>{if(e.target.value)setSection(e.target.value)}}>
            <option value="" disabled>More</option><option value="operations">Operations</option><option value="swarms">Swarms</option><option value="missions">Command workspace</option><option value="signals">Signals</option><option value="wealth">Wealth</option><option value="catalog">Catalog</option><option value="agent">Agent API</option>
          </select>
        </nav>
        <div className="header-right">
          <span className="read-only">
            <Eye size={13} /> OBSERVER
          </span>
          {!economy && <Badge
            size="1"
            color={stale ? "amber" : "green"}
            variant="soft"
            className={`status ${stale ? "bad" : ""}`}
          >
            <i />
            {stale
              ? state?.status === "live"
                ? "SYNCING"
                : "RECONNECTING"
              : "LIVE"}
          </Badge>}
        </div>
      </header>
      <MissionStatusBar onNavigate={()=>setSection("missions")}/>
      <div className="research-body"><div className="research-content">
      <div className="blackrock-host" hidden={!economy}>{returnToMission&&<Button onClick={()=>setSection('missions')}>Return to mission & supplies</Button>}{(economy || researchVisited) && <BlackrockWorkspace onOpenSwarm={id=>{setSwarmRequest({id,requestId:Date.now()});setExpanded(false);setSection("swarms");}} onAsk={askResearch} navigationId={researchNavigationId} onNavigate={navigateResearch} active={economy} researchRequest={researchRequest} onContext={setResearchContext} onEntityContext={setAssistantEntity} section={(economy?section:lastEconomy.current) as ResearchSection} onOpenWorld={(name?:string)=>{setSection("map");if(name){setExpanded(false);void selectPlayer(name);}}} />}</div>
      <div className="world-host" hidden={economy}>
      <div className="metrics" aria-label="World statistics">
        {[
          {
            icon: Users,
            value: state?.onlineCount,
            label: "Online players",
            note: "Global roster",
          },
          {
            icon: Target,
            value: state?.npcCount,
            label: "World NPCs",
            note: "Across the world",
          },
          {
            icon: Eye,
            value: state?.npcs.length,
            label: "In observation area",
            note: "Detailed live coverage",
          },
          {
            icon: Activity,
            value: state?.intel.events,
            label: "Recorded events",
            note: "Your local history",
          },
          {
            icon: Database,
            value: state?.intel.entities,
            label: "Entity dossiers",
            note: "Searchable intelligence",
          },
        ].map(({ icon: Icon, value, label: caption, note }) => (
          <Card key={caption} className="metric-card" size="1">
            <div className="metric-caption">
              <Icon size={14} />
              {caption}
            </div>
            <div className="metric-value">
              {fmt(value)}
              <span>{note}</span>
            </div>
          </Card>
        ))}
      </div>
      {(stateError || error) && (
        <div className="error-banner" role="alert">
          {stateError ? "Local API disconnected. Retrying…" : error}
          <Button
            className="icon"
            onClick={() => setError("")}
            aria-label="Dismiss error"
          >
            <X size={14} />
          </Button>
        </div>
      )}
      <main>
        <aside className="roster">
          <div className="panel-heading">
            <span>WORLD ENTITIES</span><Button size="1" variant="ghost" aria-label="Close player roster" onClick={()=>setRosterOpen(false)}><X size={14}/></Button>
            <span className="tag">
              {state?.rosterStale ? "LAST KNOWN" : "LIVE ROSTER"}
            </span>
          </div>
          <div className="roster-tabs">
            {["players", "npcs", "watch", "places"].map((t) => (
              <Button
                key={t}
                aria-pressed={list === t}
                className={list === t ? "active" : ""}
                onClick={() => setList(t)}
              >
                {t === "watch" ? <Bookmark size={13} /> : null}
                {t === "npcs" ? "NPCs" : label(t)}
                {t === "watch" && <small>{state?.watches.length ?? 0}</small>}
              </Button>
            ))}
          </div>
          {list === "places" ? (
            <div className="places">
              <h4>Jump to region</h4>
              {LOCATIONS.map((p) => (
                <Button key={p.label} onClick={() => fly(p.x, p.z, p.height, p.plane??0)}>
                  <MapPin size={14} />
                  <span>
                    {p.label}
                    <small>
                      {p.x}, {p.z}
                    </small>
                  </span>
                  <ChevronRight size={14} />
                </Button>
              ))}
              <h4>
                Saved waypoints <span>{presets.length}</span>
              </h4>
              {presets.map((p: any) => (
                <Button key={p.id} onClick={() => fly(p.x, p.z, p.height, p.plane??0)}>
                  <Bookmark size={14} />
                  <span>
                    {p.label}
                    <small>
                      {p.x}, {p.z}
                    </small>
                  </span>
                </Button>
              ))}
              <label className="field">
                Waypoint name
                <TextField.Input
                  size="2"
                  variant="surface"
                  value={waypointLabel}
                  onChange={(e) => setWaypointLabel(e.target.value)}
                  placeholder="North gate observation"
                />
              </label>
              <Button
                className="wide"
                disabled={
                  !waypointLabel.trim() || !tabCamera.at || Date.now()-tabCamera.at>5000
                }
                onClick={async () => {
                  const c = tabCamera;
                  const r = await run("/api/waypoints", {
                    label: waypointLabel,
                    x: c.x,
                    z: c.z,
                    height: c.height,
                  });
                  if (r) setWaypointLabel("");
                }}
              >
                <Plus size={14} /> Save camera location
              </Button>
            </div>
          ) : (
            <>
              <label className="search">
                <Search size={15} />
                <TextField.Input
                  size="2"
                  variant="surface"
                  aria-label="Search entities"
                  value={query}
                  onChange={(e) => setQuery(e.target.value)}
                  placeholder={
                    list === "npcs" ? "Search nearby NPCs…" : "Find a player…"
                  }
                />
                {query && (
                  <Button
                    className="icon"
                    aria-label="Clear search"
                    onClick={() => setQuery("")}
                  >
                    <X size={13} />
                  </Button>
                )}
              </label>
              {list !== "npcs" && <div className="ops-roster-filter"><TeamFilter data={operations} value={selectedTeam} onChange={setSelectedTeam}/></div>}
              <div className="filter-line">
                <span>{roster.length} results</span>
                {list !== "npcs" && (
                  <label>
                    Combat ≥{" "}
                    <FilterSelect
                      label="Minimum combat level"
                      value={String(minimum)}
                      onValueChange={(v) => setMinimum(Number(v))}
                      options={[0, 50, 80, 100, 120].map((n) => ({
                        value: String(n),
                        label: n ? String(n) : "Any",
                      }))}
                    />
                  </label>
                )}
              </div>
              <div className="list-head">
                <span>ENTITY</span>
                <span>LVL</span>
                <span>LOCATION</span>
              </div>
              <div className="entity-list">
                {roster.slice(0, limit).map((p: any) => (
                  <Button
                    className={`entity-row allegiance-${list==='npcs'?'npc':(p.allegiance??'unknown')} ${(selection?.kind === "player" && selection.name.toLowerCase() === p.name.toLowerCase()) || (selection?.kind === "npc" && selection.id === p.id && list === "npcs") ? "selected" : ""}`}
                    key={p.id}
                    aria-label={`Inspect ${p.name}${list === "npcs" ? ` NPC ${p.id}` : ""}`}
                    onClick={() =>
                      list === "npcs"
                        ? (setSelection({ kind: "npc", id: p.id }),
                          fly(p.x, p.z))
                        : selectPlayer(p.name)
                    }
                  >
                    <span className="entity-name" title={list==='npcs'?'NPC':p.allegiance==='friendly'?'Friendly · project roster':p.allegiance==='enemy'?'Enemy candidate · not on friendly roster':'Allegiance unknown'}>
                      <i
                        className={
                          p.observed || list === "npcs" ? "observed" : ""
                        }
                      />
                      {p.name}
                      {state?.watches.some(
                        (w: any) => w.name === p.name.toLowerCase(),
                      ) && <Bookmark size={10} />}
                    </span>
                    <span>{p.combatLevel ?? "—"}</span>
                    <span>
                      {p.x}, {p.z}
                    </span>
                  </Button>
                ))}
                {roster.length > limit && (
                  <Button
                    className="wide"
                    onClick={() => setLimit((v) => v + 100)}
                  >
                    Load 100 more <ChevronDown size={13} />
                  </Button>
                )}
                {!roster.length && (
                  <Empty
                    title={
                      list === "watch"
                        ? "No watched players online"
                        : "No matching entities"
                    }
                  >
                    {list === "npcs"
                      ? "NPC details cover the camera observation area."
                      : onlyMyBots ? "No known friendly identities match. Turn off Friendlies only to see everyone." : "Try another name or combat filter."}
                  </Empty>
                )}
                {list === "watch" &&
                  state?.watches
                    .filter((w: any) => !onlyMyBots || state?.friendlies?.names.some((name: string) => name.trim().toLowerCase().replace(/[_ -]/g, "") === w.name.trim().toLowerCase().replace(/[_ -]/g, "")))
                    .filter(
                      (w: any) =>
                        !state.players.some(
                          (p: any) => p.name.toLowerCase() === w.name,
                        ),
                    )
                    .map((w: any) => (
                      <Button
                        className="offline-row"
                        key={w.name}
                        onClick={() =>
                          setSelection({ kind: "player", name: w.name })
                        }
                      >
                        <Bookmark size={12} />
                        {w.name}
                        <small>Offline / not in roster</small>
                      </Button>
                    ))}
              </div>
              <div className="roster-bottom">
                <i className="tiny-dot" />{" "}
                {list === "npcs" ? "Area observations" : "Global roster"}{" "}
                <span>{age(state?.lastRosterAt)}</span>
              </div>
            </>
          )}
        </aside>
        <section className="workspace">
          <div className="workspace-heading">
            <div>
              <div className="breadcrumb">
                RS SDK <ChevronRight size={12} />{" "}
                {section === "map" ? "Live operations" : label(section)}
              </div>
              <h1>
                {section === "map"
                  ? "Live world"
                  : section === "swarms"
                    ? "Swarm intelligence"
                  : section === "missions"
                    ? "Command workspace"
                  : section === "operations"
                    ? "Fleet operations"
                  : section === "signals"
                    ? "Signal stream"
                    : section === "wealth"
                      ? "Published wealth"
                      : section === "catalog"
                        ? "World catalog"
                        : "Agent control surface"}
              </h1>
            </div>
            <div className="workspace-actions">
              {section === "map" && (
                <>
                  <Tooltip content="Show known project friendlies only; hide other players in this view">
                    <Button className={onlyMyBots ? "active" : ""} aria-pressed={onlyMyBots} onClick={() => setOnlyMyBots(v => !v)}><Shield size={14}/> Friendlies only</Button>
                  </Tooltip>
                  <Button className={`dual-3d-toggle ${dualWorld?'active':''}`} aria-pressed={dualWorld} onClick={()=>setDualWorld(v=>!v)}><Layers size={14}/>{dualWorld?'Single 3D':'Dual 3D'}</Button>
                  <Button aria-label="Toggle entity roster" aria-pressed={rosterOpen} onClick={()=>setRosterOpen(v=>!v)}><Users size={14}/> Players</Button>
                  <Button disabled={!viewerReady} onClick={()=>iframe.current?.contentWindow?.postMessage({type:'rs-watch-native-ui',action:'worldmap',open:true},location.origin)}><Map size={14}/> World map</Button>
                  <a className="button monitor-tab-link" href={monitorHref(selection?.kind === 'player' ? selection.name : tabCamera.follow)} target="_blank" rel="noopener noreferrer" title="Open an independent monitor tab for this player. Detailed NPC and equipment coverage is still shared.">New monitor tab <ArrowUpRight size={13}/></a>
                  <a className="button native-viewer-link" href={nativeViewerHref} target="_blank" rel="noreferrer" title="Open Max's native viewer with its own controls">Native viewer <ArrowUpRight size={13}/></a>
                  <Button
                    className="explore-button"
                    aria-label={expanded ? "Return to console" : "Open Max’s native world"}
                    onClick={() => setExpanded(!expanded)}
                  >
                    {expanded ? (
                      <Minimize2 size={15} />
                    ) : (
                      <Maximize2 size={15} />
                    )}
                    {expanded ? "Intelligence" : "Native world"}
                  </Button>
                </>
              )}
              <a
                className="icon bordered"
                href="/api/export"
                download="rs-palantir-export.json"
                aria-label="Export observations"
              >
                <Download size={15} />
              </a>
            </div>
          </div>
          <div className={`map-stage ${section !== "map" ? "hidden" : ""}`}>
            <div className="viewer-frame">
              {expanded && <div className="allegiance-legend"><span>● Friendly</span>{!onlyMyBots && <span>● Enemy candidate</span>}{!state?.friendlies?.ready&&<small>Roster unavailable</small>}</div>}
              {section === "map" && (
                <WorldToolbar>
                  <span className="dock-brand"><span className="mode-emblem mode-emblem-palantir"><img src="/brand/rs-palantir.png" alt=""/></span><span>RS-PALANTIR<small>WORLD INTELLIGENCE</small></span></span><Button onClick={()=>switchMode("blackrock")}><Activity size={14}/>Resume research</Button>
                  <Tooltip content="Open player dossiers, gear, signals and agent tools">
                    <Button className="primary" onClick={() => setExpanded(!expanded)}><Database size={14}/>{expanded?"Intelligence":"Map only"}</Button>
                  </Tooltip>
                    {!expanded&&<Button aria-pressed={rosterOpen} onClick={()=>setRosterOpen(!rosterOpen)}><Users size={14}/>Players</Button>}
                  <Tooltip content="Focus the world · WASD to move · hold Shift for 10× speed">
                    <Button disabled={!viewerReady} onClick={() => iframe.current?.contentWindow?.postMessage({type:'rs-watch-native-ui',action:'focus'},location.origin)}><Navigation size={14}/> Fly <kbd className="flight-key">Shift ×10</kbd></Button>
                  </Tooltip>
                  <Tooltip content="Show known project friendlies only; hide other players in this view">
                    <Button className={onlyMyBots ? "active" : ""} aria-pressed={onlyMyBots} onClick={() => setOnlyMyBots(v => !v)}><Shield size={14}/> Friendlies only</Button>
                  </Tooltip>
                  <a className="button monitor-tab-link" href={monitorHref(selection?.kind === 'player' ? selection.name : tabCamera.follow)} target="_blank" rel="noopener noreferrer" title="Open an independent monitor tab for this player. Detailed NPC and equipment coverage is still shared.">New monitor tab <ArrowUpRight size={13}/></a>
                  <div className="ops-dock-team"><TeamFilter data={operations} value={selectedTeam} onChange={setSelectedTeam}/></div>
                  <Button onClick={() => {setExpanded(false);setSection("operations");}}><Users size={14}/> Readiness {tactical?.available && !tacticalError && tactical.nearby.length > 0 ? `· ${tactical.nearby.length} nearby` : ""}</Button>
                  <Button className={`dual-3d-toggle ${dualWorld?'active':''}`} aria-pressed={dualWorld} onClick={()=>setDualWorld(v=>!v)}><Layers size={14}/>{dualWorld?'Single 3D':'Dual 3D'}</Button>
                  <LocationShortcuts fly={fly} ready={viewerReady} waypoints={presets} onSelect={p=>void fly(p.x,p.z,p.height,p.plane??0,LOCATIONS.find(place=>place.id===p.id)?.defaultPlayerFilter)}/>
                  <Tooltip content="Open the native terrain atlas">
                    <Button disabled={!viewerReady} onClick={() => iframe.current?.contentWindow?.postMessage({type:'rs-watch-native-ui',action:'worldmap',open:true},location.origin)}><Map size={14}/> World map</Button>
                  </Tooltip>
                  <Button aria-expanded={coordinationOpen} onClick={()=>setCoordinationOpen(v=>!v)}><Target size={14}/>Squad planner</Button>
                  <Button aria-expanded={showLayers} onClick={()=>setShowLayers(!showLayers)}><Layers size={14}/>Layers</Button>
                  <Popover.Root><Popover.Trigger><Button><Navigation size={14}/>Coordinates</Button></Popover.Trigger><Popover.Content size="2" aria-label="Fly to coordinates">            <div className="world-coordinate-menu">
              <div className="coordinate-fields">
                <Navigation size={14} />
                <label>
                  X{" "}
                  <TextField.Input
                    size="2"
                    variant="surface"
                    aria-label="Camera X"
                    value={x}
                    onChange={(e) => setX(e.target.value)}
                  />
                </label>
                <label>
                  Z{" "}
                  <TextField.Input
                    size="2"
                    variant="surface"
                    aria-label="Camera Z"
                    value={z}
                    onChange={(e) => setZ(e.target.value)}
                  />
                </label>
                <label>
                  ALT{" "}
                  <TextField.Input
                    size="2"
                    variant="surface"
                    aria-label="Camera height"
                    value={height}
                    onChange={(e) => setHeight(e.target.value)}
                  />
                </label>
                <Button
                  onClick={() => fly(Number(x), Number(z), Number(height))}
                >
                  Fly to <ChevronRight size={13} />
                </Button>
              </div>
              <span className="camera-readout">
                {tabCamera.at && Date.now()-tabCamera.at<=5000
                  ? `${tabCamera.x.toFixed(1)}, ${tabCamera.z.toFixed(1)} · ${tabCamera.follow ? `following ${tabCamera.follow}` : "free camera"}`
                  : "Awaiting camera"}
              </span>
            </div>
</Popover.Content></Popover.Root>
                  <CombatZoneBadge camera={tabCamera}/><Badge variant="soft" color={stale?'amber':'green'} size="1" className="explore-status"><span className="feed-status-dot"/>{stale ? "Feed stale" : "Live world"}</Badge>
                </WorldToolbar>
              )}
              {viewerSrc && (
                <FollowMonitors atlas={<PinnedAtlas researchSource={researchSource} players={state?.players??[]} friendlyNames={allegiance?.names??[]} ready={allegiance?.ready??false} stale={!state||state.rosterStale||!!stateError} onSelect={addMonitor}/>} onChangePrimary={name=>{if(!primaryFrame.current)return;iframe.current=primaryFrame.current;setActiveMonitor(null);void command({kind:"follow",name});}} players={(state?.players??[]).map((p:{name:string})=>p.name)} onChangeNames={setFollowMonitors} names={followMonitors} base={viewerSrc} primary={primaryFollow} allegiance={allegiance} onAdd={addMonitor} onAddMany={incoming=>setFollowMonitors(old=>incoming.reduce((all,name)=>addFollowMonitor(all,name,primaryFollow),old))} activeMonitor={activeMonitor} filters={effectivePaneFilters} onFilter={changePaneFilter} onInspect={inspectPane} onActivate={(name,frame)=>{iframe.current=frame;setActiveMonitor(name?botKey(name):null);setViewerReady(false);setPaneVersion(v=>v+1);}} onClose={name=>{setFollowMonitors(names=>names.filter(n=>botKey(n)!==botKey(name)));if(activeMonitor===botKey(name))setActiveMonitor(null);}}>
                  <WorldViews primaryFrame={primaryFrame} src={viewerSrc} dual={dualWorld} atlasOpen={atlasOpen} nativeUi={expanded} activeFrame={iframe} allegiance={allegiance} onActivate={()=>{setActiveMonitor(null);setViewerReady(false);setPaneVersion(v=>v+1);}} enabled={activeMonitor===null} showControls={true} filters={effectivePaneFilters} onFilter={changePaneFilter} onInspect={inspectPane} onPrimaryFollow={setPrimaryFollow}/>
                </FollowMonitors>
              )}
              <div className="map-topbar">
                <span className="scene-tag">
                  <i className="tiny-dot" />
                  {!viewerReady ? "VIEWER CONNECTING" : stale ? "RS WORLD · FEED STALE" : "RS WORLD · LIVE"}
                </span>
                <Button
                  className={showLayers ? "active" : ""}
                  onClick={() => setShowLayers(!showLayers)}
                >
                  <Layers size={14} /> Layers
                </Button>
              </div>
              {showLayers && (
                <div className="layers-panel">
                  <h4>Scene layers</h4>
                  <Button color="amber" variant="soft" disabled={!viewerReady} aria-pressed={!!tabCamera.multiCombatVisible} onClick={()=>iframe.current?.contentWindow?.postMessage({type:'rs-watch-multi-combat',enabled:!tabCamera.multiCombatVisible},location.origin)}>{tabCamera.multiCombatVisible?'Hide':'Show'} multi-combat zones</Button>
                  <small>Amber areas · local game-source boundaries, live revision unverified.</small>
                  {Object.entries(layers).map(([key, value]) => (
                    <label key={key}>
                      <Checkbox
                        size="2"
                        aria-label={key
                          .replace("show", "")
                          .replace(/([a-z])([A-Z])/g, "$1 $2")}
                        checked={value}
                        onCheckedChange={(checked) => {
                          const next = { ...layers, [key]: checked === true };
                          setLayers(next);
                          command({ kind: "layers", ...next });
                        }}
                      />
                      {key
                        .replace("show", "")
                        .replace(/([a-z])([A-Z])/g, "$1 $2")}
                    </label>
                  ))}
                </div>
              )}
              <div className="camera-hint">
                <span>
                  <kbd>W A S D</kbd> fly <kbd>E / C</kbd> elevation
                </span>
                <span>Drag to look · right-drag to pan · scroll to zoom</span>
              </div>
            </div>
            {section==='map'&&coordinationOpen&&<div className="world-coordination-drawer"><Button size="1" variant="ghost" onClick={()=>setCoordinationOpen(false)}>Close squad planner</Button>{mapContext?<Suspense fallback={<p role="status">Loading squad planner…</p>}><CoordinationMapPanel context={mapContext} onPlan={prepareMission} onTrackPlayer={name=>void selectPlayer(name)} selectedPlayerKey={selection?.name?.toLowerCase().replace(/\s+/g,'_')}/></Suspense>:<p role="status">{coordinationError?'Coordination source unavailable':'Loading current observations…'}</p>}</div>}
            <div className="live-strip">
              <div className="strip-title">
                <Activity size={13} /> OBSERVATIONS{" "}
                <Button onClick={() => setSection("signals")}>
                  View all <ArrowUpRight size={12} />
                </Button>
              </div>
              <EventRows
                events={(feed?.events ?? [])
                  .filter(
                    (e: any) =>
                      !["animation", "spot_animation"].includes(e.kind),
                  )
                  .slice(0, 4)}
                onEntity={selectPlayer}
                onFly={fly}
                compact
              />
            </div>
          </div>
          {(section === "swarms"||swarmVisited) && <div className="intelligence-route" hidden={section!=="swarms"}><Suspense fallback={<p role="status">Loading swarm intelligence…</p>}><SwarmWorkspace groupRequest={swarmRequest} active={section==='swarms'} onPrepareMission={value=>{setMissionResearch(value);setExpanded(false);setSection('missions');}} onContext={setIntelEntity} onAsk={askResearch} onInspectPlayer={name=>{setSection("map");setSelection({kind:"player",name});setExpanded(false);}} onShowPlayer={name=>{setSection("map");setExpanded(false);void selectPlayer(name);}}/></Suspense></div>}
          {(section === "missions"||missionVisited) && <div className="intelligence-route" hidden={section!=="missions"}>{missionResearch?.source==='swarm'&&<Button onClick={()=>setSection('swarms')}>Back to swarm research</Button>}<Suspense fallback={<p role="status">Loading command workspace…</p>}><MissionWorkspace active={section==='missions'} mapContext={mapContext} initialSelection={planSelection} researchSelection={missionResearch} onOpenWorld={(point,name)=>{setSection('map');if(name)void selectPlayer(name);else if(point)void fly(point.x,point.z,70);}} onTrackPlayer={name=>{setSection('map');void selectPlayer(name);}} onOpenItem={(id,name,view)=>{setReturnToMission(true);openResearch({kind:'item',key:String(id),itemId:id,name,view});}} onContext={id=>setIntelEntity({kind:"mission",key:id,name:"Mission preview"})}/></Suspense></div>}
          {section === "settings" && <SettingsPage onNavigate={setSection} onOpenAssistant={()=>toggleAssistant(true)}/>}
          {section === "operations" && <OperationsPanel tactical={tactical} tacticalError={tacticalError} data={operations} error={operationsError} players={state?.players ?? []} rosterStale={!state || state.rosterStale || !!stateError} onRefresh={refreshOperations} onInspect={name => {setExpanded(false);setSection("map");setSelection({kind:"player",name});if(state?.players.some((p:any)=>botKey(p.name)===botKey(name)))void selectPlayer(name);}}/>}
          {section === "signals" && (
            <div className="data-surface">
              <div className="surface-toolbar">
                <label className="search">
                  <Search size={14} />
                  <TextField.Input
                    size="2"
                    variant="surface"
                    aria-label="Search signals"
                    value={eventQuery}
                    onChange={(e) => setEventQuery(e.target.value)}
                    placeholder="Entity, message, item…"
                  />
                </label>
                <FilterSelect
                  label="Signal type"
                  value={eventKind}
                  onValueChange={setEventKind}
                  options={[
                    "all",
                    "chat",
                    "hitsplat",
                    "equipment",
                    "animation",
                    "teleport",
                    "roster_join",
                    "roster_leave",
                  ].map((v) => ({ value: v, label: label(v) }))}
                />
                <label className="toggle">
                  <Checkbox
                    size="2"
                    checked={onlyWatched}
                    onCheckedChange={(checked) =>
                      setOnlyWatched(checked === true)
                    }
                  />{" "}
                  Watched only
                </label>
              </div>
              <div className="source-notice">
                <Radio size={13} /> {feed?.coverage} · Latest 100 matching
                events shown.
              </div>
              <EventRows events={events} onEntity={selectPlayer} onFly={fly} />
            </div>
          )}
          {section === "wealth" && (
            <div className="data-surface">
              <div className="surface-toolbar">
                <div className="segmented">
                  <Button
                    className={catalogKind !== "npcs" ? "active" : ""}
                    onClick={() => setCatalogKind("items")}
                  >
                    Bank leaderboard
                  </Button>
                  <Button
                    className={catalogKind === "npcs" ? "active" : ""}
                    onClick={() => setCatalogKind("npcs")}
                  >
                    Outfit leaderboard
                  </Button>
                </div>
                <a
                  className="source-link"
                  href={board?.source}
                  target="_blank"
                  rel="noreferrer"
                >
                  Public source <ArrowUpRight size={12} />
                </a>
              </div>
              <div className="source-notice">
                Published top 50 · partial item previews · fetched{" "}
                {age(board?.fetchedAt)} · publication time unknown
              </div>
              {boardError && <p className="error">{boardError}</p>}
              <div className="table-scroll">
                <Table.Table className="data-table">
                  <Table.Header>
                    <Table.Row>
                      <Table.ColumnHeaderCell>RANK</Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell>PLAYER</Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell>
                        PUBLISHED VALUE
                      </Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell>
                        VISIBLE PREVIEW
                      </Table.ColumnHeaderCell>
                    </Table.Row>
                  </Table.Header>
                  <Table.Body>
                    {board?.rows.map((r: any) => (
                      <Table.Row
                        key={r.name}
                        onClick={() =>
                          setSelection({ kind: "player", name: r.name })
                        }
                      >
                        <Table.Cell>#{r.rank}</Table.Cell>
                        <Table.Cell>
                          <Button className="entity-link">{r.name}</Button>
                        </Table.Cell>
                        <Table.Cell className="gold">{r.valueLabel}</Table.Cell>
                        <Table.Cell>
                          {r.items.length?<div className="wealth-items">{r.items.map((i:any)=><span key={i.id} title={`${i.label}${i.count!=null?` · ${fmt(i.count)}`:''}`}><ItemSprite id={i.id} name={i.label}/>{i.count!=null&&<small>{fmt(i.count)}</small>}</span>)}</div>:"No preview published"}
                        </Table.Cell>
                      </Table.Row>
                    ))}
                  </Table.Body>
                </Table.Table>
              </div>
            </div>
          )}
          {section === "catalog" && (
            <div className="data-surface">
              <div className="surface-toolbar">
                <div className="segmented">
                  <Button
                    className={catalogKind === "items" ? "active" : ""}
                    onClick={() => {
                      setCatalogKind("items");
                      setCatalogOffset(0);
                    }}
                  >
                    Items
                  </Button>
                  <Button
                    className={catalogKind === "npcs" ? "active" : ""}
                    onClick={() => {
                      setCatalogKind("npcs");
                      setCatalogOffset(0);
                    }}
                  >
                    NPCs
                  </Button>
                </div>
                <label className="search">
                  <Search size={14} />
                  {catalogKind==='items'?<ItemSearchInput label="Search catalog" value={catalogQuery} onChange={value=>{setCatalogQuery(value);setCatalogOffset(0)}} onPick={item=>{setCatalogQuery(String(item.id));setCatalogOffset(0)}}/>:<TextField.Input
                    size="2"
                    variant="surface"
                    aria-label="Search catalog"
                    value={catalogQuery}
                    onChange={(e) => {
                      setCatalogQuery(e.target.value);
                      setCatalogOffset(0);
                    }}
                    placeholder="Name or definition ID…"
                  />}
                </label>
              </div>
              <div className="source-notice">
                {fmt(catalog?.total)} matching definitions · revision 289 ·
                definition data does not establish possession
              </div>
              <div className="table-scroll">
                <Table.Table className="data-table">
                  <Table.Header>
                    <Table.Row>
                      <Table.ColumnHeaderCell>ID</Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell>NAME</Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell>
                        {catalogKind === "items" ? "BASE VALUE" : "COMBAT"}
                      </Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell>EXAMINE</Table.ColumnHeaderCell>
                    </Table.Row>
                  </Table.Header>
                  <Table.Body>
                    {catalog?.results.map((r: any) => (
                      <Table.Row
                        key={r.id}
                        onClick={() =>
                          setSelection({ kind: "catalog", item: {...r,assetKind:catalogKind} })
                        }
                      >
                        <Table.Cell>{r.id}</Table.Cell>
                        <Table.Cell>
                          <div className="catalog-item">{catalogKind==='items'&&<ItemSprite id={r.id} name={r.name}/>}<Button className="entity-link">{r.name}</Button></div>
                        </Table.Cell>
                        <Table.Cell>
                          {catalogKind === "items"
                            ? `${fmt(r.baseValue)} gp`
                            : (r.combatLevel ?? "—")}
                        </Table.Cell>
                        <Table.Cell>{r.description || "—"}</Table.Cell>
                      </Table.Row>
                    ))}
                  </Table.Body>
                </Table.Table>
              </div>
            </div>
          )}
          {section === "catalog" && (
            <div className="pagination">
              <Button
                disabled={!catalogOffset}
                onClick={() => setCatalogOffset((v) => Math.max(0, v - 80))}
              >
                Previous
              </Button>
              <span>
                {catalogOffset + 1}–
                {Math.min(catalogOffset + 80, catalog?.total ?? 0)} /{" "}
                {fmt(catalog?.total)}
              </span>
              <Button
                disabled={catalogOffset + 80 >= (catalog?.total ?? 0)}
                onClick={() => setCatalogOffset((v) => v + 80)}
              >
                Next
              </Button>
            </div>
          )}
          {section === "agent" && (
            <div className="agent-surface">
              <div className="agent-intro">
                <Terminal size={24} />
                <h2>One interface for agents and you.</h2>
                <p>
                  Discover, inspect, watch, navigate. Every result carries its
                  source and time. Camera commands have an acknowledgement; a
                  queued command is not proof of execution.
                </p>
                <div>
                  <a
                    className="button primary"
                    href="/api/openapi.json"
                    target="_blank"
                    rel="noreferrer"
                  >
                    OpenAPI schema <ArrowUpRight size={14} />
                  </a>
                  <a
                    className="button"
                    href="/agent-guide"
                    target="_blank"
                    rel="noreferrer"
                  >
                    Agent guide <ArrowUpRight size={14} />
                  </a>
                </div>
              </div>
              <h4>MCP · local stdio server</h4>
              <div className="codebar">
                <code>
                  bun run world:intel:mcp
                </code>
                <Button
                  className="icon"
                  aria-label="Copy MCP command"
                  onClick={async () => {
                    await navigator.clipboard.writeText(
                      "bun run world:intel:mcp",
                    );
                    setCopied(true);
                  }}
                >
                  {copied ? <Check size={14} /> : <Copy size={14} />}
                </Button>
              </div>
              <h4>rs-sdk · shared live intelligence</h4>
              <div className="codebar"><code>bun run world:intel -- intel --observer AuraMaxxer --radius 32</code></div>
              <p className="note">Existing project MCP: world_intel / world_friendlies. HTTP: /api/intel. Native scripts: sdk/world-intel.ts. Fresh player snapshots are global; detailed NPC coverage is shared and bounded.</p>
              <p className="note">Friendly registry: {state?.friendlies?.ready ? `${fmt(state.friendlies.names.length)} known identities` : "Unavailable — allegiance unknown"}. Green = project friendly; red = enemy candidate, not proven hostile.</p>
              <a href="/api/friendlies" target="_blank" rel="noreferrer" className="button">View friendly registry <ArrowUpRight size={14}/></a>
              <h4>Agent camera control · this tab</h4>
              <Button aria-pressed={agentControl} className={agentControl ? 'active' : ''} onClick={()=>setAgentControl(v=>!v)}>{agentControl ? 'Agent control enabled' : 'Enable agent control in this tab'}</Button>
              <p className="note">Follow, Fly, filters and camera saves stay in their own browser tab. Enable shared API/MCP camera commands in just the tab you want agents to steer. Each tab has its own camera; detailed NPC, equipment and skull observations still share one feed area.</p>
              <h4>This tab’s latest camera command</h4>
              <div className="command-state"><Badge size="1" variant="soft" className="badge">{viewCommand?.status ?? 'NO COMMAND'}</Badge><span>{viewCommand?.kind} {viewCommand?.name ?? ''}</span></div>
              <h4>Latest shared agent camera command</h4>
              <div className="command-state">
                <Badge size="1" variant="soft" className="badge">
                  {state?.command?.status ?? "NO COMMAND"}
                </Badge>
                <span>
                  {state?.command?.kind} {state?.command?.name ?? ""}
                </span>
                <code>{state?.command?.id?.slice(0, 8)}</code>
              </div>
              <Json
                data={
                  state?.command ?? {
                    hint: "Use POST /api/commands or the MCP fly_to / follow_player tools",
                  }
                }
              />
              <h4>Available sources</h4>
              {caps?.sources.map((s: any) => (
                <Card className="source-card" key={s.id}>
                  <strong>
                    {s.id}
                    <span>{s.type}</span>
                  </strong>
                  <p>{s.coverage}</p>
                  <div className="chips">
                    {s.fields.map((f: string) => (
                      <span key={f}>{f}</span>
                    ))}
                  </div>
                  <small>{s.freshness}</small>
                </Card>
              ))}
              <h4>Explicit unknowns</h4>
              {caps?.unavailable.map((u: any) => (
                <div className="unknown" key={u.field}>
                  <LockKeyhole size={14} />
                  <div>
                    <strong>{label(u.field)}</strong>
                    <p>{u.reason}</p>
                  </div>
                </div>
              ))}
            </div>
          )}
        </section>
        <Inspector
          selection={selection}
          state={state}
          onClose={() => setSelection(null)}
          onFollow={selectPlayer}
          onResearch={openResearch}
          gearRequest={gearRequest}
          onWatch={watch}
          onFly={fly}
        />
      </main>
      <footer>
        <span>
          <i className={`tiny-dot ${stale ? "gray" : ""}`} /> WORLD FEED{" "}
          <b>·</b> tick {fmt(state?.tick)} <b>·</b> {age(state?.lastTickAt)}
        </span>
        <span className="footer-middle">
          Local history · {fmt(state?.intel.trailSamples)} position samples{" "}
          <b>·</b> one shared observation area
        </span>
        <span>
          <Code size={12} /> API READY <b>·</b> {state?.viewerClients ?? 0}{" "}
          viewer(s)
        </span>
      </footer>
      </div>
      </div><AssistantPanel draftRequest={assistantDraftRequest} open={assistantOpen} onClose={()=>toggleAssistant(false)} context={{mode:economy?'blackrock':'palantir',section,label:economy?researchContext:section==='swarms'?'Swarm intelligence':section==='missions'?'Command workspace':selection?.name??'World overview',entity:economy?assistantEntity:section==='swarms'?(intelEntity?.kind==='swarm'?intelEntity:null):section==='missions'?(intelEntity?.kind==='mission'?intelEntity:null):selection?.kind==='player'?{kind:'player',key:selection.name,name:selection.name}:null}}/></div>
    </div>
  );
}
createRoot(document.getElementById("root")!).render(
  <Theme
    appearance="dark"
    accentColor="blue"
    grayColor="slate"
    className="rs-theme"
  >
    <App />
  </Theme>,
);