refactor(skills): rebuild faceless-explainer + pr-to-video on the shot-sequence architecture (#1778)

* refactor(skills): rebuild faceless-explainer + pr-to-video on the shot-sequence architecture

Move both skills onto the current shot-sequence authoring architecture and
the latest shared engine, then re-narrate to each domain. The engine fixes
(pre-assembly frame guards + BGM loop-extend in assemble-index, dark-ground
caption contrast, brand-accent selection + mono role in tokens, dark-mode
polarity invert / weight-clamp / icon-font filter in build-frame) had only
landed in one copy; both skills were a generation behind.

- Authoring model: visual-design now writes a time-coded shot sequence (Scene
  windows paced to the voiceover) instead of the older effects-id phased note;
  motion-language carries the move vocabulary + the tightened motion doctrine
  (smooth over bouncy) + the seek-safe core (fromTo entrances, no CSS-transition
  motion); add cut-catalog (within-frame velocity-matched seams); frame-worker
  and SKILL Step 4/5 move to blueprint instantiation + shot-sequence fidelity.
- faceless-explainer: fold the standalone composition.md into visual-design
  (inventing-the-visual / portrait / caption geometry); keep the explainer
  story doctrine; graft cue-segmented VO + candidate-blueprint-from-Step-3.
- pr-to-video: keep the ingest pipeline (fetch-pr / ingest / fetch-people-avatars)
  and code-vocabulary; preserve the code-beat (code-* block as focal, Scenes
  choreograph the surround) and mechanism-beat treatments under the new model.
- Drop stage-assets from both (no captured assets to stage); remove derivation
  references so each skill reads standalone.

bun run scripts/lint-skills.ts passes; all scripts node --check clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(skills): resync skills-manifest after format pass

The pre-commit skills-manifest hook hashed the skills before the format hook
reformatted cut-catalog.md, so the committed manifest lagged the on-disk
content and CI's "Skills: manifest in sync" check failed. Regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
WaterrrForever
2026-06-30 00:02:24 +08:00
committed by GitHub
co-authored by Claude Opus 4.8
parent c811a2750a
commit 7cb8386539
29 changed files with 2123 additions and 779 deletions
@@ -1,123 +0,0 @@
# Composition — PR-to-video visual-design judgment
> The composition-judgment layer for **Step 4 (Visual design)**. You read it while enriching `STORYBOARD.md` frames: which layout, how much frame the hero fills, how many depth layers — **director decisions**. Concrete px (safe margins 96-150), scale (1.05 / 0.92), three-layer `box-shadow`, `perspective` values are the **frame worker's** job; you name the intent in the frame's composition note. Video composition is closer to film / poster design than webpage layout — no scrolling, no reflow; every frame is a fixed canvas, every pixel matters. Default canvas **1920×1080**; portrait `1080×1920` / square `1080×1080` per the storyboard `format`.
## Squint test
Squint (or blur the frame). Can you still pick out the most important element, the second, and clear spatial groups? If everything has equal weight after blur, hierarchy is broken — redesign before writing the note. The strongest frames pass this: one dominant block + one supporting structural element, everything else demoted.
## Canvas zones (conceptual)
```
+--------------------------------------------------+
| Optional top chrome |
| +----------------------------------------------+ |
| | Safe margin | |
| | +----------------------------------------+ | |
| | | Primary content area | | |
| | | (center 65-75% of frame) | | |
| | +----------------------------------------+ | |
| | | Caption band (bottom ~17%, HARD w/ captions) | |
| | +----------------------------------------+ | |
| +----------------------------------------------+ |
+--------------------------------------------------+
```
- **Top chrome** — rarely needed in a faceless explainer; skip unless a frame intentionally mocks an interface.
- **Safe margin** — key content stays off the edges; hero / editorial frames need more air.
- **Primary content area** — the center 65-75% is where the eye rests; body text never presses the edge.
- **Caption band (bottom ~17%, HARD-reserved when captions are on)** — when the film has captions enabled (the frame's `Captions:` flag), the bottom ~17% of **canvas height** is reserved (landscape 1080h → bottom 180px, y 900-1080; portrait 1920h → bottom 320px, y 1600-1920): primary content and key visuals **cap at the band top**, and a centered hero anchors at **y ≈ 0.42 × height** (landscape ≈454, portrait ≈806), not the canvas midpoint. Background / ambient / surface layers are exempt and may stay full-bleed. Captions disabled → keep the zone clear anyway for bottom-edge consistency across frames.
You write "hero word centered with generous safe margins"; you do not write `padding: 150px 120px 92px`.
## Portrait & square (non-16:9 canvases)
The zones, density, hierarchy, and depth principles all still apply; the **aspect ratio** changes, and a wide-frame layout does not transplant into a tall one. Design for the storyboard's `format` from the start — never plan landscape and "crop."
- **Stack vertically, not side-by-side.** Portrait has little horizontal room: split-screen / triptych / 60-40 asymmetry become **top/bottom stacks**, vertical step lists, stacked bands. Square tolerates side-by-side only for two compact items.
- **Vertical center moves with the canvas** — anchor a centered hero around **y ≈ 0.42 × height** (portrait ≈806, square ≈454), not a fixed 540.
- **Type runs larger, fewer words per line** — narrow frames wrap long headlines badly; prefer short kinetic lines, bigger type, more vertical rhythm.
- **Travels well to portrait:** Centered, Layered Depth, Full-Width Strip (stacked band), vertical Rule-of-Thirds. **Avoid** wide Split Screen and Triptych — use stacked equivalents.
- **Density still rules** — primary visual ≥ 40% of canvas, ≥ 3 depth layers, measured against the tall frame; an empty top or bottom third reads as placeholder.
## 7 composition templates
Use ≥3 different templates per video (5 frames → 3+, 9 frames → 4+). **Don't default every frame to centered**; never use the same layout class twice in a row.
1. **Centered (hero / climax)** — one dominant element, generous breathing room. Concept name, key takeaway, the hero word, the closing principle.
2. **Rule of thirds** — anchor on a thirds intersection; remaining space carries support or negative space. A mechanism step + its label.
3. **Split screen (comparison / dual focus)** — left/right halves carry separate elements. Before/after, common-belief vs reality, two options.
4. **Layered depth (immersive)** — foreground / midground / background differ in scale + opacity. Opening hooks, atmosphere, the "imagine…" scenario.
5. **Asymmetric (editorial)** — primary content pushed to one side (60/40, 70/30); intentional imbalance → tension + sophistication. A dense diagram with a caption rail.
6. **Triptych (three-panel)** — three equal zones for three items / beats at once. The rule-of-three landing.
7. **Full-width strip** — one horizontal band (a number line, a timeline, an enumeration), usually ~20% of canvas height.
## Frame density — avoid empty frames
Common failure: small elements floating in the center with empty space around them. Every frame must feel **intentionally filled**.
- **Primary visual occupies ≥ 40% of canvas** — hero text 50-75% height × 60-80% width; a centered card 30-50% × 50-70%; a diagram big enough to read its labels.
- **≥ 3 visual layers** — background (gradient / particles / grid) + midground (main content) + foreground (emphasis / decoration).
- **Openings and closings** are prone to emptiness — a bare background + a lonely line of text reads as placeholder. Add environmental layers: dual-radial swell, floating particles, brand-color ambient texture, low-opacity scanlines or a hairline grid.
- **Text-only frames still need visual elements** — a coined-term card, an icon, a halftone field, brand-derived geometry, an underline that draws on.
**Fullness test:** could this frame stand as a poster or social graphic? If it looks like a sparse slide → add layers.
## Negative space
Whitespace directs attention, it isn't waste. Tight grouping (icon + label) → small spacing; unrelated groups → large separation; asymmetric outer margins feel more designed than equal padding; a hero word keeps large side whitespace so one word carries the weight. **Failure modes:** everything equidistant (no grouping); unintended overlap; text tight against an edge; captions colliding with bottom visuals; the framework's default padding everywhere.
## In-frame visual hierarchy
Visual weight, strong → weak: **large element** **motion** (moving beats static) **high contrast** **type scale** **position** (center + upper third are golden). Combine **at least two** — an element that is large, moving, and upper-third is unquestionably primary.
A title that is only _larger_ (sharing weight/color/spacing with body) reads weak. Stack dimensions:
| Dimension | Strong contrast |
| --------- | ------------------------------------------- |
| Size | 3:1 ratio or larger |
| Weight | 800-900 vs 400 |
| Color | high contrast against background |
| Motion | one element moving vs all else static |
| Position | top / left = primary |
| Space | large surrounding whitespace vs equidistant |
## Cards and grouping
Spacing + alignment can group without a card container. **Use cards** when content is genuinely distinct (a list item, a definition, a stat callout) or when shadow-stacking communicates "lifted." **Don't** card for mere separation (use whitespace) or for a continuous diagram. **Never nest cards** — claustrophobic, muddy hierarchy.
## Inventing the visual — diagrams, type, data-viz
This is a **faceless** explainer: the frame's hero is something you design, not a screenshot. The three first-class treatments:
- **Typographic / kinetic type** — the hero word, the coined term, a number, a short enumeration. Treat type as the subject: full-bleed scale, weight contrast, one emphasized term. Strongest for hooks, concept names, takeaways.
- **Abstract graphics** — shapes, fields, paths, geometry that _embody_ the idea (the snowball, the spotlight, the staircase-not-cliff). Build the metaphor the script names; don't decorate with generic bokeh.
- **Diagram / data-viz** — nodes + edges, a chart, a number line, a formula, a process flow. The build (each part appearing on beat) is the teaching — design it to assemble, not appear whole.
Make the invented hero **fill 40-60% of the frame** — a diagram big enough to read, a hero word near full-bleed. Don't shrink the one designed element into decoration around empty space.
## Depth on a 2D canvas
Layer **2-3 depth techniques** per frame to avoid a flat poster (concrete perspective / rotate / scale values are the worker's):
| Technique | Effect |
| ---------------- | ------------------------------------------------------------------------------------------ |
| Size difference | larger = nearer, smaller = farther |
| Blur | blurred = background, sharp = foreground |
| Opacity gradient | low = receding, full = primary |
| Overlap | foreground partially covers background |
| Shadow stacking | three-layer shadow = lift + brand feel |
| Motion speed | faster parallax = closer |
| Counter-scale | camera pushes toward focus → background appears larger, focal CSS scale <1 but fills frame |
You write "3 depth layers: background swell + midground diagram + foreground label glow; background counter-scales for the push"; the worker writes the scale values.
## What should not appear
Nav bars, footers, cookie banners, scrollbars, cursor arrows, browser chrome, unclickable buttons, generic decorative shapes standing in for a designed metaphor, floating bokeh / purple-to-blue AI gradients (the "default AI cliché," banned). Faceless explainers have no real interface to show — an interface mock is correct **only** when the topic itself is about that interface and the frame intentionally reconstructs it.
## Composition note example
> "Composition: asymmetric 60/40 — the node-graph diagram occupies left 60%, the layer label + caption right 40%. Generous safe margin; text capped inside the primary content area. 3 depth layers: background hairline grid + midground graph + foreground active-node glow. Density: primary visual ~55%, ambient adds a 5% scanline."
One line per frame; never concrete px / scale / shadow recipes (the worker writes those).
@@ -0,0 +1,215 @@
# Cut catalog — within-frame seams (worker-built)
> **A worker build-recipe (Step 5) — the sibling of `../hyperframes-animation/rules/`, not a second motion doc.** These are within-frame cuts the **frame worker builds INSIDE its own composition** (Z-scale + blur + opacity tweens, or per-word x-staggers, all on the frame's own paused GSAP timeline). They are **not** the between-frame transition: story owns that via `transition_in`, which the harness's injector stamps from a **separate registry vocabulary** (`crossfade` / `blur-crossfade` / `push-slide` / `zoom-through` / `squeeze`) — the catalog names here (**cut-the-curve / inverse-zoom / waterfall**) are **not** valid `transition_in` values. Use this catalog when a frame's shot sequence has an internal seam — a within-scene text/element swap, a **Scene-to-Scene** cut (a `Scene` is a time window WITHIN one frame, **not** a frame-to-frame boundary), or a text-to-text line change — and you want it to read as one continuous move instead of a hard slideshow cut. (`zoom-through` lives in both worlds: a whole-frame wrapper transition in the registry, an element-level Z-cut here — same idea, different scope.)
Four techniques that create depth and continuity:
1. **Zoom-Through** — within-scene text swaps, Z-axis, moving TOWARD the viewer
2. **Inverse Zoom-Through** — Z-axis swaps moving AWAY from the viewer
3. **Cut the Curve** — between-scene transitions on x/y
4. **Waterfall Cut** — word-by-word cut-the-curve with staggered exits and entries
All four are the same underlying principle: **cut at peak velocity, match direction and
speed on both sides of the cut.** The differences are axis, scope, and granularity.
**Choosing which at a seam:** for an UNFINISHED phrase (building one larger idea across
several visually distinct scenes that still approach the same point — multi-line text, a
run of consecutive cards) use **cut-the-curve** / **waterfall**. For a STATE CHANGE (turning
to a NEW part of the video — most often hook → context, between two distinct chapters) use
**zoom-through**, and **inverse zoom-through** for an arrival / payoff beat. Chain these so
the frame's internal seams feel like one camera moving through the content.
---
## Blur Logic (applies to all Z-axis variants)
Blur sells the speed at the cut, but it must scale with the SUBJECT SIZE:
| Subject | Peak blur | Why |
| ------------------------------------------------------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Text-scale (headline, line, word group) | **10px** | At 20px text smears into illegibility — the eye loses the word it was tracking and the cut reads as a glitch, not speed. At 20px letterforms go mushy mid-cut; 10px keeps them readable. |
| Full-frame surface (terminal window, card, screenshot) | **1820px** | Big surfaces have edges and texture that survive heavy blur; lighter blur on a full-frame move reads as a rendering hiccup instead of motion. |
Both sides of a cut use the SAME peak blur — the value must match at the swap frame.
Apply blur to the WRAPPER, never to individual children.
---
## 1. Zoom-Through (forward)
### The Problem
Text enters, holds, exits. Then next text enters, holds, exits. Each text block is
independent — no depth, no continuity. The video feels like a slideshow.
### The Principle
A velocity-matched cut on the Z-axis. You **never see both texts at the same time.** The
outgoing text scales toward the viewer (accelerating), blur and opacity peak at the cut
point hiding a hard swap, and the incoming text continues scaling up from behind
(decelerating into the focal plane). One continuous forward motion, two different texts.
### The Three Phases
**Phase 1: Exit** — text accelerates forward (toward viewer)
- Scale: `1.0 -> 1.2`, Blur: `0px -> 10px` (text-scale; see Blur Logic), Opacity: `1.0 -> 0.15`
- Scale/blur easing: `power3.in` (steep acceleration)
- Opacity easing: `none` (linear — even dimming, separated from scale)
- Duration: 0.2s
**Phase 2: Hard cut** at peak velocity + peak blur
- Outgoing: `opacity: 0` (instant via `tl.set`)
- Incoming: `opacity: 0.15, scale: 0.75, blur: 10px` (instant via `tl.set`)
- All properties match at the cut: blur, opacity, and scale DIRECTION (both scaling up)
**Phase 3: Entry** — text continues forward (growing into focal plane)
- Scale: `0.75 -> 1.0`, Blur: `10px -> 0px`, Opacity: `0.15 -> 1.0`
- Easing: `expo.out` (steep initial burst matching exit velocity, long settle)
- Duration: 0.5s
### Why Opacity Must Be Separate on Exit
Scale uses `power3.in` but that keeps opacity near 1.0 for most of the tween. Splitting
opacity to its own tween with linear ease makes the dimming even. On entry, all properties
can share `expo.out`.
---
## 2. Inverse Zoom-Through (backward)
The mirror: the camera "pulls back" instead of pushing through. The outgoing element
RECEDES away from the viewer; the incoming element arrives OVERSIZED (as if it had been
just behind the camera) and retracts into the focal plane. Both move in the shrinking
direction — same-direction rule preserved, just reversed.
**When to use over the forward variant:** arrival beats. The incoming element lands with
presence because it comes from larger-than-frame — right for a payoff line ("That changes
today."), a giant reply, or a held end-state. Forward zoom-through reads as _progressing
through_ content; inverse reads as _arriving at_ content.
### The Three Phases
**Phase 1: Exit** — element recedes (away from viewer)
- Scale: `1.0 -> 0.8`, Blur: `0px -> 10px` (text-scale)
- Scale/blur easing: `power3.in`; Opacity: `1.0 -> 0.15` on `none` (separate tween)
- Duration: 0.2s
**Phase 2: Hard cut**
- Outgoing: `opacity: 0` via `tl.set`
- Incoming: `opacity: 0.15, scale: 1.25, blur: 10px` via `tl.set`
**Phase 3: Entry** — incoming retracts into place
- Scale: `1.25 -> 1.0`, Blur: `10px -> 0px`, Opacity: `0.15 -> 1.0`
- Easing: `expo.out`, Duration: 0.5s
---
## 3. Cut the Curve (Scene Transitions)
### The Principle
Use cut-the-curve for **all scene-to-scene transitions** on x and y axes. The outgoing
scene's hero element accelerates in one direction, the cut lands mid-motion, and the
incoming scene's hero element continues moving in the **same direction** and decelerates.
Nothing exits fully off-screen and nothing enters from fully off-screen — **speed plus
opacity fading trick the eye**; the partial moves are enough.
### Same Path, Same Direction
If Scene A's hero slides left, Scene B's hero enters from the right and continues sliding
left. Both move leftward. One continuous motion.
| Direction | Scene A exit | Scene B entry start | Scene B entry end |
| --------- | -------------- | ------------------- | ----------------- |
| Leftward | `x: 0 -> -230` | `x: +230` | `x: 0` |
| Rightward | `x: 0 -> +230` | `x: -230` | `x: 0` |
| Upward | `y: 0 -> -230` | `y: +230` | `y: 0` |
| Downward | `y: 0 -> +230` | `y: -230` | `y: 0` |
### Velocity matching via mirrored eases
The cleanest match: exit `power4.in` and entry `power4.out` with the SAME distance and
duration — mathematically the two halves of one `power4.inOut` composite, so the entering
element picks up at exactly the 50% point of the notional path at identical velocity
(e.g. 230px / 0.3s ≈ 3,070 px/s at the cut on both sides).
The fade trick: the exit's opacity completes at ~2530% of its travel (fade duration
≈ 0.180.3s vs motion 0.30.34s) — the element vanishes while still visibly accelerating,
and nothing has to reach the frame edge. Entry fades IN fast from ~0.35 under its
deceleration. Time the LAST fading element to die right at the hard cut — gaps where
nothing is moving read as awkward dead air.
### Rules
- Use cut-the-curve for all scene transitions — it's the default, not an accent
- Same direction on both sides; mirrored `.in`/`.out` eases, same distance + duration
- Exit duration short (0.20.4s), entry duration >= exit duration
- Partial travel + fade, never full off-screen moves
---
## 4. Waterfall Cut (word-by-word cut-the-curve)
Cut-the-curve at WORD granularity — the strongest version of the leftward cut for
text-to-text seams. Each word of the outgoing line ramps out on its own pronounced curve;
each word of the incoming line cascades in mid-flight. The stagger turns the cut into a
wave the eye rides across the seam.
### Exit (per word)
- Motion: `x: 0 -> -230` over 0.34s on **power4.in** — a much more pronounced ramp than
the usual power2: the word barely creeps, then RIPS
- Fade: `opacity -> 0` over 0.18s (separate tween, `power1.in`) — completes when the word
is only ~2530% into its travel
- Stagger: reading order, ~0.022s per word, timed so the LAST word finishes fading right
at the hard cut
### Entry (per word)
- `fromTo x: +230 -> 0, opacity: 0.35 -> 1` over 0.3s on **power4.out** — the mirrored
back half of the composite; every word ignites already moving at matched velocity
- Waterfall stagger with SHRINKING gaps (start 0.05s, multiply by ~0.84 per word) so the
cascade accelerates across the line — the cascade should speed up word over word, not run
at a flat per-word delay
- Pre-set all words to `x: +230, opacity: 0` at build time — `immediateRender: false`
alone leaves un-started words sitting visible at rest during the stagger window
### Whole-line variant
A single-line beat (e.g. a big intro line) exits as one group with the same pronounced
ramp, but stretch its fade to ~0.3s ending ~0.02s before the cut — a lone element that
fades early leaves dead air that a word cascade would have covered.
---
## Choosing a Variant
| | Zoom-Through | Inverse Zoom | Cut the Curve | Waterfall Cut |
| -------------- | --------------------------- | --------------------------- | ----------------- | ------------------------- |
| Scope | Within-scene text swap | Arrival/payoff beat | Between scenes | Text-to-text seam |
| Axis | Z, toward viewer | Z, away from viewer | X / Y | X, per-word |
| Peak blur | 10px text / 20px full-frame | 10px text / 20px full-frame | none required | none (fade does the work) |
| Opacity at cut | 0.15 | 0.15 | exit faded by cut | last word dies at cut |
| Feel | progressing through | arriving at | carried sideways | a wave across the seam |
---
## Anti-Patterns
| Don't | Why | Instead |
| ---------------------------------------- | ------------------------------------------- | ------------------------------------------------------ |
| Two texts visible during a zoom-through | Overlapping text breaks the Z-axis illusion | Hard cut at blur peak, one text at a time |
| 20px blur on text-scale subjects | Letterforms smear; reads as a glitch | 10px for text, 1820px only full-frame |
| Elements on different paths across a cut | Eye tracks one direction, cut goes another | Same property, same direction |
| Mismatched blur/opacity at the swap | Visible flash or brightness jump | Identical values at the cut frame |
| Gentle easing on entry (`power2.out`) | Entry velocity feels slower than exit | Mirror the exit: `power4.out` / `expo.out` |
| Full off-screen exits / entries | Wastes time and breaks the speed illusion | Partial travel + early fade |
| Lone element fading long before its cut | Dead air at the seam | Fade ends ~0.02s before the cut, or use a word cascade |
| Zoom-through on body text | Small text at 0.75 scale is unreadable | Only headlines and short phrases |
| Scene cuts without cut-the-curve | Static cuts feel like a slideshow | Cut-the-curve is the default |
+105 -90
View File
@@ -1,141 +1,156 @@
# Motion language — PR-to-video visual-design judgment
# Motion language — the move vocabulary + the motion doctrine + the seek-safe core
> The motion-judgment layer for **Step 4 (Visual design)**. You name **each shot's choreography, spring intent, beat rhythm, holds, stillness, and the idle-motion budget** while enriching `STORYBOARD.md` frames; the **frame worker** maps intent to concrete GSAP eases / ms / stagger / code (via `hyperframes-animation`). A good explainer feels like one continuous whole — one camera, one spring feel, **every shot directed across its full length** — not a pile of slides that animate once and freeze. You reference motion by **role**, never by curve: eases / durations resolve from `frame.md`'s motion tokens, named `entry` / `emphasis` / `exit` / `drift` (the pack's exact keys may differ); the worker maps the curve. Between-frame **transitions are not yours** story names `transition_in`, the harness injects it.
> The motion layer for **Step 4 (Visual design)**. When you write a frame's **time-coded shot sequence**, you name each scene's move **inline from the vocabulary below** — a named palette of the moves the golden corpus actually uses. Each move carries the **backing rule id** in this skill's local `../hyperframes-animation/rules/`; cite that id so the move resolves to a real recipe when a **frame worker** implements it in Step 5 (the worker reads the rule body in `../hyperframes-animation/rules/<id>.md` — it reproduces the move, it does not guess from the name). You name motion by **role / move name**, never by raw GSAP curve, ms, or stagger formula — the worker maps the curve. Between-frame **transitions are not yours**: story names `transition_in`, the harness injects it; that injected transition **is** the frame's exit. For cuts a worker builds INSIDE a frame (within-scene swaps, scene-to-scene seams), see the catalog in `cut-catalog.md`.
## A frame is a shot, not a slide
A good code-change explainer feels like one continuous film — one camera, one motion feel, **smooth and timed to the voiceover** — not a pile of slides that animate once and freeze. In a PR explainer the development often _is_ the reveal: the diff hunk typing in, the before→after morph, the impact stat landing. The doctrine in Part 2 is load-bearing: when in doubt, do what it says.
The single failure that makes an explainer read as PowerPoint: a frame whose content **animates in over the first ~0.8s, then freezes** for the rest of its duration while a slow drift plays underneath. The entrance is not the shot — it's the **first beat** of it. You direct the **whole duration**.
---
In explainers especially, the development beat _is_ the teaching: the formula assembling term by term, the diagram gaining a layer, the count-up landing. Don't waste it on a frozen hold.
# Part 1 — the move vocabulary
Three layers fill a shot, each governed by a different rule:
Reach into this palette when naming a scene's motion. Pick the move that matches the beat, name it in the shot sequence, and cite the rule id after `→`. The blueprints (`../hyperframes-animation/blueprints/`) name these same moves in their `rule mapping`; you're drawing from one shared palette. Compose 24 across a shot's scenes (entrance → sequential reveal → settle), not all at once.
| Layer | What | Rule |
| ------------------------- | ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------- |
| **Camera** (macro) | ONE correlated move on the frame root — slow drift / dolly / push / parallax pan | **always on, the whole shot** — this is the "someone is filming this" layer |
| **Choreography** (action) | the beat develops: entrance → mid-shot move (reveal / rearrange / morph / emphasis hit) → settle | **fill the duration** — a shot animated only at entry is a slide |
| **Idle life** (texture) | ambient continuation on the 1-2 elements that hold a live slot — breathing, glow, float | **budgeted** — this is where screensaver lives; cap it |
## Kinetic type
The reconciliation that matters: **mandate choreography, budget idle life.** Purposeful, sequential motion that carries information should fill the shot; ambient, simultaneous motion that carries none should be capped. Many elements each floating independently reads as _screensaver_; a shot that only enters then freezes reads as _slideshow_. Avoid both — **one camera move + a directed multi-phase action + 1-2 living elements**, nothing scattered.
- **hard-cut / flash word-swap** — a word or line replaces the previous one on an instant cut (no fade/roll); the swap itself is the beat. → `discrete-text-sequence`
- **in-place token cycle** — a fixed line holds and only its variable slot changes, token → token → token. → `discrete-text-sequence`
- **per-word staggered reveal** — a phrase assembles word-by-word (or chunk-by-chunk), each landing on its own beat. → `dynamic-content-sequencing`
- **kinetic beat-slam** — short phrases slam in on a shared percussive beat array, each with a distinct entrance, resolving on a locked finale; the recipe for "punchy / rhythmic" taglines. → `kinetic-beat-slam`
## Multi-phase choreography — direct the full shot
## Typewriter
Every non-still frame's timeline is choreographed across its length, not front-loaded into the entrance:
- **type-on with caret** — text types in character-by-character behind a blinking caret. → `discrete-text-sequence` (+ `context-sensitive-cursor` for the caret blink / color)
- **backspace-and-retype** — the line types, deletes the last word(s), and retypes a new one (typo-correction, reframe). → `discrete-text-sequence` (+ `context-sensitive-cursor`)
```
entrance → development → settle
```
## Count-up / data
- **entrance** — the beat's primary content arrives (hero `entry` / `heavy`; groups staggered).
- **development (the phase that's usually missing → PPT)** — mid-shot, the content _does something_: a second element reveals, elements rearrange to a new layout, a diagram gains a layer, a count-up runs, an emphasis hit lands on the keyword. This is the motion that separates video from slides.
- **settle** — the shot resolves and holds for its read; the camera + idle life continue underneath (never a hard freeze).
- **value-scaled counter** — a number counts up and its font size grows with the value, so the climb itself escalates. → `counting-dynamic-scale`
- **bars / progress / star wipe** — a number paired with a graphic that fills: bar-height stagger, a progress bar / ring filling, a fractional star-rating wipe. → `stat-bars-and-fills`
**Architecture:** in hyperframes only the **exit** is forbidden mid-video (the frame unmounts; the harness transition _is_ the exit). Everything _before_ the settle — including rich mid-shot development — is free and seek-safe. Build the development phase; skip only the exit (unless you are the final frame).
## Reveal / decode
When you name a **`blueprint`**, the development phases come from its recipe — write the composition note **shot-by-shot** to match. When you name **no blueprint**, the **≥3 cited effects ARE the phases** — sequence them (one enters, one develops, one emphasizes); **don't fire them all at t=0**.
- **3D char flip-decode** — characters flip in 3D and resolve from scrambled glyphs to the real text (decryption feel). → `hacker-flip-3d`
- **SVG self-draw** — an outline / icon / ring draws itself stroke-by-stroke. → `svg-path-draw`
## Spring intent (by role, not curve)
## Camera
| Intent | Feel | Use |
| ---------- | ------------------------------------------- | --------------------------------------- |
| **entry** | confident slight overshoot, settles quickly | primary element entry (default) |
| **gentle** | soft slide-in, no overshoot | background elements, subtle motion |
| **snappy** | tight overshoot, nearly instant | small icons, labels, list items |
| **heavy** | weighted deceleration | large diagrams, hero visuals |
| **slam** | bouncy overshoot, intentionally loud | a coined term landing, an impact moment |
- **push / focus / drift** — a sequential camera move on the frame root (pull-back → focus → push) plus continuous micro-drift; the cinematic baseline. → `multi-phase-camera`
- **zoom-to-target** — zoom into a non-centered element (scale + counter-translate to keep it framed). → `coordinate-target-zoom`
- **pan / focus-lock** — a virtual camera transforming one `.world` wrapper to pan / zoom / lock onto a region. → `viewport-change`
- **camera-cursor-tracking** — the viewport locks to a moving focal point (a typing cursor), static framing then focal-locked tracking. → `camera-cursor-tracking`
**Consistency:** similar elements share one intent (all labels `snappy`, all hero visuals `heavy`). Don't invent a unique ease + duration per element.
## Layout motion
**Forbidden:** `bounce.out` / `elastic.out` (dated; real objects decelerate, they don't bounce — low overshoot for `entry` is fine, high overshoot only for clearly playful moments); a unique ease+duration per element (visual noise).
- **cluster→outward expansion** — elements start clustered at center and expand outward to their final positions in lockstep. → `center-outward-expansion`
- **orbit** — elements flip in from 3D space and settle into a continuous elliptical orbit (entry flips in-place at the orbital position). → `orbit-3d-entry`
- **split-tilt cards** — two cards side-by-side with opposing rotationY tilts, entering from their respective sides (comparison / before-after). → `split-tilt-cards`
- **logo/avatar ring + connectors** — avatars or logos on an elliptical ring with SVG connection lines to a center point, staggered entry. → `avatar-cloud-network`
## Duration intent
## Surface / UI
Reference by **tier** ("instant feedback" / "state change" / "layout change" / "entry animation"); the worker maps concrete ms / frames at 30fps. **A single entry should not exceed ~800ms** — for a longer buildup, use multi-element stagger or a development phase, **not** one long tween.
- **3D page-scroll reveal** — a full webpage as a tilted 3D card whose internal content scrolls to reveal specific sections. → `3d-page-scroll`
- **cursor click + ripple** — a cursor moves to a target, depresses with it on click, and emits an expanding ripple. → `cursor-click-ripple`
- **button press** — a tactile press: compression then spring recovery, optional release burst / glow. → `press-release-spring` (or `physics-press-reaction` for a click that compresses cursor + target together)
- **keyword glow** — keywords light up with glow + scale + color on an attack-decay-rest envelope, synced to a word rail. → `asr-keyword-glow`
**Phase-to-phase within a shot is swift** — when one element makes way for the next (development), the outgoing move runs ~75% of an entry; arrival is deliberate, hand-off is quick. (The between-frame **exit** is the harness's transition, never your within-shot motion.)
## Morph / handoff
## Stagger cap
- **scale-swap** — two elements at the same screen center hand off: the outgoing cluster shrinks + fades as the incoming one arrives. → `scale-swap-transition`
- **card morph-anchor** — a container morphs apparent size + corner radius + surface between two shots, then fades to reveal the real target beneath (HyperFrames uses uniform `scale`, not `width`/`height`). → `card-morph-anchor`
When staggering N elements, **total ≤ 500ms** (longer feels dragged):
## Seam cuts (worker-built, inside a frame)
- **3-7 elements** — normal stagger, total 300-700ms.
- **8+ elements** — tighten per-item delay, or stagger only the first few and enter the rest with the last.
- Never let stagger run past 500ms.
The velocity-matched cuts a worker authors between a frame's own Scenes. Name the seam in the shot sequence; the recipe is in the catalog, not a single `../hyperframes-animation/rules/` id.
## Beat structure across frames (the cross-frame rhythm)
- **zoom-through / inverse zoom-through** — a within-scene swap on the Z-axis; forward reads "progressing through", inverse reads "arriving at" (payoff). → `cut-catalog.md`
- **cut-the-curve** — a scene-to-scene cut where both sides move the same direction at matched velocity. → `cut-catalog.md`
- **waterfall cut** — cut-the-curve at word granularity, a wave across a text-to-text seam. → `cut-catalog.md`
Rhythmic videos breathe: tension → release → tension → release. A clean reference shape for a ~46s explainer:
## Emphasis / marker
| Phase | Duration | Rhythm | Frame type |
| ------------ | -------- | ------------------- | ----------------------------------------- |
| Hook + gap | 4-8s | slow build | open the curiosity gap; land the hook |
| Concept name | 3-6s | deliberate | name the idea, one breathable beat |
| Body build | 12-20s | continuous, layered | the mechanism / steps / items, on a stage |
| Landing | 3-5s | still, breathable | the takeaway / principle / CTA |
- **highlight / circle / burst / scribble** — a marker-drawn emphasis on a word or element: yellow highlight sweep, hand-drawn circle, radiating burst, scribble, or rough sketch-outline. → `css-marker-patterns`
Allocate motion by a frame's energy: **high-energy** (hook, a surprising stat) → faster entry, tighter stagger, `snappy`, busier development; **breathable** (concept name, the turn in a story, the landing) → slower entry, `gentle`, longer hold, minimal development; **data / mechanism** (a step, a statistic) → medium rhythm, clean stagger, a count-up or layer-reveal as the development phase.
## Aliveness during a hold (use sparingly — see Part 2)
## Hold time — read time, not freeze time
- **subtle jitter** — the sanctioned way to keep a settled frame alive: a small, low-amplitude positional/scale jitter on the held element. The motion-graphics trick that reads "alive" without reading "weak." → `sine-wave-loop` (low-amplitude register)
- **live SVG internals** — internal SVG parts move so an icon feels alive (rotating hands, oscillating blades, pulsing dots, dash-flow); fine because it's the subject doing something, not a card breathing. → `svg-icon-enrichment`
- **finite bounded ambient** — a single bounded breathe/drift on ONE held hero, only when genuinely needed; de-emphasized — prefer sequential reveal or jitter first. → `sine-wave-loop`
After an element enters it must stay long enough to read (the worker maps concrete frames). "Hold" means **don't cut early** — the camera + idle life keep playing underneath; it is never a hard freeze.
## The added moves — now backed by local rules
| Content | Minimum hold |
| ----------------------------------- | ------------ |
| display text (1-3 words) | ~1s |
| short sentence | ~1.5s |
| data / statistic | ~1.5s |
| diagram / formula | ~2s |
| complex visual (multi-part diagram) | ~2.5s |
| hero / climax word | ~1-1.4s |
Five moves the golden corpus needs were added to this skill's `../hyperframes-animation/rules/`, rounding out the vocabulary above:
Narration shorter than the needed hold → the frame's `duration` should still give the visual its read time.
- **depth-of-field / selective-blur** — blur the off-focus subset to spotlight the focal element → `depth-of-field-blur`
- **motion-blur streak** — directional velocity blur on a fast fly-in / camera push-through → `motion-blur-streak`
- **3D depth scatter-assemble** — glyphs/elements scatter into a tumbling 3D cloud, then reassemble → `depth-scatter-assemble`
- **spring-pop entrance** — the canonical entrance pop; default to a smooth long-tail settle, overshoot only when explicitly playful → `spring-pop-entrance`
- **ambient glow / bloom** — un-triggered soft glow blooming behind a static hero → `ambient-glow-bloom`
## Stillness before climax — the marked exception
---
A **0.3-0.75s pause** between the major action and its confirmation / result — the silence builds tension before the landing (the turn in a story, the "aha" after a build). It lands **because the rest of the video is choreographed** — stillness is a contrast against motion, so it only reads when motion is the baseline. **Allocate it to only 2-3 frames per video, named in the `## Video direction` block**, where the narration lands a payoff. Stamped on every frame it becomes a tic and flattens the rhythm. Name `stillness-before-climax` in that frame's motion note; even then the camera move continues (still ≠ frozen).
# Part 2 — the motion doctrine (load-bearing)
## The idle-life budget — what may move during the hold
These four rules are the difference between a clip that reads as a serious code-change explainer and one that reads as an agent-made PowerPoint. Follow them as written.
The 1-2 elements that keep moving _after_ the development settles. This is the layer that, overdone, becomes screensaver — so it is **capped**, not mandated:
## 1. Smooth beats bouncy — `power3` is the default
1. **Camera move** — always present (the macro layer above); it alone keeps everything coherently alive.
2. **At most 1-2 secondary live elements** — the ones carrying the beat (hero, the active node). Everything else holds.
3. Prefer **macro move + depth parallax** over many independent floats.
Elements should use **long-tail decel curves that let them settle smoothly. `power3` is enough in most cases.** No bouncy, no overshoot, no `back.out` / `bounce.out` / `elastic.out` as a default.
Secondary-slot menu (formulas are the worker's): **multiplicative breathing** (hero — small ±2-5% on final scale) · **glow pulse** (the active element) · **sine float** (one decorative cluster at most) · **rotational drift** (3D cards, hero mark) · **orbit** (surrounding icons; counts as the one decorative cluster) · **halftone breathing** (atmospheric frames).
Bouncy is the **#1 instant turn-off** in user-made Remotion / HyperFrames videos, and the agent almost never gets it right — it thinks bouncy adds emphasis, but it buys that emphasis at the cost of cleanliness. The serious motion-design shops feel the same. **Smooth always wins.** Overshoot is demoted to a **rare, explicitly-playful exception** (a consumer/fun logo slam, a deliberate bell-hit) — never the house style. Name the intent as a long-tail settle; the worker maps `power3` (or `expo.out` on a fast arrival). See `../hyperframes-animation/rules/spring-pop-entrance.md` — it now leads with the smooth settle.
Multiplicative breathing is the signature for a hero **that holds a live slot** — not stamped on every hero. **Minimum amplitude ±6px or ±2-5% scale** — a 3px micro-float doesn't count.
## 2. Sequential reveal in the back ~50%, timed to the voiceover
## Seek-safe motion — intents that don't survive the renderer
This is the anti-PowerPoint mechanism — sharper than "put development in the middle."
The frame is a **paused GSAP timeline seeked frame-by-frame**, so some "continuous" intents from a real-time engine cannot render — **don't name them**:
- **Don't dump everything on screen in the first ~25%** of the scene. Rushing all content in up front is exactly what forces the slideshow feel.
- **Reveal each piece — a line, a card, even an h1 — when the voiceover mentions it**, sequencing reveals across the **later ~50%** of the scene. Same amount of agent work, but the cut becomes coherent and gains rhythm.
- **Less is more.** Fewer things on screen, each arriving on its VO beat, beats a full canvas that animated once and froze.
- **No infinite / forever motion** — "particles loop endlessly," "logo rotates forever," "marquee scrolls on repeat." Idle life is a **finite tween over the hold** (breathe up then back), never `repeat`/`yoyo`.
- **No randomness or wall-clock** — `Math.random` particle fields, `Date.now` drift. Every motion is the same on every render; name deterministic motion only.
- **Entrance + development only** (exit = final frame only) — the cross-frame exit is the harness's transition.
- Express oscillation/breathing as a **bounded finite move**, not a loop.
Practically: a frame's shot sequence front-loads almost nothing — the entrance carries only what the VO is saying at t=0, and the rest of the elements wait in the timeline for their spoken cue. A reveal maps onto a development-class move from Part 1 (`per-word staggered reveal`, `cluster→outward expansion`, a `count-up`, an `asr-keyword-glow` synced to the word rail).
## Forbidden — both failure modes
## 3. No lazy breathing, no bad pan/push — "no motion over bad motion"
**Slideshow (under-motion):**
The agent's two reflexive ways to fake "aliveness" both read cheap:
- Content animates in, then **freezes** for the rest of the shot (the PPT tell).
- Only the entrance is animated; the remaining duration is a frozen hold under a drift.
- The ≥3 cited effects **all fire at t=0** instead of sequencing into entrance / development / emphasis.
- No mid-shot development on a non-still frame.
- **No lazy breathing.** Scaling cards/text up and down in a circular loop to look "alive" is the cheap tell. Don't reach for it.
- **No bad slow pan / push in the back half.** A slow pan or push on elements in the later ~50% of a scene **disrupts the viewer's sightline and causes eye discomfort** — it actively makes the frame worse, not better.
**Screensaver (over-motion):**
The fix for both is the same: **stagger element reveals in time with the script** (rule 2). And the governing principle: **"I'd rather have NO motion than BAD motion."** A held, still frame is better than a frame kept "alive" by breathing or a drifting camera. The **only sanctioned aliveness** during a hold is **subtle jitter** — a small low-amplitude jitter that keeps a frame from feeling dead without looking weak (it's in Claude videos now). Everything else holds.
- **Every element** floating independently; idle motion with no information.
- More than 1-2 elements idling at once; scattered sine floats as the "aliveness."
- A 3px micro-float standing in for real motion.
## 4. Internal seams are velocity-matched cuts
**Always:**
When a frame has an internal seam — a within-scene swap, a Scene-to-Scene cut, a text-to-text line change — make it a **velocity-matched cut**, not a hard slideshow cut: cut at peak velocity, match direction and speed on both sides. The catalog (the four techniques, the blur logic, and which to use when) is `cut-catalog.md`; the moves are listed under **Seam cuts** in Part 1.
- `bounce.out` / `elastic.out`; a bespoke ease+duration per element; `repeat` / `yoyo`; all elements entering simultaneously (must stagger or sequence).
## One-line summary
## Motion note example
Smooth long-tail (`power3`) over bouncy; reveal sequentially in the back ~50% timed to the VO (not dumped in the first 25%); no lazy breathing and no bad slow pan/push — prefer stillness, with subtle jitter as the only aliveness; cut at peak velocity with matched direction/speed (→ `cut-catalog.md`).
> "Macro: slow dolly-in on the frame root across the whole beat. **Entrance** — the concept word enters `EASE.entry` (heavy); supporting labels snappy-stagger (4 items, ~400ms). **Development** — the diagram gains its second layer, then a count-up runs beneath it. **Stillness-before-climax 0.6s** (allocated frame; only the dolly continues). **Settle** — the takeaway emphasis: text gentle entry + glow; idle hold with the hero word breathing ±3% as the one live element."
---
One line for a single-shot frame; **shot-by-shot when the beat is multi-phase** (always, when you named a `blueprint`). Never concrete ease curves / ms / stagger formulas / JS — the worker writes those.
# Part 3 — the seek-safe core (hard rules)
The frame is a **paused GSAP timeline seeked frame-by-frame**, so some "continuous" intents from a real-time engine can't render — don't name them. These are non-negotiable regardless of doctrine.
- **No infinite / forever motion** — "particles loop endlessly," "logo rotates forever," "marquee on repeat." Any aliveness (the subtle jitter, a live SVG internal, a needed bounded ambient) is a **finite tween over the hold**, never `repeat` / `yoyo`.
- **No randomness or wall-clock** — no `Math.random` particle fields, no `Date.now` drift. Every render must be identical; name deterministic motion only (stagger and any variation derive from the element index).
- **Entrances use `fromTo`** — state the from-state explicitly so a seek to `t=0` lands the element correctly; never rely on a CSS-hidden start (it renders visible before the tween claims it, and flickers under seek).
- **No CSS `transition` / `@keyframes` for motion** — CSS animation runs on the browser clock, independent of the HF seek clock; it desyncs and flickers. Drive all motion inside the paused GSAP timeline.
- **Entrance + sequential reveal only — no mid-video exit.** The frame unmounts via the harness transition; that injected `transition_in` **is** the exit. Exit motion belongs only to the final frame. (Worker-built seam cuts in `cut-catalog.md` are within-frame, not the frame's exit.)
## Forbidden — the failure modes
**Slideshow (the primary failure):** everything dumped on screen in the first ~25%; content enters then freezes; nothing revealed on its VO cue. Fix with rule 2 (sequential reveal timed to the VO).
**Cheap aliveness:** circular breathing as "life"; a slow pan/push in the back half disrupting the eye; many elements floating independently as "motion." Fix with rule 3 (stillness + subtle jitter only).
**Bouncy:** `back.out` / `bounce.out` / `elastic.out` as the default entrance; hand-keyed overshoot. Fix with rule 1 (`power3` long-tail; overshoot only when explicitly playful).
**Always:** no `repeat` / `yoyo`; no `Math.random` / `Date.now`; no all-elements-entering-simultaneously (sequence or stagger).
## Naming motion in a shot — example
> Scene 1 (0.01.0s): solid field; hero headline enters via **per-word staggered reveal** (`dynamic-content-sequencing`) on a smooth long-tail settle (`power3`); slow **push** on the root (`multi-phase-camera`) holds steady — no back-half re-push.
> Scene 2 (1.03.0s): as the VO names each changed file, five file chips reveal **sequentially** via **cluster→outward expansion** (`center-outward-expansion`), then a **value-scaled counter** (`counting-dynamic-scale`) ticks the +/ line total up beneath them — the back-half reveal, timed to the script, not dumped at t=0.
> Scene 3 (3.04.2s): hold on the result; **keyword glow** (`asr-keyword-glow`) lands on the payoff word as the VO says it; settles and holds still — at most **subtle jitter** (`sine-wave-loop`, low amplitude) keeps it alive; no breathing, no drift.
Name the move + its rule id (or `cut-catalog.md` for a seam cut) per scene; let the worker pick curves, ms, and stagger — defaulting to `power3`.
@@ -111,7 +111,7 @@ This framework builds **one frame per worker** — there is no multi-frame "cont
1. **A consistent stage** — consecutive body frames share one composition idea (the same navy code window filling in, the same before|after split, the same counter advancing), stated in each frame's `scene` so Step 4 and the workers keep the stage stable.
2. **A consistent transition** — pick one seam type for a run (`crossfade` for a soft code reveal, `push-slide` for the next change item) and repeat it.
When a single element genuinely _transforms_ between two ideas (the failing test flips green, the old function becomes the new one), keep it **within one frame** as a development beat (entrance → transform → settle) — the worker owns that motion (a `code-diff` or `code-morph` block). Note the intent in `scene` / narrative; Step 4 turns it into the block + `effects`.
When a single element genuinely _transforms_ between two ideas (the failing test flips green, the old function becomes the new one), keep it **within one frame** as a development beat (entrance → transform → settle) — the worker owns that motion (a `code-diff` or `code-morph` block). Note the intent in `scene` / narrative; Step 4 wraps it in a time-coded shot sequence around the block (a `code-diff` / `code-morph`).
## Transitions
@@ -133,7 +133,7 @@ Code beats live on the **navy code surface** (claude's Code Surface treatment)
A diff shows **what changed in the code**. It does **not** show **what the change does** — and "what it does" is usually the more memorable, more explanatory beat. The single biggest reason a PR video feels flat is that every body frame is a code surface or a number: it _tells_ (here are the lines, here is the stat) but never _shows_ (here is the request actually recovering).
A **`mechanism` frame animates the runtime behavior** the PR changes — built as an **invented animated diagram** (SVG / HTML / GSAP on claude's cream ground: hairline-ink nodes / edges / lanes, one coral marker on the active or changed element), where **the build _is_ the teaching** — each part appears on beat, the flow plays out across the shot. It is **not** a code block and **not** a headline. Reach for the `flowchart` / `flowchart-vertical` / `data-chart` registry blocks where they fit; otherwise invent it (composition.md's diagram / abstract-graphics register).
A **`mechanism` frame animates the runtime behavior** the PR changes — built as an **invented animated diagram** (SVG / HTML / GSAP on claude's cream ground: hairline-ink nodes / edges / lanes, one coral marker on the active or changed element), where **the build _is_ the teaching** — each part appears on beat, the flow plays out across the shot. It is **not** a code block and **not** a headline. Reach for the `flowchart` / `flowchart-vertical` / `data-chart` registry blocks where they fit; otherwise invent it (visual-design.md's diagram / abstract-graphics register).
Plan **at least one `mechanism` beat** for any PR with a visible runtime behavior (most feature and fix PRs have one). What to animate, by what the change touches:
@@ -172,6 +172,8 @@ The largest quality bug in PR videos is **scripts that talk too long**. TTS runs
Estimate while writing: `duration ≈ ceil(word_count / 2.2)`. 29 words → 13s (trim); 17 words → 8s; 12 words → 6s. Trim techniques: cut the lead-in clause ("Until now, the agent shipped…" → "The agent shipped…"); move numbers off-script onto a counter; split only when the halves carry distinct beats (cause then effect). **Silent frames are allowed and common** — a diff typing on, a before→after morph, a counter running. Set `voiceover` empty, omit from `SCRIPT.md`, and make `narrativeRole` carry it. A complex change does not need a long script; it needs a careful one — if you can't headline the change in 19 words, the headline isn't sharp yet.
**Write each line as discrete cues, not one run-on breath.** Step 5 reveals each on-screen piece _when the voiceover names it_ (the anti-PowerPoint mechanism). A line with clear phrase boundaries — "Three retries — then it backs off — then it gives up clean" — hands the shot its reveal cadence for free; a single long clause leaves the frame nothing to pace to.
## Frame template
```md
@@ -186,6 +188,7 @@ Estimate while writing: `duration ≈ ceil(word_count / 2.2)`. 29 words → 13s
- type: diff
- persuasion: Before/after contrast
- beat: comprehension
- blueprint: <candidate id from the role→blueprint menu, or omit — a code beat usually omits it (the code-\* block is the shape)>
narrativeRole: What this frame does in the viewer's understanding (its job, not what's on screen).
keyMessage: The one thing the viewer should understand after this frame (one sentence).
@@ -199,6 +202,7 @@ The `credits` frame additionally carries an `asset_candidates:` line (see the cr
- The opening uses a named hook strategy; you do not read the PR description aloud.
- Each frame has one job; the body builds cumulatively, **alternating `diff` (the code) with `mechanism` (the behavior)** + `impact` / `evidence` — not a single isolated body frame, and not an unbroken stack of code surfaces.
- Every frame has `type` (PR-native), `persuasion` (a named technique), and `beat` (specific). The emotional arc matches the archetype (fix = frustration → relief; feature = curiosity → confidence).
- Each `voiceover` is phrase-segmented into cues (each a piece Step 5 can reveal on), not one run-on clause; a candidate `blueprint:` is tagged from the role→blueprint menu where a proven shape fits (a code beat usually omits it — the `code-*` block is the shape).
- **24 real diff hunks** featured, each a small legible snippet (not a whole file), each naming its `code-*` block in `scene`.
- **At least one `mechanism` beat** animates what the change _does_ at runtime (an invented diagram, or a `flowchart` / `data-chart`), named in its `scene` — unless the PR genuinely has no visible behavior (a pure docs / config bump). The body is not an unbroken run of code surfaces.
- Transitions use only registry names and repeat 23 types; frame 1 is `cut`.
+121 -63
View File
@@ -1,106 +1,164 @@
# Visual design — PR-to-video per-frame enrichment method
# Visual design — PR-to-video per-frame shot method
> The method behind **Step 4 (Frame visual design)**. You (the orchestrator) read it to **enrich `STORYBOARD.md` frames in place** — story-design wrote the skeleton (each frame's `scene`, `voiceover`, `transition_in`, and the narrative fields); you add how each frame **looks and moves**. Each frame is a **directed shot, not a static slide** — you choreograph it across its whole duration. Because a PR video is **mostly faceless, most visuals are invented** — typography, number-lockups, diagrams — so you describe visual elements rather than place captured assets; the exceptions are **code beats** (a ready-made `code-*` block) and the **credits close** (real contributor avatars), both covered below. You write **no HTML** (that's the frame workers). `frame.md` is your palette/type truth. Composition / motion detail lives in `composition.md` + `motion-language.md`; effect & blueprint **bodies** live in `hyperframes-animation`. Adding palette theory or a generic font rule here? Wrong home — `frame.md` + `hyperframes-creative`.
> The method behind **Step 4 (Frame visual design)**. You (the orchestrator) read it to **enrich `STORYBOARD.md` frames in place** — story-design wrote the skeleton (each frame's `scene`, `voiceover`, `transition_in`, the narrative fields, and optionally a candidate blueprint id); you add how each frame **looks and moves**. The unit you write per frame is a **time-coded shot sequence** — a shot directed across its whole duration, not a static slide. You write **no HTML** (that's the frame workers). A PR video is **mostly invented** — typography, number-lockups, mechanism diagrams — so you **design** those elements; the two exceptions are **code beats** (a ready-made `code-*` registry block) and the **credits close** (real contributor avatars), both covered below. `frame.md` is your palette/type truth by role. Layout is a compact vocabulary in this file (the **Layout** section below), stated inline per Scene; motion vocabulary + the motion doctrine + the seek-safe core → `motion-language.md`; the proven shapes → `../hyperframes-animation/blueprints-index.md` + `blueprints/<id>.md`; the `code-*` blocks → `code-vocabulary.md`; concrete rules resolve in Step 5 from this skill's local `../hyperframes-animation/rules/`. Adding palette theory or a generic font rule here? Wrong home — `frame.md` + `hyperframes-creative`.
## Every frame is a directed shot
## The unit is a time-coded shot sequence
A frame's visual layer is choreographed across its **full duration**, not front-loaded into an entrance. The failure that reads as PowerPoint: content animates in over the first ~0.8s, then **freezes** while a slow drift plays under it. So every frame's metadata + note describe a **shot with phases**`entrance → development → settle` — where _development_ (a reveal, a rearrange, a morph, an emphasis hit, a count-up) is the mid-shot motion that separates video from slides. The shot model and the choreography-vs-idle budget live in `motion-language.md`; here you **encode it into the frame**: the `effects` / `blueprint` ids are the motion vocabulary, and the **composition note sequences them into phases**.
A frame's visual layer is **a sequence of time windows paced to the voiceover**, not a bag of effect tags. The failure that reads as PowerPoint is **front-loading**: the agent rushes the whole canvas on screen in the first ~25%, and then it just sits. A time-coded shot sequence written **against the VO** makes that impossible: each window states what is on screen and what is moving, and **nothing appears before the voiceover reaches it.** In a PR explainer the development often _is_ the reveal — the diff hunk typing in, the before→after morph, the request-retry diagram running, the impact stat landing. Let the build _be_ the message.
In explainers, _development_ is often the teaching itself — the formula assembling term by term, the diagram gaining a layer, the count-up landing the statistic. Let the build _be_ the message.
Write each frame as a handful of windows cued by the spoken line:
Deliberate **stillness** is the marked exception — the 2-3 climax/breather frames you allocate in `## Video direction`. Every other frame develops; a held frame outside that allocation is just a slide.
```
Scene 1 (0.0Xs): only what the VO is saying at t=0 enters — never the whole canvas
Scene 2 (XsYs): the next piece reveals as the VO names it (a file chip / the hunk / a node / a stat)
… one window per spoken cue — as many or as few as the line calls for
Scene N (…–end): content has resolved; hold the read (stillness; subtle jitter at most)
```
- Each `Scene` line names **what's on screen**, **what moves in this window**, and **where it sits** (layout, inline). Times are real seconds across the frame's `duration`.
- **Pace reveals to the voiceover; never front-load.** This is the core anti-PowerPoint mechanism (→ `motion-language.md` Part 2 Rule 2). At t=0 show only what the VO is saying then; reveal each further piece — a line, a file chip, the hunk, a stat — **when the VO names it**, spreading reveals across the shot and especially the **back ~50%**. **The window count = the number of spoken cues the line calls for.** There is **no fixed count and no mandatory "middle" act**; the only sin is dumping everything up front.
- **End on a held read.** Once the content has resolved it holds and reads — **prefer stillness to bad motion**: no forced camera drift, no lazy breathing, no back-half pan/push; at most a subtle jitter keeps it alive (→ `motion-language.md`). Only the final frame has a real exit; every other frame's exit is the harness transition (story's `transition_in`).
- A **deliberately held** frame — content already revealed, now reading still — is legitimate and often right (a climax, a breather). The failure is never "too still"; it is **front-loaded-then-frozen**. Place held beats deliberately for rhythm (allocate them in `## Video direction`).
## Pick the shape — instantiate a blueprint
Don't invent each shot from scratch. The frame's **role** (its `type` / `beat`) points to a proven shape:
1. **Match the role to a blueprint.** Open `../hyperframes-animation/blueprints-index.md`, find the frame's role in the **role→blueprint menu**, and pick the blueprint whose intent fits this beat (story may already have named a candidate id — confirm or override it). Read that `blueprints/<id>.md`: it is a short, domain-agnostic, **time-coded shot template with `[slots]`** and a named **signature move**.
2. **Instantiate its `[slots]` with THIS frame's content** — three postures:
- **Reproduce** — the blueprint fits the beat and your content maps onto its slots cleanly. Fill every `[slot]` and follow its Scene timing.
- **Adapt** — the _structure_ fits but the content / surface doesn't. State **what you keep / what you change** in one line, then write the adapted Scene lines. You may never drop the **signature move**, and you keep the reveals **paced to the VO**.
- **Compose** — no blueprint fits the beat. Build the shot from the **motion vocabulary** in `motion-language.md`: still pace reveals to the VO. Mark it `blueprint: compose`.
3. **Keep the signature move.** Whichever posture, the blueprint's signature move is the spine of the shot — carry it through.
> A **code beat** is the one place you don't pick a blueprint for the centerpiece — the `code-*` block _is_ the shape (see **PR code beats** below). You still write the Scene sequence for the surrounding surface.
## What you add to each frame
Story-design's `## Frame N` block already carries the narrative. You append the visual layer as frame metadata + one composition note (story's role/message prose stays):
Story-design's `## Frame N` block already carries the narrative. You append the shot. Story's `scene` / `voiceover` / `transition_in` / role fields stay untouched.
```
## Frame 3How interest compounds
- scene: a snowball rolls downhill, gaining a labeled ring each turn ← refine only if it could read sharper
## Frame 4The retry fix
- scene: the request() retry hunk lands on the navy code surface ← refine only if it could read sharper
- voiceover: "…" ← story's; leave it
- transition_in: crossfade ← story's; leave it
- type: feature_showcase ← story's
- persuasion: Concretization + progressive disclosure
- beat: comprehension
- effects: scale-in, layer-reveal, count-upyou add: cite effect ids (≥3, sequenced into the phases below)
- blueprint: messaging-multi-phase ← you add (optional): one multi-phase blueprint id
- focal: the snowball ← you add: which INVENTED element is the hero
- roles: snowball = foreground subject; hill = background gradient; ring labels = supporting ← you add: each invented element's role
- sfx: whoosh-soft, tick ← you add: the sound the beat wants (fetched + mounted at root; never yours to embed)
- type: diff ← story's (PR-native)
- persuasion: Show-the-change
- beat: clarity
- blueprint: compose code beats compose the surround; the block owns the code motion
- focal: code-diff — the request() retry block, ~6 lines ← you add: the code-* block IS the focal
- roles: code surface = foreground subject · file header = supporting · dim grid = background
- sfx: keyclack-soft, soft-confirm
Entrance: the snowball seats upper-left on a dim hill gradient. Development: it rolls down across the beat, gaining one labeled ring per turn (layer-reveal) while a small total ticks up (count-up). Settle: the final ring emphasis holds; only the slow camera drift continues. A dense, left-anchored frame.
Scene 1 (0.01.0s): the navy Code Surface window seats in (scale-in + soft shadow), file header "client/request.ts" types on — Centered, ~60% of frame. Slow push-in underneath.
Scene 2 (1.03.2s): the camera settles onto the hunk; the `code-diff` block runs its own before→after on its cadence (the worker fits it to the duration) — you do not re-specify the code motion.
Scene 3 (3.24.5s): a coral underline draws on the changed line as the VO names it; a `+6/2` count-up ticks beside the header; settles and holds STILL.
```
- **`effects`** — name atomic effect **ids** from `hyperframes-animation`'s rules index. **Cite ≥3 when you name no `blueprint`** (the worker composes them into the beat; fewer than 3 reads as generic motion); 1+ as accents when a blueprint already carries the choreography. With no blueprint, those **≥3 effects are the shot's phases** — your note must **sequence them** (one enters, one develops, one emphasizes), not list them as a flat set that all fires at entry. You cite; the worker **reads the recipe body and reproduces it** (not a name-guess).
- **`blueprint`** — name **one** multi-phase blueprint id from `hyperframes-animation/blueprints-index.md` when a frame's beat wants a proven multi-phase shape. Two postures — both require the worker to read the recipe body first:
- **Reproduce** — the blueprint fits the beat cleanly and the frame's content maps onto its slots; write the composition note shot-by-shot to match.
- **Adapt** — the blueprint is the right _structure_ but the content / beat doesn't fit its exact form (or you want a fresher surface). Lead the note with a **`Base / Keep / Depart`** line — `Base:` the blueprint id · `Keep:` its **signature** move (never drop this) · `Depart:` what you change and why. Adapt may **extend or vary, never reduce below the shot model** — never flatten a multi-phase blueprint into a single entrance.
The lightweight tags:
Choose **Reproduce** when the shape fits as-is, **Adapt** when the structure fits but the form doesn't; **omit** `blueprint` entirely when none fits — then the cited `effects` (≥3) carry the phases (**Compose**).
- **`blueprint:`** — the id you instantiated (with `(Reproduce)` / `(Adapt)`), or `compose`. One id per frame.
- **`focal:`** — for a concept/mechanism beat, the **invented** hero (a hero word, a diagram, a number-lockup); for a **code beat**, the **`code-*` block** (name the block + the hunk); for the **credits** close, the avatar row.
- **`roles:`** — each element's role: `foreground subject` · `background` (full-bleed, dim 3050%) · `supporting`. Invented elements you **design**; the only real assets are the credits `assets/<login>.png` avatars (named in story's `asset_candidates`).
- **`sfx:`** — name the sound the beat wants; the audio script's `fetch-sfx` retrieves it and the assembler mounts it at root — you only **name** it, never embed `<audio>`.
- **`focal` / `roles`** — name the **invented** visual elements and their roles. `focal` is the hero element (a hero word, a diagram node, a chart series, a coined-term card). `roles` assigns each element a role: `foreground subject` (the thing the eye lands on, text laid around it), `background` (full-bleed field/gradient/grid, dim 30-50%), `supporting` (labels, secondary shapes, ambient layers). Since there are no captured assets, you are _designing_ these elements, not selecting them — keep them few and load-bearing. A user-supplied `public/<basename>` image, if any, is named in story's `asset_candidates`; treat it as the `focal` cutout or a `background`.
- **`sfx`** — name the sound the beat wants (an impact for a slam, a whoosh for a push, a tick for a count). The audio script's `fetch-sfx` pass retrieves it and the assembler mounts it at the root — you only **name** it, never embed an `<audio>` element.
- **composition note** — the frame's visual brief: layout, hero, depth layers, the macro move, **and the shot's phases**. **Default to a phased note**`entrance: … → development: … → settle: …` (mandatory when you named a `blueprint`). A **single still line** is correct only for a deliberately held climax or an allocated stillness frame. Full method → `composition.md` (layout) + `motion-language.md` (phases).
**Layout + motion are stated INLINE in each Scene line** — name the template / density / depth as part of "where it sits", and name the move from `motion-language.md`'s vocabulary; let it settle on a long-tail curve (`power3` default). Never write px / scale / ease curves / ms (the worker writes those).
## PR code beats — name a `code-*` block (the one place you don't invent)
## PR code beats — name a `code-*` block
(Frame `type` values are PR-native — `diff` / `before_after` / `mechanism` / `impact` / `credits` / … from story-design; the enrichment method below is the same regardless of type.)
For a `diff` / `before_after` / code beat, the frame's centerpiece is a **ready-made `code-*` registry block**, not an invented HTML visual — the one exception to "invent every visual." In that frame:
For a `diff` / `before_after` / code beat, the frame's centerpiece is a **ready-made `code-*` registry block**, not an invented HTML visual — the one exception to "invent every visual."
- **Name the block in `scene` + `focal`.** Pick the one that fits the beat (before→after = `code-diff`; refactor/rename = `code-morph`; new code written on = `code-typing`; spotlight a line = `code-highlight`; walk a long file = `code-scroll`; a hero reveal = `code-3d-extrude` / `code-particle-assemble`). Full map → `code-vocabulary.md`. Name the hunk too ("the `request()` retry block, ~6 lines"). The block is the `focal`; the Step-5 worker installs + fills it with the real diff.
- **`effects` choreograph the surrounding chrome, not the code motion.** The block owns the diff/typewriter/morph animation (it _is_ the development beat). Your `effects` / `blueprint` move the claude **Code Surface** around it the navy window seating in, a `+N/M` `count-up`, a coral underline drawing on. Cite 13 for that chrome; do not re-specify the code animation itself — the worker only fits the block's cadence to the frame's `data-duration` (a long snippet overruns a short frame otherwise).
Numbers (`+1,204 / 318`, files touched, perf delta) go on an `impact` / `evidence` frame as a `number-lockup` (claude's Number/Impact treatment) — name it the `focal`, with a `count-up`. The **`credits`** close uses the real `assets/<login>.png` avatars (named in story's `asset_candidates`) as the `focal` — an avatar row; the visual phase features non-empty `asset_candidates` like real assets. A **`mechanism`** frame is also invented — but an **animated diagram of the behavior**, not typography (see the next section). Every other frame (`hook` / `change` / `cta`) is invented typography/graphics per the method above.
- **The block owns the code animation; your Scenes choreograph the surrounding Code Surface.** The block _is_ the development beat (the diff/typewriter/morph plays on its own cadence — the worker only fits it to the frame's `data-duration` so a long snippet doesn't overrun). Your Scene windows move the claude **Code Surface** around it: the navy window seating in, the file header typing on, the camera settling onto the hunk, a `+N/M` `count-up`, a coral underline drawing on the landed line. Name those moves inline; **do not re-specify the code animation itself.** A code beat is usually `blueprint: compose` (the block is the shape).
## PR mechanism beats — invent an animated diagram of the behavior
A **`mechanism`** frame is the **show-the-behavior** beat — the antidote to a video that only shows code + text. Its `focal` is an **invented animated diagram** that plays out what the change _does_ at runtime (the request retrying, the cache filling, serial→parallel, the race resolved), **not** a `code-*` block and **not** a headline.
A **`mechanism`** frame is the **show-the-behavior** beat — the antidote to a video that only shows code + text. Its `focal` is an **invented animated diagram** that plays out what the change _does_ at runtime (the request retrying, the cache filling, serial→parallel, the race resolved) **not** a `code-*` block and **not** a headline.
- **Name the behavior + the diagram in `scene` + `focal`.** e.g. `scene: "animate the request lifecycle — fire → 500 → backoff (delay growing) → retry → 200, invented SVG flow"`; `focal: the request-lifecycle flow`. story-design's "what to animate, by what the change touches" table is the menu. Reach for the `flowchart` / `flowchart-vertical` / `data-chart` registry blocks where they fit (name them in `scene` like a `code-*` block so Step 5 pre-installs them); otherwise the worker builds it in SVG / HTML / GSAP from claude's atoms.
- **The build _is_ the development beat.** Unlike a code block (which owns its own animation), the diagram is yours to choreograph: cite **≥3 `effects`** (or a `blueprint`) and **sequence them into the phases** — the lanes / nodes draw on (entrance), the flow runs / the lane splits / the front advances (development), the resolved state + one coral emphasis lands (settle). This is exactly `composition.md`'s "diagram / data-viz where the build is the teaching" register — here the mechanism _is_ the teaching, so never let it enter then freeze.
- **Stay on claude's cream ground, hairline-ink.** Nodes / edges / lanes in hairline ink on cream; **one coral marker** on the active or changed element (the retry hop, the cache hit, the new lane); mono labels. Not the navy code surface (that's for code), not heavy shapes / bokeh. Plan it into the top ~83% (caption keep-out).
- **Name the behavior + the diagram in `scene` + `focal`.** e.g. `scene: "animate the request lifecycle — fire → 500 → backoff → retry → 200, invented SVG flow"`; `focal: the request-lifecycle flow`. Reach for the `flowchart` / `flowchart-vertical` / `data-chart` registry blocks where they fit (name them in `scene` so Step 5 pre-installs them); otherwise the worker builds it in SVG / HTML / GSAP from claude's atoms.
- **The build IS the shot sequence.** Unlike a code block (which owns its own animation), the diagram is yours to choreograph across the Scene windows — the lanes / nodes draw on (Scene 1), the flow runs / the lane splits / the front advances as the VO names each step (middle Scenes), the resolved state + one coral emphasis lands (final Scene). Never let it enter then freeze.
- **Stay on claude's cream ground, hairline-ink.** Nodes / edges / lanes in hairline ink on cream; **one coral marker** on the active or changed element; mono labels. Not the navy code surface (that's for code), not heavy shapes / bokeh. Plan it into the top ~83% (caption keep-out).
`focal` / `roles` name the invented diagram and its parts (the lanes = `foreground subject`; a timeline axis = `supporting`; a dim grid = `background`). A `mechanism` frame carries **no** `asset_candidates` (it's invented, like every non-credits frame).
A `mechanism` frame carries **no** `asset_candidates` (it's invented, like every non-credits frame).
## Video direction — write the invariants ONCE
## Impact & credits
The whole video shares one look and one motion grammar. State it **once**, at the top of `STORYBOARD.md` (a `## Video direction` block), so every frame inherits it and per-frame metadata carries only the **delta**:
- **Impact / evidence** — numbers (`+1,204 / 318`, files touched, perf delta) go on an `impact` frame as a **`number-lockup`** (claude's Number/Impact treatment): name it the `focal`, reveal it with a `count-up` paced to the VO.
- **Credits close** — the optional `credits` frame uses the real `assets/<login>.png` avatars (named in story's `asset_candidates`) as the `focal`: an avatar row that staggers in. This is the one frame with non-empty `asset_candidates` and real assets.
- **palette system** — from `frame.md`: which roles map to which hues. Never invent.
- **motion defaults + shot model** — default eases + the **choreography baseline** (every frame a directed shot: entrance → development → settle) + the **idle-life budget** (what may keep moving during the hold) (→ `motion-language.md`).
- **negative list** — what never appears: off-brand textures the pack forbids, **plus both motion failure modes** — slideshow (enter-then-freeze) and screensaver (everything floating independently) (→ `motion-language.md`).
- **stillness allocation** — name the 2-3 frames that hold still before a climax; every other frame develops.
## Inventing the visual (non-code beats)
Do **not** repeat these in every frame — each frame's metadata is the delta on top of Video direction.
Every non-code, non-credits beat (`hook` / `change` / `cta` / concept) is **designed**, not captured. Three first-class treatments:
- **Typographic / kinetic type** — a hero word, the PR's headline claim, a stat. Treat type as the subject: full-bleed scale, weight contrast, one emphasized term. Strongest for hooks and the cta.
- **Abstract graphics** — shapes / paths / geometry that _embody_ the idea the script names; don't decorate with generic bokeh.
- **Diagram / data-viz** — the mechanism diagrams above, a `data-chart` for a perf delta, a number-lockup. The build (each part on beat) is the teaching — design it to assemble across the Scenes.
Make the invented hero **fill 4060% of the frame** — big enough to read; don't shrink the one designed element into decoration around empty space.
## Layout — named inline per Scene
State each Scene's layout as part of "where it sits." **If the blueprint (or the code-\* block) already implies a composition, that wins** — describe it directly; the vocabulary below is for composing freely. Never write px / scale / shadow (the worker does). One frame's layout can EVOLVE across its Scenes. Use **≥3 different framings per video**; never the same framing twice in a row.
- **Framing vocabulary** — centered (hero / climax / a single code surface) · rule-of-thirds · split-screen (before/after, two surfaces) · layered-depth (immersive) · asymmetric 60/40 or 70/30 (a code surface + a caption rail) · triptych (three changes at once) · full-width strip (a file list / timeline). Let the beat decide, not a quota.
- **Density** — primary visual ≥ 40% of canvas; ≥ 3 depth layers; never a lone small cluster floating in empty space. Openings/closings are prone to emptiness — add environmental layers (a dim grid, low-opacity scanlines, brand-color ambient). Squint test: after blur you can still pick out the #1 element.
- **Hierarchy** — combine ≥ 2 of size (3:1) / weight (800 vs 400) / contrast / position (upper-third is golden) / motion, so one element clearly dominates.
- **Depth** — layer 23 of: size, blur, opacity gradient, overlap, shadow-stack, counter-scale on a push.
- **Don't show**: nav bars, footers, scrollbars, real cursors / browser chrome, generic decorative shapes, floating bokeh / purple-blue "AI" gradients (banned). The navy code surface is for code beats only; mechanism diagrams stay on cream.
## Portrait & square (non-16:9 canvases)
The zones, density, hierarchy, and depth principles all still apply; the **aspect ratio** changes, and a wide layout doesn't transplant into a tall one — design for the storyboard's `format` from the start.
- **Stack vertically, not side-by-side** — split-screen / triptych / 60-40 become top/bottom stacks. A code surface runs nearly full-width in portrait with fewer visible lines.
- **Vertical center moves with the canvas** — anchor a centered hero around **y ≈ 0.42 × height** (portrait ≈806, square ≈454), not a fixed 540.
- **Type runs larger, fewer words per line.** **Travels well to portrait:** Centered, Layered Depth, Full-Width Strip; **avoid** wide Split Screen / Triptych — use stacked equivalents.
## `## Video direction` — write the invariants ONCE
The whole video shares one look and one motion grammar. Write a **`## Video direction`** block ONCE at the top of `STORYBOARD.md` so every frame inherits it and per-frame Scene lines carry only the **delta**. This block is load-bearing — **keep it.**
- **palette system** — from `frame.md` (claude): which roles map to which hues. Never invent.
- **motion grammar + reveal model** — long-tail eases (`power3` default, smooth over bouncy) + the **VO-paced reveal** model + what may stay alive during a hold (subtle jitter at most) (→ `motion-language.md`).
- **rhythm / held-frame allocation** — name the **held / breather frames** so the video varies its energy.
- **negative list** — off-brand textures, **plus both motion failure modes** — slideshow (front-load then freeze) and screensaver (everything floating independently) (→ `motion-language.md`).
Do **not** repeat these per frame.
## Palette & type — from `frame.md`, never invented
- **Palette**`frame.md` (the adopted pack) is the color truth; apply its roles per frame. Generic basics (one accent, tint neutrals, avoid pure `#000`/`#fff`)`hyperframes-creative/references/house-style.md`.
- **Type** — fonts resolve via `frame.md`'s type tokens; reference them **by role** (display / body / mono / the pack's ramp), never by raw family or px. Typography craft (embedded fonts, dark-bg optical compensation, `tabular-nums`) → `hyperframes-creative/references/typography.md`. In a faceless explainer, **type is often the primary visual** — the hero word, the coined term, the kinetic enumeration — so lean on the type ramp hard.
- **Palette**`frame.md` (claude) is the color truth; apply its roles per frame. Generic basics → `hyperframes-creative/references/house-style.md`.
- **Type** — fonts resolve via `frame.md`'s type tokens; reference them **by role** (display / body / mono / the pack's ramp), never by raw family or px. Code surfaces and mechanism labels use the **mono** role. Typography craft → `hyperframes-creative/references/typography.md`.
## Caption-band keep-out (plan side)
The bottom ~17% of the canvas is reserved for the caption pill. Plan every frame's content into the **top ~83%** so nothing important lands in the band (the worker enforces the pixel cutoff; you plan the layout). Holds even when captions are disabled — bottom-edge consistency. Geometry detail → `composition.md`.
The bottom ~17% of the canvas is reserved for the caption pill. Plan every frame's content into the **top ~83%** (the worker enforces the pixel cutoff). When captions are enabled, primary content caps at the band top, and a centered hero anchors at **y ≈ 0.42 × height** (landscape ≈454, portrait ≈806); background / ambient layers are exempt and may stay full-bleed. Holds even when captions are disabled — bottom-edge consistency.
## Where the detail lives
| For… | Read |
| ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| composition — zones, density, templates, invented-visual prominence, caption geometry | `composition.md` (local) |
| motion — the shot model, phases, idle budget, beat structure, stillness | `motion-language.md` (local) |
| effect ids + blueprint ids (vocabulary + recipes) | `../hyperframes-animation/blueprints-index.md` + `../hyperframes-animation/rules-index.md` |
| palette + type tokens | the project's `frame.md`; basics → `hyperframes-creative` `house-style.md` / `typography.md` |
| transitions | story-design owns `transition_in`; you don't touch it |
| For… | Read |
| ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| the proven shapes + role→blueprint menu + how to pick | `../hyperframes-animation/blueprints-index.md``blueprints/<id>.md` |
| the `code-*` blocks (pick + fill for a code beat) | `code-vocabulary.md` (local) |
| motion — shot model, vocabulary, holds, idle budget, stillness, seek-safe | `motion-language.md` (local) |
| layout — framing, density, depth, hierarchy, inventing the visual, caption band | the **Layout** + **Inventing the visual** sections in this file |
| concrete eases / ms / stagger + rule recipe bodies (Step 5) | local `../hyperframes-animation/rules/` (the frame worker reads it; you don't) |
| palette + type tokens | the project's `frame.md` (claude); basics → `hyperframes-creative` |
| within-frame cuts / seams (zoom-through · cut-the-curve · waterfall) | `cut-catalog.md` (the worker builds them inside the composition) |
| transitions | story-design owns `transition_in`; you don't touch it |
## Before you finish — checklist
- Every frame has `effects` (≥1 cited id; **≥3 when no `blueprint`** is named); a `blueprint` where the frame matches one, with a shot-by-shot composition note.
- **Every frame's composition note is phased** (entrance → development → settle) — not a single entry that then freezes; the ≥3 effects are **sequenced across phases**, not all fired at t=0.
- **Stillness is only the 2-3 frames allocated in Video direction**; every other frame develops mid-shot.
- Each frame names its **invented** `focal` + per-element `roles` (foreground / background / supporting), kept few and load-bearing.
- A `mechanism` frame's `focal` is an **invented animated diagram of the behavior** (or a `flowchart` / `data-chart`), choreographed across its phases — not a code block, not typography; the body is not an unbroken run of code surfaces.
- **Video direction** stated once at the top (palette · shot model + idle budget · negative list incl. both failure modes · stillness allocation); per-frame entries are deltas.
- Content planned into the top ~83% (caption band clear).
- Palette / type pulled from `frame.md` by role — nothing invented.
- **`## Video direction`** written once at the top (palette · motion grammar + shot model + idle budget · stillness allocation · negative list incl. both failure modes); per-frame entries are deltas.
- Every frame is a **time-coded shot sequence** with real second windows across its `duration` — not a tag bag.
- **No frame front-loads** — at t=0 only what the VO is saying enters; each further piece reveals on its spoken cue, across the back ~50%. Window count follows the VO.
- Every frame names a **`blueprint:`** id (Reproduce / Adapt) or `compose`; an Adapt keeps the signature move; nothing collapses to a single front-loaded dump.
- **Code beats** name a `code-*` block as the `focal`, let the block own the code animation, and choreograph only the surrounding Code Surface in the Scenes.
- **Mechanism beats** name an **invented animated diagram of the behavior** (or a `flowchart` / `data-chart`), choreographed across the Scenes on claude's cream ground with one coral marker — not a code block, not typography; the body is not an unbroken run of code surfaces.
- **Impact** uses a `number-lockup` with a `count-up`; the **credits** close uses the real avatars as the `focal`.
- Each non-code, non-credits frame names its **invented** `focal` + per-element `roles`, kept few and load-bearing.
- Layout + motion named **inline** per Scene (no px / ease curves / ms / JS).
- Content planned into the top ~83% (caption band clear); palette / type pulled from `frame.md` by role.
- You wrote no HTML.