Composes
card-stagger-slide(the reveal — cards enter left-to-right with per-card offset)step-pan(the dwell — after reveal, the strip dwells on each card for ~1.1s, then pans ~0.4s to the next)pop-in(optional, on a label or eyebrow above the cards)
Layout
canvas: 1080x1920 (9:16 social)
eyebrow_zone: # optional
anchor: top-left
origin: { x: 60, y: 180 }
font_size: 36
font_weight: 700
card_strip_zone: anchor: vertically-center origin: { x: 0, y: 580 } # the strip extends offscreen-right card_width: 480 card_height: 760 card_gap: 36 card_radius: 36 cards_visible_at_once: 1.4 # one card centered, partial next card peeking
Each card has:
```yaml
internal_layout:
title: top-left, 44px, bold 700
big_price: bottom-left, 132px, black 900, font-variant-numeric tabular-nums
currency_prefix: bottom-left mid-baseline, 56px
suffix: bottom-right after price, 36px, bold 700
Choreography
phase_1_reveal:
start_frame: 0
per_card_stagger_frames: 6
per_card_slide_distance_pct: 0.6 # of canvas width
per_card_easing: spring(damping=14, stiffness=180, mass=0.7)
reveal_complete_frame: (cards_count - 1) * 6 + 14
phase_2_step_pan:
start_frame: phase_1_complete + 4
per_card_dwell_frames: 34 # ~1.1s
per_card_pan_frames: 12 # ~0.4s
pan_easing: bezier(0.22, 1, 0.36, 1)
total_step_pan_frames: cards_count * (dwell + pan) - last_pan
For 4 cards at 30fps: reveal phase ≈ 32 frames (~1.1s); step-pan phase ≈ 4 * 34 + 3 * 12 = 172 frames (~5.7s); total ~6.8s.
For pricing-only scenes (no step-pan, just reveal-then-hold): phase 2 is replaced with a single hold of 60+ frames.
Style pack compatibility
supported: [SP-A, SP-E]
preferred_in: [SP-A]
incompatible: [SP-C, SP-D, SP-F]
Pack-specific overrides
SP-A (marshmallow)
- Cards bg #FF7BB6 (saturated brand pink), ink #0F0F0F.
- Optional alternating card bgs: light/dark variants for tier hierarchy.
- Card tilts: none (axis-aligned).
- Currency: GBP, prices formatted to 2 decimals.
SP-E (chaos-card)
- Cards have ±5° tilt baked in.
- Each card a different saturated bg color.
- Sticker-tilt-wobble on each card after it lands.
- Music beat anchor on phase 1 start.
Duration
duration_seconds: [3.0, 8.0]
default: 3.0 # short pricing flash with 4 cards revealing
# use 6-8s for full step-pan dwell sequence
Tier input
The scene element specifies an array of cards:
{
"kind": "card-carousel",
"cards": [
{ "title": "Lightest", "price": 15.50, "suffix": "/mo" },
{ "title": "Essential", "price": 18.75, "suffix": "/mo" },
{ "title": "Original", "price": 22.00, "suffix": "/mo" },
{ "title": "Plus", "price": 28.50, "suffix": "/mo" }
]
}
The Remotion atom's existing renderCardCarousel handles this. The impl-brief atom passes through the array.
Quality Checks
- Card count between 3 and 5 (fewer than 3 isn't a carousel; more than 5 needs scrolling-index pattern instead).
- All cards have identical
card_height(visual hierarchy comes from price, not card size). - Step-pan dwell frames ≥ 30 (1s) so viewer can read each price.
- After reveal, the strip's
translateXis held for ≥ 4 frames before step-pan begins. - Big price uses
font-variant-numeric: tabular-nums(prevents width jitter in count-up scenarios).
Failure Modes
- Cards visually jump during reveal. Per-card stagger too tight or easing wrong. Increase stagger to 8 frames.
- Step-pan dwell too short — viewer can't read. Increase dwell to 40+ frames per card.
- Strip slides off-canvas without showing all cards. Card width × count exceeds strip viewport. Either reduce card width or use scrolling-index pattern.
- Price overflow on long currencies. Reduce price font-size, or pre-truncate decimals.