alexrsworld 1.0.0 → 1.0.2

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 (45) hide show
  1. package/Apps/codeeditor.html +163 -0
  2. package/Games/WAflash/vex.html +1 -1
  3. package/Games/emulated/GBA/mariokartsupercircuit.html +30 -0
  4. package/Games/emulated/GBA/mother.html +30 -0
  5. package/Games/emulated/GBA/mother3.html +30 -0
  6. package/Games/emulated/GBA/pacmanworld.html +30 -0
  7. package/Games/emulated/GBA/sonicadvance.html +30 -0
  8. package/Games/emulated/GBA/sonicadvance2.html +30 -0
  9. package/Games/emulated/GBA/sonicadvance3.html +30 -0
  10. package/Games/emulated/GBA/superstarsaga.html +30 -0
  11. package/Games/emulated/N64/majorasmask.html +31 -0
  12. package/Games/emulated/N64/ocarinaoftime.html +31 -0
  13. package/Games/emulated/SNES/donkeykongcountry.html +31 -0
  14. package/Games/emulated/SNES/donkeykongcountry2.html +30 -0
  15. package/Games/emulated/SNES/donkeykongcountry3.html +31 -0
  16. package/Games/emulated/SNES/linktothepast.html +31 -0
  17. package/Games/ruffle/bcubed.html +40 -0
  18. package/Games/ruffle/hobo.html +41 -0
  19. package/Games/ruffle/maxdirtbike.html +40 -0
  20. package/Games/ruffle/raftwars2.html +40 -0
  21. package/Games/ruffle/useboxmen.html +40 -0
  22. package/Games/ruffle/whackyourboss.html +40 -0
  23. package/Games/ruffle/whackyourex.html +40 -0
  24. package/Games/singlefile.html +2322 -54
  25. package/Games/standalone/amongus.html +1 -0
  26. package/Games/standalone/bendy.html +141 -0
  27. package/Games/standalone/chess.html +204 -0
  28. package/Games/standalone/gdremastered.html +247 -0
  29. package/Games/standalone/highwayracer3D.html +50 -0
  30. package/Games/standalone/omnomrun.html +45 -0
  31. package/Games/standalone/retrobowlcollege.html +0 -65
  32. package/Games/standalone/slope2player.html +76 -0
  33. package/Games/standalone/soniccd.html +229 -0
  34. package/Games/standalone/station141.html +25 -8
  35. package/Games/standalone/tanukisunset.html +19 -0
  36. package/Games/standalone/ultrakill.html +108 -0
  37. package/games.json +130 -3
  38. package/iframetrue.json +2622 -0
  39. package/index.html +298 -147
  40. package/package.json +2 -2
  41. package/singlefilegames.json +7 -0
  42. package/singleiframetrue.json +2581 -0
  43. package/start.html +25 -0
  44. package/test.html +132 -0
  45. package/port.html +0 -0
@@ -1,67 +1,2335 @@
1
- ]<!DOCTYPE html>
1
+ <!DOCTYPE html>
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8">
5
- <title>Game Loader</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Alexr's World</title>
7
+ <link rel="icon" href="https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/new%20logo.png">
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+
11
+ :root {
12
+ --ps-blue: #00439c;
13
+ --ps-light-blue: #00a0e9;
14
+ --text-white: #e3f2fd;
15
+ --card-size: 130px;
16
+ --card-selected: 180px;
17
+ --anim-speed: 0.25s;
18
+ }
19
+
20
+ * { box-sizing: border-box; user-select: none; }
21
+
22
+ body {
23
+ margin: 0;
24
+ padding: 0;
25
+ background-color: #003791;
26
+ background-image:
27
+ radial-gradient(circle at 50% 0%, rgba(255,255,255,0.1) 0%, transparent 60%),
28
+ linear-gradient(110deg, #003791 0%, #005c9e 50%, #003791 100%);
29
+ background-size: 200% 200%;
30
+ color: white;
31
+ font-family: "SST", "Segoe UI", sans-serif;
32
+ height: 100vh;
33
+ overflow: hidden;
34
+ animation: bgFlow 15s ease infinite;
35
+ background-position: 0% 50%;
36
+ background-repeat: no-repeat;
37
+ }
38
+
39
+
40
+ body.theme-default {
41
+ background-color: #003791;
42
+ background-image:
43
+ radial-gradient(circle at 50% 0%, rgba(255,255,255,0.1) 0%, transparent 60%),
44
+ linear-gradient(110deg, #003791 0%, #005c9e 50%, #003791 100%);
45
+ --ps-light-blue: #00a0e9;
46
+ }
47
+
48
+ body.theme-dark {
49
+ background-color: #050816;
50
+ background-image: radial-gradient(circle at 0 0, #222 0%, transparent 55%),
51
+ radial-gradient(circle at 100% 100%, #111 0%, transparent 55%),
52
+ linear-gradient(135deg,#050816,#000000);
53
+ --ps-light-blue: #999999;
54
+ }
55
+
56
+
57
+ body.theme-red {
58
+ background-color: #3a0000 !important;
59
+ background-image:
60
+ radial-gradient(circle at 20% 0, rgba(255,80,80,0.25) 0%, transparent 55%),
61
+ radial-gradient(circle at 80% 100%, rgba(180,0,0,0.35) 0%, transparent 55%),
62
+ linear-gradient(135deg, #3a0000, #7a0000) !important;
63
+ --ps-light-blue: #ff4d4d !important;
64
+ }
65
+ body.theme-red .card {
66
+ background: rgba(60,0,0,0.6) !important;
67
+ }
68
+ body.theme-red .card.active {
69
+ box-shadow: 0 0 0 4px #ff4d4d, 0 10px 20px rgba(0,0,0,0.8) !important;
70
+ }
71
+
72
+ body.theme-blue-dark {
73
+ background-color: #020b1a;
74
+ background-image: linear-gradient(135deg,#020b1a,#012b45);
75
+ --ps-light-blue: #44aaff;
76
+ }
77
+
78
+
79
+ body.theme-hacker {
80
+ background-color: #000000;
81
+ background-image: radial-gradient(circle at 20% 0, rgba(0,255,0,0.25) 0%, transparent 55%),
82
+ radial-gradient(circle at 80% 100%, rgba(0,255,0,0.25) 0%, transparent 55%);
83
+ color: #00ff88;
84
+ --ps-light-blue: #00ff88;
85
+ }
86
+
87
+
88
+ body.theme-tokyo {
89
+ background-color: #050016;
90
+ background-image: radial-gradient(circle at 10% 0, rgba(255,0,128,0.35) 0%, transparent 55%),
91
+ radial-gradient(circle at 90% 100%, rgba(0,255,255,0.35) 0%, transparent 55%),
92
+ linear-gradient(135deg,#050016,#001133);
93
+ color: #e3f2fd;
94
+ --ps-light-blue: #ff4aff;
95
+ }
96
+
97
+
98
+ body.has-custom-bg {
99
+ background-size: cover;
100
+ background-position: center center;
101
+ background-repeat: no-repeat;
102
+ animation: none;
103
+ }
104
+
105
+
106
+ .game-bg-overlay {
107
+ position: fixed;
108
+ top: 0;
109
+ left: 0;
110
+ width: 100%;
111
+ height: 100%;
112
+ z-index: -1;
113
+ opacity: 0;
114
+ transition: opacity 0.6s ease;
115
+ pointer-events: none;
116
+ background-size: cover;
117
+ background-position: center;
118
+ background-repeat: no-repeat;
119
+ filter: blur(20px) brightness(0.4);
120
+ transform: scale(1.1);
121
+ }
122
+
123
+ body.ps5-bg-enabled .game-bg-overlay.active {
124
+ opacity: 1;
125
+ }
126
+
127
+ @keyframes bgFlow {
128
+ 0% { background-position: 0% 50%; }
129
+ 50% { background-position: 100% 50%; }
130
+ 100% { background-position: 0% 50%; }
131
+ }
132
+
133
+
134
+ .top-bar {
135
+ display: flex;
136
+ justify-content: space-between;
137
+ align-items: center;
138
+ padding: 30px 60px;
139
+ font-size: 1.5rem;
140
+ opacity: 0.9;
141
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
142
+ position: relative;
143
+ }
144
+
145
+
146
+ .top-bar.hidden {
147
+ display: none !important;
148
+ opacity: 0 !important;
149
+ transform: translateY(-6px);
150
+ transition: opacity 0.15s ease, transform 0.12s ease;
151
+ }
152
+
153
+
154
+ .site-title-group { display: inline-block; }
155
+
156
+ .site-title {
157
+ font-weight: 300;
158
+ font-size: 1.8rem;
159
+ letter-spacing: 1px;
160
+ display: flex;
161
+ align-items: center;
162
+ }
163
+
164
+ .site-title img {
165
+ height: 30px;
166
+ margin-right: 10px;
167
+ }
168
+
169
+
170
+ .nav-hint {
171
+ position: absolute;
172
+ left: 50%;
173
+ top: 50%;
174
+ transform: translate(-50%, -50%);
175
+ z-index: 5;
176
+
177
+ font-size: 0.85rem;
178
+ font-weight: 300;
179
+ color: rgba(255, 255, 255, 0.7);
180
+
181
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
182
+ white-space: nowrap;
183
+ }
184
+
185
+ .clock-area {
186
+ font-size: 1.2rem;
187
+ font-weight: 300;
188
+ display: flex;
189
+ align-items: center;
190
+ gap: 20px;
191
+ }
192
+
193
+
194
+ .social-icons a {
195
+ color: inherit;
196
+ transition: color 0.2s;
197
+ }
198
+ .social-icons a:hover {
199
+ color: var(--ps-light-blue);
200
+ }
201
+ .social-icons {
202
+ display: flex;
203
+ gap: 15px;
204
+ font-size: 1.4rem;
205
+ color: white;
206
+ }
207
+ body.theme-hacker .social-icons { color: #00ff88; }
208
+ body.theme-tokyo .social-icons { color: #ff4aff; }
209
+ body.theme-red .social-icons { color: var(--ps-light-blue); }
210
+
211
+
212
+ .main-stage {
213
+ margin-top: 40px;
214
+ width: 100%;
215
+ position: relative;
216
+ }
217
+
218
+ .carousel-container {
219
+ display: flex;
220
+ align-items: center;
221
+ padding-left: 60px;
222
+ gap: 15px;
223
+ transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
224
+ will-change: transform;
225
+ }
226
+
227
+ .card {
228
+ width: var(--card-size);
229
+ height: var(--card-size);
230
+ background: rgba(0,0,0,0.3);
231
+ flex-shrink: 0;
232
+ position: relative;
233
+ transition: all var(--anim-speed) ease;
234
+ display: flex;
235
+ flex-direction: column;
236
+ justify-content: flex-end;
237
+ overflow: visible;
238
+ cursor: pointer;
239
+ box-shadow: 0 4px 10px rgba(0,0,0,0.3);
240
+ }
241
+
242
+ body.theme-hacker .card {
243
+ background: rgba(0,20,0,0.7);
244
+ }
245
+ body.theme-tokyo .card {
246
+ background: rgba(10,0,30,0.7);
247
+ }
248
+
249
+ .card img {
250
+ width: 100%;
251
+ height: 100%;
252
+ object-fit: cover;
253
+ opacity: 0.8;
254
+ transition: opacity 0.2s;
255
+ }
256
+
257
+
258
+ .card.system-card {
259
+ background: linear-gradient(135deg, #0b407a, #001f4d);
260
+ display: flex;
261
+ align-items: center;
262
+ justify-content: center;
263
+ border: 1px solid rgba(255,255,255,0.1);
264
+ }
265
+ body.theme-hacker .card.system-card {
266
+ background: linear-gradient(135deg,#004400,#001400);
267
+ border: 1px solid rgba(0,255,136,0.3);
268
+ }
269
+ body.theme-tokyo .card.system-card {
270
+ background: linear-gradient(135deg,#ff007a, #3b00b3);
271
+ border: 1px solid rgba(255,74,255,0.3);
272
+ }
273
+ body.theme-red .card.system-card {
274
+ background: linear-gradient(135deg,#7a0000, #3a0000);
275
+ border: 1px solid rgba(255,77,77,0.3);
276
+ }
277
+
278
+ .card.system-card i { font-size: 3rem; color: white; opacity: 0.8; }
279
+ body.theme-hacker .card.system-card i,
280
+ body.theme-tokyo .card.system-card i,
281
+ body.theme-red .card.system-card i {
282
+ color: inherit;
283
+ opacity: 1;
284
+ }
285
+
286
+
287
+ .card.active {
288
+ width: var(--card-selected);
289
+ height: var(--card-selected);
290
+ transform: translateY(10px);
291
+ background: rgba(255,255,255,0.1);
292
+ box-shadow: 0 0 0 4px white, 0 10px 20px rgba(0,0,0,0.5);
293
+ z-index: 10;
294
+ }
295
+
296
+ body.theme-hacker .card.active {
297
+ box-shadow: 0 0 0 4px #00ff88, 0 10px 20px rgba(0,0,0,0.8);
298
+ }
299
+ body.theme-tokyo .card.active {
300
+ box-shadow: 0 0 0 4px var(--ps-light-blue), 0 10px 20px rgba(0,0,0,0.8);
301
+ }
302
+
303
+ .card.active img { opacity: 1; }
304
+
305
+
306
+ .card.dragging {
307
+ opacity: 0.5;
308
+ transform: translateY(10px) scale(0.9);
309
+ }
310
+ .card.drag-over {
311
+
312
+ box-shadow: 0 0 0 4px var(--ps-light-blue), 0 10px 20px rgba(0,0,0,0.8) !important;
313
+ transform: translateY(10px) scale(1.1);
314
+ }
315
+
316
+
317
+ .info-area {
318
+ margin-top: 80px;
319
+ padding-left: 60px;
320
+ opacity: 0;
321
+ transform: translateY(20px);
322
+ transition: all 0.3s ease;
323
+ }
324
+
325
+ .info-area.visible {
326
+ opacity: 1;
327
+ transform: translateY(0);
328
+ }
329
+
330
+
331
+ .sub-content.hidden {
332
+ display: none !important;
333
+ }
334
+
335
+ .game-title {
336
+ font-size: 2.2rem;
337
+ font-weight: 300;
338
+ margin-bottom: 5px;
339
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.6);
340
+ }
341
+
342
+ .game-meta {
343
+ display: flex;
344
+ gap: 15px;
345
+ font-size: 0.9rem;
346
+ color: rgba(255,255,255,0.8);
347
+ margin-bottom: 25px;
348
+ text-transform: uppercase;
349
+ letter-spacing: 1px;
350
+ }
351
+
352
+
353
+ .sub-content {
354
+ display: flex;
355
+ gap: 20px;
356
+ margin-top: 20px;
357
+ }
358
+
359
+ .sub-tile {
360
+ width: 300px;
361
+ height: 160px;
362
+ background: rgba(0,0,0,0.4);
363
+ display: flex;
364
+ flex-direction: column;
365
+ justify-content: flex-end;
366
+ padding: 15px;
367
+ position: relative;
368
+ overflow: hidden;
369
+ border-radius: 4px;
370
+ }
371
+ .sub-tile img {
372
+ position: absolute; top:0; left:0; width:100%; height:100%; object-fit: cover; opacity: 0.5; z-index: 0;
373
+ }
374
+ .sub-tile div { z-index: 1; position: relative; text-shadow: 1px 1px 2px black; }
375
+ .sub-head { font-weight: bold; font-size: 1.1rem; }
376
+ .sub-desc { font-size: 0.8rem; color: #ccc; }
377
+
378
+ #favoriteTile {
379
+ cursor: pointer;
380
+ }
381
+
382
+
383
+ .library-overlay {
384
+ position: fixed;
385
+ inset: 0;
386
+ background: rgba(11, 23, 48, 0.98);
387
+ z-index: 1000;
388
+ display: none;
389
+ flex-direction: column;
390
+ padding: 40px 80px;
391
+ overflow-y: auto;
392
+ opacity: 0;
393
+ transition: opacity 0.3s ease;
394
+ }
395
+
396
+ body.theme-hacker .library-overlay {
397
+ background: rgba(0,0,0,0.96);
398
+ }
399
+ body.theme-tokyo .library-overlay {
400
+ background: rgba(5,0,25,0.96);
401
+ }
402
+ body.theme-red .library-overlay {
403
+ background: rgba(58,0,0,0.96);
404
+ }
405
+
406
+ .library-overlay.show { display: flex; opacity: 1; }
407
+
408
+ .lib-header {
409
+ display: flex;
410
+ justify-content: space-between;
411
+ align-items: center;
412
+ margin-bottom: 20px;
413
+ border-bottom: 1px solid rgba(255,255,255,0.1);
414
+ padding-bottom: 20px;
415
+ }
416
+ .lib-title { font-size: 2rem; font-weight: 300; }
417
+ .close-btn {
418
+ background: none; border: 1px solid white; color: white;
419
+ padding: 10px 20px; cursor: pointer; border-radius: 4px; font-size: 1rem;
420
+ transition: background 0.2s;
421
+ }
422
+ .close-btn:hover { background: white; color: black; }
423
+
424
+
425
+ .lib-filters {
426
+ margin-bottom: 30px;
427
+ }
428
+
429
+ .lib-search {
430
+ width: 100%;
431
+ padding: 15px;
432
+ margin-bottom: 15px;
433
+ font-size: 1.2rem;
434
+ border: 2px solid rgba(255,255,255,0.5);
435
+ background: rgba(0,0,0,0.2);
436
+ color: white;
437
+ border-radius: 4px;
438
+ outline: none;
439
+ transition: border-color 0.2s;
440
+ }
441
+ .lib-search:focus {
442
+ border-color: var(--ps-light-blue);
443
+ }
444
+
445
+ .lib-categories {
446
+ display: flex;
447
+ gap: 10px;
448
+ flex-wrap: wrap;
449
+ }
450
+
451
+ .category-btn {
452
+ background: rgba(255,255,255,0.1);
453
+ border: 1px solid rgba(255,255,255,0.3);
454
+ color: white;
455
+ padding: 8px 16px;
456
+ cursor: pointer;
457
+ border-radius: 20px;
458
+ font-size: 0.9rem;
459
+ transition: background 0.2s, color 0.2s, border-color 0.2s;
460
+ }
461
+
462
+ .category-btn.active {
463
+ background: var(--ps-light-blue);
464
+ border-color: var(--ps-light-blue);
465
+ font-weight: bold;
466
+ }
467
+ .category-btn:hover {
468
+ background: rgba(255,255,255,0.3);
469
+ }
470
+
471
+
472
+
473
+ .lib-grid {
474
+ display: grid;
475
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
476
+ gap: 25px;
477
+ }
478
+
479
+ .lib-card {
480
+ aspect-ratio: 1/1;
481
+ background: #222;
482
+ border-radius: 4px;
483
+ position: relative;
484
+ cursor: pointer;
485
+ transition: transform 0.2s;
486
+ overflow: hidden;
487
+ }
488
+
489
+ /* Favorite button for library cards */
490
+ .lib-fav-btn {
491
+ position: absolute;
492
+ top: 8px;
493
+ right: 8px;
494
+ background: rgba(0,0,0,0.45);
495
+ border: 1px solid rgba(255,255,255,0.12);
496
+ color: white;
497
+ padding: 6px;
498
+ border-radius: 6px;
499
+ cursor: pointer;
500
+ display: flex;
501
+ align-items: center;
502
+ justify-content: center;
503
+ z-index: 4;
504
+ }
505
+ .lib-fav-btn i { pointer-events: none; font-size: 0.9rem; }
506
+ .lib-fav-btn.favorited {
507
+ background: var(--ps-light-blue);
508
+ color: #000;
509
+ border-color: rgba(0,0,0,0.1);
510
+ }
511
+
512
+ @media (max-width: 600px) {
513
+ .lib-fav-btn { padding: 8px; top: 10px; right: 10px; }
514
+ .lib-fav-btn i { font-size: 1.05rem; }
515
+ }
516
+ body.theme-hacker .lib-card { background: #011; box-shadow: none; }
517
+ body.theme-tokyo .lib-card { background: #102; box-shadow: none; }
518
+ body.theme-red .lib-card { background: #200; box-shadow: none; }
519
+
520
+ .lib-card:hover { transform: scale(1.05); box-shadow: 0 5px 15px rgba(0,0,0,0.5); z-index: 2;}
521
+ .lib-card img { width: 100%; height: 100%; object-fit: cover; }
522
+ .lib-card-title {
523
+ position: absolute; bottom: 0; left: 0; right: 0;
524
+ background: rgba(0,0,0,0.8); padding: 8px; font-size: 0.85rem;
525
+ text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
526
+ }
527
+
528
+ .game-modal {
529
+ position: fixed !important;
530
+ top: 0 !important;
531
+ left: 0 !important;
532
+ width: 100vw !important;
533
+ height: 100vh !important;
534
+ margin: 0 !important;
535
+ padding: 0 !important;
536
+ background: black !important;
537
+ z-index: 9999 !important;
538
+ display: flex !important;
539
+ flex-direction: column !important;
540
+ border: none !important;
541
+ box-sizing: border-box !important;
542
+ }
543
+
544
+ .game-modal > div:first-child {
545
+ position: relative !important;
546
+ height: 40px !important;
547
+ flex-shrink: 0 !important;
548
+ z-index: 10000 !important;
549
+ background: #222 !important;
550
+ display: flex !important;
551
+ align-items: center !important;
552
+ padding: 0 20px !important;
553
+ justify-content: space-between !important;
554
+ color: white !important;
555
+ gap: 10px !important;
556
+ }
557
+
558
+ body.theme-hacker .game-modal > div:first-child { background: #000 !important; color: #00ff88 !important; }
559
+ body.theme-tokyo .game-modal > div:first-child { background: #111 !important; color: #ff4aff !important; }
560
+ body.theme-red .game-modal > div:first-child { background: #3a0000 !important; color: var(--ps-light-blue) !important; }
561
+
562
+ .game-modal > div:last-child {
563
+ position: relative !important;
564
+ flex: 1 !important;
565
+ width: 100% !important;
566
+ height: calc(100vh - 40px) !important;
567
+ }
568
+
569
+ .game-modal iframe {
570
+ position: absolute !important;
571
+ top: 0 !important;
572
+ left: 0 !important;
573
+ width: 100% !important;
574
+ height: 100% !important;
575
+ border: none !important;
576
+ }
577
+
578
+ .game-modal button,
579
+ .game-modal .close-btn {
580
+
581
+ background: rgba(255,255,255,0.2) !important;
582
+ border: 1px solid currentColor !important;
583
+ color: currentColor !important;
584
+ padding: 4px 12px !important;
585
+ border-radius: 4px !important;
586
+ cursor: pointer !important;
587
+ font-size: 0.85rem !important;
588
+ opacity: 1 !important;
589
+ visibility: visible !important;
590
+ display: inline-block !important;
591
+ z-index: 10001 !important;
592
+ }
593
+
594
+ .game-modal button:hover {
595
+ background: white !important;
596
+ color: black !important;
597
+ }
598
+
599
+ .game-modal .top-bar {
600
+ position: relative !important;
601
+ z-index: 10000 !important;
602
+ transition: opacity 0.2s ease !important;
603
+ }
604
+
605
+ .game-modal .show-bar-btn {
606
+ position: absolute !important;
607
+ top: 5px !important;
608
+ left: 5px !important;
609
+ z-index: 10001 !important;
610
+ width: 36px !important;
611
+ height: 36px !important;
612
+ padding: 0 !important;
613
+ border: 1px solid #666 !important;
614
+ background: rgba(0,0,0,0.9) !important;
615
+ color: white !important;
616
+ border-radius: 6px !important;
617
+ font-size: 1.2rem !important;
618
+ font-weight: bold !important;
619
+ cursor: pointer !important;
620
+ display: none !important;
621
+ line-height: 1 !important;
622
+ }
623
+
624
+ .game-modal > div:first-child[style*="display: none"] ~ div .show-bar-btn {
625
+ display: block !important;
626
+ }
627
+
628
+
629
+ .game-modal > div:first-child.hidden {
630
+ display: none !important;
631
+ opacity: 0 !important;
632
+ height: 0 !important;
633
+ padding: 0 !important;
634
+ }
635
+ .game-modal > div:first-child.hidden ~ div .show-bar-btn {
636
+ display: block !important;
637
+ }
638
+ .restore-bar-btn {
639
+ position: fixed;
640
+ top: 8px;
641
+ left: 50%;
642
+ transform: translateX(-50%) translateY(-10px);
643
+ z-index: 10002;
644
+ background: rgba(0,0,0,0.7);
645
+ color: white;
646
+ border: 1px solid rgba(255,255,255,0.08);
647
+ border-radius: 20px;
648
+ padding: 6px 12px;
649
+ font-size: 1.1rem;
650
+ cursor: pointer;
651
+ box-shadow: 0 6px 20px rgba(0,0,0,0.45);
652
+ opacity: 0;
653
+ transition: transform 0.18s ease, opacity 0.18s ease;
654
+ }
655
+ .restore-bar-btn.visible {
656
+ opacity: 1;
657
+ transform: translateX(-50%) translateY(0);
658
+ }
659
+ .restore-bar-btn:hover { transform: translateX(-50%) translateY(0) scale(1.03); }
660
+
661
+
662
+ .other-tabs {
663
+ display:flex; gap:10px; margin-bottom:20px;
664
+ flex-wrap: wrap;
665
+ }
666
+
667
+ .tab-btn {
668
+ background: none;
669
+ border: 1px solid white;
670
+ color: white;
671
+ padding: 8px 16px;
672
+ cursor: pointer;
673
+ border-radius: 4px;
674
+ font-size: 0.9rem;
675
+ transition: background 0.2s, color 0.2s;
676
+ }
677
+
678
+ .tab-btn.active,
679
+ .tab-btn:hover {
680
+ background: white;
681
+ color: black;
682
+ }
683
+
684
+
685
+ #bgUploadInput {
686
+ display:none;
687
+ }
688
+
689
+
690
+ .sound-control {
691
+ display: flex;
692
+ align-items: center;
693
+ gap: 15px;
694
+ margin-top: 15px;
695
+ }
696
+
697
+ .switch {
698
+ position: relative;
699
+ display: inline-block;
700
+ width: 60px;
701
+ height: 34px;
702
+ }
703
+
704
+ .switch input {
705
+ opacity: 0;
706
+ width: 0;
707
+ height: 0;
708
+ }
709
+
710
+ .slider {
711
+ position: absolute;
712
+ cursor: pointer;
713
+ top: 0;
714
+ left: 0;
715
+ right: 0;
716
+ bottom: 0;
717
+ background-color: #ccc;
718
+ transition: .4s;
719
+ border-radius: 34px;
720
+ }
721
+
722
+ .slider:before {
723
+ position: absolute;
724
+ content: "";
725
+ height: 26px;
726
+ width: 26px;
727
+ left: 4px;
728
+ bottom: 4px;
729
+ background-color: white;
730
+ transition: .4s;
731
+ border-radius: 50%;
732
+ }
733
+
734
+ input:checked + .slider {
735
+ background-color: var(--ps-light-blue);
736
+ }
737
+
738
+ input:focus + .slider {
739
+ box-shadow: 0 0 1px var(--ps-light-blue);
740
+ }
741
+
742
+ input:checked + .slider:before {
743
+ transform: translateX(26px);
744
+ }
745
+
746
+ /* Responsive tweaks */
747
+ @media (max-width: 1200px) {
748
+ :root {
749
+ --card-size: 110px;
750
+ --card-selected: 160px;
751
+ --anim-speed: 0.22s;
752
+ }
753
+ .top-bar { padding: 22px 36px; font-size: 1.25rem; }
754
+ .carousel-container { padding-left: 36px; gap: 12px; }
755
+ .info-area { padding-left: 36px; }
756
+ }
757
+
758
+ @media (max-width: 900px) {
759
+ :root { --card-size: 100px; --card-selected: 150px; }
760
+ .top-bar { padding: 18px 24px; font-size: 1.15rem; }
761
+ .nav-hint { display: none; }
762
+ .carousel-container { padding-left: 24px; gap: 12px; }
763
+ .sub-content { gap: 12px; }
764
+ .sub-tile { height: 140px; width: 100%; }
765
+ }
766
+
767
+ @media (max-width: 600px) {
768
+ :root { --card-size: 84px; --card-selected: 130px; }
769
+ body { height: auto; overflow: auto; }
770
+ .top-bar { padding: 12px 16px; font-size: 1rem; gap:8px; flex-wrap: wrap; align-items: center; }
771
+ .site-title { font-size: 1.1rem; }
772
+ .site-title-group { flex: 1 1 auto; }
773
+ .clock-area { font-size: 1rem; }
774
+ .carousel-container { padding-left: 16px; gap: 10px; overflow-x: auto; scroll-behavior: smooth; -webkit-overflow-scrolling: touch; }
775
+ .carousel-container .card { scroll-snap-align: center; }
776
+ .info-area { padding-left: 16px; margin-top: 18px; }
777
+ .sub-content { flex-direction: column; }
778
+ .sub-tile { width: 100%; height: 130px; }
779
+ .library-overlay { padding: 12px; }
780
+ .lib-grid { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
781
+ .game-modal > div:first-child { height: 52px; padding: 8px 12px; }
782
+ .game-modal .close-btn { padding: 10px 12px; font-size: 1rem; }
783
+ .show-bar-btn { display: block !important; }
784
+ .restore-bar-btn { top: 12px; }
785
+
786
+ /* Make top-bar more compact and stack actions */
787
+ .top-bar { display: flex; flex-direction: row; align-items: center; }
788
+ .top-bar .site-title-group { order: 1; }
789
+ .top-bar .nav-hint { order: 3; display: none; }
790
+ .top-bar .clock-area { order: 2; }
791
+
792
+ /* Overlay header & grid behavior */
793
+ .library-overlay, .otherOverlay { padding: 12px; }
794
+ .library-overlay .lib-header, .otherOverlay .lib-header { position: sticky; top: 0; z-index: 5; background: rgba(11,23,48,0.98); padding-top: 8px; padding-bottom: 8px; }
795
+ .library-overlay .lib-grid { overflow-y: auto; max-height: calc(100vh - 220px); padding-bottom: 30px; }
796
+ .library-overlay .lib-filters { position: sticky; top: 56px; z-index: 4; background: transparent; padding-top: 10px; padding-bottom: 10px; }
797
+
798
+ .lib-search { width: 100%; }
799
+ .lib-categories { gap: 8px; }
800
+
801
+ .category-btn { padding: 10px 12px; font-size: 0.95rem; }
802
+ .lib-card { min-height: 110px; border-radius: 6px; }
803
+
804
+ /* Tabs & buttons should be touch-friendly */
805
+ .other-tabs { overflow-x: auto; padding-bottom: 8px; }
806
+ .tab-btn { padding: 10px 14px; border-radius: 8px; white-space: nowrap; }
807
+ }
808
+
809
+ @media (max-width: 420px) {
810
+ :root { --card-size: 74px; --card-selected: 110px; }
811
+ .top-bar .clock-area { display: none; }
812
+ .site-title img { height: 22px; }
813
+ .game-title { font-size: 1.4rem; }
814
+ .lib-grid { grid-template-columns: 1fr; }
815
+ .nav-hint { display: none; }
816
+
817
+ .close-btn { padding: 12px 14px; font-size: 1rem; }
818
+ .tab-btn { display: inline-block; font-size: 0.95rem; }
819
+ .library-overlay .lib-header, .otherOverlay .lib-header { padding-left: 10px; padding-right: 10px; }
820
+ }
821
+
822
+ .lib-card.dragging {
823
+ opacity: 0.4;
824
+ transform: scale(0.95);
825
+ }
826
+ .lib-card.drag-over {
827
+ outline: 3px solid var(--ps-light-blue);
828
+ }
829
+
830
+ </style>
6
831
  </head>
7
832
  <body>
833
+ <div class="game-bg-overlay" id="gameBgOverlay"></div>
834
+
835
+ <div class="top-bar">
836
+ <div class="site-title-group">
837
+ <div class="site-title"><img src="https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/new%20logo.png" alt="Alexr's World Logo"> Alexr's World</div>
838
+ </div>
839
+
840
+ <div class="nav-hint" id="navHint">Navigate with &larr;&rarr; Arrow Keys or Mouse</div>
841
+
842
+ <div class="clock-area">
843
+ <div class="social-icons">
844
+ <a href="https://www.youtube.com/@Alexr5153" target="_blank" title="YouTube" onclick="playSound('select')"><i class="fab fa-youtube"></i></a>
845
+ <a href="https://discord.gg/txKXSjaY7E" target="_blank" title="Discord" onclick="playSound('select')"><i class="fab fa-discord"></i></a>
846
+ <a href="https://www.tiktok.com/@alexrunblockedftw" target="_blank" title="TikTok" onclick="playSound('select')"><i class="fab fa-tiktok"></i></a>
847
+ <a href="https://www.instagram.com/alexrgamesunblocked/" target="_blank" title="Instagram" onclick="playSound('select')"><i class="fab fa-instagram"></i></a>
848
+ </div>
849
+ <span id="clock">12:00</span>
850
+ </div>
851
+ </div>
852
+
853
+ <div class="main-stage">
854
+ <div class="carousel-container" id="carousel"></div>
855
+ </div>
856
+
857
+ <div class="info-area visible" id="infoArea">
858
+ <div class="game-title" id="gameTitle">Game Title</div>
859
+ <div class="game-meta">
860
+ <span id="gameCat">Category</span>
861
+ </div>
862
+
863
+ <div class="sub-content">
864
+ <div class="sub-tile">
865
+ <img src="" id="bgPreview1" style="background:#222">
866
+ <div class="sub-head">Overview</div>
867
+ <div class="sub-desc" id="overviewDesc">Game Info</div>
868
+ </div>
869
+ <div class="sub-tile" id="favoriteTile" style="background: linear-gradient(135deg, #00439c, #00285e); align-items: center; justify-content: center;">
870
+ <div id="favoriteLabel" style="font-size: 1.5rem; font-weight: bold;">Favorite this game</div>
871
+ <div class="sub-desc" id="favoriteStatus">Click to add to favorites</div>
872
+ </div>
873
+ </div>
874
+ </div>
875
+
876
+ <div class="library-overlay" id="libraryOverlay">
877
+ <div class="lib-header">
878
+ <div class="lib-title" id="libraryTitle">Your Collection</div>
879
+ <button class="close-btn" onclick="closeOverlay()">Close</button>
880
+ </div>
881
+
882
+ <div class="lib-filters">
883
+ <input type="text" id="libSearchInput" class="lib-search" placeholder="Search games by title..." oninput="renderOverlayGrid()" list="game-suggestions">
884
+ <datalist id="game-suggestions"></datalist>
885
+ <div class="lib-categories" id="libCategories">
886
+ </div>
887
+ </div>
888
+ <div class="lib-grid" id="libraryGrid"></div>
889
+ </div>
890
+
891
+ <div class="library-overlay" id="otherOverlay">
892
+ <div class="lib-header">
893
+ <div class="lib-title">Settings</div>
894
+ <button class="close-btn" onclick="toggleOther(false)">Close</button>
895
+ </div>
896
+
897
+ <div class="other-tabs">
898
+ <button class="tab-btn" id="tab_changelog" onclick="showOtherTab('changelog')">Changelog</button>
899
+ <button class="tab-btn" id="tab_proxy" onclick="showOtherTab('proxy')">Proxy</button>
900
+ <button class="tab-btn" id="tab_themes" onclick="showOtherTab('themes')">Themes</button>
901
+ <button class="tab-btn" id="tab_sounds" onclick="showOtherTab('sounds')">Sounds</button>
902
+ <button class="tab-btn" id="tab_cloak" onclick="showOtherTab('cloak')">Tab Cloak</button>
903
+ <button class="tab-btn" id="tab_credits" onclick="showOtherTab('credits')">Credits</button>
904
+ </div>
905
+
906
+ <div id="other_changelog">
907
+ <p>12-14-25 Beta V1: Beta release</p>
908
+ <p>12-15-25 Beta V2: Bug Fixes and implemented pokemon games and more!</p>
909
+ <p>12-17-25: Beta V3: More bug fixes and implement FNAF 4 Halloween and Ultimate Custom Night</p>
910
+ <p>12-18-25: V1.0: RELEASE! You can now drag and drop cards on menu. Added Donkey Kong Country 1-3.</p>
911
+ </div>
912
+
913
+ <div id="other_proxy" style="display:none;">
914
+ <p>Coming soon!</p>
915
+ </div>
916
+
917
+ <div id="other_themes" style="display:none;">
918
+ <p>Select a theme:</p>
919
+ <button class="close-btn" onclick="setTheme('default')">Default</button>
920
+ <button class="close-btn" onclick="setTheme('dark')">Dark</button>
921
+ <button class="close-btn" onclick="setTheme('red')">Red</button>
922
+ <button class="close-btn" onclick="setTheme('blue-dark')">Blue Dark</button>
923
+ <button class="close-btn" onclick="setTheme('hacker')">Hacker</button>
924
+ <button class="close-btn" onclick="setTheme('tokyo')">Tokyo</button>
925
+
926
+ <hr style="margin:20px 0; border-color:rgba(255,255,255,0.1);">
927
+ <p>PS5-Style Game Backgrounds:</p>
928
+ <button class="close-btn" id="ps5BgToggle" onclick="togglePS5Backgrounds()">Enable PS5 Backgrounds</button>
929
+ <p style="font-size:0.85rem; color:#aaa; margin-top:8px;">Show selected game image as blurred background</p>
930
+
931
+ <hr style="margin:20px 0; border-color:rgba(255,255,255,0.1);">
932
+ <p>Custom background image:</p>
933
+ <label class="close-btn" for="bgUploadInput">Upload Background</label>
934
+ <input type="file" id="bgUploadInput" accept="image/*">
935
+ <button class="close-btn" onclick="clearCustomBackground()">Clear Custom Background</button>
936
+ </div>
937
+
938
+
939
+ <div id="other_cloak" style="display:none;">
940
+ <p>Tab Cloaking:</p>
941
+
942
+ <p style="font-size:0.9rem; color:#aaa;">
943
+ Change the tab title and favicon to disguise this site.
944
+ </p>
945
+
946
+ <div style="margin-top:15px;">
947
+ <button class="close-btn" onclick="applyCloakPreset('Google Docs','https://ssl.gstatic.com/docs/documents/images/kix-favicon7.ico')">Google Docs</button>
948
+ <button class="close-btn" onclick="applyCloakPreset('Google Classroom','https://www.gstatic.com/classroom/logo_square_rounded.svg')">Google Classroom</button>
949
+ <button class="close-btn" onclick="applyCloakPreset('Clever | Portal','https://raw.githubusercontent.com/dskjfoisjfsjio/Standalone-games/main/assets/clever.jpg')">Clever</button>
950
+ </div>
951
+
952
+ <hr style="margin:20px 0; border-color:rgba(255,255,255,0.1);">
953
+
954
+ <p>Custom Cloak:</p>
955
+ <input id="cloakTitleInput" placeholder="Tab Title" style="width:100%; padding:10px; margin-bottom:10px;">
956
+ <input id="cloakIconInput" placeholder="Favicon URL" style="width:100%; padding:10px; margin-bottom:10px;">
957
+
958
+ <button class="close-btn" onclick="applyCustomCloak()">Apply Custom Cloak</button>
959
+ <button class="close-btn" onclick="resetCloak()">Reset Cloak</button>
960
+ </div>
961
+
962
+ <div id="other_sounds" style="display:none;">
963
+ <p>Toggle Sound Effects and Ambience:</p>
964
+ <div class="sound-control">
965
+ <label for="soundToggle">Enable Sounds</label>
966
+ <label class="switch">
967
+ <input type="checkbox" id="soundToggle" onchange="toggleSoundEffects(this.checked)">
968
+ <span class="slider"></span>
969
+ </label>
970
+ </div>
971
+
972
+ <p style="font-size:0.85rem; color:#aaa; margin-top:15px;">
973
+ Sound effects include card hovers, selection, and a menu background track.
974
+ </p>
975
+
976
+ <hr style="margin:20px 0; border-color:rgba(255,255,255,0.1);">
977
+ <p>Ambience Volume:</p>
978
+ <input type="range" id="ambienceVolume" min="0" max="1" step="0.1" value="0.5" oninput="setAmbienceVolume(this.value)" style="width: 100%; margin-top: 10px;">
979
+ </div>
980
+
981
+ <div id="other_credits" style="display:none;">
982
+ <h3>Credits</h3>
983
+ <p>Alexr - UI, UX and games.</p>
984
+ <p>Michiganontop - Sounds</p>
985
+ <p>Carteryes - Unity ports</p>
986
+ <p>Thank you!</p>
987
+ </div>
988
+ </div>
989
+
990
+ <script>
991
+
992
+ let draggedLibTitle = null;
993
+
994
+ const carousel = document.getElementById('carousel');
995
+ const infoArea = document.getElementById('infoArea');
996
+ const libOverlay = document.getElementById('libraryOverlay');
997
+ const libGrid = document.getElementById('libraryGrid');
998
+ const libraryTitle = document.getElementById('libraryTitle');
999
+ const otherOverlay = document.getElementById('otherOverlay');
1000
+ const libSearchInput = document.getElementById('libSearchInput');
1001
+ const libCategoriesDiv = document.getElementById('libCategories');
1002
+ const gameSuggestionsDatalist = document.getElementById('game-suggestions');
1003
+ const subContent = document.querySelector('.sub-content');
1004
+
1005
+ // Play select sound when interacting with the library/favorites search input
1006
+ if (libSearchInput) {
1007
+ // Use focus and Enter; avoid click to prevent double-trigger (focus + click) on some browsers
1008
+ libSearchInput.addEventListener('focus', () => playSound('select'));
1009
+ libSearchInput.addEventListener('keydown', (e) => {
1010
+ if (e.key === 'Enter') {
1011
+ playSound('select');
1012
+ renderOverlayGrid();
1013
+ }
1014
+ });
1015
+ }
1016
+
1017
+ let allGamesData = [];
1018
+ let displayList = [];
1019
+ let selectedIndex = 0;
1020
+ let isLibraryOpen = false;
1021
+ let isOtherOpen = false;
1022
+ let currentOverlayMode = 'library';
1023
+ let favoritesSet = new Set();
1024
+ let currentCategoryFilter = 'All';
1025
+ let areSoundsEnabled = false;
1026
+
1027
+
1028
+ const settingsItem = {
1029
+ title: "Settings",
1030
+ category: "Changelog, Themes & more!",
1031
+ description: "Customize themes and manage backgrounds.",
1032
+ systemType: "settings",
1033
+ icon: "fa-cog"
1034
+ };
1035
+
1036
+ const favoritesItem = {
1037
+ title: "Favorites",
1038
+ category: "Your Favorite titles",
1039
+ description: "Your favorite titles.",
1040
+ systemType: "favorites",
1041
+ icon: "fa-heart"
1042
+ };
1043
+
1044
+
1045
+ const suggestItem = {
1046
+ title: "Suggest a Title",
1047
+ category: "Suggest a title",
1048
+ description: "Suggest a game via Google Form.",
1049
+ systemType: "suggest",
1050
+ icon: "fa-lightbulb",
1051
+ path: "https://docs.google.com/forms/d/e/1FAIpQLSeOUOd7fAVrAdD4u3IWdyAVCnkcvGkgkVIf-pd0bhOBGcrC1g/viewform?usp=sf_link" // Placeholder URL
1052
+ };
1053
+
1054
+ const reportItem = {
1055
+ title: "Report Bugs",
1056
+ category: "Report a bug",
1057
+ description: "Report a bug via Google Form.",
1058
+ systemType: "report",
1059
+ icon: "fa-bug",
1060
+ path: "https://docs.google.com/forms/d/e/1FAIpQLSerfTd9MM5NwszaG1CNMSZjNoNtF4VF51_twn18E-XW6MDLew/viewform?usp=sf_link" // Placeholder URL
1061
+ };
1062
+
1063
+ const libraryItem = {
1064
+ title: "Library",
1065
+ category: "Collection",
1066
+ description: "View all games in your collection.",
1067
+ systemType: "library",
1068
+ icon: "fa-th"
1069
+ };
1070
+
8
1071
 
9
- <script>
10
- (() => {
11
-
12
- const MAIN_HTML_URL =
13
- "https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/index.html?t=" + Date.now();
14
-
15
- const GAME_JSON_URL =
16
- "https://raw.githubusercontent.com/dskjfoisjfsjio/alexrsworld/main/games.json";
17
-
18
- const GAMES_CONTAINER_ID = "games-container";
19
-
20
- fetch(MAIN_HTML_URL)
21
- .then(res => {
22
- if (!res.ok) throw new Error("HTML load failed");
23
- return res.text();
24
- })
25
- .then(html => {
26
-
27
- // Replace document safely
28
- document.open();
29
- document.write(html);
30
- document.close();
31
-
32
- // WAIT until all scripts in index.html finish loading
33
- window.addEventListener("load", () => {
34
-
35
- if (typeof window.displayGames !== "function") {
36
- console.error("displayGames() missing after load");
37
- return;
38
- }
39
-
40
- fetch(GAME_JSON_URL, { cache: "no-store" })
41
- .then(r => {
42
- if (!r.ok) throw new Error("JSON 404");
43
- return r.json();
44
- })
45
- .then(data => {
46
- window.displayGames(data);
47
- console.log("Games loaded:", data.length);
48
- })
49
- .catch(err => {
50
- console.error("Games JSON error:", err);
51
- const c = document.getElementById(GAMES_CONTAINER_ID);
52
- if (c) c.textContent = "Failed to load games.";
1072
+
1073
+
1074
+
1075
+ const ambience = new Audio('https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/sounds/ambience.mp3');
1076
+ ambience.loop = true;
1077
+ ambience.volume = localStorage.getItem('alexrGames_ambience_volume') || 0.5;
1078
+
1079
+ const soundEffects = {
1080
+ click: new Audio('https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/sounds/close.mp3'),
1081
+ hover: new Audio('https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/sounds/hover.mp3'),
1082
+ select: new Audio('https://cdn.jsdelivr.net/gh/dskjfoisjfsjio/alexrsworld@main/sounds/select.mp3'),
1083
+ };
1084
+
1085
+ // Per-sound cooldowns (ms) to avoid duplicates while allowing frequent hover sounds
1086
+ const _soundCooldowns = { hover: 80, select: 220, click: 220 };
1087
+ const _lastSoundPlay = {};
1088
+
1089
+ function loadSoundSettings() {
1090
+ const saved = localStorage.getItem('alexrGames_sounds_enabled') === 'true';
1091
+ areSoundsEnabled = saved;
1092
+
1093
+ const toggle = document.getElementById('soundToggle');
1094
+ if (toggle) toggle.checked = saved;
1095
+
1096
+ const volumeInput = document.getElementById('ambienceVolume');
1097
+ if (volumeInput) volumeInput.value = ambience.volume;
1098
+
1099
+ if (saved && !document.querySelector('.game-modal')) {
1100
+ playAmbience();
1101
+ }
1102
+ }
1103
+
1104
+ function toggleSoundEffects(enabled) {
1105
+ areSoundsEnabled = enabled;
1106
+ localStorage.setItem('alexrGames_sounds_enabled', enabled);
1107
+ if (enabled) {
1108
+ playAmbience();
1109
+ } else {
1110
+ pauseAmbience();
1111
+ }
1112
+ }
1113
+
1114
+ function setAmbienceVolume(volume) {
1115
+ ambience.volume = volume;
1116
+ localStorage.setItem('alexrGames_ambience_volume', volume);
1117
+ }
1118
+
1119
+ function playSound(name) {
1120
+ if (!areSoundsEnabled) return;
1121
+ const now = Date.now();
1122
+ const cooldown = (_soundCooldowns && _soundCooldowns[name] != null) ? _soundCooldowns[name] : 200;
1123
+ const last = _lastSoundPlay[name] || 0;
1124
+ if (now - last < cooldown) return;
1125
+ _lastSoundPlay[name] = now;
1126
+
1127
+ const audio = soundEffects[name];
1128
+ if (audio) {
1129
+ audio.currentTime = 0;
1130
+ audio.play().catch(e => console.warn("Audio playback failed:", e));
1131
+ }
1132
+ }
1133
+
1134
+ function playAmbience() {
1135
+ if (areSoundsEnabled) {
1136
+
1137
+ if (!document.querySelector('.game-modal') && !isLibraryOpen && !isOtherOpen) {
1138
+ ambience.play().catch(e => console.warn("Ambience playback failed:", e));
1139
+ }
1140
+ }
1141
+ }
1142
+
1143
+ function pauseAmbience() {
1144
+ ambience.pause();
1145
+ }
1146
+
1147
+
1148
+
1149
+
1150
+ function togglePS5Backgrounds() {
1151
+ const isEnabled = document.body.classList.toggle('ps5-bg-enabled');
1152
+ localStorage.setItem('alexrGames_ps5bg', isEnabled ? 'true' : 'false');
1153
+ const btn = document.getElementById('ps5BgToggle');
1154
+ if (btn) {
1155
+ btn.textContent = isEnabled ? 'Disable PS5 Backgrounds' : 'Enable PS5 Backgrounds';
1156
+ }
1157
+ if (!isEnabled) {
1158
+ const overlay = document.getElementById('gameBgOverlay');
1159
+ if (overlay) overlay.classList.remove('active');
1160
+ } else {
1161
+ updateGameBackground(displayList[selectedIndex]);
1162
+ }
1163
+ }
1164
+
1165
+ function loadPS5BackgroundSetting() {
1166
+ const saved = localStorage.getItem('alexrGames_ps5bg');
1167
+ const isEnabled = saved === 'true';
1168
+ if (isEnabled) {
1169
+ document.body.classList.add('ps5-bg-enabled');
1170
+ }
1171
+ const btn = document.getElementById('ps5BgToggle');
1172
+ if (btn) {
1173
+ btn.textContent = isEnabled ? 'Disable PS5 Backgrounds' : 'Enable PS5 Backgrounds';
1174
+ }
1175
+ }
1176
+
1177
+ function updateGameBackground(game) {
1178
+ const overlay = document.getElementById('gameBgOverlay');
1179
+ if (!overlay) return;
1180
+
1181
+ const isEnabled = document.body.classList.contains('ps5-bg-enabled');
1182
+
1183
+ if (!isEnabled || !game || game.systemType || !game.img) {
1184
+ overlay.classList.remove('active');
1185
+ return;
1186
+ }
1187
+
1188
+ overlay.style.backgroundImage = `url(${game.img})`;
1189
+ overlay.classList.add('active');
1190
+ }
1191
+
1192
+ function applyTheme(themeName) {
1193
+ document.body.classList.remove('theme-default','theme-dark','theme-red','theme-blue-dark','theme-hacker','theme-tokyo');
1194
+ document.body.classList.add('theme-' + themeName);
1195
+ }
1196
+
1197
+ function setTheme(themeName) {
1198
+ applyTheme(themeName);
1199
+ localStorage.setItem('alexrGames_theme', themeName);
1200
+ try { updateFavoriteTile(displayList[selectedIndex]); } catch(e) {}
1201
+ }
1202
+
1203
+ function loadTheme() {
1204
+ const savedTheme = localStorage.getItem('alexrGames_theme') || 'default';
1205
+ applyTheme(savedTheme);
1206
+ const bg = localStorage.getItem('alexrGames_bg');
1207
+ if (bg) {
1208
+ document.body.style.backgroundImage = `url(${bg})`;
1209
+ document.body.classList.add('has-custom-bg');
1210
+ }
1211
+ }
1212
+
1213
+ function clearCustomBackground() {
1214
+ localStorage.removeItem('alexrGames_bg');
1215
+ document.body.classList.remove('has-custom-bg');
1216
+ document.body.style.backgroundImage = '';
1217
+ const savedTheme = localStorage.getItem('alexrGames_theme') || 'default';
1218
+ applyTheme(savedTheme);
1219
+ }
1220
+
1221
+ const bgUploadInput = document.getElementById('bgUploadInput');
1222
+ if (bgUploadInput) {
1223
+ bgUploadInput.addEventListener('change', function() {
1224
+ const file = this.files[0];
1225
+ if (!file) return;
1226
+ const reader = new FileReader();
1227
+ reader.onload = function(e) {
1228
+ const dataURL = e.target.result;
1229
+ document.body.style.backgroundImage = `url(${dataURL})`;
1230
+ document.body.classList.add('has-custom-bg');
1231
+ localStorage.setItem('alexrGames_bg', dataURL);
1232
+ };
1233
+ reader.readAsDataURL(file);
1234
+ });
1235
+ }
1236
+
1237
+
1238
+ function loadFavorites() {
1239
+ const saved = localStorage.getItem('alexrGames_favorites');
1240
+ if (saved) {
1241
+ try {
1242
+ const arr = JSON.parse(saved);
1243
+ favoritesSet = new Set(arr);
1244
+ } catch (e) {
1245
+ favoritesSet = new Set();
1246
+ }
1247
+ }
1248
+ }
1249
+
1250
+ function saveFavorites() {
1251
+
1252
+ localStorage.setItem('alexrGames_favorites', JSON.stringify(Array.from(favoritesSet)));
1253
+ }
1254
+
1255
+
1256
+ async function loadGames() {
1257
+ loadFavorites();
1258
+
1259
+ localStorage.removeItem('alexrGames_allGamesData');
1260
+
1261
+ try {
1262
+ const response = await fetch('https://raw.githubusercontent.com/dskjfoisjfsjio/alexrsworld/main/singlefilegames.json');
1263
+ if (!response.ok) {
1264
+ throw new Error(`HTTP error! status: ${response.status}`);
1265
+ }
1266
+ const data = await response.json();
1267
+
1268
+ allGamesData = Array.isArray(data) ? data : data.allGamesData || [];
1269
+
1270
+ allGamesData.sort((a, b) => {
1271
+ return a.title.localeCompare(b.title, undefined, {
1272
+ numeric: true,
1273
+ sensitivity: 'base'
1274
+ });
1275
+ });
1276
+
1277
+ if (allGamesData.length === 0) {
1278
+ console.warn('games.json was empty or incorrectly formatted.');
1279
+ }
1280
+
1281
+ try {
1282
+ const savedCustom = JSON.parse(localStorage.getItem('alexrGames_customOrder') || '[]');
1283
+ if (Array.isArray(savedCustom) && savedCustom.length) {
1284
+ const map = new Map(allGamesData.map(g => [g.title, g]));
1285
+ const reordered = [];
1286
+ savedCustom.forEach(title => {
1287
+ if (map.has(title)) {
1288
+ reordered.push(map.get(title));
1289
+ map.delete(title);
1290
+ }
1291
+ });
1292
+ for (const g of allGamesData) {
1293
+ if (map.has(g.title)) reordered.push(g);
1294
+ }
1295
+ allGamesData = reordered;
1296
+ } else {
1297
+ const savedRecent = JSON.parse(localStorage.getItem('alexrGames_recent') || '[]');
1298
+ if (Array.isArray(savedRecent) && savedRecent.length) {
1299
+ const map = new Map(allGamesData.map(g => [g.title, g]));
1300
+ const reordered = [];
1301
+ savedRecent.forEach(title => {
1302
+ if (map.has(title)) {
1303
+ reordered.push(map.get(title));
1304
+ map.delete(title);
1305
+ }
53
1306
  });
1307
+ for (const g of allGamesData) {
1308
+ if (map.has(g.title)) reordered.push(g);
1309
+ }
1310
+ allGamesData = reordered;
1311
+ }
1312
+ }
1313
+ } catch (e) {
1314
+ console.warn('Failed to apply saved ordering:', e);
1315
+ }
1316
+
1317
+ } catch(e) {
1318
+ console.error("Failed to fetch games.json. Please ensure the file exists and is valid JSON.", e);
1319
+ allGamesData = [];
1320
+ }
1321
+
1322
+ buildLists();
1323
+ }
1324
+ function buildLists() {
1325
+ const firstSix = allGamesData.slice(0, 6);
1326
+
1327
+
1328
+ displayList = [suggestItem, reportItem, favoritesItem, settingsItem, ...firstSix, libraryItem];
1329
+
1330
+ initCarousel();
1331
+ initLibraryGrid();
1332
+ }
1333
+
1334
+
1335
+
1336
+
1337
+ function saveGameOrder() {
1338
+ try {
1339
+ const titles = (allGamesData || []).map(g => g.title).filter(Boolean);
1340
+ localStorage.setItem('alexrGames_customOrder', JSON.stringify(titles));
1341
+ } catch (e) {
1342
+ console.warn('saveGameOrder failed:', e);
1343
+ }
1344
+ }
1345
+
1346
+ function promoteToRecent(game) {
1347
+ try {
1348
+ const title = game && game.title;
1349
+ if (!title) return;
1350
+
1351
+ allGamesData = allGamesData.filter(g => g.title !== title);
1352
+ allGamesData.unshift(game);
1353
+
1354
+ const saved = JSON.parse(localStorage.getItem('alexrGames_recent') || '[]');
1355
+ const filtered = (Array.isArray(saved) ? saved.filter(t => t !== title) : []);
1356
+ filtered.unshift(title);
1357
+ const maxRecent = 12;
1358
+ const trimmed = filtered.slice(0, maxRecent);
1359
+ localStorage.setItem('alexrGames_recent', JSON.stringify(trimmed));
1360
+
1361
+ try { saveGameOrder(); } catch (se) { console.warn('Failed to save custom order on promoteToRecent:', se); }
1362
+
1363
+ } catch (e) {
1364
+ console.warn('promoteToRecent failed to persist recent:', e);
1365
+ }
1366
+
1367
+ buildLists();
1368
+
1369
+ updateSelection(4);
1370
+ }
1371
+
1372
+ function initCarousel() {
1373
+ carousel.innerHTML = '';
1374
+
1375
+
1376
+ document.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
1377
+
1378
+ displayList.forEach((game, index) => {
1379
+ const card = document.createElement('div');
1380
+ card.className = `card ${game.systemType ? 'system-card' : ''}`;
1381
+ card.dataset.index = index;
1382
+
1383
+
1384
+
1385
+ const isGameCard = index >= 4 && index < displayList.length - 1;
1386
+
1387
+ card.draggable = isGameCard ? 'true' : 'false';
1388
+
1389
+ if (isGameCard) {
1390
+
1391
+ card.addEventListener('dragstart', (e) => {
1392
+ playSound('hover');
1393
+ card.classList.add('dragging');
1394
+
1395
+ e.dataTransfer.setData('text/plain', index.toString());
1396
+
1397
+ e.dataTransfer.effectAllowed = 'move';
1398
+ });
1399
+
1400
+
1401
+ card.addEventListener('dragover', (e) => {
1402
+
1403
+ e.preventDefault();
1404
+ const draggedIdx = parseInt(e.dataTransfer.getData('text/plain'));
1405
+
1406
+ if (index !== draggedIdx && isGameCard) {
1407
+ card.classList.add('drag-over');
1408
+ e.dataTransfer.dropEffect = 'move';
1409
+ }
1410
+ });
1411
+
1412
+
1413
+ card.addEventListener('dragleave', () => {
1414
+ card.classList.remove('drag-over');
1415
+ });
1416
+
1417
+
1418
+ card.addEventListener('drop', (e) => {
1419
+ e.preventDefault();
1420
+ card.classList.remove('drag-over');
1421
+ playSound('select');
1422
+
1423
+ const draggedListIndex = parseInt(e.dataTransfer.getData('text/plain'));
1424
+ const targetListIndex = index;
1425
+
1426
+
1427
+ if (draggedListIndex < 4 || targetListIndex < 4 ||
1428
+ draggedListIndex >= displayList.length - 1 || targetListIndex >= displayList.length - 1 ||
1429
+ draggedListIndex === targetListIndex) {
1430
+ return;
1431
+ }
1432
+
1433
+
1434
+ const oldGameIndex = draggedListIndex - 4;
1435
+ const newGameIndex = targetListIndex - 4;
1436
+
1437
+
1438
+ const [movedGame] = allGamesData.splice(oldGameIndex, 1);
1439
+ allGamesData.splice(newGameIndex, 0, movedGame);
1440
+
1441
+
1442
+ buildLists();
1443
+ updateSelection(targetListIndex);
1444
+ try { saveGameOrder(); } catch (e) { console.warn('Failed to save game order after drag:', e); }
1445
+ });
1446
+
1447
+
1448
+ card.addEventListener('dragend', () => {
1449
+ card.classList.remove('dragging');
1450
+
1451
+ document.querySelectorAll('.drag-over').forEach(el => el.classList.remove('drag-over'));
1452
+ });
1453
+ }
1454
+
1455
+
1456
+ if (game.systemType) {
1457
+
1458
+
1459
+ card.innerHTML = `<i class="fas ${game.icon}"></i>`;
1460
+ card.style.justifyContent = 'center';
1461
+ card.style.alignItems = 'center';
1462
+ } else {
1463
+ const img = document.createElement('img');
1464
+ img.src = game.img || '';
1465
+ img.onerror = function() { this.style.display='none'; card.style.background='#333'; };
1466
+ card.appendChild(img);
1467
+ }
1468
+
1469
+
1470
+ card.addEventListener('click', () => {
1471
+ if(selectedIndex === index) {
1472
+ activateItem(index);
1473
+ } else {
1474
+ playSound('hover');
1475
+ updateSelection(index);
1476
+ }
1477
+ });
1478
+
1479
+ carousel.appendChild(card);
1480
+ });
1481
+
1482
+
1483
+ updateSelection(displayList.findIndex(g => !g.systemType) || 0);
1484
+ }
1485
+
1486
+ function getUniqueCategories(gamesList) {
1487
+ const source = Array.isArray(gamesList) ? gamesList : allGamesData;
1488
+ const categories = source.map(game => game.category).filter(Boolean);
1489
+ return ['All', ...new Set(categories)];
1490
+ }
1491
+
1492
+ function renderSearchSuggestions() {
1493
+ gameSuggestionsDatalist.innerHTML = '';
1494
+ const sourceList = (currentOverlayMode === 'favorites') ? allGamesData.filter(g => favoritesSet.has(g.title)) : allGamesData;
1495
+ sourceList.forEach(game => {
1496
+ const option = document.createElement('option');
1497
+ option.value = game.title;
1498
+ gameSuggestionsDatalist.appendChild(option);
54
1499
  });
1500
+ }
55
1501
 
56
- })
57
- .catch(err => {
58
- console.error("Loader error:", err);
59
- document.body.textContent = "Failed to load site.";
1502
+ function renderCategoryButtons() {
1503
+ libCategoriesDiv.innerHTML = '';
1504
+
1505
+ const sourceList = (currentOverlayMode === 'favorites') ? allGamesData.filter(g => favoritesSet.has(g.title)) : allGamesData;
1506
+ const categories = getUniqueCategories(sourceList);
1507
+
1508
+ if (!categories.includes(currentCategoryFilter)) {
1509
+ currentCategoryFilter = 'All';
1510
+ }
1511
+
1512
+ categories.forEach(category => {
1513
+ const button = document.createElement('button');
1514
+ button.className = 'category-btn';
1515
+ button.textContent = category;
1516
+ button.dataset.category = category;
1517
+
1518
+ if (category === currentCategoryFilter) {
1519
+ button.classList.add('active');
1520
+ }
1521
+
1522
+ button.onclick = () => {
1523
+ playSound('select');
1524
+ currentCategoryFilter = category;
1525
+ renderCategoryButtons();
1526
+ renderOverlayGrid();
1527
+ };
1528
+
1529
+ libCategoriesDiv.appendChild(button);
1530
+ });
1531
+ }
1532
+
1533
+ function initLibraryGrid() {
1534
+ renderSearchSuggestions();
1535
+ renderCategoryButtons();
1536
+ renderOverlayGrid();
1537
+ }
1538
+
1539
+ function renderOverlayGrid() {
1540
+ libGrid.innerHTML = '';
1541
+ let gamesToShow = [];
1542
+
1543
+ if (currentOverlayMode === 'library') {
1544
+ libraryTitle.textContent = 'All Games';
1545
+ gamesToShow = allGamesData;
1546
+
1547
+ const searchTerm = libSearchInput.value.toLowerCase().trim();
1548
+
1549
+ gamesToShow = gamesToShow.filter(game => {
1550
+ const matchesSearch = !searchTerm || game.title.toLowerCase().includes(searchTerm);
1551
+ const matchesCategory = currentCategoryFilter === 'All' || game.category === currentCategoryFilter;
1552
+ return matchesSearch && matchesCategory;
1553
+ });
1554
+
1555
+ } else {
1556
+ libraryTitle.textContent = 'Your Favorites';
1557
+ gamesToShow = allGamesData.filter(g => favoritesSet.has(g.title));
1558
+
1559
+ const searchTerm = libSearchInput.value.toLowerCase().trim();
1560
+ gamesToShow = gamesToShow.filter(game => {
1561
+ const matchesSearch = !searchTerm || game.title.toLowerCase().includes(searchTerm);
1562
+ const matchesCategory = currentCategoryFilter === 'All' || game.category === currentCategoryFilter;
1563
+ return matchesSearch && matchesCategory;
60
1564
  });
1565
+ }
1566
+
1567
+ gamesToShow.forEach((game) => {
1568
+ const card = document.createElement('div');
1569
+ card.draggable = true;
1570
+
1571
+ card.addEventListener('dragstart', () => {
1572
+ draggedLibTitle = game.title;
1573
+ card.classList.add('dragging');
1574
+ playSound('hover');
1575
+ });
1576
+
1577
+ card.addEventListener('dragend', () => {
1578
+ draggedLibTitle = null;
1579
+ card.classList.remove('dragging');
1580
+ document.querySelectorAll('.lib-card').forEach(c => c.classList.remove('drag-over'));
1581
+ });
1582
+
1583
+ card.addEventListener('dragover', (e) => {
1584
+ e.preventDefault();
1585
+ card.classList.add('drag-over');
1586
+ });
1587
+
1588
+ card.addEventListener('dragleave', () => {
1589
+ card.classList.remove('drag-over');
1590
+ });
1591
+
1592
+ card.addEventListener('drop', (e) => {
1593
+ e.preventDefault();
1594
+ card.classList.remove('drag-over');
1595
+
1596
+ if (!draggedLibTitle || draggedLibTitle === game.title) return;
1597
+
1598
+ const fromIndex = allGamesData.findIndex(g => g.title === draggedLibTitle);
1599
+ const toIndex = allGamesData.findIndex(g => g.title === game.title);
1600
+
1601
+ if (fromIndex === -1 || toIndex === -1) return;
1602
+
1603
+ const [moved] = allGamesData.splice(fromIndex, 1);
1604
+ allGamesData.splice(toIndex, 0, moved);
1605
+
1606
+ playSound('select');
1607
+ saveGameOrder();
1608
+ renderOverlayGrid();
1609
+ });
1610
+
1611
+ card.className = 'lib-card';
1612
+ card.innerHTML = `
1613
+ <img src="${game.img || ''}" onerror="this.style.display='none'">
1614
+ <button class="lib-fav-btn" aria-label="${favoritesSet.has(game.title) ? 'Unfavorite' : 'Favorite'}">
1615
+ <i class="${favoritesSet.has(game.title) ? 'fas' : 'far'} fa-heart"></i>
1616
+ </button>
1617
+ <div class="lib-card-title">${game.title}</div>
1618
+ `;
1619
+
1620
+ const favBtn = card.querySelector('.lib-fav-btn');
1621
+ if (favBtn) {
1622
+ if (favoritesSet.has(game.title)) favBtn.classList.add('favorited');
1623
+ favBtn.addEventListener('click', (e) => {
1624
+ e.stopPropagation();
1625
+ if (favoritesSet.has(game.title)) {
1626
+ favoritesSet.delete(game.title);
1627
+ favBtn.classList.remove('favorited');
1628
+ const icon = favBtn.querySelector('i'); if (icon) icon.className = 'far fa-heart';
1629
+ favBtn.setAttribute('aria-label','Favorite');
1630
+ } else {
1631
+ favoritesSet.add(game.title);
1632
+ favBtn.classList.add('favorited');
1633
+ const icon = favBtn.querySelector('i'); if (icon) icon.className = 'fas fa-heart';
1634
+ favBtn.setAttribute('aria-label','Unfavorite');
1635
+ }
1636
+ saveFavorites();
1637
+ playSound('select');
1638
+
1639
+ if (currentOverlayMode === 'favorites') renderOverlayGrid();
1640
+ try { updateFavoriteTile(displayList[selectedIndex]); } catch(e) {}
1641
+ });
1642
+ }
1643
+
1644
+ card.onclick = () => {
1645
+ playSound('select');
1646
+ toggleOverlay(false);
1647
+ launchGame(game);
1648
+ };
1649
+ libGrid.appendChild(card);
1650
+ });
1651
+ }
1652
+
1653
+ function updateSelection(index) {
1654
+ if(isLibraryOpen || isOtherOpen || document.querySelector('.game-modal')) return;
1655
+ if(index < 0) index = 0;
1656
+ if(index >= displayList.length) index = displayList.length - 1;
1657
+
1658
+ selectedIndex = index;
1659
+
1660
+ const cards = document.querySelectorAll('.card');
1661
+ cards.forEach(c => c.classList.remove('active'));
1662
+ if (cards[index]) cards[index].classList.add('active');
1663
+
1664
+ const computedGap = parseFloat(getComputedStyle(carousel).gap || getComputedStyle(carousel).columnGap || 15);
1665
+ let activeCardPosition = 0;
1666
+ for (let i = 0; i < index; i++) {
1667
+ activeCardPosition += (cards[i] ? cards[i].offsetWidth : 0) + computedGap;
1668
+ }
1669
+
1670
+ const visibleBefore = 2;
1671
+ const activeWidth = cards[index] ? cards[index].offsetWidth : (parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--card-size')) || 130);
1672
+
1673
+ let offset = activeCardPosition - (visibleBefore * (activeWidth + computedGap));
1674
+ offset = Math.max(0, offset);
1675
+
1676
+ carousel.style.transform = `translateX(-${offset}px)`;
1677
+
1678
+ const g = displayList[index];
1679
+ document.getElementById('gameTitle').innerText = g.title || '';
1680
+ document.getElementById('gameCat').innerText = g.category || 'Game';
1681
+ document.getElementById('overviewDesc').innerText = g.description || 'Game Info';
1682
+
1683
+ const bg1 = document.getElementById('bgPreview1');
1684
+
1685
+ if(g.img) {
1686
+ bg1.src = g.img;
1687
+ bg1.style.filter = "grayscale(100%) blur(2px)";
1688
+ } else {
1689
+ bg1.src = "";
1690
+ }
1691
+
1692
+
1693
+ if (g.systemType) {
1694
+ subContent.classList.add('hidden');
1695
+ document.getElementById('gameTitle').innerText = g.title;
1696
+ document.getElementById('gameCat').innerText = g.category;
1697
+ } else {
1698
+ subContent.classList.remove('hidden');
1699
+ }
1700
+
1701
+ updateFavoriteTile(g);
1702
+ updateGameBackground(g);
1703
+
1704
+ infoArea.classList.remove('visible');
1705
+ setTimeout(() => infoArea.classList.add('visible'), 50);
1706
+ }
1707
+
1708
+ function activateItem(index) {
1709
+ if (document.querySelector('.game-modal')) return;
1710
+ playSound('select');
1711
+ const item = displayList[index];
1712
+
1713
+ if (item.systemType === 'library') {
1714
+ currentOverlayMode = 'library';
1715
+ currentCategoryFilter = 'All';
1716
+ libSearchInput.value = '';
1717
+ renderCategoryButtons();
1718
+ renderOverlayGrid();
1719
+ toggleOverlay(true);
1720
+ } else if (item.systemType === 'favorites') {
1721
+ currentOverlayMode = 'favorites';
1722
+ currentCategoryFilter = 'All';
1723
+ libSearchInput.value = '';
1724
+ renderSearchSuggestions();
1725
+ renderCategoryButtons();
1726
+ renderOverlayGrid();
1727
+ toggleOverlay(true);
1728
+ } else if (item.systemType === 'settings') {
1729
+ showOtherTab('sounds');
1730
+ toggleOther(true);
1731
+ } else if (item.systemType === 'suggest' || item.systemType === 'report') {
1732
+ window.open(item.path, '_blank');
1733
+ } else {
1734
+ launchGame(item);
1735
+ }
1736
+ }
1737
+
1738
+ function toggleOverlay(show) {
1739
+ isLibraryOpen = show;
1740
+ if(show) {
1741
+ libOverlay.classList.add('show');
1742
+ libOverlay.style.opacity = '0';
1743
+ setTimeout(()=>libOverlay.style.opacity='1', 10);
1744
+ try { document.body.style.overflow = 'hidden'; } catch(e) {}
1745
+ try { libOverlay.setAttribute('role','dialog'); libOverlay.setAttribute('aria-hidden','false'); } catch(e) {}
1746
+ setTimeout(() => { try { libOverlay.querySelector('.close-btn').focus(); } catch (e) {} }, 80);
1747
+ } else {
1748
+ libOverlay.style.opacity = '0';
1749
+ setTimeout(()=>{ libOverlay.classList.remove('show'); try { libOverlay.setAttribute('aria-hidden','true'); } catch(e) {}} , 300);
1750
+ try { document.body.style.overflow = ''; } catch(e) {}
1751
+
1752
+ if (!isOtherOpen && !document.querySelector('.game-modal')) {
1753
+ playAmbience();
1754
+ }
1755
+ }
1756
+ }
1757
+
1758
+ function closeOverlay() {
1759
+ playSound('click');
1760
+ toggleOverlay(false);
1761
+ }
1762
+
1763
+ function toggleOther(show) {
1764
+ isOtherOpen = show;
1765
+ if (show) {
1766
+ otherOverlay.classList.add('show');
1767
+ otherOverlay.style.opacity = '0';
1768
+ setTimeout(() => otherOverlay.style.opacity = '1', 10);
1769
+ try { document.body.style.overflow = 'hidden'; } catch(e) {}
1770
+ try { otherOverlay.setAttribute('role','dialog'); otherOverlay.setAttribute('aria-hidden','false'); } catch(e) {}
1771
+ setTimeout(() => { try { otherOverlay.querySelector('.close-btn').focus(); } catch (e) {} }, 80);
1772
+ } else {
1773
+ playSound('click');
1774
+ otherOverlay.style.opacity = '0';
1775
+ setTimeout(() => { otherOverlay.classList.remove('show'); try { otherOverlay.setAttribute('aria-hidden','true'); } catch(e) {} }, 300);
1776
+ try { document.body.style.overflow = ''; } catch(e) {}
1777
+
1778
+ if (!isLibraryOpen && !isOtherOpen) {
1779
+ playAmbience();
1780
+ }
1781
+ }
1782
+ }
1783
+
1784
+ function showOtherTab(tab) {
1785
+ playSound('select');
1786
+ const tabs = ['changelog','proxy','themes','sounds','cloak','credits'];
1787
+ tabs.forEach(t => {
1788
+ const el = document.getElementById('other_' + t);
1789
+ if (el) el.style.display = (t === tab) ? 'block' : 'none';
1790
+ const btn = document.getElementById('tab_' + t);
1791
+ if (btn) {
1792
+ if (t === tab) btn.classList.add('active');
1793
+ else btn.classList.remove('active');
1794
+ }
1795
+ });
1796
+ }
1797
+ function launchGame(game) {
1798
+ pauseAmbience();
1799
+
1800
+ const modal = document.createElement('div');
1801
+ modal.className = 'game-modal';
1802
+
1803
+ const bar = document.createElement('div');
1804
+ Object.assign(bar.style, {
1805
+ height: '40px', background: '#222', display: 'flex',
1806
+ alignItems: 'center', padding: '0 20px',
1807
+ justifyContent: 'space-between', color:'white', gap:'10px'
1808
+ });
1809
+
1810
+ const currentBodyTheme = document.body.className.match(/theme-(\w+)/)?.[1] || 'default';
1811
+ if (currentBodyTheme === 'hacker') {
1812
+ Object.assign(bar.style, { background: '#000', color: '#00ff88' });
1813
+ } else if (currentBodyTheme === 'tokyo') {
1814
+ Object.assign(bar.style, { background: '#111', color: '#ff4aff' });
1815
+ } else if (currentBodyTheme === 'red') {
1816
+ const redAccent = getComputedStyle(document.body).getPropertyValue('--ps-light-blue');
1817
+ Object.assign(bar.style, { background: '#3a0000', color: redAccent });
1818
+ }
1819
+
1820
+ const leftSpan = document.createElement('span');
1821
+ leftSpan.textContent = game.title;
1822
+
1823
+ const controls = document.createElement('div');
1824
+ controls.style.display = 'flex';
1825
+ controls.style.gap = '8px';
1826
+
1827
+ const openAboutBlank = () => {
1828
+ const w = window.open('about:blank', '_blank');
1829
+ if (w) {
1830
+ fetch(game.path)
1831
+ .then(response => response.text())
1832
+ .then(html => {
1833
+ w.document.open();
1834
+ w.document.write(html);
1835
+ w.document.close();
1836
+ })
1837
+ .catch(err => {
1838
+ console.error("About:blank fetch failed:", err);
1839
+ w.location.href = game.path;
1840
+ });
1841
+ }
1842
+ };
1843
+
1844
+ const btnAboutBlank = document.createElement('button');
1845
+ btnAboutBlank.textContent = 'Open in about:blank';
1846
+ btnAboutBlank.className = 'close-btn';
1847
+ btnAboutBlank.onclick = openAboutBlank;
1848
+
1849
+ const btnDownload = document.createElement('button');
1850
+ btnDownload.textContent = 'Download';
1851
+ btnDownload.className = 'close-btn';
1852
+ btnDownload.onclick = () => {
1853
+ fetch(game.path)
1854
+ .then(response => response.text())
1855
+ .then(html => {
1856
+ const blob = new Blob([html], { type: 'text/html' });
1857
+ const url = URL.createObjectURL(blob);
1858
+ const a = document.createElement('a');
1859
+ a.href = url;
1860
+ a.download = `${game.title.replace(/[^a-z0-9]/gi, '_')}.html`;
1861
+ document.body.appendChild(a);
1862
+ a.click();
1863
+ document.body.removeChild(a);
1864
+ URL.revokeObjectURL(url);
1865
+ })
1866
+ .catch(err => console.error("Download failed:", err));
1867
+ };
1868
+
1869
+ const btnFav = document.createElement('button');
1870
+ btnFav.textContent = favoritesSet.has(game.title) ? 'Unfavorite' : 'Favorite';
1871
+ btnFav.className = 'close-btn';
1872
+ btnFav.onclick = () => {
1873
+ if (favoritesSet.has(game.title)) {
1874
+ favoritesSet.delete(game.title);
1875
+ btnFav.textContent = 'Favorite';
1876
+ } else {
1877
+ favoritesSet.add(game.title);
1878
+ btnFav.textContent = 'Unfavorite';
1879
+ }
1880
+ saveFavorites();
1881
+ updateFavoriteTile(game);
1882
+ };
1883
+
1884
+ const btnHide = document.createElement('button');
1885
+ btnHide.textContent = 'Hide bar';
1886
+ btnHide.className = 'close-btn';
1887
+
1888
+ const btnClose = document.createElement('button');
1889
+ btnClose.textContent = '✕ Close';
1890
+ btnClose.className = 'close-btn';
1891
+ btnClose.onclick = () => {
1892
+ playSound('click');
1893
+ const r = document.getElementById('restoreBarBtn');
1894
+ if (r) r.remove();
1895
+ const pageTop = document.querySelector('.top-bar');
1896
+ if (pageTop) pageTop.classList.remove('hidden');
1897
+ document.body.removeChild(modal);
1898
+ try { updateSelection(4); } catch(e) { console.warn('updateSelection on close failed', e); }
1899
+ if (!isLibraryOpen && !isOtherOpen) {
1900
+ playAmbience();
1901
+ }
1902
+ };
1903
+
1904
+ controls.appendChild(btnAboutBlank);
1905
+ controls.appendChild(btnDownload);
1906
+ controls.appendChild(btnFav);
1907
+ controls.appendChild(btnHide);
1908
+ controls.appendChild(btnClose);
1909
+
1910
+ bar.appendChild(leftSpan);
1911
+ bar.appendChild(controls);
1912
+
1913
+ const frameWrapper = document.createElement('div');
1914
+ frameWrapper.style.position = 'relative';
1915
+ frameWrapper.style.flex = '1';
1916
+ frameWrapper.style.display = 'flex';
1917
+ frameWrapper.style.flexDirection = 'column';
1918
+
1919
+ btnHide.onclick = () => {
1920
+ try {
1921
+ bar.classList.add('hidden');
1922
+ bar.setAttribute('aria-hidden', 'true');
1923
+ bar.style.setProperty('display', 'none', 'important');
1924
+ bar.style.setProperty('opacity', '0', 'important');
1925
+ bar.style.setProperty('height', '0px', 'important');
1926
+ bar.style.setProperty('pointer-events', 'none', 'important');
1927
+
1928
+ const pageTop = document.querySelector('.top-bar');
1929
+ if (pageTop) {
1930
+ pageTop.classList.add('hidden');
1931
+ pageTop.setAttribute('aria-hidden', 'true');
1932
+ }
1933
+ playSound('click');
1934
+
1935
+ let restore = document.getElementById('restoreBarBtn');
1936
+ if (!restore) {
1937
+ restore = document.createElement('button');
1938
+ restore.id = 'restoreBarBtn';
1939
+ restore.className = 'restore-bar-btn';
1940
+ restore.innerHTML = '▾';
1941
+ restore.onclick = () => {
1942
+ bar.classList.remove('hidden');
1943
+ bar.removeAttribute('aria-hidden');
1944
+
1945
+ bar.style.removeProperty('display');
1946
+ bar.style.removeProperty('opacity');
1947
+ bar.style.removeProperty('height');
1948
+ bar.style.removeProperty('padding');
1949
+ bar.style.removeProperty('pointer-events');
1950
+ bar.style.removeProperty('visibility');
1951
+
1952
+ bar.style.setProperty('display', 'flex', 'important');
1953
+ bar.style.setProperty('opacity', '1', 'important');
1954
+ bar.style.setProperty('height', '40px', 'important');
1955
+ bar.style.setProperty('pointer-events', 'auto', 'important');
1956
+
1957
+ const pt = document.querySelector('.top-bar');
1958
+ if (pt) {
1959
+ pt.classList.remove('hidden');
1960
+ pt.removeAttribute('aria-hidden');
1961
+ pt.style.display = '';
1962
+ }
1963
+ try { restore.remove(); } catch(e) {}
1964
+ playSound('select');
1965
+ };
1966
+ document.body.appendChild(restore);
1967
+ setTimeout(() => restore.classList.add('visible'), 10);
1968
+ }
1969
+ } catch (err) {
1970
+ console.warn('Error in hide bar handler:', err);
1971
+ }
1972
+ };
1973
+
1974
+ if (game.iframe) {
1975
+ const frame = document.createElement('iframe');
1976
+ frame.style.width = '100%';
1977
+ frame.style.height = '100%';
1978
+ frame.style.flex = '1';
1979
+ frame.style.border = 'none';
1980
+
1981
+ fetch(game.path)
1982
+ .then(response => response.text())
1983
+ .then(html => {
1984
+ frame.srcdoc = html;
1985
+ })
1986
+ .catch(err => {
1987
+ console.error("Iframe fetch failed:", err);
1988
+ frame.src = game.path;
1989
+ });
1990
+
1991
+ frameWrapper.appendChild(frame);
1992
+ } else {
1993
+ frameWrapper.style.display = 'flex';
1994
+ frameWrapper.style.flexDirection = 'column';
1995
+ frameWrapper.style.justifyContent = 'center';
1996
+ frameWrapper.style.alignItems = 'center';
1997
+ frameWrapper.style.textAlign = 'center';
1998
+ frameWrapper.style.background = '#000';
1999
+ const messageDiv = document.createElement('div');
2000
+ messageDiv.style.padding = '50px';
2001
+ messageDiv.style.maxWidth = '80%';
2002
+ const messageP = document.createElement('p');
2003
+ messageP.textContent = "Due to issues, this game is not supported here.";
2004
+ messageP.style.fontSize = '1.8rem';
2005
+ messageP.style.color = 'white';
2006
+ const bigAboutBlankBtn = btnAboutBlank.cloneNode(true);
2007
+ bigAboutBlankBtn.textContent = 'Open in About:blank';
2008
+ bigAboutBlankBtn.style.marginTop = '20px';
2009
+ bigAboutBlankBtn.onclick = openAboutBlank;
2010
+ messageDiv.appendChild(messageP);
2011
+ messageDiv.appendChild(bigAboutBlankBtn);
2012
+ frameWrapper.appendChild(messageDiv);
2013
+ }
2014
+
2015
+ modal.appendChild(bar);
2016
+ modal.appendChild(frameWrapper);
2017
+ document.body.appendChild(modal);
2018
+
2019
+ setTimeout(() => {
2020
+ try {
2021
+ const iframeEl = frameWrapper.querySelector('iframe');
2022
+ if (iframeEl) { iframeEl.focus(); }
2023
+ } catch(e) {}
2024
+ }, 50);
2025
+
2026
+ promoteToRecent(game);
2027
+ }
2028
+ function updateFavoriteTile(game) {
2029
+ const tile = document.getElementById('favoriteTile');
2030
+ const label = document.getElementById('favoriteLabel');
2031
+ const status = document.getElementById('favoriteStatus');
2032
+
2033
+ tile.style.transition = 'background 0.25s ease, border 0.15s ease';
2034
+
2035
+ if (!game || game.systemType) {
2036
+ tile.style.background = 'linear-gradient(135deg, rgba(0,0,0,0.6), rgba(0,0,0,0.4))';
2037
+ tile.style.border = 'none';
2038
+ label.textContent = "Favorite this game";
2039
+ status.textContent = "Unavailable for system tiles";
2040
+ tile.onclick = null;
2041
+ return;
2042
+ }
2043
+
2044
+ const accent = (getComputedStyle(document.body).getPropertyValue('--ps-light-blue') || '#00a0e9').trim();
2045
+ const isFav = favoritesSet.has(game.title);
2046
+
2047
+ if (isFav) {
2048
+ tile.style.background = `linear-gradient(135deg, ${accent}, rgba(0,0,0,0.35))`;
2049
+ tile.style.border = `2px solid ${accent}`;
2050
+ label.textContent = "Favorited";
2051
+ status.textContent = "Click to remove from favorites";
2052
+ } else {
2053
+ tile.style.background = `linear-gradient(135deg, rgba(0,0,0,0.6), ${accent})`;
2054
+ tile.style.border = '1px solid rgba(255,255,255,0.06)';
2055
+ label.textContent = "Favorite this game";
2056
+ status.textContent = "Click to add to favorites";
2057
+ }
2058
+
2059
+ tile.onclick = () => {
2060
+ playSound('select');
2061
+ if (favoritesSet.has(game.title)) {
2062
+ favoritesSet.delete(game.title);
2063
+ } else {
2064
+ favoritesSet.add(game.title);
2065
+ }
2066
+ saveFavorites();
2067
+ updateFavoriteTile(game);
2068
+ };
2069
+ }
2070
+
2071
+
2072
+ setInterval(() => {
2073
+ const now = new Date();
2074
+ const h = String(now.getHours()).padStart(2, '0');
2075
+ const m = String(now.getMinutes()).padStart(2, '0');
2076
+ document.getElementById('clock').innerText = `${h}:${m}`;
2077
+ }, 1000);
2078
+
2079
+
2080
+ window.addEventListener('keydown', (e) => {
2081
+ const modalOpen = !!document.querySelector('.game-modal');
2082
+
2083
+ if (isLibraryOpen || isOtherOpen || modalOpen) {
2084
+ if(e.key === 'Escape') {
2085
+ if (isLibraryOpen) {
2086
+ libSearchInput.value = '';
2087
+ currentCategoryFilter = 'All';
2088
+ renderCategoryButtons();
2089
+ renderOverlayGrid();
2090
+ closeOverlay();
2091
+ } else if (isOtherOpen) {
2092
+ toggleOther(false);
2093
+ } else if (modalOpen) {
2094
+ // Close the modal via its close button if present, otherwise remove it
2095
+ const modalEl = document.querySelector('.game-modal');
2096
+ if (modalEl) {
2097
+ const closeBtn = modalEl.querySelector('.close-btn');
2098
+ if (closeBtn) {
2099
+ closeBtn.click();
2100
+ } else {
2101
+ modalEl.remove();
2102
+ }
2103
+ }
2104
+
2105
+ // Make sure we return the selection to the first game after closing
2106
+ try { updateSelection(4); } catch (e) { console.warn('updateSelection on Escape close failed', e); }
2107
+ }
2108
+ }
2109
+ return;
2110
+ }
2111
+
2112
+ const oldIndex = selectedIndex;
2113
+
2114
+ if(e.key === 'ArrowRight') updateSelection(selectedIndex + 1);
2115
+ if(e.key === 'ArrowLeft') updateSelection(selectedIndex - 1);
2116
+
2117
+ if ((e.key === 'ArrowRight' || e.key === 'ArrowLeft') && selectedIndex !== oldIndex) {
2118
+ playSound('hover');
2119
+ }
2120
+
2121
+ if(e.key === 'Enter') activateItem(selectedIndex);
2122
+ });
2123
+
2124
+
2125
+
2126
+ function setFavicon(url) {
2127
+ let link = document.querySelector("link[rel~='icon']");
2128
+ if (!link) {
2129
+ link = document.createElement('link');
2130
+ link.rel = 'icon';
2131
+ document.head.appendChild(link);
2132
+ }
2133
+ link.href = url;
2134
+ }
2135
+
2136
+ function applyCloakPreset(title, icon) {
2137
+ document.title = title;
2138
+ setFavicon(icon);
2139
+ localStorage.setItem('alexrGames_cloak_title', title);
2140
+ localStorage.setItem('alexrGames_cloak_icon', icon);
2141
+ }
2142
+
2143
+ function applyCustomCloak() {
2144
+ const title = document.getElementById('cloakTitleInput').value.trim();
2145
+ const icon = document.getElementById('cloakIconInput').value.trim();
2146
+ if (title) {
2147
+ document.title = title;
2148
+ localStorage.setItem('alexrGames_cloak_title', title);
2149
+ }
2150
+ if (icon) {
2151
+ setFavicon(icon);
2152
+ localStorage.setItem('alexrGames_cloak_icon', icon);
2153
+ }
2154
+ }
2155
+
2156
+
2157
+ function resetCloak() {
2158
+
2159
+ document.title = "Alexr's World";
2160
+ const icon = document.querySelector("link[rel~='icon']");
2161
+ if (icon) icon.remove();
2162
+ localStorage.removeItem('alexrGames_cloak_title');
2163
+ localStorage.removeItem('alexrGames_cloak_icon');
2164
+
2165
+
2166
+ localStorage.removeItem('alexrGames_theme');
2167
+ localStorage.removeItem('alexrGames_custom_background');
2168
+
2169
+
2170
+ if (typeof setTheme === 'function') {
2171
+ setTheme('default');
2172
+ } else {
2173
+
2174
+ document.body.className = document.body.className.split(' ').filter(c => !c.startsWith('theme-')).join(' ');
2175
+ document.body.classList.add('theme-default');
2176
+ }
2177
+
2178
+
2179
+ localStorage.removeItem('alexrGames_ps5_bg_enabled');
2180
+ document.body.classList.remove('ps5-background-enabled');
2181
+
2182
+ const ps5ToggleButton = document.getElementById('ps5BgToggle');
2183
+ if (ps5ToggleButton) {
2184
+ ps5ToggleButton.textContent = 'PS5 Background: OFF';
2185
+ }
2186
+
2187
+
2188
+ localStorage.removeItem('alexrGames_sound_enabled');
2189
+ localStorage.removeItem('alexrGames_ambience_volume');
2190
+
2191
+ if (typeof toggleSound === 'function') {
2192
+
2193
+ }
2194
+ if (typeof updateAmbienceVolume === 'function') {
2195
+ updateAmbienceVolume(0.5);
2196
+ }
2197
+
2198
+
2199
+ alert("All settings (Cloaking, Theme, Sound, PS5 BG) reset to default! The page will now reload.");
2200
+ window.location.reload();
2201
+ }
2202
+ function loadCloak() {
2203
+ const t = localStorage.getItem('alexrGames_cloak_title');
2204
+ const i = localStorage.getItem('alexrGames_cloak_icon');
2205
+ if (t) document.title = t;
2206
+ if (i) setFavicon(i);
2207
+ }
2208
+
2209
+
2210
+ loadCloak();
2211
+ let lastWheelTime = 0;
2212
+ const wheelCooldownMs = 120;
2213
+
2214
+ window.addEventListener('wheel', (e) => {
2215
+ try {
2216
+ if (isLibraryOpen || isOtherOpen) return;
2217
+ if (document.querySelector('.game-modal')) return;
2218
+
2219
+ const now = Date.now();
2220
+ if (now - lastWheelTime < wheelCooldownMs) return;
2221
+
2222
+ const delta = e.deltaY || e.wheelDelta || 0;
2223
+ if (Math.abs(delta) < 5) return;
2224
+
2225
+ if (e.preventDefault) e.preventDefault();
2226
+
2227
+ let nextIndex;
2228
+ if (delta > 0) {
2229
+ nextIndex = (selectedIndex >= displayList.length - 1) ? 0 : selectedIndex + 1;
2230
+ } else {
2231
+ nextIndex = (selectedIndex <= 0) ? displayList.length - 1 : selectedIndex - 1;
2232
+ }
2233
+
2234
+ updateSelection(nextIndex);
2235
+
2236
+ playSound('hover');
2237
+
2238
+ lastWheelTime = now;
2239
+ } catch (err) {
2240
+ console.warn('Wheel navigation error:', err);
2241
+ }
2242
+ }, { passive: false });
2243
+
2244
+
2245
+ (function(){
2246
+ let touchStartX = 0;
2247
+ let touchStartY = 0;
2248
+ let touchStartTime = 0;
2249
+ let touchMoved = false;
2250
+ const swipeThreshold = 40;
2251
+ const swipeTime = 500;
2252
+
2253
+ carousel.addEventListener('touchstart', (e) => {
2254
+ if (isLibraryOpen || isOtherOpen || document.querySelector('.game-modal')) return;
2255
+ const t = e.touches[0];
2256
+ touchStartX = t.clientX;
2257
+ touchStartY = t.clientY;
2258
+ touchStartTime = Date.now();
2259
+ touchMoved = false;
2260
+ }, { passive: true });
2261
+
2262
+ carousel.addEventListener('touchmove', (e) => {
2263
+ if (!touchStartX) return;
2264
+ const t = e.touches[0];
2265
+ const dx = Math.abs(t.clientX - touchStartX);
2266
+ const dy = Math.abs(t.clientY - touchStartY);
2267
+ if (dx > 10 && dx > dy) {
2268
+ e.preventDefault && e.preventDefault();
2269
+ touchMoved = true;
2270
+ }
2271
+ }, { passive: false });
2272
+
2273
+ carousel.addEventListener('touchend', (e) => {
2274
+ if (!touchStartTime) return;
2275
+ const t = e.changedTouches[0];
2276
+ const dx = t.clientX - touchStartX;
2277
+ const dt = Date.now() - touchStartTime;
2278
+ touchStartX = 0; touchStartY = 0; touchStartTime = 0;
2279
+
2280
+ if (Math.abs(dx) > swipeThreshold && dt < swipeTime && touchMoved) {
2281
+ if (dx < 0) {
2282
+ updateSelection(Math.min(displayList.length - 1, selectedIndex + 1));
2283
+ } else {
2284
+ updateSelection(Math.max(0, selectedIndex - 1));
2285
+ }
2286
+ playSound('hover');
2287
+ }
2288
+ }, { passive: true });
2289
+ })();
2290
+
2291
+ loadTheme();
2292
+ loadGames();
2293
+ loadPS5BackgroundSetting();
2294
+ loadSoundSettings();
2295
+
2296
+ (function(){
2297
+ const bodyObserver = new MutationObserver((mutations) => {
2298
+ for (const m of mutations) {
2299
+ for (const n of m.removedNodes) {
2300
+ if (n && n.nodeType === 1 && n.classList && n.classList.contains('game-modal')) {
2301
+ const r = document.getElementById('restoreBarBtn'); if (r) try { r.remove(); } catch(e) {}
2302
+ const pageTop = document.querySelector('.top-bar'); if (pageTop) pageTop.classList.remove('hidden');
2303
+
2304
+ try { updateSelection(4); } catch(e) { console.warn('updateSelection after mutation failed', e); }
2305
+ }
2306
+ }
2307
+ }
2308
+ });
2309
+ bodyObserver.observe(document.body, { childList: true });
2310
+ })();
2311
+ </script>
2312
+
2313
+ <style>
2314
+
2315
+ body.theme-red {
2316
+ background-color: #3a0000 !important;
2317
+ background-image:
2318
+ radial-gradient(circle at 20% 0, rgba(255,80,80,0.25) 0%, transparent 55%),
2319
+ radial-gradient(circle at 80% 100%, rgba(180,0,0,0.35) 0%, transparent 55%),
2320
+ linear-gradient(135deg, #3a0000, #7a0000) !important;
2321
+ --ps-light-blue: #ff4d4d !important;
2322
+ }
2323
+
2324
+ .lib-card.dragging {
2325
+ opacity: 0.4;
2326
+ transform: scale(0.95);
2327
+ }
2328
+ .lib-card.drag-over {
2329
+ outline: 3px solid var(--ps-light-blue);
2330
+ }
61
2331
 
62
- })();
63
- </script>
2332
+ </style>
64
2333
 
65
2334
  </body>
66
- </ grass
67
- </html>
2335
+ </html>