# Info Elements Agent Guide

Base URL: `https://info-elements.aisloppy.com`

The root page is a public, example-led showcase. This raw Markdown guide and the
shared renderer and component APIs are also public so agents and consuming
applications can use the service contract without an interactive login. The
collection, graph corpus, critic, principles, and
rendered guide page remain private behind an approved Info Elements session.

## Role

Info Elements is the canonical tactical layer for presenting structured
information — a table, a diagram, a chart, a rendered 3-D scene, or the whole
single-purpose page one of them anchors. It owns reusable frontend elements,
format-selection rules, and field-tested interaction lessons. It does not
generate applications.

Illuminate may consume this service while designing a broader learning
experience. Table generation, chart interaction, table benchmarks, component guidance, graph
rendering, format selection, and demos live here as one product and API.

## Component collection

`/elements` demonstrates implemented components in four groups: Tables,
Charts, Diagrams and images, and Tooltips and lightbox. `/elements#tables`
and `/elements#overlays` open focused groups directly. The collection contains
working examples, not an interaction-pattern or general-practices catalog.
Develop a pattern in its consuming app before promoting a reusable component.

The tooltip example uses
`InfoElements.bindAnchoredTooltip`, also used by Token Trick; the lightbox
uses `InfoElements.openLightbox`, also used by FairyStack and other apps.
Hover/focus, touch, Escape, selectable detail, and viewport boundaries are
part of the components themselves. Never use native `title=` tooltips.

## Principles

`GET /principles` is a ranked set of guidelines for presenting and modelling
information, and `GET /api/principles` is the same set as JSON. Each carries the
count of times work has had to restate it, so the ordering is evidence rather
than opinion: a principle nobody has repeated is one person's preference.

They are guidelines. Contradicting one is expected — record the contradiction as
a revision rather than editing quietly, because a rule that starts collecting
arguments against it is the interesting case.

| Endpoint | Method | Body | Purpose |
|---|---|---|---|
| `/api/principles` | GET | — | List, ranked by affirmations. `?scope=presentation\|modeling`, `?include_retired=true` |
| `/api/principles` | POST | `statement`, `why`, `scope`, `origin` | Add one. Fails if the slug exists — affirm instead |
| `/api/principles/<id>/affirm` | POST | `note` | Record that work restated it, and where |
| `/api/principles/<id>/revise` | POST | `note`, `statement?`, `why?` | Change the wording, keeping what it replaced |
| `/api/principles/<id>/retire` | POST | `note` | Mark it no longer held |

```bash
curl -s https://info-elements.aisloppy.com/api/principles?scope=presentation
curl -s -X POST https://info-elements.aisloppy.com/api/principles/examples-carry-the-table/affirm \
  -H 'Content-Type: application/json' \
  -d '{"note":"A comparison table grew a description column that restated its own values."}'
```

Affirm from the work that made you reach for the rule, and say which work it was
in the note. The seed set lives in `backend/principles_seed.json` so its wording
is reviewable in a diff; affirmations, revisions and retirements accumulate at
runtime.

## Critic

`GET /critic` is a signed-in review workbench. It sends candidate markup, a
visualization specification, or precise rendered-state notes through a structured
critic grounded in the current presentation principles. The response names one
dominant defect, the smallest coherent correction, what to preserve, explicit
pass/fail/unknown coverage, confidence, and missing evidence.

| Endpoint | Method | Purpose |
|---|---|---|
| `/api/critic/reviews` | POST | Start an authenticated review with `artifact` and optional `reader_question` |
| `/api/critic/reviews/<task_id>` | GET | Poll pending, running, completed, failed, cancelled, or timed-out state |
| `/api/critic/evidence` | POST | Record a human helpfulness verdict and correction |
| `/api/critic/evidence` | GET | Inspect aggregate verdicts and recent corrections |

Corrections are evidence, not online training events. Promote a changed rubric or
learned ranker only after it improves a held-out evaluation set without reducing
principle coverage. A future Little Brain integration belongs behind this API;
the services must not import each other's code or read each other's data.

## Start Here

1. State the reader's immediate question.
2. Inventory entities, repeated dimensions, relationships, hierarchy, sequence,
   exact values, long text, and required qualifications.
3. Choose a format using the Format Selection table below and the reader’s
   question.
4. Choose one canonical organizing model. Make stages, selectors, and detail
   transform that model rather than introducing parallel taxonomies.
5. Integrate the element into the real host surface.
6. Delete prose, cards, legends, or prior visuals that merely repeat it.
7. Verify desktop and mobile, contrast, text selection, keyboard operation,
   loading, empty, failed, timed-out, and completed states.

## Format Selection

| Element | Prefer when | Reject when |
|---|---|---|
| Table | Entities share stable dimensions or exact comparison dominates | Cells become essays or topology is the claim |
| Diagram | Topology, ownership, hierarchy, sequence, convergence, or boundary crossing dominates | It merely redraws a table |
| Chart | Magnitude, distribution, or change over time dominates and values share compatible units | Exact values dominate, units conflict, or an unlabeled baseline would require invention |
| Image with HTML overlays | A generated or sourced image provides real spatial context and a few precise labels or selections belong on it | The image is decorative, exact comparison dominates, or a diagram carries the claim more directly |
| Prose | Definitions, qualifications, arguments, or narrative resist reduction | Repeated fields or relationships are hiding in sentences |
| Rendered scene | Physical arrangement, scale, or a mechanism unfolding in space dominates, and a reader would otherwise have to imagine the geometry | A labelled diagram carries the same claim, or the subject has no spatial truth to show |

Cards are not a default information element. Use them only when items are truly
independent and cross-item alignment is not a reader task.

## Table and Matrix Contract

- Rows are comparable entities; columns are stable questions asked of every row.
  A labeled key/value block is still a table: if its labels change with the
  selection, it implies a comparison that does not exist. Replace it with
  ordinary prose or attach each fact to the object it describes.
- Use semantic `table`, `caption`, `thead`, `tbody`, `th`, and `scope`.
- Keep exact values and labels selectable and copyable.
- Reclaim width from outer margins before shrinking type. Preserve comparison on
  mobile with horizontal scrolling rather than disconnected cards.
- Dense matrices need complete cell borders. Compact stable stage headers may be
  vertical when that preserves useful cell width; retain full accessible labels.
- Background color may encode a real state only with sufficient contrast and an
  accessible name. Do not repeat checkmarks or status text without new value.
- If cells expose explanation, hover and keyboard focus preview one cell in a
  shared detail panel. Click or Enter/Space pins it; repeat the action to unpin.
  Incidental hover never replaces pinned detail. Expose selection with
  `aria-pressed` or an equivalent contract.
- Use sticky headers, virtualization, filtering, and keyboard cell navigation
  when dataset scale or editing justifies them.

## Diagram Contract

- Use diagrams for relationships, not decoration.
- Match the host's canvas, typography, borders, accent, and embedded width.
- Paired text and diagram views share one selection and data model.
- Treat node, container, edge, arrowhead, and label collisions as failures.
- Render validated directed graphs with this service's `POST /api/render/graph`.
  Cycles are supported when feedback, iteration, or request/reply topology is
  meaningful; cycle-closing edges route through dedicated feedback lanes.
- In top-to-bottom branch fans, edge labels sit on the outside of the fan: the
  left branch is labeled on its left and the right branch on its right. The
  rule mirrors for converging branches so labels remain associated with their
  own paths instead of drifting toward a neighboring edge. Isolated diagonal
  edges use the same directional side rule, while a straight central edge puts
  its label inline over the path. Independent sources align with the rank
  immediately before their earliest target so long edges do not pass through
  unrelated intermediate nodes. When an edge must skip occupied ranks, route
  it through an outer lane and keep its label inline on that detour.
  Re-enter the target through the matching outer edge so the path, arrowhead,
  and node boundary share one direction. Draw feedback and outer-lane routes
  as orthogonal runs with balanced 16 px rounded corners. This makes both node
  connections intentional without the abrupt curvature change of a short
  straight segment appended to a Bézier.
- Put node detail on the node it explains and make the node body the interaction
  target. Do not add an info marker when hovering or focusing the node can drive
  the same shared detail. Bind the rendered SVG with
  `/api/components/graph-inspector.js`: hover and keyboard focus preview, click
  or Enter/Space pins, and pinned detail outranks incidental hover. The
  controller deliberately retains the last preview on pointer exit; clearing an
  expanding panel can uncover the node and cause an expand/collapse loop.
- Edges and containers inspect the same way when the binder receives `edges`
  and `containers`. An edge is its path, its label, and the renderer's invisible
  18 px hit lane (`[data-edge-hit]`), and all three share `data-edge-source` and
  `data-edge-target`: hovering the label heats the path and vice versa, because
  a reader treats the label as part of the edge. Give edges `description` and
  keep drawn labels short; the overlay carries the full relation. A container
  group is only its frame and title, so entering the frame previews the
  boundary without stealing hovers from member nodes.
- In the overlay, the explanation carries the weight: endpoint names and the
  kind are a small uppercase context line, the relation or component name is
  the title, and `description` is full-size ink. Readers came for the why, not
  for the two names they can already see on the drawing.
- Keep the overlay above the drawing at all times. Lifting the drawing above the
  overlay on hover hides exactly the panel the hovered element fills. If the
  overlay covers part of a wide drawing, give the scroll lane a margin the
  width of the overlay instead, so every part can be scrolled clear.
- Keep shared diagram detail out of layout flow. Position its fixed-size overlay
  in reserved canvas or header space and scroll overflow internally, so changing
  detail neither covers the graph nor shifts the reader's place. On narrow
  screens a fixed-height in-flow panel is acceptable when overlap would obscure
  the topology. The hosted `<graph-detail-overlay>` defaults to a stable 110 px
  height and exposes `--graph-detail-height` for the host's reserved footprint.
- `annotations: [{id, target, label, description, detail}]` remains available
  for a genuinely distinct note attached to a node. It is not the default node
  inspection mechanism and should not duplicate the node's own detail.

```js
import { bindGraphInspector } from "https://info-elements.aisloppy.com/api/components/graph-inspector.js";
const overlay = document.querySelector("graph-detail-overlay");
bindGraphInspector({ root: document.querySelector(".diagram"), overlay,
  nodes: spec.nodes, edges: spec.edges, containers: spec.containers });
```

### Architecture diagrams

Start with the reader's path through the system. Order the primary flow as the
reader encounters it, and do not assume they know which process receives a
request or where execution occurs. For example:

`Browser` → **public HTTPS** → `HTTPS edge` → **reverse proxy over localhost** →
`Web application` → **SQLite read/write** → `Durable store` → **atomic queue
claim** → `Worker` → **JSON-RPC over a private WebSocket tunnel** → `Remote agent
runtime`

In this request flow, the HTTPS edge is nginx: it receives the encrypted request,
decrypts it using the hostname's certificate, and forwards it over localhost to
the web application process. The application writes durable work to SQLite. A
separate worker atomically claims that work, then asks the remote agent runtime
to execute it over a private tunnel. The order makes the receiving process,
durability boundary, work claimant, transport, and execution location explicit.

Name every component for its present responsibility, not a historical
implementation term. An **app lifecycle service** inventories applications and
handles authenticated create, deploy, or restart requests; an **application
process** actually serves application code. Reserve *runtime* or *hosting* for a
component that executes code. Short general examples follow the same rule:
`Deployment inventory` is clearer than `Workspace manager`; `Queue worker` is
clearer than `Dispatcher`; `Application process` is clearer than `App server`
when the important fact is which process serves the request.

Replace jargon with observable behavior at first use. A *workspace* is the
directory containing an application's source and working files; a *manifest* is
the file that declares how it is configured; a *dispatcher* routes work to an
eligible consumer; a *worker* claims and performs queued work; a *control plane*
coordinates or configures execution without performing it. Prefer the concrete
nginx explanation above to “terminate TLS.”

Show important process and transport boundaries on edges: **public HTTPS**,
**reverse proxy over localhost**, **SQLite read/write**, **atomic queue claim**,
**JSON-RPC over a private WebSocket tunnel**, or **authenticated lifecycle
request**. Labels such as “connects,” “executes,” and “manages” usually hide the
architectural fact the reader needs.

Keep coordination, execution, deployment, serving, and proxying as separate
nodes when they are separate responsibilities: the durable store coordinates
work; the worker claims it; the runtime executes it; the lifecycle service
configures deployment; the application process serves code; and the proxy
receives public traffic. Do not collapse them into one “runner” or “server.”

Use one canonical topology and extend it. If an operator view is the customer
topology plus development infrastructure, derive it from the customer model and
append the extra branches. Add regression checks that shared node names,
descriptions, and edges remain identical; do not maintain copied diagrams that
can drift.

Use one visual treatment for comparable nodes. If infrastructure nodes have
icons, implementation detail, ownership labels, or drill-down behavior, apply
that system consistently. Selective decoration implies a distinction even when
none exists.

Match detail to the current reader question. Present the smallest useful
overview, then progressively disclose secondary security rules, failure modes,
and implementation caveats only where they explain the selected relationship.
When a node represents inspectable live state—a database, queue, deployment
inventory, or worker pool—let an authorized reader open a drill-down without
putting raw state in the overview.

Treat repeated reader confusion as diagram feedback. Questions such as “What is
that?”, “How does this connection work?”, or “Why is this node different?” mean
the node, edge, definition, or visual treatment needs revision. Update the
diagram instead of relying on surrounding conversation.

| Anti-pattern | Why it misleads | Better representation |
|---|---|---|
| Begin with an internal service | The reader cannot find the request entry point | Order the path from browser or caller to edge, application, store, worker, and runtime |
| Call every process a runner or server | Management, serving, and execution appear interchangeable | Name lifecycle service, application process, worker, and runtime separately |
| Label an edge “connects” | The process boundary, protocol, and trust boundary disappear | Name the transport, such as public HTTPS or JSON-RPC over a private WebSocket tunnel |
| Say “terminate TLS” without explanation | Jargon hides who decrypts and where plaintext goes next | Show nginx receiving HTTPS, decrypting with the hostname certificate, and forwarding over localhost |
| Copy customer and operator diagrams | Shared topology silently drifts | Generate both from one model and regression-check shared nodes, descriptions, and edges |
| Decorate only one comparable node | Styling implies an unexplained semantic difference | Apply the same icon, ownership, detail, and interaction rules to the whole node class |
| Attach every caveat to the overview | Secondary detail obscures the primary relationship | Keep the overview minimal and reveal relevant detail in a shared drill-down |
| Explain confusion only in prose | The next reader encounters the same missing information | Revise the responsible node, edge, definition, or visual treatment |

Before presenting an architecture diagram, check:

- [ ] Can the reader follow one end-to-end path?
- [ ] Does every node name describe its real responsibility?
- [ ] Are important process boundaries visible?
- [ ] Are important transports named?
- [ ] Are management and execution separated?
- [ ] Are repeated structures generated from one source?
- [ ] Is terminology defined at first use?
- [ ] Is visual treatment consistent?
- [ ] Is secondary detail progressively disclosed?
- [ ] Did reader confusion result in a diagram update?

## Artifact storage

The artifact API accepts `diagram`, `table`, `chart`, and `explainer` records.
Each has a stable lowercase id, title, optional project, representation `spec`,
source/provenance object, content hash and monotonic revision. Identical writes
are idempotent; changed content creates a durable revision.

- `GET /api/artifacts?kind=&project=&q=` lists public records plus private records
  owned by the authenticated identity.
- `GET /api/artifacts/<id>` returns one supported record.
- `POST /api/artifacts` and `DELETE /api/artifacts/<id>` are restricted to the
  local agent boundary; public callers receive 403.

Option Table is an independent app and is not imported, rendered or listed here.
Existing imported snapshots remain stored for recovery but are excluded from
artifact reads. New `option_table` writes are rejected.

### Resource links

Legacy `/option-tables`, `/library`, and `/library.html` URLs temporarily redirect
with `Cache-Control: no-store` to `https://option-table.aisloppy.com/tables`.
An `artifact=option-table.<id>` query opens `/tables/<id>` on that same origin.
IDs accept letters, digits, underscores and hyphens, up to 80 characters;
invalid artifact identities return 400. These fresh standalone routes avoid
cached permanent redirects from the former integration. Option Table owns
its authentication and existing saved tables.

## Component APIs

### Anchored tooltip

Never place a styled tooltip inside the clipped or scrolling content container
it explains, and never use native `title=`. Render it in a viewport-level portal
or top layer, measure the anchor and tooltip, then flip and shift it within an
explicit viewport margin. Preserve a useful maximum width at the left and right
edges instead of squeezing the text into a one-word column; move the box while a
caret continues to point to the anchor. Reposition on every ancestor scroll,
window resize, and visual-viewport change.

Hover and keyboard focus open the same selectable content. Escape, focus leaving
both anchor and tooltip, or an outside pointer action closes it; tapping the
anchor toggles it on touch screens. An array of facts is rendered as stacked
rows in one tooltip, which is the correct treatment when multiple timeline
events share a coordinate. Set readable foreground, background, and border
tokens together for both light and dark hosts.

```html
<script defer src="https://info-elements.aisloppy.com/api/components/anchored-tooltip.js"></script>
<script>
InfoElements.bindAnchoredTooltip({
  anchor: document.querySelector('[data-event-mark]'),
  content: ['Tool completed · 1:34:08 AM', 'Worker heartbeat · 1:34:08 AM']
});
</script>
```

### Lightbox

Use the hosted lightbox when a reader selects an image, source excerpt, or
generated DOM for focused inspection without leaving the current page:

```html
<script defer src="https://info-elements.aisloppy.com/components/lightbox-v1.js"></script>
<script>
InfoElements.openLightbox({
  items: [InfoElements.lightboxImage({
    title: 'Cortical layers', src: '/assets/layers.jpg',
    alt: 'Cross-section of cortical layers', timeoutMs: 10000
  })],
  returnFocus: triggerElement
});
</script>
```

Items may supply `render({signal,index})`, returning a DOM node or Promise.
Rendering defaults to a 10-second deadline; rejection and timeout produce a
selectable retry state, and navigation aborts prior work. The component traps
and restores focus, supports Escape, arrows, visible controls, touch swipe, and
singleton deduplication. Tab/Shift-Tab includes enabled visible links and controls
in supplied content, in slot order; hidden, disabled and inert controls are
skipped. Multi-item galleries loop by default; singleton views
omit navigation and count. Events on `<ik-lightbox>` are `ik-open`, `ik-change`
(`detail.index`), and `ik-close`.

Set `presentation: "contextual"` when the host interface should remain visible
beneath a wide inspection panel. Contextual presentation uses a light,
unblurred scrim, removes the immersive dialog surface and visible header,
overlays the close control, and shrink-wraps the supplied content up to the viewport edge;
focus containment, Escape-to-close, return focus, and bounded rendering remain
unchanged. Omit it for immersive media inspection.

### Image with HTML overlays

Use the hosted `image-overlay` element when a raster scene carries meaningful
spatial context but generated pixels should not own labels, links, or state.
The image remains an image with required alt text; annotations remain selectable,
keyboard-operable HTML. Overlays are optional and become an ordinary list below
the image on narrow screens. On-image labels use a nearly transparent resting surface
so they do not obscure the art, then become opaque on hover, keyboard focus, or
selection. Consumers may tune the resting opacity with
`--image-overlay-rest-opacity` and both surfaces with `--image-overlay-marker`
and `--image-overlay-marker-active`. An overlay may contain up to four
`{label, value}` facts. Keep those facts inside the overlay when they are the
complete explanation; requiring the reader to look between a spatial label and
a thin supplementary panel creates needless split attention. Activation emits
`image-overlay-select` with
`detail.contextId`, `detail.id`, and the selected overlay so a table, detail
panel, or alternate view can share the same selection.

```html
<script defer src="https://info-elements.aisloppy.com/api/components/image-overlay.js"></script>
<image-overlay id="landscape"></image-overlay>
<script>
customElements.whenDefined('image-overlay').then(() => {
  document.querySelector('#landscape').setData({
    src: '/landscape.png', alt: 'A managed application environment.',
    contextId: 'managed', caption: 'Provider-operated environment',
    overlays: [
      {id: 'control', label: 'Control box', x: 42, y: 28, facts: [
        {label: 'Owns', value: 'Routing and operator policy'},
        {label: 'Boundary', value: 'Does not run application code'}
      ]},
      {id: 'apps', label: 'App boxes', x: 62, y: 68, facts: [
        {label: 'Owns', value: 'Application processes and data'},
        {label: 'Boundary', value: 'One customer allocation'}
      ]}
    ]
  });
});
</script>
```

Do not burn text into generated pixels. Do not use overlays to rescue an image
whose spatial metaphor is unrelated to the underlying model. When an adjacent
table compares the same entities, use stable ids and one shared selection rather
than maintaining a visual taxonomy and a separate tabular taxonomy. Do not add
a separate detail panel that merely restates short overlay facts; use one only
when the selected item genuinely needs longer evidence, provenance, or a
different representation.

#### Designing a diffusion-image explorer

Use a diffusion-image explorer when the reader first needs a spatial overview,
then a model-specific explanation whose topology is easier to recognize than to
read as repeated prose. Treat it as one progressive-detail interface, not a
gallery followed by an unrelated page section.

1. **Define the spatial claim before generating art.** Name the boundaries,
   flows, hierarchy, and meaningful differences the scene must carry. If the
   image only supplies atmosphere, it is not an information element.
2. **Keep pixels illustrative and HTML factual.** Generate no words, numbers,
   logos, or factual labels in the raster. Reserve calm negative-space regions
   for four to seven overlays; keep labels selectable and concise. The image
   prompt should request the regions, not their eventual copy.
3. **Use one visual grammar.** Overview and detail plates should share viewpoint,
   palette, lighting, and architectural metaphors. Generate each detail plate
   independently so its composition can explain that model rather than crop an
   overview that lacks enough space.
4. **Use one selection model.** Stable entity ids drive the overview overlay,
   detail plate, active selector, evidence link, and accessible status. A
   selection transforms the same working object; it does not introduce a second
   taxonomy.
5. **Make the transformation visible.** If the detail result begins below the
   fold, selecting an overview region should collapse the overview into a shallow
   persistent selector as the detail rises into view. Keep every peer available
   as tabs or chips, show the active item, and let the selector background expand
   the overview again. Do not hide the only route to another selection.
6. **Do not imply unsupported scale.** If area, height, brightness, or count
   represents magnitude, state the encoding and its transform beside the image.
   Label mixed, estimated, or non-attributable evidence directly in HTML.
7. **Design the narrow state separately.** Below the component breakpoint,
   annotations become a normal list and compact selectors may scroll
   horizontally. Preserve selected state, arrow-key movement, focus visibility,
   text selection, and a reduced-motion path.

##### Diffusion with panels

Pair the diffusion scene with one changing explanation panel when the image
carries the topology and the reader needs to understand one region, path, or
boundary at a time. This is often stronger than a wide comparison table whose
cells have become paragraphs: the scene preserves the whole while the panel
gives the current selection enough room to teach.

- The scene and panel are one instrument. A scene marker, compact selector, and
  panel heading share one stable id and selected state.
- The default panel answers the page's primary question. Do not make the reader
  click merely to discover what the instrument means.
- Panel switching must visibly update the selected spatial region and the
  explanation in the same interaction. Keep the result in or entering the
  viewport; off-screen panel changes are failed feedback.
- Keep peer selectors persistent and use claim-bearing labels. Tabs such as
  “CLI · local power” carry more orientation than “Option A.”
- Panels may hold prose, a short aligned list, evidence, and one conclusion.
  They are not containers for a hidden card grid or a separate taxonomy.
- Prefer a table when exact simultaneous comparison remains the reader's task.
  Prefer diffusion with panels when topology supplies the overview and reading
  one coherent path at a time supplies the understanding.
- On narrow screens, put the image first, turn spatial markers into a normal
  selectable list, and place the active panel immediately after it. Preserve
  keyboard focus and do not require hover to reveal any fact.

Validate the initial overview, overview-to-detail transition, repeated selection
while compact, expansion, keyboard path, and mobile list. A successful click
must cause an obvious visible change; updating content outside the viewport is
functionally equivalent to weak feedback.

`InterfaceKit` remains as a compatibility alias during consumer migration.

The autocomplete state controller is available at
`/api/components/autocomplete-controller.js`. Its request tokens reject stale
results and its keyboard contract includes Tab-to-commit.

## Chart Contract

- Keep incompatible units on separate axes or separate charts. Never merge revenue, users, customers, valuation, or usage merely because each changes over time.
- Preserve an unlabeled source chart as an index. Convert it to currency or another absolute unit only when a dated source value provides an explicit anchor, and label every converted value as inferred.
- Show the first value, last value, observation count, and elapsed window beside extraordinary growth multiples. Near-zero denominators can make a mathematically correct multiple economically uninformative.
- Distinguish nominal values, interval change, exact same-period year-over-year change, and annualized change. State the formula and never label an irregular-interval annualization as calendar-year growth.
- Use a log scale when orders of magnitude hide smaller series, but expose the scale and provide a linear option. Log scales cannot represent zero or negative values.
- Put provenance on individual points and annotations. Source-backed events and analyst hypotheses require visibly different treatments; omit speculative overlays when they can be mistaken for evidence.
- Dense monthly axes should retain readable month and year labels. Rotation is preferable to ambiguous sparse labels when the exact month matters.
- Overview and detail charts must share one canonical series and annotation model. A correction should update both.
- Let readers hide dominant series without deleting them from the model, and preserve that selection during reactive data refreshes.
- Never interpolate or backfill missing observations silently. A one-point series has a known level and unknown growth.
- When companies expose different operating measures, assign one business-model-appropriate KPI explicitly, retain its unit and rationale, and compare demonstrated within-company multiples without implying cross-unit equivalence.

### Chart interaction kit

Chart Wrapper was consolidated here. These are reusable interaction components, not a chart renderer. Combine `chart-sidebar.js` (selection and pinned detail), `anchored-tooltip.js` (date/value hover, focus and touch), and `chart-range.js` (drag selection). The host owns its canonical series, scales and drawing.

- For a time chart, hovering anywhere in a day's hit area should show its full date, timezone and visible series values immediately. Provide the same facts on focus and tap; never rely on sparse axis labels or a native title tooltip.
- Use distinct colors for different data sources, even when their provider is the same. Animate surviving bars between filter states so width changes are distinguishable from value changes; respect reduced motion and rapid reversals.
- Zoom the same series with a dragged range instead of inventing a separate investigation page. Keep a visible reset/preset and a keyboard-accessible alternative. Do not imply finer time resolution than the source contains.
- Preserve range, source selection and focused controls during refresh; defer chart replacement while a drag is active. Show collection time and failures compactly. Do not substitute a frozen sample for a live series, or equate token volume with subscription quota percentage.
- Keep method explanations and collection administration out of the core chart. Put essential uncertainty in the affected value or hover, with client links in the footer when needed.

```html
<script defer src="https://info-elements.aisloppy.com/api/components/chart-range.js"></script>
```
```js
const range = InfoElements.bindChartRange({
  root: document.querySelector('#chart'),
  getItems: () => visibleDays, // ordered equal-width buckets, including gaps
  onSelect: ({start, end}) => showDateRange(start.date, end.date),
  onStart: () => closeHover(),
  onEnd: () => flushDeferredRefresh()
});
// range.isActive(), range.cancel(), range.destroy()
```

The host chart must be positioned, with `touch-action: pan-y`; horizontal dragging selects, vertical touch motion remains scrolling. Set `--chart-brush-bg` and `--chart-brush-border` for both themes. The controller snapshots the current buckets, accepts either drag direction, clamps to the chart, preserves short clicks and keyboard activation, suppresses pointer clicks after completed or cancelled drags, and cancels on Escape, pointer cancellation, lost capture or resize. It adds no requests or polling. Destroy it before unmounting; call onEnd to reconcile any refresh deferred during a gesture. Preset buttons provide the basic keyboard alternative; exact keyboard date ranges remain host-owned.

### Grouped model stacks

For usage with independent app, account and model dimensions, let the reader switch the **bar grouping** (for example FairyStack versus CLI, or accounts) while **stack colors remain models**. Keep colors and stack order stable across groups, filters and refreshes. Use stable patterns or explicit bar labels for the grouping dimension; do not make one color mean both a model and an account. Match hover values and the legend to the active grouping.

Build both views from the same cross-dimensional aggregates, apply account/provider filters before grouping, and assert that regrouping conserves totals. Retain an explicit unattributed category rather than inferring the application from its account. Full-window source/model totals must not be reconstructed from a short, truncated request-detail window. Test mixed models within each app and apps within each account, unknown provenance, total conservation, model-color stability, and mobile legends.

### Chart + pinned detail component

```html
<chart-detail-sidebar id="detail"></chart-detail-sidebar>
<script type="module">
  import { bindChartSidebar } from "https://info-elements.aisloppy.com/api/components/chart-sidebar.js";
  bindChartSidebar({
    root: document.querySelector("#chart"),
    sidebar: document.querySelector("#detail"),
    getDatum: key => data[key],
  });
</script>
```

Mark interactive DOM or SVG elements with `data-chart-key`, make them keyboard-focusable, and provide `{label, group, value, change, metrics}` for each key. Metric values may be `{value, href, color}` (`href` and `color` are optional; `color` must be a valid CSS color and adds a decorative swatch matching the host series) to retain point-level provenance. The controller supports preview, pin, clear, state inspection, and teardown. Call `destroy()` before replacing the chart.

## Diagram Construction Lessons
- Treat text and diagrams as complementary representations. Text establishes
  definitions, questions, choices, and failure modes; the diagram should spend
  its space on topology, ownership, sequence, and convergence.
- Integrate a diagram as substitution, not accumulation. After accepting the
  diagram, delete prose, cards, legends, and step lists whose only job was to
  express the same relationships. Keep only text that adds definitions,
  qualifications, controls, or failure states the diagram does not encode;
  this redundancy-deletion pass is part of completing the integration.
- Design for the host surface, not an isolated export. Before rendering, inspect
  the destination’s background, surfaces, text, muted text, borders, accents,
  typography, and embedded width; pass a matching `theme`, then verify the
  diagram in the real page at desktop and mobile sizes. A generic white canvas
  inside a dark product, mismatched type, or labels that only work at the raw
  SVG size are integration failures even when geometry validation passes.
- Theme every surface that carries text, and derive dependent surfaces from the
  ones the caller named. A knockout, badge, or label plate that keeps its own
  default while the text around it is themed produces invisible text, and
  geometry validation will still report the render as valid.
- For a paired explanatory view, give the diagram the main canvas and keep only
  authored explanation in the narrower sidebar. A useful starting ratio is
  roughly 35:65; collapse to one column on narrow screens.
- Drive both views from one underlying selection and data model. A stage change
  must update the text and diagram together so their claims cannot drift.
- A sidebar does not need a miniature schema. Use a literal title, a short
  explanation, and a takeaway. Add repeated labeled fields only when the same
  questions apply across every selection; otherwise their borders and alignment
  falsely advertise comparability. Do not duplicate every node and edge in prose.
- When a subject grows, extend the same staged diagram or selection model. Do
  not add tabs that reorganize the same nodes into competing access, threat,
  binding, or other taxonomies; a control earns its place only when it changes
  the interpretation of the canonical model.
- A diagram is not automatically clearer than text. If labels become cramped,
  routes ambiguous, or the topology adds no explanatory value, retain the text
  view and simplify or omit the diagram.
- Model containers semantically, not decoratively. A group boundary should tightly enclose only the entities it owns; external flows must not cross it unless boundary crossing is meaningful.
- Use dedicated attachment ports and orthogonal corridors when several edges converge. Prefer a few extra bends over ambiguous overlaps.
- When two edges share a meaning and source, overlap them as a single centered trunk before branching. Branch only where their destinations diverge.
- Align nodes that should connect directly, then use a straight center-to-center edge. Do not add bends merely to fill available space.
- Keep a simple linear DAG on one horizontal lane. Topological rank changes the x-coordinate; it must not also create vertical drift.
- Validate every routed segment against padded node and container bounds, not just endpoints. Moving a bend can improve its arrow approach while causing an earlier segment to cross a sibling node.
- Keep final arrow approaches long enough to expose the arrowhead base, but place the last bend outside the destination's neighboring-node bounds. Make small measured adjustments and retain explicit clearance.
- Treat every edge label as a padded obstacle. Reserve a label lane, then ensure unrelated edges do not intersect its bounding box; placing labels at an arbitrary percentage of path length is not robust.
- Place edge labels near their edge with a consistent small gap; excessive separation makes ownership ambiguous. Center labels over shared trunks when they describe the shared relationship.
- Label a semantic edge group once. When several parallel or converging edges
  all mean “capture,” “query,” or another identical relation, place one label in
  the group’s reserved corridor instead of repeating it on every path. Keep
  separate labels only when the relationships genuinely differ.
- Keep labels horizontal, concise, and selectable. Use a background knockout when needed, but avoid redundant swatches when the label and edge already share a clear visual identity.
- Size nodes and labels for the embedded viewport, not the raw SVG canvas. Primary node labels should remain comfortably readable after responsive scaling.
- Preserve a 14 px minimum for primary node labels. Wrap first, then grow the
  node and let the canvas scroll; never solve containment by shrinking the type.
- The renderer's canonical default presentation uses system sans-serif type,
  dark 2 px node borders, and a 1200 × 720 auto-layout canvas. Corpus and
  production consumers must submit the same graph spec without host theme or
  viewport overrides; embed the returned SVG responsively instead. Graph
  diagnostics return `presentation` constraints for node geometry, border,
  minimum font size and scale, and overflow policy.
- For small, stable diagrams, explicit routes and label lanes are clearer and more reliable than a general layout engine. For generated graphs, score candidate placements by label collisions, edge crossings, bends, and path length, then reroute around accepted label boxes.
- Treat learned layout as a candidate-ranking experiment, not a replacement for
  geometry validation. A learned critic may rank valid layouts using semantic
  importance and accumulated human edits; deterministic checks still reject
  collisions, crossings, unreadable labels, contrast failures, and viewport
  overflow.
- Gallery previews should be faithful miniatures of the real diagram, preserving its topology, grouping, and color semantics rather than substituting a generic sketch.
- Remove rejected routing variants once a canonical representation is chosen; comparison controls should exist only when comparison remains the purpose.

## Explainer Page Contract

A page built around one instrument, explaining one hard idea. Distilled from the
Software Atlas explainers and from Keystroke; the narrative versions were
retired with `explainer.aisloppy.com` when it merged here.

- Build one instrument, not widget paragraphs. Before adding a selector, stage,
  tab, or badge, establish that it changes one dimension of the canonical
  instrument rather than opening a parallel organization of the subject.
- Use real specimens and date-stamp them. Invented examples teach invented
  lessons.
- Teach by showing, and let absence show too: a state with nothing in it is
  evidence, not an empty slot to hide.
- Define every term of art at the point it is first used, not in a glossary the
  reader must leave the page to consult.
- An interactive model runs the real mechanism. A model that fakes its own
  behaviour teaches the fake.
- State the boundary on the artifact itself and report gaps loudly. A model that
  hides its caveats overclaims.
- One narrative spine, preferably expressed as states of the instrument; each
  stage earns the next. Cut parallel explanations and anything that would
  survive being deleted.
- Commit to an aesthetic that carries structure rather than decorating it.
- Progressive disclosure, never silent truncation.
- Drive the finished page before shipping — in the real host, at desktop and
  mobile widths, in both themes.
- Write product copy for a reader who never saw the review. Copy revised after
  feedback tends to answer the reviewer instead of the reader: "X is a spring,
  **not** a conductor" rebuts a question nobody reading it asked, and "**Not
  every** system works this way" concedes a point nobody made. State what the
  thing is, then state what the alternatives do. Distinguish this from a genuine
  teaching contrast — "a keycode is not a character until a layout says so"
  draws a distinction the reader needs — and keep those.
- Attention concentrates on the instrument and starves everything around it. If
  a reader reports not reading the opener, the sidebar, or the tables below,
  believe them: move what matters into the instrument and delete the rest.

## Rendered Scene Contract

Applies when the instrument is a 3-D scene. Learned building Keystroke.

- Put the explanation inside the view. Stage name, cost, and mechanism belong in
  a HUD over the scene; a panel beside it is not read.
- The most prominent object in the scene is a promise. Make it the control —
  pick it by raycast, and treat a drag beyond a few pixels as an orbit so
  rotating never fires it.
- Narration is what running the model does, not a mode to switch on.
  Pre-generate audio at build time and commit it: no runtime dependency, no
  per-view cost, works offline. Caption every spoken line, and say so visibly
  when a browser refuses to play rather than continuing in silence.
- Whatever paces a run must also drive its animation. Sequences with a wide
  ratio between steps cannot be made legible by any playback speed — pace by the
  narration instead — and when the pacing source changes, the value the scene
  animates from must change with it or the motion silently dies.
- Every run is interruptible: pause, resume, restart, step. Space pauses and
  never restarts; a global key handler that replays on any key destroys the
  reader's place at the moment they try to stop.
- Scales more than two orders of magnitude apart need separate models, not one
  camera. Ease within a model; cut through a short fade between models, because
  dollying across those scales reads as a glitch.
- Compute framing from the subject. A stage names what it is about; distance
  comes from that object's bounding sphere and the field of view. Hand-dialled
  targets and radii are an hour of directing per model and none of it is
  knowledge.
- Model one real design and name the alternatives in a line. A composite of two
  designs cannot answer "what touches what".
- Design the idle frame. It is the one a reader studies, and it is not the last
  frame of the animation.
- No two surfaces may share a plane. Coplanar geometry z-fights, and the result
  reads as random flicker rather than as a bug with a cause. Give every stacked
  layer its own height, and cut a hole in the layer a part passes through rather
  than overlapping it.
- An animation that flickers is either depicting something or broken; decide
  which. Time a deliberate flicker to the event it depicts — a contact bounces
  *after* it closes, not while it is still approaching.
- Fade or hide whatever occludes the mechanism once it has done its job. The
  part a reader presses is rarely the part worth watching.
- Keep the model the source of truth and the scene a view of it: the data half
  of the code should not reference the renderer, derived figures should be
  computed rather than typed into prose, and the page must stay complete with no
  WebGL.

## Guide As Design Memory
- Treat this guide as durable operational memory for agents, not as a session
  transcript. Add stable, reusable lessons that have survived implementation
  and visual inspection.
- Keep experiment-specific observations and rejected variants in an
  investigation note. Promote only the resulting general rule here, with
  literal language an implementing agent can act on.
- Update or replace a contradicted rule rather than appending another opinion.
  The guide should expose the current model, not preserve chronological debris.
- Keep the guide versioned with renderer behavior and regression coverage.
  Examples are evidence; they do not override a documented rule silently.

## Renderer Regression Corpus
- `/demos?case=fairystack-releases` is the captured FairyStack Releases page.
  It preserves the host’s 0.72 scale (54.72 px nodes), links to the actual Releases
  surface, and retains the pre-fix screenshot. Container headings are reserved
  obstacles during edge-label placement; diagnostics report any remaining
  `container_title_edge_label_overlap` instead of calling it valid.
- Every corpus case packages graph input, renderer viewport, and final host
  frame. These are distinct constraints: a consumer may request layout while
  wide, then settle into a narrow column and scale or scroll the returned SVG.
  The review gallery must reproduce the consumer transform rather than applying
  generic responsive CSS.
- Consumer-derived cases retain provenance: app, durable context link, capture
  time, observed dimensions, and any immutable screenshot of the rendered UI.
  The compact corpus page shows only the rendered graph and its source-message
  link; detailed provenance and diagnostics remain available from the API.
- A corpus gallery contains only captured consumer evidence with a durable
  source-context link. Keep synthetic geometry fixtures in automated tests,
  outside the review corpus.
- During the temporary broad-capture phase, bounded automatic FairyStack
  answer captures may also appear as explicitly provisional cases. They require
  a durable message link and source revision, remain distinct from screenshot-
  backed curated evidence, and must never be presented as human-reviewed. Match
  FairyStack's answer transform by trimming the graph bounds and scaling nodes
  to 52 px rather than showing the renderer's intrinsic SVG dimensions.
  For these vertically scrolling corpus frames, the gallery may select the
  opposite flow direction when the source exceeds 125% of the host width,
  the alternate is geometry-valid, and it reduces displayed width by at least
  25%. This stacks broad fans vertically while leaving tall chains alone.
  The captured `spec` remains unchanged; `diagnostics.layout.direction` names
  the displayed direction and `diagnostics.orientation` records the adaptation
  or a concrete alternate-render error. Intrinsic reference cases retain their
  source orientation. Text is always upright; no SVG rotation is applied.
- A responsive host contributes one corpus case per materially different
  constraint regime. Do not call a case responsive while reviewing it in an
  unspecified or browser-dependent box.
- Curated graphs are created only after reviewing a renderer's unaided output; never seed them from a hand-tuned diagram.
- Unseen topology cases live in `tests/fixtures/zero-shot/`; accepted results graduate into the curated corpus.
- `/demos` renders the complete current corpus as a compact visual index. Each
  item contains only its graph and FairyStack source-message link.
  Each canvas also preserves the captured host height instead of imposing one
  gallery-wide vertical cap; narrow screens cap it at 70vh and retain scrolling.
  Its grid span comes from both dimensions of the captured host frame: compact
  frames occupy one column, large frames occupy at least two, and especially
  wide or large frames may occupy three. Do not collapse a large square frame
  merely because its aspect ratio resembles a compact card.
  Never infer gallery size from provider identity or graph node count.
  The page-level renderer-code disclosure shows a syntax-highlighted,
  selectable Python view of the real `render_graph(spec)` orchestrator and its
  helper-function boundary. Calls in the orchestrator open the current helper
  source in the adjacent pane and are the sole helper navigation; source comes
  from the authenticated renderer source endpoint so the browser does not
  drift from deployed code. Keep code first. Beneath the selected helper,
  progressively reveal one compact contextual note: its role, relevant active
  consumers, algorithm family, limitation, or controlled next experiment.
  Associate simplified Sugiyama details only with ranking, and theme consumers
  only with theme resolution; do not lead with an always-visible summary. A
  compact layout selector compares custom layered, ELK Layered, and
  force-directed output. The force-directed view uses compact boxes and direct
  arrows so dense non-hierarchical placement remains a real alternative rather
  than a second layered diagram. Custom layered remains the default and neither
  comparison changes `POST /api/render/graph` output.
  Comparison requests have explicit browser deadlines and visible running,
  completed, or failed states. State
  explicitly that node placement and SVG assembly remain inline; do not imply
  a cleaner decomposition than the implementation has.
  This disclosure is not repeated inside corpus items.
  Automated geometry flags and human verdicts remain API evidence rather than card UI.
  Show one item per source message, preferring its newest automatic capture to
  an older curated specimen so the index matches FairyStack's current layout.
  Each item has a compact dismissal control. Dismissal archives its durable
  source URL across every comparison strategy without deleting captured or
  curated evidence, and the immediate Undo action restores it.
  Each strategy view also exposes persistent correct/incorrect ratings beside
  the dismissal control on every case. Selected votes remain visible, and the header score reports correct
  votes divided by rated cases plus the remaining unrated count. Never include
  unrated cases in the correctness denominator.
  Store one mutable automatic capture per source URL rather than letting repeat
  renders evict older unique messages from the bounded corpus. Order rare cyclic
  cases first so feedback-edge regressions remain visible.
- `GET /api/render/corpus` returns every corpus specification, current SVG,
  renderer version, geometry diagnostics, and aggregate human verdicts.
- Authenticated reviewers submit `{case_id, verdict, note}` to
  `POST /api/render/corpus/reviews`. One reviewer has one revisable verdict per
  case and renderer version. Earlier-version judgments remain available for
  before/after comparison instead of being overwritten by a later review.
- Run `python3 -m unittest tests.test_graph_renderer` before changing renderer geometry.
- A render fails when it has a hard constraint violation, collision—including
  overlapping edge labels—missing node/edge, malformed SVG, or unexpected
  golden-contract change.
- After every geometry change, visually inspect the entire route—not only the edited corner—and test node, label, container, and arrowhead intersections.
- A dense whole-program call graph is not made inspectable merely by fitting it
  inside a static node-link SVG. Measure edge crossings and unrelated-node
  intersections; reject the layout when either is nonzero. Preserve the graph
  as data, then expose a selected neighborhood, staged subgraph, or adjacency
  matrix instead of claiming the all-edges overview is valid.


## Hosted Table Element

```html
<script defer src="https://info-elements.aisloppy.com/components/info-table.js?v=1.0.0"></script>
<info-table id="comparison"></info-table>
<script>
customElements.whenDefined("info-table").then(() => {
  document.querySelector("#comparison").setData({
    caption: "One structure, different arenas",
    columns: [
      {key: "arena", label: "Arena"},
      {key: "stakes", label: "Stakes"}
    ],
    rows: [{arena: "Interpersonal", stakes: "Safety, autonomy"}]
  });
});
</script>
```

The host owns and validates its domain data. The element constructs DOM safely,
preserves selectable text, uses semantic markup, and scrolls horizontally rather
than changing the information model on narrow screens.

## API

- `GET /api/elements` — versioned registry of supported elements, selection
  rules, maturity, and artifacts.
- `POST /api/recommend` — send `{data, question?}` and receive structural
  analysis plus ranked component ideas. `data` is either a non-empty array of
  up to 10,000 values/records or a graph object with `nodes` and `edges`.
- `POST /api/render/graph` — render a versioned DAG specification to SVG with
  geometry diagnostics. Renderable layouts that fail quality checks still return
  HTTP 200 with `svg`, `diagnostics.valid: false`, and `quality_warning`; consumers
  should show the diagram with a visible warning instead of suppressing it.
  Send `{schema_version:1,title,containers,nodes,edges,theme?}`.
  The response's `metering` makes the cost boundary explicit: canonical graph
  rendering is deterministic, makes zero provider calls, and reports
  `provider_cost_usd: 0`. Optional critic reviews are the only diagram-adjacent
  BrightWrapper operation and preserve upstream 402 budget and 429 rate-limit
  failures.
  `theme` accepts `canvas`, `surface`, `surface_alt`, `text`, `muted`, `line`,
  `edge` as 6-digit hex, plus `font_family` of `system-ui` or `ui-monospace`;
  any other field is a `400`. `surface_alt` is the edge-label knockout and
  defaults to whatever `surface` you pass, so a dark caller does not have to
  name it. Every painted text pair — `text` on `surface`, `text` on
  `surface_alt`, `muted` on `surface` — must clear 3:1 or the request is a
  `400`. Invalid input returns `400`; failed layout validation returns `422`
  with diagnostics.
- `GET /api/render/corpus` — render the curated graph-layout evaluation corpus
  with current diagnostics and aggregate human review counts.
- `POST /api/render/corpus/reviews` — authenticated human verdict for one corpus
  case: `readable`, `label_overlap`, `edge_crossing`, `node_collision`, or
  `other_issue`, plus an optional note.
- `GET /api/components/chart-sidebar.js` — dependency-free ES module for chart selection and inspectable pinned detail; cross-origin imports are enabled.
- `GET /api/components/graph-inspector.js` — node, edge, and container hover/focus/pin behavior (edges heat path + label + hit lane together) plus a fixed-height shared detail overlay that leads with the explanation; cross-origin imports are enabled.
- `GET /api/components/anchored-tooltip.js` — dependency-free viewport-safe tooltip controller with collision handling, stacked facts, and keyboard/touch support.
- `GET /api/components/image-overlay.js` — generated or sourced raster image with optional accessible HTML overlays and a shared-selection event.
- `GET /components/lightbox-v1.js` — dependency-free accessible lightbox for images, selectable DOM, and bounded asynchronous renderers.
- `GET /api/generate-data?count=1000` and
  `GET /api/generate-edges?count=500` — bounded benchmark fixtures for table and
  relationship components.
- `GET /agent-guide.md` — this machine-readable guide.
- `GET /agent-guide` — rendered human guide.
- `GET /api/version` and `GET /api/health` — deployment status.

All endpoints are synchronous and public. JSON writes require
`Content-Type: application/json`. Give every request an explicit timeout and
propagate concrete errors; never silently choose a fallback format.

## Completion Gate

- The format answers the reader's actual question.
- One canonical model drives all linked views; no competing taxonomy organizes
  the same entities a second time.
- Tables and labeled value groups use stable fields across every compared item;
  irregular explanation remains prose.
- Every visible boundary communicates grouping or state.
- Interaction changes selection, interpretation, or downstream detail.
- Redundant representations were removed.
- Text remains selectable; keyboard and contrast states work independently.
- Error, empty, timeout, and mobile behavior were verified in the host page.

For a page or a rendered scene, additionally:

- The explanation sits inside the instrument; nothing load-bearing sits beside or below it.
- The most prominent object is the control it appears to be.
- A run narrates by default, captions every line, and states it when audio is blocked.
- Pause, resume, restart, and step all exist; space pauses and never restarts.
- Motion is driven by whatever paces the run and freezes when it pauses.
- Each scale regime is its own model, and framing is computed from a named subject.
- The idle frame was designed rather than inherited from the end of the animation.
- One real design is modelled, with the alternatives named in a line.


## Resource links

- `/demos`: current graph corpus; approved Info Elements sign-in required.
- `/demos?case=<case_id>`: focus on one current case. Repeat `case` to compare a set. IDs exactly match `GET /api/render/corpus`. Unknown or dismissed IDs show an explicit empty state. Focused links include rated cases and retain focus across layout strategies.
- `/demos?case=work-terrain-world-1&case=work-terrain-world-2`: the two story graphs, captured from Work Terrain revision `5783b70`. Source links open current worlds; saved screenshots identify the captured version. These are evaluation cases, not approved layouts.
- For consumer labels longer than the renderer's 80-character limit, retain full original text in node `detail` and use concise card labels.

## Code explorer

First-class reusable `<info-code-explorer>` web component, API version 1.1.0.
Use for Python-like code or pseudocode whose calls open helper source and host-owned detail in a right column (stacked on narrow screens). Source is rendered as text, never executed or interpreted as HTML.

```html
<script src="https://info-elements.aisloppy.com/api/components/code-explorer.js?v=1.1.0"></script>
<info-code-explorer id="code">
  <div slot="status">Current snapshot</div>
  <div slot="detail">Host-owned figures, controls or notes</div>
</info-code-explorer>
<script>
const code = document.querySelector('#code');
code.configure({
  label: 'Training · pseudocode',
  source: 'probabilities = predict(vector, W)',
  functions: {predict: 'def predict(vector, W):\n  return softmax(vector @ W)'},
  selected: 'predict'
});
code.addEventListener('code-select', event => {
  // event.detail.name identifies the clicked function.
});
</script>
```

`model = spec` is equivalent to `configure(spec)`. `source` and the nonempty `functions` map are required strings; function keys must be identifiers. `label` and `selected` are optional. Unknown selected functions and malformed inputs throw concrete errors; invalid configuration also shows a visible error. `select(name)` selects programmatically without emitting; `select(name, true)` emits `code-select`. `selected` returns the current selection. `showError(message)` replaces the code surface with a visible host-owned load failure.

Optional `lineLinks` maps exact source lines to keys in `functions`, making each entire line one keyboard-accessible button without nested call buttons. Lines must exist in source and targets must exist in functions; invalid mappings throw and show an error. `--code-explorer-columns` can set desktop column proportions; narrow screens still stack. By default only calls present in `functions` are buttons. Click or Enter/Space selects; hover does not change detail. Selection is exposed with `aria-pressed`. Identical configuration preserves source nodes, focus, selection, scroll, host inputs and disclosures. Host detail slots are never replaced by the component. Changed source is an explicit replacement; updating live host figures does not require reconfiguring the component. `theme="light|dark"` overrides document `data-theme`, then OS preference. Inherited `--ink`, `--panel`, `--line`, `--muted`, `--accent` support native app palettes. `--code-explorer-max-height` optionally bounds each code pane. The component owns no fetching, jobs or polling; hosts own bounded source loading and visible failures.

### Resource links — code explorer

- `/api/components/code-explorer.js?v=1.1.0`: public JavaScript, wildcard CORS, five-minute cache. No identity or authorization required; classic script or module-script loading both work.
- `/code-explorer-demo`: public browser example, no identifiers or authentication.
- `/demos`: authenticated graph-layout consumer; its helper source still comes from the authenticated `/api/render/graph/source` endpoint. No renderer source is made public by this component.
- `/api/elements`: public catalog includes `code-explorer` and its component/demo URLs.

The renderer demo and Coding Agents' training inspector both consume this component; neither keeps its own syntax highlighter or code-selection renderer.

Code explorer imports are bounded: Coding Agents abandons a stalled component import after 12 seconds; renderer demos bound combined component/source loading to 15 seconds. Failures appear in the host and do not block unrelated visuals.

Code Explorer 1.2.0 adds CSS parts `layout`, `source-panel`, `detail-panel`, and `detail-heading`, a `source-heading` slot, and a bubbling `code-source-activate` event when its source panel is clicked without a text selection. Consumers can retain and narrow a parent explorer alongside a nested explorer; stop nested selection events at the nested host. Existing configure/select behavior is unchanged.

## Agent entry through the public showcase

An agent-login fragment arriving at `/` is forwarded intact to `/elements`, where the existing private shell consumes it through AuthReturn. Ordinary visits remain on the public showcase. No ticket is redeemed by the showcase itself.

The private shell waits for an incoming handoff before restoring any previous identity. A rejected or timed-out ticket leaves AuthReturn’s error visible instead of reloading it away. `tests/showcase-handoff.cjs` checks the public-to-private route and success/failure/timeout behavior using fixture-only provider responses.

## Experimental overlapping node groups

- `/demos?case=notebook-node-groups-desktop&case=notebook-node-groups-mobile`: first anonymized notebook consumer experiment, retained with its reported edge-overlap and boundary-ambiguity defects. Captured SVG is reference evidence, not production-engine output or an approved layout.
- `/demos?case=notebook-node-groups-desktop&layout=groups`: separate experimental membership-region adapter. ELK.js 0.11.1 rectangle packing places exact membership intersections; libavoid-js 0.5.0-beta.5 routes observed edges. A shared computer remains one identity. An empty membership list means unknown, not proven exclusion from all networks.
- `/demos#node-group-options`: sourced candidate comparison, cached Software Atlas LOC and initial trial limitations. The standalone weighted-table generation failed because its configured model provider had insufficient credit; evidence is retained locally.
- `POST /api/render/node-groups`: public, stateless layout; JSON `{nodes:[{id,label}],edges:[{source,target}],groups:[{id,label,members:[node_id]}]}`. Limits: 100 nodes, 200 edges, 0..20 groups. Returns `{state,engine,svg,geometry,diagnostics}`; malformed input is 400, worker failure 502, hard 25-second worker timeout 504 with `state: timed_out`. Anonymous consumers must anonymize private identities before submitting; requests are not corpus capture. Existing `/api/render/graph` is unchanged.
- `GET /api/render/corpus/shadow/groups`: approved reviewer session required. Only membership-group cases are evaluated. Other engines omit these cases rather than silently discarding memberships. Desktop/mobile cases preserve independent frame evidence.
- `groups` is an experimental correctness-rating strategy. Captured reference output has no production-layout rating controls.
