## What it builds

Silent, looping films of 20–60 s with full-frame cuts, the product shown as its own real components on a 3D plate with a camera that never stops. One engine, two routes.

Route A

### With footage

Licensed live-action cuts of people alternate with product beats. Each product beat follows the human action that motivates it: someone at a laptop, then the prompt; a meeting, then the share dialog.

Route B

### Pure product

Product beats only, joined by the plate, with captions or end cards if the surface needs words. Faster to make and needs no licence.

**Keep the rig in one place.** Put the camera code and the numbers below in `references/camera-rig.md` next to the skill and read it before writing any beat. Tune from there; do not re-derive.

## When to use

### Use it for

* A new hero, section or launch film, or a recut, new beat or new ending of an existing one.
* Showing the product at fidelity, in motion, without screen-recording the real app.

### Not for

* Feature walkthroughs captured from the live app with test data.
* Films with music or a voice track – use an editor timeline for those.
* Deploying the finished MP4 to your site.

## Inputs to settle before rendering

Ask only for what cannot be inferred; state assumptions otherwise.

| Input              | What to settle                                                                                                                                                                                                  |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Reference          | A film the user points at. Fetch it, find the video URL, probe it with ffprobe: resolution, fps, length, bitrate, audio track, poster, preload. The spec becomes the target; the structure becomes the grammar. |
| Story and screen   | One line: what the user does in the product, who reacts, how the result reaches them. Which screens appear (dashboard, editor, dialog, notification) and their exact copy.                                      |
| Route              | A if people footage is wanted and licensable; B if the film is product-only or footage is out of scope.                                                                                                         |
| Surface and length | Home hero loop (30 s, silent, poster, preload="none") · landing section (20–25 s) · social (45–60 s, captions; 9:16 reflow is a separate pass).                                                                 |
| Brand              | The product's design tokens, fonts and any third-party brands shown in the UI.                                                                                                                                  |

## The engine

Every product beat is an HTML page that exposes `window.seek(t)` and draws frame `t` deterministically. A renderer opens it headless at 1920×1080 with device scale factor 2, calls `seek(i/25)` for each frame and screenshots to JPEG; ffmpeg encodes the beat with grain and a vignette. An assembly script cuts footage segments (Route A) and concatenates everything into a master, a 1080p web file, a 720p review file and a poster.

**beats.html**seek(t) draws each frame

→

**render.js**Playwright frames → seg/<beat>.mp4

→

**assemble.sh**cut list → master, 1080p, 720p, poster

→

**review-vNN.html**one review page, many versions

| Layer    | File            | Role                                                                                                                     |
| -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Beats    | beats.html      | One <div class="beat"> per beat, real product markup and CSS inside, 3D world and camera, particles on a canvas, seek(t) |
| Renderer | render.js       | Playwright frames → seg/<beat>.mp4; an ONLY=b4,b6 env var renders a subset                                               |
| Assembly | assemble.sh     | Cut list → segments → concat → master → 1080p, 720p, poster                                                              |
| Review   | review-vNN.html | 720p and poster embedded as base64, "What changed since" on top, republished as new versions of one shared page          |

Build these four once as templates in the skill's `templates/` folder and copy them for each film; do not start from a blank file.

## Product fidelity

The UI on screen must be the product's own components, not a mock-up.

* **Markup and CSS** from your shipped frontend components or an existing marketing render. Collect them into one product stylesheet inside `beats.html` and reuse the markup with new copy.
* **Colours** from your design tokens. Override only what the film needs, such as an accent.
* **Emails and notifications** follow the real templates: subject format, headline phrasing, link colour.
* **Fonts** load at render time; the renderer waits for `document.fonts.ready` before the first frame.

When a component isn't available as code, rebuild it from the shipped UI and your tokens, and say so on the review page.

## The numbers

These held across sixteen cuts of a production hero film. Start here; change one at a time and re-render only the beat you changed.

**1.5 s**median footage cut (0.75–1.8 s)

**2.5–3.8 s**product beat, Route A; 2.5–5 s in Route B

**5 / 10 s**cuts per ten seconds, always short–short–long

**≥ 0.5 s**end-state hold on every product beat

### Pacing

* **Route A:** footage and product each take half the running time. Five cuts per ten seconds, always short–short–long.
* **Route B:** beats 2.5–5 s, joined by the plate; one idea per beat; captions change on beat boundaries.
* Longest hold on the text-heaviest beat. The first product beat carries the most text.

### Camera

A perspective world at `perspective:1500px`; the camera translates on z – a dolly, never a 2D scale.

magnification = 1500 / (1500 − z\_cam − z\_obj)

| Rule              | Value                                                                                                                                                                                          |
| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Move              | One per beat, cubic ease-in-out over the whole beat: z from about −80 to +180…+500                                                                                                             |
| Drift             | x += 6·sin(0.9t), y += 4·sin(1.25t) under the move, so a hold never freezes                                                                                                                    |
| Rotation          | Hand-held objects only (popovers, dialogs, phones): settle from 7° / −9° to 1.5° / −2°, plus ±0.6–1.2° sine. Never on screen-fixed objects (input bar, main card) – tilt there reads as a bug. |
| Subject vs ground | Subject to z 260–280, scale 1.45–1.7×. Ground scales 0.98, dims to 0.86, blurs ≤ 1.6 px as the focus plane follows the subject. The ground is never removed.                                   |
| Depth of field    | blur = clamp((\|z − focus| − 90) / 110, 0, 3.5) px; opacity × (1 − 0.1·blur/3.5)                                                                                                               |

### Entrances

* Every element has one: y +44→0, scale 0.94→1, opacity 0→1, 0.3–0.6 s, cubic ease-out. Stagger siblings 60–140 ms in reading order. Chips and avatars pop with a back-ease (1.4→1).
* Text is typed, not faded: \~45 characters per second with a following caret; the camera tracks the caret.
* Unfolds use max-height 0→N with ease-out, not opacity alone.

### Plate and texture

* Two soft colour blobs on an off-white plate, 150–200 seeded particles drifting; on the key beat they converge into the main object as it lands.
* Glass panels: `rgba(255,255,255,.7)` with `backdrop-filter: blur(22px)`. Popovers, pills and dialogs are solid white with a border and a deep shadow.
* Encode every beat with `noise=alls=6:allf=t+u` and `vignette=PI/6:mode=backward` so it sits with footage.

### Footage (Route A)

* One shoot for every people shot, one protagonist, one mild grade: `eq=saturation=0.92:contrast=1.03`.
* Each product beat is preceded by the human action that motivates it.
* Wide exterior as opener and closer so the loop seams.
* Licence at the highest tier; pull the largest file. Do not scale-then-refit footage expecting a zoom – a real push-in is `zoompan` on an 8K upscale.

## Procedure

1. **Working folder** outside the repo and outside any temporary scratch space, e.g. `~/films/<film>/`. Agent scratch directories get pruned and take the session's work with them.
2. **Reference and concept.** Probe the reference. Write a one-page concept: what the film is not, who the protagonist is, the beat sheet, the register of the humans (Route A). Share it for review.
3. **Storyboard.** Route A: search a stock-footage library through a headed, persistent browser profile (bot protection blocks headless), sample frames, build the storyboard from real frames – all shots from one shoot, every shot 0.5–2 s. Route B: the beat list with the copy per beat. Iterate as versions of one page.
4. **Sources.** Route A: the user buys the licence and downloads the files; they go in `footage/`. Route B: nothing to fetch.
5. **Beats.** Copy `templates/beats.html`, paste the product stylesheet, build one beat at a time; check each with a contact sheet (`ffmpeg -vf "fps=2.5,scale=480:-1,tile=4x2"`) before rendering the next.
6. **Render** with `render.js` from a directory where `require('playwright')` resolves.
7. **Assemble** with `assemble.sh`: cut list with in-points, durations and per-cut comments; build; make a whole-film contact sheet at 1 fps and look at it.
8. **Review page.** Embed the 720p and the poster, write "What changed since cut vN", publish it as a new version of the same review page. Send the MP4 to the user as a file. Post frames, not prose, when asking for a decision.
9. **Loop on feedback.** Collect open comments, act on each, reply with what changed, close the thread.
10. **Ship** the 1080p file and poster to your site, with the poster at the same frame the film uses.

## Review discipline

* Never publish a cut as a new page: one page, many versions, the change log on top of each.
* Before sending any cut, look at four frames of every changed beat at full size – start, mid, end and the frame after the last change. Clipping at the frame edge and blurred children are the two recurring defects.
* Keep a short note on the film current before the session ends: the current cut, the pipeline, what the user rejected and why.

## Gotchas already paid for

| Symptom                  | Fix                                                                                            |
| ------------------------ | ---------------------------------------------------------------------------------------------- |
| Children go soft         | A CSS filter on a parent blurs its children; sharp objects must be siblings of blurred panels. |
| Slow push jitters        | zoompan at 1080p jitters; compute it on a 7680-wide upscale.                                   |
| Concat can't find files  | The concat list's paths resolve relative to the list file.                                     |
| Script breaks on vars    | zsh mangles $var:flags; write assembly scripts in bash.                                        |
| Phone UI in 16:9         | A floating device on the plate with the camera pushing into its screen.                        |
| Email after a phone shot | Show the mobile mail app, not the desktop one.                                                 |

## What this skill does not do

* Choose the shoot, write the shot outline, buy the licence or make taste calls. Present candidates and frames; the user decides.
* Record the real application.
* Compose music or voice.