Social hook — words slam in (9:16)
A vertical hook: a 4–7 word line slams in word by word with screen shake, then resolves into a clean caption for the clip that follows.
Prompt
# Social hook — words slam in (9:16)
## Format (read first)
- 1080×1920 (9:16), 30fps, 3.0s = 90 frames
- Target: Single HTML file
## Brief
A vertical hook (3s) that stops the scroll. Line: "Nobody tells you this about money". Emphasize the word "Nobody". Must be readable with sound off.
## Look
- Palette (background, accent, text): #0a0a0a, #ff3b30, #ffffff
- Type: Condensed heavy sans, all caps, 120px/900, line-height 0.95. Center-stacked, max 2 words per line, every line fits inside the 80px side margins.
## Beats (exact timings)
1. 0.0–1.8s — Each word of the hook slams in one by one (every 0.25–0.3s): scale 1.6→1 in 140ms with ease-out-back, plus a 6px, 120ms screen shake on impact. "Nobody" is in the accent color and 15% larger.
2. 1.8–2.4s — Hold all words still for readability.
3. 2.4–3.0s — The block shrinks to 45% and moves to the top safe area (y = 220px) as a caption, ready for the clip underneath.
## Motion rules
- Keep text inside the 9:16 safe area: 120px top/bottom UI margins, 80px sides.
- Shake is translation only — never rotate text.
## Known failure modes — avoid these
- Words wrapping or overflowing the frame — lay out lines explicitly (max 2 words per line) and measure the widest line against the safe area.
## Build
- Output ONE self-contained HTML file. No CDNs, web fonts, images or scripts from URLs.
- The stage is exactly 1080×1920 and scales to fit the window (letterbox) while keeping the aspect ratio.
- Expose window.__addmotion = { width: 1080, height: 1920, fps: 30, duration: 3, seek(t) } where seek(t) renders the exact frame for time t in seconds.
- Make every frame a pure function of t: compute positions from t (with easing functions you define) rather than accumulating state. Use requestAnimationFrame only to call seek(performance.now()/1000 % duration) for live preview.
- If you use CSS @keyframes, seek(t) must set animation-play-state: paused and a negative animation-delay of -t seconds on every animated element.
- To export MP4, step seek(t) frame by frame in a headless browser, screenshot each frame, and encode with ffmpeg.
## Done when
- [ ] Full line readable for ≥ 0.6s
- [ ] Final caption sits inside the safe area
- [ ] You scrubbed the start, middle and end of every beat and nothing overlaps, clips or jumps
Before writing code, restate the beat list with frame numbers. Then build it in one pass.