# mem.jbnx.io front end — design notes

**Reference:** Obsidian's graph view in 3D (orbit camera, domain-colored nodes, click to focus) with a Linear-style right-hand detail panel (quiet chips, dense tables, one primary action).

**Palette:** page `#07080c`, panels `#10121a`, 1px borders `#23263a`, text `#e6e8ef`, muted `#8b90a5`, accent `#7c5cff`. Warning amber for Confidential, red for Secret. No gradients. System font stack, 14px base, 10px radius.

**Layout:** the 3D graph fills the viewport. A translucent top bar holds brand, search, filters, counts and actions; the domain legend sits below it as toggle chips. The detail panel docks right (460px) and becomes a bottom sheet under 720px. The admin drawer slides in from the right. Stats line bottom-left.

**Graph:** one ForceGraph3D instance, never recreated. Filters (domain set, kind, hide-closed) build filtered node/link copies for `graphData`; node objects persist across refetches so positions are kept. Node size = `1 + importance*6` (hubs larger), color = domain color, `nodeResolution` 8 (6 above 5000 nodes), `cooldownTicks(200)`, `warmupTicks(30)`. Above 4000 links they render as hairlines. The map opens zoomed out to fit every visible node (`fitAll`: once on the first tick, then smoothly when the layout settles). Every cluster gets a title: `memClusters` (label propagation over links, domain/root hubs left out) groups the map, and each cluster of 10+ is named by its central project, else a shared label prefix, else its best-linked member, shown as a signpost at the cluster's centre with its member count; clicking it frames the cluster. Selecting a node dims everything except it and its neighbors; links touching it brighten. Particles run only while memory is accessed: the server streams new `brain.access_log` rows over `/ui/flow` (server-sent events, owner session only), the touched nodes glow (cyan = read, amber = write) and emit particles along their links for ~6s, then go dark. A small feed bottom-left shows who, what op and which memories, and fades when idle. The top-bar "Flow" dot is green while the stream is connected and pulses during access.

**Interactions:** click a node or a neighbor row, or pick a search hit, to fly the camera to it (~120 units) and open the panel. `/` focuses search, `Esc` closes the topmost layer (key dialog is the exception, see below). Domain chip click toggles, shift-click isolates.

**Writes:** + Memory, Add fact (also "Replace", which sends `supersedes_id`), Link to…, and the admin forms. Every non-GET sends `x-mem-csrf: 1` and JSON content type. Server errors become a toast.

**Secrets:** an issued key lives only in a read-only textarea inside a modal, with a "shown once" warning. It is wiped on close and on sign-out, and is never written to storage. Esc and backdrop clicks do not dismiss it.

**CSP rules honored:** no inline script, `style=""`, `<style>`, `on*=` attributes, eval or external URLs. All server text goes through `textContent`; the one HTML string (the graph tooltip) is escaped first. Only `http(s)` URLs become links, with `rel="noopener noreferrer"`. The graph library normally injects `<style>` tags, which CSP blocks, so its few rules are copied into `app.css`. Colors set from JS use `element.style` (allowed).
