---
name: create-vmax-tools-page
url: https://vmax.ai/skill/template
description: Clone the /-/vmax-tools-template shell into a CSS-perfect replica — the same measurements, fonts, spacing, layout, and structure — for any context (an evaluation viewer, a data explorer, a dashboard, an annotation tool, anything). Use when standing up or restyling a tools page, reusing the ApplicationNavigation / ToolbarControlsFixed chrome and the sidebar, composing a body from the next-vmax-tools Form* primitives (tables, diffs, JSON explorer, graph compares, inputs, modals, a terminal window), or adding a new Form* component for a case they do not cover.
---

# create-vmax-tools-page

## Overview

The `/-/vmax-tools-template` page is a **reference implementation to clone for any context**. The whole point of this skill is that anyone can stand up a new tools page that is a CSS-perfect replica of it — the same measurements, fonts, spacing, layout, and structure — for whatever they need: sharing controls, an evaluation viewer, a data explorer, a training dashboard, an annotation tool. The frame stays identical; only the body changes.

The whole kit lives in `next-vmax-tools/` (alias `@nextjs-vmax-tools/*`):

- **`AppContainer` is the shell** — `ApplicationNavigation` (the top bar), a sidebar, and `ToolbarControlsFixed` (the fixed bottom toolbar), wrapping the body columns a caller passes.
- **Reuse the chrome as-is.** `ApplicationNavigation` and `ToolbarControlsFixed` are shared so every tools page reads the same; lean on the **sidebar** for overflow — a long page's table of contents, links out — wherever it earns its place.
- **Compose the body from `Form*` components** (tables, diffs, JSON explorer, graphs, inputs, a modal, a terminal window). When a case isn't covered, **add a new `Form*.tsx` with its paired `.module.css`** — that is the expected way to grow the kit, not a one-off.
- **It stays a pixel replica because everything is built off the variables** — sizes off `--theme-grid-block` / `--theme-grid-block-applications`, colour/border/font off `--theme-*`, the body on `--theme-font-family-client-apps`. Never inline a value a variable already names (see [Conventions](#conventions)); that discipline is what makes a clone match the template exactly across both themes.

The routes read the `TOOLS_PAGES` registry in `tools-pages.tsx`, which also holds the default sidebar's `EXAMPLE_TEMPLATE_SECTIONS` and the `authenticated` page's `AUTHENTICATED_ROUTE_GROUPS`.

This skill is the narrow one for the tools shell. For the broader `next-vmax` application-component library (`ApplicationWindow`, `DefaultLayout`, `Logo`, the graph references), read [`create-old-vmax-application-ui`](../create-old-vmax-application-ui/SKILL.md) too (deprecated).

## The registry is the single source

`next-vmax-tools/tools-pages.tsx` holds one `TOOLS_PAGES` row per page, the way `next-vmax/islands/island-scenes.ts` holds one row per scene. A row **is** the route and its metadata:

```tsx
export interface ToolsPage {
  label: string;
  description: string;
  icon: React.ReactNode;
  sections: ToolsPageSection[];
  columns: React.ReactNode[];
  emptySidebar?: boolean;
  columnVariant?: 'client-ui-columns' | 'horizontal-columns';
  sidebarGroups?: ToolsSidebarGroup[];
}
```

`ToolsSidebarGroup` / `ToolsSidebarRoute` (a group is a sidebar `Item`, each route a `SubItem`):

```tsx
export interface ToolsSidebarRoute {
  path: string;
  description?: string;
  icon: React.ReactNode;
  href?: string;
  tone?: 'system';
  action?: 'write' | 'modal';
}

export interface ToolsSidebarGroup {
  label: string;
  description?: string;
  icon: React.ReactNode;
  isViewer?: boolean;
  routes: ToolsSidebarRoute[];
}
```

A group that declares `isViewer` (even `false`) opts into the focus-primary state: its Item glass button renders `active` when the value is `true`. The `[...tool]` route computes the real value from `Server.setup` (the signed-in viewer) and injects it into any group whose `isViewer` is defined, so the `authenticated` page's "Signed in" group lights up only when the visitor is actually authenticated.

`label` is the route metadata title; `description` feeds the page metadata (title + OpenGraph + Twitter); `icon` (a `next-vmax/Icon` glyph) and `sections` are page metadata not drawn while the template is one page; `columns` holds one node per available content column; `emptySidebar` (default off) renders the shell's sidebar frame with no items — the blank-canvas variant, used by the `client` page (`FormClientExperience` + `FormClientExperienceOptions`); `columnVariant: 'client-ui-columns'` swaps the default equal-width column grid for the top-nav's fixed-rail-plus-fluid-centre technique (a measured fluid first column, a fixed 240px second column, reverting to the default grid under 880px — see `next-vmax-tools/AGENTS.md`); `columnVariant: 'horizontal-columns'` (the `horizontal` page) drops the sidebar and renders every column as a horizontally scrollable rail of 424px full-height columns, ignoring `?columns` so all of them are always shown — a layout that reads one column at a time on mobile (see `next-vmax-tools/AGENTS.md`). `columnVariant: 'island-scene'` (the `islands` page) renders one fluid full-height column holding `FormIslandScene` (the isometric engine, filling `calc(100dvh - 48px)`) and hides the sidebar under 568px so the scene is full-screen on mobile. The catch-all `[...tool]` route reads these flags and passes them to `AppContainer`.

- A page may supply its own sidebar with `sidebarGroups` (`ToolsSidebarGroup[]` — each group a `label`, optional `description`, an `icon`, and `routes` of `{ path, description, icon, href?, tone? }`). When set, `AppContainer` renders each group as a **`SidebarItem`** header followed by a **`SidebarSubItem`** per route — the same three components (`Sidebar` / `SidebarItem` / `SidebarSubItem`, each in `next-vmax-tools/` with its own `.module.css`) that render the default template sidebar, so there is ONE Item/SubItem pattern, not two. Every field is optional and hides when absent: a `description` stacks a muted second line under the label (smallest FormTemplate font, single-line ellipsis with the full text on `title` hover); `href` makes a row a link (absent, it renders as static text, e.g. a `/[username]/-/[id]` pattern you cannot visit); `action: 'write' | 'modal'` makes it a clickable button instead (a serializable token the client `AppContainer` maps to a handler — `'write'` creates a post and opens the editor, live only for a signed-in viewer; `'modal'` opens `FormModal`); `tone: 'system'` strikes a SubItem's label through for plumbing routes no user should open; a group's `isViewer` lights its header glass with the focus-primary state. Build a new sidebar by composing these three components; never re-inline the row JSX. The `authenticated` page is the reference (`AUTHENTICATED_ROUTE_GROUPS` maps every app route by audience over a single `FormEmptyColumn`).
- The sidebar (`AppContainer`) is otherwise **not** a page switcher; on the default template it holds one fixed group from `tools-pages.tsx`: a "Template" Item whose SubItems (`EXAMPLE_TEMPLATE_SECTIONS`) anchor the default page's headings by id — `FormTemplate` in the first column, `FormTemplateGraphs` in the second; sibling examples share one grouped section ("Comparisons", "Elements", "Tables", "Graphs") to keep the list short, and the list is alphabetized by label. Each Item's icon is the app's glass square button (`ButtonActionGlass`); its plain-square SubItems read a level apart, and each SubItem is a link (`sectionAnchorHref`) that carries the current query string forward: it jumps to its heading by `id`, forcing `?columns=2` first for a section marked `columns: 2` (a second-column `FormTemplateGraphs` heading) so it is rendered before the scroll. Every SubItem id MUST match the heading id it anchors — no test guards this, so when adding or renaming a section check the id against the `FormTemplate` / `FormTemplateGraphs` heading it points at by hand.
- The bare `/-/vmax-tools-template` route serves `DEFAULT_TOOLS_PAGE_KEY`; the `[...tool]` catch-all route 404s on any key that is not registered.
- `?columns=N` opens N of a page's `columns` (AppContainer clamps to what the page actually provides).

The module is neutral (no `'use client'`): the server routes read `columns` + metadata.

## Add a tools page

1. **Build the body** — a `'use client'` component in `next-vmax-tools/` composing the primitives below, paired with its `.module.css`. `next-vmax-tools/FormTemplate.tsx` is the worked reference: it composes nearly every primitive over the sharing form. Reuse an existing body (`FormTemplate`, `FormTemplateGraphs`) when it already fits.
2. **Register it** — add a `TOOLS_PAGES` row in `tools-pages.tsx`: a `label`, `description`, an `icon` from `@nextjs-vmax/Icon`, `sections`, and `columns` (one `<YourBody />` per column). Give each column element a stable `key`. Serve it at the bare path by pointing `DEFAULT_TOOLS_PAGE_KEY` at its key.
3. **The route renders it.** `app/-/vmax-tools-template/[...tool]/page.tsx` (a catch-all) renders any registered key from `tool[0]`, so a deeper path like `/islands/{type}` reaches the one `islands` page and the body reads `{type}` from the pathname — no per-type folders and `buildToolsPageMetadata` gives it a title and card. The sidebar is not a page switcher today — reintroduce a registry-driven page-nav in `AppContainer` if a page needs to be reachable from the sidebar.

## Body primitives (`next-vmax-tools/`)

| Component | What it renders |
|---|---|
| `FormTemplate` | The template settings form — `GlassButton` actions (its "Open test modal" opens `FormModal` through the modal system) and inline examples of every primitive below, grouped so sibling examples share one section and one sidebar row: "Comparisons" (both text diffs over a paraphrase plus the code edit), the `FormCode` section (a python verifier gate, the shell commands that reproduce its run, and its verdict log), the `FormJSONExplorer` walk of a run record, "Elements" (the `FormInput` and `FormTextArea` demos, the `Checkbox` sharing toggles, the `FormList` pair, and the "Text styles" reference — including the `InlineCode` `tone` variants), the three live experiment-control sections (`FormSelect` picking an evaluation suite whose note echoes beneath it, `FormSlider` scaling test-time compute — the K slider reprices a labeled corpus live — plus stepped context-window and LoRA-rank knobs, `FormSegmentedControl` sweeping the planner and an uncontrolled memory-module strip), "Tables" (the three `FormTable` variants, the LoRA-adapter card, and the grouped main-results table), then the UNIX-CTF + LoRA sections (a `FormMetric` training-signal strip, a `FormEquation` pair — the frontier band and a `$$`-wrapped cases reward, a `FormRatioBar` parameter-efficiency meter plus a harvest-yield funnel, a `FormStackedBar` pair — the difficulty mix on semantic band colours and a palette-cycled environment-dressing row, a `FormPipeline` pair — the harvest stages and a looped self-play step, a `FormPreferencePair`). The default page's first column. |
| `FormModal` | A dismissable modal modelled on `next-vmax/ModalAuthentication` (gradient-edged brand-over-content panel) but on the AppContainer client-app font: a small VMAX logo, a gradient divider, body copy, and a "Close this Modal" `GlassButton`. Opened via `useModals().open(FormModal, {})` (`@components/ModalContext`); the root layout's `Providers` mounts the `ModalRenderer`. |
| `FormTemplateGraphs` | A `WebTerminalWindow` section (the full `Loader` set) with the `FormTranscript` agent episode directly beneath it, the `FormDisclosure` rollout inspector (three stacked rollouts — the open one carrying toned-`InlineCode` gate marks and a `FormCode` verdict log, the third deep-linkable by hash), a `FormTokenHeatmap` token-credit run, a `FormMatrix` capability grid, a `FormRollout` world-model rollout, a `FormSparkline` training-dynamics pair, a `FormDumbbell` benchmark-category-shift pair (UNIX-CTF / world-model themed), and a `FormLineage` figure of how the capstone task was bred, then one "Graphs" section holding every chart: the `FormDistributionCompare` grouped bars, the `FormGraphCompare` before/after pair, and the rest of the document-system graph catalogue over frontier-model evaluation data, each catalogue entry titled at the caption weight below the one anchored heading (the Line entry is the multi-series form — two self-play solve-rate curves with confidence bands). The `FormInput` demo lives in `FormTemplate`'s "Elements" section. The default page's second column (`?columns=2`). Exports `MODEL_GRAPHS`. |
| `FormIslandScene` | Fills a `calc(100dvh - 48px)` fluid column with an isometric scene, following the tools theme (no `Providers`). Reads the pathname's last segment: `whole-world` renders the distant-islands scene through `next-vmax/islands/WorldStage`, any `isIslandKey` value renders through `next-vmax/islands/IslandStage`, else `DEFAULT_ISLAND_KEY`. Used by the `islands` page under the `island-scene` variant. |
| `FormEmptyColumn` | An empty full-height fluid column body (`min-height: calc(100dvh - 48px)`, no content). For a page whose payload is the sidebar or chrome, not the content well — the `authenticated` route uses it as its single column. |
| `FormHorizontalColumn` | A minimal column body — a `heading` and a `paragraph` on the shared `FormTemplate` typographic scale, nothing else. The `horizontal` page fills its 424px full-height rail with eight of these to label each column. Copy it as the skeleton when a horizontal-rail column needs real content. |
| `FormTable` | Data table capped at 768px, scrolling sideways once a `fluid` column pushes it wider. `columns` (`{ key, label, fluid?, align?, sortable? }`), `rows` OR `groups` (`{ label, rows }[]` — labelled row batches behind full-width header rows, the paper results-table shape; `<strong>` marks best values), `caption?`, `variant` (`gradient` \| `ruled` \| `striped`). A `sortable` column's whole header cell sorts the rows — descending first, then ascending, shown by a stacked up/down caret that lights the active direction; groups sort within themselves so a sectioned table keeps its sections — comparing what a cell means (a string sorts by its leading figure, a bold-wrapped best-value ranks by its figure too, a text-less cell sinks). Exports the pure `cellSortValue` / `sortTableRows`. |
| `FormTextCompare` | Dependency-free unified before/after diff (LCS line diff); removed lines wash red, added green, each snapped to a grid block. `before`, `after`, `caption?`. Exports the pure `diffLines`. The `--theme-diff-*` tokens (root `global.css`) shared by every before/after surface are DELIBERATELY theme-independent — translucent washes for line/row backgrounds, solid values for markers and graph-diff bar fills — because the light red / light green reads on both VMAX themes; do not add per-theme overrides. |
| `FormTextCompareSideBySide` | The split-view sibling — before left, after right, row-for-row aligned with hatched fillers. Reuses `diffLines`; exports `pairDiffRows`. |
| `FormJSONExplorer` | Collapsible JSON tree in the same measured code-diff rows, a `+`/`-` toggle per object/array. `data`, `caption?`. Exports the pure `flattenJson`. |
| `FormCode` | Syntax-highlighted code in the same measured rows: a numbered gutter, one grid block per line, 768px cap with sideways scroll. `code`, `language?` (`python` \| `json` \| `shell` \| `log` \| `text`, default `text`), `caption?`, `copyable?` (a copy-to-clipboard control on the caption line, for command blocks). A dependency-free per-line regex tokenizer (backtracking-hardened string classes — keep that shape when editing or adding a language pattern; `next-vmax-tools/AGENTS.md`) colours tokens by mixing `--theme-graph-option-*` toward `--theme-text`, so both themes stay legible; log verdict words (`PASSED` / `FAILED` / `ERROR` / `WARNING`) take the diff tones. Lines past 400 chars render plain; rendering caps at 512 lines behind a counted footer. Exports the pure `tokenizeCode`. |
| `FormDisclosure` | The fold: a native `<details>` on the kit's hairline panel. `label`, `meta?` (right-aligned monospace facts — a reward, a step count), `children`, `defaultOpen?`, `id?` (deep-linkable: a matching URL hash opens it before scrolling), `caption?`. The `+`/`-` marker is `FormJSONExplorer`'s toggle in the focus accent; adjacent disclosures pull flush so a stack reads as one accordion — the per-rollout inspector, the verifier reference, the raw-artifact dump. |
| `FormLineage` | The ancestry figure — how a task, an adapter, or a population member was bred. `nodes` (`{ id, label, detail?, note?, tone? }`), `edges` (`{ from, to }`), `caption?`. Layers a DAG by longest path, orders each layer by its parents' mean position, and places every node at the fraction an equal-flex row centres its cell at — so the SVG edges land on the DOM cells by construction (a multi-generation edge resumes on course after each gap; a cycle is guarded). Cells read like a `FormPipeline` stage; `tone` washes a line (`accent` seeds, `positive` survivors, `negative` dead ends). Exports the pure `lineageLayout`. |
| `FormInput` | An input field derived from the app's Input fields, re-dressed to the tools small scale. `label?`, `placeholder?`, `buttonIcon?` (the sidebar `ButtonActionGlass` square, centred on the right with grid-block-applications gutters), `onSubmit?` (fires on Enter and the button), `value?`/`defaultValue?` (controlled or uncontrolled), `attached?` (drops the top hairline so it seats flush beneath a `WebTerminalWindow`). No side borders; the top and bottom edges are transparent→border→transparent gradient hairlines. |
| `FormTextArea` | The multiline sibling of `FormInput` on the same hairline surface, with its own `.module.css` — a `<textarea>` that auto-grows to fit its content instead of a single-line input, matched to the input's one-line height when empty. Same props as `FormInput` plus `rows?`; a one-line textarea equals a `FormInput`. Enter inserts a newline, Cmd/Ctrl+Enter (or the `buttonIcon` glass square) submits. The auto-resize measures against the `placeholder` when the field is empty, so it opens sized to its example, and reruns on every value/placeholder change and on window resize — see `next-vmax-tools/AGENTS.md`. Used for the `/client` "Describe your Campaign" field. |
| `FormSelect` | The pick-one control: a native select restyled onto `FormInput`'s hairline surface, so keyboard, form, and screen-reader behaviour come free and the open list stays the platform's (both option colours pinned for OS/page theme mismatches). `label?` (omit it for a thin, label-less variant — a compact inline filter/sort control), `name?`, `options` (`{ value, label, disabled? }[]`), `value?`/`defaultValue?` (controlled or uncontrolled), `onChange?(value)`. Unlike the text fields it also draws left and right gradient hairlines (transparent-tipped, on `.row`) so a select never reads as an editable input. For the run, model, or benchmark picker that feeds a query or API call. |
| `FormSlider` | The continuous control: `FormRatioBar`'s labelled meter made live — field label + monospace figure over the grid-block track, filled by the shared `--theme-table-ltr-gradient-background` ramp ending at a flush square thumb of solid `--theme-border`. `label`, `min`, `max`, `step?`, `value?`/`defaultValue?`, `format?` (prints the figure the way the field reads it — `128k tokens`, `r = 16`), `onChange?(value)`. For test-time compute budgets, context windows, adapter ranks. `bare` drops the label/value head (and its margin + max-width) so the raw track sits inline in a dense row — how `SettingsColumn` places it beside a `FormFieldMini`. Exports the pure `sliderFraction`. |
| `FormFieldMini` / `FormSelectMini` | The smallest-scale input and select — a single grid-block (24px) tall box for a dense figma-style control panel, the density siblings of the 48px `FormInput` / `FormSelect`. Their value text is the `'SFMonoSquare-Regular'` monospace so digits column-align in the tight boxes. Both take optional `prefix` (`FormFieldMini` also `suffix`, `align`) and controlled/uncontrolled `value` / `defaultValue`; `FormSelectMini` reuses `FormSelectOption`. Reach for them only inside a panel like `SettingsColumn`, not for a page's main form fields. |
| `SettingsColumn` | The figma-style control panel the `client` page swaps in for its second column when `?settings=true` — a compute-fleet configurator over twelve collapsible gradient-headed `Section`s (one per computer), split into two internal columns of six with a hairline divider, stacked flush so there is no gap between computers. Each section is a stack of `Row`s whose `controls` flex reads as 1–4 equal cells, composing `FormFieldMini`, `FormSelectMini`, `FormSlider bare`, and a local `ToggleMini` (gradient-hairline segmented buttons); a section head is a `−`/`+` toggle that shows or hides its rows. Widens its rail to a fixed 480px (kept on mobile so it scrolls horizontally). Rendered by `AppContainer` (via the `Settings` control), not registered as a page column. |
| `FormSegmentedControl` | The pick-one-of-few: native radios (clipped, not hidden, so the group keeps focus and arrow keys) behind equal segments on the `FormInput` surface, split by `FormMetric`'s vertical hairlines. `label?`, `name`, `options` (`{ value, label, disabled? }[]`), `value?`/`defaultValue?`, `onChange?(value)`. Unselected segments sit at the muted-label opacity; the selection is just full text at label weight — no box — with one grid-block-applications gutter on every side keeping the strip tight. For the two-to-four-way choices an experiment sweeps — planner, initialization, memory module. |
| `FormGraphCompare` / `FormGraphCompareSideBySide` | Before/after chart diffs (stacked / split). `before`, `after`, `graphIdBase`, `caption?`. `colorizeByChange` recolors the after chart against the before. |
| `FormMetric` | A stat strip for RL headline numbers: `metrics` (`{ label, value, delta?, deltaSuffix?, goodDirection? }[]`), `caption?`. Coloured `▲`/`▼` delta via the diff tokens; `goodDirection: 'down'` makes a fall in loss/KL/refusals read green. Exports the pure `deltaTone`. |
| `FormPreferencePair` | The RLHF chosen-vs-rejected split: `prompt`, `chosen`/`rejected` (`{ text, reward? }`), `caption?`. Insert-green and delete-red panes with the reward margin beneath. Exports the pure `preferenceMargin`. |
| `FormDistributionCompare` | Student-teacher distillation: `tokens`, `teacherLogits`, `studentLogits`, `temperature?`, `graphId?`, `caption?`. Softmaxes both logit sets and draws a grouped bar chart via `GraphBlockWrapper`, with the KL and top-1 agreement beneath. Exports the pure `softmax` and `klDivergence`. |
| `FormTokenHeatmap` | Per-token credit: `tokens` (`{ token, value }[]`), `min?`/`max?`, `caption?`. An inline token run washed by its scalar through a diverging red↔green scale. Exports the pure `signedIntensity` / `scalarRange`. |
| `FormRollout` | A world-model / trajectory strip: `steps` (`{ label, value? }[]`), `real?` (a second trajectory → imagined-vs-real two-row grid), `min?`/`max?`, `caption?`. Timestep cells (`t0 › t1 › …`) tinted by the per-step scalar through the same diverging scale, scrolling sideways when long. Reuses `FormTokenHeatmap`'s `signedIntensity`. |
| `FormTranscript` | An agent episode as logs inside a `WebTerminalWindow`: `turns` (`{ role: system\|user\|assistant\|tool, text, timestamp, name? }[]` — `name` overrides the displayed alias while keeping the role's tone, so a dialogue can name its speakers), `title`, `caption?`. Each line is three columns: a coloured name alias, a fluid message, and a fixed-width timestamp, so the container stays measured. The log body caps at 88 `--theme-grid-block-applications` and scrolls vertically past it on the shared scrollbar. |
| `FormMatrix` | A labelled grid (models × benchmarks, predicted × actual) with cells tinted by value: `columns`, `rows` (`{ label, values[] }`), `diverging?` (signed red↔green vs sequential green), `min?`/`max?`, `format?`, `caption?`. 768px cap, sideways scroll. Reuses `signedIntensity`/`scalarRange`. |
| `FormRatioBar` | A stack of proportion meters: `ratios` (`{ label, value, max?, display? }[]`), `caption?`. One labelled bar per ratio (a `value/max` fill with a 1px floor), for LoRA trainable-%, KL budget, context used. |
| `FormStackedBar` | The composition figure: `segments` (`{ label, color? }[]` — colours default to the `--theme-graph-option-*` palette cycle), `rows` (`{ label, values[] }[]`, values aligned to segments by index), `caption?`. One 100% bar per row, each normalized against its own total so differently-sized samples compare, with one legend naming every segment. For a difficulty mix, an outcome split, an environment dressing. Exports the pure `segmentShares`. |
| `FormDumbbell` | The per-category before/after: `items` (`{ label, before, after, display?, goodDirection? }[]`), `min?`/`max?`, `caption?`. One row per category — a hollow dot where the run started, a filled dot where it ended, the span washed in the change's tone — on one shared axis so a jump reads big against a flat row. `deltaTone` judges after against before with the per-row `goodDirection` flip. Exports the pure `dumbbellPositions`. |
| `FormEquation` | Display math on the tools scale: `formula`, `caption?`. Server-safe KaTeX in the 768px column, scrolling sideways when wide, each block's height snapped to a grid-block multiple after paint. Takes a formula the way a paper carries it — `\[..\]` / `$$..$$` / `\(..\)` wrappers strip (multi-pair blocks split into separate equations), environments and raw math pass through. Exports the pure `extractEquations`. |
| `FormPipeline` | The method-flow figure as measured stages instead of an image: `stages` (`{ label, detail?, note? }[]` — the note is a monospace count/cost), `loop?` (appends a return cell back to the first stage, the training-loop shape), `caption?`. Stage panels on the FormMetric surface joined by › arrows, scrolling sideways past the cap. |
| `FormSparkline` | Labelled trend rows — the small multiples of a training-dynamics section: `items` (`{ label, series, display?, goodDirection? }[]`), `caption?`. Each row is a field label, an inline polyline of the series, and the monospace final value; line and value take the `deltaTone` colours judged last-against-first, with `goodDirection: 'down'` making a falling loss/KL/episode-length read green. Exports the pure `sparklinePoints`. |
| `FormList` | The list primitive: `items` (`React.ReactNode[]`), `ordered?`, `caption?`. Real `<ul>`/`<ol>` semantics with the native markers stripped: unordered rows take the kit's outline square (the Checkbox hairline at bullet size), `ordered` swaps it for the code-diff gutter's right-aligned muted monospace number. Both markers share one fixed gutter, so the two forms keep a single text edge and wrapped lines hang off it. |

From the wider library, bodies also use `GraphBlockWrapper` (`@document-system-components`), and `WebTerminalWindow` (a titled terminal window, cloned from `ApplicationWindow`), `Loader` (the animated braille-spinner set), `ButtonActionGlass` (the sidebar glass square `FormInput` mounts as its submit button), `Checkbox`, `GlassButton`, and `InlineCode` (all `@nextjs-vmax`; `InlineCode` takes a `tone` — `positive` \| `negative` \| `accent` — that makes it the status mark for a pass/fail/running word). A common composition is a `WebTerminalWindow` over a `Loader`, with a `FormInput attached` docked flush beneath it.

## Open a modal from a page

The modal system is global: the root layout's `Providers` mounts a `ModalProvider` and a `ModalRenderer`, so any client body can open one without extra wiring. Call `useModals().open(Component, props)` from a handler; the component renders centred over the page and closes itself with `useModals().close()`, or on an outside click unless it sets a static `disableOutsideDismiss`. `FormModal` is the model to copy for a tools-page modal: a `next-vmax-tools/` client component with its paired `.module.css`, on the client-app font, gradient-edged like `ModalAuthentication`.

```tsx
'use client';
import { useModals } from '@components/ModalContext';
import FormModal from '@nextjs-vmax-tools/FormModal';

function RestoreButton() {
  const { open } = useModals();
  return <GlassButton onClick={() => open(FormModal, {})}>Open test modal</GlassButton>;
}
```

## The shell (`AppContainer`)

`<AppContainer columns={n}>{page.columns}</AppContainer>` — the route passes the registry row's `columns` as children and the `?columns=N` count. The shell owns the live controls and hands the same handlers (`ToolsControls`, in `tools-controls.ts`) to both the top `ApplicationNavigation` and the fixed `ToolbarControlsFixed`:

- **Light** — swaps `theme-light` / `theme-dark` via `Utilities.setTheme`.
- **Holy** — toggles the Mekzantine display font via `Utilities.toggleFontClassName('font-use-mekzantine')`, scoped to the shell so it never leaks. `?holy=true` on the URL opens the page with it already on (applied once on mount, so a manual toggle-off still sticks).
- **Grain** — a `GrainOverlay` hosted on a wrapper that isolates it over the nav, sidebar, and content but never the fixed toolbar.
- **Col-1 / Col-2** — sets `?columns=N` on the router. The `Col-2` option is hidden (in both the nav and the fixed toolbar) when the layout uses the content space in a way that makes side-by-side columns impossible: `AppContainer` sets `ToolsControls.singleColumn` for the `horizontal-columns` and `island-scene` variants, and both control bars read it.
- **Settings** — an action sitting next to `Col-1` / `Col-2` that writes `?settings=true` (and forces `columns=2`) on the URL, so selecting it opens `SettingsColumn` in the fixed second column. `Col-1` / `Col-2` / `Settings` act as three mutually-exclusive views of that column: the column buttons clear `settings` (so `Col-1` hides the panel), and only one lights active at a time. It renders only on the `client-ui-columns` variant (`ToolsControls.showSettings`). See `next-vmax-tools/AGENTS.md`.

In dark mode the shell steps `--theme-background` one shade below the global dark theme, scoped to this route.

The shell's ScrollSpacer wiring is a pinned contract (`next-vmax-tools/AGENTS.md`): `.content` is the spacer's `limitRef` and must NOT get `align-self: stretch`, and the sidebar subtree keeps `overflow-anchor: none` — preserve both in any shell edit; neither is a cleanup target.

## Conventions

- **Route files are server components.** No `'use client'` on `page.tsx`; export `dynamic = 'force-dynamic'` and `generateMetadata` (route metadata comes from `buildToolsPageMetadata`).
- **Bodies are client components.** `'use client'` at the top, paired with a `.module.css`. Match the `root` / `heading` / `subHeading` / `paragraph` / `divider` typographic scale the existing bodies share (small font, `--theme-grid-block` line height, 768px max width).
- **Grid-snap and theme tokens only.** Size off `--theme-grid-block` / `--theme-grid-block-applications`; reference `--theme-*` custom properties in CSS, never inline theme values.
- **Client-app font.** The shell sets `font-family: var(--theme-font-family-client-apps)` (the system stack); keep bodies on it so they match `AppContainer` and `ModalAuthentication`.
- **Brand casing.** `Vmax` in prose and PascalCase identifiers; `VMAX` only for the wordmark/product/domain and `SCREAMING_SNAKE_CASE` constants.
- **No comments.** Comments are banned everywhere in this codebase (see the root `AGENTS.md`). Only machine-read directives like `@ts-expect-error` or `eslint-disable` survive where technically required; knowledge that would have been a comment lives in the deepest owning `AGENTS.md`, citing the exact function or symbol name.

## Rules

- **One registry.** Add pages to `TOOLS_PAGES`; do not hand-wire a route. The `[...tool]` route derives from the registry, so a one-off route drifts from it. The default sidebar's Template TOC (`EXAMPLE_TEMPLATE_SECTIONS`) and the `authenticated` route inventory (`AUTHENTICATED_ROUTE_GROUPS`) both live in that same file, not a per-page nav.
- **Every new body pairs a `.module.css`.** No component in `next-vmax-tools/` without its module.
- **Stable column keys.** Each element in a row's `columns` needs a `key` (they are module-level elements).
- **Keep the default on the bare path.** Route new pages through `toolsPageHref`; only change which key is default via `DEFAULT_TOOLS_PAGE_KEY`.
