-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathstudio.html
More file actions
3515 lines (3175 loc) · 397 KB
/
studio.html
File metadata and controls
3515 lines (3175 loc) · 397 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>FIZX STUDIO</title>
<!-- Self-hosted subsetted fonts — no network request, works fully offline.
Base64 strings injected by compile.py from generated .b64 files in assets/fonts/.
Subsetting via pyftsubset keeps size reasonable (~20KB per variant).
format('woff2') has 96%+ browser support; no woff fallback needed for modern targets. -->
<style>
@font-face {
font-family: 'merri';
font-weight: normal;
font-style: normal;
font-display: block;
src: url('data:font/woff2;base64,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') format('woff2');
}
@font-face {
font-family: 'deja';
font-weight: normal;
font-style: normal;
font-display: block;
src: url('data:font/woff2;base64,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') format('woff2');
}
</style>
<style>
:root {
--bg: #1c1b19;
--surface: #1a1815;
--surface-2: #24221f;
--text: #e6ddd0;
--text-dim: #7a7062;
--accent: #e0a96d;
--accent-dim: rgba(224, 169, 109, 0.12);
--accent-glow: rgba(224, 169, 109, 0.25);
--border-accent:rgba(224, 169, 109, 0.35);
--border: rgba(255, 255, 255, 0.04);
--prim: 'merri', 'Consolas', monospace;
--scnd: 'deja', 'Segoe UI', system-ui, -apple-system, sans-serif; /* DejaVuSans for Arabic */
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--scnd);
font-weight: 300;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 32px 20px;
background-image: repeating-linear-gradient(
0deg,
transparent, transparent 2px,
rgba(0,255,136,0.008) 2px, rgba(0,255,136,0.008) 4px
);
}
svg {
vertical-align: middle; /* SVGs often sit slightly "higher" than text */
margin-bottom: 0.125em; /* Fine-tune to align with your font baseline */
}
.container { max-width: 780px; width: 100%; }
/* ── Header ──────────────────────────────────────────────── */
.header { display: flex; align-items: baseline; gap: 16px; margin-bottom: 40px; }
.header h1 {
font-family: var(--prim);
font-size: 2rem;
font-weight: 700;
color: var(--accent);
letter-spacing: 0.12em;
text-transform: uppercase;
}
.header-sub { font-family: var(--prim); font-size: 0.9rem; color: var(--text-dim); letter-spacing: 0.1em; }
.header-line { flex: 1; height: 1px; background: linear-gradient(to right, var(--border-accent), transparent); }
/* ── Panels ──────────────────────────────────────────────── */
.panel { background: var(--bg); border: 1px solid var(--border); border-radius: 12px; padding: 24px; margin-bottom: 16px; }
.panel-label { font-family: var(--prim); font-size: 0.8rem; letter-spacing: 0.18em; color: var(--text-dim); text-transform: uppercase; margin-bottom: 14px; }
/* ── Drop Zone ───────────────────────────────────────────── */
#drop-zone {
border: 1px dashed var(--border-accent);
border-radius: 10px;
cursor: pointer;
transition: all 0.25s ease;
background: var(--accent-dim);
text-align: center;
font-family: var(--prim);
font-size: 1.2rem;
color: var(--accent);
margin: 10px auto 20px;
width: 30%;
padding: 10px;
overflow-wrap: break-word; /* Allows breaking long filenames */
word-wrap: break-word; /* Legacy support */
box-sizing: border-box; /* Ensures padding doesn't push width past 30% */
}
#drop-zone .dz-hint { font-size: 0.8rem; color: var(--text-dim); margin-top: 8px; letter-spacing: 0.05em; }
#drop-zone.hover, #drop-zone:hover { background: rgba(0,255,136,0.18); border-style: solid; border-color: var(--accent); box-shadow: 0 0 20px var(--accent-glow); }
#drop-zone.loaded { border-style: solid; border-color: var(--accent); background: rgba(0,255,136,0.06); }
/* ── Textarea ────────────────────────────────────────────── */
textarea {
width: 100%; height: 250px;
background: var(--bg); color: var(--text);
border: 1px solid var(--border); border-radius: 10px;
padding: 14px 16px;
font-family: var(--scnd); font-size: 1rem; line-height: 1.7;
resize: vertical; transition: border 0.2s ease;
}
textarea:focus { outline: none; border-color: var(--border-accent); box-shadow: 0 0 0 3px var(--accent-dim); }
textarea::placeholder { color: var(--text-dim); }
/* ── Mode Selector ───────────────────────────────────────── */
.mode-selector { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 16px; text-align: center; }
.mode-selector.three-col { grid-template-columns: 1fr 1fr 1fr; }
.mode-option { position: relative; }
.mode-option input[type="radio"] { position: absolute; opacity: 0; width: 0; }
.mode-option label {
display: flex; flex-direction: column; gap: 6px;
padding: 14px 16px;
border: 1px solid var(--border); border-radius: 10px;
cursor: pointer; transition: all 0.2s ease;
background: var(--surface-2);
}
.mode-option label:hover { border-color: var(--border-accent); }
.mode-option input:checked + label { border-color: var(--accent); background: var(--accent-dim); box-shadow: 0 0 12px var(--accent-glow); }
.mode-title { font-family: var(--prim); font-size: 1.2rem; color: var(--accent); font-weight: 700; letter-spacing: 0.05em; }
.mode-desc { font-size: 0.9rem; color: var(--text-dim); line-height: 1.4; }
/* Sub-engine selector */
#engine-row {
display: none; /* toggled by JS when snap is selected */
margin-top: 10px;
}
#engine-row.visible { display: block; }
.engine-label { font-family: var(--prim); font-size: 0.8rem; color: var(--text-dim); letter-spacing: 0.15em; text-transform: uppercase; margin: 14px auto; }
/* ── Buttons ─────────────────────────────────────────────── */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
border: none; padding: 11px 22px;
font-family: var(--prim); font-size: 1rem; font-weight: 700;
letter-spacing: 0.1em; text-transform: uppercase;
border-radius: 6px; cursor: pointer; transition: all 0.2s ease; white-space: nowrap;
}
.btn-primary { background: var(--accent); color: #000; }
.btn-primary:hover:not(:disabled) { box-shadow: 0 0 20px var(--accent-glow); transform: translateY(-1px); }
.btn-ghost { background: transparent; color: var(--accent); border: 1px solid var(--border-accent); }
.btn-ghost:hover:not(:disabled) { background: var(--accent-dim); }
.btn-nav { background: transparent; color: #7d776b; border: 1px solid #a16d0f; }
.btn-nav:hover:not(:disabled) { box-shadow: 0 0 20px rgba(255,255,255,0.3); }
.btn-muted { background: var(--surface-2); color: var(--text-dim); border: 1px solid var(--border); }
.btn-muted:hover:not(:disabled) { color: var(--text); border-color: var(--border-accent); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; transform: none !important; box-shadow: none !important; }
.btn-full { width: 100%; }
/* ── Stage (session) ─────────────────────────────────────── */
.stage {
background: transparent; border: 1px solid var(--border-accent); border-radius: 12px;
padding: 32px 32px 24px; text-align: center; margin-bottom: 16px;
box-shadow: 0 0 40px rgba(0,255,136,0.06), inset 0 0 60px rgba(0,0,0,0.5);
position: relative; overflow: hidden;
}
.stage::before {
content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
background: linear-gradient(to right, transparent, var(--accent), transparent); opacity: 0.6;
}
#curr-line {
font-family: var(--scnd); font-weight: 500;
font-size: clamp(1.4rem, 4vw, 2rem); color: #807b7b;
text-shadow: 0 0 30px var(--accent-glow);
min-height: 2.5rem; line-height: 1.3; letter-spacing: -0.01em;
}
.tap-hint { font-family: var(--prim); font-size: 0.8rem; color: var(--text-dim); margin-top: 16px; letter-spacing: 0.12em; }
.tap-hint kbd { background: var(--surface-2); border: 1px solid var(--border); border-radius: 4px; padding: 2px 7px; font-family: var(--prim); color: var(--accent); }
/* ── Playback bar ────────────────────────────────────────── */
.playback-bar {
display: flex; align-items: center; gap: 12px;
margin-top: 20px; padding-top: 16px;
border-top: 1px solid var(--border);
}
.pb-btn {
background: none; border: 1px solid var(--border-accent);
color: var(--accent); border-radius: 6px;
width: 34px; height: 34px; cursor: pointer;
font-size: 1rem; display: flex; align-items: center; justify-content: center;
transition: all 0.15s ease; flex-shrink: 0;
}
.pb-btn:hover { background: var(--accent-dim); }
/* Seek track — clicking sets playback position */
.seek-track {
flex: 1; height: 3px; background: var(--surface-2);
border-radius: 2px; position: relative; cursor: pointer;
}
.seek-fill { height: 100%; background: var(--accent); width: 0%; border-radius: 2px; box-shadow: 0 0 6px var(--accent); pointer-events: none; }
.pb-time { font-family: var(--prim); font-size: 0.8rem; color: var(--text-dim); flex-shrink: 0; min-width: 70px; text-align: right; }
/* ── Progress + stats (tap progress, separate from seek) ─── */
.progress-track { width: 100%; height: 2px; background: var(--surface-2); border-radius: 1px; margin: 20px 0 12px; overflow: hidden; }
.progress-fill { height: 100%; background: var(--accent); width: 0%; transition: width 0.3s ease; box-shadow: 0 0 8px var(--accent); }
.stats-row { display: flex; justify-content: space-between; align-items: center; font-family: var(--prim); font-size: 0.9rem; color: var(--text-dim); }
.stat-val { color: var(--accent); font-weight: 700; }
.bpm-badge { background: var(--accent-dim); border: 1px solid var(--border-accent); padding: 3px 10px; border-radius: 4px; letter-spacing: 0.08em; color: var(--accent); }
.session-controls { display: grid; grid-template-columns: 1fr 1fr 1fr auto; gap: 10px; }
/* ── Floating preview ────────────────────────────────────────
*
* Implemented as position:fixed so it floats over session content
* without displacing layout. The user can drag it by the title bar
* and resize it by the bottom-right corner handle.
*/
#preview-wrap {
display: none;
position: fixed; /* fixed so it doesn't scroll with page */
top: 60px; right: 24px;
width: 400px;
min-width: 300px; min-height: 300px;
background: var(--surface);
border: 1px solid var(--border-accent);
border-radius: 12px;
box-shadow: 0 8px 40px rgba(0,0,0,0.7), 0 0 0 1px rgba(0,255,136,0.08);
z-index: 200;
flex-direction: column; /* flex column so editor and preview share vertical space */
overflow: hidden;
/* resize handle is a separate element; CSS resize disabled to
allow custom handle with better UX on touch */
}
#preview-wrap.visible { display: flex; }
/* Drag handle — entire titlebar is draggable */
#preview-titlebar {
display: flex; align-items: center; justify-content: space-between;
padding: 8px 12px;
background: var(--surface-2);
border-bottom: 1px solid var(--border);
cursor: grab; user-select: none; touch-action: none;
flex-shrink: 0;
}
#preview-titlebar:active { cursor: grabbing; }
#preview-title {
font-family: var(--prim); font-size: 0.8rem;
color: var(--accent); letter-spacing: 0.12em;
}
.preview-titlebar-btns { display: flex; gap: 6px; align-items: center; }
.ptb-btn {
background: none; border: 1px solid var(--border);
color: var(--text-dim); border-radius: 4px;
font-family: var(--prim); font-size: 0.8rem;
padding: 2px 7px; cursor: pointer; transition: all 0.15s ease;
white-space: nowrap;
}
.ptb-btn:hover { border-color: var(--border-accent); color: var(--accent); }
.ptb-btn.accent { border-color: var(--border-accent); color: var(--accent); }
.ptb-btn.btn-active { background: var(--accent-dim); color: var(--accent); border-color: var(--border-accent); }
/* Timing editor — scrollable list of result rows.
Each row lets the user adjust text and timestamps.
Height is capped so the preview gets meaningful space below. */
#preview-editor {
flex-shrink: 0;
max-height: 220px;
overflow-y: auto;
border-bottom: 1px solid var(--border);
/* custom scrollbar to match theme */
scrollbar-width: thin;
scrollbar-color: var(--border-accent) transparent;
}
#preview-editor::-webkit-scrollbar { width: 4px; }
#preview-editor::-webkit-scrollbar-thumb { background: var(--border-accent); border-radius: 2px; }
/* Individual result row inside the editor */
.edit-row {
display: grid;
grid-template-columns: 20px 1fr 70px 20px;
grid-template-rows: auto auto;
gap: 5px 6px; align-items: center;
padding: 5px 10px;
border-bottom: 1px solid var(--border);
font-family: var(--prim);
}
.edit-row:last-child { border-bottom: none; }
.edit-row input[type="text"],
.edit-row input[type="number"] {
background: var(--bg); color: var(--text);
border: 1px solid var(--border); border-radius: 4px;
padding: 3px 5px; font-family: var(--scnd);
font-size: 0.9rem; width: 100%;
}
.edit-row input:focus { outline: none; border-color: var(--border-accent); }
/* Delete button for a result row */
.edit-row-del {
background: none; border: none; color: var(--text-dim);
cursor: pointer; font-size: 0.75rem; padding: 0;
line-height: 1; transition: color 0.15s;
}
.edit-row-del:hover { color: #ff4d6a; }
.edit-row.row-active { background: var(--accent-dim); }
.edit-row.row-dimmed { opacity: 0.25; }
.edit-num { font-family: var(--prim); font-size: 0.8rem; color: var(--text-dim); text-align:center; }
.edit-row.row-active .edit-num { color: var(--accent); font-weight: 700; }
.edit-timeline {
-webkit-appearance: none; appearance: none;
grid-column: 1 / -1; width: 100%; height: 3px;
background: var(--surface-2); border-radius: 2px;
outline: none; cursor: pointer; accent-color: var(--accent); margin-top:1px;
}
.edit-timeline::-webkit-slider-thumb {
-webkit-appearance: none; width: 12px; height: 12px; border-radius: 50%;
background: var(--accent); box-shadow: 0 0 4px var(--accent-glow); cursor: grab;
}
.edit-timeline:active::-webkit-slider-thumb { cursor: grabbing; }
.edit-timeline::-moz-range-thumb {
width: 12px; height: 12px; border: none; border-radius: 50%; background: var(--accent);
}
/* Empty state shown when no results exist yet */
#preview-editor-empty {
padding: 12px; text-align: center;
font-family: var(--prim); font-size: 0.8rem; color: var(--text-dim);
}
#preview-canvas { width:100%; display:block; background:#000; flex:1; min-height:200px; cursor:crosshair; }
/* Resize handle — bottom-right corner drag target.
Kept small (16×16) so it doesn't obscure the frame content.
cursor:nwse-resize signals resize intent to the user. */
#preview-resize {
position: absolute; bottom: 0; right: 0;
width: 16px; height: 16px; cursor: nwse-resize; touch-action: none;
/* Subtle visual indicator — two diagonal lines */
background: linear-gradient(
135deg,
transparent 30%, var(--border-accent) 30%,
var(--border-accent) 40%, transparent 40%,
transparent 60%, var(--border-accent) 60%,
var(--border-accent) 70%, transparent 70%
);
}
/* ── Analysis overlay ────────────────────────────────────── */
#analysis-overlay {
display: none; position: fixed; inset: 0;
background: rgba(8,8,8,0.94); z-index: 100;
align-items: center; justify-content: center; flex-direction: column; gap: 20px;
}
#analysis-overlay.visible { display: flex; }
.analysis-label { font-family: var(--prim); font-size: 1rem; color: var(--accent); letter-spacing: 0.14em; }
.analysis-bar-track { width: 280px; height: 3px; background: var(--surface-2); border-radius: 2px; overflow: hidden; }
.analysis-bar-fill { height: 100%; background: var(--accent); width: 0%; transition: width 0.15s ease; box-shadow: 0 0 8px var(--accent); }
.analysis-sub { font-family: var(--prim); font-size: 0.9rem; color: var(--text-dim); letter-spacing: 0.1em; }
/* Spinner — shown when no incremental progress.
Visibility toggled by showOverlay(engine). */
.analysis-spinner {
width: 28px; height: 28px;
border: 2px solid rgba(0,255,136,0.15);
border-top-color: var(--accent);
border-radius: 50%;
animation: spin 0.8s linear infinite;
box-shadow: 0 0 10px var(--accent-glow);
}
@keyframes spin { to { transform: rotate(360deg); } }
#analysis-spinner-wrap, #analysis-bar-wrap { display: none; }
/* ── Param panels ─────────────────────────────────────────── */
.param-panel { display: none; margin-top: 12px; }
.param-panel.visible { display: block; }
.param-row {
display: grid;
grid-template-columns: 1fr auto auto;
align-items: center;
gap: 8px 12px;
padding: 8px 0;
border-bottom: 1px solid var(--border);
}
.param-row:last-child { border-bottom: none; }
.param-label-wrap { display: flex; align-items: center; gap: 6px; position: relative; }
.param-label { font-family: var(--prim); font-size: 0.9rem; color: var(--text-dim); letter-spacing: 0.06em; }
/* Tooltip: pure CSS on ⓘ hover. absolute positioning keeps it
inside scroll flow without z-index fights with fixed elements. */
.param-hint-icon { font-size: 0.75rem; color: var(--text-dim); cursor: default; position: relative; }
.param-hint-icon:hover .param-tooltip { display: block; }
.param-tooltip {
display: none;
position: absolute;
left: 20px; top: -100px;
width: 220px;
background: var(--surface-2);
border: 1px solid var(--border-accent);
border-radius: 6px;
padding: 8px 10px;
font-family: var(--scnd);
font-size: 0.8rem;
color: var(--text);
line-height: 1.5;
z-index: 50;
pointer-events: none;
box-shadow: 0 4px 20px rgba(0,0,0,0.6);
}
.param-slider {
-webkit-appearance: none; appearance: none;
width: 120px; height: 3px;
background: var(--surface-2); border-radius: 2px;
outline: none; cursor: pointer;
}
.param-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px; height: 12px;
border-radius: 50%;
background: var(--accent);
box-shadow: 0 0 6px var(--accent-glow);
cursor: pointer;
}
.param-slider::-moz-range-thumb {
width: 12px; height: 12px; border: none;
border-radius: 50%; background: var(--accent);
}
.param-value { font-family: var(--prim); font-size: 0.8rem; color: var(--accent); min-width: 36px; text-align: right; }
.toggle-switch { grid-column: span 2; justify-self:end; }
/* Selector for non-slider params */
.model-selector { display: flex; gap: 8px; margin-top: 4px; text-align: center;}
.model-option { position: relative; flex: 1; }
.model-option input[type="radio"] { position: absolute; opacity: 0; width: 0; }
.model-option label {
display: flex; flex-direction: column; gap: 3px;
padding: 10px 12px;
border: 1px solid var(--border); border-radius: 8px;
cursor: pointer; transition: all 0.15s ease; background: var(--surface-2);
}
.model-option label:hover { border-color: var(--border-accent); }
.model-option input:checked + label { border-color: var(--accent); background: var(--accent-dim); }
.model-name { font-family: var(--prim); font-size: 0.9rem; color: var(--accent); font-weight: 700; }
.model-desc { font-size: 0.8rem; color: var(--text-dim); line-height: 1.3; }
/* Session params collapsible */
#session-params { margin-bottom: 12px; border: 1px solid var(--border); border-radius: 10px; overflow: hidden; display: none; }
#session-params.visible { display: block; }
#session-params-toggle {
width: 100%; display: flex; align-items: center; justify-content: space-between;
padding: 10px 16px; background: var(--surface-2); border: none; cursor: pointer;
font-family: var(--prim); font-size: 0.8rem; color: var(--text-dim);
letter-spacing: 0.12em; text-transform: uppercase; transition: color 0.15s ease;
}
#session-params-toggle:hover { color: var(--accent); }
#session-params-toggle .arrow { transition: transform 0.2s ease; display: inline-block; }
#session-params-toggle.open .arrow { transform: rotate(180deg); }
#session-params-body { display: none; padding: 12px 16px; background: var(--surface); }
#session-params-body.open { display: block; }
.toggle-switch { position: relative; display: inline-flex; align-items: center; cursor: pointer; }
.toggle-switch input { position: absolute; opacity: 0; width: 0; }
.toggle-track {
width: 36px; height: 20px;
background: var(--surface-2); border: 1px solid var(--border);
border-radius: 10px; transition: all 0.2s ease; position: relative;
}
.toggle-track::after {
content: ''; position: absolute;
top: 3px; left: 3px;
width: 12px; height: 12px;
border-radius: 50%; background: var(--text-dim);
transition: all 0.2s ease;
}
.toggle-switch input:checked + .toggle-track {
background: var(--accent-dim); border-color: var(--border-accent);
}
.toggle-switch input:checked + .toggle-track::after {
transform: translateX(16px); background: var(--accent);
box-shadow: 0 0 6px var(--accent-glow);
}
/* ── Output view ─────────────────────────────────────────── */
.output-header { display: grid; align-items: center; gap: 12px; margin-bottom: 20px; text-align:center; }
.output-header h2 { font-family: var(--prim); font-size: 1.3rem; font-weight: 700; letter-spacing: 0.1em; color: var(--accent); }
.output-summary { font-size: 0.9rem; color: var(--text-dim); font-family: var(--prim); }
.export-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 14px; }
.export-controls {margin:16px auto; display:grid; gap: 10px;}
/* ── Toast ───────────────────────────────────────────────── */
#toast {
position: fixed; top: 24px; right: 24px;
background: var(--surface); border: 1px solid var(--border-accent);
color: var(--accent); font-family: var(--prim); font-size: 1.5rem;
padding: 10px 16px; border-radius: 6px;
opacity: 0; transform: translateY(6px); transition: all 0.2s ease;
pointer-events: none; letter-spacing: 0.06em;
}
#toast.show { opacity: 1; transform: translateY(0); }
.divider { height: 1px; background: var(--border); margin: 20px 0; }
/* ── Mobile layout ─────────────────────────────────────────────── */
@media (max-width: 600px) {
.mode-selector, .mode-selector.three-col,
.export-grid, .export-controls { grid-template-columns: 1fr; }
#preview-wrap {
width: 350px;
min-height: 250px;
}
/* session buttons */
.session-controls { grid-template-columns: 1fr; gap: 8px; }
/* Full-width drop zone on mobile */
#drop-zone { width: 80%; padding: 20px; }
/* Shorter sliders to fit narrow viewport */
.param-slider { width: 70px; }
/* Larger tap target for the stage */
.stage { padding: 24px 16px; cursor: pointer; user-select: none; }
/* Hide keyboard hint, show touch hint */
.tap-hint .kb-hint { display: none; }
.tap-hint .touch-hint { display: inline; }
/* Session params panel: full width labels */
.param-row { grid-template-columns: 1fr auto; }
.param-value { min-width: 28px; }
}
@media (min-width: 601px) {
.tap-hint .touch-hint { display: none; }
.tap-hint .kb-hint { display: inline; }
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>FIZX Studio</h1>
<span class="header-sub">@AmMoPy</span>
<div class="header-line"></div>
</div>
<div id="setup-view">
<textarea id="lyrics-input" placeholder="Paste lyrics here (Optional), one line per entry: Yeah... They said I couldn't build it 4 gigs of RAM, 2 cores, and a dream... QUICK START: drop audio > select mode > start session > tap spacebar > preview > export"></textarea>
<div id="drop-zone">
DROP AUDIO FILE
<div class="dz-hint">MP3 · WAV · OGG · FLAC</div>
</div>
<input type="file" id="file-input" style="display:none" accept="audio/*">
<div class="panel-label">Extraction Mode</div>
<div class="mode-selector">
<div class="mode-option">
<input type="radio" name="mode" id="mode-buffer" value="buffer" checked>
<label for="mode-buffer">
<span class="mode-title">Lyrics Only</span>
<span class="mode-desc">Manually capture lyrics timing</span>
</label>
</div>
<div class="mode-option">
<input type="radio" name="mode" id="mode-snap" value="snap">
<label for="mode-snap">
<span class="mode-title">Lyrics & Beats</span>
<span class="mode-desc">Snap lyrics to the nearest detected beat</span>
</label>
</div>
</div>
<!-- Engine sub-row — only relevant when snap mode is active -->
<div id="engine-row">
<div class="divider"></div>
<div class="engine-label">Analysis Engine</div>
<!-- injected by initEngineSelector() from ENGINE_REGISTRY -->
<div id="engine-selector" class="mode-selector three-col"></div>
<!-- Param panels — one per engine, shown/hidden by updateParamVisibility()
Rendered by renderParamPanel() and initialized by initParamPanels() from ENGINE_REGISTRY
Adding a new tunable: add one entry to ENGINE_REGISTRY, no HTML changes needed -->
<div id="engine-param-panels"></div>
<div class="divider"></div>
<!-- Vocal stem proxy params -->
<div class="engine-label" id="vocal-params-label" style="display:none;">VOCAL ANALYSIS PARAMS</div>
<div class="param-panel" id="params-vocal"></div>
</div>
<div style="margin-top:16px; display:flex; justify-content:flex-end;">
<button id="start-btn" class="btn btn-primary">START SESSION</button>
</div>
</div>
<div id="session-view" style="display:none">
<div class="stage">
<div id="curr-line">READY — PRESS SPACEBAR</div>
<div class="tap-hint">
<span class="kb-hint">TAP <kbd>SPACE</kbd></span>
<span class="touch-hint">TAP <kbd>SCREEN</kbd></span>
ON EACH LYRIC · <kbd>FINISH</kbd> WHEN DONE
</div>
<!-- Playback bar: play/pause toggles, stop resets position to 0 -->
<div class="playback-bar">
<button class="pb-btn" id="pb-play" title="Play / Pause"></button>
<button class="pb-btn" id="pb-stop" title="Stop"></button>
<div class="seek-track" id="seek-track">
<div class="seek-fill" id="seek-fill"></div>
</div>
<span class="pb-time" id="time-display">0:00 / 0:00</span>
</div>
<!-- Tap progress: separate from seek, tracks lyric lines tapped -->
<div class="progress-track"><div class="progress-fill" id="progress-fill"></div></div>
<div class="stats-row">
<span><span class="stat-val" id="line-counter">0</span> / <span id="total-lines">—</span> tapped</span>
<button class="btn btn-muted" id="reset-tap-btn" title="Reset lyrics timing"></button>
<span id="bpm-display" class="bpm-badge">BPM —</span>
</div>
</div>
<!-- Session params — mirrors setup engine params, visible when mode=snap.
Collapsible to keep the session screen uncluttered by default -->
<div id="session-params">
<button id="session-params-toggle">
ANALYSIS PARAMS <span class="arrow">▾</span>
</button>
<div id="session-params-body">
<div class="engine-label">Analysis Engine</div>
<!-- Mirrors of setup param panels -->
<div id="session-param-panels"></div>
<div class="divider"></div>
<!-- Vocal params always visible in session when snap mode active -->
<div id="session-params-vocal" class="param-panel visible"></div>
</div>
</div>
<!-- session-controls auto-assign is disabled until beats are available (set in startBtn handler). -->
<div class="session-controls">
<button id="preview-btn" class="btn btn-ghost"></button>
<button id="auto-assign-btn" class="btn btn-ghost" disabled title="Approximate lyrics timing for manual adjustment"></button>
<button id="reanalyze-btn" class="btn btn-ghost"></button>
<button id="finish-btn" class="btn btn-nav">FINISH & EXPORT</button>
<button id="cancel-btn" class="btn btn-muted">CANCEL</button>
</div>
<!-- Floating preview panel -->
<div id="preview-wrap">
<!-- Titlebar doubles as drag handle (mousedown/touchstart) -->
<div id="preview-titlebar" title="LYRICS SYNC EDITOR">
<span id="preview-title">PREVIEW</span>
<div class="preview-titlebar-btns">
<button class="ptb-btn accent" id="preview-refresh-btn" title="Apply Lyrics Timing"></button>
<button class="ptb-btn" id="preview-add-btn" title="Insert New Lyrics Line">+</button>
<button class="ptb-btn" id="toggle-onsets-btn" title="Show/hide onset dots"></button>
<button class="ptb-btn" id="zoom-reset-btn" title="Reset zoom (or double-click canvas)">1:1</button>
<button class="ptb-btn" id="close-preview-btn" title="Exit">x</button>
</div>
</div>
<!-- Single canvas: lyric text (top) + waveform/onsets/lines (bottom).
All interaction (drag lyric lines, click to seek) handled in JS. -->
<canvas id="preview-canvas"></canvas>
<!-- Timing editor: renders one row per state.results entry.
Changes update state.results in place immediately (no buffer). -->
<div id="preview-editor">
<div id="preview-editor-empty">No lyrics tapped yet</div>
</div>
<!-- Bottom-right resize drag target -->
<div id="preview-resize"></div>
</div>
</div>
<div id="output-view" style="display:none">
<div class="panel">
<div class="output-header">
<h2>EXPORT</h2>
<span class="output-summary" id="export-summary"></span>
</div>
<button id="generate-fizx-btn" class="btn btn-ghost btn-full" style="height:52px;">GENERATE FIZIX.HTML</button>
<div class="divider"></div>
<div class="export-grid">
<button id="download-beats-btn" class="btn btn-ghost"></button>
<button id="download-lyrics-btn" class="btn btn-ghost"></button>
<button id="copy-beats-btn" class="btn btn-muted"></button>
<button id="copy-lyrics-btn" class="btn btn-muted"></button>
</div>
<div class="divider"></div>
<div class="export-controls">
<button id="cnt-session-btn" class="btn btn-nav"></button>
<button id="new-session-btn" class="btn btn-primary"></button>
</div>
</div>
</div>
</div>
<!-- Analysis overlay — covers the page during audio analysis -->
<div id="analysis-overlay">
<div class="analysis-label" id="analysis-label">ANALYZING AUDIO</div>
<!-- Bar: shown for real progress from Worker -->
<div id="analysis-bar-wrap">
<div class="analysis-bar-track"><div class="analysis-bar-fill" id="analysis-bar"></div></div>
</div>
<!-- Spinner: shown for synchronous/blocking analysis, no progress ticks -->
<div id="analysis-spinner-wrap">
<div class="analysis-spinner"></div>
</div>
<div class="analysis-sub" id="analysis-sub">decoding…</div>
</div>
<div id="toast"></div>
<script>
// ═══════════════════════════════════════════════════════════════════
// INJECTED BY compile.py — do not edit this line.
// The template still contains [[BEATS_DATA]], [[LYRICS_DATA]],
// [[BPM_DATA]] as placeholders; those are substituted at export time.
// ═══════════════════════════════════════════════════════════════════
const FIZX_TEMPLATE = decodeURIComponent("%3C%21DOCTYPE%20html%3E%0A%3Chtml%20lang%3D%22en%22%3E%0A%3Chead%3E%0A%20%20%20%20%3Cmeta%20charset%3D%22UTF-8%22%3E%0A%20%20%20%20%3Cmeta%20name%3D%22viewport%22%20content%3D%22width%3Ddevice-width%2C%20initial-scale%3D1.0%2C%20user-scalable%3Dno%2C%20viewport-fit%3Dcover%22%3E%0A%20%20%20%20%3Ctitle%3EFIZX%20%C2%B7%20DOX_BEST%3C/title%3E%0A%20%20%20%20%3C%21--%20Self-hosted%20subsetted%20fonts%20%E2%80%94%20no%20network%20request%2C%20works%20fully%20offline.%0A%20%20%20%20%20%20%20%20%20Base64%20strings%20injected%20by%20compile.py%20from%20generated%20.b64%20files%20in%20assets/fonts/.--%3E%20%20%20%20%0A%20%20%20%20%3Cstyle%3E%0A%20%20%20%20%20%20%20%20/%2A%20%40%40FONTS%40%40%20%2A/%0A%40font-face%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20%27merri%27%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20normal%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-style%3A%20normal%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-display%3A%20block%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20src%3A%20url%28%27data%3Afont/woff2%3Bbase64%2Cd09GMgABAAAAAB64AA8AAAAAMlAAAB5bAAIZmgAAAAAAAAAAAAAAAAAAAAAAAAAAGigbIBwwBmA/U1RBVIEOAIFeEQgK0XDEKQE2AiQDgyQLgVQABCAFhgAHIBuyKbOiZpFe9hT/ZQI3hkJ9YB9cEoegjcQxaLfTRSIYKG7rscFl%2BM5efwP8EuHSc8v5Yot9nDMUjYen/b6dmbn3/fWEWyhuUbypto1sQ7qHTIg0QrNQIGTxGZ7fZg8BXSsCYtQUc85EnViEgGBQKij1%2BWIFpbMw59zMWKqbzpVuLnV1cxHpuQ4v0sI5Tcff5D4FQjUCo3akU%2B9s/pdd5rkz4LB0epCsAJH0BWWV1hBhiSYnmtv/AObgDVhnaeHVxIbB6TB43/3y144B0%2BadGhB84XbOT5ixwrQ5gqlpzW5o9Y7Sy/gH9cKBxphks5fcZq/v5z%2B5/ZpQ7nvuS6X0LlQv3SIZPMriLYNDMWgUl01thJFJAaLQ1tKiViguSX4315YCmOK0XaEgOOsuXBq7myCc2XKI8wUm2LKHOHQkNgEsgALo/2tAqOZevCG/HO5KiSYX2N9V6vKBvQTn2BG/lTZoIOfFBODFbNX1nbAq0yfNAa317APP69RpuiM4xXHjoMnEv1kAxt%2B2J15Yxcm8BiCoJX4rC6xQ88ZXs4vR7I1MHQIAs1d1mQgEQDx4qC1F2DL6P5kBCHfIZu8oi1hz5Y8GmRtmVNr8I0A3DzEbKqBtI6CsZKk3SG51DwCCEVGM2kKfGBJiZke5pK9gBl/dMj5CUKSAZMlWymDC4VU4p2i3wex9vVV8kVFJKGXLUYa6RyOgnwL6lGnHAn2n9pFnrR6cAQj2gB2AQ1ikYy53P6YkHFIqyUR4%2BNhk6OLQxMheKQNEToFFLEGsRKm4MglICMG5MTsgryCrLj50/RiYugDgGje5p/d61LgG6qi%2BQYX5HCVXHpNmE7Yjokatd2IVgBa8KnQdHY%2BQnRG8gkS27knkK4vMmAdfufYaklOLxFPwYSqMfEfKceigIxd1ioiShyErcmN6V4aK2UdyKWsO0hrKUOXd7kKLMsjYJo9Y955GA1Ecarvw/d/PNLW/de2fJXTtbWrabuftNAiIndDBuFTU4pBEmUrqHDShsgTLkHGBCc8c61FrywGBP9s1xbgyMRqWj6%2Bdzp7HaaJVN0J4jYmNfwzXFzCvZZXu33gg9uCoqiztOhEdQpguy6GE9btWcn0NjJ1sSPVwtm0/7Q6/Ukj57O2JsWQri61D5B4uGETkhfIlDczWotrk%2BnnkriwXkuhKb8qLcm9Q4BlgIXcU6uESPROekVh6guFkiEyXH%2BdY9O/MuDqiIkqSIg5ARzxQfHc21yARLPirWTvlHDRX2gALADI2PJZvcl0qkmQZplpG5UixU3oLGUJIWGNVE5v3CkyIEFs1RAjirDIdiQ/mTKyrkfLc9yCnv%2BlSwtoZ4hp6zwgkRy0f3zBHeonlI1XS5%2B0W6yVX%2BB4urcwsFpuFEWxH7etwZ/meMkIcso2CVVhonM4TF2OY1JjuUOtXv%2BT4KVKKNqzEoQRh%2BY6O4T7P4jk6yb6QSVJ5sY0WarRp7lBpxc0BCSVDij5ifqOlyiFaTjfMbCHmsoDwzmR52U1aWHylJaUbEygrmbDqoI3gOTwWrSSBjdqTULJCF%2BY53sKOZFSuG43KmOe4KTGbKiNEYews7XNEnAKh58VgOszcj5hPn4R20dKJS8xQhS5hVaHu4Hzow8qJCXAKLyhVJUcV0hnQavkeERyBVHkc/mB7UFgIWRTW8KUfBKIUFI8J98iLqXS16k6TDvKSx6G8B0N0mN8bUFGiJ5Ioo0TI81mXswnBNyILdNAiHBrdjaSzOxNv77b65mfHYRRIdzEcIqzzd0wdWQ5jg/aonSIgaMVeZqjtwdJBoKjCiTfYYU5zcdBQhgWVrkV4bpSo8Fm8iHAw3M1zhmjfdv42C5unxAwKSKQwKsK2GhTAj1xFjAMHBDyTkk71Cbfslg6MlM60qvsGnoMc/sw23HXbnZ18r7v0qrlIiBy/qh1%2BL%2BQFLB4TpYN4s6QceybbBcqg3BOTjwKRgU2sqfxy0kxgtNedtOYSdGzUBTnkntY%2Bg5R3y7VQA%2BB6evVNWH3qIkxwstu80jnbe6PX51xqJEALOfyi%2B3IlnwI9uBeBnlIRzruAQccYLeaIULaGjUefmcll9jS6xa7GoxyF/HClElsWF4H9OZdmcUsWGinB9GnWQneTge1rQ3GMRH0lw9TSLmLlstL97RxGTjSQwbNCnbv6HvS9phuc6%2BNQTjraJG0rPvahtQm/jx2k/GGu%2BEzaxcvXiUWXz0UmnLnX%2BTpwPb9F2lavaWLTnV5KByVGHEo7XsbgJDWUGEwKM8dFyltYvikD%2BaAF5Pi6RxqGfZt95jFlSX1WEfwpGNB%2Bq6h8SPCUIndqvM%2BWlMcdQLuGgpMd1E6EvwY96txvVMSacSa1M4OT%2BzlK/cjRjGt%2BT21P3Ly6QhCJinO7X3y04AcVRmHn4KDccI1fuhjBHKwNh8BZvcH0YIHDU9fZ%2BPRVSnT8e2BELq8i4m4pBrrUS0D6BDHq3oJbMYuzQbAyMRmztJWbrGvrA4fFupXx14CCd6ZkTEgpRlbORYrqjNg4Y04GZ%2BVGMZHUrOxUT1SnjNiIqeFmukvVQpRFbKrgLJJwGy12Vma/luFzHoxZyAVpt2Z1qtmEDM%2Bdc00EdDOZveh7OHqNHEdR94VfKJIUwBO/TB2upo1Dx%2BY90ygyaSOJdSIPvv3IhC/YcTWB7esdNsIOkH7Y49Lwe%2BhQA5gCcDoVdUI1/BqsqGg3XrMSqmMTSpPmKbZoip1Bss296Y3%2B8YDbYaoB4xwZhlJFp3uzR%2BPr64%2BGDjBJH1XpMUi/rt8oIkfv5X%2Bk4R9B99UDxoeDI7Vw5v5NtIE5ndY3/ReR0g5tq1eLw1mbEPe4SH3ycxUqEVjnLIzff8ZDD54X/xHVjBzyibjyqvL%2BGosihkVUZQ6XqzAp/w0k0NKnAxApNAudEdRWYNUoGUH6uBjZgEmDCA4HBVskSWJ93VRTNsKFamrNQfOkf0mZ/P7fyvPlNMwY%2B0JNh2urW4g6o2hLmcsSnfwHTG77TLC2Jpg8ZYhqMWoH9ZfNNMhCVlF/J1z0Pz9c9M7fgIXMoK1YuSdN7jmGhe4CBbl98szaFcO%2Bow9qwXftr4OW8JLwTBOlcUDb2ZVFYlJZvDx6Q0OmwsxgdFgIb8pNCR1urPrE3rdKgk5DRy%2BByp5S9lHIWFkFM6JesW5d/aQbC9Vt/n/GpHE8wrFplMtuVw1sahy3ZgA2%2BEoFb/CjIGvPWbHF7l05NjNX0fn9zIqleTgJBmueXef3338M62wzZuX5Do3VtfWB8/nTO7P1MbD5M63gapmZN7E9jiC1MeDBkCmFjxKiBUg3DLQNOzdAgNHhWhONcfgpXOrEVCJ66FCu54V7eHVvYRfiRWe108ONtC9rh68KGz2ZqEO8P4pjTeIQ4Lt2bogAG9kEFVLPfFkSxDG22557Jdd2d4yxZCVWjdQuD4cTIPCbdm6MAHvihJWUX/9ZFp2GJu7IuJtB3JtmEuGHhcYg8BO/A60%2BcFm%2BdON0ltPMeVz6Tm6PWS0BNm/HSFu9v13m2K22EG580KK2vFlPmlefPFBQScnHK9zWnfgTp2FoNObWkACvsCuwrceCD9qI3mAbeAfB/pj9KAG2hgcIc5uxkB00gmUfY%2B/BQkQIILVzhwgwCz5MmDuCl7Mg4GBemRbFzcBHIc2K0fsaKo5bwRx40tJ3J/If80TLgurhQ2lpGBAHbceG/Rjs4WHCSc6JUQL8Ae4n7H1pndOUP5qxQ9rCkATK8IrlBCqXWysalG6TtJ08/6JuEqdQALaBwV9tOHyaMPcnpoynNWfMgAntItzcMwJsazazhoYIQ2Cwa7DwHGER7tfdOIgCHcCif9S5VAudcTrz9djaEzmr3WrQL%2BpmsBASAnRoEw7gntSJ2UNwiQfyluYuwHnvJsB34M0EgE/Y6il812sMcOXTWAjM4O8a2Z1qTvp1X6r1wYvz2W13rA%2B3xD2bSLWbvPqjoD7VMNWXsXLugtz%2ByEBdxVRHuuuz83KXQ1vBt5Q%2B8oVhUuNctPDSKxV0Yu22jteo0p3OfMSPqmNLcVAjVBbWCaNo8i0caN8mHad1gFLsFYVKqwKJynxhRWRGqjJz9v3AMK1yjzPWBSnZiqgcb6ZV51aLRBZMPCdyjq9%2B44ewz/qtFKgotYnS2HvmVbJqvFJC31lbDdkd5CrY5dSaUE0guygGp1hoFZJAWcfcxOmIyVHgo%2BMAZc76DBZy/plYwWZ0q6Vh9e2cKvuZg/sKf5GBZBCGV%2B/zLPUyMkuvdEYKuS65lhH5DRERuniBRL05Kc3z4rro4syYNlW%2Bfs9j1QZ2qbnSmmkhWBxX5vrb5XcpmCBFOSlMxRInF2wWqHwZDm1xZSpOvSQuvLe67BIHMLXf4xfHDlY5428sx1exB/mLv5thoX8g8IfpzL8NpdZPXrYQYAI8ZvlHK2lGWbQuMKl0UMed24oYsYKvwm0E8Ptw/Lv2nhkWMErLhfHwOAGbup32ifUVOzO0iQAfg6sI9q8SdtsfTABG%2BlzYDj5CMLu9O3hR5KKX/wTvbSLAvvApOER1CHYwnfwze5YAP4SrCZavEs6a/5g1186esEYLwBHtcMGmvG4rWAgfswr6K/ifcQL8Ht5i1cWY7u3DQnzoMJbnGOV4DguZQ8BMe90mwKzYEl4IbySY2VB6GD0Ym2ECfB6utrCeMb05i4P6IJBNSvS9U6/rEFqkLdQsW6G/7CX0JRqq%2Bw8Ts8h6K5/doAif3MOQNueq8hqnkj64GxDsZJcMyCtFtyd6oZ%2BKVss8GpLROMxl7l5v0IsSWEu5i5wCT4ZSVytj1oYa1QfXR3iJO8GpSFkuk%2B0vEXkCoP%2BYEOGCvqYwVUSUunG0ZwpWfz5ZHT/bPXpHXpcxapNDvATQQ6fngxVEFok5%2B0SznJ7ekye8AzYY9cUfXyMCgfjRp50F/w8qY91rHrohaYjS4JNsdyzwq15xKfIh1dQ5eY2Scxw0gVLfQ6Li4lBmWI61yO02MR6hobWvAD/wSRspq%2BsZV5vHGrBg24FA%2BeIswwV8osp5wThAP9elNOThyL4O4TTLNHb1SuT6EIjICgx5/IeaSId6FeLriFHEhvhTdWLggR993JGLGquLvdvYe5yvnrp6BXUyHCVW70YoVmxN0JdHCoU7yXdcEkkVtI2eqpEnwMwJW5a6sThI4sJI8GgGMUA5gpEdBeKjrrAyZntV%2BTBD7MtBg5P2EIEKvvLcXWX8UbNaM%2BjhMiUgWlYc8tRzQwqZlIDMxFiXtz6C0JUrEljMA6leWdu1JXU7b6UUqbZ4liSVRsSG773zinXSubTowDW4zjPWxO1%2BKzEa9i5%2BVTNC45aLkmXdIwUPIjILod3k/DxusHMM7Ap9dqagtim9YBqLnF4TZIJtL8pprpeQdOkhsVHZAaEaucDXkM0CNwoAOn07LpHuYnwc3Fuww68yZiOJa1ivCLy0xbfRDHoMlZv55do0imgiUq6TMjBsvh4D3YcADdqCYXY5Z/i7Gt%2B6v8EUAmmvTMEkvFA6d8G2jqKxHi8AzsU9xNwAV%2BP9oWWkQ/KSEgpgQ%2BUYgFYN4BLJLqg9T5GrZ971W0nfSgesHVaWhpWZQq%2BhcozVicu3UeKOFgwELqgBWrUFlxjugt4D0A/GTCFLCCRCqzHMFqdsG6LL0nhAH22M2xBAz6mKXh7Hr2H0eUfLygKj8%2B13Fvs3Y6CHUAlmRa5Di4SR7AvbiVa%2BlppEGcWu8GnEQHNQFcY317pRQEuK28JJma/BQOCqWv2dWtAhNB9ZE7eJFG9oVIRc3BzQsEx5Hyox9c91aBTTkgJznZVBoT/rrRwaUDybRfe7O6ZjVONP%2B7JRwxD34L9CnLSaE6KSjaE3UaLkilTaxd50%2B3FqTU50nMSQSr/Ql%2BEwTq/JZQHPZ4oWPEC7tDll2bgYa4p%2BpEbX%2BfI0ZwtKPp8yxM12jt2S1/nlmXqhRq9cwSsW%2BkF9qpQ7Y1qvbbysTN%2BwVQpHlk/Q11e5bkD%2BTPIIT0dvwygpZR4czTld6c8TYw0YxOCBqCqLT7DdYgVAH12Hl3C4IXJnhh/p50GtO1PeqxTdROxA9MefW6MALEXH0pgWp2xzosmQK2ocXRUUJ0uoDhPYoYfvHdnjwyt0iyUdPPw7RgZu4jnddiWuS0A1ccN%2BOjGF4MdVunux/UJY8fVUBRFyqVVQq1KSs2oPRWuSKi3FtkW/F7t3VpGjTd1PSYj%2BVG8Gm18URLWhWJeSlXEhOTFKVdsJHmjH5ehSa/5rTXH5e2PPEeQjuJeQiBcm6Y0cWbXM7BxThYffKoPtf3aiN/k%2Bax1B2Ffz4MxARlRhXH0Aa2rjhefUfAeRhQ0qek%2BuY50onBeaSuQF9H5rdUxaaYN%2B8FEPxAQ5Dvy6zLLzvcRKdl3WYHUmiv8n89uF2DorWZsszcoS%2BR6AyXnZXVkfYYgb/GfwP%2Bd/uTl5OuAf6t%2BbY7usZD2yPEts54dTJ4/7vA94f%2B34FFb7CTSqhZP8Q6kE2UfZesLk/yE6KekYoo0g65cVEmgb5DeyMfLf5bXYA4vDJYHiE8u2Y%2BW/yMGSeYfgN/vPldh5xtlymCyDs1JJGH4ssZI9k/USzgrENgLbSX4dQdYiUxFCj6i84z%2BQx1yyNGH96lud2cB38sWHZa6/lIFInmsC8QWmxrDYXPlMs3SlsNhD5OK8EOjM157XVV2PsARD4Esn79%2BJLevO5BrOh%2BLAyKcm8Z/TPSr1mo4Qp/tDy/9gjalFPKEunr3Z%2Beu2Pqc/2JNaJVijbhjIsC2w1FkUsJZ1htRyHiCLsQCd8tUcugYBkgAOu7pr%2BnLn/aS7UBa7tiYyMbk2TCK4mRQ/JRaXxGzwljVfzM45WpIufNT64NCF82NQuHolc0yF8181DYJtfQqWlhaSExe4DD2H9SNpYkZpF1WH6JTJahwzM3iEeP5yAy86B0TzImUadw673CHVP5aUDOpi9pOy1NOUwiZ1DKUsT7E2mmnZsPRyrLpLichLnTragI4B9/3U/smh6YVPcl6oXsgfeClDtwpsh0WKxrcOJuv3L21dtnTY1A8MiE5/t%2B38qG48pk2lV62JUHnnYNLtqOfgm2mMUucbIdKb39PdVltH1ByRKicM%2BoyrpwaWikcsvqHTgNKm4piickfks%2BrJEsp9lM2eQXuvpEqPk3UNOyRnbLLuDKVo2s%2BVpt9tzGFM17We5KG1OeKKIC7HDuMaxsY2eZZESZUkqqIsK3w3vI46rk0AQvA/aPb46QMnhY8gE%2BGicosVTQN1INY8Ze%2BKlpSEtvRYenNDWZYX3aMgg94gL6sevZ6kqz4pyOlSadefrqWLfDs0ok6VlNvZmHUwWAxHV0bwtNwUdckkpAw8IUshqSlwTGtz/PjfIu/MzuRkiqGIzmcVr2JlceLl6j4pcFnzrPakpGw49F7ZlWrxUxPC9m7LFUnFrieqG0fl5wk5d2SUEndm5ghdXxExkZcddby281SCsfPq2LIAIfur/XKEmWDZALGepkoPZkPl6tB98paoiRI%2B%2BIb15zBCIkV8cmR4nB8tMR50XaN6Yf%2BiKDxLIt3eV33yDI3lkRa4/%2BcqZUlJ27n1Quk3IIUYUgwjhJLCJ1Mi4vypSVzkbRe4VGSiHy0pXrRMAA2lGGjCXUSao0VeFLncQjeKN/1BIoDleQSy26MMOe8B6vsipdwQeiTHn5ocF%2BkXTwcxs/H2SjOpW7BPHC8yM6L%2BFS3OW0VOThzan9G8Umn61L5UwsUrlwT034o31AePJsuZjbk5RaJUbhm7C0MxXS1mf/eLp4cozwbSewJiogIqTw1/xUTxgKjH4Vymm2tnt0o2zhcfCT0S/IdWvHFuc0el17QtdtrCzHW6qXpui2TgZ9FU2FTwn4WyrU82dRpWTNlhAbzxJa7vbUT9N2zPPH1cN6ijYIzrrZ9xPV8o9X8JJP4bsVFHfBDlPbQyMLJeySkEvFyt7Vjlo60V%2BC6HR50IwCsHwo3Otdq1glraTONqM9AIIh2TQfzfiHhQu2GCTzS8Xj9anPGVv2qLWJLZLb8RsRg5jDwY4gm9zdsBZc4khY/r5E3D1Z8TIsDtvAkesVKq%2Bo6i9LopGT2KvoyGUIc4ra3ms/DSKswnh2tLmfbFrvl2eWRfN6beTQ/a9EBvV%2BETCvM8nv/IGeXD1ZPh2djNyW2DbYtDMq2pLHqEU4/TRhaNm1jhy1jEWFhB4qbQWE4DTl2RUZToTCvy0raadsnmbDyoMA8bW/dKBjVbl5BbjcJiyNu7nQaaplSQGAvtftzE273RqSeCTmVlWJMXt7UPpuzNxoYdGKouZ9gXu%2BTb54X6NOvt9K06oHdzZ/qS7fP6u1vMLB2qBZUYmtDGRcAH6EcCF77QhiYwWOwWYIBxLkY4a8CDXzHxlfjPwmWf4BgiXEDj2jjzBas0vrNg4Q8EsBvR3ulVMXpBk/x6VtppxwVXQvMmrlJHjkb/2MID1Sd7KzsoQSSRCCXwWtyISfXQlNxqW8ujVcj5ExU5jBe1M8dLigMrbUL5GhQyLF/U5rDZ4bDVQRTbJN8OslhVJVRvYT1vOZTzz%2BENbxgVe3xb3i6s1b46Adnv%2BuW1sj5ms1WDZ9lytkcPZZII7cEVzcxlhMoWhllXhor8fXSGwmsexWcs4jAxPGfBcgnOkZHLYze4jAFpgHd%2Bp4XMb5S77svj6xQiC68UnS/pMmPrqbQQYdTNUMnGtynVu0oTk1q782VOtKTcM2qFcmQqfW1%2BDy92DT%2BTP9BvyIugJpX1VjayorTelaY1KWss8kmKWL8SFmTMW6XM9CwVrrRXcZiRLAndS72EVExv82kIz9cX1GXVvk/xzWiRx4nqNnOB7eQm6YBU2i9TSvsGxFIVrSPul8Jgw2UYJOoiuajIIE7WlkrIqyBZCHBIugRMqoZGPx9A%2BRvxRoze5KgjDiwTlXv%2B2cN1yjCPgGtDaAWCcLIsjbsmmuPK91wjgjICasWqjL5JhXrJk6ZHscgqG5nDuoWUEq/3DzKcYhCxZT5sOFYo1XaKeV4pfv2SwrSISnG2cs8veS1LEG8Be%2BQtenQf%2BuzA2X0L0bvebX9nsqtv4dzAXB/aZPQtYCIftNS3rIJdGsuagRdbdv2ubwvuCl3%2BIR8wLmI7/7t8QNv1h7761MD%2BH3t/yam0qagl6auT/AVEk/X3YKk/gamsSTKlT2asFgeIndkNfI4v8P78wCrq2gL2xTzuwvwOD3cNlG1Cz75wm5%2BlVRVsKUg4mr825/g%2BOMuwKS96StuQN7UfEqcrUkebU9xa%2BQKKPHWsRdBmFYhYt47Um1dVb8g51lY%2Be0rTkF8nOV2e1iTZtVUgPKRqR3OCewtfDJBrzweNz/xbncL4GWA3W0RSYgPJzORoo4j9nOmVsyQs4PzciPeQtzpdoRL2GMJbYynAeAMiYJA%2BBpRytSVAcaIjj4iKARg2goHuymSWvTwIY5CBVZinSUWZA/hAV/D44NXf/bfGi9UlGGyPm3jQmjQbi1GJJtPIqGG4/ej/Cn94t530bzBQ1NylxW92nqxdrzAN%2B7YAaXID4Mv7wRkA%2BPrs7wf/Rv1DpvjxIh9NI4As%2Bscf3%2BQcptwfJ2A1IrucW8M1k1nev2aQuLQWko/ExXfxcQFsMixgoAXq81FSn8s%2BhPMeOeLogMUDEtBzEC3%2BETcrlwXvRGxmyFwr8hfe6d0fjU73h9ljWSL/fy4GgeHBM7b3P9tHlNiDFksBuX4REvtR08mKWoVGKvR3/njD%2BZxUPEYHRaazyGhAjdM8ohutlWC1K0QNCZzjmeDI6H8PrX0%2BNsBGLXSECMoFIL2tlRiDAkNDEJcFRJPWOCoz1DgxAoppwnILKD/pSVNh%2BZf%2BVsRUsvZ2TLKX/W38rivXqWwLAauJkKPW5NjrUVftbf%2BtJatrur79o/j2bWwKZ1qK2%2B2%2BwZpscPSn5vygsPUpbtFFmK6OJ9nDUdNHGeJls21xwuQh0svusWUgJzP5AxdIyjWyeASK3nLDEmXQ0PCMRHe8GXshIWIhAGUCAKAlglNZ1NLIsuLVEimqkFui%2BBdDSzTrMtHCmG%2B51BECgKnyV0sEWFSNW9pYUs1a2rKsti0dLKgk7I4sHYY9NFx%2BNLoChUpoZMmQSceRP1/%2BSBwlwFwajhsTzQUXz5mSjttUwxFPL5ANkVQNjEqP4QqwtiHumvkGF0I6mc83y5CFC9eDeUvF4zweZkCpafIJ5fUcWcDk%2BVjPTeLBUbE%2BvsUCHtHycdEYq7pUpgL5fuF4aZIXy%2BQ5NYvrxDtSmBFWSGcrPINeLiXB/pLfkh9fvkIJJaAL1SRfJ7KSjKnliTk2JAnFtLKiHM/Rl7HrARDJG58qkkXVkfhNwHk4losrzbKkQrojIZBQKk0vY9PxIY4JFMia%2BZBYv/oSYQAAAA%3D%3D%27%29%20format%28%27woff2%27%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20%40font-face%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20%27deja%27%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20normal%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-style%3A%20normal%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-display%3A%20block%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20src%3A%20url%28%27data%3Afont/woff2%3Bbase64%2Cd09GMgABAAAAAEMkAA8AAAAAmMAAAELJAAJeuAAAAAAAAAAAAAAAAAAAAAAAAAAAGnAbmQgciU4fhRAGVgCDVBEMCoHiMIG/eAE2AiQDkgwLiQgABCAFg1QHIBv9gVUHYtg4AAO7dzlGEWwcYHHgdzsqyvkopuz/TwmaxBhMPdhbZRgkihMgorV6YjGGRluVepIwcFDEatNxr3R6R3If85kQ%2Bkw5D6HfG/aTn%2BFg6OV1vPccVv8MK7xHncrUA7HWWsW5ymPgOga%2BCIxb7Kgz81qIU%2B9fsgJ1Oe2I4wEjxKmLI%2BAMwcPzc%2BtNUkFEGDVqMEAyRm/UxjZqbMDIDTZqAYwM6RBaSpyFlB8QSSWNU9DDDtTTu7POg4rWHvzq6Z65C5GNUIkkoSIjNAC6REgWKsYQSA8f3u0SLK/uSgPARjixgej8LcorVdg1zARa43Xw65gOdmbI0QhYXKuanaNvZw4Zm%2BQAOepAPQgPwrMw74AdG/lXzspuYF33DLyRiwIFoUpBsoa944wzDJ3MsLKwG4T6WN5FypwBVs6ESgId1aZ7rd%2BG1H4u7z5FfxHRulwFWofWgZjLEZ/ZPakA0QQUmP3/bfq55TyNHY%2BkBU1QDjvoH8CiWaCSiiZQYf/mzpVn3nsjefTG/tFIHyQtWXb%2B%2BTKQvCRrQf5IVQArRNPukRcNC8h1CMrf5aQCLFOlKNOWC124KEtC/7%2Bpn1SjshnZs6140zpbAL9SC18UlBOARk9Xet%2BW5XxbSivdT7J/pNTG0gFflNIOr6gBxBK28DmVl3cHydI/iGDEx4ggjDGdOtR34/23J3aN%2BxC5fEREJIhImgbfwL9WN3N6UqhAjuoqNeGAX/sZW2/571dvV15ZVD0BBdzgcase%2B%2BK1y263UNKPiAwyiAQJg3juIiAAOsGijDDA0JF1ugpC6EzsfOVucNmH3lT7s2jv9OWcZNtFFdmhiVnRIU2xj2K1CtFp3YXoHgb6A714xRf6gKBAuZijbj43qhKPcMVnSBSAaMdnRAKtrqxIPjDvzU5AAnMIA4D9P/jJSq2UEHH%2BG7NAqteb4QEEa1GGgISGzLNTCEFJTPG0p/otamzBCcIju4SXr0PUkSHwAMb4r4fAtQ3%2BfWFGNX/PBFX6ggcuWD/EVOjvkBAHH4%2B3dAre6WDo7dLhDCHtnqY2HkX/p38pUDzk/Ou5jkVREOJStrwtIhL1opokKd/FUFDBUE502F3dylM7HmGermtlL0409AGDwBwaaUMIQIgX9A/xZzkxCjesYigLadF3H76mTz9sA3JDfarreo/86MgdBzczLNGuNSb6SxuqnvuIXhkPdwSgsaepacOXvJLaiAxWZwvWGIRBLiUjGXRwxWFU4QQTh7hL9L1NtoimJr4tjbHtoO%2B3%2BnQ/Brk5M/r2SFe5S/V5mDK8RByiH9OcT3sMik0CI1KZ8GyCQzdS1mQHEcvsVsyOTeHeiJrxtqQI5lj4nIJSHDHzSTzkMAFW7LH6wvzTziFyOgDDySdjF8ki9hqEv%2BevINjvbUchpouXAyVlEx1OSNc2oOBQ9HGmIKPcFlUL5KQ6Uh9YRioPaFb8RmdUSUtCf/70Kp%2B4ftu32f2IBTfelKtA4b4uI4QhpRg1UEqM/OBYC81HXb5L8MIs5R0KGFFQLPTmpysZyTOcV%2BqRaZ1OETBKc911VOS80qLc9HKTlPMyc4VjmqQ8mOPAckKfeI3oTbeoSM/icxs8Ub%2BUuX7r6MRHAj0GbmglJlc/kek7PsSWh4%2BgU9yt8/AAFY9yNIicoHDJOjRL%2BgpjQBRJLApdREi8ggA9PG7aILwDRIu6aBDjN%2BWCShHoLQ734F8E0SmvNBeTwtvJbBCK%2BEbKi/QbHUyWMhfIbFbnivREcoJQ4TSSdEUCt7MeVdOXdvz9TpmxC1F9kS/V6S8BUZ189YXKwjevZg984zLaCgwO/3CL%2B34QRfX%2BbNw3PGBRQy31NNBEMy20cox2BHqpV3qtN3qrd3qvDzBc6EY1AFs5iRZO8wM6pKgJUm/OQpIJSTUkEynTG7JGoYH/2tT3sKKDcu9QFWu6z2PWpU%2B0bBy1iygafDj9UtwBGTkoE8Zn4q8umCvZanGttO220qRIEj2LZstkI%2BGy4Vq/YAmYfTA4/Asi6T543kWRc1DoNxxghF0JwBrEZjiZU7ZCjeiTXx7Z4jEGojNX83byShOM8Uoq7RG8a/Ony4eQ9B01o9WhKqOsL0WfGDh5VfbJlbzNJ%2BAiqYCa4bgveeGn0kGNaKkwYHBiSDqcDNPxq7dnWrYiKkYu0KPyp2ulcMWxpgBVRjTfqGXGPPw5PiarA4pa%2Bwb%2BKh/N8evr5JPRW9PKukIfST8fWXjZTLQbGGDl4aozrp4n8mce5rnzWly4KM3i4GZONmGc8Y1vrcNqRbvAfvvmEQRIURosR/gCMG864y4BBXYvbp02bEOrwbGliJ%2BtmfORxVhRhZuzgejTiMxkYTzKb5GCWmBaxtr%2BJ3AeOfm3mbHO2WBgSIEgSsS80agdVccCD9AG61QePRQ8LfWv7dGTQkrnzOhqDXlK6whP1C/ZSQnk5SkpGXmKPDisUFVqYeUpRwu4SSPJFDvE66KnN98ejDMYqd4gx0RSp0zTIIh7lMw7dxlvSGMESQR5zZsGj%2BZyhTeKr6QnJc8JZECUUGcYQJsgs3j6ICvQw6sA8L3eqMYABnM4V45URbYghvt%2BICMmkeepDHlKZpTMILwDnLlS5GAImsOUJKyRzAJMFAhBojiiDo4kDGaWbjF8ZxIPwDjjpAycx2qw/rUrKwoi6oORIVYuhh6A9aGRkjPYDCIkfmcafgPh9LQz2pPW/HmDYEw1WkSr53CEhu/VnuIAAs0yBj4EeW6c9sGjZtnFHMXlgdH3u%2B/R%2BCKDl6zHsviRTD6n8VVWAj/CZ7Ht1xZKnPmQxR19cyqM/FuMBKTTUnjK20qsfIedRd%2BNYXCjH6Ty%2BNGPonkxtIfe1kOZCAgrafCsGxKoNSIHs5DI4zJlCfI9aP%2B5zsFb4jyg8y7uEcCwPycuRviYO%2BHAE4DKhKruoRHsgAmU3s1WTlysvoQLv90xAQZiYokWIxYbR4JEydJkypGvoLDoaGlFdV1jy7HOnuODJwRnRs%2BPzS07QoOidVuUaVFz3U0WTO2wZsvZno8%2BceTAyWd/wXrui6/u%2B9s/MP71zU%2B/g7FkB7spoYMcRj9ZRsmnxCWV1NNETZt7uogZwGfsYWZZZpMdbz45hkH2J5ogwUIwPBHqxRhJtxUC9lSjdNOvoqrWZvzZpJYji8fDlypDtjwl5VW1Dc1tHd19A8MnT5%2Bjm5i/TA8KwqZrVOlA%2BsM2K%2BbuQLP3w38BNit2sU5JHeIIhskxSTFlbqmlkZbAjsClzysjhEw9ziLrbHOwyi/nXFDcFeWZcLFBtj9yxuFKkiJdllzFZZU19U2t7V29/UMjp85GGL8UwCvSlaaUtKGrUpG21HVDt2QpM92Wjez0XX9lc7HGTg6ixJLicBkUXMalUAi5lmohC6WA0pFH6YlUhvIsEz0q87IqdNnrinzLqbDy164i9VRh7IMbvUIH6LNwGPqRyj4MpxAxTKCCQ5hBNfIzIYrf6gwIXOUz9PAvvsPnTl8hP4SgbzEkvSW9grHgKvAW7y6aUcwvt855iFzd1H52oxFFBFSgAzQIcFpbX3TZ90vybsfFepOp79W3xcJwIwzymKmYxxFzW8XsXolZfLMSeP/2wKDuqC5tRXiOGUJag4RnvHLIox5X9YdXMG3S8CKb/yNeURcHHTjuA4DHA2RbgHY9wYhulLSdutLWNWJ7S1IIwIPhzXm4o7OorGSRle6rv8ZFg3/mkJK4XbqsUIExxMAojHBKOcttychUHFUC8wV2wY2ScEIoYYQDAyMtMAgDaUGO9iJXKoBUGndTDTDS3h%2B4FgM4De4G1wE5G4IbdDVov/3UMBKthpK8/Q95gkaRhK96yDERgbGVuQhv2/eExzNaaU5gSNcFqKauB5TsgKIPKCqQNqjtC6Q1qKZKoGxQ6gbKUgzOmWQIzUUE0Ap0O4DS7s3ETTqXzLfKQUoIp2SWQHoC24LqvuLOBBiQ6vt4Fjw7Dq6edUXEhSepiZtdK4JSXRi%2BTHYgL7zra/SIKbfSVdQoj/rYDXUCJ3gSJ2XmZ2225trcmJ35frO7Mr0SVS7eEzQGQ%2BjPz5wrs6l8%2BkmS/fh/wWiC%2BCLCw3GB5cyODQtGDOjTo00ChI3reBytUR6VXo12SCsVKsewbTourdwh2o91r9HRqCiNTkal0uhsVDqNLqHKMCw%2BVVdVVjVS48dNoLHUqOZTLEO1VX3V8nwgLGqHGqbi1U51Qp1SX9RZdU59H1%2BAxk%2BjLmn8Mur6FL9RH9Wv4R%2B2MJylbc/0OnCFpfTHJgOoHiDZCAAyrQQgsj4AQQs/5Hy47AIaGNTmXfFYcJFw26kS15rL7ECyn3DsmoWld7yYdbm91UbpveKUkgpUQAepUBEzmm15BZEfTPZmVUNL9s9q7xEGhHJc9nCw2R8/FEi8htapm9AoTwiZY%2BbN0Se5yZNBcunXKwpruTC5t5xKOIgZLweW/D/Ff%2BhEIE5l4F%2BHZw%2BLJGEpmG%2BhtecxNceTcf8sE7YJm3szAogTOg%2Bd889plpmOaC2bNNSWWOVkJvQh26U%2BUTqXp0gL5yw9XedYJoFjAAjBMpBuLfhdCI%2BnM7P5GuvJRo6Cl9suFhcD8yWmnf%2B18FRxxGIY/Ggt%2BxdgawBMDfZMw9QkyNvNURtfFOXJS%2BfjfGL3aO3xcp3ITu3KMCfIiDHiIadbDZrt6zZfiC71fXqYt//mdV9KLU5qlHYB3zObuF/LYjk7rHXpV5WZeOnsxEonnL/WM3xc2u3Z2/kiXD2J9nV%2BgMgKAwkHY1qUgFoRvKKosc7GOuEwqDAlYdTjndNiw0ZY49Dn7uh4jAwGg4DijQMWBit%2BBWDnEm/O0TEpbiSXOzkqL0oqhlRn595fOCU5v5dUM8QH%2B3Mec6UMtmlULWRJxp81UzAJCkIBjNMGRamlDqnR0OmxcK2C3V86muvoNFlbH2x45K%2BNNjfidZ%2B9TQkiCOeJQoxaMcOYklXHeApipEaheLnKpe5PtFG5qKMLVCwtaDnWvTXcZKPhzmYEd3C%2B3BdQIXeAAz1yl/DpFxdECxmnKpSE8i0hwnV5tzlJqjNuM5BHfohyF6H3QVhZTCwMtHqUaVEcir1UGc9bSpqy9qXqEuUuUpLw9ql9o9EV8GU%2B0C7m9jZILGIRU2nXE/36yzz7wkBeHDaihkEzrEdERKCvCSU1kKGIQAbgCppKCg4lE7LSRvGP0iL1Tzn4tPex1D5uKfNzki0GzSAH%2BkyPGvcjWlTWLiDIYcW2M7LuFZjPc%2Bka1gj5wprsRRkbWgzHdBholO0H3nqjjUhsGYraCtMFL1/MvV0MFTxP0%2BXZsj9ZUhtadKGMAU5CqfXO7sQkvsIoqJy9NnIN0ZUKB1GLDbU6UYbv9hwsb8R73eoro24qQJjUZi2QpJRJ%2BnR7nNCyLi8Q0WrgxBmtTDpLJWB56IW7JNmYgm79zDaN3m18hyE9FKlxuDBezXbUBy0kbjC0yujZzHAIVjkaOQ2Zny67j9CQhMJti9mdDdkBldOITOWXG69h2X5S3j/kPUMHeyTZjqeeed3cR3oX91DujNBAhkGJoUyMQhZCuaadLbo1F3fYph3grsQ4IATGWO1IQvawFb85iqperkg2AmdyTqyCLbyhiP8Y4FvVHK1z%2BwyE1wjc%2BeoCEhauXxw0vjcCZN8BClSbOoTMjaD/0WGu6I6xA84UndCNzD4OmC9ZD23wo5gnFhyoP3HdJT%2B8Fh87T9KyRXrzmjwRWJqORagQ8WuUgLFNdNV%2BwH/7hh66j16IiVd4Gf1bXxxyHvfS2bdF93LCxz2rDKCCZcqQ/QGhRch%2B0qIBJbsVXQMXEoBqoDTbuwaMJ944oEm7t0BJZUgI6CjUURC21/wZ1rkgS0THbC34PQn3gBhRGja8JYS1xTVfWz8XsFxN9mgxBzZnmEJbFAC3l0HKS0NAjhjROszKaVHPgLGNStNuUl67hb3Vl6q41LKQ9ywmLjD4qficBIFtPCjrOdZFKilwtYy6rCw4PkAgyqAuaBYc9/S053fDmkC%2BO8CEAdsgkvFTjWC9/HgeBsKEQ4VacqZgLttOGUVhpvbZmwfKiMr6OqSRL3X9ysMBjNH0fHv60ulgNoFyP/8vBhZamI/sjRRlzhKfPblNGewFNOJQDx8h2xRwyKH/mhmSagr4YlH0Sk4emNaTyUizBZvAOQOUw5EDihMtNuqCBJQoDwpijPrFSIXudHFWHBRwQdd1dzuOKVCrpSnp0tZIejAVZftpYLsuOD57tjbbqIv6hbI9qNfgaiCQ6LSmZWH5XhxZKHFV0d8O9oI/aGOcFjfLmulIx5SnB2ovveHXKLHN7tVLshHnDdf/dYwZS23bJMoGHkge7w07gBEI2bac25RsA9O66BZZG5DpOZJfrZ7nV9wJniAxoZcCl0FzVTfh0ONunC12UXjVacYUgKU2sZPskII5mItnhOYjy42VUMZtqYp8VvgDhoBfD7Nopiq2WVJLDk30M%2B0AsvCKGxRi6Ia5tVLx6jJxr32NE9MUg9hczZwYonYUNizVBnu4mMOWY%2BOzVARVVsyndtsddAz0wsf65IQ7kEid2T0lzTqolIriphlgVYokRBcUOpdruejMBrZxUs8lJSYD5t6KRWQhYFtEE%2B4AjwLZq7AP5ZOVX6zTCy6WoUqDstnQCLbb3o%2Bm5vJGZUTZeSIvKNTn6cD7MKZAgeLy0J3iRhh7mLhFCkFV85r4j4rqpEDJlmVIpfQcmmdhLqV4SsQyq0h7AAAdpdYamToo025MCV6h%2BuLscBoU09BDSiiiCWnTVKWhIKKvlkI5JB6Y074DRdHFyR7UuhqOKlBHR5XVkHrOlspbATrLp90ahRHQq4MuZ2M3AHiZLYuYNc9qMmb/k2B1BHRROE6hym37sT43x7Eub6bUKVEL1ghGY9JrDOBu%2BqiUQH2GKkGKlSh2yLJoK8X51CIP0S/M/WwcEAKbvlXB1M50MTqtrF8ZosSkxWTuf6YHGMKRMuxpDBELhtiF%2BNgIKSYknLAyJcuYcqWPXy719J1reeWgQyfS6NVmylMLzztiipSYBA6GaMVypAJVyty0qX0%2BgcTtydkwNzJlyiFy7UCRWzp%2BdsmEjvFDTMGi052OMYme82/cw0TorGGdKxbNO%2BKZlzx7cpoHhAOFVsAoSYb9gi15kvHqhqdVolU1j/rscL9FlTe6/DcLHUXP3XIzmhYs5bnZpuiIr8ZdSO34EcoVRgsAbPu7M1GazqVR3QECUhd91l34gv/zk%2BO%2BK%2BOGSfZGjOJuYWVU6nUBG1ZlDsnrYguGCYiCMQacIGqpCetGZgp90UyaeYiwVozUksagXxcndp7ymig8O/N/hq4E/QmYKdqg1Jmy/wLxOlw6r21TNUwNgLl4IU4oeSZUmRT1ED1wPx1JBKr9GRkNoVyLwsik0H6ImoFeWoPirubt5%2BfqVQG1JsEs/knkWbR0dYuDPLBXMoxDB%2BPEpae8FpHSCnUA7DLyzfmm3cggNDA88VdghcINGThxI51xAC2sWgxyihzgqGtclVU15Q6d/aw5uHY8e2Mz9S1sBqsm78jSVHPixJjSmelTPAnS4xiIqgsC4TETOj2216sPuO%2BnCqPSn4%2BkZQ6GDLbm%2BXVRPGZmI%2B3ujD5H/qTJTvGjwlzN9b0QJnOvUivccxbXXSvE6clzoTWpABrItpDjJViEuN%2Bhn6DxUhUPTFxM/nEqH7SmJQcQNdlB9IKiEl5YMq7YZOAj4AJwgtO91dua9d%2Bz0W7WnjUsjeod1I3Kq6h5xKEDudKPJtp48qq2djTUYsqRtGId2UxkiS1qYtjOxyH8HbcS3Cufhe/YdgbEDgYrN1mLsMZyGmOC0Y6JyHNG7TSGlxGtzUF6tG7CsGSvwJ3qzF3IbODAbhmHRTeiQXNSU7zsY35jhnSghecbMumEYtvop8b%2BCYTu1ISAn4KnWlhANbiUAjzHT8sCDIFShnS/Xn%2BaDzYCVA6uOiGrkIdRsaHT8SEPvJCwRjnx32o6NaFhSWUuZHgW1s1WFqZ6fINJ9GujsnpzZ92s%2BiAbTDOskVgq54Rt2DSAS6PjWCK1HnCROA%2BPbO4udqozWyxu6blrxzyi%2B112ax21%2BZPs9HyP6ZGWtRORnezxbVi1ku3tusZ0M9XRwdlNrrdhClqhihQLz10CVxTCeYqTpPxyAvOsuXdgJGfFbc1QldBZhhCJ9jQOR0%2BLwvojRPoZT106s5vEQyr4dS1RQh/NSnAV1uXNKD2jRILPIVccHlQXG/cP9oTPS/DGIielfGQrcLlyFOeSNbfkUVCQyjcqFrOrlROL2xZjshoeLCYyIkl8uBPDOl4TEP4x/q0fHC0WZ1BAjCLOkyUvbebfxDxXTjE7fmj3nz5euXKk3TinUWWe5aU4R3JuSM27PZ85apracVUXeRI/lDTKNRnTo%2BRFjop3nrq13NGmgGljDJ3Va6genaY%2B4xbx1GZIlbkBXHy9d7FYIQIuQXQkdOk6LOJGkKHayZ3WQQ%2BYj90Z7rQ2c/h90wuxacWUX145TaxeWdUcN/pOP/SfvQmYe%2BQnPRWkqcVKu1YNWRqdPFtb501/3p2uuYRd75pr4lmFvsoFqKcIa52V8n7OBx6/5N8yaNWb37cl5kcmEvm/0aDc4CBdMoJ98D4jwOvQmgvPbmRnXs1%2Bmpxdmx7h53U6jXUly3Czsg72sy3VkEbP3/PPp28lZasNE4lhrDBjeO1kI%2BvMNOY2Wbt/YPSUvUDtkA3/MVdmV1cl%2B2%2BvIR%2BrNopYXfF3P45nlrn70XPz//RLj2f7ocF3Y5xf615tNnLDQ59fIWFbfwbG/75/E2/lINJu39ayr7Lf6B0dDnwzd7v7pu/OXuScdSMjIJwvZjQFtqtZGNViIZ3fZxd9sA/2z8Ge2Xzx3Mxuby90F2jPgw6oo1DBvpS%2BoMZbi30RQ0545%2BLan2J2QdgNkaQZiaMxsNBceZVftY0xQc8Cjn42k7lTUPK4JPK3WY5D/4MVyH8QRUFNBRh3m7rFFfGCLOyitXTJwJEpmZIXhYuvXhWtFD/va3nbfPnxo6bl1nfggIeM2fGuefnRY5eG7L4ETRoFNReBg%2BWj6MXoJVuGz0YmP/NON2pQp902clUM7F4IGxkJVjHyOwvw8b9bpwecC/2cQVSR8qAyuGvP6vctgCzZESBLmwRAd9K3q8vc0oK0rm7NwrJ5V%2B9P8eqsTPaTtcysh2Pw30lCcjQOYuI2gIgBQRAKyk8uCiYE/LTqej06h9TqmT7cUhpxE5vuspVpesss6LXsq6VQt8HghGoJOLD4ol2v1r9c/8Dks2L4Fa04rWV/01Uz7qUrLXXFUPPQ2eGplu60xOT%2BrGP%2Bc6gk7Qm%2B%2Bpo67aXEjbGcGgEwZWmrP1P9LeR/9ffVnZEd%2Bjq9fxkc3NlREtB5lwKlDH%2BgDr/9bHDgcPiSeoz6Js103oR9Zn2yqwtmccavD%2BWvDfRd7ct7pvuP98yUQ2rmeL%2B%2BJcUzICKqQ5u0dvOglLRdwPlQ9hNh6XcvD8k9FTpcnRnpM6hM6lAFB0p2FgI0XuTfUn9hcBVUrtQPtpeVYaG%2BfqijFGcMphaq158vGP99/eGj/66PF%2BB46g0a/ZfrdsVf/fsv1tqtG7iMo6fUFx4taixMSSG36CheQPBYDSKiOrWJazclpQ7bUs%2BHcB4LS5Ftkq7OYni/s05Vx1ZmJfhoLYaiNDgk/Jg4YswZwsY/YQoTMIXcKV%2BNP4bxlcDBpn5fJBghn4jAcBOfLvWYwjC4oHS0/qa/Uj/fwbIppFFpQcXmYWa0Ii7HZ8yd7nIxDL1gEwM9XOwl4cpp1EDqHjODEmIJvivW1fTnZFcP1FbXDWZl1g4gL9iTOvAxp29/W1u78/1UTCfBjni07nGD3ODD82ynWWefmCL9ytHsJ5gI5y7WKGGxly/HiYuNkeV0F34LiPGl8XLoOtHHpnFF%2BTn5OSX4uakyz9ycgjy/0rnt%2BDNZvQSG94tsMUcTBRFgcFnq8EttRf%2BzL1AtOzfL8o6X5e2AxjWU20BXaVV1V/GAK2vJoLmex0%2BqT2j%2BOiT86vv8%2BYr849myzeqDG8fWDu3%2BVATk1MgdVvjLsCJwCK175BAaFL0MY4XfisO9xxX7l/TExJb0%2BJe%2B9wBKViYVX63UFIwN1eSt5oZ6x1piC4ra4kZ7gWTLzqjg5cyqYFX1m2Bp/7YJFtRDvgLf9Q1AMt1pIAzLFKaQlSh94ZXHuLzKY%2BGNojie%2B52E8J8RVTIyZibGMmYy7BNavYo3mA%2BZG0CEI6sPV%2BEA9mLrKblSW1%2BeeQmcS7ktS4dnnt6dJvz8gtmA6oGta9c75A3uCI5vxzVSuPVN4cs3IyCyc5ma1BXhYNM48JWGDmk95pMnkBCphpinku7vwykjqNn9%2BwCHUG5%2Bb8AufTCSUWJnnzEQGZkxKH2x2e%2Ba9oayUvhu1awOrf0ET%2B89AE9%2B/g7aGDqe0N9m%2BzW/3t%2BDaUhPwUEGt0c2f%2BEbSQB98yJGLqvOMXerbfdSnmdLJSPjfrqU9/Y18HPnz5qi6TRvmd%2BNzaVHjzYXNwK3ksHLfOtNq4ihsfy0qDTmkFqm4hP9239O7Wo%2BUorfM8k3vpHl9s6df2a2LI9Xmt2ulqXwxHB9/tQV%2BWd/x%2B1qA8Da%2BSiQASIGm0R5HAntnc2qT2duLi/BK36q2hzUQuohlK%2BYFiCghnZuqTa/UR18cxPVsgOE3Xe2ZAf3ZJv3nj3e5lcVUnd%2By05Ky76Q/g3E5XZ2%2Bfop%2BruPtpBC4rsDWcNZdzX8VQFw39ntZnWwdjWEpFY3V4GY1U5Lh71ww6pg%2BWoe7UBCFD5S9wmICn%2BqGwEHhnairIHFQOcfOBDTDJOP432sA/aXd9QM1y9UCnud72kAi6XU6dYB%2BX%2BilVLb3VPBnPmjU6zsyAJ%2BQjv054TeBWI%2BJHBizdLLT28s9M/9mv%2B5TyZ/uLKNPmnkbyRg6szpBH/5%2BBM4kU02h182Ng7xzetxk/xso0bp2Vu/XcXFwd3K1tSe3cg0EIVPA6cUq9Ne%2Bl36vuOXn169iu64L1Kcq4CEGkSF/afnKCVjaCtKPa4J7FBT4VC1hEbQ7JmzqW3meOi3FLpOvNzcPEHpngZwf4UHe0vMbV25EH/FgmFxNlLvugFbEm1zQFbX5ddTUc0Ly7DWwfHyDujMwW5gUgpvH40i%2BcRGmdSZ/DH9x2jdqPfsGcEcsNN0XBu819JM2tqq1txyiS%2B8VftPXpRxSsVXddzXoFuPPDl%2BcT4EHXmekXPtp62QMLnXcjS1FyCAgIfL4CDU05vdndNyam4wU86zi8Qyx6JSFy/vAUk4HaJL4r2B9sZNud0/P%2BnRZRZHpLeHArquxHD12bdpPX3nJxHv/wmtiZoGkvLw%2BzkCl6YKt%2Bz51KJzc1%2BfDk73HE/qTK0begeqXqE/qSO9ZujSwFeSPn3WKfocIzNLkM0InLdefrAesXjUfOrl%2BrrgZUvTqVdrq6deN3Z%2BOmwDlz2M/vRZGi0Ll7YB/BPkaSB6YFpgwU3ZvDXyroCxsSE2yfezFbXnI4CA6AEIWA5MPMFLG3YNb1zy5ueRwZY1HVkiuz0B0BPOZ72OnDgSXFHuT8Ew8E3ID8t6g8cquv6Z/DN4SZOteYlledmSUVQV5Edk%2Bgi0fgtQbc1FPY9OXYseVwUKqT1O2XXZ9AqCV/Jcx9zO8Z6b9LK6MidwZnoY6u3N7slpOT03mA3RodMurNHI1MXVjypfRoDQNn1Z7tnp9ZyXEW70H0CESYekouKl/ON3/8wWkqHQdw/Jth5Cte4CDzd6M/Kk0677BfEKu2OeNXK7urVzB40xXqtNmTlNHRySP868BOmvQvFelnvGwcmXgRicHj9i4Ko90XT0ZhadAJt4qd7p%2B0jTg49t7ORaxzq8LPH8lk8PRhq/O9W51VADa751B9dwFXkIWk3Xt/owgHipCsSBgGqC/7suV1Oodd%2B6aDU8BE9RXsbub7WUgGqPOufvw40PPrWw%2BbVOdU617OS2jw%2BGm75P0/M0G4v1HnNl8BrsBrCRzzscw4NOkRjkE4rbIo32TVj%2B9t%2BAA0aIwEBe/nGDbNz//D35LLXjjK3sR4puS8u3Xe3DknKrtxd8y9KTkk4lbjIo2J%2BiK/tjke7J/68gGIo%2BOYayX5JdtHU8kunArkwvnLT/F1UbBe89AAtFU/TcURjjP/CPnTg%2BMoG8W16HfjmHe7h/CCO64iq4i9NIFPCvwblrxnWhypLoPyZEcvH73j8iB5dn0PS9T18b3gdiC05WbkiczVJuNJWdSBvvUIwc8hHwULZUOHEukeVXF41F4WOOvHJXwiiSQz7TAOKZdld582am8ytl9r/fm79FGrMtQygZzL1AKo2KKyf1PlyEYhZswtAX6S5j7j6cIi7NzDysmBZEo9oUsjpAP2X0BVnRVda0RsGcaOlNwcIJqqnRTpEOkTya3HSUmjsMuT/CBCQkr5rV9OBkGqdp9ls%2BL2m%2B/X/R9t86Iuu0rLVs1aFze/jRGZEyz4i8AeLirVSoJfJH/8hiUcvjfbceRjDLLVg9MrlSMYagtPWUqWZClr63fTK24HTDs8RgbLZfW1yUOsFnUUT3tlF0tDbE16NogP7udnyeZRlkllpMzjUK0%2BxQbnWBTDfydyuAz4Khwph/fPHJBUcfpYx8wFGW8cD/EzsOsa%2BCe2CsUTtcwS8yXCvDJKzbmQ1qQM4v5zcQqvOQ56xdQTLZIFMregm6quE8wqtV1IECVkMSCSEsBTzS81uFu3SWaU%2B4qpr7vqMiwBP8FdjxgYT%2B/hQdrHXcVb8Cyc5v0qk4JXW7LYnfkMXO5RZYeWv4ghwYXWva483JC5z1dBW4juO69EzfCCnX9wS7fnH2Q4HTTDBwh5WWy7gBS2no4AmhwPVymvpm%2Bo6mp3CdE6AEIBIK/fWnk2xkCNItg6Uz7fYOWT9AHk3r9tfxnx6HhmYZvPXzAczEIpIi6kOgTdGNzbxs0q%2BTy8mYUItY4rdVY93Cbrf/VCBppXfNN4qmUwEcKoCAo3AC2VMozEdVQ%2BWPedf9LKkgXqCen463S1aQ634jh4ppNR0vac9Dyr8PwnwJ2hR9%2BSgyEQ87D53HcbOah%2B%2BrlcmkgmswpqpW4DLxK7RrCAvpNcrQzK8OQrQZAyzS0yv3WTwU78ioyxgVxpZoUCPwcqgQIA0qnEMgDD%2B5aMgx39OXo3HGEjB0MvVIhu54O0V/eGBftrG9wNXVTZe7oEzwqvB5v/b%2BTDoLYx2spNt8/CjJKdTLGzVJVh7OQTcyMOk%2BQedhYEbPBRmg5AaygB%2B7yjYkFE0wonphMVnpbWNXJnurs5vBaiNhkFJM99Uc9vw4qeXlD4EPy85QopaF8bsFiGqHN5KwPNUbHhiqN0/lQjElxBPhjk1p6y3IpGRGRU1QQEx0BQ1nXkhTrJVmASpdknUyAD%2BqnVGIU9KKEUl1SoSwRSAS1jhgSFe6nHugQGEU6e9zwoqQq4KVSjJyQTR%2BA/zWOc9wrRsRetUOiNPQFhaZ7Abzgt2e1LTJ0fuXXEeiwIKAKzh4WcDDFa486fH%2BAJCTTFdPUQ4bHR7oZjHh7JnG8jkyiZSShvNMzCy7CF4Uajjfpv11UjBjWpNgiu75oKNTrKipDFcYUVHRXrQB994gUrmZgyszf0%2Bi2TJTqMe2iUCXRd4HpuN3FvX5j7HTbnqrWU28Y5UcBjUBT0DhtgcaIsVSRDG0m7tPrSyXe4jBOJCDphTqyq4KnYT50ego62Ua7mzyWowEPqVkyDCIvN9D/PEZoMf66hwYiMEGBHy7WIzuyhx72dP79JPznpdgf4l1b1Y02QXeZW1XF68bSMylOdNUXOSsNInSJGlX98F8oJpmzPEQZebekntV4ZKwq/iSgguz93/cBxcnTmrdM0V2clcyNPFs9Q6AiGIg4uSTlsTbszfHKrZDy69tIgtm1axGEfOZEMvQ5USKfEkAiHErhQCpIPW5VQQPP8ZJR7IdX%2BEk8v2GJx7%2BSXly/N36xoRZvAe7s%2BQ/c/tdYG6FHMmUZlmUuWBQIWiSibfQIK2UmODMJ/%2B08/QEUYHyuUiq/gpeK1uZKc9qSciJShgqjc/HEAMVcOrzcVKwlif5CU5Apzm2lHrLa17ojNAVD74u9rcu1oMvdEXojNf8sbpLoCSc9nbcipISW7e00odrNW7eQqV0YPkCrqVFV4EzMlcormoEZT1YfYRy7diw2cS42dBNsCX9N8O257/cFyvtVjQ7kxMIhMkJppcaK4OG6VMUK9XvQOUyfGKgB2pSd9QWtIW20dZf4OAS5driafPWWGZ2XTg9K6elI4RSkCY5tL/0cIuWOS1kJbjTgBvImfWgh/qUpuJsJ0%2BQZT2kgSnmx%2BR3TXuMCCckxnOMa5V8fOhyEa2KXgkCYq/30GBiLO6iCJB237l1bMCp0M8JBPwTk0ARzWM0bvEoqJtU/rUdcJe6pWgeZURmvsDBWcV2er5A25MuWekUAmHCynM3XGI02qbF0b%2BtXBKtQ4n21YBQpFTpZY81fz1qmpmxz5eVoY/P//7pON/zDd6JkFVCcGEEFFpmyze1uHmhSFGF0TSdNCSrb8u%2BKmT7r4tXe8zATVB8icQ9O%2BF35wIF2tchMW75HKg0/4Hn4YHYUKvX4fEvX43oWV2hTqZMaYwL7DrCKwyxP10lOoAE2GS6M45pc8cd3KUd1I8U6uKjgPg/pMn1dyVlg0jTUMTeQN%2B2i2Jry3rx9SAJxppF4IxYqaAc9gSYIoOgRZ8ujPV3Y3crrFW2ih%2BqXjMtSu%2B11Qz5u4cqLlgJ%2BVzGk5WVneTItKSKU57ijy/ka3syJJQUydvAXl6gkswWFzkjzThOP9dBty1SDLd%2BWmFOOYNw6Gv9V/tbv3fxrvv41%2Bp/gdq%2BQpwW4zhx8fgMGbvEhkhrQClEn4k6z1355kpH%2BBZztHBxzHfGvpBp3ekPaxahCmEkD9kFS%2BIDDQYdYj2AYjqB1ecPZRFYw0AsE57qam6HL2O3tXjzbJnqCw6nCX9DD3Tds7WVERkLOKzK0cO%2BeIKBryHZVYw%2B5Xtwehd7f8JLWiImxzPxXHGeE%2BVIEyN2wdEsV9qFG%2BbYCFSK4U6yGYG2IQ4cSqBiVwjKFZZ444mcZfHLz26waumJJSXHmgbfKFDjBMinGfaZa8h4IuID7ogs7VFgEI1D3b7rgCiW8tQ2jz920a4faW7oM%2Bq2vJCpE748lironUsNnzGpshwy6%2B5qADcwv3kTrrwoivd2CaftLIOewKvOCFSbCDBPdvqNxUR6RUIx1RG6E4/xCW0lU5aPAV9o%2BW1b75Npk6F9fUKStMy8w7JAQqxiGeyvWQYKNhVvQ/JGGt9tdPRdd6v70Ktt/hZUC72VzTtMyxA62De0b9qk90nbW2AlVtGaYhYA3Tud2bcWvVqltNzua6KN6byREBIfFuxP6lt%2BgHhcID72YLkP7E8F1UJaPVtIArp3srJKHr7x6AH3SKub8jNALPURrz6je0Z2VInlykIHtIOmhd4uZuOc4ozkBW44ASMstDDrKERy40aOS84oQsy2bGB1pxSIcTrQVFcWS2lUFsrq5tU%2BAWJMYHUzPT3Yf2wG9MsBsdx2dMBKm8nQ6h/dCanAemlxNJvZBinOSI5zI0luQo6zC8JCBAw3/ILkjOIcs7EL6NuUzgAx5hNebVY3xNFXj9oBaAsZkcQR%2By38Bch%2BTKFFn26Mzoh5qHss0MLOYYKgYmxbtL44Pb0OeO9zN6AjIuR8lH0ciFARTn0fJ6gx/coelo4ITUnUwsVHnTOuhkzLx9%2BvWdIUSEHesosmhKdIZiLE3ACjPW%2BvD1gmUoie1dq4VKQSqO8lcKBus78iLzle2DXSyIUPzwhj6EJB/eynRli3SMnPDx7ZJaotWCKN47RdfKrdKYmWl1o7dYA%2BFUftDoZQUbMgfhfanXUOQ6IeO4t4MBtlA/FawQL/ONRS%2ByFJhIo6fEICmMCPDulCZoVZLX5RDp3h%2BPNJCgyfR1O/KjzDCgY8c/j7q%2BTBiCqZUplwXeiQ06LOqlUH971KsHkvLpyWBqzMVcny0fIUmfFDfqJ9IhBOlAD6T2nCLO4xOogKgRZDDE9sfQEBcd9noABiPivAxdgmx4VRwZFnks27GUYWr2vm9UdMGt8FCvgj90SqHd3NqLjzZpiaNBYANJzQmZPHntG9/Mp9S1I9yWQRP1QTvTA0/xqyqHAUlx4pN5BuD1dNMNofjSd1sooGFam6yv0oLjC%2BZc3r7fg8EFIw6QUzydYK3ygW4gy0H64hNcI1mKNYNHwwviSVw9jLkqtlmwvyKwSJRKOoIogYNevjw1yAujAGUfqRsdzlyoc8yQU1MPn73QzlHFvucPioSWcE0lUBG4kzvQB8bsBPbRNdPN5y4d5GAzG%2BoHTZ1p75pdDLssMa3XEagbuxl577vR%2BbZCJUdDQ6ZHoz9bB1ObTMEfbyRGE62KOs8txBmraGE1oHYDQ5rIrTElxgPuSteNhnkVkS7n%2BsvM6JgKQmx%2BtMu74vkLb2mGLraBJHZ25noh6Op00/ekF7VwCDLcaiqbE6s6OKszn5Cws5D6DPVFje7UoCkkBnXbehsRdOe1r/PJeo0dAWCRvqmmVkNs5ULbom6/NynOkme%2BEmec6JIcEulWHqrs6TJGofVH/g32ftBWxOdXx3xOKt1GstZTb0axZawP7C8fVQgoJdYFS1%2BGZODunnybvT5f/Pkd09bDyR6gXQwErYwFgQBD5Ai9BGGkRnY48MpNEprrTbRupr/8RYBBnuBRnGWPyzpm50m8QIJO25Gqwm0qG0jRMUy9vvkKrR4dHhqsh3JejHSnywniNbD1t0SkZYgWqFuPNrT8zqzgXWBMi62TDmi1Zwf5dqqTH6GdFWhHe0rDwTuF4HWloWuxbVFYle5qonyl4tyu3JweDwk/OGsCPO8scCmD4hHc7ZA8S3QIAcuFv2LUwUN8s4USBPMT/yYbXYhzhZ7VUoHwF3jfuAwPujTnocGZNyyQ6JI0JF54A1xpLOarrGZfwf2welWAZ4YGQnSn3CbPxGg/3pQVDdIS9Ht14TDUt3YuqxIONgeyk6s/veVbf3Gu%2B/EGF4rMoCWXoYY2TPJkJFo%2B%2BWh00ii5ULrGcfsaqUfrzUA0pvFhYlmOkSfi68IFXxcbPkG9b6EDiBOgNn2ni/SzzcdsF482vBFfgudM9Fk44zm3kcV9Akaaeug3M0fu5vh%2BUEo9SY8NRvZZZeBwxjpdwOe2EohmQTGkk86Zvioeudsf%2BnnR/csn2c4mg7O123sQISuKZ/bXP77s5LwwHppeIGyBdk9SOISbSZd7ILKeL7C3LZ8YWVbtNtPqGfHVS5CXyD4hwqC9nxBfniYueg6Aup2jjPauJE%2B66LCKI%2BqL5A%2Bqbg8wCadlFZlSb8/Yov9A%2B05A4UOwsKyORY4/8hhOOO/t1T6r4rlQr6cUH7S9WOITrOEUYwIADmZlocIlctSQZuCxrB5hoH3q/emXHE2vuE%2BR/c0U8NKMlfRqIgFD/hFHsQofViAWWKQeBV92XtYyRS8yN21YIkYi%2Bm6GpFi7Y70SAMrZ0/5IVVGw8hiTVLAfsvLoaVu9YUksfWeE88QR2KUKcmlbWpDXqMIwbmN6y1yqGzELDAOtgF4zHr48cvz3jxrIa/c7dvHFR4jvWXOGi9%2BeB7izQrH%2BTUwQY5UfVAVEMdY6Ko7x6P2ljVFQe8z99uuNs9d3aAolM88D5kBWxH0WvLfba3VrK18UwgNkpairabQAg2Z33FNoszkO50BZIhgJgCrRh9roKCvbQpyNHBRwORrr8MJm%2BQfNUMXqtYjSM2/bIz1S3kOmYJrZZkX7Ny8uEWS7V6n7gCMTFIMo73MAaTBScc9LBW1FWkrRmQZdC6WFMvQFVclO1OfvjPlFZ5bwn70I9PXtJhhTAvt9Lr4T2y%2BoZXrWdZtgFHpwELWN%2BnfBRakvQ9MSouZvSmfGi3HEJD6PJdKL%2BJvooieZEM8%2BVroYAHWGenC4eU8lOaVQ1S1Fxd5Z6j3Ao3so0Vt0UV6AHiFwewuf%2BZZmUjZ9qRdmSw0a0oMDEpMtCs3lZk58uI%2Be42fWyYg3t%2BfbfXWeoW6KewX90qcocdIyDH%2BE7SSftC0ervXLcZbJpQ5iMBMoSnikbGavqe3UoOM4h2UbKvUeJu1piL5HQ9/ZgZ2ZlZDH38/2vCvffDAlbfW574HPmoPEE68mXof1P7PBcExbeYskX29/jscMn3WV4SE931YGRvROn43Xbfx8%2Br3L7g0MJzZHRTYQl5TgDu8y0ATY/HGBjo3Bek8J3ichRitvNowAnKn475pXgTpKs9hXdXYiJ8Ps4rOO9ERMAJIG2gZatq6X4FeoOvIq37LtWXlpM63W7rDw1Qv5IZvhcjo7sDdsS2q19qRaePH2KiIM%2Bqi1VZb8ZTanYQQwoayzmIurSw3OMZSU8yUs3KmHmKk0M4P%2BN57fTrJtcVNCo9o3Xbjf13RqsnMz6TJfHhYlPUKu7735c0XwLsYGfXLP7P3RsMAJ1UfI2oNwCLsa2L99qicePmtHn%2BqTq56iMOmN%2B8xkpmMaJYh3Yx2oGUQWAxlRs9KiXGazg44h0qUeJZFsOeF5paCuKF/mqllVkFipy0W8m9dBUk6DgkJWsaK06u/vuP5ErFOHecH5KXwdpWSLnN/Gp4olgjNM/9BlhcdBYZStlXu%2BUCfup/llrSecSWC190bIn%2BerQmZliv80ygq1qmao4dZb%2BmYpndOVkN3I7o0THmXZ2snmVngK/RhS2G5UbbZyhGZag6z5uHNV18OFOGntk1kBzDqWUGFrudgcWO/tqzFYsGehuX4OROrzfV%2B5lT7eGweInv7b1qqSealRRrrgZhTdNdb4CxTxtgeg2fi2cQxOKwMbUTN4sCpf3AsrqoemCRM/GHOJjqX2MfS3Wuu8tab0Dn6YtXmoFefOesLe3X7NRRXZg18WpjCazoAR9BuFnBt7JhD15/qhcI9vulDJNrMxIrDANVWG8sm2ar8vqmzhDTvqnL1RXqSnWVuk5dTzbEjRjRBxa7xEuXhujy%2BA12mMc8vgjjYzR0P1G07Hue8ilO7WLEArj4XFxchiRCWnOhJdohcQIcnQTSrdtgJSKZLDhde%2BoqWlyUYZ6CpZuc/LrIsTZn1LFsZRxrlqe3GdQAud4wP2tQB%2B2i7WxldUlQ6exXu3yqZ2jon6SEuUT8EJImiVqBhci0OGNIkTCRaQRvyke8VOjsjCxMc5inFCxuOy5rXJIrvbvvtd1%2Bd9yXtiIPNZD72o7iOu6rb8ijhEgUN/Fez/d%2BGcz%2BOlHY7i56e%2Bgcj6SK%2BtUAYt%2BPDrij6bryEpPBNpY3VSm8oiFsH68V9Zb1xH97%2B4qIG%2BVRd%2B/SlcvGbsUEi71RkNnA4p%2B/laDLrTkUxgJh6cxtXBVdouTxxd15ijM%2BzcOXsjO3jVENOi5NhhUXRc2IyTiZiN/pvkaQlR%2B8uJtNKzNgRVFPHJbSyd/%2BzkaFm%2Bhogl/cr8poSadiMzRUWeoe0apO3dZoqL6sVaINfkupkz12WvdLQ22lBuzJwraJ6MpqGCKfZbK8OKHZ/koacsVlNUJDCcO7tXTuN%2BZbmezQeeXFY3dd0qm5su3w2nqJXpIX147fYIMj/BgkCW5ZFeMW6q6vzanM42k6RyP5cRCqk7nlXkFZFEFeygC8AYsg78m9MrUyhcUxM9dxQlFh%2BJNC%2B4ZXpCgujAgXfoUR7VIPCscuWhh7zRZR2EZS/e0f3eRvHnDxkvH33bwNlqTFhJrw26bh7o3Ld709qgbATUuwQEVqiSl9xZvVrbdfuaFI5Qzmoma8GKFwJPDzBCS1Bnb/TAEG%2B73FGwcY2uRrDWz0nLDDA4RF/SZfUbGx12Kift/dp17SObr12yOovpHRklc9ehDgGkSSqGo2OhXHQ0WVql9B0apOxWFhqLJUPdGqVt3RwdG2y6wcCOAHkaEEMhg%2BS5gdI2ubE4oKIwRrigsjDGuqCiMURTWfOXzNRfwyZFUsx30LFqMcuKd2OWpGTN4tOy5nTkX9qq%2Bv0YHO8UPKbkEq1nYUARLOKsbx99a2TF33i6MeERwEzmByXwPACuAAI5B4VxN0Bs%2BxioboCxYdLgbbea%2BDIS9/MgzDZxzgMyUYVoV46nwD%2B8WVt9VHrmbnmTdWzstqHIDveDsLnicJGwD7tGXho6rDx%2B8Y9XfYlgvBUGe/ec6X/fP3hQ7wx697eD1a%2Bf//2u%2Bvoe21H34Q6/38/3eb8NPWj/hT9iSAJx1H4QDo6PHfPrUkRdlMwlmSdZ%2BBU7S%2BaAlxfnP/vKtuqvYV9wWB3u8rI4l5fPeS1LI6mn0ySu6%2Ba%2BWk1VHtPUXRmiulEtkRWS/ibt96gLo3cFJvkfvbwzcPHj3te9gL350%2BWf1e79UZrLVWvL9pTTZevlFLMEE%2Bma/hScdi%2BYG9wpT93pdbq/YK7/Y2ae%2BRaugOcbbqe0crWDUluMsVfS53BXcEzRbuvtb/Sq/60ORD%2BhPBaGK8bZQWgjzJ0MnorktsE9l2Ovy9MoS9Rpy5UouUlB0TPbUpoxh9NoP3UHuziKvEDGbm3dlzjv8bWHqkzDUCnc0MmV49sE8cejNDFue2PSGNV%2B90GKTc7gpK1Jqt8rT0wHXrx0rernj0jDKkuj32Ru9Kg0emLV9XhF2ZUW%2BmHi/42d8xbZmMZPTFWxAd/uz2W8P7v1APBWmpmuui5AHj5oZDdig%2BOqRQRiiHlWEO81bxudPKSRwfqnu/v5WmVBCaR0vrdH/R%2B70O4iSApuSpBhbJKX9x2w/jvM2wxBwzdIa2x15RZqT2T6bvDBrd1aUdzaOWU4XaJOl2msNstAU0lC4MrYIJzqbJ6MYWjP5lTY42pGyT3rZfHHgxxkvOLpIFwENAX3eRE1Aa2AV2B2JH05JUOaIyKkon2XOe84tHrYPiWJ3ysp6baW3AyTm93VA4O8nnm7acZdRLn6fia58Hik9WsY27LSL6dp542rYAmieipvTszHdezqv81QqRd6jVijJNQyVbR7g%2BPsIrxq9sLuSh%2BZkF2%2B42LJ1RDXRIGlhDslkhekZuff/7/WwOZH3YJHL943qB3aHaSuUX7ZAh9eOdM660a1ypNkYUrrpC/PTq6scK75qQTMP3JA/2Z6qEwCzMEE%2BMHlTs%2B696uLgeqOriis2NBqOfVWmvjbOi0WHkBvQAeqp8uAdTz41338XipdWqn81gfWVFmTkPntJ9TgPdRA0sqRUIAo/T4WGF1TQhWWdPDUZUHitXFm2T4089mHuM4Dp0MKLysLJjvud%2BoPY4lU2jz78kvIM6nKlWsE5p3/OR3M%2B8tY6eNDs/P6kmuDlATJAxFlz5LqUDvJhFCTxJo1xb/SQjXu3HAiusPUcS2WLJPAyBCElajM1FH8KwsBoWJO2QS3jatIeZsrbSKpN3CHjGQsAyIZmGz5IXeSi1AAux2ArAesWfkY%2BIRG8iarEybAV8h29FAOQlotmsfr22wRzmtARajkm95/bMaA%2BEBRZKNIcC7Aa00nhCkTX7ravb6mqbtW1FxF1wVq4cooCoe4r/CgwAt/7hatvzf0YccvhbPN0JAODqX8e/%2BYf%2BvXEjDe2O%2BwD/GGGw4WmOXks1QMj/hf6fH2Vi%2Bxe5Qq7SuaoAopCsfJbLM0FPKyA4OkgaRwrLgstWZ/QBFwYDUGrFdncLPfWMjlm4V2MAWNlDnpBW0kWYpIfUkRFSxQr32Cd4T3sL5hhnmk6AKZ2JtJbLereSPHppS6GlIzkWVRGWFYrcbm3njqSyV2v2K59HIjm3tK6btOGU5dn%2B0XfZ32%2BY4w0VOxvHN/slHqoD5BoSq6OjwH6fwva8YsRZYSbRJCmSlPf5%2BoM8cUjLMFO2LD5H73vph3tsGkpeXnD5DEczYVgMSDAKm3742VzEUVURkNw%2BTiotCGoFAd4vYcDofCsBfDMVgknHjZJq4R7AX1vBlmFeZkPZ7kl3cvxgn9qPGleKxtvlPDVmVIF/ZuvT/VTXkwt8gHvHCibqTj/1iKb%2BqbnCIDjN3JPYNRsr2HMs2Tf6nl%2Bp7scs6N9O7TSS/P4AcE%2BdUu%2BcpYbq2M7JPV9kTUE48iFkRVLnVJmiBg6K%2Bc0l1Q7gGYwllMC/BDnPAtDpCiArXdhBu4S0Y2GwVQTnVxjH8dOdS8JGaSVA9jV4yCdMD73q8GgnizNHrhE0AwGaRT2ubaVem5CXzQoOp0i9/dhVfPcGPgFgry95Fi1SWh30hvG2cgS%2B4bjH%2BLLb7yv%2BedlIn3jtW%2BcQSdI8y19uHejTsSDXb4vfp8jmqa%2BvVtlaOt46CjQhk3aSlsLcoLVH1%2BaSwIl9Mc196i1zSk%2B2y/zqxD3pGRweuZry21i04wuEQINV62cD69d3WuiZ0AdKxPCOq87mOcyn0XHaDsM5hfpZXNaJZaPveyZyZnfNadhgq2KcQ7oSdQ2fpP2pjmeVPquF5szmzE7yXLu7HkqucCm0AmhulgqLFpKT1b4uTQHslfFWRwBWCXmhUcimGZA2rNFm797D3BpPY6bmea7/bCUWugwXIxlp7Q3H5OWlhPlzXL/LkG1clPjUWRAz6M7QAZmCAWKRvJv025yQABxojhBIugDKC6DjCdFkejzDSZXbotSWOF5DcvIWX1oFD0XjSgKJZMInjhhiSQEJXUShl745TINZfPxIMgdzWespJNPNRVkw4GLoe8K7iXEBCwcOreQ%2BIjlhrGSsfZY2GHNrbQ8MbLAeToFBGlzineERI4ExOA2HsYpfFn%2BiB5DKovYEo5NgsQVDTRMScfBY4tqPhFNIVIFJAtwfKI/aQ/h/LiTjQkJk2AQMpJXECZ6ODzCGBWwarM%2BeELbXZ%2BcRDA%3D%3D%27%29%20format%28%27woff2%27%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A/%2A%20%40%40END_FONTS%40%40%20%2A/%0A%20%20%20%20%3C/style%3E%0A%20%20%20%20%3Cstyle%3E%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20DESIGN%20TOKENS%20%28defaults%20overwritten%20at%20runtime%20by%20applyPreset%29%20%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20%3Aroot%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20--bg-body%3A%20%20%20%20%20%20%20%23030603%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--bg-container%3A%20%20linear-gradient%28145deg%2C%23141a14%2C%230a0f0a%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--bg-screen%3A%20%20%20%20%20%230a0e0a%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--border-screen%3A%20%232a4a2a%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--color-primary%3A%20%231eff00%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--color-dim%3A%20%20%20%20%20%233c9e3c%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--color-faint%3A%20%20%20%232a8a2a%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--color-muted%3A%20%20%20%233a7a3a%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--shadow-outer%3A%20%200%200%200%202px%20%231a3a1a%2C0%2020px%2040px%20rgba%280%2C0%2C0%2C0.5%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--shadow-screen%3A%20inset%200%200%2025px%20rgba%280%2C255%2C0%2C0.06%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--bg-stats%3A%20%20%20%20%20%20%230a0c0a%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--bg-btn%3A%20%20%20%20%20%20%20%20%231a2a1a%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--border-btn%3A%20%20%20%20%233c9e3c%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--color-btn%3A%20%20%20%20%20%23aaffaa%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--flash-color%3A%20%20%20rgba%280%2C255%2C0%2C0.4%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20--prim%3A%20%20%20%20%20%20%20%20%20%20%27merri%27%2C%27deja%27%2C%27Consolas%27%2Cmonospace%3B%20/%2A%20DejaVuSans%20for%20Arabic%20fallback%20%2A/%0A%20%20%20%20%20%20%20%20%20%20%20%20--crt-on%3A%20%20%20%20%20%20%20%200%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%2A%20%7B%20margin%3A0%3B%20padding%3A0%3B%20box-sizing%3Aborder-box%3B%20user-select%3Anone%3B%20%7D%0A%0A%20%20%20%20%20%20%20%20body%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--bg-body%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20min-height%3A%20100vh%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20justify-content%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20align-items%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20var%28--prim%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20background%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20svg%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20vertical-align%3A%20middle%3B%20/%2A%20SVGs%20often%20sit%20slightly%20%22higher%22%20than%20text%20%2A/%0A%20%20%20%20%20%20%20%20%20%20%20%20margin-bottom%3A%200.125em%3B%20/%2A%20Fine-tune%20to%20align%20with%20font%20baseline%20%2A/%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20STAGE%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%20%20%20%20%20%20%20%20%20%20%209%3A16%20default.%20.ar-landscape%20flips%20to%2016%3A9.%0A%20%20%20%20%20%20%20%20%20%20%20max-width%20%3D%20min%28%29%20prevents%20overflow%20on%20both%20axes.%0A%20%20%20%20%20%20%20%20%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20.stage%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20aspect-ratio%3A%209%20/%2016%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20max-width%3A%20min%28100vw%2C%20calc%28100vh%20%2A%209%20/%2016%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20relative%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-shadow%3A%20var%28--shadow-outer%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20box-shadow%200.5s%2C%20max-width%200.3s%2C%20aspect-ratio%200.3s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.stage.ar-landscape%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20aspect-ratio%3A%2016%20/%209%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20max-width%3A%20min%28100vw%2C%20calc%28100vh%20%2A%2016%20/%209%29%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.container%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--bg-container%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20flex-direction%3A%20column%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%201px%202px%2014px%202px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20background%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20SCREEN%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20.screen%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20flex%3A%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--bg-screen%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%2010px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-shadow%3A%20var%28--shadow-screen%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20relative%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20overflow%3A%20hidden%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border%3A%201px%20solid%20var%28--border-screen%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20margin-bottom%3A%206px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20background%200.5s%2C%20border-color%200.5s%2C%20box-shadow%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20CRT%20scanlines%20%E2%80%94%20CSS%20pseudo-elements%2C%20GPU%20composited.%0A%20%20%20%20%20%20%20%20%20%20%20Toggled%20via%20.crt-on%20class%20by%20applyPreset%28%29.%20%2A/%0A%20%20%20%20%20%20%20%20.screen.crt-on%3A%3Abefore%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20content%3A%20%22%22%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20inset%3A%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20repeating-linear-gradient%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200deg%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rgba%280%2C255%2C0%2C0.04%29%200px%2C%20rgba%280%2C255%2C0%2C0.04%29%202px%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20transparent%202px%2C%20transparent%205px%0A%20%20%20%20%20%20%20%20%20%20%20%20%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pointer-events%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20z-index%3A%205%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.screen.crt-on%3A%3Aafter%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20content%3A%20%22%22%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20inset%3A%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20radial-gradient%28circle%20at%2050%25%2040%25%2C%20transparent%2050%25%2C%20rgba%280%2C0%2C0%2C0.5%29%20100%25%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pointer-events%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20z-index%3A%205%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20Screen%20flash%20%E2%80%94%20animates%20box-shadow%20via%20CSS%20keyframe.%0A%20%20%20%20%20%20%20%20%20%20%20The%20browser%20GPU%20compositor%20runs%20this%20at%20native%20frame%20rate%0A%20%20%20%20%20%20%20%20%20%20%20with%20zero%20JS%20involvement%20after%20the%20class%20is%20added.%20%2A/%0A%20%20%20%20%20%20%20%20%40keyframes%20kScreenFlash%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20box-shadow%3A%20inset%200%200%2060px%20var%28--flash-color%29%2C%200%200%2020px%20var%28--flash-color%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20box-shadow%3A%20var%28--shadow-screen%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.screen.screen-flash%20%7B%20animation%3A%20kScreenFlash%200.12s%20ease-out%20forwards%3B%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20MASTER%20CANVAS%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20/%2A%20Renders%3A%20background%20fill%2C%20CRT%20%28canvas%20version%20when%20crt-on%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20dust%20particles%2C%20visualizer%20drawImage.%0A%20%20%20%20%20%20%20%20%20%20%20Does%20NOT%20render%3A%20lyrics%2C%20flash%20%E2%80%94%20those%20stay%20in%20the%20DOM%0A%20%20%20%20%20%20%20%20%20%20%20where%20the%20CSS%20engine%20animates%20them%20at%20native%20quality.%20%2A/%0A%20%20%20%20%20%20%20%20%23masterCanvas%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20inset%3A%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3A%20100%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20block%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20z-index%3A%201%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20LYRIC%20AREA%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20/%2A%20Sits%20above%20the%20canvas%20in%20z-order.%20The%20CSS%20engine%20handles%20all%0A%20%20%20%20%20%20%20%20%20%20%20text%20rendering%20and%20animation%20%E2%80%94%20subpixel%20antialiasing%2C%20kerning%2C%0A%20%20%20%20%20%20%20%20%20%20%20hardware-accelerated%20transforms.%20No%20canvas%20approximation.%20%2A/%0A%20%20%20%20%20%20%20%20.lyric-area%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20inset%3A%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20flex-direction%3A%20column%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20justify-content%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20align-items%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-align%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20z-index%3A%2010%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%2030px%2020px%2080px%2020px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pointer-events%3A%20none%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.current-line%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20var%28--prim%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%20clamp%282.5rem%2C%206vw%2C%202.8rem%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20bold%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var%28--color-primary%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-shadow%3A%200%200%205px%20var%28--color-dim%29%2C%200%200%208px%20var%28--color-faint%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20margin-bottom%3A%2016px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20line-height%3A%201.4%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20will-change%3A%20transform%2C%20opacity%2C%20filter%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20color%200.5s%2C%20text-shadow%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20.next-line%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20var%28--prim%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%20clamp%280.5rem%2C%203vw%2C%201rem%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var%28--color-dim%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3A%200.7%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-top%3A%201px%20dashed%20var%28--color-faint%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding-top%3A%2015px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20color%200.5s%2C%20border-color%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20VISUALIZER%20WRAP%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20/%2A%20Invisible%20positioning%20anchor%20%E2%80%94%20the%20visualizer%27s%20canvas%20is%0A%20%20%20%20%20%20%20%20%20%20%20drawImage%27d%20onto%20masterCanvas%20each%20frame.%20visWrap%20itself%20is%0A%20%20%20%20%20%20%20%20%20%20%20never%20painted%3B%20it%20just%20provides%20getBoundingClientRect%28%29.%20%2A/%0A%20%20%20%20%20%20%20%20.visualizer-wrap%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20bottom%3A%20-30px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20left%3A%2050%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transform%3A%20translateX%28-50%25%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20visibility%3A%20hidden%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pointer-events%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20z-index%3A%202%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20SEEKBAR%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20.seekbar-container%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20align-items%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gap%3A%206px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%204px%2010px%200%2010px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%23seekBar%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20flex%3A%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20-webkit-appearance%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20appearance%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20height%3A%203px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%202px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--bg-btn%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20outline%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20pointer%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%23seekBar%3A%3A-webkit-slider-thumb%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20-webkit-appearance%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%2010px%3B%20height%3A%2010px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%2050%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--color-primary%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20pointer%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20box-shadow%3A%200%200%204px%20var%28--color-dim%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%23seekBar%3A%3A-moz-range-thumb%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%2010px%3B%20height%3A%2010px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border%3A%20none%3B%20border-radius%3A%2050%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--color-primary%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20pointer%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20STATS%20BAR%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20.stats%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20justify-content%3A%20space-between%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20align-items%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var%28--color-faint%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%208px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--bg-stats%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%204px%2010px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%2020px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20monospace%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20background%200.5s%2C%20color%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20CONTROLS%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20.controls%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20flex-direction%3A%20column%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20align-items%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gap%3A%2010px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20margin-top%3A%206px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20.controls-row-main%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20gap%3A%2010px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20justify-content%3A%20center%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20/%2A%20Icon-only%20circular%20buttons%20%E2%80%94%20tooltip%20via%20data-tip%20%2B%20%3A%3Aafter%20%2A/%0A%20%20%20%20%20%20%20%20button%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20var%28--bg-btn%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border%3A%201px%20solid%20var%28--border-btn%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var%28--color-btn%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20monospace%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-weight%3A%20bold%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%2032px%3B%20height%3A%2032px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%2050%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20cursor%3A%20pointer%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%200.85rem%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20line-height%3A%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20display%3A%20flex%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20align-items%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20justify-content%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20relative%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20filter%200.1s%2C%20box-shadow%200.1s%2C%20border-color%200.4s%2C%20color%200.4s%2C%20background%200.4s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20button%3Ahover%20%7B%20filter%3Abrightness%281.5%29%3B%20box-shadow%3A0%200%208px%20var%28--color-primary%29%3B%20%7D%0A%20%20%20%20%20%20%20%20button%3A%3Aafter%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20content%3A%20attr%28data-tip%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20position%3A%20absolute%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20bottom%3A%20calc%28100%25%20%2B%206px%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20left%3A%2050%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transform%3A%20translateX%28-50%25%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20background%3A%20%23111%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var%28--color-btn%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%207px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20white-space%3A%20nowrap%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%202px%206px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%204px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20pointer-events%3A%20none%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3A%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20opacity%200.15s%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border%3A%201px%20solid%20var%28--border-btn%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20button%3Ahover%3A%3Aafter%20%7B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20/%2A%20Util%20buttons%20%E2%80%94%20pill%20shape%2C%20smaller%20text%20%2A/%0A%20%20%20%20%20%20%20%20button.btn-util%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20width%3A%20auto%3B%20height%3A%2020px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20border-radius%3A%2010px%2030px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%200.6rem%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20padding%3A%200%208px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20margin-top%3A%2014px%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20button.btn-util%3Ahover%3A%3Aafter%20%7B%20bottom%3Acalc%28100%25%20%2B%204px%29%3B%20%7D%0A%0A%20%20%20%20%20%20%20%20.status-text%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-size%3A%207px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20text-align%3A%20center%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20margin-top%3A%204px%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3A%20var%28--color-muted%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20font-family%3A%20monospace%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20transition%3A%20color%200.5s%3B%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20/%2A%20%E2%94%80%E2%94%80%20PRESET%20STYLES%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%20%2A/%0A%20%20%20%20%20%20%20%20/%2A%20compile.py%20injects%20all%20%40keyframes%20and%20.fx-%2A%20classes%20here.%0A%20%20%20%20%20%20%20%20%20%20%20Template%20owns%20zero%20preset-specific%20CSS.%20%2A/%0A%20%20%20%20%20%20%20%20/%2A%20%40%40STYLES%40%40%20%2A/%0A/%2A%20%E2%94%80%E2%94%80%20RAP%20%E2%94%80%E2%94%80%20%2A/%0A%40keyframes%20kShake%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20transform%3Atranslate%280%2C0%29%3B%20%20%20%20%20%20text-shadow%3A0%200%203px%20%230f0%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2025%25%20%7B%20transform%3Atranslate%28-2px%2C1px%29%3B%20text-shadow%3A2px%200%20%23ff00aa%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2050%25%20%7B%20transform%3Atranslate%282px%2C-1px%29%3B%20text-shadow%3A-2px%200%20%230effff%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2075%25%20%7B%20transform%3Atranslate%28-1px%2C1px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20transform%3Atranslate%280%2C0%29%3B%20%20%20%20%20%20text-shadow%3A0%200%205px%20%231eff00%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kGlitch%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A-1px%200%20%23ff00aa%2C1px%200%20%2300ffff%3B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2050%25%20%7B%20text-shadow%3A2px%200%20%23ff00aa%2C-2px%200%20%2300ffff%3B%20opacity%3A0.9%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%205px%20%231eff00%3B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kChroma%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20filter%3Anone%3B%20text-shadow%3A0%200%200%20transparent%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2030%25%20%7B%20text-shadow%3A-4px%200%20%23ff0044%2C4px%200%20%2300ffcc%3B%20filter%3Abrightness%281.4%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2060%25%20%7B%20text-shadow%3A3px%200%20%23ff0044%2C-3px%200%20%2300ffcc%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20filter%3Anone%3B%20text-shadow%3A0%200%205px%20%231eff00%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kFlicker%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A1%3B%20%7D%20%2015%25%7B%20opacity%3A0.2%3B%20%7D%2030%25%7B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2050%25%20%7B%20opacity%3A0.5%3B%7D%2065%25%7B%20opacity%3A1%3B%20%20%7D%2080%25%7B%20opacity%3A0.3%3B%20%7D%20100%25%7B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kZoom%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20transform%3Ascale%281%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2040%25%20%7B%20transform%3Ascale%281.08%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20transform%3Ascale%281%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kScanWipe%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20background-position%3A0%20-100%25%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20background-position%3A0%20200%25%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.screen.crt-on.scan-active%3A%3Abefore%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20background%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20repeating-linear-gradient%280deg%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20rgba%280%2C255%2C0%2C0.04%29%200px%2Crgba%280%2C255%2C0%2C0.04%29%202px%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20transparent%202px%2Ctransparent%205px%29%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linear-gradient%28to%20bottom%2Ctransparent%200%25%2Crgba%280%2C255%2C0%2C0.18%29%2050%25%2Ctransparent%20100%25%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20background-size%3A100%25%20100%25%2C100%25%2030%25%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20animation%3AkScanWipe%200.25s%20linear%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-shake%20%20%20%7B%20animation%3AkShake%20%20%200.08s%20ease-in-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-glitch%20%20%7B%20animation%3AkGlitch%20%200.12s%202%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-chroma%20%20%7B%20animation%3AkChroma%20%200.18s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-flicker%20%7B%20animation%3AkFlicker%200.20s%20linear%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-zoom%20%20%20%20%7B%20animation%3AkZoom%20%20%20%200.15s%20ease-out%3B%20%7D%0A%0A/%2A%20%E2%94%80%E2%94%80%20ETHEREAL%20%E2%94%80%E2%94%80%20%2A/%0A%40keyframes%20kDrift%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20transform%3AtranslateY%28-5px%29%3B%20opacity%3A0.7%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20transform%3AtranslateY%280%29%3B%20%20%20%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kBreathe%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20letter-spacing%3A1px%3B%20opacity%3A0.7%3B%20filter%3Ablur%280.8px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20letter-spacing%3Anormal%3B%20%20opacity%3A1%3B%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kEtherealAurora%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A0%200%2018px%20%23c084fc%2C0%200%2036px%20%23a855f7%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2060%25%20%7B%20text-shadow%3A0%200%208px%20%23818cf8%2C0%200%2016px%20%236366f1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%205px%20%23a855f7%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kDissolve%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.45%3B%20filter%3Ablur%282.5px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2050%25%20%7B%20opacity%3A0.8%3B%20%20filter%3Ablur%280.5px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kFloat%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20transform%3Ascale%281.05%29%20translateY%28-4px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20transform%3Ascale%281%29%20%20%20%20translateY%280%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-drift%20%20%20%20%7B%20animation%3AkDrift%20%20%20%20%20%20%20%20%200.25s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-breathe%20%20%7B%20animation%3AkBreathe%20%20%20%20%20%20%20%200.30s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-aurora%20%20%20%7B%20animation%3AkEtherealAurora%200.35s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-dissolve%20%7B%20animation%3AkDissolve%20%20%20%20%20%20%200.28s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-float%20%20%20%20%7B%20animation%3AkFloat%20%20%20%20%20%20%20%20%20%200.22s%20ease-out%3B%20%7D%0A%0A/%2A%20%E2%94%80%E2%94%80%20VOID%20%E2%94%80%E2%94%80%20%2A/%0A%40keyframes%20kVoidBlink%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%7B%20opacity%3A0.05%3B%20%7D%2015%25%7B%20opacity%3A1%3B%20%7D%20100%25%7B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kVoidSlice%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20clip-path%3Ainset%280%200%2060%25%200%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2040%25%20%7B%20clip-path%3Ainset%2840%25%200%200%200%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20clip-path%3Ainset%280%200%200%200%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kVoidDim%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.25%3B%20filter%3Abrightness%280.3%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kVoidShift%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20letter-spacing%3A0.9px%3B%20opacity%3A0.6%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20letter-spacing%3Anormal%3B%20opacity%3A1%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kVoidStatic%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A4px%200%20%23fff%2C-4px%200%20%236070a0%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%2040%25%20%7B%20text-shadow%3A-3px%200%20%23fff%2C3px%200%20%236070a0%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%202px%20%23d0d8e8%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-void-blink%20%20%7B%20animation%3AkVoidBlink%20%200.12s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-void-slice%20%20%7B%20animation%3AkVoidSlice%20%200.14s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-void-dim%20%20%20%20%7B%20animation%3AkVoidDim%20%20%20%200.18s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-void-shift%20%20%7B%20animation%3AkVoidShift%20%200.16s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-void-static%20%7B%20animation%3AkVoidStatic%200.15s%20ease-out%3B%20%7D%0A%0A/%2A%20%E2%94%80%E2%94%80%20DREAM%20%E2%94%80%E2%94%80%20%2A/%0A%40keyframes%20kDreamFade%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.35%3B%20filter%3Ablur%281.5px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kDreamFog%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20filter%3Ablur%282px%29%20brightness%281.3%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kDreamPulse%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.5%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kDreamGlow%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A0%200%2022px%20%237ee8e8%2C0%200%2045px%20%233ab0b0%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%204px%20%237ee8e8%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kDreamRipple%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20letter-spacing%3A1px%3B%20opacity%3A0.6%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20letter-spacing%3Anormal%3B%20opacity%3A1%3B%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-dream-fade%20%20%20%7B%20animation%3AkDreamFade%20%20%200.22s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-dream-fog%20%20%20%20%7B%20animation%3AkDreamFog%20%20%20%200.25s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-dream-pulse%20%20%7B%20animation%3AkDreamPulse%20%200.18s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-dream-glow%20%20%20%7B%20animation%3AkDreamGlow%20%20%200.30s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-dream-ripple%20%7B%20animation%3AkDreamRipple%200.20s%20ease-out%3B%20%7D%0A%0A/%2A%20%E2%94%80%E2%94%80%20AURORA%20%E2%94%80%E2%94%80%20%2A/%0A%40keyframes%20kAuroraShift%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A0%200%2020px%20%23e080ff%2C0%200%2010px%20%2380ffcc%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%204px%20%23a8d8ff%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kAuroraSweep%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.55%3B%20filter%3Ahue-rotate%2860deg%29%20brightness%281.3%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kAuroraSwell%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A0%200%2030px%20%23c080ff%2C0%200%2060px%20%238040ff%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%204px%20%23a8d8ff%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kAuroraShimmer%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.5%3B%20filter%3Abrightness%281.6%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kAuroraHaze%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20filter%3Ablur%280.8px%29%20hue-rotate%2850deg%29%20brightness%281.25%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-aurora-shift%20%20%20%7B%20animation%3AkAuroraShift%20%20%200.22s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-aurora-sweep%20%20%20%7B%20animation%3AkAuroraSweep%20%20%200.20s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-aurora-swell%20%20%20%7B%20animation%3AkAuroraSwell%20%20%200.35s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-aurora-shimmer%20%7B%20animation%3AkAuroraShimmer%200.18s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-aurora-haze%20%20%20%20%7B%20animation%3AkAuroraHaze%20%20%20%200.28s%20ease-out%3B%20%7D%0A%0A/%2A%20%E2%94%80%E2%94%80%20EMBER%20%E2%94%80%E2%94%80%20%2A/%0A%40keyframes%20kEmberFlare%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20text-shadow%3A0%200%2028px%20%23ff4000%2C0%200%2050px%20%23ff2000%3B%20filter%3Abrightness%281.7%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20text-shadow%3A0%200%204px%20%23ff9040%3B%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kEmberChar%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.4%3B%20filter%3Asepia%281%29%20brightness%280.5%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kEmberSmolder%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20letter-spacing%3A0.5px%3B%20text-shadow%3A0%200%2018px%20%23ff6020%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20letter-spacing%3Anormal%3B%20text-shadow%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kEmberHeat%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20filter%3Abrightness%281.4%29%20saturate%281.5%29%20blur%280.5px%29%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20filter%3Anone%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%40keyframes%20kEmberPulse%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%200%25%20%20%7B%20opacity%3A0.5%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20100%25%7B%20opacity%3A1%3B%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-ember-flare%20%20%20%7B%20animation%3AkEmberFlare%20%20%200.18s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-ember-char%20%20%20%20%7B%20animation%3AkEmberChar%20%20%20%200.20s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-ember-smolder%20%7B%20animation%3AkEmberSmolder%200.22s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-ember-heat%20%20%20%20%7B%20animation%3AkEmberHeat%20%20%20%200.25s%20ease-out%3B%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20.fx-ember-pulse%20%20%20%7B%20animation%3AkEmberPulse%20%20%200.16s%20ease-out%3B%20%7D%0A/%2A%20%40%40END_STYLES%40%40%20%2A/%0A%20%20%20%20%3C/style%3E%0A%3C/head%3E%0A%3Cbody%3E%0A%0A%3Cdiv%20class%3D%22stage%22%20id%3D%22stage%22%3E%0A%20%20%20%20%3Cdiv%20class%3D%22container%22%3E%0A%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22screen%22%20id%3D%22screen%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%21--%20Canvas%3A%20background%2C%20CRT%20scanlines%2C%20dust%2C%20visualizer%20--%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Ccanvas%20id%3D%22masterCanvas%22%3E%3C/canvas%3E%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%21--%20DOM%20lyric%20layer%3A%20CSS%20engine%20handles%20text%20rendering%20%2B%20animation%20--%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22lyric-area%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22current-line%22%20id%3D%22currentLine%22%3E%3C/div%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22next-line%22%20%20%20%20id%3D%22nextLine%22%3E%3C/div%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C%21--%20Invisible%20anchor%20for%20visualizer%20getBoundingClientRect%28%29%20--%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22visualizer-wrap%22%20id%3D%22visualizerWrap%22%3E%3C/div%3E%0A%20%20%20%20%20%20%20%20%3C/div%3E%0A%0A%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22seekbar-container%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cinput%20type%3D%22range%22%20id%3D%22seekBar%22%20min%3D%220%22%20max%3D%22100%22%20value%3D%220%22%20step%3D%220.1%22%3E%0A%20%20%20%20%20%20%20%20%3C/div%3E%0A%0A%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22stats%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%20id%3D%22statusMsg%22%3E%3C/span%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%20id%3D%22timeDisplay%22%3E0%3A00%3C/span%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cspan%20id%3D%22bpmDisplay%22%3E--%20BPM%3C/span%3E%0A%20%20%20%20%20%20%20%20%3C/div%3E%0A%0A%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22controls%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22controls-row-main%22%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20class%3D%22btn-util%22%20id%3D%22arBtn%22%20%20%20%20data-tip%3D%22Aspect%20ratio%22%3E%3C/button%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20id%3D%22playBtn%22%20%20%20data-tip%3D%22Play%20/%20Pause%22%3E%3C/button%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20id%3D%22resetBtn%22%20%20data-tip%3D%22Reset%20%2B%20new%20file%22%3E%3C/button%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20id%3D%22modeBtn%22%20%20%20data-tip%3D%22Sync%20mode%20A/B%22%3E%3C/button%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20id%3D%22presetBtn%22%20data-tip%3D%22Switch%20preset%22%3E%3C/button%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3Cbutton%20class%3D%22btn-util%22%20id%3D%22vizBtn%22%20%20%20data-tip%3D%22Visualizer%22%3E%3C/button%3E%0A%20%20%20%20%20%20%20%20%20%20%20%20%3C/div%3E%0A%20%20%20%20%20%20%20%20%3C/div%3E%0A%20%20%20%20%20%20%20%20%3Cdiv%20class%3D%22status-text%22%20id%3D%22statusText%22%3E%3C/div%3E%0A%20%20%20%20%3C/div%3E%0A%3C/div%3E%0A%0A%3Cscript%3E%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20INJECTED%20BLOCKS%0A//%20compile.py%20replaces%20each%20block%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A%0A/%2A%20%40%40VISUALIZERS%40%40%20%2A/%0Aconst%20CANVAS_SCALE%20%3D%202.6%3B%0Aconst%20ROOT_STYLE%20%3D%20document.documentElement%3B%0Afunction%20readVizVars%28%29%20%7B%0A%20%20%20%20const%20s%20%3D%20getComputedStyle%28ROOT_STYLE%29%3B%0A%20%20%20%20return%20%7B%0A%20%20%20%20%20%20%20%20color%3A%20%20%20%20%20s.getPropertyValue%28%27--viz-color%27%29.trim%28%29%20%20%20%20%20%7C%7C%20%27%23ffffff%27%2C%0A%20%20%20%20%20%20%20%20glow%3A%20%20%20%20%20%20s.getPropertyValue%28%27--viz-glow%27%29.trim%28%29%20%20%20%20%20%20%7C%7C%20%27rgba%28255%2C255%2C255%2C0.4%29%27%2C%0A%20%20%20%20%20%20%20%20secondary%3A%20s.getPropertyValue%28%27--viz-secondary%27%29.trim%28%29%20%7C%7C%20%27rgba%28255%2C255%2C255%2C0.2%29%27%2C%0A%20%20%20%20%7D%3B%0A%7D%0Afunction%20easeOutExpo%28t%29%20%7B%0A%20%20%20%20return%20t%20%3D%3D%3D%201%20%3F%201%20%3A%201%20-%20Math.pow%282%2C%20-10%20%2A%20t%29%3B%0A%7D%0Afunction%20easeOutSine%28t%29%20%7B%20return%20Math.sin%28%28t%20%2A%20Math.PI%29%20/%202%29%3B%20%7D%0Aclass%20BaseVisualizer%20%7B%0A%20%20%20%20constructor%28wrap%29%20%7B%0A%20%20%20%20%20%20%20%20this.wrap%20%20%20%3D%20wrap%3B%0A%20%20%20%20%20%20%20%20this.canvas%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20this.ctx%20%20%20%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20this._init%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_init%28%29%20%7B%20throw%20new%20Error%28%27BaseVisualizer._init%28%29%20must%20be%20implemented%27%29%3B%20%7D%0A%20%20%20%20pulse%28%29%20%7B%20throw%20new%20Error%28%27BaseVisualizer.pulse%28%29%20must%20be%20implemented%27%29%3B%20%7D%0A%20%20%20%20_cleanup%28%29%20%7B%7D%0A%20%20%20%20destroy%28%29%20%7B%0A%20%20%20%20%20%20%20%20this._cleanup%28%29%3B%0A%20%20%20%20%20%20%20%20if%20%28this._rafId%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20cancelAnimationFrame%28this._rafId%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this._rafId%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20if%20%28this.canvas%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28this.ctx%29%20this.ctx.clearRect%280%2C%200%2C%20this.canvas.width%2C%20this.canvas.height%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this.canvas.width%20%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this.canvas.height%20%3D%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28this.canvas.parentNode%29%20this.canvas.parentNode.removeChild%28this.canvas%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this.canvas%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this.ctx%20%20%20%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20this.wrap%20%3D%20null%3B%0A%20%20%20%20%7D%0A%20%20%20%20_makeCanvas%28w%2C%20h%29%20%7B%0A%20%20%20%20%20%20%20%20this.wrap.innerHTML%20%3D%20%27%27%3B%0A%20%20%20%20%20%20%20%20const%20canvas%20%3D%20document.createElement%28%27canvas%27%29%3B%0A%20%20%20%20%20%20%20%20canvas.width%20%20%3D%20w%3B%0A%20%20%20%20%20%20%20%20canvas.height%20%3D%20h%3B%0A%20%20%20%20%20%20%20%20canvas.style.cssText%20%3D%20%60width%3A%24%7Bw%7Dpx%3Bheight%3A%24%7Bh%7Dpx%3Bdisplay%3Ablock%3B%60%3B%0A%20%20%20%20%20%20%20%20canvas.style.willChange%20%3D%20%27transform%2C%20opacity%27%3B%0A%20%20%20%20%20%20%20%20this.wrap.appendChild%28canvas%29%3B%0A%20%20%20%20%20%20%20%20this.canvas%20%3D%20canvas%3B%0A%20%20%20%20%20%20%20%20this.ctx%20%20%20%20%3D%20canvas.getContext%28%272d%27%29%3B%0A%20%20%20%20%20%20%20%20return%20canvas%3B%0A%20%20%20%20%7D%0A%7D%0Aclass%20RingVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this.scale%20%20%20%3D%201%3B%0A%20%20%20%20%20%20%20%20this.BASE%20%20%20%20%3D%2060%3B%0A%20%20%20%20%20%20%20%20this.MAX%20%20%20%20%20%3D%201.65%3B%0A%20%20%20%20%20%20%20%20this.DECAY%20%20%20%3D%200.88%3B%0A%20%20%20%20%20%20%20%20this.RING_W%20%20%3D%203%3B%0A%20%20%20%20%20%20%20%20const%20sz%20%20%20%20%20%3D%20this.BASE%20%2A%20CANVAS_SCALE%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28sz%2C%20sz%29%3B%0A%20%20%20%20%20%20%20%20this._loop%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%0A%20%20%20%20%20%20%20%20this.scale%20%3D%20this.MAX%3B%0A%20%20%20%20%7D%0A%20%20%20%20_loop%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20this.scale%20%3D%201%20%2B%20%28this.scale%20-%201%29%20%2A%20this.DECAY%3B%0A%20%20%20%20%20%20%20%20this._draw%28%29%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28%28%29%20%3D%3E%20this._loop%28%29%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_draw%28%29%20%7B%0A%20%20%20%20%20%20%20%20const%20%7B%20canvas%2C%20ctx%2C%20scale%2C%20BASE%2C%20RING_W%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20color%2C%20glow%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20const%20cx%20%3D%20canvas.width%20/%202%2C%20cy%20%3D%20canvas.height%20/%202%3B%0A%20%20%20%20%20%20%20%20const%20r%20%20%3D%20%28BASE%20/%202%29%20%2A%20scale%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20canvas.width%2C%20canvas.height%29%3B%0A%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.arc%28cx%2C%20cy%2C%20r%2C%200%2C%20Math.PI%20%2A%202%29%3B%0A%20%20%20%20%20%20%20%20ctx.strokeStyle%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20ctx.lineWidth%20%20%20%3D%20RING_W%3B%0A%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%206%20%2B%20%28scale%20-%201%29%20%2A%2030%3B%0A%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%20Math.min%281%2C%200.5%20%2B%20%28scale%20-%201%29%20%2A%201.5%29%3B%0A%20%20%20%20%20%20%20%20ctx.stroke%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%7D%0A%7D%0Aclass%20BloomVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this.BASE%20%20%20%20%20%20%3D%2065%3B%0A%20%20%20%20%20%20%20%20this.intensity%20%3D%200%3B%0A%20%20%20%20%20%20%20%20this.DECAY%20%20%20%20%20%3D%200.92%3B%0A%20%20%20%20%20%20%20%20this._cycleIdx%20%3D%200%3B%0A%20%20%20%20%20%20%20%20const%20sz%20%20%20%20%20%20%20%3D%20this.BASE%20%2A%20CANVAS_SCALE%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28sz%2C%20sz%29%3B%0A%20%20%20%20%20%20%20%20this._loop%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%0A%20%20%20%20%20%20%20%20this.intensity%20%3D%201.0%3B%0A%20%20%20%20%20%20%20%20this._cycleIdx%20%3D%20%28this._cycleIdx%20%2B%201%29%20%25%204%3B%0A%20%20%20%20%7D%0A%20%20%20%20_loop%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20this.intensity%20%2A%3D%20this.DECAY%3B%0A%20%20%20%20%20%20%20%20this._draw%28%29%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28%28%29%20%3D%3E%20this._loop%28%29%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_draw%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20canvas%2C%20ctx%2C%20BASE%2C%20intensity%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20if%20%28intensity%20%3C%200.005%29%20%7B%20ctx.clearRect%280%2C%200%2C%20canvas.width%2C%20canvas.height%29%3B%20return%3B%20%7D%0A%20%20%20%20%20%20%20%20const%20%7B%20glow%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20const%20cx%20%3D%20canvas.width%20/%202%2C%20cy%20%3D%20canvas.height%20/%202%3B%0A%20%20%20%20%20%20%20%20const%20r%20%20%3D%20%28BASE%20/%202%29%20%2A%20%281%20%2B%20intensity%20%2A%200.5%29%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20canvas.width%2C%20canvas.height%29%3B%0A%20%20%20%20%20%20%20%20const%20aMod%20%3D%200.85%20%2B%20this._cycleIdx%20%2A%200.05%3B%0A%20%20%20%20%20%20%20%20const%20g%20%3D%20ctx.createRadialGradient%28cx%2C%20cy%2C%200%2C%20cx%2C%20cy%2C%20r%20%2A%201.4%29%3B%0A%20%20%20%20%20%20%20%20g.addColorStop%280%2C%20%20%20%20this._a%28glow%2C%20intensity%20%2A%200.85%20%2A%20aMod%29%29%3B%0A%20%20%20%20%20%20%20%20g.addColorStop%280.4%2C%20%20this._a%28glow%2C%20intensity%20%2A%200.55%20%2A%20aMod%29%29%3B%0A%20%20%20%20%20%20%20%20g.addColorStop%280.75%2C%20this._a%28glow%2C%20intensity%20%2A%200.2%29%29%3B%0A%20%20%20%20%20%20%20%20g.addColorStop%281%2C%20%20%20%20%27rgba%280%2C0%2C0%2C0%29%27%29%3B%0A%20%20%20%20%20%20%20%20ctx.fillStyle%20%20%20%3D%20g%3B%0A%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%2020%20%2B%20intensity%20%2A%2030%3B%0A%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.arc%28cx%2C%20cy%2C%20r%20%2A%201.4%2C%200%2C%20Math.PI%20%2A%202%29%3B%0A%20%20%20%20%20%20%20%20ctx.fill%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%0A%20%20%20%20%7D%0A%20%20%20%20_a%28rgba%2C%20a%29%20%7B%0A%20%20%20%20%20%20%20%20return%20rgba.replace%28/%5B%5Cd.%5D%2B%5C%29%24/%2C%20%60%24%7BMath.min%28a%2C1%29.toFixed%283%29%7D%29%60%29%3B%0A%20%20%20%20%7D%0A%7D%0Aclass%20HeartbeatVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this._lock%20%20%20%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20this.BASE%20%20%20%20%20%3D%2060%3B%0A%20%20%20%20%20%20%20%20this.DURATION%20%3D%20280%3B%0A%20%20%20%20%20%20%20%20const%20w%20%3D%20this.BASE%20%2A%203%2C%20h%20%3D%20this.BASE%20%2A%201.2%3B%0A%20%20%20%20%20%20%20%20this.w%20%3D%20w%3B%20this.h%20%3D%20h%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28w%2C%20h%29%3B%0A%20%20%20%20%20%20%20%20this._draw%280%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28this._lock%29%20return%3B%0A%20%20%20%20%20%20%20%20this._lock%20%3D%20true%3B%0A%20%20%20%20%20%20%20%20const%20start%20%3D%20performance.now%28%29%3B%0A%20%20%20%20%20%20%20%20const%20go%20%3D%20now%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20p%20%3D%20Math.min%28%28now%20-%20start%29%20/%20this.DURATION%2C%201%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20env%20%3D%20p%20%3C%200.5%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3F%20easeOutExpo%28p%20%2A%202%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3A%201%20-%20easeOutExpo%28%28p%20-%200.5%29%2A2%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this._draw%28env%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28p%20%3C%201%29%20this._rafId%20%3D%20requestAnimationFrame%28go%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20%7B%20this._draw%280%29%3B%20this._lock%20%3D%20false%3B%20%7D%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20if%20%28this._rafId%29%20cancelAnimationFrame%28this._rafId%29%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28go%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_draw%28env%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20ctx%2C%20w%2C%20h%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20color%2C%20glow%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20w%2C%20h%29%3B%0A%20%20%20%20%20%20%20%20const%20cy%20%20%3D%20h%20/%202%3B%0A%20%20%20%20%20%20%20%20const%20amp%20%3D%20%28h%20%2A%200.38%29%20%2A%20env%3B%0A%20%20%20%20%20%20%20%20const%20profile%20%3D%20x%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.25%29%20return%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.32%29%20return%20-%28x%20-%200.25%29%20/%200.07%20%2A%200.15%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.38%29%20return%20%20%28x%20-%200.32%29%20/%200.06%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.42%29%20return%201%20-%20%28x%20-%200.38%29%20/%200.04%20%2A%201.6%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.46%29%20return%20-0.6%20%2B%20%28x%20-%200.42%29%20/%200.04%20%2A%200.6%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.55%29%20return%200%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28x%20%3C%200.65%29%20return%20Math.sin%28%28x%20-%200.55%29%20/%200.10%20%2A%20Math.PI%29%20%2A%200.18%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%200%3B%0A%20%20%20%20%20%20%20%20%7D%3B%0A%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20for%20%28let%20px%20%3D%200%3B%20px%20%3C%20w%3B%20px%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20y%20%3D%20cy%20-%20profile%28px%20/%20w%29%20%2A%20amp%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20px%20%3D%3D%3D%200%20%3F%20ctx.moveTo%28px%2C%20y%29%20%3A%20ctx.lineTo%28px%2C%20y%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20ctx.strokeStyle%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20ctx.lineWidth%20%20%20%3D%201.5%3B%0A%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%204%20%2B%20amp%20%2A%200.4%3B%0A%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%200.4%20%2B%20env%20%2A%200.6%3B%0A%20%20%20%20%20%20%20%20ctx.stroke%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%7D%0A%20%20%20%20_cleanup%28%29%20%7B%20this._lock%20%3D%20false%3B%20%7D%0A%7D%0Aclass%20RippleVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this._rings%20%20%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20%20%20this.BASE%20%20%20%20%20%3D%2050%3B%0A%20%20%20%20%20%20%20%20this.MAX_RINGS%3D%204%3B%0A%20%20%20%20%20%20%20%20this.DURATION%20%3D%20600%3B%0A%20%20%20%20%20%20%20%20const%20sz%20%20%20%20%20%20%3D%20this.BASE%20%2A%20CANVAS_SCALE%3B%0A%20%20%20%20%20%20%20%20this.sz%20%20%20%20%20%20%20%3D%20sz%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28sz%2C%20sz%29%3B%0A%20%20%20%20%20%20%20%20this._loop%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28this._rings.length%20%3E%3D%20this.MAX_RINGS%29%20this._rings.shift%28%29%3B%0A%20%20%20%20%20%20%20%20this._rings.push%28%7B%20t0%3A%20performance.now%28%29%20-%20this.DURATION%20%2A%200.08%2C%20dur%3A%20this.DURATION%20%7D%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_loop%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20ctx%2C%20sz%2C%20BASE%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20color%2C%20glow%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20const%20cx%20%3D%20sz%20/%202%2C%20cy%20%3D%20sz%20/%202%3B%0A%20%20%20%20%20%20%20%20const%20maxR%20%3D%20%28BASE%20/%202%29%20%2A%202.2%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20sz%2C%20sz%29%3B%0A%20%20%20%20%20%20%20%20const%20now%20%3D%20performance.now%28%29%3B%0A%20%20%20%20%20%20%20%20this._rings%20%3D%20this._rings.filter%28ring%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20p%20%3D%20%28now%20-%20ring.t0%29%20/%20ring.dur%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28p%20%3E%3D%201%29%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20r%20%3D%20maxR%20%2A%20easeOutSine%28p%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.arc%28cx%2C%20cy%2C%20r%2C%200%2C%20Math.PI%20%2A%202%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.strokeStyle%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.lineWidth%20%20%20%3D%201.8%20%2A%20%281%20-%20p%20%2A%200.6%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%2014%20%2A%20%281%20-%20p%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%20%281%20-%20p%29%20%2A%200.8%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.stroke%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28%28%29%20%3D%3E%20this._loop%28%29%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_cleanup%28%29%20%7B%20this._rings%20%3D%20%5B%5D%3B%20%7D%0A%7D%0Aclass%20WaveformVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this._amp%20%20%20%3D%200%3B%0A%20%20%20%20%20%20%20%20this._phase%20%3D%200%3B%0A%20%20%20%20%20%20%20%20this.MAX_AMP%20%20%3D%2016%3B%0A%20%20%20%20%20%20%20%20this.FREQ%20%20%20%20%20%3D%203%3B%0A%20%20%20%20%20%20%20%20this.DECAY%20%20%20%20%3D%200.91%3B%0A%20%20%20%20%20%20%20%20const%20w%20%3D%2060%2A3%2C%20h%20%3D%2060%2A1.4%3B%0A%20%20%20%20%20%20%20%20this.w%20%3D%20w%3B%20this.h%20%3D%20h%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28w%2C%20h%29%3B%0A%20%20%20%20%20%20%20%20this._loop%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%0A%20%20%20%20%20%20%20%20this._amp%20%3D%20this.MAX_AMP%3B%0A%20%20%20%20%7D%0A%20%20%20%20_loop%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20ctx%2C%20w%2C%20h%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20color%2C%20glow%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20const%20cy%20%3D%20h%20/%202%3B%0A%20%20%20%20%20%20%20%20this._amp%20%20%20%2A%3D%20this.DECAY%3B%0A%20%20%20%20%20%20%20%20this._phase%20%2B%3D%200.05%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20w%2C%20h%29%3B%0A%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20for%20%28let%20px%20%3D%200%3B%20px%20%3C%3D%20w%3B%20px%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20x%20%20%20%3D%20px%20/%20w%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20env%20%3D%20Math.sin%28x%20%2A%20Math.PI%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20y%20%20%20%3D%20cy%20-%20Math.sin%28x%20%2A%20Math.PI%20%2A%202%20%2A%20this.FREQ%20%2B%20this._phase%29%20%2A%20this._amp%20%2A%20env%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20px%20%3D%3D%3D%200%20%3F%20ctx.moveTo%28px%2C%20y%29%20%3A%20ctx.lineTo%28px%2C%20y%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20const%20intensity%20%3D%20this._amp%20/%20this.MAX_AMP%3B%0A%20%20%20%20%20%20%20%20ctx.strokeStyle%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20ctx.lineWidth%20%20%20%3D%202%3B%0A%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%204%20%2B%20intensity%20%2A%2016%3B%0A%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%200.35%20%2B%20intensity%20%2A%200.65%3B%0A%20%20%20%20%20%20%20%20ctx.stroke%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28%28%29%20%3D%3E%20this._loop%28%29%29%3B%0A%20%20%20%20%7D%0A%7D%0Aclass%20ParticlesVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this._particles%20%3D%20%5B%5D%3B%0A%20%20%20%20%20%20%20%20this.MAX_P%20%20%20%20%3D%2072%3B%0A%20%20%20%20%20%20%20%20this.PER_BURST%3D%2014%3B%0A%20%20%20%20%20%20%20%20this.BASE%20%20%20%20%20%3D%2060%3B%0A%20%20%20%20%20%20%20%20const%20sz%20%3D%20this.BASE%20%2A%20CANVAS_SCALE%3B%0A%20%20%20%20%20%20%20%20this.sz%20%20%3D%20sz%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28sz%2C%20sz%29%3B%0A%20%20%20%20%20%20%20%20this._loop%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%0A%20%20%20%20%20%20%20%20const%20%7B%20sz%2C%20MAX_P%2C%20PER_BURST%2C%20BASE%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20cx%20%3D%20sz%20/%202%2C%20cy%20%3D%20sz%20/%202%3B%0A%20%20%20%20%20%20%20%20if%20%28this._particles.length%20%2B%20PER_BURST%20%3E%20MAX_P%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20this._particles.splice%280%2C%20PER_BURST%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20for%20%28let%20i%20%3D%200%3B%20i%20%3C%20PER_BURST%3B%20i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20angle%20%3D%20%28Math.PI%20%2A%202%20/%20PER_BURST%29%20%2A%20i%20%2B%20%28Math.random%28%29%20-%200.5%29%20%2A%200.5%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20spd%20%20%20%3D%20BASE%20%2A%200.016%20%2B%20Math.random%28%29%20%2A%20BASE%20%2A%200.018%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20this._particles.push%28%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20x%3A%20cx%2C%20y%3A%20cy%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20vx%3A%20Math.cos%28angle%29%20%2A%20spd%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20vy%3A%20Math.sin%28angle%29%20%2A%20spd%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20r%3A%201.5%20%2B%20Math.random%28%29%20%2A%202.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20life%3A%201.0%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20decay%3A%200.022%20%2B%20Math.random%28%29%2A0.014%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20_loop%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20ctx%2C%20sz%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20color%2C%20glow%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20sz%2C%20sz%29%3B%0A%20%20%20%20%20%20%20%20this._particles%20%3D%20this._particles.filter%28p%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20p.x%20%2B%3D%20p.vx%3B%20p.y%20%2B%3D%20p.vy%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20p.vx%20%2A%3D%200.94%3B%20p.vy%20%2A%3D%200.94%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20p.life%20-%3D%20p.decay%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28p.life%20%3C%3D%200%29%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.arc%28p.x%2C%20p.y%2C%20p.r%2Ap.life%2C%200%2C%20Math.PI%2A2%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.fillStyle%20%20%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%205%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%20p.life%20%2A%200.9%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.fill%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20true%3B%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28%28%29%20%3D%3E%20this._loop%28%29%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_cleanup%28%29%20%7B%20this._particles%20%3D%20%5B%5D%3B%20%7D%0A%7D%0Aclass%20DNAVisualizer%20extends%20BaseVisualizer%20%7B%0A%20%20%20%20_init%28%29%20%7B%0A%20%20%20%20%20%20%20%20this.BASE%20%20%20%20%20%3D%2060%3B%0A%20%20%20%20%20%20%20%20this._restAmp%20%3D%20this.BASE%20%2A%200.10%3B%0A%20%20%20%20%20%20%20%20this._maxAmp%20%20%3D%20this.BASE%20%2A%200.44%3B%0A%20%20%20%20%20%20%20%20this._amp%20%20%20%20%20%3D%20this._restAmp%3B%0A%20%20%20%20%20%20%20%20this.DECAY%20%20%20%20%3D%200.93%3B%0A%20%20%20%20%20%20%20%20this._phase%20%20%20%3D%200%3B%0A%20%20%20%20%20%20%20%20const%20w%20%3D%20this.BASE%2A3%2C%20h%20%3D%20this.BASE%2A1.4%3B%0A%20%20%20%20%20%20%20%20this.w%20%3D%20w%3B%20this.h%20%3D%20h%3B%0A%20%20%20%20%20%20%20%20this._makeCanvas%28w%2C%20h%29%3B%0A%20%20%20%20%20%20%20%20this._loop%28%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20pulse%28%29%20%7B%20this._amp%20%3D%20this._maxAmp%3B%20%7D%0A%20%20%20%20_loop%28%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21this.canvas%29%20return%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20ctx%2C%20w%2C%20h%20%7D%20%3D%20this%3B%0A%20%20%20%20%20%20%20%20const%20%7B%20color%2C%20glow%2C%20secondary%20%7D%20%3D%20readVizVars%28%29%3B%0A%20%20%20%20%20%20%20%20const%20cy%20%20%20%20%3D%20h%20/%202%3B%0A%20%20%20%20%20%20%20%20const%20freq%20%20%3D%202.5%3B%0A%20%20%20%20%20%20%20%20const%20rungs%20%3D%208%3B%0A%20%20%20%20%20%20%20%20this._amp%20%20%20%3D%20this._restAmp%20%2B%20%28this._amp%20-%20this._restAmp%29%20%2A%20this.DECAY%3B%0A%20%20%20%20%20%20%20%20this._phase%20%2B%3D%200.03%3B%0A%20%20%20%20%20%20%20%20ctx.clearRect%280%2C%200%2C%20w%2C%20h%29%3B%0A%20%20%20%20%20%20%20%20const%20norm%20%20%3D%20%28this._amp%20-%20this._restAmp%29%20/%20%28this._maxAmp%20-%20this._restAmp%29%3B%0A%20%20%20%20%20%20%20%20const%20gBlur%20%3D%20norm%20%2A%2016%3B%0A%20%20%20%20%20%20%20%20const%20alpha%20%3D%200.4%20%2B%20norm%20%2A%200.5%3B%0A%20%20%20%20%20%20%20%20this._strand%28ctx%2C%20w%2C%20cy%2C%20freq%2C%200%2C%20%20%20%20%20%20%20color%2C%20%20%20%20%20gBlur%2C%20%20%20%20%20%20%20alpha%2C%20%20%20%20%20%20%20glow%29%3B%0A%20%20%20%20%20%20%20%20this._strand%28ctx%2C%20w%2C%20cy%2C%20freq%2C%20Math.PI%2C%20secondary%2C%20gBlur%20%2A%200.6%2C%20alpha%20%2A%200.65%2C%20glow%29%3B%0A%20%20%20%20%20%20%20%20ctx.strokeStyle%20%3D%20color%3B%0A%20%20%20%20%20%20%20%20ctx.lineWidth%20%20%20%3D%200.9%3B%0A%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%20alpha%20%2A%200.3%3B%0A%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20glow%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%20gBlur%20%2A%200.4%3B%0A%20%20%20%20%20%20%20%20for%20%28let%20i%20%3D%200%3B%20i%20%3C%3D%20rungs%3B%20i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20xN%20%3D%20i%20/%20rungs%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20y1%20%3D%20cy%20-%20Math.sin%28xN%20%2A%20Math.PI%20%2A%202%20%2A%20freq%20%2B%20this._phase%29%20%2A%20this._amp%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20y2%20%3D%20cy%20-%20Math.sin%28xN%20%2A%20Math.PI%20%2A%202%20%2A%20freq%20%2B%20this._phase%20%2B%20Math.PI%29%20%2A%20this._amp%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%20ctx.moveTo%28xN%20%2A%20w%2C%20y1%29%3B%20ctx.lineTo%28xN%20%2A%20w%2C%20y2%29%3B%20ctx.stroke%28%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%20%20%20%20this._rafId%20%3D%20requestAnimationFrame%28%28%29%20%3D%3E%20this._loop%28%29%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20_strand%28ctx%2C%20w%2C%20cy%2C%20freq%2C%20offset%2C%20strokeColor%2C%20glowBlur%2C%20alpha%2C%20shadowColor%29%20%7B%0A%20%20%20%20%20%20%20%20ctx.beginPath%28%29%3B%0A%20%20%20%20%20%20%20%20for%20%28let%20px%20%3D%200%3B%20px%20%3C%3D%20w%3B%20px%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20y%20%3D%20cy%20-%20Math.sin%28%28px%20/%20w%29%20%2A%20Math.PI%20%2A%202%20%2A%20freq%20%2B%20this._phase%20%2B%20offset%29%20%2A%20this._amp%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20px%20%3D%3D%3D%200%20%3F%20ctx.moveTo%28px%2C%20y%29%20%3A%20ctx.lineTo%28px%2C%20y%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20ctx.strokeStyle%20%3D%20strokeColor%3B%0A%20%20%20%20%20%20%20%20ctx.lineWidth%20%20%20%3D%201.5%3B%0A%20%20%20%20%20%20%20%20ctx.shadowColor%20%3D%20shadowColor%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%20%3D%20glowBlur%3B%0A%20%20%20%20%20%20%20%20ctx.globalAlpha%20%3D%20alpha%3B%0A%20%20%20%20%20%20%20%20ctx.stroke%28%29%3B%0A%20%20%20%20%20%20%20%20ctx.shadowBlur%20%3D%200%3B%20ctx.globalAlpha%20%3D%201%3B%0A%20%20%20%20%7D%0A%7D%0Aconst%20VISUALIZERS%20%3D%20%7B%0A%20%20%20%20ring%3A%20%20%20%20%20%20RingVisualizer%2C%0A%20%20%20%20bloom%3A%20%20%20%20%20BloomVisualizer%2C%0A%20%20%20%20heartbeat%3A%20HeartbeatVisualizer%2C%0A%20%20%20%20ripple%3A%20%20%20%20RippleVisualizer%2C%0A%20%20%20%20waveform%3A%20%20WaveformVisualizer%2C%0A%20%20%20%20particles%3A%20ParticlesVisualizer%2C%0A%20%20%20%20dna%3A%20%20%20%20%20%20%20DNAVisualizer%2C%0A%7D%3B%0Aconst%20VISUALIZER_ORDER%20%3D%20%5B%27off%27%2C%20%27particles%27%2C%20%27waveform%27%2C%20%27dna%27%2C%20%27heartbeat%27%2C%20%27ripple%27%2C%20%27ring%27%2C%20%27bloom%27%5D%3B%0A/%2A%20%40%40END_VISUALIZERS%40%40%20%2A/%0A%0A/%2A%20%40%40PRESETS%40%40%20%2A/%0Aconst%20PRESETS%20%3D%20%7B%0Arap%3A%20%7B%0A%20%20%20%20%20%20%20%20label%3A%20%27RAP%27%2C%0A%20%20%20%20%20%20%20%20cssVars%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-body%27%3A%20%20%20%20%20%20%20%27%23030603%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-container%27%3A%20%20%27linear-gradient%28145deg%2C%23141a14%2C%230a0f0a%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-screen%27%3A%20%20%20%20%20%27%230a0e0a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-screen%27%3A%20%27%232a4a2a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-primary%27%3A%20%27%231eff00%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-dim%27%3A%20%20%20%20%20%27%233c9e3c%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-faint%27%3A%20%20%20%27%232a8a2a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-muted%27%3A%20%20%20%27%233a7a3a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-screen%27%3A%20%27inset%200%200%2025px%20rgba%280%2C255%2C0%2C0.06%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-outer%27%3A%20%20%270%200%200%202px%20%231a3a1a%2C0%2020px%2040px%20rgba%280%2C0%2C0%2C0.5%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-stats%27%3A%20%20%20%20%20%20%27%230a0c0a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-btn%27%3A%20%20%20%20%20%20%20%20%27%231a2a1a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-btn%27%3A%20%20%20%20%27%233c9e3c%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-btn%27%3A%20%20%20%20%20%27%23aaffaa%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--crt-on%27%3A%20%20%20%20%20%20%20%20%271%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-color%27%3A%20%20%20%20%20%27%231eff00%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-glow%27%3A%20%20%20%20%20%20%27rgba%2830%2C255%2C0%2C0.55%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-secondary%27%3A%20%27rgba%2830%2C255%2C0%2C0.25%29%27%7D%2C%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20dustColor%3A%20%20%27rgba%2830%2C255%2C0%2C0.25%29%27%2C%0A%20%20%20%20%20%20%20%20flashColor%3A%20%27rgba%280%2C255%2C0%2C0.4%29%27%2C%0A%20%20%20%20%20%20%20%20effects%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-shake%27%2C%20%20%20duration%3A100%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-glitch%27%2C%20%20duration%3A260%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-chroma%27%2C%20%20duration%3A200%2C%20useFlash%3Afalse%2C%20useScan%3Atrue%20%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-flicker%27%2C%20duration%3A220%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-zoom%27%2C%20%20%20%20duration%3A160%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%7D%2C%0Aethereal%3A%20%7B%0A%20%20%20%20%20%20%20%20label%3A%20%27ETHEREAL%27%2C%0A%20%20%20%20%20%20%20%20cssVars%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-body%27%3A%20%20%20%20%20%20%20%27%2306030f%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-container%27%3A%20%20%27linear-gradient%28145deg%2C%230e0819%2C%23070410%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-screen%27%3A%20%20%20%20%20%27%230b0718%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-screen%27%3A%20%27%232e1650%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-primary%27%3A%20%27%23b388ff%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-dim%27%3A%20%20%20%20%20%27%237c4daa%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-faint%27%3A%20%20%20%27%234a2870%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-muted%27%3A%20%20%20%27%232e1650%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-screen%27%3A%20%27inset%200%200%2035px%20rgba%28140%2C70%2C220%2C0.06%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-outer%27%3A%20%20%270%200%200%202px%20%231e0a38%2C0%2020px%2060px%20rgba%2860%2C0%2C100%2C0.55%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-stats%27%3A%20%20%20%20%20%20%27%23080514%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-btn%27%3A%20%20%20%20%20%20%20%20%27%23140a28%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-btn%27%3A%20%20%20%20%27%235c3080%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-btn%27%3A%20%20%20%20%20%27%23c4a0f0%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--crt-on%27%3A%20%20%20%20%20%20%20%20%270%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-color%27%3A%20%20%20%20%20%27%23c084fc%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-glow%27%3A%20%20%20%20%20%20%27rgba%28160%2C80%2C255%2C0.5%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-secondary%27%3A%20%27rgba%28120%2C60%2C200%2C0.25%29%27%7D%2C%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20dustColor%3A%20%20%27rgba%28160%2C100%2C255%2C0.15%29%27%2C%0A%20%20%20%20%20%20%20%20flashColor%3A%20%27rgba%28140%2C70%2C255%2C0.20%29%27%2C%0A%20%20%20%20%20%20%20%20effects%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-drift%27%2C%20%20%20%20duration%3A250%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-breathe%27%2C%20%20duration%3A300%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-aurora%27%2C%20%20%20duration%3A350%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-dissolve%27%2C%20duration%3A280%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-float%27%2C%20%20%20%20duration%3A220%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%7D%2C%0Avoid%3A%20%7B%0A%20%20%20%20%20%20%20%20label%3A%20%27VOID%27%2C%0A%20%20%20%20%20%20%20%20cssVars%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-body%27%3A%20%20%20%20%20%20%20%27%23020202%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-container%27%3A%20%20%27linear-gradient%28145deg%2C%230a0a0c%2C%23050507%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-screen%27%3A%20%20%20%20%20%27%23080809%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-screen%27%3A%20%27%231a1a2a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-primary%27%3A%20%27%23d0d8e8%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-dim%27%3A%20%20%20%20%20%27%236070a0%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-faint%27%3A%20%20%20%27%232a3050%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-muted%27%3A%20%20%20%27%23181828%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-screen%27%3A%20%27inset%200%200%2030px%20rgba%2880%2C100%2C180%2C0.04%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-outer%27%3A%20%20%270%200%200%201px%20%230e0e1e%2C0%2020px%2050px%20rgba%280%2C0%2C0%2C0.85%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-stats%27%3A%20%20%20%20%20%20%27%2306060a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-btn%27%3A%20%20%20%20%20%20%20%20%27%230e0e18%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-btn%27%3A%20%20%20%20%27%232a3060%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-btn%27%3A%20%20%20%20%20%27%238090c0%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--crt-on%27%3A%20%20%20%20%20%20%20%20%270%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-color%27%3A%20%20%20%20%20%27%236080d0%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-glow%27%3A%20%20%20%20%20%20%27rgba%2880%2C120%2C220%2C0.35%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-secondary%27%3A%20%27rgba%2860%2C80%2C160%2C0.2%29%27%7D%2C%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20dustColor%3A%20%20%27rgba%28160%2C180%2C255%2C0.07%29%27%2C%0A%20%20%20%20%20%20%20%20flashColor%3A%20%27rgba%28200%2C220%2C255%2C0.14%29%27%2C%0A%20%20%20%20%20%20%20%20effects%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-void-blink%27%2C%20%20duration%3A120%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-void-slice%27%2C%20%20duration%3A140%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-void-dim%27%2C%20%20%20%20duration%3A180%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-void-shift%27%2C%20%20duration%3A160%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-void-static%27%2C%20duration%3A150%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%7D%2C%0Adream%3A%20%7B%0A%20%20%20%20%20%20%20%20label%3A%20%27DREAM%27%2C%0A%20%20%20%20%20%20%20%20cssVars%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-body%27%3A%20%20%20%20%20%20%20%27%23030a0e%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-container%27%3A%20%20%27linear-gradient%28145deg%2C%23071418%2C%2303080c%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-screen%27%3A%20%20%20%20%20%27%23060e14%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-screen%27%3A%20%27%230d3040%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-primary%27%3A%20%27%237ee8e8%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-dim%27%3A%20%20%20%20%20%27%233a8a8a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-faint%27%3A%20%20%20%27%231a4a4a%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-muted%27%3A%20%20%20%27%230d2828%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-screen%27%3A%20%27inset%200%200%2040px%20rgba%2860%2C180%2C200%2C0.05%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-outer%27%3A%20%20%270%200%200%202px%20%23061820%2C0%2020px%2050px%20rgba%280%2C40%2C60%2C0.7%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-stats%27%3A%20%20%20%20%20%20%27%23040c10%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-btn%27%3A%20%20%20%20%20%20%20%20%27%23071418%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-btn%27%3A%20%20%20%20%27%231a6060%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-btn%27%3A%20%20%20%20%20%27%237ecece%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--crt-on%27%3A%20%20%20%20%20%20%20%20%270%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-color%27%3A%20%20%20%20%20%27%237ee8e8%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-glow%27%3A%20%20%20%20%20%20%27rgba%28100%2C220%2C220%2C0.35%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-secondary%27%3A%20%27rgba%2860%2C160%2C160%2C0.2%29%27%7D%2C%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20dustColor%3A%20%20%27rgba%28100%2C220%2C220%2C0.10%29%27%2C%0A%20%20%20%20%20%20%20%20flashColor%3A%20%27rgba%2860%2C200%2C200%2C0.13%29%27%2C%0A%20%20%20%20%20%20%20%20effects%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-dream-fade%27%2C%20%20%20duration%3A220%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-dream-fog%27%2C%20%20%20%20duration%3A250%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-dream-pulse%27%2C%20%20duration%3A180%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-dream-glow%27%2C%20%20%20duration%3A300%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-dream-ripple%27%2C%20duration%3A200%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%7D%2C%0Aaurora%3A%20%7B%0A%20%20%20%20%20%20%20%20label%3A%20%27AURORA%27%2C%0A%20%20%20%20%20%20%20%20cssVars%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-body%27%3A%20%20%20%20%20%20%20%27%2304020c%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-container%27%3A%20%20%27linear-gradient%28145deg%2C%230a0618%2C%2306030e%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-screen%27%3A%20%20%20%20%20%27%23080412%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-screen%27%3A%20%27%232a1548%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-primary%27%3A%20%27%23a8d8ff%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-dim%27%3A%20%20%20%20%20%27%235070a8%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-faint%27%3A%20%20%20%27%23281850%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-muted%27%3A%20%20%20%27%23140c30%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-screen%27%3A%20%27inset%200%200%2040px%20rgba%2880%2C40%2C180%2C0.06%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-outer%27%3A%20%20%270%200%200%202px%20%23140830%2C0%2020px%2060px%20rgba%2820%2C0%2C60%2C0.75%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-stats%27%3A%20%20%20%20%20%20%27%23060310%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-btn%27%3A%20%20%20%20%20%20%20%20%27%230e0820%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-btn%27%3A%20%20%20%20%27%233a2070%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-btn%27%3A%20%20%20%20%20%27%23a0b8e8%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--crt-on%27%3A%20%20%20%20%20%20%20%20%270%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-color%27%3A%20%20%20%20%20%27%23a0b8ff%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-glow%27%3A%20%20%20%20%20%20%27rgba%28120%2C80%2C255%2C0.45%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-secondary%27%3A%20%27rgba%2880%2C40%2C200%2C0.2%29%27%7D%2C%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20dustColor%3A%20%20%27rgba%28120%2C100%2C255%2C0.10%29%27%2C%0A%20%20%20%20%20%20%20%20flashColor%3A%20%27rgba%28100%2C80%2C220%2C0.18%29%27%2C%0A%20%20%20%20%20%20%20%20flashCycle%3A%20true%2C%0A%20%20%20%20%20%20%20%20flashCycleColors%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27rgba%28180%2C80%2C255%2C0.20%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27rgba%2880%2C200%2C255%2C0.16%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27rgba%28255%2C80%2C160%2C0.16%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27rgba%2880%2C255%2C180%2C0.13%29%27%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20effects%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-aurora-shift%27%2C%20%20%20duration%3A220%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-aurora-sweep%27%2C%20%20%20duration%3A200%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-aurora-shimmer%27%2C%20duration%3A180%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-aurora-swell%27%2C%20%20%20duration%3A350%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-aurora-haze%27%2C%20%20%20%20duration%3A280%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%7D%2C%0Aember%3A%20%7B%0A%20%20%20%20%20%20%20%20label%3A%20%27EMBER%27%2C%0A%20%20%20%20%20%20%20%20cssVars%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-body%27%3A%20%20%20%20%20%20%20%27%23080200%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-container%27%3A%20%20%27linear-gradient%28145deg%2C%23140800%2C%230a0400%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-screen%27%3A%20%20%20%20%20%27%230e0500%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-screen%27%3A%20%27%233a1400%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-primary%27%3A%20%27%23ff9040%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-dim%27%3A%20%20%20%20%20%27%23a04010%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-faint%27%3A%20%20%20%27%23501800%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-muted%27%3A%20%20%20%27%232a0c00%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-screen%27%3A%20%27inset%200%200%2035px%20rgba%28200%2C60%2C0%2C0.06%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--shadow-outer%27%3A%20%20%270%200%200%202px%20%23200800%2C0%2020px%2050px%20rgba%2880%2C10%2C0%2C0.7%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-stats%27%3A%20%20%20%20%20%20%27%230a0300%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--bg-btn%27%3A%20%20%20%20%20%20%20%20%27%23160600%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--border-btn%27%3A%20%20%20%20%27%236a2000%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--color-btn%27%3A%20%20%20%20%20%27%23e08040%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--crt-on%27%3A%20%20%20%20%20%20%20%20%270%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-color%27%3A%20%20%20%20%20%27%23ff6020%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-glow%27%3A%20%20%20%20%20%20%27rgba%28255%2C100%2C20%2C0.45%29%27%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%27--viz-secondary%27%3A%20%27rgba%28180%2C50%2C0%2C0.2%29%27%7D%2C%0A%20%20%20%20%20%20%20%20%0A%20%20%20%20%20%20%20%20dustColor%3A%20%20%27rgba%28255%2C120%2C20%2C0.16%29%27%2C%0A%20%20%20%20%20%20%20%20flashColor%3A%20%27rgba%28220%2C80%2C10%2C0.22%29%27%2C%0A%20%20%20%20%20%20%20%20effects%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-ember-flare%27%2C%20%20%20duration%3A180%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-ember-char%27%2C%20%20%20%20duration%3A200%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-ember-smolder%27%2C%20duration%3A220%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-ember-pulse%27%2C%20%20%20duration%3A160%2C%20useFlash%3Atrue%2C%20%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%20className%3A%27fx-ember-heat%27%2C%20%20%20%20duration%3A250%2C%20useFlash%3Afalse%2C%20useScan%3Afalse%20%7D%2C%0A%20%20%20%20%20%20%20%20%5D%7D%0A%7D%3B%0Aconst%20PRESET_ORDER%20%3D%20%5B%22rap%22%2C%20%22ethereal%22%2C%20%22void%22%2C%20%22dream%22%2C%20%22aurora%22%2C%20%22ember%22%5D%3B%0A/%2A%20%40%40END_PRESETS%40%40%20%2A/%0A%0A/%2A%20%40%40LYRICS%40%40%20%2A/%0Aconst%20LYRICS%20%3D%20%5B%5BLYRICS_DATA%5D%5D%3B%0A/%2A%20%40%40END_LYRICS%40%40%20%2A/%0A%0A/%2A%20%40%40BEATS%40%40%20%2A/%0Aconst%20BEAT_DATA%20%3D%20%7B%20beats%3A%20%5B%5BBEATS_DATA%5D%5D%2C%20bpm%3A%20%5B%5BBPM_DATA%5D%5D%20%7D%3B%0A/%2A%20%40%40END_BEATS%40%40%20%2A/%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20DOM%20REFS%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Aconst%20stageEl%20%20%20%20%20%20%3D%20document.getElementById%28%27stage%27%29%3B%0Aconst%20screenEl%20%20%20%20%20%3D%20document.getElementById%28%27screen%27%29%3B%0Aconst%20masterCanvas%20%3D%20document.getElementById%28%27masterCanvas%27%29%3B%0Aconst%20mCtx%20%20%20%20%20%20%20%20%20%3D%20masterCanvas.getContext%28%272d%27%29%3B%0Aconst%20visWrap%20%20%20%20%20%20%3D%20document.getElementById%28%27visualizerWrap%27%29%3B%0Aconst%20currentLineEl%20%3D%20document.getElementById%28%27currentLine%27%29%3B%0Aconst%20nextLineEl%20%20%20%20%3D%20document.getElementById%28%27nextLine%27%29%3B%0Aconst%20timeDisplay%20%20%3D%20document.getElementById%28%27timeDisplay%27%29%3B%0Aconst%20bpmDisplay%20%20%20%3D%20document.getElementById%28%27bpmDisplay%27%29%3B%0Aconst%20statusMsg%20%20%20%20%3D%20document.getElementById%28%27statusMsg%27%29%3B%0Aconst%20playBtn%20%20%20%20%20%20%3D%20document.getElementById%28%27playBtn%27%29%3B%0Aconst%20resetBtn%20%20%20%20%20%3D%20document.getElementById%28%27resetBtn%27%29%3B%0Aconst%20modeBtn%20%20%20%20%20%20%3D%20document.getElementById%28%27modeBtn%27%29%3B%0Aconst%20presetBtn%20%20%20%20%3D%20document.getElementById%28%27presetBtn%27%29%3B%0Aconst%20arBtn%20%20%20%20%20%20%20%20%3D%20document.getElementById%28%27arBtn%27%29%3B%0Aconst%20vizBtn%20%20%20%20%20%20%20%3D%20document.getElementById%28%27vizBtn%27%29%3B%0Aconst%20seekBar%20%20%20%20%20%20%3D%20document.getElementById%28%27seekBar%27%29%3B%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20STATE%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Alet%20beatTimestamps%20%20%20%3D%20%5B...BEAT_DATA.beats%5D%3B%20%20//%20Copy%20to%20allow%20modification%0Alet%20bpm%20%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%20BEAT_DATA.bpm%3B%0Alet%20currentMode%20%20%20%20%20%20%3D%20%27A%27%3B%20//%20%27A%27%20%3D%20beat%20triggers%2C%20%27B%27%20%3D%20lyric%20start%20triggers%0Alet%20currentPreset%20%20%20%20%3D%20PRESET_ORDER%5B0%5D%3B%0Alet%20isLandscape%20%20%20%20%20%20%3D%20false%3B%0Alet%20isPlaying%20%20%20%20%20%20%20%20%3D%20false%3B%0Alet%20flashCycleIdx%20%20%20%20%3D%200%3B%0Alet%20lastFiredBeatIdx%20%3D%200%3B%0Alet%20currentLyricIdx%20%20%3D%20-1%3B%20%20//%20-2%3Dpre-roll%2C%20-3%3Dcredits%0Alet%20animFrameId%20%20%20%20%20%20%3D%20null%3B%0Alet%20isSeeking%20%20%20%20%20%20%20%20%3D%20false%3B%0A%0A//%20VISUALIZER_ORDER%20is%20injected%20by%20compile.py%20from%20visualizers.js.%0A//%20The%20%27off%27%20sentinel%20maps%20to%20null%20%28no%20visualizer%20rendered%29.%0Aconst%20VIZ_ORDER%20%3D%20%28typeof%20VISUALIZER_ORDER%20%21%3D%3D%20%27undefined%27%29%0A%20%20%20%20%3F%20VISUALIZER_ORDER%20%3A%20%5B%27off%27%5D%3B%0Alet%20vizIndex%20%3D%200%3B%20%20%20%20//%20current%20position%20in%20VIZ_ORDER%0Alet%20vizs%20%20%20%20%20%3D%20null%3B%20//%20active%20BaseVisualizer%20instance%20or%20null%0A%0A//%20%E2%94%80%E2%94%80%20Effect%20class%20removal%20debounce%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A//%20Short-lived%20CSS%20cleanup%20timers%20only%20%E2%80%94%20beat%20scheduling%20is%20rAF-driven.%0Aconst%20effectRemovalTimers%20%3D%20%7B%7D%3B%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20AUDIO%20PIPELINE%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Alet%20audioContext%20%20%3D%20null%3B%0Alet%20analyserNode%20%20%3D%20null%3B%0Alet%20sourceNode%20%20%20%20%3D%20null%3B%0Alet%20audioBuffer%20%20%20%3D%20null%3B%0Alet%20startOffset%20%20%20%3D%200%3B%0Alet%20startedAt%20%20%20%20%20%3D%200%3B%0Alet%20audioDuration%20%3D%200%3B%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20MASTER%20CANVAS%20%E2%80%94%20background%2C%20dust%2C%20visualizer%0A//%0A//%20Architecture%3A%20two%20rendering%20systems%2C%20each%20doing%20what%20it%20does%20best.%0A//%0A//%20%20%20masterCanvas%20%28canvas%202D%29%3A%0A//%20%20%20%20%20-%20Background%20fill%20%28--bg-screen%29%0A//%20%20%20%20%20-%20Dust%20particles%20%28typed%20Float32Array%2C%20single%20draw%20call%29%0A//%20%20%20%20%20-%20Visualizer%20%28drawImage%20from%20vizs.canvas%29%0A//%20%20%20%20%20Internal%20resolution%20%3D%20screen%20px%20%C3%97%20DPR%2C%20capped%20at%201080p%20height%0A//%20%20%20%20%20to%20protect%20legacy%20hardware%20fill%20rate.%0A//%0A//%20%20%20DOM%20layer%20%28z-index%20above%20canvas%29%3A%0A//%20%20%20%20%20-%20.current-line%20/%20.next-line%20text%0A//%20%20%20%20%20-%20CSS%20keyframe%20animations%20%28fx-%2A%20classes%20from%20presets%29%0A//%20%20%20%20%20-%20screen-flash%20box-shadow%20animation%0A//%20%20%20%20%20-%20CRT%20%3A%3Abefore/%3A%3Aafter%20pseudo-elements%0A//%20%20%20%20%20The%20browser%20GPU%20compositor%20handles%20these%20at%20native%20quality%20%E2%80%94%0A//%20%20%20%20%20subpixel%20text%2C%20hardware-accelerated%20transforms%2C%20no%20JS%20overhead.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Alet%20mW%20%3D%201%3B%0Alet%20mH%20%3D%201%3B%0A%0Afunction%20resizeMasterCanvas%28%29%20%7B%0A%20%20%20%20const%20rect%20%3D%20screenEl.getBoundingClientRect%28%29%3B%0A%20%20%20%20const%20dpr%20%20%3D%20window.devicePixelRatio%20%7C%7C%201%3B%0A%20%20%20%20let%20w%20%3D%20%28rect.width%20%20%2A%20dpr%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20let%20h%20%3D%20%28rect.height%20%2A%20dpr%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20//%20Cap%20longer%20dimension%20at%201920%20%E2%80%94%20keeps%20fill%20rate%20manageable%20on%202-core%20GPU%0A%20%20%20%20const%20longer%20%3D%20Math.max%28w%2C%20h%29%3B%0A%20%20%20%20if%20%28longer%20%3E%201920%29%20%7B%0A%20%20%20%20%20%20%20%20const%20s%20%3D%201920%20/%20longer%3B%0A%20%20%20%20%20%20%20%20w%20%3D%20%28w%20%2A%20s%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20h%20%3D%20%28h%20%2A%20s%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20%28masterCanvas.width%20%21%3D%3D%20w%20%7C%7C%20masterCanvas.height%20%21%3D%3D%20h%29%20%7B%0A%20%20%20%20%20%20%20%20masterCanvas.width%20%20%3D%20w%3B%0A%20%20%20%20%20%20%20%20masterCanvas.height%20%3D%20h%3B%0A%20%20%20%20%20%20%20%20mW%20%3D%20w%3B%0A%20%20%20%20%20%20%20%20mH%20%3D%20h%3B%0A%20%20%20%20%20%20%20%20_initAllDust%28%29%3B%0A%20%20%20%20%7D%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20PRESET%20VARS%20CACHE%0A//%20Read%20once%20per%20preset%20change%20via%20_readPresetVars%28%29.%0A//%20Zero%20getComputedStyle%20calls%20inside%20rAF.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Aconst%20PV%20%3D%20%7B%0A%20%20%20%20bgScreen%3A%20%20%27%230a0e0a%27%2C%0A%20%20%20%20colorDim%3A%20%20%27%233c9e3c%27%2C%0A%20%20%20%20dustColor%3A%20%27%231eff00%27%2C%0A%20%20%20%20label%3A%20%20%20%20%20%27%27%2C%0A%7D%3B%0A%0Afunction%20_readPresetVars%28%29%20%7B%0A%20%20%20%20const%20root%20%20%20%20%3D%20getComputedStyle%28document.documentElement%29%3B%0A%20%20%20%20PV.bgScreen%20%20%20%3D%20root.getPropertyValue%28%27--bg-screen%27%29.trim%28%29%3B%0A%20%20%20%20PV.colorDim%20%20%20%3D%20root.getPropertyValue%28%27--color-dim%27%29.trim%28%29%3B%0A%20%20%20%20const%20p%20%20%20%20%20%20%20%3D%20PRESETS%5BcurrentPreset%5D%3B%0A%20%20%20%20PV.dustColor%20%20%3D%20p.dustColor%3B%0A%20%20%20%20PV.label%20%20%20%20%20%20%3D%20p.label%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20DUST%20PARTICLE%20SYSTEM%0A//%20Static%20Float32Array%20%E2%80%94%20one%20allocation%20at%20boot%2C%20never%20reallocated.%0A//%20Single%20beginPath/fill%20call%20per%20frame%3A%20one%20GPU%20draw%20call%20for%20all%2020.%0A//%20Draws%20directly%20onto%20masterCanvas.%0A//%0A//%20-%20Stores%20all%20particle%20state%20in%20one%20static%20Float32Array%20%28no%20GC%29.%0A//%20-%20Builds%20a%20single%20canvas%20path%20per%20frame%20and%20calls%20fill%28%29%20once.%0A//%20-%20Clamps%20internal%20canvas%20resolution%20to%20480px%20max%20width%3B%20CSS%0A//%20%20%20scales%20it%20smoothly%20%E2%80%94%20reduces%20GPU%20fill%20rate%20on%20HiDPI%20screens.%0A//%20-%20Uses%20bitwise%20OR%200%20for%20integer%20pixel%20coordinates%2C%20preventing%0A//%20%20%20sub-pixel%20rasterization%20on%20legacy%20GPU%20pipelines.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Aconst%20DUST_COUNT%20%20%3D%2020%3B%0Aconst%20DUST_STRIDE%20%3D%207%3B%20%20//%20%5Bx%2C%20y%2C%20vx%2C%20vy%2C%20alpha%2C%20size%2C%20fadeSpeed%5D%0Aconst%20dustState%20%20%20%3D%20new%20Float32Array%28DUST_COUNT%20%2A%20DUST_STRIDE%29%3B%0A%0Afunction%20_initDustParticle%28i%29%20%7B%0A%20%20%20%20const%20b%20%3D%20i%20%2A%20DUST_STRIDE%3B%0A%20%20%20%20dustState%5Bb%2B0%5D%20%3D%20Math.random%28%29%20%2A%20mW%3B%0A%20%20%20%20dustState%5Bb%2B1%5D%20%3D%20mH%20-%20Math.random%28%29%20%2A%20%28mH%20%2A%200.4%29%3B%0A%20%20%20%20dustState%5Bb%2B2%5D%20%3D%20%28Math.random%28%29%20-%200.5%29%20%2A%200.4%3B%0A%20%20%20%20dustState%5Bb%2B3%5D%20%3D%20-%280.2%20%2B%20Math.random%28%29%20%2A%200.6%29%3B%0A%20%20%20%20dustState%5Bb%2B4%5D%20%3D%200%3B%0A%20%20%20%20dustState%5Bb%2B5%5D%20%3D%201%20%2B%20Math.random%28%29%20%2A%202%3B%0A%20%20%20%20dustState%5Bb%2B6%5D%20%3D%200.004%20%2B%20Math.random%28%29%20%2A%200.006%3B%0A%7D%0A%0Afunction%20_initAllDust%28%29%20%7B%0A%20%20%20%20for%20%28let%20i%20%3D%200%3B%20i%20%3C%20DUST_COUNT%3B%20i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20_initDustParticle%28i%29%3B%0A%20%20%20%20%20%20%20%20//%20Stagger%20starting%20y%20so%20particles%20don%27t%20all%20rise%20in%20sync%20at%20boot%0A%20%20%20%20%20%20%20%20dustState%5Bi%20%2A%20DUST_STRIDE%20%2B%201%5D%20%3D%20Math.random%28%29%20%2A%20mH%3B%0A%20%20%20%20%7D%0A%7D%0A%0Afunction%20_drawDust%28%29%20%7B%0A%20%20%20%20mCtx.fillStyle%20%3D%20PV.dustColor%3B%0A%20%20%20%20mCtx.beginPath%28%29%3B%0A%20%20%20%20for%20%28let%20i%20%3D%200%3B%20i%20%3C%20DUST_COUNT%3B%20i%2B%2B%29%20%7B%0A%20%20%20%20%20%20%20%20const%20b%20%3D%20i%20%2A%20DUST_STRIDE%3B%0A%20%20%20%20%20%20%20%20const%20x%20%3D%20dustState%5Bb%2B0%5D%3B%0A%20%20%20%20%20%20%20%20const%20y%20%3D%20dustState%5Bb%2B1%5D%3B%0A%20%20%20%20%20%20%20%20let%20alpha%20%3D%20dustState%5Bb%2B4%5D%3B%0A%0A%20%20%20%20%20%20%20%20//%20Advance%20particle%20%E2%80%94%20mutate%20in-place%0A%20%20%20%20%20%20%20%20dustState%5Bb%2B0%5D%20%3D%20x%20%2B%20dustState%5Bb%2B2%5D%3B%0A%20%20%20%20%20%20%20%20dustState%5Bb%2B1%5D%20%3D%20y%20%2B%20dustState%5Bb%2B3%5D%3B%0A%20%20%20%20%20%20%20%20dustState%5Bb%2B4%5D%20%3D%20alpha%20%3C%200.5%0A%20%20%20%20%20%20%20%20%20%20%20%20%3F%20alpha%20%2B%20dustState%5Bb%2B6%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%3A%20alpha%20-%20dustState%5Bb%2B6%5D%20%2A%200.3%3B%0A%0A%20%20%20%20%20%20%20%20if%20%28y%20%3C%200%20%7C%7C%20dustState%5Bb%2B4%5D%20%3C%3D%200%29%20%7B%20_initDustParticle%28i%29%3B%20continue%3B%20%7D%0A%0A%20%20%20%20%20%20%20%20//%20Bitwise%20OR%200%20truncates%20to%20integer%20%E2%80%94%20no%20sub-pixel%20rasterization%0A%20%20%20%20%20%20%20%20const%20ix%20%3D%20%28x%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20const%20iy%20%3D%20%28y%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20const%20ir%20%3D%20%28%28dustState%5Bb%2B5%5D%20%2A%200.5%29%20%2B%200.5%29%20%7C%200%20%7C%7C%201%3B%0A%20%20%20%20%20%20%20%20mCtx.globalAlpha%20%3D%20alpha%3B%0A%20%20%20%20%20%20%20%20mCtx.moveTo%28ix%20%2B%20ir%2C%20iy%29%3B%0A%20%20%20%20%20%20%20%20mCtx.arc%28ix%2C%20iy%2C%20ir%2C%200%2C%206.2832%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20mCtx.fill%28%29%3B%0A%20%20%20%20mCtx.globalAlpha%20%3D%201%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20CANVAS%20DRAW%20FRAME%0A//%20Draws%20only%20the%20canvas%20layers.%20DOM%20layers%20%28lyrics%2C%20flash%2C%20CRT%20CSS%29%0A//%20are%20composited%20above%20this%20by%20the%20browser%27s%20own%20GPU%20pipeline.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20drawFrame%28%29%20%7B%0A%20%20%20%20//%20Background%0A%20%20%20%20mCtx.globalAlpha%20%3D%201%3B%0A%20%20%20%20mCtx.shadowBlur%20%20%3D%200%3B%0A%20%20%20%20mCtx.fillStyle%20%20%20%3D%20PV.bgScreen%3B%0A%20%20%20%20mCtx.fillRect%280%2C%200%2C%20mW%2C%20mH%29%3B%0A%0A%20%20%20%20//%20Dust%0A%20%20%20%20_drawDust%28%29%3B%0A%0A%20%20%20%20//%20Visualizer%20%E2%80%94%20drawImage%20from%20vizs.canvas%20onto%20masterCanvas.%0A%20%20%20%20//%20Position%20derived%20from%20visWrap.getBoundingClientRect%28%29%20relative%0A%20%20%20%20//%20to%20screenEl%20%E2%80%94%20correct%20in%20both%20portrait%20and%20landscape.%0A%20%20%20%20if%20%28vizs%20%26%26%20vizs.canvas%20%26%26%20vizs.canvas.width%20%3E%200%29%20%7B%0A%20%20%20%20%20%20%20%20const%20sr%20%20%3D%20screenEl.getBoundingClientRect%28%29%3B%0A%20%20%20%20%20%20%20%20const%20vr%20%20%3D%20visWrap.getBoundingClientRect%28%29%3B%0A%20%20%20%20%20%20%20%20const%20dpr%20%3D%20window.devicePixelRatio%20%7C%7C%201%3B%0A%20%20%20%20%20%20%20%20const%20vx%20%20%3D%20%28%28vr.left%20-%20sr.left%29%20%2A%20dpr%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20const%20vy%20%20%3D%20%28%28vr.top%20%20-%20sr.top%29%20%20%2A%20dpr%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20const%20vw%20%20%3D%20%28vr.width%20%20%2A%20dpr%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20const%20vh%20%20%3D%20%28vr.height%20%2A%20dpr%20%2B%200.5%29%20%7C%200%3B%0A%20%20%20%20%20%20%20%20mCtx.shadowBlur%20%3D%200%3B%0A%20%20%20%20%20%20%20%20mCtx.drawImage%28vizs.canvas%2C%20vx%2C%20vy%2C%20vw%2C%20vh%29%3B%0A%20%20%20%20%7D%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20ICON%20REGISTRY%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Aconst%20ICONS%20%3D%20%7B%0A%20%20%20%20play%3A%20%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Cpath%20d%3D%22M3%202.5v11l10-5.5z%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20pause%3A%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Crect%20x%3D%223%22%20y%3D%222%22%20width%3D%224%22%20height%3D%2212%22%20rx%3D%221%22/%3E%3Crect%20x%3D%229%22%20y%3D%222%22%20width%3D%224%22%20height%3D%2212%22%20rx%3D%221%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20stop%3A%20%20%20%20%60%3Csvg%20width%3D%2212%22%20height%3D%2212%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%2212%22%20height%3D%2212%22%20rx%3D%221%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20warning%3A%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Cpath%20d%3D%22M8%202.5L1.5%2013.5h13L8%202.5zM7.25%2011h1.5v1.5h-1.5zm0-5.5h1.5v4h-1.5z%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20grid%3A%20%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.5%22%3E%3Crect%20x%3D%222%22%20y%3D%222%22%20width%3D%225.5%22%20height%3D%225.5%22%20rx%3D%220.5%22/%3E%3Crect%20x%3D%228.5%22%20y%3D%222%22%20width%3D%225.5%22%20height%3D%225.5%22%20rx%3D%220.5%22/%3E%3Crect%20x%3D%222%22%20y%3D%228.5%22%20width%3D%225.5%22%20height%3D%225.5%22%20rx%3D%220.5%22/%3E%3Crect%20x%3D%228.5%22%20y%3D%228.5%22%20width%3D%225.5%22%20height%3D%225.5%22%20rx%3D%220.5%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20swap%3A%20%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%3E%3Cpath%20d%3D%22M11%204l3%203-3%203M14%207H5M5%2012l-3-3%203-3M2%209h9%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20theme%3A%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Cpath%20d%3D%22M8%201a7%207%200%201%200%207%207%202%202%200%200%200-2-2h-1c-1%200-1-1-1-1V4a3%203%200%200%200-3-3zM4%208a1%201%200%201%201%200-2%201%201%200%200%201%200%202zm3-3a1%201%200%201%201%200-2%201%201%200%200%201%200%202zm4%201a1%201%200%201%201%200-2%201%201%200%200%201%200%202zm1%204a1%201%200%201%201%200-2%201%201%200%200%201%200%202z%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20refresh%3A%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.5%22%20stroke-linecap%3D%22round%22%3E%3Cpath%20d%3D%22M1%208a7%207%200%201%201%202%204.9L1%2015v-4h4%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20record%3A%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20stroke%3D%22currentColor%22%20stroke-width%3D%221.5%22%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%227%22/%3E%3Ccircle%20cx%3D%228%22%20cy%3D%228%22%20r%3D%222.5%22%20fill%3D%22currentColor%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20star%3A%20%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Cpath%20d%3D%22M8%201l2.16%204.38%204.84.7-3.5%203.41.83%204.82L8%2012.04l-4.33%202.27.83-4.82-3.5-3.41%204.84-.7z%22/%3E%3C/svg%3E%60%2C%0A%20%20%20%20bolt%3A%20%20%20%20%60%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22currentColor%22%3E%3Cpath%20d%3D%22M10%201L3%209h4l-1%206%207-8H9z%22/%3E%3C/svg%3E%60%2C%0A%7D%3B%0A%0Afunction%20icon%28name%29%20%7B%20return%20ICONS%5Bname%5D%20%7C%7C%20%27%27%3B%20%7D%0A%0A//%20Set%20initial%20icon%20state%20%E2%80%94%20called%20once%20after%20DOM%20refs%20are%20established%0A%28function%20_initIcons%28%29%20%7B%0A%20%20%20%20playBtn.innerHTML%20%20%20%3D%20icon%28%27play%27%29%3B%0A%20%20%20%20resetBtn.innerHTML%20%20%3D%20icon%28%27refresh%27%29%3B%0A%20%20%20%20modeBtn.innerHTML%20%20%20%3D%20icon%28%27swap%27%29%3B%0A%20%20%20%20presetBtn.innerHTML%20%3D%20icon%28%27theme%27%29%3B%0A%20%20%20%20arBtn.innerHTML%20%20%20%20%20%3D%20icon%28%27grid%27%29%3B%0A%20%20%20%20vizBtn.innerHTML%20%20%20%20%3D%20icon%28%27record%27%29%3B%0A%20%20%20%20statusMsg.innerHTML%20%3D%20%60click%20%24%7Bicon%28%27play%27%29%7D%20to%20load%20audio%60%3B%0A%7D%29%28%29%3B%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20VISUALIZER%20MANAGEMENT%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20buildVisualizer%28key%29%20%7B%0A%20%20%20%20//%20Destroy%20existing%20instance%20with%20full%20GPU%20cleanup%20before%20creating%20new%20one.%0A%20%20%20%20//%20destroy%28%29%20in%20BaseVisualizer%20zeros%20canvas%20dimensions%20-%3E%20immediate%20GPU%20release.%0A%20%20%20%20if%20%28vizs%29%20%7B%20vizs.destroy%28%29%3B%20vizs%20%3D%20null%3B%20%7D%0A%20%20%20%20if%20%28key%20%3D%3D%3D%20%27off%27%20%7C%7C%20%21key%20%7C%7C%20typeof%20VISUALIZERS%20%3D%3D%3D%20%27undefined%27%20%7C%7C%20%21VISUALIZERS%5Bkey%5D%29%20%7B%0A%20%20%20%20%20%20%20%20vizBtn.setAttribute%28%27data-tip%27%2C%20%27off%27%29%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%20%20%20%20vizs%20%3D%20new%20VISUALIZERS%5Bkey%5D%28visWrap%29%3B%0A%20%20%20%20vizBtn.setAttribute%28%27data-tip%27%2C%20key%29%3B%0A%7D%0A%0Afunction%20cycleVisualizer%28%29%20%7B%0A%20%20%20%20vizIndex%20%3D%20%28vizIndex%20%2B%201%29%20%25%20VIZ_ORDER.length%3B%0A%20%20%20%20buildVisualizer%28VIZ_ORDER%5BvizIndex%5D%29%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20PRESET%20APPLICATION%0A//%20Injects%20CSS%20vars%2C%20sets%20flash%20color%20var%2C%20toggles%20CRT%20class%2C%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20applyPreset%28key%29%20%7B%0A%20%20%20%20const%20p%20%3D%20PRESETS%5Bkey%5D%3B%0A%20%20%20%20if%20%28%21p%29%20return%3B%0A%20%20%20%20currentPreset%20%3D%20key%3B%0A%20%20%20%20flashCycleIdx%20%3D%200%3B%0A%0A%20%20%20%20//%20CSS%20custom%20properties%20are%20injected%20via%0A%20%20%20%20//%20root.style.setProperty%28%29%20which%20overrides%20any%20existing%20inline%20styles.%0A%20%20%20%20//%20This%20is%20intentional%20%E2%80%94%20presets%20fully%20own%20the%20visual%20theme.%20%20%0A%20%20%20%20const%20root%20%3D%20document.documentElement%3B%0A%20%20%20%20Object.entries%28p.cssVars%29.forEach%28%28%5Bk%2C%20v%5D%29%20%3D%3E%20root.style.setProperty%28k%2C%20v%29%29%3B%0A%20%20%20%20//%20--flash-color%20drives%20%40keyframes%20kScreenFlash%20%E2%80%94%20set%20here%20so%20the%0A%20%20%20%20//%20flash%20is%20always%20theme-correct%20with%20no%20JS%20branching%20in%20triggerVisual.%20%20%20%20%0A%20%20%20%20root.style.setProperty%28%27--flash-color%27%2C%20p.flashColor%29%3B%0A%20%20%20%20//%20The%20%27--crt-on%27%20property%20is%20read%20as%20a%20string%2C%20not%20a%20boolean.%20%20%20%20%0A%20%20%20%20screenEl.classList.toggle%28%27crt-on%27%2C%20p.cssVars%5B%27--crt-on%27%5D%20%3D%3D%3D%20%271%27%29%3B%0A%0A%20%20%20%20_readPresetVars%28%29%3B%0A%0A%20%20%20%20//%20Do%20NOT%20rebuild%20the%20visualizer%20here.%20CSS%20vars%20are%20now%20injected%20above%2C%0A%20%20%20%20//%20and%20the%20running%20visualizer%20reads%20them%20on%20its%20next%20draw%20frame%20automatically.%0A%20%20%20%20//%20This%20is%20zero-coupling%20contract%3A%20preset%20switch%20%3D%20CSS%20vars%20only.%0A%0A%20%20%20%20const%20nextKey%20%3D%20PRESET_ORDER%5B%28PRESET_ORDER.indexOf%28key%29%20%2B%201%29%20%25%20PRESET_ORDER.length%5D%3B%0A%20%20%20%20presetBtn.setAttribute%28%27data-tip%27%2C%20%60%5Cu27a1%20%24%7BPRESETS%5BnextKey%5D.label%7D%60%29%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20VISUAL%20TRIGGER%0A//%20Called%20from%20the%20rAF%20beat%20loop.%20Applies%20a%20CSS%20effect%20class%20to%0A//%20.current-line%20and%20optionally%20fires%20the%20screen-flash%20animation.%0A//%0A//%20rAF-defer%20pattern%20%28replaces%20void%20offsetWidth%20reflow%29%3A%0A//%20%20%20Frame%20N%3A%20%20%20remove%20class%0A//%20%20%20Frame%20N%2B1%3A%20re-add%20class%20%E2%86%92%20browser%20sees%20a%20genuine%20remove%2Badd%20cycle%2C%0A//%20%20%20%20%20%20%20%20%20%20%20%20%20%20restarting%20the%20CSS%20animation%20from%200%25%0A//%20%20%20Timer%3A%20%20%20%20%20remove%20class%20after%20duration%20%2B%20buffer%0A//%0A//%20The%20effectRemovalTimers%20registry%20cancels%20any%20stale%20removal%20timer%0A//%20before%20re-arming%2C%20so%20rapid%20beats%20don%27t%20cut%20animations%20short.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20triggerVisual%28%29%20%7B%0A%20%20%20%20const%20p%20%20%20%20%20%20%3D%20PRESETS%5BcurrentPreset%5D%3B%0A%20%20%20%20const%20effect%20%3D%20p.effects%5B%28Math.random%28%29%20%2A%20p.effects.length%29%20%7C%200%5D%3B%0A%0A%20%20%20%20//%20Cancel%20stale%20removal%20timer%20so%20a%20fast%20re-trigger%20doesn%27t%20cut%20the%0A%20%20%20%20//%20new%20animation%20short%20with%20the%20old%20timer%27s%20expiry%20callback.%0A%20%20%20%20if%20%28effectRemovalTimers%5Beffect.className%5D%29%20%7B%0A%20%20%20%20%20%20%20%20clearTimeout%28effectRemovalTimers%5Beffect.className%5D%29%3B%0A%20%20%20%20%20%20%20%20delete%20effectRemovalTimers%5Beffect.className%5D%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20currentLineEl.classList.remove%28effect.className%29%3B%0A%20%20%20%20requestAnimationFrame%28%28%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20currentLineEl.classList.add%28effect.className%29%3B%0A%20%20%20%20%20%20%20%20effectRemovalTimers%5Beffect.className%5D%20%3D%20setTimeout%28%28%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20currentLineEl.classList.remove%28effect.className%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20delete%20effectRemovalTimers%5Beffect.className%5D%3B%0A%20%20%20%20%20%20%20%20%7D%2C%20effect.duration%20%2B%2020%29%3B%0A%20%20%20%20%7D%29%3B%0A%0A%20%20%20%20if%20%28effect.useFlash%29%20%7B%0A%20%20%20%20%20%20%20%20if%20%28p.flashCycle%20%26%26%20p.flashCycleColors%20%26%26%20p.flashCycleColors.length%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20flashCycleIdx%20%3D%20%28flashCycleIdx%20%2B%201%29%20%25%20p.flashCycleColors.length%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20document.documentElement.style.setProperty%28%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%27--flash-color%27%2C%20p.flashCycleColors%5BflashCycleIdx%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%29%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20screenEl.classList.remove%28%27screen-flash%27%29%3B%0A%20%20%20%20%20%20%20%20requestAnimationFrame%28%28%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20screenEl.classList.add%28%27screen-flash%27%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20setTimeout%28%28%29%20%3D%3E%20screenEl.classList.remove%28%27screen-flash%27%29%2C%20150%29%3B%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20%28effect.useScan%20%26%26%20screenEl.classList.contains%28%27crt-on%27%29%29%20%7B%0A%20%20%20%20%20%20%20%20screenEl.classList.remove%28%27scan-active%27%29%3B%0A%20%20%20%20%20%20%20%20requestAnimationFrame%28%28%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20screenEl.classList.add%28%27scan-active%27%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20setTimeout%28%28%29%20%3D%3E%20screenEl.classList.remove%28%27scan-active%27%29%2C%20280%29%3B%0A%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20%28vizs%29%20vizs.pulse%28%29%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20LYRICS%20CURSOR%0A//%20Updates%20DOM%20text%20content%20only%20when%20the%20lyric%20index%20changes.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20updateLyrics%28t%29%20%7B%0A%20%20%20%20if%20%28%21LYRICS.length%29%20%7B%0A%20%20%20%20%20%20%20%20currentLineEl.innerHTML%20%3D%20%60%24%7Bicon%28%27bolt%27%29%7D%20FIZX%20READY%20%24%7Bicon%28%27bolt%27%29%7D%60%3B%0A%20%20%20%20%20%20%20%20nextLineEl.innerHTML%20%20%20%20%3D%20%21beatTimestamps.length%0A%20%20%20%20%20%20%20%20%20%20%20%20%3F%20%60%24%7Bicon%28%27warning%27%29%7D%20No%20lyrics%20or%20beats%20detected%60%0A%20%20%20%20%20%20%20%20%20%20%20%20%3A%20%60%24%7Bicon%28%27warning%27%29%7D%20No%20lyrics%20detected%60%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20if%20%28t%20%3C%20LYRICS%5B0%5D.start%29%20%7B%20//%20%22Before%20Song%20Starts%22%0A%20%20%20%20%20%20%20%20if%20%28currentLyricIdx%20%21%3D%3D%20-2%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20currentLineEl.innerHTML%20%3D%20%60%24%7Bicon%28%27bolt%27%29%7D%20FIZX%20READY%20%24%7Bicon%28%27bolt%27%29%7D%60%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20nextLineEl.textContent%20%20%3D%20%60%5Cu27a1%20%24%7BLYRICS%5B0%5D.text%7D%60%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20currentLyricIdx%20%3D%20-2%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20//%20Cursor%20Logic%3A%20Advance%20index%20based%20on%20time%0A%20%20%20%20//%20We%20find%20the%20LAST%20index%20where%20t%20%3E%3D%20start%0A%20%20%20%20let%20newIdx%20%3D%20currentLyricIdx%20%3C%200%20%3F%200%20%3A%20currentLyricIdx%3B%0A%20%20%20%20while%20%28LYRICS%5BnewIdx%20%2B%201%5D%20%26%26%20t%20%3E%3D%20LYRICS%5BnewIdx%20%2B%201%5D.start%29%20newIdx%2B%2B%3B%0A%20%20%20%20//%20Reverse%20cursor%3A%20allow%20seeking%20backwards%20in%20the%20seekbar%0A%20%20%20%20while%20%28newIdx%20%3E%200%20%26%26%20t%20%3C%20LYRICS%5BnewIdx%5D.start%29%20newIdx--%3B%0A%20%20%20%20//%20Update%20DOM%20only%20when%20the%20index%20actually%20changes%0A%20%20%20%20if%20%28newIdx%20%21%3D%3D%20currentLyricIdx%29%20%7B%0A%20%20%20%20%20%20%20%20currentLyricIdx%20%3D%20newIdx%3B%0A%20%20%20%20%20%20%20%20currentLineEl.textContent%20%3D%20LYRICS%5BcurrentLyricIdx%5D.text%3B%0A%20%20%20%20%20%20%20%20const%20next%20%3D%20LYRICS%5BcurrentLyricIdx%20%2B%201%5D%3B%0A%20%20%20%20%20%20%20%20nextLineEl.textContent%20%3D%20next%20%3F%20%60%5Cu27a1%20%24%7Bnext.text%7D%60%20%3A%20%27Audit%20Complete%27%3B%0A%20%20%20%20%7D%0A%0A%20%20%20%20//%20Post-Song%20Credits%0A%20%20%20%20//%20Trigger%205%20seconds%20after%20the%20final%20lyric%20starts%0A%20%20%20%20const%20lastLyric%20%3D%20LYRICS%5BLYRICS.length%20-%201%5D%3B%0A%20%20%20%20if%20%28t%20%3E%20lastLyric.start%20%2B%205%20%26%26%20currentLyricIdx%20%21%3D%3D%20-3%29%20%7B%20//%20-3%20as%20unique%20state%20for%20%22Credits%22%0A%20%20%20%20%20%20%20%20currentLineEl.textContent%20%3D%20%27%40AmMoPy%27%3B%0A%20%20%20%20%20%20%20%20nextLineEl.innerHTML%20%20%20%20%20%20%3D%20%60%24%7Bicon%28%27star%27%29%7D%20SHOW%20SOME%20LOVE%20%24%7Bicon%28%27star%27%29%7D%60%3B%0A%20%20%20%20%20%20%20%20currentLyricIdx%20%3D%20-3%3B%0A%20%20%20%20%7D%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20SEEKBAR%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20_updateSeekBarVisual%28t%29%20%7B%0A%20%20%20%20if%20%28%21audioDuration%20%7C%7C%20isSeeking%29%20return%3B%0A%20%20%20%20const%20pct%20%3D%20%28t%20/%20audioDuration%29%20%2A%20100%3B%0A%20%20%20%20seekBar.value%20%3D%20pct%3B%0A%20%20%20%20//%20Gradient%20fill%20%E2%80%94%20painted%20once%20per%20rAF%2C%20not%20on%20every%20input%20event%0A%20%20%20%20const%20col%20%3D%20getComputedStyle%28document.documentElement%29%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20.getPropertyValue%28%27--color-primary%27%29.trim%28%29%3B%0A%20%20%20%20seekBar.style.background%20%3D%0A%20%20%20%20%20%20%20%20%60linear-gradient%28to%20right%2C%20%24%7Bcol%7D%20%24%7Bpct%7D%25%2C%20var%28--bg-btn%29%20%24%7Bpct%7D%25%29%60%3B%0A%7D%0A%0Afunction%20seekTo%28pct%29%20%7B%0A%20%20%20%20const%20target%20%3D%20%28pct%20/%20100%29%20%2A%20audioDuration%3B%0A%20%20%20%20//%20Reset%20pointers%20and%20fast-forward%20to%20target%20without%20firing%20triggers%0A%20%20%20%20lastFiredBeatIdx%20%3D%200%3B%0A%20%20%20%20currentLyricIdx%20%20%3D%20-1%3B%0A%20%20%20%20const%20src%20%3D%20currentMode%20%3D%3D%3D%20%27A%27%20%3F%20beatTimestamps%20%3A%20LYRICS.map%28l%20%3D%3E%20l.start%29%3B%0A%20%20%20%20while%20%28src%5BlastFiredBeatIdx%5D%20%21%3D%3D%20undefined%20%26%26%20src%5BlastFiredBeatIdx%5D%20%3C%20target%29%0A%20%20%20%20%20%20%20%20lastFiredBeatIdx%2B%2B%3B%0A%20%20%20%20if%20%28%21isPlaying%29%20%7B%0A%20%20%20%20%20%20%20%20startOffset%20%3D%20target%3B%0A%20%20%20%20%20%20%20%20updateLyrics%28target%29%3B%0A%20%20%20%20%20%20%20%20timeDisplay.textContent%20%3D%20fmt%28target%29%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%20%20%20%20//%20Playing%3A%20restart%20source%20from%20new%20offset%0A%20%20%20%20_startSourceAt%28target%29%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20CENTRALIZED%20DISPLAY%20%2B%20BEAT%20LOOP%0A//%0A//%20One%20rAF%20loop%20owns%3A%0A//%20%20%20-%20AudioContext%20clock%20read%20%28no%20drift%29%0A//%20%20%20-%20Seekbar%20%2B%20time%20display%20updates%0A//%20%20%20-%20Lyric%20cursor%20advancement%0A//%20%20%20-%20Beat%20firing%20via%20O%281%29%20amortized%20pointer%20advance%0A//%20%20%20-%20Dust%20canvas%20draw%0A//%0A//%20Beat%20scheduling%20moved%20entirely%20here%20%E2%80%94%20eliminates%20the%20setTimeout%0A//%20forest%20%28scheduleAll%20/%20clearScheduled%20pattern%29%20and%20its%20allocation%0A//%20cost.%20A%20single%20integer%20pointer%20advances%20monotonically%3B%20on%20seek%20it%0A//%20is%20reset%20and%20fast-forwarded%20in%20a%20tight%20while%20loop%20%28no%20rAF%20cost%29.%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20displayLoop%28%29%20%7B%0A%20%20%20%20//%20Current%20playback%20position%20derived%20from%20AudioContext%20wall%20clock.%0A%20%20%20%20//%20Accurate%20to%20~1ms%20%E2%80%94%20tied%20to%20sound%20card%20driver%2C%20not%20JS%20event%20queue.%0A%20%20%20%20const%20t%20%3D%20%28isPlaying%20%26%26%20audioContext%29%0A%20%20%20%20%20%20%20%20%3F%20Math.min%28audioContext.currentTime%20-%20startedAt%20%2B%20startOffset%2C%20audioDuration%29%0A%20%20%20%20%20%20%20%20%3A%20startOffset%3B%0A%0A%20%20%20%20if%20%28isPlaying%20%26%26%20%21isSeeking%29%20%7B%0A%20%20%20%20%20%20%20%20timeDisplay.textContent%20%3D%20fmt%28t%29%3B%0A%20%20%20%20%20%20%20%20_updateSeekBarVisual%28t%29%3B%0A%20%20%20%20%20%20%20%20updateLyrics%28t%29%3B%0A%0A%20%20%20%20%20%20%20%20//%20%E2%94%80%E2%94%80%20Inline%20beat%20scheduler%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A%20%20%20%20%20%20%20%20//%20Mode%20A%3A%20fire%20against%20beatTimestamps%20%28pre-sorted%20by%20compile.py%29.%0A%20%20%20%20%20%20%20%20//%20Mode%20B%3A%20fire%20against%20lyric%20start%20times.%0A%20%20%20%20%20%20%20%20//%20Pointer%20only%20moves%20forward%20%E2%80%94%20wrap-around%20handled%20by%20seekTo%28%29.%0A%20%20%20%20%20%20%20%20const%20src%20%3D%20currentMode%20%3D%3D%3D%20%27A%27%0A%20%20%20%20%20%20%20%20%20%20%20%20%3F%20beatTimestamps%0A%20%20%20%20%20%20%20%20%20%20%20%20%3A%20LYRICS.map%28l%20%3D%3E%20l.start%29%3B%0A%20%20%20%20%20%20%20%20while%20%28src%5BlastFiredBeatIdx%5D%20%21%3D%3D%20undefined%20%26%26%20t%20%3E%3D%20src%5BlastFiredBeatIdx%5D%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20triggerVisual%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20lastFiredBeatIdx%2B%2B%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%0A%20%20%20%20drawFrame%28%29%3B%0A%20%20%20%20animFrameId%20%3D%20requestAnimationFrame%28displayLoop%29%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20AUDIO%20LIFECYCLE%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0Afunction%20_ensureContext%28%29%20%7B%0A%20%20%20%20if%20%28%21audioContext%29%20%7B%0A%20%20%20%20%20%20%20%20audioContext%20%3D%20new%20%28window.AudioContext%20%7C%7C%20window.webkitAudioContext%29%28%29%3B%0A%20%20%20%20%20%20%20%20analyserNode%20%3D%20audioContext.createAnalyser%28%29%3B%0A%20%20%20%20%20%20%20%20//%20256-bin%20FFT%20%E2%80%94%20sufficient%20for%20all%20current%20visualizers%3B%20keeps%0A%20%20%20%20%20%20%20%20//%20the%20analysis%20buffer%20small%20and%20fast%20to%20read%20each%20frame.%0A%20%20%20%20%20%20%20%20analyserNode.fftSize%20%3D%20256%3B%0A%20%20%20%20%20%20%20%20analyserNode.connect%28audioContext.destination%29%3B%0A%20%20%20%20%7D%0A%7D%0A%0A//%20Internal%3A%20create%20and%20start%20a%20new%20AudioBufferSourceNode%20at%20the%20given%20offset.%0A//%20Stops%20and%20disconnects%20any%20existing%20source%20first.%0Afunction%20_startSourceAt%28offset%29%20%7B%0A%20%20%20%20if%20%28sourceNode%29%20%7B%0A%20%20%20%20%20%20%20%20sourceNode.onended%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20try%20%7B%20sourceNode.stop%28%29%3B%20%7D%20catch%20%28_%29%20%7B%7D%0A%20%20%20%20%20%20%20%20sourceNode.disconnect%28%29%3B%0A%20%20%20%20%20%20%20%20sourceNode%20%3D%20null%3B%0A%20%20%20%20%7D%0A%20%20%20%20sourceNode%20%3D%20audioContext.createBufferSource%28%29%3B%0A%20%20%20%20sourceNode.buffer%20%3D%20audioBuffer%3B%0A%20%20%20%20//%20Route%20through%20analyser%20%E2%80%94%20visualizers%20can%20read%20frequency%20data%20without%0A%20%20%20%20//%20separate%20wiring.%0A%20%20%20%20sourceNode.connect%28analyserNode%29%3B%0A%20%20%20%20startOffset%20%3D%20offset%3B%0A%20%20%20%20startedAt%20%20%20%3D%20audioContext.currentTime%3B%0A%20%20%20%20sourceNode.start%280%2C%20offset%29%3B%0A%20%20%20%20//%20onended%20fires%20at%20natural%20buffer%20completion%2C%20not%20on%20manual%20stop%28%29.%0A%20%20%20%20//%20This%20is%20the%20only%20place%20end-of-track%20state%20is%20set.%0A%20%20%20%20sourceNode.onended%20%3D%20%28%29%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20if%20%28%21isPlaying%29%20return%3B%20//%20fired%20by%20manual%20stop%20%E2%80%94%20ignore%0A%20%20%20%20%20%20%20%20isPlaying%20%20%20%20%20%20%20%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20startOffset%20%20%20%20%20%20%3D%200%3B%0A%20%20%20%20%20%20%20%20lastFiredBeatIdx%20%3D%200%3B%0A%20%20%20%20%20%20%20%20currentLyricIdx%20%20%3D%20-1%3B%0A%20%20%20%20%20%20%20%20playBtn.innerHTML%20%20%20%3D%20icon%28%27play%27%29%3B%0A%20%20%20%20%20%20%20%20statusMsg.innerHTML%20%3D%20%60%24%7Bicon%28%27stop%27%29%7D%20done%60%3B%0A%20%20%20%20%20%20%20%20_updateSeekBarVisual%280%29%3B%0A%20%20%20%20%7D%3B%0A%7D%0A%0Aasync%20function%20loadAudioBuffer%28file%29%20%7B%0A%20%20%20%20_ensureContext%28%29%3B%0A%20%20%20%20statusMsg.textContent%20%3D%20%27decoding%27%3B%0A%20%20%20%20const%20arrayBuf%20%3D%20await%20file.arrayBuffer%28%29%3B%0A%20%20%20%20audioBuffer%20%20%20%20%3D%20await%20audioContext.decodeAudioData%28arrayBuf%29%3B%0A%20%20%20%20audioDuration%20%20%3D%20audioBuffer.duration%3B%0A%20%20%20%20bpmDisplay.textContent%20%3D%20%60%24%7Bbpm%7D%20BPM%60%3B%0A%20%20%20%20statusMsg.innerHTML%20%20%20%20%3D%20%60%24%7Bicon%28%27bolt%27%29%7D%20%24%7Bfile.name.slice%280%2C%2022%29%7D%60%3B%0A%7D%0A%0Afunction%20selectAudio%28%29%20%7B%0A%20%20%20%20const%20input%20%20%3D%20document.createElement%28%27input%27%29%3B%0A%20%20%20%20input.type%20%20%20%3D%20%27file%27%3B%0A%20%20%20%20input.accept%20%3D%20%27audio/mpeg%2Caudio/wav%2Caudio/ogg%2Caudio/mp3%27%3B%0A%20%20%20%20input.onchange%20%3D%20async%20e%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20const%20file%20%3D%20e.target.files%5B0%5D%3B%0A%20%20%20%20%20%20%20%20if%20%28%21file%29%20return%3B%0A%20%20%20%20%20%20%20%20//%20Tear%20down%20any%20running%20source%20before%20replacing%20the%20buffer%0A%20%20%20%20%20%20%20%20if%20%28sourceNode%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20isPlaying%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20sourceNode.onended%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%20sourceNode.stop%28%29%3B%20%7D%20catch%20%28_%29%20%7B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20sourceNode.disconnect%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20sourceNode%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20startOffset%20%3D%200%3B%20lastFiredBeatIdx%20%3D%200%3B%20currentLyricIdx%20%3D%20-1%3B%0A%20%20%20%20%20%20%20%20await%20loadAudioBuffer%28file%29%3B%0A%20%20%20%20%20%20%20%20updateLyrics%280%29%3B%0A%20%20%20%20%20%20%20%20timeDisplay.textContent%20%3D%20fmt%280%29%3B%0A%20%20%20%20%20%20%20%20_updateSeekBarVisual%280%29%3B%0A%20%20%20%20%20%20%20%20statusMsg.textContent%20%3D%20%60%24%7BbeatTimestamps.length%7D%20beats%20%5Cu00b7%20%24%7BLYRICS.length%7D%20lines%60%3B%0A%20%20%20%20%7D%3B%0A%20%20%20%20input.click%28%29%3B%0A%7D%0A%0A//%20%E2%94%80%E2%94%80%20Play%20/%20Pause%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Aasync%20function%20togglePlay%28%29%20%7B%0A%20%20%20%20if%20%28%21audioBuffer%29%20%7B%20selectAudio%28%29%3B%20return%3B%20%7D%0A%20%20%20%20_ensureContext%28%29%3B%0A%20%20%20%20if%20%28audioContext.state%20%3D%3D%3D%20%27suspended%27%29%20await%20audioContext.resume%28%29%3B%0A%20%20%20%20if%20%28isPlaying%29%20%7B%0A%20%20%20%20%20%20%20%20startOffset%20%3D%20audioContext.currentTime%20-%20startedAt%20%2B%20startOffset%3B%0A%20%20%20%20%20%20%20%20if%20%28sourceNode%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20sourceNode.onended%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20try%20%7B%20sourceNode.stop%28%29%3B%20%7D%20catch%20%28_%29%20%7B%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20sourceNode.disconnect%28%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20sourceNode%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20isPlaying%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20playBtn.innerHTML%20%20%20%3D%20icon%28%27play%27%29%3B%0A%20%20%20%20%20%20%20%20statusMsg.innerHTML%20%3D%20%60%24%7Bicon%28%27pause%27%29%7D%20%24%7Bfmt%28startOffset%29%7D%60%3B%0A%20%20%20%20%20%20%20%20return%3B%0A%20%20%20%20%7D%0A%20%20%20%20_startSourceAt%28startOffset%29%3B%0A%20%20%20%20isPlaying%20%3D%20true%3B%0A%20%20%20%20playBtn.innerHTML%20%20%20%3D%20icon%28%27pause%27%29%3B%0A%20%20%20%20statusMsg.innerHTML%20%3D%20%60%24%7Bicon%28%27play%27%29%7D%20%24%7BPV.label%7D%20%5Cu00b7%20%24%7BcurrentMode%7D%60%3B%0A%7D%0A%0A//%20%E2%94%80%E2%94%80%20Reset%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0A//%20Teardown%20order%20matters%20for%20correctness%3A%0A//%20%20%201.%20Stop%20source%20%28prevents%20onended%20from%20firing%20post-reset%29%0A//%20%20%202.%20Clear%20CSS%20cleanup%20timers%0A//%20%20%203.%20Cancel%20rAF%20loop%0A//%20%20%204.%20Close%20AudioContext%20%28await%20%E2%80%94%20releases%20DSP%20before%20next%20context%20opens%29%0A//%20%20%205.%20Destroy%20visualizer%20GPU%20buffers%0A//%20%20%206.%20Reset%20all%20state%20variables%20%2B%20UI%0A//%20%20%207.%20Restart%20display%20loop%20and%20visualizer%20%28display-only%2C%20no%20audio%29%0A//%20%20%208.%20Prompt%20for%20new%20file%0Aasync%20function%20resetAudio%28%29%20%7B%0A%20%20%20%20//%201.%0A%20%20%20%20if%20%28sourceNode%29%20%7B%0A%20%20%20%20%20%20%20%20isPlaying%20%3D%20false%3B%0A%20%20%20%20%20%20%20%20sourceNode.onended%20%3D%20null%3B%0A%20%20%20%20%20%20%20%20try%20%7B%20sourceNode.stop%28%29%3B%20%7D%20catch%20%28_%29%20%7B%7D%0A%20%20%20%20%20%20%20%20sourceNode.disconnect%28%29%3B%0A%20%20%20%20%20%20%20%20sourceNode%20%3D%20null%3B%0A%20%20%20%20%7D%0A%20%20%20%20isPlaying%20%3D%20false%3B%0A%0A%20%20%20%20//%202.%0A%20%20%20%20Object.keys%28effectRemovalTimers%29.forEach%28k%20%3D%3E%20%7B%0A%20%20%20%20%20%20%20%20clearTimeout%28effectRemovalTimers%5Bk%5D%29%3B%0A%20%20%20%20%20%20%20%20delete%20effectRemovalTimers%5Bk%5D%3B%0A%20%20%20%20%7D%29%3B%0A%0A%20%20%20%20//%203.%0A%20%20%20%20if%20%28animFrameId%29%20%7B%20cancelAnimationFrame%28animFrameId%29%3B%20animFrameId%20%3D%20null%3B%20%7D%0A%20%20%20%20//%204.%20AudioContext%20%E2%80%94%20await%20ensures%20DSP%20resources%20are%20released%20before%0A%20%20%20%20//%20%20%20%20a%20new%20context%20might%20be%20created%20by%20setupAudio%28%29%0A%20%20%20%20if%20%28audioContext%29%20%7B%20await%20audioContext.close%28%29%3B%20audioContext%20%3D%20null%3B%20analyserNode%20%3D%20null%3B%20%7D%0A%20%20%20%20//%205.%0A%20%20%20%20if%20%28vizs%29%20%7B%20vizs.destroy%28%29%3B%20vizs%20%3D%20null%3B%20%7D%0A%0A%20%20%20%20//%206.%0A%20%20%20%20audioBuffer%20%3D%20null%3B%20audioDuration%20%3D%200%3B%20startOffset%20%3D%200%3B%20startedAt%20%3D%200%3B%0A%20%20%20%20lastFiredBeatIdx%20%3D%200%3B%20currentLyricIdx%20%3D%20-1%3B%20flashCycleIdx%20%3D%200%3B%0A%0A%20%20%20%20timeDisplay.textContent%20%20%20%3D%20fmt%280%29%3B%0A%20%20%20%20bpmDisplay.textContent%20%20%20%20%3D%20%27--%20BPM%27%3B%0A%20%20%20%20seekBar.value%20%20%20%20%20%20%20%20%20%20%20%20%20%3D%200%3B%0A%20%20%20%20seekBar.style.background%20%20%3D%20%27%27%3B%0A%20%20%20%20playBtn.innerHTML%20%20%20%20%20%20%20%20%20%3D%20icon%28%27play%27%29%3B%0A%20%20%20%20currentLineEl.innerHTML%20%20%20%3D%20%60%24%7Bicon%28%27bolt%27%29%7D%20FIZX%20READY%20%24%7Bicon%28%27bolt%27%29%7D%60%3B%0A%20%20%20%20nextLineEl.innerHTML%20%20%20%20%20%20%3D%20%60%24%7Bicon%28%27play%27%29%7D%20select%20audio%20file%60%3B%0A%20%20%20%20statusMsg.innerHTML%20%20%20%20%20%20%20%3D%20%60%24%7Bicon%28%27refresh%27%29%7D%20select%20new%20file%60%3B%0A%0A%20%20%20%20//%207.%0A%20%20%20%20requestAnimationFrame%28displayLoop%29%3B%0A%20%20%20%20buildVisualizer%28VIZ_ORDER%5BvizIndex%5D%29%3B%0A%0A%20%20%20%20//%208.%0A%20%20%20%20selectAudio%28%29%3B%0A%7D%0A%0A//%20%E2%94%80%E2%94%80%20Mode%20toggle%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Afunction%20toggleMode%28%29%20%7B%0A%20%20%20%20currentMode%20%3D%20currentMode%20%3D%3D%3D%20%27A%27%20%3F%20%27B%27%20%3A%20%27A%27%3B%0A%20%20%20%20//%20Fast-forward%20pointer%20to%20current%20playback%20position%20in%20the%20new%20source%20array%0A%20%20%20%20lastFiredBeatIdx%20%3D%200%3B%0A%20%20%20%20const%20t%20%20%20%3D%20%28isPlaying%20%26%26%20audioContext%29%0A%20%20%20%20%20%20%20%20%3F%20audioContext.currentTime%20-%20startedAt%20%2B%20startOffset%20%3A%20startOffset%3B%0A%20%20%20%20const%20src%20%3D%20currentMode%20%3D%3D%3D%20%27A%27%20%3F%20beatTimestamps%20%3A%20LYRICS.map%28l%20%3D%3E%20l.start%29%3B%0A%20%20%20%20while%20%28src%5BlastFiredBeatIdx%5D%20%21%3D%3D%20undefined%20%26%26%20src%5BlastFiredBeatIdx%5D%20%3C%20t%29%0A%20%20%20%20%20%20%20%20lastFiredBeatIdx%2B%2B%3B%0A%20%20%20%20statusMsg.textContent%20%3D%20%60MODE%20%24%7BcurrentMode%7D%60%3B%0A%7D%0A%0A//%20%E2%94%80%E2%94%80%20Preset%20toggle%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Afunction%20togglePreset%28%29%20%7B%0A%20%20%20%20const%20next%20%3D%20PRESET_ORDER%5B%28PRESET_ORDER.indexOf%28currentPreset%29%20%2B%201%29%20%25%20PRESET_ORDER.length%5D%3B%0A%20%20%20%20applyPreset%28next%29%3B%0A%7D%0A%0A//%20%E2%94%80%E2%94%80%20AR%20toggle%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Afunction%20toggleAR%28%29%20%7B%0A%20%20%20%20isLandscape%20%3D%20%21isLandscape%3B%0A%20%20%20%20stageEl.classList.toggle%28%27ar-landscape%27%2C%20isLandscape%29%3B%0A%20%20%20%20arBtn.setAttribute%28%27data-tip%27%2C%20isLandscape%20%3F%20%279%3A16%27%20%3A%20%2716%3A9%27%29%3B%0A%20%20%20%20//%20Resize%20dust%20canvas%20on%20next%20paint%20after%20layout%20settles%0A%20%20%20%20requestAnimationFrame%28%28%29%20%3D%3E%20%7B%20resizeMasterCanvas%28%29%3B%20_readPresetVars%28%29%3B%20%7D%29%3B%0A%7D%0A%0A//%20%E2%94%80%E2%94%80%20Utilities%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Afunction%20fmt%28sec%29%20%7B%0A%20%20%20%20if%20%28isNaN%28sec%29%20%7C%7C%20sec%20%3C%200%29%20return%20%270%3A00%27%3B%0A%20%20%20%20const%20m%20%3D%20%28sec%20/%2060%29%20%7C%200%3B%0A%20%20%20%20const%20s%20%3D%20%28sec%20%25%2060%29%20%7C%200%3B%0A%20%20%20%20return%20%60%24%7Bm%7D%3A%24%7Bs%20%3C%2010%20%3F%20%270%27%20%3A%20%27%27%7D%24%7Bs%7D%60%3B%0A%7D%0A%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A//%20EVENT%20LISTENERS%0A//%20%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%E2%95%90%0A%0A//%20%E2%94%80%E2%94%80%20Seekbar%20listeners%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0AseekBar.addEventListener%28%27pointerdown%27%2C%20%28%29%20%3D%3E%20%7B%20isSeeking%20%3D%20true%3B%20%7D%29%3B%0AseekBar.addEventListener%28%27input%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20%20%20if%20%28%21audioDuration%29%20return%3B%0A%20%20%20%20const%20t%20%3D%20%28seekBar.value%20/%20100%29%20%2A%20audioDuration%3B%0A%20%20%20%20timeDisplay.textContent%20%3D%20fmt%28t%29%3B%0A%20%20%20%20updateLyrics%28t%29%3B%0A%7D%29%3B%0AseekBar.addEventListener%28%27change%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20%20%20isSeeking%20%3D%20false%3B%0A%20%20%20%20if%20%28%21audioDuration%29%20return%3B%0A%20%20%20%20seekTo%28parseFloat%28seekBar.value%29%29%3B%0A%7D%29%3B%0AseekBar.addEventListener%28%27pointerup%27%2C%20%28%29%20%3D%3E%20%7B%20isSeeking%20%3D%20false%3B%20%7D%29%3B%0A%0A//%20%E2%94%80%E2%94%80%20Button%20listeners%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0AplayBtn.onclick%20%20%20%3D%20togglePlay%3B%0AresetBtn.onclick%20%20%3D%20resetAudio%3B%0AmodeBtn.onclick%20%20%20%3D%20toggleMode%3B%0ApresetBtn.onclick%20%3D%20togglePreset%3B%0AarBtn.onclick%20%20%20%20%20%3D%20toggleAR%3B%0AvizBtn.onclick%20%20%20%20%3D%20cycleVisualizer%3B%0A%0A//%20%E2%94%80%E2%94%80%20Resize%20observer%20%E2%80%94%20keeps%20dust%20canvas%20sized%20to%20screen%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Aconst%20_resizeObs%20%3D%20new%20ResizeObserver%28%28%29%20%3D%3E%20%7B%0A%20%20%20%20resizeMasterCanvas%28%29%3B%0A%20%20%20%20_readPresetVars%28%29%3B%0A%7D%29%3B%0A_resizeObs.observe%28screenEl%29%3B%0A%0A//%20%E2%94%80%E2%94%80%20Boot%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0AapplyPreset%28PRESET_ORDER%5B0%5D%29%3B%0AbpmDisplay.textContent%20%3D%20%60%24%7Bbpm%7D%20BPM%60%3B%0AresizeMasterCanvas%28%29%3B%0AupdateLyrics%280%29%3B%0ArequestAnimationFrame%28displayLoop%29%3B%0AbuildVisualizer%28VIZ_ORDER%5BvizIndex%5D%29%3B%0A%0A//%20%E2%94%80%E2%94%80%20Cleanup%20on%20unload%20%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%E2%94%80%0Awindow.addEventListener%28%27beforeunload%27%2C%20%28%29%20%3D%3E%20%7B%0A%20%20%20%20Object.values%28effectRemovalTimers%29.forEach%28id%20%3D%3E%20clearTimeout%28id%29%29%3B%0A%20%20%20%20if%20%28animFrameId%29%20%20cancelAnimationFrame%28animFrameId%29%3B%0A%20%20%20%20if%20%28sourceNode%29%20%20%20%7B%20try%20%7B%20sourceNode.stop%28%29%3B%20%7D%20catch%20%28_%29%20%7B%7D%20%7D%0A%20%20%20%20if%20%28audioContext%29%20audioContext.close%28%29%3B%0A%20%20%20%20if%20%28vizs%29%20%20%20%20%20%20%20%20%20vizs.destroy%28%29%3B%0A%20%20%20%20_resizeObs.disconnect%28%29%3B%0A%7D%29%3B%0A%3C/script%3E%0A%3C/body%3E%0A%3C/html%3E");
// ═══════════════════════════════════════════════════════════════════
// WORKER SOURCE (inlined as Blob — no separate file needed)
//
// Two engines share one Worker, selected by the 'engine' param:
//
// 'spectral' — Spectral Flux (default, mirrors ex_beats.py):
// - Radix-2 Cooley-Tukey FFT, O(N log N)
// - Hann window, N=2048, hop=512
// - Spectral flux envelope, aggregate = median per frame
// (matches librosa aggregate=np.median)
// - Peak-pick: delta=0.07, wait=8 frames (~85ms at 48kHz/512-hop)
// - backtrack to nearest preceding local minimum (backtrack=True)
// - Secondary 120ms de-dupe pass (MIN_GAP=0.12)
// - Good for: timbral transients, rap, dense percussion
// - Weaker at: pure amplitude attacks where spectrum doesn't change
//
// 'energy' — Energy RMS (zero external deps, complementary to spectral):
// - Short-time RMS envelope, no FFT needed
// - Same peak-pick / backtrack / de-dupe pipeline as spectral
// - Good for: loud amplitude attacks, kick drums, spoken word
// - Weaker at: quiet timbral changes, hi-hats against loud pads
//
// Both use the same delta/wait/minGap params from PARAMS.
// Tradeoff: one Worker blob for both keeps file size minimal.
// Adding a third engine: add a branch in the onmessage switch below.
//
// Receives: { samples, sr, engine, delta, wait, minGap }
// Posts: { type:'progress', pct, label }
// { type:'done', onsets, bpm }
// { type:'error', message }
// ═══════════════════════════════════════════════════════════════════
const RADIX_WORKER_SRC = `
'use strict';
// ── Twiddle factor table ──────────────────────────────────────────
// Precomputed once for N=2048. The butterfly loop reads wRe[len] and
// wIm[len] instead of calling Math.cos/Math.sin per pass.
// Keys are len values (2, 4, 8, ..., 2048).
// Same table is reused across all frames (N=2048 is fixed).
// Saves ~numFrames × log2(2048) × 2 trig evaluations ≈ 120K calls
// on a 3-minute track.
// FFT size N=2048: not parameterized, adding a path for power-of-2
// validation adds code complexity with negligible real benefit.
const FFT_N = 2048;
const twRe = new Float64Array(FFT_N + 1);
const twIm = new Float64Array(FFT_N + 1);
for (let len = 2; len <= FFT_N; len <<= 1) {
const theta = -2 * Math.PI / len;
twRe[len] = Math.cos(theta);
twIm[len] = Math.sin(theta);
}
// ── Radix-2 Cooley-Tukey in-place FFT ────────────────────────────
// re, im: Float64Arrays of length N (must be FFT_N = 2048).
// Iterative butterfly — no recursion, no stack depth risk.
// Reads twiddle factors from precomputed table instead of computing
// Math.cos/Math.sin per butterfly pass.
function fft(re, im) {
const N = re.length;
// Bit-reversal permutation
let j = 0;
for (let i = 1; i < N; i++) {
let bit = N >> 1;
for (; j & bit; bit >>= 1) j ^= bit;
j ^= bit;
if (i < j) {
let t = re[i]; re[i] = re[j]; re[j] = t;
t = im[i]; im[i] = im[j]; im[j] = t;
}
}
// Butterfly passes — twiddle factors from table
for (let len = 2; len <= N; len <<= 1) {
const half = len >> 1;
const wRe = twRe[len];
const wIm = twIm[len];
for (let i = 0; i < N; i += len) {
let curRe = 1, curIm = 0;
for (let k = 0; k < half; k++) {
const uRe = re[i+k], uIm = im[i+k];
const vRe = re[i+k+half], vIm = im[i+k+half];
const tRe = curRe*vRe - curIm*vIm;
const tIm = curRe*vIm + curIm*vRe;
re[i+k] = uRe+tRe; im[i+k] = uIm+tIm;
re[i+k+half] = uRe-tRe; im[i+k+half] = uIm-tIm;
const nr = curRe*wRe - curIm*wIm;
curIm = curRe*wIm + curIm*wRe;
curRe = nr;
}
}
}
}
// ── Quickselect median O(n) ───────────────────────────────────────
// Replaces Float64Array.from(arr).sort() which is O(n log n) and
// allocates a new typed array on every call (GC pressure in hot loop).
//
// Strategy: in-place Quickselect on a reusable scratch buffer.
// - scratch is pre-allocated once before the frame loop and passed in.
// - We copy arr → scratch (unavoidable to preserve original fluxBuf),
// then partition in-place until the median index is found.
// - Average O(n), worst-case O(n²) — but the median-of-three pivot
// selection below makes worst-case extremely unlikely in practice.
//
// arr: source Float64Array (read-only)
// scratch: pre-allocated Float64Array of same length (mutated in-place)
function medianQ(arr, scratch) {
const n = arr.length;
scratch.set(arr);
// Median-of-three pivot: avoids O(n²) on sorted/reverse-sorted input.
// The spectral flux envelope is not random — this matters.
function pivotOf3(a, lo, hi) {
const mid = (lo + hi) >> 1;
if (scratch[a = lo] > scratch[mid]) { let t = scratch[lo]; scratch[lo] = scratch[mid]; scratch[mid] = t; }
if (scratch[lo] > scratch[hi]) { let t = scratch[lo]; scratch[lo] = scratch[hi]; scratch[hi] = t; }
if (scratch[mid] > scratch[hi]) { let t = scratch[mid];scratch[mid]= scratch[hi]; scratch[hi] = t; }
return scratch[mid];
}
function partition(lo, hi) {
const pivot = pivotOf3(lo, lo, hi);
let i = lo - 1, j = hi + 1;
while (true) {
do { i++; } while (scratch[i] < pivot);
do { j--; } while (scratch[j] > pivot);
if (i >= j) return j;
const t = scratch[i]; scratch[i] = scratch[j]; scratch[j] = t;
}
}
const target = n >> 1;
let lo = 0, hi = n - 1;
while (lo < hi) {
const p = partition(lo, hi);
if (p < target) lo = p + 1;
else hi = p;
}
return (n & 1)
? scratch[target]
: (scratch[target - 1] + scratch[target]) * 0.5;
}
// ── BPM via autocorrelation of the onset envelope ─────────────────
// Rationale: runs autocorrelation on the normalised onset envelope
// to find the dominant periodicity, then resolves octave ambiguity
// via a log-BPM prior:
// 1. Compute autocorrelation of the normalized envelope.
// 2. Convert the lag with the highest correlation to BPM.
// 3. Apply octave correction: if a sub-harmonic lag (×2) has
// correlation ≥ 0.9× the best, prefer the lower BPM (more musical).
// env: Float64Array of normalized onset strengths.
// sr, HOP: sample rate and hop size, used to convert lag → BPM.
// BPM range 40–240 constrains the lag search window.
// Optimization note: O(N²) over lagMax-lagMin range ≈ 280 lags
// × envLen ≈ 5600 = 1.6M multiplies. Fast enough; no change needed.
function bpmFromAutocorrelation(env, sr, HOP) {
const frameRate = sr / HOP; // frames per second (~93.75 at 48kHz/512)
// Convert BPM bounds to frame-lag bounds
const lagMin = Math.floor(frameRate * 60 / 240); // 240 BPM → shortest lag
const lagMax = Math.ceil(frameRate * 60 / 40); // 40 BPM → longest lag
const N = env.length;
let bestLag = lagMin, bestCorr = -Infinity;
// Unbiased autocorrelation at each candidate lag.
// We normalize by (N - lag) so longer lags are not penalized
// relative to shorter ones — same convention as librosa.
for (let lag = lagMin; lag <= Math.min(lagMax, N - 1); lag++) {
let corr = 0;
for (let i = 0; i < N - lag; i++) corr += env[i] * env[i + lag];
corr /= (N - lag); // unbiased normalization
if (corr > bestCorr) { bestCorr = corr; bestLag = lag; }
}
let bpm = Math.round(frameRate * 60 / bestLag);
// Octave correction: prefer higher BPM if the half-lag (2× tempo)
// has correlation ≥ 93% of the best. This handles the common case
// where the autocorrelation peaks at the half-tempo period (every
// 2 beats) before the beat period, producing 64 instead of 129.
// We check upward (÷2 lag = ×2 BPM) before downward to match
// librosa's log-BPM prior which biases toward the higher tempo
// when the signal is ambiguous.
const halfLag = Math.round(bestLag / 2);
if (halfLag >= lagMin) {
let corrHalf = 0;
for (let i = 0; i < N - halfLag; i++) corrHalf += env[i] * env[i + halfLag];
corrHalf /= (N - halfLag);
if (corrHalf >= bestCorr * 0.93) bpm = Math.round(frameRate * 60 / halfLag);
}
return bpm;
}
// ── Energy RMS onset detection ───────────────────────────────────
// Complementary to spectral flux: operates on amplitude envelope rather
// than spectral change. Catches loud attacks that don't shift the spectrum
// (e.g. kick against a sustained synth pad). Shares the same peak-pick,
// backtrack, and de-dupe pipeline as the spectral engine.
//
// frameSize: RMS window in samples (matches FFT N for consistent hop ratio)
// hop: same as spectral so timestamps are directly comparable
//
// Optimization note: Math.sqrt kept as envelope linearity matters for
// BPM autocorrelation even though peak-pick only needs relative order.
function buildEnergyEnvelope(samples, frameSize, hop) {
const numFrames = Math.floor((samples.length - frameSize) / hop) + 1;
const env = new Float64Array(numFrames);
for (let f = 0; f < numFrames; f++) {
const off = f * hop;
let sum = 0;
for (let i = 0; i < frameSize; i++) sum += samples[off + i] * samples[off + i];
env[f] = Math.sqrt(sum / frameSize);
}
return env;
}
// ── peakPick: reusable so the grid search can call it cheaply ─────
// Accepts a pre-computed, pre-normalised envelope (Float64Array).
// Returns onset timestamps in seconds.
// Factored out of the main path so the grid search can call it ~150
// times over the same pre-computed envelope without re-running FFT/RMS.
// backtrack mirrors librosa backtrack=True: snaps detected peak back
// to the nearest preceding local minimum (attack, not peak).
function peakPick(envelope, envLen, delta, wait, minGap, sr, HOP) {
// Adaptive threshold: mean + delta. delta from PARAMS (default 0.07 = ex_beats.py).
// Lower delta → more sensitive (catches quieter hits, more false positives).
// Higher delta → less sensitive (only strong transients pass).
let sum = 0;
for (let i = 0; i < envLen; i++) sum += envelope[i];
const threshold = (sum / envLen) + delta;
const rawOnsets = [];
let lastPeak = -wait - 1;
for (let i = 1; i < envLen - 1; i++) {
const v = envelope[i];
if (v > threshold && v >= envelope[i-1] && v >= envelope[i+1] && (i - lastPeak) >= wait) {
let snap = i;
for (let j = i-1; j >= Math.max(0, i-wait); j--) {
if (envelope[j] <= envelope[j+1]) snap = j; else break;
}
rawOnsets.push(snap);
lastPeak = i;
}
}
// Frames → seconds + de-dupe pass. minGap from PARAMS (default 0.12 = ex_beats.py MIN_GAP).
// Raise minGap to suppress double-triggers on long decaying hits.
const onsets = [];
for (const f of rawOnsets) {
const t = parseFloat(((f * HOP) / sr).toFixed(3));
if (!onsets.length || (t - onsets[onsets.length - 1]) >= minGap) onsets.push(t);
}
return onsets;
}
// ── scoreOnsets: unsupervised proxy quality score ────────
// Measures structural correctness of an onset array given a target count.
//
// The proxy objectives are imperfect. The count score is the strongest signal,
// the gap and coverage scores prevent degenerate solutions (e.g. a config that
// produces exactly 33 onsets all within the first 5 seconds scores high on count
// but low on coverage, getting regularized down). In practice this should find params
// that produce the right count with reasonable temporal spread, getting user within 200–500ms
// of correct for most lines. The remaining error is what the preview sliders handle.
// The fundamental ceiling doesn't change — without ground truth (actual lyrics timing) this is
// just an optimization for structural correctness (correct onset count/distribution),
// not semantic correctness (distinguish a drum hit from a vocal onset at the same time).
//
// Three components (weighted):