# Loader module contract

Each loader owns exactly one file in this directory and exports one default
object:

```js
export default {
  id: "unique-kebab-name",
  name: "Human name",
  kind: "webgl", // or "svg", "canvas", "dom"
  technique: "Short implementation label",
  label: "Working",
  mount(container) {
    return {
      render(timeSeconds, deltaSeconds) {},
      resize(widthCssPixels, heightCssPixels, devicePixelRatio) {},
      reset() {},
      destroy() {},
    };
  },
};
```

Constraints:

- The visual slot is exactly 30 × 30 CSS pixels and is clipped by containment.
  `mount` receives a disposable host element that fills the slot, one per mount,
  so a module may attach a shadow root to it without leaking into a later mount.
- Do not start a private RAF, interval, or timeout. The gallery owns the clock.
- The local clock resets to zero when the real button is clicked.
- Make the loop continuous at its seam; target a 2.4–4.8 second primary cadence.
- Draw with alpha so the shared translucent button surface remains visible.
- Keep all styles inside a shadow root or inline SVG; never add global selectors.
- WebGL modules use `createShaderVisual` from `../core/webgl.js`; the helper
  automatically shares one WebGL 2 context across all shader modules. Every
  module must include an original Canvas 2D fallback with the same
  silhouette/motion idea. The fragment body must define
  `vec4 shade(vec2 uv, float timeSeconds, float seed)`; normalized `uv` is
  centered on the slot and spans roughly `-1..1` on its short axis.

  ```js
  return createShaderVisual(container, {
    name: "Loader name",
    seed: 1,
    fragment,
    fallback: (context, time, delta, width, height) => {},
    maxDpr: 3,
  });
  ```
- Canvas modules use `createCanvasVisual` from `../core/canvas.js`.
- The runtime freezes naturally for reduced-motion users and pauses off-screen;
  a module never needs to check either itself.
- No third-party runtime dependencies, remote assets, text, emoji, or stock art.
- Favor legible silhouette, cadence, stagger, anticipation, follow-through, and
  a clean motion hierarchy over raw particle count.

Register the module in `index.js` so gallery order stays deterministic, then
preview it at:

`http://127.0.0.1:4173/preview.html?design=<filename-without-.js>`

Append `&t=<seconds>` to freeze a deterministic still, or call
`window.__motionPreview.setTime(seconds)` / `.play()` from the preview console.

Inspect the actual-size button, click-to-reset behavior, hover compression, and
the enlarged motion loupe. Iterate after observing at least one full loop.
