@babylonjs-toolkit/agent 1.1.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.
- package/README.md +344 -0
- package/bin/bt-agent.js +266 -0
- package/lib/doctor.js +59 -0
- package/lib/install.js +145 -0
- package/lib/manifest.js +46 -0
- package/lib/paths.js +66 -0
- package/lib/payload.js +56 -0
- package/lib/persona.js +177 -0
- package/lib/targets.js +105 -0
- package/package.json +43 -0
- package/persona.md +5 -0
- package/scripts/postinstall.js +49 -0
- package/skills/bt-atlas/SKILL.md +192 -0
- package/skills/bt-atlas/scripts/composite_skin.py +58 -0
- package/skills/bt-atlas/scripts/preview.py +70 -0
- package/skills/bt-atlas/scripts/requirements.txt +2 -0
- package/skills/bt-atlas/scripts/uv_island_mask.py +87 -0
- package/skills/bt-convert/SKILL.md +32 -0
- package/skills/bt-copycat/SKILL.md +184 -0
- package/skills/bt-design/SKILL.md +187 -0
- package/skills/bt-design/references/3d-hero-docs.md +976 -0
- package/skills/bt-design/references/3d-hero-scroll.md +269 -0
- package/skills/bt-design/templates/3d-hero-scroll/HeroScroll.tsx +167 -0
- package/skills/bt-design/templates/3d-hero-scroll/hero-scroll.css +268 -0
- package/skills/bt-design/templates/3d-hero-scroll/hero-scroll.d.ts +67 -0
- package/skills/bt-design/templates/3d-hero-scroll/hero-scroll.html +78 -0
- package/skills/bt-design/templates/3d-hero-scroll/hero-scroll.js +559 -0
- package/skills/bt-execute/SKILL.md +130 -0
- package/skills/bt-gauntlet/SKILL.md +335 -0
- package/skills/bt-hero/SKILL.md +158 -0
- package/skills/bt-landing/SKILL.md +126 -0
- package/skills/bt-plan/SKILL.md +172 -0
- package/skills/bt-prototype/SKILL.md +161 -0
- package/skills/bt-spec/SKILL.md +328 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/* ═══════════════════════════════════════════════════════════════════
|
|
2
|
+
3D-HERO-SCROLL — structural + control styles
|
|
3
|
+
Brand-agnostic: every color/font flows through --hs-* tokens.
|
|
4
|
+
MAP THESE TO THE HOST SITE'S DESIGN SYSTEM — do not ship defaults
|
|
5
|
+
unless the host has no tokens of its own.
|
|
6
|
+
═══════════════════════════════════════════════════════════════════ */
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--hs-bg: #060607; /* page/veil background */
|
|
10
|
+
--hs-ink: #e8ecee; /* primary text */
|
|
11
|
+
--hs-dim: #878e93; /* secondary text */
|
|
12
|
+
--hs-accent: #35e9ff; /* the ONE accent — HUD / highlights */
|
|
13
|
+
--hs-accent-soft: rgba(53, 233, 255, 0.35);
|
|
14
|
+
--hs-accent-ghost: rgba(53, 233, 255, 0.08);
|
|
15
|
+
--hs-display: inherit; /* host display font */
|
|
16
|
+
--hs-mono: ui-monospace, "SFMono-Regular", Menlo, monospace;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* ── loading gate: no scrolling until footage is ready ─────────── */
|
|
20
|
+
|
|
21
|
+
body[data-hs-state="loading"] { overflow: hidden; }
|
|
22
|
+
|
|
23
|
+
/* ── journey stage ──────────────────────────────────────────────── */
|
|
24
|
+
|
|
25
|
+
#hs-journey { position: relative; /* height set inline or by host, e.g. 640vh */ }
|
|
26
|
+
|
|
27
|
+
#hs-journey .hs-stage {
|
|
28
|
+
position: sticky; top: 0;
|
|
29
|
+
height: 100vh; width: 100%;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
background: var(--hs-bg);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#hs-video {
|
|
35
|
+
position: absolute; inset: 0;
|
|
36
|
+
width: 100%; height: 100%;
|
|
37
|
+
object-fit: cover;
|
|
38
|
+
will-change: filter;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
body.hs-no-video #hs-video { display: none; }
|
|
42
|
+
body.hs-no-video .hs-stage {
|
|
43
|
+
background: var(--hs-poster, var(--hs-bg)) center/cover no-repeat;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.hs-stage-shade {
|
|
47
|
+
position: absolute; inset: 0; pointer-events: none;
|
|
48
|
+
background: linear-gradient(180deg, rgba(0,0,0,0.35) 0%, transparent 30%,
|
|
49
|
+
transparent 62%, rgba(0,0,0,0.55) 100%);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/* ── overlays (choreographed by the engine) ─────────────────────── */
|
|
53
|
+
|
|
54
|
+
.hs-ovl {
|
|
55
|
+
position: absolute; inset: 0;
|
|
56
|
+
display: flex; flex-direction: column; justify-content: center;
|
|
57
|
+
padding: 0 clamp(24px, 7vw, 120px);
|
|
58
|
+
opacity: 0; visibility: hidden;
|
|
59
|
+
pointer-events: none;
|
|
60
|
+
will-change: opacity, transform, filter;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* directional scrim so copy survives bright footage behind it.
|
|
64
|
+
Add .hs-scrim to any overlay with left-anchored text. */
|
|
65
|
+
.hs-ovl.hs-scrim::before {
|
|
66
|
+
content: "";
|
|
67
|
+
position: absolute; inset: 0; z-index: -1;
|
|
68
|
+
background: linear-gradient(100deg, rgba(0, 0, 0, 0.82) 0%,
|
|
69
|
+
rgba(0, 0, 0, 0.5) 38%, rgba(0, 0, 0, 0.08) 68%, transparent 82%);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* ── rewind veil (injected by the engine) ───────────────────────── */
|
|
73
|
+
|
|
74
|
+
#hs-veil {
|
|
75
|
+
position: fixed; inset: 0; z-index: 150;
|
|
76
|
+
background: var(--hs-bg);
|
|
77
|
+
opacity: 0;
|
|
78
|
+
pointer-events: none;
|
|
79
|
+
transition: opacity 0.45s ease;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#hs-veil.hs-on { opacity: 1; pointer-events: auto; }
|
|
83
|
+
|
|
84
|
+
/* accent sweep while on black — the journey re-arming */
|
|
85
|
+
#hs-veil::after {
|
|
86
|
+
content: "";
|
|
87
|
+
position: absolute; left: 22%; right: 22%; top: 50%; height: 1px;
|
|
88
|
+
background: var(--hs-accent);
|
|
89
|
+
box-shadow: 0 0 12px var(--hs-accent), 0 0 44px var(--hs-accent-soft);
|
|
90
|
+
transform: scaleX(0);
|
|
91
|
+
opacity: 0;
|
|
92
|
+
transition: transform 0.55s cubic-bezier(0.16, 1, 0.3, 1) 0.25s,
|
|
93
|
+
opacity 0.2s ease 0.25s;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#hs-veil.hs-on::after { transform: scaleX(1); opacity: 1; }
|
|
97
|
+
|
|
98
|
+
/* ── loader (optional) ──────────────────────────────────────────── */
|
|
99
|
+
|
|
100
|
+
#hs-loader {
|
|
101
|
+
position: fixed; inset: 0; z-index: 200;
|
|
102
|
+
background: var(--hs-bg);
|
|
103
|
+
display: flex; flex-direction: column; align-items: center;
|
|
104
|
+
justify-content: center; gap: 22px;
|
|
105
|
+
transition: opacity 0.9s ease 0.35s, visibility 0.9s ease 0.35s;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
body[data-hs-state="ready"] #hs-loader { opacity: 0; visibility: hidden; }
|
|
109
|
+
|
|
110
|
+
.hs-loader-bar {
|
|
111
|
+
width: min(420px, 70vw); height: 2px;
|
|
112
|
+
background: rgba(255, 255, 255, 0.08);
|
|
113
|
+
overflow: hidden;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#hs-loader-fill {
|
|
117
|
+
display: block; height: 100%; width: 0%;
|
|
118
|
+
background: var(--hs-accent);
|
|
119
|
+
box-shadow: 0 0 12px var(--hs-accent), 0 0 32px var(--hs-accent-soft);
|
|
120
|
+
transition: width 0.25s ease;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ── telemetry HUD (optional) ───────────────────────────────────── */
|
|
124
|
+
|
|
125
|
+
#hs-hud {
|
|
126
|
+
position: fixed; right: clamp(18px, 3vw, 44px); bottom: clamp(18px, 3vw, 40px);
|
|
127
|
+
z-index: 100;
|
|
128
|
+
width: 200px;
|
|
129
|
+
padding: 14px 16px 12px;
|
|
130
|
+
background: rgba(0, 0, 0, 0.55);
|
|
131
|
+
border: 1px solid rgba(255, 255, 255, 0.07);
|
|
132
|
+
border-left: 2px solid var(--hs-accent);
|
|
133
|
+
backdrop-filter: blur(10px);
|
|
134
|
+
-webkit-backdrop-filter: blur(10px);
|
|
135
|
+
opacity: 0;
|
|
136
|
+
transform: translateY(12px);
|
|
137
|
+
transition: opacity 0.6s ease, transform 0.6s ease;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
body[data-hs-state="ready"] #hs-hud { opacity: 1; transform: none; }
|
|
141
|
+
body[data-hs-state="ready"] #hs-hud.hs-hud-off {
|
|
142
|
+
opacity: 0; transform: translateY(12px); pointer-events: none;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#hs-hud-value {
|
|
146
|
+
font: 700 52px/0.9 var(--hs-display);
|
|
147
|
+
color: var(--hs-ink);
|
|
148
|
+
font-variant-numeric: tabular-nums;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.hs-hud-unit { font: 600 11px var(--hs-mono); letter-spacing: 0.3em; color: var(--hs-accent); }
|
|
152
|
+
|
|
153
|
+
.hs-hud-bar { margin: 12px 0 8px; height: 2px; background: rgba(255,255,255,0.1); overflow: hidden; }
|
|
154
|
+
|
|
155
|
+
#hs-hud-progress {
|
|
156
|
+
display: block; height: 100%; width: 100%;
|
|
157
|
+
background: var(--hs-accent);
|
|
158
|
+
box-shadow: 0 0 10px var(--hs-accent-soft);
|
|
159
|
+
transform: scaleX(0); transform-origin: left center;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.hs-hud-meta {
|
|
163
|
+
display: flex; justify-content: space-between;
|
|
164
|
+
font: 400 9px var(--hs-mono); letter-spacing: 0.14em; color: var(--hs-dim);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* ── play button (optional) ─────────────────────────────────────── */
|
|
168
|
+
|
|
169
|
+
#hs-play {
|
|
170
|
+
position: fixed; top: clamp(18px, 3vh, 30px); left: 50%;
|
|
171
|
+
transform: translateX(-50%) translateY(-8px);
|
|
172
|
+
z-index: 110;
|
|
173
|
+
display: flex; align-items: center; gap: 12px;
|
|
174
|
+
padding: 13px 26px;
|
|
175
|
+
background: rgba(0, 0, 0, 0.55);
|
|
176
|
+
border: 1px solid var(--hs-accent-soft);
|
|
177
|
+
border-radius: 999px;
|
|
178
|
+
color: var(--hs-accent);
|
|
179
|
+
font: 600 10px var(--hs-mono);
|
|
180
|
+
letter-spacing: 0.32em;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
backdrop-filter: blur(10px);
|
|
183
|
+
-webkit-backdrop-filter: blur(10px);
|
|
184
|
+
opacity: 0;
|
|
185
|
+
pointer-events: none;
|
|
186
|
+
transition: opacity 0.6s ease, transform 0.6s ease, background 0.35s ease,
|
|
187
|
+
border-color 0.35s ease, box-shadow 0.35s ease, color 0.35s ease;
|
|
188
|
+
animation: hs-breathe 3.2s ease-in-out infinite;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
body[data-hs-state="ready"] #hs-play {
|
|
192
|
+
opacity: 1; pointer-events: auto;
|
|
193
|
+
transform: translateX(-50%) translateY(0);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
@keyframes hs-breathe {
|
|
197
|
+
0%, 100% { box-shadow: 0 0 0 rgba(0, 0, 0, 0); }
|
|
198
|
+
50% { box-shadow: 0 0 22px var(--hs-accent-soft), 0 0 60px var(--hs-accent-ghost); }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
#hs-play:hover {
|
|
202
|
+
background: var(--hs-accent-ghost);
|
|
203
|
+
border-color: var(--hs-accent);
|
|
204
|
+
box-shadow: 0 0 26px var(--hs-accent-soft);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.hs-play-icon {
|
|
208
|
+
width: 0; height: 0;
|
|
209
|
+
border-left: 9px solid var(--hs-accent);
|
|
210
|
+
border-top: 6px solid transparent;
|
|
211
|
+
border-bottom: 6px solid transparent;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
#hs-play.hs-playing {
|
|
215
|
+
animation: none;
|
|
216
|
+
border-color: rgba(255, 255, 255, 0.18);
|
|
217
|
+
color: var(--hs-dim);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
#hs-play.hs-playing .hs-play-icon {
|
|
221
|
+
border: none;
|
|
222
|
+
width: 9px; height: 9px;
|
|
223
|
+
background: var(--hs-dim);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/* ── jump nav (optional) ────────────────────────────────────────── */
|
|
227
|
+
|
|
228
|
+
#hs-jump-nav {
|
|
229
|
+
position: fixed; left: clamp(18px, 3vw, 44px); bottom: clamp(18px, 3vw, 40px);
|
|
230
|
+
z-index: 100;
|
|
231
|
+
display: flex; gap: 10px;
|
|
232
|
+
opacity: 0;
|
|
233
|
+
transform: translateY(12px);
|
|
234
|
+
transition: opacity 0.6s ease, transform 0.6s ease;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
body[data-hs-state="ready"] #hs-jump-nav { opacity: 1; transform: none; }
|
|
238
|
+
|
|
239
|
+
#hs-jump-nav button {
|
|
240
|
+
display: flex; align-items: center; gap: 9px;
|
|
241
|
+
padding: 11px 18px;
|
|
242
|
+
background: rgba(0, 0, 0, 0.55);
|
|
243
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
244
|
+
color: var(--hs-dim);
|
|
245
|
+
font: 600 9px var(--hs-mono);
|
|
246
|
+
letter-spacing: 0.28em;
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
backdrop-filter: blur(10px);
|
|
249
|
+
-webkit-backdrop-filter: blur(10px);
|
|
250
|
+
transition: border-color 0.3s ease, color 0.3s ease,
|
|
251
|
+
box-shadow 0.3s ease, opacity 0.3s ease;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
#hs-jump-nav .hs-arrow { color: var(--hs-accent); font-size: 11px; }
|
|
255
|
+
|
|
256
|
+
#hs-jump-nav button:hover {
|
|
257
|
+
border-color: var(--hs-accent);
|
|
258
|
+
color: var(--hs-ink);
|
|
259
|
+
box-shadow: 0 0 18px var(--hs-accent-ghost);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#hs-jump-nav button.hs-nav-dim { opacity: 0.3; pointer-events: none; }
|
|
263
|
+
|
|
264
|
+
/* ── reduced motion ─────────────────────────────────────────────── */
|
|
265
|
+
|
|
266
|
+
@media (prefers-reduced-motion: reduce) {
|
|
267
|
+
#hs-play { animation: none; }
|
|
268
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/* Type declarations for the framework-agnostic hero-scroll engine. */
|
|
2
|
+
|
|
3
|
+
export interface HeroScrollTelemetry {
|
|
4
|
+
/** HUD value at endP. */
|
|
5
|
+
max: number;
|
|
6
|
+
/** Progress (0–1) where the value leaves 0 — CALIBRATE from footage frames. */
|
|
7
|
+
startP: number;
|
|
8
|
+
/** Progress where the value reaches max (default 0.95). */
|
|
9
|
+
endP?: number;
|
|
10
|
+
/** Curve exponent: <1 = violent launch, 1 = linear (default 0.45). */
|
|
11
|
+
exponent?: number;
|
|
12
|
+
/** [progressThreshold, label] pairs; the HUD shows the last one reached. */
|
|
13
|
+
segments?: Array<[number, string]>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface HeroScrollConfig {
|
|
17
|
+
/** Path to the ALL-INTRA (`-g 1`) scrub-encoded mp4. */
|
|
18
|
+
video?: string;
|
|
19
|
+
/** Scrub low-pass factor, 1 = no smoothing (default 0.16). */
|
|
20
|
+
smoothing?: number;
|
|
21
|
+
/** Scroll-velocity motion blur on the footage (default true). */
|
|
22
|
+
motionBlur?: boolean;
|
|
23
|
+
/** Body class applied when the footage can't load (default "hs-no-video"). */
|
|
24
|
+
fallbackClass?: string;
|
|
25
|
+
/** Telemetry HUD curve; omit/null to disable HUD math. */
|
|
26
|
+
telemetry?: HeroScrollTelemetry | null;
|
|
27
|
+
/**
|
|
28
|
+
* Where PLAY/END consider "the end":
|
|
29
|
+
* - "page" (default): PLAY glides on to the document bottom, END jumps there.
|
|
30
|
+
* - "hero": PLAY/END stop at the journey's end (land on the next section).
|
|
31
|
+
* Named `sweep` (not `reach`) so it never collides with a spec/plan's
|
|
32
|
+
* route/DOM-scope terminology.
|
|
33
|
+
*/
|
|
34
|
+
sweep?: "page" | "hero";
|
|
35
|
+
/** Autoplay tail speed after the journey, as a fraction of the viewport
|
|
36
|
+
* (sweep: "page" only; default 0.5). */
|
|
37
|
+
tailRate?: number;
|
|
38
|
+
/** Veiled-cut fade duration in ms (default 480). */
|
|
39
|
+
veilFadeMs?: number;
|
|
40
|
+
/** Hold-on-black duration in ms while the sweep re-arms (default 450). */
|
|
41
|
+
veilHoldMs?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface HeroScrollController {
|
|
45
|
+
/** Start film-speed autoplay. */
|
|
46
|
+
play(): void;
|
|
47
|
+
/** Stop autoplay / veil. */
|
|
48
|
+
stop(): void;
|
|
49
|
+
/** Fade to black, jump to `y`, re-sync, lift; optional continuation. */
|
|
50
|
+
veilTo(y: number, done?: (token: number) => void): void;
|
|
51
|
+
/** Current smoothed journey progress (0–1). */
|
|
52
|
+
progress(): number;
|
|
53
|
+
/** Tear everything down (listeners, rAF, injected veil, blob URL). */
|
|
54
|
+
destroy(): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Mount the hero-scroll engine against a root element (or `document`).
|
|
59
|
+
* The root must already contain the `#hs-journey` markup. Returns a
|
|
60
|
+
* controller — call `.destroy()` on unmount.
|
|
61
|
+
*/
|
|
62
|
+
export function initHeroScroll(
|
|
63
|
+
root?: ParentNode,
|
|
64
|
+
config?: HeroScrollConfig,
|
|
65
|
+
): HeroScrollController;
|
|
66
|
+
|
|
67
|
+
export default initHeroScroll;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<!-- ═══════════════════════════════════════════════════════════════════
|
|
2
|
+
3D-HERO-SCROLL — markup skeleton
|
|
3
|
+
Replaces a placeholder 100vh hero. Every block marked OPTIONAL can be
|
|
4
|
+
deleted; the engine activates features by element presence only.
|
|
5
|
+
Set --hs-* tokens (hero-scroll.css) to the host brand before shipping.
|
|
6
|
+
═══════════════════════════════════════════════════════════════════ -->
|
|
7
|
+
|
|
8
|
+
<!-- OPTIONAL · preload gate (scroll is locked until footage is ready) -->
|
|
9
|
+
<div id="hs-loader" role="status" aria-label="Loading">
|
|
10
|
+
<div class="hs-loader-mark"><!-- host wordmark/logo --></div>
|
|
11
|
+
<div class="hs-loader-bar"><span id="hs-loader-fill"></span></div>
|
|
12
|
+
<span id="hs-loader-pct">000</span>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<!-- OPTIONAL · film-speed autoplay. data-idle / data-busy set the labels -->
|
|
16
|
+
<button id="hs-play" aria-pressed="false" title="Play — cinematic auto-scroll">
|
|
17
|
+
<span class="hs-play-icon" aria-hidden="true"></span>
|
|
18
|
+
<span class="hs-play-label" data-idle="PLAY THE RUN" data-busy="STOP">PLAY THE RUN</span>
|
|
19
|
+
</button>
|
|
20
|
+
|
|
21
|
+
<!-- OPTIONAL · veiled jump nav -->
|
|
22
|
+
<nav id="hs-jump-nav" aria-label="Quick navigation">
|
|
23
|
+
<button id="hs-top" title="Go to top"><span class="hs-arrow" aria-hidden="true">↑</span><span>TOP</span></button>
|
|
24
|
+
<button id="hs-end" title="Skip to the end"><span class="hs-arrow" aria-hidden="true">↓</span><span>END</span></button>
|
|
25
|
+
</nav>
|
|
26
|
+
|
|
27
|
+
<!-- OPTIONAL · telemetry HUD (value curve configured in HS_CONFIG) -->
|
|
28
|
+
<aside id="hs-hud" aria-label="Telemetry">
|
|
29
|
+
<div><span id="hs-hud-value">0</span> <span class="hs-hud-unit">UNIT</span></div>
|
|
30
|
+
<div class="hs-hud-bar"><span id="hs-hud-progress"></span></div>
|
|
31
|
+
<div class="hs-hud-meta"><span id="hs-hud-seg"></span><span id="hs-hud-pct">000%</span></div>
|
|
32
|
+
</aside>
|
|
33
|
+
|
|
34
|
+
<!-- REQUIRED · the journey. height = scroll length: ~15–20vh per second
|
|
35
|
+
of footage (e.g. 32s film → 560–640vh). Poster = FIRST frame of the
|
|
36
|
+
scrub file, not a glamour still, or the load-in will jump-cut. -->
|
|
37
|
+
<div id="hs-journey" style="height: 640vh">
|
|
38
|
+
<div class="hs-stage">
|
|
39
|
+
<video id="hs-video" muted playsinline preload="none" poster="media/poster.jpg"></video>
|
|
40
|
+
<div class="hs-stage-shade" aria-hidden="true"></div>
|
|
41
|
+
|
|
42
|
+
<!-- OPTIONAL · choreographed overlays. data-from/data-to are journey
|
|
43
|
+
progress (0–1). Add .hs-scrim for left-anchored copy over bright
|
|
44
|
+
footage. [data-hs-depth] children parallax; [data-hs-count]
|
|
45
|
+
numbers count up when the overlay activates. -->
|
|
46
|
+
<section class="hs-ovl" data-from="0" data-to="0.115">
|
|
47
|
+
<!-- hero wordmark / tagline / scroll cue -->
|
|
48
|
+
</section>
|
|
49
|
+
|
|
50
|
+
<section class="hs-ovl hs-scrim" data-from="0.26" data-to="0.46">
|
|
51
|
+
<!-- e.g. stats: <span data-hs-count data-target="1200" data-group="1">0</span> -->
|
|
52
|
+
</section>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<!-- The rest of the host page (below) becomes the autoplay "tail". -->
|
|
57
|
+
|
|
58
|
+
<script>
|
|
59
|
+
window.HS_CONFIG = {
|
|
60
|
+
video: "media/journey-scrub.mp4", // ALL-INTRA (-g 1) scrub encode
|
|
61
|
+
sweep: "page", // "page" (default): PLAY/END traverse the whole landing
|
|
62
|
+
// page; "hero": PLAY/END stop at the journey's end — you
|
|
63
|
+
// land on the next regular HTML section, same as a
|
|
64
|
+
// normal scroll (opt in explicitly). Named `sweep`, not
|
|
65
|
+
// `reach`, to avoid colliding with route/DOM-scope terms.
|
|
66
|
+
telemetry: {
|
|
67
|
+
max: 250, // HUD value at endP
|
|
68
|
+
startP: 0.286, // CALIBRATE: first frame of real subject motion
|
|
69
|
+
endP: 0.95,
|
|
70
|
+
exponent: 0.45, // <1 = hard launch
|
|
71
|
+
segments: [[0, "SEG 01"], [0.25, "SEG 02"], [0.5, "SEG 03"], [0.75, "SEG 04"]],
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
</script>
|
|
75
|
+
<!-- ESM module: auto-boots against the document because HS_CONFIG is set above.
|
|
76
|
+
(React/bundler hosts import initHeroScroll from hero-scroll.js instead —
|
|
77
|
+
see HeroScroll.tsx — and never set window.HS_CONFIG.) -->
|
|
78
|
+
<script type="module" src="hero-scroll.js"></script>
|