class-studys 7.0.2 → 9.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 (103) hide show
  1. package/.well-known/discord +1 -0
  2. package/21fn9e.js +54 -0
  3. package/README.md +25 -0
  4. package/assets/AccountPage-DmlYaBSE-8068ec8f.js +1 -0
  5. package/assets/AccountPage-fg4fizGa-8068ec8f.css +1 -0
  6. package/assets/AiPage-CTjB7gWo-8068ec8f.css +1 -0
  7. package/assets/AiPage-CbTtOj3W-8068ec8f.js +18 -0
  8. package/assets/DarkVeil-CC-BBhGo-8068ec8f.js +69 -0
  9. package/assets/GamesPage--YRYDVtw-8068ec8f.css +1 -0
  10. package/assets/GamesPage-D-HV0rpM-8068ec8f.js +1 -0
  11. package/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  12. package/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  13. package/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  14. package/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  15. package/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  16. package/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  17. package/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  18. package/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  19. package/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  20. package/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  21. package/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  22. package/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  23. package/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  24. package/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  25. package/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  26. package/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  27. package/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  28. package/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  29. package/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  30. package/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  31. package/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  32. package/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  33. package/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  34. package/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  35. package/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  36. package/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  37. package/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  38. package/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  39. package/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  40. package/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  41. package/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  42. package/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  43. package/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  44. package/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  45. package/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  46. package/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  47. package/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  48. package/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  49. package/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  50. package/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  51. package/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  52. package/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  53. package/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  54. package/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  55. package/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  56. package/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  57. package/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  58. package/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  59. package/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  60. package/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  61. package/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  62. package/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  63. package/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  64. package/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  65. package/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  66. package/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  67. package/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  68. package/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  69. package/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  70. package/assets/PlusPage-DBE836ej-8068ec8f.js +1 -0
  71. package/assets/PlusPage-vOvT9Zss-8068ec8f.css +1 -0
  72. package/assets/SettingsPage-CsV-N8Y0-8068ec8f.js +1 -0
  73. package/assets/boot-D3vqjHTs-8068ec8f.js +1 -0
  74. package/assets/fingerprint-Y5nV5FVa-8068ec8f.js +4 -0
  75. package/assets/index-COUSXtyQ-8068ec8f.js +3 -0
  76. package/assets/index-D4-Q3uqd-8068ec8f.css +1 -0
  77. package/assets/markdown-BtlGIx_H-8068ec8f.js +291 -0
  78. package/assets/markdown-dIRGbAJA-8068ec8f.css +1 -0
  79. package/assets/motion-B1hdS1Lc-8068ec8f.js +9 -0
  80. package/assets/ogl-BWe_HKY6-8068ec8f.js +1 -0
  81. package/assets/proxy-runtime-hRo6K69j-8068ec8f.js +13 -0
  82. package/assets/react-BNEptbwn-8068ec8f.js +49 -0
  83. package/assets/settings-page-B-LiQw8a-8068ec8f.css +1 -0
  84. package/assets/verity-mini-DjLHPujK.png +0 -0
  85. package/branding/ignite-os-browser-834cc7f9f1eda4fb.png +0 -0
  86. package/branding/ignite-os-browser-spooky-805a89482927bc04.png +0 -0
  87. package/branding/ignite-os-browser-spooky-no-drips-52ef55ca90dacae1.png +0 -0
  88. package/equ4q/67140b.js +2 -0
  89. package/equ4q/8tzsxm.js +41 -0
  90. package/equ4q/datyqq.js +2 -0
  91. package/favicon.svg +8 -0
  92. package/ignite-ember.css +237 -0
  93. package/index.html +29 -942
  94. package/index.svg +36 -0
  95. package/jr1n9/4m74rv.js +31 -0
  96. package/jr1n9/cw3zk6.wasm +0 -0
  97. package/logo.svg +36 -0
  98. package/package.json +25 -63
  99. package/privacy.html +113 -0
  100. package/release-manifest.json +1 -0
  101. package/release.json +1 -0
  102. package/study.html +29 -0
  103. package/terms.html +96 -0
package/index.html CHANGED
@@ -1,942 +1,29 @@
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>IGNITE OS</title>
7
- <style>
8
- :root{
9
- --accent:#ff6b1a;
10
- --bg:#050505;
11
- --glass:rgba(255,255,255,.06);
12
- --glass-border:rgba(255,255,255,.12);
13
- --text:#f5f5f5;
14
- }
15
- *{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
16
- html,body{height:100%;overflow:hidden;background:var(--bg);color:var(--text);
17
- font-family:-apple-system,BlinkMacSystemFont,"SF Pro Display","Segoe UI",Inter,Roboto,sans-serif;}
18
- ::selection{background:var(--accent);color:#000}
19
- button{font-family:inherit;cursor:pointer;border:none;background:none;color:inherit}
20
-
21
- /* ---------- particles ---------- */
22
- #particles{position:fixed;inset:0;z-index:0;pointer-events:none}
23
-
24
- /* ---------- ambient glow ---------- */
25
- .glow{position:fixed;z-index:0;pointer-events:none;filter:blur(120px);opacity:.13;border-radius:50%;
26
- background:var(--accent);animation:glowFloat 14s ease-in-out infinite alternate}
27
- .glow.g1{width:55vw;height:55vw;top:-20vw;right:-15vw}
28
- .glow.g2{width:40vw;height:40vw;bottom:-15vw;left:-10vw;animation-delay:-7s}
29
- @keyframes glowFloat{from{transform:translate(0,0) scale(1)}to{transform:translate(4vw,-3vw) scale(1.15)}}
30
-
31
- /* ---------- screens ---------- */
32
- .screen{position:fixed;inset:0;z-index:2;display:none;flex-direction:column;
33
- opacity:0;transform:scale(.985);transition:opacity .45s cubic-bezier(.22,1,.36,1),transform .45s cubic-bezier(.22,1,.36,1)}
34
- .screen.active{display:flex;opacity:1;transform:scale(1)}
35
-
36
- /* ---------- liquid glass ---------- */
37
- .glass{background:var(--glass);backdrop-filter:blur(28px) saturate(180%);
38
- -webkit-backdrop-filter:blur(28px) saturate(180%);
39
- border:1px solid var(--glass-border);border-radius:24px;position:relative;overflow:hidden;
40
- box-shadow:0 8px 32px rgba(0,0,0,.45), inset 0 1px 0 rgba(255,255,255,.15)}
41
- .glass::before{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
42
- background:linear-gradient(135deg,rgba(255,255,255,.14),transparent 40%,transparent 70%,rgba(255,255,255,.05))}
43
-
44
- /* ============================================================
45
- HOME — left-aligned hero, long scrollable page
46
- ============================================================ */
47
- #home{overflow-y:auto;scrollbar-width:none;display:block;padding:0}
48
- #proxy{padding:0}
49
- #proxyFrame{flex:1;width:100%;height:100%;border:none;background:#000}
50
- #home::-webkit-scrollbar{display:none}
51
-
52
- .hero{min-height:100vh;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;
53
- padding:12vh 7vw 10vh;position:relative;text-align:left;overflow:hidden}
54
- /* left edge accent beam */
55
- .hero::before{content:"";position:absolute;left:0;top:15%;bottom:15%;width:3px;border-radius:3px;
56
- background:linear-gradient(180deg,transparent,var(--accent),transparent);
57
- animation:beamPulse 4s ease-in-out infinite;opacity:.7}
58
- @keyframes beamPulse{0%,100%{opacity:.35;filter:blur(1px)}50%{opacity:.9;filter:blur(0)}}
59
-
60
- .os-badge{display:inline-flex;align-items:center;gap:8px;padding:6px 16px;border-radius:99px;
61
- font-size:12px;letter-spacing:.25em;text-transform:uppercase;color:var(--accent);
62
- border:1px solid color-mix(in srgb,var(--accent) 40%,transparent);margin-bottom:26px;
63
- animation:slideLeft .8s cubic-bezier(.22,1,.36,1) both, pulseBadge 3s ease-in-out infinite 1s}
64
- @keyframes slideLeft{from{opacity:0;transform:translateX(-46px)}to{opacity:1;transform:none}}
65
- @keyframes pulseBadge{0%,100%{box-shadow:0 0 0 0 color-mix(in srgb,var(--accent) 35%,transparent)}
66
- 50%{box-shadow:0 0 24px 4px color-mix(in srgb,var(--accent) 25%,transparent)}}
67
- .os-badge .dot{width:7px;height:7px;border-radius:50%;background:var(--accent);animation:blink 1.6s infinite}
68
- @keyframes blink{50%{opacity:.25}}
69
-
70
- #bigTitle{font-size:clamp(60px,10vw,138px);font-weight:800;line-height:.93;letter-spacing:-.035em;
71
- display:flex;flex-direction:column;align-items:flex-start}
72
- #bigTitle {
73
- font-size: clamp(60px, 10vw, 138px);
74
- font-weight: 800;
75
- line-height: 1;
76
- letter-spacing: -0.035em;
77
- display: flex;
78
- flex-direction: row;
79
- align-items: center;
80
- gap: 0.2em;
81
- white-space: nowrap;
82
- }
83
-
84
- #rotator{height:30px;margin-top:26px;font-size:clamp(15px,1.6vw,20px);color:#9a9a9a;overflow:hidden;position:relative;width:min(560px,80vw)}
85
- #rotator .line{position:absolute;inset:0;display:flex;align-items:center;gap:10px;
86
- opacity:0;transform:translateY(26px);transition:opacity .6s,transform .6s cubic-bezier(.22,1,.36,1)}
87
- #rotator .line.on{opacity:1;transform:none}
88
- #rotator .line.off{opacity:0;transform:translateY(-26px)}
89
- #rotator .tick{width:22px;height:2px;background:var(--accent);border-radius:2px;flex:none;
90
- box-shadow:0 0 10px var(--accent)}
91
-
92
- /* widget row */
93
- .widget-row{display:flex;gap:16px;margin-top:48px;flex-wrap:wrap;animation:slideLeft 1s .55s cubic-bezier(.22,1,.36,1) both}
94
- .widget{padding:18px 22px;display:flex;align-items:center;gap:16px;min-width:200px;
95
- animation:floaty 5.5s ease-in-out infinite;animation-delay:var(--fd,0s);
96
- transition:transform .35s cubic-bezier(.22,1,.36,1),box-shadow .35s}
97
- .widget:hover{transform:translateY(-6px) scale(1.03);box-shadow:0 16px 44px rgba(0,0,0,.6),0 0 30px color-mix(in srgb,var(--accent) 14%,transparent)}
98
- @keyframes floaty{0%,100%{translate:0 0}50%{translate:0 -7px}}
99
- .widget .wicon{font-size:30px;filter:drop-shadow(0 0 12px color-mix(in srgb,var(--accent) 45%,transparent))}
100
- .widget .wlabel{font-size:11px;letter-spacing:.18em;text-transform:uppercase;color:#888}
101
- .widget .wvalue{font-size:20px;font-weight:700;margin-top:2px}
102
- .ring{transform:rotate(-90deg)}
103
- .ring .bg{stroke:rgba(255,255,255,.1)}
104
- .ring .fg{stroke:var(--accent);stroke-linecap:round;transition:stroke-dashoffset 1s cubic-bezier(.22,1,.36,1);
105
- filter:drop-shadow(0 0 6px var(--accent))}
106
-
107
- /* nav */
108
- .home-nav{display:flex;gap:14px;margin-top:50px;flex-wrap:wrap;animation:slideLeft 1.1s .7s cubic-bezier(.22,1,.36,1) both}
109
- .nav-btn{padding:15px 30px;border-radius:18px;font-size:15px;font-weight:600;letter-spacing:.04em;
110
- display:flex;align-items:center;gap:10px;transition:all .3s cubic-bezier(.22,1,.36,1)}
111
- .nav-btn:hover{transform:translateY(-4px) scale(1.03);border-color:color-mix(in srgb,var(--accent) 55%,transparent);
112
- box-shadow:0 12px 34px rgba(0,0,0,.55),0 0 26px color-mix(in srgb,var(--accent) 18%,transparent)}
113
- .nav-btn .arr{transition:transform .3s}
114
- .nav-btn:hover .arr{transform:translateX(6px)}
115
- .nav-btn.primary{background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 70%,#000));
116
- color:#0a0a0a;box-shadow:0 8px 26px color-mix(in srgb,var(--accent) 35%,transparent)}
117
-
118
- /* scroll cue */
119
- .scroll-cue{position:absolute;left:50%;bottom:26px;transform:translateX(-50%);display:flex;flex-direction:column;
120
- align-items:center;gap:6px;color:#666;font-size:10px;letter-spacing:.35em;text-transform:uppercase;
121
- animation:fadeIn 1.4s 1.6s both}
122
- .scroll-cue .chev{width:12px;height:12px;border-right:2px solid var(--accent);border-bottom:2px solid var(--accent);
123
- transform:rotate(45deg);animation:chevBounce 1.6s ease-in-out infinite}
124
- @keyframes chevBounce{0%,100%{transform:rotate(45deg) translate(0,0);opacity:.5}50%{transform:rotate(45deg) translate(5px,5px);opacity:1}}
125
- @keyframes fadeIn{from{opacity:0}to{opacity:1}}
126
-
127
- /* ---------- library marquee ---------- */
128
- .library{width:100%;padding:9vh 0 16vh;display:flex;flex-direction:column;gap:24px;
129
- animation:fadeIn 1s both}
130
- .library .lib-title{padding:0 7vw;font-size:clamp(28px,4vw,48px);font-weight:800;letter-spacing:-.02em;display:flex;align-items:baseline;gap:16px}
131
- .library .lib-title em{font-style:normal;color:var(--accent)}
132
- .library .lib-title .n{font-size:13px;color:#666;font-weight:600;letter-spacing:.2em}
133
- .library .lib-sub{padding:0 7vw;color:#777;font-size:14px;margin-top:-12px}
134
- .rail-window{overflow:hidden;width:100%;position:relative;padding:8px 0;
135
- -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
136
- mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
137
- .rail-track{display:flex;gap:20px;width:max-content;padding-right:20px;animation:railScroll 42s linear infinite}
138
- .rail-track.rev{animation-direction:reverse;animation-duration:52s}
139
- .rail-window:hover .rail-track{animation-play-state:paused}
140
- @keyframes railScroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
141
- .rail-game{width:96px;height:96px;flex:none;border-radius:26px;overflow:hidden;
142
- position:relative;border:1px solid var(--glass-border);cursor:pointer;
143
- transition:transform .35s cubic-bezier(.34,1.56,.64,1),box-shadow .35s;animation:floaty 6s ease-in-out infinite;animation-delay:var(--fd,0s)}
144
- .rail-game:hover{transform:scale(1.16) rotate(-4deg);animation-play-state:paused;
145
- box-shadow:0 12px 34px rgba(0,0,0,.65),0 0 26px var(--gc,#ff6b1a)}
146
- .rail-game img{width:100%;height:100%;object-fit:cover;display:block}
147
-
148
- /* section headers */
149
- .hub{padding:34px 5vw 130px;overflow-y:auto;scrollbar-width:none}
150
- .hub::-webkit-scrollbar{display:none}
151
- .hub-head{display:flex;align-items:flex-end;justify-content:space-between;margin-bottom:34px;
152
- animation:slideLeft .7s cubic-bezier(.22,1,.36,1) both}
153
- .hub-head h2{font-size:clamp(34px,4.5vw,58px);font-weight:800;letter-spacing:-.02em}
154
- .hub-head h2 em{font-style:normal;color:var(--accent)}
155
- .hub-head .count{font-size:13px;color:#777;letter-spacing:.15em;text-transform:uppercase}
156
-
157
- /* games grid: rows of 5 */
158
- .games-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:22px}
159
- @media(max-width:1100px){.games-grid{grid-template-columns:repeat(4,1fr)}}
160
- @media(max-width:800px){.games-grid{grid-template-columns:repeat(3,1fr)}.hero{padding:10vh 6vw}}
161
- @media(max-width:520px){.games-grid{grid-template-columns:repeat(2,1fr)}}
162
- .game-tile{aspect-ratio:1;border-radius:26px;overflow:hidden;position:relative;cursor:pointer;
163
- border:1px solid var(--glass-border);
164
- animation:tileIn .7s cubic-bezier(.22,1,.36,1) both;animation-delay:var(--d,0s);
165
- transition:transform .4s cubic-bezier(.34,1.56,.64,1),box-shadow .4s}
166
- @keyframes tileIn{from{opacity:0;transform:translateY(34px) scale(.92)}to{opacity:1;transform:none}}
167
- .game-tile:hover{transform:translateY(-8px) scale(1.04);z-index:2;
168
- box-shadow:0 20px 50px rgba(0,0,0,.65),0 0 34px var(--gc,#ff6b1a)}
169
- .game-tile img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s cubic-bezier(.22,1,.36,1)}
170
- .game-tile:hover img{transform:scale(1.12)}
171
- .game-tile .shine{position:absolute;inset:0;pointer-events:none;
172
- background:linear-gradient(115deg,transparent 30%,rgba(255,255,255,.22) 48%,transparent 62%);
173
- transform:translateX(-120%);transition:transform .7s ease}
174
- .game-tile:hover .shine{transform:translateX(120%)}
175
- .game-tile .tip{position:absolute;left:50%;bottom:-8px;transform:translate(-50%,10px);opacity:0;
176
- background:rgba(10,10,10,.88);backdrop-filter:blur(10px);border:1px solid var(--glass-border);
177
- padding:7px 15px;border-radius:12px;font-size:13px;font-weight:600;white-space:nowrap;pointer-events:none;
178
- transition:all .3s cubic-bezier(.22,1,.36,1);z-index:3}
179
- .game-tile:hover .tip{opacity:1;transform:translate(-50%,0)}
180
- .game-tile .play-hint{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
181
- background:rgba(0,0,0,.45);opacity:0;transition:opacity .3s}
182
- .game-tile:hover .play-hint{opacity:1}
183
- .play-hint .tri{width:0;height:0;border-left:22px solid #fff;border-top:13px solid transparent;border-bottom:13px solid transparent;
184
- filter:drop-shadow(0 0 14px var(--accent));margin-left:6px}
185
- .game-tile .feat-tag{position:absolute;top:10px;left:10px;background:var(--accent);color:#000;
186
- font-size:9px;font-weight:800;letter-spacing:.12em;padding:4px 9px;border-radius:8px}
187
-
188
- /* movies */
189
- .movies-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(190px,1fr));gap:26px}
190
- .movie-tile{border-radius:20px;overflow:hidden;cursor:pointer;position:relative;aspect-ratio:2/3;
191
- border:1px solid var(--glass-border);
192
- animation:tileIn .7s cubic-bezier(.22,1,.36,1) both;animation-delay:var(--d,0s);
193
- transition:transform .45s cubic-bezier(.22,1,.36,1),box-shadow .45s}
194
- .movie-tile:hover{transform:translateY(-10px) scale(1.05);box-shadow:0 24px 60px rgba(0,0,0,.7),0 0 40px color-mix(in srgb,var(--accent) 20%,transparent)}
195
- .movie-tile img{width:100%;height:100%;object-fit:cover;display:block;transition:transform .6s cubic-bezier(.22,1,.36,1)}
196
- .movie-tile:hover img{transform:scale(1.1)}
197
- .movie-tile .veil{position:absolute;inset:0;background:linear-gradient(180deg,transparent 55%,rgba(0,0,0,.75));
198
- opacity:0;transition:opacity .35s;display:flex;align-items:flex-end;justify-content:center;padding-bottom:20px}
199
- .movie-tile:hover .veil{opacity:1}
200
- .movie-tile .tri{width:0;height:0;border-left:20px solid #fff;border-top:12px solid transparent;border-bottom:12px solid transparent;
201
- filter:drop-shadow(0 0 14px var(--accent));transition:transform .3s}
202
- .movie-tile:hover .tri{transform:scale(1.2)}
203
-
204
- /* play modal */
205
- #playModal{position:fixed;inset:0;z-index:50;display:none;align-items:center;justify-content:center;
206
- background:rgba(0,0,0,.82);backdrop-filter:blur(18px);-webkit-backdrop-filter:blur(18px)}
207
- #playModal.open{display:flex;animation:fadeIn .3s ease}
208
- .play-shell{width:min(1100px,94vw);height:min(86vh,90vh);border-radius:26px;overflow:hidden;
209
- display:flex;flex-direction:column;border:1px solid var(--glass-border);background:#0a0a0a;
210
- box-shadow:0 40px 120px rgba(0,0,0,.8),0 0 60px color-mix(in srgb,var(--accent) 10%,transparent);
211
- animation:popIn .45s cubic-bezier(.34,1.56,.64,1)}
212
- @keyframes popIn{from{opacity:0;transform:scale(.9)}to{opacity:1;transform:none}}
213
- .play-bar{display:flex;align-items:center;gap:14px;padding:12px 18px;
214
- background:rgba(255,255,255,.04);border-bottom:1px solid var(--glass-border)}
215
- .play-bar .ttl{font-weight:700;font-size:15px;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
216
- .play-bar .live{width:8px;height:8px;border-radius:50%;background:var(--accent);animation:blink 1.4s infinite;flex:none}
217
- .play-bar button{padding:8px 18px;border-radius:12px;font-size:13px;font-weight:700;
218
- background:rgba(255,255,255,.08);border:1px solid var(--glass-border);transition:all .25s}
219
- .play-bar button:hover{background:var(--accent);color:#000}
220
- #playFrame{flex:1;width:100%;border:none;background:#000}
221
- .play-shell:fullscreen{width:100vw;height:100vh;border-radius:0}
222
- .play-shell:fullscreen .play-bar{padding:14px 22px}
223
- .play-loading{position:absolute;inset:44px 0 0;display:flex;flex-direction:column;gap:18px;
224
- align-items:center;justify-content:center;background:#0a0a0a;transition:opacity .4s;z-index:2}
225
- .play-loading.gone{opacity:0;pointer-events:none}
226
- .spinner{width:52px;height:52px;border-radius:50%;border:3px solid rgba(255,255,255,.1);
227
- border-top-color:var(--accent);animation:spin 1s linear infinite}
228
- @keyframes spin{to{transform:rotate(360deg)}}
229
-
230
- /* dock */
231
- #dock{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);z-index:20;display:flex;gap:6px;
232
- padding:8px;border-radius:24px;animation:riseIn 1s .5s cubic-bezier(.22,1,.36,1) both}
233
- @keyframes riseIn{from{opacity:0;transform:translate(-50%,40px)}to{opacity:1;transform:translate(-50%,0)}}
234
- .dock-btn{padding:12px 22px;border-radius:17px;font-size:14px;font-weight:600;color:#aaa;
235
- display:flex;align-items:center;gap:8px;transition:all .3s cubic-bezier(.22,1,.36,1);position:relative}
236
- .dock-btn:hover{color:#fff;transform:translateY(-3px)}
237
- .dock-btn.on{color:#0a0a0a;background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 75%,#000));
238
- box-shadow:0 6px 22px color-mix(in srgb,var(--accent) 40%,transparent)}
239
- .dock-btn .dicon{font-size:16px}
240
-
241
- /* settings */
242
- #settingsModal{position:fixed;inset:0;z-index:60;display:none;align-items:center;justify-content:center;
243
- background:rgba(0,0,0,.7);backdrop-filter:blur(14px)}
244
- #settingsModal.open{display:flex;animation:fadeIn .25s ease}
245
- .settings-card{width:min(560px,92vw);max-height:86vh;overflow-y:auto;padding:32px;scrollbar-width:none;
246
- animation:popIn .4s cubic-bezier(.34,1.56,.64,1)}
247
- .settings-card::-webkit-scrollbar{display:none}
248
- .settings-card h3{font-size:26px;font-weight:800;margin-bottom:6px}
249
- .settings-card .sub{color:#888;font-size:13px;margin-bottom:26px}
250
- .set-row{display:flex;align-items:center;justify-content:space-between;gap:18px;padding:16px 0;
251
- border-bottom:1px solid rgba(255,255,255,.07)}
252
- .set-row .lbl{font-size:14px;font-weight:600}
253
- .set-row .hint{font-size:11px;color:#777;margin-top:3px}
254
- .swatches{display:flex;gap:10px;flex-wrap:wrap}
255
- .swatch{width:36px;height:36px;border-radius:12px;cursor:pointer;border:2px solid transparent;transition:all .25s}
256
- .swatch:hover{transform:scale(1.15)}
257
- .swatch.on{border-color:#fff;transform:scale(1.15);box-shadow:0 0 16px currentColor}
258
- input[type=text],input[type=range]{accent-color:var(--accent)}
259
- .set-input{background:rgba(255,255,255,.07);border:1px solid var(--glass-border);color:#fff;
260
- padding:10px 14px;border-radius:12px;font-size:14px;width:170px;outline:none;transition:border .25s}
261
- .set-input:focus{border-color:var(--accent)}
262
-
263
- /* ---------- TOUR ---------- */
264
- #tour{position:fixed;inset:0;z-index:80;display:none}
265
- #tour.open{display:block}
266
- #tour .dim{position:absolute;inset:0;background:rgba(0,0,0,.72);backdrop-filter:blur(6px);animation:fadeIn .3s}
267
- .tour-card{position:absolute;left:50%;bottom:110px;transform:translateX(-50%);width:min(440px,90vw);
268
- padding:26px 28px;animation:popIn .45s cubic-bezier(.34,1.56,.64,1)}
269
- .tour-card .step-n{font-size:10px;letter-spacing:.3em;color:var(--accent);font-weight:800;text-transform:uppercase}
270
- .tour-card h4{font-size:20px;font-weight:800;margin:8px 0 8px}
271
- .tour-card p{font-size:14px;color:#b5b5b5;line-height:1.6}
272
- .tour-card .tour-nav{display:flex;gap:10px;margin-top:20px;align-items:center}
273
- .tour-card .tour-nav .prog{flex:1;display:flex;gap:6px}
274
- .tour-card .tour-nav .prog i{flex:1;height:3px;border-radius:3px;background:rgba(255,255,255,.14);transition:background .3s}
275
- .tour-card .tour-nav .prog i.on{background:var(--accent);box-shadow:0 0 8px var(--accent)}
276
- .tour-btn{padding:10px 20px;border-radius:13px;font-size:13px;font-weight:700;
277
- background:rgba(255,255,255,.08);border:1px solid var(--glass-border);transition:all .25s}
278
- .tour-btn.next{background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 75%,#000));color:#0a0a0a;border:none}
279
- .tour-btn:hover{transform:translateY(-2px)}
280
- .tour-spot{position:absolute;z-index:81;border-radius:28px;pointer-events:none;
281
- box-shadow:0 0 0 3px var(--accent),0 0 44px 6px color-mix(in srgb,var(--accent) 45%,transparent);
282
- animation:spotPulse 1.6s ease-in-out infinite;transition:all .5s cubic-bezier(.22,1,.36,1)}
283
- @keyframes spotPulse{0%,100%{opacity:.85}50%{opacity:1}}
284
-
285
- /* ---------- AI ---------- */
286
- .ai-wrap{flex:1;display:flex;flex-direction:column;max-width:900px;width:100%;margin:0 auto;padding:30px 4vw 120px}
287
- .ai-head{display:flex;align-items:center;gap:16px;margin-bottom:20px;animation:slideLeft .6s both}
288
- .ai-orb{width:52px;height:52px;border-radius:50%;flex:none;position:relative;
289
- background:radial-gradient(circle at 32% 30%,color-mix(in srgb,var(--accent) 80%,#fff),var(--accent) 45%,#3a1500);
290
- animation:orbFloat 4s ease-in-out infinite;box-shadow:0 0 34px color-mix(in srgb,var(--accent) 45%,transparent)}
291
- @keyframes orbFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
292
- .ai-orb::after{content:"";position:absolute;inset:-7px;border-radius:50%;
293
- border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);animation:orbSpin 7s linear infinite}
294
- @keyframes orbSpin{to{transform:rotate(360deg)}}
295
- .ai-head h2{font-size:26px;font-weight:800}
296
- .ai-head p{font-size:13px;color:#999}
297
- #chatLog{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:16px;padding:10px 4px;scrollbar-width:none}
298
- #chatLog::-webkit-scrollbar{display:none}
299
- .msg{max-width:82%;padding:15px 19px;border-radius:22px;font-size:15px;line-height:1.55;
300
- animation:msgIn .45s cubic-bezier(.22,1,.36,1) both;white-space:pre-wrap;word-break:break-word}
301
- @keyframes msgIn{from{opacity:0;transform:translateY(16px) scale(.97)}to{opacity:1;transform:none}}
302
- .msg.user{align-self:flex-end;background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 75%,#000));
303
- color:#0d0d0d;font-weight:500;border-bottom-right-radius:6px}
304
- .msg.bot{align-self:flex-start;background:var(--glass);backdrop-filter:blur(20px);
305
- border:1px solid var(--glass-border);border-bottom-left-radius:6px}
306
- .msg.bot .who{font-size:10px;letter-spacing:.2em;text-transform:uppercase;color:var(--accent);font-weight:800;margin-bottom:6px}
307
- .code-widget{margin:12px 0;border-radius:16px;overflow:hidden;border:1px solid var(--glass-border);
308
- background:#0b0b0f;box-shadow:0 8px 24px rgba(0,0,0,.4)}
309
- .code-head{display:flex;align-items:center;gap:10px;padding:9px 14px;background:rgba(255,255,255,.05);
310
- border-bottom:1px solid var(--glass-border)}
311
- .code-head .dots{display:flex;gap:6px}
312
- .code-head .dots i{width:10px;height:10px;border-radius:50%;background:rgba(255,255,255,.15)}
313
- .code-head .dots i:first-child{background:var(--accent)}
314
- .code-head .lang{font-size:11px;letter-spacing:.12em;text-transform:uppercase;color:#888;flex:1}
315
- .code-head .collapse,.code-head .copy{font-size:11px;font-weight:700;padding:5px 12px;border-radius:9px;
316
- background:rgba(255,255,255,.07);border:1px solid var(--glass-border);transition:all .2s}
317
- .code-head .copy:hover{background:var(--accent);color:#000}
318
- .code-widget pre{max-height:220px;overflow:auto;padding:15px;font-size:12.5px;line-height:1.6;
319
- font-family:"SF Mono",Menlo,Consolas,monospace;color:#d8d8e8;scrollbar-width:thin;transition:max-height .4s ease}
320
- .code-widget.collapsed pre{max-height:0;padding-top:0;padding-bottom:0}
321
- .code-widget pre::-webkit-scrollbar{width:6px;height:6px}
322
- .code-widget pre::-webkit-scrollbar-thumb{background:color-mix(in srgb,var(--accent) 50%,transparent);border-radius:3px}
323
- .typing{align-self:flex-start;display:flex;gap:6px;padding:16px 20px}
324
- .typing i{width:8px;height:8px;border-radius:50%;background:var(--accent);animation:tp 1.2s infinite}
325
- .typing i:nth-child(2){animation-delay:.18s}.typing i:nth-child(3){animation-delay:.36s}
326
- @keyframes tp{0%,60%,100%{transform:translateY(0);opacity:.4}30%{transform:translateY(-7px);opacity:1}}
327
- #aiInputBar{display:flex;gap:12px;margin-top:18px;padding:10px 10px 10px 22px;align-items:center;
328
- animation:riseInDock .8s .2s both}
329
- @keyframes riseInDock{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:none}}
330
- #aiInput{flex:1;background:none;border:none;outline:none;color:#fff;font-size:15px;font-family:inherit}
331
- #aiInput::placeholder{color:#666}
332
- #sendBtn{width:48px;height:48px;border-radius:16px;flex:none;display:flex;align-items:center;justify-content:center;
333
- background:linear-gradient(135deg,var(--accent),color-mix(in srgb,var(--accent) 70%,#000));color:#0a0a0a;
334
- font-size:19px;transition:transform .25s,box-shadow .25s;box-shadow:0 6px 20px color-mix(in srgb,var(--accent) 35%,transparent)}
335
- #sendBtn:hover{transform:scale(1.1) rotate(-8deg)}
336
- .chips{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}
337
- .chip{font-size:12px;padding:8px 16px;border-radius:99px;border:1px solid var(--glass-border);
338
- color:#aaa;background:rgba(255,255,255,.04);transition:all .25s}
339
- .chip:hover{color:#fff;border-color:var(--accent);transform:translateY(-2px)}
340
-
341
- #toast{position:fixed;left:50%;bottom:96px;transform:translate(-50%,20px);z-index:99;opacity:0;
342
- background:rgba(15,15,10,.92);backdrop-filter:blur(14px);border:1px solid var(--glass-border);
343
- padding:11px 22px;border-radius:14px;font-size:13px;font-weight:600;pointer-events:none;
344
- transition:all .35s cubic-bezier(.22,1,.36,1)}
345
- #toast.on{opacity:1;transform:translate(-50%,0)}
346
- </style>
347
- <base target="_blank">
348
- <base target="_blank">
349
- </head>
350
- <body>
351
-
352
- <canvas id="particles"></canvas>
353
- <div class="glow g1"></div>
354
- <div class="glow g2"></div>
355
-
356
- <!-- ============ HOME ============ -->
357
- <div class="screen active" id="home">
358
- <div class="hero" id="hero">
359
- <div class="os-badge"><span class="dot"></span>IGNITE OS · v2.1</div>
360
- <h1 id="bigTitle">
361
- <span class="row" data-txt="IGNITE"></span>
362
- <span class="row os" data-txt="OS"></span>
363
- </h1>
364
- <div id="rotator"></div>
365
-
366
- <div class="widget-row" id="widgetRow">
367
- <div class="widget glass" style="--fd:0s">
368
- <div class="wicon" id="wIcon">⛅</div>
369
- <div><div class="wlabel">Weather</div><div class="wvalue" id="wVal">--°</div></div>
370
- </div>
371
- <div class="widget glass" style="--fd:1.4s">
372
- <svg class="ring" width="52" height="52" viewBox="0 0 52 52">
373
- <circle class="bg" cx="26" cy="26" r="21" fill="none" stroke-width="5"/>
374
- <circle class="fg" id="battRing" cx="26" cy="26" r="21" fill="none" stroke-width="5"
375
- stroke-dasharray="131.9" stroke-dashoffset="131.9"/>
376
- </svg>
377
- <div><div class="wlabel">Battery</div><div class="wvalue" id="battVal">--%</div></div>
378
- </div>
379
- <div class="widget glass" style="--fd:2.8s">
380
- <div class="wicon">🕒</div>
381
- <div><div class="wlabel">Time</div><div class="wvalue" id="clockVal">--:--</div></div>
382
- </div>
383
- </div>
384
-
385
- <div class="home-nav">
386
- <button class="nav-btn primary glass" onclick="go('games')">Enter Games Hub <span class="arr">→</span></button>
387
- <button class="nav-btn glass" onclick="go('movies')">Movies <span class="arr">→</span></button>
388
- <button class="nav-btn glass" onclick="go('ai')">Ask IGNITE AI <span class="arr">→</span></button>
389
- </div>
390
-
391
- <div class="scroll-cue"><span>Scroll</span><div class="chev"></div></div>
392
- </div>
393
-
394
- <!-- library marquee -->
395
- <div class="library" id="librarySec">
396
- <div class="lib-title">Game <em>Library</em> <span class="n">20 TITLES</span></div>
397
- <div class="lib-sub">Auto-scrolling · hover to pause · click any icon to play instantly</div>
398
- <div class="rail-window"><div class="rail-track" id="railTrack"></div></div>
399
- <div class="rail-window"><div class="rail-track rev" id="railTrack2"></div></div>
400
- </div>
401
- </div>
402
-
403
- <!-- ============ GAMES HUB ============ -->
404
- <div class="screen" id="games">
405
- <div class="hub">
406
- <div class="hub-head">
407
- <h2>Games <em>Hub</em></h2>
408
- <div class="count" id="gameCount"></div>
409
- </div>
410
- <div class="games-grid" id="gamesGrid"></div>
411
- </div>
412
- </div>
413
-
414
- <!-- ============ MOVIE HUB ============ -->
415
- <div class="screen" id="movies">
416
- <div class="hub">
417
- <div class="hub-head">
418
- <h2>Movie <em>Theater</em></h2>
419
- <div class="count" id="movieCount"></div>
420
- </div>
421
- <div class="movies-grid" id="moviesGrid"></div>
422
- </div>
423
- </div>
424
-
425
- <!-- ============ AI ============ -->
426
- <div class="screen" id="ai">
427
- <div class="ai-wrap">
428
- <div class="ai-head">
429
- <div class="ai-orb"></div>
430
- <div>
431
- <h2>IGNITE <span style="color:var(--accent)">AI</span></h2>
432
- <p>Super-smart assistant · great at coding, answers & ideas</p>
433
- </div>
434
- </div>
435
- <div id="chatLog"></div>
436
- <div class="chips">
437
- <button class="chip" onclick="quickAsk('Write a Python script that sorts a list and explain it')">💻 Give me code</button>
438
- <button class="chip" onclick="quickAsk('Explain quantum computing simply')">🧠 Teach me something</button>
439
- <button class="chip" onclick="quickAsk('Help me build a game in JavaScript')">🎮 Build a game</button>
440
- </div>
441
- <div id="aiInputBar" class="glass">
442
- <input id="aiInput" placeholder="Ask IGNITE AI anything… even code" autocomplete="off">
443
- <button id="sendBtn" onclick="sendMsg()">➤</button>
444
- </div>
445
- </div>
446
- </div>
447
-
448
- <!-- ============ PROXY ============ -->
449
- <div class="screen" id="proxy">
450
- <iframe id="proxyFrame" src="about:blank"
451
- sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"></iframe>
452
- </div>
453
-
454
- <!-- ============ DOCK ============ -->
455
- <nav id="dock" class="glass">
456
- <button class="dock-btn on" data-s="home" onclick="go('home')"><span class="dicon">⌂</span>Home</button>
457
- <button class="dock-btn" data-s="games" onclick="go('games')"><span class="dicon">🎮</span>Games</button>
458
- <button class="dock-btn" data-s="movies" onclick="go('movies')"><span class="dicon">🎬</span>Movies</button>
459
- <button class="dock-btn" data-s="ai" onclick="go('ai')"><span class="dicon">✦</span>AI</button>
460
- <button class="dock-btn" data-s="proxy" onclick="go('proxy')"><span class="dicon">🧭</span>Proxy</button>
461
- <button class="dock-btn" id="settingsBtn" onclick="openSettings()"><span class="dicon">⚙</span></button>
462
- </nav>
463
-
464
- <!-- ============ PLAY MODAL ============ -->
465
- <div id="playModal">
466
- <div class="play-shell">
467
- <div class="play-bar">
468
- <span class="live"></span>
469
- <span class="ttl" id="playTitle">Now Playing</span>
470
- <button onclick="toggleFullScreen()" title="Fullscreen">⛶ Fullscreen</button>
471
- <button onclick="closePlay()">✕ Close</button>
472
- </div>
473
- <div style="position:relative;flex:1;display:flex">
474
- <div class="play-loading" id="playLoading"><div class="spinner"></div><div style="color:#888;font-size:13px">Loading…</div></div>
475
- <iframe id="playFrame" allowfullscreen sandbox="allow-scripts allow-same-origin allow-forms allow-fullscreen allow-popups-to-escape-sandbox"></iframe>
476
- </div>
477
- </div>
478
- </div>
479
-
480
- <!-- ============ SETTINGS ============ -->
481
- <div id="settingsModal" onclick="if(event.target===this)closeSettings()">
482
- <div class="settings-card glass">
483
- <h3>Settings</h3>
484
- <div class="sub">Customize IGNITE OS — accent color, weather, particles & AI.</div>
485
-
486
- <div class="set-row">
487
- <div><div class="lbl">Accent Color</div><div class="hint">The tiny bit of orange — make it yours.</div></div>
488
- <div class="swatches" id="swatches"></div>
489
- </div>
490
-
491
- <div class="set-row">
492
- <div><div class="lbl">Weather City</div><div class="hint">Leave blank to use your location.</div></div>
493
- <input class="set-input" id="cityInput" type="text" placeholder="e.g. New York">
494
- </div>
495
-
496
- <div class="set-row">
497
- <div><div class="lbl">Particle Density</div><div class="hint">Snow intensity on the home screen.</div></div>
498
- <input type="range" id="particleRange" min="40" max="260" step="10">
499
- </div>
500
-
501
- <div class="set-row">
502
- <div><div class="lbl">AI Model</div><div class="hint">Which OpenAI model IGNITE AI uses.</div></div>
503
- <input class="set-input" id="modelInput" type="text" placeholder="model name">
504
- </div>
505
-
506
- <div class="set-row">
507
- <div><div class="lbl">Beginner Tour</div><div class="hint">Replay the welcome walkthrough.</div></div>
508
- <button class="tour-btn" onclick="closeSettings();startTour()">▶ Replay</button>
509
- </div>
510
-
511
- <div style="display:flex;gap:12px;margin-top:26px">
512
- <button class="nav-btn primary glass" style="flex:1;justify-content:center" onclick="saveSettings()">Save</button>
513
- <button class="nav-btn glass" style="flex:1;justify-content:center" onclick="closeSettings()">Cancel</button>
514
- </div>
515
- </div>
516
- </div>
517
-
518
- <!-- ============ TOUR ============ -->
519
- <div id="tour">
520
- <div class="dim"></div>
521
- <div class="tour-spot" id="tourSpot"></div>
522
- <div class="tour-card glass">
523
- <div class="step-n" id="tourStepN">Step 1 of 5</div>
524
- <h4 id="tourTitle"></h4>
525
- <p id="tourText"></p>
526
- <div class="tour-nav">
527
- <div class="prog" id="tourProg"></div>
528
- <button class="tour-btn" onclick="endTour()">Skip</button>
529
- <button class="tour-btn next" id="tourNext" onclick="tourNext()">Next →</button>
530
- </div>
531
- </div>
532
- </div>
533
-
534
- <div id="toast"></div>
535
-
536
- <script>
537
- /* ================= DATA (links encoded — not exposed) ================= */
538
- const GAMES = [{id:'cookie-clicker',name:'Cookie Clicker',color:'#22d3ee',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWNvb2tpZS1jbGlja2VyQDUuMC4wL2luZGV4Lmh0bWw=',cover:'https://i.imgur.com/3ZQ3Z3Z.png'},
539
- {id:'recoil',name:'Recoil',color:'#ff6b1a',featured:false,u:'aHR0cHM6Ly9nZW5lcmljLWFscGFjYS0zMjUzMjEuZnJhbWVyLmFwcC8=',cover:'https://fancade.com/images/5E9F40DA7F9C9FA8.jpg'},
540
- {id:'retro-ball',name:'Retro Ball',color:'#a855f7',featured:false,u:'aHR0cHM6Ly9vYmplY3RzdG9yYWdlLnVzLWFzaGJ1cm4tMS5vcmFjbGVjbG91ZC5jb20vbi9pZHpneWZpN2F3d3IvYi9idWNrZXQtMjAyMzEwMDEtMTUxOC1yYjEuMC9vL2luZGV4Lmh0bWw=',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTfQEUbXopS95V30bru2UdgdystMD8ElzCDFzH6k6_pUQ&s'},
541
- {id:'drift-boss',name:'Drift Boss',color:'#3b82f6',featured:false,u:'aHR0cHM6Ly93d3cubWF0aHBsYXlncm91bmQuY29tL2RyaWZ0LWJvc3MtdjMvaW5kZXguaHRtbA==',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTMW_L-ZhbnvWj1lwNrWeAGDaK0rG0lCw52zSIsTfTmWNZ9oOVhXvQ_oJRA&s=10'},
542
- {id:'tiny-fishing',name:'Tiny Fishing',color:'#facc15',featured:false,u:'aHR0cHM6Ly9nZW5lcmljLWFscGFjYS0zMjUzMjEuZnJhbWVyLmFwcC9wYWdlLTM=',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR-I2lTQx2HkSPrPeyCNmAnGGd_c29Um34tPc8TpiyJjg&s=10'},
543
- {id:'small-world-cup',name:'A Small World Cup',color:'#34d399',featured:false,u:'aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9hL21hY3Jvcy9tY3BzbWQubmV0L3MvQUtmeWNid1NLNUlxTFhiWnBwTXo5em5JSlZ5Y0I0QlpDSG9kRTN1U2dVRjRDMWZSZUVxbU1TRFl1YTgxRXc3UnAtQ244NWdvamcvZXhlYw==',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTY4AGQkfiAEPBItQdxMlfKm0Wz7eUOzt-SsZUGejt7cA&s=10'},
544
- {id:'air-hockey',name:'Air Hockey CUP',color:'#f43f5e',featured:false,u:'aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9tYWNyb3Mvcy9BS2Z5Y2J3N1ZuaFlLV0lUbEpvOXA4cXlUOEV4T2Z2bmp2aXpxNTdfamo3TXpEbnlKV1R1SHROYmVlM05Ib0VvUmh1UzZlcW8vZXhlYw==',cover:'https://play-lh.googleusercontent.com/EFevQ0zkoUL5FEPIsI8-yFKoK6QCyu3TSZRXEJiLkRaWrCidW3b6ZKNiNzuvfeaLKW9FyHeY3WYh4RR839Qanck'},
545
- {id:'slope-remake',name:'Slope REMAKE',color:'#fb7185',featured:false,u:'aHR0cHM6Ly9zY3JpcHQuZ29vZ2xlLmNvbS9hL21hY3Jvcy9tY3BzbWQubmV0L3MvQUtmeWNieWVuZFN6Zld6aml2R1VBb05nSDVRRHBKSXhKRHlKdlNZd1hXeGNWXzhnbDl4a2RUTVYyQnZEcGppV053NXBHYVZUL2V4ZWM=',cover:'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg'},
546
- {id:'subway-surfers',name:'Subway Surfers',color:'#fb7185',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vc3Vid2F5c3RwZXRlcnNidXJnQDEuMC4wL2luZGV4Lmh0bWw=',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTzwmhMeCmyvXSpjVLHUUCok1FZhyrP-D9klmlq8Pt-Ww&s=10'},
547
- {id:'bacon-may-die',name:'Bacon May Die',color:'#fb7185',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vc2Nob29sdWstYmFjb24tbWF5LWRpZUAxLjAuMC9pbmRleC5odG1s',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRG8Ra7wjHyaMeLnHp70ql98oi25b33UsPI63N-Jwbapw&s=10'},
548
- {id:'paper-fly',name:'Paper Fly',color:'#22d3ee',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vcGFwZXJnbWZseUAxLjAuMC9pbmRleC5odG1s',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQQFa7pNRwbmpuk0rwFYmYYjwAdFMI6qH18R1rO_rd8XQ&s=10'},
549
- {id:'slope-505',name:'Slope 505',color:'#fb7185',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vc2xvZXBsdXNAMS4wLjAvaW5kZXguaHRtbA==',cover:'https://play-lh.googleusercontent.com/Ev5gkPTONr-1k1r7erT0iiW-pUZoGUH_6FVQdD91e9ghSAqZRMTaFVGQW5Z6PLOfFgxZnJhdEBE2z-TgfZ1bsg'},
550
- {id:'boat-game',name:'Boat Game',color:'#3b82f6',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vYm9hdGVtYW5AMS4wLjAvaW5kZXguaHRtbA==',cover:'https://play-lh.googleusercontent.com/k80-V4CxUpsoNUTiZCasfAF4yTxy428anUQiM_AieHnLRMq2qmCPIHIYWT8dYATGyo1eTjZLZEa3aa3b4kyingA'},
551
- {id:'1v1-lol',name:'1v1.lol',color:'#22d3ee',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLTF2MS1sb2xANS4wLjAvaW5kZXguaHRtbA==',cover:'https://play-lh.googleusercontent.com/NAE7nd8mxERLlT-eIM68aEQi1RthPUzVd8KN_C-RPmvDgBLJ-C_kBHrBQFYPi8wQwr_nkxGUadoS0xStbU_jc7s=w240-h480-rw'},
552
- {id:'crossy-road',name:'Crossy Road',color:'#facc15',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWNyb3NzeS1yb2FkQDUuMC4wL2luZGV4Lmh0bWw=',cover:'https://play-lh.googleusercontent.com/LX__Y8tKCvkvEgboZtQfDsvYiNFa6kwWSpqObipZI2MLK5rFVd5tEU8xgMNoMj1ICOf9TO7PFoFmiKGeIpf1=w240-h480-rw'},
553
- {id:'basketball-stars',name:'Basketball Stars',color:'#ff6b1a',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWJhc2tldGJhbGwtc3RhcnNANS4wLjAvaW5kZXguaHRtbA==',cover:'https://m.media-amazon.com/images/I/81BL1Hx2KuL.png'},
554
- {id:'bitlife',name:'Bitlife',color:'#34d399',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWJpdGxpZmVANS4wLjAvaW5kZXguaHRtbA==',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRZdo5cBqkv1uAcfCofpC-WAViocRNLlysohwHMV2Lqzw&s=10'},
555
- {id:'tag',name:'Tag',color:'#facc15',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLXRhZ0AxLjAuMC9pbmRleC5odG1s',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSFwGQ9vG6ZifxDpWHtuu8oSzYqll79xZ9rW_KwPwzCdA&s'},
556
- {id:'block-blast',name:'Block Blast',color:'#a855f7',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWJsb2NrLWJsYXN0QDUuMC4wL2luZGV4Lmh0bWw=',cover:'https://imgs.crazygames.com/block-puzzle-master_1x1/20250924025352/block-puzzle-master_1x1-cover?format=auto&quality=70&metadata=none'},
557
- {id:'cut-the-rope',name:'Cut the Rope',color:'#34d399',featured:false,u:'aHR0cHM6Ly91bnBrZy5jb20vZWRjdWJlLWN1dC10aGUtcm9wZUA1LjAuMC9pbmRleC5odG1s',cover:'https://play-lh.googleusercontent.com/2bTmHHM7DBMVe7fLh16Em-J-tVBMFGL2HJCxPAMNF7gAYIfU6EZoYG1__lQ0cvFMEyp3GU9CGijZzEB1qJHF'},
558
- {id:'duck-math',name:'Duck Math',color:'#34d399',featured:true,u:'aHR0cHM6Ly91bnBrZy5jb20vaWduaXRlLWdvb2JlckAxLjEuNC9pbmRleC5odG1sIy9jbGFzcy9kcmlmdC1raW5n',cover:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRJFXWvY3bIWxbR2SZEnbZaVFRYqFzZJZlOo4SYkWOexg&s'}];
559
- const MOVIES = [{name:'Spiderman: Far From Home',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xdDYyWVk1VjJXRnFQQTgwMWNERUN3dzFFUVlrNGZNbmsvcHJldmlldw==',poster:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSjdXbHSbJUSaJdgj45vDisJUiPj1qljxkOPbfojU3M1w&s=10'},
560
- {name:'Spiderman: Homecoming',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xaFVJeDA1RzNOX3VjZzhTd2RaSFJ0XzVNZHFMUVp0YWkvcHJldmlldw==',poster:'https://resizing.flixster.com/-XZAfHZM39UwaGJIFWKAE8fS0ak=/v3/t/assets/p12798844_p_v8_ao.jpg'},
561
- {name:'F1',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xa196UkR0NFJuXzlGb3RZSlVmcmhKWjBaREtmd3FRazEvcHJldmlldw==',poster:'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSr2SgmDLkFhLGuF1CnP_xJv1stiKvK027DkN6m6wdqdg&s=10'},
562
- {name:'Obsession',u:'aHR0cHM6Ly9kcml2ZS5nb29nbGUuY29tL2ZpbGUvZC8xRkVOMTZMd001WnpUQlNiQnRrWTk1THJJemxzSFhGeDcvcHJldmlldw==',poster:'https://images.contentstack.io/v3/assets/blt13adb7e2033fcee5/blt5c8bcbfee5dade5c/6a2b47a8c284bac540ef3353/Obsession_keyart_poster_2000x3000.jpg?width=2560'}];
563
- const dec = s => atob(s);
564
- const PROXY_URL = dec('aHR0cHM6Ly9tb2JpbGUtc2VydmljZS0zODYzMTEuZnJhbWVyLmFwcC8=');
565
- const AI_KEY = "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";
566
-
567
- /* ================= SETTINGS ================= */
568
- const DEFAULTS = {accent:"#ff6b1a", city:"", particles:140, model:"gpt-4o"};
569
- let S = {...DEFAULTS, ...JSON.parse(localStorage.getItem("igniteos")||"{}")};
570
- const saveS = () => localStorage.setItem("igniteos", JSON.stringify(S));
571
- const applyAccent = () => document.documentElement.style.setProperty("--accent", S.accent);
572
-
573
- /* ================= NAV ================= */
574
- function go(id){
575
- document.querySelectorAll(".screen").forEach(s=>s.classList.remove("active"));
576
- document.getElementById(id).classList.add("active");
577
- document.querySelectorAll(".dock-btn[data-s]").forEach(b=>b.classList.toggle("on", b.dataset.s===id));
578
- if(id==="proxy"){
579
- const pf = document.getElementById("proxyFrame");
580
- if(!pf.src || pf.src==="about:blank") pf.src = PROXY_URL;
581
- }
582
- if(id==="home") startParticles(); else stopParticles();
583
- window.scrollTo(0,0); document.getElementById(id==='home'?'home':id).scrollTop = 0;
584
- }
585
-
586
- /* ================= PARTICLES ================= */
587
- const cv = document.getElementById("particles"), ctx = cv.getContext("2d");
588
- let flakes = [], raf = null;
589
- function sizeCanvas(){ cv.width = innerWidth; cv.height = innerHeight; }
590
- addEventListener("resize", sizeCanvas); sizeCanvas();
591
- function makeFlakes(){
592
- flakes = Array.from({length:S.particles}, () => ({
593
- x: Math.random()*cv.width, y: Math.random()*cv.height,
594
- r: Math.random()*2.4+.6, s: Math.random()*.9+.35,
595
- a: Math.random()*Math.PI*2, w: Math.random()*.6+.15, o: Math.random()*.55+.2
596
- }));
597
- }
598
- function drawFlakes(){
599
- ctx.clearRect(0,0,cv.width,cv.height);
600
- const acc = getComputedStyle(document.documentElement).getPropertyValue("--accent").trim() || "#ff6b1a";
601
- for(const f of flakes){
602
- f.y += f.s; f.a += .012; f.x += Math.sin(f.a)*f.w;
603
- if(f.y > cv.height+6){ f.y = -6; f.x = Math.random()*cv.width; }
604
- const tw = .75 + Math.sin(f.a*2)*.25;
605
- ctx.beginPath(); ctx.arc(f.x, f.y, f.r, 0, Math.PI*2);
606
- ctx.fillStyle = f.r > 1.9 ? hexA(acc, f.o*tw*.9) : `rgba(255,255,255,${f.o*tw})`;
607
- ctx.fill();
608
- }
609
- raf = requestAnimationFrame(drawFlakes);
610
- }
611
- function hexA(h, a){
612
- const n = parseInt(h.slice(1),16);
613
- return `rgba(${n>>16&255},${n>>8&255},${n&255},${a})`;
614
- }
615
- function startParticles(){ if(!raf){ makeFlakes(); drawFlakes(); } }
616
- function stopParticles(){ cancelAnimationFrame(raf); raf = null; ctx.clearRect(0,0,cv.width,cv.height); }
617
-
618
- /* ================= TITLE letters ================= */
619
- document.querySelectorAll("#bigTitle .row").forEach((row, ri)=>{
620
- [...row.dataset.txt].forEach((ch, i)=>{
621
- const s = document.createElement("span");
622
- s.className = "ch"; s.textContent = ch;
623
- s.style.setProperty("--cd", (0.15 + ri*0.28 + i*0.055) + "s");
624
- row.appendChild(s);
625
- });
626
- });
627
-
628
- /* ================= ROTATOR ================= */
629
- const LINES = [
630
- "20 hand-picked games. Zero loading pain.",
631
- "A private movie theater, right in your browser.",
632
- "An AI that codes, explains and builds with you.",
633
- "Liquid glass. Falling light. Pure black.",
634
- "Your arcade. Your cinema. Your OS."
635
- ];
636
- const rot = document.getElementById("rotator");
637
- LINES.forEach((t,i)=>{
638
- const d = document.createElement("div");
639
- d.className = "line" + (i===0?" on":"");
640
- d.innerHTML = `<span class="tick"></span>${t}`;
641
- rot.appendChild(d);
642
- });
643
- let li = 0;
644
- setInterval(()=>{
645
- const lines = rot.children;
646
- lines[li].classList.replace("on","off");
647
- const prev = li; li = (li+1)%lines.length;
648
- setTimeout(()=>lines[prev].classList.remove("off"), 600);
649
- lines[li].classList.add("on");
650
- }, 3800);
651
-
652
- /* ================= HERO mouse parallax ================= */
653
- const hero = document.getElementById("hero");
654
- hero.addEventListener("mousemove", e=>{
655
- const rx = (e.clientX/innerWidth - .5), ry = (e.clientY/innerHeight - .5);
656
- hero.querySelectorAll(".os-badge,#bigTitle,.widget-row,.home-nav").forEach((el,i)=>{
657
- const d = (i+1)*4;
658
- el.style.transform = `translate(${-rx*d}px, ${-ry*d}px)`;
659
- });
660
- });
661
- hero.addEventListener("mouseleave", ()=>{
662
- hero.querySelectorAll(".os-badge,#bigTitle,.widget-row,.home-nav").forEach(el=>el.style.transform="");
663
- });
664
-
665
- /* ================= CLOCK ================= */
666
- setInterval(()=>{
667
- document.getElementById("clockVal").textContent =
668
- new Date().toLocaleTimeString([], {hour:"2-digit", minute:"2-digit"});
669
- }, 1000);
670
-
671
- /* ================= BATTERY ================= */
672
- (async ()=>{
673
- try{
674
- const b = await navigator.getBattery();
675
- const upd = ()=>{
676
- const pct = Math.round(b.level*100);
677
- document.getElementById("battVal").textContent = pct + "%" + (b.charging?" ⚡":"");
678
- document.getElementById("battRing").style.strokeDashoffset = 131.9*(1-b.level);
679
- };
680
- b.onlevelchange = b.onchargingchange = upd; upd();
681
- }catch(e){ document.getElementById("battVal").textContent = "N/A"; }
682
- })();
683
-
684
- /* ================= WEATHER ================= */
685
- const WMO = {0:"☀️",1:"🌤️",2:"⛅",3:"☁️",45:"🌫️",48:"🌫️",51:"🌦️",53:"🌦️",55:"🌧️",56:"🌧️",57:"🌧️",
686
- 61:"🌧️",63:"🌧️",65:"🌧️",66:"🌧️",67:"🌧️",71:"🌨️",73:"🌨️",75:"🌨️",77:"🌨️",80:"🌦️",81:"🌧️",82:"⛈️",
687
- 85:"🌨️",86:"🌨️",95:"⛈️",96:"⛈️",99:"⛈️"};
688
- async function loadWeather(){
689
- const val = document.getElementById("wVal"), ic = document.getElementById("wIcon");
690
- try{
691
- let lat, lon, name = "";
692
- if(S.city){
693
- const g = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(S.city)}&count=1`).then(r=>r.json());
694
- if(!g.results?.length) throw 0;
695
- ({latitude:lat, longitude:lon, name} = g.results[0]);
696
- } else {
697
- const p = await new Promise((ok,no)=>navigator.geolocation?navigator.geolocation.getCurrentPosition(ok,no,{timeout:5000}):no());
698
- lat = p.coords.latitude; lon = p.coords.longitude;
699
- }
700
- const w = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current_weather=true&temperature_unit=fahrenheit`).then(r=>r.json());
701
- const cw = w.current_weather;
702
- val.textContent = Math.round(cw.temperature) + "°F" + (name?` · ${name}`:"");
703
- ic.textContent = WMO[cw.weathercode] || "⛅";
704
- }catch(e){ val.textContent = "Set city ⚙"; ic.textContent = "🌍"; }
705
- }
706
-
707
- /* ================= BUILD GAMES GRID ================= */
708
- const gamesGrid = document.getElementById("gamesGrid");
709
- GAMES.forEach((g,i)=>{
710
- const t = document.createElement("div");
711
- t.className = "game-tile"; t.style.setProperty("--gc", g.color); t.style.setProperty("--d", (i*0.04)+"s");
712
- t.innerHTML = `<img src="${g.cover}" alt="" loading="lazy" onerror="this.style.display='none'">
713
- ${g.featured?'<span class="feat-tag">★ FEATURED</span>':''}
714
- <div class="shine"></div>
715
- <div class="play-hint"><div class="tri"></div></div>
716
- <div class="tip">${g.name}</div>`;
717
- t.onclick = ()=>openPlay(g.name, dec(g.u));
718
- gamesGrid.appendChild(t);
719
- });
720
- document.getElementById("gameCount").textContent = GAMES.length + " titles · click to play";
721
-
722
- /* library marquee rows */
723
- function buildRail(el, list){
724
- [...list, ...list].forEach((g,i)=>{
725
- const d = document.createElement("div");
726
- d.className = "rail-game"; d.style.setProperty("--gc", g.color);
727
- d.style.setProperty("--fd", ((i%10)*0.35)+"s");
728
- d.innerHTML = `<img src="${g.cover}" alt="" loading="lazy" onerror="this.style.display='none'">`;
729
- d.title = g.name;
730
- d.onclick = ()=>openPlay(g.name, dec(g.u));
731
- el.appendChild(d);
732
- });
733
- }
734
- buildRail(document.getElementById("railTrack"), GAMES);
735
- buildRail(document.getElementById("railTrack2"), [...GAMES].reverse());
736
-
737
- /* ================= BUILD MOVIES ================= */
738
- const mg = document.getElementById("moviesGrid");
739
- MOVIES.forEach((m,i)=>{
740
- const t = document.createElement("div");
741
- t.className = "movie-tile"; t.style.setProperty("--d", (i*0.08)+"s");
742
- t.innerHTML = `<img src="${m.poster}" alt="" loading="lazy"><div class="veil"><div class="tri"></div></div>`;
743
- t.onclick = ()=>openPlay(m.name, dec(m.u));
744
- mg.appendChild(t);
745
- });
746
- document.getElementById("movieCount").textContent = MOVIES.length + " now streaming";
747
-
748
- /* ================= PLAY MODAL ================= */
749
- const pm = document.getElementById("playModal"), pf = document.getElementById("playFrame"),
750
- pl = document.getElementById("playLoading");
751
- function openPlay(title, url){
752
- document.getElementById("playTitle").textContent = title;
753
- pl.classList.remove("gone");
754
- pm.classList.add("open");
755
- pf.src = url;
756
- pf.onload = ()=>setTimeout(()=>pl.classList.add("gone"), 400);
757
- }
758
- function closePlay(){
759
- if(document.fullscreenElement) document.exitFullscreen().catch(()=>{});
760
- pm.classList.remove("open");
761
- pf.src = "about:blank";
762
- }
763
- function toggleFullScreen(){
764
- const shell = pm.querySelector(".play-shell");
765
- if(document.fullscreenElement){ document.exitFullscreen().catch(()=>{}); return; }
766
- if(shell.requestFullscreen) shell.requestFullscreen().catch(()=>toast("Fullscreen blocked by browser"));
767
- else toast("Fullscreen not supported");
768
- }
769
- document.addEventListener("fullscreenchange", ()=>{
770
- const btn = document.querySelector("#playModal .play-bar button[onclick='toggleFullScreen()']");
771
- if(btn) btn.innerHTML = document.fullscreenElement ? "🗗 Exit Full" : "⛶ Fullscreen";
772
- });
773
- addEventListener("keydown", e=>{ if(e.key==="Escape"){ closePlay(); closeSettings(); endTour(); } });
774
-
775
- /* ================= SETTINGS ================= */
776
- const COLORS = ["#ff6b1a","#ff3b30","#ff9500","#ffd60a","#34c759","#00c7be","#32ade6","#7d7aff","#bf5af2","#ff375f"];
777
- const sw = document.getElementById("swatches");
778
- COLORS.forEach(c=>{
779
- const b = document.createElement("div");
780
- b.className = "swatch" + (S.accent===c?" on":""); b.style.background = c; b.style.color = c;
781
- b.onclick = ()=>{ S.accent = c; applyAccent(); saveS();
782
- sw.querySelectorAll(".swatch").forEach(x=>x.classList.remove("on")); b.classList.add("on"); toast("Accent updated"); };
783
- sw.appendChild(b);
784
- });
785
- function openSettings(){
786
- document.getElementById("cityInput").value = S.city;
787
- document.getElementById("particleRange").value = S.particles;
788
- document.getElementById("modelInput").value = S.model;
789
- document.getElementById("settingsModal").classList.add("open");
790
- }
791
- function closeSettings(){ document.getElementById("settingsModal").classList.remove("open"); }
792
- function saveSettings(){
793
- S.city = document.getElementById("cityInput").value.trim();
794
- S.particles = +document.getElementById("particleRange").value;
795
- S.model = document.getElementById("modelInput").value.trim() || DEFAULTS.model;
796
- saveS(); makeFlakes(); loadWeather(); closeSettings(); toast("Settings saved");
797
- }
798
- function toast(t){
799
- const el = document.getElementById("toast");
800
- el.textContent = t; el.classList.add("on");
801
- clearTimeout(el._t); el._t = setTimeout(()=>el.classList.remove("on"), 2200);
802
- }
803
-
804
- /* ================= TOUR ================= */
805
- const TOUR_STEPS = [
806
- {t:"#bigTitle", title:"Welcome to IGNITE OS ✦", text:"This is your command center. Everything runs right here in the browser — snow falls, glass glows, and your whole library lives one click away."},
807
- {t:"#widgetRow", title:"Live widgets", text:"Real-time weather, your battery ring, and the clock — all in liquid glass. Set your city later in Settings (the ⚙ button)."},
808
- {t:"#librarySec", title:"The Game Library", text:"All 20 games scroll past automatically — two rows, opposite directions. Hover to pause, click any icon and it starts instantly. No links, no new tabs."},
809
- {t:"#dock", title:"The Dock", text:"Jump between Home, Games Hub, Movie Theater and IGNITE AI anytime. The ✦ button opens your AI assistant."},
810
- {t:"#settingsBtn", title:"Make it yours", text:"Tap the gear to change the accent color, weather city, snow density, and the AI model. That's it — you're ready. Have fun!"}
811
- ];
812
- let tIdx = 0;
813
- const tourEl = document.getElementById("tour"), spot = document.getElementById("tourSpot");
814
- function startTour(){
815
- if(!document.getElementById("home").classList.contains("active")) go("home");
816
- document.getElementById("home").scrollTop = 0;
817
- tourEl.classList.add("open"); tIdx = 0; showStep();
818
- }
819
- function showStep(){
820
- const s = TOUR_STEPS[tIdx];
821
- document.getElementById("tourStepN").textContent = `Step ${tIdx+1} of ${TOUR_STEPS.length}`;
822
- document.getElementById("tourTitle").textContent = s.title;
823
- document.getElementById("tourText").textContent = s.text;
824
- document.getElementById("tourNext").textContent = tIdx===TOUR_STEPS.length-1 ? "Finish ✦" : "Next →";
825
- const prog = document.getElementById("tourProg");
826
- prog.innerHTML = TOUR_STEPS.map((_,i)=>`<i class="${i<=tIdx?'on':''}"></i>`).join("");
827
- const el = document.querySelector(s.t);
828
- el.scrollIntoView({block:"center", behavior:"smooth"});
829
- setTimeout(()=>{
830
- const r = el.getBoundingClientRect();
831
- spot.style.cssText = `left:${r.left-14}px;top:${r.top-14}px;width:${r.width+28}px;height:${r.height+28}px;display:block`;
832
- }, 350);
833
- }
834
- function tourNext(){
835
- if(tIdx === TOUR_STEPS.length-1){ endTour(); return; }
836
- tIdx++; showStep();
837
- }
838
- function endTour(){
839
- tourEl.classList.remove("open");
840
- spot.style.display = "none";
841
- localStorage.setItem("igniteos_toured", "1");
842
- }
843
-
844
- /* ================= AI ================= */
845
- const chatLog = document.getElementById("chatLog");
846
- const history = [];
847
- function addMsg(text, who){
848
- const d = document.createElement("div");
849
- d.className = "msg " + who;
850
- if(who==="bot") d.innerHTML = '<div class="who">IGNITE AI</div>' + renderBot(text);
851
- else d.textContent = text;
852
- chatLog.appendChild(d); chatLog.scrollTop = chatLog.scrollHeight;
853
- }
854
- function renderBot(text){
855
- const parts = text.split(/```(\w*)\n?([\s\S]*?)(?:```|$)/g);
856
- let out = "";
857
- for(let i=0;i<parts.length;i++){
858
- if(i%3===0){ out += esc(parts[i]).replace(/\n/g,"<br>"); }
859
- else if(i%3===2){
860
- const lang = parts[i-1] || "code";
861
- out += `<div class="code-widget collapsed">
862
- <div class="code-head"><div class="dots"><i></i><i></i><i></i></div>
863
- <span class="lang">${esc(lang)||"code"}</span>
864
- <button class="collapse">Expand</button>
865
- <button class="copy">Copy</button></div>
866
- <pre>${esc(parts[i])}</pre></div>`;
867
- }
868
- }
869
- return out;
870
- }
871
- function esc(s){ return (s||"").replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;"); }
872
- chatLog.addEventListener("click", e=>{
873
- if(e.target.classList.contains("copy")){
874
- const w = e.target.closest(".code-widget");
875
- navigator.clipboard.writeText(w.querySelector("pre").textContent).then(()=>toast("Code copied ✦"));
876
- }
877
- if(e.target.classList.contains("collapse")){
878
- const w = e.target.closest(".code-widget");
879
- w.classList.toggle("collapsed");
880
- e.target.textContent = w.classList.contains("collapsed") ? "Expand" : "Collapse";
881
- }
882
- });
883
- function quickAsk(q){ document.getElementById("aiInput").value = q; sendMsg(); }
884
-
885
- async function askOpenAI(model, q){
886
- const res = await fetch("https://api.openai.com/v1/chat/completions", {
887
- method:"POST",
888
- headers:{ "Content-Type":"application/json", "Authorization":"Bearer " + AI_KEY },
889
- body: JSON.stringify({
890
- model,
891
- messages: [
892
- {role:"system", content:"You are IGNITE AI, a super-smart, friendly assistant inside IGNITE OS. You are excellent at coding, explanations, math, creative writing, game ideas and problem solving. Always format code in triple backticks with the language name. Be concise but complete."},
893
- ...history.slice(-10),
894
- {role:"user", content:q}
895
- ]
896
- })
897
- });
898
- const data = await res.json().catch(()=>({}));
899
- if(!res.ok) throw {status:res.status, msg:data.error?.message || ("HTTP " + res.status)};
900
- if(!data.choices?.[0]?.message?.content) throw {status:res.status, msg:"Unexpected response shape"};
901
- return data.choices[0].message.content;
902
- }
903
-
904
- async function sendMsg(){
905
- const inp = document.getElementById("aiInput"), q = inp.value.trim();
906
- if(!q) return;
907
- inp.value = ""; addMsg(q, "user");
908
- const tp = document.createElement("div");
909
- tp.className = "msg bot typing"; tp.innerHTML = "<i></i><i></i><i></i>";
910
- chatLog.appendChild(tp); chatLog.scrollTop = chatLog.scrollHeight;
911
- try{
912
- let reply, lastErr;
913
- const tries = [S.model, "gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"].filter((v,i,a)=>a.indexOf(v)===i);
914
- for(const m of tries){
915
- try{ reply = await askOpenAI(m, q); if(m!==S.model) toast(`Auto-switched to ${m}`); break; }
916
- catch(e){
917
- lastErr = e;
918
- const notModelIssue = e.status===401 || e.status===403 || e.status===429;
919
- if(notModelIssue || tries.indexOf(m)===tries.length-1) throw e;
920
- }
921
- }
922
- tp.remove();
923
- history.push({role:"user",content:q},{role:"assistant",content:reply});
924
- addMsg(reply, "bot");
925
- }catch(err){
926
- tp.remove();
927
- let hint = "";
928
- if(err.status===401) hint = "The API key was rejected (401). Double-check it in the code.";
929
- else if(err.status===429) hint = "Rate limit hit (429) — wait a moment and retry.";
930
- else if(err.status===404) hint = "Model not found (404) — set a valid model in Settings.";
931
- addMsg(`⚠️ AI error: ${esc(err.msg||err.message||"unknown")}\n${hint}`, "bot");
932
- }
933
- }
934
- document.getElementById("aiInput").addEventListener("keydown", e=>{ if(e.key==="Enter") sendMsg(); });
935
-
936
- /* ================= BOOT ================= */
937
- applyAccent(); startParticles(); loadWeather();
938
- addMsg("Hey! I'm IGNITE AI ✦ I'm great at a lot of things — coding, explaining, brainstorming, math, you name it. Ask me anything, and if you want code just say so — I'll wrap it in a neat copyable block.", "bot");
939
- if(!localStorage.getItem("igniteos_toured")) setTimeout(startTour, 900);
940
- </script>
941
- </body>
942
- </html>
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
+ <script>
7
+ (function(){var w=window,s={ps:History.prototype.pushState,rs:History.prototype.replaceState};w.__lcHs=s;var h=history,f=function(){try{Object.defineProperty(h,"pushState",{value:s.ps.bind(h),configurable:!0,writable:!0}),Object.defineProperty(h,"replaceState",{value:s.rs.bind(h),configurable:!0,writable:!0})}catch(e){}};f(),setInterval(f,400)})();
8
+ </script>
9
+ <link rel="icon" type="image/png" href="./branding/ignite-os-browser-834cc7f9f1eda4fb.png" />
10
+ <title>Ignite OS</title>
11
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-0VL3ZSBXDH"></script>
12
+ <script>
13
+ window.dataLayer = window.dataLayer || [];
14
+ function gtag() { dataLayer.push(arguments); }
15
+ gtag("js", new Date());
16
+ gtag("config", "G-0VL3ZSBXDH");
17
+ </script>
18
+ <script src="jr1n9/4m74rv.js"></script>
19
+ <script src="equ4q/8tzsxm.js"></script>
20
+ <script type="module" crossorigin src="./assets/index-COUSXtyQ-8068ec8f.js"></script>
21
+ <link rel="modulepreload" crossorigin href="./assets/react-BNEptbwn-8068ec8f.js">
22
+ <link rel="modulepreload" crossorigin href="./assets/motion-B1hdS1Lc-8068ec8f.js">
23
+ <link rel="stylesheet" crossorigin href="./assets/index-D4-Q3uqd-8068ec8f.css">
24
+ <link rel="stylesheet" href="./ignite-ember.css">
25
+ </head>
26
+ <body>
27
+ <div id="root"></div>
28
+ </body>
29
+ </html>