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/backup.html ADDED
@@ -0,0 +1,2016 @@
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 Games</title>
7
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
8
+ <style>
9
+ /* --- PS4 SYSTEM FONTS & VARS --- */
10
+ :root {
11
+ --ps-blue: #00439c;
12
+ --ps-light-blue: #00a0e9; /* Default accent color */
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
+ /* Theme variants (backgrounds and accent tweaks) */
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; /* Ensure default is set */
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
+ /* MODIFIED: Red theme background update */
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; /* Use red accent color */
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
+
72
+ body.theme-blue-dark {
73
+ background-color: #020b1a;
74
+ background-image: linear-gradient(135deg,#020b1a,#012b45);
75
+ --ps-light-blue: #44aaff;
76
+ }
77
+
78
+ /* MODIFIED: Define theme accent variable for use in library/slider */
79
+ body.theme-hacker {
80
+ background-color: #000000;
81
+ background-image: radial-gradient(circle at 20% 0, rgba(0,255,0,0.25) 0%, transparent 55%),
82
+ radial-gradient(circle at 80% 100%, rgba(0,255,0,0.25) 0%, transparent 55%);
83
+ color: #00ff88;
84
+ --ps-light-blue: #00ff88; /* ACCENT COLOR FOR CATEGORY BUTTONS/SLIDER */
85
+ }
86
+
87
+ /* MODIFIED: Define theme accent variable for use in library/slider */
88
+ body.theme-tokyo {
89
+ background-color: #050016;
90
+ background-image: radial-gradient(circle at 10% 0, rgba(255,0,128,0.35) 0%, transparent 55%),
91
+ radial-gradient(circle at 90% 100%, rgba(0,255,255,0.35) 0%, transparent 55%),
92
+ linear-gradient(135deg,#050016,#001133);
93
+ color: #e3f2fd; /* Text color remains light */
94
+ --ps-light-blue: #ff4aff; /* ACCENT COLOR FOR CATEGORY BUTTONS/SLIDER */
95
+ }
96
+
97
+ /* Custom uploaded background (covers themes if present) */
98
+ body.has-custom-bg {
99
+ background-size: cover;
100
+ background-position: center center;
101
+ background-repeat: no-repeat;
102
+ animation: none;
103
+ }
104
+
105
+ /* PS5-style game background */
106
+ .game-bg-overlay {
107
+ position: fixed;
108
+ top: 0;
109
+ left: 0;
110
+ width: 100%;
111
+ height: 100%;
112
+ z-index: -1;
113
+ opacity: 0;
114
+ transition: opacity 0.6s ease;
115
+ pointer-events: none;
116
+ background-size: cover;
117
+ background-position: center;
118
+ background-repeat: no-repeat;
119
+ filter: blur(20px) brightness(0.4);
120
+ transform: scale(1.1);
121
+ }
122
+
123
+ body.ps5-bg-enabled .game-bg-overlay.active {
124
+ opacity: 1;
125
+ }
126
+
127
+ @keyframes bgFlow {
128
+ 0% { background-position: 0% 50%; }
129
+ 50% { background-position: 100% 50%; }
130
+ 100% { background-position: 0% 50%; }
131
+ }
132
+
133
+ /* --- TOP NAVIGATION BAR --- */
134
+ .top-bar {
135
+ display: flex;
136
+ justify-content: space-between;
137
+ align-items: center;
138
+ padding: 30px 60px;
139
+ font-size: 1.5rem;
140
+ opacity: 0.9;
141
+ text-shadow: 1px 1px 2px rgba(0,0,0,0.5);
142
+ }
143
+
144
+ .site-title {
145
+ font-weight: 300;
146
+ font-size: 1.8rem;
147
+ letter-spacing: 1px;
148
+ display: flex;
149
+ align-items: center;
150
+ }
151
+
152
+ .site-title img {
153
+ height: 30px;
154
+ margin-right: 10px;
155
+ }
156
+
157
+ .clock-area {
158
+ font-size: 1.2rem;
159
+ font-weight: 300;
160
+ display: flex;
161
+ align-items: center;
162
+ gap: 20px;
163
+ }
164
+
165
+ /* Social Media Icons */
166
+ .social-icons a {
167
+ color: inherit; /* Inherit color from body (e.g., #00ff88 for hacker) */
168
+ transition: color 0.2s;
169
+ }
170
+ .social-icons a:hover {
171
+ color: var(--ps-light-blue); /* Use accent color on hover */
172
+ }
173
+ .social-icons {
174
+ display: flex;
175
+ gap: 15px;
176
+ font-size: 1.4rem;
177
+ color: white; /* Default color */
178
+ }
179
+ body.theme-hacker .social-icons { color: #00ff88; }
180
+ body.theme-tokyo .social-icons { color: #ff4aff; }
181
+ body.theme-red .social-icons { color: var(--ps-light-blue); } /* Apply theme color */
182
+
183
+
184
+ /* --- MAIN CAROUSEL AREA --- */
185
+ .main-stage {
186
+ margin-top: 40px;
187
+ width: 100%;
188
+ position: relative;
189
+ }
190
+
191
+ .carousel-container {
192
+ display: flex;
193
+ align-items: center;
194
+ padding-left: 60px;
195
+ gap: 15px;
196
+ transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
197
+ will-change: transform;
198
+ }
199
+
200
+ .card {
201
+ width: var(--card-size);
202
+ height: var(--card-size);
203
+ background: rgba(0,0,0,0.3);
204
+ flex-shrink: 0;
205
+ position: relative;
206
+ transition: all var(--anim-speed) ease;
207
+ display: flex;
208
+ flex-direction: column;
209
+ justify-content: flex-end;
210
+ overflow: visible;
211
+ cursor: pointer;
212
+ box-shadow: 0 4px 10px rgba(0,0,0,0.3);
213
+ /* Drag-and-drop */
214
+ touch-action: pan-y; /* Allow vertical scroll, but let JS handle horizontal drag */
215
+ }
216
+
217
+ /* NEW: Dragging state for cards */
218
+ .card:not(.system-card) {
219
+ /* Only game cards are draggable */
220
+ cursor: grab;
221
+ }
222
+
223
+ .card.dragging {
224
+ opacity: 0.5;
225
+ border: 2px dashed var(--ps-light-blue);
226
+ box-shadow: none;
227
+ transform: translateY(0); /* Remove selection lift */
228
+ cursor: grabbing !important;
229
+ }
230
+
231
+
232
+ body.theme-hacker .card {
233
+ background: rgba(0,20,0,0.7);
234
+ }
235
+ body.theme-tokyo .card {
236
+ background: rgba(10,0,30,0.7);
237
+ }
238
+
239
+ .card img {
240
+ width: 100%;
241
+ height: 100%;
242
+ object-fit: cover;
243
+ opacity: 0.8;
244
+ transition: opacity 0.2s;
245
+ pointer-events: none; /* Ignore pointer events on img for better drag/touch */
246
+ }
247
+
248
+ /* System Cards (Settings, Favorites, Library) */
249
+ .card.system-card {
250
+ background: linear-gradient(135deg, #0b407a, #001f4d);
251
+ display: flex;
252
+ align-items: center;
253
+ justify-content: center;
254
+ border: 1px solid rgba(255,255,255,0.1);
255
+ cursor: pointer; /* System cards are not draggable */
256
+ }
257
+ body.theme-hacker .card.system-card {
258
+ background: linear-gradient(135deg,#004400,#001400);
259
+ border: 1px solid rgba(0,255,136,0.3);
260
+ }
261
+ body.theme-tokyo .card.system-card {
262
+ background: linear-gradient(135deg,#ff007a, #3b00b3);
263
+ border: 1px solid rgba(255,74,255,0.3);
264
+ }
265
+ body.theme-red .card.system-card {
266
+ background: linear-gradient(135deg,#7a0000, #3a0000);
267
+ border: 1px solid rgba(255,77,77,0.3);
268
+ }
269
+
270
+ .card.system-card i { font-size: 3rem; color: white; opacity: 0.8; }
271
+ body.theme-hacker .card.system-card i,
272
+ body.theme-tokyo .card.system-card i,
273
+ body.theme-red .card.system-card i {
274
+ color: inherit; /* Use themed color */
275
+ opacity: 1;
276
+ }
277
+
278
+
279
+ /* --- SELECTED STATE --- */
280
+ .card.active {
281
+ width: var(--card-selected);
282
+ height: var(--card-selected);
283
+ transform: translateY(10px);
284
+ background: rgba(255,255,255,0.1);
285
+ box-shadow: 0 0 0 4px white, 0 10px 20px rgba(0,0,0,0.5);
286
+ z-index: 10;
287
+ }
288
+
289
+ body.theme-hacker .card.active {
290
+ box-shadow: 0 0 0 4px #00ff88, 0 10px 20px rgba(0,0,0,0.8);
291
+ }
292
+ body.theme-tokyo .card.active {
293
+ box-shadow: 0 0 0 4px var(--ps-light-blue), 0 10px 20px rgba(0,0,0,0.8);
294
+ }
295
+
296
+ .card.active img { opacity: 1; }
297
+
298
+ /* --- INFO AREA (Below Carousel) --- */
299
+ .info-area {
300
+ margin-top: 80px;
301
+ padding-left: 60px;
302
+ opacity: 0;
303
+ transform: translateY(20px);
304
+ transition: all 0.3s ease;
305
+ }
306
+
307
+ .info-area.visible {
308
+ opacity: 1;
309
+ transform: translateY(0);
310
+ }
311
+
312
+ /* NEW: Hide sub-content when system tile is selected */
313
+ .sub-content.hidden {
314
+ display: none !important;
315
+ }
316
+
317
+ .game-title {
318
+ font-size: 2.2rem;
319
+ font-weight: 300;
320
+ margin-bottom: 5px;
321
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.6);
322
+ }
323
+
324
+ .game-meta {
325
+ display: flex;
326
+ gap: 15px;
327
+ font-size: 0.9rem;
328
+ color: rgba(255,255,255,0.8);
329
+ margin-bottom: 25px;
330
+ text-transform: uppercase;
331
+ letter-spacing: 1px;
332
+ }
333
+
334
+ /* --- SUB CONTENT ROW --- */
335
+ .sub-content {
336
+ display: flex;
337
+ gap: 20px;
338
+ margin-top: 20px;
339
+ }
340
+
341
+ .sub-tile {
342
+ width: 300px;
343
+ height: 160px;
344
+ background: rgba(0,0,0,0.4);
345
+ display: flex;
346
+ flex-direction: column;
347
+ justify-content: flex-end;
348
+ padding: 15px;
349
+ position: relative;
350
+ overflow: hidden;
351
+ border-radius: 4px;
352
+ }
353
+ .sub-tile img {
354
+ position: absolute; top:0; left:0; width:100%; height:100%; object-fit: cover; opacity: 0.5; z-index: 0;
355
+ }
356
+ .sub-tile div { z-index: 1; position: relative; text-shadow: 1px 1px 2px black; }
357
+ .sub-head { font-weight: bold; font-size: 1.1rem; }
358
+ .sub-desc { font-size: 0.8rem; color: #ccc; }
359
+
360
+ #favoriteTile {
361
+ cursor: pointer;
362
+ }
363
+
364
+ /* --- LIBRARY & SETTINGS OVERLAY (Grid / Tabs View) --- */
365
+ .library-overlay {
366
+ position: fixed;
367
+ inset: 0;
368
+ background: rgba(11, 23, 48, 0.98);
369
+ z-index: 1000;
370
+ display: none;
371
+ flex-direction: column;
372
+ padding: 40px 80px;
373
+ overflow-y: auto;
374
+ opacity: 0;
375
+ transition: opacity 0.3s ease;
376
+ }
377
+
378
+ body.theme-hacker .library-overlay {
379
+ background: rgba(0,0,0,0.96);
380
+ }
381
+ body.theme-tokyo .library-overlay {
382
+ background: rgba(5,0,25,0.96);
383
+ }
384
+ body.theme-red .library-overlay {
385
+ background: rgba(58,0,0,0.96);
386
+ }
387
+
388
+ .library-overlay.show { display: flex; opacity: 1; }
389
+
390
+ .lib-header {
391
+ display: flex;
392
+ justify-content: space-between;
393
+ align-items: center;
394
+ margin-bottom: 20px;
395
+ border-bottom: 1px solid rgba(255,255,255,0.1);
396
+ padding-bottom: 20px;
397
+ }
398
+ .lib-title { font-size: 2rem; font-weight: 300; }
399
+ .close-btn {
400
+ background: none; border: 1px solid white; color: white;
401
+ padding: 10px 20px; cursor: pointer; border-radius: 4px; font-size: 1rem;
402
+ transition: background 0.2s;
403
+ }
404
+ .close-btn:hover { background: white; color: black; }
405
+
406
+ /* New: Filter area styles */
407
+ .lib-filters {
408
+ margin-bottom: 30px;
409
+ }
410
+
411
+ .lib-search {
412
+ width: 100%;
413
+ padding: 15px;
414
+ margin-bottom: 15px;
415
+ font-size: 1.2rem;
416
+ border: 2px solid rgba(255,255,255,0.5);
417
+ background: rgba(0,0,0,0.2);
418
+ color: white;
419
+ border-radius: 4px;
420
+ outline: none;
421
+ transition: border-color 0.2s;
422
+ }
423
+ .lib-search:focus {
424
+ border-color: var(--ps-light-blue);
425
+ }
426
+
427
+ .lib-categories {
428
+ display: flex;
429
+ gap: 10px;
430
+ flex-wrap: wrap;
431
+ }
432
+
433
+ .category-btn {
434
+ background: rgba(255,255,255,0.1);
435
+ border: 1px solid rgba(255,255,255,0.3);
436
+ color: white;
437
+ padding: 8px 16px;
438
+ cursor: pointer;
439
+ border-radius: 20px;
440
+ font-size: 0.9rem;
441
+ transition: background 0.2s, color 0.2s, border-color 0.2s;
442
+ }
443
+
444
+ .category-btn.active {
445
+ background: var(--ps-light-blue); /* Uses theme accent color */
446
+ border-color: var(--ps-light-blue); /* Uses theme accent color */
447
+ font-weight: bold;
448
+ }
449
+ .category-btn:hover {
450
+ background: rgba(255,255,255,0.3);
451
+ }
452
+
453
+ /* End New: Filter area styles */
454
+
455
+
456
+ .lib-grid {
457
+ display: grid;
458
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
459
+ gap: 25px;
460
+ }
461
+
462
+ .lib-card {
463
+ aspect-ratio: 1/1;
464
+ background: #222;
465
+ border-radius: 4px;
466
+ position: relative;
467
+ cursor: pointer;
468
+ transition: transform 0.2s;
469
+ overflow: hidden;
470
+ }
471
+ body.theme-hacker .lib-card { background: #011 !box-shadow: none; }
472
+ body.theme-tokyo .lib-card { background: #102; box-shadow: none; }
473
+ body.theme-red .lib-card { background: #200; box-shadow: none; } /* Red theme adjustment */
474
+
475
+ .lib-card:hover { transform: scale(1.05); box-shadow: 0 5px 15px rgba(0,0,0,0.5); z-index: 2;}
476
+ .lib-card img { width: 100%; height: 100%; object-fit: cover; }
477
+ .lib-card-title {
478
+ position: absolute; bottom: 0; left: 0; right: 0;
479
+ background: rgba(0,0,0,0.8); padding: 8px; font-size: 0.85rem;
480
+ text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
481
+ }
482
+
483
+ /* Fullscreen iframe fix - REPLACED VERSION */
484
+ .game-modal {
485
+ position: fixed !important;
486
+ top: 0 !important;
487
+ left: 0 !important;
488
+ width: 100vw !important;
489
+ height: 100vh !important;
490
+ margin: 0 !important;
491
+ padding: 0 !important;
492
+ background: black !important;
493
+ z-index: 9999 !important;
494
+ display: flex !important;
495
+ flex-direction: column !important;
496
+ border: none !important;
497
+ box-sizing: border-box !important;
498
+ }
499
+
500
+ .game-modal > div:first-child {
501
+ position: relative !important;
502
+ height: 40px !important;
503
+ flex-shrink: 0 !important;
504
+ z-index: 10000 !important;
505
+ background: #222 !important;
506
+ display: flex !important;
507
+ align-items: center !important;
508
+ padding: 0 20px !important;
509
+ justify-content: space-between !important;
510
+ color: white !important;
511
+ gap: 10px !important;
512
+ }
513
+ /* Ensure the bar background is themed or dark */
514
+ body.theme-hacker .game-modal > div:first-child { background: #000 !important; color: #00ff88 !important; }
515
+ body.theme-tokyo .game-modal > div:first-child { background: #111 !important; color: #ff4aff !important; }
516
+ body.theme-red .game-modal > div:first-child { background: #3a0000 !important; color: var(--ps-light-blue) !important; } /* Red theme fix */
517
+
518
+ .game-modal > div:last-child {
519
+ position: relative !important;
520
+ flex: 1 !important;
521
+ width: 100% !important;
522
+ height: calc(100vh - 40px) !important;
523
+ }
524
+
525
+ .game-modal iframe {
526
+ position: absolute !important;
527
+ top: 0 !important;
528
+ left: 0 !important;
529
+ width: 100% !important;
530
+ height: 100% !important;
531
+ border: none !important;
532
+ }
533
+
534
+ /* Updated styles for iframe buttons to inherit site colors/theme better */
535
+ .game-modal button,
536
+ .game-modal .close-btn {
537
+ /* Use current color properties which change with the theme */
538
+ background: rgba(255,255,255,0.2) !important;
539
+ border: 1px solid currentColor !important; /* Uses the current text color */
540
+ color: currentColor !important; /* Uses the current text color */
541
+ padding: 4px 12px !important;
542
+ border-radius: 4px !important;
543
+ cursor: pointer !important;
544
+ font-size: 0.85rem !important;
545
+ opacity: 1 !important;
546
+ visibility: visible !important;
547
+ display: inline-block !important;
548
+ z-index: 10001 !important;
549
+ }
550
+
551
+ .game-modal button:hover {
552
+ background: white !important;
553
+ color: black !important;
554
+ }
555
+
556
+ /* Fix hide bar button - ADD THIS */
557
+ .game-modal .top-bar {
558
+ position: relative !important;
559
+ z-index: 10000 !important;
560
+ transition: opacity 0.2s ease !important;
561
+ }
562
+
563
+ .game-modal .show-bar-btn {
564
+ position: absolute !important;
565
+ top: 5px !important;
566
+ left: 5px !important;
567
+ z-index: 10001 !important;
568
+ width: 36px !important;
569
+ height: 36px !important;
570
+ padding: 0 !important;
571
+ border: 1px solid #666 !important;
572
+ background: rgba(0,0,0,0.9) !important;
573
+ color: white !important;
574
+ border-radius: 6px !important;
575
+ font-size: 1.2rem !important;
576
+ font-weight: bold !important;
577
+ cursor: pointer !important;
578
+ display: none !important;
579
+ line-height: 1 !important;
580
+ }
581
+
582
+ /* Targeting the specific element for display: none */
583
+ .game-modal > div:first-child[style*="display: none"] ~ div .show-bar-btn {
584
+ display: block !important;
585
+ }
586
+
587
+
588
+ /* Other Things tabs buttons row */
589
+ .other-tabs {
590
+ display:flex; gap:10px; margin-bottom:20px;
591
+ flex-wrap: wrap;
592
+ }
593
+
594
+ .tab-btn {
595
+ background: none;
596
+ border: 1px solid white;
597
+ color: white;
598
+ padding: 8px 16px;
599
+ cursor: pointer;
600
+ border-radius: 4px;
601
+ font-size: 0.9rem;
602
+ transition: background 0.2s, color 0.2s;
603
+ }
604
+
605
+ .tab-btn.active,
606
+ .tab-btn:hover {
607
+ background: white;
608
+ color: black;
609
+ }
610
+
611
+ /* NEW: Styling for settings input/select */
612
+ .settings-group {
613
+ margin-bottom: 20px;
614
+ padding-bottom: 20px;
615
+ border-bottom: 1px solid rgba(255,255,255,0.1);
616
+ }
617
+ .settings-group:last-child {
618
+ border-bottom: none;
619
+ }
620
+
621
+ .settings-group label {
622
+ display: block;
623
+ font-size: 0.9rem;
624
+ color: #ccc;
625
+ margin-bottom: 5px;
626
+ }
627
+
628
+ .settings-input,
629
+ .settings-select {
630
+ width: 100%;
631
+ max-width: 400px;
632
+ padding: 10px;
633
+ font-size: 1rem;
634
+ border: 2px solid rgba(255,255,255,0.5);
635
+ background: rgba(0,0,0,0.2);
636
+ color: white;
637
+ border-radius: 4px;
638
+ outline: none;
639
+ transition: border-color 0.2s;
640
+ box-sizing: border-box;
641
+ }
642
+
643
+ .settings-input:focus,
644
+ .settings-select:focus {
645
+ border-color: var(--ps-light-blue);
646
+ }
647
+ .settings-select option {
648
+ background: #000;
649
+ color: white;
650
+ }
651
+ /* END NEW: Styling for settings input/select */
652
+
653
+
654
+ /* Upload input hidden; use label button */
655
+ #bgUploadInput {
656
+ display:none;
657
+ }
658
+
659
+ /* NEW CSS for Sound Toggle Switch */
660
+ .sound-control {
661
+ display: flex;
662
+ align-items: center;
663
+ gap: 15px;
664
+ margin-top: 15px;
665
+ }
666
+
667
+ .switch {
668
+ position: relative;
669
+ display: inline-block;
670
+ width: 60px;
671
+ height: 34px;
672
+ }
673
+
674
+ .switch input {
675
+ opacity: 0;
676
+ width: 0;
677
+ height: 0;
678
+ }
679
+
680
+ .slider {
681
+ position: absolute;
682
+ cursor: pointer;
683
+ top: 0;
684
+ left: 0;
685
+ right: 0;
686
+ bottom: 0;
687
+ background-color: #ccc;
688
+ transition: .4s;
689
+ border-radius: 34px;
690
+ }
691
+
692
+ .slider:before {
693
+ position: absolute;
694
+ content: "";
695
+ height: 26px;
696
+ width: 26px;
697
+ left: 4px;
698
+ bottom: 4px;
699
+ background-color: white;
700
+ transition: .4s;
701
+ border-radius: 50%;
702
+ }
703
+
704
+ input:checked + .slider {
705
+ background-color: var(--ps-light-blue); /* Uses theme accent color */
706
+ }
707
+
708
+ input:focus + .slider {
709
+ box-shadow: 0 0 1px var(--ps-light-blue);
710
+ }
711
+
712
+ input:checked + .slider:before {
713
+ transform: translateX(26px);
714
+ }
715
+ </style>
716
+ </head>
717
+ <body>
718
+ <div class="game-bg-overlay" id="gameBgOverlay"></div>
719
+
720
+ <div class="top-bar">
721
+ <div class="site-title"><img src="/new logo.png" alt="Alexr Games Logo"> Alexr's Math World</div>
722
+ <div class="clock-area">
723
+ <div class="social-icons">
724
+ <a href="https://youtube.com/yourchannel" target="_blank" title="YouTube"><i class="fab fa-youtube"></i></a>
725
+ <a href="https://discord.gg/yourserver" target="_blank" title="Discord"><i class="fab fa-discord"></i></a>
726
+ <a href="https://tiktok.com/@youraccount" target="_blank" title="TikTok"><i class="fab fa-tiktok"></i></a>
727
+ <a href="https://instagram.com/youraccount" target="_blank" title="Instagram"><i class="fab fa-instagram"></i></a>
728
+ </div>
729
+ <span id="clock">12:00</span>
730
+ </div>
731
+ </div>
732
+
733
+ <div class="main-stage">
734
+ <div class="carousel-container" id="carousel"></div>
735
+ </div>
736
+
737
+ <div class="info-area visible" id="infoArea">
738
+ <div class="game-title" id="gameTitle">Game Title</div>
739
+ <div class="game-meta">
740
+ <span id="gameCat">Category</span> •
741
+ <span>Unblocked</span>
742
+ </div>
743
+
744
+ <div class="sub-content">
745
+ <div class="sub-tile">
746
+ <img src="" id="bgPreview1" style="background:#222">
747
+ <div class="sub-head">Overview</div>
748
+ <div class="sub-desc" id="overviewDesc">Game Info</div>
749
+ </div>
750
+ <div class="sub-tile" id="versionsTile">
751
+ <img src="" id="bgPreview2" style="background:#333">
752
+ <div class="sub-head" id="versionsHead">Other Versions</div>
753
+ <div class="sub-desc" id="versionsDesc">No other versions</div>
754
+ </div>
755
+ <div class="sub-tile" id="favoriteTile" style="background: linear-gradient(135deg, #00439c, #00285e); align-items: center; justify-content: center;">
756
+ <div id="favoriteLabel" style="font-size: 1.5rem; font-weight: bold;">Favorite this game</div>
757
+ <div class="sub-desc" id="favoriteStatus">Click to add to favorites</div>
758
+ </div>
759
+ </div>
760
+ </div>
761
+
762
+ <div class="library-overlay" id="libraryOverlay">
763
+ <div class="lib-header">
764
+ <div class="lib-title" id="libraryTitle">Your Collection</div>
765
+ <button class="close-btn" onclick="closeOverlay()">Close</button>
766
+ </div>
767
+
768
+ <div class="lib-filters">
769
+ <input type="text" id="libSearchInput" class="lib-search" placeholder="Search games by title..." oninput="renderOverlayGrid()" list="game-suggestions">
770
+ <datalist id="game-suggestions"></datalist>
771
+ <div class="lib-categories" id="libCategories">
772
+ </div>
773
+ </div>
774
+ <div class="lib-grid" id="libraryGrid"></div>
775
+ </div>
776
+
777
+ <div class="library-overlay" id="otherOverlay">
778
+ <div class="lib-header">
779
+ <div class="lib-title">Settings</div>
780
+ <button class="close-btn" onclick="toggleOther(false)">Close</button>
781
+ </div>
782
+
783
+ <div class="other-tabs">
784
+ <button class="tab-btn" id="tab_changelog" onclick="showOtherTab('changelog')">Changelog</button>
785
+ <button class="tab-btn" id="tab_proxy" onclick="showOtherTab('proxy')">Proxy</button>
786
+ <button class="tab-btn" id="tab_themes" onclick="showOtherTab('themes')">Themes</button>
787
+ <button class="tab-btn" id="tab_sounds" onclick="showOtherTab('sounds')">Sounds</button>
788
+ <button class="tab-btn" id="tab_cloaking" onclick="showOtherTab('cloaking')">Cloaking</button>
789
+ </div>
790
+
791
+ <div id="other_changelog">
792
+ <p>v1.0 – Initial release.</p>
793
+ <p>v1.1 – Added favorites, recent games, and themes.</p>
794
+ <p>v1.2 – Added custom backgrounds, more themes, and in-game actions.</p>
795
+ <p>v1.3 – Added PS5-style game background feature and dedicated Settings tile.</p>
796
+ <p>v1.4 – Added search bar and categories to the Library ("All Games") view.</p>
797
+ <p>v1.5 – Added PS4-style sound effects and ambience control.</p>
798
+ <p>v1.6 – Added Suggest/Report system tiles, themed Library, and fixed game bar toggle.</p>
799
+ <p>v1.7 – Removed text labels from system tiles for a cleaner look.</p>
800
+ <p>v1.8 – **NEW:** Added Tab Cloaking and Drag-and-Drop game reordering.</p>
801
+ </div>
802
+
803
+ <div id="other_proxy" style="display:none;">
804
+ <p>Proxy links or information go here.</p>
805
+ </div>
806
+
807
+ <div id="other_themes" style="display:none;">
808
+ <div class="settings-group">
809
+ <p>Select a theme:</p>
810
+ <button class="close-btn" onclick="setTheme('default')">Default</button>
811
+ <button class="close-btn" onclick="setTheme('dark')">Dark</button>
812
+ <button class="close-btn" onclick="setTheme('red')">Red</button>
813
+ <button class="close-btn" onclick="setTheme('blue-dark')">Blue Dark</button>
814
+ <button class="close-btn" onclick="setTheme('hacker')">Hacker</button>
815
+ <button class="close-btn" onclick="setTheme('tokyo')">Tokyo</button>
816
+ </div>
817
+
818
+ <div class="settings-group">
819
+ <p>PS5-Style Game Backgrounds:</p>
820
+ <button class="close-btn" id="ps5BgToggle" onclick="togglePS5Backgrounds()">Enable PS5 Backgrounds</button>
821
+ <p style="font-size:0.85rem; color:#aaa; margin-top:8px;">Show selected game image as blurred background</p>
822
+ </div>
823
+
824
+ <div class="settings-group">
825
+ <p>Custom background image:</p>
826
+ <label class="close-btn" for="bgUploadInput">Upload Background</label>
827
+ <input type="file" id="bgUploadInput" accept="image/*">
828
+ <button class="close-btn" onclick="clearCustomBackground()">Clear Custom Background</button>
829
+ </div>
830
+ </div>
831
+
832
+ <div id="other_sounds" style="display:none;">
833
+ <div class="settings-group">
834
+ <p>Toggle Sound Effects and Ambience:</p>
835
+ <div class="sound-control">
836
+ <label for="soundToggle">Enable Sounds</label>
837
+ <label class="switch">
838
+ <input type="checkbox" id="soundToggle" onchange="toggleSoundEffects(this.checked)">
839
+ <span class="slider"></span>
840
+ </label>
841
+ </div>
842
+
843
+ <p style="font-size:0.85rem; color:#aaa; margin-top:15px;">
844
+ Sound effects include card hovers, selection, and a menu background track.
845
+ </p>
846
+ </div>
847
+
848
+ <div class="settings-group">
849
+ <p>Ambience Volume:</p>
850
+ <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;">
851
+ </div>
852
+ </div>
853
+
854
+ <div id="other_cloaking" style="display:none;">
855
+ <div class="settings-group">
856
+ <p>Select a preset cloak:</p>
857
+ <select id="cloakPresetSelect" class="settings-select" onchange="applyCloakPreset(this.value)">
858
+ <option value="none">None (Default)</option>
859
+ <option value="drive">Google Drive</option>
860
+ <option value="wiki">Wikipedia</option>
861
+ <option value="classlink">ClassLink</option>
862
+ <option value="school">School Website</option>
863
+ </select>
864
+ <button class="close-btn" style="margin-top:10px;" onclick="applyCloakPreset('none')">Clear Cloak</button>
865
+ </div>
866
+
867
+ <div class="settings-group">
868
+ <p>Customize Tab:</p>
869
+ <label for="customTitleInput">Tab Title:</label>
870
+ <input type="text" id="customTitleInput" class="settings-input" placeholder="e.g., My Homework" oninput="setCustomTitle(this.value)">
871
+ </div>
872
+
873
+ <div class="settings-group">
874
+ <label for="customFaviconInput">Favicon URL:</label>
875
+ <input type="text" id="customFaviconInput" class="settings-input" placeholder="e.g., https://example.com/favicon.ico" oninput="setCustomFavicon(this.value)">
876
+ <p style="font-size:0.85rem; color:#aaa; margin-top:8px;">Enter a URL for a favicon image (e.g., a .ico or small .png).</p>
877
+ </div>
878
+
879
+ <button class="close-btn" onclick="saveCustomCloak()">Save Custom Cloak</button>
880
+ </div>
881
+ </div>
882
+
883
+ <script type="application/json" id="games-data">
884
+ [
885
+ {"title": "Subway Surfers", "category": "Runner", "path": "/games/subway_surfers/", "iframe": true, "img": "img/subway.png", "description": "Run, dodge, and collect coins on the train tracks."},
886
+ {"title": "Slope", "category": "Arcade", "path": "/games/slope/", "iframe": true, "img": "img/slope.png", "description": "Roll a ball down a treacherous, neon slope."},
887
+ {"title": "Tunnel Rush", "category": "Arcade", "path": "/games/tunnel_rush/", "iframe": true, "img": "img/tunnel.png", "description": "Navigate a high-speed tunnel of obstacles."},
888
+ {"title": "Retro Bowl", "category": "Sports", "path": "/games/retro_bowl/", "iframe": true, "img": "img/retro.png", "description": "Classic-style American football simulation."},
889
+ {"title": "Minecraft", "category": "Sandbox", "path": "/games/minecraft/", "iframe": true, "img": "img/minecraft.png", "description": "Explore, mine resources, and build anything you can imagine."},
890
+ {"title": "FNAF", "category": "Horror", "path": "/games/fnaf/", "iframe": true, "img": "img/fnaf.png", "description": "Survive the night at Freddy Fazbear's Pizza."},
891
+ {"title": "Drift Hunters", "category": "Racing", "path": "/games/drift_hunters/", "iframe": true, "img": "img/drift.png", "description": "High-quality 3D drifting game."},
892
+ {"title": "Basket Random", "category": "Sports", "path": "/games/basket_random/", "iframe": true, "img": "img/basket.png", "description": "Hilarious physics-based 2v2 basketball."},
893
+ {"title": "2048", "category": "Puzzle", "path": "/games/2048/", "iframe": true, "img": "img/2048.png", "description": "Combine numbered tiles to reach the 2048 tile."},
894
+ {"title": "Vex 6", "category": "Platformer", "path": "/games/vex6/", "iframe": true, "img": "img/vex6.png", "description": "The latest installment in the challenging stickman platformer series."}
895
+ ]
896
+ </script>
897
+
898
+
899
+ <script>
900
+ const carousel = document.getElementById('carousel');
901
+ const infoArea = document.getElementById('infoArea');
902
+ const libOverlay = document.getElementById('libraryOverlay');
903
+ const libGrid = document.getElementById('libraryGrid');
904
+ const libraryTitle = document.getElementById('libraryTitle');
905
+ const otherOverlay = document.getElementById('otherOverlay');
906
+ const libSearchInput = document.getElementById('libSearchInput');
907
+ const libCategoriesDiv = document.getElementById('libCategories');
908
+ const gameSuggestionsDatalist = document.getElementById('game-suggestions');
909
+ const subContent = document.querySelector('.sub-content');
910
+
911
+ let allGamesData = [];
912
+ let displayList = [];
913
+ let selectedIndex = 0;
914
+ let isLibraryOpen = false;
915
+ let isOtherOpen = false;
916
+ let currentOverlayMode = 'library';
917
+ let favoritesSet = new Set();
918
+ let currentCategoryFilter = 'All';
919
+ let areSoundsEnabled = false;
920
+
921
+ // Drag-and-Drop state
922
+ let dragSrcEl = null;
923
+ let systemTileStart = 4; // Index of the first game tile
924
+ let systemTileEnd = 1; // Number of non-game tiles at the end (Library)
925
+
926
+
927
+ // System item definitions
928
+ const settingsItem = {
929
+ title: "Settings",
930
+ category: "System",
931
+ description: "Customize themes and manage backgrounds.",
932
+ systemType: "settings",
933
+ icon: "fa-cog"
934
+ };
935
+
936
+ const favoritesItem = {
937
+ title: "Favorites",
938
+ category: "Favorites",
939
+ description: "Your favorite games.",
940
+ systemType: "favorites",
941
+ icon: "fa-heart"
942
+ };
943
+
944
+ // NEW SYSTEM TILES
945
+ const suggestItem = {
946
+ title: "Suggest Game",
947
+ category: "System",
948
+ description: "Suggest a game via Google Form.",
949
+ systemType: "suggest",
950
+ icon: "fa-lightbulb",
951
+ path: "https://docs.google.com/forms/d/e/1FAIpQLScXQf_W-j9Fk1G4_r_oA9-3A0b4vG0eYt-n0O4/viewform?usp=sf_link" // Placeholder URL
952
+ };
953
+
954
+ const reportItem = {
955
+ title: "Report Bug",
956
+ category: "System",
957
+ description: "Report a bug via Google Form.",
958
+ systemType: "report",
959
+ icon: "fa-bug",
960
+ path: "https://docs.google.com/forms/d/e/1FAIpQLScXQf_W-j9Fk1G4_r_oA9-3A0b4vG0eYt-n0O4/viewform?usp=sf_link" // Placeholder URL
961
+ };
962
+
963
+ const libraryItem = {
964
+ title: "Library",
965
+ category: "Collection",
966
+ description: "View all games in your collection.",
967
+ systemType: "library",
968
+ icon: "fa-th"
969
+ };
970
+
971
+ /* --- CLOAKING MANAGER --- */
972
+
973
+ const defaultTitle = "Alexr Games";
974
+ const defaultFavicon = 'new logo.png'; // Assuming this is the default favicon
975
+
976
+ const cloakPresets = {
977
+ none: { title: defaultTitle, favicon: defaultFavicon },
978
+ drive: { title: "My Drive - Google Drive", favicon: "https://ssl.gstatic.com/images/branding/product/2x/drive_2020q4_48dp.png" },
979
+ wiki: { title: "Wikipedia, the free encyclopedia", favicon: "https://en.wikipedia.org/static/favicon/wikipedia.ico" },
980
+ classlink: { title: "ClassLink", favicon: "https://www.classlink.com/favicon.ico" },
981
+ school: { title: "Student Portal | Home", favicon: "https://www.example.edu/favicon.ico" } // Placeholder for a school
982
+ };
983
+
984
+ function setFavicon(url) {
985
+ let link = document.querySelector("link[rel~='icon']");
986
+ if (!link) {
987
+ link = document.createElement('link');
988
+ link.rel = 'icon';
989
+ document.head.appendChild(link);
990
+ }
991
+ // Use local default if 'none' or blank
992
+ if (!url || url === defaultFavicon) {
993
+ link.href = defaultFavicon;
994
+ link.rel = 'icon'; // Keep rel as 'icon'
995
+ return;
996
+ }
997
+
998
+ link.href = url;
999
+ link.rel = 'icon'; // Ensure it's correctly set as favicon
1000
+ }
1001
+
1002
+ function setTitle(title) {
1003
+ document.title = title;
1004
+ }
1005
+
1006
+ function loadCloak() {
1007
+ const savedTitle = localStorage.getItem('alexrGames_cloakTitle');
1008
+ const savedFavicon = localStorage.getItem('alexrGames_cloakFavicon');
1009
+
1010
+ if (savedTitle && savedFavicon) {
1011
+ setTitle(savedTitle);
1012
+ setFavicon(savedFavicon);
1013
+ } else {
1014
+ setTitle(defaultTitle);
1015
+ setFavicon(defaultFavicon);
1016
+ }
1017
+
1018
+ // Update inputs in settings
1019
+ const titleInput = document.getElementById('customTitleInput');
1020
+ const faviconInput = document.getElementById('customFaviconInput');
1021
+ const presetSelect = document.getElementById('cloakPresetSelect');
1022
+
1023
+ if (titleInput) titleInput.value = savedTitle && savedTitle !== defaultTitle ? savedTitle : '';
1024
+ if (faviconInput) faviconInput.value = savedFavicon && savedFavicon !== defaultFavicon ? savedFavicon : '';
1025
+ if (presetSelect) presetSelect.value = 'none'; // Clear preset selection on load
1026
+ }
1027
+
1028
+ function saveCloak(title, favicon) {
1029
+ if (title === defaultTitle && favicon === defaultFavicon) {
1030
+ localStorage.removeItem('alexrGames_cloakTitle');
1031
+ localStorage.removeItem('alexrGames_cloakFavicon');
1032
+ } else {
1033
+ localStorage.setItem('alexrGames_cloakTitle', title);
1034
+ localStorage.setItem('alexrGames_cloakFavicon', favicon);
1035
+ }
1036
+ setTitle(title);
1037
+ setFavicon(favicon);
1038
+ }
1039
+
1040
+ function applyCloakPreset(preset) {
1041
+ const cloak = cloakPresets[preset] || cloakPresets['none'];
1042
+ saveCloak(cloak.title, cloak.favicon);
1043
+
1044
+ // Also update custom inputs
1045
+ const titleInput = document.getElementById('customTitleInput');
1046
+ const faviconInput = document.getElementById('customFaviconInput');
1047
+ if (titleInput) titleInput.value = cloak.title !== defaultTitle ? cloak.title : '';
1048
+ if (faviconInput) faviconInput.value = cloak.favicon !== defaultFavicon ? cloak.favicon : '';
1049
+ }
1050
+
1051
+ function setCustomTitle(title) {
1052
+ // Live update the title if it's not empty, but don't save to storage yet
1053
+ if(title) {
1054
+ document.title = title;
1055
+ } else if (!document.getElementById('customFaviconInput').value) {
1056
+ document.title = defaultTitle;
1057
+ }
1058
+ }
1059
+
1060
+ function setCustomFavicon(url) {
1061
+ // Live update the favicon if it's a valid URL, but don't save to storage yet
1062
+ if(url.startsWith('http')) {
1063
+ setFavicon(url);
1064
+ } else if (!document.getElementById('customTitleInput').value) {
1065
+ setFavicon(defaultFavicon);
1066
+ }
1067
+ }
1068
+
1069
+ function saveCustomCloak() {
1070
+ const titleInput = document.getElementById('customTitleInput');
1071
+ const faviconInput = document.getElementById('customFaviconInput');
1072
+ const presetSelect = document.getElementById('cloakPresetSelect');
1073
+
1074
+ const title = titleInput.value.trim() || defaultTitle;
1075
+ const favicon = faviconInput.value.trim() || defaultFavicon;
1076
+
1077
+ saveCloak(title, favicon);
1078
+
1079
+ // Reset preset selector
1080
+ if (presetSelect) presetSelect.value = 'none';
1081
+ }
1082
+
1083
+ /* --- END CLOAKING MANAGER --- */
1084
+
1085
+ /* --- SOUND MANAGER --- */
1086
+
1087
+ const ambience = new Audio('sounds/ambience.mp3');
1088
+ ambience.loop = true;
1089
+ ambience.volume = localStorage.getItem('alexrGames_ambience_volume') || 0.5;
1090
+
1091
+ const soundEffects = {
1092
+ click: new Audio('sounds/click.mp3'),
1093
+ hover: new Audio('sounds/hover.mp3'),
1094
+ select: new Audio('sounds/select.mp3'),
1095
+ };
1096
+
1097
+ function loadSoundSettings() {
1098
+ const saved = localStorage.getItem('alexrGames_sounds_enabled') === 'true';
1099
+ areSoundsEnabled = saved;
1100
+
1101
+ const toggle = document.getElementById('soundToggle');
1102
+ if (toggle) toggle.checked = saved;
1103
+
1104
+ const volumeInput = document.getElementById('ambienceVolume');
1105
+ if (volumeInput) volumeInput.value = ambience.volume;
1106
+
1107
+ if (saved && !document.querySelector('.game-modal')) {
1108
+ playAmbience();
1109
+ }
1110
+ }
1111
+
1112
+ function toggleSoundEffects(enabled) {
1113
+ areSoundsEnabled = enabled;
1114
+ localStorage.setItem('alexrGames_sounds_enabled', enabled);
1115
+ if (enabled) {
1116
+ playAmbience();
1117
+ } else {
1118
+ pauseAmbience();
1119
+ }
1120
+ }
1121
+
1122
+ function setAmbienceVolume(volume) {
1123
+ ambience.volume = volume;
1124
+ localStorage.setItem('alexrGames_ambience_volume', volume);
1125
+ }
1126
+
1127
+ function playSound(name) {
1128
+ if (!areSoundsEnabled) return;
1129
+ const audio = soundEffects[name];
1130
+ if (audio) {
1131
+ audio.currentTime = 0;
1132
+ audio.play().catch(e => console.warn("Audio playback failed:", e));
1133
+ }
1134
+ }
1135
+
1136
+ function playAmbience() {
1137
+ if (areSoundsEnabled) {
1138
+ // Check if a game is open or an overlay is up before playing
1139
+ if (!document.querySelector('.game-modal') && !isLibraryOpen && !isOtherOpen) {
1140
+ ambience.play().catch(e => console.warn("Ambience playback failed:", e));
1141
+ }
1142
+ }
1143
+ }
1144
+
1145
+ function pauseAmbience() {
1146
+ ambience.pause();
1147
+ }
1148
+ /* --- END SOUND MANAGER --- */
1149
+
1150
+ /* THEME + CUSTOM BACKGROUND */
1151
+
1152
+ function togglePS5Backgrounds() {
1153
+ const isEnabled = document.body.classList.toggle('ps5-bg-enabled');
1154
+ localStorage.setItem('alexrGames_ps5bg', isEnabled ? 'true' : 'false');
1155
+ const btn = document.getElementById('ps5BgToggle');
1156
+ if (btn) {
1157
+ btn.textContent = isEnabled ? 'Disable PS5 Backgrounds' : 'Enable PS5 Backgrounds';
1158
+ }
1159
+ if (!isEnabled) {
1160
+ const overlay = document.getElementById('gameBgOverlay');
1161
+ if (overlay) overlay.classList.remove('active');
1162
+ } else {
1163
+ updateGameBackground(displayList[selectedIndex]);
1164
+ }
1165
+ }
1166
+
1167
+ function loadPS5BackgroundSetting() {
1168
+ const saved = localStorage.getItem('alexrGames_ps5bg');
1169
+ const isEnabled = saved === 'true';
1170
+ if (isEnabled) {
1171
+ document.body.classList.add('ps5-bg-enabled');
1172
+ }
1173
+ const btn = document.getElementById('ps5BgToggle');
1174
+ if (btn) {
1175
+ btn.textContent = isEnabled ? 'Disable PS5 Backgrounds' : 'Enable PS5 Backgrounds';
1176
+ }
1177
+ }
1178
+
1179
+ function updateGameBackground(game) {
1180
+ const overlay = document.getElementById('gameBgOverlay');
1181
+ if (!overlay) return;
1182
+
1183
+ const isEnabled = document.body.classList.contains('ps5-bg-enabled');
1184
+
1185
+ if (!isEnabled || !game || game.systemType || !game.img) {
1186
+ overlay.classList.remove('active');
1187
+ return;
1188
+ }
1189
+
1190
+ overlay.style.backgroundImage = `url(${game.img})`;
1191
+ overlay.classList.add('active');
1192
+ }
1193
+
1194
+ function applyTheme(themeName) {
1195
+ document.body.classList.remove('theme-default','theme-dark','theme-red','theme-blue-dark','theme-hacker','theme-tokyo');
1196
+ document.body.classList.add('theme-' + themeName);
1197
+ }
1198
+
1199
+ function setTheme(themeName) {
1200
+ applyTheme(themeName);
1201
+ localStorage.setItem('alexrGames_theme', themeName);
1202
+ }
1203
+
1204
+ function loadTheme() {
1205
+ const savedTheme = localStorage.getItem('alexrGames_theme') || 'default';
1206
+ applyTheme(savedTheme);
1207
+ const bg = localStorage.getItem('alexrGames_bg');
1208
+ if (bg) {
1209
+ document.body.style.backgroundImage = `url(${bg})`;
1210
+ document.body.classList.add('has-custom-bg');
1211
+ }
1212
+ }
1213
+
1214
+ function clearCustomBackground() {
1215
+ localStorage.removeItem('alexrGames_bg');
1216
+ document.body.classList.remove('has-custom-bg');
1217
+ document.body.style.backgroundImage = '';
1218
+ const savedTheme = localStorage.getItem('alexrGames_theme') || 'default';
1219
+ applyTheme(savedTheme);
1220
+ }
1221
+
1222
+ const bgUploadInput = document.getElementById('bgUploadInput');
1223
+ if (bgUploadInput) {
1224
+ bgUploadInput.addEventListener('change', function() {
1225
+ const file = this.files[0];
1226
+ if (!file) return;
1227
+ const reader = new FileReader();
1228
+ reader.onload = function(e) {
1229
+ const dataURL = e.target.result;
1230
+ document.body.style.backgroundImage = `url(${dataURL})`;
1231
+ document.body.classList.add('has-custom-bg');
1232
+ localStorage.setItem('alexrGames_bg', dataURL);
1233
+ };
1234
+ reader.readAsDataURL(file);
1235
+ });
1236
+ }
1237
+
1238
+ // Load favorites from localStorage
1239
+ function loadFavorites() {
1240
+ const saved = localStorage.getItem('alexrGames_favorites');
1241
+ if (saved) {
1242
+ try {
1243
+ const arr = JSON.parse(saved);
1244
+ favoritesSet = new Set(arr);
1245
+ } catch (e) {
1246
+ favoritesSet = new Set();
1247
+ }
1248
+ }
1249
+ }
1250
+
1251
+ function saveFavorites() {
1252
+ localStorage.setItem('alexrGames_favorites', JSON.stringify(Array.from(favoritesSet)));
1253
+ }
1254
+
1255
+ // Load game data with localStorage override
1256
+ function loadGames() {
1257
+ loadFavorites();
1258
+
1259
+ const saved = localStorage.getItem('alexrGames_allGamesData');
1260
+ if (saved) {
1261
+ try {
1262
+ allGamesData = JSON.parse(saved);
1263
+ buildLists();
1264
+ return;
1265
+ } catch (e) {
1266
+ console.warn('Bad saved data, falling back to default');
1267
+ }
1268
+ }
1269
+
1270
+ // Using the embedded JSON as fallback/default source
1271
+ try {
1272
+ const embedded = JSON.parse(document.getElementById('games-data').textContent);
1273
+ allGamesData = embedded;
1274
+ buildLists();
1275
+ } catch(e) {
1276
+ console.error("Failed to load embedded game data:", e);
1277
+ allGamesData = [];
1278
+ buildLists();
1279
+ }
1280
+ }
1281
+
1282
+ function buildLists() {
1283
+ const firstSix = allGamesData.slice(0, 6);
1284
+
1285
+ // MODIFIED: Order system tiles as requested: [Suggest, Report, Favorites, Settings, ...Games, Library]
1286
+ displayList = [suggestItem, reportItem, favoritesItem, settingsItem, ...firstSix, libraryItem];
1287
+
1288
+ // Adjust systemTileStart based on the current system cards (4 system cards before first game)
1289
+ systemTileStart = 4;
1290
+
1291
+ initCarousel();
1292
+ initLibraryGrid();
1293
+ }
1294
+
1295
+ function saveGamesOrder() {
1296
+ // Save only the *game* data, excluding system tiles
1297
+ const gameDataToSave = displayList.slice(systemTileStart, displayList.length - systemTileEnd)
1298
+ .map(item => ({
1299
+ title: item.title,
1300
+ category: item.category,
1301
+ path: item.path,
1302
+ iframe: item.iframe,
1303
+ img: item.img,
1304
+ description: item.description,
1305
+ versions: item.versions // Preserve other game data
1306
+ }));
1307
+ localStorage.setItem('alexrGames_allGamesData', JSON.stringify(gameDataToSave));
1308
+ allGamesData = gameDataToSave; // Update the source of truth
1309
+ }
1310
+
1311
+ function promoteToRecent(game) {
1312
+ // Find the index of the game in the full game list
1313
+ const gameIndex = allGamesData.findIndex(g => g.title === game.title);
1314
+ if (gameIndex > 0) {
1315
+ // Move to the beginning (most recent)
1316
+ const [movedGame] = allGamesData.splice(gameIndex, 1);
1317
+ allGamesData.unshift(movedGame);
1318
+ saveGamesOrder();
1319
+ buildLists();
1320
+ // index 0=Suggest, 1=Report, 2=Favorites, 3=Settings, 4=first game
1321
+ updateSelection(systemTileStart);
1322
+ }
1323
+ }
1324
+
1325
+ /* --- DRAG-AND-DROP HANDLERS --- */
1326
+ function handleDragStart(e) {
1327
+ if (e.target.classList.contains('system-card')) return; // Ignore system cards
1328
+
1329
+ e.target.classList.add('dragging');
1330
+ dragSrcEl = e.target;
1331
+ e.dataTransfer.effectAllowed = 'move';
1332
+ e.dataTransfer.setData('text/html', e.target.outerHTML);
1333
+ }
1334
+
1335
+ function handleDragOver(e) {
1336
+ e.preventDefault();
1337
+ e.dataTransfer.dropEffect = 'move';
1338
+
1339
+ const target = e.target.closest('.card:not(.system-card)');
1340
+ if (!target || target === dragSrcEl) return;
1341
+
1342
+ // Simple visual feedback: indicate where the card will drop
1343
+ // Find current game card elements for comparison
1344
+ const cards = Array.from(carousel.querySelectorAll('.card:not(.system-card)'));
1345
+ const draggedIndex = cards.findIndex(c => c === dragSrcEl);
1346
+ const targetIndex = cards.findIndex(c => c === target);
1347
+
1348
+ // Determine if dragging left or right over the midpoint of the target
1349
+ const targetRect = target.getBoundingClientRect();
1350
+ const midpoint = targetRect.left + (targetRect.width / 2);
1351
+
1352
+ if (draggedIndex < targetIndex && e.clientX > midpoint) {
1353
+ // Dragging from left to right, drop after target
1354
+ target.style.marginLeft = '15px'; // Push target right
1355
+ target.style.marginRight = '0';
1356
+ } else if (draggedIndex > targetIndex && e.clientX < midpoint) {
1357
+ // Dragging from right to left, drop before target
1358
+ target.style.marginRight = '15px'; // Push target left
1359
+ target.style.marginLeft = '0';
1360
+ } else {
1361
+ // Reset margins if not dropping immediately next to it
1362
+ target.style.marginLeft = '0';
1363
+ target.style.marginRight = '0';
1364
+ }
1365
+ }
1366
+
1367
+ function handleDragLeave(e) {
1368
+ // Reset margin when leaving a card
1369
+ const target = e.target.closest('.card:not(.system-card)');
1370
+ if (target) {
1371
+ target.style.marginLeft = '0';
1372
+ target.style.marginRight = '0';
1373
+ }
1374
+ }
1375
+
1376
+ function handleDrop(e) {
1377
+ e.stopPropagation();
1378
+ const targetCard = e.target.closest('.card:not(.system-card)');
1379
+
1380
+ if (!targetCard || dragSrcEl === targetCard) {
1381
+ return false;
1382
+ }
1383
+
1384
+ // Get the indices of the games in the *displayList*
1385
+ const dragIndex = parseInt(dragSrcEl.dataset.index);
1386
+ const dropIndex = parseInt(targetCard.dataset.index);
1387
+
1388
+ // 1. Update the underlying allGamesData array
1389
+ // Convert to indices relative to allGamesData (which starts after the 4 system tiles)
1390
+ const dragGameIndex = dragIndex - systemTileStart;
1391
+ const dropGameIndex = dropIndex - systemTileStart;
1392
+
1393
+ const [draggedGameData] = allGamesData.splice(dragGameIndex, 1);
1394
+ allGamesData.splice(dropGameIndex, 0, draggedGameData);
1395
+
1396
+ // 2. Persist the new order
1397
+ saveGamesOrder();
1398
+
1399
+ // 3. Re-render the carousel and set selection
1400
+ buildLists();
1401
+ updateSelection(dropIndex); // Select the newly dropped card
1402
+
1403
+ return false;
1404
+ }
1405
+
1406
+ function handleDragEnd(e) {
1407
+ e.target.classList.remove('dragging');
1408
+
1409
+ // Reset all card margins
1410
+ document.querySelectorAll('.card:not(.system-card)').forEach(card => {
1411
+ card.style.marginLeft = '0';
1412
+ card.style.marginRight = '0';
1413
+ });
1414
+
1415
+ dragSrcEl = null;
1416
+ }
1417
+ /* --- END DRAG-AND-DROP HANDLERS --- */
1418
+
1419
+
1420
+ function initCarousel() {
1421
+ carousel.innerHTML = '';
1422
+
1423
+ displayList.forEach((game, index) => {
1424
+ const card = document.createElement('div');
1425
+ card.className = `card ${game.systemType ? 'system-card' : ''}`;
1426
+ card.dataset.index = index;
1427
+
1428
+ if (game.systemType) {
1429
+ card.innerHTML = `<i class="fas ${game.icon}"></i>`;
1430
+ card.style.justifyContent = 'center';
1431
+ card.style.alignItems = 'center';
1432
+ // System cards are not draggable
1433
+ } else {
1434
+ const img = document.createElement('img');
1435
+ img.src = game.img || '';
1436
+ img.onerror = function() { this.style.display='none'; card.style.background='#333'; };
1437
+ card.appendChild(img);
1438
+
1439
+ // Game cards are draggable
1440
+ card.setAttribute('draggable', true);
1441
+ card.addEventListener('dragstart', handleDragStart);
1442
+ card.addEventListener('dragover', handleDragOver);
1443
+ card.addEventListener('dragleave', handleDragLeave);
1444
+ card.addEventListener('drop', handleDrop);
1445
+ card.addEventListener('dragend', handleDragEnd);
1446
+ }
1447
+
1448
+ card.addEventListener('click', () => {
1449
+ if(selectedIndex === index) activateItem(index);
1450
+ else updateSelection(index);
1451
+ });
1452
+
1453
+ // Add hover sound listener for non-touch devices
1454
+ card.addEventListener('mouseover', () => {
1455
+ if (!card.classList.contains('active')) {
1456
+ playSound('hover');
1457
+ }
1458
+ });
1459
+
1460
+ carousel.appendChild(card);
1461
+ });
1462
+
1463
+ // Default to first game if exists (index 4 in new layout)
1464
+ updateSelection(displayList.findIndex(g => !g.systemType) || 0);
1465
+ }
1466
+
1467
+ function getUniqueCategories() {
1468
+ const categories = allGamesData.map(game => game.category).filter(Boolean);
1469
+ return ['All', ...new Set(categories)];
1470
+ }
1471
+
1472
+ function renderSearchSuggestions() {
1473
+ gameSuggestionsDatalist.innerHTML = '';
1474
+ allGamesData.forEach(game => {
1475
+ const option = document.createElement('option');
1476
+ option.value = game.title;
1477
+ gameSuggestionsDatalist.appendChild(option);
1478
+ });
1479
+ }
1480
+
1481
+ function renderCategoryButtons() {
1482
+ libCategoriesDiv.innerHTML = '';
1483
+ const categories = getUniqueCategories();
1484
+
1485
+ categories.forEach(category => {
1486
+ const button = document.createElement('button');
1487
+ button.className = 'category-btn';
1488
+ button.textContent = category;
1489
+ button.dataset.category = category;
1490
+
1491
+ if (category === currentCategoryFilter) {
1492
+ button.classList.add('active');
1493
+ }
1494
+
1495
+ button.onclick = () => {
1496
+ currentCategoryFilter = category;
1497
+ renderCategoryButtons();
1498
+ renderOverlayGrid();
1499
+ };
1500
+
1501
+ libCategoriesDiv.appendChild(button);
1502
+ });
1503
+ }
1504
+
1505
+ function initLibraryGrid() {
1506
+ renderSearchSuggestions();
1507
+ renderCategoryButtons();
1508
+ renderOverlayGrid();
1509
+ }
1510
+
1511
+ function renderOverlayGrid() {
1512
+ libGrid.innerHTML = '';
1513
+ let gamesToShow = [];
1514
+
1515
+ if (currentOverlayMode === 'library') {
1516
+ libraryTitle.textContent = 'All Games';
1517
+ gamesToShow = allGamesData;
1518
+
1519
+ // Filtering logic
1520
+ const searchTerm = libSearchInput.value.toLowerCase().trim();
1521
+
1522
+ gamesToShow = gamesToShow.filter(game => {
1523
+ const matchesSearch = !searchTerm || game.title.toLowerCase().includes(searchTerm);
1524
+ const matchesCategory = currentCategoryFilter === 'All' || game.category === currentCategoryFilter;
1525
+ return matchesSearch && matchesCategory;
1526
+ });
1527
+
1528
+ } else {
1529
+ libraryTitle.textContent = 'Your Favorites';
1530
+ gamesToShow = allGamesData.filter(g => favoritesSet.has(g.title));
1531
+ }
1532
+
1533
+ gamesToShow.forEach((game) => {
1534
+ const card = document.createElement('div');
1535
+ card.className = 'lib-card';
1536
+ card.innerHTML = `
1537
+ <img src="${game.img || ''}" onerror="this.style.display='none'">
1538
+ <div class="lib-card-title">${game.title}</div>
1539
+ `;
1540
+ card.onclick = () => {
1541
+ playSound('select');
1542
+ toggleOverlay(false);
1543
+ launchGame(game);
1544
+ };
1545
+ libGrid.appendChild(card);
1546
+ });
1547
+ }
1548
+
1549
+ function updateSelection(index) {
1550
+ if(isLibraryOpen || isOtherOpen) return;
1551
+ if(index < 0) index = 0;
1552
+ if(index >= displayList.length) index = displayList.length - 1;
1553
+
1554
+ selectedIndex = index;
1555
+
1556
+ const cards = document.querySelectorAll('.card');
1557
+ cards.forEach(c => c.classList.remove('active'));
1558
+ if (cards[index]) cards[index].classList.add('active');
1559
+
1560
+ const gap = 15;
1561
+ let offset = 0;
1562
+
1563
+ // Calculate the position of the selected card
1564
+ let activeCardPosition = 0;
1565
+ for(let i = 0; i < index; i++) {
1566
+ const cardElement = cards[i];
1567
+ const cardWidth = cardElement.classList.contains('active') ? 180 : 130;
1568
+ activeCardPosition += cardWidth + gap + (parseFloat(cardElement.style.marginLeft) || 0) + (parseFloat(cardElement.style.marginRight) || 0);
1569
+ }
1570
+
1571
+ // Calculate the required translation to center the selected card
1572
+ // We want to bring the left edge of the selected card to 60px from the left,
1573
+ // minus the drag-and-drop margins that might be temporarily applied.
1574
+ const targetOffset = activeCardPosition - 0;
1575
+ offset = Math.max(0, targetOffset);
1576
+
1577
+ carousel.style.transform = `translateX(-${offset}px)`;
1578
+
1579
+
1580
+ const g = displayList[index];
1581
+ document.getElementById('gameTitle').innerText = g.title || '';
1582
+ document.getElementById('gameCat').innerText = g.category || 'Game';
1583
+ document.getElementById('overviewDesc').innerText = g.description || 'Game Info';
1584
+
1585
+ const bg1 = document.getElementById('bgPreview1');
1586
+ const bg2 = document.getElementById('bgPreview2');
1587
+
1588
+ if(g.img) {
1589
+ bg1.src = g.img; bg2.src = g.img;
1590
+ bg1.style.filter = "grayscale(100%) blur(2px)";
1591
+ bg2.style.filter = "sepia(50%)";
1592
+ } else {
1593
+ bg1.src = ""; bg2.src = "";
1594
+ }
1595
+
1596
+ // NEW FIX: Hide sub-content area for system tiles
1597
+ if (g.systemType) {
1598
+ subContent.classList.add('hidden');
1599
+ document.getElementById('gameTitle').innerText = g.title;
1600
+ document.getElementById('gameCat').innerText = g.category;
1601
+ } else {
1602
+ subContent.classList.remove('hidden');
1603
+ }
1604
+
1605
+ updateFavoriteTile(g);
1606
+ updateVersionsTile(g);
1607
+ updateGameBackground(g);
1608
+
1609
+ infoArea.classList.remove('visible');
1610
+ setTimeout(() => infoArea.classList.add('visible'), 50);
1611
+ }
1612
+
1613
+ function activateItem(index) {
1614
+ playSound('select');
1615
+ const item = displayList[index];
1616
+
1617
+ if (item.systemType === 'library') {
1618
+ currentOverlayMode = 'library';
1619
+ currentCategoryFilter = 'All';
1620
+ libSearchInput.value = '';
1621
+ renderCategoryButtons();
1622
+ renderOverlayGrid();
1623
+ toggleOverlay(true);
1624
+ } else if (item.systemType === 'favorites') {
1625
+ currentOverlayMode = 'favorites';
1626
+ renderOverlayGrid();
1627
+ toggleOverlay(true);
1628
+ } else if (item.systemType === 'settings') {
1629
+ // Default to 'themes' tab when opening settings
1630
+ showOtherTab('themes');
1631
+ toggleOther(true);
1632
+ } else if (item.systemType === 'suggest' || item.systemType === 'report') {
1633
+ window.open(item.path, '_blank');
1634
+ } else {
1635
+ launchGame(item);
1636
+ }
1637
+ }
1638
+
1639
+ function toggleOverlay(show) {
1640
+ isLibraryOpen = show;
1641
+ if(show) {
1642
+ libOverlay.classList.add('show');
1643
+ libOverlay.style.opacity = '0';
1644
+ setTimeout(()=>libOverlay.style.opacity='1', 10);
1645
+ } else {
1646
+ libOverlay.style.opacity = '0';
1647
+ setTimeout(()=>libOverlay.classList.remove('show'), 300);
1648
+ // Resume ambience if returning to home menu (no other overlay and no game)
1649
+ if (!isOtherOpen && !document.querySelector('.game-modal')) {
1650
+ playAmbience();
1651
+ }
1652
+ }
1653
+ }
1654
+
1655
+ function closeOverlay() {
1656
+ playSound('click');
1657
+ toggleOverlay(false);
1658
+ }
1659
+
1660
+ function toggleOther(show) {
1661
+ isOtherOpen = show;
1662
+ if (show) {
1663
+ otherOverlay.classList.add('show');
1664
+ otherOverlay.style.opacity = '0';
1665
+ setTimeout(() => otherOverlay.style.opacity = '1', 10);
1666
+ } else {
1667
+ playSound('click');
1668
+ otherOverlay.style.opacity = '0';
1669
+ setTimeout(() => otherOverlay.classList.remove('show'), 300);
1670
+ // Resume ambience if returning to home menu (no other overlay and no game)
1671
+ if (!isLibraryOpen && !document.querySelector('.game-modal')) {
1672
+ playAmbience();
1673
+ }
1674
+ }
1675
+ }
1676
+
1677
+ function showOtherTab(tab) {
1678
+ const tabs = ['changelog','proxy','themes', 'sounds', 'cloaking'];
1679
+ tabs.forEach(t => {
1680
+ document.getElementById('other_' + t).style.display = (t === tab) ? 'block' : 'none';
1681
+ const btn = document.getElementById('tab_' + t);
1682
+ if (btn) {
1683
+ if (t === tab) btn.classList.add('active');
1684
+ else btn.classList.remove('active');
1685
+ }
1686
+ });
1687
+
1688
+ // Re-load cloaking settings when opening its tab
1689
+ if (tab === 'cloaking') {
1690
+ loadCloak();
1691
+ }
1692
+ }
1693
+
1694
+ function launchGame(game) {
1695
+ pauseAmbience();
1696
+
1697
+ const modal = document.createElement('div');
1698
+ modal.className = 'game-modal';
1699
+
1700
+ // --- 1. Top Bar Setup (Common to both) ---
1701
+ const bar = document.createElement('div');
1702
+ Object.assign(bar.style, {
1703
+ height: '40px', background: '#222', display: 'flex',
1704
+ alignItems: 'center', padding: '0 20px',
1705
+ justifyContent: 'space-between', color:'white', gap:'10px'
1706
+ });
1707
+
1708
+ // Apply theme colors to the bar explicitly
1709
+ const currentBodyTheme = document.body.className.match(/theme-(\w+)/)?.[1] || 'default';
1710
+ if (currentBodyTheme === 'hacker') {
1711
+ Object.assign(bar.style, { background: '#000', color: '#00ff88' });
1712
+ } else if (currentBodyTheme === 'tokyo') {
1713
+ Object.assign(bar.style, { background: '#111', color: '#ff4aff' });
1714
+ } else if (currentBodyTheme === 'red') {
1715
+ // Red theme fix using CSS variable
1716
+ const redAccent = getComputedStyle(document.body).getPropertyValue('--ps-light-blue');
1717
+ Object.assign(bar.style, { background: '#3a0000', color: redAccent });
1718
+ }
1719
+
1720
+ const leftSpan = document.createElement('span');
1721
+ leftSpan.textContent = game.title;
1722
+
1723
+ const controls = document.createElement('div');
1724
+ controls.style.display = 'flex';
1725
+ controls.style.gap = '8px';
1726
+
1727
+ // --- 2. Button Handlers (Common to both) ---
1728
+
1729
+ // Reusable logic for opening in about:blank
1730
+ const openAboutBlank = () => {
1731
+ const w = window.open('about:blank', '_blank');
1732
+ if (w && w.document) {
1733
+ const iframe = w.document.createElement('iframe');
1734
+ iframe.style.border = 'none';
1735
+ iframe.style.width = '100%';
1736
+ iframe.style.height = '100%';
1737
+ iframe.src = game.path;
1738
+ w.document.body.style.margin = '0';
1739
+ w.document.body.appendChild(iframe);
1740
+ }
1741
+ };
1742
+
1743
+ const btnAboutBlank = document.createElement('button');
1744
+ btnAboutBlank.textContent = 'Open in about:blank';
1745
+ btnAboutBlank.className = 'close-btn';
1746
+ btnAboutBlank.onclick = openAboutBlank;
1747
+
1748
+ const btnDownload = document.createElement('button');
1749
+ btnDownload.textContent = 'Download';
1750
+ btnDownload.className = 'close-btn';
1751
+ btnDownload.onclick = () => {
1752
+ const a = document.createElement('a');
1753
+ a.href = game.path;
1754
+ a.download = '';
1755
+ document.body.appendChild(a);
1756
+ a.click();
1757
+ document.body.removeChild(a);
1758
+ };
1759
+
1760
+ const btnFav = document.createElement('button');
1761
+ btnFav.textContent = favoritesSet.has(game.title) ? 'Unfavorite' : 'Favorite';
1762
+ btnFav.className = 'close-btn';
1763
+ btnFav.onclick = () => {
1764
+ if (favoritesSet.has(game.title)) {
1765
+ favoritesSet.delete(game.title);
1766
+ btnFav.textContent = 'Favorite';
1767
+ } else {
1768
+ favoritesSet.add(game.title);
1769
+ btnFav.textContent = 'Unfavorite';
1770
+ }
1771
+ saveFavorites();
1772
+ updateFavoriteTile(game);
1773
+ };
1774
+
1775
+ const btnHide = document.createElement('button');
1776
+ btnHide.textContent = 'Hide bar';
1777
+ btnHide.className = 'close-btn';
1778
+
1779
+ const btnClose = document.createElement('button');
1780
+ btnClose.textContent = '✕ Close';
1781
+ btnClose.className = 'close-btn';
1782
+ btnClose.onclick = () => {
1783
+ playSound('click');
1784
+ document.body.removeChild(modal);
1785
+ // Only resume ambience if no system overlay is open
1786
+ if (!isLibraryOpen && !isOtherOpen) {
1787
+ playAmbience();
1788
+ }
1789
+ };
1790
+
1791
+ // Assemble controls
1792
+ controls.appendChild(btnAboutBlank);
1793
+ controls.appendChild(btnDownload);
1794
+ controls.appendChild(btnFav);
1795
+ controls.appendChild(btnHide);
1796
+ controls.appendChild(btnClose);
1797
+
1798
+ bar.appendChild(leftSpan);
1799
+ bar.appendChild(controls);
1800
+
1801
+ // --- 3. Content Area Setup (Custom per iframe status) ---
1802
+ const frameWrapper = document.createElement('div');
1803
+ frameWrapper.style.position = 'relative';
1804
+ frameWrapper.style.flex = '1';
1805
+
1806
+ const showBarBtn = document.createElement('button');
1807
+ showBarBtn.textContent = '≡';
1808
+ showBarBtn.className = 'show-bar-btn';
1809
+
1810
+ btnHide.onclick = () => {
1811
+ bar.style.display = 'none';
1812
+ showBarBtn.style.display = 'block';
1813
+ };
1814
+ showBarBtn.onclick = () => {
1815
+ bar.style.display = 'flex';
1816
+ showBarBtn.style.display = 'none';
1817
+ };
1818
+
1819
+
1820
+ if (game.iframe) {
1821
+ // Content: Game iframe
1822
+ const frame = document.createElement('iframe');
1823
+ frame.src = game.path;
1824
+ frame.style.flex = 1;
1825
+ frame.style.border = 'none';
1826
+
1827
+ frameWrapper.appendChild(frame);
1828
+ frameWrapper.appendChild(showBarBtn);
1829
+
1830
+ } else {
1831
+ // Content: Custom message for unsupported game (User Request)
1832
+ frameWrapper.style.display = 'flex';
1833
+ frameWrapper.style.flexDirection = 'column';
1834
+ frameWrapper.style.justifyContent = 'center';
1835
+ frameWrapper.style.alignItems = 'center';
1836
+ frameWrapper.style.textAlign = 'center';
1837
+ frameWrapper.style.background = '#000';
1838
+
1839
+ const messageDiv = document.createElement('div');
1840
+ messageDiv.style.padding = '50px';
1841
+ messageDiv.style.maxWidth = '80%';
1842
+
1843
+ const messageP = document.createElement('p');
1844
+ messageP.textContent = "Due to issues, this game is not supported here.";
1845
+ messageP.style.fontSize = '1.8rem';
1846
+ messageP.style.color = 'white';
1847
+ messageP.style.marginBottom = '0.5rem';
1848
+
1849
+ const instructionsP = document.createElement('p');
1850
+ instructionsP.textContent = "Click Open in About:blank to proceed.";
1851
+ instructionsP.style.fontSize = '1.2rem';
1852
+ instructionsP.style.color = '#ccc';
1853
+ instructionsP.style.marginTop = '0';
1854
+
1855
+ // Use a styled button for the About:blank link
1856
+ const bigAboutBlankBtn = btnAboutBlank.cloneNode(true);
1857
+ bigAboutBlankBtn.textContent = 'Open in About:blank';
1858
+ bigAboutBlankBtn.style.marginTop = '20px';
1859
+ bigAboutBlankBtn.style.padding = '10px 30px';
1860
+ bigAboutBlankBtn.style.fontSize = '1.1rem';
1861
+ bigAboutBlankBtn.onclick = openAboutBlank; // Re-wire handler
1862
+
1863
+ messageDiv.appendChild(messageP);
1864
+ messageDiv.appendChild(instructionsP);
1865
+ messageDiv.appendChild(bigAboutBlankBtn);
1866
+
1867
+ frameWrapper.appendChild(messageDiv);
1868
+ }
1869
+
1870
+ // --- 4. Final Assembly & Launch (Common to both) ---
1871
+ modal.appendChild(bar);
1872
+ modal.appendChild(frameWrapper);
1873
+ document.body.appendChild(modal);
1874
+ promoteToRecent(game);
1875
+ }
1876
+ function updateFavoriteTile(game) {
1877
+ const tile = document.getElementById('favoriteTile');
1878
+ const label = document.getElementById('favoriteLabel');
1879
+ const status = document.getElementById('favoriteStatus');
1880
+
1881
+ if (!game || game.systemType) {
1882
+ // Should not happen as sub-content is hidden, but for safety:
1883
+ tile.style.background = 'linear-gradient(135deg, #555, #333)';
1884
+ label.textContent = "Favorite this game";
1885
+ status.textContent = "Unavailable for system tiles";
1886
+ tile.onclick = null;
1887
+ return;
1888
+ }
1889
+
1890
+ const isFav = favoritesSet.has(game.title);
1891
+
1892
+ if (isFav) {
1893
+ tile.style.background = 'linear-gradient(135deg, #00c853, #64dd17)';
1894
+ label.textContent = "Favorited";
1895
+ status.textContent = "Click to remove from favorites";
1896
+ } else {
1897
+ tile.style.background = 'linear-gradient(135deg, #00439c, #00285e)';
1898
+ label.textContent = "Favorite this game";
1899
+ status.textContent = "Click to add to favorites";
1900
+ }
1901
+
1902
+ tile.onclick = () => {
1903
+ if (favoritesSet.has(game.title)) {
1904
+ favoritesSet.delete(game.title);
1905
+ } else {
1906
+ favoritesSet.add(game.title);
1907
+ }
1908
+ saveFavorites();
1909
+ updateFavoriteTile(game);
1910
+ };
1911
+ }
1912
+
1913
+ function updateVersionsTile(game) {
1914
+ const head = document.getElementById('versionsHead');
1915
+ const desc = document.getElementById('versionsDesc');
1916
+ const tile = document.getElementById('versionsTile');
1917
+
1918
+ if (!game || game.systemType) {
1919
+ head.textContent = "Other Versions";
1920
+ desc.textContent = "Not available";
1921
+ tile.style.opacity = '0.5';
1922
+ tile.style.cursor = 'default';
1923
+ tile.onclick = null;
1924
+ return;
1925
+ }
1926
+
1927
+ const versions = game.versions || [];
1928
+
1929
+ if (versions.length > 0) {
1930
+ head.textContent = "Other Versions";
1931
+ desc.textContent = versions.length + " available version(s)";
1932
+ tile.style.opacity = '1';
1933
+ tile.style.cursor = 'pointer';
1934
+
1935
+ tile.onclick = () => {
1936
+ const choice = prompt(
1937
+ "Other versions:\n" + versions.map((v, i) => (i + 1) + ". " + v).join("\n") +
1938
+ "\nEnter number to launch:"
1939
+ );
1940
+ const idx = parseInt(choice, 10) - 1;
1941
+ if (!isNaN(idx) && versions[idx]) {
1942
+ window.open(versions[idx], '_blank');
1943
+ }
1944
+ };
1945
+ } else {
1946
+ head.textContent = "Other Versions";
1947
+ desc.textContent = "No other versions";
1948
+ tile.style.opacity = '0.5';
1949
+ tile.style.cursor = 'default';
1950
+ tile.onclick = null;
1951
+ }
1952
+ }
1953
+
1954
+ // Clock
1955
+ setInterval(() => {
1956
+ const now = new Date();
1957
+ const h = String(now.getHours()).padStart(2, '0');
1958
+ const m = String(now.getMinutes()).padStart(2, '0');
1959
+ document.getElementById('clock').innerText = `${h}:${m}`;
1960
+ }, 1000);
1961
+
1962
+ // Inputs
1963
+ window.addEventListener('keydown', (e) => {
1964
+ if (isLibraryOpen || isOtherOpen) {
1965
+ if(e.key === 'Escape') {
1966
+ if(isLibraryOpen) {
1967
+ libSearchInput.value = '';
1968
+ currentCategoryFilter = 'All';
1969
+ renderCategoryButtons();
1970
+ renderOverlayGrid();
1971
+ closeOverlay();
1972
+ } else if (isOtherOpen) {
1973
+ toggleOther(false);
1974
+ }
1975
+ }
1976
+ return;
1977
+ }
1978
+
1979
+ // Do not allow movement while dragging a card
1980
+ if (dragSrcEl) return;
1981
+
1982
+ const oldIndex = selectedIndex;
1983
+
1984
+ if(e.key === 'ArrowRight') updateSelection(selectedIndex + 1);
1985
+ if(e.key === 'ArrowLeft') updateSelection(selectedIndex - 1);
1986
+
1987
+ if ((e.key === 'ArrowRight' || e.key === 'ArrowLeft') && selectedIndex !== oldIndex) {
1988
+ playSound('hover');
1989
+ }
1990
+
1991
+ if(e.key === 'Enter') activateItem(selectedIndex);
1992
+ });
1993
+
1994
+ // Initialize
1995
+ loadTheme();
1996
+ loadGames();
1997
+ loadPS5BackgroundSetting();
1998
+ loadSoundSettings();
1999
+ loadCloak();
2000
+ </script>
2001
+
2002
+ <style>
2003
+ /* RED THEME OVERRIDES (match other themes) */
2004
+ /* The main theme-red block above handles most of this now, but keeping the background override for safety/clarity */
2005
+ body.theme-red {
2006
+ background-color: #3a0000 !important;
2007
+ background-image:
2008
+ radial-gradient(circle at 20% 0, rgba(255,80,80,0.25) 0%, transparent 55%),
2009
+ radial-gradient(circle at 80% 100%, rgba(180,0,0,0.35) 0%, transparent 55%),
2010
+ linear-gradient(135deg, #3a0000, #7a0000) !important;
2011
+ --ps-light-blue: #ff4d4d !important;
2012
+ }
2013
+ </style>
2014
+
2015
+ </body>
2016
+ </html>