@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.
Files changed (55) hide show
  1. package/README.md +343 -467
  2. package/bin/cli.js +229 -15
  3. package/commands/README.md +124 -251
  4. package/commands/api-create.md +318 -136
  5. package/commands/api-interview.md +252 -256
  6. package/commands/api-research.md +209 -234
  7. package/commands/api-verify.md +231 -0
  8. package/demo/audio/generate-all-narrations.js +581 -0
  9. package/demo/audio/generate-narration.js +120 -56
  10. package/demo/audio/generate-voice-previews.js +140 -0
  11. package/demo/audio/narration-adam-timing.json +4675 -0
  12. package/demo/audio/narration-adam.mp3 +0 -0
  13. package/demo/audio/narration-creature-timing.json +4675 -0
  14. package/demo/audio/narration-creature.mp3 +0 -0
  15. package/demo/audio/narration-gaming-timing.json +4675 -0
  16. package/demo/audio/narration-gaming.mp3 +0 -0
  17. package/demo/audio/narration-hope-timing.json +4675 -0
  18. package/demo/audio/narration-hope.mp3 +0 -0
  19. package/demo/audio/narration-mark-timing.json +4675 -0
  20. package/demo/audio/narration-mark.mp3 +0 -0
  21. package/demo/audio/previews/manifest.json +30 -0
  22. package/demo/audio/previews/preview-creature.mp3 +0 -0
  23. package/demo/audio/previews/preview-gaming.mp3 +0 -0
  24. package/demo/audio/previews/preview-hope.mp3 +0 -0
  25. package/demo/audio/previews/preview-mark.mp3 +0 -0
  26. package/demo/audio/voices-manifest.json +50 -0
  27. package/demo/hustle-together/blog/gemini-vs-claude-widgets.html +30 -28
  28. package/demo/hustle-together/blog/interview-driven-api-development.html +37 -23
  29. package/demo/hustle-together/index.html +142 -109
  30. package/demo/workflow-demo.html +2618 -1036
  31. package/hooks/api-workflow-check.py +2 -0
  32. package/hooks/enforce-deep-research.py +180 -0
  33. package/hooks/enforce-disambiguation.py +149 -0
  34. package/hooks/enforce-documentation.py +187 -0
  35. package/hooks/enforce-environment.py +249 -0
  36. package/hooks/enforce-refactor.py +187 -0
  37. package/hooks/enforce-research.py +93 -46
  38. package/hooks/enforce-schema.py +186 -0
  39. package/hooks/enforce-scope.py +156 -0
  40. package/hooks/enforce-tdd-red.py +246 -0
  41. package/hooks/enforce-verify.py +186 -0
  42. package/hooks/periodic-reground.py +154 -0
  43. package/hooks/session-startup.py +151 -0
  44. package/hooks/track-tool-use.py +109 -17
  45. package/hooks/verify-after-green.py +282 -0
  46. package/package.json +3 -2
  47. package/scripts/collect-test-results.ts +404 -0
  48. package/scripts/extract-parameters.ts +483 -0
  49. package/scripts/generate-test-manifest.ts +520 -0
  50. package/templates/CLAUDE-SECTION.md +84 -0
  51. package/templates/api-dev-state.json +83 -8
  52. package/templates/api-test/page.tsx +315 -0
  53. package/templates/api-test/test-structure/route.ts +269 -0
  54. package/templates/research-index.json +6 -0
  55. package/templates/settings.json +59 -0
@@ -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
- 90s RETRO TERMINAL AESTHETIC
16
- Black & White, Monospace, Dashed Borders
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
- --white: #e0e0e0;
27
- --grey: #888;
28
- --dark-grey: #444;
26
+ /* Dark theme colors */
29
27
  --black: #0a0a0a;
30
- --glow: rgba(255, 255, 255, 0.3);
31
- --accent-red: #BA0C2F;
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', Courier, monospace;
39
- line-height: 1.7;
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
- overflow: hidden;
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(--grey);
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(--grey);
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
- border: 1px solid var(--grey);
92
- background: transparent;
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
- background: var(--grey);
109
- }
110
-
111
- .particle.plus::before {
112
- width: 12px;
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
- background: transparent;
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
- background: var(--grey);
138
- }
139
-
140
- .particle.x-shape::before {
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
- border-radius: 0;
159
- width: 20px;
138
+ width: 12px;
160
139
  height: 1px;
161
- background: var(--grey);
140
+ border-radius: 0;
162
141
  }
163
142
 
164
143
  .particle.line-v {
165
- border-radius: 0;
166
144
  width: 1px;
167
- height: 20px;
168
- background: var(--grey);
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(--grey);
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: 6px solid transparent;
186
- border-right: 6px solid transparent;
187
- border-bottom: 10px solid var(--grey);
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: 300px;
194
- height: 300px;
195
- background: radial-gradient(circle, rgba(255,255,255,0.08) 0%, transparent 70%);
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: 0;
174
+ z-index: -1;
198
175
  transform: translate(-50%, -50%);
199
- transition: opacity 0.3s;
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.1) 2px,
216
- rgba(0, 0, 0, 0.1) 4px
190
+ rgba(0, 0, 0, 0.05) 2px,
191
+ rgba(0, 0, 0, 0.05) 4px
217
192
  );
218
- opacity: 0.3;
193
+ pointer-events: none;
194
+ z-index: 9999;
195
+ opacity: 0.2;
219
196
  }
220
197
 
221
- /* Animated corner decorations */
198
+ /* Corner decorations */
222
199
  .corner-decoration {
223
200
  position: fixed;
224
- width: 60px;
225
- height: 60px;
226
- border: 1px dashed var(--dark-grey);
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: 80px;
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
- /* Visual flow lines between sections */
260
- .flow-connector-line {
261
- position: absolute;
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: 20px;
480
- right: 20px;
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 dashed var(--grey);
489
- color: var(--white);
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.3s;
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
- box-shadow: 0 0 15px var(--accent-red-glow);
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.5;
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
- background: transparent;
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 NARRATION PLAYER
291
+ AUDIO PLAYER
524
292
  ============================================ */
525
293
  .audio-progress-container {
526
294
  position: fixed;
527
- bottom: 60px;
528
- left: 50%;
529
- transform: translateX(-50%);
530
- width: 80%;
531
- max-width: 600px;
532
- background: rgba(0, 0, 0, 0.9);
533
- border: 1px dashed var(--grey);
534
- padding: 15px 20px;
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.75rem;
307
+ font-size: 0.8rem;
543
308
  color: var(--grey);
544
- margin-bottom: 10px;
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: 6px;
551
- background: var(--dark-grey);
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: -3px;
574
- width: 2px;
575
- height: 12px;
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
- .audio-section-marker:hover {
581
- opacity: 1;
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 for elements being narrated */
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-bottom: 3px solid var(--accent-red) !important;
609
- padding-bottom: 8px;
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: 0;
562
+ top: 61px;
616
563
  left: 0;
617
- height: 3px;
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
- /* Section base */
571
+ /* ============================================
572
+ SECTIONS & CONTENT
573
+ ============================================ */
624
574
  section {
625
- min-height: 100vh;
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(--grey);
595
+ border: 1px dashed var(--border-color);
637
596
  padding: 50px;
638
597
  position: relative;
639
- max-width: 1000px;
598
+ max-width: 900px;
640
599
  width: 100%;
641
- /* Blurred glass backdrop so boxes don't interfere with background animation */
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-size: 2.5rem;
666
- font-weight: normal;
667
- letter-spacing: 4px;
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-size: 1.8rem;
673
- font-weight: normal;
674
- letter-spacing: 2px;
675
- margin-bottom: 10px;
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: 1.2rem;
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 - THE KEY EDUCATIONAL ELEMENT */
646
+ /* Explanation blocks */
687
647
  .explanation {
688
- background: rgba(255, 255, 255, 0.03);
689
- border-left: 3px solid var(--grey);
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.9;
654
+ line-height: 1.8;
694
655
  opacity: 0;
695
656
  }
696
657
 
697
658
  .explanation-title {
698
- font-size: 0.85rem;
659
+ font-size: 0.75rem;
699
660
  text-transform: uppercase;
700
- letter-spacing: 3px;
701
- color: var(--grey);
661
+ letter-spacing: 2px;
662
+ color: var(--accent-red);
702
663
  margin-bottom: 15px;
703
- display: flex;
704
- align-items: center;
705
- gap: 10px;
664
+ font-weight: 600;
706
665
  }
707
666
 
708
667
  .explanation-title::before {
709
- content: '?';
710
- width: 24px;
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: normal;
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: rgba(255, 255, 255, 0.05);
736
- border: 1px dashed var(--white);
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(--black);
700
+ background: var(--card-bg);
748
701
  padding: 0 10px;
749
- font-size: 0.75rem;
702
+ font-size: 0.7rem;
750
703
  letter-spacing: 2px;
751
- color: var(--grey);
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(--white);
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(--white);
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 30px var(--accent-red-glow), 0 0 60px var(--accent-red-glow);
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: 15px;
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(--dark-grey);
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.4s ease;
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: -2px;
912
- left: -2px;
913
- right: -2px;
914
- bottom: -2px;
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
- box-shadow: 0 0 20px var(--accent-red-glow), inset 0 0 20px var(--accent-red-glow);
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.1);
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.8rem;
897
+ font-size: 1.6rem;
942
898
  font-weight: bold;
943
899
  margin-bottom: 8px;
944
900
  color: var(--grey);
945
- transition: all 0.4s ease;
901
+ transition: all 0.3s ease;
946
902
  }
947
903
 
948
904
  .phase-label {
949
- font-size: 0.7rem;
905
+ font-size: 0.65rem;
950
906
  text-transform: uppercase;
951
907
  letter-spacing: 2px;
952
- color: var(--dark-grey);
953
- transition: all 0.4s ease;
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(--dark-grey);
958
- font-size: 1.5rem;
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: 1px dashed var(--dark-grey);
1024
- padding: 25px 30px;
1025
- margin-bottom: 20px;
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.3s;
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
- box-shadow: 0 0 25px var(--accent-red-glow);
996
+ transform: translateX(5px);
1035
997
  }
1036
998
 
1037
- .gap-item::before {
1038
- content: 'X';
1039
- position: absolute;
1040
- left: -35px;
1041
- top: 50%;
1042
- transform: translateY(-50%);
1043
- color: var(--accent-red);
1044
- font-size: 1.2rem;
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(--grey);
1049
- font-size: 0.8rem;
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.15rem;
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.95rem;
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: rgba(255,255,255,0.03);
1069
- border-left: 2px solid var(--dark-grey);
1070
- margin-top: 10px;
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(--white);
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: 25px;
1053
+ gap: 20px;
1089
1054
  margin-top: 30px;
1090
1055
  }
1091
1056
 
1092
1057
  .solution-card {
1093
- border: 1px dashed var(--dark-grey);
1094
- padding: 30px 25px;
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.3s;
1064
+ transition: all 0.2s;
1099
1065
  }
1100
1066
 
1101
1067
  .solution-card:hover {
1102
1068
  border-color: var(--accent-red);
1103
- box-shadow: 0 0 25px var(--accent-red-glow);
1069
+ transform: translateY(-3px);
1104
1070
  }
1105
1071
 
1106
1072
  .solution-icon {
1107
- font-size: 2rem;
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: 1rem;
1114
- margin-bottom: 10px;
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: 1px dashed var(--grey);
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.3s;
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(--grey);
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(--white);
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(--grey);
1164
- font-size: 0.85rem;
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-left: 2px solid var(--grey);
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.3s;
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
- background: rgba(186, 12, 47, 0.1);
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
- .result-box {
1623
+ .claude-choice-marker {
1624
+ width: 18px;
1625
+ height: 18px;
1626
+ border: 2px solid #888;
1627
+ border-radius: 50%;
1198
1628
  display: flex;
1199
- gap: 40px;
1629
+ align-items: center;
1200
1630
  justify-content: center;
1201
- margin-top: 25px;
1202
1631
  }
1203
1632
 
1204
- .result-allowed, .result-blocked {
1205
- padding: 18px 35px;
1206
- border: 2px solid;
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
- transition: all 0.3s;
1654
+ display: none;
1209
1655
  }
1210
1656
 
1211
- .result-allowed {
1212
- border-color: var(--grey);
1657
+ .ascii-startup.visible {
1658
+ opacity: 1;
1659
+ display: block;
1213
1660
  }
1214
1661
 
1215
- .result-blocked {
1216
- border-color: var(--accent-red);
1217
- color: var(--accent-red);
1662
+ .ascii-startup .welcome-text {
1663
+ color: #e5e5e5;
1218
1664
  }
1219
1665
 
1220
- .result-allowed:hover, .result-blocked:hover {
1221
- transform: scale(1.05);
1666
+ .ascii-startup .tips-text {
1667
+ color: #888;
1222
1668
  }
1223
1669
 
1224
- /* ============================================
1225
- SECTION 5: 10-PHASE WORKFLOW
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
- .phase-box {
1236
- border: 1px dashed var(--dark-grey);
1237
- padding: 25px 15px;
1238
- text-align: center;
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
- .phase-box:hover {
1246
- border-color: var(--accent-red);
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
- .phase-box.active {
1252
- border-color: var(--accent-red);
1253
- background: rgba(186, 12, 47, 0.1);
1693
+ .claude-prompt {
1694
+ color: var(--claude-orange);
1695
+ font-size: 1.1rem;
1696
+ font-weight: bold;
1254
1697
  }
1255
1698
 
1256
- .phase-number {
1257
- font-size: 2.2rem;
1258
- color: var(--dark-grey);
1259
- margin-bottom: 10px;
1699
+ .claude-input {
1700
+ color: #e5e5e5;
1701
+ font-family: 'SF Mono', 'Monaco', 'Inconsolata', monospace;
1260
1702
  }
1261
1703
 
1262
- .phase-box.active .phase-number {
1263
- color: var(--white);
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
- .phase-name {
1267
- font-size: 0.75rem;
1268
- text-transform: uppercase;
1269
- letter-spacing: 1px;
1714
+ @keyframes blink-cursor {
1715
+ 0%, 50% { opacity: 1; }
1716
+ 51%, 100% { opacity: 0; }
1270
1717
  }
1271
1718
 
1272
- .phase-status {
1273
- position: absolute;
1274
- top: 8px;
1275
- right: 8px;
1276
- font-size: 0.7rem;
1277
- color: var(--grey);
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
- .phase-connector {
1281
- grid-column: span 5;
1282
- text-align: center;
1283
- color: var(--grey);
1284
- font-size: 0.8rem;
1285
- opacity: 0;
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
- .phase-desc {
1290
- font-size: 0.7rem;
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
- SECTION 6: REAL WALKTHROUGH
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
- .walkthrough-step.active {
1308
- border-color: var(--accent-red);
1309
- box-shadow: 0 0 20px var(--accent-red-glow);
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
- .walkthrough-header {
1754
+ /* Thinking indicator */
1755
+ .claude-thinking {
1313
1756
  display: flex;
1314
1757
  align-items: center;
1315
- gap: 20px;
1316
- margin-bottom: 20px;
1758
+ gap: 8px;
1759
+ padding: 8px 28px;
1760
+ color: #888;
1761
+ font-style: italic;
1762
+ opacity: 0;
1317
1763
  }
1318
1764
 
1319
- .walkthrough-num {
1320
- width: 45px;
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
- .walkthrough-title {
1331
- font-size: 1.1rem;
1332
- text-transform: uppercase;
1333
- letter-spacing: 1px;
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
- .walkthrough-content {
1337
- padding-left: 65px;
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
- .walkthrough-desc {
1341
- color: var(--grey);
1342
- margin-bottom: 15px;
1343
- line-height: 1.8;
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
- .walkthrough-example {
1347
- background: #111;
1348
- padding: 15px 20px;
1349
- font-size: 0.85rem;
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
- .walkthrough-example .label {
1354
- color: var(--grey);
1355
- font-size: 0.75rem;
1356
- text-transform: uppercase;
1357
- letter-spacing: 1px;
1358
- margin-bottom: 8px;
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
- SECTION 7: LIVE DEMO TERMINAL
1363
- ============================================ */
1364
- .terminal {
1365
- background: #0d0d0d;
1366
- border: 1px solid var(--grey);
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
- position: relative;
1820
+ text-transform: uppercase;
1821
+ letter-spacing: 1px;
1371
1822
  }
1372
1823
 
1373
- .terminal-header {
1374
- display: flex;
1375
- gap: 8px;
1376
- margin-bottom: 20px;
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
- .terminal-dot {
1382
- width: 12px;
1383
- height: 12px;
1384
- border-radius: 50%;
1385
- border: 1px solid var(--grey);
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
- .terminal-title {
1389
- margin-left: auto;
1390
- color: var(--grey);
1391
- font-size: 0.8rem;
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
- .terminal-line {
1395
- padding: 8px 0;
1396
- opacity: 0;
1846
+ .explanation-step-phase {
1397
1847
  display: flex;
1398
- align-items: flex-start;
1399
- gap: 12px;
1848
+ align-items: center;
1849
+ gap: 8px;
1850
+ margin-bottom: 8px;
1400
1851
  }
1401
1852
 
1402
- .terminal-prompt {
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
- .terminal-command {
1408
- color: var(--white);
1866
+ .explanation-step.active .explanation-step-num {
1867
+ background: var(--claude-orange);
1868
+ color: white;
1409
1869
  }
1410
1870
 
1411
- .terminal-result {
1412
- padding-left: 25px;
1871
+ .explanation-step-name {
1872
+ font-weight: 600;
1873
+ color: var(--grey);
1874
+ font-size: 0.85rem;
1413
1875
  }
1414
1876
 
1415
- .terminal-blocked {
1416
- color: var(--accent-red);
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
- .terminal-allowed {
1881
+ .explanation-step-desc {
1423
1882
  color: var(--grey);
1424
- border-left: 3px solid var(--grey);
1425
- padding-left: 15px;
1883
+ font-size: 0.8rem;
1884
+ line-height: 1.5;
1426
1885
  }
1427
1886
 
1428
- .terminal-logged {
1429
- color: var(--grey);
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
- .terminal-comment {
1434
- color: var(--dark-grey);
1435
- font-size: 0.8rem;
1436
- padding-left: 25px;
1437
- margin-bottom: 5px;
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: #0d0d0d;
1445
- border: 1px solid var(--grey);
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(--grey);
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.15);
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: 2px solid var(--accent-red);
1944
+ border-left: 3px solid var(--accent-red);
1475
1945
  }
1476
1946
 
1477
1947
  .json-comment {
1478
- color: var(--dark-grey);
1479
- font-size: 0.75rem;
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: #0d0d0d;
1488
- border: 1px solid var(--grey);
1489
- padding: 20px 30px;
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(--white);
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 0;
1508
- border-bottom: 1px dashed var(--dark-grey);
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
- border-bottom: none;
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.2rem;
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(--grey);
1534
- font-size: 0.9rem;
2007
+ color: var(--white);
2008
+ font-size: 1rem;
2009
+ font-weight: 500;
1535
2010
  }
1536
2011
 
1537
2012
  .install-arrow {
1538
- color: var(--grey);
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.95rem;
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.8rem;
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: 30px;
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: 1px dashed var(--grey);
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.3s;
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: #fff;
2064
+ color: var(--white);
1581
2065
  border-color: var(--accent-red);
1582
- box-shadow: 0 0 25px var(--accent-red-glow);
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.95rem;
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: 25px;
2082
+ right: 20px;
1599
2083
  top: 50%;
1600
2084
  transform: translateY(-50%);
1601
2085
  display: flex;
1602
2086
  flex-direction: column;
1603
- gap: 12px;
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: 1px solid var(--grey);
1611
- border-radius: 50%;
2094
+ border: 2px solid var(--border-color);
2095
+ border-radius: 0;
1612
2096
  cursor: pointer;
1613
- transition: all 0.3s;
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 12px var(--accent-red-glow);
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.7</div>
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>What Goes Wrong Without Structure</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
- When you ask an AI assistant to build an API, it often <strong>skips important steps</strong>.
1795
- It might use outdated information from its training data, make assumptions about what you want,
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-number">Gap 01</span>
1806
- <div class="gap-title">AI Doesn't Use Your Exact Words</div>
1807
- <div class="gap-desc">You say one thing, but the AI searches for something different.</div>
1808
- <div class="gap-example">
1809
- <span class="bad">You: "Use Vercel AI Gateway"</span><br>
1810
- <span class="bad">AI searches: "Vercel AI SDK" (wrong!)</span><br>
1811
- <span class="good">Should search: "Vercel AI Gateway" (your exact words)</span>
1812
- </div>
1813
- </li>
1814
- <li class="gap-item">
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-number">Gap 03</span>
1825
- <div class="gap-title">Skipped Tests Go Uninvestigated</div>
1826
- <div class="gap-desc">AI sees "9 tests skipped" and says "that's expected" without checking why.</div>
1827
- <div class="gap-example">
1828
- Tests might be skipping because they check the wrong environment variables.
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-number">Gap 04</span>
1833
- <div class="gap-title">Tasks Marked Done Without Verification</div>
1834
- <div class="gap-desc">AI declares "complete!" without checking if the code matches your requirements.</div>
1835
- <div class="gap-example">
1836
- You asked for "single API key" but code still uses 4 separate keys.
1837
- </div>
1838
- </li>
1839
- <li class="gap-item">
1840
- <span class="gap-number">Gap 05</span>
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">[R]</span>
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">[I]</span>
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">[V]</span>
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>10 Enforced Phases</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 10 phases. The system tracks progress and won't let
1982
- the AI skip ahead. This ensures <strong>thorough, consistent results</strong> every time.
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">Ask user questions</div>
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">Based on interview answers</div>
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">Refactor</div>
2049
- <div class="phase-desc">Clean up the code</div>
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
- </div>
2059
- </div>
2060
- </section>
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 7: TERMINAL SIMULATION
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>The Interview Flow</h3>
2630
+ <h3>12-Phase Workflow (v3.0)</h3>
2213
2631
 
2214
2632
  <div class="explanation">
2215
- <div class="explanation-title">Structured Questions in Action</div>
2633
+ <div class="explanation-title">The Complete v3.0 Flow</div>
2216
2634
  <p>
2217
- This simulates the interview phase. Notice how the AI uses
2218
- <strong>AskUserQuestion with options</strong> based on research findings.
2219
- Your answers are captured and used throughout implementation.
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="terminal">
2224
- <div class="terminal-header">
2225
- <div class="terminal-dot"></div>
2226
- <div class="terminal-dot"></div>
2227
- <div class="terminal-dot"></div>
2228
- <div class="terminal-title">claude-code session: /api-create brandfetch</div>
2229
- <div class="status-badge active" style="margin-left: auto;">
2230
- <span class="pulse"></span>
2231
- <span>Live</span>
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
- &nbsp;&nbsp;question: "What's the primary purpose?"<br>
2257
- &nbsp;&nbsp;options: [<br>
2258
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "lookup", label: "Brand lookup by domain"},<br>
2259
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "logos", label: "Logo extraction only"},<br>
2260
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "full_kit", label: "Full brand kit"},<br>
2261
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "custom", label: "Type something..."}<br>
2262
- &nbsp;&nbsp;]<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
- <div class="terminal-line" data-step="8">
2270
- <span class="terminal-prompt">claude></span>
2271
- <span class="terminal-command">AskUserQuestion(<br>
2272
- &nbsp;&nbsp;question: "How should API keys be handled?"<br>
2273
- &nbsp;&nbsp;options: [<br>
2274
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "server_only", label: "Server env vars only"},<br>
2275
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "header", label: "Custom header override"},<br>
2276
- &nbsp;&nbsp;&nbsp;&nbsp;{value: "both", label: "Both methods"},<br>
2277
- &nbsp;&nbsp;]<br>
2278
- )</span>
2279
- </div>
2280
- <div class="terminal-line terminal-result" data-step="9">
2281
- <span class="terminal-allowed">[USER] Selected: "server_only"<br>[TRACKED] decisions.api_key_handling = "server_only"</span>
2282
- </div>
2283
-
2284
- <div class="terminal-comment" data-step="10">// Step 3: Interview complete, now writing code</div>
2285
- <div class="terminal-line" data-step="11">
2286
- <span class="terminal-prompt">claude></span>
2287
- <span class="terminal-command">Write src/app/api/v2/brandfetch/route.ts</span>
2288
- </div>
2289
- <div class="terminal-line terminal-result" data-step="12">
2290
- <span class="terminal-allowed">✅ Interview complete. REMEMBER THE USER'S DECISIONS:<br>
2291
- &nbsp;&nbsp;• Purpose: full_kit<br>
2292
- &nbsp;&nbsp;• 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
- <div class="terminal-comment" data-step="13">// Decisions are injected on EVERY write operation</div>
2297
- <div class="terminal-line" data-step="14">
2298
- <span class="terminal-prompt">claude></span>
2299
- <span class="terminal-command">Write src/lib/schemas/brandfetch.ts</span>
2300
- </div>
2301
- <div class="terminal-line terminal-result" data-step="15">
2302
- <span class="terminal-allowed">✅ REMEMBER THE USER'S DECISIONS:<br>
2303
- &nbsp;&nbsp;• Purpose: full_kit<br>
2304
- &nbsp;&nbsp;• API Key Handling: server_only<br>
2305
- ALLOWED: Writing schema file...</span>
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
- <div class="terminal-line" data-step="16" style="margin-top: 15px;">
2309
- <span class="terminal-prompt">claude></span>
2310
- <div class="typing-indicator">
2311
- <span></span>
2312
- <span></span>
2313
- <span></span>
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">Everything Gets Recorded</div>
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 includes <strong>your interview decisions</strong>, which are injected
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">21</div>
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">---></div>
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">5</div>
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">---></div>
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">+</div>
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">---></div>
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">*</div>
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">---></div>
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">VIEW ON GITHUB</a>
2470
- <a href="https://www.npmjs.com/package/@hustle-together/api-dev-tools" class="credit-link" target="_blank">INSTALL FROM NPM</a>
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
- v1.9.0 | MIT License<br>
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', 'walkthrough', 'demo', 'state', 'install', 'credits'];
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: WALKTHROUGH ANIMATION
3753
+ // SECTION 6: CLAUDE CODE TERMINAL ANIMATION
3754
+ // Animates both terminal messages and explanation panel
2693
3755
  // ============================================
2694
- const walkthroughTL = gsap.timeline({
3756
+ const demoTL = gsap.timeline({
2695
3757
  scrollTrigger: {
2696
- trigger: '#walkthrough',
3758
+ trigger: '#demo',
2697
3759
  start: 'top 60%',
2698
3760
  toggleActions: 'play none none reverse'
2699
3761
  }
2700
3762
  });
2701
3763
 
2702
- walkthroughTL.to('#walkthrough .explanation', { opacity: 1, duration: 0.5 });
3764
+ demoTL.to('#demo .explanation', { opacity: 1, duration: 0.5 });
2703
3765
 
2704
- document.querySelectorAll('.walkthrough-step').forEach((step, i) => {
2705
- walkthroughTL.to(step, {
2706
- opacity: 1,
2707
- y: 0,
2708
- duration: 0.4,
2709
- ease: 'power2.out'
2710
- }, i * 0.25 + 0.3);
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
- // SECTION 7: DEMO TERMINAL ANIMATION
2715
- // ============================================
2716
- const demoTL = gsap.timeline({
2717
- scrollTrigger: {
2718
- trigger: '#demo',
2719
- start: 'top 60%',
2720
- toggleActions: 'play none none reverse'
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
- demoTL.to('#demo .explanation', { opacity: 1, duration: 0.5 });
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
- // Get all terminal elements in order
2727
- const terminalElements = document.querySelectorAll('#demo .terminal-comment, #demo .terminal-line');
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
- duration: 0.3,
2733
- ease: 'power2.out'
2734
- }, delay);
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
- // Load timing data
2823
- async function loadTimingData() {
2824
- if (timingData) return timingData;
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('audio/narration-timing.json');
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 0 0px var(--accent-red-glow)' },
2876
- { boxShadow: '0 0 30px var(--accent-red-glow)', duration: 0.4, ease: 'power2.out' }
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
- if (!timingData || !narrationAudio.duration) return;
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
- narrationAudio.currentTime = seekTime;
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
- // Reset highlight tracking
4277
+ // Update highlight tracking for the new position
2935
4278
  highlightIndex = 0;
2936
- for (let i = 0; i < timingData.highlights.length; i++) {
2937
- if (timingData.highlights[i].timestamp <= seekTime) {
2938
- highlightIndex = i;
2939
- } else {
2940
- break;
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
- // Pause the intro lighting animation to avoid visual conflict
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.pause();
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
- // Resume the intro lighting animation
3020
- if (window.introLightingTL) {
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>