@mtldev514/retro-portfolio-engine 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +408 -0
  2. package/bin/cli.js +103 -0
  3. package/engine/admin/admin.css +720 -0
  4. package/engine/admin/admin.html +801 -0
  5. package/engine/admin/admin_api.py +230 -0
  6. package/engine/admin/scripts/backup.sh +116 -0
  7. package/engine/admin/scripts/config_loader.py +180 -0
  8. package/engine/admin/scripts/init.sh +141 -0
  9. package/engine/admin/scripts/manager.py +308 -0
  10. package/engine/admin/scripts/restore.sh +121 -0
  11. package/engine/admin/scripts/server.py +41 -0
  12. package/engine/admin/scripts/update.sh +321 -0
  13. package/engine/admin/scripts/validate_json.py +62 -0
  14. package/engine/fonts.css +37 -0
  15. package/engine/index.html +190 -0
  16. package/engine/js/config-loader.js +370 -0
  17. package/engine/js/config.js +173 -0
  18. package/engine/js/counter.js +17 -0
  19. package/engine/js/effects.js +97 -0
  20. package/engine/js/i18n.js +68 -0
  21. package/engine/js/init.js +107 -0
  22. package/engine/js/media.js +264 -0
  23. package/engine/js/render.js +282 -0
  24. package/engine/js/router.js +133 -0
  25. package/engine/js/sparkle.js +123 -0
  26. package/engine/js/themes.js +607 -0
  27. package/engine/style.css +2037 -0
  28. package/index.js +35 -0
  29. package/package.json +48 -0
  30. package/scripts/admin.js +67 -0
  31. package/scripts/build.js +142 -0
  32. package/scripts/init.js +237 -0
  33. package/scripts/post-install.js +16 -0
  34. package/scripts/serve.js +54 -0
  35. package/templates/user-portfolio/.github/workflows/deploy.yml +57 -0
  36. package/templates/user-portfolio/config/app.json +36 -0
  37. package/templates/user-portfolio/config/categories.json +241 -0
  38. package/templates/user-portfolio/config/languages.json +15 -0
  39. package/templates/user-portfolio/config/media-types.json +59 -0
  40. package/templates/user-portfolio/data/painting.json +3 -0
  41. package/templates/user-portfolio/data/projects.json +3 -0
  42. package/templates/user-portfolio/lang/en.json +114 -0
  43. package/templates/user-portfolio/lang/fr.json +114 -0
@@ -0,0 +1,607 @@
1
+ /**
2
+ * Theme Switcher for Alex's Portfolio
3
+ * Follows same pattern as i18n.js — global object, localStorage, dropdown
4
+ *
5
+ * Palettes:
6
+ * JR-16 — Retro 16-color: navy header, mint terminal, clean & bright
7
+ * Ambre Chaud — Dark CRT amber: deep chocolate bg, gold/amber glow
8
+ * Naples Yellow — Mediterranean warmth: cream & sand, terracotta, olive
9
+ * Béton — Dark brutalist grey: concrete & steel, cool monochrome
10
+ */
11
+ const themes = {
12
+ currentTheme: localStorage.getItem('selectedTheme') || 'ciment',
13
+
14
+ definitions: {
15
+
16
+ /* ──────────────────────────────────────────────
17
+ * JR-16 — Retro 16-color palette (Lospec)
18
+ * Cool & bright. Navy header, mint green terminal,
19
+ * off-white page, clean Win95 buttons.
20
+ * ────────────────────────────────────────────── */
21
+ jr16: {
22
+ name: 'JR-16',
23
+ emoji: '🌿',
24
+ colors: {
25
+ /* Page */
26
+ '--page-bg': '#e8e8f0',
27
+ '--container-bg': '#f5fffd',
28
+ '--text-primary': '#040404',
29
+ '--text-secondary': '#4b4956',
30
+ '--text-muted': '#7a7a88',
31
+ '--text-dim': '#4b4956',
32
+ '--text-light': '#a2a2ac',
33
+ /* Header */
34
+ '--header-bg': '#00374b',
35
+ '--header-text': '#f5fffd',
36
+ '--header-border-light': '#81f4b1',
37
+ '--header-border-dark': '#040404',
38
+ /* Borders */
39
+ '--border-light': '#f5fffd',
40
+ '--border-dark': '#4b4956',
41
+ '--border-groove': '#a2a2ac',
42
+ '--border-mid': '#7a7a88',
43
+ /* Buttons */
44
+ '--btn-bg-start': '#f5fffd',
45
+ '--btn-bg-mid': '#e8e8f0',
46
+ '--btn-bg-end': '#a2a2ac',
47
+ '--btn-hover-start': '#e0f8ef',
48
+ '--btn-hover-mid': '#f5fffd',
49
+ '--btn-hover-end': '#a2a2ac',
50
+ '--btn-active-start': '#a2a2ac',
51
+ '--btn-active-mid': '#4b4956',
52
+ '--btn-active-end': '#e8e8f0',
53
+ '--btn-text': '#040404',
54
+ '--btn-text-active': '#00374b',
55
+ /* Filter / Lang */
56
+ '--filter-bg': '#e8e8f0',
57
+ '--lang-btn-bg': '#a2a2ac',
58
+ '--lang-dropdown-bg': '#f5fffd',
59
+ /* Terminal */
60
+ '--term-bg': '#040404',
61
+ '--term-green': '#81f4b1',
62
+ '--term-green-dim': '#003808',
63
+ '--term-cyan': '#72ffff',
64
+ '--term-text': '#a2a2ac',
65
+ '--term-dim': '#4b4956',
66
+ '--term-titlebar': '#00374b',
67
+ '--term-titlebar-border': '#81f4b1',
68
+ '--term-btn-close': '#d76f6c',
69
+ '--term-btn-min': '#f6f8ad',
70
+ '--term-btn-max': '#81f4b1',
71
+ /* Marquee */
72
+ '--marquee-bg': '#040404',
73
+ '--marquee-text': '#81f4b1',
74
+ /* Winamp */
75
+ '--wp-bg': '#040404',
76
+ '--wp-surface': '#4b4956',
77
+ '--wp-display': '#040404',
78
+ '--wp-titlebar-start': '#00374b',
79
+ '--wp-titlebar-mid': '#520a54',
80
+ '--wp-playlist-bg': '#040404',
81
+ '--wp-btn-start': '#4b4956',
82
+ '--wp-btn-mid': '#00374b',
83
+ '--wp-btn-end': '#040404',
84
+ '--wp-btn-hover-start': '#a2a2ac',
85
+ '--wp-btn-hover-mid': '#4b4956',
86
+ '--wp-btn-hover-end': '#00374b',
87
+ '--wp-text': '#f6f8ad',
88
+ '--wp-green': '#81f4b1',
89
+ '--wp-info': '#72ffff',
90
+ '--wp-selected': '#00374b',
91
+ /* Music */
92
+ '--music-bg': '#040404',
93
+ '--music-gold': '#f6f8ad',
94
+ '--music-text': '#f5fffd',
95
+ '--music-text-dim': '#a2a2ac',
96
+ /* Gallery / Detail */
97
+ '--gallery-border': '#00374b',
98
+ '--detail-meta-bg': '#e8e8f0',
99
+ /* Badges */
100
+ '--badge-public-bg': '#e8e8f0',
101
+ '--badge-public-text': '#4a4a5e',
102
+ '--badge-private-bg': '#e8e8f0',
103
+ '--badge-private-text': '#4a4a5e',
104
+ /* Counter / Accents */
105
+ '--counter-text': '#d76f6c',
106
+ '--accent-navy': '#00374b',
107
+ '--accent-magenta': '#d887f0',
108
+ '--accent-yellow': '#f6f8ad',
109
+ /* Chrome / UI tones */
110
+ '--chrome-black': '#000',
111
+ '--chrome-darkest': '#111',
112
+ '--chrome-darker': '#222',
113
+ '--chrome-dark': '#333',
114
+ '--chrome-mid': '#444',
115
+ '--chrome-gray': '#555',
116
+ '--chrome-light': '#777',
117
+ '--chrome-border': '#999',
118
+ '--chrome-soft': '#ccc',
119
+ '--chrome-active': '#252525',
120
+ '--chrome-grip-alt': '#4a4a4a',
121
+ '--chrome-vol-start': '#060',
122
+ /* Sparkle */
123
+ '--sparkle-1': '#ffd700',
124
+ '--sparkle-2': '#ff69b4',
125
+ '--sparkle-3': '#00ffff',
126
+ '--sparkle-4': '#ff00ff',
127
+ '--sparkle-5': '#fff',
128
+ '--sparkle-6': '#7fff00',
129
+ '--sparkle-7': '#ff4500',
130
+ '--petal-color': '#88d8b0',
131
+ /* Mirror title — mint chrome */
132
+ '--mirror-shine': '#ffffff',
133
+ '--mirror-light': '#c0fad8',
134
+ '--mirror-mid': '#80d8a8'
135
+ }
136
+ },
137
+
138
+ /* ──────────────────────────────────────────────
139
+ * BÉTON — Dark brutalist grey
140
+ * Concrete & steel. Cool monochrome, no warmth.
141
+ * Like a rainy Montreal overpass at 6 AM.
142
+ * ────────────────────────────────────────────── */
143
+ beton: {
144
+ name: 'Béton',
145
+ emoji: '🌫️',
146
+ colors: {
147
+ /* Page — dark concrete */
148
+ '--page-bg': '#1a1a1e',
149
+ '--container-bg': '#242428',
150
+ '--text-primary': '#d8d8dc',
151
+ '--text-secondary': '#a0a0a8',
152
+ '--text-muted': '#707078',
153
+ '--text-dim': '#a0a0a8',
154
+ '--text-light': '#707078',
155
+ /* Header — charcoal steel */
156
+ '--header-bg': '#2e2e34',
157
+ '--header-text': '#e0e0e4',
158
+ '--header-border-light': '#58585e',
159
+ '--header-border-dark': '#0a0a0c',
160
+ /* Borders — cool grey 3D */
161
+ '--border-light': '#58585e',
162
+ '--border-dark': '#0a0a0c',
163
+ '--border-groove': '#3a3a40',
164
+ '--border-mid': '#48484e',
165
+ /* Buttons — steel gradient */
166
+ '--btn-bg-start': '#3a3a40',
167
+ '--btn-bg-mid': '#2e2e34',
168
+ '--btn-bg-end': '#1e1e22',
169
+ '--btn-hover-start': '#48484e',
170
+ '--btn-hover-mid': '#58585e',
171
+ '--btn-hover-end': '#3a3a40',
172
+ '--btn-active-start': '#1e1e22',
173
+ '--btn-active-mid': '#3a3a40',
174
+ '--btn-active-end': '#2e2e34',
175
+ '--btn-text': '#c0c0c8',
176
+ '--btn-text-active': '#e0e0e4',
177
+ /* Filter / Lang */
178
+ '--filter-bg': '#1e1e22',
179
+ '--lang-btn-bg': '#3a3a40',
180
+ '--lang-dropdown-bg': '#242428',
181
+ /* Terminal — white phosphor on black */
182
+ '--term-bg': '#0a0a0c',
183
+ '--term-green': '#c0c0c8',
184
+ '--term-green-dim': '#3a3a40',
185
+ '--term-cyan': '#e0e0e4',
186
+ '--term-text': '#707078',
187
+ '--term-dim': '#48484e',
188
+ '--term-titlebar': '#2e2e34',
189
+ '--term-titlebar-border': '#58585e',
190
+ '--term-btn-close': '#8a4040',
191
+ '--term-btn-min': '#8a8a50',
192
+ '--term-btn-max': '#4a7a4a',
193
+ /* Marquee — light on dark */
194
+ '--marquee-bg': '#0a0a0c',
195
+ '--marquee-text': '#a0a0a8',
196
+ /* Winamp — dark steel surfaces */
197
+ '--wp-bg': '#0a0a0c',
198
+ '--wp-surface': '#2e2e34',
199
+ '--wp-display': '#0a0a0c',
200
+ '--wp-titlebar-start': '#2e2e34',
201
+ '--wp-titlebar-mid': '#3a3a40',
202
+ '--wp-playlist-bg': '#0a0a0c',
203
+ '--wp-btn-start': '#2e2e34',
204
+ '--wp-btn-mid': '#1e1e22',
205
+ '--wp-btn-end': '#0a0a0c',
206
+ '--wp-btn-hover-start': '#48484e',
207
+ '--wp-btn-hover-mid': '#2e2e34',
208
+ '--wp-btn-hover-end': '#1e1e22',
209
+ '--wp-text': '#a0a0a8',
210
+ '--wp-green': '#c0c0c8',
211
+ '--wp-info': '#e0e0e4',
212
+ '--wp-selected': '#3a3a40',
213
+ /* Music */
214
+ '--music-bg': '#0a0a0c',
215
+ '--music-gold': '#a0a0a8',
216
+ '--music-text': '#d8d8dc',
217
+ '--music-text-dim': '#707078',
218
+ /* Gallery / Detail */
219
+ '--gallery-border': '#48484e',
220
+ '--detail-meta-bg': '#1e1e22',
221
+ /* Badges */
222
+ '--badge-public-bg': '#2a2a30',
223
+ '--badge-public-text': '#909098',
224
+ '--badge-private-bg': '#2a2a30',
225
+ '--badge-private-text': '#909098',
226
+ /* Counter / Accents */
227
+ '--counter-text': '#9a6a6a',
228
+ '--accent-navy': '#48484e',
229
+ '--accent-magenta': '#8a8a90',
230
+ '--accent-yellow': '#c0c0c8',
231
+ /* Chrome / UI tones — cold steel */
232
+ '--chrome-black': '#0a0a0c',
233
+ '--chrome-darkest': '#121214',
234
+ '--chrome-darker': '#1a1a1e',
235
+ '--chrome-dark': '#242428',
236
+ '--chrome-mid': '#2e2e34',
237
+ '--chrome-gray': '#3a3a40',
238
+ '--chrome-light': '#58585e',
239
+ '--chrome-border': '#707078',
240
+ '--chrome-soft': '#a0a0a8',
241
+ '--chrome-active': '#1a1a1e',
242
+ '--chrome-grip-alt': '#3a3a40',
243
+ '--chrome-vol-start': '#1a2a1a',
244
+ /* Glow overrides — no green */
245
+ '--marquee-glow': 'rgba(160,160,168,0.1)',
246
+ '--marquee-glow-text': 'rgba(160,160,168,0.3)',
247
+ '--term-glow': 'rgba(160,160,168,0.15)',
248
+ '--wp-glow-mid': 'rgba(192,192,200,0.15)',
249
+ '--wp-glow-soft': 'rgba(192,192,200,0.08)',
250
+ '--wp-glow-faint': 'rgba(192,192,200,0.04)',
251
+ /* Sparkle — cold silver */
252
+ '--sparkle-1': '#e0e0e4',
253
+ '--sparkle-2': '#a0a0a8',
254
+ '--sparkle-3': '#c0c0c8',
255
+ '--sparkle-4': '#707078',
256
+ '--sparkle-5': '#ffffff',
257
+ '--sparkle-6': '#8a8a90',
258
+ '--sparkle-7': '#58585e',
259
+ '--petal-color': '#d88ca0',
260
+ /* Mirror title — cold steel on dark */
261
+ '--mirror-shine': '#ffffff',
262
+ '--mirror-light': '#e0e0e8',
263
+ '--mirror-mid': '#b8b8c0'
264
+ }
265
+ },
266
+
267
+ /* ──────────────────────────────────────────────
268
+ * CIMENT — Light neutral grey
269
+ * Overcast sky, newsprint, pencil on paper.
270
+ * Béton's daytime sibling.
271
+ * ────────────────────────────────────────────── */
272
+ ciment: {
273
+ name: 'Ciment',
274
+ emoji: '🪨',
275
+ colors: {
276
+ /* Page — light concrete */
277
+ '--page-bg': '#d8d8dc',
278
+ '--container-bg': '#e8e8ec',
279
+ '--text-primary': '#1a1a1e',
280
+ '--text-secondary': '#48484e',
281
+ '--text-muted': '#70707a',
282
+ '--text-dim': '#48484e',
283
+ '--text-light': '#90909a',
284
+ /* Header — light concrete */
285
+ '--header-bg': '#9a9aa0',
286
+ '--header-text': '#e0e0e4',
287
+ '--header-border-light': '#b8b8be',
288
+ '--header-border-dark': '#70707a',
289
+ /* Borders — neutral 3D */
290
+ '--border-light': '#e8e8ec',
291
+ '--border-dark': '#90909a',
292
+ '--border-groove': '#b0b0b8',
293
+ '--border-mid': '#70707a',
294
+ /* Buttons — light steel gradient */
295
+ '--btn-bg-start': '#e8e8ec',
296
+ '--btn-bg-mid': '#d8d8dc',
297
+ '--btn-bg-end': '#b0b0b8',
298
+ '--btn-hover-start': '#f0f0f4',
299
+ '--btn-hover-mid': '#e0e0e4',
300
+ '--btn-hover-end': '#b0b0b8',
301
+ '--btn-active-start': '#b0b0b8',
302
+ '--btn-active-mid': '#70707a',
303
+ '--btn-active-end': '#d8d8dc',
304
+ '--btn-text': '#1a1a1e',
305
+ '--btn-text-active': '#000000',
306
+ /* Filter / Lang */
307
+ '--filter-bg': '#d0d0d4',
308
+ '--lang-btn-bg': '#b0b0b8',
309
+ '--lang-dropdown-bg': '#e8e8ec',
310
+ /* Terminal — dark on light */
311
+ '--term-bg': '#0e0e10',
312
+ '--term-green': '#b0b0b8',
313
+ '--term-green-dim': '#3a3a3e',
314
+ '--term-cyan': '#d0d0d4',
315
+ '--term-text': '#70707a',
316
+ '--term-dim': '#48484e',
317
+ '--term-titlebar': '#3a3a3e',
318
+ '--term-titlebar-border': '#70707a',
319
+ '--term-btn-close': '#8a4545',
320
+ '--term-btn-min': '#8a8a50',
321
+ '--term-btn-max': '#4a7a4a',
322
+ /* Marquee — dark on grey */
323
+ '--marquee-bg': '#0e0e10',
324
+ '--marquee-text': '#b0b0b8',
325
+ /* Winamp — dark surfaces (stays dark like other themes) */
326
+ '--wp-bg': '#0e0e10',
327
+ '--wp-surface': '#2a2a2e',
328
+ '--wp-display': '#0e0e10',
329
+ '--wp-titlebar-start': '#3a3a3e',
330
+ '--wp-titlebar-mid': '#48484e',
331
+ '--wp-playlist-bg': '#0e0e10',
332
+ '--wp-btn-start': '#2a2a2e',
333
+ '--wp-btn-mid': '#1a1a1e',
334
+ '--wp-btn-end': '#0e0e10',
335
+ '--wp-btn-hover-start': '#48484e',
336
+ '--wp-btn-hover-mid': '#2a2a2e',
337
+ '--wp-btn-hover-end': '#1a1a1e',
338
+ '--wp-text': '#b0b0b8',
339
+ '--wp-green': '#c0c0c8',
340
+ '--wp-info': '#d8d8dc',
341
+ '--wp-selected': '#3a3a3e',
342
+ /* Music */
343
+ '--music-bg': '#0e0e10',
344
+ '--music-gold': '#b0b0b8',
345
+ '--music-text': '#d8d8dc',
346
+ '--music-text-dim': '#70707a',
347
+ /* Gallery / Detail */
348
+ '--gallery-border': '#90909a',
349
+ '--detail-meta-bg': '#d0d0d4',
350
+ /* Badges */
351
+ '--badge-public-bg': '#e0e0e6',
352
+ '--badge-public-text': '#5a5a68',
353
+ '--badge-private-bg': '#e0e0e6',
354
+ '--badge-private-text': '#5a5a68',
355
+ /* Counter / Accents */
356
+ '--counter-text': '#7a3a3a',
357
+ '--accent-navy': '#505058',
358
+ '--accent-magenta': '#70707a',
359
+ '--accent-yellow': '#b0b0b8',
360
+ /* Chrome / UI tones — light steel */
361
+ '--chrome-black': '#0e0e10',
362
+ '--chrome-darkest': '#1a1a1e',
363
+ '--chrome-darker': '#2a2a2e',
364
+ '--chrome-dark': '#3a3a3e',
365
+ '--chrome-mid': '#48484e',
366
+ '--chrome-gray': '#58585e',
367
+ '--chrome-light': '#70707a',
368
+ '--chrome-border': '#90909a',
369
+ '--chrome-soft': '#b0b0b8',
370
+ '--chrome-active': '#2a2a2e',
371
+ '--chrome-grip-alt': '#48484e',
372
+ '--chrome-vol-start': '#2a3a2a',
373
+ /* Glow overrides — no green */
374
+ '--marquee-glow': 'rgba(180,180,190,0.1)',
375
+ '--marquee-glow-text': 'rgba(180,180,190,0.3)',
376
+ '--term-glow': 'rgba(180,180,190,0.15)',
377
+ '--wp-glow-mid': 'rgba(176,176,184,0.15)',
378
+ '--wp-glow-soft': 'rgba(176,176,184,0.08)',
379
+ '--wp-glow-faint': 'rgba(176,176,184,0.04)',
380
+ /* Sparkle — silver & pencil */
381
+ '--sparkle-1': '#48484e',
382
+ '--sparkle-2': '#90909a',
383
+ '--sparkle-3': '#b0b0b8',
384
+ '--sparkle-4': '#70707a',
385
+ '--sparkle-5': '#1a1a1e',
386
+ '--sparkle-6': '#d0d0d4',
387
+ '--sparkle-7': '#505058',
388
+ '--petal-color': '#d8a0b0',
389
+ /* Mirror title — pencil chrome on light header */
390
+ '--mirror-shine': '#ffffff',
391
+ '--mirror-light': '#e8e8f0',
392
+ '--mirror-mid': '#b8b8c8'
393
+ }
394
+ },
395
+
396
+ /* ──────────────────────────────────────────────
397
+ * BUBBLE GUM — Pastel pink & soft yellow
398
+ * Candy wrapper, strawberry milk, warm afternoons.
399
+ * Ciment's playful sibling.
400
+ * ────────────────────────────────────────────── */
401
+ bubblegum: {
402
+ name: 'Bubble Gum',
403
+ emoji: '🍬',
404
+ colors: {
405
+ /* Page — soft blush */
406
+ '--page-bg': '#f2dce4',
407
+ '--container-bg': '#fceef3',
408
+ '--text-primary': '#4a2838',
409
+ '--text-secondary': '#7a5068',
410
+ '--text-muted': '#a88898',
411
+ '--text-dim': '#7a5068',
412
+ '--text-light': '#c0a0b0',
413
+ /* Header — muted rose */
414
+ '--header-bg': '#c898a8',
415
+ '--header-text': '#fff4f8',
416
+ '--header-border-light': '#dbb8c8',
417
+ '--header-border-dark': '#a87888',
418
+ /* Borders — blush 3D */
419
+ '--border-light': '#f0dce6',
420
+ '--border-dark': '#d0b0bc',
421
+ '--border-groove': '#dcc0cc',
422
+ '--border-mid': '#c0a0b0',
423
+ /* Buttons — soft candy gradient */
424
+ '--btn-bg-start': '#fceef3',
425
+ '--btn-bg-mid': '#f4d8e4',
426
+ '--btn-bg-end': '#dcc0cc',
427
+ '--btn-hover-start': '#fff4f8',
428
+ '--btn-hover-mid': '#fae4ec',
429
+ '--btn-hover-end': '#ecd0dc',
430
+ '--btn-active-start': '#dcc0cc',
431
+ '--btn-active-mid': '#c0a0b0',
432
+ '--btn-active-end': '#f4d8e4',
433
+ '--btn-text': '#4a2838',
434
+ '--btn-text-active': '#3a1828',
435
+ /* Filter / Lang */
436
+ '--filter-bg': '#f4d8e4',
437
+ '--lang-btn-bg': '#dcc0cc',
438
+ '--lang-dropdown-bg': '#fceef3',
439
+ /* Terminal — dark on blush */
440
+ '--term-bg': '#201018',
441
+ '--term-green': '#d8a8c0',
442
+ '--term-green-dim': '#5a3048',
443
+ '--term-cyan': '#e8c8d8',
444
+ '--term-text': '#b890a8',
445
+ '--term-dim': '#6a4058',
446
+ '--term-titlebar': '#382030',
447
+ '--term-titlebar-border': '#6a4058',
448
+ '--term-btn-close': '#b86070',
449
+ '--term-btn-min': '#b8a060',
450
+ '--term-btn-max': '#60a078',
451
+ /* Marquee — dark on blush */
452
+ '--marquee-bg': '#201018',
453
+ '--marquee-text': '#d8a8c0',
454
+ /* Winamp — dark with blush accent */
455
+ '--wp-bg': '#201018',
456
+ '--wp-surface': '#302030',
457
+ '--wp-display': '#201018',
458
+ '--wp-titlebar-start': '#382030',
459
+ '--wp-titlebar-mid': '#503848',
460
+ '--wp-playlist-bg': '#201018',
461
+ '--wp-btn-start': '#302030',
462
+ '--wp-btn-mid': '#281828',
463
+ '--wp-btn-end': '#201018',
464
+ '--wp-btn-hover-start': '#503848',
465
+ '--wp-btn-hover-mid': '#382030',
466
+ '--wp-btn-hover-end': '#281828',
467
+ '--wp-text': '#d8a8c0',
468
+ '--wp-green': '#e8c0d0',
469
+ '--wp-info': '#f0dce6',
470
+ '--wp-selected': '#382030',
471
+ /* Music */
472
+ '--music-bg': '#201018',
473
+ '--music-gold': '#d8a8c0',
474
+ '--music-text': '#f0dce6',
475
+ '--music-text-dim': '#a88898',
476
+ /* Gallery / Detail */
477
+ '--gallery-border': '#d0b0bc',
478
+ '--detail-meta-bg': '#f4d8e4',
479
+ /* Badges */
480
+ '--badge-public-bg': '#f0e0e8',
481
+ '--badge-public-text': '#6a4858',
482
+ '--badge-private-bg': '#f0e0e8',
483
+ '--badge-private-text': '#6a4858',
484
+ /* Counter / Accents */
485
+ '--counter-text': '#b86878',
486
+ '--accent-navy': '#6a4060',
487
+ '--accent-magenta': '#d0a0b8',
488
+ '--accent-yellow': '#f0e4b8',
489
+ /* Chrome / UI tones — blush steel */
490
+ '--chrome-black': '#201018',
491
+ '--chrome-darkest': '#281828',
492
+ '--chrome-darker': '#302030',
493
+ '--chrome-dark': '#382030',
494
+ '--chrome-mid': '#503848',
495
+ '--chrome-gray': '#684860',
496
+ '--chrome-light': '#a88898',
497
+ '--chrome-border': '#d0b0bc',
498
+ '--chrome-soft': '#dcc0cc',
499
+ '--chrome-active': '#302030',
500
+ '--chrome-grip-alt': '#503848',
501
+ '--chrome-vol-start': '#303030',
502
+ /* Glow overrides — soft pink */
503
+ '--marquee-glow': 'rgba(200,160,180,0.1)',
504
+ '--marquee-glow-text': 'rgba(200,160,180,0.3)',
505
+ '--term-glow': 'rgba(200,160,180,0.15)',
506
+ '--wp-glow-mid': 'rgba(200,160,180,0.15)',
507
+ '--wp-glow-soft': 'rgba(200,160,180,0.08)',
508
+ '--wp-glow-faint': 'rgba(200,160,180,0.04)',
509
+ /* Sparkle — soft candy */
510
+ '--sparkle-1': '#d888a8',
511
+ '--sparkle-2': '#e8b0c8',
512
+ '--sparkle-3': '#f0d8a0',
513
+ '--sparkle-4': '#c8a0b8',
514
+ '--sparkle-5': '#fff4f8',
515
+ '--sparkle-6': '#e8d0a0',
516
+ '--sparkle-7': '#b87898',
517
+ '--petal-color': '#dca0b8',
518
+ /* Mirror title — candy chrome */
519
+ '--mirror-shine': '#ffffff',
520
+ '--mirror-light': '#ffe0ec',
521
+ '--mirror-mid': '#f0b0c8'
522
+ }
523
+ }
524
+ },
525
+
526
+ init() {
527
+ this.applyTheme(this.currentTheme);
528
+ this.updateSwitcherUI();
529
+ },
530
+
531
+ applyTheme(themeId) {
532
+ const theme = this.definitions[themeId];
533
+ if (!theme) return;
534
+
535
+ const root = document.documentElement;
536
+
537
+ if (themeId === 'jr16') {
538
+ // JR-16 is the CSS default — remove overrides so :root values take effect
539
+ Object.keys(theme.colors).forEach(prop => {
540
+ root.style.removeProperty(prop);
541
+ });
542
+ } else {
543
+ // Clear any previous theme overrides first
544
+ const defaultKeys = Object.keys(this.definitions.jr16.colors);
545
+ defaultKeys.forEach(prop => root.style.removeProperty(prop));
546
+ // Apply new theme
547
+ Object.entries(theme.colors).forEach(([prop, value]) => {
548
+ root.style.setProperty(prop, value);
549
+ });
550
+ }
551
+
552
+ this.currentTheme = themeId;
553
+ localStorage.setItem('selectedTheme', themeId);
554
+
555
+ // Force h1 chrome gradient to re-render with new --mirror-* values
556
+ // Browsers cache background-clip:text gradients; re-assign background inline
557
+ const h1 = document.querySelector('h1');
558
+ if (h1) {
559
+ const cs = getComputedStyle(document.documentElement);
560
+ const shine = cs.getPropertyValue('--mirror-shine').trim();
561
+ const light = cs.getPropertyValue('--mirror-light').trim();
562
+ const mid = cs.getPropertyValue('--mirror-mid').trim();
563
+ h1.style.background = `linear-gradient(135deg, ${shine} 0%, ${light} 25%, ${mid} 50%, ${light} 75%, ${shine} 100%)`;
564
+ h1.style.webkitBackgroundClip = 'text';
565
+ h1.style.backgroundClip = 'text';
566
+ }
567
+
568
+ // Update sparkle colors for the new theme
569
+ if (window.sparkle) sparkle.refreshColors();
570
+
571
+ // Toggle petal rain for Béton theme
572
+ this.togglePetals(themeId);
573
+ },
574
+
575
+ changeTheme(themeId) {
576
+ this.applyTheme(themeId);
577
+ this.updateSwitcherUI();
578
+ },
579
+
580
+ updateSwitcherUI() {
581
+ // Gear button is static — no UI update needed
582
+ },
583
+
584
+ togglePetals() {
585
+ const existing = document.getElementById('petal-rain');
586
+ if (existing) existing.remove();
587
+
588
+ const container = document.createElement('div');
589
+ container.id = 'petal-rain';
590
+ const count = 60;
591
+ for (let i = 0; i < count; i++) {
592
+ const petal = document.createElement('span');
593
+ petal.className = 'petal';
594
+ const size = 6 + Math.floor(Math.random() * 6);
595
+ petal.style.width = size + 'px';
596
+ petal.style.height = (size * 1.4) + 'px';
597
+ petal.style.left = Math.random() * 100 + '%';
598
+ petal.style.animationDuration = (8 + Math.random() * 12) + 's';
599
+ petal.style.animationDelay = -(Math.random() * 20) + 's';
600
+ petal.style.opacity = 0.2 + Math.random() * 0.2;
601
+ container.appendChild(petal);
602
+ }
603
+ document.body.appendChild(container);
604
+ }
605
+ };
606
+
607
+ window.themes = themes;