luckiest-co 1.0.13 → 1.0.14

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.
Files changed (30) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/package.json +1 -1
  4. package/skills/luckiest-design-website/ATTRIBUTION.md +38 -0
  5. package/skills/luckiest-design-website/CHANGELOG.md +104 -0
  6. package/skills/luckiest-design-website/LICENSE +21 -0
  7. package/skills/luckiest-design-website/SKILL.md +372 -0
  8. package/skills/luckiest-design-website/UPSTREAM-CHANGELOG.md +526 -0
  9. package/skills/luckiest-design-website/engine/scrollcraft.css +432 -0
  10. package/skills/luckiest-design-website/engine/scrollcraft.js +1167 -0
  11. package/skills/luckiest-design-website/evals/evals.json +33 -0
  12. package/skills/luckiest-design-website/references/assets.md +286 -0
  13. package/skills/luckiest-design-website/references/device-diag.html +214 -0
  14. package/skills/luckiest-design-website/references/devices.md +466 -0
  15. package/skills/luckiest-design-website/references/feel.md +277 -0
  16. package/skills/luckiest-design-website/references/registry-examples.md +305 -0
  17. package/skills/luckiest-design-website/references/taste.md +304 -0
  18. package/skills/luckiest-design-website/references/template.html +138 -0
  19. package/skills/luckiest-design-website/references/uniqueness.md +480 -0
  20. package/skills/luckiest-design-website/references/verify.md +381 -0
  21. package/skills/luckiest-design-website/references/worldflight.md +349 -0
  22. package/skills/luckiest-design-website/references/worlds.md +178 -0
  23. package/skills/luckiest-design-website/scripts/doctor.mjs +177 -0
  24. package/skills/luckiest-design-website/scripts/encode.sh +80 -0
  25. package/skills/luckiest-design-website/scripts/kie.mjs +202 -0
  26. package/skills/luckiest-design-website/scripts/serve.mjs +52 -0
  27. package/skills/luckiest-design-website/scripts/shoot.mjs +644 -0
  28. package/skills/luckiest-design-website/scripts/workspace.mjs +106 -0
  29. package/skills/luckiest-design-website/scripts/worldflight-assert.mjs +273 -0
  30. package/skills/luckiest-design-website/templates/FINGERPRINTS.md +65 -0
@@ -0,0 +1,432 @@
1
+ /* ============================================================================
2
+ scrollcraft.css: the taste floor
3
+ ----------------------------------------------------------------------------
4
+ Two layers, and the split matters:
5
+
6
+ TOKENS what you override per brand. Colour roles, the type ramp, the
7
+ spacing scale, elevation, motion. A brand is ~12 values.
8
+ DEVICES what the engine drives. Do not restyle these to taste; they are
9
+ the mechanism. Style your own markup instead.
10
+
11
+ Nothing here draws a "component". There are no card, badge, or pill classes,
12
+ because a stylesheet that ships those is how every page built on it ends up
13
+ with the same shapes. You get a floor and a vocabulary; the composition is
14
+ yours.
15
+ ========================================================================== */
16
+
17
+ /* ---------------------------------------------------------------- tokens -- */
18
+ :root {
19
+ /* --- colour roles. Override these six and the page is rebranded. -------- */
20
+ --sc-canvas: #08090b; /* the page ground. Drift interpolates this. */
21
+ --sc-surface: #101217; /* anything raised off the ground */
22
+ --sc-ink: #f4f2ef; /* primary text */
23
+ --sc-ink-soft: #9a9ba1; /* secondary text. Tinted, never flat gray. */
24
+ --sc-accent: #d8ff3e; /* ONE accent. It owns a region, not confetti. */
25
+ --sc-accent-ink: #08090b; /* text that sits on the accent */
26
+
27
+ --sc-hairline: color-mix(in oklab, var(--sc-ink) 12%, transparent);
28
+ --sc-hairline-strong: color-mix(in oklab, var(--sc-ink) 22%, transparent);
29
+
30
+ /* --- type. Two families max. Display carries voice, text carries prose. - */
31
+ --sc-font-display: "Instrument Sans", "Geist", system-ui, sans-serif;
32
+ --sc-font-text: "Geist", system-ui, sans-serif;
33
+ --sc-font-mono: "Geist Mono", ui-monospace, monospace;
34
+
35
+ /* Fluid ramp. Tracking tightens as size grows because a face set at 6rem
36
+ with 0 tracking reads loose; this is optical correction, not decoration. */
37
+ --sc-t-xs: clamp(0.75rem, 0.72rem + 0.15vw, 0.82rem);
38
+ --sc-t-sm: clamp(0.875rem, 0.84rem + 0.18vw, 0.95rem);
39
+ --sc-t-base: clamp(1rem, 0.96rem + 0.22vw, 1.125rem);
40
+ --sc-t-lg: clamp(1.2rem, 1.1rem + 0.5vw, 1.5rem);
41
+ --sc-t-xl: clamp(1.6rem, 1.35rem + 1.2vw, 2.25rem);
42
+ --sc-t-2xl: clamp(2.1rem, 1.6rem + 2.4vw, 3.4rem);
43
+ --sc-t-3xl: clamp(2.8rem, 1.9rem + 4.2vw, 5rem);
44
+ --sc-t-4xl: clamp(3.4rem, 1.9rem + 6.6vw, 7.5rem);
45
+
46
+ --sc-track-tight: -0.035em;
47
+ --sc-track-snug: -0.02em;
48
+ --sc-track-normal: -0.005em;
49
+ --sc-track-wide: 0.08em;
50
+
51
+ --sc-leading-none: 0.94;
52
+ --sc-leading-tight: 1.06;
53
+ --sc-leading-body: 1.62;
54
+
55
+ --sc-measure: 62ch;
56
+
57
+ /* --- space. 4px base: the useful middle steps an 8-only scale misses. --- */
58
+ --sc-1: 0.25rem; --sc-2: 0.5rem; --sc-3: 0.75rem; --sc-4: 1rem;
59
+ --sc-5: 1.5rem; --sc-6: 2rem; --sc-7: 3rem; --sc-8: 4rem;
60
+ --sc-9: 6rem; --sc-10: 8rem; --sc-11: 12rem;
61
+
62
+ /* Section rhythm is fluid so a phone doesn't inherit desktop air. */
63
+ --sc-section: clamp(4.5rem, 9vw, 11rem);
64
+ --sc-gutter: clamp(1.25rem, 5vw, 5.5rem);
65
+ --sc-maxw: 82rem;
66
+
67
+ /* --- radius. Pick one scale for the page and hold it. ------------------- */
68
+ --sc-r-sm: 6px; --sc-r-md: 12px; --sc-r-lg: 20px; --sc-r-pill: 999px;
69
+
70
+ /* --- elevation. Every shadow has an offset AND a soft blur, and is tinted
71
+ to the canvas hue. A zero-offset coloured halo is decoration, not
72
+ depth, so there isn't one here. ---------------------------------- */
73
+ --sc-shadow-color: 220 40% 2%;
74
+ --sc-e1: 0 1px 2px hsl(var(--sc-shadow-color) / 0.28),
75
+ 0 2px 6px -1px hsl(var(--sc-shadow-color) / 0.20);
76
+ --sc-e2: 0 2px 4px hsl(var(--sc-shadow-color) / 0.24),
77
+ 0 8px 18px -4px hsl(var(--sc-shadow-color) / 0.30);
78
+ --sc-e3: 0 4px 8px hsl(var(--sc-shadow-color) / 0.22),
79
+ 0 18px 40px -8px hsl(var(--sc-shadow-color) / 0.38),
80
+ 0 48px 90px -24px hsl(var(--sc-shadow-color) / 0.30);
81
+
82
+ /* Edge light: a 1px top highlight sells a raised surface better than any
83
+ amount of blur, because real raised things catch light on their lip. */
84
+ --sc-edge: inset 0 1px 0 color-mix(in oklab, var(--sc-ink) 10%, transparent);
85
+
86
+ /* --- motion. Built-in CSS easings are too weak for UI. ------------------ */
87
+ --sc-ease-out: cubic-bezier(0.23, 1, 0.32, 1);
88
+ --sc-ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
89
+ --sc-ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
90
+ --sc-d-fast: 160ms; --sc-d-base: 240ms; --sc-d-slow: 420ms;
91
+
92
+ --sc-z-stage: 1; --sc-z-copy: 20; --sc-z-chrome: 60;
93
+ }
94
+
95
+ /* --------------------------------------------------------------- reset -- */
96
+ *, *::before, *::after { box-sizing: border-box; }
97
+ html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
98
+ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
99
+
100
+ body {
101
+ margin: 0;
102
+ background: var(--sc-canvas);
103
+ color: var(--sc-ink);
104
+ font-family: var(--sc-font-text);
105
+ font-size: var(--sc-t-base);
106
+ line-height: var(--sc-leading-body);
107
+ letter-spacing: var(--sc-track-normal);
108
+ -webkit-font-smoothing: antialiased;
109
+ text-rendering: optimizeLegibility;
110
+ font-synthesis-weight: none;
111
+ overflow-x: clip;
112
+ }
113
+ img, video, canvas, svg { display: block; max-width: 100%; }
114
+ button, input, select, textarea { font: inherit; color: inherit; }
115
+
116
+ /* ------------------------------------------------- the browser surfaces --
117
+ The parts you did not draw still carry the design. Selection, caret, focus
118
+ ring, scrollbar and tabular numerals all ship with defaults that belong to no
119
+ design system. Theming them is the cheapest signal that a page was built
120
+ rather than assembled, and it is the step that gets skipped most reliably. */
121
+ ::selection { background: var(--sc-accent); color: var(--sc-accent-ink); }
122
+ :root { caret-color: var(--sc-accent); accent-color: var(--sc-accent); }
123
+ :focus-visible {
124
+ outline: 2px solid var(--sc-accent);
125
+ outline-offset: 3px;
126
+ border-radius: var(--sc-r-sm);
127
+ }
128
+ :where(a) { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 0.22em; }
129
+ @supports (scrollbar-color: auto) {
130
+ html { scrollbar-color: color-mix(in oklab, var(--sc-ink) 26%, transparent) transparent; scrollbar-width: thin; }
131
+ }
132
+ :where(table, .sc-nums, [data-sc-count]) { font-variant-numeric: tabular-nums; }
133
+
134
+ /* ----------------------------------------------------------- typography -- */
135
+ .sc-display {
136
+ font-family: var(--sc-font-display);
137
+ font-weight: 500;
138
+ line-height: var(--sc-leading-none);
139
+ letter-spacing: var(--sc-track-tight);
140
+ text-wrap: balance;
141
+ margin: 0;
142
+ }
143
+ .sc-display--xl { font-size: var(--sc-t-4xl); }
144
+ .sc-display--lg { font-size: var(--sc-t-3xl); }
145
+ .sc-display--md { font-size: var(--sc-t-2xl); letter-spacing: var(--sc-track-snug); }
146
+ .sc-lede {
147
+ font-size: var(--sc-t-lg);
148
+ line-height: 1.44;
149
+ letter-spacing: var(--sc-track-snug);
150
+ color: var(--sc-ink);
151
+ max-width: 46ch;
152
+ text-wrap: pretty;
153
+ margin: 0;
154
+ }
155
+ .sc-body { max-width: var(--sc-measure); color: var(--sc-ink-soft); text-wrap: pretty; margin: 0; }
156
+ .sc-label {
157
+ font-family: var(--sc-font-mono);
158
+ font-size: var(--sc-t-xs);
159
+ letter-spacing: var(--sc-track-wide);
160
+ text-transform: uppercase;
161
+ color: var(--sc-ink-soft);
162
+ }
163
+
164
+ /* ---------------------------------------------------------------- layout -- */
165
+ .sc-wrap { width: 100%; max-width: var(--sc-maxw); margin-inline: auto; padding-inline: var(--sc-gutter); }
166
+ .sc-section { padding-block: var(--sc-section); }
167
+ /* More space above a heading than below it: the gap belongs to the boundary,
168
+ not to the pair. */
169
+ .sc-stack > * + * { margin-top: var(--sc-4); }
170
+ .sc-stack > :is(h1,h2,h3) { margin-top: var(--sc-7); }
171
+ .sc-stack > :is(h1,h2,h3):first-child { margin-top: 0; }
172
+ .sc-rule { height: 1px; border: 0; background: var(--sc-hairline); margin: 0; }
173
+
174
+ /* =============================================================== DEVICES ==
175
+ Driven by the engine. Restyle your own markup, not these. */
176
+
177
+ .sc-act--pinned { position: relative; }
178
+ .sc-stage {
179
+ position: sticky; top: 0;
180
+ height: 100vh; height: 100svh;
181
+ overflow: clip;
182
+ z-index: var(--sc-z-stage);
183
+ }
184
+
185
+ /* scrub / sequence media */
186
+ .sc-stage :is(video[data-sc-scrub], canvas[data-sc-sequence], .sc-stage__poster) {
187
+ position: absolute; inset: 0;
188
+ width: 100%; height: 100%;
189
+ object-fit: cover;
190
+ }
191
+ video[data-sc-scrub] { opacity: 0; transition: opacity 380ms var(--sc-ease-out); }
192
+ .sc-has-clip video[data-sc-scrub] { opacity: 1; }
193
+ /* The poster is a live frame-holder, not a placeholder: it stays up until a
194
+ real video frame has painted, which is what stops the blank-stage flash. */
195
+ .sc-stage__poster { z-index: 0; transition: opacity 380ms var(--sc-ease-out); }
196
+ .sc-has-clip .sc-stage__poster { opacity: 0; }
197
+
198
+ /* A scrim only where text sits. A full-frame darkening layer flattens the
199
+ image everywhere to fix contrast in one corner. */
200
+ .sc-scrim {
201
+ position: absolute; inset: 0; pointer-events: none; z-index: 1;
202
+ background: linear-gradient(
203
+ var(--sc-scrim-angle, 180deg),
204
+ color-mix(in oklab, var(--sc-canvas) var(--sc-scrim-a, 78%), transparent) 0%,
205
+ color-mix(in oklab, var(--sc-canvas) 30%, transparent) 42%,
206
+ transparent 68%);
207
+ }
208
+ /* Point the scrim at the copy. A scrim gradient that darkens the top while the
209
+ copy sits at the bottom fixes contrast nowhere and flattens the image
210
+ everywhere, which is the most common way text-over-video goes wrong. */
211
+ .sc-scrim--bottom { --sc-scrim-angle: 0deg; }
212
+ .sc-scrim--left { --sc-scrim-angle: 90deg; }
213
+ .sc-scrim--right { --sc-scrim-angle: 270deg; }
214
+
215
+ /* Corner scrims, for copy anchored to a corner. An edge gradient has to darken
216
+ a whole band across the frame to cover one corner; a corner gradient puts the
217
+ density where the text is and leaves the rest of the image alone. Pair with
218
+ .sc-copy--lead / .sc-copy--trail. */
219
+ .sc-scrim--lead {
220
+ background: linear-gradient(to top right,
221
+ color-mix(in oklab, var(--sc-canvas) 94%, transparent) 0%,
222
+ color-mix(in oklab, var(--sc-canvas) 72%, transparent) 30%,
223
+ color-mix(in oklab, var(--sc-canvas) 30%, transparent) 52%,
224
+ transparent 72%);
225
+ }
226
+ .sc-scrim--trail {
227
+ background: linear-gradient(to top left,
228
+ color-mix(in oklab, var(--sc-canvas) 94%, transparent) 0%,
229
+ color-mix(in oklab, var(--sc-canvas) 72%, transparent) 30%,
230
+ color-mix(in oklab, var(--sc-canvas) 30%, transparent) 52%,
231
+ transparent 72%);
232
+ }
233
+ /* A band across the bottom, for copy that spans the full width of the frame.
234
+ That is what both corner anchors become below 860px, and it is also the right
235
+ shape whenever the copy block is wider than a corner. */
236
+ .sc-scrim--band {
237
+ background: linear-gradient(to top,
238
+ color-mix(in oklab, var(--sc-canvas) 94%, transparent) 0%,
239
+ color-mix(in oklab, var(--sc-canvas) 74%, transparent) 22%,
240
+ color-mix(in oklab, var(--sc-canvas) 30%, transparent) 42%,
241
+ transparent 58%);
242
+ }
243
+ .sc-scrim--vignette {
244
+ background: radial-gradient(120% 90% at 50% 45%, transparent 40%,
245
+ color-mix(in oklab, var(--sc-canvas) 70%, transparent) 100%);
246
+ }
247
+
248
+ /* copy over a stage.
249
+ max-width is in rem, never ch. A `ch` on this container resolves against the
250
+ CONTAINER's font-size (body size), not the display size of the heading inside
251
+ it, so `max-width: 20ch` here silently produces a ~180px column and wraps a
252
+ hero headline to six lines. Set a ch measure on the text element itself. */
253
+ .sc-copy {
254
+ position: absolute; z-index: var(--sc-z-copy);
255
+ inset-inline: var(--sc-gutter);
256
+ max-width: min(46rem, 76vw);
257
+ will-change: opacity, transform;
258
+ }
259
+ .sc-copy--lead { left: var(--sc-gutter); bottom: clamp(3rem, 12vh, 9rem); }
260
+ /* inset-inline FIRST. It is the shorthand for left+right, so declaring it after
261
+ `left: 50%` resets left to auto and the block drifts off the left edge. */
262
+ .sc-copy--center { inset-inline: auto; left: 50%; top: 50%; translate: -50% -50%; text-align: center; }
263
+ .sc-copy--trail { inset-inline: auto; right: var(--sc-gutter); bottom: clamp(3rem, 12vh, 9rem); text-align: right; }
264
+
265
+ /* cue defaults: the engine writes opacity/transform inline, these are the
266
+ pre-paint state so nothing flashes before the first read() */
267
+ [data-sc-cue] { opacity: 0; will-change: opacity, transform; }
268
+ .sc-ready [data-sc-cue] { transition: none; }
269
+
270
+ /* kinetic split */
271
+ .sc-split { display: inline-block; overflow: hidden; vertical-align: top; }
272
+ /* Descenders live below the baseline; a line mask clipped to the line box eats
273
+ the tails off g, y, p, j. The padding buys them room back. */
274
+ .sc-split--line { display: block; padding-bottom: 0.14em; margin-bottom: -0.14em; }
275
+ .sc-split__i { display: inline-block; will-change: transform, opacity; }
276
+ .sc-is-split { opacity: 1 !important; }
277
+
278
+ /* horizontal rail */
279
+ [data-sc-pan] { display: flex; will-change: transform; }
280
+
281
+ /* reveal + parallax */
282
+ [data-sc-reveal] { will-change: clip-path; }
283
+ [data-sc-parallax] { will-change: transform; }
284
+
285
+ /* flow reveal (fires once) */
286
+ [data-sc-in], [data-sc-stagger] > * {
287
+ opacity: 0;
288
+ transform: translate3d(0, 14px, 0);
289
+ transition: opacity 620ms var(--sc-ease-out), transform 620ms var(--sc-ease-out);
290
+ }
291
+ [data-sc-in].sc-in, [data-sc-stagger] > .sc-in { opacity: 1; transform: none; }
292
+
293
+ /* pointer devices */
294
+ [data-sc-tilt] { transform-style: preserve-3d; will-change: transform; }
295
+ [data-sc-magnet] { will-change: transform; }
296
+ /* isolation always; position only if nothing more specific already set it.
297
+ Written with :where() (zero specificity) on purpose: a plain `position:
298
+ relative` here has the same weight as `.sc-stage` and, being later in the
299
+ file, silently wins. A stage carrying both attributes then stops being
300
+ sticky and the whole act scrolls away instead of pinning. */
301
+ [data-sc-spotlight] { isolation: isolate; }
302
+ :where([data-sc-spotlight]) { position: relative; }
303
+ [data-sc-spotlight]::after {
304
+ content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 2;
305
+ background: radial-gradient(
306
+ 28rem 28rem at calc(var(--sc-mx, 0.5) * 100%) calc(var(--sc-my, 0.5) * 100%),
307
+ color-mix(in oklab, var(--sc-accent) 16%, transparent), transparent 70%);
308
+ opacity: 0; transition: opacity var(--sc-d-slow) var(--sc-ease-out);
309
+ }
310
+ [data-sc-spotlight]:hover::after { opacity: 1; }
311
+
312
+ /* =========================================================== WORLDFLIGHT ==
313
+ One fixed stage for the whole page. The only thing in document flow is the
314
+ spacer, and it is empty. If you find yourself adding a section here, you want
315
+ act mode instead: the moment a real block scrolls past the fixed stage, the
316
+ page has a seam again and the whole point is gone. */
317
+
318
+ [data-sc-mode="worldflight"] { position: relative; }
319
+
320
+ .sc-world {
321
+ position: fixed; inset: 0;
322
+ overflow: clip;
323
+ z-index: var(--sc-z-stage);
324
+ background: var(--sc-canvas);
325
+ }
326
+
327
+ /* Every leg is mounted for the life of the page and stacked in the same box.
328
+ The engine owns opacity, visibility and z-index here; do not set them. */
329
+ .sc-world__seg {
330
+ position: absolute; inset: 0;
331
+ opacity: 0;
332
+ will-change: opacity;
333
+ }
334
+ .sc-world__seg > :is(video, img, canvas) {
335
+ position: absolute; inset: 0;
336
+ width: 100%; height: 100%;
337
+ object-fit: cover;
338
+ }
339
+ /* The poster is the frame-holder AND the first camera move: the engine pushes
340
+ it in slowly until a real decoded frame is available to replace it. */
341
+ .sc-world__poster {
342
+ z-index: 0;
343
+ transform-origin: 50% 50%;
344
+ transition: opacity 420ms var(--sc-ease-out);
345
+ }
346
+ .sc-world__seg > video { z-index: 1; }
347
+ .sc-world__seg.sc-has-clip .sc-world__poster { opacity: 0; }
348
+ /* The act stylesheet lights a clip through its ancestor act. A worldflight leg
349
+ has no act, so match the class the engine also writes on the clip itself. */
350
+ video[data-sc-scrub].sc-has-clip { opacity: 1; }
351
+
352
+ /* The copy layer is fixed too, and inert by default: the engine hands
353
+ pointer-events back to a block only while it is actually legible. */
354
+ .sc-world__copy {
355
+ position: fixed; inset: 0;
356
+ z-index: var(--sc-z-copy);
357
+ pointer-events: none;
358
+ }
359
+ .sc-world__scrim { position: absolute; inset: 0; pointer-events: none; z-index: 0; }
360
+ [data-sc-copy] {
361
+ opacity: 0;
362
+ pointer-events: none;
363
+ will-change: opacity, transform;
364
+ }
365
+ .sc-ready [data-sc-copy] { transition: none; }
366
+
367
+ /* The track. Empty on purpose. */
368
+ .sc-world__spacer { pointer-events: none; }
369
+
370
+ /* scroll progress */
371
+ [data-sc-progress] {
372
+ position: fixed; inset: 0 0 auto 0; height: 2px; z-index: var(--sc-z-chrome);
373
+ background: var(--sc-accent); transform-origin: 0 50%; transform: scaleX(0);
374
+ will-change: transform;
375
+ }
376
+
377
+ /* ----------------------------------------------------------- atmosphere --
378
+ Depth is not only shadow. Grain keeps a flat dark ground from banding, and
379
+ it is the difference between "a dark page" and "a lit room". */
380
+ .sc-grain { position: fixed; inset: 0; pointer-events: none; z-index: 3; opacity: 0.045; }
381
+ .sc-grain::before {
382
+ content: ""; position: absolute; inset: -50%;
383
+ background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
384
+ }
385
+
386
+ /* -------------------------------------------------------- reduced motion -- */
387
+ @media (prefers-reduced-motion: reduce) {
388
+ /* Fewer and gentler, not zero. Opacity still carries the reveal so the page
389
+ is comprehensible; every position change is dropped. */
390
+ [data-sc-in], [data-sc-stagger] > * { transform: none; transition-duration: 220ms; }
391
+ [data-sc-parallax], [data-sc-pan], .sc-split__i { transform: none !important; }
392
+ /* A wipe is a position change too. Show reveal content settled. */
393
+ [data-sc-reveal] { clip-path: none !important; }
394
+ /* A rail's transform IS its navigation, not decoration. Zeroing it the way
395
+ parallax and kinetic type are zeroed parks the act on its first screenful
396
+ and makes everything past the fold unreachable: a device degrading into
397
+ missing content. Hand the travel back to the reader as an ordinary scroll
398
+ region instead, so the same items are all still gettable without motion.
399
+ A build that would rather re-lay the rail out as a grid can still do that;
400
+ this is the floor, not the ceiling. */
401
+ [data-sc-act="pan"] .sc-stage,
402
+ [data-sc-act="pan"] [data-sc-stage] {
403
+ overflow-x: auto; overflow-y: hidden;
404
+ overscroll-behavior-x: contain;
405
+ scroll-snap-type: x proximity;
406
+ }
407
+ [data-sc-pan] > * { scroll-snap-align: center; }
408
+ [data-sc-spotlight]::after { display: none; }
409
+ /* A worldflight keeps its whole story here. No clip is ever fetched, so the
410
+ posters are the film, and they cross-dissolve through exactly the same
411
+ seams at exactly the same scroll positions. The push-in and the copy drift
412
+ are the only things that go. */
413
+ .sc-world__poster, [data-sc-copy] { transform: none !important; }
414
+ }
415
+
416
+ /* --------------------------------------------------------------- mobile -- */
417
+ @media (max-width: 860px) {
418
+ .sc-copy { inset-inline: var(--sc-gutter); max-width: none; }
419
+ .sc-copy--trail { text-align: left; }
420
+ .sc-stage { height: 100svh; }
421
+ /* .sc-copy--trail re-anchors to the left and spans the full width here, so a
422
+ bottom-RIGHT corner gradient darkens the one corner the copy just left and
423
+ guarantees a contrast failure over any bright clip. Point the density at
424
+ the copy: the same band .sc-scrim--band paints. */
425
+ .sc-scrim--trail {
426
+ background: linear-gradient(to top,
427
+ color-mix(in oklab, var(--sc-canvas) 94%, transparent) 0%,
428
+ color-mix(in oklab, var(--sc-canvas) 74%, transparent) 22%,
429
+ color-mix(in oklab, var(--sc-canvas) 30%, transparent) 42%,
430
+ transparent 58%);
431
+ }
432
+ }