studyup 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.html CHANGED
@@ -1,518 +1,546 @@
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>StudyUp - Student Portal</title>
7
- <!-- Material Design 1 / Roboto Typography & Icons -->
8
- <link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap" rel="stylesheet">
9
- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
10
-
11
- <style>
12
- /* Material Design 1 Core Variables & Palette */
13
- :root {
14
- --primary-color: #3f51b5; /* Indigo */
15
- --primary-dark: #303f9f;
16
- --accent-color: #ff4081;
17
- --background-color: #eeeeee;
18
- --card-background: #ffffff;
19
- --text-primary: rgba(0, 0, 0, 0.87);
20
- --text-secondary: rgba(0, 0, 0, 0.54);
21
- --error-color: #d32f2f;
22
- --shadow-elevation-2: 0 3px 1px -2px rgba(0,0,0,0.2), 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12);
23
- --shadow-elevation-4: 0 4px 5px -2px rgba(0,0,0,0.2), 0 7px 10px 1px rgba(0,0,0,0.14), 0 2px 16px 1px rgba(0,0,0,0.12);
24
- }
25
-
26
- body {
27
- font-family: 'Roboto', sans-serif;
28
- background-color: var(--background-color);
29
- color: var(--text-primary);
30
- margin: 0;
31
- padding: 0;
32
- display: flex;
33
- flex-direction: column;
34
- min-height: 100vh;
35
- justify-content: space-between;
36
- }
37
-
38
- /* Material Toolbar (Header) */
39
- header {
40
- background-color: var(--primary-color);
41
- color: white;
42
- padding: 16px 24px;
43
- box-shadow: var(--shadow-elevation-2);
44
- display: flex;
45
- align-items: center;
46
- justify-content: space-between;
47
- }
48
-
49
- header h1 {
50
- margin: 0;
51
- font-size: 20px;
52
- font-weight: 500;
53
- letter-spacing: 0.5px;
54
- }
55
-
56
- /* View Switching (Login vs Portal Dashboard) */
57
- .view {
58
- display: none;
59
- flex: 1;
60
- flex-direction: column;
61
- }
62
- .view.active {
63
- display: flex;
64
- }
65
-
66
- /* Main Container & Login Card */
67
- .container {
68
- max-width: 420px;
69
- width: 100%;
70
- margin: auto;
71
- padding: 16px;
72
- box-sizing: border-box;
73
- flex: 1;
74
- display: flex;
75
- align-items: center;
76
- justify-content: center;
77
- }
78
-
79
- .card {
80
- background-color: var(--card-background);
81
- border-radius: 2px;
82
- box-shadow: var(--shadow-elevation-4);
83
- padding: 32px 24px;
84
- display: flex;
85
- flex-direction: column;
86
- align-items: center;
87
- text-align: center;
88
- width: 100%;
89
- box-sizing: border-box;
90
- }
91
-
92
- .card-icon-wrapper {
93
- background-color: rgba(63, 81, 181, 0.1);
94
- color: var(--primary-color);
95
- width: 64px;
96
- height: 64px;
97
- border-radius: 50%;
98
- display: flex;
99
- align-items: center;
100
- justify-content: center;
101
- margin-bottom: 20px;
102
- }
103
-
104
- .card-icon-wrapper i {
105
- font-size: 32px;
106
- }
107
-
108
- .card-title {
109
- font-size: 24px;
110
- font-weight: 500;
111
- margin: 0 0 8px 0;
112
- color: var(--text-primary);
113
- }
114
-
115
- .card-subtitle {
116
- font-size: 14px;
117
- color: var(--text-secondary);
118
- margin: 0 0 24px 0;
119
- line-height: 1.4;
120
- }
121
-
122
- /* Material Floating Label Input Field */
123
- .input-container {
124
- position: relative;
125
- margin-bottom: 24px;
126
- width: 100%;
127
- text-align: left;
128
- }
129
-
130
- .input-container input {
131
- width: 100%;
132
- padding: 12px;
133
- font-size: 16px;
134
- font-family: 'Roboto', sans-serif;
135
- border: 1px solid rgba(0, 0, 0, 0.2);
136
- border-radius: 2px;
137
- outline: none;
138
- box-sizing: border-box;
139
- background: transparent;
140
- transition: border-color 0.2s;
141
- }
142
-
143
- .input-container input:focus {
144
- border-color: var(--primary-color);
145
- border-width: 2px;
146
- padding: 11px;
147
- }
148
-
149
- .input-container label {
150
- position: absolute;
151
- left: 12px;
152
- top: 14px;
153
- color: var(--text-secondary);
154
- font-size: 16px;
155
- pointer-events: none;
156
- transition: 0.2s ease all;
157
- background: white;
158
- padding: 0 4px;
159
- }
160
-
161
- .input-container input:focus ~ label,
162
- .input-container input:not(:placeholder-shown) ~ label {
163
- top: -10px;
164
- font-size: 12px;
165
- color: var(--primary-color);
166
- font-weight: 500;
167
- }
168
-
169
- /* Error Message */
170
- #error-msg {
171
- color: var(--error-color);
172
- font-size: 13px;
173
- margin-bottom: 16px;
174
- display: none;
175
- font-weight: 500;
176
- }
177
-
178
- /* Material Raised Button */
179
- .btn-raised {
180
- background-color: var(--primary-color);
181
- color: white;
182
- border: none;
183
- font-family: 'Roboto', sans-serif;
184
- font-weight: 500;
185
- font-size: 14px;
186
- text-transform: uppercase;
187
- padding: 12px 24px;
188
- border-radius: 2px;
189
- cursor: pointer;
190
- width: 100%;
191
- box-shadow: var(--shadow-elevation-2);
192
- transition: background-color 0.2s, box-shadow 0.2s;
193
- letter-spacing: 0.5px;
194
- }
195
-
196
- .btn-raised:hover {
197
- background-color: var(--primary-dark);
198
- box-shadow: var(--shadow-elevation-4);
199
- }
200
-
201
- /* Portal Dashboard Grid */
202
- .portal-container {
203
- max-width: 900px;
204
- width: 100%;
205
- margin: 40px auto;
206
- padding: 0 16px;
207
- box-sizing: border-box;
208
- display: grid;
209
- grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
210
- gap: 24px;
211
- flex: 1;
212
- }
213
-
214
- .portal-card {
215
- background-color: var(--card-background);
216
- border-radius: 2px;
217
- box-shadow: var(--shadow-elevation-2);
218
- transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
219
- display: flex;
220
- flex-direction: column;
221
- overflow: hidden;
222
- }
223
-
224
- .portal-card:hover {
225
- box-shadow: var(--shadow-elevation-4);
226
- }
227
-
228
- .card-media {
229
- height: 140px;
230
- display: flex;
231
- align-items: center;
232
- justify-content: center;
233
- color: white;
234
- }
235
-
236
- .card-content {
237
- padding: 24px;
238
- flex: 1;
239
- }
240
-
241
- .portal-card-title {
242
- font-size: 20px;
243
- font-weight: 500;
244
- margin: 0 0 8px 0;
245
- color: var(--text-primary);
246
- }
247
-
248
- .card-text {
249
- font-size: 14px;
250
- color: var(--text-secondary);
251
- line-height: 1.4;
252
- margin: 0;
253
- }
254
-
255
- .card-actions {
256
- padding: 8px 16px 16px 16px;
257
- display: flex;
258
- justify-content: flex-end;
259
- }
260
-
261
- .btn-flat {
262
- background: transparent;
263
- border: none;
264
- color: var(--primary-color);
265
- font-family: 'Roboto', sans-serif;
266
- font-weight: 500;
267
- font-size: 14px;
268
- text-transform: uppercase;
269
- padding: 8px 12px;
270
- border-radius: 2px;
271
- cursor: pointer;
272
- text-decoration: none;
273
- transition: background-color 0.2s;
274
- letter-spacing: 0.5px;
275
- }
276
-
277
- .btn-flat:hover {
278
- background-color: rgba(63, 81, 181, 0.08);
279
- }
280
-
281
- /* Modal / Window Overlay */
282
- #game-modal {
283
- display: none;
284
- position: fixed;
285
- top: 0;
286
- left: 0;
287
- width: 100vw;
288
- height: 100vh;
289
- background-color: rgba(0, 0, 0, 0.7);
290
- z-index: 1000;
291
- flex-direction: column;
292
- align-items: center;
293
- justify-content: center;
294
- }
295
-
296
- .modal-content {
297
- width: 90vw;
298
- height: 85vh;
299
- background-color: #000;
300
- border-radius: 4px;
301
- box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
302
- display: flex;
303
- flex-direction: column;
304
- overflow: hidden;
305
- }
306
-
307
- .modal-header {
308
- background-color: #212121;
309
- color: white;
310
- padding: 12px 20px;
311
- display: flex;
312
- align-items: center;
313
- justify-content: space-between;
314
- font-family: 'Roboto', sans-serif;
315
- }
316
-
317
- .modal-title {
318
- margin: 0;
319
- font-size: 16px;
320
- font-weight: 500;
321
- }
322
-
323
- .modal-header-actions {
324
- display: flex;
325
- align-items: center;
326
- gap: 8px;
327
- }
328
-
329
- .modal-btn {
330
- background: transparent;
331
- border: none;
332
- color: white;
333
- cursor: pointer;
334
- font-size: 20px;
335
- display: flex;
336
- align-items: center;
337
- justify-content: center;
338
- padding: 6px;
339
- border-radius: 50%;
340
- transition: background-color 0.2s;
341
- }
342
-
343
- .modal-btn:hover {
344
- background-color: rgba(255, 255, 255, 0.1);
345
- }
346
-
347
- .modal-body {
348
- flex: 1;
349
- width: 100%;
350
- background: #fff;
351
- }
352
-
353
- .modal-body iframe {
354
- width: 100%;
355
- height: 100%;
356
- border: none;
357
- }
358
-
359
- /* Footer */
360
- footer {
361
- text-align: center;
362
- padding: 16px;
363
- color: var(--text-secondary);
364
- font-size: 12px;
365
- }
366
- </style>
367
- </head>
368
- <body>
369
-
370
- <!-- Material Design Toolbar -->
371
- <header>
372
- <h1>StudyUp</h1>
373
- <i class="material-icons">school</i>
374
- </header>
375
-
376
- <!-- VIEW 1: Login Screen -->
377
- <div id="login-view" class="view active">
378
- <main class="container">
379
- <div class="card">
380
- <div class="card-icon-wrapper">
381
- <i class="material-icons">lock_outline</i>
382
- </div>
383
- <h2 class="card-title">Welcome Student</h2>
384
- <p class="card-subtitle">Please enter your class code to access your study materials and interactive guides.</p>
385
-
386
- <div class="input-container">
387
- <input type="text" id="class-code-input" placeholder="e.g. 123456" maxlength="6" autofocus>
388
- <label for="class-code-input">Class Code</label>
389
- </div>
390
-
391
- <div id="error-msg">Invalid class code. Please check with your teacher or instructor.</div>
392
-
393
- <button class="btn-raised" onclick="verifyCode()">Enter Code</button>
394
- </div>
395
- </main>
396
- </div>
397
-
398
- <!-- VIEW 2: Portal Dashboard -->
399
- <div id="portal-view" class="view">
400
- <div class="portal-container">
401
- <!-- Card 1: Sandboxels -->
402
- <div class="portal-card">
403
- <div class="card-media" style="background-color: #00897b;">
404
- <i class="material-icons">science</i>
405
- </div>
406
- <div class="card-content">
407
- <h2 class="portal-card-title">Sandboxels</h2>
408
- <p class="card-text">A falling sand and physics sandbox game where you can mix elements, trigger chemical reactions, and experiment on a grid. Great for life science.</p>
409
- </div>
410
- <div class="card-actions">
411
- <button class="btn-flat" onclick="openGame('Sandboxels', 'sandboxels-package/sandboxels-1.13.2/index.html')">Launch App</button>
412
- </div>
413
- </div>
414
-
415
- <!-- Card 2: HexGL -->
416
- <div class="portal-card">
417
- <div class="card-media" style="background-color: #e53935;">
418
- <i class="material-icons">speed</i>
419
- </div>
420
- <div class="card-content">
421
- <h2 class="portal-card-title">HexGL</h2>
422
- <p class="card-text">A futuristic 3D WebGL racing game built with HTML5 where you pilot a high-speed craft through complex tracks. Great for testing your shape naming skills.</p>
423
- </div>
424
- <div class="card-actions">
425
- <button class="btn-flat" onclick="openGame('HexGL', 'hexgl-package/HexGL-master/index.html')">Launch App</button>
426
- </div>
427
- </div>
428
- </div>
429
- </div>
430
-
431
- <!-- Game Modal Window Overlay -->
432
- <div id="game-modal">
433
- <div class="modal-content" id="modal-box">
434
- <div class="modal-header">
435
- <h3 id="modal-game-title" class="modal-title">Game Window</h3>
436
- <div class="modal-header-actions">
437
- <button class="modal-btn" onclick="toggleFullScreen()" title="Toggle Fullscreen">
438
- <i class="material-icons" id="fullscreen-icon">fullscreen</i>
439
- </button>
440
- <button class="modal-btn" onclick="closeGame()" title="Close Window">
441
- <i class="material-icons">close</i>
442
- </button>
443
- </div>
444
- </div>
445
- <div class="modal-body">
446
- <iframe id="game-iframe" src="" title="Game View"></iframe>
447
- </div>
448
- </div>
449
- </div>
450
-
451
- <footer>
452
- StudyUp &bull; Student Resource Portal &bull; 2026
453
- </footer>
454
-
455
- <script>
456
- // Allow pressing 'Enter' to submit code
457
- document.getElementById('class-code-input').addEventListener('keypress', function (e) {
458
- if (e.key === 'Enter') {
459
- verifyCode();
460
- }
461
- });
462
-
463
- function verifyCode() {
464
- const inputVal = document.getElementById('class-code-input').value.trim();
465
- const errorMsg = document.getElementById('error-msg');
466
-
467
- if (inputVal === '814212') {
468
- errorMsg.style.display = 'none';
469
- document.getElementById('login-view').classList.remove('active');
470
- document.getElementById('portal-view').classList.add('active');
471
- } else {
472
- errorMsg.style.display = 'block';
473
- document.getElementById('class-code-input').focus();
474
- }
475
- }
476
-
477
- function openGame(gameName, gameUrl) {
478
- document.getElementById('modal-game-title').innerText = gameName;
479
- document.getElementById('game-iframe').src = gameUrl;
480
- document.getElementById('game-modal').style.display = 'flex';
481
- }
482
-
483
- function closeGame() {
484
- // Exit fullscreen if active when closing
485
- if (document.fullscreenElement) {
486
- document.exitFullscreen();
487
- }
488
- document.getElementById('game-modal').style.display = 'none';
489
- document.getElementById('game-iframe').src = '';
490
- }
491
-
492
- function toggleFullScreen() {
493
- const modalBox = document.getElementById('modal-box');
494
- const icon = document.getElementById('fullscreen-icon');
495
-
496
- if (!document.fullscreenElement) {
497
- modalBox.requestFullscreen().catch(err => {
498
- alert(`Error attempting to enable fullscreen: ${err.message}`);
499
- });
500
- icon.innerText = 'fullscreen_exit';
501
- } else {
502
- document.exitFullscreen();
503
- icon.innerText = 'fullscreen';
504
- }
505
- }
506
-
507
- // Keep fullscreen icon updated if user exits via keyboard (Esc)
508
- document.addEventListener('fullscreenchange', () => {
509
- const icon = document.getElementById('fullscreen-icon');
510
- if (!document.fullscreenElement) {
511
- icon.innerText = 'fullscreen';
512
- } else {
513
- icon.innerText = 'fullscreen_exit';
514
- }
515
- });
516
- </script>
517
- </body>
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>StudyUp - Student Portal</title>
7
+ <!-- Material Design 1 / Roboto Typography & Icons -->
8
+ <link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700&display=swap" rel="stylesheet">
9
+ <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
10
+
11
+ <style>
12
+ /* Material Design 1 Core Variables & Palette */
13
+ :root {
14
+ --primary-color: #3f51b5; /* Indigo */
15
+ --primary-dark: #303f9f;
16
+ --accent-color: #ff4081;
17
+ --background-color: #eeeeee;
18
+ --card-background: #ffffff;
19
+ --text-primary: rgba(0, 0, 0, 0.87);
20
+ --text-secondary: rgba(0, 0, 0, 0.54);
21
+ --error-color: #d32f2f;
22
+ --shadow-elevation-2: 0 3px 1px -2px rgba(0,0,0,0.2), 0 2px 2px 0 rgba(0,0,0,0.14), 0 1px 5px 0 rgba(0,0,0,0.12);
23
+ --shadow-elevation-4: 0 4px 5px -2px rgba(0,0,0,0.2), 0 7px 10px 1px rgba(0,0,0,0.14), 0 2px 16px 1px rgba(0,0,0,0.12);
24
+ }
25
+
26
+ body {
27
+ font-family: 'Roboto', sans-serif;
28
+ background-color: var(--background-color);
29
+ color: var(--text-primary);
30
+ margin: 0;
31
+ padding: 0;
32
+ display: flex;
33
+ flex-direction: column;
34
+ min-height: 100vh;
35
+ justify-content: space-between;
36
+ }
37
+
38
+ /* Material Toolbar (Header) */
39
+ header {
40
+ background-color: var(--primary-color);
41
+ color: white;
42
+ padding: 16px 24px;
43
+ box-shadow: var(--shadow-elevation-2);
44
+ display: flex;
45
+ align-items: center;
46
+ justify-content: space-between;
47
+ }
48
+
49
+ header h1 {
50
+ margin: 0;
51
+ font-size: 20px;
52
+ font-weight: 500;
53
+ letter-spacing: 0.5px;
54
+ }
55
+
56
+ /* View Switching (Login vs Portal Dashboard) */
57
+ .view {
58
+ display: none;
59
+ flex: 1;
60
+ flex-direction: column;
61
+ }
62
+ .view.active {
63
+ display: flex;
64
+ }
65
+
66
+ /* Main Container & Login Card */
67
+ .container {
68
+ max-width: 420px;
69
+ width: 100%;
70
+ margin: auto;
71
+ padding: 16px;
72
+ box-sizing: border-box;
73
+ flex: 1;
74
+ display: flex;
75
+ align-items: center;
76
+ justify-content: center;
77
+ }
78
+
79
+ .card {
80
+ background-color: var(--card-background);
81
+ border-radius: 2px;
82
+ box-shadow: var(--shadow-elevation-4);
83
+ padding: 32px 24px;
84
+ display: flex;
85
+ flex-direction: column;
86
+ align-items: center;
87
+ text-align: center;
88
+ width: 100%;
89
+ box-sizing: border-box;
90
+ }
91
+
92
+ .card-icon-wrapper {
93
+ background-color: rgba(63, 81, 181, 0.1);
94
+ color: var(--primary-color);
95
+ width: 64px;
96
+ height: 64px;
97
+ border-radius: 50%;
98
+ display: flex;
99
+ align-items: center;
100
+ justify-content: center;
101
+ margin-bottom: 20px;
102
+ }
103
+
104
+ .card-icon-wrapper i {
105
+ font-size: 32px;
106
+ }
107
+
108
+ .card-title {
109
+ font-size: 24px;
110
+ font-weight: 500;
111
+ margin: 0 0 8px 0;
112
+ color: var(--text-primary);
113
+ }
114
+
115
+ .card-subtitle {
116
+ font-size: 14px;
117
+ color: var(--text-secondary);
118
+ margin: 0 0 24px 0;
119
+ line-height: 1.4;
120
+ }
121
+
122
+ /* Material Floating Label Input Field */
123
+ .input-container {
124
+ position: relative;
125
+ margin-bottom: 24px;
126
+ width: 100%;
127
+ text-align: left;
128
+ }
129
+
130
+ .input-container input {
131
+ width: 100%;
132
+ padding: 12px;
133
+ font-size: 16px;
134
+ font-family: 'Roboto', sans-serif;
135
+ border: 1px solid rgba(0, 0, 0, 0.2);
136
+ border-radius: 2px;
137
+ outline: none;
138
+ box-sizing: border-box;
139
+ background: transparent;
140
+ transition: border-color 0.2s;
141
+ }
142
+
143
+ .input-container input:focus {
144
+ border-color: var(--primary-color);
145
+ border-width: 2px;
146
+ padding: 11px;
147
+ }
148
+
149
+ .input-container label {
150
+ position: absolute;
151
+ left: 12px;
152
+ top: 14px;
153
+ color: var(--text-secondary);
154
+ font-size: 16px;
155
+ pointer-events: none;
156
+ transition: 0.2s ease all;
157
+ background: white;
158
+ padding: 0 4px;
159
+ }
160
+
161
+ .input-container input:focus ~ label,
162
+ .input-container input:not(:placeholder-shown) ~ label {
163
+ top: -10px;
164
+ font-size: 12px;
165
+ color: var(--primary-color);
166
+ font-weight: 500;
167
+ }
168
+
169
+ /* Error Message */
170
+ #error-msg {
171
+ color: var(--error-color);
172
+ font-size: 13px;
173
+ margin-bottom: 16px;
174
+ display: none;
175
+ font-weight: 500;
176
+ }
177
+
178
+ /* Material Raised Button */
179
+ .btn-raised {
180
+ background-color: var(--primary-color);
181
+ color: white;
182
+ border: none;
183
+ font-family: 'Roboto', sans-serif;
184
+ font-weight: 500;
185
+ font-size: 14px;
186
+ text-transform: uppercase;
187
+ padding: 12px 24px;
188
+ border-radius: 2px;
189
+ cursor: pointer;
190
+ width: 100%;
191
+ box-shadow: var(--shadow-elevation-2);
192
+ transition: background-color 0.2s, box-shadow 0.2s;
193
+ letter-spacing: 0.5px;
194
+ }
195
+
196
+ .btn-raised:hover {
197
+ background-color: var(--primary-dark);
198
+ box-shadow: var(--shadow-elevation-4);
199
+ }
200
+
201
+ /* Portal Dashboard Grid */
202
+ .portal-container {
203
+ max-width: 900px;
204
+ width: 100%;
205
+ margin: 40px auto;
206
+ padding: 0 16px;
207
+ box-sizing: border-box;
208
+ display: grid;
209
+ grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
210
+ gap: 24px;
211
+ flex: 1;
212
+ }
213
+
214
+ .portal-card {
215
+ background-color: var(--card-background);
216
+ border-radius: 2px;
217
+ box-shadow: var(--shadow-elevation-2);
218
+ transition: box-shadow 0.3s cubic-bezier(0.4, 0, 0.2, 1);
219
+ display: flex;
220
+ flex-direction: column;
221
+ overflow: hidden;
222
+ }
223
+
224
+ .portal-card:hover {
225
+ box-shadow: var(--shadow-elevation-4);
226
+ }
227
+
228
+ .card-media {
229
+ height: 140px;
230
+ display: flex;
231
+ align-items: center;
232
+ justify-content: center;
233
+ color: white;
234
+ }
235
+
236
+ .card-content {
237
+ padding: 24px;
238
+ flex: 1;
239
+ }
240
+
241
+ .portal-card-title {
242
+ font-size: 20px;
243
+ font-weight: 500;
244
+ margin: 0 0 8px 0;
245
+ color: var(--text-primary);
246
+ }
247
+
248
+ .card-text {
249
+ font-size: 14px;
250
+ color: var(--text-secondary);
251
+ line-height: 1.4;
252
+ margin: 0;
253
+ }
254
+
255
+ .card-actions {
256
+ padding: 8px 16px 16px 16px;
257
+ display: flex;
258
+ justify-content: flex-end;
259
+ }
260
+
261
+ .btn-flat {
262
+ background: transparent;
263
+ border: none;
264
+ color: var(--primary-color);
265
+ font-family: 'Roboto', sans-serif;
266
+ font-weight: 500;
267
+ font-size: 14px;
268
+ text-transform: uppercase;
269
+ padding: 8px 12px;
270
+ border-radius: 2px;
271
+ cursor: pointer;
272
+ text-decoration: none;
273
+ transition: background-color 0.2s;
274
+ letter-spacing: 0.5px;
275
+ }
276
+
277
+ .btn-flat:hover {
278
+ background-color: rgba(63, 81, 181, 0.08);
279
+ }
280
+
281
+ /* Modal / Window Overlay */
282
+ #game-modal {
283
+ display: none;
284
+ position: fixed;
285
+ top: 0;
286
+ left: 0;
287
+ width: 100vw;
288
+ height: 100vh;
289
+ background-color: rgba(0, 0, 0, 0.7);
290
+ z-index: 1000;
291
+ flex-direction: column;
292
+ align-items: center;
293
+ justify-content: center;
294
+ }
295
+
296
+ .modal-content {
297
+ width: 90vw;
298
+ height: 85vh;
299
+ background-color: #000;
300
+ border-radius: 4px;
301
+ box-shadow: 0 8px 10px 1px rgba(0,0,0,0.14), 0 3px 14px 2px rgba(0,0,0,0.12), 0 5px 5px -3px rgba(0,0,0,0.2);
302
+ display: flex;
303
+ flex-direction: column;
304
+ overflow: hidden;
305
+ }
306
+
307
+ .modal-header {
308
+ background-color: #212121;
309
+ color: white;
310
+ padding: 12px 20px;
311
+ display: flex;
312
+ align-items: center;
313
+ justify-content: space-between;
314
+ font-family: 'Roboto', sans-serif;
315
+ }
316
+
317
+ .modal-title {
318
+ margin: 0;
319
+ font-size: 16px;
320
+ font-weight: 500;
321
+ }
322
+
323
+ .modal-header-actions {
324
+ display: flex;
325
+ align-items: center;
326
+ gap: 8px;
327
+ }
328
+
329
+ .modal-btn {
330
+ background: transparent;
331
+ border: none;
332
+ color: white;
333
+ cursor: pointer;
334
+ font-size: 20px;
335
+ display: flex;
336
+ align-items: center;
337
+ justify-content: center;
338
+ padding: 6px;
339
+ border-radius: 50%;
340
+ transition: background-color 0.2s;
341
+ }
342
+
343
+ .modal-btn:hover {
344
+ background-color: rgba(255, 255, 255, 0.1);
345
+ }
346
+
347
+ .modal-body {
348
+ flex: 1;
349
+ width: 100%;
350
+ background: #fff;
351
+ }
352
+
353
+ .modal-body iframe {
354
+ width: 100%;
355
+ height: 100%;
356
+ border: none;
357
+ }
358
+
359
+ /* Footer */
360
+ footer {
361
+ text-align: center;
362
+ padding: 16px;
363
+ color: var(--text-secondary);
364
+ font-size: 12px;
365
+ }
366
+ </style>
367
+ </head>
368
+ <body>
369
+
370
+ <!-- Material Design Toolbar -->
371
+ <header>
372
+ <h1>StudyUp</h1>
373
+ <i class="material-icons">school</i>
374
+ </header>
375
+
376
+ <!-- VIEW 1: Login Screen -->
377
+ <div id="login-view" class="view active">
378
+ <main class="container">
379
+ <div class="card">
380
+ <div class="card-icon-wrapper">
381
+ <i class="material-icons">lock_outline</i>
382
+ </div>
383
+ <h2 class="card-title">Welcome Student</h2>
384
+ <p class="card-subtitle">Please enter your class code to access your study materials and interactive guides.</p>
385
+
386
+ <div class="input-container">
387
+ <input type="text" id="class-code-input" placeholder="e.g. 123456" maxlength="6" autofocus>
388
+ <label for="class-code-input">Class Code</label>
389
+ </div>
390
+
391
+ <div id="error-msg">Invalid class code. Please check with your teacher or instructor.</div>
392
+
393
+ <button class="btn-raised" onclick="verifyCode()">Enter Code</button>
394
+ </div>
395
+ </main>
396
+ </div>
397
+
398
+ <!-- VIEW 2: Portal Dashboard -->
399
+ <div id="portal-view" class="view">
400
+ <div class="portal-container">
401
+ <!-- Card 1: Sandboxels -->
402
+ <div class="portal-card">
403
+ <div class="card-media" style="background-color: #00897b;">
404
+ <i class="material-icons">science</i>
405
+ </div>
406
+ <div class="card-content">
407
+ <h2 class="portal-card-title">Sandboxels</h2>
408
+ <p class="card-text">A falling sand and physics sandbox game where you can mix elements, trigger chemical reactions, and experiment on a grid. Great for life science.</p>
409
+ </div>
410
+ <div class="card-actions">
411
+ <button class="btn-flat" onclick="openGame('Sandboxels', 'sandboxels-package/sandboxels-1.13.2/index.html')">Launch App</button>
412
+ </div>
413
+ </div>
414
+
415
+ <!-- Card 2: HexGL -->
416
+ <div class="portal-card">
417
+ <div class="card-media" style="background-color: #e53935;">
418
+ <i class="material-icons">speed</i>
419
+ </div>
420
+ <div class="card-content">
421
+ <h2 class="portal-card-title">HexGL</h2>
422
+ <p class="card-text">A futuristic 3D WebGL racing game built with HTML5 where you pilot a high-speed craft through complex tracks. Great for testing your shape naming skills.</p>
423
+ </div>
424
+ <div class="card-actions">
425
+ <button class="btn-flat" onclick="openGame('HexGL', 'hexgl-package/HexGL-master/index.html')">Launch App</button>
426
+ </div>
427
+ </div>
428
+
429
+ <!-- Card 3: ClassiCube -->
430
+ <div class="portal-card">
431
+ <div class="card-media" style="background-color: #00acc1;">
432
+ <i class="material-icons">terrain</i>
433
+ </div>
434
+ <div class="card-content">
435
+ <h2 class="portal-card-title">ClassiCube</h2>
436
+ <p class="card-text">A lightweight multiplayer-compatible classic block building sandbox game. Great for architecture and spatial visualization exercises.</p>
437
+ </div>
438
+ <div class="card-actions">
439
+ <button class="btn-flat" onclick="openGame('ClassiCube', 'classicube-package/index.html')">Launch App</button>
440
+ </div>
441
+ </div>
442
+
443
+ <!-- Card 4: 2048 -->
444
+ <div class="portal-card">
445
+ <div class="card-media" style="background-color: #ffb300;">
446
+ <i class="material-icons">grid_on</i>
447
+ </div>
448
+ <div class="card-content">
449
+ <h2 class="portal-card-title">2048</h2>
450
+ <p class="card-text">A classic sliding tile puzzle game where you combine matching numbers to reach the 2048 tile. Great for math logic and strategic thinking.</p>
451
+ </div>
452
+ <div class="card-actions">
453
+ <button class="btn-flat" onclick="openGame('2048', '2048-package/index.html')">Launch App</button>
454
+ </div>
455
+ </div>
456
+ </div>
457
+ </div>
458
+
459
+ <!-- Game Modal Window Overlay -->
460
+ <div id="game-modal">
461
+ <div class="modal-content" id="modal-box">
462
+ <div class="modal-header">
463
+ <h3 id="modal-game-title" class="modal-title">Game Window</h3>
464
+ <div class="modal-header-actions">
465
+ <button class="modal-btn" onclick="toggleFullScreen()" title="Toggle Fullscreen">
466
+ <i class="material-icons" id="fullscreen-icon">fullscreen</i>
467
+ </button>
468
+ <button class="modal-btn" onclick="closeGame()" title="Close Window">
469
+ <i class="material-icons">close</i>
470
+ </button>
471
+ </div>
472
+ </div>
473
+ <div class="modal-body">
474
+ <iframe id="game-iframe" src="" title="Game View" sandbox="allow-scripts allow-same-origin allow-modals allow-pointer-lock allow-downloads"></iframe>
475
+ </div>
476
+ </div>
477
+ </div>
478
+
479
+ <footer>
480
+ StudyUp &bull; Student Resource Portal &bull; 2026
481
+ </footer>
482
+
483
+ <script>
484
+ // Allow pressing 'Enter' to submit code
485
+ document.getElementById('class-code-input').addEventListener('keypress', function (e) {
486
+ if (e.key === 'Enter') {
487
+ verifyCode();
488
+ }
489
+ });
490
+
491
+ function verifyCode() {
492
+ const inputVal = document.getElementById('class-code-input').value.trim();
493
+ const errorMsg = document.getElementById('error-msg');
494
+
495
+ if (inputVal === '814212') {
496
+ errorMsg.style.display = 'none';
497
+ document.getElementById('login-view').classList.remove('active');
498
+ document.getElementById('portal-view').classList.add('active');
499
+ } else {
500
+ errorMsg.style.display = 'block';
501
+ document.getElementById('class-code-input').focus();
502
+ }
503
+ }
504
+
505
+ function openGame(gameName, gameUrl) {
506
+ document.getElementById('modal-game-title').innerText = gameName;
507
+ document.getElementById('game-iframe').src = gameUrl;
508
+ document.getElementById('game-modal').style.display = 'flex';
509
+ }
510
+
511
+ function closeGame() {
512
+ // Exit fullscreen if active when closing
513
+ if (document.fullscreenElement) {
514
+ document.exitFullscreen();
515
+ }
516
+ document.getElementById('game-modal').style.display = 'none';
517
+ document.getElementById('game-iframe').src = '';
518
+ }
519
+
520
+ function toggleFullScreen() {
521
+ const modalBox = document.getElementById('modal-box');
522
+ const icon = document.getElementById('fullscreen-icon');
523
+
524
+ if (!document.fullscreenElement) {
525
+ modalBox.requestFullscreen().catch(err => {
526
+ alert(`Error attempting to enable fullscreen: ${err.message}`);
527
+ });
528
+ icon.innerText = 'fullscreen_exit';
529
+ } else {
530
+ document.exitFullscreen();
531
+ icon.innerText = 'fullscreen';
532
+ }
533
+ }
534
+
535
+ // Keep fullscreen icon updated if user exits via keyboard (Esc)
536
+ document.addEventListener('fullscreenchange', () => {
537
+ const icon = document.getElementById('fullscreen-icon');
538
+ if (!document.fullscreenElement) {
539
+ icon.innerText = 'fullscreen';
540
+ } else {
541
+ icon.innerText = 'fullscreen_exit';
542
+ }
543
+ });
544
+ </script>
545
+ </body>
518
546
  </html>