# 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`, 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/.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`. ## The unit is a time-coded shot sequence 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. Write each frame as a handful of windows cued by the spoken line: ``` Scene 1 (0.0–Xs): only what the VO is saying at t=0 enters — never the whole canvas Scene 2 (Xs–Ys): 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/.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 shot. Story's `scene` / `voiceover` / `transition_in` / role fields stay untouched. ``` ## Frame 4 — The 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: 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 Scene 1 (0.0–1.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.0–3.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.2–4.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. ``` The lightweight tags: - **`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 30–50%) · `supporting`. Invented elements you **design**; the only real assets are the credits `assets/.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 `