@hustle-together/api-dev-tools 2.0.7 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +343 -467
- package/bin/cli.js +229 -15
- 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 +581 -0
- package/demo/audio/generate-narration.js +120 -56
- package/demo/audio/generate-voice-previews.js +140 -0
- package/demo/audio/narration-adam-timing.json +4675 -0
- package/demo/audio/narration-adam.mp3 +0 -0
- package/demo/audio/narration-creature-timing.json +4675 -0
- package/demo/audio/narration-creature.mp3 +0 -0
- package/demo/audio/narration-gaming-timing.json +4675 -0
- package/demo/audio/narration-gaming.mp3 +0 -0
- package/demo/audio/narration-hope-timing.json +4675 -0
- package/demo/audio/narration-hope.mp3 +0 -0
- package/demo/audio/narration-mark-timing.json +4675 -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 +2618 -1036
- package/hooks/api-workflow-check.py +2 -0
- package/hooks/enforce-deep-research.py +180 -0
- package/hooks/enforce-disambiguation.py +149 -0
- package/hooks/enforce-documentation.py +187 -0
- package/hooks/enforce-environment.py +249 -0
- package/hooks/enforce-refactor.py +187 -0
- package/hooks/enforce-research.py +93 -46
- package/hooks/enforce-schema.py +186 -0
- package/hooks/enforce-scope.py +156 -0
- package/hooks/enforce-tdd-red.py +246 -0
- package/hooks/enforce-verify.py +186 -0
- 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 +282 -0
- package/package.json +3 -2
- package/scripts/collect-test-results.ts +404 -0
- package/scripts/extract-parameters.ts +483 -0
- package/scripts/generate-test-manifest.ts +520 -0
- package/templates/CLAUDE-SECTION.md +84 -0
- package/templates/api-dev-state.json +83 -8
- package/templates/api-test/page.tsx +315 -0
- package/templates/api-test/test-structure/route.ts +269 -0
- package/templates/research-index.json +6 -0
- package/templates/settings.json +59 -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,32 @@
|
|
|
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
|
+
/* Main accent - Hustle red */
|
|
35
|
+
--accent-red: #ba0c2f;
|
|
32
36
|
--accent-red-glow: rgba(186, 12, 47, 0.4);
|
|
37
|
+
/* Claude Code section only - orange */
|
|
38
|
+
--claude-orange: #f97316;
|
|
39
|
+
--claude-orange-glow: rgba(249, 115, 22, 0.4);
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
body {
|
|
36
43
|
background: var(--black);
|
|
37
44
|
color: var(--white);
|
|
38
|
-
font-family: 'Courier New',
|
|
39
|
-
line-height: 1.
|
|
45
|
+
font-family: 'Courier New', monospace;
|
|
46
|
+
line-height: 1.6;
|
|
40
47
|
overflow-x: hidden;
|
|
41
48
|
}
|
|
42
49
|
|
|
43
50
|
/* ============================================
|
|
44
|
-
ANIMATED BACKGROUND
|
|
51
|
+
ANIMATED BACKGROUND GRID
|
|
45
52
|
============================================ */
|
|
46
53
|
.background-pattern {
|
|
47
54
|
position: fixed;
|
|
@@ -49,21 +56,20 @@
|
|
|
49
56
|
left: 0;
|
|
50
57
|
width: 100%;
|
|
51
58
|
height: 100%;
|
|
52
|
-
pointer-events: none;
|
|
53
59
|
z-index: -1;
|
|
54
|
-
|
|
60
|
+
opacity: 0.4;
|
|
55
61
|
}
|
|
56
62
|
|
|
57
63
|
.grid-svg {
|
|
58
64
|
width: 100%;
|
|
59
65
|
height: 100%;
|
|
60
|
-
opacity: 0.15;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
68
|
.grid-line {
|
|
64
|
-
stroke: var(--
|
|
69
|
+
stroke: var(--accent-red);
|
|
65
70
|
stroke-width: 0.5;
|
|
66
71
|
fill: none;
|
|
72
|
+
opacity: 0.4;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
/* Floating particles */
|
|
@@ -73,130 +79,101 @@
|
|
|
73
79
|
left: 0;
|
|
74
80
|
width: 100%;
|
|
75
81
|
height: 100%;
|
|
76
|
-
pointer-events: none;
|
|
77
82
|
z-index: -1;
|
|
83
|
+
pointer-events: none;
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
.particle {
|
|
81
87
|
position: absolute;
|
|
82
88
|
width: 4px;
|
|
83
89
|
height: 4px;
|
|
84
|
-
background: var(--
|
|
85
|
-
opacity: 0.3;
|
|
90
|
+
background: var(--accent-red);
|
|
86
91
|
border-radius: 50%;
|
|
92
|
+
opacity: 0;
|
|
93
|
+
box-shadow: 0 0 6px var(--accent-red-glow);
|
|
87
94
|
}
|
|
88
95
|
|
|
96
|
+
/* Particle shape variations */
|
|
89
97
|
.particle.square {
|
|
90
98
|
border-radius: 0;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
width: 8px;
|
|
94
|
-
height: 8px;
|
|
99
|
+
width: 6px;
|
|
100
|
+
height: 6px;
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
.particle.plus {
|
|
104
|
+
width: 8px;
|
|
105
|
+
height: 2px;
|
|
106
|
+
background: var(--accent-red);
|
|
98
107
|
border-radius: 0;
|
|
99
|
-
background: transparent;
|
|
100
|
-
width: 12px;
|
|
101
|
-
height: 12px;
|
|
102
108
|
}
|
|
103
109
|
|
|
104
|
-
.particle.plus::before
|
|
105
|
-
.particle.plus::after {
|
|
110
|
+
.particle.plus::before {
|
|
106
111
|
content: '';
|
|
107
112
|
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;
|
|
113
|
+
width: 2px;
|
|
114
|
+
height: 8px;
|
|
115
|
+
background: var(--accent-red);
|
|
116
|
+
left: 3px;
|
|
117
|
+
top: -3px;
|
|
123
118
|
}
|
|
124
119
|
|
|
125
|
-
/* X shape particle */
|
|
126
120
|
.particle.x-shape {
|
|
121
|
+
width: 8px;
|
|
122
|
+
height: 2px;
|
|
123
|
+
background: var(--accent-red);
|
|
127
124
|
border-radius: 0;
|
|
128
|
-
|
|
129
|
-
width: 14px;
|
|
130
|
-
height: 14px;
|
|
125
|
+
transform: rotate(45deg);
|
|
131
126
|
}
|
|
132
127
|
|
|
133
|
-
.particle.x-shape::before
|
|
134
|
-
.particle.x-shape::after {
|
|
128
|
+
.particle.x-shape::before {
|
|
135
129
|
content: '';
|
|
136
130
|
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);
|
|
131
|
+
width: 8px;
|
|
132
|
+
height: 2px;
|
|
133
|
+
background: var(--accent-red);
|
|
134
|
+
transform: rotate(90deg);
|
|
154
135
|
}
|
|
155
136
|
|
|
156
|
-
/* Line/dash particle */
|
|
157
137
|
.particle.line {
|
|
158
|
-
|
|
159
|
-
width: 20px;
|
|
138
|
+
width: 12px;
|
|
160
139
|
height: 1px;
|
|
161
|
-
|
|
140
|
+
border-radius: 0;
|
|
162
141
|
}
|
|
163
142
|
|
|
164
143
|
.particle.line-v {
|
|
165
|
-
border-radius: 0;
|
|
166
144
|
width: 1px;
|
|
167
|
-
height:
|
|
168
|
-
|
|
145
|
+
height: 12px;
|
|
146
|
+
border-radius: 0;
|
|
169
147
|
}
|
|
170
148
|
|
|
171
|
-
/* Circle outline particle */
|
|
172
149
|
.particle.circle-outline {
|
|
173
|
-
width: 16px;
|
|
174
|
-
height: 16px;
|
|
175
|
-
border-radius: 50%;
|
|
176
150
|
background: transparent;
|
|
177
|
-
border: 1px solid var(--
|
|
151
|
+
border: 1px solid var(--accent-red);
|
|
152
|
+
width: 8px;
|
|
153
|
+
height: 8px;
|
|
178
154
|
}
|
|
179
155
|
|
|
180
|
-
/* Triangle particle */
|
|
181
156
|
.particle.triangle {
|
|
182
157
|
width: 0;
|
|
183
158
|
height: 0;
|
|
184
159
|
background: transparent;
|
|
185
|
-
border-left:
|
|
186
|
-
border-right:
|
|
187
|
-
border-bottom:
|
|
160
|
+
border-left: 4px solid transparent;
|
|
161
|
+
border-right: 4px solid transparent;
|
|
162
|
+
border-bottom: 7px solid var(--accent-red);
|
|
163
|
+
border-radius: 0;
|
|
188
164
|
}
|
|
189
165
|
|
|
190
166
|
/* Cursor glow effect */
|
|
191
167
|
.cursor-glow {
|
|
192
168
|
position: fixed;
|
|
193
|
-
width:
|
|
194
|
-
height:
|
|
195
|
-
|
|
169
|
+
width: 500px;
|
|
170
|
+
height: 500px;
|
|
171
|
+
border-radius: 50%;
|
|
172
|
+
background: radial-gradient(circle, var(--accent-red-glow) 0%, rgba(186, 12, 47, 0.15) 30%, transparent 70%);
|
|
196
173
|
pointer-events: none;
|
|
197
|
-
z-index:
|
|
174
|
+
z-index: -1;
|
|
198
175
|
transform: translate(-50%, -50%);
|
|
199
|
-
|
|
176
|
+
opacity: 0.7;
|
|
200
177
|
}
|
|
201
178
|
|
|
202
179
|
/* Scanlines overlay */
|
|
@@ -206,26 +183,27 @@
|
|
|
206
183
|
left: 0;
|
|
207
184
|
width: 100%;
|
|
208
185
|
height: 100%;
|
|
209
|
-
pointer-events: none;
|
|
210
|
-
z-index: 9999;
|
|
211
186
|
background: repeating-linear-gradient(
|
|
212
187
|
0deg,
|
|
213
188
|
transparent,
|
|
214
189
|
transparent 2px,
|
|
215
|
-
rgba(0, 0, 0, 0.
|
|
216
|
-
rgba(0, 0, 0, 0.
|
|
190
|
+
rgba(0, 0, 0, 0.05) 2px,
|
|
191
|
+
rgba(0, 0, 0, 0.05) 4px
|
|
217
192
|
);
|
|
218
|
-
|
|
193
|
+
pointer-events: none;
|
|
194
|
+
z-index: 9999;
|
|
195
|
+
opacity: 0.2;
|
|
219
196
|
}
|
|
220
197
|
|
|
221
|
-
/*
|
|
198
|
+
/* Corner decorations */
|
|
222
199
|
.corner-decoration {
|
|
223
200
|
position: fixed;
|
|
224
|
-
width:
|
|
225
|
-
height:
|
|
226
|
-
border: 1px dashed var(--
|
|
227
|
-
opacity: 0.4;
|
|
201
|
+
width: 100px;
|
|
202
|
+
height: 100px;
|
|
203
|
+
border: 1px dashed var(--accent-red);
|
|
228
204
|
z-index: 1;
|
|
205
|
+
pointer-events: none;
|
|
206
|
+
opacity: 0.4;
|
|
229
207
|
}
|
|
230
208
|
|
|
231
209
|
.corner-decoration.top-left {
|
|
@@ -237,7 +215,7 @@
|
|
|
237
215
|
|
|
238
216
|
.corner-decoration.top-right {
|
|
239
217
|
top: 20px;
|
|
240
|
-
right:
|
|
218
|
+
right: 20px;
|
|
241
219
|
border-left: none;
|
|
242
220
|
border-bottom: none;
|
|
243
221
|
}
|
|
@@ -256,299 +234,86 @@
|
|
|
256
234
|
border-top: none;
|
|
257
235
|
}
|
|
258
236
|
|
|
259
|
-
/*
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
width: 2px;
|
|
263
|
-
background: linear-gradient(to bottom, transparent, var(--dark-grey), transparent);
|
|
264
|
-
left: 50%;
|
|
265
|
-
bottom: -60px;
|
|
266
|
-
height: 60px;
|
|
267
|
-
opacity: 0.3;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/* Status badge animations */
|
|
271
|
-
.status-badge {
|
|
272
|
-
display: inline-flex;
|
|
273
|
-
align-items: center;
|
|
274
|
-
gap: 8px;
|
|
275
|
-
padding: 6px 14px;
|
|
276
|
-
border: 1px dashed var(--grey);
|
|
277
|
-
font-size: 0.75rem;
|
|
278
|
-
text-transform: uppercase;
|
|
279
|
-
letter-spacing: 2px;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
.status-badge .pulse {
|
|
283
|
-
width: 8px;
|
|
284
|
-
height: 8px;
|
|
285
|
-
border-radius: 50%;
|
|
286
|
-
background: var(--grey);
|
|
287
|
-
animation: pulse 2s infinite;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
.status-badge.active .pulse {
|
|
291
|
-
background: var(--accent-red);
|
|
292
|
-
box-shadow: 0 0 10px var(--accent-red);
|
|
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;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
.data-node-label {
|
|
325
|
-
font-size: 0.75rem;
|
|
326
|
-
text-transform: uppercase;
|
|
327
|
-
letter-spacing: 1px;
|
|
328
|
-
color: var(--grey);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.data-arrow {
|
|
332
|
-
color: var(--grey);
|
|
333
|
-
font-size: 1.5rem;
|
|
334
|
-
animation: arrowPulse 1.5s infinite;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
@keyframes arrowPulse {
|
|
338
|
-
0%, 100% { opacity: 0.3; transform: translateX(0); }
|
|
339
|
-
50% { opacity: 1; transform: translateX(5px); }
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
/* Visual checkmarks for completed items */
|
|
343
|
-
.visual-check {
|
|
344
|
-
display: inline-flex;
|
|
345
|
-
align-items: center;
|
|
346
|
-
justify-content: center;
|
|
347
|
-
width: 24px;
|
|
348
|
-
height: 24px;
|
|
349
|
-
border: 2px solid var(--grey);
|
|
350
|
-
margin-right: 12px;
|
|
351
|
-
font-size: 0.8rem;
|
|
352
|
-
transition: all 0.3s;
|
|
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);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/* Animated underline effect */
|
|
366
|
-
.highlight-underline {
|
|
367
|
-
position: relative;
|
|
368
|
-
display: inline-block;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.highlight-underline::after {
|
|
372
|
-
content: '';
|
|
373
|
-
position: absolute;
|
|
374
|
-
bottom: -4px;
|
|
375
|
-
left: 0;
|
|
376
|
-
width: 0;
|
|
377
|
-
height: 2px;
|
|
378
|
-
background: var(--accent-red);
|
|
379
|
-
transition: width 0.3s ease;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
.highlight-underline:hover::after {
|
|
383
|
-
width: 100%;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
/* Visual timeline indicator */
|
|
387
|
-
.timeline-indicator {
|
|
388
|
-
position: absolute;
|
|
389
|
-
left: -40px;
|
|
390
|
-
top: 0;
|
|
391
|
-
bottom: 0;
|
|
392
|
-
width: 2px;
|
|
393
|
-
background: var(--dark-grey);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
.timeline-indicator::before {
|
|
397
|
-
content: '';
|
|
398
|
-
position: absolute;
|
|
399
|
-
top: 50%;
|
|
400
|
-
left: -4px;
|
|
401
|
-
width: 10px;
|
|
402
|
-
height: 10px;
|
|
403
|
-
border: 2px solid var(--grey);
|
|
404
|
-
border-radius: 50%;
|
|
405
|
-
background: var(--black);
|
|
406
|
-
transform: translateY(-50%);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
.timeline-indicator.active::before {
|
|
410
|
-
border-color: var(--accent-red);
|
|
411
|
-
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/* Animated typing indicator */
|
|
415
|
-
.typing-indicator {
|
|
416
|
-
display: inline-flex;
|
|
417
|
-
gap: 4px;
|
|
418
|
-
padding: 10px 15px;
|
|
419
|
-
background: rgba(255,255,255,0.03);
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
.typing-indicator span {
|
|
423
|
-
width: 6px;
|
|
424
|
-
height: 6px;
|
|
425
|
-
background: var(--grey);
|
|
426
|
-
border-radius: 50%;
|
|
427
|
-
animation: typing 1.4s infinite both;
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
|
|
431
|
-
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
|
|
432
|
-
|
|
433
|
-
@keyframes typing {
|
|
434
|
-
0%, 100% { transform: translateY(0); opacity: 0.3; }
|
|
435
|
-
50% { transform: translateY(-8px); opacity: 1; }
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/* Phase connection arrows */
|
|
439
|
-
.phase-arrow {
|
|
440
|
-
display: none;
|
|
441
|
-
position: absolute;
|
|
442
|
-
right: -22px;
|
|
443
|
-
top: 50%;
|
|
444
|
-
transform: translateY(-50%);
|
|
445
|
-
color: var(--dark-grey);
|
|
446
|
-
font-size: 1rem;
|
|
447
|
-
z-index: 2;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
.phase-box:not(:nth-child(5n)) .phase-arrow {
|
|
451
|
-
display: block;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
/* Glowing border effect for active elements */
|
|
455
|
-
.glow-border {
|
|
456
|
-
position: relative;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
.glow-border::before {
|
|
460
|
-
content: '';
|
|
461
|
-
position: absolute;
|
|
462
|
-
top: -2px;
|
|
463
|
-
left: -2px;
|
|
464
|
-
right: -2px;
|
|
465
|
-
bottom: -2px;
|
|
466
|
-
background: linear-gradient(45deg, transparent, var(--white), transparent);
|
|
467
|
-
opacity: 0;
|
|
468
|
-
transition: opacity 0.3s;
|
|
469
|
-
z-index: -1;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
.glow-border:hover::before {
|
|
473
|
-
opacity: 0.1;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/* Navigation */
|
|
237
|
+
/* ============================================
|
|
238
|
+
NAVIGATION
|
|
239
|
+
============================================ */
|
|
477
240
|
.nav {
|
|
478
241
|
position: fixed;
|
|
479
|
-
top:
|
|
480
|
-
|
|
242
|
+
top: 0;
|
|
243
|
+
left: 0;
|
|
244
|
+
right: 0;
|
|
481
245
|
z-index: 1000;
|
|
482
246
|
display: flex;
|
|
247
|
+
justify-content: flex-end;
|
|
483
248
|
gap: 10px;
|
|
249
|
+
padding: 20px 40px;
|
|
250
|
+
background: rgba(10, 10, 10, 0.9);
|
|
251
|
+
border-bottom: 1px dashed var(--border-color);
|
|
484
252
|
}
|
|
485
253
|
|
|
486
254
|
.nav-btn {
|
|
487
255
|
background: transparent;
|
|
488
|
-
border: 1px
|
|
489
|
-
color: var(--
|
|
256
|
+
border: 1px solid var(--border-color);
|
|
257
|
+
color: var(--grey);
|
|
490
258
|
padding: 8px 16px;
|
|
491
259
|
font-family: inherit;
|
|
260
|
+
font-size: 0.75rem;
|
|
492
261
|
cursor: pointer;
|
|
493
|
-
transition: all 0.
|
|
262
|
+
transition: all 0.2s;
|
|
263
|
+
text-transform: uppercase;
|
|
264
|
+
letter-spacing: 1px;
|
|
494
265
|
}
|
|
495
266
|
|
|
496
267
|
.nav-btn:hover {
|
|
497
|
-
background: var(--accent-red);
|
|
498
|
-
color: #fff;
|
|
499
268
|
border-color: var(--accent-red);
|
|
500
|
-
|
|
269
|
+
color: var(--accent-red);
|
|
270
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
501
271
|
}
|
|
502
272
|
|
|
503
273
|
.nav-btn.active {
|
|
504
274
|
background: var(--accent-red);
|
|
505
|
-
color: #fff;
|
|
506
275
|
border-color: var(--accent-red);
|
|
276
|
+
color: var(--white);
|
|
507
277
|
}
|
|
508
278
|
|
|
509
279
|
.nav-btn.disabled {
|
|
510
|
-
opacity: 0.
|
|
280
|
+
opacity: 0.4;
|
|
511
281
|
cursor: not-allowed;
|
|
512
|
-
text-decoration: line-through;
|
|
513
282
|
}
|
|
514
283
|
|
|
515
284
|
.nav-btn.disabled:hover {
|
|
516
|
-
|
|
285
|
+
border-color: var(--border-color);
|
|
517
286
|
color: var(--grey);
|
|
518
|
-
border-color: var(--grey);
|
|
519
287
|
box-shadow: none;
|
|
520
288
|
}
|
|
521
289
|
|
|
522
290
|
/* ============================================
|
|
523
|
-
AUDIO
|
|
291
|
+
AUDIO PLAYER
|
|
524
292
|
============================================ */
|
|
525
293
|
.audio-progress-container {
|
|
526
294
|
position: fixed;
|
|
527
|
-
bottom:
|
|
528
|
-
left:
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
border: 1px dashed var(--grey);
|
|
534
|
-
padding: 15px 20px;
|
|
295
|
+
bottom: 0;
|
|
296
|
+
left: 0;
|
|
297
|
+
right: 0;
|
|
298
|
+
background: rgba(10, 10, 10, 0.95);
|
|
299
|
+
border-top: 1px dashed var(--border-color);
|
|
300
|
+
padding: 15px 40px;
|
|
535
301
|
z-index: 1001;
|
|
536
|
-
border-radius: 0;
|
|
537
302
|
}
|
|
538
303
|
|
|
539
304
|
.audio-time-display {
|
|
540
305
|
display: flex;
|
|
541
306
|
justify-content: space-between;
|
|
542
|
-
font-size: 0.
|
|
307
|
+
font-size: 0.8rem;
|
|
543
308
|
color: var(--grey);
|
|
544
|
-
margin-bottom:
|
|
545
|
-
font-family: 'Courier New', monospace;
|
|
309
|
+
margin-bottom: 8px;
|
|
546
310
|
}
|
|
547
311
|
|
|
548
312
|
.audio-progress-bar {
|
|
549
313
|
position: relative;
|
|
550
|
-
height:
|
|
551
|
-
background: var(--
|
|
314
|
+
height: 12px;
|
|
315
|
+
background: var(--darker-grey);
|
|
316
|
+
border: 1px solid var(--border-color);
|
|
552
317
|
cursor: pointer;
|
|
553
318
|
}
|
|
554
319
|
|
|
@@ -557,6 +322,8 @@
|
|
|
557
322
|
background: var(--accent-red);
|
|
558
323
|
width: 0%;
|
|
559
324
|
transition: width 0.1s linear;
|
|
325
|
+
pointer-events: none;
|
|
326
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
560
327
|
}
|
|
561
328
|
|
|
562
329
|
.audio-section-markers {
|
|
@@ -570,154 +337,341 @@
|
|
|
570
337
|
|
|
571
338
|
.audio-section-marker {
|
|
572
339
|
position: absolute;
|
|
573
|
-
top: -
|
|
574
|
-
width:
|
|
575
|
-
height:
|
|
340
|
+
top: -2px;
|
|
341
|
+
width: 3px;
|
|
342
|
+
height: 16px;
|
|
576
343
|
background: var(--white);
|
|
577
344
|
opacity: 0.5;
|
|
345
|
+
pointer-events: none; /* Don't intercept clicks - let them pass to progress bar */
|
|
346
|
+
margin-left: -1px;
|
|
578
347
|
}
|
|
579
348
|
|
|
580
|
-
|
|
581
|
-
|
|
349
|
+
/* Chapter buttons */
|
|
350
|
+
.chapter-buttons {
|
|
351
|
+
display: flex;
|
|
352
|
+
gap: 8px;
|
|
353
|
+
margin-bottom: 12px;
|
|
354
|
+
flex-wrap: wrap;
|
|
355
|
+
justify-content: center;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.chapter-btn {
|
|
359
|
+
padding: 6px 12px;
|
|
360
|
+
font-size: 0.65rem;
|
|
361
|
+
background: transparent;
|
|
362
|
+
border: 1px solid var(--border-color);
|
|
363
|
+
color: var(--grey);
|
|
364
|
+
cursor: pointer;
|
|
365
|
+
text-transform: uppercase;
|
|
366
|
+
letter-spacing: 1px;
|
|
367
|
+
transition: all 0.2s;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.chapter-btn:hover {
|
|
371
|
+
border-color: var(--accent-red);
|
|
372
|
+
color: var(--accent-red);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.chapter-btn.active {
|
|
376
|
+
background: var(--accent-red);
|
|
377
|
+
border-color: var(--accent-red);
|
|
378
|
+
color: var(--white);
|
|
379
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* Voice Modal */
|
|
383
|
+
.voice-modal-overlay {
|
|
384
|
+
position: fixed;
|
|
385
|
+
top: 0;
|
|
386
|
+
left: 0;
|
|
387
|
+
width: 100%;
|
|
388
|
+
height: 100%;
|
|
389
|
+
background: rgba(0, 0, 0, 0.8);
|
|
390
|
+
z-index: 10000;
|
|
391
|
+
display: none;
|
|
392
|
+
align-items: center;
|
|
393
|
+
justify-content: center;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.voice-modal-overlay.active {
|
|
397
|
+
display: flex;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.voice-modal {
|
|
401
|
+
background: var(--darker-grey);
|
|
402
|
+
border: 2px solid var(--border-color);
|
|
403
|
+
padding: 30px;
|
|
404
|
+
max-width: 500px;
|
|
405
|
+
width: 90%;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
.voice-modal-header {
|
|
409
|
+
display: flex;
|
|
410
|
+
justify-content: space-between;
|
|
411
|
+
align-items: center;
|
|
412
|
+
margin-bottom: 25px;
|
|
413
|
+
padding-bottom: 15px;
|
|
414
|
+
border-bottom: 1px dashed var(--border-color);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
.voice-modal-title {
|
|
418
|
+
font-size: 1.2rem;
|
|
419
|
+
font-weight: 600;
|
|
420
|
+
color: var(--white);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
.voice-modal-close {
|
|
424
|
+
background: transparent;
|
|
425
|
+
border: 1px solid var(--border-color);
|
|
426
|
+
color: var(--grey);
|
|
427
|
+
width: 32px;
|
|
428
|
+
height: 32px;
|
|
429
|
+
cursor: pointer;
|
|
430
|
+
font-size: 1.2rem;
|
|
431
|
+
display: flex;
|
|
432
|
+
align-items: center;
|
|
433
|
+
justify-content: center;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.voice-modal-close:hover {
|
|
437
|
+
background: var(--accent-red);
|
|
438
|
+
border-color: var(--accent-red);
|
|
439
|
+
color: var(--white);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
.voice-option {
|
|
443
|
+
display: flex;
|
|
444
|
+
align-items: center;
|
|
445
|
+
gap: 15px;
|
|
446
|
+
padding: 15px;
|
|
447
|
+
margin-bottom: 10px;
|
|
448
|
+
border: 1px solid var(--border-color);
|
|
449
|
+
background: var(--card-bg);
|
|
450
|
+
cursor: pointer;
|
|
451
|
+
transition: all 0.2s;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.voice-option:hover {
|
|
455
|
+
border-color: var(--accent-red);
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.voice-option.selected {
|
|
459
|
+
border-color: var(--accent-red);
|
|
460
|
+
background: rgba(186, 12, 47, 0.1);
|
|
461
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
.voice-play-btn {
|
|
465
|
+
width: 40px;
|
|
466
|
+
height: 40px;
|
|
467
|
+
border: 1px solid var(--border-color);
|
|
468
|
+
background: transparent;
|
|
469
|
+
color: var(--grey);
|
|
470
|
+
cursor: pointer;
|
|
471
|
+
display: flex;
|
|
472
|
+
align-items: center;
|
|
473
|
+
justify-content: center;
|
|
474
|
+
font-size: 1rem;
|
|
475
|
+
flex-shrink: 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
.voice-play-btn:hover {
|
|
479
|
+
background: var(--accent-red);
|
|
480
|
+
border-color: var(--accent-red);
|
|
481
|
+
color: var(--white);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.voice-info { flex: 1; }
|
|
485
|
+
|
|
486
|
+
.voice-name {
|
|
487
|
+
font-size: 0.95rem;
|
|
488
|
+
font-weight: 600;
|
|
489
|
+
margin-bottom: 4px;
|
|
490
|
+
color: var(--white);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.voice-desc {
|
|
494
|
+
font-size: 0.8rem;
|
|
495
|
+
color: var(--grey);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.voice-select-btn {
|
|
499
|
+
background: transparent;
|
|
500
|
+
border: 1px solid var(--border-color);
|
|
501
|
+
color: var(--grey);
|
|
502
|
+
padding: 6px 12px;
|
|
503
|
+
font-size: 0.75rem;
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
.voice-option.selected .voice-select-btn {
|
|
508
|
+
background: var(--accent-red);
|
|
509
|
+
border-color: var(--accent-red);
|
|
510
|
+
color: var(--white);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
.voice-modal-footer {
|
|
514
|
+
margin-top: 20px;
|
|
515
|
+
padding-top: 15px;
|
|
516
|
+
border-top: 1px dashed var(--border-color);
|
|
517
|
+
text-align: center;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.voice-modal-note {
|
|
521
|
+
font-size: 0.8rem;
|
|
522
|
+
color: var(--grey);
|
|
523
|
+
margin-bottom: 15px;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.voice-confirm-btn {
|
|
527
|
+
background: var(--accent-red);
|
|
528
|
+
border: 1px solid var(--accent-red);
|
|
529
|
+
color: var(--white);
|
|
530
|
+
padding: 12px 30px;
|
|
531
|
+
font-size: 0.85rem;
|
|
532
|
+
cursor: pointer;
|
|
533
|
+
transition: all 0.2s;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.voice-confirm-btn:hover {
|
|
537
|
+
background: transparent;
|
|
538
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
582
539
|
}
|
|
583
540
|
|
|
584
|
-
/* Audio highlight effect
|
|
541
|
+
/* Audio highlight effect */
|
|
585
542
|
.audio-highlighted {
|
|
586
543
|
outline: 2px solid var(--accent-red) !important;
|
|
587
544
|
outline-offset: 4px;
|
|
588
|
-
border-color: var(--accent-red) !important;
|
|
589
545
|
transition: all 0.3s ease !important;
|
|
546
|
+
box-shadow: 0 0 20px var(--accent-red-glow) !important;
|
|
590
547
|
}
|
|
591
548
|
|
|
592
|
-
/* For container elements (cards, boxes), use glow effect */
|
|
593
549
|
.audio-highlighted.solution-card,
|
|
594
550
|
.audio-highlighted.phase-box,
|
|
595
551
|
.audio-highlighted.gap-item,
|
|
596
552
|
.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
553
|
outline: none !important;
|
|
608
|
-
border-
|
|
609
|
-
|
|
554
|
+
border-color: var(--accent-red) !important;
|
|
555
|
+
background: rgba(186, 12, 47, 0.15) !important;
|
|
556
|
+
box-shadow: 0 0 25px var(--accent-red-glow) !important;
|
|
610
557
|
}
|
|
611
558
|
|
|
612
559
|
/* Progress indicator */
|
|
613
560
|
.progress-bar {
|
|
614
561
|
position: fixed;
|
|
615
|
-
top:
|
|
562
|
+
top: 61px;
|
|
616
563
|
left: 0;
|
|
617
|
-
height:
|
|
564
|
+
height: 2px;
|
|
618
565
|
background: var(--accent-red);
|
|
619
566
|
width: 0%;
|
|
620
567
|
z-index: 1001;
|
|
568
|
+
box-shadow: 0 0 10px var(--accent-red-glow);
|
|
621
569
|
}
|
|
622
570
|
|
|
623
|
-
/*
|
|
571
|
+
/* ============================================
|
|
572
|
+
SECTIONS & CONTENT
|
|
573
|
+
============================================ */
|
|
624
574
|
section {
|
|
625
|
-
|
|
575
|
+
/* Account for fixed header (60px) and audio player bottom bar (120px) */
|
|
576
|
+
min-height: calc(100vh - 180px);
|
|
577
|
+
max-height: calc(100vh - 100px);
|
|
626
578
|
display: flex;
|
|
627
579
|
flex-direction: column;
|
|
628
580
|
justify-content: center;
|
|
629
581
|
align-items: center;
|
|
630
|
-
padding: 80px 40px;
|
|
582
|
+
padding: 80px 40px 60px;
|
|
631
583
|
position: relative;
|
|
584
|
+
overflow-y: auto;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/* When audio is playing, reduce section height to fit */
|
|
588
|
+
body.audio-playing section {
|
|
589
|
+
min-height: calc(100vh - 240px);
|
|
590
|
+
max-height: calc(100vh - 160px);
|
|
591
|
+
padding: 60px 40px 40px;
|
|
632
592
|
}
|
|
633
593
|
|
|
634
|
-
/* ASCII border decoration */
|
|
635
594
|
.ascii-border {
|
|
636
|
-
border: 1px dashed var(--
|
|
595
|
+
border: 1px dashed var(--border-color);
|
|
637
596
|
padding: 50px;
|
|
638
597
|
position: relative;
|
|
639
|
-
max-width:
|
|
598
|
+
max-width: 900px;
|
|
640
599
|
width: 100%;
|
|
641
|
-
|
|
642
|
-
background: rgba(10, 10, 10, 0.85);
|
|
643
|
-
backdrop-filter: blur(12px);
|
|
644
|
-
-webkit-backdrop-filter: blur(12px);
|
|
600
|
+
background: rgba(10, 10, 10, 0.8);
|
|
645
601
|
}
|
|
646
602
|
|
|
647
|
-
.ascii-border::before
|
|
603
|
+
.ascii-border::before,
|
|
604
|
+
.ascii-border::after {
|
|
648
605
|
content: '+';
|
|
649
606
|
position: absolute;
|
|
607
|
+
color: var(--border-color);
|
|
608
|
+
font-size: 1rem;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.ascii-border::before {
|
|
650
612
|
top: -8px;
|
|
651
613
|
left: -8px;
|
|
652
|
-
color: var(--grey);
|
|
653
614
|
}
|
|
654
615
|
|
|
655
616
|
.ascii-border::after {
|
|
656
|
-
content: '+';
|
|
657
|
-
position: absolute;
|
|
658
617
|
bottom: -8px;
|
|
659
618
|
right: -8px;
|
|
660
|
-
color: var(--grey);
|
|
661
619
|
}
|
|
662
620
|
|
|
663
621
|
/* Typography */
|
|
664
622
|
h1 {
|
|
665
|
-
font-
|
|
666
|
-
font-
|
|
667
|
-
|
|
623
|
+
font-family: 'Fredoka', sans-serif;
|
|
624
|
+
font-size: 2.8rem;
|
|
625
|
+
font-weight: 700;
|
|
668
626
|
margin-bottom: 20px;
|
|
627
|
+
color: var(--white);
|
|
669
628
|
}
|
|
670
629
|
|
|
671
630
|
h2 {
|
|
672
|
-
font-
|
|
673
|
-
font-
|
|
674
|
-
|
|
675
|
-
margin-bottom:
|
|
631
|
+
font-family: 'Fredoka', sans-serif;
|
|
632
|
+
font-size: 2rem;
|
|
633
|
+
font-weight: 600;
|
|
634
|
+
margin-bottom: 15px;
|
|
635
|
+
color: var(--white);
|
|
676
636
|
}
|
|
677
637
|
|
|
678
638
|
h3 {
|
|
679
|
-
font-size:
|
|
639
|
+
font-size: 1rem;
|
|
680
640
|
margin-bottom: 15px;
|
|
681
641
|
text-transform: uppercase;
|
|
682
642
|
letter-spacing: 1px;
|
|
683
643
|
color: var(--grey);
|
|
684
644
|
}
|
|
685
645
|
|
|
686
|
-
/* Explanation blocks
|
|
646
|
+
/* Explanation blocks */
|
|
687
647
|
.explanation {
|
|
688
|
-
background:
|
|
689
|
-
border
|
|
648
|
+
background: var(--darker-grey);
|
|
649
|
+
border: 1px dashed var(--border-color);
|
|
650
|
+
border-left: 3px solid var(--accent-red);
|
|
690
651
|
padding: 25px 30px;
|
|
691
652
|
margin: 30px 0;
|
|
692
653
|
font-size: 1rem;
|
|
693
|
-
line-height: 1.
|
|
654
|
+
line-height: 1.8;
|
|
694
655
|
opacity: 0;
|
|
695
656
|
}
|
|
696
657
|
|
|
697
658
|
.explanation-title {
|
|
698
|
-
font-size: 0.
|
|
659
|
+
font-size: 0.75rem;
|
|
699
660
|
text-transform: uppercase;
|
|
700
|
-
letter-spacing:
|
|
701
|
-
color: var(--
|
|
661
|
+
letter-spacing: 2px;
|
|
662
|
+
color: var(--accent-red);
|
|
702
663
|
margin-bottom: 15px;
|
|
703
|
-
|
|
704
|
-
align-items: center;
|
|
705
|
-
gap: 10px;
|
|
664
|
+
font-weight: 600;
|
|
706
665
|
}
|
|
707
666
|
|
|
708
667
|
.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;
|
|
668
|
+
content: '// ';
|
|
669
|
+
color: var(--grey);
|
|
717
670
|
}
|
|
718
671
|
|
|
719
672
|
.explanation p {
|
|
720
673
|
margin-bottom: 15px;
|
|
674
|
+
color: var(--grey);
|
|
721
675
|
}
|
|
722
676
|
|
|
723
677
|
.explanation p:last-child {
|
|
@@ -726,14 +680,13 @@
|
|
|
726
680
|
|
|
727
681
|
.explanation strong {
|
|
728
682
|
color: var(--white);
|
|
729
|
-
font-weight:
|
|
730
|
-
border-bottom: 1px dashed var(--grey);
|
|
683
|
+
font-weight: 600;
|
|
731
684
|
}
|
|
732
685
|
|
|
733
686
|
/* Real example callout */
|
|
734
687
|
.real-example {
|
|
735
|
-
background:
|
|
736
|
-
border:
|
|
688
|
+
background: var(--darker-grey);
|
|
689
|
+
border: 2px solid var(--border-color);
|
|
737
690
|
padding: 20px 25px;
|
|
738
691
|
margin: 20px 0;
|
|
739
692
|
position: relative;
|
|
@@ -744,11 +697,12 @@
|
|
|
744
697
|
position: absolute;
|
|
745
698
|
top: -10px;
|
|
746
699
|
left: 20px;
|
|
747
|
-
background: var(--
|
|
700
|
+
background: var(--card-bg);
|
|
748
701
|
padding: 0 10px;
|
|
749
|
-
font-size: 0.
|
|
702
|
+
font-size: 0.7rem;
|
|
750
703
|
letter-spacing: 2px;
|
|
751
|
-
color: var(--
|
|
704
|
+
color: var(--accent-red);
|
|
705
|
+
border: 1px solid var(--border-color);
|
|
752
706
|
}
|
|
753
707
|
|
|
754
708
|
.real-example-content {
|
|
@@ -760,7 +714,7 @@
|
|
|
760
714
|
display: inline-block;
|
|
761
715
|
width: 10px;
|
|
762
716
|
height: 20px;
|
|
763
|
-
background: var(--
|
|
717
|
+
background: var(--accent-red);
|
|
764
718
|
animation: blink 1s infinite;
|
|
765
719
|
vertical-align: middle;
|
|
766
720
|
margin-left: 5px;
|
|
@@ -778,10 +732,11 @@
|
|
|
778
732
|
justify-content: center;
|
|
779
733
|
width: 50px;
|
|
780
734
|
height: 50px;
|
|
781
|
-
border: 2px solid var(--
|
|
735
|
+
border: 2px solid var(--accent-red);
|
|
782
736
|
font-size: 1.5rem;
|
|
783
737
|
margin-right: 20px;
|
|
784
738
|
flex-shrink: 0;
|
|
739
|
+
color: var(--accent-red);
|
|
785
740
|
}
|
|
786
741
|
|
|
787
742
|
.step-header {
|
|
@@ -848,11 +803,12 @@
|
|
|
848
803
|
|
|
849
804
|
.hustle-highlight {
|
|
850
805
|
color: var(--accent-red);
|
|
851
|
-
text-shadow: 0 0
|
|
806
|
+
text-shadow: 0 0 20px var(--accent-red-glow);
|
|
852
807
|
}
|
|
853
808
|
|
|
854
809
|
.text-red {
|
|
855
810
|
color: var(--accent-red);
|
|
811
|
+
font-weight: 600;
|
|
856
812
|
}
|
|
857
813
|
|
|
858
814
|
/* Intro section centering */
|
|
@@ -883,13 +839,13 @@
|
|
|
883
839
|
}
|
|
884
840
|
|
|
885
841
|
/* ============================================
|
|
886
|
-
PHASE FLOW - LIGHTING UP SEQUENCE
|
|
842
|
+
PHASE FLOW - LIGHTING UP SEQUENCE (boxy)
|
|
887
843
|
============================================ */
|
|
888
844
|
.phase-flow {
|
|
889
845
|
display: flex;
|
|
890
846
|
align-items: center;
|
|
891
847
|
justify-content: center;
|
|
892
|
-
gap:
|
|
848
|
+
gap: 12px;
|
|
893
849
|
margin-top: 50px;
|
|
894
850
|
flex-wrap: wrap;
|
|
895
851
|
opacity: 0;
|
|
@@ -897,30 +853,31 @@
|
|
|
897
853
|
|
|
898
854
|
.phase-node {
|
|
899
855
|
position: relative;
|
|
900
|
-
border: 2px solid var(--
|
|
856
|
+
border: 2px solid var(--border-color);
|
|
857
|
+
background: var(--darker-grey);
|
|
901
858
|
padding: 20px 25px;
|
|
902
859
|
text-align: center;
|
|
903
860
|
min-width: 100px;
|
|
904
|
-
transition: all 0.
|
|
861
|
+
transition: all 0.3s ease;
|
|
905
862
|
opacity: 0;
|
|
906
863
|
transform: translateY(20px);
|
|
907
864
|
}
|
|
908
865
|
|
|
909
866
|
.phase-node .phase-glow {
|
|
910
867
|
position: absolute;
|
|
911
|
-
top:
|
|
912
|
-
left:
|
|
913
|
-
right:
|
|
914
|
-
bottom:
|
|
868
|
+
top: 0;
|
|
869
|
+
left: 0;
|
|
870
|
+
right: 0;
|
|
871
|
+
bottom: 0;
|
|
915
872
|
border: 2px solid var(--accent-red);
|
|
916
873
|
opacity: 0;
|
|
917
|
-
|
|
918
|
-
transition: opacity 0.4s ease;
|
|
874
|
+
transition: opacity 0.3s ease;
|
|
919
875
|
}
|
|
920
876
|
|
|
921
877
|
.phase-node.active {
|
|
922
878
|
border-color: var(--accent-red);
|
|
923
|
-
background: rgba(186, 12, 47, 0.
|
|
879
|
+
background: rgba(186, 12, 47, 0.2);
|
|
880
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
924
881
|
}
|
|
925
882
|
|
|
926
883
|
.phase-node.active .phase-glow {
|
|
@@ -929,7 +886,6 @@
|
|
|
929
886
|
|
|
930
887
|
.phase-node.active .phase-icon {
|
|
931
888
|
color: var(--accent-red);
|
|
932
|
-
text-shadow: 0 0 10px var(--accent-red);
|
|
933
889
|
}
|
|
934
890
|
|
|
935
891
|
.phase-node.active .phase-label {
|
|
@@ -938,31 +894,31 @@
|
|
|
938
894
|
|
|
939
895
|
.phase-icon {
|
|
940
896
|
display: block;
|
|
941
|
-
font-size: 1.
|
|
897
|
+
font-size: 1.6rem;
|
|
942
898
|
font-weight: bold;
|
|
943
899
|
margin-bottom: 8px;
|
|
944
900
|
color: var(--grey);
|
|
945
|
-
transition: all 0.
|
|
901
|
+
transition: all 0.3s ease;
|
|
946
902
|
}
|
|
947
903
|
|
|
948
904
|
.phase-label {
|
|
949
|
-
font-size: 0.
|
|
905
|
+
font-size: 0.65rem;
|
|
950
906
|
text-transform: uppercase;
|
|
951
907
|
letter-spacing: 2px;
|
|
952
|
-
color: var(--
|
|
953
|
-
transition: all 0.
|
|
908
|
+
color: var(--grey);
|
|
909
|
+
transition: all 0.3s ease;
|
|
910
|
+
font-weight: bold;
|
|
954
911
|
}
|
|
955
912
|
|
|
956
913
|
.phase-connector-arrow {
|
|
957
|
-
color: var(--
|
|
958
|
-
font-size: 1.
|
|
914
|
+
color: var(--border-color);
|
|
915
|
+
font-size: 1.2rem;
|
|
959
916
|
opacity: 0;
|
|
960
917
|
transition: all 0.3s ease;
|
|
961
918
|
}
|
|
962
919
|
|
|
963
920
|
.phase-connector-arrow.active {
|
|
964
921
|
color: var(--accent-red);
|
|
965
|
-
text-shadow: 0 0 10px var(--accent-red-glow);
|
|
966
922
|
}
|
|
967
923
|
|
|
968
924
|
@media (max-width: 768px) {
|
|
@@ -1012,62 +968,70 @@
|
|
|
1012
968
|
}
|
|
1013
969
|
|
|
1014
970
|
/* ============================================
|
|
1015
|
-
SECTION 2: THE PROBLEM - GAPS
|
|
971
|
+
SECTION 2: THE PROBLEM - GAPS - boxy cards
|
|
1016
972
|
============================================ */
|
|
1017
973
|
.gap-list {
|
|
1018
974
|
list-style: none;
|
|
1019
975
|
width: 100%;
|
|
976
|
+
display: flex;
|
|
977
|
+
flex-direction: column;
|
|
978
|
+
gap: 15px;
|
|
1020
979
|
}
|
|
1021
980
|
|
|
1022
981
|
.gap-item {
|
|
1023
|
-
border:
|
|
1024
|
-
|
|
1025
|
-
|
|
982
|
+
border: 2px solid var(--border-color);
|
|
983
|
+
background: var(--darker-grey);
|
|
984
|
+
padding: 20px 25px;
|
|
1026
985
|
opacity: 0;
|
|
1027
986
|
transform: translateX(-30px);
|
|
1028
|
-
transition: all 0.
|
|
987
|
+
transition: all 0.2s;
|
|
1029
988
|
position: relative;
|
|
989
|
+
display: flex;
|
|
990
|
+
gap: 20px;
|
|
991
|
+
align-items: flex-start;
|
|
1030
992
|
}
|
|
1031
993
|
|
|
1032
994
|
.gap-item:hover {
|
|
1033
995
|
border-color: var(--accent-red);
|
|
1034
|
-
|
|
996
|
+
transform: translateX(5px);
|
|
1035
997
|
}
|
|
1036
998
|
|
|
1037
|
-
.gap-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
999
|
+
.gap-icon {
|
|
1000
|
+
font-size: 2rem;
|
|
1001
|
+
flex-shrink: 0;
|
|
1002
|
+
line-height: 1;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
.gap-content {
|
|
1006
|
+
flex: 1;
|
|
1045
1007
|
}
|
|
1046
1008
|
|
|
1047
1009
|
.gap-number {
|
|
1048
|
-
color: var(--
|
|
1049
|
-
font-size: 0.
|
|
1010
|
+
color: var(--accent-red);
|
|
1011
|
+
font-size: 0.75rem;
|
|
1050
1012
|
text-transform: uppercase;
|
|
1051
1013
|
letter-spacing: 2px;
|
|
1014
|
+
font-weight: bold;
|
|
1052
1015
|
}
|
|
1053
1016
|
|
|
1054
1017
|
.gap-title {
|
|
1055
|
-
font-size: 1.
|
|
1018
|
+
font-size: 1.1rem;
|
|
1056
1019
|
margin: 8px 0;
|
|
1057
1020
|
}
|
|
1058
1021
|
|
|
1059
1022
|
.gap-desc {
|
|
1060
1023
|
color: var(--grey);
|
|
1061
|
-
font-size: 0.
|
|
1024
|
+
font-size: 0.9rem;
|
|
1062
1025
|
margin-bottom: 10px;
|
|
1063
1026
|
}
|
|
1064
1027
|
|
|
1065
1028
|
.gap-example {
|
|
1066
1029
|
font-size: 0.85rem;
|
|
1067
1030
|
padding: 12px 15px;
|
|
1068
|
-
background:
|
|
1069
|
-
border
|
|
1070
|
-
|
|
1031
|
+
background: var(--card-bg);
|
|
1032
|
+
border: 1px solid var(--border-color);
|
|
1033
|
+
border-left: 3px solid var(--accent-red);
|
|
1034
|
+
margin-top: 12px;
|
|
1071
1035
|
}
|
|
1072
1036
|
|
|
1073
1037
|
.gap-example .bad {
|
|
@@ -1076,44 +1040,48 @@
|
|
|
1076
1040
|
}
|
|
1077
1041
|
|
|
1078
1042
|
.gap-example .good {
|
|
1079
|
-
color: var(--
|
|
1043
|
+
color: var(--accent-red);
|
|
1044
|
+
font-weight: bold;
|
|
1080
1045
|
}
|
|
1081
1046
|
|
|
1082
1047
|
/* ============================================
|
|
1083
|
-
SECTION 3: SOLUTION OVERVIEW
|
|
1048
|
+
SECTION 3: SOLUTION OVERVIEW - boxy grid
|
|
1084
1049
|
============================================ */
|
|
1085
1050
|
.solution-grid {
|
|
1086
1051
|
display: grid;
|
|
1087
1052
|
grid-template-columns: repeat(3, 1fr);
|
|
1088
|
-
gap:
|
|
1053
|
+
gap: 20px;
|
|
1089
1054
|
margin-top: 30px;
|
|
1090
1055
|
}
|
|
1091
1056
|
|
|
1092
1057
|
.solution-card {
|
|
1093
|
-
border:
|
|
1094
|
-
|
|
1058
|
+
border: 2px solid var(--border-color);
|
|
1059
|
+
background: var(--darker-grey);
|
|
1060
|
+
padding: 35px 25px;
|
|
1095
1061
|
text-align: center;
|
|
1096
1062
|
opacity: 0;
|
|
1097
1063
|
transform: translateY(20px);
|
|
1098
|
-
transition: all 0.
|
|
1064
|
+
transition: all 0.2s;
|
|
1099
1065
|
}
|
|
1100
1066
|
|
|
1101
1067
|
.solution-card:hover {
|
|
1102
1068
|
border-color: var(--accent-red);
|
|
1103
|
-
|
|
1069
|
+
transform: translateY(-3px);
|
|
1104
1070
|
}
|
|
1105
1071
|
|
|
1106
1072
|
.solution-icon {
|
|
1107
|
-
font-size:
|
|
1073
|
+
font-size: 2.5rem;
|
|
1108
1074
|
margin-bottom: 15px;
|
|
1109
1075
|
display: block;
|
|
1110
1076
|
}
|
|
1111
1077
|
|
|
1112
1078
|
.solution-title {
|
|
1113
|
-
font-size:
|
|
1114
|
-
margin-bottom:
|
|
1079
|
+
font-size: 0.95rem;
|
|
1080
|
+
margin-bottom: 12px;
|
|
1115
1081
|
text-transform: uppercase;
|
|
1116
1082
|
letter-spacing: 1px;
|
|
1083
|
+
color: var(--white);
|
|
1084
|
+
font-weight: 600;
|
|
1117
1085
|
}
|
|
1118
1086
|
|
|
1119
1087
|
.solution-desc {
|
|
@@ -1123,7 +1091,7 @@
|
|
|
1123
1091
|
}
|
|
1124
1092
|
|
|
1125
1093
|
/* ============================================
|
|
1126
|
-
SECTION 4: HOOK SYSTEM
|
|
1094
|
+
SECTION 4: HOOK SYSTEM - clean boxy flow
|
|
1127
1095
|
============================================ */
|
|
1128
1096
|
.hook-diagram {
|
|
1129
1097
|
width: 100%;
|
|
@@ -1131,49 +1099,53 @@
|
|
|
1131
1099
|
}
|
|
1132
1100
|
|
|
1133
1101
|
.flow-box {
|
|
1134
|
-
border:
|
|
1102
|
+
border: 2px solid var(--border-color);
|
|
1103
|
+
background: var(--darker-grey);
|
|
1135
1104
|
padding: 18px 30px;
|
|
1136
1105
|
margin: 12px 0;
|
|
1137
1106
|
text-align: center;
|
|
1138
1107
|
opacity: 0;
|
|
1139
|
-
transition: all 0.
|
|
1108
|
+
transition: all 0.2s;
|
|
1140
1109
|
}
|
|
1141
1110
|
|
|
1142
1111
|
.flow-box:hover {
|
|
1143
|
-
background: rgba(186, 12, 47, 0.1);
|
|
1144
|
-
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1145
1112
|
border-color: var(--accent-red);
|
|
1113
|
+
transform: translateX(5px);
|
|
1146
1114
|
}
|
|
1147
1115
|
|
|
1148
1116
|
.flow-arrow {
|
|
1149
1117
|
text-align: center;
|
|
1150
|
-
color: var(--
|
|
1118
|
+
color: var(--accent-red);
|
|
1151
1119
|
font-size: 1.5rem;
|
|
1152
1120
|
opacity: 0;
|
|
1153
1121
|
}
|
|
1154
1122
|
|
|
1155
1123
|
.hook-group {
|
|
1156
|
-
border: 2px dashed var(--
|
|
1124
|
+
border: 2px dashed var(--border-color);
|
|
1125
|
+
background: var(--darker-grey);
|
|
1157
1126
|
padding: 25px;
|
|
1158
1127
|
margin: 25px 0;
|
|
1159
1128
|
opacity: 0;
|
|
1160
1129
|
}
|
|
1161
1130
|
|
|
1162
1131
|
.hook-group h4 {
|
|
1163
|
-
color: var(--
|
|
1164
|
-
font-size: 0.
|
|
1132
|
+
color: var(--accent-red);
|
|
1133
|
+
font-size: 0.8rem;
|
|
1165
1134
|
text-transform: uppercase;
|
|
1166
1135
|
letter-spacing: 2px;
|
|
1167
1136
|
margin-bottom: 15px;
|
|
1137
|
+
font-weight: bold;
|
|
1168
1138
|
}
|
|
1169
1139
|
|
|
1170
1140
|
.hook-file {
|
|
1171
1141
|
padding: 12px 20px;
|
|
1172
1142
|
margin: 8px 0;
|
|
1173
|
-
border
|
|
1143
|
+
border: 1px solid var(--border-color);
|
|
1144
|
+
border-left: 3px solid var(--grey);
|
|
1145
|
+
background: var(--card-bg);
|
|
1174
1146
|
font-size: 0.9rem;
|
|
1175
1147
|
opacity: 0;
|
|
1176
|
-
transition: all 0.
|
|
1148
|
+
transition: all 0.2s;
|
|
1177
1149
|
display: flex;
|
|
1178
1150
|
justify-content: space-between;
|
|
1179
1151
|
align-items: center;
|
|
@@ -1181,268 +1153,765 @@
|
|
|
1181
1153
|
|
|
1182
1154
|
.hook-file:hover {
|
|
1183
1155
|
border-left-color: var(--accent-red);
|
|
1184
|
-
|
|
1185
|
-
padding-left: 25px;
|
|
1156
|
+
border-color: var(--accent-red);
|
|
1186
1157
|
}
|
|
1187
1158
|
|
|
1188
1159
|
.hook-file code {
|
|
1189
1160
|
color: var(--white);
|
|
1161
|
+
font-weight: 500;
|
|
1190
1162
|
}
|
|
1191
1163
|
|
|
1192
1164
|
.hook-purpose {
|
|
1165
|
+
color: var(--grey);
|
|
1166
|
+
font-size: 0.75rem;
|
|
1167
|
+
text-transform: uppercase;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
.result-box {
|
|
1171
|
+
display: flex;
|
|
1172
|
+
gap: 30px;
|
|
1173
|
+
justify-content: center;
|
|
1174
|
+
margin-top: 25px;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
.result-allowed, .result-blocked {
|
|
1178
|
+
padding: 18px 35px;
|
|
1179
|
+
border: 2px solid;
|
|
1180
|
+
background: var(--card-bg);
|
|
1181
|
+
opacity: 0;
|
|
1182
|
+
transition: all 0.2s;
|
|
1183
|
+
font-weight: bold;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
.result-allowed {
|
|
1187
|
+
border-color: var(--grey);
|
|
1188
|
+
color: var(--grey);
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
.result-blocked {
|
|
1192
|
+
border-color: var(--accent-red);
|
|
1193
|
+
color: var(--accent-red);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
.result-allowed:hover, .result-blocked:hover {
|
|
1197
|
+
transform: scale(1.03);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/* ============================================
|
|
1201
|
+
SECTION 5: 10-PHASE WORKFLOW - boxy grid
|
|
1202
|
+
============================================ */
|
|
1203
|
+
.phase-grid {
|
|
1204
|
+
display: grid;
|
|
1205
|
+
grid-template-columns: repeat(5, 1fr);
|
|
1206
|
+
gap: 15px;
|
|
1207
|
+
width: 100%;
|
|
1208
|
+
margin-top: 30px;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
.phase-box {
|
|
1212
|
+
border: 2px solid var(--border-color);
|
|
1213
|
+
background: var(--darker-grey);
|
|
1214
|
+
padding: 25px 15px;
|
|
1215
|
+
text-align: center;
|
|
1216
|
+
opacity: 0;
|
|
1217
|
+
transform: scale(0.9);
|
|
1218
|
+
transition: all 0.2s;
|
|
1219
|
+
position: relative;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.phase-box:hover {
|
|
1223
|
+
border-color: var(--accent-red);
|
|
1224
|
+
transform: scale(1.02);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
.phase-box.active {
|
|
1228
|
+
border-color: var(--accent-red);
|
|
1229
|
+
background: rgba(186, 12, 47, 0.2);
|
|
1230
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
.phase-number {
|
|
1234
|
+
font-size: 2rem;
|
|
1235
|
+
color: var(--grey);
|
|
1236
|
+
margin-bottom: 10px;
|
|
1237
|
+
font-weight: bold;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.phase-box.active .phase-number {
|
|
1241
|
+
color: var(--accent-red);
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
.phase-name {
|
|
1245
|
+
font-size: 0.7rem;
|
|
1246
|
+
text-transform: uppercase;
|
|
1247
|
+
letter-spacing: 1px;
|
|
1248
|
+
font-weight: bold;
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.phase-status {
|
|
1252
|
+
position: absolute;
|
|
1253
|
+
top: 8px;
|
|
1254
|
+
right: 8px;
|
|
1255
|
+
font-size: 0.65rem;
|
|
1256
|
+
color: var(--grey);
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
.phase-connector {
|
|
1260
|
+
grid-column: span 5;
|
|
1261
|
+
text-align: center;
|
|
1193
1262
|
color: var(--grey);
|
|
1194
1263
|
font-size: 0.8rem;
|
|
1264
|
+
opacity: 0;
|
|
1265
|
+
padding: 10px 0;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
.phase-desc {
|
|
1269
|
+
font-size: 0.65rem;
|
|
1270
|
+
color: var(--grey);
|
|
1271
|
+
margin-top: 8px;
|
|
1272
|
+
line-height: 1.4;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
/* ============================================
|
|
1276
|
+
SECTION 6: REAL WALKTHROUGH - boxy steps
|
|
1277
|
+
============================================ */
|
|
1278
|
+
.walkthrough-step {
|
|
1279
|
+
border: 2px solid var(--border-color);
|
|
1280
|
+
background: var(--darker-grey);
|
|
1281
|
+
padding: 30px;
|
|
1282
|
+
margin-bottom: 20px;
|
|
1283
|
+
opacity: 0;
|
|
1284
|
+
transform: translateY(20px);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.walkthrough-step.active {
|
|
1288
|
+
border-color: var(--accent-red);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.walkthrough-header {
|
|
1292
|
+
display: flex;
|
|
1293
|
+
align-items: center;
|
|
1294
|
+
gap: 20px;
|
|
1295
|
+
margin-bottom: 20px;
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
.walkthrough-num {
|
|
1299
|
+
width: 45px;
|
|
1300
|
+
height: 45px;
|
|
1301
|
+
border: 2px solid var(--accent-red);
|
|
1302
|
+
background: transparent;
|
|
1303
|
+
display: flex;
|
|
1304
|
+
align-items: center;
|
|
1305
|
+
justify-content: center;
|
|
1306
|
+
font-size: 1.2rem;
|
|
1307
|
+
flex-shrink: 0;
|
|
1308
|
+
color: var(--accent-red);
|
|
1309
|
+
font-weight: bold;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.walkthrough-title {
|
|
1313
|
+
font-size: 1rem;
|
|
1314
|
+
text-transform: uppercase;
|
|
1315
|
+
letter-spacing: 1px;
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
.walkthrough-content {
|
|
1319
|
+
padding-left: 65px;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
.walkthrough-desc {
|
|
1323
|
+
color: var(--grey);
|
|
1324
|
+
margin-bottom: 15px;
|
|
1325
|
+
line-height: 1.8;
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
.walkthrough-example {
|
|
1329
|
+
background: var(--card-bg);
|
|
1330
|
+
border: 1px solid var(--border-color);
|
|
1331
|
+
padding: 15px 20px;
|
|
1332
|
+
font-size: 0.85rem;
|
|
1333
|
+
border-left: 3px solid var(--accent-red);
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1336
|
+
.walkthrough-example .label {
|
|
1337
|
+
color: var(--accent-red);
|
|
1338
|
+
font-size: 0.7rem;
|
|
1339
|
+
text-transform: uppercase;
|
|
1340
|
+
letter-spacing: 1px;
|
|
1341
|
+
margin-bottom: 8px;
|
|
1342
|
+
font-weight: bold;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/* ============================================
|
|
1346
|
+
SECTION 7: LIVE DEMO TERMINAL - boxy style
|
|
1347
|
+
============================================ */
|
|
1348
|
+
.terminal {
|
|
1349
|
+
background: var(--card-bg);
|
|
1350
|
+
border: 2px solid var(--border-color);
|
|
1351
|
+
padding: 25px;
|
|
1352
|
+
width: 100%;
|
|
1353
|
+
font-size: 0.85rem;
|
|
1354
|
+
position: relative;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
.terminal-header {
|
|
1358
|
+
display: flex;
|
|
1359
|
+
gap: 8px;
|
|
1360
|
+
margin-bottom: 20px;
|
|
1361
|
+
padding-bottom: 15px;
|
|
1362
|
+
border-bottom: 1px dashed var(--border-color);
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
.terminal-dot {
|
|
1366
|
+
width: 12px;
|
|
1367
|
+
height: 12px;
|
|
1368
|
+
border-radius: 0;
|
|
1369
|
+
border: 2px solid var(--border-color);
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
.terminal-dot:first-child {
|
|
1373
|
+
border-color: var(--accent-red);
|
|
1374
|
+
background: var(--accent-red);
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
.terminal-title {
|
|
1378
|
+
margin-left: auto;
|
|
1379
|
+
color: var(--grey);
|
|
1380
|
+
font-size: 0.75rem;
|
|
1381
|
+
text-transform: uppercase;
|
|
1382
|
+
letter-spacing: 1px;
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
.terminal-line {
|
|
1386
|
+
padding: 8px 0;
|
|
1387
|
+
opacity: 0;
|
|
1388
|
+
display: flex;
|
|
1389
|
+
align-items: flex-start;
|
|
1390
|
+
gap: 12px;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
.terminal-prompt {
|
|
1394
|
+
color: var(--accent-red);
|
|
1395
|
+
flex-shrink: 0;
|
|
1396
|
+
font-weight: bold;
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
.terminal-command {
|
|
1400
|
+
color: var(--white);
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
.terminal-result {
|
|
1404
|
+
padding-left: 25px;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1407
|
+
.terminal-blocked {
|
|
1408
|
+
color: var(--accent-red);
|
|
1409
|
+
border-left: 3px solid var(--accent-red);
|
|
1410
|
+
padding-left: 15px;
|
|
1411
|
+
background: rgba(186, 12, 47, 0.1);
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1414
|
+
.terminal-allowed {
|
|
1415
|
+
color: #22c55e;
|
|
1416
|
+
border-left: 3px solid #22c55e;
|
|
1417
|
+
padding-left: 15px;
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
.terminal-logged {
|
|
1421
|
+
color: var(--grey);
|
|
1422
|
+
font-style: italic;
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
.terminal-comment {
|
|
1426
|
+
color: var(--grey);
|
|
1427
|
+
font-size: 0.75rem;
|
|
1428
|
+
padding-left: 25px;
|
|
1429
|
+
margin-bottom: 5px;
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
/* ============================================
|
|
1433
|
+
CLAUDE CODE TERMINAL SIMULATION
|
|
1434
|
+
With side panel for explanations
|
|
1435
|
+
============================================ */
|
|
1436
|
+
.claude-code-container {
|
|
1437
|
+
display: flex;
|
|
1438
|
+
gap: 20px;
|
|
1439
|
+
width: 100%;
|
|
1440
|
+
max-width: 1400px;
|
|
1441
|
+
margin: 0 auto;
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
.claude-code-terminal {
|
|
1445
|
+
flex: 2;
|
|
1446
|
+
background: #1e1e1e;
|
|
1447
|
+
border-radius: 12px;
|
|
1448
|
+
overflow: hidden;
|
|
1449
|
+
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.5);
|
|
1450
|
+
border: 1px solid rgba(249, 115, 22, 0.2);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
.claude-code-header {
|
|
1454
|
+
display: flex;
|
|
1455
|
+
align-items: center;
|
|
1456
|
+
gap: 12px;
|
|
1457
|
+
padding: 12px 16px;
|
|
1458
|
+
background: #2d2d2d;
|
|
1459
|
+
border-bottom: 1px solid #3d3d3d;
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
.claude-code-dots {
|
|
1463
|
+
display: flex;
|
|
1464
|
+
gap: 8px;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
.claude-code-dot {
|
|
1468
|
+
width: 12px;
|
|
1469
|
+
height: 12px;
|
|
1470
|
+
border-radius: 50%;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
.claude-code-dot.red { background: #ff5f56; }
|
|
1474
|
+
.claude-code-dot.yellow { background: #ffbd2e; }
|
|
1475
|
+
.claude-code-dot.green { background: #27ca40; }
|
|
1476
|
+
|
|
1477
|
+
.claude-code-title {
|
|
1478
|
+
flex: 1;
|
|
1479
|
+
text-align: center;
|
|
1480
|
+
color: #888;
|
|
1481
|
+
font-size: 0.85rem;
|
|
1482
|
+
font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
.claude-code-badge {
|
|
1486
|
+
display: flex;
|
|
1487
|
+
align-items: center;
|
|
1488
|
+
gap: 6px;
|
|
1489
|
+
padding: 4px 12px;
|
|
1490
|
+
background: rgba(249, 115, 22, 0.15);
|
|
1491
|
+
border: 1px solid var(--claude-orange);
|
|
1492
|
+
border-radius: 4px;
|
|
1493
|
+
color: var(--claude-orange);
|
|
1494
|
+
font-size: 0.75rem;
|
|
1495
|
+
font-weight: 600;
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
.claude-code-badge::before {
|
|
1499
|
+
content: '◉';
|
|
1500
|
+
animation: pulse-orange 1.5s infinite;
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
@keyframes pulse-orange {
|
|
1504
|
+
0%, 100% { opacity: 1; }
|
|
1505
|
+
50% { opacity: 0.5; }
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
.claude-code-body {
|
|
1509
|
+
padding: 20px;
|
|
1510
|
+
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Courier New', monospace;
|
|
1511
|
+
font-size: 0.9rem;
|
|
1512
|
+
line-height: 1.6;
|
|
1513
|
+
max-height: 600px;
|
|
1514
|
+
overflow-y: auto;
|
|
1515
|
+
scroll-behavior: smooth;
|
|
1516
|
+
position: relative;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.claude-msg {
|
|
1520
|
+
margin-bottom: 16px;
|
|
1521
|
+
opacity: 0;
|
|
1522
|
+
transform: translateY(10px);
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
.claude-msg.visible {
|
|
1526
|
+
opacity: 1;
|
|
1527
|
+
transform: translateY(0);
|
|
1528
|
+
transition: all 0.3s ease;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
.claude-msg-role {
|
|
1532
|
+
display: flex;
|
|
1533
|
+
align-items: center;
|
|
1534
|
+
gap: 8px;
|
|
1535
|
+
margin-bottom: 8px;
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
.claude-msg-role.ai {
|
|
1539
|
+
color: var(--claude-orange);
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
.claude-msg-role.user {
|
|
1543
|
+
color: #3b82f6;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
.claude-msg-role.system {
|
|
1547
|
+
color: #22c55e;
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
.claude-msg-icon {
|
|
1551
|
+
font-size: 1rem;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
.claude-msg-name {
|
|
1555
|
+
font-weight: 600;
|
|
1556
|
+
text-transform: uppercase;
|
|
1557
|
+
letter-spacing: 0.5px;
|
|
1558
|
+
font-size: 0.75rem;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
.claude-msg-content {
|
|
1562
|
+
padding-left: 28px;
|
|
1563
|
+
color: #e0e0e0;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
.claude-msg-content code {
|
|
1567
|
+
background: rgba(249, 115, 22, 0.15);
|
|
1568
|
+
color: var(--claude-orange);
|
|
1569
|
+
padding: 2px 6px;
|
|
1570
|
+
border-radius: 4px;
|
|
1571
|
+
font-size: 0.85em;
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
.claude-tool-call {
|
|
1575
|
+
margin: 8px 0;
|
|
1576
|
+
padding: 12px;
|
|
1577
|
+
background: rgba(0, 0, 0, 0.3);
|
|
1578
|
+
border: 1px solid #3d3d3d;
|
|
1579
|
+
border-radius: 6px;
|
|
1580
|
+
border-left: 3px solid var(--claude-orange);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
.claude-tool-name {
|
|
1584
|
+
color: var(--claude-orange);
|
|
1585
|
+
font-weight: 600;
|
|
1586
|
+
margin-bottom: 4px;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
.claude-tool-result {
|
|
1590
|
+
color: #888;
|
|
1591
|
+
font-size: 0.85rem;
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
.claude-user-choice {
|
|
1595
|
+
margin: 12px 0;
|
|
1596
|
+
padding: 12px;
|
|
1597
|
+
background: rgba(59, 130, 246, 0.1);
|
|
1598
|
+
border: 1px solid rgba(59, 130, 246, 0.3);
|
|
1599
|
+
border-radius: 6px;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
.claude-choice-option {
|
|
1603
|
+
display: flex;
|
|
1604
|
+
align-items: center;
|
|
1605
|
+
gap: 8px;
|
|
1606
|
+
padding: 8px 12px;
|
|
1607
|
+
margin: 4px 0;
|
|
1608
|
+
background: rgba(0, 0, 0, 0.2);
|
|
1609
|
+
border-radius: 4px;
|
|
1610
|
+
cursor: pointer;
|
|
1611
|
+
transition: all 0.2s;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.claude-choice-option:hover {
|
|
1615
|
+
background: rgba(59, 130, 246, 0.2);
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
.claude-choice-option.selected {
|
|
1619
|
+
background: rgba(59, 130, 246, 0.3);
|
|
1620
|
+
border: 1px solid #3b82f6;
|
|
1195
1621
|
}
|
|
1196
1622
|
|
|
1197
|
-
.
|
|
1623
|
+
.claude-choice-marker {
|
|
1624
|
+
width: 18px;
|
|
1625
|
+
height: 18px;
|
|
1626
|
+
border: 2px solid #888;
|
|
1627
|
+
border-radius: 50%;
|
|
1198
1628
|
display: flex;
|
|
1199
|
-
|
|
1629
|
+
align-items: center;
|
|
1200
1630
|
justify-content: center;
|
|
1201
|
-
margin-top: 25px;
|
|
1202
1631
|
}
|
|
1203
1632
|
|
|
1204
|
-
.
|
|
1205
|
-
|
|
1206
|
-
|
|
1633
|
+
.claude-choice-option.selected .claude-choice-marker {
|
|
1634
|
+
border-color: #3b82f6;
|
|
1635
|
+
background: #3b82f6;
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
.claude-choice-option.selected .claude-choice-marker::after {
|
|
1639
|
+
content: '✓';
|
|
1640
|
+
color: white;
|
|
1641
|
+
font-size: 0.7rem;
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/* ASCII Art Startup Screen */
|
|
1645
|
+
.ascii-startup {
|
|
1646
|
+
font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Courier New', monospace;
|
|
1647
|
+
font-size: 11px;
|
|
1648
|
+
line-height: 1.3;
|
|
1649
|
+
color: var(--claude-orange);
|
|
1650
|
+
white-space: pre;
|
|
1651
|
+
margin: 0;
|
|
1652
|
+
padding: 16px;
|
|
1207
1653
|
opacity: 0;
|
|
1208
|
-
|
|
1654
|
+
display: none;
|
|
1209
1655
|
}
|
|
1210
1656
|
|
|
1211
|
-
.
|
|
1212
|
-
|
|
1657
|
+
.ascii-startup.visible {
|
|
1658
|
+
opacity: 1;
|
|
1659
|
+
display: block;
|
|
1213
1660
|
}
|
|
1214
1661
|
|
|
1215
|
-
.
|
|
1216
|
-
|
|
1217
|
-
color: var(--accent-red);
|
|
1662
|
+
.ascii-startup .welcome-text {
|
|
1663
|
+
color: #e5e5e5;
|
|
1218
1664
|
}
|
|
1219
1665
|
|
|
1220
|
-
.
|
|
1221
|
-
|
|
1666
|
+
.ascii-startup .tips-text {
|
|
1667
|
+
color: #888;
|
|
1222
1668
|
}
|
|
1223
1669
|
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
============================================ */
|
|
1227
|
-
.phase-grid {
|
|
1228
|
-
display: grid;
|
|
1229
|
-
grid-template-columns: repeat(5, 1fr);
|
|
1230
|
-
gap: 18px;
|
|
1231
|
-
width: 100%;
|
|
1232
|
-
margin-top: 30px;
|
|
1670
|
+
.ascii-startup .model-text {
|
|
1671
|
+
color: var(--claude-orange);
|
|
1233
1672
|
}
|
|
1234
1673
|
|
|
1235
|
-
.
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1674
|
+
.ascii-startup .path-text {
|
|
1675
|
+
color: #666;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/* Prompt Line */
|
|
1679
|
+
.claude-prompt-line {
|
|
1680
|
+
display: flex;
|
|
1681
|
+
align-items: center;
|
|
1682
|
+
gap: 8px;
|
|
1683
|
+
padding: 12px 16px;
|
|
1684
|
+
background: rgba(0, 0, 0, 0.2);
|
|
1685
|
+
border-top: 1px solid #3d3d3d;
|
|
1239
1686
|
opacity: 0;
|
|
1240
|
-
transform: scale(0.9);
|
|
1241
|
-
transition: all 0.3s;
|
|
1242
|
-
position: relative;
|
|
1243
1687
|
}
|
|
1244
1688
|
|
|
1245
|
-
.
|
|
1246
|
-
|
|
1247
|
-
box-shadow: 0 0 25px var(--accent-red-glow);
|
|
1248
|
-
transform: scale(1.02);
|
|
1689
|
+
.claude-prompt-line.visible {
|
|
1690
|
+
opacity: 1;
|
|
1249
1691
|
}
|
|
1250
1692
|
|
|
1251
|
-
.
|
|
1252
|
-
|
|
1253
|
-
|
|
1693
|
+
.claude-prompt {
|
|
1694
|
+
color: var(--claude-orange);
|
|
1695
|
+
font-size: 1.1rem;
|
|
1696
|
+
font-weight: bold;
|
|
1254
1697
|
}
|
|
1255
1698
|
|
|
1256
|
-
.
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
margin-bottom: 10px;
|
|
1699
|
+
.claude-input {
|
|
1700
|
+
color: #e5e5e5;
|
|
1701
|
+
font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
|
|
1260
1702
|
}
|
|
1261
1703
|
|
|
1262
|
-
.
|
|
1263
|
-
|
|
1704
|
+
.claude-cursor {
|
|
1705
|
+
display: inline-block;
|
|
1706
|
+
width: 8px;
|
|
1707
|
+
height: 16px;
|
|
1708
|
+
background: var(--claude-orange);
|
|
1709
|
+
animation: blink-cursor 1s infinite;
|
|
1710
|
+
margin-left: 2px;
|
|
1711
|
+
vertical-align: middle;
|
|
1264
1712
|
}
|
|
1265
1713
|
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
letter-spacing: 1px;
|
|
1714
|
+
@keyframes blink-cursor {
|
|
1715
|
+
0%, 50% { opacity: 1; }
|
|
1716
|
+
51%, 100% { opacity: 0; }
|
|
1270
1717
|
}
|
|
1271
1718
|
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1719
|
+
/* Loading Dots Animation */
|
|
1720
|
+
.loading-dots {
|
|
1721
|
+
display: inline-flex;
|
|
1722
|
+
gap: 4px;
|
|
1723
|
+
padding: 8px 16px;
|
|
1724
|
+
margin: 8px 0;
|
|
1278
1725
|
}
|
|
1279
1726
|
|
|
1280
|
-
.
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
padding: 10px 0;
|
|
1727
|
+
.loading-dots span {
|
|
1728
|
+
width: 8px;
|
|
1729
|
+
height: 8px;
|
|
1730
|
+
background: var(--claude-orange);
|
|
1731
|
+
border-radius: 50%;
|
|
1732
|
+
animation: pulse-dot 1.2s infinite ease-in-out;
|
|
1287
1733
|
}
|
|
1288
1734
|
|
|
1289
|
-
.
|
|
1290
|
-
|
|
1291
|
-
color: var(--grey);
|
|
1292
|
-
margin-top: 8px;
|
|
1293
|
-
line-height: 1.4;
|
|
1735
|
+
.loading-dots span:nth-child(2) {
|
|
1736
|
+
animation-delay: 0.2s;
|
|
1294
1737
|
}
|
|
1295
1738
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
============================================ */
|
|
1299
|
-
.walkthrough-step {
|
|
1300
|
-
border: 1px dashed var(--dark-grey);
|
|
1301
|
-
padding: 30px;
|
|
1302
|
-
margin-bottom: 25px;
|
|
1303
|
-
opacity: 0;
|
|
1304
|
-
transform: translateY(20px);
|
|
1739
|
+
.loading-dots span:nth-child(3) {
|
|
1740
|
+
animation-delay: 0.4s;
|
|
1305
1741
|
}
|
|
1306
1742
|
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1743
|
+
@keyframes pulse-dot {
|
|
1744
|
+
0%, 80%, 100% {
|
|
1745
|
+
opacity: 0.3;
|
|
1746
|
+
transform: scale(0.8);
|
|
1747
|
+
}
|
|
1748
|
+
40% {
|
|
1749
|
+
opacity: 1;
|
|
1750
|
+
transform: scale(1);
|
|
1751
|
+
}
|
|
1310
1752
|
}
|
|
1311
1753
|
|
|
1312
|
-
|
|
1754
|
+
/* Thinking indicator */
|
|
1755
|
+
.claude-thinking {
|
|
1313
1756
|
display: flex;
|
|
1314
1757
|
align-items: center;
|
|
1315
|
-
gap:
|
|
1316
|
-
|
|
1758
|
+
gap: 8px;
|
|
1759
|
+
padding: 8px 28px;
|
|
1760
|
+
color: #888;
|
|
1761
|
+
font-style: italic;
|
|
1762
|
+
opacity: 0;
|
|
1317
1763
|
}
|
|
1318
1764
|
|
|
1319
|
-
.
|
|
1320
|
-
|
|
1321
|
-
height: 45px;
|
|
1322
|
-
border: 2px solid var(--white);
|
|
1323
|
-
display: flex;
|
|
1324
|
-
align-items: center;
|
|
1325
|
-
justify-content: center;
|
|
1326
|
-
font-size: 1.3rem;
|
|
1327
|
-
flex-shrink: 0;
|
|
1765
|
+
.claude-thinking.visible {
|
|
1766
|
+
opacity: 1;
|
|
1328
1767
|
}
|
|
1329
1768
|
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1769
|
+
/* Loop-back message styling */
|
|
1770
|
+
.claude-msg.loop-back .claude-msg-content {
|
|
1771
|
+
border-left: 3px solid #f59e0b;
|
|
1772
|
+
padding-left: 12px;
|
|
1773
|
+
background: rgba(245, 158, 11, 0.1);
|
|
1774
|
+
border-radius: 0 6px 6px 0;
|
|
1775
|
+
padding: 8px 12px;
|
|
1334
1776
|
}
|
|
1335
1777
|
|
|
1336
|
-
.
|
|
1337
|
-
|
|
1778
|
+
.loop-back-badge {
|
|
1779
|
+
display: inline-block;
|
|
1780
|
+
background: rgba(245, 158, 11, 0.2);
|
|
1781
|
+
color: #f59e0b;
|
|
1782
|
+
padding: 2px 8px;
|
|
1783
|
+
border-radius: 4px;
|
|
1784
|
+
font-size: 0.75rem;
|
|
1785
|
+
margin-bottom: 8px;
|
|
1338
1786
|
}
|
|
1339
1787
|
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1788
|
+
/* Gap found styling */
|
|
1789
|
+
.gap-found {
|
|
1790
|
+
background: rgba(239, 68, 68, 0.1);
|
|
1791
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
1792
|
+
border-radius: 6px;
|
|
1793
|
+
padding: 12px;
|
|
1794
|
+
margin: 8px 0;
|
|
1344
1795
|
}
|
|
1345
1796
|
|
|
1346
|
-
.
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
border-left: 3px solid var(--grey);
|
|
1797
|
+
.gap-found-title {
|
|
1798
|
+
color: #ef4444;
|
|
1799
|
+
font-weight: 600;
|
|
1800
|
+
margin-bottom: 4px;
|
|
1351
1801
|
}
|
|
1352
1802
|
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1803
|
+
/* Explanation Side Panel */
|
|
1804
|
+
.explanation-panel {
|
|
1805
|
+
flex: 1;
|
|
1806
|
+
min-width: 300px;
|
|
1807
|
+
background: var(--darker-grey);
|
|
1808
|
+
border: 1px solid var(--border-color);
|
|
1809
|
+
border-radius: 8px;
|
|
1810
|
+
overflow: hidden;
|
|
1359
1811
|
}
|
|
1360
1812
|
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
padding: 25px;
|
|
1368
|
-
width: 100%;
|
|
1813
|
+
.explanation-panel-header {
|
|
1814
|
+
padding: 12px 16px;
|
|
1815
|
+
background: rgba(249, 115, 22, 0.1);
|
|
1816
|
+
border-bottom: 1px solid var(--border-color);
|
|
1817
|
+
font-weight: 600;
|
|
1818
|
+
color: var(--claude-orange);
|
|
1369
1819
|
font-size: 0.85rem;
|
|
1370
|
-
|
|
1820
|
+
text-transform: uppercase;
|
|
1821
|
+
letter-spacing: 1px;
|
|
1371
1822
|
}
|
|
1372
1823
|
|
|
1373
|
-
.
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
padding-bottom: 15px;
|
|
1378
|
-
border-bottom: 1px dashed var(--dark-grey);
|
|
1824
|
+
.explanation-panel-body {
|
|
1825
|
+
padding: 16px;
|
|
1826
|
+
max-height: 600px;
|
|
1827
|
+
overflow-y: auto;
|
|
1379
1828
|
}
|
|
1380
1829
|
|
|
1381
|
-
.
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
border:
|
|
1830
|
+
.explanation-step {
|
|
1831
|
+
padding: 16px;
|
|
1832
|
+
margin-bottom: 12px;
|
|
1833
|
+
background: rgba(0, 0, 0, 0.2);
|
|
1834
|
+
border-radius: 6px;
|
|
1835
|
+
border-left: 3px solid var(--border-color);
|
|
1836
|
+
opacity: 0.5;
|
|
1837
|
+
transition: all 0.3s ease;
|
|
1386
1838
|
}
|
|
1387
1839
|
|
|
1388
|
-
.
|
|
1389
|
-
|
|
1390
|
-
color: var(--
|
|
1391
|
-
|
|
1840
|
+
.explanation-step.active {
|
|
1841
|
+
opacity: 1;
|
|
1842
|
+
border-left-color: var(--claude-orange);
|
|
1843
|
+
background: rgba(249, 115, 22, 0.05);
|
|
1392
1844
|
}
|
|
1393
1845
|
|
|
1394
|
-
.
|
|
1395
|
-
padding: 8px 0;
|
|
1396
|
-
opacity: 0;
|
|
1846
|
+
.explanation-step-phase {
|
|
1397
1847
|
display: flex;
|
|
1398
|
-
align-items:
|
|
1399
|
-
gap:
|
|
1848
|
+
align-items: center;
|
|
1849
|
+
gap: 8px;
|
|
1850
|
+
margin-bottom: 8px;
|
|
1400
1851
|
}
|
|
1401
1852
|
|
|
1402
|
-
.
|
|
1853
|
+
.explanation-step-num {
|
|
1854
|
+
width: 24px;
|
|
1855
|
+
height: 24px;
|
|
1856
|
+
background: var(--border-color);
|
|
1857
|
+
border-radius: 50%;
|
|
1858
|
+
display: flex;
|
|
1859
|
+
align-items: center;
|
|
1860
|
+
justify-content: center;
|
|
1861
|
+
font-size: 0.75rem;
|
|
1862
|
+
font-weight: 600;
|
|
1403
1863
|
color: var(--grey);
|
|
1404
|
-
flex-shrink: 0;
|
|
1405
1864
|
}
|
|
1406
1865
|
|
|
1407
|
-
.
|
|
1408
|
-
|
|
1866
|
+
.explanation-step.active .explanation-step-num {
|
|
1867
|
+
background: var(--claude-orange);
|
|
1868
|
+
color: white;
|
|
1409
1869
|
}
|
|
1410
1870
|
|
|
1411
|
-
.
|
|
1412
|
-
|
|
1871
|
+
.explanation-step-name {
|
|
1872
|
+
font-weight: 600;
|
|
1873
|
+
color: var(--grey);
|
|
1874
|
+
font-size: 0.85rem;
|
|
1413
1875
|
}
|
|
1414
1876
|
|
|
1415
|
-
.
|
|
1416
|
-
color: var(--
|
|
1417
|
-
border-left: 3px solid var(--accent-red);
|
|
1418
|
-
padding-left: 15px;
|
|
1419
|
-
background: rgba(186, 12, 47, 0.1);
|
|
1877
|
+
.explanation-step.active .explanation-step-name {
|
|
1878
|
+
color: var(--white);
|
|
1420
1879
|
}
|
|
1421
1880
|
|
|
1422
|
-
.
|
|
1881
|
+
.explanation-step-desc {
|
|
1423
1882
|
color: var(--grey);
|
|
1424
|
-
|
|
1425
|
-
|
|
1883
|
+
font-size: 0.8rem;
|
|
1884
|
+
line-height: 1.5;
|
|
1426
1885
|
}
|
|
1427
1886
|
|
|
1428
|
-
.
|
|
1429
|
-
color:
|
|
1887
|
+
.explanation-step.active .explanation-step-desc {
|
|
1888
|
+
color: #aaa;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
.explanation-step-why {
|
|
1892
|
+
margin-top: 8px;
|
|
1893
|
+
padding-top: 8px;
|
|
1894
|
+
border-top: 1px dashed var(--border-color);
|
|
1895
|
+
font-size: 0.75rem;
|
|
1896
|
+
color: var(--claude-orange);
|
|
1430
1897
|
font-style: italic;
|
|
1431
1898
|
}
|
|
1432
1899
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1900
|
+
@media (max-width: 1000px) {
|
|
1901
|
+
.claude-code-container {
|
|
1902
|
+
flex-direction: column;
|
|
1903
|
+
}
|
|
1904
|
+
.explanation-panel {
|
|
1905
|
+
min-width: unset;
|
|
1906
|
+
}
|
|
1438
1907
|
}
|
|
1439
1908
|
|
|
1440
1909
|
/* ============================================
|
|
1441
|
-
SECTION 8: STATE FILE
|
|
1910
|
+
SECTION 8: STATE FILE - boxy JSON viewer
|
|
1442
1911
|
============================================ */
|
|
1443
1912
|
.json-viewer {
|
|
1444
|
-
background:
|
|
1445
|
-
border:
|
|
1913
|
+
background: var(--card-bg);
|
|
1914
|
+
border: 2px solid var(--border-color);
|
|
1446
1915
|
padding: 25px;
|
|
1447
1916
|
font-size: 0.85rem;
|
|
1448
1917
|
width: 100%;
|
|
@@ -1450,7 +1919,8 @@
|
|
|
1450
1919
|
}
|
|
1451
1920
|
|
|
1452
1921
|
.json-key {
|
|
1453
|
-
color: var(--
|
|
1922
|
+
color: var(--accent-red);
|
|
1923
|
+
font-weight: 600;
|
|
1454
1924
|
}
|
|
1455
1925
|
|
|
1456
1926
|
.json-value {
|
|
@@ -1467,33 +1937,36 @@
|
|
|
1467
1937
|
}
|
|
1468
1938
|
|
|
1469
1939
|
.json-line.highlight {
|
|
1470
|
-
background: rgba(186, 12, 47, 0.
|
|
1940
|
+
background: rgba(186, 12, 47, 0.1);
|
|
1471
1941
|
margin: 0 -25px;
|
|
1472
1942
|
padding-left: 25px;
|
|
1473
1943
|
padding-right: 25px;
|
|
1474
|
-
border-left:
|
|
1944
|
+
border-left: 3px solid var(--accent-red);
|
|
1475
1945
|
}
|
|
1476
1946
|
|
|
1477
1947
|
.json-comment {
|
|
1478
|
-
color: var(--
|
|
1479
|
-
font-size: 0.
|
|
1948
|
+
color: var(--grey);
|
|
1949
|
+
font-size: 0.7rem;
|
|
1480
1950
|
margin-left: 20px;
|
|
1951
|
+
text-transform: uppercase;
|
|
1481
1952
|
}
|
|
1482
1953
|
|
|
1483
1954
|
/* ============================================
|
|
1484
|
-
SECTION 9: INSTALLATION
|
|
1955
|
+
SECTION 9: INSTALLATION - boxy style
|
|
1485
1956
|
============================================ */
|
|
1486
1957
|
.install-command {
|
|
1487
|
-
background:
|
|
1488
|
-
border:
|
|
1489
|
-
padding:
|
|
1958
|
+
background: var(--darker-grey);
|
|
1959
|
+
border: 2px solid var(--accent-red);
|
|
1960
|
+
padding: 25px 35px;
|
|
1490
1961
|
font-size: 1.1rem;
|
|
1491
1962
|
margin: 30px 0;
|
|
1492
1963
|
text-align: center;
|
|
1964
|
+
box-shadow: 0 0 20px var(--accent-red-glow);
|
|
1493
1965
|
}
|
|
1494
1966
|
|
|
1495
1967
|
.install-command code {
|
|
1496
|
-
color: var(--
|
|
1968
|
+
color: var(--accent-red);
|
|
1969
|
+
font-weight: bold;
|
|
1497
1970
|
}
|
|
1498
1971
|
|
|
1499
1972
|
.install-flow {
|
|
@@ -1504,25 +1977,26 @@
|
|
|
1504
1977
|
display: flex;
|
|
1505
1978
|
align-items: center;
|
|
1506
1979
|
gap: 25px;
|
|
1507
|
-
padding: 20px
|
|
1508
|
-
|
|
1980
|
+
padding: 20px;
|
|
1981
|
+
margin-bottom: 15px;
|
|
1982
|
+
border: 2px solid var(--border-color);
|
|
1983
|
+
background: var(--darker-grey);
|
|
1509
1984
|
opacity: 0;
|
|
1510
1985
|
transform: translateY(20px);
|
|
1511
1986
|
}
|
|
1512
1987
|
|
|
1513
1988
|
.install-step:last-child {
|
|
1514
|
-
|
|
1989
|
+
margin-bottom: 0;
|
|
1515
1990
|
}
|
|
1516
1991
|
|
|
1517
1992
|
.install-icon {
|
|
1518
1993
|
width: 50px;
|
|
1519
1994
|
height: 50px;
|
|
1520
|
-
border: 1px dashed var(--grey);
|
|
1521
1995
|
display: flex;
|
|
1522
1996
|
align-items: center;
|
|
1523
1997
|
justify-content: center;
|
|
1524
1998
|
flex-shrink: 0;
|
|
1525
|
-
font-size: 1.
|
|
1999
|
+
font-size: 1.8rem;
|
|
1526
2000
|
}
|
|
1527
2001
|
|
|
1528
2002
|
.install-content {
|
|
@@ -1530,29 +2004,35 @@
|
|
|
1530
2004
|
}
|
|
1531
2005
|
|
|
1532
2006
|
.install-from {
|
|
1533
|
-
color: var(--
|
|
1534
|
-
font-size:
|
|
2007
|
+
color: var(--white);
|
|
2008
|
+
font-size: 1rem;
|
|
2009
|
+
font-weight: 500;
|
|
1535
2010
|
}
|
|
1536
2011
|
|
|
1537
2012
|
.install-arrow {
|
|
1538
|
-
color: var(--
|
|
2013
|
+
color: var(--accent-red);
|
|
1539
2014
|
flex-shrink: 0;
|
|
1540
2015
|
font-size: 1.2rem;
|
|
2016
|
+
font-weight: bold;
|
|
1541
2017
|
}
|
|
1542
2018
|
|
|
1543
2019
|
.install-to {
|
|
1544
2020
|
color: var(--white);
|
|
1545
|
-
font-size: 0.
|
|
2021
|
+
font-size: 0.9rem;
|
|
2022
|
+
font-weight: 500;
|
|
2023
|
+
min-width: 180px;
|
|
2024
|
+
text-align: right;
|
|
2025
|
+
font-family: 'JetBrains Mono', 'SF Mono', monospace;
|
|
1546
2026
|
}
|
|
1547
2027
|
|
|
1548
2028
|
.install-note {
|
|
1549
2029
|
color: var(--grey);
|
|
1550
|
-
font-size: 0.
|
|
2030
|
+
font-size: 0.75rem;
|
|
1551
2031
|
margin-top: 5px;
|
|
1552
2032
|
}
|
|
1553
2033
|
|
|
1554
2034
|
/* ============================================
|
|
1555
|
-
SECTION 10: CREDITS
|
|
2035
|
+
SECTION 10: CREDITS - boxy style
|
|
1556
2036
|
============================================ */
|
|
1557
2037
|
#credits {
|
|
1558
2038
|
text-align: center;
|
|
@@ -1560,64 +2040,69 @@
|
|
|
1560
2040
|
|
|
1561
2041
|
.credit-links {
|
|
1562
2042
|
display: flex;
|
|
1563
|
-
gap:
|
|
2043
|
+
gap: 20px;
|
|
1564
2044
|
margin-top: 40px;
|
|
1565
2045
|
flex-wrap: wrap;
|
|
1566
2046
|
justify-content: center;
|
|
1567
2047
|
}
|
|
1568
2048
|
|
|
1569
2049
|
.credit-link {
|
|
1570
|
-
border:
|
|
2050
|
+
border: 2px solid var(--border-color);
|
|
2051
|
+
background: var(--darker-grey);
|
|
1571
2052
|
padding: 18px 35px;
|
|
1572
2053
|
text-decoration: none;
|
|
1573
2054
|
color: var(--white);
|
|
1574
|
-
transition: all 0.
|
|
2055
|
+
transition: all 0.2s;
|
|
1575
2056
|
opacity: 0;
|
|
2057
|
+
text-transform: uppercase;
|
|
2058
|
+
letter-spacing: 1px;
|
|
2059
|
+
font-size: 0.85rem;
|
|
1576
2060
|
}
|
|
1577
2061
|
|
|
1578
2062
|
.credit-link:hover {
|
|
1579
2063
|
background: var(--accent-red);
|
|
1580
|
-
color:
|
|
2064
|
+
color: var(--white);
|
|
1581
2065
|
border-color: var(--accent-red);
|
|
1582
|
-
box-shadow: 0 0
|
|
2066
|
+
box-shadow: 0 0 15px var(--accent-red-glow);
|
|
1583
2067
|
}
|
|
1584
2068
|
|
|
1585
2069
|
.made-with {
|
|
1586
2070
|
margin-top: 50px;
|
|
1587
2071
|
color: var(--grey);
|
|
1588
|
-
font-size: 0.
|
|
2072
|
+
font-size: 0.9rem;
|
|
1589
2073
|
opacity: 0;
|
|
1590
2074
|
line-height: 1.8;
|
|
1591
2075
|
}
|
|
1592
2076
|
|
|
1593
2077
|
/* ============================================
|
|
1594
|
-
SECTION INDICATORS
|
|
2078
|
+
SECTION INDICATORS - square boxy dots
|
|
1595
2079
|
============================================ */
|
|
1596
2080
|
.section-indicator {
|
|
1597
2081
|
position: fixed;
|
|
1598
|
-
right:
|
|
2082
|
+
right: 20px;
|
|
1599
2083
|
top: 50%;
|
|
1600
2084
|
transform: translateY(-50%);
|
|
1601
2085
|
display: flex;
|
|
1602
2086
|
flex-direction: column;
|
|
1603
|
-
gap:
|
|
2087
|
+
gap: 10px;
|
|
1604
2088
|
z-index: 1000;
|
|
1605
2089
|
}
|
|
1606
2090
|
|
|
1607
2091
|
.section-dot {
|
|
1608
2092
|
width: 10px;
|
|
1609
2093
|
height: 10px;
|
|
1610
|
-
border:
|
|
1611
|
-
border-radius:
|
|
2094
|
+
border: 2px solid var(--border-color);
|
|
2095
|
+
border-radius: 0;
|
|
1612
2096
|
cursor: pointer;
|
|
1613
|
-
transition: all 0.
|
|
2097
|
+
transition: all 0.2s;
|
|
2098
|
+
background: var(--card-bg);
|
|
1614
2099
|
}
|
|
1615
2100
|
|
|
1616
2101
|
.section-dot:hover,
|
|
1617
2102
|
.section-dot.active {
|
|
1618
2103
|
background: var(--accent-red);
|
|
1619
2104
|
border-color: var(--accent-red);
|
|
1620
|
-
box-shadow: 0 0
|
|
2105
|
+
box-shadow: 0 0 8px var(--accent-red-glow);
|
|
1621
2106
|
}
|
|
1622
2107
|
|
|
1623
2108
|
/* ============================================
|
|
@@ -1700,16 +2185,82 @@
|
|
|
1700
2185
|
<nav class="nav">
|
|
1701
2186
|
<button class="nav-btn" id="playBtn">▶ AUTO PLAY</button>
|
|
1702
2187
|
<button class="nav-btn" id="audioToggleBtn" title="Toggle narration audio">🔊 WITH AUDIO</button>
|
|
2188
|
+
<button class="nav-btn" id="voiceSelectBtn" title="Choose narrator voice">🎙 VOICE</button>
|
|
1703
2189
|
<button class="nav-btn" id="resetBtn">↺ RESTART</button>
|
|
1704
2190
|
</nav>
|
|
1705
2191
|
|
|
2192
|
+
<!-- Voice Preview Modal -->
|
|
2193
|
+
<div class="voice-modal-overlay" id="voiceModalOverlay">
|
|
2194
|
+
<div class="voice-modal">
|
|
2195
|
+
<div class="voice-modal-header">
|
|
2196
|
+
<div class="voice-modal-title">Choose Narrator Voice</div>
|
|
2197
|
+
<button class="voice-modal-close" id="voiceModalClose">×</button>
|
|
2198
|
+
</div>
|
|
2199
|
+
|
|
2200
|
+
<div class="voice-options">
|
|
2201
|
+
<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">
|
|
2202
|
+
<button class="voice-play-btn" data-preview="audio/narration-adam.mp3">▶</button>
|
|
2203
|
+
<div class="voice-info">
|
|
2204
|
+
<div class="voice-name">Adam</div>
|
|
2205
|
+
<div class="voice-desc">Deep, professional (~6 min)</div>
|
|
2206
|
+
</div>
|
|
2207
|
+
<button class="voice-select-btn">Selected</button>
|
|
2208
|
+
</div>
|
|
2209
|
+
|
|
2210
|
+
<div class="voice-option" data-voice="mark" data-file="audio/narration-mark.mp3" data-timing="audio/narration-mark-timing.json" data-duration="338.7">
|
|
2211
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-mark.mp3">▶</button>
|
|
2212
|
+
<div class="voice-info">
|
|
2213
|
+
<div class="voice-name">Mark</div>
|
|
2214
|
+
<div class="voice-desc">Warm, conversational (~5.5 min)</div>
|
|
2215
|
+
</div>
|
|
2216
|
+
<button class="voice-select-btn">Select</button>
|
|
2217
|
+
</div>
|
|
2218
|
+
|
|
2219
|
+
<div class="voice-option" data-voice="hope" data-file="audio/narration-hope.mp3" data-timing="audio/narration-hope-timing.json" data-duration="305.2">
|
|
2220
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-hope.mp3">▶</button>
|
|
2221
|
+
<div class="voice-info">
|
|
2222
|
+
<div class="voice-name">Hope</div>
|
|
2223
|
+
<div class="voice-desc">Bright, energetic (~5 min)</div>
|
|
2224
|
+
</div>
|
|
2225
|
+
<button class="voice-select-btn">Select</button>
|
|
2226
|
+
</div>
|
|
2227
|
+
|
|
2228
|
+
<div class="voice-option" data-voice="creature" data-file="audio/narration-creature.mp3" data-timing="audio/narration-creature-timing.json" data-duration="466.6">
|
|
2229
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-creature.mp3">▶</button>
|
|
2230
|
+
<div class="voice-info">
|
|
2231
|
+
<div class="voice-name">Creature</div>
|
|
2232
|
+
<div class="voice-desc">Unique, character voice (~7.5 min)</div>
|
|
2233
|
+
</div>
|
|
2234
|
+
<button class="voice-select-btn">Select</button>
|
|
2235
|
+
</div>
|
|
2236
|
+
|
|
2237
|
+
<div class="voice-option" data-voice="gaming" data-file="audio/narration-gaming.mp3" data-timing="audio/narration-gaming-timing.json" data-duration="644.1">
|
|
2238
|
+
<button class="voice-play-btn" data-preview="audio/previews/preview-gaming.mp3">▶</button>
|
|
2239
|
+
<div class="voice-info">
|
|
2240
|
+
<div class="voice-name">Gaming</div>
|
|
2241
|
+
<div class="voice-desc">Dynamic, enthusiastic (~10.5 min)</div>
|
|
2242
|
+
</div>
|
|
2243
|
+
<button class="voice-select-btn">Select</button>
|
|
2244
|
+
</div>
|
|
2245
|
+
</div>
|
|
2246
|
+
|
|
2247
|
+
<div class="voice-modal-footer">
|
|
2248
|
+
<div class="voice-modal-note">All voices have full narration with synchronized highlights.</div>
|
|
2249
|
+
<button class="voice-confirm-btn" id="voiceConfirmBtn">Confirm Selection</button>
|
|
2250
|
+
</div>
|
|
2251
|
+
</div>
|
|
2252
|
+
</div>
|
|
2253
|
+
|
|
1706
2254
|
<!-- Audio Narration Player (hidden) -->
|
|
1707
2255
|
<audio id="narrationAudio" preload="auto">
|
|
1708
|
-
<source src="audio/narration.mp3" type="audio/mpeg">
|
|
2256
|
+
<source src="audio/narration-adam.mp3" type="audio/mpeg">
|
|
1709
2257
|
</audio>
|
|
1710
2258
|
|
|
1711
2259
|
<!-- Audio Progress Bar -->
|
|
1712
2260
|
<div class="audio-progress-container" id="audioProgressContainer" style="display: none;">
|
|
2261
|
+
<div class="chapter-buttons" id="chapterButtons">
|
|
2262
|
+
<!-- Chapter buttons populated dynamically from timing data -->
|
|
2263
|
+
</div>
|
|
1713
2264
|
<div class="audio-time-display">
|
|
1714
2265
|
<span id="audioCurrentTime">0:00</span>
|
|
1715
2266
|
<span id="audioTotalTime">0:00</span>
|
|
@@ -1733,7 +2284,7 @@
|
|
|
1733
2284
|
<span class="hustle-word hustle-highlight">API-DEV-TOOLS</span>
|
|
1734
2285
|
</div>
|
|
1735
2286
|
<div class="package-name" id="packageName">@hustle-together/api-dev-tools</div>
|
|
1736
|
-
<div class="version" id="versionText">v2.0.
|
|
2287
|
+
<div class="version" id="versionText">v2.0.8</div>
|
|
1737
2288
|
<p class="tagline">"Hustle together. Share resources. Build stronger."<span class="cursor"></span></p>
|
|
1738
2289
|
|
|
1739
2290
|
<div class="intro-text">
|
|
@@ -1786,63 +2337,52 @@
|
|
|
1786
2337
|
<section id="problems">
|
|
1787
2338
|
<div class="ascii-border">
|
|
1788
2339
|
<h2>THE PROBLEM</h2>
|
|
1789
|
-
<h3>
|
|
2340
|
+
<h3>LLMs Make Their Own Decisions</h3>
|
|
1790
2341
|
|
|
1791
2342
|
<div class="explanation">
|
|
1792
2343
|
<div class="explanation-title">Why This Matters</div>
|
|
1793
2344
|
<p>
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
or claim tasks are complete without actually verifying them.
|
|
1797
|
-
</p>
|
|
1798
|
-
<p>
|
|
1799
|
-
These are the <strong>5 most common gaps</strong> we've identified - and the problems they cause:
|
|
2345
|
+
LLMs are powerful but <strong>ungrounded</strong>. Without structure, they default to training data,
|
|
2346
|
+
make assumptions, and forget context. These are the <strong>3 critical gaps</strong> that compound:
|
|
1800
2347
|
</p>
|
|
1801
2348
|
</div>
|
|
1802
2349
|
|
|
1803
2350
|
<ul class="gap-list">
|
|
1804
2351
|
<li class="gap-item">
|
|
1805
|
-
<span class="gap-
|
|
1806
|
-
<div class="gap-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
<
|
|
1810
|
-
<
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
<span class="gap-number">Gap 02</span>
|
|
1816
|
-
<div class="gap-title">No Proof Files Were Actually Changed</div>
|
|
1817
|
-
<div class="gap-desc">AI says "I updated all the files" but never runs git diff to prove it.</div>
|
|
1818
|
-
<div class="gap-example">
|
|
1819
|
-
AI: "Migration complete! All 6 files updated."<br>
|
|
1820
|
-
Reality: Only 4 files were changed, 2 were missed.
|
|
2352
|
+
<span class="gap-icon">🔍</span>
|
|
2353
|
+
<div class="gap-content">
|
|
2354
|
+
<span class="gap-number">Gap 01</span>
|
|
2355
|
+
<div class="gap-title">AI Doesn't Use Your Exact Words</div>
|
|
2356
|
+
<div class="gap-desc">You say one thing, but the AI searches for something different. Wrong library. Wrong documentation.</div>
|
|
2357
|
+
<div class="gap-example">
|
|
2358
|
+
<span class="bad">You: "Use Brandfetch API"</span><br>
|
|
2359
|
+
<span class="bad">AI searches: "brand fetch library" (wrong!)</span><br>
|
|
2360
|
+
<span class="good">Solution: Phase 0 disambiguation before research</span>
|
|
2361
|
+
</div>
|
|
1821
2362
|
</div>
|
|
1822
2363
|
</li>
|
|
1823
2364
|
<li class="gap-item">
|
|
1824
|
-
<span class="gap-
|
|
1825
|
-
<div class="gap-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
2365
|
+
<span class="gap-icon">🧠</span>
|
|
2366
|
+
<div class="gap-content">
|
|
2367
|
+
<span class="gap-number">Gap 02</span>
|
|
2368
|
+
<div class="gap-title">Memory-Based Implementation</div>
|
|
2369
|
+
<div class="gap-desc">After research, the AI forgets what it learned and implements from training data instead.</div>
|
|
2370
|
+
<div class="gap-example">
|
|
2371
|
+
AI researches Brandfetch v2 API, then implements v1 patterns from memory.<br>
|
|
2372
|
+
<span class="good">Solution: Phase 9 verification re-reads docs after implementation</span>
|
|
2373
|
+
</div>
|
|
1829
2374
|
</div>
|
|
1830
2375
|
</li>
|
|
1831
2376
|
<li class="gap-item">
|
|
1832
|
-
<span class="gap-
|
|
1833
|
-
<div class="gap-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
<div class="gap-title">Tests Don't Match Production Code</div>
|
|
1842
|
-
<div class="gap-desc">Production code uses new patterns, but tests still check old patterns.</div>
|
|
1843
|
-
<div class="gap-example">
|
|
1844
|
-
<span class="bad">Production: AI_GATEWAY_API_KEY</span><br>
|
|
1845
|
-
<span class="bad">Tests: OPENAI_API_KEY (outdated!)</span>
|
|
2377
|
+
<span class="gap-icon">💨</span>
|
|
2378
|
+
<div class="gap-content">
|
|
2379
|
+
<span class="gap-number">Gap 03</span>
|
|
2380
|
+
<div class="gap-title">Context Dilution</div>
|
|
2381
|
+
<div class="gap-desc">After many turns, the AI forgets project structure, documentation locations, and your decisions.</div>
|
|
2382
|
+
<div class="gap-example">
|
|
2383
|
+
Turn 50: "What API key should I use?" (you already told it 40 turns ago)<br>
|
|
2384
|
+
<span class="good">Solution: Re-grounding every 7 turns via hooks</span>
|
|
2385
|
+
</div>
|
|
1846
2386
|
</div>
|
|
1847
2387
|
</li>
|
|
1848
2388
|
</ul>
|
|
@@ -1871,7 +2411,7 @@
|
|
|
1871
2411
|
|
|
1872
2412
|
<div class="solution-grid">
|
|
1873
2413
|
<div class="solution-card">
|
|
1874
|
-
<span class="solution-icon"
|
|
2414
|
+
<span class="solution-icon">📚</span>
|
|
1875
2415
|
<div class="solution-title">Research First</div>
|
|
1876
2416
|
<div class="solution-desc">
|
|
1877
2417
|
AI must search for live documentation before writing any code.
|
|
@@ -1879,7 +2419,7 @@
|
|
|
1879
2419
|
</div>
|
|
1880
2420
|
</div>
|
|
1881
2421
|
<div class="solution-card">
|
|
1882
|
-
<span class="solution-icon"
|
|
2422
|
+
<span class="solution-icon">💬</span>
|
|
1883
2423
|
<div class="solution-title">Interview Required</div>
|
|
1884
2424
|
<div class="solution-desc">
|
|
1885
2425
|
AI must ask YOU questions and wait for answers.
|
|
@@ -1887,7 +2427,7 @@
|
|
|
1887
2427
|
</div>
|
|
1888
2428
|
</div>
|
|
1889
2429
|
<div class="solution-card">
|
|
1890
|
-
<span class="solution-icon"
|
|
2430
|
+
<span class="solution-icon">✅</span>
|
|
1891
2431
|
<div class="solution-title">Verification Built-In</div>
|
|
1892
2432
|
<div class="solution-desc">
|
|
1893
2433
|
System checks implementation matches interview.
|
|
@@ -1973,17 +2513,24 @@
|
|
|
1973
2513
|
<section id="phases">
|
|
1974
2514
|
<div class="ascii-border">
|
|
1975
2515
|
<h2>THE WORKFLOW</h2>
|
|
1976
|
-
<h3>
|
|
2516
|
+
<h3>12 Enforced Phases (v3.0)</h3>
|
|
1977
2517
|
|
|
1978
2518
|
<div class="explanation">
|
|
1979
2519
|
<div class="explanation-title">The Complete Process</div>
|
|
1980
2520
|
<p>
|
|
1981
|
-
Every API goes through these
|
|
1982
|
-
the AI skip ahead.
|
|
2521
|
+
Every API goes through these 12 phases. The system tracks progress and won't let
|
|
2522
|
+
the AI skip ahead. <strong>Phase 0 (Disambiguation)</strong> and <strong>Phase 9 (Verify)</strong> are new in v3.0!
|
|
1983
2523
|
</p>
|
|
1984
2524
|
</div>
|
|
1985
2525
|
|
|
1986
2526
|
<div class="phase-grid">
|
|
2527
|
+
<div class="phase-box glow-border" data-phase="0">
|
|
2528
|
+
<div class="phase-status"></div>
|
|
2529
|
+
<div class="phase-number">00</div>
|
|
2530
|
+
<div class="phase-name">Disambiguate</div>
|
|
2531
|
+
<div class="phase-desc">Clarify ambiguous terms</div>
|
|
2532
|
+
<span class="phase-arrow">→</span>
|
|
2533
|
+
</div>
|
|
1987
2534
|
<div class="phase-box glow-border" data-phase="1">
|
|
1988
2535
|
<div class="phase-status"></div>
|
|
1989
2536
|
<div class="phase-number">01</div>
|
|
@@ -2002,14 +2549,14 @@
|
|
|
2002
2549
|
<div class="phase-status"></div>
|
|
2003
2550
|
<div class="phase-number">03</div>
|
|
2004
2551
|
<div class="phase-name">Interview</div>
|
|
2005
|
-
<div class="phase-desc">
|
|
2552
|
+
<div class="phase-desc">Questions FROM research</div>
|
|
2006
2553
|
<span class="phase-arrow">→</span>
|
|
2007
2554
|
</div>
|
|
2008
2555
|
<div class="phase-box glow-border" data-phase="4">
|
|
2009
2556
|
<div class="phase-status"></div>
|
|
2010
2557
|
<div class="phase-number">04</div>
|
|
2011
2558
|
<div class="phase-name">Deep Research</div>
|
|
2012
|
-
<div class="phase-desc">
|
|
2559
|
+
<div class="phase-desc">Adaptive, not shotgun</div>
|
|
2013
2560
|
<span class="phase-arrow">→</span>
|
|
2014
2561
|
</div>
|
|
2015
2562
|
<div class="phase-box glow-border" data-phase="5">
|
|
@@ -2042,275 +2589,740 @@
|
|
|
2042
2589
|
<div class="phase-desc">Make tests pass</div>
|
|
2043
2590
|
<span class="phase-arrow">→</span>
|
|
2044
2591
|
</div>
|
|
2045
|
-
<div class="phase-box glow-border" data-phase="9">
|
|
2592
|
+
<div class="phase-box glow-border" data-phase="9" style="border-color: var(--claude-orange);">
|
|
2046
2593
|
<div class="phase-status"></div>
|
|
2047
2594
|
<div class="phase-number">09</div>
|
|
2048
|
-
<div class="phase-name">
|
|
2049
|
-
<div class="phase-desc">
|
|
2595
|
+
<div class="phase-name">Verify</div>
|
|
2596
|
+
<div class="phase-desc">Re-research, find gaps</div>
|
|
2050
2597
|
<span class="phase-arrow">→</span>
|
|
2051
2598
|
</div>
|
|
2052
2599
|
<div class="phase-box glow-border" data-phase="10">
|
|
2053
2600
|
<div class="phase-status"></div>
|
|
2054
2601
|
<div class="phase-number">10</div>
|
|
2602
|
+
<div class="phase-name">Refactor</div>
|
|
2603
|
+
<div class="phase-desc">Clean up the code</div>
|
|
2604
|
+
<span class="phase-arrow">→</span>
|
|
2605
|
+
</div>
|
|
2606
|
+
<div class="phase-box glow-border" data-phase="11">
|
|
2607
|
+
<div class="phase-status"></div>
|
|
2608
|
+
<div class="phase-number">11</div>
|
|
2055
2609
|
<div class="phase-name">Documentation</div>
|
|
2056
2610
|
<div class="phase-desc">Update all docs</div>
|
|
2611
|
+
<span class="phase-arrow">→</span>
|
|
2057
2612
|
</div>
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
SECTION 6: REAL EXAMPLE WALKTHROUGH
|
|
2064
|
-
============================================ -->
|
|
2065
|
-
<section id="walkthrough">
|
|
2066
|
-
<div class="ascii-border">
|
|
2067
|
-
<h2>REAL EXAMPLE</h2>
|
|
2068
|
-
<h3>Creating a Brandfetch API</h3>
|
|
2069
|
-
|
|
2070
|
-
<div class="explanation">
|
|
2071
|
-
<div class="explanation-title">The Complete Flow</div>
|
|
2072
|
-
<p>
|
|
2073
|
-
Let's walk through what happens when you run <code>/api-create brandfetch</code>.
|
|
2074
|
-
This shows the full interview-driven workflow, from research to implementation,
|
|
2075
|
-
with <strong>structured questions based on real documentation</strong>.
|
|
2076
|
-
</p>
|
|
2077
|
-
</div>
|
|
2078
|
-
|
|
2079
|
-
<div class="walkthrough-step">
|
|
2080
|
-
<div class="walkthrough-header">
|
|
2081
|
-
<div class="walkthrough-num">1</div>
|
|
2082
|
-
<div class="walkthrough-title">You Run the Command</div>
|
|
2083
|
-
</div>
|
|
2084
|
-
<div class="walkthrough-content">
|
|
2085
|
-
<div class="walkthrough-desc">
|
|
2086
|
-
You type the command to create a new Brandfetch API endpoint.
|
|
2087
|
-
The workflow immediately begins.
|
|
2088
|
-
</div>
|
|
2089
|
-
<div class="walkthrough-example">
|
|
2090
|
-
<div class="label">Your Command:</div>
|
|
2091
|
-
/api-create brandfetch
|
|
2092
|
-
</div>
|
|
2093
|
-
</div>
|
|
2094
|
-
</div>
|
|
2095
|
-
|
|
2096
|
-
<div class="walkthrough-step">
|
|
2097
|
-
<div class="walkthrough-header">
|
|
2098
|
-
<div class="walkthrough-num">2</div>
|
|
2099
|
-
<div class="walkthrough-title">Research Phase (Automatic)</div>
|
|
2100
|
-
</div>
|
|
2101
|
-
<div class="walkthrough-content">
|
|
2102
|
-
<div class="walkthrough-desc">
|
|
2103
|
-
Before asking any questions, the AI is <strong>required</strong> to research.
|
|
2104
|
-
It fetches live documentation from Context7 and searches the web.
|
|
2105
|
-
</div>
|
|
2106
|
-
<div class="walkthrough-example">
|
|
2107
|
-
<div class="label">Research Logged to State File:</div>
|
|
2108
|
-
- Context7: Brandfetch SDK docs → found logos, colors, fonts endpoints<br>
|
|
2109
|
-
- WebSearch: "Brandfetch API rate limits 2025" → 5 req/second<br>
|
|
2110
|
-
- WebSearch: "Brandfetch API response format" → JSON with asset URLs<br>
|
|
2111
|
-
- Context7: Authentication → Bearer token required
|
|
2112
|
-
</div>
|
|
2113
|
-
</div>
|
|
2114
|
-
</div>
|
|
2115
|
-
|
|
2116
|
-
<div class="walkthrough-step">
|
|
2117
|
-
<div class="walkthrough-header">
|
|
2118
|
-
<div class="walkthrough-num">3</div>
|
|
2119
|
-
<div class="walkthrough-title">Structured Interview (Based on Research)</div>
|
|
2120
|
-
</div>
|
|
2121
|
-
<div class="walkthrough-content">
|
|
2122
|
-
<div class="walkthrough-desc">
|
|
2123
|
-
Now the AI asks YOU questions - but with <strong>multiple-choice options</strong>
|
|
2124
|
-
derived from what it actually found in the documentation. No guessing!
|
|
2125
|
-
</div>
|
|
2126
|
-
<div class="walkthrough-example">
|
|
2127
|
-
<div class="label">Question 1 (with options from research):</div>
|
|
2128
|
-
"What's the primary purpose of this endpoint?"<br><br>
|
|
2129
|
-
<strong>1.</strong> Brand lookup (get brand by domain)<br>
|
|
2130
|
-
<strong>2.</strong> Logo extraction (get logo assets)<br>
|
|
2131
|
-
<strong>3.</strong> Color palette extraction<br>
|
|
2132
|
-
<strong>4.</strong> Full brand kit (all assets)<br>
|
|
2133
|
-
<strong>5.</strong> Type something else...<br><br>
|
|
2134
|
-
<em>You select: "4. Full brand kit (all assets)"</em>
|
|
2135
|
-
</div>
|
|
2136
|
-
</div>
|
|
2137
|
-
</div>
|
|
2138
|
-
|
|
2139
|
-
<div class="walkthrough-step">
|
|
2140
|
-
<div class="walkthrough-header">
|
|
2141
|
-
<div class="walkthrough-num">4</div>
|
|
2142
|
-
<div class="walkthrough-title">More Questions, All Tracked</div>
|
|
2143
|
-
</div>
|
|
2144
|
-
<div class="walkthrough-content">
|
|
2145
|
-
<div class="walkthrough-desc">
|
|
2146
|
-
Each question is tracked with your answer. These decisions are stored
|
|
2147
|
-
in the state file and <strong>injected during implementation</strong> to ensure consistency.
|
|
2148
|
-
</div>
|
|
2149
|
-
<div class="walkthrough-example">
|
|
2150
|
-
<div class="label">Decisions Captured:</div>
|
|
2151
|
-
• Purpose: full_brand_kit<br>
|
|
2152
|
-
• Response Format: JSON with asset URLs<br>
|
|
2153
|
-
• API Key Handling: server environment variables only<br>
|
|
2154
|
-
• Error Handling: detailed (error, code, details)<br>
|
|
2155
|
-
• Required Params: domain (string)<br>
|
|
2156
|
-
• Optional Params: include_colors, include_fonts
|
|
2157
|
-
</div>
|
|
2158
|
-
</div>
|
|
2159
|
-
</div>
|
|
2160
|
-
|
|
2161
|
-
<div class="walkthrough-step">
|
|
2162
|
-
<div class="walkthrough-header">
|
|
2163
|
-
<div class="walkthrough-num">5</div>
|
|
2164
|
-
<div class="walkthrough-title">Implementation (With Decision Reminders)</div>
|
|
2165
|
-
</div>
|
|
2166
|
-
<div class="walkthrough-content">
|
|
2167
|
-
<div class="walkthrough-desc">
|
|
2168
|
-
When the AI writes code, the hook <strong>injects your decisions</strong> as a reminder.
|
|
2169
|
-
This ensures the implementation matches what you actually asked for.
|
|
2170
|
-
</div>
|
|
2171
|
-
<div class="walkthrough-example">
|
|
2172
|
-
<div class="label">Injected on Every Write:</div>
|
|
2173
|
-
✅ Interview complete. REMEMBER THE USER'S DECISIONS:<br><br>
|
|
2174
|
-
• Purpose: Full brand kit (all assets)<br>
|
|
2175
|
-
• Response Format: JSON with asset URLs<br>
|
|
2176
|
-
• API Key Handling: server environment variables only<br>
|
|
2177
|
-
• Error Handling: detailed (error, code, details)<br><br>
|
|
2178
|
-
<em>Your implementation MUST align with these choices.</em>
|
|
2179
|
-
</div>
|
|
2180
|
-
</div>
|
|
2181
|
-
</div>
|
|
2182
|
-
|
|
2183
|
-
<div class="walkthrough-step">
|
|
2184
|
-
<div class="walkthrough-header">
|
|
2185
|
-
<div class="walkthrough-num">6</div>
|
|
2186
|
-
<div class="walkthrough-title">TDD + Final Result</div>
|
|
2187
|
-
</div>
|
|
2188
|
-
<div class="walkthrough-content">
|
|
2189
|
-
<div class="walkthrough-desc">
|
|
2190
|
-
Tests are written first (TDD), then implementation, then docs.
|
|
2191
|
-
Everything is tracked and verified against your interview decisions.
|
|
2192
|
-
</div>
|
|
2193
|
-
<div class="walkthrough-example">
|
|
2194
|
-
<div class="label">Files Created:</div>
|
|
2195
|
-
- src/lib/schemas/brandfetch.ts (Zod validation)<br>
|
|
2196
|
-
- src/lib/__tests__/brandfetch.test.ts (tests first!)<br>
|
|
2197
|
-
- src/app/api/v2/brandfetch/route.ts<br>
|
|
2198
|
-
- Interview doc: src/v2/docs/endpoints/brandfetch.md<br>
|
|
2199
|
-
- All tests passing ✓
|
|
2200
|
-
</div>
|
|
2613
|
+
<div class="phase-box glow-border" data-phase="12">
|
|
2614
|
+
<div class="phase-status"></div>
|
|
2615
|
+
<div class="phase-number">12</div>
|
|
2616
|
+
<div class="phase-name">Complete</div>
|
|
2617
|
+
<div class="phase-desc">Final verification</div>
|
|
2201
2618
|
</div>
|
|
2202
2619
|
</div>
|
|
2203
2620
|
</div>
|
|
2204
2621
|
</section>
|
|
2205
2622
|
|
|
2206
2623
|
<!-- ============================================
|
|
2207
|
-
SECTION
|
|
2624
|
+
SECTION 6: LIVE DEMO - CLAUDE CODE TERMINAL
|
|
2625
|
+
Full 12-phase v3.0 workflow simulation
|
|
2208
2626
|
============================================ -->
|
|
2209
2627
|
<section id="demo">
|
|
2210
|
-
<div class="ascii-border">
|
|
2628
|
+
<div class="ascii-border" style="max-width: 1400px;">
|
|
2211
2629
|
<h2>LIVE SIMULATION</h2>
|
|
2212
|
-
<h3>
|
|
2630
|
+
<h3>12-Phase Workflow (v3.0)</h3>
|
|
2213
2631
|
|
|
2214
2632
|
<div class="explanation">
|
|
2215
|
-
<div class="explanation-title">
|
|
2633
|
+
<div class="explanation-title">The Complete v3.0 Flow</div>
|
|
2216
2634
|
<p>
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2635
|
+
Watch the full <strong>/api-create brandfetch</strong> command in action.
|
|
2636
|
+
The side panel explains each step as it happens. Notice the AI/User conversation format
|
|
2637
|
+
and how research findings drive the interview questions.
|
|
2220
2638
|
</p>
|
|
2221
2639
|
</div>
|
|
2222
2640
|
|
|
2223
|
-
<div class="
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
<div class="
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2641
|
+
<div class="claude-code-container">
|
|
2642
|
+
<!-- Claude Code Terminal (Left) -->
|
|
2643
|
+
<div class="claude-code-terminal" id="claudeTerminal">
|
|
2644
|
+
<div class="claude-code-header">
|
|
2645
|
+
<div class="claude-code-dots">
|
|
2646
|
+
<div class="claude-code-dot red"></div>
|
|
2647
|
+
<div class="claude-code-dot yellow"></div>
|
|
2648
|
+
<div class="claude-code-dot green"></div>
|
|
2649
|
+
</div>
|
|
2650
|
+
<div class="claude-code-title">~/my-project</div>
|
|
2651
|
+
<div class="claude-code-badge">CLAUDE CODE</div>
|
|
2232
2652
|
</div>
|
|
2233
|
-
</div>
|
|
2234
|
-
|
|
2235
|
-
<div class="terminal-comment" data-step="0">// Step 1: Research phase starts automatically</div>
|
|
2236
|
-
<div class="terminal-line" data-step="1">
|
|
2237
|
-
<span class="terminal-prompt">claude></span>
|
|
2238
|
-
<span class="terminal-command">mcp__context7__get-library-docs("brandfetch")</span>
|
|
2239
|
-
</div>
|
|
2240
|
-
<div class="terminal-line terminal-result" data-step="2">
|
|
2241
|
-
<span class="terminal-logged">[LOGGED] Context7 source: brandfetch SDK documentation</span>
|
|
2242
|
-
</div>
|
|
2243
|
-
|
|
2244
|
-
<div class="terminal-line" data-step="3">
|
|
2245
|
-
<span class="terminal-prompt">claude></span>
|
|
2246
|
-
<span class="terminal-command">WebSearch "Brandfetch API endpoints 2025"</span>
|
|
2247
|
-
</div>
|
|
2248
|
-
<div class="terminal-line terminal-result" data-step="4">
|
|
2249
|
-
<span class="terminal-logged">[LOGGED] WebSearch: found /v2/brands, /v2/search, /v2/logos</span>
|
|
2250
|
-
</div>
|
|
2251
|
-
|
|
2252
|
-
<div class="terminal-comment" data-step="5">// Step 2: Interview with structured options (from research)</div>
|
|
2253
|
-
<div class="terminal-line" data-step="6">
|
|
2254
|
-
<span class="terminal-prompt">claude></span>
|
|
2255
|
-
<span class="terminal-command">AskUserQuestion(<br>
|
|
2256
|
-
question: "What's the primary purpose?"<br>
|
|
2257
|
-
options: [<br>
|
|
2258
|
-
{value: "lookup", label: "Brand lookup by domain"},<br>
|
|
2259
|
-
{value: "logos", label: "Logo extraction only"},<br>
|
|
2260
|
-
{value: "full_kit", label: "Full brand kit"},<br>
|
|
2261
|
-
{value: "custom", label: "Type something..."}<br>
|
|
2262
|
-
]<br>
|
|
2263
|
-
)</span>
|
|
2264
|
-
</div>
|
|
2265
|
-
<div class="terminal-line terminal-result" data-step="7">
|
|
2266
|
-
<span class="terminal-allowed">[USER] Selected: "full_kit"<br>[TRACKED] decisions.purpose = "full_kit"</span>
|
|
2267
|
-
</div>
|
|
2268
2653
|
|
|
2269
|
-
|
|
2270
|
-
<
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
<span class="terminal-allowed">✅ Interview complete. REMEMBER THE USER'S DECISIONS:<br>
|
|
2291
|
-
• Purpose: full_kit<br>
|
|
2292
|
-
• API Key Handling: server_only<br>
|
|
2293
|
-
ALLOWED: Writing file...</span>
|
|
2294
|
-
</div>
|
|
2654
|
+
<!-- ASCII Art Startup Screen -->
|
|
2655
|
+
<pre class="ascii-startup" id="asciiStartup">
|
|
2656
|
+
<span style="color: var(--claude-orange);">╭─────────────────────────────────────────────────────────────────────────────╮</span>
|
|
2657
|
+
<span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text">Tips for getting started</span> <span style="color: var(--claude-orange);">│</span>
|
|
2658
|
+
<span style="color: var(--claude-orange);">│</span> <span class="welcome-text">Welcome back, Developer!</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text">Run /init to create a CLAUDE.md file...</span> <span style="color: var(--claude-orange);">│</span>
|
|
2659
|
+
<span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text">Try /api-create to start a new endpoint</span> <span style="color: var(--claude-orange);">│</span>
|
|
2660
|
+
<span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">▗ ▗ ▖ ▖</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text">────────────────────────────────────────</span> <span style="color: var(--claude-orange);">│</span>
|
|
2661
|
+
<span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text">Recent activity</span> <span style="color: var(--claude-orange);">│</span>
|
|
2662
|
+
<span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">▘▘ ▝▝</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text"> api/v2/search - completed</span> <span style="color: var(--claude-orange);">│</span>
|
|
2663
|
+
<span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">│</span> <span class="tips-text"> api/v2/analyze - 8 tests passing</span> <span style="color: var(--claude-orange);">│</span>
|
|
2664
|
+
<span style="color: var(--claude-orange);">│</span> <span class="model-text">Opus 4.5 · Claude Max</span> <span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">│</span>
|
|
2665
|
+
<span style="color: var(--claude-orange);">│</span> <span class="path-text">~/my-project</span> <span style="color: var(--claude-orange);">│</span> <span style="color: var(--claude-orange);">│</span>
|
|
2666
|
+
<span style="color: var(--claude-orange);">╰─────────────────────────────────────────────────────────────────────────────╯</span>
|
|
2667
|
+
</pre>
|
|
2668
|
+
|
|
2669
|
+
<!-- Prompt Line (shows before messages) -->
|
|
2670
|
+
<div class="claude-prompt-line" id="promptLine">
|
|
2671
|
+
<span class="claude-prompt">❯</span>
|
|
2672
|
+
<span class="claude-input" id="promptInput"></span>
|
|
2673
|
+
<span class="claude-cursor"></span>
|
|
2674
|
+
</div>
|
|
2295
2675
|
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2676
|
+
<div class="claude-code-body" id="claudeBody">
|
|
2677
|
+
<!-- Loading dots (shown between messages) -->
|
|
2678
|
+
<div class="loading-dots" id="loadingDots" style="display: none;">
|
|
2679
|
+
<span></span><span></span><span></span>
|
|
2680
|
+
</div>
|
|
2681
|
+
|
|
2682
|
+
<!-- Phase 0: User Command -->
|
|
2683
|
+
<div class="claude-msg" data-phase="0" data-step="0">
|
|
2684
|
+
<div class="claude-msg-role user">
|
|
2685
|
+
<span class="claude-msg-icon">👤</span>
|
|
2686
|
+
<span class="claude-msg-name">USER</span>
|
|
2687
|
+
</div>
|
|
2688
|
+
<div class="claude-msg-content">
|
|
2689
|
+
<code>/api-create brandfetch</code>
|
|
2690
|
+
</div>
|
|
2691
|
+
</div>
|
|
2692
|
+
|
|
2693
|
+
<!-- Phase 1: Scope Confirmation -->
|
|
2694
|
+
<div class="claude-msg" data-phase="1" data-step="1">
|
|
2695
|
+
<div class="claude-msg-role ai">
|
|
2696
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2697
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2698
|
+
</div>
|
|
2699
|
+
<div class="claude-msg-content">
|
|
2700
|
+
I understand you want to create an API endpoint for <strong>Brandfetch</strong> - a service for fetching brand assets (logos, colors, fonts) by domain.
|
|
2701
|
+
<br><br>Is this correct? <span style="color: #22c55e;">✓ Yes</span>
|
|
2702
|
+
</div>
|
|
2703
|
+
</div>
|
|
2704
|
+
|
|
2705
|
+
<!-- Phase 2: Initial Research -->
|
|
2706
|
+
<div class="claude-msg" data-phase="2" data-step="2">
|
|
2707
|
+
<div class="claude-msg-role ai">
|
|
2708
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2709
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2710
|
+
</div>
|
|
2711
|
+
<div class="claude-msg-content">
|
|
2712
|
+
Starting research... I need to find the real documentation before asking questions.
|
|
2713
|
+
<div class="claude-tool-call">
|
|
2714
|
+
<div class="claude-tool-name">mcp__context7__get-library-docs</div>
|
|
2715
|
+
<div class="claude-tool-result">"brandfetch" → Found SDK docs, /v2/brands endpoint, auth method</div>
|
|
2716
|
+
</div>
|
|
2717
|
+
<div class="claude-tool-call">
|
|
2718
|
+
<div class="claude-tool-name">WebSearch</div>
|
|
2719
|
+
<div class="claude-tool-result">"Brandfetch API rate limits 2025" → 5 req/second</div>
|
|
2720
|
+
</div>
|
|
2721
|
+
</div>
|
|
2722
|
+
</div>
|
|
2723
|
+
|
|
2724
|
+
<!-- User Loop-back: Request more research -->
|
|
2725
|
+
<div class="claude-msg loop-back" data-phase="2" data-step="3">
|
|
2726
|
+
<div class="claude-msg-role user">
|
|
2727
|
+
<span class="claude-msg-icon">👤</span>
|
|
2728
|
+
<span class="claude-msg-name">USER</span>
|
|
2729
|
+
</div>
|
|
2730
|
+
<div class="claude-msg-content">
|
|
2731
|
+
<span class="loop-back-badge">↩ LOOP BACK</span>
|
|
2732
|
+
Can you also check what error responses look like?
|
|
2733
|
+
</div>
|
|
2734
|
+
</div>
|
|
2735
|
+
|
|
2736
|
+
<div class="claude-msg" data-phase="2" data-step="4">
|
|
2737
|
+
<div class="claude-msg-role ai">
|
|
2738
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2739
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2740
|
+
</div>
|
|
2741
|
+
<div class="claude-msg-content">
|
|
2742
|
+
Good catch! Researching error handling...
|
|
2743
|
+
<div class="claude-tool-call">
|
|
2744
|
+
<div class="claude-tool-name">WebSearch</div>
|
|
2745
|
+
<div class="claude-tool-result">"Brandfetch API error codes" → 401, 404, 429 with JSON error format</div>
|
|
2746
|
+
</div>
|
|
2747
|
+
</div>
|
|
2748
|
+
</div>
|
|
2749
|
+
|
|
2750
|
+
<!-- Phase 3: Interview (Extensive - 5+ questions) -->
|
|
2751
|
+
<div class="claude-msg" data-phase="3" data-step="5">
|
|
2752
|
+
<div class="claude-msg-role ai">
|
|
2753
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2754
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2755
|
+
</div>
|
|
2756
|
+
<div class="claude-msg-content">
|
|
2757
|
+
Based on my research, I found these capabilities. <strong>Question 1/5:</strong> What's the primary purpose?
|
|
2758
|
+
<div class="claude-user-choice">
|
|
2759
|
+
<div class="claude-choice-option">
|
|
2760
|
+
<div class="claude-choice-marker"></div>
|
|
2761
|
+
<span>Brand lookup by domain</span>
|
|
2762
|
+
</div>
|
|
2763
|
+
<div class="claude-choice-option">
|
|
2764
|
+
<div class="claude-choice-marker"></div>
|
|
2765
|
+
<span>Logo extraction only</span>
|
|
2766
|
+
</div>
|
|
2767
|
+
<div class="claude-choice-option selected">
|
|
2768
|
+
<div class="claude-choice-marker"></div>
|
|
2769
|
+
<span>Full brand kit (logos, colors, fonts)</span>
|
|
2770
|
+
</div>
|
|
2771
|
+
</div>
|
|
2772
|
+
</div>
|
|
2773
|
+
</div>
|
|
2774
|
+
|
|
2775
|
+
<div class="claude-msg" data-phase="3" data-step="6">
|
|
2776
|
+
<div class="claude-msg-role user">
|
|
2777
|
+
<span class="claude-msg-icon">👤</span>
|
|
2778
|
+
<span class="claude-msg-name">USER</span>
|
|
2779
|
+
</div>
|
|
2780
|
+
<div class="claude-msg-content">
|
|
2781
|
+
<span style="color: #3b82f6;">Selected: Full brand kit (logos, colors, fonts)</span>
|
|
2782
|
+
</div>
|
|
2783
|
+
</div>
|
|
2784
|
+
|
|
2785
|
+
<div class="claude-msg" data-phase="3" data-step="7">
|
|
2786
|
+
<div class="claude-msg-role ai">
|
|
2787
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2788
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2789
|
+
</div>
|
|
2790
|
+
<div class="claude-msg-content">
|
|
2791
|
+
<strong>Question 2/5:</strong> How should <strong>API keys</strong> be handled?
|
|
2792
|
+
<div class="claude-user-choice">
|
|
2793
|
+
<div class="claude-choice-option selected">
|
|
2794
|
+
<div class="claude-choice-marker"></div>
|
|
2795
|
+
<span>Server environment variables only</span>
|
|
2796
|
+
</div>
|
|
2797
|
+
<div class="claude-choice-option">
|
|
2798
|
+
<div class="claude-choice-marker"></div>
|
|
2799
|
+
<span>Custom header override (X-Brandfetch-Key)</span>
|
|
2800
|
+
</div>
|
|
2801
|
+
<div class="claude-choice-option">
|
|
2802
|
+
<div class="claude-choice-marker"></div>
|
|
2803
|
+
<span>Both methods</span>
|
|
2804
|
+
</div>
|
|
2805
|
+
</div>
|
|
2806
|
+
</div>
|
|
2807
|
+
</div>
|
|
2808
|
+
|
|
2809
|
+
<div class="claude-msg" data-phase="3" data-step="8">
|
|
2810
|
+
<div class="claude-msg-role user">
|
|
2811
|
+
<span class="claude-msg-icon">👤</span>
|
|
2812
|
+
<span class="claude-msg-name">USER</span>
|
|
2813
|
+
</div>
|
|
2814
|
+
<div class="claude-msg-content">
|
|
2815
|
+
<span style="color: #3b82f6;">Selected: Server environment variables only</span>
|
|
2816
|
+
</div>
|
|
2817
|
+
</div>
|
|
2818
|
+
|
|
2819
|
+
<div class="claude-msg" data-phase="3" data-step="9">
|
|
2820
|
+
<div class="claude-msg-role ai">
|
|
2821
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2822
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2823
|
+
</div>
|
|
2824
|
+
<div class="claude-msg-content">
|
|
2825
|
+
<strong>Question 3/5:</strong> What <strong>caching strategy</strong> should we use?
|
|
2826
|
+
<div class="claude-user-choice">
|
|
2827
|
+
<div class="claude-choice-option">
|
|
2828
|
+
<div class="claude-choice-marker"></div>
|
|
2829
|
+
<span>No caching</span>
|
|
2830
|
+
</div>
|
|
2831
|
+
<div class="claude-choice-option">
|
|
2832
|
+
<div class="claude-choice-marker"></div>
|
|
2833
|
+
<span>Short cache (5 minutes)</span>
|
|
2834
|
+
</div>
|
|
2835
|
+
<div class="claude-choice-option selected">
|
|
2836
|
+
<div class="claude-choice-marker"></div>
|
|
2837
|
+
<span>Long cache (24 hours) - brands rarely change</span>
|
|
2838
|
+
</div>
|
|
2839
|
+
</div>
|
|
2840
|
+
</div>
|
|
2841
|
+
</div>
|
|
2842
|
+
|
|
2843
|
+
<div class="claude-msg" data-phase="3" data-step="10">
|
|
2844
|
+
<div class="claude-msg-role user">
|
|
2845
|
+
<span class="claude-msg-icon">👤</span>
|
|
2846
|
+
<span class="claude-msg-name">USER</span>
|
|
2847
|
+
</div>
|
|
2848
|
+
<div class="claude-msg-content">
|
|
2849
|
+
<span style="color: #3b82f6;">Selected: Long cache (24 hours)</span>
|
|
2850
|
+
</div>
|
|
2851
|
+
</div>
|
|
2852
|
+
|
|
2853
|
+
<div class="claude-msg" data-phase="3" data-step="11">
|
|
2854
|
+
<div class="claude-msg-role ai">
|
|
2855
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2856
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2857
|
+
</div>
|
|
2858
|
+
<div class="claude-msg-content">
|
|
2859
|
+
<strong>Question 4/5:</strong> How should we handle <strong>errors</strong>?
|
|
2860
|
+
<div class="claude-user-choice">
|
|
2861
|
+
<div class="claude-choice-option">
|
|
2862
|
+
<div class="claude-choice-marker"></div>
|
|
2863
|
+
<span>Throw exceptions (let caller handle)</span>
|
|
2864
|
+
</div>
|
|
2865
|
+
<div class="claude-choice-option selected">
|
|
2866
|
+
<div class="claude-choice-marker"></div>
|
|
2867
|
+
<span>Return error objects with status codes</span>
|
|
2868
|
+
</div>
|
|
2869
|
+
<div class="claude-choice-option">
|
|
2870
|
+
<div class="claude-choice-marker"></div>
|
|
2871
|
+
<span>Fallback to empty brand object</span>
|
|
2872
|
+
</div>
|
|
2873
|
+
</div>
|
|
2874
|
+
</div>
|
|
2875
|
+
</div>
|
|
2876
|
+
|
|
2877
|
+
<div class="claude-msg" data-phase="3" data-step="12">
|
|
2878
|
+
<div class="claude-msg-role user">
|
|
2879
|
+
<span class="claude-msg-icon">👤</span>
|
|
2880
|
+
<span class="claude-msg-name">USER</span>
|
|
2881
|
+
</div>
|
|
2882
|
+
<div class="claude-msg-content">
|
|
2883
|
+
<span style="color: #3b82f6;">Selected: Return error objects with status codes</span>
|
|
2884
|
+
</div>
|
|
2885
|
+
</div>
|
|
2886
|
+
|
|
2887
|
+
<div class="claude-msg" data-phase="3" data-step="13">
|
|
2888
|
+
<div class="claude-msg-role ai">
|
|
2889
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2890
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2891
|
+
</div>
|
|
2892
|
+
<div class="claude-msg-content">
|
|
2893
|
+
<strong>Question 5/5:</strong> Should we expose <strong>rate limit headers</strong>?
|
|
2894
|
+
<div class="claude-user-choice">
|
|
2895
|
+
<div class="claude-choice-option">
|
|
2896
|
+
<div class="claude-choice-marker"></div>
|
|
2897
|
+
<span>No - handle internally</span>
|
|
2898
|
+
</div>
|
|
2899
|
+
<div class="claude-choice-option selected">
|
|
2900
|
+
<div class="claude-choice-marker"></div>
|
|
2901
|
+
<span>Yes - X-RateLimit-Remaining, X-RateLimit-Reset</span>
|
|
2902
|
+
</div>
|
|
2903
|
+
</div>
|
|
2904
|
+
</div>
|
|
2905
|
+
</div>
|
|
2906
|
+
|
|
2907
|
+
<div class="claude-msg" data-phase="3" data-step="14">
|
|
2908
|
+
<div class="claude-msg-role user">
|
|
2909
|
+
<span class="claude-msg-icon">👤</span>
|
|
2910
|
+
<span class="claude-msg-name">USER</span>
|
|
2911
|
+
</div>
|
|
2912
|
+
<div class="claude-msg-content">
|
|
2913
|
+
<span style="color: #3b82f6;">Selected: Yes - expose rate limit headers</span>
|
|
2914
|
+
</div>
|
|
2915
|
+
</div>
|
|
2916
|
+
|
|
2917
|
+
<!-- Phase 4: Deep Research -->
|
|
2918
|
+
<div class="claude-msg" data-phase="4" data-step="15">
|
|
2919
|
+
<div class="claude-msg-role ai">
|
|
2920
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2921
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2922
|
+
</div>
|
|
2923
|
+
<div class="claude-msg-content">
|
|
2924
|
+
Based on your selections, I'll research more specifically:
|
|
2925
|
+
<div class="claude-tool-call">
|
|
2926
|
+
<div class="claude-tool-name">WebSearch</div>
|
|
2927
|
+
<div class="claude-tool-result">"Brandfetch full brand response format" → JSON with logos[], colors[], fonts[]</div>
|
|
2928
|
+
</div>
|
|
2929
|
+
<div class="claude-tool-call">
|
|
2930
|
+
<div class="claude-tool-name">WebSearch</div>
|
|
2931
|
+
<div class="claude-tool-result">"Brandfetch rate limit headers" → X-RateLimit-Limit, X-RateLimit-Remaining</div>
|
|
2932
|
+
</div>
|
|
2933
|
+
</div>
|
|
2934
|
+
</div>
|
|
2935
|
+
|
|
2936
|
+
<!-- Phase 5: Schema -->
|
|
2937
|
+
<div class="claude-msg" data-phase="5" data-step="16">
|
|
2938
|
+
<div class="claude-msg-role ai">
|
|
2939
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2940
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2941
|
+
</div>
|
|
2942
|
+
<div class="claude-msg-content">
|
|
2943
|
+
Creating Zod schema based on research + interview decisions...
|
|
2944
|
+
<div class="claude-tool-call">
|
|
2945
|
+
<div class="claude-tool-name">Write</div>
|
|
2946
|
+
<div class="claude-tool-result">src/lib/schemas/brandfetch.ts → BrandRequestSchema, BrandResponseSchema, BrandErrorSchema</div>
|
|
2947
|
+
</div>
|
|
2948
|
+
</div>
|
|
2949
|
+
</div>
|
|
2950
|
+
|
|
2951
|
+
<!-- Phase 6: Environment -->
|
|
2952
|
+
<div class="claude-msg" data-phase="6" data-step="17">
|
|
2953
|
+
<div class="claude-msg-role system">
|
|
2954
|
+
<span class="claude-msg-icon">⚙️</span>
|
|
2955
|
+
<span class="claude-msg-name">HOOK</span>
|
|
2956
|
+
</div>
|
|
2957
|
+
<div class="claude-msg-content">
|
|
2958
|
+
<strong style="color: var(--claude-orange);">check-env.py:</strong> Checking required environment variables...<br>
|
|
2959
|
+
<span style="color: #22c55e;">✓ BRANDFETCH_API_KEY found in .env.local</span>
|
|
2960
|
+
</div>
|
|
2961
|
+
</div>
|
|
2962
|
+
|
|
2963
|
+
<!-- Phase 7: TDD Red -->
|
|
2964
|
+
<div class="claude-msg" data-phase="7" data-step="18">
|
|
2965
|
+
<div class="claude-msg-role ai">
|
|
2966
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2967
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2968
|
+
</div>
|
|
2969
|
+
<div class="claude-msg-content">
|
|
2970
|
+
Writing failing tests first (TDD Red)...
|
|
2971
|
+
<div class="claude-tool-call">
|
|
2972
|
+
<div class="claude-tool-name">Write</div>
|
|
2973
|
+
<div class="claude-tool-result">src/lib/__tests__/brandfetch.test.ts (15 test cases)</div>
|
|
2974
|
+
</div>
|
|
2975
|
+
<div style="margin-top: 8px; color: #888; font-size: 0.85rem;">
|
|
2976
|
+
Tests cover: full brand kit, caching, error objects, rate limit headers
|
|
2977
|
+
</div>
|
|
2978
|
+
</div>
|
|
2979
|
+
</div>
|
|
2980
|
+
|
|
2981
|
+
<!-- Phase 8: TDD Green -->
|
|
2982
|
+
<div class="claude-msg" data-phase="8" data-step="19">
|
|
2983
|
+
<div class="claude-msg-role ai">
|
|
2984
|
+
<span class="claude-msg-icon">🤖</span>
|
|
2985
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
2986
|
+
</div>
|
|
2987
|
+
<div class="claude-msg-content">
|
|
2988
|
+
Implementing to pass tests...
|
|
2989
|
+
<div class="claude-tool-call">
|
|
2990
|
+
<div class="claude-tool-name">Write</div>
|
|
2991
|
+
<div class="claude-tool-result">src/app/api/v2/brandfetch/route.ts</div>
|
|
2992
|
+
</div>
|
|
2993
|
+
</div>
|
|
2994
|
+
</div>
|
|
2995
|
+
|
|
2996
|
+
<div class="claude-msg" data-phase="8" data-step="20">
|
|
2997
|
+
<div class="claude-msg-role system">
|
|
2998
|
+
<span class="claude-msg-icon">⚙️</span>
|
|
2999
|
+
<span class="claude-msg-name">HOOK</span>
|
|
3000
|
+
</div>
|
|
3001
|
+
<div class="claude-msg-content">
|
|
3002
|
+
<strong style="color: var(--claude-orange);">enforce-interview.py:</strong> REMEMBER USER DECISIONS:<br>
|
|
3003
|
+
• Purpose: full_brand_kit<br>
|
|
3004
|
+
• API Key Handling: server_only<br>
|
|
3005
|
+
• Caching: 24_hours<br>
|
|
3006
|
+
• Error Handling: return_error_objects<br>
|
|
3007
|
+
• Rate Limits: expose_headers
|
|
3008
|
+
</div>
|
|
3009
|
+
</div>
|
|
3010
|
+
|
|
3011
|
+
<div class="claude-msg" data-phase="8" data-step="21">
|
|
3012
|
+
<div class="claude-msg-role ai">
|
|
3013
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3014
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3015
|
+
</div>
|
|
3016
|
+
<div class="claude-msg-content">
|
|
3017
|
+
<span style="color: #22c55e;">✓ All 15 tests passing!</span>
|
|
3018
|
+
</div>
|
|
3019
|
+
</div>
|
|
3020
|
+
|
|
3021
|
+
<!-- Phase 9: Verify - GAP FOUND! -->
|
|
3022
|
+
<div class="claude-msg" data-phase="9" data-step="22">
|
|
3023
|
+
<div class="claude-msg-role ai">
|
|
3024
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3025
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3026
|
+
</div>
|
|
3027
|
+
<div class="claude-msg-content">
|
|
3028
|
+
Re-reading docs to verify implementation matches...
|
|
3029
|
+
<div class="claude-tool-call">
|
|
3030
|
+
<div class="claude-tool-name">mcp__context7__get-library-docs</div>
|
|
3031
|
+
<div class="claude-tool-result">"brandfetch" → Comparing implementation to docs...</div>
|
|
3032
|
+
</div>
|
|
3033
|
+
<div class="gap-found">
|
|
3034
|
+
<div class="gap-found-title">⚠️ GAP DETECTED</div>
|
|
3035
|
+
Missing <code>include_fonts</code> query parameter documented in v2 API
|
|
3036
|
+
</div>
|
|
3037
|
+
</div>
|
|
3038
|
+
</div>
|
|
3039
|
+
|
|
3040
|
+
<!-- Loop back to fix gap -->
|
|
3041
|
+
<div class="claude-msg loop-back" data-phase="9" data-step="23">
|
|
3042
|
+
<div class="claude-msg-role system">
|
|
3043
|
+
<span class="claude-msg-icon">🔄</span>
|
|
3044
|
+
<span class="claude-msg-name">HOOK</span>
|
|
3045
|
+
</div>
|
|
3046
|
+
<div class="claude-msg-content">
|
|
3047
|
+
<span class="loop-back-badge">↩ LOOP BACK TO PHASE 7</span>
|
|
3048
|
+
<strong style="color: var(--claude-orange);">verify-implementation.py:</strong> Gap found! Returning to TDD Red phase to add missing test...
|
|
3049
|
+
</div>
|
|
3050
|
+
</div>
|
|
3051
|
+
|
|
3052
|
+
<div class="claude-msg" data-phase="7" data-step="24">
|
|
3053
|
+
<div class="claude-msg-role ai">
|
|
3054
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3055
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3056
|
+
</div>
|
|
3057
|
+
<div class="claude-msg-content">
|
|
3058
|
+
Adding test for include_fonts parameter...
|
|
3059
|
+
<div class="claude-tool-call">
|
|
3060
|
+
<div class="claude-tool-name">Edit</div>
|
|
3061
|
+
<div class="claude-tool-result">src/lib/__tests__/brandfetch.test.ts → Added test case #16</div>
|
|
3062
|
+
</div>
|
|
3063
|
+
</div>
|
|
3064
|
+
</div>
|
|
3065
|
+
|
|
3066
|
+
<div class="claude-msg" data-phase="8" data-step="25">
|
|
3067
|
+
<div class="claude-msg-role ai">
|
|
3068
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3069
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3070
|
+
</div>
|
|
3071
|
+
<div class="claude-msg-content">
|
|
3072
|
+
Updating implementation for include_fonts...
|
|
3073
|
+
<div class="claude-tool-call">
|
|
3074
|
+
<div class="claude-tool-name">Edit</div>
|
|
3075
|
+
<div class="claude-tool-result">src/app/api/v2/brandfetch/route.ts → Added include_fonts query param</div>
|
|
3076
|
+
</div>
|
|
3077
|
+
<span style="color: #22c55e;">✓ All 16 tests passing!</span>
|
|
3078
|
+
</div>
|
|
3079
|
+
</div>
|
|
3080
|
+
|
|
3081
|
+
<div class="claude-msg" data-phase="9" data-step="26">
|
|
3082
|
+
<div class="claude-msg-role ai">
|
|
3083
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3084
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3085
|
+
</div>
|
|
3086
|
+
<div class="claude-msg-content">
|
|
3087
|
+
Re-verifying implementation...
|
|
3088
|
+
<div class="claude-tool-call">
|
|
3089
|
+
<div class="claude-tool-name">mcp__context7__get-library-docs</div>
|
|
3090
|
+
<div class="claude-tool-result">"brandfetch" → All documented features now implemented</div>
|
|
3091
|
+
</div>
|
|
3092
|
+
<span style="color: #22c55e;">✓ Verification complete - no gaps remaining</span>
|
|
3093
|
+
</div>
|
|
3094
|
+
</div>
|
|
3095
|
+
|
|
3096
|
+
<!-- Phase 10-12: Refactor, Docs, Complete -->
|
|
3097
|
+
<div class="claude-msg" data-phase="10" data-step="27">
|
|
3098
|
+
<div class="claude-msg-role ai">
|
|
3099
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3100
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3101
|
+
</div>
|
|
3102
|
+
<div class="claude-msg-content">
|
|
3103
|
+
Refactoring code (tests still pass)...
|
|
3104
|
+
<div class="claude-tool-call">
|
|
3105
|
+
<div class="claude-tool-name">Edit</div>
|
|
3106
|
+
<div class="claude-tool-result">Extracted caching logic to utils, improved type safety</div>
|
|
3107
|
+
</div>
|
|
3108
|
+
<span style="color: #22c55e;">✓ 16/16 tests passing</span>
|
|
3109
|
+
</div>
|
|
3110
|
+
</div>
|
|
3111
|
+
|
|
3112
|
+
<div class="claude-msg" data-phase="11" data-step="28">
|
|
3113
|
+
<div class="claude-msg-role ai">
|
|
3114
|
+
<span class="claude-msg-icon">🤖</span>
|
|
3115
|
+
<span class="claude-msg-name">CLAUDE</span>
|
|
3116
|
+
</div>
|
|
3117
|
+
<div class="claude-msg-content">
|
|
3118
|
+
Updating documentation...
|
|
3119
|
+
<div class="claude-tool-call">
|
|
3120
|
+
<div class="claude-tool-name">Write</div>
|
|
3121
|
+
<div class="claude-tool-result">api-tests-manifest.json → Added /api/v2/brandfetch entry</div>
|
|
3122
|
+
</div>
|
|
3123
|
+
<div class="claude-tool-call">
|
|
3124
|
+
<div class="claude-tool-name">Edit</div>
|
|
3125
|
+
<div class="claude-tool-result">openapi.yaml → Added brandfetch endpoint schema</div>
|
|
3126
|
+
</div>
|
|
3127
|
+
<div class="claude-tool-call">
|
|
3128
|
+
<div class="claude-tool-name">Write</div>
|
|
3129
|
+
<div class="claude-tool-result">.claude/research/brandfetch/ → Cached research for future sessions</div>
|
|
3130
|
+
</div>
|
|
3131
|
+
</div>
|
|
3132
|
+
</div>
|
|
3133
|
+
|
|
3134
|
+
<div class="claude-msg" data-phase="12" data-step="29">
|
|
3135
|
+
<div class="claude-msg-role system">
|
|
3136
|
+
<span class="claude-msg-icon">✅</span>
|
|
3137
|
+
<span class="claude-msg-name">COMPLETE</span>
|
|
3138
|
+
</div>
|
|
3139
|
+
<div class="claude-msg-content">
|
|
3140
|
+
<span style="color: #22c55e; font-weight: 600;">All 12 phases complete! Brandfetch API ready.</span><br><br>
|
|
3141
|
+
<div style="display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; font-size: 0.85rem;">
|
|
3142
|
+
<div>📁 Files created: <strong>5</strong></div>
|
|
3143
|
+
<div>✅ Tests passing: <strong>16/16</strong></div>
|
|
3144
|
+
<div>🔄 Loop-backs: <strong>2</strong></div>
|
|
3145
|
+
<div>📋 Interview decisions: <strong>5</strong></div>
|
|
3146
|
+
</div>
|
|
3147
|
+
</div>
|
|
3148
|
+
</div>
|
|
3149
|
+
</div>
|
|
2306
3150
|
</div>
|
|
2307
3151
|
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
<div class="
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
3152
|
+
<!-- Explanation Panel (Right) -->
|
|
3153
|
+
<div class="explanation-panel" id="explanationPanel">
|
|
3154
|
+
<div class="explanation-panel-header">
|
|
3155
|
+
Phase Explanation
|
|
3156
|
+
</div>
|
|
3157
|
+
<div class="explanation-panel-body">
|
|
3158
|
+
<div class="explanation-step active" data-phase="0">
|
|
3159
|
+
<div class="explanation-step-phase">
|
|
3160
|
+
<div class="explanation-step-num">0</div>
|
|
3161
|
+
<div class="explanation-step-name">User Command</div>
|
|
3162
|
+
</div>
|
|
3163
|
+
<div class="explanation-step-desc">
|
|
3164
|
+
The user triggers the full 12-phase workflow with a single command.
|
|
3165
|
+
</div>
|
|
3166
|
+
<div class="explanation-step-why">
|
|
3167
|
+
This starts the entire interview-driven development process.
|
|
3168
|
+
</div>
|
|
3169
|
+
</div>
|
|
3170
|
+
|
|
3171
|
+
<div class="explanation-step" data-phase="1">
|
|
3172
|
+
<div class="explanation-step-phase">
|
|
3173
|
+
<div class="explanation-step-num">1</div>
|
|
3174
|
+
<div class="explanation-step-name">Scope Confirmation</div>
|
|
3175
|
+
</div>
|
|
3176
|
+
<div class="explanation-step-desc">
|
|
3177
|
+
Claude confirms understanding of what we're building before doing anything.
|
|
3178
|
+
</div>
|
|
3179
|
+
<div class="explanation-step-why">
|
|
3180
|
+
Prevents building the wrong thing. Loop back if unclear.
|
|
3181
|
+
</div>
|
|
3182
|
+
</div>
|
|
3183
|
+
|
|
3184
|
+
<div class="explanation-step" data-phase="2">
|
|
3185
|
+
<div class="explanation-step-phase">
|
|
3186
|
+
<div class="explanation-step-num">2</div>
|
|
3187
|
+
<div class="explanation-step-name">Initial Research</div>
|
|
3188
|
+
</div>
|
|
3189
|
+
<div class="explanation-step-desc">
|
|
3190
|
+
Claude researches REAL documentation using Context7 and WebSearch. No training data assumptions!
|
|
3191
|
+
</div>
|
|
3192
|
+
<div class="explanation-step-why">
|
|
3193
|
+
Questions will be generated FROM these findings - not generic templates.
|
|
3194
|
+
</div>
|
|
3195
|
+
</div>
|
|
3196
|
+
|
|
3197
|
+
<div class="explanation-step" data-phase="3">
|
|
3198
|
+
<div class="explanation-step-phase">
|
|
3199
|
+
<div class="explanation-step-num">3</div>
|
|
3200
|
+
<div class="explanation-step-name">Interview</div>
|
|
3201
|
+
</div>
|
|
3202
|
+
<div class="explanation-step-desc">
|
|
3203
|
+
Claude asks structured questions with options derived from research. User selects, answers tracked.
|
|
3204
|
+
</div>
|
|
3205
|
+
<div class="explanation-step-why">
|
|
3206
|
+
Every decision is recorded and injected during implementation.
|
|
3207
|
+
</div>
|
|
3208
|
+
</div>
|
|
3209
|
+
|
|
3210
|
+
<div class="explanation-step" data-phase="4">
|
|
3211
|
+
<div class="explanation-step-phase">
|
|
3212
|
+
<div class="explanation-step-num">4</div>
|
|
3213
|
+
<div class="explanation-step-name">Deep Research</div>
|
|
3214
|
+
</div>
|
|
3215
|
+
<div class="explanation-step-desc">
|
|
3216
|
+
Based on interview answers, Claude proposes targeted follow-up searches.
|
|
3217
|
+
</div>
|
|
3218
|
+
<div class="explanation-step-why">
|
|
3219
|
+
Adaptive research - not shotgun. Only what's relevant to YOUR choices.
|
|
3220
|
+
</div>
|
|
3221
|
+
</div>
|
|
3222
|
+
|
|
3223
|
+
<div class="explanation-step" data-phase="5">
|
|
3224
|
+
<div class="explanation-step-phase">
|
|
3225
|
+
<div class="explanation-step-num">5</div>
|
|
3226
|
+
<div class="explanation-step-name">Schema Design</div>
|
|
3227
|
+
</div>
|
|
3228
|
+
<div class="explanation-step-desc">
|
|
3229
|
+
Zod schemas created based on research + interview decisions.
|
|
3230
|
+
</div>
|
|
3231
|
+
<div class="explanation-step-why">
|
|
3232
|
+
Types before code. Schema drives tests and implementation.
|
|
3233
|
+
</div>
|
|
3234
|
+
</div>
|
|
3235
|
+
|
|
3236
|
+
<div class="explanation-step" data-phase="6">
|
|
3237
|
+
<div class="explanation-step-phase">
|
|
3238
|
+
<div class="explanation-step-num">6</div>
|
|
3239
|
+
<div class="explanation-step-name">Environment</div>
|
|
3240
|
+
</div>
|
|
3241
|
+
<div class="explanation-step-desc">
|
|
3242
|
+
Verify API keys exist and are configured correctly.
|
|
3243
|
+
</div>
|
|
3244
|
+
<div class="explanation-step-why">
|
|
3245
|
+
Catches config issues BEFORE writing code.
|
|
3246
|
+
</div>
|
|
3247
|
+
</div>
|
|
3248
|
+
|
|
3249
|
+
<div class="explanation-step" data-phase="7">
|
|
3250
|
+
<div class="explanation-step-phase">
|
|
3251
|
+
<div class="explanation-step-num">7</div>
|
|
3252
|
+
<div class="explanation-step-name">TDD Red</div>
|
|
3253
|
+
</div>
|
|
3254
|
+
<div class="explanation-step-desc">
|
|
3255
|
+
Write failing tests first. Define success BEFORE implementation.
|
|
3256
|
+
</div>
|
|
3257
|
+
<div class="explanation-step-why">
|
|
3258
|
+
No implementation until tests exist. Enforced by hooks.
|
|
3259
|
+
</div>
|
|
3260
|
+
</div>
|
|
3261
|
+
|
|
3262
|
+
<div class="explanation-step" data-phase="8">
|
|
3263
|
+
<div class="explanation-step-phase">
|
|
3264
|
+
<div class="explanation-step-num">8</div>
|
|
3265
|
+
<div class="explanation-step-name">TDD Green</div>
|
|
3266
|
+
</div>
|
|
3267
|
+
<div class="explanation-step-desc">
|
|
3268
|
+
Write minimal code to pass tests. Interview decisions injected by hook!
|
|
3269
|
+
</div>
|
|
3270
|
+
<div class="explanation-step-why">
|
|
3271
|
+
Hook reminds Claude of YOUR decisions on every write.
|
|
3272
|
+
</div>
|
|
3273
|
+
</div>
|
|
3274
|
+
|
|
3275
|
+
<div class="explanation-step" data-phase="9">
|
|
3276
|
+
<div class="explanation-step-phase">
|
|
3277
|
+
<div class="explanation-step-num">9</div>
|
|
3278
|
+
<div class="explanation-step-name">Verify</div>
|
|
3279
|
+
</div>
|
|
3280
|
+
<div class="explanation-step-desc">
|
|
3281
|
+
Re-read original docs, compare to implementation. Find gaps!
|
|
3282
|
+
</div>
|
|
3283
|
+
<div class="explanation-step-why">
|
|
3284
|
+
Catches memory-based errors. Loops back if gaps found.
|
|
3285
|
+
</div>
|
|
3286
|
+
</div>
|
|
3287
|
+
|
|
3288
|
+
<div class="explanation-step" data-phase="10">
|
|
3289
|
+
<div class="explanation-step-phase">
|
|
3290
|
+
<div class="explanation-step-num">10</div>
|
|
3291
|
+
<div class="explanation-step-name">Refactor</div>
|
|
3292
|
+
</div>
|
|
3293
|
+
<div class="explanation-step-desc">
|
|
3294
|
+
Clean up code while tests stay green.
|
|
3295
|
+
</div>
|
|
3296
|
+
<div class="explanation-step-why">
|
|
3297
|
+
Safe improvements - tests prove nothing broke.
|
|
3298
|
+
</div>
|
|
3299
|
+
</div>
|
|
3300
|
+
|
|
3301
|
+
<div class="explanation-step" data-phase="11">
|
|
3302
|
+
<div class="explanation-step-phase">
|
|
3303
|
+
<div class="explanation-step-num">11</div>
|
|
3304
|
+
<div class="explanation-step-name">Documentation</div>
|
|
3305
|
+
</div>
|
|
3306
|
+
<div class="explanation-step-desc">
|
|
3307
|
+
Update OpenAPI spec, test manifest, research cache.
|
|
3308
|
+
</div>
|
|
3309
|
+
<div class="explanation-step-why">
|
|
3310
|
+
No "complete" without docs. Stop hook enforces this.
|
|
3311
|
+
</div>
|
|
3312
|
+
</div>
|
|
3313
|
+
|
|
3314
|
+
<div class="explanation-step" data-phase="12">
|
|
3315
|
+
<div class="explanation-step-phase">
|
|
3316
|
+
<div class="explanation-step-num">12</div>
|
|
3317
|
+
<div class="explanation-step-name">Completion</div>
|
|
3318
|
+
</div>
|
|
3319
|
+
<div class="explanation-step-desc">
|
|
3320
|
+
All phases verified. API is ready for use!
|
|
3321
|
+
</div>
|
|
3322
|
+
<div class="explanation-step-why">
|
|
3323
|
+
api-workflow-check.py blocks completion if anything incomplete.
|
|
3324
|
+
</div>
|
|
3325
|
+
</div>
|
|
2314
3326
|
</div>
|
|
2315
3327
|
</div>
|
|
2316
3328
|
</div>
|
|
@@ -2326,12 +3338,34 @@
|
|
|
2326
3338
|
<h3>The State File</h3>
|
|
2327
3339
|
|
|
2328
3340
|
<div class="explanation">
|
|
2329
|
-
<div class="explanation-title">
|
|
3341
|
+
<div class="explanation-title">Why This Matters</div>
|
|
2330
3342
|
<p>
|
|
2331
3343
|
All progress is saved to <code>.claude/api-dev-state.json</code>.
|
|
2332
|
-
This
|
|
2333
|
-
during implementation to ensure consistency.
|
|
3344
|
+
This solves three critical problems:
|
|
2334
3345
|
</p>
|
|
3346
|
+
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 16px; margin-top: 16px;">
|
|
3347
|
+
<div style="background: rgba(249, 115, 22, 0.1); border-left: 3px solid var(--claude-orange); padding: 12px; border-radius: 4px;">
|
|
3348
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
3349
|
+
<span style="font-size: 1.5rem;">🎯</span>
|
|
3350
|
+
<strong style="color: var(--claude-orange);">Re-Grounding</strong>
|
|
3351
|
+
</div>
|
|
3352
|
+
<p style="margin: 8px 0 0; font-size: 0.85rem; color: #aaa;">Hooks inject your decisions into every file write, preventing the AI from "forgetting" what you asked for.</p>
|
|
3353
|
+
</div>
|
|
3354
|
+
<div style="background: rgba(249, 115, 22, 0.1); border-left: 3px solid var(--claude-orange); padding: 12px; border-radius: 4px;">
|
|
3355
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
3356
|
+
<span style="font-size: 1.5rem;">💾</span>
|
|
3357
|
+
<strong style="color: var(--claude-orange);">Session Persistence</strong>
|
|
3358
|
+
</div>
|
|
3359
|
+
<p style="margin: 8px 0 0; font-size: 0.85rem; color: #aaa;">Come back tomorrow and pick up where you left off. The state file remembers research, interview answers, and progress.</p>
|
|
3360
|
+
</div>
|
|
3361
|
+
<div style="background: rgba(249, 115, 22, 0.1); border-left: 3px solid var(--claude-orange); padding: 12px; border-radius: 4px;">
|
|
3362
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
3363
|
+
<span style="font-size: 1.5rem;">🔄</span>
|
|
3364
|
+
<strong style="color: var(--claude-orange);">Loop-Back Tracking</strong>
|
|
3365
|
+
</div>
|
|
3366
|
+
<p style="margin: 8px 0 0; font-size: 0.85rem; color: #aaa;">When verification finds gaps, we know exactly which phase to return to. No lost context.</p>
|
|
3367
|
+
</div>
|
|
3368
|
+
</div>
|
|
2335
3369
|
</div>
|
|
2336
3370
|
|
|
2337
3371
|
<div class="json-viewer">
|
|
@@ -2393,39 +3427,39 @@
|
|
|
2393
3427
|
|
|
2394
3428
|
<div class="install-flow">
|
|
2395
3429
|
<div class="install-step">
|
|
2396
|
-
<div class="install-icon"
|
|
3430
|
+
<div class="install-icon">📝</div>
|
|
2397
3431
|
<div class="install-content">
|
|
2398
|
-
<div class="install-from">Slash Commands</div>
|
|
3432
|
+
<div class="install-from">21 Slash Commands</div>
|
|
2399
3433
|
<div class="install-note">/api-create, /api-interview, /red, /green, /refactor...</div>
|
|
2400
3434
|
</div>
|
|
2401
|
-
<div class="install-arrow"
|
|
3435
|
+
<div class="install-arrow">→</div>
|
|
2402
3436
|
<div class="install-to">.claude/commands/</div>
|
|
2403
3437
|
</div>
|
|
2404
3438
|
<div class="install-step">
|
|
2405
|
-
<div class="install-icon"
|
|
3439
|
+
<div class="install-icon">🐍</div>
|
|
2406
3440
|
<div class="install-content">
|
|
2407
|
-
<div class="install-from">Python Hooks</div>
|
|
3441
|
+
<div class="install-from">5 Python Hooks</div>
|
|
2408
3442
|
<div class="install-note">enforce-research, enforce-interview, verify-implementation...</div>
|
|
2409
3443
|
</div>
|
|
2410
|
-
<div class="install-arrow"
|
|
3444
|
+
<div class="install-arrow">→</div>
|
|
2411
3445
|
<div class="install-to">.claude/hooks/</div>
|
|
2412
3446
|
</div>
|
|
2413
3447
|
<div class="install-step">
|
|
2414
|
-
<div class="install-icon"
|
|
3448
|
+
<div class="install-icon">⚙️</div>
|
|
2415
3449
|
<div class="install-content">
|
|
2416
3450
|
<div class="install-from">Settings Configuration</div>
|
|
2417
3451
|
<div class="install-note">Hook registration, permissions, matchers</div>
|
|
2418
3452
|
</div>
|
|
2419
|
-
<div class="install-arrow"
|
|
3453
|
+
<div class="install-arrow">→</div>
|
|
2420
3454
|
<div class="install-to">.claude/settings.json</div>
|
|
2421
3455
|
</div>
|
|
2422
3456
|
<div class="install-step">
|
|
2423
|
-
<div class="install-icon"
|
|
3457
|
+
<div class="install-icon">🔌</div>
|
|
2424
3458
|
<div class="install-content">
|
|
2425
3459
|
<div class="install-from">MCP Servers</div>
|
|
2426
3460
|
<div class="install-note">Context7 (live docs), GitHub (issues/PRs)</div>
|
|
2427
3461
|
</div>
|
|
2428
|
-
<div class="install-arrow"
|
|
3462
|
+
<div class="install-arrow">→</div>
|
|
2429
3463
|
<div class="install-to">claude mcp add</div>
|
|
2430
3464
|
</div>
|
|
2431
3465
|
</div>
|
|
@@ -2466,14 +3500,41 @@
|
|
|
2466
3500
|
</div>
|
|
2467
3501
|
|
|
2468
3502
|
<div class="credit-links">
|
|
2469
|
-
<a href="https://github.com/hustle-together/api-dev-tools" class="credit-link" target="_blank">
|
|
2470
|
-
|
|
3503
|
+
<a href="https://github.com/hustle-together/api-dev-tools" class="credit-link" target="_blank">
|
|
3504
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" style="margin-right: 8px;">
|
|
3505
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
|
|
3506
|
+
</svg>
|
|
3507
|
+
VIEW ON GITHUB
|
|
3508
|
+
</a>
|
|
3509
|
+
<a href="https://www.npmjs.com/package/@hustle-together/api-dev-tools" class="credit-link" target="_blank">
|
|
3510
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" style="margin-right: 8px;">
|
|
3511
|
+
<path d="M0 7.334v8h6.666v1.332H12v-1.332h12v-8H0zm6.666 6.664H5.334v-4H3.999v4H1.335V8.667h5.331v5.331zm4 0v1.336H8.001V8.667h5.334v5.332h-2.669v-.001zm12.001 0h-1.33v-4h-1.336v4h-1.335v-4h-1.33v4h-2.671V8.667h8.002v5.331zM10.665 10H12v2.667h-1.335V10z"/>
|
|
3512
|
+
</svg>
|
|
3513
|
+
INSTALL FROM NPM
|
|
3514
|
+
</a>
|
|
3515
|
+
</div>
|
|
3516
|
+
|
|
3517
|
+
<!-- Attribution -->
|
|
3518
|
+
<div style="margin-top: 32px; padding: 20px; background: rgba(0,0,0,0.2); border-radius: 8px; text-align: center;">
|
|
3519
|
+
<p style="color: #888; font-size: 0.85rem; margin-bottom: 16px;">Built on the shoulders of giants:</p>
|
|
3520
|
+
<div style="display: flex; justify-content: center; align-items: center; gap: 32px; flex-wrap: wrap;">
|
|
3521
|
+
<a href="https://github.com/wbern/claude-instructions" target="_blank" style="display: flex; align-items: center; gap: 8px; color: var(--claude-orange); text-decoration: none;">
|
|
3522
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
|
|
3523
|
+
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/>
|
|
3524
|
+
</svg>
|
|
3525
|
+
<span>@wbern/claude-instructions</span>
|
|
3526
|
+
</a>
|
|
3527
|
+
<a href="https://context7.com" target="_blank" style="display: flex; align-items: center; gap: 8px; color: var(--claude-orange); text-decoration: none;">
|
|
3528
|
+
<span style="font-weight: bold; font-size: 1.1rem;">C7</span>
|
|
3529
|
+
<span>Context7</span>
|
|
3530
|
+
</a>
|
|
3531
|
+
</div>
|
|
2471
3532
|
</div>
|
|
2472
3533
|
|
|
2473
3534
|
<div class="made-with">
|
|
2474
3535
|
<p>Made for developers who want AI assistants<br>that actually follow instructions.</p>
|
|
2475
3536
|
<p style="margin-top: 20px; color: var(--dark-grey);">
|
|
2476
|
-
|
|
3537
|
+
v3.0.0 | MIT License<br>
|
|
2477
3538
|
"Interview first, test first, document always"
|
|
2478
3539
|
</p>
|
|
2479
3540
|
</div>
|
|
@@ -2487,7 +3548,7 @@
|
|
|
2487
3548
|
// ============================================
|
|
2488
3549
|
// CONFIGURATION
|
|
2489
3550
|
// ============================================
|
|
2490
|
-
const sections = ['intro', 'problems', 'solution', 'hooks', 'phases', '
|
|
3551
|
+
const sections = ['intro', 'problems', 'solution', 'hooks', 'phases', 'demo', 'state', 'install', 'credits'];
|
|
2491
3552
|
let isPlaying = false;
|
|
2492
3553
|
|
|
2493
3554
|
// ============================================
|
|
@@ -2689,51 +3750,162 @@
|
|
|
2689
3750
|
phasesTL.to('.phase-connector', { opacity: 1, duration: 0.3 }, '-=0.8');
|
|
2690
3751
|
|
|
2691
3752
|
// ============================================
|
|
2692
|
-
// SECTION 6:
|
|
3753
|
+
// SECTION 6: CLAUDE CODE TERMINAL ANIMATION
|
|
3754
|
+
// Animates both terminal messages and explanation panel
|
|
2693
3755
|
// ============================================
|
|
2694
|
-
const
|
|
3756
|
+
const demoTL = gsap.timeline({
|
|
2695
3757
|
scrollTrigger: {
|
|
2696
|
-
trigger: '#
|
|
3758
|
+
trigger: '#demo',
|
|
2697
3759
|
start: 'top 60%',
|
|
2698
3760
|
toggleActions: 'play none none reverse'
|
|
2699
3761
|
}
|
|
2700
3762
|
});
|
|
2701
3763
|
|
|
2702
|
-
|
|
3764
|
+
demoTL.to('#demo .explanation', { opacity: 1, duration: 0.5 });
|
|
2703
3765
|
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
3766
|
+
// Get all Claude Code messages and explanation steps
|
|
3767
|
+
const claudeMessages = document.querySelectorAll('#claudeBody .claude-msg');
|
|
3768
|
+
const explanationSteps = document.querySelectorAll('#explanationPanel .explanation-step');
|
|
3769
|
+
const asciiStartup = document.getElementById('asciiStartup');
|
|
3770
|
+
const promptLine = document.getElementById('promptLine');
|
|
3771
|
+
const promptInput = document.getElementById('promptInput');
|
|
3772
|
+
const loadingDots = document.getElementById('loadingDots');
|
|
3773
|
+
|
|
3774
|
+
// Track current phase for syncing explanation panel
|
|
3775
|
+
let currentDemoPhase = 0;
|
|
3776
|
+
|
|
3777
|
+
// Function to update explanation panel
|
|
3778
|
+
function updateExplanationPanel(phase) {
|
|
3779
|
+
explanationSteps.forEach(step => {
|
|
3780
|
+
const stepPhase = parseInt(step.dataset.phase);
|
|
3781
|
+
if (stepPhase === phase) {
|
|
3782
|
+
step.classList.add('active');
|
|
3783
|
+
// Scroll the explanation panel to show the active step
|
|
3784
|
+
step.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
3785
|
+
} else if (stepPhase < phase) {
|
|
3786
|
+
// Keep previous phases visible but not active
|
|
3787
|
+
step.classList.remove('active');
|
|
3788
|
+
step.style.opacity = '0.7';
|
|
3789
|
+
} else {
|
|
3790
|
+
step.classList.remove('active');
|
|
3791
|
+
step.style.opacity = '0.5';
|
|
3792
|
+
}
|
|
3793
|
+
});
|
|
3794
|
+
}
|
|
2712
3795
|
|
|
2713
|
-
//
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
3796
|
+
// Typing effect for prompt
|
|
3797
|
+
function typeText(element, text, speed = 50) {
|
|
3798
|
+
return new Promise(resolve => {
|
|
3799
|
+
let i = 0;
|
|
3800
|
+
element.textContent = '';
|
|
3801
|
+
const interval = setInterval(() => {
|
|
3802
|
+
if (i < text.length) {
|
|
3803
|
+
element.textContent += text.charAt(i);
|
|
3804
|
+
i++;
|
|
3805
|
+
} else {
|
|
3806
|
+
clearInterval(interval);
|
|
3807
|
+
resolve();
|
|
3808
|
+
}
|
|
3809
|
+
}, speed);
|
|
3810
|
+
});
|
|
3811
|
+
}
|
|
3812
|
+
|
|
3813
|
+
// Show loading dots animation
|
|
3814
|
+
function showLoadingDots(show = true) {
|
|
3815
|
+
loadingDots.style.display = show ? 'inline-flex' : 'none';
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3818
|
+
// Step 1: Show ASCII startup screen (2 seconds)
|
|
3819
|
+
demoTL.to(asciiStartup, {
|
|
3820
|
+
opacity: 1,
|
|
3821
|
+
duration: 0.5,
|
|
3822
|
+
onStart: () => {
|
|
3823
|
+
asciiStartup.style.display = 'block';
|
|
3824
|
+
asciiStartup.classList.add('visible');
|
|
2721
3825
|
}
|
|
2722
|
-
});
|
|
3826
|
+
}, 0.5);
|
|
3827
|
+
|
|
3828
|
+
// Step 2: Show prompt line and type command (after ASCII)
|
|
3829
|
+
demoTL.to(promptLine, {
|
|
3830
|
+
opacity: 1,
|
|
3831
|
+
duration: 0.3,
|
|
3832
|
+
onStart: () => {
|
|
3833
|
+
promptLine.classList.add('visible');
|
|
3834
|
+
},
|
|
3835
|
+
onComplete: () => {
|
|
3836
|
+
// Type the command
|
|
3837
|
+
typeText(promptInput, '/api-create brandfetch', 60);
|
|
3838
|
+
}
|
|
3839
|
+
}, 2.5);
|
|
3840
|
+
|
|
3841
|
+
// Step 3: Hide ASCII and prompt, show messages (after typing)
|
|
3842
|
+
demoTL.to([asciiStartup, promptLine], {
|
|
3843
|
+
opacity: 0,
|
|
3844
|
+
duration: 0.3,
|
|
3845
|
+
onComplete: () => {
|
|
3846
|
+
asciiStartup.style.display = 'none';
|
|
3847
|
+
promptLine.style.display = 'none';
|
|
3848
|
+
}
|
|
3849
|
+
}, 5.5);
|
|
2723
3850
|
|
|
2724
|
-
|
|
3851
|
+
// Get terminal body for scrolling
|
|
3852
|
+
const claudeBody = document.getElementById('claudeBody');
|
|
3853
|
+
|
|
3854
|
+
// Animate each message SEQUENTIALLY using relative positioning
|
|
3855
|
+
// Each message waits 1.5s after the PREVIOUS one completes
|
|
3856
|
+
claudeMessages.forEach((msg, i) => {
|
|
3857
|
+
const phase = parseInt(msg.dataset.phase);
|
|
3858
|
+
|
|
3859
|
+
// First message starts after ASCII animation (at 6s mark)
|
|
3860
|
+
// Subsequent messages use +=1.5 (1.5s after previous completes)
|
|
3861
|
+
const position = i === 0 ? 6.0 : '+=1.0'; // 1 second gap between messages
|
|
3862
|
+
|
|
3863
|
+
// Show loading dots before each message (except first)
|
|
3864
|
+
if (i > 0) {
|
|
3865
|
+
// Add loading dots that appear for 0.8s before message
|
|
3866
|
+
demoTL.call(() => {
|
|
3867
|
+
showLoadingDots(true);
|
|
3868
|
+
}, null, '+=0.2'); // Small gap after previous message
|
|
3869
|
+
|
|
3870
|
+
demoTL.to({}, { duration: 0.8 }); // Hold for loading dots
|
|
3871
|
+
|
|
3872
|
+
demoTL.call(() => {
|
|
3873
|
+
showLoadingDots(false);
|
|
3874
|
+
});
|
|
3875
|
+
}
|
|
2725
3876
|
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
terminalElements.forEach((el, i) => {
|
|
2729
|
-
const delay = i * 0.25 + 0.5;
|
|
2730
|
-
demoTL.to(el, {
|
|
3877
|
+
// Add message to timeline
|
|
3878
|
+
demoTL.to(msg, {
|
|
2731
3879
|
opacity: 1,
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
3880
|
+
y: 0,
|
|
3881
|
+
duration: 0.5,
|
|
3882
|
+
ease: 'power2.out',
|
|
3883
|
+
onStart: () => {
|
|
3884
|
+
msg.classList.add('visible');
|
|
3885
|
+
// Update explanation panel when phase changes
|
|
3886
|
+
if (phase !== currentDemoPhase) {
|
|
3887
|
+
currentDemoPhase = phase;
|
|
3888
|
+
updateExplanationPanel(phase);
|
|
3889
|
+
}
|
|
3890
|
+
},
|
|
3891
|
+
onComplete: () => {
|
|
3892
|
+
// Scroll terminal to show new message after it appears
|
|
3893
|
+
if (claudeBody) {
|
|
3894
|
+
const msgTop = msg.offsetTop;
|
|
3895
|
+
const bodyHeight = claudeBody.clientHeight;
|
|
3896
|
+
const scrollTarget = Math.max(0, msgTop - bodyHeight + msg.offsetHeight + 40);
|
|
3897
|
+
claudeBody.scrollTo({
|
|
3898
|
+
top: scrollTarget,
|
|
3899
|
+
behavior: 'smooth'
|
|
3900
|
+
});
|
|
3901
|
+
}
|
|
3902
|
+
}
|
|
3903
|
+
}, i === 0 ? position : '<'); // '<' means at same time as loading dots hide
|
|
2735
3904
|
});
|
|
2736
3905
|
|
|
3906
|
+
// Initialize explanation panel (show first step as active)
|
|
3907
|
+
updateExplanationPanel(0);
|
|
3908
|
+
|
|
2737
3909
|
// ============================================
|
|
2738
3910
|
// SECTION 8: STATE FILE ANIMATION
|
|
2739
3911
|
// ============================================
|
|
@@ -2804,6 +3976,7 @@
|
|
|
2804
3976
|
const audioCurrentTime = document.getElementById('audioCurrentTime');
|
|
2805
3977
|
const audioTotalTime = document.getElementById('audioTotalTime');
|
|
2806
3978
|
const audioSectionMarkers = document.getElementById('audioSectionMarkers');
|
|
3979
|
+
const chapterButtons = document.getElementById('chapterButtons');
|
|
2807
3980
|
|
|
2808
3981
|
let audioEnabled = true; // Audio on by default
|
|
2809
3982
|
let timingData = null;
|
|
@@ -2819,11 +3992,15 @@
|
|
|
2819
3992
|
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
2820
3993
|
}
|
|
2821
3994
|
|
|
2822
|
-
//
|
|
2823
|
-
|
|
2824
|
-
|
|
3995
|
+
// Current timing URL (can be changed when voice changes)
|
|
3996
|
+
let currentTimingUrl = 'audio/narration-adam-timing.json';
|
|
3997
|
+
|
|
3998
|
+
// Load timing data (pass URL to force reload with different voice)
|
|
3999
|
+
async function loadTimingData(url = null, forceReload = false) {
|
|
4000
|
+
if (url) currentTimingUrl = url;
|
|
4001
|
+
if (timingData && !forceReload) return timingData;
|
|
2825
4002
|
try {
|
|
2826
|
-
const response = await fetch(
|
|
4003
|
+
const response = await fetch(currentTimingUrl);
|
|
2827
4004
|
if (!response.ok) throw new Error('Failed to load timing data');
|
|
2828
4005
|
timingData = await response.json();
|
|
2829
4006
|
|
|
@@ -2845,6 +4022,116 @@
|
|
|
2845
4022
|
});
|
|
2846
4023
|
}
|
|
2847
4024
|
|
|
4025
|
+
// Create chapter buttons for navigation
|
|
4026
|
+
if (chapterButtons && timingData.sections) {
|
|
4027
|
+
chapterButtons.innerHTML = '';
|
|
4028
|
+
timingData.sections.forEach((section, i) => {
|
|
4029
|
+
const btn = document.createElement('button');
|
|
4030
|
+
btn.className = 'chapter-btn';
|
|
4031
|
+
btn.textContent = section.id.toUpperCase();
|
|
4032
|
+
btn.dataset.timestamp = section.timestamp;
|
|
4033
|
+
btn.dataset.sectionId = section.id;
|
|
4034
|
+
btn.dataset.index = i;
|
|
4035
|
+
|
|
4036
|
+
// Click handler: seek audio to timestamp + scroll to section
|
|
4037
|
+
btn.addEventListener('click', (e) => {
|
|
4038
|
+
e.stopPropagation(); // Prevent bubbling to progress bar
|
|
4039
|
+
|
|
4040
|
+
const seekTime = section.timestamp;
|
|
4041
|
+
console.log(`Chapter click: seeking to ${section.id} at ${seekTime.toFixed(1)}s`);
|
|
4042
|
+
|
|
4043
|
+
// Function to perform the seek after audio is ready
|
|
4044
|
+
const doSeek = () => {
|
|
4045
|
+
console.log(`Performing seek to ${seekTime.toFixed(1)}s, audio readyState: ${narrationAudio.readyState}`);
|
|
4046
|
+
narrationAudio.currentTime = seekTime;
|
|
4047
|
+
console.log(`After setting currentTime: ${narrationAudio.currentTime.toFixed(1)}s`);
|
|
4048
|
+
};
|
|
4049
|
+
|
|
4050
|
+
// If not playing, start first then seek
|
|
4051
|
+
if (!isPlaying) {
|
|
4052
|
+
isPlaying = true;
|
|
4053
|
+
playBtn.classList.add('active');
|
|
4054
|
+
playBtn.textContent = '⏸ STOP';
|
|
4055
|
+
|
|
4056
|
+
if (window.introLightingTL) {
|
|
4057
|
+
window.introLightingTL.kill();
|
|
4058
|
+
window.introLightingTL = null;
|
|
4059
|
+
document.querySelectorAll('#introFlow .phase-node').forEach(n => n.classList.remove('active'));
|
|
4060
|
+
document.querySelectorAll('#introFlow .phase-connector-arrow').forEach(a => a.classList.remove('active'));
|
|
4061
|
+
}
|
|
4062
|
+
|
|
4063
|
+
audioProgressContainer.style.display = 'block';
|
|
4064
|
+
|
|
4065
|
+
// Wait for audio to be ready enough to seek
|
|
4066
|
+
if (narrationAudio.readyState >= 1) {
|
|
4067
|
+
doSeek();
|
|
4068
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
4069
|
+
} else {
|
|
4070
|
+
// Audio not ready yet, wait for loadedmetadata
|
|
4071
|
+
narrationAudio.addEventListener('loadedmetadata', function onLoaded() {
|
|
4072
|
+
narrationAudio.removeEventListener('loadedmetadata', onLoaded);
|
|
4073
|
+
doSeek();
|
|
4074
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
4075
|
+
});
|
|
4076
|
+
narrationAudio.load(); // Trigger loading
|
|
4077
|
+
}
|
|
4078
|
+
} else {
|
|
4079
|
+
// Already playing - pause, seek, then play
|
|
4080
|
+
narrationAudio.pause();
|
|
4081
|
+
|
|
4082
|
+
const performSeekAndPlay = () => {
|
|
4083
|
+
doSeek();
|
|
4084
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
4085
|
+
};
|
|
4086
|
+
|
|
4087
|
+
// Use timingData.duration as indicator that we have valid timing for this voice
|
|
4088
|
+
// Also check readyState >= 2 (HAVE_CURRENT_DATA) for reliable seeking
|
|
4089
|
+
if (timingData && timingData.duration > 0 && narrationAudio.readyState >= 2) {
|
|
4090
|
+
performSeekAndPlay();
|
|
4091
|
+
} else {
|
|
4092
|
+
// Audio not ready (e.g. after voice switch), wait for canplay
|
|
4093
|
+
console.log('Audio not ready for seek, waiting for canplay...');
|
|
4094
|
+
narrationAudio.addEventListener('canplay', function onReady() {
|
|
4095
|
+
narrationAudio.removeEventListener('canplay', onReady);
|
|
4096
|
+
performSeekAndPlay();
|
|
4097
|
+
}, { once: true });
|
|
4098
|
+
// Also try loadedmetadata as backup
|
|
4099
|
+
narrationAudio.addEventListener('loadedmetadata', function onMeta() {
|
|
4100
|
+
narrationAudio.removeEventListener('loadedmetadata', onMeta);
|
|
4101
|
+
if (narrationAudio.readyState >= 2) {
|
|
4102
|
+
performSeekAndPlay();
|
|
4103
|
+
}
|
|
4104
|
+
}, { once: true });
|
|
4105
|
+
narrationAudio.load();
|
|
4106
|
+
}
|
|
4107
|
+
}
|
|
4108
|
+
|
|
4109
|
+
// Update highlight tracking for new position
|
|
4110
|
+
highlightIndex = 0;
|
|
4111
|
+
if (timingData && timingData.highlights) {
|
|
4112
|
+
for (let j = 0; j < timingData.highlights.length; j++) {
|
|
4113
|
+
if (timingData.highlights[j].timestamp <= seekTime) {
|
|
4114
|
+
highlightIndex = j;
|
|
4115
|
+
} else {
|
|
4116
|
+
break;
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
// Scroll to section
|
|
4122
|
+
const sectionEl = document.getElementById(section.id);
|
|
4123
|
+
if (sectionEl) {
|
|
4124
|
+
sectionEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
4125
|
+
}
|
|
4126
|
+
|
|
4127
|
+
// Update active state
|
|
4128
|
+
updateActiveChapter(i);
|
|
4129
|
+
});
|
|
4130
|
+
|
|
4131
|
+
chapterButtons.appendChild(btn);
|
|
4132
|
+
});
|
|
4133
|
+
}
|
|
4134
|
+
|
|
2848
4135
|
console.log('Timing data loaded:', timingData.sections.length, 'sections,', timingData.highlights.length, 'highlights');
|
|
2849
4136
|
return timingData;
|
|
2850
4137
|
} catch (error) {
|
|
@@ -2872,8 +4159,8 @@
|
|
|
2872
4159
|
|
|
2873
4160
|
if (isContainer) {
|
|
2874
4161
|
gsap.fromTo(el,
|
|
2875
|
-
{ boxShadow: '0
|
|
2876
|
-
{ boxShadow: '0
|
|
4162
|
+
{ boxShadow: '0 4px 6px rgba(0,0,0,0.05)' },
|
|
4163
|
+
{ boxShadow: '0 8px 25px rgba(0,0,0,0.15)', duration: 0.4, ease: 'power2.out' }
|
|
2877
4164
|
);
|
|
2878
4165
|
}
|
|
2879
4166
|
}
|
|
@@ -2891,6 +4178,29 @@
|
|
|
2891
4178
|
}
|
|
2892
4179
|
}
|
|
2893
4180
|
|
|
4181
|
+
// Update active chapter button based on index
|
|
4182
|
+
function updateActiveChapter(index) {
|
|
4183
|
+
if (!chapterButtons) return;
|
|
4184
|
+
const buttons = chapterButtons.querySelectorAll('.chapter-btn');
|
|
4185
|
+
buttons.forEach((btn, i) => {
|
|
4186
|
+
btn.classList.toggle('active', i === index);
|
|
4187
|
+
});
|
|
4188
|
+
}
|
|
4189
|
+
|
|
4190
|
+
// Find current section index based on audio time
|
|
4191
|
+
function getCurrentSectionIndex(currentTime) {
|
|
4192
|
+
if (!timingData || !timingData.sections) return 0;
|
|
4193
|
+
let idx = 0;
|
|
4194
|
+
for (let i = 0; i < timingData.sections.length; i++) {
|
|
4195
|
+
if (currentTime >= timingData.sections[i].timestamp) {
|
|
4196
|
+
idx = i;
|
|
4197
|
+
} else {
|
|
4198
|
+
break;
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
return idx;
|
|
4202
|
+
}
|
|
4203
|
+
|
|
2894
4204
|
// Handle audio time update (syncs highlights with narration)
|
|
2895
4205
|
function onAudioTimeUpdate() {
|
|
2896
4206
|
if (!timingData || !audioEnabled) return;
|
|
@@ -2906,6 +4216,13 @@
|
|
|
2906
4216
|
audioCurrentTime.textContent = formatTime(currentTime);
|
|
2907
4217
|
}
|
|
2908
4218
|
|
|
4219
|
+
// Update active chapter button
|
|
4220
|
+
const sectionIdx = getCurrentSectionIndex(currentTime);
|
|
4221
|
+
if (sectionIdx !== currentSectionIdx) {
|
|
4222
|
+
currentSectionIdx = sectionIdx;
|
|
4223
|
+
updateActiveChapter(sectionIdx);
|
|
4224
|
+
}
|
|
4225
|
+
|
|
2909
4226
|
// Check for highlight triggers
|
|
2910
4227
|
for (let i = highlightIndex; i < timingData.highlights.length; i++) {
|
|
2911
4228
|
const highlight = timingData.highlights[i];
|
|
@@ -2922,24 +4239,82 @@
|
|
|
2922
4239
|
|
|
2923
4240
|
// Handle seek on progress bar click
|
|
2924
4241
|
function onProgressBarClick(e) {
|
|
2925
|
-
|
|
4242
|
+
e.stopPropagation(); // Prevent event bubbling
|
|
4243
|
+
e.preventDefault();
|
|
4244
|
+
|
|
4245
|
+
// Must have timing data loaded to seek
|
|
4246
|
+
if (!timingData || !timingData.duration) {
|
|
4247
|
+
console.warn('Cannot seek: timing data not loaded');
|
|
4248
|
+
return;
|
|
4249
|
+
}
|
|
2926
4250
|
|
|
4251
|
+
// Calculate seek position from click location
|
|
2927
4252
|
const rect = audioProgressBar.getBoundingClientRect();
|
|
2928
4253
|
const clickX = e.clientX - rect.left;
|
|
2929
|
-
const percentage = clickX / rect.width;
|
|
4254
|
+
const percentage = Math.max(0, Math.min(1, clickX / rect.width));
|
|
2930
4255
|
const seekTime = percentage * timingData.duration;
|
|
2931
4256
|
|
|
2932
|
-
|
|
4257
|
+
console.log(`Progress bar click: seeking to ${seekTime.toFixed(1)}s (${(percentage * 100).toFixed(1)}%)`);
|
|
4258
|
+
|
|
4259
|
+
// Remember if we were playing
|
|
4260
|
+
const wasPlaying = !narrationAudio.paused;
|
|
4261
|
+
|
|
4262
|
+
// Directly set currentTime - DON'T call load() or pause() first
|
|
4263
|
+
// The browser handles seeking while playing
|
|
4264
|
+
try {
|
|
4265
|
+
narrationAudio.currentTime = seekTime;
|
|
4266
|
+
console.log(`Set currentTime to: ${narrationAudio.currentTime.toFixed(1)}s`);
|
|
4267
|
+
} catch (err) {
|
|
4268
|
+
console.error('Failed to set currentTime:', err);
|
|
4269
|
+
return;
|
|
4270
|
+
}
|
|
4271
|
+
|
|
4272
|
+
// If we weren't playing but user clicked progress bar, start playing
|
|
4273
|
+
if (wasPlaying || isPlaying) {
|
|
4274
|
+
narrationAudio.play().catch(err => console.error('Playback failed:', err));
|
|
4275
|
+
}
|
|
2933
4276
|
|
|
2934
|
-
//
|
|
4277
|
+
// Update highlight tracking for the new position
|
|
2935
4278
|
highlightIndex = 0;
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
4279
|
+
if (timingData.highlights) {
|
|
4280
|
+
for (let i = 0; i < timingData.highlights.length; i++) {
|
|
4281
|
+
if (timingData.highlights[i].timestamp <= seekTime) {
|
|
4282
|
+
highlightIndex = i;
|
|
4283
|
+
} else {
|
|
4284
|
+
break;
|
|
4285
|
+
}
|
|
4286
|
+
}
|
|
4287
|
+
// Apply highlight immediately
|
|
4288
|
+
if (highlightIndex >= 0 && highlightIndex < timingData.highlights.length) {
|
|
4289
|
+
applyHighlight(timingData.highlights[highlightIndex].selector);
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
|
|
4293
|
+
// Scroll to correct section
|
|
4294
|
+
if (timingData.sections) {
|
|
4295
|
+
let sectionIdx = 0;
|
|
4296
|
+
for (let i = 0; i < timingData.sections.length; i++) {
|
|
4297
|
+
if (seekTime >= timingData.sections[i].timestamp) {
|
|
4298
|
+
sectionIdx = i;
|
|
4299
|
+
} else {
|
|
4300
|
+
break;
|
|
4301
|
+
}
|
|
4302
|
+
}
|
|
4303
|
+
currentSectionIdx = sectionIdx;
|
|
4304
|
+
updateActiveChapter(sectionIdx);
|
|
4305
|
+
const sectionEl = document.getElementById(timingData.sections[sectionIdx].id);
|
|
4306
|
+
if (sectionEl) {
|
|
4307
|
+
sectionEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
2941
4308
|
}
|
|
2942
4309
|
}
|
|
4310
|
+
|
|
4311
|
+
// Update UI immediately
|
|
4312
|
+
if (audioProgressFill) {
|
|
4313
|
+
audioProgressFill.style.width = `${percentage * 100}%`;
|
|
4314
|
+
}
|
|
4315
|
+
if (audioCurrentTime) {
|
|
4316
|
+
audioCurrentTime.textContent = formatTime(seekTime);
|
|
4317
|
+
}
|
|
2943
4318
|
}
|
|
2944
4319
|
|
|
2945
4320
|
// Silent auto-play (no audio, timed sections)
|
|
@@ -2963,9 +4338,11 @@
|
|
|
2963
4338
|
playBtn.classList.add('active');
|
|
2964
4339
|
playBtn.textContent = '⏸ STOP';
|
|
2965
4340
|
|
|
2966
|
-
//
|
|
4341
|
+
// KILL the intro lighting animation completely to avoid visual conflict
|
|
4342
|
+
// (Don't just pause - kill it so it won't restart)
|
|
2967
4343
|
if (window.introLightingTL) {
|
|
2968
|
-
window.introLightingTL.
|
|
4344
|
+
window.introLightingTL.kill();
|
|
4345
|
+
window.introLightingTL = null;
|
|
2969
4346
|
// Reset the phase nodes to clean state
|
|
2970
4347
|
document.querySelectorAll('#introFlow .phase-node').forEach(n => n.classList.remove('active'));
|
|
2971
4348
|
document.querySelectorAll('#introFlow .phase-connector-arrow').forEach(a => a.classList.remove('active'));
|
|
@@ -2979,6 +4356,7 @@
|
|
|
2979
4356
|
if (audioEnabled && timingData) {
|
|
2980
4357
|
// Try to play audio
|
|
2981
4358
|
audioProgressContainer.style.display = 'block';
|
|
4359
|
+
document.body.classList.add('audio-playing'); // Adjust section heights
|
|
2982
4360
|
|
|
2983
4361
|
try {
|
|
2984
4362
|
await narrationAudio.play();
|
|
@@ -2990,6 +4368,7 @@
|
|
|
2990
4368
|
audioToggleBtn.textContent = '🔇 NO AUDIO';
|
|
2991
4369
|
audioToggleBtn.classList.add('disabled');
|
|
2992
4370
|
audioProgressContainer.style.display = 'none';
|
|
4371
|
+
document.body.classList.remove('audio-playing');
|
|
2993
4372
|
startSilentAutoPlay();
|
|
2994
4373
|
}
|
|
2995
4374
|
} else {
|
|
@@ -3012,14 +4391,13 @@
|
|
|
3012
4391
|
narrationAudio.pause();
|
|
3013
4392
|
narrationAudio.currentTime = 0;
|
|
3014
4393
|
audioProgressContainer.style.display = 'none';
|
|
4394
|
+
document.body.classList.remove('audio-playing'); // Reset section heights
|
|
3015
4395
|
removeHighlight();
|
|
3016
4396
|
highlightIndex = 0;
|
|
3017
4397
|
currentSectionIdx = 0;
|
|
3018
4398
|
|
|
3019
|
-
//
|
|
3020
|
-
|
|
3021
|
-
window.introLightingTL.restart();
|
|
3022
|
-
}
|
|
4399
|
+
// Note: We killed the intro lighting animation, so don't try to restart it
|
|
4400
|
+
// The animation was distracting during/after narration playback
|
|
3023
4401
|
}
|
|
3024
4402
|
|
|
3025
4403
|
// Audio ended
|
|
@@ -3069,6 +4447,31 @@
|
|
|
3069
4447
|
audioToggleBtn.addEventListener('click', toggleAudio);
|
|
3070
4448
|
narrationAudio.addEventListener('timeupdate', onAudioTimeUpdate);
|
|
3071
4449
|
narrationAudio.addEventListener('ended', onAudioEnded);
|
|
4450
|
+
|
|
4451
|
+
// DEBUG: Global click handler to see what's being clicked anywhere on page
|
|
4452
|
+
document.addEventListener('click', (e) => {
|
|
4453
|
+
// Only log clicks in the bottom 200px of the screen (where the audio player is)
|
|
4454
|
+
if (e.clientY > window.innerHeight - 200) {
|
|
4455
|
+
console.log('%c CLICK DETECTED ', 'background: red; color: white; font-size: 14px;', {
|
|
4456
|
+
element: e.target.tagName,
|
|
4457
|
+
id: e.target.id || '(no id)',
|
|
4458
|
+
class: e.target.className || '(no class)',
|
|
4459
|
+
x: e.clientX,
|
|
4460
|
+
y: e.clientY,
|
|
4461
|
+
audioCurrentTime: narrationAudio.currentTime.toFixed(1),
|
|
4462
|
+
audioDuration: narrationAudio.duration ? narrationAudio.duration.toFixed(1) : 'not loaded'
|
|
4463
|
+
});
|
|
4464
|
+
}
|
|
4465
|
+
}, true); // Use capture phase to see ALL clicks before they're handled
|
|
4466
|
+
|
|
4467
|
+
// Debug: track seeking events
|
|
4468
|
+
narrationAudio.addEventListener('seeking', () => {
|
|
4469
|
+
console.log('%c AUDIO SEEKING ', 'background: blue; color: white;', 'to:', narrationAudio.currentTime.toFixed(1));
|
|
4470
|
+
});
|
|
4471
|
+
narrationAudio.addEventListener('seeked', () => {
|
|
4472
|
+
console.log('%c AUDIO SEEKED ', 'background: green; color: white;', 'to:', narrationAudio.currentTime.toFixed(1));
|
|
4473
|
+
});
|
|
4474
|
+
|
|
3072
4475
|
audioProgressBar.addEventListener('click', onProgressBarClick);
|
|
3073
4476
|
|
|
3074
4477
|
// Check if audio file is accessible
|
|
@@ -3087,6 +4490,10 @@
|
|
|
3087
4490
|
// RESET BUTTON
|
|
3088
4491
|
// ============================================
|
|
3089
4492
|
document.getElementById('resetBtn').addEventListener('click', () => {
|
|
4493
|
+
// Stop audio and auto-play first
|
|
4494
|
+
stopAutoPlay();
|
|
4495
|
+
|
|
4496
|
+
// Scroll to top
|
|
3090
4497
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
3091
4498
|
|
|
3092
4499
|
// Reset animations
|
|
@@ -3097,18 +4504,26 @@
|
|
|
3097
4504
|
gsap.set('.flow-box, .flow-arrow, .hook-group, .hook-file', { opacity: 0 });
|
|
3098
4505
|
gsap.set('.result-allowed, .result-blocked', { opacity: 0 });
|
|
3099
4506
|
gsap.set('.phase-box', { opacity: 0, scale: 0.9 });
|
|
3100
|
-
gsap.set('.walkthrough-step', { opacity: 0, y: 20 });
|
|
3101
4507
|
gsap.set('.terminal-line, .terminal-comment', { opacity: 0 });
|
|
3102
4508
|
gsap.set('.json-line', { opacity: 0 });
|
|
3103
4509
|
gsap.set('.install-step', { opacity: 0, y: 20 });
|
|
3104
4510
|
gsap.set('.credit-link, .made-with', { opacity: 0 });
|
|
3105
4511
|
gsap.set('.intro-text, .scroll-hint', { opacity: 0 });
|
|
3106
4512
|
|
|
4513
|
+
// Reset phase boxes
|
|
3107
4514
|
document.querySelectorAll('.phase-box').forEach(box => {
|
|
3108
4515
|
box.classList.remove('active');
|
|
3109
4516
|
box.querySelector('.phase-status').textContent = '';
|
|
3110
4517
|
});
|
|
3111
4518
|
|
|
4519
|
+
// Reset intro phase nodes
|
|
4520
|
+
document.querySelectorAll('.phase-node').forEach(node => {
|
|
4521
|
+
node.classList.remove('active');
|
|
4522
|
+
});
|
|
4523
|
+
document.querySelectorAll('.phase-connector-arrow').forEach(arrow => {
|
|
4524
|
+
arrow.classList.remove('active');
|
|
4525
|
+
});
|
|
4526
|
+
|
|
3112
4527
|
ScrollTrigger.refresh();
|
|
3113
4528
|
}, 500);
|
|
3114
4529
|
});
|
|
@@ -3282,6 +4697,173 @@
|
|
|
3282
4697
|
});
|
|
3283
4698
|
});
|
|
3284
4699
|
|
|
4700
|
+
// ============================================
|
|
4701
|
+
// VOICE PREVIEW MODAL
|
|
4702
|
+
// ============================================
|
|
4703
|
+
const voiceSelectBtn = document.getElementById('voiceSelectBtn');
|
|
4704
|
+
const voiceModalOverlay = document.getElementById('voiceModalOverlay');
|
|
4705
|
+
const voiceModalClose = document.getElementById('voiceModalClose');
|
|
4706
|
+
const voiceConfirmBtn = document.getElementById('voiceConfirmBtn');
|
|
4707
|
+
const voiceOptions = document.querySelectorAll('.voice-option');
|
|
4708
|
+
const voicePlayBtns = document.querySelectorAll('.voice-play-btn');
|
|
4709
|
+
|
|
4710
|
+
let previewAudio = null;
|
|
4711
|
+
let currentlyPlayingBtn = null;
|
|
4712
|
+
let selectedVoice = 'adam';
|
|
4713
|
+
|
|
4714
|
+
// Open modal
|
|
4715
|
+
voiceSelectBtn.addEventListener('click', () => {
|
|
4716
|
+
voiceModalOverlay.classList.add('active');
|
|
4717
|
+
});
|
|
4718
|
+
|
|
4719
|
+
// Close modal
|
|
4720
|
+
function closeVoiceModal() {
|
|
4721
|
+
voiceModalOverlay.classList.remove('active');
|
|
4722
|
+
// Stop any playing preview
|
|
4723
|
+
if (previewAudio) {
|
|
4724
|
+
previewAudio.pause();
|
|
4725
|
+
previewAudio = null;
|
|
4726
|
+
}
|
|
4727
|
+
if (currentlyPlayingBtn) {
|
|
4728
|
+
currentlyPlayingBtn.textContent = '▶';
|
|
4729
|
+
currentlyPlayingBtn.classList.remove('playing');
|
|
4730
|
+
currentlyPlayingBtn = null;
|
|
4731
|
+
}
|
|
4732
|
+
}
|
|
4733
|
+
|
|
4734
|
+
voiceModalClose.addEventListener('click', closeVoiceModal);
|
|
4735
|
+
voiceModalOverlay.addEventListener('click', (e) => {
|
|
4736
|
+
if (e.target === voiceModalOverlay) {
|
|
4737
|
+
closeVoiceModal();
|
|
4738
|
+
}
|
|
4739
|
+
});
|
|
4740
|
+
|
|
4741
|
+
// Play preview
|
|
4742
|
+
voicePlayBtns.forEach(btn => {
|
|
4743
|
+
btn.addEventListener('click', (e) => {
|
|
4744
|
+
e.stopPropagation();
|
|
4745
|
+
const previewSrc = btn.dataset.preview;
|
|
4746
|
+
|
|
4747
|
+
// If clicking the same button that's playing, stop it
|
|
4748
|
+
if (currentlyPlayingBtn === btn && previewAudio && !previewAudio.paused) {
|
|
4749
|
+
previewAudio.pause();
|
|
4750
|
+
btn.textContent = '▶';
|
|
4751
|
+
btn.classList.remove('playing');
|
|
4752
|
+
currentlyPlayingBtn = null;
|
|
4753
|
+
return;
|
|
4754
|
+
}
|
|
4755
|
+
|
|
4756
|
+
// Stop any currently playing preview
|
|
4757
|
+
if (previewAudio) {
|
|
4758
|
+
previewAudio.pause();
|
|
4759
|
+
}
|
|
4760
|
+
if (currentlyPlayingBtn) {
|
|
4761
|
+
currentlyPlayingBtn.textContent = '▶';
|
|
4762
|
+
currentlyPlayingBtn.classList.remove('playing');
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
// Play new preview
|
|
4766
|
+
previewAudio = new Audio(previewSrc);
|
|
4767
|
+
previewAudio.play().catch(err => {
|
|
4768
|
+
console.error('Audio play failed:', err);
|
|
4769
|
+
btn.textContent = '⚠';
|
|
4770
|
+
setTimeout(() => { btn.textContent = '▶'; }, 2000);
|
|
4771
|
+
});
|
|
4772
|
+
|
|
4773
|
+
btn.textContent = '⏸';
|
|
4774
|
+
btn.classList.add('playing');
|
|
4775
|
+
currentlyPlayingBtn = btn;
|
|
4776
|
+
|
|
4777
|
+
previewAudio.addEventListener('ended', () => {
|
|
4778
|
+
btn.textContent = '▶';
|
|
4779
|
+
btn.classList.remove('playing');
|
|
4780
|
+
currentlyPlayingBtn = null;
|
|
4781
|
+
});
|
|
4782
|
+
});
|
|
4783
|
+
});
|
|
4784
|
+
|
|
4785
|
+
// Select voice
|
|
4786
|
+
voiceOptions.forEach(option => {
|
|
4787
|
+
option.addEventListener('click', () => {
|
|
4788
|
+
// Remove selected from all
|
|
4789
|
+
voiceOptions.forEach(opt => {
|
|
4790
|
+
opt.classList.remove('selected');
|
|
4791
|
+
opt.querySelector('.voice-select-btn').textContent = 'Select';
|
|
4792
|
+
});
|
|
4793
|
+
|
|
4794
|
+
// Add selected to clicked
|
|
4795
|
+
option.classList.add('selected');
|
|
4796
|
+
option.querySelector('.voice-select-btn').textContent = 'Selected';
|
|
4797
|
+
selectedVoice = option.dataset.voice;
|
|
4798
|
+
});
|
|
4799
|
+
});
|
|
4800
|
+
|
|
4801
|
+
// Confirm selection
|
|
4802
|
+
voiceConfirmBtn.addEventListener('click', async () => {
|
|
4803
|
+
const selectedOption = document.querySelector('.voice-option.selected');
|
|
4804
|
+
if (selectedOption) {
|
|
4805
|
+
const voiceFile = selectedOption.dataset.file;
|
|
4806
|
+
const timingFile = selectedOption.dataset.timing;
|
|
4807
|
+
const voiceName = selectedOption.querySelector('.voice-name').textContent;
|
|
4808
|
+
|
|
4809
|
+
// Stop current playback if any
|
|
4810
|
+
if (isPlaying) {
|
|
4811
|
+
stopAutoPlay();
|
|
4812
|
+
}
|
|
4813
|
+
|
|
4814
|
+
// Update audio source
|
|
4815
|
+
narrationAudio.src = voiceFile;
|
|
4816
|
+
|
|
4817
|
+
// Force load the new audio and wait for it to be ready
|
|
4818
|
+
narrationAudio.load();
|
|
4819
|
+
|
|
4820
|
+
// Wait for the audio to load metadata AND have valid duration (don't trust stale readyState)
|
|
4821
|
+
await new Promise((resolve) => {
|
|
4822
|
+
let resolved = false;
|
|
4823
|
+
const tryResolve = () => {
|
|
4824
|
+
if (!resolved && narrationAudio.duration > 0 && !isNaN(narrationAudio.duration)) {
|
|
4825
|
+
resolved = true;
|
|
4826
|
+
resolve();
|
|
4827
|
+
}
|
|
4828
|
+
};
|
|
4829
|
+
// Listen for multiple events to ensure we catch when audio is truly ready
|
|
4830
|
+
narrationAudio.addEventListener('loadedmetadata', tryResolve, { once: true });
|
|
4831
|
+
narrationAudio.addEventListener('canplay', tryResolve, { once: true });
|
|
4832
|
+
narrationAudio.addEventListener('durationchange', tryResolve, { once: true });
|
|
4833
|
+
// Timeout fallback in case load fails
|
|
4834
|
+
setTimeout(() => {
|
|
4835
|
+
if (!resolved) {
|
|
4836
|
+
console.warn('Audio load timeout, proceeding anyway');
|
|
4837
|
+
resolved = true;
|
|
4838
|
+
resolve();
|
|
4839
|
+
}
|
|
4840
|
+
}, 3000);
|
|
4841
|
+
});
|
|
4842
|
+
|
|
4843
|
+
// Reset audio position and clear old state
|
|
4844
|
+
narrationAudio.currentTime = 0;
|
|
4845
|
+
timingData = null;
|
|
4846
|
+
highlightIndex = 0;
|
|
4847
|
+
currentSectionIdx = 0;
|
|
4848
|
+
await loadTimingData(timingFile, true);
|
|
4849
|
+
|
|
4850
|
+
// Update voice button to show selected voice
|
|
4851
|
+
voiceSelectBtn.textContent = `🎙 ${voiceName.toUpperCase()}`;
|
|
4852
|
+
|
|
4853
|
+
console.log(`Voice changed to: ${voiceName} (${voiceFile})`);
|
|
4854
|
+
console.log(`Audio duration: ${narrationAudio.duration}s, readyState: ${narrationAudio.readyState}`);
|
|
4855
|
+
console.log(`Timing data loaded: ${timingFile}`);
|
|
4856
|
+
}
|
|
4857
|
+
closeVoiceModal();
|
|
4858
|
+
});
|
|
4859
|
+
|
|
4860
|
+
// Keyboard close (Escape)
|
|
4861
|
+
document.addEventListener('keydown', (e) => {
|
|
4862
|
+
if (e.key === 'Escape' && voiceModalOverlay.classList.contains('active')) {
|
|
4863
|
+
closeVoiceModal();
|
|
4864
|
+
}
|
|
4865
|
+
});
|
|
4866
|
+
|
|
3285
4867
|
</script>
|
|
3286
4868
|
|
|
3287
4869
|
</body>
|