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