dxlorhuggingface commited on
Commit
785383f
·
verified ·
1 Parent(s): 52437f4

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +1466 -19
index.html CHANGED
@@ -1,19 +1,1466 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </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
+ <title>Nuclear Reactor Simulator</title>
7
+ <style>
8
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&family=Roboto+Mono:wght@400;500;700&display=swap');
9
+
10
+ :root {
11
+ --bg: #0a0a0f;
12
+ --primary: #00b4ff;
13
+ --caution: #ffab00;
14
+ --alarm: #ff3b30;
15
+ --coolant: #4df0ff;
16
+ --graphite: #444;
17
+ --glow: #00d1ff33;
18
+ --success: #32d74b;
19
+ }
20
+
21
+ * {
22
+ margin: 0;
23
+ padding: 0;
24
+ box-sizing: border-box;
25
+ }
26
+
27
+ body {
28
+ background: var(--bg);
29
+ color: var(--primary);
30
+ font-family: 'Roboto Mono', monospace;
31
+ overflow: hidden;
32
+ height: 100vh;
33
+ position: relative;
34
+ }
35
+
36
+ .container {
37
+ display: flex;
38
+ height: 100vh;
39
+ }
40
+
41
+ .control-panel {
42
+ width: 35%;
43
+ background: linear-gradient(135deg, #0d0d1a, #1a1a2e);
44
+ border-right: 2px solid var(--primary);
45
+ padding: 20px;
46
+ overflow-y: auto;
47
+ position: relative;
48
+ }
49
+
50
+ .reactor-cavity {
51
+ width: 65%;
52
+ background: radial-gradient(ellipse at center, #0f0f1f, var(--bg));
53
+ position: relative;
54
+ overflow: hidden;
55
+ }
56
+
57
+ .panel-header {
58
+ font-family: 'Orbitron', sans-serif;
59
+ font-weight: 700;
60
+ font-size: 1.2em;
61
+ color: var(--primary);
62
+ margin-bottom: 20px;
63
+ text-align: center;
64
+ text-shadow: 0 0 10px var(--glow);
65
+ }
66
+
67
+ .control-group {
68
+ margin-bottom: 20px;
69
+ background: rgba(0, 180, 255, 0.05);
70
+ border: 1px solid rgba(0, 180, 255, 0.3);
71
+ border-radius: 8px;
72
+ padding: 15px;
73
+ }
74
+
75
+ .control-label {
76
+ font-size: 0.9em;
77
+ margin-bottom: 8px;
78
+ color: var(--coolant);
79
+ text-transform: uppercase;
80
+ letter-spacing: 1px;
81
+ }
82
+
83
+ .slider-container {
84
+ position: relative;
85
+ height: 30px;
86
+ background: var(--graphite);
87
+ border-radius: 15px;
88
+ margin: 10px 0;
89
+ }
90
+
91
+ .slider {
92
+ width: 100%;
93
+ height: 100%;
94
+ background: transparent;
95
+ outline: none;
96
+ border: none;
97
+ border-radius: 15px;
98
+ -webkit-appearance: none;
99
+ appearance: none;
100
+ cursor: pointer;
101
+ }
102
+
103
+ .slider::-webkit-slider-thumb {
104
+ -webkit-appearance: none;
105
+ width: 25px;
106
+ height: 25px;
107
+ background: var(--primary);
108
+ border-radius: 50%;
109
+ cursor: pointer;
110
+ box-shadow: 0 0 15px var(--glow);
111
+ border: 2px solid var(--coolant);
112
+ }
113
+
114
+ .slider::-moz-range-thumb {
115
+ width: 25px;
116
+ height: 25px;
117
+ background: var(--primary);
118
+ border-radius: 50%;
119
+ cursor: pointer;
120
+ box-shadow: 0 0 15px var(--glow);
121
+ border: 2px solid var(--coolant);
122
+ }
123
+
124
+ .power-bar {
125
+ height: 200px;
126
+ width: 40px;
127
+ background: var(--graphite);
128
+ border-radius: 20px;
129
+ position: relative;
130
+ margin: 0 auto;
131
+ border: 2px solid var(--primary);
132
+ }
133
+
134
+ .power-fill {
135
+ position: absolute;
136
+ bottom: 2px;
137
+ left: 2px;
138
+ right: 2px;
139
+ background: linear-gradient(0deg, var(--primary), var(--coolant));
140
+ border-radius: 18px;
141
+ transition: height 0.3s ease;
142
+ box-shadow: 0 0 20px var(--glow);
143
+ height: 0%;
144
+ }
145
+
146
+ .gauge {
147
+ width: 100px;
148
+ height: 100px;
149
+ margin: 10px auto;
150
+ position: relative;
151
+ background: radial-gradient(circle, var(--graphite), #222);
152
+ border-radius: 50%;
153
+ border: 3px solid var(--primary);
154
+ }
155
+
156
+ .gauge-needle {
157
+ position: absolute;
158
+ top: 50%;
159
+ left: 50%;
160
+ width: 2px;
161
+ height: 40px;
162
+ background: var(--alarm);
163
+ transform-origin: bottom center;
164
+ transform: translate(-50%, -100%) rotate(-90deg);
165
+ transition: transform 0.3s ease;
166
+ box-shadow: 0 0 10px var(--alarm);
167
+ }
168
+
169
+ .action-button {
170
+ background: var(--primary);
171
+ color: var(--bg);
172
+ border: none;
173
+ padding: 12px 20px;
174
+ border-radius: 8px;
175
+ font-family: 'Orbitron', sans-serif;
176
+ font-weight: 700;
177
+ cursor: pointer;
178
+ transition: all 0.3s ease;
179
+ text-transform: uppercase;
180
+ letter-spacing: 1px;
181
+ margin: 5px;
182
+ font-size: 12px;
183
+ display: inline-block;
184
+ min-width: 120px;
185
+ }
186
+
187
+ .action-button:hover {
188
+ background: var(--coolant);
189
+ box-shadow: 0 0 20px var(--glow);
190
+ transform: scale(1.02);
191
+ }
192
+
193
+ .action-button.caution {
194
+ background: var(--caution);
195
+ }
196
+
197
+ .action-button.caution:hover {
198
+ background: #ff8f00;
199
+ }
200
+
201
+ .action-button.success {
202
+ background: var(--success);
203
+ }
204
+
205
+ .action-button.success:hover {
206
+ background: #28a745;
207
+ }
208
+
209
+ .scram-button {
210
+ background: var(--alarm);
211
+ color: white;
212
+ border: none;
213
+ padding: 15px 25px;
214
+ border-radius: 10px;
215
+ font-family: 'Orbitron', sans-serif;
216
+ font-weight: 700;
217
+ cursor: pointer;
218
+ transition: all 0.3s ease;
219
+ text-transform: uppercase;
220
+ letter-spacing: 2px;
221
+ margin: 20px auto;
222
+ display: block;
223
+ width: 100%;
224
+ font-size: 16px;
225
+ }
226
+
227
+ .scram-button:hover {
228
+ background: #ff1a1a;
229
+ box-shadow: 0 0 30px var(--alarm);
230
+ transform: scale(1.05);
231
+ }
232
+
233
+ .emergency-controls {
234
+ display: grid;
235
+ grid-template-columns: 1fr 1fr;
236
+ gap: 10px;
237
+ margin-top: 15px;
238
+ }
239
+
240
+ .annunciator-grid {
241
+ display: grid;
242
+ grid-template-columns: repeat(4, 1fr);
243
+ gap: 5px;
244
+ margin: 15px 0;
245
+ }
246
+
247
+ .annunciator {
248
+ width: 25px;
249
+ height: 25px;
250
+ background: var(--graphite);
251
+ border-radius: 3px;
252
+ border: 1px solid var(--primary);
253
+ transition: all 0.3s ease;
254
+ position: relative;
255
+ }
256
+
257
+ .annunciator.active {
258
+ background: var(--alarm);
259
+ box-shadow: 0 0 10px var(--alarm);
260
+ animation: blink 1s infinite;
261
+ }
262
+
263
+ .annunciator.caution {
264
+ background: var(--caution);
265
+ box-shadow: 0 0 10px var(--caution);
266
+ }
267
+
268
+ @keyframes blink {
269
+ 0%, 50% { opacity: 1; }
270
+ 51%, 100% { opacity: 0.3; }
271
+ }
272
+
273
+ .reactor-3d {
274
+ width: 100%;
275
+ height: 100%;
276
+ position: relative;
277
+ display: flex;
278
+ justify-content: center;
279
+ align-items: center;
280
+ }
281
+
282
+ .reactor-core {
283
+ width: 300px;
284
+ height: 400px;
285
+ background: linear-gradient(45deg, var(--graphite), #666);
286
+ border-radius: 20px;
287
+ border: 2px solid var(--primary);
288
+ overflow: hidden;
289
+ box-shadow: 0 0 50px var(--glow);
290
+ position: relative;
291
+ }
292
+
293
+ .fuel-assembly-grid {
294
+ display: grid;
295
+ grid-template-columns: repeat(17, 1fr);
296
+ grid-template-rows: repeat(17, 1fr);
297
+ height: 100%;
298
+ padding: 10px;
299
+ gap: 1px;
300
+ }
301
+
302
+ .fuel-pin {
303
+ background: var(--primary);
304
+ border-radius: 1px;
305
+ transition: all 0.3s ease;
306
+ opacity: 0.3;
307
+ }
308
+
309
+ .fuel-pin.hot {
310
+ background: var(--coolant);
311
+ box-shadow: 0 0 3px var(--coolant);
312
+ opacity: 1;
313
+ }
314
+
315
+ .control-rod {
316
+ position: absolute;
317
+ width: 8px;
318
+ height: 300px;
319
+ background: linear-gradient(0deg, var(--graphite), #666);
320
+ border: 1px solid var(--primary);
321
+ left: 50%;
322
+ top: 10px;
323
+ transform: translateX(-50%);
324
+ transition: top 0.5s ease;
325
+ border-radius: 4px;
326
+ }
327
+
328
+ .coolant-loops {
329
+ position: absolute;
330
+ width: 100%;
331
+ height: 100%;
332
+ top: 0;
333
+ left: 0;
334
+ }
335
+
336
+ .coolant-loop {
337
+ position: absolute;
338
+ width: 60px;
339
+ height: 180px;
340
+ border: 4px solid var(--coolant);
341
+ border-radius: 30px;
342
+ background: transparent;
343
+ }
344
+
345
+ .loop-1 { top: 50px; left: -40px; }
346
+ .loop-2 { top: 50px; right: -40px; }
347
+ .loop-3 { bottom: 50px; left: -40px; }
348
+ .loop-4 { bottom: 50px; right: -40px; }
349
+
350
+ .coolant-particle {
351
+ position: absolute;
352
+ width: 4px;
353
+ height: 8px;
354
+ background: var(--coolant);
355
+ border-radius: 2px;
356
+ opacity: 0.8;
357
+ }
358
+
359
+ .readout {
360
+ font-family: 'Roboto Mono', monospace;
361
+ font-size: 0.9em;
362
+ color: var(--coolant);
363
+ text-align: center;
364
+ background: rgba(0, 0, 0, 0.8);
365
+ padding: 5px 8px;
366
+ border-radius: 5px;
367
+ border: 1px solid var(--primary);
368
+ margin-top: 10px;
369
+ }
370
+
371
+ .status-indicator {
372
+ display: inline-block;
373
+ width: 12px;
374
+ height: 12px;
375
+ border-radius: 50%;
376
+ margin-right: 8px;
377
+ animation: pulse 2s infinite;
378
+ }
379
+
380
+ .status-indicator.critical {
381
+ background: var(--alarm);
382
+ }
383
+
384
+ .status-indicator.subcritical {
385
+ background: var(--caution);
386
+ }
387
+
388
+ .status-indicator.shutdown {
389
+ background: var(--success);
390
+ }
391
+
392
+ @keyframes pulse {
393
+ 0%, 100% { opacity: 1; }
394
+ 50% { opacity: 0.5; }
395
+ }
396
+
397
+ .tooltip {
398
+ position: fixed;
399
+ top: 50%;
400
+ left: 50%;
401
+ transform: translate(-50%, -50%);
402
+ background: rgba(0, 0, 0, 0.95);
403
+ border: 2px solid var(--primary);
404
+ border-radius: 15px;
405
+ padding: 30px;
406
+ max-width: 500px;
407
+ text-align: center;
408
+ font-family: 'Orbitron', sans-serif;
409
+ z-index: 1000;
410
+ box-shadow: 0 0 30px var(--glow);
411
+ }
412
+
413
+ .close-tooltip {
414
+ margin-top: 20px;
415
+ padding: 10px 20px;
416
+ background: var(--primary);
417
+ color: var(--bg);
418
+ border: none;
419
+ border-radius: 5px;
420
+ cursor: pointer;
421
+ font-family: 'Orbitron', sans-serif;
422
+ font-weight: 700;
423
+ }
424
+
425
+ .particles {
426
+ position: absolute;
427
+ width: 100%;
428
+ height: 100%;
429
+ pointer-events: none;
430
+ overflow: hidden;
431
+ }
432
+
433
+ .neutron {
434
+ position: absolute;
435
+ width: 3px;
436
+ height: 3px;
437
+ background: #00ffff;
438
+ border-radius: 50%;
439
+ box-shadow: 0 0 6px #00ffff;
440
+ }
441
+
442
+ .audio-controls {
443
+ display: flex;
444
+ justify-content: space-between;
445
+ align-items: center;
446
+ margin: 10px 0;
447
+ }
448
+
449
+ .volume-control {
450
+ display: flex;
451
+ align-items: center;
452
+ gap: 10px;
453
+ }
454
+
455
+ .mute-button {
456
+ background: var(--graphite);
457
+ color: var(--primary);
458
+ border: 1px solid var(--primary);
459
+ padding: 8px;
460
+ border-radius: 5px;
461
+ cursor: pointer;
462
+ font-family: 'Roboto Mono', monospace;
463
+ font-size: 12px;
464
+ }
465
+
466
+ .mute-button.muted {
467
+ background: var(--alarm);
468
+ color: white;
469
+ }
470
+
471
+ .scanlines {
472
+ position: absolute;
473
+ top: 0;
474
+ left: 0;
475
+ right: 0;
476
+ bottom: 0;
477
+ background: repeating-linear-gradient(
478
+ 0deg,
479
+ transparent,
480
+ transparent 2px,
481
+ rgba(0, 180, 255, 0.02) 2px,
482
+ rgba(0, 180, 255, 0.02) 4px
483
+ );
484
+ pointer-events: none;
485
+ z-index: 10;
486
+ }
487
+
488
+ @media (max-width: 900px) {
489
+ .container {
490
+ flex-direction: column;
491
+ }
492
+ .control-panel {
493
+ width: 100%;
494
+ height: 40%;
495
+ }
496
+ .reactor-cavity {
497
+ width: 100%;
498
+ height: 60%;
499
+ }
500
+ }
501
+ </style>
502
+ </head>
503
+ <body>
504
+ <div class="scanlines"></div>
505
+
506
+ <div class="tooltip" id="welcomeTooltip">
507
+ <h2>Welcome, Dr. Voss</h2>
508
+ <p>Reactor is cold and shutdown. Bring power to 100% without exceeding DNBR < 1.3.</p>
509
+ <p style="color: var(--caution); margin-top: 15px;">Good hunting.</p>
510
+ <button class="close-tooltip" onclick="closeTooltip()">Initialize Reactor</button>
511
+ </div>
512
+
513
+ <div class="container">
514
+ <div class="control-panel">
515
+ <div class="panel-header">CONTROL ROD DRIVE PANEL</div>
516
+
517
+ <div class="control-group">
518
+ <div class="control-label">Reactor Power</div>
519
+ <div class="power-bar">
520
+ <div class="power-fill" id="powerFill"></div>
521
+ </div>
522
+ <div class="readout" id="powerReadout">
523
+ <span class="status-indicator shutdown" id="statusIndicator"></span>
524
+ 0 MWth (0%)
525
+ </div>
526
+ </div>
527
+
528
+ <div class="control-group">
529
+ <div class="control-label">Core ΔT</div>
530
+ <div class="gauge">
531
+ <div class="gauge-needle" id="tempNeedle"></div>
532
+ </div>
533
+ <div class="readout" id="tempReadout">0°C</div>
534
+ </div>
535
+
536
+ <div class="control-group">
537
+ <div class="control-label">Control Rod Position</div>
538
+ <div class="slider-container">
539
+ <input type="range" class="slider" id="rodPosition" min="0" max="100" value="0">
540
+ </div>
541
+ <div class="readout" id="rodReadout">0% Withdrawn</div>
542
+ </div>
543
+
544
+ <div class="control-group">
545
+ <div class="control-label">Boron Concentration</div>
546
+ <div class="slider-container">
547
+ <input type="range" class="slider" id="boronLevel" min="0" max="2000" value="2000" step="50">
548
+ </div>
549
+ <div class="readout" id="boronReadout">2000 ppm</div>
550
+ </div>
551
+
552
+ <div class="control-group">
553
+ <div class="control-label">RCS Pressure</div>
554
+ <div class="gauge">
555
+ <div class="gauge-needle" id="pressureNeedle"></div>
556
+ </div>
557
+ <div class="readout" id="pressureReadout">15.5 MPa</div>
558
+ </div>
559
+
560
+ <div class="control-group">
561
+ <div class="control-label">Audio System</div>
562
+ <div class="audio-controls">
563
+ <button class="mute-button" id="muteButton">🔊 Audio On</button>
564
+ <div class="volume-control">
565
+ <span style="font-size: 12px;">Vol:</span>
566
+ <input type="range" class="slider" id="volumeControl" min="0" max="100" value="50" style="width: 80px; height: 20px;">
567
+ </div>
568
+ </div>
569
+ </div>
570
+
571
+ <div class="control-group">
572
+ <div class="control-label">Emergency Procedures</div>
573
+ <div class="emergency-controls">
574
+ <button class="action-button success" id="startupBtn">Startup</button>
575
+ <button class="action-button caution" id="cooldownBtn">Cooldown</button>
576
+ <button class="action-button" id="criticalityBtn">To Critical</button>
577
+ <button class="action-button caution" id="resetBtn">Reset All</button>
578
+ </div>
579
+ </div>
580
+
581
+ <div class="control-group">
582
+ <div class="control-label">Annunciators</div>
583
+ <div class="annunciator-grid" id="annunciatorGrid"></div>
584
+ </div>
585
+
586
+ <button class="scram-button" id="scramButton">SCRAM</button>
587
+
588
+ <div class="control-group">
589
+ <div class="control-label">Time Scale</div>
590
+ <div class="slider-container">
591
+ <input type="range" class="slider" id="timeScale" min="0.1" max="5" value="1" step="0.1">
592
+ </div>
593
+ <div class="readout" id="timeReadout">1.0x Real Time</div>
594
+ </div>
595
+ </div>
596
+
597
+ <div class="reactor-cavity">
598
+ <div class="reactor-3d">
599
+ <div class="reactor-core" id="reactorCore">
600
+ <div class="fuel-assembly-grid" id="fuelGrid"></div>
601
+ <div class="control-rod" id="controlRod"></div>
602
+
603
+ <div class="coolant-loops">
604
+ <div class="coolant-loop loop-1"></div>
605
+ <div class="coolant-loop loop-2"></div>
606
+ <div class="coolant-loop loop-3"></div>
607
+ <div class="coolant-loop loop-4"></div>
608
+ </div>
609
+ </div>
610
+
611
+ <div class="particles" id="particles"></div>
612
+ </div>
613
+ </div>
614
+ </div>
615
+
616
+ <script>
617
+ // Global variables
618
+ let reactor;
619
+
620
+ // Utility functions (defined first to avoid reference errors)
621
+ function closeTooltip() {
622
+ const tooltip = document.getElementById('welcomeTooltip');
623
+ if (tooltip) {
624
+ tooltip.style.display = 'none';
625
+ }
626
+
627
+ // Start the reactor simulation
628
+ if (reactor) {
629
+ reactor.start();
630
+ }
631
+ }
632
+
633
+ // Enhanced Audio System
634
+ class ReactorAudio {
635
+ constructor() {
636
+ this.audioContext = null;
637
+ this.enabled = false;
638
+ this.muted = false;
639
+ this.volume = 0.5;
640
+ this.alarmInterval = null;
641
+ this.backgroundHum = null;
642
+ this.init();
643
+ }
644
+
645
+ init() {
646
+ try {
647
+ this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
648
+ this.enabled = true;
649
+ console.log('Audio system initialized successfully');
650
+ } catch (e) {
651
+ console.log('Audio not available:', e);
652
+ this.enabled = false;
653
+ }
654
+ }
655
+
656
+ async ensureAudioContext() {
657
+ if (this.audioContext && this.audioContext.state === 'suspended') {
658
+ try {
659
+ await this.audioContext.resume();
660
+ } catch (e) {
661
+ console.log('Failed to resume audio context:', e);
662
+ }
663
+ }
664
+ }
665
+
666
+ playTone(frequency, duration = 0.1, type = 'sine', volume = null) {
667
+ if (!this.enabled || !this.audioContext || this.muted) return;
668
+
669
+ this.ensureAudioContext();
670
+
671
+ try {
672
+ const oscillator = this.audioContext.createOscillator();
673
+ const gainNode = this.audioContext.createGain();
674
+
675
+ oscillator.type = type;
676
+ oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime);
677
+
678
+ const vol = (volume !== null ? volume : this.volume) * 0.3;
679
+ gainNode.gain.setValueAtTime(vol, this.audioContext.currentTime);
680
+ gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration);
681
+
682
+ oscillator.connect(gainNode);
683
+ gainNode.connect(this.audioContext.destination);
684
+
685
+ oscillator.start();
686
+ oscillator.stop(this.audioContext.currentTime + duration);
687
+ } catch (e) {
688
+ console.log('Audio playback failed:', e);
689
+ }
690
+ }
691
+
692
+ playClick() {
693
+ this.playTone(800, 0.05, 'square', 0.2);
694
+ }
695
+
696
+ playAlarm() {
697
+ if (this.alarmInterval) return; // Already playing
698
+
699
+ this.playTone(1000, 0.8, 'sine', 0.8);
700
+
701
+ // Continue alarm pattern
702
+ this.alarmInterval = setInterval(() => {
703
+ this.playTone(1000, 0.4, 'sine', 0.6);
704
+ setTimeout(() => {
705
+ this.playTone(1200, 0.4, 'sine', 0.6);
706
+ }, 500);
707
+ }, 1500);
708
+
709
+ // Stop after 10 seconds
710
+ setTimeout(() => {
711
+ this.stopAlarm();
712
+ }, 10000);
713
+ }
714
+
715
+ stopAlarm() {
716
+ if (this.alarmInterval) {
717
+ clearInterval(this.alarmInterval);
718
+ this.alarmInterval = null;
719
+ }
720
+ }
721
+
722
+ playCriticalityAlarm() {
723
+ this.stopAlarm();
724
+ this.playTone(1500, 0.2, 'sawtooth', 0.7);
725
+ setTimeout(() => {
726
+ this.playTone(1200, 0.2, 'sawtooth', 0.7);
727
+ }, 300);
728
+ setTimeout(() => {
729
+ this.playTone(1800, 0.3, 'sawtooth', 0.7);
730
+ }, 600);
731
+ }
732
+
733
+ playSubcriticalBeep() {
734
+ this.playTone(600, 0.15, 'triangle', 0.4);
735
+ }
736
+
737
+ startBackgroundHum(powerLevel = 0) {
738
+ if (!this.enabled || this.muted) return;
739
+
740
+ this.ensureAudioContext();
741
+
742
+ try {
743
+ if (this.backgroundHum) {
744
+ this.backgroundHum.frequency.setValueAtTime(
745
+ 50 + (powerLevel * 100),
746
+ this.audioContext.currentTime
747
+ );
748
+ this.backgroundHum.gain.gain.setValueAtTime(
749
+ Math.min(0.1, powerLevel * 0.05) * this.volume,
750
+ this.audioContext.currentTime
751
+ );
752
+ return;
753
+ }
754
+
755
+ const oscillator = this.audioContext.createOscillator();
756
+ const gainNode = this.audioContext.createGain();
757
+ const filterNode = this.audioContext.createBiquadFilter();
758
+
759
+ oscillator.type = 'sawtooth';
760
+ oscillator.frequency.setValueAtTime(50 + (powerLevel * 100), this.audioContext.currentTime);
761
+
762
+ filterNode.type = 'lowpass';
763
+ filterNode.frequency.setValueAtTime(200, this.audioContext.currentTime);
764
+
765
+ gainNode.gain.setValueAtTime(Math.min(0.1, powerLevel * 0.05) * this.volume, this.audioContext.currentTime);
766
+
767
+ oscillator.connect(filterNode);
768
+ filterNode.connect(gainNode);
769
+ gainNode.connect(this.audioContext.destination);
770
+
771
+ oscillator.start();
772
+
773
+ this.backgroundHum = {
774
+ oscillator: oscillator,
775
+ gain: gainNode,
776
+ frequency: oscillator.frequency
777
+ };
778
+ } catch (e) {
779
+ console.log('Background hum failed:', e);
780
+ }
781
+ }
782
+
783
+ stopBackgroundHum() {
784
+ if (this.backgroundHum) {
785
+ try {
786
+ this.backgroundHum.oscillator.stop();
787
+ } catch (e) {}
788
+ this.backgroundHum = null;
789
+ }
790
+ }
791
+
792
+ setMute(muted) {
793
+ this.muted = muted;
794
+ if (muted) {
795
+ this.stopAlarm();
796
+ this.stopBackgroundHum();
797
+ }
798
+ }
799
+
800
+ setVolume(volume) {
801
+ this.volume = Math.max(0, Math.min(1, volume));
802
+ }
803
+ }
804
+
805
+ // Reactor Physics Engine
806
+ class ReactorPhysics {
807
+ constructor() {
808
+ this.power = 0; // MWth
809
+ this.rodPosition = 0; // % withdrawn
810
+ this.boronConc = 2000; // ppm
811
+ this.pressure = 15.5; // MPa
812
+ this.temperature = 291; // °C
813
+ this.timeScale = 1.0;
814
+ this.scrammed = false;
815
+ this.running = false;
816
+ this.lastState = 'shutdown';
817
+
818
+ // Physics constants
819
+ this.maxPower = 3000; // MWth
820
+ this.criticalBoron = 1000; // ppm for criticality
821
+
822
+ this.lastUpdate = Date.now();
823
+ }
824
+
825
+ update() {
826
+ if (!this.running) return;
827
+
828
+ const now = Date.now();
829
+ const dt = Math.min((now - this.lastUpdate) / 1000 * this.timeScale, 0.1);
830
+ this.lastUpdate = now;
831
+
832
+ if (this.scrammed) {
833
+ this.power = Math.max(0, this.power - (this.power * 2 * dt));
834
+ return;
835
+ }
836
+
837
+ // Simple reactivity calculation
838
+ const reactivity = this.calculateReactivity();
839
+
840
+ // Power change based on reactivity
841
+ if (reactivity > 0) {
842
+ this.power = Math.min(this.maxPower, this.power + (reactivity * 1000 * dt));
843
+ } else {
844
+ this.power = Math.max(0, this.power + (reactivity * 1000 * dt));
845
+ }
846
+
847
+ // Temperature response
848
+ if (this.power > 0) {
849
+ const targetTemp = 291 + (this.power / this.maxPower) * 50;
850
+ this.temperature += (targetTemp - this.temperature) * 2 * dt;
851
+ } else {
852
+ this.temperature += (291 - this.temperature) * 1 * dt;
853
+ }
854
+
855
+ // Pressure response
856
+ const tempRatio = this.temperature / 291;
857
+ this.pressure = 15.5 * tempRatio;
858
+ }
859
+
860
+ calculateReactivity() {
861
+ // Rod reactivity
862
+ const rodReactivity = (this.rodPosition / 100) * 0.2;
863
+
864
+ // Boron reactivity
865
+ const boronReactivity = -(this.boronConc - this.criticalBoron) * 0.0002;
866
+
867
+ // Temperature feedback
868
+ const tempReactivity = -(this.temperature - 291) * 0.0001;
869
+
870
+ // Base subcritical
871
+ return rodReactivity + boronReactivity + tempReactivity - 0.05;
872
+ }
873
+
874
+ getState() {
875
+ if (this.scrammed) return 'scrammed';
876
+ if (this.power > 1000) return 'critical';
877
+ if (this.power > 10) return 'subcritical';
878
+ return 'shutdown';
879
+ }
880
+
881
+ scram() {
882
+ this.scrammed = true;
883
+ this.rodPosition = 0;
884
+ }
885
+
886
+ reset() {
887
+ this.scrammed = false;
888
+ this.power = 0;
889
+ this.temperature = 291;
890
+ this.pressure = 15.5;
891
+ this.rodPosition = 0;
892
+ this.boronConc = 2000;
893
+ this.lastState = 'shutdown';
894
+ }
895
+
896
+ start() {
897
+ this.running = true;
898
+ this.lastUpdate = Date.now();
899
+ }
900
+ }
901
+
902
+ // Main Reactor Simulator
903
+ class ReactorSimulator {
904
+ constructor() {
905
+ this.physics = new ReactorPhysics();
906
+ this.audio = new ReactorAudio();
907
+ this.keyBuffer = '';
908
+ this.cherenkovMode = false;
909
+ this.animationId = null;
910
+ this.lastReactorState = 'shutdown';
911
+
912
+ this.initializeElements();
913
+ this.createFuelGrid();
914
+ this.createAnnunciators();
915
+ this.bindEvents();
916
+ }
917
+
918
+ initializeElements() {
919
+ this.elements = {
920
+ powerFill: document.getElementById('powerFill'),
921
+ powerReadout: document.getElementById('powerReadout'),
922
+ statusIndicator: document.getElementById('statusIndicator'),
923
+ tempNeedle: document.getElementById('tempNeedle'),
924
+ tempReadout: document.getElementById('tempReadout'),
925
+ pressureNeedle: document.getElementById('pressureNeedle'),
926
+ pressureReadout: document.getElementById('pressureReadout'),
927
+ rodPosition: document.getElementById('rodPosition'),
928
+ rodReadout: document.getElementById('rodReadout'),
929
+ boronLevel: document.getElementById('boronLevel'),
930
+ boronReadout: document.getElementById('boronReadout'),
931
+ timeScale: document.getElementById('timeScale'),
932
+ timeReadout: document.getElementById('timeReadout'),
933
+ scramButton: document.getElementById('scramButton'),
934
+ controlRod: document.getElementById('controlRod'),
935
+ reactorCore: document.getElementById('reactorCore'),
936
+ particles: document.getElementById('particles'),
937
+ fuelGrid: document.getElementById('fuelGrid'),
938
+ muteButton: document.getElementById('muteButton'),
939
+ volumeControl: document.getElementById('volumeControl'),
940
+ startupBtn: document.getElementById('startupBtn'),
941
+ cooldownBtn: document.getElementById('cooldownBtn'),
942
+ criticalityBtn: document.getElementById('criticalityBtn'),
943
+ resetBtn: document.getElementById('resetBtn')
944
+ };
945
+ }
946
+
947
+ createFuelGrid() {
948
+ const grid = this.elements.fuelGrid;
949
+ grid.innerHTML = '';
950
+ for (let i = 0; i < 289; i++) { // 17x17 grid
951
+ const pin = document.createElement('div');
952
+ pin.className = 'fuel-pin';
953
+ grid.appendChild(pin);
954
+ }
955
+ }
956
+
957
+ createAnnunciators() {
958
+ const grid = document.getElementById('annunciatorGrid');
959
+ grid.innerHTML = '';
960
+ const labels = [
961
+ 'PWR HIGH', 'TEMP HIGH', 'PRESS HIGH', 'FLOW LOW',
962
+ 'ROD DROP', 'SCRAM', 'DNBR LOW', 'CRITICAL',
963
+ 'COOLING', 'CONTAIN', 'RADIAT', 'TURBINE',
964
+ 'BACKUP', 'DIESEL', 'BATTERY', 'ALARM'
965
+ ];
966
+
967
+ for (let i = 0; i < 16; i++) {
968
+ const annunciator = document.createElement('div');
969
+ annunciator.className = 'annunciator';
970
+ annunciator.title = labels[i] || `ANN ${i+1}`;
971
+ grid.appendChild(annunciator);
972
+ }
973
+ }
974
+
975
+ bindEvents() {
976
+ // Control sliders
977
+ this.elements.rodPosition.addEventListener('input', (e) => {
978
+ if (!this.physics.scrammed) {
979
+ this.physics.rodPosition = parseFloat(e.target.value);
980
+ this.audio.playClick();
981
+ }
982
+ });
983
+
984
+ this.elements.boronLevel.addEventListener('input', (e) => {
985
+ this.physics.boronConc = parseFloat(e.target.value);
986
+ this.audio.playClick();
987
+ });
988
+
989
+ this.elements.timeScale.addEventListener('input', (e) => {
990
+ this.physics.timeScale = parseFloat(e.target.value);
991
+ });
992
+
993
+ // SCRAM button
994
+ this.elements.scramButton.addEventListener('click', () => {
995
+ if (this.physics.scrammed) {
996
+ this.resetReactor();
997
+ } else {
998
+ this.physics.scram();
999
+ this.elements.scramButton.textContent = 'RESET';
1000
+ this.elements.scramButton.style.background = 'var(--caution)';
1001
+ this.audio.playAlarm();
1002
+ }
1003
+ });
1004
+
1005
+ // Audio controls
1006
+ this.elements.muteButton.addEventListener('click', () => {
1007
+ this.audio.setMute(!this.audio.muted);
1008
+ this.elements.muteButton.textContent = this.audio.muted ? '🔇 Audio Off' : '🔊 Audio On';
1009
+ this.elements.muteButton.classList.toggle('muted', this.audio.muted);
1010
+ });
1011
+
1012
+ this.elements.volumeControl.addEventListener('input', (e) => {
1013
+ this.audio.setVolume(parseFloat(e.target.value) / 100);
1014
+ });
1015
+
1016
+ // Emergency procedure buttons
1017
+ this.elements.startupBtn.addEventListener('click', () => {
1018
+ this.startupProcedure();
1019
+ this.audio.playClick();
1020
+ });
1021
+
1022
+ this.elements.cooldownBtn.addEventListener('click', () => {
1023
+ this.cooldownProcedure();
1024
+ this.audio.playClick();
1025
+ });
1026
+
1027
+ this.elements.criticalityBtn.addEventListener('click', () => {
1028
+ this.toCriticality();
1029
+ this.audio.playClick();
1030
+ });
1031
+
1032
+ this.elements.resetBtn.addEventListener('click', () => {
1033
+ this.resetReactor();
1034
+ this.audio.playClick();
1035
+ });
1036
+
1037
+ // Cherenkov easter egg
1038
+ document.addEventListener('keydown', (e) => {
1039
+ if (e.shiftKey) {
1040
+ this.keyBuffer += e.key.toLowerCase();
1041
+ if (this.keyBuffer.includes('cherenkov')) {
1042
+ this.toggleCherenkovMode();
1043
+ this.keyBuffer = '';
1044
+ }
1045
+ }
1046
+ });
1047
+
1048
+ document.addEventListener('keyup', (e) => {
1049
+ if (!e.shiftKey) {
1050
+ this.keyBuffer = '';
1051
+ }
1052
+ });
1053
+
1054
+ // Keyboard shortcuts
1055
+ document.addEventListener('keydown', (e) => {
1056
+ if (e.target.tagName === 'INPUT') return;
1057
+
1058
+ switch(e.key.toLowerCase()) {
1059
+ case ' ':
1060
+ e.preventDefault();
1061
+ this.elements.scramButton.click();
1062
+ break;
1063
+ case 'r':
1064
+ if (e.ctrlKey) {
1065
+ e.preventDefault();
1066
+ this.resetReactor();
1067
+ }
1068
+ break;
1069
+ case 's':
1070
+ if (e.ctrlKey) {
1071
+ e.preventDefault();
1072
+ this.startupProcedure();
1073
+ }
1074
+ break;
1075
+ case 'c':
1076
+ if (e.ctrlKey) {
1077
+ e.preventDefault();
1078
+ this.cooldownProcedure();
1079
+ }
1080
+ break;
1081
+ case '0':
1082
+ case '1':
1083
+ case '2':
1084
+ case '3':
1085
+ case '4':
1086
+ case '5':
1087
+ case '6':
1088
+ case '7':
1089
+ case '8':
1090
+ case '9':
1091
+ if (!this.physics.scrammed) {
1092
+ const position = parseInt(e.key) * 10;
1093
+ this.physics.rodPosition = position;
1094
+ this.elements.rodPosition.value = position;
1095
+ this.audio.playClick();
1096
+ }
1097
+ break;
1098
+ }
1099
+ });
1100
+ }
1101
+
1102
+ toggleCherenkovMode() {
1103
+ this.cherenkovMode = !this.cherenkovMode;
1104
+ if (this.cherenkovMode) {
1105
+ this.elements.reactorCore.style.filter = 'hue-rotate(200deg) brightness(1.5) saturate(2)';
1106
+ console.log('Cherenkov debug mode activated');
1107
+ } else {
1108
+ this.elements.reactorCore.style.filter = '';
1109
+ console.log('Cherenkov debug mode deactivated');
1110
+ }
1111
+ }
1112
+
1113
+ startupProcedure() {
1114
+ if (this.physics.scrammed) {
1115
+ console.log('Cannot start: Reactor is scrammed');
1116
+ return;
1117
+ }
1118
+
1119
+ console.log('PROCEDURE: Reactor startup sequence initiated');
1120
+
1121
+ // Step 1: Ensure high boron concentration
1122
+ this.physics.boronConc = 1800;
1123
+ this.elements.boronLevel.value = 1800;
1124
+
1125
+ // Step 2: Gradual rod withdrawal
1126
+ let step = 0;
1127
+ const withdrawStep = () => {
1128
+ if (step < 40 && this.physics.power < 100 && !this.physics.scrammed) {
1129
+ this.physics.rodPosition = Math.min(80, this.physics.rodPosition + 2);
1130
+ this.elements.rodPosition.value = this.physics.rodPosition;
1131
+ step++;
1132
+ setTimeout(withdrawStep, 800);
1133
+ }
1134
+ };
1135
+
1136
+ setTimeout(withdrawStep, 1000);
1137
+ }
1138
+
1139
+ cooldownProcedure() {
1140
+ console.log('PROCEDURE: Initiating controlled cooldown');
1141
+ this.physics.rodPosition = Math.max(0, this.physics.rodPosition - 30);
1142
+ this.physics.boronConc = Math.min(2000, this.physics.boronConc + 300);
1143
+ this.updateControls();
1144
+ }
1145
+
1146
+ toCriticality() {
1147
+ if (this.physics.scrammed) {
1148
+ console.log('Cannot achieve criticality: Reactor is scrammed');
1149
+ return;
1150
+ }
1151
+
1152
+ console.log('PROCEDURE: Approaching criticality');
1153
+
1154
+ // Reduce boron to critical level
1155
+ this.physics.boronConc = 1100;
1156
+ this.elements.boronLevel.value = 1100;
1157
+
1158
+ // Set rods to critical position
1159
+ let targetRods = 60;
1160
+ const adjustRods = () => {
1161
+ if (Math.abs(this.physics.rodPosition - targetRods) > 2 && !this.physics.scrammed) {
1162
+ if (this.physics.rodPosition < targetRods) {
1163
+ this.physics.rodPosition = Math.min(targetRods, this.physics.rodPosition + 3);
1164
+ } else {
1165
+ this.physics.rodPosition = Math.max(targetRods, this.physics.rodPosition - 3);
1166
+ }
1167
+ this.elements.rodPosition.value = this.physics.rodPosition;
1168
+ setTimeout(adjustRods, 500);
1169
+ }
1170
+ };
1171
+
1172
+ adjustRods();
1173
+ }
1174
+
1175
+ resetReactor() {
1176
+ this.physics.reset();
1177
+ this.elements.scramButton.textContent = 'SCRAM';
1178
+ this.elements.scramButton.style.background = 'var(--alarm)';
1179
+ this.updateControls();
1180
+ this.audio.stopAlarm();
1181
+ this.audio.stopBackgroundHum();
1182
+ console.log('Reactor reset to cold shutdown');
1183
+ }
1184
+
1185
+ updateControls() {
1186
+ this.elements.rodPosition.value = this.physics.rodPosition;
1187
+ this.elements.boronLevel.value = this.physics.boronConc;
1188
+ this.elements.timeScale.value = this.physics.timeScale;
1189
+ }
1190
+
1191
+ updateDisplay() {
1192
+ // Power display
1193
+ const powerPercent = Math.min((this.physics.power / 3000) * 100, 120);
1194
+ this.elements.powerFill.style.height = powerPercent + '%';
1195
+
1196
+ // Status indicator and reactor state monitoring
1197
+ const currentState = this.physics.getState();
1198
+ if (currentState !== this.lastReactorState) {
1199
+ this.onStateChange(this.lastReactorState, currentState);
1200
+ this.lastReactorState = currentState;
1201
+ }
1202
+
1203
+ // Update status indicator
1204
+ this.elements.statusIndicator.className = `status-indicator ${currentState}`;
1205
+
1206
+ this.elements.powerReadout.innerHTML =
1207
+ `<span class="status-indicator ${currentState}"></span>${this.physics.power.toFixed(0)} MWth (${(this.physics.power/30).toFixed(1)}%)`;
1208
+
1209
+ if (powerPercent > 100) {
1210
+ this.elements.powerFill.style.background = 'linear-gradient(0deg, var(--alarm), var(--caution))';
1211
+ } else {
1212
+ this.elements.powerFill.style.background = 'linear-gradient(0deg, var(--primary), var(--coolant))';
1213
+ }
1214
+
1215
+ // Temperature
1216
+ const tempDelta = this.physics.temperature - 291;
1217
+ const tempAngle = Math.min((tempDelta / 50) * 180, 180);
1218
+ this.elements.tempNeedle.style.transform =
1219
+ `translate(-50%, -100%) rotate(${tempAngle - 90}deg)`;
1220
+ this.elements.tempReadout.textContent = `+${tempDelta.toFixed(1)}°C`;
1221
+
1222
+ // Pressure
1223
+ const pressureAngle = Math.min(((this.physics.pressure - 15.5) / 2) * 180, 180);
1224
+ this.elements.pressureNeedle.style.transform =
1225
+ `translate(-50%, -100%) rotate(${pressureAngle - 90}deg)`;
1226
+ this.elements.pressureReadout.textContent = `${this.physics.pressure.toFixed(1)} MPa`;
1227
+
1228
+ // Control displays
1229
+ this.elements.rodReadout.textContent = `${this.physics.rodPosition.toFixed(0)}% Withdrawn`;
1230
+ this.elements.boronReadout.textContent = `${this.physics.boronConc.toFixed(0)} ppm`;
1231
+ this.elements.timeReadout.textContent = `${this.physics.timeScale.toFixed(1)}x Real Time`;
1232
+
1233
+ // Control rod visual position
1234
+ const rodTop = 10 + (100 - this.physics.rodPosition) * 2.9;
1235
+ this.elements.controlRod.style.top = rodTop + 'px';
1236
+
1237
+ // Update fuel pins
1238
+ this.updateFuelPins();
1239
+
1240
+ // Update annunciators
1241
+ this.updateAnnunciators();
1242
+
1243
+ // Update background hum based on power level
1244
+ if (this.physics.power > 50) {
1245
+ this.audio.startBackgroundHum(this.physics.power / 3000);
1246
+ } else {
1247
+ this.audio.stopBackgroundHum();
1248
+ }
1249
+
1250
+ // Create neutron particles in Cherenkov mode
1251
+ if (this.cherenkovMode && this.physics.power > 50 && Math.random() < 0.1) {
1252
+ this.createNeutronParticle();
1253
+ }
1254
+ }
1255
+
1256
+ onStateChange(oldState, newState) {
1257
+ console.log(`Reactor state changed: ${oldState} → ${newState}`);
1258
+
1259
+ switch(newState) {
1260
+ case 'critical':
1261
+ if (oldState === 'subcritical') {
1262
+ console.log('🔥 REACTOR IS NOW CRITICAL');
1263
+ this.audio.playCriticalityAlarm();
1264
+ }
1265
+ break;
1266
+ case 'subcritical':
1267
+ if (oldState === 'shutdown') {
1268
+ console.log('⚡ Reactor is now subcritical');
1269
+ this.audio.playSubcriticalBeep();
1270
+ }
1271
+ break;
1272
+ case 'scrammed':
1273
+ console.log('🚨 REACTOR SCRAMMED - EMERGENCY SHUTDOWN');
1274
+ this.audio.playAlarm();
1275
+ break;
1276
+ case 'shutdown':
1277
+ console.log('✅ Reactor shutdown complete');
1278
+ this.audio.stopAlarm();
1279
+ break;
1280
+ }
1281
+ }
1282
+
1283
+ updateFuelPins() {
1284
+ const pins = this.elements.fuelGrid.querySelectorAll('.fuel-pin');
1285
+ const powerLevel = this.physics.power / 3000;
1286
+
1287
+ pins.forEach((pin, index) => {
1288
+ const row = Math.floor(index / 17);
1289
+ const col = index % 17;
1290
+ const centerDistance = Math.sqrt((row - 8) ** 2 + (col - 8) ** 2);
1291
+ const peaking = Math.max(0, 1 - centerDistance / 12);
1292
+ const localPower = powerLevel * peaking;
1293
+
1294
+ if (localPower > 0.1) {
1295
+ pin.classList.add('hot');
1296
+ pin.style.opacity = Math.min(1, localPower * 2);
1297
+ } else {
1298
+ pin.classList.remove('hot');
1299
+ pin.style.opacity = 0.3;
1300
+ }
1301
+ });
1302
+ }
1303
+
1304
+ updateAnnunciators() {
1305
+ const annunciators = document.querySelectorAll('.annunciator');
1306
+
1307
+ // Clear all
1308
+ annunciators.forEach(ann => {
1309
+ ann.classList.remove('active', 'caution');
1310
+ });
1311
+
1312
+ // Power high
1313
+ if (this.physics.power > 2500) {
1314
+ annunciators[0].classList.add('active');
1315
+ }
1316
+
1317
+ // Temperature high
1318
+ if (this.physics.temperature > 330) {
1319
+ annunciators[1].classList.add('caution');
1320
+ }
1321
+
1322
+ // Pressure high
1323
+ if (this.physics.pressure > 17.0) {
1324
+ annunciators[2].classList.add('caution');
1325
+ }
1326
+
1327
+ // SCRAM
1328
+ if (this.physics.scrammed) {
1329
+ annunciators[5].classList.add('active');
1330
+ }
1331
+
1332
+ // Critical
1333
+ if (this.physics.power > 1000) {
1334
+ annunciators[7].classList.add('caution');
1335
+ }
1336
+
1337
+ // DNBR low
1338
+ if (this.physics.power > 2800 && this.physics.temperature > 335) {
1339
+ annunciators[6].classList.add('active');
1340
+ }
1341
+ }
1342
+
1343
+ createNeutronParticle() {
1344
+ const neutron = document.createElement('div');
1345
+ neutron.className = 'neutron';
1346
+ neutron.style.left = (40 + Math.random() * 20) + '%';
1347
+ neutron.style.top = (30 + Math.random() * 40) + '%';
1348
+
1349
+ this.elements.particles.appendChild(neutron);
1350
+
1351
+ // Animate neutron
1352
+ let y = 0;
1353
+ const animate = () => {
1354
+ y += 2;
1355
+ neutron.style.transform = `translateY(${y}px)`;
1356
+ neutron.style.opacity = Math.max(0, 1 - y / 200);
1357
+
1358
+ if (y < 200 && neutron.parentNode) {
1359
+ requestAnimationFrame(animate);
1360
+ } else if (neutron.parentNode) {
1361
+ neutron.parentNode.removeChild(neutron);
1362
+ }
1363
+ };
1364
+ animate();
1365
+ }
1366
+
1367
+ start() {
1368
+ this.physics.start();
1369
+ const animate = () => {
1370
+ this.physics.update();
1371
+ this.updateDisplay();
1372
+ this.animationId = requestAnimationFrame(animate);
1373
+ };
1374
+ animate();
1375
+ }
1376
+
1377
+ stop() {
1378
+ if (this.animationId) {
1379
+ cancelAnimationFrame(this.animationId);
1380
+ this.animationId = null;
1381
+ }
1382
+ this.audio.stopAlarm();
1383
+ this.audio.stopBackgroundHum();
1384
+ }
1385
+ }
1386
+
1387
+ // Initialize everything when page loads
1388
+ window.addEventListener('load', () => {
1389
+ console.log('Initializing Nuclear Reactor Simulator...');
1390
+
1391
+ try {
1392
+ // Initialize reactor simulator
1393
+ reactor = new ReactorSimulator();
1394
+
1395
+ console.log('Reactor simulator initialized successfully');
1396
+ console.log('Controls:');
1397
+ console.log('- Numbers 0-9: Set rod position (0%, 10%, 20%, etc.)');
1398
+ console.log('- Space: Emergency SCRAM');
1399
+ console.log('- Ctrl+R: Reset reactor');
1400
+ console.log('- Ctrl+S: Startup procedure');
1401
+ console.log('- Ctrl+C: Cooldown procedure');
1402
+ console.log('- Shift+CHERENKOV: Debug mode');
1403
+
1404
+ } catch (error) {
1405
+ console.error('Failed to initialize reactor simulator:', error);
1406
+ }
1407
+ });
1408
+
1409
+ // Handle page visibility changes
1410
+ document.addEventListener('visibilitychange', () => {
1411
+ if (reactor) {
1412
+ if (document.hidden) {
1413
+ reactor.stop();
1414
+ } else {
1415
+ reactor.start();
1416
+ }
1417
+ }
1418
+ });
1419
+
1420
+ // Prevent accidental page reload
1421
+ window.addEventListener('beforeunload', (e) => {
1422
+ if (reactor && reactor.physics.power > 500) {
1423
+ e.preventDefault();
1424
+ e.returnValue = 'Reactor is at power. Are you sure you want to leave?';
1425
+ return e.returnValue;
1426
+ }
1427
+ });
1428
+
1429
+ // Add status monitoring
1430
+ setInterval(() => {
1431
+ if (reactor && reactor.physics.running) {
1432
+ const status = {
1433
+ power: reactor.physics.power.toFixed(1) + ' MWth',
1434
+ temperature: reactor.physics.temperature.toFixed(1) + '°C',
1435
+ pressure: reactor.physics.pressure.toFixed(1) + ' MPa',
1436
+ rods: reactor.physics.rodPosition.toFixed(1) + '% withdrawn',
1437
+ boron: reactor.physics.boronConc.toFixed(0) + ' ppm',
1438
+ status: reactor.physics.scrammed ? 'SCRAMMED' :
1439
+ reactor.physics.power > 1000 ? 'CRITICAL' :
1440
+ reactor.physics.power > 10 ? 'SUBCRITICAL' : 'SHUTDOWN'
1441
+ };
1442
+
1443
+ // Update page title with reactor status
1444
+ document.title = `Reactor: ${status.power} | ${status.status}`;
1445
+
1446
+ // Log critical conditions
1447
+ if (reactor.physics.power > 2500) {
1448
+ console.warn('WARNING: Reactor power exceeding 2500 MWth');
1449
+ }
1450
+ if (reactor.physics.temperature > 340) {
1451
+ console.warn('WARNING: Core temperature exceeding 340°C');
1452
+ }
1453
+ if (reactor.physics.pressure > 17) {
1454
+ console.warn('WARNING: RCS pressure exceeding 17 MPa');
1455
+ }
1456
+ }
1457
+ }, 5000);
1458
+
1459
+ console.log('Dr. Elara Voss Nuclear Reactor Simulator v2.0');
1460
+ console.log('======================================================');
1461
+ console.log('Enhanced with improved audio and button controls');
1462
+ console.log('Reactor systems nominal. Awaiting initialization...');
1463
+ console.log('======================================================');
1464
+ </script>
1465
+ </body>
1466
+ </html>