@hustle-together/api-dev-tools 2.0.6 → 3.0.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 +283 -478
- package/bin/cli.js +55 -11
- package/commands/README.md +124 -251
- package/commands/api-create.md +318 -136
- package/commands/api-interview.md +252 -256
- package/commands/api-research.md +209 -234
- package/commands/api-verify.md +231 -0
- package/demo/audio/generate-all-narrations.js +516 -0
- package/demo/audio/generate-voice-previews.js +140 -0
- package/demo/audio/narration-adam-timing.json +3666 -0
- package/demo/audio/narration-adam.mp3 +0 -0
- package/demo/audio/narration-creature-timing.json +3666 -0
- package/demo/audio/narration-creature.mp3 +0 -0
- package/demo/audio/narration-gaming-timing.json +3666 -0
- package/demo/audio/narration-gaming.mp3 +0 -0
- package/demo/audio/narration-hope-timing.json +3666 -0
- package/demo/audio/narration-hope.mp3 +0 -0
- package/demo/audio/narration-mark-timing.json +3666 -0
- package/demo/audio/narration-mark.mp3 +0 -0
- package/demo/audio/previews/manifest.json +30 -0
- package/demo/audio/previews/preview-creature.mp3 +0 -0
- package/demo/audio/previews/preview-gaming.mp3 +0 -0
- package/demo/audio/previews/preview-hope.mp3 +0 -0
- package/demo/audio/previews/preview-mark.mp3 +0 -0
- package/demo/audio/voices-manifest.json +50 -0
- package/demo/hustle-together/blog/gemini-vs-claude-widgets.html +30 -28
- package/demo/hustle-together/blog/interview-driven-api-development.html +37 -23
- package/demo/hustle-together/index.html +142 -109
- package/demo/workflow-demo.html +1054 -544
- package/hooks/periodic-reground.py +154 -0
- package/hooks/session-startup.py +151 -0
- package/hooks/track-tool-use.py +109 -17
- package/hooks/verify-after-green.py +152 -0
- package/package.json +2 -2
- package/templates/api-dev-state.json +42 -7
- package/templates/research-index.json +6 -0
- package/templates/settings.json +23 -0
package/demo/workflow-demo.html
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/TextPlugin.min.js"></script>
|
|
13
13
|
<style>
|
|
14
14
|
/* ============================================
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
DARK THEME WITH RED ACCENTS & ANIMATED GRID
|
|
16
|
+
90s terminal aesthetic with glow effects
|
|
17
17
|
============================================ */
|
|
18
18
|
|
|
19
19
|
* {
|
|
@@ -23,25 +23,28 @@
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
:root {
|
|
26
|
-
|
|
27
|
-
--grey: #888;
|
|
28
|
-
--dark-grey: #444;
|
|
26
|
+
/* Dark theme colors */
|
|
29
27
|
--black: #0a0a0a;
|
|
30
|
-
--
|
|
31
|
-
--
|
|
28
|
+
--darker-grey: #111;
|
|
29
|
+
--dark-grey: #1a1a1a;
|
|
30
|
+
--card-bg: #0d0d0d;
|
|
31
|
+
--border-color: #333;
|
|
32
|
+
--grey: #888;
|
|
33
|
+
--white: #f0f0f0;
|
|
34
|
+
--accent-red: #ba0c2f;
|
|
32
35
|
--accent-red-glow: rgba(186, 12, 47, 0.4);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
38
|
body {
|
|
36
39
|
background: var(--black);
|
|
37
40
|
color: var(--white);
|
|
38
|
-
font-family: 'Courier New',
|
|
39
|
-
line-height: 1.
|
|
41
|
+
font-family: 'Courier New', monospace;
|
|
42
|
+
line-height: 1.6;
|
|
40
43
|
overflow-x: hidden;
|
|
41
44
|
}
|
|
42
45
|
|
|
43
46
|
/* ============================================
|
|
44
|
-
ANIMATED BACKGROUND
|
|
47
|
+
ANIMATED BACKGROUND GRID
|
|
45
48
|
============================================ */
|
|
46
49
|
.background-pattern {
|
|
47
50
|
position: fixed;
|
|
@@ -49,21 +52,20 @@
|
|
|
49
52
|
left: 0;
|
|
50
53
|
width: 100%;
|
|
51
54
|
height: 100%;
|
|
52
|
-
pointer-events: none;
|
|
53
55
|
z-index: -1;
|
|
54
|
-
|
|
56
|
+
opacity: 0.4;
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
.grid-svg {
|
|
58
60
|
width: 100%;
|
|
59
61
|
height: 100%;
|
|
60
|
-
opacity: 0.15;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
.grid-line {
|
|
64
|
-
stroke: var(--
|
|
65
|
+
stroke: var(--accent-red);
|
|
65
66
|
stroke-width: 0.5;
|
|
66
67
|
fill: none;
|
|
68
|
+
opacity: 0.4;
|
|
67
69
|
}
|
|
68
70
|
|
|
69
71
|
/* Floating particles */
|
|
@@ -73,130 +75,101 @@
|
|
|
73
75
|
left: 0;
|
|
74
76
|
width: 100%;
|
|
75
77
|
height: 100%;
|
|
76
|
-
pointer-events: none;
|
|
77
78
|
z-index: -1;
|
|
79
|
+
pointer-events: none;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
.particle {
|
|
81
83
|
position: absolute;
|
|
82
84
|
width: 4px;
|
|
83
85
|
height: 4px;
|
|
84
|
-
background: var(--
|
|
85
|
-
opacity: 0.3;
|
|
86
|
+
background: var(--accent-red);
|
|
86
87
|
border-radius: 50%;
|
|
88
|
+
opacity: 0;
|
|
89
|
+
box-shadow: 0 0 6px var(--accent-red-glow);
|
|
87
90
|
}
|
|
88
91
|
|
|
92
|
+
/* Particle shape variations */
|
|
89
93
|
.particle.square {
|
|
90
94
|
border-radius: 0;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
width: 8px;
|
|
94
|
-
height: 8px;
|
|
95
|
+
width: 6px;
|
|
96
|
+
height: 6px;
|
|
95
97
|
}
|
|
96
98
|
|
|
97
99
|
.particle.plus {
|
|
100
|
+
width: 8px;
|
|
101
|
+
height: 2px;
|
|
102
|
+
background: var(--accent-red);
|
|
98
103
|
border-radius: 0;
|
|
99
|
-
background: transparent;
|
|
100
|
-
width: 12px;
|
|
101
|
-
height: 12px;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
.particle.plus::before
|
|
105
|
-
.particle.plus::after {
|
|
106
|
+
.particle.plus::before {
|
|
106
107
|
content: '';
|
|
107
108
|
position: absolute;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
height: 1px;
|
|
114
|
-
top: 5.5px;
|
|
115
|
-
left: 0;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
.particle.plus::after {
|
|
119
|
-
width: 1px;
|
|
120
|
-
height: 12px;
|
|
121
|
-
left: 5.5px;
|
|
122
|
-
top: 0;
|
|
109
|
+
width: 2px;
|
|
110
|
+
height: 8px;
|
|
111
|
+
background: var(--accent-red);
|
|
112
|
+
left: 3px;
|
|
113
|
+
top: -3px;
|
|
123
114
|
}
|
|
124
115
|
|
|
125
|
-
/* X shape particle */
|
|
126
116
|
.particle.x-shape {
|
|
117
|
+
width: 8px;
|
|
118
|
+
height: 2px;
|
|
119
|
+
background: var(--accent-red);
|
|
127
120
|
border-radius: 0;
|
|
128
|
-
|
|
129
|
-
width: 14px;
|
|
130
|
-
height: 14px;
|
|
121
|
+
transform: rotate(45deg);
|
|
131
122
|
}
|
|
132
123
|
|
|
133
|
-
.particle.x-shape::before
|
|
134
|
-
.particle.x-shape::after {
|
|
124
|
+
.particle.x-shape::before {
|
|
135
125
|
content: '';
|
|
136
126
|
position: absolute;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
width: 14px;
|
|
142
|
-
height: 1px;
|
|
143
|
-
top: 6.5px;
|
|
144
|
-
left: 0;
|
|
145
|
-
transform: rotate(45deg);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.particle.x-shape::after {
|
|
149
|
-
width: 14px;
|
|
150
|
-
height: 1px;
|
|
151
|
-
top: 6.5px;
|
|
152
|
-
left: 0;
|
|
153
|
-
transform: rotate(-45deg);
|
|
127
|
+
width: 8px;
|
|
128
|
+
height: 2px;
|
|
129
|
+
background: var(--accent-red);
|
|
130
|
+
transform: rotate(90deg);
|
|
154
131
|
}
|
|
155
132
|
|
|
156
|
-
/* Line/dash particle */
|
|
157
133
|
.particle.line {
|
|
158
|
-
|
|
159
|
-
width: 20px;
|
|
134
|
+
width: 12px;
|
|
160
135
|
height: 1px;
|
|
161
|
-
|
|
136
|
+
border-radius: 0;
|
|
162
137
|
}
|
|
163
138
|
|
|
164
139
|
.particle.line-v {
|
|
165
|
-
border-radius: 0;
|
|
166
140
|
width: 1px;
|
|
167
|
-
height:
|
|
168
|
-
|
|
141
|
+
height: 12px;
|
|
142
|
+
border-radius: 0;
|
|
169
143
|
}
|
|
170
144
|
|
|
171
|
-
/* Circle outline particle */
|
|
172
145
|
.particle.circle-outline {
|
|
173
|
-
width: 16px;
|
|
174
|
-
height: 16px;
|
|
175
|
-
border-radius: 50%;
|
|
176
146
|
background: transparent;
|
|
177
|
-
border: 1px solid var(--
|
|
147
|
+
border: 1px solid var(--accent-red);
|
|
148
|
+
width: 8px;
|
|
149
|
+
height: 8px;
|
|
178
150
|
}
|
|
179
151
|
|
|
180
|
-
/* Triangle particle */
|
|
181
152
|
.particle.triangle {
|
|
182
153
|
width: 0;
|
|
183
154
|
height: 0;
|
|
184
155
|
background: transparent;
|
|
185
|
-
border-left:
|
|
186
|
-
border-right:
|
|
187
|
-
border-bottom:
|
|
156
|
+
border-left: 4px solid transparent;
|
|
157
|
+
border-right: 4px solid transparent;
|
|
158
|
+
border-bottom: 7px solid var(--accent-red);
|
|
159
|
+
border-radius: 0;
|
|
188
160
|
}
|
|
189
161
|
|
|
190
162
|
/* Cursor glow effect */
|
|
191
163
|
.cursor-glow {
|
|
192
164
|
position: fixed;
|
|
193
|
-
width:
|
|
194
|
-
height:
|
|
195
|
-
|
|
165
|
+
width: 500px;
|
|
166
|
+
height: 500px;
|
|
167
|
+
border-radius: 50%;
|
|
168
|
+
background: radial-gradient(circle, var(--accent-red-glow) 0%, rgba(186, 12, 47, 0.15) 30%, transparent 70%);
|
|
196
169
|
pointer-events: none;
|
|
197
|
-
z-index:
|
|
170
|
+
z-index: -1;
|
|
198
171
|
transform: translate(-50%, -50%);
|
|
199
|
-
|
|
172
|
+
opacity: 0.7;
|
|
200
173
|
}
|
|
201
174
|
|
|
202
175
|
/* Scanlines overlay */
|
|
@@ -206,26 +179,27 @@
|
|
|
206
179
|
left: 0;
|
|
207
180
|
width: 100%;
|
|
208
181
|
height: 100%;
|
|
209
|
-
pointer-events: none;
|
|
210
|
-
z-index: 9999;
|
|
211
182
|
background: repeating-linear-gradient(
|
|
212
183
|
0deg,
|
|
213
184
|
transparent,
|
|
214
185
|
transparent 2px,
|
|
215
|
-
rgba(0, 0, 0, 0.
|
|
216
|
-
rgba(0, 0, 0, 0.
|
|
186
|
+
rgba(0, 0, 0, 0.05) 2px,
|
|
187
|
+
rgba(0, 0, 0, 0.05) 4px
|
|
217
188
|
);
|
|
218
|
-
|
|
189
|
+
pointer-events: none;
|
|
190
|
+
z-index: 9999;
|
|
191
|
+
opacity: 0.2;
|
|
219
192
|
}
|
|
220
193
|
|
|
221
|
-
/*
|
|
194
|
+
/* Corner decorations */
|
|
222
195
|
.corner-decoration {
|
|
223
196
|
position: fixed;
|
|
224
|
-
width:
|
|
225
|
-
height:
|
|
226
|
-
border: 1px dashed var(--
|
|
227
|
-
opacity: 0.4;
|
|
197
|
+
width: 100px;
|
|
198
|
+
height: 100px;
|
|
199
|
+
border: 1px dashed var(--accent-red);
|
|
228
200
|
z-index: 1;
|
|
201
|
+
pointer-events: none;
|
|
202
|
+
opacity: 0.4;
|
|
229
203
|
}
|
|
230
204
|
|
|
231
205
|
.corner-decoration.top-left {
|
|
@@ -237,7 +211,7 @@
|
|
|
237
211
|
|
|
238
212
|
.corner-decoration.top-right {
|
|
239
213
|
top: 20px;
|
|
240
|
-
right:
|
|
214
|
+
right: 20px;
|
|
241
215
|
border-left: none;
|
|
242
216
|
border-bottom: none;
|
|
243
217
|
}
|
|
@@ -256,448 +230,441 @@
|
|
|
256
230
|
border-top: none;
|
|
257
231
|
}
|
|
258
232
|
|
|
259
|
-
/*
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
233
|
+
/* ============================================
|
|
234
|
+
NAVIGATION
|
|
235
|
+
============================================ */
|
|
236
|
+
.nav {
|
|
237
|
+
position: fixed;
|
|
238
|
+
top: 0;
|
|
239
|
+
left: 0;
|
|
240
|
+
right: 0;
|
|
241
|
+
z-index: 1000;
|
|
242
|
+
display: flex;
|
|
243
|
+
justify-content: flex-end;
|
|
244
|
+
gap: 10px;
|
|
245
|
+
padding: 20px 40px;
|
|
246
|
+
background: rgba(10, 10, 10, 0.9);
|
|
247
|
+
border-bottom: 1px dashed var(--border-color);
|
|
268
248
|
}
|
|
269
249
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
border: 1px dashed var(--grey);
|
|
250
|
+
.nav-btn {
|
|
251
|
+
background: transparent;
|
|
252
|
+
border: 1px solid var(--border-color);
|
|
253
|
+
color: var(--grey);
|
|
254
|
+
padding: 8px 16px;
|
|
255
|
+
font-family: inherit;
|
|
277
256
|
font-size: 0.75rem;
|
|
257
|
+
cursor: pointer;
|
|
258
|
+
transition: all 0.2s;
|
|
278
259
|
text-transform: uppercase;
|
|
279
|
-
letter-spacing:
|
|
260
|
+
letter-spacing: 1px;
|
|
280
261
|
}
|
|
281
262
|
|
|
282
|
-
.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
background: var(--grey);
|
|
287
|
-
animation: pulse 2s infinite;
|
|
263
|
+
.nav-btn:hover {
|
|
264
|
+
border-color: var(--accent-red);
|
|
265
|
+
color: var(--accent-red);
|
|
266
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
288
267
|
}
|
|
289
268
|
|
|
290
|
-
.
|
|
269
|
+
.nav-btn.active {
|
|
291
270
|
background: var(--accent-red);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
@keyframes pulse {
|
|
296
|
-
0%, 100% { transform: scale(1); opacity: 1; }
|
|
297
|
-
50% { transform: scale(1.2); opacity: 0.5; }
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
/* Visual data flow diagram */
|
|
301
|
-
.data-flow {
|
|
302
|
-
display: flex;
|
|
303
|
-
align-items: center;
|
|
304
|
-
justify-content: center;
|
|
305
|
-
gap: 20px;
|
|
306
|
-
margin: 30px 0;
|
|
307
|
-
flex-wrap: wrap;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
.data-node {
|
|
311
|
-
border: 1px dashed var(--grey);
|
|
312
|
-
padding: 15px 25px;
|
|
313
|
-
text-align: center;
|
|
314
|
-
position: relative;
|
|
315
|
-
min-width: 120px;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
.data-node-icon {
|
|
319
|
-
font-size: 1.5rem;
|
|
320
|
-
margin-bottom: 8px;
|
|
321
|
-
display: block;
|
|
271
|
+
border-color: var(--accent-red);
|
|
272
|
+
color: var(--white);
|
|
322
273
|
}
|
|
323
274
|
|
|
324
|
-
.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
letter-spacing: 1px;
|
|
328
|
-
color: var(--grey);
|
|
275
|
+
.nav-btn.disabled {
|
|
276
|
+
opacity: 0.4;
|
|
277
|
+
cursor: not-allowed;
|
|
329
278
|
}
|
|
330
279
|
|
|
331
|
-
.
|
|
280
|
+
.nav-btn.disabled:hover {
|
|
281
|
+
border-color: var(--border-color);
|
|
332
282
|
color: var(--grey);
|
|
333
|
-
|
|
334
|
-
animation: arrowPulse 1.5s infinite;
|
|
283
|
+
box-shadow: none;
|
|
335
284
|
}
|
|
336
285
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
286
|
+
/* ============================================
|
|
287
|
+
AUDIO PLAYER
|
|
288
|
+
============================================ */
|
|
289
|
+
.audio-progress-container {
|
|
290
|
+
position: fixed;
|
|
291
|
+
bottom: 0;
|
|
292
|
+
left: 0;
|
|
293
|
+
right: 0;
|
|
294
|
+
background: rgba(10, 10, 10, 0.95);
|
|
295
|
+
border-top: 1px dashed var(--border-color);
|
|
296
|
+
padding: 15px 40px;
|
|
297
|
+
z-index: 1001;
|
|
340
298
|
}
|
|
341
299
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
align-items: center;
|
|
346
|
-
justify-content: center;
|
|
347
|
-
width: 24px;
|
|
348
|
-
height: 24px;
|
|
349
|
-
border: 2px solid var(--grey);
|
|
350
|
-
margin-right: 12px;
|
|
300
|
+
.audio-time-display {
|
|
301
|
+
display: flex;
|
|
302
|
+
justify-content: space-between;
|
|
351
303
|
font-size: 0.8rem;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
.visual-check.checked {
|
|
356
|
-
border-color: var(--accent-red);
|
|
357
|
-
background: rgba(186, 12, 47, 0.2);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
.visual-check.checked::after {
|
|
361
|
-
content: '✓';
|
|
362
|
-
color: var(--accent-red);
|
|
304
|
+
color: var(--grey);
|
|
305
|
+
margin-bottom: 8px;
|
|
363
306
|
}
|
|
364
307
|
|
|
365
|
-
|
|
366
|
-
.highlight-underline {
|
|
308
|
+
.audio-progress-bar {
|
|
367
309
|
position: relative;
|
|
368
|
-
|
|
310
|
+
height: 12px;
|
|
311
|
+
background: var(--darker-grey);
|
|
312
|
+
border: 1px solid var(--border-color);
|
|
313
|
+
cursor: pointer;
|
|
369
314
|
}
|
|
370
315
|
|
|
371
|
-
.
|
|
372
|
-
|
|
373
|
-
position: absolute;
|
|
374
|
-
bottom: -4px;
|
|
375
|
-
left: 0;
|
|
376
|
-
width: 0;
|
|
377
|
-
height: 2px;
|
|
316
|
+
.audio-progress-fill {
|
|
317
|
+
height: 100%;
|
|
378
318
|
background: var(--accent-red);
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
width: 100%;
|
|
319
|
+
width: 0%;
|
|
320
|
+
transition: width 0.1s linear;
|
|
321
|
+
pointer-events: none;
|
|
322
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
384
323
|
}
|
|
385
324
|
|
|
386
|
-
|
|
387
|
-
.timeline-indicator {
|
|
325
|
+
.audio-section-markers {
|
|
388
326
|
position: absolute;
|
|
389
|
-
left: -40px;
|
|
390
327
|
top: 0;
|
|
391
|
-
|
|
392
|
-
width:
|
|
393
|
-
|
|
328
|
+
left: 0;
|
|
329
|
+
width: 100%;
|
|
330
|
+
height: 100%;
|
|
331
|
+
pointer-events: none;
|
|
394
332
|
}
|
|
395
333
|
|
|
396
|
-
.
|
|
397
|
-
content: '';
|
|
334
|
+
.audio-section-marker {
|
|
398
335
|
position: absolute;
|
|
399
|
-
top:
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
336
|
+
top: -2px;
|
|
337
|
+
width: 3px;
|
|
338
|
+
height: 16px;
|
|
339
|
+
background: var(--white);
|
|
340
|
+
opacity: 0.5;
|
|
341
|
+
pointer-events: auto;
|
|
342
|
+
cursor: pointer;
|
|
343
|
+
margin-left: -1px;
|
|
407
344
|
}
|
|
408
345
|
|
|
409
|
-
.
|
|
410
|
-
|
|
411
|
-
|
|
346
|
+
.audio-section-marker:hover {
|
|
347
|
+
opacity: 1;
|
|
348
|
+
background: var(--accent-red);
|
|
349
|
+
box-shadow: 0 0 8px var(--accent-red-glow);
|
|
412
350
|
}
|
|
413
351
|
|
|
414
|
-
/*
|
|
415
|
-
.
|
|
416
|
-
display:
|
|
417
|
-
gap:
|
|
418
|
-
|
|
419
|
-
|
|
352
|
+
/* Chapter buttons */
|
|
353
|
+
.chapter-buttons {
|
|
354
|
+
display: flex;
|
|
355
|
+
gap: 8px;
|
|
356
|
+
margin-bottom: 12px;
|
|
357
|
+
flex-wrap: wrap;
|
|
358
|
+
justify-content: center;
|
|
420
359
|
}
|
|
421
360
|
|
|
422
|
-
.
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
background:
|
|
426
|
-
border
|
|
427
|
-
|
|
361
|
+
.chapter-btn {
|
|
362
|
+
padding: 6px 12px;
|
|
363
|
+
font-size: 0.65rem;
|
|
364
|
+
background: transparent;
|
|
365
|
+
border: 1px solid var(--border-color);
|
|
366
|
+
color: var(--grey);
|
|
367
|
+
cursor: pointer;
|
|
368
|
+
text-transform: uppercase;
|
|
369
|
+
letter-spacing: 1px;
|
|
370
|
+
transition: all 0.2s;
|
|
428
371
|
}
|
|
429
372
|
|
|
430
|
-
.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
@keyframes typing {
|
|
434
|
-
0%, 100% { transform: translateY(0); opacity: 0.3; }
|
|
435
|
-
50% { transform: translateY(-8px); opacity: 1; }
|
|
373
|
+
.chapter-btn:hover {
|
|
374
|
+
border-color: var(--accent-red);
|
|
375
|
+
color: var(--accent-red);
|
|
436
376
|
}
|
|
437
377
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
top: 50%;
|
|
444
|
-
transform: translateY(-50%);
|
|
445
|
-
color: var(--dark-grey);
|
|
446
|
-
font-size: 1rem;
|
|
447
|
-
z-index: 2;
|
|
378
|
+
.chapter-btn.active {
|
|
379
|
+
background: var(--accent-red);
|
|
380
|
+
border-color: var(--accent-red);
|
|
381
|
+
color: var(--white);
|
|
382
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
448
383
|
}
|
|
449
384
|
|
|
450
|
-
|
|
451
|
-
|
|
385
|
+
/* Voice Modal */
|
|
386
|
+
.voice-modal-overlay {
|
|
387
|
+
position: fixed;
|
|
388
|
+
top: 0;
|
|
389
|
+
left: 0;
|
|
390
|
+
width: 100%;
|
|
391
|
+
height: 100%;
|
|
392
|
+
background: rgba(0, 0, 0, 0.8);
|
|
393
|
+
z-index: 10000;
|
|
394
|
+
display: none;
|
|
395
|
+
align-items: center;
|
|
396
|
+
justify-content: center;
|
|
452
397
|
}
|
|
453
398
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
position: relative;
|
|
399
|
+
.voice-modal-overlay.active {
|
|
400
|
+
display: flex;
|
|
457
401
|
}
|
|
458
402
|
|
|
459
|
-
.
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
bottom: -2px;
|
|
466
|
-
background: linear-gradient(45deg, transparent, var(--white), transparent);
|
|
467
|
-
opacity: 0;
|
|
468
|
-
transition: opacity 0.3s;
|
|
469
|
-
z-index: -1;
|
|
403
|
+
.voice-modal {
|
|
404
|
+
background: var(--darker-grey);
|
|
405
|
+
border: 2px solid var(--border-color);
|
|
406
|
+
padding: 30px;
|
|
407
|
+
max-width: 500px;
|
|
408
|
+
width: 90%;
|
|
470
409
|
}
|
|
471
410
|
|
|
472
|
-
.
|
|
473
|
-
|
|
411
|
+
.voice-modal-header {
|
|
412
|
+
display: flex;
|
|
413
|
+
justify-content: space-between;
|
|
414
|
+
align-items: center;
|
|
415
|
+
margin-bottom: 25px;
|
|
416
|
+
padding-bottom: 15px;
|
|
417
|
+
border-bottom: 1px dashed var(--border-color);
|
|
474
418
|
}
|
|
475
419
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
right: 20px;
|
|
481
|
-
z-index: 1000;
|
|
482
|
-
display: flex;
|
|
483
|
-
gap: 10px;
|
|
420
|
+
.voice-modal-title {
|
|
421
|
+
font-size: 1.2rem;
|
|
422
|
+
font-weight: 600;
|
|
423
|
+
color: var(--white);
|
|
484
424
|
}
|
|
485
425
|
|
|
486
|
-
.
|
|
426
|
+
.voice-modal-close {
|
|
487
427
|
background: transparent;
|
|
488
|
-
border: 1px
|
|
489
|
-
color: var(--
|
|
490
|
-
|
|
491
|
-
|
|
428
|
+
border: 1px solid var(--border-color);
|
|
429
|
+
color: var(--grey);
|
|
430
|
+
width: 32px;
|
|
431
|
+
height: 32px;
|
|
492
432
|
cursor: pointer;
|
|
493
|
-
|
|
433
|
+
font-size: 1.2rem;
|
|
434
|
+
display: flex;
|
|
435
|
+
align-items: center;
|
|
436
|
+
justify-content: center;
|
|
494
437
|
}
|
|
495
438
|
|
|
496
|
-
.
|
|
439
|
+
.voice-modal-close:hover {
|
|
497
440
|
background: var(--accent-red);
|
|
498
|
-
color: #fff;
|
|
499
441
|
border-color: var(--accent-red);
|
|
500
|
-
|
|
442
|
+
color: var(--white);
|
|
501
443
|
}
|
|
502
444
|
|
|
503
|
-
.
|
|
504
|
-
|
|
505
|
-
|
|
445
|
+
.voice-option {
|
|
446
|
+
display: flex;
|
|
447
|
+
align-items: center;
|
|
448
|
+
gap: 15px;
|
|
449
|
+
padding: 15px;
|
|
450
|
+
margin-bottom: 10px;
|
|
451
|
+
border: 1px solid var(--border-color);
|
|
452
|
+
background: var(--card-bg);
|
|
453
|
+
cursor: pointer;
|
|
454
|
+
transition: all 0.2s;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
.voice-option:hover {
|
|
506
458
|
border-color: var(--accent-red);
|
|
507
459
|
}
|
|
508
460
|
|
|
509
|
-
.
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
461
|
+
.voice-option.selected {
|
|
462
|
+
border-color: var(--accent-red);
|
|
463
|
+
background: rgba(186, 12, 47, 0.1);
|
|
464
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
513
465
|
}
|
|
514
466
|
|
|
515
|
-
.
|
|
467
|
+
.voice-play-btn {
|
|
468
|
+
width: 40px;
|
|
469
|
+
height: 40px;
|
|
470
|
+
border: 1px solid var(--border-color);
|
|
516
471
|
background: transparent;
|
|
517
472
|
color: var(--grey);
|
|
518
|
-
|
|
519
|
-
|
|
473
|
+
cursor: pointer;
|
|
474
|
+
display: flex;
|
|
475
|
+
align-items: center;
|
|
476
|
+
justify-content: center;
|
|
477
|
+
font-size: 1rem;
|
|
478
|
+
flex-shrink: 0;
|
|
520
479
|
}
|
|
521
480
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
position: fixed;
|
|
527
|
-
bottom: 60px;
|
|
528
|
-
left: 50%;
|
|
529
|
-
transform: translateX(-50%);
|
|
530
|
-
width: 80%;
|
|
531
|
-
max-width: 600px;
|
|
532
|
-
background: rgba(0, 0, 0, 0.9);
|
|
533
|
-
border: 1px dashed var(--grey);
|
|
534
|
-
padding: 15px 20px;
|
|
535
|
-
z-index: 1001;
|
|
536
|
-
border-radius: 0;
|
|
481
|
+
.voice-play-btn:hover {
|
|
482
|
+
background: var(--accent-red);
|
|
483
|
+
border-color: var(--accent-red);
|
|
484
|
+
color: var(--white);
|
|
537
485
|
}
|
|
538
486
|
|
|
539
|
-
.
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
font-size: 0.
|
|
487
|
+
.voice-info { flex: 1; }
|
|
488
|
+
|
|
489
|
+
.voice-name {
|
|
490
|
+
font-size: 0.95rem;
|
|
491
|
+
font-weight: 600;
|
|
492
|
+
margin-bottom: 4px;
|
|
493
|
+
color: var(--white);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.voice-desc {
|
|
497
|
+
font-size: 0.8rem;
|
|
543
498
|
color: var(--grey);
|
|
544
|
-
margin-bottom: 10px;
|
|
545
|
-
font-family: 'Courier New', monospace;
|
|
546
499
|
}
|
|
547
500
|
|
|
548
|
-
.
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
501
|
+
.voice-select-btn {
|
|
502
|
+
background: transparent;
|
|
503
|
+
border: 1px solid var(--border-color);
|
|
504
|
+
color: var(--grey);
|
|
505
|
+
padding: 6px 12px;
|
|
506
|
+
font-size: 0.75rem;
|
|
552
507
|
cursor: pointer;
|
|
553
508
|
}
|
|
554
509
|
|
|
555
|
-
.
|
|
556
|
-
height: 100%;
|
|
510
|
+
.voice-option.selected .voice-select-btn {
|
|
557
511
|
background: var(--accent-red);
|
|
558
|
-
|
|
559
|
-
|
|
512
|
+
border-color: var(--accent-red);
|
|
513
|
+
color: var(--white);
|
|
560
514
|
}
|
|
561
515
|
|
|
562
|
-
.
|
|
563
|
-
|
|
564
|
-
top:
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
height: 100%;
|
|
568
|
-
pointer-events: none;
|
|
516
|
+
.voice-modal-footer {
|
|
517
|
+
margin-top: 20px;
|
|
518
|
+
padding-top: 15px;
|
|
519
|
+
border-top: 1px dashed var(--border-color);
|
|
520
|
+
text-align: center;
|
|
569
521
|
}
|
|
570
522
|
|
|
571
|
-
.
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
height: 12px;
|
|
576
|
-
background: var(--white);
|
|
577
|
-
opacity: 0.5;
|
|
523
|
+
.voice-modal-note {
|
|
524
|
+
font-size: 0.8rem;
|
|
525
|
+
color: var(--grey);
|
|
526
|
+
margin-bottom: 15px;
|
|
578
527
|
}
|
|
579
528
|
|
|
580
|
-
.
|
|
581
|
-
|
|
529
|
+
.voice-confirm-btn {
|
|
530
|
+
background: var(--accent-red);
|
|
531
|
+
border: 1px solid var(--accent-red);
|
|
532
|
+
color: var(--white);
|
|
533
|
+
padding: 12px 30px;
|
|
534
|
+
font-size: 0.85rem;
|
|
535
|
+
cursor: pointer;
|
|
536
|
+
transition: all 0.2s;
|
|
582
537
|
}
|
|
583
538
|
|
|
584
|
-
|
|
539
|
+
.voice-confirm-btn:hover {
|
|
540
|
+
background: transparent;
|
|
541
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/* Audio highlight effect */
|
|
585
545
|
.audio-highlighted {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
transform: scale(1.02);
|
|
546
|
+
outline: 2px solid var(--accent-red) !important;
|
|
547
|
+
outline-offset: 4px;
|
|
589
548
|
transition: all 0.3s ease !important;
|
|
549
|
+
box-shadow: 0 0 20px var(--accent-red-glow) !important;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.audio-highlighted.solution-card,
|
|
553
|
+
.audio-highlighted.phase-box,
|
|
554
|
+
.audio-highlighted.gap-item,
|
|
555
|
+
.audio-highlighted.phase-node {
|
|
556
|
+
outline: none !important;
|
|
557
|
+
border-color: var(--accent-red) !important;
|
|
558
|
+
background: rgba(186, 12, 47, 0.15) !important;
|
|
559
|
+
box-shadow: 0 0 25px var(--accent-red-glow) !important;
|
|
590
560
|
}
|
|
591
561
|
|
|
592
562
|
/* Progress indicator */
|
|
593
563
|
.progress-bar {
|
|
594
564
|
position: fixed;
|
|
595
|
-
top:
|
|
565
|
+
top: 61px;
|
|
596
566
|
left: 0;
|
|
597
|
-
height:
|
|
567
|
+
height: 2px;
|
|
598
568
|
background: var(--accent-red);
|
|
599
569
|
width: 0%;
|
|
600
570
|
z-index: 1001;
|
|
571
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
601
572
|
}
|
|
602
573
|
|
|
603
|
-
/*
|
|
574
|
+
/* ============================================
|
|
575
|
+
SECTIONS & CONTENT
|
|
576
|
+
============================================ */
|
|
604
577
|
section {
|
|
605
578
|
min-height: 100vh;
|
|
606
579
|
display: flex;
|
|
607
580
|
flex-direction: column;
|
|
608
581
|
justify-content: center;
|
|
609
582
|
align-items: center;
|
|
610
|
-
padding:
|
|
583
|
+
padding: 120px 40px 80px;
|
|
611
584
|
position: relative;
|
|
612
585
|
}
|
|
613
586
|
|
|
614
|
-
/* ASCII border decoration */
|
|
615
587
|
.ascii-border {
|
|
616
|
-
border: 1px dashed var(--
|
|
588
|
+
border: 1px dashed var(--border-color);
|
|
617
589
|
padding: 50px;
|
|
618
590
|
position: relative;
|
|
619
|
-
max-width:
|
|
591
|
+
max-width: 900px;
|
|
620
592
|
width: 100%;
|
|
621
|
-
|
|
622
|
-
background: rgba(10, 10, 10, 0.85);
|
|
623
|
-
backdrop-filter: blur(12px);
|
|
624
|
-
-webkit-backdrop-filter: blur(12px);
|
|
593
|
+
background: rgba(10, 10, 10, 0.8);
|
|
625
594
|
}
|
|
626
595
|
|
|
627
|
-
.ascii-border::before
|
|
596
|
+
.ascii-border::before,
|
|
597
|
+
.ascii-border::after {
|
|
628
598
|
content: '+';
|
|
629
599
|
position: absolute;
|
|
600
|
+
color: var(--border-color);
|
|
601
|
+
font-size: 1rem;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.ascii-border::before {
|
|
630
605
|
top: -8px;
|
|
631
606
|
left: -8px;
|
|
632
|
-
color: var(--grey);
|
|
633
607
|
}
|
|
634
608
|
|
|
635
609
|
.ascii-border::after {
|
|
636
|
-
content: '+';
|
|
637
|
-
position: absolute;
|
|
638
610
|
bottom: -8px;
|
|
639
611
|
right: -8px;
|
|
640
|
-
color: var(--grey);
|
|
641
612
|
}
|
|
642
613
|
|
|
643
614
|
/* Typography */
|
|
644
615
|
h1 {
|
|
645
|
-
font-
|
|
646
|
-
font-
|
|
647
|
-
|
|
616
|
+
font-family: 'Fredoka', sans-serif;
|
|
617
|
+
font-size: 2.8rem;
|
|
618
|
+
font-weight: 700;
|
|
648
619
|
margin-bottom: 20px;
|
|
620
|
+
color: var(--white);
|
|
649
621
|
}
|
|
650
622
|
|
|
651
623
|
h2 {
|
|
652
|
-
font-
|
|
653
|
-
font-
|
|
654
|
-
|
|
655
|
-
margin-bottom:
|
|
624
|
+
font-family: 'Fredoka', sans-serif;
|
|
625
|
+
font-size: 2rem;
|
|
626
|
+
font-weight: 600;
|
|
627
|
+
margin-bottom: 15px;
|
|
628
|
+
color: var(--white);
|
|
656
629
|
}
|
|
657
630
|
|
|
658
631
|
h3 {
|
|
659
|
-
font-size:
|
|
632
|
+
font-size: 1rem;
|
|
660
633
|
margin-bottom: 15px;
|
|
661
634
|
text-transform: uppercase;
|
|
662
635
|
letter-spacing: 1px;
|
|
663
636
|
color: var(--grey);
|
|
664
637
|
}
|
|
665
638
|
|
|
666
|
-
/* Explanation blocks
|
|
639
|
+
/* Explanation blocks */
|
|
667
640
|
.explanation {
|
|
668
|
-
background:
|
|
669
|
-
border
|
|
641
|
+
background: var(--darker-grey);
|
|
642
|
+
border: 1px dashed var(--border-color);
|
|
643
|
+
border-left: 3px solid var(--accent-red);
|
|
670
644
|
padding: 25px 30px;
|
|
671
645
|
margin: 30px 0;
|
|
672
646
|
font-size: 1rem;
|
|
673
|
-
line-height: 1.
|
|
647
|
+
line-height: 1.8;
|
|
674
648
|
opacity: 0;
|
|
675
649
|
}
|
|
676
650
|
|
|
677
651
|
.explanation-title {
|
|
678
|
-
font-size: 0.
|
|
652
|
+
font-size: 0.75rem;
|
|
679
653
|
text-transform: uppercase;
|
|
680
|
-
letter-spacing:
|
|
681
|
-
color: var(--
|
|
654
|
+
letter-spacing: 2px;
|
|
655
|
+
color: var(--accent-red);
|
|
682
656
|
margin-bottom: 15px;
|
|
683
|
-
|
|
684
|
-
align-items: center;
|
|
685
|
-
gap: 10px;
|
|
657
|
+
font-weight: 600;
|
|
686
658
|
}
|
|
687
659
|
|
|
688
660
|
.explanation-title::before {
|
|
689
|
-
content: '
|
|
690
|
-
|
|
691
|
-
height: 24px;
|
|
692
|
-
border: 1px dashed var(--grey);
|
|
693
|
-
display: flex;
|
|
694
|
-
align-items: center;
|
|
695
|
-
justify-content: center;
|
|
696
|
-
font-size: 0.9rem;
|
|
661
|
+
content: '// ';
|
|
662
|
+
color: var(--grey);
|
|
697
663
|
}
|
|
698
664
|
|
|
699
665
|
.explanation p {
|
|
700
666
|
margin-bottom: 15px;
|
|
667
|
+
color: var(--grey);
|
|
701
668
|
}
|
|
702
669
|
|
|
703
670
|
.explanation p:last-child {
|
|
@@ -706,14 +673,13 @@
|
|
|
706
673
|
|
|
707
674
|
.explanation strong {
|
|
708
675
|
color: var(--white);
|
|
709
|
-
font-weight:
|
|
710
|
-
border-bottom: 1px dashed var(--grey);
|
|
676
|
+
font-weight: 600;
|
|
711
677
|
}
|
|
712
678
|
|
|
713
679
|
/* Real example callout */
|
|
714
680
|
.real-example {
|
|
715
|
-
background:
|
|
716
|
-
border:
|
|
681
|
+
background: var(--darker-grey);
|
|
682
|
+
border: 2px solid var(--border-color);
|
|
717
683
|
padding: 20px 25px;
|
|
718
684
|
margin: 20px 0;
|
|
719
685
|
position: relative;
|
|
@@ -724,11 +690,12 @@
|
|
|
724
690
|
position: absolute;
|
|
725
691
|
top: -10px;
|
|
726
692
|
left: 20px;
|
|
727
|
-
background: var(--
|
|
693
|
+
background: var(--card-bg);
|
|
728
694
|
padding: 0 10px;
|
|
729
|
-
font-size: 0.
|
|
695
|
+
font-size: 0.7rem;
|
|
730
696
|
letter-spacing: 2px;
|
|
731
|
-
color: var(--
|
|
697
|
+
color: var(--accent-red);
|
|
698
|
+
border: 1px solid var(--border-color);
|
|
732
699
|
}
|
|
733
700
|
|
|
734
701
|
.real-example-content {
|
|
@@ -740,7 +707,7 @@
|
|
|
740
707
|
display: inline-block;
|
|
741
708
|
width: 10px;
|
|
742
709
|
height: 20px;
|
|
743
|
-
background: var(--
|
|
710
|
+
background: var(--accent-red);
|
|
744
711
|
animation: blink 1s infinite;
|
|
745
712
|
vertical-align: middle;
|
|
746
713
|
margin-left: 5px;
|
|
@@ -758,10 +725,11 @@
|
|
|
758
725
|
justify-content: center;
|
|
759
726
|
width: 50px;
|
|
760
727
|
height: 50px;
|
|
761
|
-
border: 2px solid var(--
|
|
728
|
+
border: 2px solid var(--accent-red);
|
|
762
729
|
font-size: 1.5rem;
|
|
763
730
|
margin-right: 20px;
|
|
764
731
|
flex-shrink: 0;
|
|
732
|
+
color: var(--accent-red);
|
|
765
733
|
}
|
|
766
734
|
|
|
767
735
|
.step-header {
|
|
@@ -828,11 +796,12 @@
|
|
|
828
796
|
|
|
829
797
|
.hustle-highlight {
|
|
830
798
|
color: var(--accent-red);
|
|
831
|
-
text-shadow: 0 0
|
|
799
|
+
text-shadow: 0 0 20px var(--accent-red-glow);
|
|
832
800
|
}
|
|
833
801
|
|
|
834
802
|
.text-red {
|
|
835
803
|
color: var(--accent-red);
|
|
804
|
+
font-weight: 600;
|
|
836
805
|
}
|
|
837
806
|
|
|
838
807
|
/* Intro section centering */
|
|
@@ -863,13 +832,13 @@
|
|
|
863
832
|
}
|
|
864
833
|
|
|
865
834
|
/* ============================================
|
|
866
|
-
PHASE FLOW - LIGHTING UP SEQUENCE
|
|
835
|
+
PHASE FLOW - LIGHTING UP SEQUENCE (boxy)
|
|
867
836
|
============================================ */
|
|
868
837
|
.phase-flow {
|
|
869
838
|
display: flex;
|
|
870
839
|
align-items: center;
|
|
871
840
|
justify-content: center;
|
|
872
|
-
gap:
|
|
841
|
+
gap: 12px;
|
|
873
842
|
margin-top: 50px;
|
|
874
843
|
flex-wrap: wrap;
|
|
875
844
|
opacity: 0;
|
|
@@ -877,30 +846,31 @@
|
|
|
877
846
|
|
|
878
847
|
.phase-node {
|
|
879
848
|
position: relative;
|
|
880
|
-
border: 2px solid var(--
|
|
849
|
+
border: 2px solid var(--border-color);
|
|
850
|
+
background: var(--darker-grey);
|
|
881
851
|
padding: 20px 25px;
|
|
882
852
|
text-align: center;
|
|
883
853
|
min-width: 100px;
|
|
884
|
-
transition: all 0.
|
|
854
|
+
transition: all 0.3s ease;
|
|
885
855
|
opacity: 0;
|
|
886
856
|
transform: translateY(20px);
|
|
887
857
|
}
|
|
888
858
|
|
|
889
859
|
.phase-node .phase-glow {
|
|
890
860
|
position: absolute;
|
|
891
|
-
top:
|
|
892
|
-
left:
|
|
893
|
-
right:
|
|
894
|
-
bottom:
|
|
861
|
+
top: 0;
|
|
862
|
+
left: 0;
|
|
863
|
+
right: 0;
|
|
864
|
+
bottom: 0;
|
|
895
865
|
border: 2px solid var(--accent-red);
|
|
896
866
|
opacity: 0;
|
|
897
|
-
|
|
898
|
-
transition: opacity 0.4s ease;
|
|
867
|
+
transition: opacity 0.3s ease;
|
|
899
868
|
}
|
|
900
869
|
|
|
901
870
|
.phase-node.active {
|
|
902
871
|
border-color: var(--accent-red);
|
|
903
|
-
background: rgba(186, 12, 47, 0.
|
|
872
|
+
background: rgba(186, 12, 47, 0.2);
|
|
873
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
904
874
|
}
|
|
905
875
|
|
|
906
876
|
.phase-node.active .phase-glow {
|
|
@@ -909,7 +879,6 @@
|
|
|
909
879
|
|
|
910
880
|
.phase-node.active .phase-icon {
|
|
911
881
|
color: var(--accent-red);
|
|
912
|
-
text-shadow: 0 0 10px var(--accent-red);
|
|
913
882
|
}
|
|
914
883
|
|
|
915
884
|
.phase-node.active .phase-label {
|
|
@@ -918,31 +887,31 @@
|
|
|
918
887
|
|
|
919
888
|
.phase-icon {
|
|
920
889
|
display: block;
|
|
921
|
-
font-size: 1.
|
|
890
|
+
font-size: 1.6rem;
|
|
922
891
|
font-weight: bold;
|
|
923
892
|
margin-bottom: 8px;
|
|
924
893
|
color: var(--grey);
|
|
925
|
-
transition: all 0.
|
|
894
|
+
transition: all 0.3s ease;
|
|
926
895
|
}
|
|
927
896
|
|
|
928
897
|
.phase-label {
|
|
929
|
-
font-size: 0.
|
|
898
|
+
font-size: 0.65rem;
|
|
930
899
|
text-transform: uppercase;
|
|
931
900
|
letter-spacing: 2px;
|
|
932
|
-
color: var(--
|
|
933
|
-
transition: all 0.
|
|
901
|
+
color: var(--grey);
|
|
902
|
+
transition: all 0.3s ease;
|
|
903
|
+
font-weight: bold;
|
|
934
904
|
}
|
|
935
905
|
|
|
936
906
|
.phase-connector-arrow {
|
|
937
|
-
color: var(--
|
|
938
|
-
font-size: 1.
|
|
907
|
+
color: var(--border-color);
|
|
908
|
+
font-size: 1.2rem;
|
|
939
909
|
opacity: 0;
|
|
940
910
|
transition: all 0.3s ease;
|
|
941
911
|
}
|
|
942
912
|
|
|
943
913
|
.phase-connector-arrow.active {
|
|
944
914
|
color: var(--accent-red);
|
|
945
|
-
text-shadow: 0 0 10px var(--accent-red-glow);
|
|
946
915
|
}
|
|
947
916
|
|
|
948
917
|
@media (max-width: 768px) {
|
|
@@ -992,62 +961,67 @@
|
|
|
992
961
|
}
|
|
993
962
|
|
|
994
963
|
/* ============================================
|
|
995
|
-
SECTION 2: THE PROBLEM - GAPS
|
|
964
|
+
SECTION 2: THE PROBLEM - GAPS - boxy cards
|
|
996
965
|
============================================ */
|
|
997
966
|
.gap-list {
|
|
998
967
|
list-style: none;
|
|
999
968
|
width: 100%;
|
|
969
|
+
display: flex;
|
|
970
|
+
flex-direction: column;
|
|
971
|
+
gap: 15px;
|
|
1000
972
|
}
|
|
1001
973
|
|
|
1002
974
|
.gap-item {
|
|
1003
|
-
border:
|
|
1004
|
-
|
|
1005
|
-
|
|
975
|
+
border: 2px solid var(--border-color);
|
|
976
|
+
background: var(--darker-grey);
|
|
977
|
+
padding: 25px 30px 25px 50px;
|
|
1006
978
|
opacity: 0;
|
|
1007
979
|
transform: translateX(-30px);
|
|
1008
|
-
transition: all 0.
|
|
980
|
+
transition: all 0.2s;
|
|
1009
981
|
position: relative;
|
|
1010
982
|
}
|
|
1011
983
|
|
|
1012
984
|
.gap-item:hover {
|
|
1013
985
|
border-color: var(--accent-red);
|
|
1014
|
-
|
|
986
|
+
transform: translateX(5px);
|
|
1015
987
|
}
|
|
1016
988
|
|
|
1017
989
|
.gap-item::before {
|
|
1018
|
-
content: '
|
|
990
|
+
content: '✕';
|
|
1019
991
|
position: absolute;
|
|
1020
|
-
left:
|
|
1021
|
-
top:
|
|
1022
|
-
transform: translateY(-50%);
|
|
992
|
+
left: 18px;
|
|
993
|
+
top: 26px;
|
|
1023
994
|
color: var(--accent-red);
|
|
1024
|
-
font-size:
|
|
995
|
+
font-size: 1rem;
|
|
996
|
+
font-weight: bold;
|
|
1025
997
|
}
|
|
1026
998
|
|
|
1027
999
|
.gap-number {
|
|
1028
|
-
color: var(--
|
|
1029
|
-
font-size: 0.
|
|
1000
|
+
color: var(--accent-red);
|
|
1001
|
+
font-size: 0.75rem;
|
|
1030
1002
|
text-transform: uppercase;
|
|
1031
1003
|
letter-spacing: 2px;
|
|
1004
|
+
font-weight: bold;
|
|
1032
1005
|
}
|
|
1033
1006
|
|
|
1034
1007
|
.gap-title {
|
|
1035
|
-
font-size: 1.
|
|
1008
|
+
font-size: 1.1rem;
|
|
1036
1009
|
margin: 8px 0;
|
|
1037
1010
|
}
|
|
1038
1011
|
|
|
1039
1012
|
.gap-desc {
|
|
1040
1013
|
color: var(--grey);
|
|
1041
|
-
font-size: 0.
|
|
1014
|
+
font-size: 0.9rem;
|
|
1042
1015
|
margin-bottom: 10px;
|
|
1043
1016
|
}
|
|
1044
1017
|
|
|
1045
1018
|
.gap-example {
|
|
1046
1019
|
font-size: 0.85rem;
|
|
1047
1020
|
padding: 12px 15px;
|
|
1048
|
-
background:
|
|
1049
|
-
border
|
|
1050
|
-
|
|
1021
|
+
background: var(--card-bg);
|
|
1022
|
+
border: 1px solid var(--border-color);
|
|
1023
|
+
border-left: 3px solid var(--accent-red);
|
|
1024
|
+
margin-top: 12px;
|
|
1051
1025
|
}
|
|
1052
1026
|
|
|
1053
1027
|
.gap-example .bad {
|
|
@@ -1056,44 +1030,50 @@
|
|
|
1056
1030
|
}
|
|
1057
1031
|
|
|
1058
1032
|
.gap-example .good {
|
|
1059
|
-
color: var(--
|
|
1033
|
+
color: var(--accent-red);
|
|
1034
|
+
font-weight: bold;
|
|
1060
1035
|
}
|
|
1061
1036
|
|
|
1062
1037
|
/* ============================================
|
|
1063
|
-
SECTION 3: SOLUTION OVERVIEW
|
|
1038
|
+
SECTION 3: SOLUTION OVERVIEW - boxy grid
|
|
1064
1039
|
============================================ */
|
|
1065
1040
|
.solution-grid {
|
|
1066
1041
|
display: grid;
|
|
1067
1042
|
grid-template-columns: repeat(3, 1fr);
|
|
1068
|
-
gap:
|
|
1043
|
+
gap: 20px;
|
|
1069
1044
|
margin-top: 30px;
|
|
1070
1045
|
}
|
|
1071
1046
|
|
|
1072
1047
|
.solution-card {
|
|
1073
|
-
border:
|
|
1074
|
-
|
|
1048
|
+
border: 2px solid var(--border-color);
|
|
1049
|
+
background: var(--darker-grey);
|
|
1050
|
+
padding: 35px 25px;
|
|
1075
1051
|
text-align: center;
|
|
1076
1052
|
opacity: 0;
|
|
1077
1053
|
transform: translateY(20px);
|
|
1078
|
-
transition: all 0.
|
|
1054
|
+
transition: all 0.2s;
|
|
1079
1055
|
}
|
|
1080
1056
|
|
|
1081
1057
|
.solution-card:hover {
|
|
1082
1058
|
border-color: var(--accent-red);
|
|
1083
|
-
|
|
1059
|
+
transform: translateY(-3px);
|
|
1084
1060
|
}
|
|
1085
1061
|
|
|
1086
1062
|
.solution-icon {
|
|
1087
|
-
font-size:
|
|
1063
|
+
font-size: 1.8rem;
|
|
1088
1064
|
margin-bottom: 15px;
|
|
1089
1065
|
display: block;
|
|
1066
|
+
color: var(--accent-red);
|
|
1067
|
+
font-weight: bold;
|
|
1090
1068
|
}
|
|
1091
1069
|
|
|
1092
1070
|
.solution-title {
|
|
1093
|
-
font-size:
|
|
1094
|
-
margin-bottom:
|
|
1071
|
+
font-size: 0.95rem;
|
|
1072
|
+
margin-bottom: 12px;
|
|
1095
1073
|
text-transform: uppercase;
|
|
1096
1074
|
letter-spacing: 1px;
|
|
1075
|
+
color: var(--white);
|
|
1076
|
+
font-weight: 600;
|
|
1097
1077
|
}
|
|
1098
1078
|
|
|
1099
1079
|
.solution-desc {
|
|
@@ -1103,7 +1083,7 @@
|
|
|
1103
1083
|
}
|
|
1104
1084
|
|
|
1105
1085
|
/* ============================================
|
|
1106
|
-
SECTION 4: HOOK SYSTEM
|
|
1086
|
+
SECTION 4: HOOK SYSTEM - clean boxy flow
|
|
1107
1087
|
============================================ */
|
|
1108
1088
|
.hook-diagram {
|
|
1109
1089
|
width: 100%;
|
|
@@ -1111,49 +1091,53 @@
|
|
|
1111
1091
|
}
|
|
1112
1092
|
|
|
1113
1093
|
.flow-box {
|
|
1114
|
-
border:
|
|
1094
|
+
border: 2px solid var(--border-color);
|
|
1095
|
+
background: var(--darker-grey);
|
|
1115
1096
|
padding: 18px 30px;
|
|
1116
1097
|
margin: 12px 0;
|
|
1117
1098
|
text-align: center;
|
|
1118
1099
|
opacity: 0;
|
|
1119
|
-
transition: all 0.
|
|
1100
|
+
transition: all 0.2s;
|
|
1120
1101
|
}
|
|
1121
1102
|
|
|
1122
1103
|
.flow-box:hover {
|
|
1123
|
-
background: rgba(186, 12, 47, 0.1);
|
|
1124
|
-
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1125
1104
|
border-color: var(--accent-red);
|
|
1105
|
+
transform: translateX(5px);
|
|
1126
1106
|
}
|
|
1127
1107
|
|
|
1128
1108
|
.flow-arrow {
|
|
1129
1109
|
text-align: center;
|
|
1130
|
-
color: var(--
|
|
1110
|
+
color: var(--accent-red);
|
|
1131
1111
|
font-size: 1.5rem;
|
|
1132
1112
|
opacity: 0;
|
|
1133
1113
|
}
|
|
1134
1114
|
|
|
1135
1115
|
.hook-group {
|
|
1136
|
-
border: 2px dashed var(--
|
|
1116
|
+
border: 2px dashed var(--border-color);
|
|
1117
|
+
background: var(--darker-grey);
|
|
1137
1118
|
padding: 25px;
|
|
1138
1119
|
margin: 25px 0;
|
|
1139
1120
|
opacity: 0;
|
|
1140
1121
|
}
|
|
1141
1122
|
|
|
1142
1123
|
.hook-group h4 {
|
|
1143
|
-
color: var(--
|
|
1144
|
-
font-size: 0.
|
|
1124
|
+
color: var(--accent-red);
|
|
1125
|
+
font-size: 0.8rem;
|
|
1145
1126
|
text-transform: uppercase;
|
|
1146
1127
|
letter-spacing: 2px;
|
|
1147
1128
|
margin-bottom: 15px;
|
|
1129
|
+
font-weight: bold;
|
|
1148
1130
|
}
|
|
1149
1131
|
|
|
1150
1132
|
.hook-file {
|
|
1151
1133
|
padding: 12px 20px;
|
|
1152
1134
|
margin: 8px 0;
|
|
1153
|
-
border
|
|
1135
|
+
border: 1px solid var(--border-color);
|
|
1136
|
+
border-left: 3px solid var(--grey);
|
|
1137
|
+
background: var(--card-bg);
|
|
1154
1138
|
font-size: 0.9rem;
|
|
1155
1139
|
opacity: 0;
|
|
1156
|
-
transition: all 0.
|
|
1140
|
+
transition: all 0.2s;
|
|
1157
1141
|
display: flex;
|
|
1158
1142
|
justify-content: space-between;
|
|
1159
1143
|
align-items: center;
|
|
@@ -1161,22 +1145,23 @@
|
|
|
1161
1145
|
|
|
1162
1146
|
.hook-file:hover {
|
|
1163
1147
|
border-left-color: var(--accent-red);
|
|
1164
|
-
|
|
1165
|
-
padding-left: 25px;
|
|
1148
|
+
border-color: var(--accent-red);
|
|
1166
1149
|
}
|
|
1167
1150
|
|
|
1168
1151
|
.hook-file code {
|
|
1169
1152
|
color: var(--white);
|
|
1153
|
+
font-weight: 500;
|
|
1170
1154
|
}
|
|
1171
1155
|
|
|
1172
1156
|
.hook-purpose {
|
|
1173
1157
|
color: var(--grey);
|
|
1174
|
-
font-size: 0.
|
|
1158
|
+
font-size: 0.75rem;
|
|
1159
|
+
text-transform: uppercase;
|
|
1175
1160
|
}
|
|
1176
1161
|
|
|
1177
1162
|
.result-box {
|
|
1178
1163
|
display: flex;
|
|
1179
|
-
gap:
|
|
1164
|
+
gap: 30px;
|
|
1180
1165
|
justify-content: center;
|
|
1181
1166
|
margin-top: 25px;
|
|
1182
1167
|
}
|
|
@@ -1184,12 +1169,15 @@
|
|
|
1184
1169
|
.result-allowed, .result-blocked {
|
|
1185
1170
|
padding: 18px 35px;
|
|
1186
1171
|
border: 2px solid;
|
|
1172
|
+
background: var(--card-bg);
|
|
1187
1173
|
opacity: 0;
|
|
1188
|
-
transition: all 0.
|
|
1174
|
+
transition: all 0.2s;
|
|
1175
|
+
font-weight: bold;
|
|
1189
1176
|
}
|
|
1190
1177
|
|
|
1191
1178
|
.result-allowed {
|
|
1192
1179
|
border-color: var(--grey);
|
|
1180
|
+
color: var(--grey);
|
|
1193
1181
|
}
|
|
1194
1182
|
|
|
1195
1183
|
.result-blocked {
|
|
@@ -1198,62 +1186,65 @@
|
|
|
1198
1186
|
}
|
|
1199
1187
|
|
|
1200
1188
|
.result-allowed:hover, .result-blocked:hover {
|
|
1201
|
-
transform: scale(1.
|
|
1189
|
+
transform: scale(1.03);
|
|
1202
1190
|
}
|
|
1203
1191
|
|
|
1204
1192
|
/* ============================================
|
|
1205
|
-
SECTION 5: 10-PHASE WORKFLOW
|
|
1193
|
+
SECTION 5: 10-PHASE WORKFLOW - boxy grid
|
|
1206
1194
|
============================================ */
|
|
1207
1195
|
.phase-grid {
|
|
1208
1196
|
display: grid;
|
|
1209
1197
|
grid-template-columns: repeat(5, 1fr);
|
|
1210
|
-
gap:
|
|
1198
|
+
gap: 15px;
|
|
1211
1199
|
width: 100%;
|
|
1212
1200
|
margin-top: 30px;
|
|
1213
1201
|
}
|
|
1214
1202
|
|
|
1215
1203
|
.phase-box {
|
|
1216
|
-
border:
|
|
1204
|
+
border: 2px solid var(--border-color);
|
|
1205
|
+
background: var(--darker-grey);
|
|
1217
1206
|
padding: 25px 15px;
|
|
1218
1207
|
text-align: center;
|
|
1219
1208
|
opacity: 0;
|
|
1220
1209
|
transform: scale(0.9);
|
|
1221
|
-
transition: all 0.
|
|
1210
|
+
transition: all 0.2s;
|
|
1222
1211
|
position: relative;
|
|
1223
1212
|
}
|
|
1224
1213
|
|
|
1225
1214
|
.phase-box:hover {
|
|
1226
1215
|
border-color: var(--accent-red);
|
|
1227
|
-
box-shadow: 0 0 25px var(--accent-red-glow);
|
|
1228
1216
|
transform: scale(1.02);
|
|
1229
1217
|
}
|
|
1230
1218
|
|
|
1231
1219
|
.phase-box.active {
|
|
1232
1220
|
border-color: var(--accent-red);
|
|
1233
|
-
background: rgba(186, 12, 47, 0.
|
|
1221
|
+
background: rgba(186, 12, 47, 0.2);
|
|
1222
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1234
1223
|
}
|
|
1235
1224
|
|
|
1236
1225
|
.phase-number {
|
|
1237
|
-
font-size:
|
|
1238
|
-
color: var(--
|
|
1226
|
+
font-size: 2rem;
|
|
1227
|
+
color: var(--grey);
|
|
1239
1228
|
margin-bottom: 10px;
|
|
1229
|
+
font-weight: bold;
|
|
1240
1230
|
}
|
|
1241
1231
|
|
|
1242
1232
|
.phase-box.active .phase-number {
|
|
1243
|
-
color: var(--
|
|
1233
|
+
color: var(--accent-red);
|
|
1244
1234
|
}
|
|
1245
1235
|
|
|
1246
1236
|
.phase-name {
|
|
1247
|
-
font-size: 0.
|
|
1237
|
+
font-size: 0.7rem;
|
|
1248
1238
|
text-transform: uppercase;
|
|
1249
1239
|
letter-spacing: 1px;
|
|
1240
|
+
font-weight: bold;
|
|
1250
1241
|
}
|
|
1251
1242
|
|
|
1252
1243
|
.phase-status {
|
|
1253
1244
|
position: absolute;
|
|
1254
1245
|
top: 8px;
|
|
1255
1246
|
right: 8px;
|
|
1256
|
-
font-size: 0.
|
|
1247
|
+
font-size: 0.65rem;
|
|
1257
1248
|
color: var(--grey);
|
|
1258
1249
|
}
|
|
1259
1250
|
|
|
@@ -1267,26 +1258,26 @@
|
|
|
1267
1258
|
}
|
|
1268
1259
|
|
|
1269
1260
|
.phase-desc {
|
|
1270
|
-
font-size: 0.
|
|
1261
|
+
font-size: 0.65rem;
|
|
1271
1262
|
color: var(--grey);
|
|
1272
1263
|
margin-top: 8px;
|
|
1273
1264
|
line-height: 1.4;
|
|
1274
1265
|
}
|
|
1275
1266
|
|
|
1276
1267
|
/* ============================================
|
|
1277
|
-
SECTION 6: REAL WALKTHROUGH
|
|
1268
|
+
SECTION 6: REAL WALKTHROUGH - boxy steps
|
|
1278
1269
|
============================================ */
|
|
1279
1270
|
.walkthrough-step {
|
|
1280
|
-
border:
|
|
1271
|
+
border: 2px solid var(--border-color);
|
|
1272
|
+
background: var(--darker-grey);
|
|
1281
1273
|
padding: 30px;
|
|
1282
|
-
margin-bottom:
|
|
1274
|
+
margin-bottom: 20px;
|
|
1283
1275
|
opacity: 0;
|
|
1284
1276
|
transform: translateY(20px);
|
|
1285
1277
|
}
|
|
1286
1278
|
|
|
1287
1279
|
.walkthrough-step.active {
|
|
1288
1280
|
border-color: var(--accent-red);
|
|
1289
|
-
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1290
1281
|
}
|
|
1291
1282
|
|
|
1292
1283
|
.walkthrough-header {
|
|
@@ -1299,16 +1290,19 @@
|
|
|
1299
1290
|
.walkthrough-num {
|
|
1300
1291
|
width: 45px;
|
|
1301
1292
|
height: 45px;
|
|
1302
|
-
border: 2px solid var(--
|
|
1293
|
+
border: 2px solid var(--accent-red);
|
|
1294
|
+
background: transparent;
|
|
1303
1295
|
display: flex;
|
|
1304
1296
|
align-items: center;
|
|
1305
1297
|
justify-content: center;
|
|
1306
|
-
font-size: 1.
|
|
1298
|
+
font-size: 1.2rem;
|
|
1307
1299
|
flex-shrink: 0;
|
|
1300
|
+
color: var(--accent-red);
|
|
1301
|
+
font-weight: bold;
|
|
1308
1302
|
}
|
|
1309
1303
|
|
|
1310
1304
|
.walkthrough-title {
|
|
1311
|
-
font-size:
|
|
1305
|
+
font-size: 1rem;
|
|
1312
1306
|
text-transform: uppercase;
|
|
1313
1307
|
letter-spacing: 1px;
|
|
1314
1308
|
}
|
|
@@ -1324,26 +1318,28 @@
|
|
|
1324
1318
|
}
|
|
1325
1319
|
|
|
1326
1320
|
.walkthrough-example {
|
|
1327
|
-
background:
|
|
1321
|
+
background: var(--card-bg);
|
|
1322
|
+
border: 1px solid var(--border-color);
|
|
1328
1323
|
padding: 15px 20px;
|
|
1329
1324
|
font-size: 0.85rem;
|
|
1330
|
-
border-left: 3px solid var(--
|
|
1325
|
+
border-left: 3px solid var(--accent-red);
|
|
1331
1326
|
}
|
|
1332
1327
|
|
|
1333
1328
|
.walkthrough-example .label {
|
|
1334
|
-
color: var(--
|
|
1335
|
-
font-size: 0.
|
|
1329
|
+
color: var(--accent-red);
|
|
1330
|
+
font-size: 0.7rem;
|
|
1336
1331
|
text-transform: uppercase;
|
|
1337
1332
|
letter-spacing: 1px;
|
|
1338
1333
|
margin-bottom: 8px;
|
|
1334
|
+
font-weight: bold;
|
|
1339
1335
|
}
|
|
1340
1336
|
|
|
1341
1337
|
/* ============================================
|
|
1342
|
-
SECTION 7: LIVE DEMO TERMINAL
|
|
1338
|
+
SECTION 7: LIVE DEMO TERMINAL - boxy style
|
|
1343
1339
|
============================================ */
|
|
1344
1340
|
.terminal {
|
|
1345
|
-
background:
|
|
1346
|
-
border:
|
|
1341
|
+
background: var(--card-bg);
|
|
1342
|
+
border: 2px solid var(--border-color);
|
|
1347
1343
|
padding: 25px;
|
|
1348
1344
|
width: 100%;
|
|
1349
1345
|
font-size: 0.85rem;
|
|
@@ -1355,20 +1351,27 @@
|
|
|
1355
1351
|
gap: 8px;
|
|
1356
1352
|
margin-bottom: 20px;
|
|
1357
1353
|
padding-bottom: 15px;
|
|
1358
|
-
border-bottom: 1px dashed var(--
|
|
1354
|
+
border-bottom: 1px dashed var(--border-color);
|
|
1359
1355
|
}
|
|
1360
1356
|
|
|
1361
1357
|
.terminal-dot {
|
|
1362
1358
|
width: 12px;
|
|
1363
1359
|
height: 12px;
|
|
1364
|
-
border-radius:
|
|
1365
|
-
border:
|
|
1360
|
+
border-radius: 0;
|
|
1361
|
+
border: 2px solid var(--border-color);
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
.terminal-dot:first-child {
|
|
1365
|
+
border-color: var(--accent-red);
|
|
1366
|
+
background: var(--accent-red);
|
|
1366
1367
|
}
|
|
1367
1368
|
|
|
1368
1369
|
.terminal-title {
|
|
1369
1370
|
margin-left: auto;
|
|
1370
1371
|
color: var(--grey);
|
|
1371
|
-
font-size: 0.
|
|
1372
|
+
font-size: 0.75rem;
|
|
1373
|
+
text-transform: uppercase;
|
|
1374
|
+
letter-spacing: 1px;
|
|
1372
1375
|
}
|
|
1373
1376
|
|
|
1374
1377
|
.terminal-line {
|
|
@@ -1380,8 +1383,9 @@
|
|
|
1380
1383
|
}
|
|
1381
1384
|
|
|
1382
1385
|
.terminal-prompt {
|
|
1383
|
-
color: var(--
|
|
1386
|
+
color: var(--accent-red);
|
|
1384
1387
|
flex-shrink: 0;
|
|
1388
|
+
font-weight: bold;
|
|
1385
1389
|
}
|
|
1386
1390
|
|
|
1387
1391
|
.terminal-command {
|
|
@@ -1400,8 +1404,8 @@
|
|
|
1400
1404
|
}
|
|
1401
1405
|
|
|
1402
1406
|
.terminal-allowed {
|
|
1403
|
-
color:
|
|
1404
|
-
border-left: 3px solid
|
|
1407
|
+
color: #22c55e;
|
|
1408
|
+
border-left: 3px solid #22c55e;
|
|
1405
1409
|
padding-left: 15px;
|
|
1406
1410
|
}
|
|
1407
1411
|
|
|
@@ -1411,18 +1415,18 @@
|
|
|
1411
1415
|
}
|
|
1412
1416
|
|
|
1413
1417
|
.terminal-comment {
|
|
1414
|
-
color: var(--
|
|
1415
|
-
font-size: 0.
|
|
1418
|
+
color: var(--grey);
|
|
1419
|
+
font-size: 0.75rem;
|
|
1416
1420
|
padding-left: 25px;
|
|
1417
1421
|
margin-bottom: 5px;
|
|
1418
1422
|
}
|
|
1419
1423
|
|
|
1420
1424
|
/* ============================================
|
|
1421
|
-
SECTION 8: STATE FILE
|
|
1425
|
+
SECTION 8: STATE FILE - boxy JSON viewer
|
|
1422
1426
|
============================================ */
|
|
1423
1427
|
.json-viewer {
|
|
1424
|
-
background:
|
|
1425
|
-
border:
|
|
1428
|
+
background: var(--card-bg);
|
|
1429
|
+
border: 2px solid var(--border-color);
|
|
1426
1430
|
padding: 25px;
|
|
1427
1431
|
font-size: 0.85rem;
|
|
1428
1432
|
width: 100%;
|
|
@@ -1430,7 +1434,8 @@
|
|
|
1430
1434
|
}
|
|
1431
1435
|
|
|
1432
1436
|
.json-key {
|
|
1433
|
-
color: var(--
|
|
1437
|
+
color: var(--accent-red);
|
|
1438
|
+
font-weight: 600;
|
|
1434
1439
|
}
|
|
1435
1440
|
|
|
1436
1441
|
.json-value {
|
|
@@ -1447,33 +1452,36 @@
|
|
|
1447
1452
|
}
|
|
1448
1453
|
|
|
1449
1454
|
.json-line.highlight {
|
|
1450
|
-
background: rgba(186, 12, 47, 0.
|
|
1455
|
+
background: rgba(186, 12, 47, 0.1);
|
|
1451
1456
|
margin: 0 -25px;
|
|
1452
1457
|
padding-left: 25px;
|
|
1453
1458
|
padding-right: 25px;
|
|
1454
|
-
border-left:
|
|
1459
|
+
border-left: 3px solid var(--accent-red);
|
|
1455
1460
|
}
|
|
1456
1461
|
|
|
1457
1462
|
.json-comment {
|
|
1458
|
-
color: var(--
|
|
1459
|
-
font-size: 0.
|
|
1463
|
+
color: var(--grey);
|
|
1464
|
+
font-size: 0.7rem;
|
|
1460
1465
|
margin-left: 20px;
|
|
1466
|
+
text-transform: uppercase;
|
|
1461
1467
|
}
|
|
1462
1468
|
|
|
1463
1469
|
/* ============================================
|
|
1464
|
-
SECTION 9: INSTALLATION
|
|
1470
|
+
SECTION 9: INSTALLATION - boxy style
|
|
1465
1471
|
============================================ */
|
|
1466
1472
|
.install-command {
|
|
1467
|
-
background:
|
|
1468
|
-
border:
|
|
1469
|
-
padding:
|
|
1473
|
+
background: var(--darker-grey);
|
|
1474
|
+
border: 2px solid var(--accent-red);
|
|
1475
|
+
padding: 25px 35px;
|
|
1470
1476
|
font-size: 1.1rem;
|
|
1471
1477
|
margin: 30px 0;
|
|
1472
1478
|
text-align: center;
|
|
1479
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1473
1480
|
}
|
|
1474
1481
|
|
|
1475
1482
|
.install-command code {
|
|
1476
|
-
color: var(--
|
|
1483
|
+
color: var(--accent-red);
|
|
1484
|
+
font-weight: bold;
|
|
1477
1485
|
}
|
|
1478
1486
|
|
|
1479
1487
|
.install-flow {
|
|
@@ -1484,25 +1492,29 @@
|
|
|
1484
1492
|
display: flex;
|
|
1485
1493
|
align-items: center;
|
|
1486
1494
|
gap: 25px;
|
|
1487
|
-
padding: 20px
|
|
1488
|
-
|
|
1495
|
+
padding: 20px;
|
|
1496
|
+
margin-bottom: 15px;
|
|
1497
|
+
border: 2px solid var(--border-color);
|
|
1498
|
+
background: var(--darker-grey);
|
|
1489
1499
|
opacity: 0;
|
|
1490
1500
|
transform: translateY(20px);
|
|
1491
1501
|
}
|
|
1492
1502
|
|
|
1493
1503
|
.install-step:last-child {
|
|
1494
|
-
|
|
1504
|
+
margin-bottom: 0;
|
|
1495
1505
|
}
|
|
1496
1506
|
|
|
1497
1507
|
.install-icon {
|
|
1498
1508
|
width: 50px;
|
|
1499
1509
|
height: 50px;
|
|
1500
|
-
border:
|
|
1510
|
+
border: 2px solid var(--accent-red);
|
|
1511
|
+
background: transparent;
|
|
1501
1512
|
display: flex;
|
|
1502
1513
|
align-items: center;
|
|
1503
1514
|
justify-content: center;
|
|
1504
1515
|
flex-shrink: 0;
|
|
1505
1516
|
font-size: 1.2rem;
|
|
1517
|
+
color: var(--accent-red);
|
|
1506
1518
|
}
|
|
1507
1519
|
|
|
1508
1520
|
.install-content {
|
|
@@ -1511,28 +1523,30 @@
|
|
|
1511
1523
|
|
|
1512
1524
|
.install-from {
|
|
1513
1525
|
color: var(--grey);
|
|
1514
|
-
font-size: 0.
|
|
1526
|
+
font-size: 0.85rem;
|
|
1515
1527
|
}
|
|
1516
1528
|
|
|
1517
1529
|
.install-arrow {
|
|
1518
|
-
color: var(--
|
|
1530
|
+
color: var(--accent-red);
|
|
1519
1531
|
flex-shrink: 0;
|
|
1520
1532
|
font-size: 1.2rem;
|
|
1533
|
+
font-weight: bold;
|
|
1521
1534
|
}
|
|
1522
1535
|
|
|
1523
1536
|
.install-to {
|
|
1524
1537
|
color: var(--white);
|
|
1525
|
-
font-size: 0.
|
|
1538
|
+
font-size: 0.9rem;
|
|
1539
|
+
font-weight: 500;
|
|
1526
1540
|
}
|
|
1527
1541
|
|
|
1528
1542
|
.install-note {
|
|
1529
1543
|
color: var(--grey);
|
|
1530
|
-
font-size: 0.
|
|
1544
|
+
font-size: 0.75rem;
|
|
1531
1545
|
margin-top: 5px;
|
|
1532
1546
|
}
|
|
1533
1547
|
|
|
1534
1548
|
/* ============================================
|
|
1535
|
-
SECTION 10: CREDITS
|
|
1549
|
+
SECTION 10: CREDITS - boxy style
|
|
1536
1550
|
============================================ */
|
|
1537
1551
|
#credits {
|
|
1538
1552
|
text-align: center;
|
|
@@ -1540,64 +1554,69 @@
|
|
|
1540
1554
|
|
|
1541
1555
|
.credit-links {
|
|
1542
1556
|
display: flex;
|
|
1543
|
-
gap:
|
|
1557
|
+
gap: 20px;
|
|
1544
1558
|
margin-top: 40px;
|
|
1545
1559
|
flex-wrap: wrap;
|
|
1546
1560
|
justify-content: center;
|
|
1547
1561
|
}
|
|
1548
1562
|
|
|
1549
1563
|
.credit-link {
|
|
1550
|
-
border:
|
|
1564
|
+
border: 2px solid var(--border-color);
|
|
1565
|
+
background: var(--darker-grey);
|
|
1551
1566
|
padding: 18px 35px;
|
|
1552
1567
|
text-decoration: none;
|
|
1553
1568
|
color: var(--white);
|
|
1554
|
-
transition: all 0.
|
|
1569
|
+
transition: all 0.2s;
|
|
1555
1570
|
opacity: 0;
|
|
1571
|
+
text-transform: uppercase;
|
|
1572
|
+
letter-spacing: 1px;
|
|
1573
|
+
font-size: 0.85rem;
|
|
1556
1574
|
}
|
|
1557
1575
|
|
|
1558
1576
|
.credit-link:hover {
|
|
1559
1577
|
background: var(--accent-red);
|
|
1560
|
-
color:
|
|
1578
|
+
color: var(--white);
|
|
1561
1579
|
border-color: var(--accent-red);
|
|
1562
|
-
box-shadow: 0 0
|
|
1580
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
1563
1581
|
}
|
|
1564
1582
|
|
|
1565
1583
|
.made-with {
|
|
1566
1584
|
margin-top: 50px;
|
|
1567
1585
|
color: var(--grey);
|
|
1568
|
-
font-size: 0.
|
|
1586
|
+
font-size: 0.9rem;
|
|
1569
1587
|
opacity: 0;
|
|
1570
1588
|
line-height: 1.8;
|
|
1571
1589
|
}
|
|
1572
1590
|
|
|
1573
1591
|
/* ============================================
|
|
1574
|
-
SECTION INDICATORS
|
|
1592
|
+
SECTION INDICATORS - square boxy dots
|
|
1575
1593
|
============================================ */
|
|
1576
1594
|
.section-indicator {
|
|
1577
1595
|
position: fixed;
|
|
1578
|
-
right:
|
|
1596
|
+
right: 20px;
|
|
1579
1597
|
top: 50%;
|
|
1580
1598
|
transform: translateY(-50%);
|
|
1581
1599
|
display: flex;
|
|
1582
1600
|
flex-direction: column;
|
|
1583
|
-
gap:
|
|
1601
|
+
gap: 10px;
|
|
1584
1602
|
z-index: 1000;
|
|
1585
1603
|
}
|
|
1586
1604
|
|
|
1587
1605
|
.section-dot {
|
|
1588
1606
|
width: 10px;
|
|
1589
1607
|
height: 10px;
|
|
1590
|
-
border:
|
|
1591
|
-
border-radius:
|
|
1608
|
+
border: 2px solid var(--border-color);
|
|
1609
|
+
border-radius: 0;
|
|
1592
1610
|
cursor: pointer;
|
|
1593
|
-
transition: all 0.
|
|
1611
|
+
transition: all 0.2s;
|
|
1612
|
+
background: var(--card-bg);
|
|
1594
1613
|
}
|
|
1595
1614
|
|
|
1596
1615
|
.section-dot:hover,
|
|
1597
1616
|
.section-dot.active {
|
|
1598
1617
|
background: var(--accent-red);
|
|
1599
1618
|
border-color: var(--accent-red);
|
|
1600
|
-
box-shadow: 0 0
|
|
1619
|
+
box-shadow: 0 0 8px var(--accent-red-glow);
|
|
1601
1620
|
}
|
|
1602
1621
|
|
|
1603
1622
|
/* ============================================
|
|
@@ -1680,16 +1699,82 @@
|
|
|
1680
1699
|
<nav class="nav">
|
|
1681
1700
|
<button class="nav-btn" id="playBtn">▶ AUTO PLAY</button>
|
|
1682
1701
|
<button class="nav-btn" id="audioToggleBtn" title="Toggle narration audio">🔊 WITH AUDIO</button>
|
|
1702
|
+
<button class="nav-btn" id="voiceSelectBtn" title="Choose narrator voice">🎙 VOICE</button>
|
|
1683
1703
|
<button class="nav-btn" id="resetBtn">↺ RESTART</button>
|
|
1684
1704
|
</nav>
|
|
1685
1705
|
|
|
1706
|
+
<!-- Voice Preview Modal -->
|
|
1707
|
+
<div class="voice-modal-overlay" id="voiceModalOverlay">
|
|
1708
|
+
<div class="voice-modal">
|
|
1709
|
+
<div class="voice-modal-header">
|
|
1710
|
+
<div class="voice-modal-title">Choose Narrator Voice</div>
|
|
1711
|
+
<button class="voice-modal-close" id="voiceModalClose">×</button>
|
|
1712
|
+
</div>
|
|
1713
|
+
|
|
1714
|
+
<div class="voice-options">
|
|
1715
|
+
<div class="voice-option selected" data-voice="adam" data-file="audio/narration-adam.mp3" data-timing="audio/narration-adam-timing.json" data-duration="370.8">
|
|
1716
|
+
<button class="voice-play-btn" data-preview="audio/narration-adam.mp3">▶</button>
|
|
1717
|
+
<div class="voice-info">
|
|
1718
|
+
<div class="voice-name">Adam</div>
|
|
1719
|
+
<div class="voice-desc">Deep, professional (~6 min)</div>
|
|
1720
|
+
</div>
|
|
1721
|
+
<button class="voice-select-btn">Selected</button>
|
|
1722
|
+
</div>
|
|
1723
|
+
|
|
1724
|
+
<div class="voice-option" data-voice="mark" data-file="audio/narration-mark.mp3" data-timing="audio/narration-mark-timing.json" data-duration="338.7">
|
|
1725
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-mark.mp3">▶</button>
|
|
1726
|
+
<div class="voice-info">
|
|
1727
|
+
<div class="voice-name">Mark</div>
|
|
1728
|
+
<div class="voice-desc">Warm, conversational (~5.5 min)</div>
|
|
1729
|
+
</div>
|
|
1730
|
+
<button class="voice-select-btn">Select</button>
|
|
1731
|
+
</div>
|
|
1732
|
+
|
|
1733
|
+
<div class="voice-option" data-voice="hope" data-file="audio/narration-hope.mp3" data-timing="audio/narration-hope-timing.json" data-duration="305.2">
|
|
1734
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-hope.mp3">▶</button>
|
|
1735
|
+
<div class="voice-info">
|
|
1736
|
+
<div class="voice-name">Hope</div>
|
|
1737
|
+
<div class="voice-desc">Bright, energetic (~5 min)</div>
|
|
1738
|
+
</div>
|
|
1739
|
+
<button class="voice-select-btn">Select</button>
|
|
1740
|
+
</div>
|
|
1741
|
+
|
|
1742
|
+
<div class="voice-option" data-voice="creature" data-file="audio/narration-creature.mp3" data-timing="audio/narration-creature-timing.json" data-duration="466.6">
|
|
1743
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-creature.mp3">▶</button>
|
|
1744
|
+
<div class="voice-info">
|
|
1745
|
+
<div class="voice-name">Creature</div>
|
|
1746
|
+
<div class="voice-desc">Unique, character voice (~7.5 min)</div>
|
|
1747
|
+
</div>
|
|
1748
|
+
<button class="voice-select-btn">Select</button>
|
|
1749
|
+
</div>
|
|
1750
|
+
|
|
1751
|
+
<div class="voice-option" data-voice="gaming" data-file="audio/narration-gaming.mp3" data-timing="audio/narration-gaming-timing.json" data-duration="644.1">
|
|
1752
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-gaming.mp3">▶</button>
|
|
1753
|
+
<div class="voice-info">
|
|
1754
|
+
<div class="voice-name">Gaming</div>
|
|
1755
|
+
<div class="voice-desc">Dynamic, enthusiastic (~10.5 min)</div>
|
|
1756
|
+
</div>
|
|
1757
|
+
<button class="voice-select-btn">Select</button>
|
|
1758
|
+
</div>
|
|
1759
|
+
</div>
|
|
1760
|
+
|
|
1761
|
+
<div class="voice-modal-footer">
|
|
1762
|
+
<div class="voice-modal-note">All voices have full narration with synchronized highlights.</div>
|
|
1763
|
+
<button class="voice-confirm-btn" id="voiceConfirmBtn">Confirm Selection</button>
|
|
1764
|
+
</div>
|
|
1765
|
+
</div>
|
|
1766
|
+
</div>
|
|
1767
|
+
|
|
1686
1768
|
<!-- Audio Narration Player (hidden) -->
|
|
1687
1769
|
<audio id="narrationAudio" preload="auto">
|
|
1688
|
-
<source src="audio/narration.mp3" type="audio/mpeg">
|
|
1770
|
+
<source src="audio/narration-adam.mp3" type="audio/mpeg">
|
|
1689
1771
|
</audio>
|
|
1690
1772
|
|
|
1691
1773
|
<!-- Audio Progress Bar -->
|
|
1692
1774
|
<div class="audio-progress-container" id="audioProgressContainer" style="display: none;">
|
|
1775
|
+
<div class="chapter-buttons" id="chapterButtons">
|
|
1776
|
+
<!-- Chapter buttons populated dynamically from timing data -->
|
|
1777
|
+
</div>
|
|
1693
1778
|
<div class="audio-time-display">
|
|
1694
1779
|
<span id="audioCurrentTime">0:00</span>
|
|
1695
1780
|
<span id="audioTotalTime">0:00</span>
|
|
@@ -1713,7 +1798,7 @@
|
|
|
1713
1798
|
<span class="hustle-word hustle-highlight">API-DEV-TOOLS</span>
|
|
1714
1799
|
</div>
|
|
1715
1800
|
<div class="package-name" id="packageName">@hustle-together/api-dev-tools</div>
|
|
1716
|
-
<div class="version" id="versionText">v2.0.
|
|
1801
|
+
<div class="version" id="versionText">v2.0.8</div>
|
|
1717
1802
|
<p class="tagline">"Hustle together. Share resources. Build stronger."<span class="cursor"></span></p>
|
|
1718
1803
|
|
|
1719
1804
|
<div class="intro-text">
|
|
@@ -2546,11 +2631,13 @@
|
|
|
2546
2631
|
const phaseArrows = document.querySelectorAll('#introFlow .phase-connector-arrow');
|
|
2547
2632
|
|
|
2548
2633
|
// Create a separate timeline for the lighting sequence that loops
|
|
2549
|
-
|
|
2634
|
+
// Store globally so we can pause during narration
|
|
2635
|
+
window.introLightingTL = gsap.timeline({
|
|
2550
2636
|
repeat: -1,
|
|
2551
2637
|
repeatDelay: 1,
|
|
2552
2638
|
delay: 3 // Wait for intro animation to complete
|
|
2553
2639
|
});
|
|
2640
|
+
const lightingTL = window.introLightingTL;
|
|
2554
2641
|
|
|
2555
2642
|
phaseNodes.forEach((node, i) => {
|
|
2556
2643
|
lightingTL
|
|
@@ -2782,6 +2869,7 @@
|
|
|
2782
2869
|
const audioCurrentTime = document.getElementById('audioCurrentTime');
|
|
2783
2870
|
const audioTotalTime = document.getElementById('audioTotalTime');
|
|
2784
2871
|
const audioSectionMarkers = document.getElementById('audioSectionMarkers');
|
|
2872
|
+
const chapterButtons = document.getElementById('chapterButtons');
|
|
2785
2873
|
|
|
2786
2874
|
let audioEnabled = true; // Audio on by default
|
|
2787
2875
|
let timingData = null;
|
|
@@ -2797,11 +2885,15 @@
|
|
|
2797
2885
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
2798
2886
|
}
|
|
2799
2887
|
|
|
2800
|
-
//
|
|
2801
|
-
|
|
2802
|
-
|
|
2888
|
+
// Current timing URL (can be changed when voice changes)
|
|
2889
|
+
let currentTimingUrl = 'audio/narration-adam-timing.json';
|
|
2890
|
+
|
|
2891
|
+
// Load timing data (pass URL to force reload with different voice)
|
|
2892
|
+
async function loadTimingData(url = null, forceReload = false) {
|
|
2893
|
+
if (url) currentTimingUrl = url;
|
|
2894
|
+
if (timingData && !forceReload) return timingData;
|
|
2803
2895
|
try {
|
|
2804
|
-
const response = await fetch(
|
|
2896
|
+
const response = await fetch(currentTimingUrl);
|
|
2805
2897
|
if (!response.ok) throw new Error('Failed to load timing data');
|
|
2806
2898
|
timingData = await response.json();
|
|
2807
2899
|
|
|
@@ -2823,6 +2915,116 @@
|
|
|
2823
2915
|
});
|
|
2824
2916
|
}
|
|
2825
2917
|
|
|
2918
|
+
// Create chapter buttons for navigation
|
|
2919
|
+
if (chapterButtons && timingData.sections) {
|
|
2920
|
+
chapterButtons.innerHTML = '';
|
|
2921
|
+
timingData.sections.forEach((section, i) => {
|
|
2922
|
+
const btn = document.createElement('button');
|
|
2923
|
+
btn.className = 'chapter-btn';
|
|
2924
|
+
btn.textContent = section.id.toUpperCase();
|
|
2925
|
+
btn.dataset.timestamp = section.timestamp;
|
|
2926
|
+
btn.dataset.sectionId = section.id;
|
|
2927
|
+
btn.dataset.index = i;
|
|
2928
|
+
|
|
2929
|
+
// Click handler: seek audio to timestamp + scroll to section
|
|
2930
|
+
btn.addEventListener('click', (e) => {
|
|
2931
|
+
e.stopPropagation(); // Prevent bubbling to progress bar
|
|
2932
|
+
|
|
2933
|
+
const seekTime = section.timestamp;
|
|
2934
|
+
console.log(`Chapter click: seeking to ${section.id} at ${seekTime.toFixed(1)}s`);
|
|
2935
|
+
|
|
2936
|
+
// Function to perform the seek after audio is ready
|
|
2937
|
+
const doSeek = () => {
|
|
2938
|
+
console.log(`Performing seek to ${seekTime.toFixed(1)}s, audio readyState: ${narrationAudio.readyState}`);
|
|
2939
|
+
narrationAudio.currentTime = seekTime;
|
|
2940
|
+
console.log(`After setting currentTime: ${narrationAudio.currentTime.toFixed(1)}s`);
|
|
2941
|
+
};
|
|
2942
|
+
|
|
2943
|
+
// If not playing, start first then seek
|
|
2944
|
+
if (!isPlaying) {
|
|
2945
|
+
isPlaying = true;
|
|
2946
|
+
playBtn.classList.add('active');
|
|
2947
|
+
playBtn.textContent = '⏸ STOP';
|
|
2948
|
+
|
|
2949
|
+
if (window.introLightingTL) {
|
|
2950
|
+
window.introLightingTL.kill();
|
|
2951
|
+
window.introLightingTL = null;
|
|
2952
|
+
document.querySelectorAll('#introFlow .phase-node').forEach(n => n.classList.remove('active'));
|
|
2953
|
+
document.querySelectorAll('#introFlow .phase-connector-arrow').forEach(a => a.classList.remove('active'));
|
|
2954
|
+
}
|
|
2955
|
+
|
|
2956
|
+
audioProgressContainer.style.display = 'block';
|
|
2957
|
+
|
|
2958
|
+
// Wait for audio to be ready enough to seek
|
|
2959
|
+
if (narrationAudio.readyState >= 1) {
|
|
2960
|
+
doSeek();
|
|
2961
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
2962
|
+
} else {
|
|
2963
|
+
// Audio not ready yet, wait for loadedmetadata
|
|
2964
|
+
narrationAudio.addEventListener('loadedmetadata', function onLoaded() {
|
|
2965
|
+
narrationAudio.removeEventListener('loadedmetadata', onLoaded);
|
|
2966
|
+
doSeek();
|
|
2967
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
2968
|
+
});
|
|
2969
|
+
narrationAudio.load(); // Trigger loading
|
|
2970
|
+
}
|
|
2971
|
+
} else {
|
|
2972
|
+
// Already playing - pause, seek, then play
|
|
2973
|
+
narrationAudio.pause();
|
|
2974
|
+
|
|
2975
|
+
const performSeekAndPlay = () => {
|
|
2976
|
+
doSeek();
|
|
2977
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
2978
|
+
};
|
|
2979
|
+
|
|
2980
|
+
// Use timingData.duration as indicator that we have valid timing for this voice
|
|
2981
|
+
// Also check readyState >= 2 (HAVE_CURRENT_DATA) for reliable seeking
|
|
2982
|
+
if (timingData && timingData.duration > 0 && narrationAudio.readyState >= 2) {
|
|
2983
|
+
performSeekAndPlay();
|
|
2984
|
+
} else {
|
|
2985
|
+
// Audio not ready (e.g. after voice switch), wait for canplay
|
|
2986
|
+
console.log('Audio not ready for seek, waiting for canplay...');
|
|
2987
|
+
narrationAudio.addEventListener('canplay', function onReady() {
|
|
2988
|
+
narrationAudio.removeEventListener('canplay', onReady);
|
|
2989
|
+
performSeekAndPlay();
|
|
2990
|
+
}, { once: true });
|
|
2991
|
+
// Also try loadedmetadata as backup
|
|
2992
|
+
narrationAudio.addEventListener('loadedmetadata', function onMeta() {
|
|
2993
|
+
narrationAudio.removeEventListener('loadedmetadata', onMeta);
|
|
2994
|
+
if (narrationAudio.readyState >= 2) {
|
|
2995
|
+
performSeekAndPlay();
|
|
2996
|
+
}
|
|
2997
|
+
}, { once: true });
|
|
2998
|
+
narrationAudio.load();
|
|
2999
|
+
}
|
|
3000
|
+
}
|
|
3001
|
+
|
|
3002
|
+
// Update highlight tracking for new position
|
|
3003
|
+
highlightIndex = 0;
|
|
3004
|
+
if (timingData && timingData.highlights) {
|
|
3005
|
+
for (let j = 0; j < timingData.highlights.length; j++) {
|
|
3006
|
+
if (timingData.highlights[j].timestamp <= seekTime) {
|
|
3007
|
+
highlightIndex = j;
|
|
3008
|
+
} else {
|
|
3009
|
+
break;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
// Scroll to section
|
|
3015
|
+
const sectionEl = document.getElementById(section.id);
|
|
3016
|
+
if (sectionEl) {
|
|
3017
|
+
sectionEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
// Update active state
|
|
3021
|
+
updateActiveChapter(i);
|
|
3022
|
+
});
|
|
3023
|
+
|
|
3024
|
+
chapterButtons.appendChild(btn);
|
|
3025
|
+
});
|
|
3026
|
+
}
|
|
3027
|
+
|
|
2826
3028
|
console.log('Timing data loaded:', timingData.sections.length, 'sections,', timingData.highlights.length, 'highlights');
|
|
2827
3029
|
return timingData;
|
|
2828
3030
|
} catch (error) {
|
|
@@ -2840,10 +3042,20 @@
|
|
|
2840
3042
|
currentHighlight = el;
|
|
2841
3043
|
el.classList.add('audio-highlighted');
|
|
2842
3044
|
el.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
3045
|
+
|
|
3046
|
+
// Only apply box-shadow GSAP animation to container elements, not text
|
|
3047
|
+
const isContainer = el.classList.contains('solution-card') ||
|
|
3048
|
+
el.classList.contains('phase-box') ||
|
|
3049
|
+
el.classList.contains('gap-item') ||
|
|
3050
|
+
el.classList.contains('phase-node') ||
|
|
3051
|
+
el.classList.contains('install-command');
|
|
3052
|
+
|
|
3053
|
+
if (isContainer) {
|
|
3054
|
+
gsap.fromTo(el,
|
|
3055
|
+
{ boxShadow: '0 4px 6px rgba(0,0,0,0.05)' },
|
|
3056
|
+
{ boxShadow: '0 8px 25px rgba(0,0,0,0.15)', duration: 0.4, ease: 'power2.out' }
|
|
3057
|
+
);
|
|
3058
|
+
}
|
|
2847
3059
|
}
|
|
2848
3060
|
} catch (e) {
|
|
2849
3061
|
console.warn('Could not highlight:', selector, e);
|
|
@@ -2859,6 +3071,29 @@
|
|
|
2859
3071
|
}
|
|
2860
3072
|
}
|
|
2861
3073
|
|
|
3074
|
+
// Update active chapter button based on index
|
|
3075
|
+
function updateActiveChapter(index) {
|
|
3076
|
+
if (!chapterButtons) return;
|
|
3077
|
+
const buttons = chapterButtons.querySelectorAll('.chapter-btn');
|
|
3078
|
+
buttons.forEach((btn, i) => {
|
|
3079
|
+
btn.classList.toggle('active', i === index);
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
// Find current section index based on audio time
|
|
3084
|
+
function getCurrentSectionIndex(currentTime) {
|
|
3085
|
+
if (!timingData || !timingData.sections) return 0;
|
|
3086
|
+
let idx = 0;
|
|
3087
|
+
for (let i = 0; i < timingData.sections.length; i++) {
|
|
3088
|
+
if (currentTime >= timingData.sections[i].timestamp) {
|
|
3089
|
+
idx = i;
|
|
3090
|
+
} else {
|
|
3091
|
+
break;
|
|
3092
|
+
}
|
|
3093
|
+
}
|
|
3094
|
+
return idx;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
2862
3097
|
// Handle audio time update (syncs highlights with narration)
|
|
2863
3098
|
function onAudioTimeUpdate() {
|
|
2864
3099
|
if (!timingData || !audioEnabled) return;
|
|
@@ -2874,6 +3109,13 @@
|
|
|
2874
3109
|
audioCurrentTime.textContent = formatTime(currentTime);
|
|
2875
3110
|
}
|
|
2876
3111
|
|
|
3112
|
+
// Update active chapter button
|
|
3113
|
+
const sectionIdx = getCurrentSectionIndex(currentTime);
|
|
3114
|
+
if (sectionIdx !== currentSectionIdx) {
|
|
3115
|
+
currentSectionIdx = sectionIdx;
|
|
3116
|
+
updateActiveChapter(sectionIdx);
|
|
3117
|
+
}
|
|
3118
|
+
|
|
2877
3119
|
// Check for highlight triggers
|
|
2878
3120
|
for (let i = highlightIndex; i < timingData.highlights.length; i++) {
|
|
2879
3121
|
const highlight = timingData.highlights[i];
|
|
@@ -2889,23 +3131,73 @@
|
|
|
2889
3131
|
}
|
|
2890
3132
|
|
|
2891
3133
|
// Handle seek on progress bar click
|
|
2892
|
-
function onProgressBarClick(e) {
|
|
2893
|
-
|
|
3134
|
+
async function onProgressBarClick(e) {
|
|
3135
|
+
e.stopPropagation(); // Prevent event bubbling
|
|
2894
3136
|
|
|
3137
|
+
// Use the progress bar's rect for calculation
|
|
2895
3138
|
const rect = audioProgressBar.getBoundingClientRect();
|
|
2896
3139
|
const clickX = e.clientX - rect.left;
|
|
2897
|
-
const percentage = clickX / rect.width;
|
|
2898
|
-
|
|
3140
|
+
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
3141
|
+
|
|
3142
|
+
// ALWAYS prefer timingData.duration (most reliable after voice switch)
|
|
3143
|
+
// Only fall back to narrationAudio.duration if timing data unavailable
|
|
3144
|
+
let duration = (timingData && timingData.duration > 0) ? timingData.duration : narrationAudio.duration;
|
|
3145
|
+
|
|
3146
|
+
if (!duration || duration === 0 || isNaN(duration)) {
|
|
3147
|
+
console.warn('No duration available, waiting for audio to load...');
|
|
3148
|
+
// Wait for audio to load
|
|
3149
|
+
await new Promise((resolve) => {
|
|
3150
|
+
let resolved = false;
|
|
3151
|
+
const tryResolve = () => {
|
|
3152
|
+
if (!resolved && narrationAudio.duration > 0) {
|
|
3153
|
+
resolved = true;
|
|
3154
|
+
resolve();
|
|
3155
|
+
}
|
|
3156
|
+
};
|
|
3157
|
+
narrationAudio.addEventListener('loadedmetadata', tryResolve, { once: true });
|
|
3158
|
+
narrationAudio.addEventListener('canplay', tryResolve, { once: true });
|
|
3159
|
+
narrationAudio.load();
|
|
3160
|
+
setTimeout(() => { if (!resolved) { resolved = true; resolve(); } }, 2000);
|
|
3161
|
+
});
|
|
3162
|
+
duration = narrationAudio.duration;
|
|
3163
|
+
if (!duration || duration === 0 || isNaN(duration)) {
|
|
3164
|
+
console.error('Still no duration available after waiting');
|
|
3165
|
+
return;
|
|
3166
|
+
}
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
const seekTime = percentage * duration;
|
|
3170
|
+
console.log(`Progress bar click: seeking to ${seekTime.toFixed(1)}s (${(percentage * 100).toFixed(1)}%) using duration ${duration.toFixed(1)}s`);
|
|
3171
|
+
|
|
3172
|
+
// Pause, seek, then play (most reliable method)
|
|
3173
|
+
const wasPlaying = !narrationAudio.paused;
|
|
3174
|
+
narrationAudio.pause();
|
|
3175
|
+
|
|
3176
|
+
// Ensure audio is ready to seek (especially important after voice switch)
|
|
3177
|
+
if (narrationAudio.readyState < 2) {
|
|
3178
|
+
console.log('Waiting for audio to be ready for seeking...');
|
|
3179
|
+
await new Promise((resolve) => {
|
|
3180
|
+
narrationAudio.addEventListener('canplay', resolve, { once: true });
|
|
3181
|
+
setTimeout(resolve, 1000);
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
2899
3184
|
|
|
2900
3185
|
narrationAudio.currentTime = seekTime;
|
|
3186
|
+
console.log(`After setting currentTime: ${narrationAudio.currentTime.toFixed(1)}s`);
|
|
3187
|
+
|
|
3188
|
+
if (wasPlaying) {
|
|
3189
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
3190
|
+
}
|
|
2901
3191
|
|
|
2902
3192
|
// Reset highlight tracking
|
|
2903
3193
|
highlightIndex = 0;
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
3194
|
+
if (timingData && timingData.highlights) {
|
|
3195
|
+
for (let i = 0; i < timingData.highlights.length; i++) {
|
|
3196
|
+
if (timingData.highlights[i].timestamp <= seekTime) {
|
|
3197
|
+
highlightIndex = i;
|
|
3198
|
+
} else {
|
|
3199
|
+
break;
|
|
3200
|
+
}
|
|
2909
3201
|
}
|
|
2910
3202
|
}
|
|
2911
3203
|
}
|
|
@@ -2931,6 +3223,16 @@
|
|
|
2931
3223
|
playBtn.classList.add('active');
|
|
2932
3224
|
playBtn.textContent = '⏸ STOP';
|
|
2933
3225
|
|
|
3226
|
+
// KILL the intro lighting animation completely to avoid visual conflict
|
|
3227
|
+
// (Don't just pause - kill it so it won't restart)
|
|
3228
|
+
if (window.introLightingTL) {
|
|
3229
|
+
window.introLightingTL.kill();
|
|
3230
|
+
window.introLightingTL = null;
|
|
3231
|
+
// Reset the phase nodes to clean state
|
|
3232
|
+
document.querySelectorAll('#introFlow .phase-node').forEach(n => n.classList.remove('active'));
|
|
3233
|
+
document.querySelectorAll('#introFlow .phase-connector-arrow').forEach(a => a.classList.remove('active'));
|
|
3234
|
+
}
|
|
3235
|
+
|
|
2934
3236
|
// Scroll to top first
|
|
2935
3237
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
2936
3238
|
|
|
@@ -2975,6 +3277,9 @@
|
|
|
2975
3277
|
removeHighlight();
|
|
2976
3278
|
highlightIndex = 0;
|
|
2977
3279
|
currentSectionIdx = 0;
|
|
3280
|
+
|
|
3281
|
+
// Note: We killed the intro lighting animation, so don't try to restart it
|
|
3282
|
+
// The animation was distracting during/after narration playback
|
|
2978
3283
|
}
|
|
2979
3284
|
|
|
2980
3285
|
// Audio ended
|
|
@@ -3024,6 +3329,31 @@
|
|
|
3024
3329
|
audioToggleBtn.addEventListener('click', toggleAudio);
|
|
3025
3330
|
narrationAudio.addEventListener('timeupdate', onAudioTimeUpdate);
|
|
3026
3331
|
narrationAudio.addEventListener('ended', onAudioEnded);
|
|
3332
|
+
|
|
3333
|
+
// DEBUG: Global click handler to see what's being clicked anywhere on page
|
|
3334
|
+
document.addEventListener('click', (e) => {
|
|
3335
|
+
// Only log clicks in the bottom 200px of the screen (where the audio player is)
|
|
3336
|
+
if (e.clientY > window.innerHeight - 200) {
|
|
3337
|
+
console.log('%c CLICK DETECTED ', 'background: red; color: white; font-size: 14px;', {
|
|
3338
|
+
element: e.target.tagName,
|
|
3339
|
+
id: e.target.id || '(no id)',
|
|
3340
|
+
class: e.target.className || '(no class)',
|
|
3341
|
+
x: e.clientX,
|
|
3342
|
+
y: e.clientY,
|
|
3343
|
+
audioCurrentTime: narrationAudio.currentTime.toFixed(1),
|
|
3344
|
+
audioDuration: narrationAudio.duration ? narrationAudio.duration.toFixed(1) : 'not loaded'
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
}, true); // Use capture phase to see ALL clicks before they're handled
|
|
3348
|
+
|
|
3349
|
+
// Debug: track seeking events
|
|
3350
|
+
narrationAudio.addEventListener('seeking', () => {
|
|
3351
|
+
console.log('%c AUDIO SEEKING ', 'background: blue; color: white;', 'to:', narrationAudio.currentTime.toFixed(1));
|
|
3352
|
+
});
|
|
3353
|
+
narrationAudio.addEventListener('seeked', () => {
|
|
3354
|
+
console.log('%c AUDIO SEEKED ', 'background: green; color: white;', 'to:', narrationAudio.currentTime.toFixed(1));
|
|
3355
|
+
});
|
|
3356
|
+
|
|
3027
3357
|
audioProgressBar.addEventListener('click', onProgressBarClick);
|
|
3028
3358
|
|
|
3029
3359
|
// Check if audio file is accessible
|
|
@@ -3042,6 +3372,10 @@
|
|
|
3042
3372
|
// RESET BUTTON
|
|
3043
3373
|
// ============================================
|
|
3044
3374
|
document.getElementById('resetBtn').addEventListener('click', () => {
|
|
3375
|
+
// Stop audio and auto-play first
|
|
3376
|
+
stopAutoPlay();
|
|
3377
|
+
|
|
3378
|
+
// Scroll to top
|
|
3045
3379
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
3046
3380
|
|
|
3047
3381
|
// Reset animations
|
|
@@ -3059,11 +3393,20 @@
|
|
|
3059
3393
|
gsap.set('.credit-link, .made-with', { opacity: 0 });
|
|
3060
3394
|
gsap.set('.intro-text, .scroll-hint', { opacity: 0 });
|
|
3061
3395
|
|
|
3396
|
+
// Reset phase boxes
|
|
3062
3397
|
document.querySelectorAll('.phase-box').forEach(box => {
|
|
3063
3398
|
box.classList.remove('active');
|
|
3064
3399
|
box.querySelector('.phase-status').textContent = '';
|
|
3065
3400
|
});
|
|
3066
3401
|
|
|
3402
|
+
// Reset intro phase nodes
|
|
3403
|
+
document.querySelectorAll('.phase-node').forEach(node => {
|
|
3404
|
+
node.classList.remove('active');
|
|
3405
|
+
});
|
|
3406
|
+
document.querySelectorAll('.phase-connector-arrow').forEach(arrow => {
|
|
3407
|
+
arrow.classList.remove('active');
|
|
3408
|
+
});
|
|
3409
|
+
|
|
3067
3410
|
ScrollTrigger.refresh();
|
|
3068
3411
|
}, 500);
|
|
3069
3412
|
});
|
|
@@ -3237,6 +3580,173 @@
|
|
|
3237
3580
|
});
|
|
3238
3581
|
});
|
|
3239
3582
|
|
|
3583
|
+
// ============================================
|
|
3584
|
+
// VOICE PREVIEW MODAL
|
|
3585
|
+
// ============================================
|
|
3586
|
+
const voiceSelectBtn = document.getElementById('voiceSelectBtn');
|
|
3587
|
+
const voiceModalOverlay = document.getElementById('voiceModalOverlay');
|
|
3588
|
+
const voiceModalClose = document.getElementById('voiceModalClose');
|
|
3589
|
+
const voiceConfirmBtn = document.getElementById('voiceConfirmBtn');
|
|
3590
|
+
const voiceOptions = document.querySelectorAll('.voice-option');
|
|
3591
|
+
const voicePlayBtns = document.querySelectorAll('.voice-play-btn');
|
|
3592
|
+
|
|
3593
|
+
let previewAudio = null;
|
|
3594
|
+
let currentlyPlayingBtn = null;
|
|
3595
|
+
let selectedVoice = 'adam';
|
|
3596
|
+
|
|
3597
|
+
// Open modal
|
|
3598
|
+
voiceSelectBtn.addEventListener('click', () => {
|
|
3599
|
+
voiceModalOverlay.classList.add('active');
|
|
3600
|
+
});
|
|
3601
|
+
|
|
3602
|
+
// Close modal
|
|
3603
|
+
function closeVoiceModal() {
|
|
3604
|
+
voiceModalOverlay.classList.remove('active');
|
|
3605
|
+
// Stop any playing preview
|
|
3606
|
+
if (previewAudio) {
|
|
3607
|
+
previewAudio.pause();
|
|
3608
|
+
previewAudio = null;
|
|
3609
|
+
}
|
|
3610
|
+
if (currentlyPlayingBtn) {
|
|
3611
|
+
currentlyPlayingBtn.textContent = '▶';
|
|
3612
|
+
currentlyPlayingBtn.classList.remove('playing');
|
|
3613
|
+
currentlyPlayingBtn = null;
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
voiceModalClose.addEventListener('click', closeVoiceModal);
|
|
3618
|
+
voiceModalOverlay.addEventListener('click', (e) => {
|
|
3619
|
+
if (e.target === voiceModalOverlay) {
|
|
3620
|
+
closeVoiceModal();
|
|
3621
|
+
}
|
|
3622
|
+
});
|
|
3623
|
+
|
|
3624
|
+
// Play preview
|
|
3625
|
+
voicePlayBtns.forEach(btn => {
|
|
3626
|
+
btn.addEventListener('click', (e) => {
|
|
3627
|
+
e.stopPropagation();
|
|
3628
|
+
const previewSrc = btn.dataset.preview;
|
|
3629
|
+
|
|
3630
|
+
// If clicking the same button that's playing, stop it
|
|
3631
|
+
if (currentlyPlayingBtn === btn && previewAudio && !previewAudio.paused) {
|
|
3632
|
+
previewAudio.pause();
|
|
3633
|
+
btn.textContent = '▶';
|
|
3634
|
+
btn.classList.remove('playing');
|
|
3635
|
+
currentlyPlayingBtn = null;
|
|
3636
|
+
return;
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
// Stop any currently playing preview
|
|
3640
|
+
if (previewAudio) {
|
|
3641
|
+
previewAudio.pause();
|
|
3642
|
+
}
|
|
3643
|
+
if (currentlyPlayingBtn) {
|
|
3644
|
+
currentlyPlayingBtn.textContent = '▶';
|
|
3645
|
+
currentlyPlayingBtn.classList.remove('playing');
|
|
3646
|
+
}
|
|
3647
|
+
|
|
3648
|
+
// Play new preview
|
|
3649
|
+
previewAudio = new Audio(previewSrc);
|
|
3650
|
+
previewAudio.play().catch(err => {
|
|
3651
|
+
console.error('Audio play failed:', err);
|
|
3652
|
+
btn.textContent = '⚠';
|
|
3653
|
+
setTimeout(() => { btn.textContent = '▶'; }, 2000);
|
|
3654
|
+
});
|
|
3655
|
+
|
|
3656
|
+
btn.textContent = '⏸';
|
|
3657
|
+
btn.classList.add('playing');
|
|
3658
|
+
currentlyPlayingBtn = btn;
|
|
3659
|
+
|
|
3660
|
+
previewAudio.addEventListener('ended', () => {
|
|
3661
|
+
btn.textContent = '▶';
|
|
3662
|
+
btn.classList.remove('playing');
|
|
3663
|
+
currentlyPlayingBtn = null;
|
|
3664
|
+
});
|
|
3665
|
+
});
|
|
3666
|
+
});
|
|
3667
|
+
|
|
3668
|
+
// Select voice
|
|
3669
|
+
voiceOptions.forEach(option => {
|
|
3670
|
+
option.addEventListener('click', () => {
|
|
3671
|
+
// Remove selected from all
|
|
3672
|
+
voiceOptions.forEach(opt => {
|
|
3673
|
+
opt.classList.remove('selected');
|
|
3674
|
+
opt.querySelector('.voice-select-btn').textContent = 'Select';
|
|
3675
|
+
});
|
|
3676
|
+
|
|
3677
|
+
// Add selected to clicked
|
|
3678
|
+
option.classList.add('selected');
|
|
3679
|
+
option.querySelector('.voice-select-btn').textContent = 'Selected';
|
|
3680
|
+
selectedVoice = option.dataset.voice;
|
|
3681
|
+
});
|
|
3682
|
+
});
|
|
3683
|
+
|
|
3684
|
+
// Confirm selection
|
|
3685
|
+
voiceConfirmBtn.addEventListener('click', async () => {
|
|
3686
|
+
const selectedOption = document.querySelector('.voice-option.selected');
|
|
3687
|
+
if (selectedOption) {
|
|
3688
|
+
const voiceFile = selectedOption.dataset.file;
|
|
3689
|
+
const timingFile = selectedOption.dataset.timing;
|
|
3690
|
+
const voiceName = selectedOption.querySelector('.voice-name').textContent;
|
|
3691
|
+
|
|
3692
|
+
// Stop current playback if any
|
|
3693
|
+
if (isPlaying) {
|
|
3694
|
+
stopAutoPlay();
|
|
3695
|
+
}
|
|
3696
|
+
|
|
3697
|
+
// Update audio source
|
|
3698
|
+
narrationAudio.src = voiceFile;
|
|
3699
|
+
|
|
3700
|
+
// Force load the new audio and wait for it to be ready
|
|
3701
|
+
narrationAudio.load();
|
|
3702
|
+
|
|
3703
|
+
// Wait for the audio to load metadata AND have valid duration (don't trust stale readyState)
|
|
3704
|
+
await new Promise((resolve) => {
|
|
3705
|
+
let resolved = false;
|
|
3706
|
+
const tryResolve = () => {
|
|
3707
|
+
if (!resolved && narrationAudio.duration > 0 && !isNaN(narrationAudio.duration)) {
|
|
3708
|
+
resolved = true;
|
|
3709
|
+
resolve();
|
|
3710
|
+
}
|
|
3711
|
+
};
|
|
3712
|
+
// Listen for multiple events to ensure we catch when audio is truly ready
|
|
3713
|
+
narrationAudio.addEventListener('loadedmetadata', tryResolve, { once: true });
|
|
3714
|
+
narrationAudio.addEventListener('canplay', tryResolve, { once: true });
|
|
3715
|
+
narrationAudio.addEventListener('durationchange', tryResolve, { once: true });
|
|
3716
|
+
// Timeout fallback in case load fails
|
|
3717
|
+
setTimeout(() => {
|
|
3718
|
+
if (!resolved) {
|
|
3719
|
+
console.warn('Audio load timeout, proceeding anyway');
|
|
3720
|
+
resolved = true;
|
|
3721
|
+
resolve();
|
|
3722
|
+
}
|
|
3723
|
+
}, 3000);
|
|
3724
|
+
});
|
|
3725
|
+
|
|
3726
|
+
// Reset audio position and clear old state
|
|
3727
|
+
narrationAudio.currentTime = 0;
|
|
3728
|
+
timingData = null;
|
|
3729
|
+
highlightIndex = 0;
|
|
3730
|
+
currentSectionIdx = 0;
|
|
3731
|
+
await loadTimingData(timingFile, true);
|
|
3732
|
+
|
|
3733
|
+
// Update voice button to show selected voice
|
|
3734
|
+
voiceSelectBtn.textContent = `🎙 ${voiceName.toUpperCase()}`;
|
|
3735
|
+
|
|
3736
|
+
console.log(`Voice changed to: ${voiceName} (${voiceFile})`);
|
|
3737
|
+
console.log(`Audio duration: ${narrationAudio.duration}s, readyState: ${narrationAudio.readyState}`);
|
|
3738
|
+
console.log(`Timing data loaded: ${timingFile}`);
|
|
3739
|
+
}
|
|
3740
|
+
closeVoiceModal();
|
|
3741
|
+
});
|
|
3742
|
+
|
|
3743
|
+
// Keyboard close (Escape)
|
|
3744
|
+
document.addEventListener('keydown', (e) => {
|
|
3745
|
+
if (e.key === 'Escape' && voiceModalOverlay.classList.contains('active')) {
|
|
3746
|
+
closeVoiceModal();
|
|
3747
|
+
}
|
|
3748
|
+
});
|
|
3749
|
+
|
|
3240
3750
|
</script>
|
|
3241
3751
|
|
|
3242
3752
|
</body>
|