@hustle-together/api-dev-tools 2.0.7 → 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 +1023 -558
- 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,468 +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
546
|
outline: 2px solid var(--accent-red) !important;
|
|
587
547
|
outline-offset: 4px;
|
|
588
|
-
border-color: var(--accent-red) !important;
|
|
589
548
|
transition: all 0.3s ease !important;
|
|
549
|
+
box-shadow: 0 0 20px var(--accent-red-glow) !important;
|
|
590
550
|
}
|
|
591
551
|
|
|
592
|
-
/* For container elements (cards, boxes), use glow effect */
|
|
593
552
|
.audio-highlighted.solution-card,
|
|
594
553
|
.audio-highlighted.phase-box,
|
|
595
554
|
.audio-highlighted.gap-item,
|
|
596
555
|
.audio-highlighted.phase-node {
|
|
597
|
-
box-shadow: 0 0 25px var(--accent-red-glow) !important;
|
|
598
|
-
outline: none !important;
|
|
599
|
-
transform: scale(1.02);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
/* For text elements (headings, brand), use subtle underline/border */
|
|
603
|
-
.audio-highlighted h2,
|
|
604
|
-
h2.audio-highlighted,
|
|
605
|
-
.hustle-brand.audio-highlighted {
|
|
606
|
-
box-shadow: none !important;
|
|
607
556
|
outline: none !important;
|
|
608
|
-
border-
|
|
609
|
-
|
|
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;
|
|
610
560
|
}
|
|
611
561
|
|
|
612
562
|
/* Progress indicator */
|
|
613
563
|
.progress-bar {
|
|
614
564
|
position: fixed;
|
|
615
|
-
top:
|
|
565
|
+
top: 61px;
|
|
616
566
|
left: 0;
|
|
617
|
-
height:
|
|
567
|
+
height: 2px;
|
|
618
568
|
background: var(--accent-red);
|
|
619
569
|
width: 0%;
|
|
620
570
|
z-index: 1001;
|
|
571
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
621
572
|
}
|
|
622
573
|
|
|
623
|
-
/*
|
|
574
|
+
/* ============================================
|
|
575
|
+
SECTIONS & CONTENT
|
|
576
|
+
============================================ */
|
|
624
577
|
section {
|
|
625
578
|
min-height: 100vh;
|
|
626
579
|
display: flex;
|
|
627
580
|
flex-direction: column;
|
|
628
581
|
justify-content: center;
|
|
629
582
|
align-items: center;
|
|
630
|
-
padding:
|
|
583
|
+
padding: 120px 40px 80px;
|
|
631
584
|
position: relative;
|
|
632
585
|
}
|
|
633
586
|
|
|
634
|
-
/* ASCII border decoration */
|
|
635
587
|
.ascii-border {
|
|
636
|
-
border: 1px dashed var(--
|
|
588
|
+
border: 1px dashed var(--border-color);
|
|
637
589
|
padding: 50px;
|
|
638
590
|
position: relative;
|
|
639
|
-
max-width:
|
|
591
|
+
max-width: 900px;
|
|
640
592
|
width: 100%;
|
|
641
|
-
|
|
642
|
-
background: rgba(10, 10, 10, 0.85);
|
|
643
|
-
backdrop-filter: blur(12px);
|
|
644
|
-
-webkit-backdrop-filter: blur(12px);
|
|
593
|
+
background: rgba(10, 10, 10, 0.8);
|
|
645
594
|
}
|
|
646
595
|
|
|
647
|
-
.ascii-border::before
|
|
596
|
+
.ascii-border::before,
|
|
597
|
+
.ascii-border::after {
|
|
648
598
|
content: '+';
|
|
649
599
|
position: absolute;
|
|
600
|
+
color: var(--border-color);
|
|
601
|
+
font-size: 1rem;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.ascii-border::before {
|
|
650
605
|
top: -8px;
|
|
651
606
|
left: -8px;
|
|
652
|
-
color: var(--grey);
|
|
653
607
|
}
|
|
654
608
|
|
|
655
609
|
.ascii-border::after {
|
|
656
|
-
content: '+';
|
|
657
|
-
position: absolute;
|
|
658
610
|
bottom: -8px;
|
|
659
611
|
right: -8px;
|
|
660
|
-
color: var(--grey);
|
|
661
612
|
}
|
|
662
613
|
|
|
663
614
|
/* Typography */
|
|
664
615
|
h1 {
|
|
665
|
-
font-
|
|
666
|
-
font-
|
|
667
|
-
|
|
616
|
+
font-family: 'Fredoka', sans-serif;
|
|
617
|
+
font-size: 2.8rem;
|
|
618
|
+
font-weight: 700;
|
|
668
619
|
margin-bottom: 20px;
|
|
620
|
+
color: var(--white);
|
|
669
621
|
}
|
|
670
622
|
|
|
671
623
|
h2 {
|
|
672
|
-
font-
|
|
673
|
-
font-
|
|
674
|
-
|
|
675
|
-
margin-bottom:
|
|
624
|
+
font-family: 'Fredoka', sans-serif;
|
|
625
|
+
font-size: 2rem;
|
|
626
|
+
font-weight: 600;
|
|
627
|
+
margin-bottom: 15px;
|
|
628
|
+
color: var(--white);
|
|
676
629
|
}
|
|
677
630
|
|
|
678
631
|
h3 {
|
|
679
|
-
font-size:
|
|
632
|
+
font-size: 1rem;
|
|
680
633
|
margin-bottom: 15px;
|
|
681
634
|
text-transform: uppercase;
|
|
682
635
|
letter-spacing: 1px;
|
|
683
636
|
color: var(--grey);
|
|
684
637
|
}
|
|
685
638
|
|
|
686
|
-
/* Explanation blocks
|
|
639
|
+
/* Explanation blocks */
|
|
687
640
|
.explanation {
|
|
688
|
-
background:
|
|
689
|
-
border
|
|
641
|
+
background: var(--darker-grey);
|
|
642
|
+
border: 1px dashed var(--border-color);
|
|
643
|
+
border-left: 3px solid var(--accent-red);
|
|
690
644
|
padding: 25px 30px;
|
|
691
645
|
margin: 30px 0;
|
|
692
646
|
font-size: 1rem;
|
|
693
|
-
line-height: 1.
|
|
647
|
+
line-height: 1.8;
|
|
694
648
|
opacity: 0;
|
|
695
649
|
}
|
|
696
650
|
|
|
697
651
|
.explanation-title {
|
|
698
|
-
font-size: 0.
|
|
652
|
+
font-size: 0.75rem;
|
|
699
653
|
text-transform: uppercase;
|
|
700
|
-
letter-spacing:
|
|
701
|
-
color: var(--
|
|
654
|
+
letter-spacing: 2px;
|
|
655
|
+
color: var(--accent-red);
|
|
702
656
|
margin-bottom: 15px;
|
|
703
|
-
|
|
704
|
-
align-items: center;
|
|
705
|
-
gap: 10px;
|
|
657
|
+
font-weight: 600;
|
|
706
658
|
}
|
|
707
659
|
|
|
708
660
|
.explanation-title::before {
|
|
709
|
-
content: '
|
|
710
|
-
|
|
711
|
-
height: 24px;
|
|
712
|
-
border: 1px dashed var(--grey);
|
|
713
|
-
display: flex;
|
|
714
|
-
align-items: center;
|
|
715
|
-
justify-content: center;
|
|
716
|
-
font-size: 0.9rem;
|
|
661
|
+
content: '// ';
|
|
662
|
+
color: var(--grey);
|
|
717
663
|
}
|
|
718
664
|
|
|
719
665
|
.explanation p {
|
|
720
666
|
margin-bottom: 15px;
|
|
667
|
+
color: var(--grey);
|
|
721
668
|
}
|
|
722
669
|
|
|
723
670
|
.explanation p:last-child {
|
|
@@ -726,14 +673,13 @@
|
|
|
726
673
|
|
|
727
674
|
.explanation strong {
|
|
728
675
|
color: var(--white);
|
|
729
|
-
font-weight:
|
|
730
|
-
border-bottom: 1px dashed var(--grey);
|
|
676
|
+
font-weight: 600;
|
|
731
677
|
}
|
|
732
678
|
|
|
733
679
|
/* Real example callout */
|
|
734
680
|
.real-example {
|
|
735
|
-
background:
|
|
736
|
-
border:
|
|
681
|
+
background: var(--darker-grey);
|
|
682
|
+
border: 2px solid var(--border-color);
|
|
737
683
|
padding: 20px 25px;
|
|
738
684
|
margin: 20px 0;
|
|
739
685
|
position: relative;
|
|
@@ -744,11 +690,12 @@
|
|
|
744
690
|
position: absolute;
|
|
745
691
|
top: -10px;
|
|
746
692
|
left: 20px;
|
|
747
|
-
background: var(--
|
|
693
|
+
background: var(--card-bg);
|
|
748
694
|
padding: 0 10px;
|
|
749
|
-
font-size: 0.
|
|
695
|
+
font-size: 0.7rem;
|
|
750
696
|
letter-spacing: 2px;
|
|
751
|
-
color: var(--
|
|
697
|
+
color: var(--accent-red);
|
|
698
|
+
border: 1px solid var(--border-color);
|
|
752
699
|
}
|
|
753
700
|
|
|
754
701
|
.real-example-content {
|
|
@@ -760,7 +707,7 @@
|
|
|
760
707
|
display: inline-block;
|
|
761
708
|
width: 10px;
|
|
762
709
|
height: 20px;
|
|
763
|
-
background: var(--
|
|
710
|
+
background: var(--accent-red);
|
|
764
711
|
animation: blink 1s infinite;
|
|
765
712
|
vertical-align: middle;
|
|
766
713
|
margin-left: 5px;
|
|
@@ -778,10 +725,11 @@
|
|
|
778
725
|
justify-content: center;
|
|
779
726
|
width: 50px;
|
|
780
727
|
height: 50px;
|
|
781
|
-
border: 2px solid var(--
|
|
728
|
+
border: 2px solid var(--accent-red);
|
|
782
729
|
font-size: 1.5rem;
|
|
783
730
|
margin-right: 20px;
|
|
784
731
|
flex-shrink: 0;
|
|
732
|
+
color: var(--accent-red);
|
|
785
733
|
}
|
|
786
734
|
|
|
787
735
|
.step-header {
|
|
@@ -848,11 +796,12 @@
|
|
|
848
796
|
|
|
849
797
|
.hustle-highlight {
|
|
850
798
|
color: var(--accent-red);
|
|
851
|
-
text-shadow: 0 0
|
|
799
|
+
text-shadow: 0 0 20px var(--accent-red-glow);
|
|
852
800
|
}
|
|
853
801
|
|
|
854
802
|
.text-red {
|
|
855
803
|
color: var(--accent-red);
|
|
804
|
+
font-weight: 600;
|
|
856
805
|
}
|
|
857
806
|
|
|
858
807
|
/* Intro section centering */
|
|
@@ -883,13 +832,13 @@
|
|
|
883
832
|
}
|
|
884
833
|
|
|
885
834
|
/* ============================================
|
|
886
|
-
PHASE FLOW - LIGHTING UP SEQUENCE
|
|
835
|
+
PHASE FLOW - LIGHTING UP SEQUENCE (boxy)
|
|
887
836
|
============================================ */
|
|
888
837
|
.phase-flow {
|
|
889
838
|
display: flex;
|
|
890
839
|
align-items: center;
|
|
891
840
|
justify-content: center;
|
|
892
|
-
gap:
|
|
841
|
+
gap: 12px;
|
|
893
842
|
margin-top: 50px;
|
|
894
843
|
flex-wrap: wrap;
|
|
895
844
|
opacity: 0;
|
|
@@ -897,30 +846,31 @@
|
|
|
897
846
|
|
|
898
847
|
.phase-node {
|
|
899
848
|
position: relative;
|
|
900
|
-
border: 2px solid var(--
|
|
849
|
+
border: 2px solid var(--border-color);
|
|
850
|
+
background: var(--darker-grey);
|
|
901
851
|
padding: 20px 25px;
|
|
902
852
|
text-align: center;
|
|
903
853
|
min-width: 100px;
|
|
904
|
-
transition: all 0.
|
|
854
|
+
transition: all 0.3s ease;
|
|
905
855
|
opacity: 0;
|
|
906
856
|
transform: translateY(20px);
|
|
907
857
|
}
|
|
908
858
|
|
|
909
859
|
.phase-node .phase-glow {
|
|
910
860
|
position: absolute;
|
|
911
|
-
top:
|
|
912
|
-
left:
|
|
913
|
-
right:
|
|
914
|
-
bottom:
|
|
861
|
+
top: 0;
|
|
862
|
+
left: 0;
|
|
863
|
+
right: 0;
|
|
864
|
+
bottom: 0;
|
|
915
865
|
border: 2px solid var(--accent-red);
|
|
916
866
|
opacity: 0;
|
|
917
|
-
|
|
918
|
-
transition: opacity 0.4s ease;
|
|
867
|
+
transition: opacity 0.3s ease;
|
|
919
868
|
}
|
|
920
869
|
|
|
921
870
|
.phase-node.active {
|
|
922
871
|
border-color: var(--accent-red);
|
|
923
|
-
background: rgba(186, 12, 47, 0.
|
|
872
|
+
background: rgba(186, 12, 47, 0.2);
|
|
873
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
924
874
|
}
|
|
925
875
|
|
|
926
876
|
.phase-node.active .phase-glow {
|
|
@@ -929,7 +879,6 @@
|
|
|
929
879
|
|
|
930
880
|
.phase-node.active .phase-icon {
|
|
931
881
|
color: var(--accent-red);
|
|
932
|
-
text-shadow: 0 0 10px var(--accent-red);
|
|
933
882
|
}
|
|
934
883
|
|
|
935
884
|
.phase-node.active .phase-label {
|
|
@@ -938,31 +887,31 @@
|
|
|
938
887
|
|
|
939
888
|
.phase-icon {
|
|
940
889
|
display: block;
|
|
941
|
-
font-size: 1.
|
|
890
|
+
font-size: 1.6rem;
|
|
942
891
|
font-weight: bold;
|
|
943
892
|
margin-bottom: 8px;
|
|
944
893
|
color: var(--grey);
|
|
945
|
-
transition: all 0.
|
|
894
|
+
transition: all 0.3s ease;
|
|
946
895
|
}
|
|
947
896
|
|
|
948
897
|
.phase-label {
|
|
949
|
-
font-size: 0.
|
|
898
|
+
font-size: 0.65rem;
|
|
950
899
|
text-transform: uppercase;
|
|
951
900
|
letter-spacing: 2px;
|
|
952
|
-
color: var(--
|
|
953
|
-
transition: all 0.
|
|
901
|
+
color: var(--grey);
|
|
902
|
+
transition: all 0.3s ease;
|
|
903
|
+
font-weight: bold;
|
|
954
904
|
}
|
|
955
905
|
|
|
956
906
|
.phase-connector-arrow {
|
|
957
|
-
color: var(--
|
|
958
|
-
font-size: 1.
|
|
907
|
+
color: var(--border-color);
|
|
908
|
+
font-size: 1.2rem;
|
|
959
909
|
opacity: 0;
|
|
960
910
|
transition: all 0.3s ease;
|
|
961
911
|
}
|
|
962
912
|
|
|
963
913
|
.phase-connector-arrow.active {
|
|
964
914
|
color: var(--accent-red);
|
|
965
|
-
text-shadow: 0 0 10px var(--accent-red-glow);
|
|
966
915
|
}
|
|
967
916
|
|
|
968
917
|
@media (max-width: 768px) {
|
|
@@ -1012,62 +961,67 @@
|
|
|
1012
961
|
}
|
|
1013
962
|
|
|
1014
963
|
/* ============================================
|
|
1015
|
-
SECTION 2: THE PROBLEM - GAPS
|
|
964
|
+
SECTION 2: THE PROBLEM - GAPS - boxy cards
|
|
1016
965
|
============================================ */
|
|
1017
966
|
.gap-list {
|
|
1018
967
|
list-style: none;
|
|
1019
968
|
width: 100%;
|
|
969
|
+
display: flex;
|
|
970
|
+
flex-direction: column;
|
|
971
|
+
gap: 15px;
|
|
1020
972
|
}
|
|
1021
973
|
|
|
1022
974
|
.gap-item {
|
|
1023
|
-
border:
|
|
1024
|
-
|
|
1025
|
-
|
|
975
|
+
border: 2px solid var(--border-color);
|
|
976
|
+
background: var(--darker-grey);
|
|
977
|
+
padding: 25px 30px 25px 50px;
|
|
1026
978
|
opacity: 0;
|
|
1027
979
|
transform: translateX(-30px);
|
|
1028
|
-
transition: all 0.
|
|
980
|
+
transition: all 0.2s;
|
|
1029
981
|
position: relative;
|
|
1030
982
|
}
|
|
1031
983
|
|
|
1032
984
|
.gap-item:hover {
|
|
1033
985
|
border-color: var(--accent-red);
|
|
1034
|
-
|
|
986
|
+
transform: translateX(5px);
|
|
1035
987
|
}
|
|
1036
988
|
|
|
1037
989
|
.gap-item::before {
|
|
1038
|
-
content: '
|
|
990
|
+
content: '✕';
|
|
1039
991
|
position: absolute;
|
|
1040
|
-
left:
|
|
1041
|
-
top:
|
|
1042
|
-
transform: translateY(-50%);
|
|
992
|
+
left: 18px;
|
|
993
|
+
top: 26px;
|
|
1043
994
|
color: var(--accent-red);
|
|
1044
|
-
font-size:
|
|
995
|
+
font-size: 1rem;
|
|
996
|
+
font-weight: bold;
|
|
1045
997
|
}
|
|
1046
998
|
|
|
1047
999
|
.gap-number {
|
|
1048
|
-
color: var(--
|
|
1049
|
-
font-size: 0.
|
|
1000
|
+
color: var(--accent-red);
|
|
1001
|
+
font-size: 0.75rem;
|
|
1050
1002
|
text-transform: uppercase;
|
|
1051
1003
|
letter-spacing: 2px;
|
|
1004
|
+
font-weight: bold;
|
|
1052
1005
|
}
|
|
1053
1006
|
|
|
1054
1007
|
.gap-title {
|
|
1055
|
-
font-size: 1.
|
|
1008
|
+
font-size: 1.1rem;
|
|
1056
1009
|
margin: 8px 0;
|
|
1057
1010
|
}
|
|
1058
1011
|
|
|
1059
1012
|
.gap-desc {
|
|
1060
1013
|
color: var(--grey);
|
|
1061
|
-
font-size: 0.
|
|
1014
|
+
font-size: 0.9rem;
|
|
1062
1015
|
margin-bottom: 10px;
|
|
1063
1016
|
}
|
|
1064
1017
|
|
|
1065
1018
|
.gap-example {
|
|
1066
1019
|
font-size: 0.85rem;
|
|
1067
1020
|
padding: 12px 15px;
|
|
1068
|
-
background:
|
|
1069
|
-
border
|
|
1070
|
-
|
|
1021
|
+
background: var(--card-bg);
|
|
1022
|
+
border: 1px solid var(--border-color);
|
|
1023
|
+
border-left: 3px solid var(--accent-red);
|
|
1024
|
+
margin-top: 12px;
|
|
1071
1025
|
}
|
|
1072
1026
|
|
|
1073
1027
|
.gap-example .bad {
|
|
@@ -1076,44 +1030,50 @@
|
|
|
1076
1030
|
}
|
|
1077
1031
|
|
|
1078
1032
|
.gap-example .good {
|
|
1079
|
-
color: var(--
|
|
1033
|
+
color: var(--accent-red);
|
|
1034
|
+
font-weight: bold;
|
|
1080
1035
|
}
|
|
1081
1036
|
|
|
1082
1037
|
/* ============================================
|
|
1083
|
-
SECTION 3: SOLUTION OVERVIEW
|
|
1038
|
+
SECTION 3: SOLUTION OVERVIEW - boxy grid
|
|
1084
1039
|
============================================ */
|
|
1085
1040
|
.solution-grid {
|
|
1086
1041
|
display: grid;
|
|
1087
1042
|
grid-template-columns: repeat(3, 1fr);
|
|
1088
|
-
gap:
|
|
1043
|
+
gap: 20px;
|
|
1089
1044
|
margin-top: 30px;
|
|
1090
1045
|
}
|
|
1091
1046
|
|
|
1092
1047
|
.solution-card {
|
|
1093
|
-
border:
|
|
1094
|
-
|
|
1048
|
+
border: 2px solid var(--border-color);
|
|
1049
|
+
background: var(--darker-grey);
|
|
1050
|
+
padding: 35px 25px;
|
|
1095
1051
|
text-align: center;
|
|
1096
1052
|
opacity: 0;
|
|
1097
1053
|
transform: translateY(20px);
|
|
1098
|
-
transition: all 0.
|
|
1054
|
+
transition: all 0.2s;
|
|
1099
1055
|
}
|
|
1100
1056
|
|
|
1101
1057
|
.solution-card:hover {
|
|
1102
1058
|
border-color: var(--accent-red);
|
|
1103
|
-
|
|
1059
|
+
transform: translateY(-3px);
|
|
1104
1060
|
}
|
|
1105
1061
|
|
|
1106
1062
|
.solution-icon {
|
|
1107
|
-
font-size:
|
|
1063
|
+
font-size: 1.8rem;
|
|
1108
1064
|
margin-bottom: 15px;
|
|
1109
1065
|
display: block;
|
|
1066
|
+
color: var(--accent-red);
|
|
1067
|
+
font-weight: bold;
|
|
1110
1068
|
}
|
|
1111
1069
|
|
|
1112
1070
|
.solution-title {
|
|
1113
|
-
font-size:
|
|
1114
|
-
margin-bottom:
|
|
1071
|
+
font-size: 0.95rem;
|
|
1072
|
+
margin-bottom: 12px;
|
|
1115
1073
|
text-transform: uppercase;
|
|
1116
1074
|
letter-spacing: 1px;
|
|
1075
|
+
color: var(--white);
|
|
1076
|
+
font-weight: 600;
|
|
1117
1077
|
}
|
|
1118
1078
|
|
|
1119
1079
|
.solution-desc {
|
|
@@ -1123,7 +1083,7 @@
|
|
|
1123
1083
|
}
|
|
1124
1084
|
|
|
1125
1085
|
/* ============================================
|
|
1126
|
-
SECTION 4: HOOK SYSTEM
|
|
1086
|
+
SECTION 4: HOOK SYSTEM - clean boxy flow
|
|
1127
1087
|
============================================ */
|
|
1128
1088
|
.hook-diagram {
|
|
1129
1089
|
width: 100%;
|
|
@@ -1131,49 +1091,53 @@
|
|
|
1131
1091
|
}
|
|
1132
1092
|
|
|
1133
1093
|
.flow-box {
|
|
1134
|
-
border:
|
|
1094
|
+
border: 2px solid var(--border-color);
|
|
1095
|
+
background: var(--darker-grey);
|
|
1135
1096
|
padding: 18px 30px;
|
|
1136
1097
|
margin: 12px 0;
|
|
1137
1098
|
text-align: center;
|
|
1138
1099
|
opacity: 0;
|
|
1139
|
-
transition: all 0.
|
|
1100
|
+
transition: all 0.2s;
|
|
1140
1101
|
}
|
|
1141
1102
|
|
|
1142
1103
|
.flow-box:hover {
|
|
1143
|
-
background: rgba(186, 12, 47, 0.1);
|
|
1144
|
-
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1145
1104
|
border-color: var(--accent-red);
|
|
1105
|
+
transform: translateX(5px);
|
|
1146
1106
|
}
|
|
1147
1107
|
|
|
1148
1108
|
.flow-arrow {
|
|
1149
1109
|
text-align: center;
|
|
1150
|
-
color: var(--
|
|
1110
|
+
color: var(--accent-red);
|
|
1151
1111
|
font-size: 1.5rem;
|
|
1152
1112
|
opacity: 0;
|
|
1153
1113
|
}
|
|
1154
1114
|
|
|
1155
1115
|
.hook-group {
|
|
1156
|
-
border: 2px dashed var(--
|
|
1116
|
+
border: 2px dashed var(--border-color);
|
|
1117
|
+
background: var(--darker-grey);
|
|
1157
1118
|
padding: 25px;
|
|
1158
1119
|
margin: 25px 0;
|
|
1159
1120
|
opacity: 0;
|
|
1160
1121
|
}
|
|
1161
1122
|
|
|
1162
1123
|
.hook-group h4 {
|
|
1163
|
-
color: var(--
|
|
1164
|
-
font-size: 0.
|
|
1124
|
+
color: var(--accent-red);
|
|
1125
|
+
font-size: 0.8rem;
|
|
1165
1126
|
text-transform: uppercase;
|
|
1166
1127
|
letter-spacing: 2px;
|
|
1167
1128
|
margin-bottom: 15px;
|
|
1129
|
+
font-weight: bold;
|
|
1168
1130
|
}
|
|
1169
1131
|
|
|
1170
1132
|
.hook-file {
|
|
1171
1133
|
padding: 12px 20px;
|
|
1172
1134
|
margin: 8px 0;
|
|
1173
|
-
border
|
|
1135
|
+
border: 1px solid var(--border-color);
|
|
1136
|
+
border-left: 3px solid var(--grey);
|
|
1137
|
+
background: var(--card-bg);
|
|
1174
1138
|
font-size: 0.9rem;
|
|
1175
1139
|
opacity: 0;
|
|
1176
|
-
transition: all 0.
|
|
1140
|
+
transition: all 0.2s;
|
|
1177
1141
|
display: flex;
|
|
1178
1142
|
justify-content: space-between;
|
|
1179
1143
|
align-items: center;
|
|
@@ -1181,22 +1145,23 @@
|
|
|
1181
1145
|
|
|
1182
1146
|
.hook-file:hover {
|
|
1183
1147
|
border-left-color: var(--accent-red);
|
|
1184
|
-
|
|
1185
|
-
padding-left: 25px;
|
|
1148
|
+
border-color: var(--accent-red);
|
|
1186
1149
|
}
|
|
1187
1150
|
|
|
1188
1151
|
.hook-file code {
|
|
1189
1152
|
color: var(--white);
|
|
1153
|
+
font-weight: 500;
|
|
1190
1154
|
}
|
|
1191
1155
|
|
|
1192
1156
|
.hook-purpose {
|
|
1193
1157
|
color: var(--grey);
|
|
1194
|
-
font-size: 0.
|
|
1158
|
+
font-size: 0.75rem;
|
|
1159
|
+
text-transform: uppercase;
|
|
1195
1160
|
}
|
|
1196
1161
|
|
|
1197
1162
|
.result-box {
|
|
1198
1163
|
display: flex;
|
|
1199
|
-
gap:
|
|
1164
|
+
gap: 30px;
|
|
1200
1165
|
justify-content: center;
|
|
1201
1166
|
margin-top: 25px;
|
|
1202
1167
|
}
|
|
@@ -1204,12 +1169,15 @@
|
|
|
1204
1169
|
.result-allowed, .result-blocked {
|
|
1205
1170
|
padding: 18px 35px;
|
|
1206
1171
|
border: 2px solid;
|
|
1172
|
+
background: var(--card-bg);
|
|
1207
1173
|
opacity: 0;
|
|
1208
|
-
transition: all 0.
|
|
1174
|
+
transition: all 0.2s;
|
|
1175
|
+
font-weight: bold;
|
|
1209
1176
|
}
|
|
1210
1177
|
|
|
1211
1178
|
.result-allowed {
|
|
1212
1179
|
border-color: var(--grey);
|
|
1180
|
+
color: var(--grey);
|
|
1213
1181
|
}
|
|
1214
1182
|
|
|
1215
1183
|
.result-blocked {
|
|
@@ -1218,62 +1186,65 @@
|
|
|
1218
1186
|
}
|
|
1219
1187
|
|
|
1220
1188
|
.result-allowed:hover, .result-blocked:hover {
|
|
1221
|
-
transform: scale(1.
|
|
1189
|
+
transform: scale(1.03);
|
|
1222
1190
|
}
|
|
1223
1191
|
|
|
1224
1192
|
/* ============================================
|
|
1225
|
-
SECTION 5: 10-PHASE WORKFLOW
|
|
1193
|
+
SECTION 5: 10-PHASE WORKFLOW - boxy grid
|
|
1226
1194
|
============================================ */
|
|
1227
1195
|
.phase-grid {
|
|
1228
1196
|
display: grid;
|
|
1229
1197
|
grid-template-columns: repeat(5, 1fr);
|
|
1230
|
-
gap:
|
|
1198
|
+
gap: 15px;
|
|
1231
1199
|
width: 100%;
|
|
1232
1200
|
margin-top: 30px;
|
|
1233
1201
|
}
|
|
1234
1202
|
|
|
1235
1203
|
.phase-box {
|
|
1236
|
-
border:
|
|
1204
|
+
border: 2px solid var(--border-color);
|
|
1205
|
+
background: var(--darker-grey);
|
|
1237
1206
|
padding: 25px 15px;
|
|
1238
1207
|
text-align: center;
|
|
1239
1208
|
opacity: 0;
|
|
1240
1209
|
transform: scale(0.9);
|
|
1241
|
-
transition: all 0.
|
|
1210
|
+
transition: all 0.2s;
|
|
1242
1211
|
position: relative;
|
|
1243
1212
|
}
|
|
1244
1213
|
|
|
1245
1214
|
.phase-box:hover {
|
|
1246
1215
|
border-color: var(--accent-red);
|
|
1247
|
-
box-shadow: 0 0 25px var(--accent-red-glow);
|
|
1248
1216
|
transform: scale(1.02);
|
|
1249
1217
|
}
|
|
1250
1218
|
|
|
1251
1219
|
.phase-box.active {
|
|
1252
1220
|
border-color: var(--accent-red);
|
|
1253
|
-
background: rgba(186, 12, 47, 0.
|
|
1221
|
+
background: rgba(186, 12, 47, 0.2);
|
|
1222
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1254
1223
|
}
|
|
1255
1224
|
|
|
1256
1225
|
.phase-number {
|
|
1257
|
-
font-size:
|
|
1258
|
-
color: var(--
|
|
1226
|
+
font-size: 2rem;
|
|
1227
|
+
color: var(--grey);
|
|
1259
1228
|
margin-bottom: 10px;
|
|
1229
|
+
font-weight: bold;
|
|
1260
1230
|
}
|
|
1261
1231
|
|
|
1262
1232
|
.phase-box.active .phase-number {
|
|
1263
|
-
color: var(--
|
|
1233
|
+
color: var(--accent-red);
|
|
1264
1234
|
}
|
|
1265
1235
|
|
|
1266
1236
|
.phase-name {
|
|
1267
|
-
font-size: 0.
|
|
1237
|
+
font-size: 0.7rem;
|
|
1268
1238
|
text-transform: uppercase;
|
|
1269
1239
|
letter-spacing: 1px;
|
|
1240
|
+
font-weight: bold;
|
|
1270
1241
|
}
|
|
1271
1242
|
|
|
1272
1243
|
.phase-status {
|
|
1273
1244
|
position: absolute;
|
|
1274
1245
|
top: 8px;
|
|
1275
1246
|
right: 8px;
|
|
1276
|
-
font-size: 0.
|
|
1247
|
+
font-size: 0.65rem;
|
|
1277
1248
|
color: var(--grey);
|
|
1278
1249
|
}
|
|
1279
1250
|
|
|
@@ -1287,26 +1258,26 @@
|
|
|
1287
1258
|
}
|
|
1288
1259
|
|
|
1289
1260
|
.phase-desc {
|
|
1290
|
-
font-size: 0.
|
|
1261
|
+
font-size: 0.65rem;
|
|
1291
1262
|
color: var(--grey);
|
|
1292
1263
|
margin-top: 8px;
|
|
1293
1264
|
line-height: 1.4;
|
|
1294
1265
|
}
|
|
1295
1266
|
|
|
1296
1267
|
/* ============================================
|
|
1297
|
-
SECTION 6: REAL WALKTHROUGH
|
|
1268
|
+
SECTION 6: REAL WALKTHROUGH - boxy steps
|
|
1298
1269
|
============================================ */
|
|
1299
1270
|
.walkthrough-step {
|
|
1300
|
-
border:
|
|
1271
|
+
border: 2px solid var(--border-color);
|
|
1272
|
+
background: var(--darker-grey);
|
|
1301
1273
|
padding: 30px;
|
|
1302
|
-
margin-bottom:
|
|
1274
|
+
margin-bottom: 20px;
|
|
1303
1275
|
opacity: 0;
|
|
1304
1276
|
transform: translateY(20px);
|
|
1305
1277
|
}
|
|
1306
1278
|
|
|
1307
1279
|
.walkthrough-step.active {
|
|
1308
1280
|
border-color: var(--accent-red);
|
|
1309
|
-
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1310
1281
|
}
|
|
1311
1282
|
|
|
1312
1283
|
.walkthrough-header {
|
|
@@ -1319,16 +1290,19 @@
|
|
|
1319
1290
|
.walkthrough-num {
|
|
1320
1291
|
width: 45px;
|
|
1321
1292
|
height: 45px;
|
|
1322
|
-
border: 2px solid var(--
|
|
1293
|
+
border: 2px solid var(--accent-red);
|
|
1294
|
+
background: transparent;
|
|
1323
1295
|
display: flex;
|
|
1324
1296
|
align-items: center;
|
|
1325
1297
|
justify-content: center;
|
|
1326
|
-
font-size: 1.
|
|
1298
|
+
font-size: 1.2rem;
|
|
1327
1299
|
flex-shrink: 0;
|
|
1300
|
+
color: var(--accent-red);
|
|
1301
|
+
font-weight: bold;
|
|
1328
1302
|
}
|
|
1329
1303
|
|
|
1330
1304
|
.walkthrough-title {
|
|
1331
|
-
font-size:
|
|
1305
|
+
font-size: 1rem;
|
|
1332
1306
|
text-transform: uppercase;
|
|
1333
1307
|
letter-spacing: 1px;
|
|
1334
1308
|
}
|
|
@@ -1344,26 +1318,28 @@
|
|
|
1344
1318
|
}
|
|
1345
1319
|
|
|
1346
1320
|
.walkthrough-example {
|
|
1347
|
-
background:
|
|
1321
|
+
background: var(--card-bg);
|
|
1322
|
+
border: 1px solid var(--border-color);
|
|
1348
1323
|
padding: 15px 20px;
|
|
1349
1324
|
font-size: 0.85rem;
|
|
1350
|
-
border-left: 3px solid var(--
|
|
1325
|
+
border-left: 3px solid var(--accent-red);
|
|
1351
1326
|
}
|
|
1352
1327
|
|
|
1353
1328
|
.walkthrough-example .label {
|
|
1354
|
-
color: var(--
|
|
1355
|
-
font-size: 0.
|
|
1329
|
+
color: var(--accent-red);
|
|
1330
|
+
font-size: 0.7rem;
|
|
1356
1331
|
text-transform: uppercase;
|
|
1357
1332
|
letter-spacing: 1px;
|
|
1358
1333
|
margin-bottom: 8px;
|
|
1334
|
+
font-weight: bold;
|
|
1359
1335
|
}
|
|
1360
1336
|
|
|
1361
1337
|
/* ============================================
|
|
1362
|
-
SECTION 7: LIVE DEMO TERMINAL
|
|
1338
|
+
SECTION 7: LIVE DEMO TERMINAL - boxy style
|
|
1363
1339
|
============================================ */
|
|
1364
1340
|
.terminal {
|
|
1365
|
-
background:
|
|
1366
|
-
border:
|
|
1341
|
+
background: var(--card-bg);
|
|
1342
|
+
border: 2px solid var(--border-color);
|
|
1367
1343
|
padding: 25px;
|
|
1368
1344
|
width: 100%;
|
|
1369
1345
|
font-size: 0.85rem;
|
|
@@ -1375,20 +1351,27 @@
|
|
|
1375
1351
|
gap: 8px;
|
|
1376
1352
|
margin-bottom: 20px;
|
|
1377
1353
|
padding-bottom: 15px;
|
|
1378
|
-
border-bottom: 1px dashed var(--
|
|
1354
|
+
border-bottom: 1px dashed var(--border-color);
|
|
1379
1355
|
}
|
|
1380
1356
|
|
|
1381
1357
|
.terminal-dot {
|
|
1382
1358
|
width: 12px;
|
|
1383
1359
|
height: 12px;
|
|
1384
|
-
border-radius:
|
|
1385
|
-
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);
|
|
1386
1367
|
}
|
|
1387
1368
|
|
|
1388
1369
|
.terminal-title {
|
|
1389
1370
|
margin-left: auto;
|
|
1390
1371
|
color: var(--grey);
|
|
1391
|
-
font-size: 0.
|
|
1372
|
+
font-size: 0.75rem;
|
|
1373
|
+
text-transform: uppercase;
|
|
1374
|
+
letter-spacing: 1px;
|
|
1392
1375
|
}
|
|
1393
1376
|
|
|
1394
1377
|
.terminal-line {
|
|
@@ -1400,8 +1383,9 @@
|
|
|
1400
1383
|
}
|
|
1401
1384
|
|
|
1402
1385
|
.terminal-prompt {
|
|
1403
|
-
color: var(--
|
|
1386
|
+
color: var(--accent-red);
|
|
1404
1387
|
flex-shrink: 0;
|
|
1388
|
+
font-weight: bold;
|
|
1405
1389
|
}
|
|
1406
1390
|
|
|
1407
1391
|
.terminal-command {
|
|
@@ -1420,8 +1404,8 @@
|
|
|
1420
1404
|
}
|
|
1421
1405
|
|
|
1422
1406
|
.terminal-allowed {
|
|
1423
|
-
color:
|
|
1424
|
-
border-left: 3px solid
|
|
1407
|
+
color: #22c55e;
|
|
1408
|
+
border-left: 3px solid #22c55e;
|
|
1425
1409
|
padding-left: 15px;
|
|
1426
1410
|
}
|
|
1427
1411
|
|
|
@@ -1431,18 +1415,18 @@
|
|
|
1431
1415
|
}
|
|
1432
1416
|
|
|
1433
1417
|
.terminal-comment {
|
|
1434
|
-
color: var(--
|
|
1435
|
-
font-size: 0.
|
|
1418
|
+
color: var(--grey);
|
|
1419
|
+
font-size: 0.75rem;
|
|
1436
1420
|
padding-left: 25px;
|
|
1437
1421
|
margin-bottom: 5px;
|
|
1438
1422
|
}
|
|
1439
1423
|
|
|
1440
1424
|
/* ============================================
|
|
1441
|
-
SECTION 8: STATE FILE
|
|
1425
|
+
SECTION 8: STATE FILE - boxy JSON viewer
|
|
1442
1426
|
============================================ */
|
|
1443
1427
|
.json-viewer {
|
|
1444
|
-
background:
|
|
1445
|
-
border:
|
|
1428
|
+
background: var(--card-bg);
|
|
1429
|
+
border: 2px solid var(--border-color);
|
|
1446
1430
|
padding: 25px;
|
|
1447
1431
|
font-size: 0.85rem;
|
|
1448
1432
|
width: 100%;
|
|
@@ -1450,7 +1434,8 @@
|
|
|
1450
1434
|
}
|
|
1451
1435
|
|
|
1452
1436
|
.json-key {
|
|
1453
|
-
color: var(--
|
|
1437
|
+
color: var(--accent-red);
|
|
1438
|
+
font-weight: 600;
|
|
1454
1439
|
}
|
|
1455
1440
|
|
|
1456
1441
|
.json-value {
|
|
@@ -1467,33 +1452,36 @@
|
|
|
1467
1452
|
}
|
|
1468
1453
|
|
|
1469
1454
|
.json-line.highlight {
|
|
1470
|
-
background: rgba(186, 12, 47, 0.
|
|
1455
|
+
background: rgba(186, 12, 47, 0.1);
|
|
1471
1456
|
margin: 0 -25px;
|
|
1472
1457
|
padding-left: 25px;
|
|
1473
1458
|
padding-right: 25px;
|
|
1474
|
-
border-left:
|
|
1459
|
+
border-left: 3px solid var(--accent-red);
|
|
1475
1460
|
}
|
|
1476
1461
|
|
|
1477
1462
|
.json-comment {
|
|
1478
|
-
color: var(--
|
|
1479
|
-
font-size: 0.
|
|
1463
|
+
color: var(--grey);
|
|
1464
|
+
font-size: 0.7rem;
|
|
1480
1465
|
margin-left: 20px;
|
|
1466
|
+
text-transform: uppercase;
|
|
1481
1467
|
}
|
|
1482
1468
|
|
|
1483
1469
|
/* ============================================
|
|
1484
|
-
SECTION 9: INSTALLATION
|
|
1470
|
+
SECTION 9: INSTALLATION - boxy style
|
|
1485
1471
|
============================================ */
|
|
1486
1472
|
.install-command {
|
|
1487
|
-
background:
|
|
1488
|
-
border:
|
|
1489
|
-
padding:
|
|
1473
|
+
background: var(--darker-grey);
|
|
1474
|
+
border: 2px solid var(--accent-red);
|
|
1475
|
+
padding: 25px 35px;
|
|
1490
1476
|
font-size: 1.1rem;
|
|
1491
1477
|
margin: 30px 0;
|
|
1492
1478
|
text-align: center;
|
|
1479
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1493
1480
|
}
|
|
1494
1481
|
|
|
1495
1482
|
.install-command code {
|
|
1496
|
-
color: var(--
|
|
1483
|
+
color: var(--accent-red);
|
|
1484
|
+
font-weight: bold;
|
|
1497
1485
|
}
|
|
1498
1486
|
|
|
1499
1487
|
.install-flow {
|
|
@@ -1504,25 +1492,29 @@
|
|
|
1504
1492
|
display: flex;
|
|
1505
1493
|
align-items: center;
|
|
1506
1494
|
gap: 25px;
|
|
1507
|
-
padding: 20px
|
|
1508
|
-
|
|
1495
|
+
padding: 20px;
|
|
1496
|
+
margin-bottom: 15px;
|
|
1497
|
+
border: 2px solid var(--border-color);
|
|
1498
|
+
background: var(--darker-grey);
|
|
1509
1499
|
opacity: 0;
|
|
1510
1500
|
transform: translateY(20px);
|
|
1511
1501
|
}
|
|
1512
1502
|
|
|
1513
1503
|
.install-step:last-child {
|
|
1514
|
-
|
|
1504
|
+
margin-bottom: 0;
|
|
1515
1505
|
}
|
|
1516
1506
|
|
|
1517
1507
|
.install-icon {
|
|
1518
1508
|
width: 50px;
|
|
1519
1509
|
height: 50px;
|
|
1520
|
-
border:
|
|
1510
|
+
border: 2px solid var(--accent-red);
|
|
1511
|
+
background: transparent;
|
|
1521
1512
|
display: flex;
|
|
1522
1513
|
align-items: center;
|
|
1523
1514
|
justify-content: center;
|
|
1524
1515
|
flex-shrink: 0;
|
|
1525
1516
|
font-size: 1.2rem;
|
|
1517
|
+
color: var(--accent-red);
|
|
1526
1518
|
}
|
|
1527
1519
|
|
|
1528
1520
|
.install-content {
|
|
@@ -1531,28 +1523,30 @@
|
|
|
1531
1523
|
|
|
1532
1524
|
.install-from {
|
|
1533
1525
|
color: var(--grey);
|
|
1534
|
-
font-size: 0.
|
|
1526
|
+
font-size: 0.85rem;
|
|
1535
1527
|
}
|
|
1536
1528
|
|
|
1537
1529
|
.install-arrow {
|
|
1538
|
-
color: var(--
|
|
1530
|
+
color: var(--accent-red);
|
|
1539
1531
|
flex-shrink: 0;
|
|
1540
1532
|
font-size: 1.2rem;
|
|
1533
|
+
font-weight: bold;
|
|
1541
1534
|
}
|
|
1542
1535
|
|
|
1543
1536
|
.install-to {
|
|
1544
1537
|
color: var(--white);
|
|
1545
|
-
font-size: 0.
|
|
1538
|
+
font-size: 0.9rem;
|
|
1539
|
+
font-weight: 500;
|
|
1546
1540
|
}
|
|
1547
1541
|
|
|
1548
1542
|
.install-note {
|
|
1549
1543
|
color: var(--grey);
|
|
1550
|
-
font-size: 0.
|
|
1544
|
+
font-size: 0.75rem;
|
|
1551
1545
|
margin-top: 5px;
|
|
1552
1546
|
}
|
|
1553
1547
|
|
|
1554
1548
|
/* ============================================
|
|
1555
|
-
SECTION 10: CREDITS
|
|
1549
|
+
SECTION 10: CREDITS - boxy style
|
|
1556
1550
|
============================================ */
|
|
1557
1551
|
#credits {
|
|
1558
1552
|
text-align: center;
|
|
@@ -1560,64 +1554,69 @@
|
|
|
1560
1554
|
|
|
1561
1555
|
.credit-links {
|
|
1562
1556
|
display: flex;
|
|
1563
|
-
gap:
|
|
1557
|
+
gap: 20px;
|
|
1564
1558
|
margin-top: 40px;
|
|
1565
1559
|
flex-wrap: wrap;
|
|
1566
1560
|
justify-content: center;
|
|
1567
1561
|
}
|
|
1568
1562
|
|
|
1569
1563
|
.credit-link {
|
|
1570
|
-
border:
|
|
1564
|
+
border: 2px solid var(--border-color);
|
|
1565
|
+
background: var(--darker-grey);
|
|
1571
1566
|
padding: 18px 35px;
|
|
1572
1567
|
text-decoration: none;
|
|
1573
1568
|
color: var(--white);
|
|
1574
|
-
transition: all 0.
|
|
1569
|
+
transition: all 0.2s;
|
|
1575
1570
|
opacity: 0;
|
|
1571
|
+
text-transform: uppercase;
|
|
1572
|
+
letter-spacing: 1px;
|
|
1573
|
+
font-size: 0.85rem;
|
|
1576
1574
|
}
|
|
1577
1575
|
|
|
1578
1576
|
.credit-link:hover {
|
|
1579
1577
|
background: var(--accent-red);
|
|
1580
|
-
color:
|
|
1578
|
+
color: var(--white);
|
|
1581
1579
|
border-color: var(--accent-red);
|
|
1582
|
-
box-shadow: 0 0
|
|
1580
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
1583
1581
|
}
|
|
1584
1582
|
|
|
1585
1583
|
.made-with {
|
|
1586
1584
|
margin-top: 50px;
|
|
1587
1585
|
color: var(--grey);
|
|
1588
|
-
font-size: 0.
|
|
1586
|
+
font-size: 0.9rem;
|
|
1589
1587
|
opacity: 0;
|
|
1590
1588
|
line-height: 1.8;
|
|
1591
1589
|
}
|
|
1592
1590
|
|
|
1593
1591
|
/* ============================================
|
|
1594
|
-
SECTION INDICATORS
|
|
1592
|
+
SECTION INDICATORS - square boxy dots
|
|
1595
1593
|
============================================ */
|
|
1596
1594
|
.section-indicator {
|
|
1597
1595
|
position: fixed;
|
|
1598
|
-
right:
|
|
1596
|
+
right: 20px;
|
|
1599
1597
|
top: 50%;
|
|
1600
1598
|
transform: translateY(-50%);
|
|
1601
1599
|
display: flex;
|
|
1602
1600
|
flex-direction: column;
|
|
1603
|
-
gap:
|
|
1601
|
+
gap: 10px;
|
|
1604
1602
|
z-index: 1000;
|
|
1605
1603
|
}
|
|
1606
1604
|
|
|
1607
1605
|
.section-dot {
|
|
1608
1606
|
width: 10px;
|
|
1609
1607
|
height: 10px;
|
|
1610
|
-
border:
|
|
1611
|
-
border-radius:
|
|
1608
|
+
border: 2px solid var(--border-color);
|
|
1609
|
+
border-radius: 0;
|
|
1612
1610
|
cursor: pointer;
|
|
1613
|
-
transition: all 0.
|
|
1611
|
+
transition: all 0.2s;
|
|
1612
|
+
background: var(--card-bg);
|
|
1614
1613
|
}
|
|
1615
1614
|
|
|
1616
1615
|
.section-dot:hover,
|
|
1617
1616
|
.section-dot.active {
|
|
1618
1617
|
background: var(--accent-red);
|
|
1619
1618
|
border-color: var(--accent-red);
|
|
1620
|
-
box-shadow: 0 0
|
|
1619
|
+
box-shadow: 0 0 8px var(--accent-red-glow);
|
|
1621
1620
|
}
|
|
1622
1621
|
|
|
1623
1622
|
/* ============================================
|
|
@@ -1700,16 +1699,82 @@
|
|
|
1700
1699
|
<nav class="nav">
|
|
1701
1700
|
<button class="nav-btn" id="playBtn">▶ AUTO PLAY</button>
|
|
1702
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>
|
|
1703
1703
|
<button class="nav-btn" id="resetBtn">↺ RESTART</button>
|
|
1704
1704
|
</nav>
|
|
1705
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
|
+
|
|
1706
1768
|
<!-- Audio Narration Player (hidden) -->
|
|
1707
1769
|
<audio id="narrationAudio" preload="auto">
|
|
1708
|
-
<source src="audio/narration.mp3" type="audio/mpeg">
|
|
1770
|
+
<source src="audio/narration-adam.mp3" type="audio/mpeg">
|
|
1709
1771
|
</audio>
|
|
1710
1772
|
|
|
1711
1773
|
<!-- Audio Progress Bar -->
|
|
1712
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>
|
|
1713
1778
|
<div class="audio-time-display">
|
|
1714
1779
|
<span id="audioCurrentTime">0:00</span>
|
|
1715
1780
|
<span id="audioTotalTime">0:00</span>
|
|
@@ -1733,7 +1798,7 @@
|
|
|
1733
1798
|
<span class="hustle-word hustle-highlight">API-DEV-TOOLS</span>
|
|
1734
1799
|
</div>
|
|
1735
1800
|
<div class="package-name" id="packageName">@hustle-together/api-dev-tools</div>
|
|
1736
|
-
<div class="version" id="versionText">v2.0.
|
|
1801
|
+
<div class="version" id="versionText">v2.0.8</div>
|
|
1737
1802
|
<p class="tagline">"Hustle together. Share resources. Build stronger."<span class="cursor"></span></p>
|
|
1738
1803
|
|
|
1739
1804
|
<div class="intro-text">
|
|
@@ -2804,6 +2869,7 @@
|
|
|
2804
2869
|
const audioCurrentTime = document.getElementById('audioCurrentTime');
|
|
2805
2870
|
const audioTotalTime = document.getElementById('audioTotalTime');
|
|
2806
2871
|
const audioSectionMarkers = document.getElementById('audioSectionMarkers');
|
|
2872
|
+
const chapterButtons = document.getElementById('chapterButtons');
|
|
2807
2873
|
|
|
2808
2874
|
let audioEnabled = true; // Audio on by default
|
|
2809
2875
|
let timingData = null;
|
|
@@ -2819,11 +2885,15 @@
|
|
|
2819
2885
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
2820
2886
|
}
|
|
2821
2887
|
|
|
2822
|
-
//
|
|
2823
|
-
|
|
2824
|
-
|
|
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;
|
|
2825
2895
|
try {
|
|
2826
|
-
const response = await fetch(
|
|
2896
|
+
const response = await fetch(currentTimingUrl);
|
|
2827
2897
|
if (!response.ok) throw new Error('Failed to load timing data');
|
|
2828
2898
|
timingData = await response.json();
|
|
2829
2899
|
|
|
@@ -2845,6 +2915,116 @@
|
|
|
2845
2915
|
});
|
|
2846
2916
|
}
|
|
2847
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
|
+
|
|
2848
3028
|
console.log('Timing data loaded:', timingData.sections.length, 'sections,', timingData.highlights.length, 'highlights');
|
|
2849
3029
|
return timingData;
|
|
2850
3030
|
} catch (error) {
|
|
@@ -2872,8 +3052,8 @@
|
|
|
2872
3052
|
|
|
2873
3053
|
if (isContainer) {
|
|
2874
3054
|
gsap.fromTo(el,
|
|
2875
|
-
{ boxShadow: '0
|
|
2876
|
-
{ boxShadow: '0
|
|
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' }
|
|
2877
3057
|
);
|
|
2878
3058
|
}
|
|
2879
3059
|
}
|
|
@@ -2891,6 +3071,29 @@
|
|
|
2891
3071
|
}
|
|
2892
3072
|
}
|
|
2893
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
|
+
|
|
2894
3097
|
// Handle audio time update (syncs highlights with narration)
|
|
2895
3098
|
function onAudioTimeUpdate() {
|
|
2896
3099
|
if (!timingData || !audioEnabled) return;
|
|
@@ -2906,6 +3109,13 @@
|
|
|
2906
3109
|
audioCurrentTime.textContent = formatTime(currentTime);
|
|
2907
3110
|
}
|
|
2908
3111
|
|
|
3112
|
+
// Update active chapter button
|
|
3113
|
+
const sectionIdx = getCurrentSectionIndex(currentTime);
|
|
3114
|
+
if (sectionIdx !== currentSectionIdx) {
|
|
3115
|
+
currentSectionIdx = sectionIdx;
|
|
3116
|
+
updateActiveChapter(sectionIdx);
|
|
3117
|
+
}
|
|
3118
|
+
|
|
2909
3119
|
// Check for highlight triggers
|
|
2910
3120
|
for (let i = highlightIndex; i < timingData.highlights.length; i++) {
|
|
2911
3121
|
const highlight = timingData.highlights[i];
|
|
@@ -2921,23 +3131,73 @@
|
|
|
2921
3131
|
}
|
|
2922
3132
|
|
|
2923
3133
|
// Handle seek on progress bar click
|
|
2924
|
-
function onProgressBarClick(e) {
|
|
2925
|
-
|
|
3134
|
+
async function onProgressBarClick(e) {
|
|
3135
|
+
e.stopPropagation(); // Prevent event bubbling
|
|
2926
3136
|
|
|
3137
|
+
// Use the progress bar's rect for calculation
|
|
2927
3138
|
const rect = audioProgressBar.getBoundingClientRect();
|
|
2928
3139
|
const clickX = e.clientX - rect.left;
|
|
2929
|
-
const percentage = clickX / rect.width;
|
|
2930
|
-
|
|
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
|
+
}
|
|
2931
3184
|
|
|
2932
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
|
+
}
|
|
2933
3191
|
|
|
2934
3192
|
// Reset highlight tracking
|
|
2935
3193
|
highlightIndex = 0;
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
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
|
+
}
|
|
2941
3201
|
}
|
|
2942
3202
|
}
|
|
2943
3203
|
}
|
|
@@ -2963,9 +3223,11 @@
|
|
|
2963
3223
|
playBtn.classList.add('active');
|
|
2964
3224
|
playBtn.textContent = '⏸ STOP';
|
|
2965
3225
|
|
|
2966
|
-
//
|
|
3226
|
+
// KILL the intro lighting animation completely to avoid visual conflict
|
|
3227
|
+
// (Don't just pause - kill it so it won't restart)
|
|
2967
3228
|
if (window.introLightingTL) {
|
|
2968
|
-
window.introLightingTL.
|
|
3229
|
+
window.introLightingTL.kill();
|
|
3230
|
+
window.introLightingTL = null;
|
|
2969
3231
|
// Reset the phase nodes to clean state
|
|
2970
3232
|
document.querySelectorAll('#introFlow .phase-node').forEach(n => n.classList.remove('active'));
|
|
2971
3233
|
document.querySelectorAll('#introFlow .phase-connector-arrow').forEach(a => a.classList.remove('active'));
|
|
@@ -3016,10 +3278,8 @@
|
|
|
3016
3278
|
highlightIndex = 0;
|
|
3017
3279
|
currentSectionIdx = 0;
|
|
3018
3280
|
|
|
3019
|
-
//
|
|
3020
|
-
|
|
3021
|
-
window.introLightingTL.restart();
|
|
3022
|
-
}
|
|
3281
|
+
// Note: We killed the intro lighting animation, so don't try to restart it
|
|
3282
|
+
// The animation was distracting during/after narration playback
|
|
3023
3283
|
}
|
|
3024
3284
|
|
|
3025
3285
|
// Audio ended
|
|
@@ -3069,6 +3329,31 @@
|
|
|
3069
3329
|
audioToggleBtn.addEventListener('click', toggleAudio);
|
|
3070
3330
|
narrationAudio.addEventListener('timeupdate', onAudioTimeUpdate);
|
|
3071
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
|
+
|
|
3072
3357
|
audioProgressBar.addEventListener('click', onProgressBarClick);
|
|
3073
3358
|
|
|
3074
3359
|
// Check if audio file is accessible
|
|
@@ -3087,6 +3372,10 @@
|
|
|
3087
3372
|
// RESET BUTTON
|
|
3088
3373
|
// ============================================
|
|
3089
3374
|
document.getElementById('resetBtn').addEventListener('click', () => {
|
|
3375
|
+
// Stop audio and auto-play first
|
|
3376
|
+
stopAutoPlay();
|
|
3377
|
+
|
|
3378
|
+
// Scroll to top
|
|
3090
3379
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
3091
3380
|
|
|
3092
3381
|
// Reset animations
|
|
@@ -3104,11 +3393,20 @@
|
|
|
3104
3393
|
gsap.set('.credit-link, .made-with', { opacity: 0 });
|
|
3105
3394
|
gsap.set('.intro-text, .scroll-hint', { opacity: 0 });
|
|
3106
3395
|
|
|
3396
|
+
// Reset phase boxes
|
|
3107
3397
|
document.querySelectorAll('.phase-box').forEach(box => {
|
|
3108
3398
|
box.classList.remove('active');
|
|
3109
3399
|
box.querySelector('.phase-status').textContent = '';
|
|
3110
3400
|
});
|
|
3111
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
|
+
|
|
3112
3410
|
ScrollTrigger.refresh();
|
|
3113
3411
|
}, 500);
|
|
3114
3412
|
});
|
|
@@ -3282,6 +3580,173 @@
|
|
|
3282
3580
|
});
|
|
3283
3581
|
});
|
|
3284
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
|
+
|
|
3285
3750
|
</script>
|
|
3286
3751
|
|
|
3287
3752
|
</body>
|