AmrGaberr commited on
Commit
6986a81
·
verified ·
1 Parent(s): f3bcc00

Update UI2/chatbot.html

Browse files
Files changed (1) hide show
  1. UI2/chatbot.html +1165 -1165
UI2/chatbot.html CHANGED
@@ -1,1166 +1,1166 @@
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>AthleteGuard AI</title>
7
- <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Outfit:wght@300;400;500;600&display=swap" rel="stylesheet" />
8
- </head>
9
- <body class="nebula">
10
- <!-- Loader -->
11
- <div class="loader-wrapper">
12
- <div class="loader">
13
- <div class="space-robot-loader">
14
- <div class="robot-core"></div>
15
- <div class="robot-panel panel-1"></div>
16
- <div class="robot-panel panel-2"></div>
17
- <div class="robot-eye"></div>
18
- <div class="shockwave"></div>
19
- <div class="pulse-ring"></div>
20
- </div>
21
- <div class="progress-bar">
22
- <div class="progress-arc"></div>
23
- </div>
24
- <span>Initializing AI...</span>
25
- </div>
26
- </div>
27
-
28
- <!-- Header -->
29
- <header>
30
- <div class="navbar">
31
- <div class="nav-logo">
32
- <svg class="space-robot-logo" viewBox="0 0 40 40" aria-label=".team
33
- Space Robot Logo">
34
- <circle cx="20" cy="20" r="15" fill="url(#robotGradient)" />
35
- <circle cx="15" cy="15" r="4" fill="#f0f0ff" />
36
- <path d="M10 20 L8 18 M30 20 L32 18" stroke="#c0c0c0" stroke-width="1" />
37
- <circle cx="20" cy="20" r="18" fill="none" stroke="#c0c0c0" stroke-width="0.5" opacity="0.5" />
38
- <defs>
39
- <radialGradient id="robotGradient" cx="0.5" cy="0.5" r="0.5">
40
- <stop offset="0%" stop-color="#c0c0c0" />
41
- <stop offset="100%" stop-color="#3c2f5f" />
42
- </radialGradient>
43
- </defs>
44
- </svg>
45
- </div>
46
- <div class="hamburger" aria-label="Toggle navigation">
47
- <span></span>
48
- <span></span>
49
- <span></span>
50
- </div>
51
- <ul class="nav-menu">
52
- <li><a href="index.html">Home</a></li>
53
- <li><a href="index.html#features">Features</a></li>
54
- <li><a href="calculator.html">Calculator</a></li>
55
- <li><a href="about.html">About</a></li>
56
- <li><a href="chatbot.html" class="active">Chatbot</a></li>
57
- <li><a href="index.html#faq">FAQ</a></li>
58
- </ul>
59
- <select id="theme-toggle" aria-label="Select theme">
60
- <option value="nebula">Nebula</option>
61
- <option value="starlight">Starlight</option>
62
- <option value="void">Void</option>
63
- </select>
64
- </div>
65
- </header>
66
-
67
- <!-- Chatbot Section -->
68
- <section class="section chatbot-section">
69
- <canvas id="starfield-canvas"></canvas>
70
- <div class="container">
71
- <h1 class="header">AthleteGuard AI</h1>
72
- <p class="description">Your intelligent assistant for training and injury prevention.</p>
73
- <div class="chatbot-container glassmorphism" id="chatbotContainer">
74
- <div class="chat-header">
75
- <div class="space-robot"></div>
76
- <span>AthleteGuard AI</span>
77
- </div>
78
- <div class="chat-body" id="chat-output"></div>
79
- <div class="chat-input-wrapper">
80
- <input type="text" id="user-input" placeholder="Ask about training, nutrition, or injuries..." aria-label="Chat input" />
81
- <button id="sendButton" aria-label="Send message">➤</button>
82
- </div>
83
- </div>
84
- </div>
85
- </section>
86
-
87
- <!-- Footer -->
88
- <footer class="footer">
89
- <div class="container">
90
- <p>© 2025 AthleteGuard AI. All rights reserved.</p>
91
- </div>
92
- </footer>
93
-
94
- <!-- Embedded CSS -->
95
- <style>
96
- * {
97
- margin: 0;
98
- padding: 0;
99
- box-sizing: border-box;
100
- }
101
-
102
- body {
103
- font-family: 'Outfit', sans-serif;
104
- background: linear-gradient(135deg, #0a0a23 0%, #1a1a3d 100%);
105
- color: #f0f0ff;
106
- line-height: 1.6;
107
- overflow-x: hidden;
108
- visibility: visible;
109
- }
110
-
111
- /* Loader */
112
- .loader-wrapper {
113
- position: fixed;
114
- top: 0;
115
- left: 0;
116
- width: 100%;
117
- height: 100%;
118
- background: #0a0a23;
119
- display: flex;
120
- align-items: center;
121
- justify-content: center;
122
- z-index: 1000;
123
- transition: opacity 0.5s ease;
124
- }
125
-
126
- .loader {
127
- display: flex;
128
- flex-direction: column;
129
- align-items: center;
130
- gap: 15px;
131
- }
132
-
133
- .space-robot-loader {
134
- width: 80px;
135
- height: 80px;
136
- position: relative;
137
- animation: loaderAssemble 3s linear forwards;
138
- }
139
-
140
- .robot-core {
141
- width: 50px;
142
- height: 50px;
143
- background: radial-gradient(circle, #c0c0c0 50%, #3c2f5f 100%);
144
- border-radius: 50%;
145
- position: absolute;
146
- top: 15px;
147
- left: 15px;
148
- opacity: 0;
149
- animation: coreFade 1s ease forwards;
150
- }
151
-
152
- .robot-panel {
153
- width: 20px;
154
- height: 20px;
155
- background: #c0c0c0;
156
- position: absolute;
157
- opacity: 0;
158
- }
159
-
160
- .panel-1 {
161
- top: 10px;
162
- left: 10px;
163
- transform: rotate(45deg);
164
- animation: panelSlide 1s ease 1s forwards;
165
- }
166
-
167
- .panel-2 {
168
- bottom: 10px;
169
- right: 10px;
170
- transform: rotate(-45deg);
171
- animation: panelSlide 1s ease 1.5s forwards;
172
- }
173
-
174
- .robot-eye {
175
- width: 12px;
176
- height: 12px;
177
- background: #f0f0ff;
178
- border-radius: 50%;
179
- position: absolute;
180
- top: 25px;
181
- left: 25px;
182
- opacity: 0;
183
- animation: eyeGlow 0.5s ease 2s forwards;
184
- }
185
-
186
- .shockwave {
187
- width: 100px;
188
- height: 100px;
189
- border: 2px solid #c0c0c0;
190
- border-radius: 50%;
191
- position: absolute;
192
- top: -10px;
193
- left: -10px;
194
- opacity: 0;
195
- animation: shockwaveExpand 0.8s ease 2.5s infinite;
196
- }
197
-
198
- .pulse-ring {
199
- width: 120px;
200
- height: 120px;
201
- border: 3px solid rgba(192, 192, 192, 0.3);
202
- border-radius: 50%;
203
- position: absolute;
204
- top: -20px;
205
- left: -20px;
206
- opacity: 0;
207
- animation: pulseRing 1.5s ease 3s infinite;
208
- }
209
-
210
- @keyframes loaderAssemble {
211
- 0% { transform: scale(0.5) rotate(0deg); }
212
- 100% { transform: scale(1) rotate(360deg); }
213
- }
214
-
215
- @keyframes coreFade {
216
- 0% { opacity: 0; transform: scale(0.5); }
217
- 100% { opacity: 1; transform: scale(1); }
218
- }
219
-
220
- @keyframes panelSlide {
221
- 0% { opacity: 0; transform: translateX(-20px) rotate(45deg); }
222
- 100% { opacity: 1; transform: translateX(0) rotate(45deg); }
223
- }
224
-
225
- @keyframes eyeGlow {
226
- 0% { opacity: 0; transform: scale(0.5); }
227
- 100% { opacity: 1; transform: scale(1); }
228
- }
229
-
230
- @keyframes shockwaveExpand {
231
- 0% { opacity: 0.5; transform: scale(0.8); }
232
- 100% { opacity: 0; transform: scale(1.8); }
233
- }
234
-
235
- @keyframes pulseRing {
236
- 0% { opacity: 0.3; transform: scale(0.7); }
237
- 50% { opacity: 0.6; transform: scale(1.2); }
238
- 100% { opacity: 0; transform: scale(1.5); }
239
- }
240
-
241
- .progress-bar {
242
- width: 120px;
243
- height: 12px;
244
- background: rgba(255, 255, 255, 0.1);
245
- border-radius: 6px;
246
- overflow: hidden;
247
- position: relative;
248
- }
249
-
250
- .progress-arc {
251
- width: 0;
252
- height: 100%;
253
- background: linear-gradient(45deg, #3c2f5f, #c0c0c0);
254
- animation: progressFill 3.5s ease-in-out forwards;
255
- }
256
-
257
- @keyframes progressFill {
258
- 0% { width: 0; }
259
- 100% { width: 100%; }
260
- }
261
-
262
- .loader span {
263
- font-family: 'Space Grotesk', sans-serif;
264
- font-size: 18px;
265
- color: #f0f0ff;
266
- letter-spacing: 1px;
267
- }
268
-
269
- .loader-wrapper.fade-out {
270
- opacity: 0;
271
- pointer-events: none;
272
- }
273
-
274
- /* Header */
275
- .navbar {
276
- display: flex;
277
- justify-content: space-between;
278
- align-items: center;
279
- padding: 15px 30px;
280
- background: rgba(255, 255, 255, 0.05);
281
- backdrop-filter: blur(4px);
282
- position: fixed;
283
- width: 100%;
284
- z-index: 100;
285
- border-bottom: 1px solid rgba(255, 255, 255, 0.1);
286
- }
287
-
288
- .nav-logo {
289
- display: flex;
290
- align-items: center;
291
- }
292
-
293
- .space-robot-logo {
294
- width: 30px;
295
- height: 30px;
296
- transition: transform 0.3s ease;
297
- }
298
-
299
- .space-robot-logo:hover {
300
- transform: rotate(15deg) scale(1.1);
301
- }
302
-
303
- .nav-menu {
304
- display: flex;
305
- list-style: none;
306
- }
307
-
308
- .nav-menu li {
309
- margin-left: 20px;
310
- }
311
-
312
- .nav-menu a {
313
- color: #f0f0ff;
314
- text-decoration: none;
315
- font-weight: 500;
316
- transition: color 0.3s ease;
317
- }
318
-
319
- .nav-menu a:hover,
320
- .nav-menu a.active {
321
- color: #c0c0c0;
322
- }
323
-
324
- .hamburger {
325
- display: none;
326
- cursor: pointer;
327
- background: none;
328
- border: none;
329
- flex-direction: column;
330
- gap: 4px;
331
- }
332
-
333
- .hamburger span {
334
- width: 20px;
335
- height: 2px;
336
- background: #f0f0ff;
337
- transition: all 0.3s ease;
338
- }
339
-
340
- #theme-toggle {
341
- background: rgba(255, 255, 255, 0.15);
342
- border: 1px solid rgba(255, 255, 255, 0.3);
343
- color: #f0f0ff;
344
- padding: 8px;
345
- border-radius: 8px;
346
- font-size: 14px;
347
- cursor: pointer;
348
- transition: background 0.3s ease, color 0.3s ease;
349
- }
350
-
351
- #theme-toggle option {
352
- background: #1a1a3d;
353
- color: #f0f0ff;
354
- }
355
-
356
- #theme-toggle:hover {
357
- background: rgba(255, 255, 255, 0.25);
358
- }
359
-
360
- /* Chatbot Section */
361
- .chatbot-section {
362
- min-height: 100vh;
363
- display: flex !important;
364
- align-items: center;
365
- justify-content: center;
366
- text-align: center;
367
- padding: 60px 20px;
368
- position: relative;
369
- visibility: visible;
370
- }
371
-
372
- #starfield-canvas {
373
- position: absolute;
374
- top: 0;
375
- left: 0;
376
- width: 100%;
377
- height: 100%;
378
- z-index: 1;
379
- opacity: 0.85;
380
- }
381
-
382
- .container {
383
- position: relative;
384
- z-index: 2;
385
- max-width: 1000px;
386
- margin: 0 auto;
387
- visibility: visible;
388
- }
389
-
390
- .header {
391
- font-family: 'Space Grotesk', sans-serif;
392
- font-size: 40px;
393
- font-weight: 600;
394
- margin-bottom: 15px;
395
- color: #f0f0ff;
396
- text-shadow: 0 0 5px rgba(192, 192, 192, 0.5);
397
- }
398
-
399
- .description {
400
- font-size: 18px;
401
- margin-bottom: 30px;
402
- color: #c0c0c0;
403
- }
404
-
405
- /* Chatbot Container */
406
- .chatbot-container {
407
- width: 80vw;
408
- height: 70vh;
409
- max-width: 900px;
410
- max-height: 600px;
411
- background: rgba(255, 255, 255, 0.05);
412
- backdrop-filter: blur(4px);
413
- border: 1px solid rgba(255, 255, 255, 0.1);
414
- border-radius: 15px;
415
- box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
416
- display: flex;
417
- flex-direction: column;
418
- transition: box-shadow 0.3s ease;
419
- }
420
-
421
- .chatbot-container:hover {
422
- box-shadow: 0 4px 20px rgba(192, 192, 192, 0.3);
423
- }
424
-
425
- .chat-header {
426
- padding: 15px;
427
- background: linear-gradient(90deg, #3c2f5f, #1a1a3d);
428
- color: #f0f0ff;
429
- font-family: 'Space Grotesk', sans-serif;
430
- font-size: 20px;
431
- font-weight: 600;
432
- border-radius: 15px 15px 0 0;
433
- display: flex;
434
- align-items: center;
435
- }
436
-
437
- .space-robot {
438
- width: 24px;
439
- height: 24px;
440
- background: radial-gradient(circle, #c0c0c0 40%, #3c2f5f 100%);
441
- border-radius: 50%;
442
- position: relative;
443
- animation: float 2s ease-in-out infinite;
444
- margin-right: 10px;
445
- }
446
-
447
- .space-robot::after {
448
- content: '';
449
- position: absolute;
450
- width: 6px;
451
- height: 6px;
452
- background: #f0f0ff;
453
- border-radius: 50%;
454
- top: 9px;
455
- left: 9px;
456
- }
457
-
458
- .space-robot.sending {
459
- animation: spin 0.5s linear;
460
- }
461
-
462
- @keyframes float {
463
- 0% { transform: translateY(0); }
464
- 50% { transform: translateY(-5px); }
465
- 100% { transform: translateY(0); }
466
- }
467
-
468
- @keyframes spin {
469
- 0% { transform: rotate(0deg); }
470
- 100% { transform: rotate(360deg); }
471
- }
472
-
473
- #chat-output {
474
- flex: 1;
475
- padding: 20px;
476
- overflow-y: auto;
477
- background: rgba(0, 0, 0, 0.3);
478
- visibility: visible;
479
- min-height: 200px;
480
- display: block;
481
- text-align: left;
482
- }
483
-
484
- #chat-output p {
485
- margin-bottom: 15px;
486
- font-size: 15px;
487
- line-height: 1.5;
488
- opacity: 1;
489
- position: relative;
490
- display: block;
491
- visibility: visible;
492
- }
493
-
494
- #chat-output p:nth-child(odd) {
495
- color: #c0c0c0;
496
- background: rgba(192, 192, 192, 0.1);
497
- padding: 10px 15px;
498
- border-radius: 12px 12px 0 12px;
499
- max-width: 70%;
500
- margin-left: auto;
501
- text-align: right;
502
- }
503
-
504
- #chat-output p:nth-child(even) {
505
- color: #f0f0ff;
506
- background: rgba(255, 255, 255, 0.05);
507
- padding: 10px 15px;
508
- border-radius: 12px 12px 12px 0;
509
- max-width: 70%;
510
- }
511
-
512
- .chat-input-wrapper {
513
- display: flex;
514
- padding: 15px;
515
- background: rgba(255, 255, 255, 0.05);
516
- border-top: 1px solid rgba(255, 255, 255, 0.1);
517
- border-radius: 0 0 15px 15px;
518
- }
519
-
520
- #user-input {
521
- flex: 1;
522
- padding: 12px;
523
- background: transparent;
524
- border: 1px solid rgba(255, 255, 255, 0.2);
525
- border-radius: 8px;
526
- color: #f0f0ff;
527
- font-size: 15px;
528
- outline: none;
529
- transition: border-color 0.3s ease, box-shadow 0.3s ease;
530
- }
531
-
532
- #user-input:focus {
533
- border-color: #c0c0c0;
534
- box-shadow: 0 0 5px rgba(192, 192, 192, 0.5);
535
- }
536
-
537
- #user-input::placeholder {
538
- color: rgba(255, 255, 255, 0.4);
539
- }
540
-
541
- #sendButton {
542
- background: linear-gradient(45deg, #3c2f5f, #c0c0c0);
543
- color: #0a0a23;
544
- border: none;
545
- border-radius: 8px;
546
- padding: 12px 15px;
547
- margin-left: 10px;
548
- cursor: pointer;
549
- font-size: 16px;
550
- transition: transform 0.2s ease;
551
- pointer-events: auto;
552
- }
553
-
554
- #sendButton:hover {
555
- transform: scale(1.05);
556
- }
557
-
558
- /* Glassmorphism */
559
- .glassmorphism {
560
- background: rgba(255, 255, 255, 0.05);
561
- backdrop-filter: blur(4px);
562
- border: 1px solid rgba(255, 255, 255, 0.1);
563
- }
564
-
565
- /* Footer */
566
- .footer {
567
- padding: 15px;
568
- text-align: center;
569
- background: rgba(255, 255, 255, 0.05);
570
- border-top: 1px solid rgba(255, 255, 255, 0.1);
571
- color: #c0c0c0;
572
- }
573
-
574
- /* Theme Styles */
575
- body.starlight {
576
- background: linear-gradient(135deg, #d9d9e6 0%, #f0f0f5 100%);
577
- color: #1a1a3d;
578
- }
579
-
580
- body.starlight .navbar,
581
- body.starlight .chatbot-container,
582
- body.starlight .footer {
583
- background: rgba(0, 0, 0, 0.05);
584
- border-color: rgba(0, 0, 0, 0.1);
585
- }
586
-
587
- body.starlight .header {
588
- color: #3c2f5f;
589
- text-shadow: none;
590
- }
591
-
592
- body.starlight .nav-menu a {
593
- color: #1a1a3d;
594
- }
595
-
596
- body.starlight .nav-menu a:hover,
597
- body.starlight .nav-menu a.active,
598
- body.starlight #chat-output p:nth-child(odd) {
599
- color: #3c2f5f;
600
- }
601
-
602
- body.starlight .chat-header,
603
- body.starlight #sendButton {
604
- background: linear-gradient(45deg, #3c2f5f, #c0c0c0);
605
- color: #f0f0ff;
606
- }
607
-
608
- body.starlight .space-robot {
609
- background: radial-gradient(circle, #c0c0c0 40%, #3c2f5f 100%);
610
- }
611
-
612
- body.starlight #user-input {
613
- color: #1a1a3d;
614
- border-color: rgba(0, 0, 0, 0.2);
615
- }
616
-
617
- body.starlight #user-input:focus {
618
- border-color: #3c2f5f;
619
- box-shadow: 0 0 5px rgba(60, 47, 95, 0.5);
620
- }
621
-
622
- body.starlight #user-input::placeholder {
623
- color: rgba(0, 0, 0, 0.4);
624
- }
625
-
626
- body.starlight .footer {
627
- color: #3c2f5f;
628
- }
629
-
630
- body.starlight #theme-toggle {
631
- background: rgba(0, 0, 0, 0.15);
632
- color: #3c2f5f;
633
- }
634
-
635
- body.starlight #theme-toggle option {
636
- background: #f0f0f5;
637
- color: #1a1a3d;
638
- }
639
-
640
- body.void {
641
- background: linear-gradient(135deg, #000014 0%, #0a0a23 100%);
642
- }
643
-
644
- body.void .header {
645
- color: #c0c0c0;
646
- text-shadow: 0 0 5px rgba(192, 192, 192, 0.7);
647
- }
648
-
649
- body.void .nav-menu a:hover,
650
- body.void .nav-menu a.active,
651
- body.void #chat-output p:nth-child(odd) {
652
- color: #c0c0c0;
653
- }
654
-
655
- body.void .chat-header,
656
- body.void #sendButton {
657
- background: linear-gradient(45deg, #0a0a23, #c0c0c0);
658
- }
659
-
660
- body.void #user-input:focus {
661
- border-color: #c0c0c0;
662
- box-shadow: 0 0 5px rgba(192, 192, 192, 0.7);
663
- }
664
-
665
- body.void #theme-toggle {
666
- background: rgba(255, 255, 255, 0.15);
667
- color: #f0f0ff;
668
- }
669
-
670
- body.void #theme-toggle option {
671
- background: #0a0a23;
672
- color: #f0f0ff;
673
- }
674
-
675
- /* Responsive Design */
676
- @media (max-width: 768px) {
677
- .nav-menu {
678
- display: none;
679
- position: absolute;
680
- top: 70px;
681
- left: 0;
682
- width: 100%;
683
- background: rgba(255, 255, 255, 0.05);
684
- backdrop-filter: blur(4px);
685
- flex-direction: column;
686
- padding: 15px;
687
- }
688
-
689
- .nav-menu.active {
690
- display: flex;
691
- }
692
-
693
- .hamburger {
694
- display: flex;
695
- }
696
-
697
- .header {
698
- font-size: 32px;
699
- }
700
-
701
- .chatbot-container {
702
- width: 90vw;
703
- height: 65vh;
704
- max-height: 550px;
705
- }
706
-
707
- .space-robot-logo {
708
- width: 24px;
709
- height: 24px;
710
- }
711
- }
712
- </style>
713
-
714
- <!-- Embedded JavaScript -->
715
- <script>
716
- console.log('Script loaded');
717
-
718
- try {
719
- /* Utility Functions */
720
- const utils = {
721
- sanitizeInput(input) {
722
- console.log('Sanitizing input:', input);
723
- return input.replace(/</g, '<').replace(/>/g, '>');
724
- },
725
- debounce(func, wait) {
726
- let timeout;
727
- return function (...args) {
728
- clearTimeout(timeout);
729
- timeout = setTimeout(() => func.apply(this, args), wait);
730
- };
731
- },
732
- formatTimestamp() {
733
- const now = new Date();
734
- return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
735
- },
736
- displayError(message) {
737
- const chatOutput = document.getElementById('chat-output');
738
- if (chatOutput) {
739
- const errorMsg = document.createElement('p');
740
- errorMsg.innerHTML = `<strong>AthleteGuard AI:</strong> Client error: ${utils.sanitizeInput(message)}. Please refresh and try again. <small>(${utils.formatTimestamp()})</small>`;
741
- chatOutput.appendChild(errorMsg);
742
- chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
743
- console.log('Displayed client error in chat');
744
- }
745
- },
746
- };
747
-
748
- /* Loader Fade-out */
749
- function hideLoader() {
750
- try {
751
- console.log('Attempting to hide loader');
752
- const loader = document.querySelector('.loader-wrapper');
753
- if (!loader) {
754
- console.error('Loader not found');
755
- return;
756
- }
757
- loader.classList.add('fade-out');
758
- console.log('Added fade-out class to loader');
759
- setTimeout(() => {
760
- loader.style.display = 'none';
761
- console.log('Loader hidden');
762
- document.body.style.visibility = 'visible';
763
- const chatbotSection = document.querySelector('.chatbot-section');
764
- if (chatbotSection) {
765
- chatbotSection.style.display = 'flex';
766
- console.log('Chatbot section set to visible');
767
- }
768
- }, 500);
769
- } catch (error) {
770
- console.error('Loader hide error:', error);
771
- utils.displayError(error.message);
772
- }
773
- }
774
-
775
- /* Nebula Starfield Background */
776
- function setupStarfield() {
777
- try {
778
- console.log('Initializing starfield');
779
- const canvas = document.getElementById('starfield-canvas');
780
- if (!canvas) {
781
- console.error('Starfield canvas not found');
782
- return;
783
- }
784
- const ctx = canvas.getContext('2d');
785
- if (!ctx) {
786
- console.error('Canvas context not available');
787
- return;
788
- }
789
-
790
- function setCanvasSize() {
791
- const dpr = window.devicePixelRatio || 1;
792
- canvas.width = window.innerWidth * dpr;
793
- canvas.height = window.innerHeight * dpr;
794
- ctx.scale(dpr, dpr);
795
- }
796
- setCanvasSize();
797
-
798
- let starsArray = [];
799
- let shootingStars = [];
800
- let nebulae = [];
801
- const numberOfStars = 150;
802
- const numberOfShootingStars = 2;
803
- const numberOfNebulae = 3;
804
-
805
- class Star {
806
- constructor(x, y, size, speed, color, layer) {
807
- this.x = x;
808
- this.y = y;
809
- this.size = size;
810
- this.speed = speed;
811
- this.color = color;
812
- this.layer = layer;
813
- this.opacity = Math.random() * 0.5 + 0.5;
814
- }
815
- update() {
816
- this.x -= this.speed * this.layer;
817
- if (this.x < 0) this.x = canvas.width / (window.devicePixelRatio || 1);
818
- this.opacity = Math.sin(Date.now() * 0.001 + this.x) * 0.3 + 0.5;
819
- }
820
- draw() {
821
- ctx.globalAlpha = this.opacity;
822
- ctx.fillStyle = this.color;
823
- ctx.beginPath();
824
- ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
825
- ctx.fill();
826
- ctx.globalAlpha = 1;
827
- }
828
- }
829
-
830
- class ShootingStar {
831
- constructor() {
832
- this.x = canvas.width / (window.devicePixelRatio || 1);
833
- this.y = Math.random() * (canvas.height / (window.devicePixelRatio || 1));
834
- this.speedX = -(Math.random() * 5 + 5);
835
- this.speedY = Math.random() * 2 - 1;
836
- this.length = Math.random() * 50 + 20;
837
- this.opacity = 1;
838
- }
839
- update() {
840
- this.x += this.speedX;
841
- this.y += this.speedY;
842
- this.opacity -= 0.02;
843
- if (this.opacity <= 0) {
844
- this.x = canvas.width / (window.devicePixelRatio || 1);
845
- this.y = Math.random() * (canvas.height / (window.devicePixelRatio || 1));
846
- this.speedX = -(Math.random() * 5 + 5);
847
- this.speedY = Math.random() * 2 - 1;
848
- this.length = Math.random() * 50 + 20;
849
- this.opacity = 1;
850
- }
851
- }
852
- draw() {
853
- ctx.globalAlpha = this.opacity;
854
- ctx.strokeStyle = '#f0f0ff';
855
- ctx.lineWidth = 2;
856
- ctx.beginPath();
857
- ctx.moveTo(this.x, this.y);
858
- ctx.lineTo(this.x + this.length * this.speedX / 10, this.y + this.length * this.speedY / 10);
859
- ctx.stroke();
860
- ctx.globalAlpha = 1;
861
- }
862
- }
863
-
864
- class Nebula {
865
- constructor(x, y, size, color) {
866
- this.x = x;
867
- this.y = y;
868
- this.size = size;
869
- this.color = color;
870
- this.opacity = Math.random() * 0.2 + 0.1;
871
- }
872
- update() {
873
- this.opacity = Math.sin(Date.now() * 0.0005 + this.x) * 0.1 + 0.2;
874
- }
875
- draw() {
876
- ctx.globalAlpha = this.opacity;
877
- ctx.fillStyle = this.color;
878
- ctx.beginPath();
879
- ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
880
- ctx.filter = 'blur(20px)';
881
- ctx.fill();
882
- ctx.filter = 'none';
883
- ctx.globalAlpha = 1;
884
- }
885
- }
886
-
887
- function initStarfield() {
888
- starsArray = [];
889
- shootingStars = [];
890
- nebulae = [];
891
- const colors = ['#f0f0ff', '#c0c0c0', '#3c2f5f'];
892
- const nebulaColors = ['rgba(60, 47, 95, 0.5)', 'rgba(26, 26, 61, 0.5)'];
893
- for (let i = 0; i < numberOfStars; i++) {
894
- const x = Math.random() * canvas.width / (window.devicePixelRatio || 1);
895
- const y = Math.random() * canvas.height / (window.devicePixelRatio || 1);
896
- const size = Math.random() * (i % 2 === 0 ? 1 : 2);
897
- const speed = Math.random() * 0.5 + 0.1;
898
- const color = colors[Math.floor(Math.random() * colors.length)];
899
- const layer = Math.random() * 0.5 + 0.5;
900
- starsArray.push(new Star(x, y, size, speed, color, layer));
901
- }
902
- for (let i = 0; i < numberOfShootingStars; i++) {
903
- shootingStars.push(new ShootingStar());
904
- }
905
- for (let i = 0; i < numberOfNebulae; i++) {
906
- const x = Math.random() * canvas.width / (window.devicePixelRatio || 1);
907
- const y = Math.random() * canvas.height / (window.devicePixelRatio || 1);
908
- const size = Math.random() * 100 + 50;
909
- const color = nebulaColors[Math.floor(Math.random() * nebulaColors.length)];
910
- nebulae.push(new Nebula(x, y, size, color));
911
- }
912
- }
913
-
914
- function animateStarfield() {
915
- ctx.clearRect(0, 0, canvas.width, canvas.height);
916
- nebulae.forEach(nebula => {
917
- nebula.update();
918
- nebula.draw();
919
- });
920
- starsArray.forEach(star => {
921
- star.update();
922
- star.draw();
923
- });
924
- shootingStars.forEach(star => {
925
- star.update();
926
- star.draw();
927
- });
928
- requestAnimationFrame(animateStarfield);
929
- }
930
-
931
- initStarfield();
932
- animateStarfield();
933
-
934
- window.addEventListener('resize', utils.debounce(() => {
935
- setCanvasSize();
936
- initStarfield();
937
- }, 200));
938
- } catch (error) {
939
- console.error('Starfield setup error:', error);
940
- utils.displayError(error.message);
941
- }
942
- }
943
-
944
- /* Chatbot Setup */
945
- function setupChatbot() {
946
- try {
947
- const chatOutput = document.getElementById('chat-output');
948
- const userInput = document.getElementById('user-input');
949
- const sendButton = document.getElementById('sendButton');
950
-
951
- console.log('Checking DOM elements:', { chatOutput, userInput, sendButton });
952
- if (!chatOutput || !userInput || !sendButton) {
953
- console.error('Missing DOM elements:', { chatOutput, userInput, sendButton });
954
- utils.displayError('Chatbot initialization failed: Missing DOM elements');
955
- return false;
956
- }
957
-
958
- userInput.focus();
959
- console.log('User input focused');
960
-
961
- let isProcessing = false;
962
- const processedMessages = new Set();
963
-
964
- function clearInput() {
965
- console.log('Clearing input');
966
- userInput.value = '';
967
- userInput.focus();
968
- userInput.dispatchEvent(new Event('input'));
969
- setTimeout(() => {
970
- if (userInput.value !== '') {
971
- console.warn('Fallback: Forcing input clear');
972
- userInput.value = '';
973
- }
974
- }, 100);
975
- }
976
-
977
- const sendMessage = async (message, eventType, messageId) => {
978
- console.log(`Processing message [${messageId}] (via ${eventType}):`, message);
979
- if (isProcessing || processedMessages.has(messageId)) {
980
- console.log(`Skipping duplicate message [${messageId}]: isProcessing=${isProcessing}`);
981
- return;
982
- }
983
- processedMessages.add(messageId);
984
- isProcessing = true;
985
-
986
- const input = message.trim();
987
- console.log('Validating message:', input);
988
- if (!input || typeof input !== 'string' || input.length === 0) {
989
- console.warn(`Invalid input [${messageId}], skipping request`);
990
- isProcessing = false;
991
- processedMessages.delete(messageId);
992
- return;
993
- }
994
-
995
- try {
996
- userInput.disabled = true;
997
- sendButton.disabled = true;
998
- const spaceRobot = document.querySelector('.space-robot');
999
- if (spaceRobot) spaceRobot.classList.add('sending');
1000
-
1001
- clearInput();
1002
-
1003
- const chatOutput = document.getElementById('chat-output');
1004
- if (!chatOutput) {
1005
- throw new Error(`chat-output not found during sendMessage [${messageId}]`);
1006
- }
1007
-
1008
- const userMsg = document.createElement('p');
1009
- userMsg.innerHTML = `<strong>You:</strong> ${utils.sanitizeInput(input)} <small>(${utils.formatTimestamp()})</small>`;
1010
- console.log(`Appending user message [${messageId}] to chat-output`);
1011
- chatOutput.appendChild(userMsg);
1012
- chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
1013
-
1014
- const payload = { message: input };
1015
- console.log(`Fetch initiated [${messageId}]:`, payload);
1016
- const response = await fetch('http://127.0.0.1:8000/chat', {
1017
- method: 'POST',
1018
- headers: { 'Content-Type': 'application/json' },
1019
- body: JSON.stringify(payload),
1020
- signal: AbortSignal.timeout(5000),
1021
- });
1022
- console.log(`Fetch completed [${messageId}]:`, { status: response.status, ok: response.ok });
1023
-
1024
- if (!response.ok) {
1025
- throw new Error(`HTTP error: ${response.status}`);
1026
- }
1027
-
1028
- const data = await response.json();
1029
- console.log(`Response data [${messageId}]:`, data);
1030
-
1031
- if (data.error) {
1032
- throw new Error(data.error);
1033
- }
1034
-
1035
- if (!data.response) {
1036
- throw new Error('No response field in data');
1037
- }
1038
-
1039
- const botMsg = document.createElement('p');
1040
- botMsg.innerHTML = `<div class="space-robot"></div><strong>AthleteGuard AI:</strong> ${utils.sanitizeInput(data.response)} <small>(${utils.formatTimestamp()})</small>`;
1041
- console.log(`Appending bot message [${messageId}] to chat-output`);
1042
- chatOutput.appendChild(botMsg);
1043
- chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
1044
-
1045
- if (spaceRobot) spaceRobot.classList.remove('sending');
1046
- } catch (error) {
1047
- console.error(`Chat error [${messageId}]:`, error.message);
1048
- const chatOutput = document.getElementById('chat-output');
1049
- if (chatOutput) {
1050
- const errorMsg = document.createElement('p');
1051
- errorMsg.innerHTML = `<div class="space-robot"></div><strong>AthleteGuard AI:</strong> Error: ${utils.sanitizeInput(error.message)}. Please try again. <small>(${utils.formatTimestamp()})</small>`;
1052
- console.log(`Appending error message [${messageId}] to chat-output`);
1053
- chatOutput.appendChild(errorMsg);
1054
- chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
1055
- }
1056
- clearInput();
1057
- } finally {
1058
- userInput.disabled = false;
1059
- sendButton.disabled = false;
1060
- clearInput();
1061
- setTimeout(() => {
1062
- isProcessing = false;
1063
- console.log(`Reset isProcessing [${messageId}]`);
1064
- }, 500);
1065
- }
1066
- };
1067
-
1068
- console.log('Removing old listeners');
1069
- const oldKeypress = userInput.__keypressHandler;
1070
- const oldClick = sendButton.__clickHandler;
1071
- if (oldKeypress) userInput.removeEventListener('keypress', oldKeypress);
1072
- if (oldClick) sendButton.removeEventListener('click', oldClick);
1073
-
1074
- const handleKeypress = (e) => {
1075
- console.log('Keypress event:', e.key);
1076
- if (e.key === 'Enter') {
1077
- e.preventDefault();
1078
- e.stopPropagation();
1079
- const messageId = Date.now() + '-keypress';
1080
- console.log(`Enter key pressed, sending message [${messageId}]`);
1081
- sendMessage(userInput.value, 'keypress', messageId);
1082
- }
1083
- };
1084
-
1085
- const handleClick = (e) => {
1086
- e.preventDefault();
1087
- e.stopPropagation();
1088
- const messageId = Date.now() + '-click';
1089
- console.log(`Send button clicked, sending message [${messageId}]`);
1090
- sendMessage(userInput.value, 'click', messageId);
1091
- };
1092
-
1093
- userInput.__keypressHandler = handleKeypress;
1094
- sendButton.__clickHandler = handleClick;
1095
-
1096
- console.log('Attaching new listeners');
1097
- userInput.addEventListener('keypress', handleKeypress);
1098
- sendButton.addEventListener('click', handleClick);
1099
-
1100
- console.log('Event listeners attached');
1101
- return true;
1102
- } catch (error) {
1103
- console.error('Chatbot setup error:', error);
1104
- utils.displayError(error.message);
1105
- return false;
1106
- }
1107
- }
1108
-
1109
- /* Initial Setup */
1110
- document.addEventListener('DOMContentLoaded', () => {
1111
- console.log('DOM content loaded');
1112
- try {
1113
- setTimeout(hideLoader, 3500);
1114
- setTimeout(() => {
1115
- const loader = document.querySelector('.loader-wrapper');
1116
- if (loader && loader.style.display !== 'none') {
1117
- console.warn('Fallback: Forcing loader hide');
1118
- loader.style.display = 'none';
1119
- document.body.style.visibility = 'visible';
1120
- const chatbotSection = document.querySelector('.chatbot-section');
1121
- if (chatbotSection) chatbotSection.style.display = 'flex';
1122
- }
1123
- }, 4500);
1124
-
1125
- setupStarfield();
1126
-
1127
- const hamburger = document.querySelector('.hamburger');
1128
- const navMenu = document.querySelector('.nav-menu');
1129
- if (hamburger && navMenu) {
1130
- hamburger.addEventListener('click', () => {
1131
- navMenu.classList.toggle('active');
1132
- hamburger.classList.toggle('active');
1133
- console.log('Hamburger menu toggled');
1134
- });
1135
- }
1136
-
1137
- const themeToggle = document.getElementById('theme-toggle');
1138
- if (themeToggle) {
1139
- themeToggle.addEventListener('change', () => {
1140
- document.body.className = themeToggle.value;
1141
- console.log('Theme changed to:', themeToggle.value);
1142
- });
1143
- }
1144
-
1145
- if (!setupChatbot()) {
1146
- console.warn('Chatbot setup failed, retrying in 500ms');
1147
- setTimeout(setupChatbot, 500);
1148
- }
1149
- } catch (error) {
1150
- console.error('DOMContentLoaded error:', error);
1151
- utils.displayError(error.message);
1152
- }
1153
- });
1154
-
1155
- /* Fallback Setup */
1156
- setTimeout(() => {
1157
- console.log('Running fallback chatbot setup');
1158
- setupChatbot();
1159
- }, 1000);
1160
- } catch (error) {
1161
- console.error('Global script error:', error);
1162
- utils.displayError(error.message);
1163
- }
1164
- </script>
1165
- </body>
1166
  </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>AthleteGuard AI</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Outfit:wght@300;400;500;600&display=swap" rel="stylesheet" />
8
+ </head>
9
+ <body class="nebula">
10
+ <!-- Loader -->
11
+ <div class="loader-wrapper">
12
+ <div class="loader">
13
+ <div class="space-robot-loader">
14
+ <div class="robot-core"></div>
15
+ <div class="robot-panel panel-1"></div>
16
+ <div class="robot-panel panel-2"></div>
17
+ <div class="robot-eye"></div>
18
+ <div class="shockwave"></div>
19
+ <div class="pulse-ring"></div>
20
+ </div>
21
+ <div class="progress-bar">
22
+ <div class="progress-arc"></div>
23
+ </div>
24
+ <span>Initializing AI...</span>
25
+ </div>
26
+ </div>
27
+
28
+ <!-- Header -->
29
+ <header>
30
+ <div class="navbar">
31
+ <div class="nav-logo">
32
+ <svg class="space-robot-logo" viewBox="0 0 40 40" aria-label=".team
33
+ Space Robot Logo">
34
+ <circle cx="20" cy="20" r="15" fill="url(#robotGradient)" />
35
+ <circle cx="15" cy="15" r="4" fill="#f0f0ff" />
36
+ <path d="M10 20 L8 18 M30 20 L32 18" stroke="#c0c0c0" stroke-width="1" />
37
+ <circle cx="20" cy="20" r="18" fill="none" stroke="#c0c0c0" stroke-width="0.5" opacity="0.5" />
38
+ <defs>
39
+ <radialGradient id="robotGradient" cx="0.5" cy="0.5" r="0.5">
40
+ <stop offset="0%" stop-color="#c0c0c0" />
41
+ <stop offset="100%" stop-color="#3c2f5f" />
42
+ </radialGradient>
43
+ </defs>
44
+ </svg>
45
+ </div>
46
+ <div class="hamburger" aria-label="Toggle navigation">
47
+ <span></span>
48
+ <span></span>
49
+ <span></span>
50
+ </div>
51
+ <ul class="nav-menu">
52
+ <li><a href="index.html">Home</a></li>
53
+ <li><a href="index.html#features">Features</a></li>
54
+ <li><a href="calculator.html">Calculator</a></li>
55
+ <li><a href="about.html">About</a></li>
56
+ <li><a href="chatbot.html" class="active">Chatbot</a></li>
57
+ <li><a href="index.html#faq">FAQ</a></li>
58
+ </ul>
59
+ <select id="theme-toggle" aria-label="Select theme">
60
+ <option value="nebula">Nebula</option>
61
+ <option value="starlight">Starlight</option>
62
+ <option value="void">Void</option>
63
+ </select>
64
+ </div>
65
+ </header>
66
+
67
+ <!-- Chatbot Section -->
68
+ <section class="section chatbot-section">
69
+ <canvas id="starfield-canvas"></canvas>
70
+ <div class="container">
71
+ <h1 class="header">AthleteGuard AI</h1>
72
+ <p class="description">Your intelligent assistant for training and injury prevention.</p>
73
+ <div class="chatbot-container glassmorphism" id="chatbotContainer">
74
+ <div class="chat-header">
75
+ <div class="space-robot"></div>
76
+ <span>AthleteGuard AI</span>
77
+ </div>
78
+ <div class="chat-body" id="chat-output"></div>
79
+ <div class="chat-input-wrapper">
80
+ <input type="text" id="user-input" placeholder="Ask about training, nutrition, or injuries..." aria-label="Chat input" />
81
+ <button id="sendButton" aria-label="Send message">➤</button>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </section>
86
+
87
+ <!-- Footer -->
88
+ <footer class="footer">
89
+ <div class="container">
90
+ <p>© 2025 AthleteGuard AI. All rights reserved.</p>
91
+ </div>
92
+ </footer>
93
+
94
+ <!-- Embedded CSS -->
95
+ <style>
96
+ * {
97
+ margin: 0;
98
+ padding: 0;
99
+ box-sizing: border-box;
100
+ }
101
+
102
+ body {
103
+ font-family: 'Outfit', sans-serif;
104
+ background: linear-gradient(135deg, #0a0a23 0%, #1a1a3d 100%);
105
+ color: #f0f0ff;
106
+ line-height: 1.6;
107
+ overflow-x: hidden;
108
+ visibility: visible;
109
+ }
110
+
111
+ /* Loader */
112
+ .loader-wrapper {
113
+ position: fixed;
114
+ top: 0;
115
+ left: 0;
116
+ width: 100%;
117
+ height: 100%;
118
+ background: #0a0a23;
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ z-index: 1000;
123
+ transition: opacity 0.5s ease;
124
+ }
125
+
126
+ .loader {
127
+ display: flex;
128
+ flex-direction: column;
129
+ align-items: center;
130
+ gap: 15px;
131
+ }
132
+
133
+ .space-robot-loader {
134
+ width: 80px;
135
+ height: 80px;
136
+ position: relative;
137
+ animation: loaderAssemble 3s linear forwards;
138
+ }
139
+
140
+ .robot-core {
141
+ width: 50px;
142
+ height: 50px;
143
+ background: radial-gradient(circle, #c0c0c0 50%, #3c2f5f 100%);
144
+ border-radius: 50%;
145
+ position: absolute;
146
+ top: 15px;
147
+ left: 15px;
148
+ opacity: 0;
149
+ animation: coreFade 1s ease forwards;
150
+ }
151
+
152
+ .robot-panel {
153
+ width: 20px;
154
+ height: 20px;
155
+ background: #c0c0c0;
156
+ position: absolute;
157
+ opacity: 0;
158
+ }
159
+
160
+ .panel-1 {
161
+ top: 10px;
162
+ left: 10px;
163
+ transform: rotate(45deg);
164
+ animation: panelSlide 1s ease 1s forwards;
165
+ }
166
+
167
+ .panel-2 {
168
+ bottom: 10px;
169
+ right: 10px;
170
+ transform: rotate(-45deg);
171
+ animation: panelSlide 1s ease 1.5s forwards;
172
+ }
173
+
174
+ .robot-eye {
175
+ width: 12px;
176
+ height: 12px;
177
+ background: #f0f0ff;
178
+ border-radius: 50%;
179
+ position: absolute;
180
+ top: 25px;
181
+ left: 25px;
182
+ opacity: 0;
183
+ animation: eyeGlow 0.5s ease 2s forwards;
184
+ }
185
+
186
+ .shockwave {
187
+ width: 100px;
188
+ height: 100px;
189
+ border: 2px solid #c0c0c0;
190
+ border-radius: 50%;
191
+ position: absolute;
192
+ top: -10px;
193
+ left: -10px;
194
+ opacity: 0;
195
+ animation: shockwaveExpand 0.8s ease 2.5s infinite;
196
+ }
197
+
198
+ .pulse-ring {
199
+ width: 120px;
200
+ height: 120px;
201
+ border: 3px solid rgba(192, 192, 192, 0.3);
202
+ border-radius: 50%;
203
+ position: absolute;
204
+ top: -20px;
205
+ left: -20px;
206
+ opacity: 0;
207
+ animation: pulseRing 1.5s ease 3s infinite;
208
+ }
209
+
210
+ @keyframes loaderAssemble {
211
+ 0% { transform: scale(0.5) rotate(0deg); }
212
+ 100% { transform: scale(1) rotate(360deg); }
213
+ }
214
+
215
+ @keyframes coreFade {
216
+ 0% { opacity: 0; transform: scale(0.5); }
217
+ 100% { opacity: 1; transform: scale(1); }
218
+ }
219
+
220
+ @keyframes panelSlide {
221
+ 0% { opacity: 0; transform: translateX(-20px) rotate(45deg); }
222
+ 100% { opacity: 1; transform: translateX(0) rotate(45deg); }
223
+ }
224
+
225
+ @keyframes eyeGlow {
226
+ 0% { opacity: 0; transform: scale(0.5); }
227
+ 100% { opacity: 1; transform: scale(1); }
228
+ }
229
+
230
+ @keyframes shockwaveExpand {
231
+ 0% { opacity: 0.5; transform: scale(0.8); }
232
+ 100% { opacity: 0; transform: scale(1.8); }
233
+ }
234
+
235
+ @keyframes pulseRing {
236
+ 0% { opacity: 0.3; transform: scale(0.7); }
237
+ 50% { opacity: 0.6; transform: scale(1.2); }
238
+ 100% { opacity: 0; transform: scale(1.5); }
239
+ }
240
+
241
+ .progress-bar {
242
+ width: 120px;
243
+ height: 12px;
244
+ background: rgba(255, 255, 255, 0.1);
245
+ border-radius: 6px;
246
+ overflow: hidden;
247
+ position: relative;
248
+ }
249
+
250
+ .progress-arc {
251
+ width: 0;
252
+ height: 100%;
253
+ background: linear-gradient(45deg, #3c2f5f, #c0c0c0);
254
+ animation: progressFill 3.5s ease-in-out forwards;
255
+ }
256
+
257
+ @keyframes progressFill {
258
+ 0% { width: 0; }
259
+ 100% { width: 100%; }
260
+ }
261
+
262
+ .loader span {
263
+ font-family: 'Space Grotesk', sans-serif;
264
+ font-size: 18px;
265
+ color: #f0f0ff;
266
+ letter-spacing: 1px;
267
+ }
268
+
269
+ .loader-wrapper.fade-out {
270
+ opacity: 0;
271
+ pointer-events: none;
272
+ }
273
+
274
+ /* Header */
275
+ .navbar {
276
+ display: flex;
277
+ justify-content: space-between;
278
+ align-items: center;
279
+ padding: 15px 30px;
280
+ background: rgba(255, 255, 255, 0.05);
281
+ backdrop-filter: blur(4px);
282
+ position: fixed;
283
+ width: 100%;
284
+ z-index: 100;
285
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
286
+ }
287
+
288
+ .nav-logo {
289
+ display: flex;
290
+ align-items: center;
291
+ }
292
+
293
+ .space-robot-logo {
294
+ width: 30px;
295
+ height: 30px;
296
+ transition: transform 0.3s ease;
297
+ }
298
+
299
+ .space-robot-logo:hover {
300
+ transform: rotate(15deg) scale(1.1);
301
+ }
302
+
303
+ .nav-menu {
304
+ display: flex;
305
+ list-style: none;
306
+ }
307
+
308
+ .nav-menu li {
309
+ margin-left: 20px;
310
+ }
311
+
312
+ .nav-menu a {
313
+ color: #f0f0ff;
314
+ text-decoration: none;
315
+ font-weight: 500;
316
+ transition: color 0.3s ease;
317
+ }
318
+
319
+ .nav-menu a:hover,
320
+ .nav-menu a.active {
321
+ color: #c0c0c0;
322
+ }
323
+
324
+ .hamburger {
325
+ display: none;
326
+ cursor: pointer;
327
+ background: none;
328
+ border: none;
329
+ flex-direction: column;
330
+ gap: 4px;
331
+ }
332
+
333
+ .hamburger span {
334
+ width: 20px;
335
+ height: 2px;
336
+ background: #f0f0ff;
337
+ transition: all 0.3s ease;
338
+ }
339
+
340
+ #theme-toggle {
341
+ background: rgba(255, 255, 255, 0.15);
342
+ border: 1px solid rgba(255, 255, 255, 0.3);
343
+ color: #f0f0ff;
344
+ padding: 8px;
345
+ border-radius: 8px;
346
+ font-size: 14px;
347
+ cursor: pointer;
348
+ transition: background 0.3s ease, color 0.3s ease;
349
+ }
350
+
351
+ #theme-toggle option {
352
+ background: #1a1a3d;
353
+ color: #f0f0ff;
354
+ }
355
+
356
+ #theme-toggle:hover {
357
+ background: rgba(255, 255, 255, 0.25);
358
+ }
359
+
360
+ /* Chatbot Section */
361
+ .chatbot-section {
362
+ min-height: 100vh;
363
+ display: flex !important;
364
+ align-items: center;
365
+ justify-content: center;
366
+ text-align: center;
367
+ padding: 60px 20px;
368
+ position: relative;
369
+ visibility: visible;
370
+ }
371
+
372
+ #starfield-canvas {
373
+ position: absolute;
374
+ top: 0;
375
+ left: 0;
376
+ width: 100%;
377
+ height: 100%;
378
+ z-index: 1;
379
+ opacity: 0.85;
380
+ }
381
+
382
+ .container {
383
+ position: relative;
384
+ z-index: 2;
385
+ max-width: 1000px;
386
+ margin: 0 auto;
387
+ visibility: visible;
388
+ }
389
+
390
+ .header {
391
+ font-family: 'Space Grotesk', sans-serif;
392
+ font-size: 40px;
393
+ font-weight: 600;
394
+ margin-bottom: 15px;
395
+ color: #f0f0ff;
396
+ text-shadow: 0 0 5px rgba(192, 192, 192, 0.5);
397
+ }
398
+
399
+ .description {
400
+ font-size: 18px;
401
+ margin-bottom: 30px;
402
+ color: #c0c0c0;
403
+ }
404
+
405
+ /* Chatbot Container */
406
+ .chatbot-container {
407
+ width: 80vw;
408
+ height: 70vh;
409
+ max-width: 900px;
410
+ max-height: 600px;
411
+ background: rgba(255, 255, 255, 0.05);
412
+ backdrop-filter: blur(4px);
413
+ border: 1px solid rgba(255, 255, 255, 0.1);
414
+ border-radius: 15px;
415
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
416
+ display: flex;
417
+ flex-direction: column;
418
+ transition: box-shadow 0.3s ease;
419
+ }
420
+
421
+ .chatbot-container:hover {
422
+ box-shadow: 0 4px 20px rgba(192, 192, 192, 0.3);
423
+ }
424
+
425
+ .chat-header {
426
+ padding: 15px;
427
+ background: linear-gradient(90deg, #3c2f5f, #1a1a3d);
428
+ color: #f0f0ff;
429
+ font-family: 'Space Grotesk', sans-serif;
430
+ font-size: 20px;
431
+ font-weight: 600;
432
+ border-radius: 15px 15px 0 0;
433
+ display: flex;
434
+ align-items: center;
435
+ }
436
+
437
+ .space-robot {
438
+ width: 24px;
439
+ height: 24px;
440
+ background: radial-gradient(circle, #c0c0c0 40%, #3c2f5f 100%);
441
+ border-radius: 50%;
442
+ position: relative;
443
+ animation: float 2s ease-in-out infinite;
444
+ margin-right: 10px;
445
+ }
446
+
447
+ .space-robot::after {
448
+ content: '';
449
+ position: absolute;
450
+ width: 6px;
451
+ height: 6px;
452
+ background: #f0f0ff;
453
+ border-radius: 50%;
454
+ top: 9px;
455
+ left: 9px;
456
+ }
457
+
458
+ .space-robot.sending {
459
+ animation: spin 0.5s linear;
460
+ }
461
+
462
+ @keyframes float {
463
+ 0% { transform: translateY(0); }
464
+ 50% { transform: translateY(-5px); }
465
+ 100% { transform: translateY(0); }
466
+ }
467
+
468
+ @keyframes spin {
469
+ 0% { transform: rotate(0deg); }
470
+ 100% { transform: rotate(360deg); }
471
+ }
472
+
473
+ #chat-output {
474
+ flex: 1;
475
+ padding: 20px;
476
+ overflow-y: auto;
477
+ background: rgba(0, 0, 0, 0.3);
478
+ visibility: visible;
479
+ min-height: 200px;
480
+ display: block;
481
+ text-align: left;
482
+ }
483
+
484
+ #chat-output p {
485
+ margin-bottom: 15px;
486
+ font-size: 15px;
487
+ line-height: 1.5;
488
+ opacity: 1;
489
+ position: relative;
490
+ display: block;
491
+ visibility: visible;
492
+ }
493
+
494
+ #chat-output p:nth-child(odd) {
495
+ color: #c0c0c0;
496
+ background: rgba(192, 192, 192, 0.1);
497
+ padding: 10px 15px;
498
+ border-radius: 12px 12px 0 12px;
499
+ max-width: 70%;
500
+ margin-left: auto;
501
+ text-align: right;
502
+ }
503
+
504
+ #chat-output p:nth-child(even) {
505
+ color: #f0f0ff;
506
+ background: rgba(255, 255, 255, 0.05);
507
+ padding: 10px 15px;
508
+ border-radius: 12px 12px 12px 0;
509
+ max-width: 70%;
510
+ }
511
+
512
+ .chat-input-wrapper {
513
+ display: flex;
514
+ padding: 15px;
515
+ background: rgba(255, 255, 255, 0.05);
516
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
517
+ border-radius: 0 0 15px 15px;
518
+ }
519
+
520
+ #user-input {
521
+ flex: 1;
522
+ padding: 12px;
523
+ background: transparent;
524
+ border: 1px solid rgba(255, 255, 255, 0.2);
525
+ border-radius: 8px;
526
+ color: #f0f0ff;
527
+ font-size: 15px;
528
+ outline: none;
529
+ transition: border-color 0.3s ease, box-shadow 0.3s ease;
530
+ }
531
+
532
+ #user-input:focus {
533
+ border-color: #c0c0c0;
534
+ box-shadow: 0 0 5px rgba(192, 192, 192, 0.5);
535
+ }
536
+
537
+ #user-input::placeholder {
538
+ color: rgba(255, 255, 255, 0.4);
539
+ }
540
+
541
+ #sendButton {
542
+ background: linear-gradient(45deg, #3c2f5f, #c0c0c0);
543
+ color: #0a0a23;
544
+ border: none;
545
+ border-radius: 8px;
546
+ padding: 12px 15px;
547
+ margin-left: 10px;
548
+ cursor: pointer;
549
+ font-size: 16px;
550
+ transition: transform 0.2s ease;
551
+ pointer-events: auto;
552
+ }
553
+
554
+ #sendButton:hover {
555
+ transform: scale(1.05);
556
+ }
557
+
558
+ /* Glassmorphism */
559
+ .glassmorphism {
560
+ background: rgba(255, 255, 255, 0.05);
561
+ backdrop-filter: blur(4px);
562
+ border: 1px solid rgba(255, 255, 255, 0.1);
563
+ }
564
+
565
+ /* Footer */
566
+ .footer {
567
+ padding: 15px;
568
+ text-align: center;
569
+ background: rgba(255, 255, 255, 0.05);
570
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
571
+ color: #c0c0c0;
572
+ }
573
+
574
+ /* Theme Styles */
575
+ body.starlight {
576
+ background: linear-gradient(135deg, #d9d9e6 0%, #f0f0f5 100%);
577
+ color: #1a1a3d;
578
+ }
579
+
580
+ body.starlight .navbar,
581
+ body.starlight .chatbot-container,
582
+ body.starlight .footer {
583
+ background: rgba(0, 0, 0, 0.05);
584
+ border-color: rgba(0, 0, 0, 0.1);
585
+ }
586
+
587
+ body.starlight .header {
588
+ color: #3c2f5f;
589
+ text-shadow: none;
590
+ }
591
+
592
+ body.starlight .nav-menu a {
593
+ color: #1a1a3d;
594
+ }
595
+
596
+ body.starlight .nav-menu a:hover,
597
+ body.starlight .nav-menu a.active,
598
+ body.starlight #chat-output p:nth-child(odd) {
599
+ color: #3c2f5f;
600
+ }
601
+
602
+ body.starlight .chat-header,
603
+ body.starlight #sendButton {
604
+ background: linear-gradient(45deg, #3c2f5f, #c0c0c0);
605
+ color: #f0f0ff;
606
+ }
607
+
608
+ body.starlight .space-robot {
609
+ background: radial-gradient(circle, #c0c0c0 40%, #3c2f5f 100%);
610
+ }
611
+
612
+ body.starlight #user-input {
613
+ color: #1a1a3d;
614
+ border-color: rgba(0, 0, 0, 0.2);
615
+ }
616
+
617
+ body.starlight #user-input:focus {
618
+ border-color: #3c2f5f;
619
+ box-shadow: 0 0 5px rgba(60, 47, 95, 0.5);
620
+ }
621
+
622
+ body.starlight #user-input::placeholder {
623
+ color: rgba(0, 0, 0, 0.4);
624
+ }
625
+
626
+ body.starlight .footer {
627
+ color: #3c2f5f;
628
+ }
629
+
630
+ body.starlight #theme-toggle {
631
+ background: rgba(0, 0, 0, 0.15);
632
+ color: #3c2f5f;
633
+ }
634
+
635
+ body.starlight #theme-toggle option {
636
+ background: #f0f0f5;
637
+ color: #1a1a3d;
638
+ }
639
+
640
+ body.void {
641
+ background: linear-gradient(135deg, #000014 0%, #0a0a23 100%);
642
+ }
643
+
644
+ body.void .header {
645
+ color: #c0c0c0;
646
+ text-shadow: 0 0 5px rgba(192, 192, 192, 0.7);
647
+ }
648
+
649
+ body.void .nav-menu a:hover,
650
+ body.void .nav-menu a.active,
651
+ body.void #chat-output p:nth-child(odd) {
652
+ color: #c0c0c0;
653
+ }
654
+
655
+ body.void .chat-header,
656
+ body.void #sendButton {
657
+ background: linear-gradient(45deg, #0a0a23, #c0c0c0);
658
+ }
659
+
660
+ body.void #user-input:focus {
661
+ border-color: #c0c0c0;
662
+ box-shadow: 0 0 5px rgba(192, 192, 192, 0.7);
663
+ }
664
+
665
+ body.void #theme-toggle {
666
+ background: rgba(255, 255, 255, 0.15);
667
+ color: #f0f0ff;
668
+ }
669
+
670
+ body.void #theme-toggle option {
671
+ background: #0a0a23;
672
+ color: #f0f0ff;
673
+ }
674
+
675
+ /* Responsive Design */
676
+ @media (max-width: 768px) {
677
+ .nav-menu {
678
+ display: none;
679
+ position: absolute;
680
+ top: 70px;
681
+ left: 0;
682
+ width: 100%;
683
+ background: rgba(255, 255, 255, 0.05);
684
+ backdrop-filter: blur(4px);
685
+ flex-direction: column;
686
+ padding: 15px;
687
+ }
688
+
689
+ .nav-menu.active {
690
+ display: flex;
691
+ }
692
+
693
+ .hamburger {
694
+ display: flex;
695
+ }
696
+
697
+ .header {
698
+ font-size: 32px;
699
+ }
700
+
701
+ .chatbot-container {
702
+ width: 90vw;
703
+ height: 65vh;
704
+ max-height: 550px;
705
+ }
706
+
707
+ .space-robot-logo {
708
+ width: 24px;
709
+ height: 24px;
710
+ }
711
+ }
712
+ </style>
713
+
714
+ <!-- Embedded JavaScript -->
715
+ <script>
716
+ console.log('Script loaded');
717
+
718
+ try {
719
+ /* Utility Functions */
720
+ const utils = {
721
+ sanitizeInput(input) {
722
+ console.log('Sanitizing input:', input);
723
+ return input.replace(/</g, '<').replace(/>/g, '>');
724
+ },
725
+ debounce(func, wait) {
726
+ let timeout;
727
+ return function (...args) {
728
+ clearTimeout(timeout);
729
+ timeout = setTimeout(() => func.apply(this, args), wait);
730
+ };
731
+ },
732
+ formatTimestamp() {
733
+ const now = new Date();
734
+ return now.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
735
+ },
736
+ displayError(message) {
737
+ const chatOutput = document.getElementById('chat-output');
738
+ if (chatOutput) {
739
+ const errorMsg = document.createElement('p');
740
+ errorMsg.innerHTML = `<strong>AthleteGuard AI:</strong> Client error: ${utils.sanitizeInput(message)}. Please refresh and try again. <small>(${utils.formatTimestamp()})</small>`;
741
+ chatOutput.appendChild(errorMsg);
742
+ chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
743
+ console.log('Displayed client error in chat');
744
+ }
745
+ },
746
+ };
747
+
748
+ /* Loader Fade-out */
749
+ function hideLoader() {
750
+ try {
751
+ console.log('Attempting to hide loader');
752
+ const loader = document.querySelector('.loader-wrapper');
753
+ if (!loader) {
754
+ console.error('Loader not found');
755
+ return;
756
+ }
757
+ loader.classList.add('fade-out');
758
+ console.log('Added fade-out class to loader');
759
+ setTimeout(() => {
760
+ loader.style.display = 'none';
761
+ console.log('Loader hidden');
762
+ document.body.style.visibility = 'visible';
763
+ const chatbotSection = document.querySelector('.chatbot-section');
764
+ if (chatbotSection) {
765
+ chatbotSection.style.display = 'flex';
766
+ console.log('Chatbot section set to visible');
767
+ }
768
+ }, 500);
769
+ } catch (error) {
770
+ console.error('Loader hide error:', error);
771
+ utils.displayError(error.message);
772
+ }
773
+ }
774
+
775
+ /* Nebula Starfield Background */
776
+ function setupStarfield() {
777
+ try {
778
+ console.log('Initializing starfield');
779
+ const canvas = document.getElementById('starfield-canvas');
780
+ if (!canvas) {
781
+ console.error('Starfield canvas not found');
782
+ return;
783
+ }
784
+ const ctx = canvas.getContext('2d');
785
+ if (!ctx) {
786
+ console.error('Canvas context not available');
787
+ return;
788
+ }
789
+
790
+ function setCanvasSize() {
791
+ const dpr = window.devicePixelRatio || 1;
792
+ canvas.width = window.innerWidth * dpr;
793
+ canvas.height = window.innerHeight * dpr;
794
+ ctx.scale(dpr, dpr);
795
+ }
796
+ setCanvasSize();
797
+
798
+ let starsArray = [];
799
+ let shootingStars = [];
800
+ let nebulae = [];
801
+ const numberOfStars = 150;
802
+ const numberOfShootingStars = 2;
803
+ const numberOfNebulae = 3;
804
+
805
+ class Star {
806
+ constructor(x, y, size, speed, color, layer) {
807
+ this.x = x;
808
+ this.y = y;
809
+ this.size = size;
810
+ this.speed = speed;
811
+ this.color = color;
812
+ this.layer = layer;
813
+ this.opacity = Math.random() * 0.5 + 0.5;
814
+ }
815
+ update() {
816
+ this.x -= this.speed * this.layer;
817
+ if (this.x < 0) this.x = canvas.width / (window.devicePixelRatio || 1);
818
+ this.opacity = Math.sin(Date.now() * 0.001 + this.x) * 0.3 + 0.5;
819
+ }
820
+ draw() {
821
+ ctx.globalAlpha = this.opacity;
822
+ ctx.fillStyle = this.color;
823
+ ctx.beginPath();
824
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
825
+ ctx.fill();
826
+ ctx.globalAlpha = 1;
827
+ }
828
+ }
829
+
830
+ class ShootingStar {
831
+ constructor() {
832
+ this.x = canvas.width / (window.devicePixelRatio || 1);
833
+ this.y = Math.random() * (canvas.height / (window.devicePixelRatio || 1));
834
+ this.speedX = -(Math.random() * 5 + 5);
835
+ this.speedY = Math.random() * 2 - 1;
836
+ this.length = Math.random() * 50 + 20;
837
+ this.opacity = 1;
838
+ }
839
+ update() {
840
+ this.x += this.speedX;
841
+ this.y += this.speedY;
842
+ this.opacity -= 0.02;
843
+ if (this.opacity <= 0) {
844
+ this.x = canvas.width / (window.devicePixelRatio || 1);
845
+ this.y = Math.random() * (canvas.height / (window.devicePixelRatio || 1));
846
+ this.speedX = -(Math.random() * 5 + 5);
847
+ this.speedY = Math.random() * 2 - 1;
848
+ this.length = Math.random() * 50 + 20;
849
+ this.opacity = 1;
850
+ }
851
+ }
852
+ draw() {
853
+ ctx.globalAlpha = this.opacity;
854
+ ctx.strokeStyle = '#f0f0ff';
855
+ ctx.lineWidth = 2;
856
+ ctx.beginPath();
857
+ ctx.moveTo(this.x, this.y);
858
+ ctx.lineTo(this.x + this.length * this.speedX / 10, this.y + this.length * this.speedY / 10);
859
+ ctx.stroke();
860
+ ctx.globalAlpha = 1;
861
+ }
862
+ }
863
+
864
+ class Nebula {
865
+ constructor(x, y, size, color) {
866
+ this.x = x;
867
+ this.y = y;
868
+ this.size = size;
869
+ this.color = color;
870
+ this.opacity = Math.random() * 0.2 + 0.1;
871
+ }
872
+ update() {
873
+ this.opacity = Math.sin(Date.now() * 0.0005 + this.x) * 0.1 + 0.2;
874
+ }
875
+ draw() {
876
+ ctx.globalAlpha = this.opacity;
877
+ ctx.fillStyle = this.color;
878
+ ctx.beginPath();
879
+ ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
880
+ ctx.filter = 'blur(20px)';
881
+ ctx.fill();
882
+ ctx.filter = 'none';
883
+ ctx.globalAlpha = 1;
884
+ }
885
+ }
886
+
887
+ function initStarfield() {
888
+ starsArray = [];
889
+ shootingStars = [];
890
+ nebulae = [];
891
+ const colors = ['#f0f0ff', '#c0c0c0', '#3c2f5f'];
892
+ const nebulaColors = ['rgba(60, 47, 95, 0.5)', 'rgba(26, 26, 61, 0.5)'];
893
+ for (let i = 0; i < numberOfStars; i++) {
894
+ const x = Math.random() * canvas.width / (window.devicePixelRatio || 1);
895
+ const y = Math.random() * canvas.height / (window.devicePixelRatio || 1);
896
+ const size = Math.random() * (i % 2 === 0 ? 1 : 2);
897
+ const speed = Math.random() * 0.5 + 0.1;
898
+ const color = colors[Math.floor(Math.random() * colors.length)];
899
+ const layer = Math.random() * 0.5 + 0.5;
900
+ starsArray.push(new Star(x, y, size, speed, color, layer));
901
+ }
902
+ for (let i = 0; i < numberOfShootingStars; i++) {
903
+ shootingStars.push(new ShootingStar());
904
+ }
905
+ for (let i = 0; i < numberOfNebulae; i++) {
906
+ const x = Math.random() * canvas.width / (window.devicePixelRatio || 1);
907
+ const y = Math.random() * canvas.height / (window.devicePixelRatio || 1);
908
+ const size = Math.random() * 100 + 50;
909
+ const color = nebulaColors[Math.floor(Math.random() * nebulaColors.length)];
910
+ nebulae.push(new Nebula(x, y, size, color));
911
+ }
912
+ }
913
+
914
+ function animateStarfield() {
915
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
916
+ nebulae.forEach(nebula => {
917
+ nebula.update();
918
+ nebula.draw();
919
+ });
920
+ starsArray.forEach(star => {
921
+ star.update();
922
+ star.draw();
923
+ });
924
+ shootingStars.forEach(star => {
925
+ star.update();
926
+ star.draw();
927
+ });
928
+ requestAnimationFrame(animateStarfield);
929
+ }
930
+
931
+ initStarfield();
932
+ animateStarfield();
933
+
934
+ window.addEventListener('resize', utils.debounce(() => {
935
+ setCanvasSize();
936
+ initStarfield();
937
+ }, 200));
938
+ } catch (error) {
939
+ console.error('Starfield setup error:', error);
940
+ utils.displayError(error.message);
941
+ }
942
+ }
943
+
944
+ /* Chatbot Setup */
945
+ function setupChatbot() {
946
+ try {
947
+ const chatOutput = document.getElementById('chat-output');
948
+ const userInput = document.getElementById('user-input');
949
+ const sendButton = document.getElementById('sendButton');
950
+
951
+ console.log('Checking DOM elements:', { chatOutput, userInput, sendButton });
952
+ if (!chatOutput || !userInput || !sendButton) {
953
+ console.error('Missing DOM elements:', { chatOutput, userInput, sendButton });
954
+ utils.displayError('Chatbot initialization failed: Missing DOM elements');
955
+ return false;
956
+ }
957
+
958
+ userInput.focus();
959
+ console.log('User input focused');
960
+
961
+ let isProcessing = false;
962
+ const processedMessages = new Set();
963
+
964
+ function clearInput() {
965
+ console.log('Clearing input');
966
+ userInput.value = '';
967
+ userInput.focus();
968
+ userInput.dispatchEvent(new Event('input'));
969
+ setTimeout(() => {
970
+ if (userInput.value !== '') {
971
+ console.warn('Fallback: Forcing input clear');
972
+ userInput.value = '';
973
+ }
974
+ }, 100);
975
+ }
976
+
977
+ const sendMessage = async (message, eventType, messageId) => {
978
+ console.log(`Processing message [${messageId}] (via ${eventType}):`, message);
979
+ if (isProcessing || processedMessages.has(messageId)) {
980
+ console.log(`Skipping duplicate message [${messageId}]: isProcessing=${isProcessing}`);
981
+ return;
982
+ }
983
+ processedMessages.add(messageId);
984
+ isProcessing = true;
985
+
986
+ const input = message.trim();
987
+ console.log('Validating message:', input);
988
+ if (!input || typeof input !== 'string' || input.length === 0) {
989
+ console.warn(`Invalid input [${messageId}], skipping request`);
990
+ isProcessing = false;
991
+ processedMessages.delete(messageId);
992
+ return;
993
+ }
994
+
995
+ try {
996
+ userInput.disabled = true;
997
+ sendButton.disabled = true;
998
+ const spaceRobot = document.querySelector('.space-robot');
999
+ if (spaceRobot) spaceRobot.classList.add('sending');
1000
+
1001
+ clearInput();
1002
+
1003
+ const chatOutput = document.getElementById('chat-output');
1004
+ if (!chatOutput) {
1005
+ throw new Error(`chat-output not found during sendMessage [${messageId}]`);
1006
+ }
1007
+
1008
+ const userMsg = document.createElement('p');
1009
+ userMsg.innerHTML = `<strong>You:</strong> ${utils.sanitizeInput(input)} <small>(${utils.formatTimestamp()})</small>`;
1010
+ console.log(`Appending user message [${messageId}] to chat-output`);
1011
+ chatOutput.appendChild(userMsg);
1012
+ chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
1013
+
1014
+ const payload = { message: input };
1015
+ console.log(`Fetch initiated [${messageId}]:`, payload);
1016
+ const response = await fetch('http://0.0.0.0:7860/chat', {
1017
+ method: 'POST',
1018
+ headers: { 'Content-Type': 'application/json' },
1019
+ body: JSON.stringify(payload),
1020
+ signal: AbortSignal.timeout(5000),
1021
+ });
1022
+ console.log(`Fetch completed [${messageId}]:`, { status: response.status, ok: response.ok });
1023
+
1024
+ if (!response.ok) {
1025
+ throw new Error(`HTTP error: ${response.status}`);
1026
+ }
1027
+
1028
+ const data = await response.json();
1029
+ console.log(`Response data [${messageId}]:`, data);
1030
+
1031
+ if (data.error) {
1032
+ throw new Error(data.error);
1033
+ }
1034
+
1035
+ if (!data.response) {
1036
+ throw new Error('No response field in data');
1037
+ }
1038
+
1039
+ const botMsg = document.createElement('p');
1040
+ botMsg.innerHTML = `<div class="space-robot"></div><strong>AthleteGuard AI:</strong> ${utils.sanitizeInput(data.response)} <small>(${utils.formatTimestamp()})</small>`;
1041
+ console.log(`Appending bot message [${messageId}] to chat-output`);
1042
+ chatOutput.appendChild(botMsg);
1043
+ chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
1044
+
1045
+ if (spaceRobot) spaceRobot.classList.remove('sending');
1046
+ } catch (error) {
1047
+ console.error(`Chat error [${messageId}]:`, error.message);
1048
+ const chatOutput = document.getElementById('chat-output');
1049
+ if (chatOutput) {
1050
+ const errorMsg = document.createElement('p');
1051
+ errorMsg.innerHTML = `<div class="space-robot"></div><strong>AthleteGuard AI:</strong> Error: ${utils.sanitizeInput(error.message)}. Please try again. <small>(${utils.formatTimestamp()})</small>`;
1052
+ console.log(`Appending error message [${messageId}] to chat-output`);
1053
+ chatOutput.appendChild(errorMsg);
1054
+ chatOutput.scrollTo({ top: chatOutput.scrollHeight, behavior: 'smooth' });
1055
+ }
1056
+ clearInput();
1057
+ } finally {
1058
+ userInput.disabled = false;
1059
+ sendButton.disabled = false;
1060
+ clearInput();
1061
+ setTimeout(() => {
1062
+ isProcessing = false;
1063
+ console.log(`Reset isProcessing [${messageId}]`);
1064
+ }, 500);
1065
+ }
1066
+ };
1067
+
1068
+ console.log('Removing old listeners');
1069
+ const oldKeypress = userInput.__keypressHandler;
1070
+ const oldClick = sendButton.__clickHandler;
1071
+ if (oldKeypress) userInput.removeEventListener('keypress', oldKeypress);
1072
+ if (oldClick) sendButton.removeEventListener('click', oldClick);
1073
+
1074
+ const handleKeypress = (e) => {
1075
+ console.log('Keypress event:', e.key);
1076
+ if (e.key === 'Enter') {
1077
+ e.preventDefault();
1078
+ e.stopPropagation();
1079
+ const messageId = Date.now() + '-keypress';
1080
+ console.log(`Enter key pressed, sending message [${messageId}]`);
1081
+ sendMessage(userInput.value, 'keypress', messageId);
1082
+ }
1083
+ };
1084
+
1085
+ const handleClick = (e) => {
1086
+ e.preventDefault();
1087
+ e.stopPropagation();
1088
+ const messageId = Date.now() + '-click';
1089
+ console.log(`Send button clicked, sending message [${messageId}]`);
1090
+ sendMessage(userInput.value, 'click', messageId);
1091
+ };
1092
+
1093
+ userInput.__keypressHandler = handleKeypress;
1094
+ sendButton.__clickHandler = handleClick;
1095
+
1096
+ console.log('Attaching new listeners');
1097
+ userInput.addEventListener('keypress', handleKeypress);
1098
+ sendButton.addEventListener('click', handleClick);
1099
+
1100
+ console.log('Event listeners attached');
1101
+ return true;
1102
+ } catch (error) {
1103
+ console.error('Chatbot setup error:', error);
1104
+ utils.displayError(error.message);
1105
+ return false;
1106
+ }
1107
+ }
1108
+
1109
+ /* Initial Setup */
1110
+ document.addEventListener('DOMContentLoaded', () => {
1111
+ console.log('DOM content loaded');
1112
+ try {
1113
+ setTimeout(hideLoader, 3500);
1114
+ setTimeout(() => {
1115
+ const loader = document.querySelector('.loader-wrapper');
1116
+ if (loader && loader.style.display !== 'none') {
1117
+ console.warn('Fallback: Forcing loader hide');
1118
+ loader.style.display = 'none';
1119
+ document.body.style.visibility = 'visible';
1120
+ const chatbotSection = document.querySelector('.chatbot-section');
1121
+ if (chatbotSection) chatbotSection.style.display = 'flex';
1122
+ }
1123
+ }, 4500);
1124
+
1125
+ setupStarfield();
1126
+
1127
+ const hamburger = document.querySelector('.hamburger');
1128
+ const navMenu = document.querySelector('.nav-menu');
1129
+ if (hamburger && navMenu) {
1130
+ hamburger.addEventListener('click', () => {
1131
+ navMenu.classList.toggle('active');
1132
+ hamburger.classList.toggle('active');
1133
+ console.log('Hamburger menu toggled');
1134
+ });
1135
+ }
1136
+
1137
+ const themeToggle = document.getElementById('theme-toggle');
1138
+ if (themeToggle) {
1139
+ themeToggle.addEventListener('change', () => {
1140
+ document.body.className = themeToggle.value;
1141
+ console.log('Theme changed to:', themeToggle.value);
1142
+ });
1143
+ }
1144
+
1145
+ if (!setupChatbot()) {
1146
+ console.warn('Chatbot setup failed, retrying in 500ms');
1147
+ setTimeout(setupChatbot, 500);
1148
+ }
1149
+ } catch (error) {
1150
+ console.error('DOMContentLoaded error:', error);
1151
+ utils.displayError(error.message);
1152
+ }
1153
+ });
1154
+
1155
+ /* Fallback Setup */
1156
+ setTimeout(() => {
1157
+ console.log('Running fallback chatbot setup');
1158
+ setupChatbot();
1159
+ }, 1000);
1160
+ } catch (error) {
1161
+ console.error('Global script error:', error);
1162
+ utils.displayError(error.message);
1163
+ }
1164
+ </script>
1165
+ </body>
1166
  </html>