# Lab Kit

`@examplicity/lab-kit` is a small, dependency-free browser runtime and CSS
primitive set for interactive labs. It supplies capabilities that recur across
subjects while leaving the visual model, content, state machine and learning
sequence in each lab.

The browser entry point is `src/lab-kit.js`. It assigns one namespaced API to
`globalThis.LabKit`; it does not create a module loader or make network calls.
The CSS entry point is `src/lab-kit.css`. Both files are intended to be inlined
by a lab build so the final HTML remains standalone.

## Runtime surface

```js
const { dom, numeric, rng, status, history, svg, animation, direct } = LabKit;
```

- `dom.get`, `dom.all`, `dom.byId`, `dom.require`, and `dom.on` provide small
  lookup and event helpers without imposing a component tree.
- `numeric.parse`, `clamp`, `lerp`, `inverseLerp`, `round`, and `format` cover
  common displayed-value and bounded-control work.
- `rng.create(seed)` returns deterministic `next`, `float`, `int`, `pick`, and
  non-mutating `shuffle` methods. Pass an explicit seed when a scenario must be
  reproducible.
- `status.set`/`announce` updates a supplied live region with `role=status`,
  `aria-live`, `aria-atomic`, and optional `aria-busy`; `status.ensure` creates
  a visually hidden region in a document-like root.
- `history.create({ limit, initial, clone })` provides bounded record, undo,
  redo, reset and inspection operations for learner evidence or revisions.
- `svg.point(svg, event)` converts pointer/touch client coordinates into local
  SVG coordinates, using the screen CTM when available and a viewBox fallback
  otherwise.
- `animation.create({ duration, onFrame, onComplete, reducedMotion })` provides
  start, pause, stop, reset, seek and completion state. Inject
  `requestFrame`/`cancelFrame` in tests or special runtimes.
- `direct.keyboardAdjustable(target, options)` gives a direct-manipulation
  handle arrow/Home/End/Page controls, bounds, step changes, value ARIA and a
  `destroy` method. Pair it with pointer dragging against the same state.
- `direct.movableCompletion(card, bounds, options)` keeps a short completion
  card inside its visible workspace. Its default handle is the living guide's
  `[data-dialog-drag]` / `.dialog-move`; it uses pointer dragging, arrow keys
  and Shift+arrow keys, and returns `move` and `destroy`.

## Publication declarations

Declare the two entrypoints in authoring source. Do not point a delivered lab
at this package at runtime:

```html
<link rel="stylesheet" href="./lab-kit.css" data-lab-resource="lab-kit.css">
<script src="./lab-kit.js" data-lab-resource="lab-kit.js"></script>
```

The live-site repository vendors a hash-pinned release and owns the compiler
that replaces these declarations with marked inline blocks. The creation kit
owns the shared source; the final monolithic HTML has no runtime package, sibling
checkout or network dependency.

Regenerate the release manifest after changing package files:

```text
npm run manifest
```

The manifest records bytes and SHA-256 hashes for the package sources. It omits
itself to avoid a circular hash. A lab package should copy the kit version and
the relevant file hashes into its own build provenance.

Repository automation starts at `agent/README.md`. Portable ChatGPT authoring
uses its lab-specific `LAB_PACKET.md` plus synchronized resource copies; it
does not require repository-only agent files.

## CSS capability contract

Classes are namespaced with `lab-kit-` and are intentionally primitives:
surface, operated canvas, stack, cluster, grid, field, control, status, scroll
and SVG sizing. They do not prescribe a lab’s page rail, panel order or diagram
geometry. Their defaults map to the living v3 guide tokens; do not introduce a
second palette in a lab. A lab may map the `--lab-kit-*` aliases to the current
shared tokens when the site supplies them.

`src/lab-design.css` and `src/lab-design.js` own recurring activity-bar,
working-toggle, stage-marker, concept-surface, attention and completion-card
behavior. Use their shared classes and helpers without copying a specimen's
page structure. The living design guide demonstrates visual intent; it is not
a workflow authority or a required layout.

The reviewed design resources are published through the developer area. The
publication compiler embeds the approved version after the base kit and local
layout CSS. Do not leave developer URLs as delivered runtime dependencies.

Use `.lab-kit-canvas` on a primary operated visual region when it needs the
shared light surface, line, radius and inset definition. Add
`data-grid="square"` only when a quiet construction grid helps the learner read
position. The primitive deliberately sets no width, height, aspect ratio,
layout or overflow; those remain consequences of the lab's model. Use
`.lab-kit-svg` only when an SVG should scale to its container.

SVG geometry stays local. Give every instructional SVG a stable `viewBox`, a
useful accessible name and description, and visible non-colour evidence for
important state. Interactive handles need pointer and keyboard paths that
mutate the same model state, visible focus, bounded coordinates and recovery.
Use `svg.point` for coordinate conversion rather than deriving scale from page
pixels. Do not make a primary visual scroll merely to preserve an arbitrary
authored width; fit or responsively recompose it unless panning is itself part
of the learning operation.

Surface primitives are opt-in, not wrappers to apply at every DOM level. Start
with one meaningful frame. Let self-describing objects such as nodes, servers
or protocol layers occupy that field directly, and add a nested border or
background only when it conveys containment, grouping, hardware, clipping or
another instructional relationship.

`.lab-kit-work` supplies a stable grid for a persistent working/evidence
region. Its idle items use spacing rather than a decorative rail. A checked
`.lab-kit-work-item` receives a full semantic surface without prescribing the
item's internal layout. Set `data-state="needed"`,
`data-state="mistake"` or `data-state="correct"` as the learner edits a
meaningfully checkable response. Include `.lab-kit-work-state` with a visible
text label and a `.lab-kit-work-mark` span; the shared CSS renders a red `×`
for mistakes and a green `✓` for correct work. Needed work uses the shared
yellow treatment. These states supplement rather than replace concise text and
accessible status announcements. Keep domain-specific validation local.

```html
<section class="lab-kit-work" aria-label="Working and evidence">
  <div class="lab-kit-work-item" data-state="needed">
    <!-- The lab-specific working, evidence or input stays here. -->
    <span class="lab-kit-work-state" role="status">
      <span class="lab-kit-work-mark" aria-hidden="true"></span>
      <span>Work needed</span>
    </span>
  </div>
</section>
```

Update both `data-state` and the visible state text from the lab's own input
handler. Do not put generic correctness rules in the kit: only the lab knows
when a response is complete enough to be a real mistake.

When the guide's short completion card is appropriate, use its labelled
`.dialog-move` button with `data-dialog-drag` and call
`LabKit.direct.movableCompletion(card, workspace)`. Keep learner work,
predictions and evidence outside the card.

## Boundary

Keep syllabus claims, model rules, generated scenarios, correctness predicates,
visual renderers, domain-specific working and authored progression in the lab.
Use this kit for repeated mechanics and accessibility affordances only. The
kit cannot decide what a learner should notice, what counts as evidence, or how
an authentic system should behave.


## Shared design components

`src/lab-design.css` is the canonical implementation for recurring design components. The site compiler expands `<!-- LAB_DESIGN_COMPONENTS -->` at the end of the source head into this stylesheet and `src/lab-design.js`, then packages the shared CSS used by that lab. Authors include the marker rather than pasting these files into source HTML. Existing compatible base-runtime 0.2.1 declarations and the design/distribution 0.3.0 release are separate versioned layers.

New labs use `.lab-toggle` with `data-kind="working|exam|mode"`, `.lab-action` with optional `data-priority="primary"`, `.lab-work-card` with `data-work-state="needed|good|bad|reference"` and `data-interacted="true"`, `.lab-math-surface`, `.lab-stage-surface`, `.lab-stage-heading` and `.lab-guide-target`. Use native button/input state and update the attributes from real learner state. Stop attention on pointer/keyboard interaction, reset for a new activity, and keep geometry/learning logic local. The stylesheet includes compatibility selectors for the six reviewed labs.

The living guide documents visual intent. Change the shared stylesheet for reusable visual changes; keep only layout/model-specific adapters in a lab.

For adopted legacy labs, add `lab-adopt-v3` to the body and inline `src/lab-design.js` as well. It provides shared working-attention dismissal and movable completion handles; unique model behaviour remains local. The publication marker embeds both resources automatically.


Required-action introductions use `data-lab-intro`, `data-lab-intro-title` and
`data-lab-intro-copy` on the operated target. Stable keys survive rerenders;
distinct newly unlocked operations receive their own keys. The adopted-lab
runtime places one non-blocking tooltip at a time, hides it on interaction or
Escape, and pauses introductions during completion. Group the SVG visible
handle and hit target so both dismiss the same introduction.

`LabDesign.bindSvgDrag(stage, {start, move, end})` supplies shared SVG pointer
capture, pickup offsets, grab cursors and release/cancellation handling.

Visual-discovery labs add `.lab-discovery` to their authored root and mount the
shared shell with `LabDesign.discovery.mount(root, createModel)`. The factory
receives scoped DOM, SVG, feedback, hint, snapshot, tracing and transition
helpers, then returns `lesson`, `state`, `syncScene`, `reset`, `enter`, `render`
and its own `controls` callback. It may also return `sceneHeight`, `restoreView`,
`snapshotExtras`, `restoreExtras` or `stopModel`. Curriculum logic, model state,
SVG geometry and completion criteria stay in the authored lab. Subject code
should not destructure `controls` from the runtime; `validNumber` is available
for the existing numeric-input models.

`start(event, point)` returns the model handle's current `{x,y}` or nothing
for a locked/non-draggable target. For an unconstrained coordinate, return the
pointer coordinate. `move(point, event)` receives the offset-adjusted SVG
coordinate; `end(event)` distinguishes release from cancellation. Keep model
constraints, snapping, keyboard actions and success checks in the lab.
Histogram, Motion Graphs and Bounds use this same implementation. The compiler embeds
the runtime before the lab script, including in standalone downloads.

Shared introductions and completion cards prefer empty visible workspace, using rendered text, controls and SVG objects as placement obstacles. Completion placement runs on opening/content changes and preserves user movement. Use `data-lab-popup-avoid` on an otherwise unrecognisable custom visual region.


`LabDesign.introduce(scope, targets, key, title, copy)` updates model-specific
introduction targets after a redraw, removes stale target annotations in that
scope, and schedules the shared cue. Use distinct keys for newly unlocked
operations; field annotations outside that model scope remain independent.

Use `data-lab-readable-chart="14"` on ordinary SVG charts whose labels need a
stable displayed size (`12` for short labels). Shared size/viewBox observers
set `--lab-chart-text-size`; `.lab-geometry` adopts it for SVG text. Keep label
positions and space for insets local. Complex illustrations need no opt-in.

The geometry compatibility surface covers `.work-step`, `.working-step` and
`.calc-step`, their existing state classes, working choices and mathematical
readouts. It preserves completed progress at None. `.lab-checkpoint-detail`
adds hover/focus task and work detail to existing `.lab-checkpoint` markers.
Right-triangle resizing, non-right-triangle pegging and trig rotation now use
`bindSvgDrag`; angular models retain their own angular offset and constraints.


`LabDesign.digitalReadout(svgParent, fixedPointText, {x, y, label})` draws a
non-negative fixed-point measurement with inline seven-segment SVG and an
accessible label. Pass the recorded precision explicitly (for example `12.40`);
it preserves trailing zeros. `.lab-digital-display` supplies the concept colour
and flat face. Keep rounding rules, true-value state and ruler geometry local.


Investigation adoption uses `lab-investigation` for shared rounded actions and
date selects, right-side working cards and fitted charts. Keep supplied reference
values distinct from learner success. `LabDesign.checkpoints(host, labels,
current, completed, detail)` supplies accessible compact progress; labs retain
ownership of phase semantics. `bindSvgDrag` also supports extrusion/reshaping
handles without pickup jumps. These CSS/JS resources are compiled inline in
standalone downloads. Visual examples live in the design guide.
