dreative 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,216 +1,216 @@
1
- # Dreative Specialist Skill — Media as Motion Material
2
-
3
- Load this file when `plan.skills` includes `media`, when the plan's blueprint
4
- contains any `generate-*` media cell, or when the brief involves imagery/video
5
- on a page with motion dial ≥ 6. It EXTENDS DESIGN.md §7 (sourcing priority and
6
- bans still apply) and STACKS with motion.md (timing), 3d.md (shader craft), and
7
- cinematic.md (the living surface). This file exists because agents know how to
8
- *embed* media but not how to *use* it: award-grade sites (unseen.co family)
9
- treat images and video as the raw material the motion system acts on — media is
10
- choreographed, distorted, revealed, and lit, never just placed.
11
-
12
- ## 0. The doctrine
13
-
14
- - **No bare media at dial ≥ 7.** Every meaningful image/video gets three
15
- decisions: an **entrance** (how it arrives — reveal, wipe, materialize), an
16
- **idle** (what it does at rest — loop, drift, ken-burns, nothing-on-purpose),
17
- and a **response** (what interaction does to it — hover wake, distortion,
18
- parallax). "None" is a valid answer per axis, but it must be chosen, not
19
- defaulted.
20
- - **Media and type arrive as one beat.** Time the media entrance with the
21
- section's line-mask type reveal (motion.md §4) — image and headline landing
22
- together reads as choreography; landing separately reads as two plugins.
23
- - **One treatment vocabulary per page.** Pick 1-2 media treatments and stamp
24
- them consistently (all work cards hover-wake; all section images curtain-
25
- reveal). Five different reveal styles = assembled, not designed.
26
- - **Invent, don't pick.** §2/§3 are springboards, not a menu — the signature
27
- media treatment should be COMPOSED for this brand and this prompt using the
28
- grammar in §2.5 (a torn-paper archive scrolls differently than a liquid
29
- fashion film). Stock-vocabulary media reads as template exactly like stock
30
- layouts do; if the user's prompt describes an effect ("images tear apart on
31
- scroll"), build THAT effect for real, at the tier it needs.
32
- - **Real assets outrank generated ones** (DESIGN.md §7). Generated media fills
33
- gaps and creates atmosphere; it never replaces the client's product shots.
34
-
35
- ## 1. Production pipeline (generate → grade → compress)
36
-
37
- When the plan calls for generated media, produce it BEFORE section code
38
- (PLAN.md §4) and produce it correctly:
39
-
40
- - **Prompt for the world, grade for the page.** Prompt the brand's physical
41
- subject (DESIGN.md §7) PLUS the page's light: temperature ("warm tungsten
42
- side-light", "cool overcast daylight"), palette anchors ("deep green shadows,
43
- bone highlights"), and grain/mood. Every asset on a page shares ONE lighting
44
- logic — mismatched light is the #1 generated-media tell.
45
- - **Exact aspect ratios** from the blueprint (16:9 hero, 4:5 cards, 1:1
46
- thumbnails, 21:9 banners). Never generate square and crop-hope.
47
- - **Video loops**: 5-10s, prompt explicitly for seamlessness ("seamless loop,
48
- continuous motion, no scene change") and slow motion content (drift, steam,
49
- fabric, water — fast action never loops cleanly). Generate or extract a
50
- **poster frame** for every video (first frame, same grade).
51
- - **Image sequences** for scroll scrubs: 40-80 frames of one continuous
52
- transformation (assemble/rotate/morph). Image-to-video tools can produce
53
- these from a single generated still; export frames with ffmpeg
54
- (`ffmpeg -i loop.mp4 -vf fps=12,scale=1280:-1 frames/f_%03d.webp`).
55
- - **Compress before committing**: images → WebP/AVIF at the largest displayed
56
- size ×2 max; video → H.264 + AV1 sources, CRF ~28-32, no audio track, ≤ 2-4MB
57
- per loop (`ffmpeg -i in.mp4 -an -c:v libx264 -crf 30 -movflags +faststart`).
58
- A 40MB hero loop fails the plan even if it's beautiful.
59
- - Textures too: grain tiles, masks, mattes, displacement maps — generate them
60
- instead of hand-rolling SVG (which is banned, DESIGN.md §7).
61
-
62
- ## 2. DOM-tier treatments (no WebGL — the budget vocabulary)
63
-
64
- 80% of the effect at 5% of the cost; the right tier below dial 8 or without a
65
- canvas already on the page. All honor the floors in §5.
66
-
67
- - **Curtain / inset reveal**: media enters behind a wipe —
68
- `clip-path: inset(100% 0 0 0) → inset(0)` (or 2-4 panel slides), 900ms
69
- expo-out, triggered in-view once, synced to the headline's line-mask.
70
- - **Hover-woken loop**: still poster crossfades to its video loop on
71
- hover/focus (240ms), `video.play()` on enter, pause + reset to poster on
72
- leave. The "living thumbnail" for work/product cards. Preload `metadata`
73
- only; play() only after the crossfade starts.
74
- - **Mask-shaped video**: the loop plays inside display type
75
- (`background-clip: text` on 10vw+ headlines), an arch/circle `clip-path`, or
76
- the brand mark (SVG mask). Video becomes identity, not a rectangle. One per
77
- page.
78
- - **Floating media**: 3-5 images hovering in depth — slow damped drift
79
- (±6-10px, 4-8s loops, each phase-offset), parallax differential by depth
80
- layer, soft tinted contact shadow. The "paper sheets in space" look, pure CSS
81
- + one IntersectionObserver.
82
- - **Ken-burns**: slow scale 1.0→1.06 over 12-20s inside a fixed frame,
83
- alternate direction per instance. The refined register's entire motion budget
84
- for imagery (refined.md).
85
- - **Pixel/dither swap**: stepped `image-rendering: pixelated` downscale swap
86
- (24px mosaic → full res in 4-6 steps) on scroll progress or hover. Digital-
87
- craft registers.
88
- - **Inner-zoom parallax**: image at 115% height inside `overflow-hidden` frame,
89
- `object-position` or translateY driven by scroll (-8%→8%). Media moves, frame
90
- doesn't — never parallax the frame itself past text.
91
- - **Scroll-scrubbed `<canvas>` sequence**: preload frames (§1), draw the frame
92
- for the current damped scroll progress; sticky wrapper ≤ 2.5 viewports
93
- (motion.md §7's scrub showcase). No WebGL needed.
94
-
95
- ## 2.5 The invention grammar (compose the signature treatment)
96
-
97
- Every media effect — including any the user describes in their own words — is
98
- three choices multiplied together. Compose deliberately:
99
-
100
- - **PROPERTY** (what changes): position/scale · opacity · clip/mask SHAPE
101
- (inset, circle, arch, brand mark, jagged polygon) · slices/fragments (the
102
- media cut into strips, tiles, or shards that move independently) · UV
103
- distortion (WebGL: ripple, stretch, melt, swirl) · resolution (mosaic →
104
- sharp) · color channels (RGB split, duotone → full color) · blend/exposure
105
- (media develops like a photo print).
106
- - **DRIVER** (what pushes it): scroll progress (scrubbed) · scroll VELOCITY
107
- (agitation that decays) · in-view trigger (one-shot) · hover/focus ·
108
- cursor position within the element (directional!) · drag · hold · time.
109
- - **SHAPE OF PROGRESSION** (how it travels across the media): uniform ·
110
- directional sweep (left→right, top→bottom) · radial from a point (often the
111
- cursor's entry point) · per-slice stagger · noise-mask (organic, torn) ·
112
- along the brand mark's silhouette.
113
-
114
- The user's prompt usually fixes one or two axes; you compose the rest from the
115
- brand's world. "Tearing" = slices + jagged noise-mask edges, driven by scroll.
116
- "Video fades from left to right on hover" = opacity/mask, hover-driven,
117
- directional sweep — a gradient `mask-image` whose position animates, 400ms,
118
- from the edge the cursor entered. Name the composed treatment in the plan
119
- blueprint like a signature element, and stamp it consistently (§0).
120
-
121
- **Worked exotics (copy the construction, not the skin):**
122
-
123
- - **Paper tear on scroll**: image duplicated into 2-3 layers, each clipped by
124
- a jagged `clip-path` polygon sharing torn edges; scroll progress translates/
125
- rotates the pieces apart (±2-6°) with a hairline of background showing
126
- through; WebGL version displaces UV along a noise seam. Archive/editorial/
127
- punk registers.
128
- - **Strip-slice reveal**: media as 5-9 vertical strips (repeated
129
- `background-image` with offset `background-position`, or plane-per-strip);
130
- strips slide in staggered from alternating directions, or shear on scroll
131
- velocity. The classic award-site gallery entrance.
132
- - **Directional hover wipe**: detect the pointer's entry edge (compare
133
- enter coordinates to bounds); the reveal (opacity mask, color→duotone,
134
- still→video crossfade) sweeps FROM that edge. Feels alive because it
135
- answers the gesture's direction.
136
- - **Cursor-torch reveal**: media sits dimmed/blurred/halftoned; a radial mask
137
- tracking the (damped) cursor reveals it sharp and graded — the flashlight
138
- over an archive. Pair with a "drag to explore" label.
139
- - **Melt/liquid exit**: on section leave or route change, UV y-displacement
140
- grows by a noise column pattern — the image drips out of frame (WebGL), or
141
- budget version: per-strip translateY with eased random offsets.
142
- - **Shatter/scatter**: media as an instanced tile grid (WebGL) that explodes
143
- along scroll velocity and reassembles at rest — the §3 plane system with a
144
- per-instance offset uniform; cap tiles ≤ 400.
145
- - **Print develop**: media enters as paper-white → exposure/contrast/duotone
146
- ramps to full grade (CSS `filter` keyframes or a LUT shader), timed with
147
- the headline reveal. Photography/portfolio registers.
148
-
149
- Discipline stays the law: the composed treatment must still clear §5's floors,
150
- DESIGN.md §6's "one sentence of communication value", and one-signature-
151
- per-page — an inventive effect stamped on every image is as loud as a marquee
152
- on every section.
153
-
154
- ## 3. WebGL tier — the media plane (the unseen.co mechanic)
155
-
156
- When dial ≥ 8 or cinematic/immersive is active, media renders THROUGH the
157
- canvas so shaders can touch it. The core pattern, in order:
158
-
159
- 1. **Sync planes to DOM rects.** Real `<img>`/`<video>` elements stay in the
160
- document (a11y, SEO, layout, fallback) but render invisible
161
- (`opacity: 0`, NOT `display:none` — layout must persist). For each, a
162
- textured plane in an orthographic/fitted scene copies its
163
- `getBoundingClientRect()` every scroll/resize (rAF-batched; lerp the
164
- position for the floaty feel). Texture from `TextureLoader` or
165
- `new THREE.VideoTexture(videoEl)` (video must be `muted playsinline` and
166
- playing; `texture.colorSpace = SRGBColorSpace`).
167
- 2. **Distortion in the fragment/vertex shader**, uniforms driven by input:
168
- - *Hover ripple/lens*: damped `uMouse` + `uHoverStrength` 0→1 spring; UV
169
- displacement by radial falloff or noise.
170
- - *Velocity stretch / RGB split*: scroll or drag velocity (from Lenis /
171
- `useScroll`) → vertex bend + per-channel UV offset, hard-clamped, damped
172
- decay to zero (cinematic.md §2). Peak effect at a fast flick stays legible.
173
- - *Transition dissolve*: two textures + noise mask `mix()` for gallery/
174
- route image swaps (3d.md §4).
175
- 3. **Curved/warped planes**: subtle vertex bend (unseen's floating paper
176
- sheets) — displace z by a small curve of UV.x + scroll velocity.
177
-
178
- Rules: ONE media-plane system per page owning all planes (one scene, one rAF —
179
- never a canvas per image); planes pause syncing when off-screen; the DOM
180
- element remains the interaction target (click/focus/hover listeners on the DOM,
181
- effects on the plane); reduced-motion and WebGL-failure both fall back to
182
- simply un-hiding the DOM media (set `opacity: 1`) — this fallback must be
183
- wired, not theoretical, and it's the first thing runtime verification checks.
184
-
185
- ## 4. Choosing the treatment (the reasoning frame)
186
-
187
- Per media element, walk this in one line each — it's the media ledger the plan
188
- blueprint (PLAN.md §2) captures:
189
-
190
- 1. **Role**: hero atmosphere / product evidence / work-card / texture / story
191
- beat? Evidence media (screenshots, product photos) gets QUIET treatments —
192
- curtain reveal, ken-burns; atmosphere media can take the loud/invented ones.
193
- 2. **Tier**: does the page already pay for WebGL? If yes, media planes (§3);
194
- if no, a DOM treatment (§2) or the DOM construction of a §2.5 invention —
195
- never add a canvas only to distort one image.
196
- 3. **Entrance / idle / response** per §0, matched to register and dial.
197
- 4. **Failure path**: poster/static version named (reduced-motion, load
198
- failure, mobile tier).
199
-
200
- ## 5. Floors (every treatment, non-negotiable)
201
-
202
- - `prefers-reduced-motion`: poster frame / static image, no autoplaying loops,
203
- no scrub (static end-state). Test by toggling it.
204
- - Videos: `muted autoplay loop playsinline preload="metadata"` + `poster`;
205
- pause when off-screen (IntersectionObserver) and on `document.hidden`; no
206
- audio tracks ever on ambient loops.
207
- - Loading: explicit `width/height` or `aspect-ratio` on every media box (zero
208
- CLS); LCP image/poster preloaded, everything below the fold lazy; a media box
209
- is never blank — poster, dominant-color fill, or blur-up while loading.
210
- - Hover treatments have focus equivalents; touch gets the tap/visible-default
211
- story (DESIGN.md §13); media planes and canvases are `pointer-events-none`
212
- unless they ARE the control.
213
- - Budgets: one video loop playing per view, ≤ 2-4MB per loop, sequences ≤
214
- ~6MB total, `dpr` capped on media-plane canvases (3d.md §7).
215
- - Alt text carries voice on images; videos that convey content (not
216
- atmosphere) get a text alternative nearby.
1
+ # Dreative Specialist Skill — Media as Motion Material
2
+
3
+ Load this file when `plan.skills` includes `media`, when the plan's blueprint
4
+ contains any `generate-*` media cell, or when the brief involves imagery/video
5
+ on a page with motion dial ≥ 6. It EXTENDS DESIGN.md §7 (sourcing priority and
6
+ bans still apply) and STACKS with motion.md (timing), 3d.md (shader craft), and
7
+ cinematic.md (the living surface). This file exists because agents know how to
8
+ *embed* media but not how to *use* it: award-grade sites (unseen.co family)
9
+ treat images and video as the raw material the motion system acts on — media is
10
+ choreographed, distorted, revealed, and lit, never just placed.
11
+
12
+ ## 0. The doctrine
13
+
14
+ - **No bare media at dial ≥ 7.** Every meaningful image/video gets three
15
+ decisions: an **entrance** (how it arrives — reveal, wipe, materialize), an
16
+ **idle** (what it does at rest — loop, drift, ken-burns, nothing-on-purpose),
17
+ and a **response** (what interaction does to it — hover wake, distortion,
18
+ parallax). "None" is a valid answer per axis, but it must be chosen, not
19
+ defaulted.
20
+ - **Media and type arrive as one beat.** Time the media entrance with the
21
+ section's line-mask type reveal (motion.md §4) — image and headline landing
22
+ together reads as choreography; landing separately reads as two plugins.
23
+ - **One treatment vocabulary per page.** Pick 1-2 media treatments and stamp
24
+ them consistently (all work cards hover-wake; all section images curtain-
25
+ reveal). Five different reveal styles = assembled, not designed.
26
+ - **Invent, don't pick.** §2/§3 are springboards, not a menu — the signature
27
+ media treatment should be COMPOSED for this brand and this prompt using the
28
+ grammar in §2.5 (a torn-paper archive scrolls differently than a liquid
29
+ fashion film). Stock-vocabulary media reads as template exactly like stock
30
+ layouts do; if the user's prompt describes an effect ("images tear apart on
31
+ scroll"), build THAT effect for real, at the tier it needs.
32
+ - **Real assets outrank generated ones** (DESIGN.md §7). Generated media fills
33
+ gaps and creates atmosphere; it never replaces the client's product shots.
34
+
35
+ ## 1. Production pipeline (generate → grade → compress)
36
+
37
+ When the plan calls for generated media, produce it BEFORE section code
38
+ (PLAN.md §4) and produce it correctly:
39
+
40
+ - **Prompt for the world, grade for the page.** Prompt the brand's physical
41
+ subject (DESIGN.md §7) PLUS the page's light: temperature ("warm tungsten
42
+ side-light", "cool overcast daylight"), palette anchors ("deep green shadows,
43
+ bone highlights"), and grain/mood. Every asset on a page shares ONE lighting
44
+ logic — mismatched light is the #1 generated-media tell.
45
+ - **Exact aspect ratios** from the blueprint (16:9 hero, 4:5 cards, 1:1
46
+ thumbnails, 21:9 banners). Never generate square and crop-hope.
47
+ - **Video loops**: 5-10s, prompt explicitly for seamlessness ("seamless loop,
48
+ continuous motion, no scene change") and slow motion content (drift, steam,
49
+ fabric, water — fast action never loops cleanly). Generate or extract a
50
+ **poster frame** for every video (first frame, same grade).
51
+ - **Image sequences** for scroll scrubs: 40-80 frames of one continuous
52
+ transformation (assemble/rotate/morph). Image-to-video tools can produce
53
+ these from a single generated still; export frames with ffmpeg
54
+ (`ffmpeg -i loop.mp4 -vf fps=12,scale=1280:-1 frames/f_%03d.webp`).
55
+ - **Compress before committing**: images → WebP/AVIF at the largest displayed
56
+ size ×2 max; video → H.264 + AV1 sources, CRF ~28-32, no audio track, ≤ 2-4MB
57
+ per loop (`ffmpeg -i in.mp4 -an -c:v libx264 -crf 30 -movflags +faststart`).
58
+ A 40MB hero loop fails the plan even if it's beautiful.
59
+ - Textures too: grain tiles, masks, mattes, displacement maps — generate them
60
+ instead of hand-rolling SVG (which is banned, DESIGN.md §7).
61
+
62
+ ## 2. DOM-tier treatments (no WebGL — the budget vocabulary)
63
+
64
+ 80% of the effect at 5% of the cost; the right tier below dial 8 or without a
65
+ canvas already on the page. All honor the floors in §5.
66
+
67
+ - **Curtain / inset reveal**: media enters behind a wipe —
68
+ `clip-path: inset(100% 0 0 0) → inset(0)` (or 2-4 panel slides), 900ms
69
+ expo-out, triggered in-view once, synced to the headline's line-mask.
70
+ - **Hover-woken loop**: still poster crossfades to its video loop on
71
+ hover/focus (240ms), `video.play()` on enter, pause + reset to poster on
72
+ leave. The "living thumbnail" for work/product cards. Preload `metadata`
73
+ only; play() only after the crossfade starts.
74
+ - **Mask-shaped video**: the loop plays inside display type
75
+ (`background-clip: text` on 10vw+ headlines), an arch/circle `clip-path`, or
76
+ the brand mark (SVG mask). Video becomes identity, not a rectangle. One per
77
+ page.
78
+ - **Floating media**: 3-5 images hovering in depth — slow damped drift
79
+ (±6-10px, 4-8s loops, each phase-offset), parallax differential by depth
80
+ layer, soft tinted contact shadow. The "paper sheets in space" look, pure CSS
81
+ + one IntersectionObserver.
82
+ - **Ken-burns**: slow scale 1.0→1.06 over 12-20s inside a fixed frame,
83
+ alternate direction per instance. The refined register's entire motion budget
84
+ for imagery (refined.md).
85
+ - **Pixel/dither swap**: stepped `image-rendering: pixelated` downscale swap
86
+ (24px mosaic → full res in 4-6 steps) on scroll progress or hover. Digital-
87
+ craft registers.
88
+ - **Inner-zoom parallax**: image at 115% height inside `overflow-hidden` frame,
89
+ `object-position` or translateY driven by scroll (-8%→8%). Media moves, frame
90
+ doesn't — never parallax the frame itself past text.
91
+ - **Scroll-scrubbed `<canvas>` sequence**: preload frames (§1), draw the frame
92
+ for the current damped scroll progress; sticky wrapper ≤ 2.5 viewports
93
+ (motion.md §7's scrub showcase). No WebGL needed.
94
+
95
+ ## 2.5 The invention grammar (compose the signature treatment)
96
+
97
+ Every media effect — including any the user describes in their own words — is
98
+ three choices multiplied together. Compose deliberately:
99
+
100
+ - **PROPERTY** (what changes): position/scale · opacity · clip/mask SHAPE
101
+ (inset, circle, arch, brand mark, jagged polygon) · slices/fragments (the
102
+ media cut into strips, tiles, or shards that move independently) · UV
103
+ distortion (WebGL: ripple, stretch, melt, swirl) · resolution (mosaic →
104
+ sharp) · color channels (RGB split, duotone → full color) · blend/exposure
105
+ (media develops like a photo print).
106
+ - **DRIVER** (what pushes it): scroll progress (scrubbed) · scroll VELOCITY
107
+ (agitation that decays) · in-view trigger (one-shot) · hover/focus ·
108
+ cursor position within the element (directional!) · drag · hold · time.
109
+ - **SHAPE OF PROGRESSION** (how it travels across the media): uniform ·
110
+ directional sweep (left→right, top→bottom) · radial from a point (often the
111
+ cursor's entry point) · per-slice stagger · noise-mask (organic, torn) ·
112
+ along the brand mark's silhouette.
113
+
114
+ The user's prompt usually fixes one or two axes; you compose the rest from the
115
+ brand's world. "Tearing" = slices + jagged noise-mask edges, driven by scroll.
116
+ "Video fades from left to right on hover" = opacity/mask, hover-driven,
117
+ directional sweep — a gradient `mask-image` whose position animates, 400ms,
118
+ from the edge the cursor entered. Name the composed treatment in the plan
119
+ blueprint like a signature element, and stamp it consistently (§0).
120
+
121
+ **Worked exotics (copy the construction, not the skin):**
122
+
123
+ - **Paper tear on scroll**: image duplicated into 2-3 layers, each clipped by
124
+ a jagged `clip-path` polygon sharing torn edges; scroll progress translates/
125
+ rotates the pieces apart (±2-6°) with a hairline of background showing
126
+ through; WebGL version displaces UV along a noise seam. Archive/editorial/
127
+ punk registers.
128
+ - **Strip-slice reveal**: media as 5-9 vertical strips (repeated
129
+ `background-image` with offset `background-position`, or plane-per-strip);
130
+ strips slide in staggered from alternating directions, or shear on scroll
131
+ velocity. The classic award-site gallery entrance.
132
+ - **Directional hover wipe**: detect the pointer's entry edge (compare
133
+ enter coordinates to bounds); the reveal (opacity mask, color→duotone,
134
+ still→video crossfade) sweeps FROM that edge. Feels alive because it
135
+ answers the gesture's direction.
136
+ - **Cursor-torch reveal**: media sits dimmed/blurred/halftoned; a radial mask
137
+ tracking the (damped) cursor reveals it sharp and graded — the flashlight
138
+ over an archive. Pair with a "drag to explore" label.
139
+ - **Melt/liquid exit**: on section leave or route change, UV y-displacement
140
+ grows by a noise column pattern — the image drips out of frame (WebGL), or
141
+ budget version: per-strip translateY with eased random offsets.
142
+ - **Shatter/scatter**: media as an instanced tile grid (WebGL) that explodes
143
+ along scroll velocity and reassembles at rest — the §3 plane system with a
144
+ per-instance offset uniform; cap tiles ≤ 400.
145
+ - **Print develop**: media enters as paper-white → exposure/contrast/duotone
146
+ ramps to full grade (CSS `filter` keyframes or a LUT shader), timed with
147
+ the headline reveal. Photography/portfolio registers.
148
+
149
+ Discipline stays the law: the composed treatment must still clear §5's floors,
150
+ DESIGN.md §6's "one sentence of communication value", and one-signature-
151
+ per-page — an inventive effect stamped on every image is as loud as a marquee
152
+ on every section.
153
+
154
+ ## 3. WebGL tier — the media plane (the unseen.co mechanic)
155
+
156
+ When dial ≥ 8 or cinematic/immersive is active, media renders THROUGH the
157
+ canvas so shaders can touch it. The core pattern, in order:
158
+
159
+ 1. **Sync planes to DOM rects.** Real `<img>`/`<video>` elements stay in the
160
+ document (a11y, SEO, layout, fallback) but render invisible
161
+ (`opacity: 0`, NOT `display:none` — layout must persist). For each, a
162
+ textured plane in an orthographic/fitted scene copies its
163
+ `getBoundingClientRect()` every scroll/resize (rAF-batched; lerp the
164
+ position for the floaty feel). Texture from `TextureLoader` or
165
+ `new THREE.VideoTexture(videoEl)` (video must be `muted playsinline` and
166
+ playing; `texture.colorSpace = SRGBColorSpace`).
167
+ 2. **Distortion in the fragment/vertex shader**, uniforms driven by input:
168
+ - *Hover ripple/lens*: damped `uMouse` + `uHoverStrength` 0→1 spring; UV
169
+ displacement by radial falloff or noise.
170
+ - *Velocity stretch / RGB split*: scroll or drag velocity (from Lenis /
171
+ `useScroll`) → vertex bend + per-channel UV offset, hard-clamped, damped
172
+ decay to zero (cinematic.md §2). Peak effect at a fast flick stays legible.
173
+ - *Transition dissolve*: two textures + noise mask `mix()` for gallery/
174
+ route image swaps (3d.md §4).
175
+ 3. **Curved/warped planes**: subtle vertex bend (unseen's floating paper
176
+ sheets) — displace z by a small curve of UV.x + scroll velocity.
177
+
178
+ Rules: ONE media-plane system per page owning all planes (one scene, one rAF —
179
+ never a canvas per image); planes pause syncing when off-screen; the DOM
180
+ element remains the interaction target (click/focus/hover listeners on the DOM,
181
+ effects on the plane); reduced-motion and WebGL-failure both fall back to
182
+ simply un-hiding the DOM media (set `opacity: 1`) — this fallback must be
183
+ wired, not theoretical, and it's the first thing runtime verification checks.
184
+
185
+ ## 4. Choosing the treatment (the reasoning frame)
186
+
187
+ Per media element, walk this in one line each — it's the media ledger the plan
188
+ blueprint (PLAN.md §2) captures:
189
+
190
+ 1. **Role**: hero atmosphere / product evidence / work-card / texture / story
191
+ beat? Evidence media (screenshots, product photos) gets QUIET treatments —
192
+ curtain reveal, ken-burns; atmosphere media can take the loud/invented ones.
193
+ 2. **Tier**: does the page already pay for WebGL? If yes, media planes (§3);
194
+ if no, a DOM treatment (§2) or the DOM construction of a §2.5 invention —
195
+ never add a canvas only to distort one image.
196
+ 3. **Entrance / idle / response** per §0, matched to register and dial.
197
+ 4. **Failure path**: poster/static version named (reduced-motion, load
198
+ failure, mobile tier).
199
+
200
+ ## 5. Floors (every treatment, non-negotiable)
201
+
202
+ - `prefers-reduced-motion`: poster frame / static image, no autoplaying loops,
203
+ no scrub (static end-state). Test by toggling it.
204
+ - Videos: `muted autoplay loop playsinline preload="metadata"` + `poster`;
205
+ pause when off-screen (IntersectionObserver) and on `document.hidden`; no
206
+ audio tracks ever on ambient loops.
207
+ - Loading: explicit `width/height` or `aspect-ratio` on every media box (zero
208
+ CLS); LCP image/poster preloaded, everything below the fold lazy; a media box
209
+ is never blank — poster, dominant-color fill, or blur-up while loading.
210
+ - Hover treatments have focus equivalents; touch gets the tap/visible-default
211
+ story (DESIGN.md §13); media planes and canvases are `pointer-events-none`
212
+ unless they ARE the control.
213
+ - Budgets: one video loop playing per view, ≤ 2-4MB per loop, sequences ≤
214
+ ~6MB total, `dpr` capped on media-plane canvases (3d.md §7).
215
+ - Alt text carries voice on images; videos that convey content (not
216
+ atmosphere) get a text alternative nearby.