## "Mechanical Deploy" Animation Style Spec

### Core Philosophy
Structures never *appear* — they **assemble themselves out of the ground and out of each other**, like industrial machinery or a Transformer. Every part must have a physical origin: it either rises out of the terrain, extends out of a parent part, or slides out of a central core. Nothing spawns in mid-air, nothing scales from zero in open space, and nothing floats.

### The Golden Rules

1. **Physical provenance** — every part animates *out of* an existing part:
   - **Start with nothing**: The starting state should be nothing, all parts must come gradually from the ground.
   - **Telescope**: pieces extend straight up/out of the structure below them (offset starts recessed inside the parent, slides to final position)
   - **Radial expand**: ring/panel segments slide outward from a central column (scale on the horizontal plane only, never vertical)
   - **Screw**: fasteners rotate as they translate along one axis (rise + spin simultaneously)
   - **Hinge/fold**: doors and petals rotate around a real edge hinge, never a floating pivot
   - **Concealment precondition**: a recessed start only hides a part if something is actually there to hide it. On the frame a part becomes visible, its bounding box (children included — folded petals, stowed barrels) must be below ground or inside the already-visible geometry it emerges from. A parent can never conceal a child with a wider cross-section than its own, no matter the offset, so a wide part must not telescope out of a narrow support. Give the wide part a wide origin instead: lift it out of the ground/base and ride it up the finished structure like an elevator sleeve, split it into a narrow core plus panels that radial-expand or fold from that core, or widen the parent.
   - **Prefer small out of big** — but big-from-small is legitimate whenever the mechanism explains it: a wide ring may *scale* out of a narrow shaft on the horizontal plane (origami-style unfolding, the radial-expand archetype), panels may fold out from a core, a sleeve may ride a finished mast, and a stack may be **jacked**: bring a figure's body in first, resting low over the open shaft, then let the legs surface beneath it, dock against its underside, and push the whole body up as one separate, exactly-synchronized step — feet planting as the jack completes. What stays forbidden is the unexplained version: a wide rigid part sliding up along a narrow support as if hoisted by nothing (a body climbing its own legs).

2. **Rigid, machine-like motion** — use `easeInOut` (accelerate → decelerate, like hydraulics). **Never** use bouncy/overshoot easing (`easeOutBack`, elastic). Motion travels along a single axis or single rotation per part. No tumbling, no random rotation. This governs assembly; the one exception is the `destroyed` clip (item 20), which is ballistic by design.

3. **Terrain hand-off** — before anything deploys, the ground itself must open with an animation: a circular slab of terrain sinks down into a shaft (~0.6s), *then* the machine rises. On teardown, reverse it: machine retracts, slab rises to seal flush. The terrain is treated as just another mechanical part. The host's ground plane is solid — there is no real hole, and no hole dressing is wanted: a dark disc under the machine reads as a rendering void, not a shaft. The `shaft` part is just a metal rim ring marking the seam. Everything below y=0 is genuinely occluded by the host's opaque terrain, so the slab simply sinks through the ground and parts rise through it inside the rim. The slab itself is a **terrain plug**: matte soil (the stock `soil` material — metalness 0, roughness 1, earth-toned), never bare metal — a flat metal disc that wide catches the sun and reads as a bright pool. Around the machine you only ever see ground or slab, and the slab looks like ground with a rim. Fully retracted means **nothing remains at all**, not even the seal: after the slab seals flush, the whole seam — slab and shaft dressing together — sinks back under the ground skin and vanishes, and deploy begins by raising that seam out of the dirt the same way. Both seam moves stay within ~0.05 of ground level, so they are concealed by construction. The shaft is open only while parts are in transit: once the last underground part has emerged, the slab **reseals flush beneath the machine** — a deployed machine stands on solid ground, never in a black pool — and on teardown the slab reopens before anything sinks. The slab therefore runs open → sealed within deploy, and open → sealed again within retract.

### Sequencing

4. **Many small parts, staggered** — split the structure into 5–15 individual parts organized into logical **groups** (base segments → plates → trim → lights → beacons → mechanisms). Groups run **in parallel with offset start times** (~100–450ms apart), while parts *within* a group stagger by **~33–50ms** each. This creates a rippling "many robot arms working at once" effect. Per-part animation duration: **~240ms**. Where these windows collide with rule 3 (terrain opens first) or rule 5 (dependency order), those rules win — stretch the stagger, never break the order.

5. **Dependency order** — a part may only start after its parent group has *started* (not finished). Build bottom-up: base → surface plates → details → mechanisms. Retract top-down in reverse, base ring last, so nothing is ever left unsupported.

6. **Above-ground visibility** — all deploy/retract motion happens **at or above ground level** so terrain never occludes it. Anything happening below (portals, elevators) needs the hole/doors already open.

### Presentation

7. **Shapes and materials**: bulky, industrial, rounded corners. Brushed metal with scratch streaks, rust patches with drip stains, riveted panel seams, weathered yellow/black hazard chevrons, dark floor grating. Medium metalness (0.4–0.6), medium roughness. **Never leave a sharp box edge**: every box gets a chamfer (~0.02) or rounding — use the stock `chamferBox(THREE, w, h, d, chamfer?)`, a drop-in `BoxGeometry` replacement that auto-shrinks the chamfer on thin plates; extruded shapes carry their bevels; cylinders and tori already read as rounded. Every deployed part stays partly proud of its neighbours — a roof overhangs the walls beneath it (raise the ridge above the wall tops and fill the gable ends; a pitched panel whose eaves land inside the walls is a roof sunk into its own building), a lid overhangs its rim, a fitting shows its head.
8. **Emissives**: LED glow strips/rings (slow pulse), blinking beacons.
9. **Master pace**: full deploy ≈ 2.5–3.5s, full retract slightly faster. Expose a single speed multiplier (currently 1.5×) that scales every duration, delay, and stagger uniformly. The 2.5–3.5s window is **wall-clock as played at that default 1.5×** — so author 3.75–5.25 seconds of timeline at speed 1.

### Sounds effect

10. Each parts/groups should have its own assembly sounds effect that trigger at time of deployment. The set of assembly sounds is fixed: six recordings held on the requester's side, one per sound id (item 21) — nothing else exists, and you never load or need the audio yourself, since sounds are ids in data. Procedurally generated sounds exist only as the requester's offline fallback when the recordings cannot load.
11. Only add sounds effect for the top 3-5 parts that are visible and big size, ignore small parts sound. The budget is per clip, not per model.
12. Only make sounds when the part become visible, if a part have multiple movement, it should only make sounds when the movement is actually visible and not currently stay underground or inside other parts. Every sound must be caused by a movement: a sound with nothing moving is never allowed. Give the final lock clunk a real seating motion — lift the top part a few hundredths past its socket, then press it down into place and hang the `lock` on that.

### Implementation details

13. Aim to use as few triangles as possible. Avoid using performance heavy features.
14. Overlap and flickering: if two parts that have the same height that was overlapping on its retracted state, we should make add some offset to the part so that no two parts occupy the same plane/edge, which cause flaking and flickering during render. This applies to every pose a part rests in, not just the retracted state — a deployed cap flush with the rim it sits in fights for the whole life of the machine. Keep same-facing faces at least ~0.005 apart wherever footprints overlap; remember extrude bevels reach below a part's base plane. Opposite-facing coincident faces (a lid resting on a rim) are safe under backface culling, and segments of one ring family keep angular gaps instead of height offsets.
15. Note that an object may have multiple animation/transformation scenarios, we can call these clips. The movement of the parts, timing, and sounds are tied to each clip. In each clip, there can be multiple sequences which should all have names.
16. Each part must have a name (and index if we have multiple instances of that part), each sounds must be specifically associated with a part.
17. The object must be created in an independent JS file that can be import to be used in a threejs project. The file should export enough information for debugging/tweak purpose as well. Design the file so that we can plug it into an animation/clip debugger to do the following: inspect all parts, play the clips/sequences, explosion view, have a timeline slider for the animation, enable/disable each part and sounds of each part, hover to the part to see its name. Export the factory as the module's default export and import nothing but the deployable runtime, by relative path — a debugger must be able to load the file blind, knowing no names inside it.
18. Verify the concealment precondition mechanically, never by eye. Both acceptance audits work on **world axis-aligned bounding boxes**, so a curved surface's tangent extents count as faces — a cylinder of radius r contributes ±r planes on x and z. Provenance: for every sequence flagged `appear` (probed at its start) or `vanish` (probed at its end) — only those transitions, nothing else — the part's bounding-box probes (8 corners inset a quarter toward the centre, plus the centre) must each be underground (y < 0.05) or inside another *visible* part's box; a part with most probes in open air is popping into or out of existence. Overlap: at every resting pose (clip start and end, just before and after each sequence), for every pair of *visible* meshes — including two meshes of one part; only distinct parts sharing a group are exempt — same-facing box faces closer than 0.004 units with footprints overlapping by more than 0.01 are flagged, as is any face lying in the ground plane; hidden parts are skipped entirely, so underground parking spots may interpenetrate freely; so is any part riding a moving ancestor (it is not at rest), and so are meshes whose world orientation is not axis-aligned — a tilted box's AABB faces are not real surfaces, which is what lets the destroyed clip's tumbled wreck rest without false flags. Exposure: at each clip's end pose, no visible part's box may sit entirely inside a single axis-aligned sibling's box — that is buried geometry, like a roof sunk into its cabin (rule 7). Attachment: at each clip's end pose, every visible part's box must touch (within ~0.01) at least one other part's box or reach the ground — a part touching nothing hangs unattached, a hook with no trolley, a horn with no strut; give every floating-looking part a visible connector. A model must report zero violations from all four — as the author you cannot run the audits, so do this box arithmetic yourself while placing parts.

### The contract — the file you hand over

The deliverable is one JavaScript ES module, authored against this document alone. You will not have the requester's project — not the runtime, not the sound recordings, not the viewer — and you never need it: your file imports `./contract.js` and `./stock.js` sight unseen (the requester's viewer supplies them when it loads your file), and every signature you may use is specified here in full. You cannot run or preview the file yourself, so make it correct by construction — work the geometry numbers for rules 1 and 14 out as you author. The requester's rig lab loads the file blind — file picker or drag-drop, knowing no names inside it — so the file must have exactly this shape.

19. **Module shape.** Export the factory as the **default export**. The factory is
    `create*({ THREE, playSound = null, speed = 1.5 } = {}) -> deployable` — `THREE` is the
    host's three.js module, so never `import 'three'` yourself and the file runs on any three
    version. Import nothing except, by relative path, the deployable runtime:
    `import { createRig, seq, ch } from './contract.js';` and optionally
    `import { makeMaterials, ringSeg, canvasTex } from './stock.js';`. A file with zero imports
    that implements the full interface of item 22 by hand is equally valid. Y is up, the ground
    is y=0, and the whole structure should fit within ~5 units of the origin — the viewer's
    camera, ground plane and audits assume that. One unit reads as roughly one metre, which
    is what makes the audit thresholds (0.004 coplanarity, 0.05 underground) sensible.

20. **Describe, don't animate.** Build every mesh at its **deployed** pose (a channel a clip
    never touches falls back to this build pose), then hand `createRig` a declarative
    description: `createRig({ name, root, parts, clips, fx?, playSound? }) -> deployable`.
    The terrain slab is built **flush**, which is both its sealed pose and — since it
    reseals beneath the machine mid-deploy (rule 3) — its deployed pose, so extra clips
    need no slab holds. The terrain parts appear with the seam-rise and vanish after the
    seam-sink, so a parked machine is bare ground with nothing visible at all.
    - `root`: a `THREE.Group` holding everything; the host adds it to a scene.
    - `parts`: `[{ name, group, index, object, explode }]` — every part named (`column-2`),
      grouped (`column`), indexed, pointing at its Object3D (parts may nest inside other
      parts' objects), with an `[x, y, z]` explosion-view offset in the part's parent space,
      applied scaled by `setExplode(k)`.
    - `clips`: `{ deploy: { name, sequences }, retract: { … }, …more }`. The two clips named
      exactly `deploy` and `retract` are required — hosts call them by name — and the lab
      auto-plays whichever clip is listed first. Sequences are authored with
      `seq(name, part, at, dur, channels, extra?)` and `ch(target, from, to)`, where
      `target` is one of `position|rotation|scale` dot `x|y|z` and `from`/`to` are **absolute
      values in the part's local space**. `extra` takes `{ ease?, appear?, vanish?, sound? }`;
      ease defaults to `easeInOut` (also `linear|easeIn|easeOut`, plus `gravity` (t²) for
      destruction only — nothing bouncy in assembly). Before a
      channel's first sequence the part sits at that sequence's `from`; a channel may appear
      in several sequences of one clip (lift, then seat) and holds the previous sequence's
      `to` between them. `appear: true` shows the part at the sequence's start,
      `vanish: true` hides it at its end; a part with neither is visible for the whole clip,
      and children of a hidden parent stay hidden with it. Any sequence may carry the flags,
      zero-motion holds included. A third clip named `destroyed` is recommended: a collapse,
      not an explosion. The machine breaks into its top-level parts and every part only ever
      descends — `gravity` for the drop, `linear` for outward scatter and tumble, fall time
      scaling with drop height — with one exception: a bounce after an impact may rise
      briefly before `gravity` brings it back down. Nothing pops upward at the start, and a
      part's resting pose must never sit higher than where it started, so cap each part's
      tumble tilt until its computed rest height is at or below its starting height.
      Scatter distance and bounce height both scale with the part's actual drop — energy
      comes from falling, so ground-level rings barely shift while tall parts are flung
      further and land harder. Parts come to rest
      just above the ground (0.01–0.03 of clearance, jittered per part, keeping the wreck
      clear of the overlap audit), nested parts ride their parent assemblies, the slab —
      already resealed flush beneath the machine — stays put, and the 3–5 biggest landings
      thud with `heavy`.
    - `fx({ clock, dt, playing, clip, time })`: optional per-frame idle effects (LED pulse,
      beacon blink). `clip`/`time` are the currently posed clip and time — a destroyed
      machine is dead, so kill every emissive while `clip === 'destroyed'`.

21. **Sounds are data.** `sound: { id, rate?, gain?, at? }` on a sequence — `at` is a 0..1
    fraction of the sequence, defaulting to 1 (the end, the landing clunk). A sequence
    carrying a sound must animate at least one channel with `from !== to`: every sound rides
    a movement, per rule 12, and `validateDeployable` rejects a soundtrack with nothing
    moving. Never load audio files yourself; sounds fire through the injected
    `playSound({ id, rate, gain, part, sequence })` and the host plays them. The vocabulary
    is exactly `SOUND_IDS` — six ids, each backed by one recording in `deployables/sounds/`,
    and `validateDeployable` rejects any other id: `heavy` (slabs, rings, columns —
    `rig_heavy.mp3`), `piece` (housings, plates — `rig_piece.mp3`), `small` (fittings —
    `rig_small.mp3`), `servo` (hinges, folds — `rig_fold.mp3`), `stow` (retract parking —
    `rig_stow.mp3`), `lock` (the final latch — `rig_lock.mp3`). The requester's hosts own
    playback of those recordings; you only ever write the ids. Sounds fire only during
    `play()` — scrubbing with `setTime` is always silent, and `play({ from })` skips every
    cue before `from`.

22. **The returned deployable** — `createRig` provides all of it; a hand-rolled one must too:
    fields `api` (= 1), `name`, `root`, `parts`, `clips`, and methods `duration(clip)`,
    `play(clip, { onDone?, from? })`, `stop()`, `playing()`, `clip()`, `time()`, `update(dt)`,
    `setTime(clip, t)`, `setSpeed(k)`, `setExplode(k)`, `setPartEnabled(name, on)`,
    `partEnabled(name)`, `setSoundEnabled(name, on)`, `soundEnabled(name)`, `setMuted(on)`,
    `partOf(object3d)`, `dispose()`. Semantics: all times are authored seconds at speed 1 —
    `duration()` reports them and `setSpeed(k)` only scales wall-clock playback (rule 9's
    multiplier; factories default it to 1.5). `update(dt)` advances playback and `fx`,
    returning whether a clip is playing. `partOf` maps any picked mesh to its owning part,
    deepest nested part first. `dispose()` frees every geometry, material and texture and
    detaches `root` from its scene.

23. **Skeleton** (`stock.js` extras: `makeMaterials(THREE, { metal?, dark?, led? })` — hex
    strings for the two metal tints, `led: [baseColor, emissiveColor]` numbers — returns
    `{ metal, dark, hazard, led, beacon, soil }`, all `MeshStandardMaterial` — `soil` is the
    matte terrain plug for the slab (rule 3) — so `fx` may drive `emissiveIntensity` on
    `led` and `beacon`; `chamferBox(THREE, w, h, d, chamfer?)` a chamfered drop-in for `BoxGeometry` (rule 7);
    `ringSeg(THREE, rIn, rOut, a0, a1, depth)` builds a flat arc slab centred on
    the part's origin, lying in the ground plane with a point at angle `a` landing at
    `(cos(a)·r, ·, −sin(a)·r)`, rising from y=0 to y=`depth` with ~0.02 bevels beyond both;
    `canvasTex(THREE, paint, w?, h?)` a procedural texture; and
    `wreckClip(THREE, { root, parts, hold, impacts })` in `./wreck.js`, which generates the
    whole `destroyed` clip from the built parts — `impacts` names the parts that thud and
    bounce, and `hold` pins channels for the clip's length in the rare case a pose must
    differ from the build pose):

    ```js
    import { createRig, seq, ch } from './contract.js';
    import { makeMaterials } from './stock.js';

    export function createDrill({ THREE, playSound = null, speed = 1.5 } = {}) {
      const mat = makeMaterials(THREE);
      const root = new THREE.Group();
      const parts = [];
      const slab = new THREE.Mesh(new THREE.CylinderGeometry(1.6, 1.55, 0.26, 32), mat.dark);
      slab.position.y = -0.1;
      root.add(slab);
      parts.push({ name: 'slab', group: 'terrain', index: 0, object: slab, explode: [0, -2, 0] });
      // ...more parts, each built at its deployed pose...
      const clips = {
        deploy: { name: 'deploy', sequences: [
          seq('slab-open', 'slab', 0, 0.6, [ch('position.y', -0.1, -1.6)],
              { sound: { id: 'heavy', rate: 0.72, at: 0 } }),
          // ...bottom-up, staggered...
        ] },
        retract: { name: 'retract', sequences: [ /* reverse, top-down, slab seals last */ ] },
      };
      const rig = createRig({ name: 'drill', root, parts, clips, playSound });
      rig.setSpeed(speed);
      return rig;
    }
    export default createDrill;
    ```

24. **Acceptance.** The requester loads your file in their rig lab, which runs the item 18
    audits automatically. The CONTRACT panel must read `CONTRACT v1 OK`,
    `provenance: every part appears and vanishes concealed`, and
    `overlap: no coplanar resting faces`. Anything else is a violation of this spec: the
    requester sends back the exact violation list, and you fix and resend.
