# 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. (Learned 2026-06-10: the until-now inverse zooms shipped at 20px and read mushy; 10px keeps letterforms readable mid-cut.) |
| Full-frame surface (terminal window, card, screenshot) | **18–20px** | 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

Shipped examples: `boring → until-now` and `B3 → "That changes today."` (sfx-music-launch);
seams 3/4 in claude-paper (`follow-up → thinking`, `thinking → compose UI`).

---

## 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 ~25–30% of its travel (fade duration
≈ 0.18–0.3s vs motion 0.3–0.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.2–0.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 ~25–30% 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.

Shipped example: `until-now.html` B1→B2→B3 (sfx-music-launch).

---

## 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, 18–20px 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                           |
