Motion · Transição
Hidden-cut transitions
Three hidden-cut transitions — foreground occlusion invisible cut, versus-slam opener, and warm light-leak burn; the hard cut hides inside 1-3 frames of occlusion/impact/light peak, so the viewer never sees the scissors.
Fonte: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composição Remotion (React/TSX) — vídeo de prévia, sem player no navegador
Variantes
一张超画幅卡片带重运动模糊贴脸横扫,糊满全屏的遮挡帧内背景 A→B 硬切,卡片飞出观众以为还是同一镜
想让换景完全无痕的页面→页面;伪"一条 take"的主力款
左右两半屏带斜切边从画外加速对冲撞合,撞击帧白闪+震屏+VS 盖章,切点就是撞击本身
对比/对阵语义的开屏或章节头(新旧方案、双产品、before/after)
三团琥珀柔光沿对角线斜扫,光峰帧吞掉旧页约七成时硬切新页,光退散时新页已就位
想要暖调/胶片味的章节过渡;比白闪柔、有方向、有温度
When to use
When two shots need a "seamless scene change" or a "ceremonial reveal" at the join (a technique card, chosen from the same tier as the six shot-transitions moves)
Intention
Same family as shot-transitions style A, flash-cut: flash-cut covers a hard cut with one layer of white flash; these three swap in three different "decoys" — a sweeping foreground card (A), a collision-impact frame (B), a light leak climbing to its peak (C). The shared principle is magician's misdirection: the viewer's eyes get pulled away by a big motion/bright flash for those 1-3 frames while the hard cut happens, and when they refocus the scene has already changed, all "without ever seeing the scissors." flash-cut is the plainest version; pick A for a sense of direction, B for ceremony, C for warmth.
Duration & energy
- Duration: n/a (a technique card; each style's frame count is in its parameter table, drawn from the neighboring shots' budget)
- Energy: n/a (a technique card, doesn't occupy an energy slot)
Shot recipe (original Chinese — timing, easing, parameter table, known pitfalls)
意图
与 shot-transitions A 式 flash-cut 同族:flash-cut 用一层白闪盖住硬切, 这三式换了三种"障眼物"——扫过的前景卡片(A)、对撞的撞击帧(B)、 爬到顶峰的漏光(C)。共同原理是魔术误导:观众的眼睛被大动作/强光 吸走的那 1-3 帧里完成硬切,回过神来景已经换了,全程"看不见剪刀"。 flash-cut 是最素的一款;要方向感选 A,要仪式感选 B,要温度选 C。
三式选型
| 式 | 做法 | 适用接缝 | |----|------|----------| | A invisible-cut 前景遮挡隐形切 | 一张超画幅卡片带重运动模糊贴脸横扫,糊满全屏的遮挡帧内背景 A→B 硬切,卡片飞出观众以为还是同一镜 | 想让换景完全无痕的页面→页面;伪"一条 take"的主力款 | | B versus-slam 对撞开屏 | 左右两半屏带斜切边从画外加速对冲撞合,撞击帧白闪+震屏+VS 盖章,切点就是撞击本身 | 对比/对阵语义的开屏或章节头(新旧方案、双产品、before/after)| | C light-leak-burn 暖色漏光 | 三团暖色柔光沿对角线斜扫,光峰帧吞掉旧页约七成时硬切新页,光退散时新页已就位 | 想要暖调/胶片味的章节过渡;比白闪柔、有方向、有温度 |
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | A 横扫 | 卡片 1600x1000 再 scale(1.6),x 从 -2200→2600 共 14f,bezier(0.3,0,0.7,1);切点在扫掠中点全遮帧(demo 里 f47) | 中点必须覆盖 -280..2280 糊满 1920 画幅;两端完全出画 | | A 糊感 | 全场包 <CameraMotionBlur shutterAngle={300} samples={12}> + 卡片自身 blur(8px)/skewX(-v·0.018°) + 4 层手动残影(opacity 0.35→0.07、blur 14px、间隔 0.55f) | 三层糊叠出"贴脸呼啸";只靠 CameraMotionBlur 糊不满遮挡窗口 | | A 带风推挤 | 背景 ±40px:切前 A 被拖向扫掠方向(ease-in),切后 B 从反侧 13f 回稳(ease-out) | 卖"同一镜被风带了一下"的错觉,是无痕感的一半功劳 | | B 斜缝几何 | clip-path polygon 斜边约 78°(缝顶 x=1075→缝底 x=845);两半屏从 ±1200px 以 ease-in(cubic) 10f 对冲到位 | ease-in 加速才有"砸"感;垂直缝读作 PPT 分屏 | | B 撞击三件套 | 撞击帧同发:白闪 0.9→0 共 3f + 整机 shake 12px·e^(−t/1.6) 约 5f 收干 + VS 字块 scale 1.6→1 back(2.6) overshoot 6f 压出 | 三件必须同帧起跑,错开一帧就散;震屏幅度须过肉眼阈值(可感性判例)| | C 光层 | 3 团径向渐变(#f6c878/#e8a44a/#d98a2b,直径 1500/1950/2400px,blur 90px,mixBlendMode screen)沿右上→左下扫约 70f | 光团偏移沿扫掠方向拖尾排布,避免完美同心的"手电筒感" | | C 光强包络 | ease-in 爬升 27f(蓄力)→ 峰值帧藏切 → ease-out 收敛 43f(余温);峰值叠 8% 全屏暖罩 + 近白热核(opacity=intensity²) | 爬升慢收敛快会读作故障闪光;热核只在临近峰值烧起来 | | C 冲淡 | 峰值时页面 filter:contrast 降 45%、brightness 抬 35% | 这是"烧穿"感的来源——光只叠不冲淡,读作贴了张贴纸 |
已知坑
- demo 在灰阶/占位素材上调校通过——参数是调校起点非实战定稿,
首次实战须以真实素材回验
- C 式是 Q4 光效判例解封后首个入库光效手法——严守 Q4 三约束:
单点使用(一支片一处,不群发)、正常速度回放自检好看才算过、 光效服务切点而非装饰;与 flash-cut 同片混用算两次光效藏切
- A 式遮挡物必须真的全遮:demo 里 1600x1000 的卡要 scale(1.6) 才盖满
1920 画幅——换素材尺寸后先逐帧检查切点帧有没有露出背景边缘, 露一条缝整个魔术就穿帮
- B 式 VS 字块语义仅适配"对比/对阵"内容——没有两方对峙关系时
别当通用转场用;去掉 VS 只留对撞则退化为一款斜缝 wipe,语义要求同降
- 转场不叠加(规矩同 shot-transitions):一个接缝只用一式;
这三式与六式同层选型,同片同类接缝别既用 flash-cut 又用 C 式
- 分镜表阶段就逐接缝标注(写进"关键动效"列),帧预算从相邻镜头划(R3)
参考实现
demos/transition/transition-hidden-cut/ (InvisibleCut.tsx / LightLeakBurn.tsx / VersusSlam.tsx)
Reference implementation (Remotion)
- demos/transition/transition-hidden-cut/InvisibleCut.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/transition-hidden-cut/InvisibleCut.tsx
- demos/transition/transition-hidden-cut/LightLeakBurn.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/transition-hidden-cut/LightLeakBurn.tsx
- demos/transition/transition-hidden-cut/VersusSlam.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/transition/transition-hidden-cut/VersusSlam.tsx
The component imports shared fixtures from demos/_fixtures and helpers from assets/lib in the repo; SKILL.md there documents the render workflow.
Tudo nesta página também é legível por máquina. Aponte seu agente para o JSON ou adicione o servidor MCP do gimgs e peça para buscar templates de motion.
- Baixe o JSON — contém a receita do plano (timing, easing, tabela de parâmetros, armadilhas) e o código TSX completo.
- Clone o repositório video-shotcraft; o componente importa fixtures de demos/_fixtures e helpers de assets/lib.
- Registre o componente como <Composition> em um projeto Remotion e renderize com `npx remotion render`, ou reimplemente a receita no seu próprio engine.
curl -s https://gimgs.net/motion/transition-hidden-cut.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/transition/transition-hidden-cut/InvisibleCut.tsx, demos/transition/transition-hidden-cut/LightLeakBurn.tsx, demos/transition/transition-hidden-cut/VersusSlam.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("transition-hidden-cut")Prompt
Use the video-shotcraft shot recipe "Hidden-cut transitions" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/transition-hidden-cut.json Raw TSX: https://gimgs.net/motion/transition-hidden-cut/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/transition/transition-hidden-cut/InvisibleCut.tsx, demos/transition/transition-hidden-cut/LightLeakBurn.tsx, demos/transition/transition-hidden-cut/VersusSlam.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 4.05s 1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls). 2. Adapt it for: <describe what you want>. 3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).
// ===== demos/transition/transition-hidden-cut/InvisibleCut.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { CameraMotionBlur } from '@remotion/motion-blur';
import { FakeDashboard, Card, G } from '../../_fixtures/Fixtures';
// invisible-cut:前景遮挡隐形切——一张放大到超出画幅的卡片带重运动模糊
// 从左侧贴脸扫过,糊满屏幕的瞬间背景从 A 无痕换成 B,卡片飞出右侧时
// 观众以为还是同一镜。(invisible-cut + foreground-occlusion-swipe)
const SW_START = 40; // 卡片入场
const SW_END = 54; // 卡片出场(14f 横扫)
const CUT = 47; // 硬切点:扫掠中点,卡片完全糊满画面的那一帧
// 卡片中扫掠曲线上的水平位置(容器 left,未缩放坐标)
const xAt = (f: number) =>
// 卡片 scale(1.6) 后半宽 1280:起点右缘 -120 / 终点左缘 2120,均完全出画;
// 中点 f47 覆盖 -280..2280,糊满整个 1920 画幅
interpolate(f, [SW_START, SW_END], [-2200, 2600], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.3, 0, 0.7, 1),
});
const Scene: React.FC = () => {
const frame = useCurrentFrame();
const x = xAt(frame);
// 瞬时速度(px/帧),驱动斜切与残影强度
const v = xAt(frame + 0.5) - xAt(frame - 0.5);
const sweeping = frame > SW_START - 2 && frame < SW_END + 3;
// 背景被"带风"轻推:A 被拖向左,切成 B 后从右侧回稳——卖同一镜错觉
const shove =
frame < CUT
? interpolate(frame, [SW_START, CUT], [0, -40], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.quad),
})
: interpolate(frame, [CUT, CUT + 13], [40, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
{/* 背景层:硬切藏在遮挡帧内 */}
<div style={{ position: 'absolute', inset: 0, transform: `translateX(${shove}px)` }}>
{frame < CUT ? <FakeDashboard variant="A" /> : <FakeDashboard variant="B" />}
</div>
{/* 手动残影:4 层拖尾(在主卡身后),保证遮挡窗口糊满全屏 */}
{sweeping &&
[4, 3, 2, 1].map((i) => (
<div
key={i}
style={{
position: 'absolute',
left: xAt(frame - i * 0.55),
top: 40,
width: 1600,
height: 1000,
transform: 'scale(1.6)',
opacity: [0, 0.35, 0.22, 0.13, 0.07][i],
filter: 'blur(14px)',
}}
>
<Card w={1600} h={1000} seed={9} style={{ width: '100%', height: '100%' }} />
</div>
))}
{/* 主卡:1600x1000 放大 1.6 倍(2560x1600 超出画幅),自带 blur 加强糊感 */}
{sweeping && (
<div
style={{
position: 'absolute',
left: x,
top: 40,
width: 1600,
height: 1000,
transform: `scale(1.6) skewX(${-v * 0.018}deg)`,
filter: 'blur(8px)',
boxShadow: '0 24px 80px rgba(0,0,0,0.3)',
borderRadius: 20,
}}
>
<Card w={1600} h={1000} seed={9} style={{ width: '100%', height: '100%' }} />
</div>
)}
</AbsoluteFill>
);
};
export const InvisibleCut: React.FC = () => (
<CameraMotionBlur shutterAngle={300} samples={12}>
<Scene />
</CameraMotionBlur>
);
// ===== demos/transition/transition-hidden-cut/LightLeakBurn.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, TitleBlock, G } from '../../_fixtures/Fixtures';
// light-leak-burn〔转场〕:一团琥珀橙柔光从右上角斜扫入画,亮度顶峰时
// 吞掉旧画面约七成(高光溢出、对比度被冲淡),光峰帧后切新页藏切点,
// 光退散时新页已在光下就位——比白闪柔、有方向、有温度。
// 节拍:0–25 建立旧页 hold;25–52 光斜扫入、爬向峰值;52 峰值帧切页;
// 52–95 光沿对角线退出、强度衰减;95–130 新页静止 hold。
const PEAK = 52; // 光峰帧 = 藏切点
export const LightLeakBurn: React.FC = () => {
const frame = useCurrentFrame();
// 光团沿对角线的位移进度:右上外 → 左下外,贯穿 25–95
const sweep = interpolate(frame, [25, 95], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.bezier(0.4, 0, 0.3, 1),
});
// 光强包络:25–PEAK 爬升(ease-in 有蓄力感),PEAK–95 收敛(ease-out 余温)
const rise = interpolate(frame, [25, PEAK], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.quad),
});
const fall = interpolate(frame, [PEAK, 95], [1, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.cubic),
});
const intensity = frame <= PEAK ? rise : fall;
// 光团中心:从画面右上外侧斜扫到左下外侧(有方向的漏光)
const cx = interpolate(sweep, [0, 1], [2350, -650]);
const cy = interpolate(sweep, [0, 1], [-500, 1500]);
// 三团琥珀光的偏移与直径(seed 正弦哈希做微差,避免完美同心)
const blobs = [0, 1, 2].map((i) => {
const j1 = (Math.sin(i * 127.3) * 43758) % 1; // -1..1 小数
const j2 = (Math.sin(i * 311.7) * 27183) % 1;
return {
dx: i * 260 - 260 + j1 * 90, // 沿扫掠方向拖尾排布
dy: i * 200 - 200 + j2 * 70,
d: 1500 + i * 450, // 直径 1500 / 1950 / 2400
color: ['#f6c878', '#e8a44a', '#d98a2b'][i],
alpha: [0.95, 0.8, 0.55][i],
};
});
// 峰值时页面被光冲淡:对比度降、亮度抬(旧页被"烧穿"的感觉)
const pageFilter = `contrast(${1 - intensity * 0.45}) brightness(${1 + intensity * 0.35})`;
// 全屏暖色罩:峰值时约 8%
const warmWash = intensity * 0.08;
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
{/* 页面层:光峰帧前是旧页(网格),之后是新页(列表+标题)——切点藏在最亮处 */}
<AbsoluteFill style={{ filter: pageFilter }}>
{frame <= PEAK ? (
<FakeDashboard variant="A" />
) : (
<>
<FakeDashboard variant="B" />
<div style={{ position: 'absolute', left: 288, top: 132 }}>
<TitleBlock text="Next Page" size={64} />
</div>
</>
)}
</AbsoluteFill>
{/* 全屏暖色罩:峰值 8%,让高光溢出带温度 */}
<AbsoluteFill
style={{
background: '#e8a44a',
opacity: warmWash,
mixBlendMode: 'screen',
pointerEvents: 'none',
}}
/>
{/* 琥珀漏光层:3 团大直径径向渐变,blur 90px,screen 叠加,沿对角线扫过 */}
<AbsoluteFill style={{ pointerEvents: 'none' }}>
{blobs.map((b, i) => (
<div
key={i}
style={{
position: 'absolute',
left: cx + b.dx - b.d / 2,
top: cy + b.dy - b.d / 2,
width: b.d,
height: b.d,
borderRadius: '50%',
background: `radial-gradient(circle, ${b.color} 0%, ${b.color}cc 30%, transparent 68%)`,
filter: 'blur(90px)',
mixBlendMode: 'screen',
opacity: b.alpha * intensity,
}}
/>
))}
{/* 峰值核心过曝:光心一小团接近白的热核,峰值帧吞掉约七成画面 */}
<div
style={{
position: 'absolute',
left: cx - 550,
top: cy - 550,
width: 1100,
height: 1100,
borderRadius: '50%',
background: 'radial-gradient(circle, #fff3dd 0%, #f6c878aa 45%, transparent 70%)',
filter: 'blur(80px)',
mixBlendMode: 'screen',
opacity: intensity * intensity, // 只在临近峰值时才烧起来
}}
/>
</AbsoluteFill>
</AbsoluteFill>
);
};
// ===== demos/transition/transition-hidden-cut/VersusSlam.tsx =====
import React from 'react';
import { AbsoluteFill, Easing, interpolate, useCurrentFrame } from 'remotion';
import { FakeDashboard, TitleBlock, G } from '../../_fixtures/Fixtures';
// versus-slam 对撞开屏:左右两个半屏画面(带 78° 斜切边)从画外加速对冲,
// 沿斜缝砰地撞合;撞击帧白闪 + 整机震屏指数衰减 + "VS" 字块盖章压出,结尾静止 hold。
const IMPACT = 30; // 撞击帧(前 20f 建立 hold + 10f ease-in 对冲)
// 斜缝几何:78° 斜边 → 1080 高度上水平偏移 1080/tan(78°) ≈ 230px,中线 x=960 ±115
const SEAM_TOP_X = 1075; // 缝顶端 x
const SEAM_BOT_X = 845; // 缝底端 x
// CSS 旋转顺时针为正:缝顶端偏右(1075 > 845)→ 正角度 ≈ +12°
const SEAM_DEG = (Math.atan2(SEAM_TOP_X - SEAM_BOT_X, 1080) * 180) / Math.PI;
export const VersusSlam: React.FC = () => {
const frame = useCurrentFrame();
// 两半屏对冲:ease-in 加速,10f 从 ±1200px 冲到位
const leftX = interpolate(frame, [20, IMPACT], [-1200, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.in(Easing.cubic),
});
const rightX = -leftX;
// 撞击帧起:整机震屏 12px 指数衰减(约 5f 收干)
const since = frame - IMPACT;
const env = since >= 0 ? 12 * Math.exp(-since / 1.6) : 0;
const shakeX = env * Math.sin(since * 3.4);
const shakeY = env * 0.6 * Math.sin(since * 4.1 + 0.7);
// 白闪:撞击帧 0.9 → 0,3f 收掉
const flash = interpolate(frame, [IMPACT, IMPACT + 3], [0.9, 0], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
// "VS" 盖章:scale 1.6 → 1 带 back overshoot,6f 压出
const vsScale = interpolate(frame, [IMPACT, IMPACT + 6], [1.6, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
easing: Easing.out(Easing.back(2.6)),
});
const vsOpacity = interpolate(frame, [IMPACT, IMPACT + 2], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
const impacted = frame >= IMPACT;
return (
<AbsoluteFill style={{ background: G.bg, overflow: 'hidden' }}>
<div style={{ position: 'absolute', inset: 0, transform: `translate(${shakeX}px, ${shakeY}px)` }}>
{/* 建立段的斜缝虚线预示(撞合后被实缝替代) */}
{!impacted && (
<svg width={1920} height={1080} style={{ position: 'absolute', inset: 0 }}>
<line
x1={SEAM_TOP_X} y1={0} x2={SEAM_BOT_X} y2={1080}
stroke={G.bar} strokeWidth={4} strokeDasharray="18 16"
/>
</svg>
)}
{/* 左半屏:FakeDashboard A 裁左半,斜边 78° */}
<div style={{
position: 'absolute', inset: 0,
transform: `translateX(${leftX}px)`,
clipPath: `polygon(0px 0px, ${SEAM_TOP_X}px 0px, ${SEAM_BOT_X}px 1080px, 0px 1080px)`,
}}>
<FakeDashboard variant="A" />
</div>
{/* 右半屏:FakeDashboard B 裁右半 */}
<div style={{
position: 'absolute', inset: 0,
transform: `translateX(${rightX}px)`,
clipPath: `polygon(${SEAM_TOP_X}px 0px, 1920px 0px, 1920px 1080px, ${SEAM_BOT_X}px 1080px)`,
}}>
<FakeDashboard variant="B" />
</div>
{/* 撞合后的实体斜缝条 */}
{impacted && (
<div style={{
position: 'absolute', left: 960 - 6, top: 540 - 700,
width: 12, height: 1400, background: G.ink,
transform: `rotate(${SEAM_DEG}deg)`,
}} />
)}
{/* "VS" 字块盖章:贴缝、随缝倾斜 */}
{impacted && (
<div style={{
position: 'absolute', left: 960, top: 540,
transform: `translate(-50%, -50%) rotate(${SEAM_DEG}deg) scale(${vsScale})`,
opacity: vsOpacity,
background: G.card, border: `6px solid ${G.ink}`, borderRadius: 20,
padding: '18px 46px', boxShadow: '0 18px 60px rgba(0,0,0,0.35)',
}}>
<TitleBlock text="VS" size={140} />
</div>
)}
</div>
{/* 撞击白闪(不随震屏位移) */}
<AbsoluteFill style={{ background: '#ffffff', opacity: flash, pointerEvents: 'none' }} />
</AbsoluteFill>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/transition/transition-hidden-cut/InvisibleCut.tsx, demos/transition/transition-hidden-cut/LightLeakBurn.tsx, demos/transition/transition-hidden-cut/VersusSlam.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
Mais nesta categoria

White flash: logo simplifies
A colorful liquid-gradient wordmark holds with flowing light, then the frame flashes white and overexposes; a flat wordmark fades in on white and settles — one flash completes the downgrade to simplicity.

Geometric wipe transitions
Two geometric wipe transitions — clock-wipe, a radar-hand sweep changing the page, and blinds-slice, 12 vertical strips flipping in staggered waves.

Travel-through transitions
Two travel-through transitions — shared-element homecoming and letter-cavity pass-through; the camera drills through a real element inside the frame to complete the scene change.

Glitch-tear strip cut
A tearing transition — glitch-displace noise tear (16 horizontal strips jitter out of alignment, cut mid-jitter), a strip-level tear with digital-glitch meaning.