BinKhoaLe1812 commited on
Commit
7881b0f
·
verified ·
1 Parent(s): fed601b
Files changed (5) hide show
  1. assets/.DS_Store +0 -0
  2. assets/app.js +238 -20
  3. assets/logo.svg +7 -11
  4. assets/styles.css +481 -5
  5. index.html +89 -89
assets/.DS_Store ADDED
Binary file (6.15 kB). View file
 
assets/app.js CHANGED
@@ -1,54 +1,272 @@
1
- // Theme toggle
2
  const toggle = document.getElementById('themeToggle');
3
  const root = document.documentElement;
4
- const saved = localStorage.getItem('theme');
5
- if (saved === 'light') document.documentElement.classList.remove('dark');
6
- else document.documentElement.classList.add('dark');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  toggle?.addEventListener('click', () => {
9
  const isDark = document.documentElement.classList.toggle('dark');
10
  localStorage.setItem('theme', isDark ? 'dark' : 'light');
 
 
 
 
 
 
 
11
  });
12
 
13
- // Tabs logic
14
  document.querySelectorAll('.tab').forEach(btn => {
15
  btn.addEventListener('click', () => {
16
- document.querySelectorAll('.tab').forEach(b => b.classList.remove('active'));
 
 
 
 
 
 
17
  btn.classList.add('active');
 
 
 
18
  const target = btn.getAttribute('data-target');
19
- document.querySelectorAll('.diagram').forEach(d => d.classList.remove('visible'));
20
- document.querySelector(target)?.classList.add('visible');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  });
22
  });
23
 
24
- // Metric counters
 
 
 
 
 
 
 
 
 
 
25
  const counters = document.querySelectorAll('.metric-value');
26
  const easeOutCubic = t => 1 - Math.pow(1 - t, 3);
27
- const animateCount = (el, to, duration=1200) => {
 
 
28
  const start = performance.now();
29
  const from = 0;
 
 
 
30
  const step = (now) => {
31
  const p = Math.min(1, (now - start) / duration);
32
- const v = Math.floor(from + (to - from) * easeOutCubic(p));
33
- el.textContent = v.toLocaleString();
34
- if (p < 1) requestAnimationFrame(step);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  };
36
  requestAnimationFrame(step);
37
  };
 
 
38
  const observer = new IntersectionObserver(entries => {
39
  entries.forEach(e => {
40
  if (e.isIntersecting) {
41
  const el = e.target;
42
- animateCount(el, parseInt(el.dataset.count, 10) || 0);
 
 
 
 
 
 
 
 
 
 
43
  observer.unobserve(el);
44
  }
45
  });
46
- }, { threshold: 0.4 });
 
 
 
 
 
47
  counters.forEach(c => observer.observe(c));
48
 
49
- // VanillaTilt init
50
- if (window.VanillaTilt) {
51
- document.querySelectorAll('[data-tilt]').forEach(el => {
52
- VanillaTilt.init(el, { max: 6, speed: 400, glare: true, 'max-glare': 0.1 });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  });
54
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Enhanced theme toggle with smooth transitions
2
  const toggle = document.getElementById('themeToggle');
3
  const root = document.documentElement;
4
+ const saved = localStorage.getItem('theme') || 'dark';
5
+
6
+ // Apply saved theme
7
+ if (saved === 'light') {
8
+ document.documentElement.classList.remove('dark');
9
+ } else {
10
+ document.documentElement.classList.add('dark');
11
+ }
12
+
13
+ // Update theme toggle icon
14
+ const updateThemeIcon = () => {
15
+ const icon = toggle?.querySelector('i');
16
+ if (icon) {
17
+ icon.setAttribute('data-lucide', document.documentElement.classList.contains('dark') ? 'sun-medium' : 'moon');
18
+ if (window.lucide) {
19
+ lucide.createIcons();
20
+ }
21
+ }
22
+ };
23
+
24
+ // Initialize theme icon
25
+ updateThemeIcon();
26
 
27
  toggle?.addEventListener('click', () => {
28
  const isDark = document.documentElement.classList.toggle('dark');
29
  localStorage.setItem('theme', isDark ? 'dark' : 'light');
30
+ updateThemeIcon();
31
+
32
+ // Add smooth transition effect
33
+ document.body.style.transition = 'background-color 0.3s ease, color 0.3s ease';
34
+ setTimeout(() => {
35
+ document.body.style.transition = '';
36
+ }, 300);
37
  });
38
 
39
+ // Enhanced tabs logic with smooth transitions and ARIA support
40
  document.querySelectorAll('.tab').forEach(btn => {
41
  btn.addEventListener('click', () => {
42
+ // Remove active class and ARIA attributes from all tabs
43
+ document.querySelectorAll('.tab').forEach(b => {
44
+ b.classList.remove('active');
45
+ b.setAttribute('aria-selected', 'false');
46
+ });
47
+
48
+ // Add active class and ARIA attributes to clicked tab
49
  btn.classList.add('active');
50
+ btn.setAttribute('aria-selected', 'true');
51
+
52
+ // Get target diagram
53
  const target = btn.getAttribute('data-target');
54
+ const targetDiagram = document.querySelector(target);
55
+
56
+ if (targetDiagram) {
57
+ // Hide all diagrams with fade out and update ARIA
58
+ document.querySelectorAll('.diagram').forEach(d => {
59
+ d.style.opacity = '0';
60
+ d.style.transform = 'translateY(20px)';
61
+ d.setAttribute('aria-hidden', 'true');
62
+ setTimeout(() => {
63
+ d.classList.remove('visible');
64
+ }, 150);
65
+ });
66
+
67
+ // Show target diagram with fade in and update ARIA
68
+ setTimeout(() => {
69
+ targetDiagram.classList.add('visible');
70
+ targetDiagram.style.opacity = '1';
71
+ targetDiagram.style.transform = 'translateY(0)';
72
+ targetDiagram.setAttribute('aria-hidden', 'false');
73
+ }, 150);
74
+ }
75
  });
76
  });
77
 
78
+ // Initialize first tab as active
79
+ document.addEventListener('DOMContentLoaded', () => {
80
+ const firstTab = document.querySelector('.tab.active');
81
+ const firstDiagram = document.querySelector('.diagram.visible');
82
+ if (firstTab && firstDiagram) {
83
+ firstDiagram.style.opacity = '1';
84
+ firstDiagram.style.transform = 'translateY(0)';
85
+ }
86
+ });
87
+
88
+ // Enhanced metric counters with better performance
89
  const counters = document.querySelectorAll('.metric-value');
90
  const easeOutCubic = t => 1 - Math.pow(1 - t, 3);
91
+ const easeOutQuart = t => 1 - Math.pow(1 - t, 4);
92
+
93
+ const animateCount = (el, to, duration = 1200) => {
94
  const start = performance.now();
95
  const from = 0;
96
+ const isLargeNumber = to > 1000;
97
+ const easing = isLargeNumber ? easeOutQuart : easeOutCubic;
98
+
99
  const step = (now) => {
100
  const p = Math.min(1, (now - start) / duration);
101
+ const v = Math.floor(from + (to - from) * easing(p));
102
+
103
+ // Format number with appropriate separators
104
+ if (to >= 1000000) {
105
+ el.textContent = (v / 1000000).toFixed(1) + 'M';
106
+ } else if (to >= 1000) {
107
+ el.textContent = (v / 1000).toFixed(1) + 'K';
108
+ } else {
109
+ el.textContent = v.toLocaleString();
110
+ }
111
+
112
+ if (p < 1) {
113
+ requestAnimationFrame(step);
114
+ } else {
115
+ // Ensure final value is exact
116
+ if (to >= 1000000) {
117
+ el.textContent = (to / 1000000).toFixed(1) + 'M';
118
+ } else if (to >= 1000) {
119
+ el.textContent = (to / 1000).toFixed(1) + 'K';
120
+ } else {
121
+ el.textContent = to.toLocaleString();
122
+ }
123
+ }
124
  };
125
  requestAnimationFrame(step);
126
  };
127
+
128
+ // Enhanced intersection observer with better performance
129
  const observer = new IntersectionObserver(entries => {
130
  entries.forEach(e => {
131
  if (e.isIntersecting) {
132
  const el = e.target;
133
+ const count = parseInt(el.dataset.count, 10) || 0;
134
+
135
+ // Add loading state
136
+ el.classList.add('loading');
137
+
138
+ // Animate with slight delay for better visual effect
139
+ setTimeout(() => {
140
+ animateCount(el, count);
141
+ el.classList.remove('loading');
142
+ }, 200);
143
+
144
  observer.unobserve(el);
145
  }
146
  });
147
+ }, {
148
+ threshold: 0.3,
149
+ rootMargin: '0px 0px -50px 0px'
150
+ });
151
+
152
+ // Observe counters
153
  counters.forEach(c => observer.observe(c));
154
 
155
+ // Enhanced VanillaTilt initialization with error handling
156
+ const initTilt = () => {
157
+ if (window.VanillaTilt) {
158
+ try {
159
+ document.querySelectorAll('[data-tilt]').forEach(el => {
160
+ VanillaTilt.init(el, {
161
+ max: 6,
162
+ speed: 400,
163
+ glare: true,
164
+ 'max-glare': 0.1,
165
+ scale: 1.02,
166
+ gyroscope: false
167
+ });
168
+ });
169
+ } catch (error) {
170
+ console.warn('VanillaTilt initialization failed:', error);
171
+ }
172
+ }
173
+ };
174
+
175
+ // Initialize tilt effects
176
+ initTilt();
177
+
178
+ // Smooth scrolling for navigation links
179
+ document.querySelectorAll('a[href^="#"]').forEach(link => {
180
+ link.addEventListener('click', (e) => {
181
+ e.preventDefault();
182
+ const targetId = link.getAttribute('href');
183
+ const targetElement = document.querySelector(targetId);
184
+
185
+ if (targetElement) {
186
+ const headerHeight = document.querySelector('header')?.offsetHeight || 0;
187
+ const targetPosition = targetElement.offsetTop - headerHeight - 20;
188
+
189
+ window.scrollTo({
190
+ top: targetPosition,
191
+ behavior: 'smooth'
192
+ });
193
+ }
194
+ });
195
+ });
196
+
197
+ // Enhanced error handling for external resources
198
+ const handleResourceError = (resource, fallback) => {
199
+ resource.addEventListener('error', () => {
200
+ console.warn(`Failed to load resource, using fallback`);
201
+ if (fallback) {
202
+ resource.src = fallback;
203
+ }
204
  });
205
+ };
206
+
207
+ // Handle external script loading errors
208
+ window.addEventListener('error', (e) => {
209
+ if (e.target.tagName === 'SCRIPT' && e.target.src) {
210
+ console.warn(`Script failed to load: ${e.target.src}`);
211
+ }
212
+ });
213
+
214
+ // Performance monitoring
215
+ const logPerformance = () => {
216
+ if ('performance' in window) {
217
+ window.addEventListener('load', () => {
218
+ setTimeout(() => {
219
+ const perfData = performance.getEntriesByType('navigation')[0];
220
+ console.log(`Page load time: ${perfData.loadEventEnd - perfData.loadEventStart}ms`);
221
+ }, 0);
222
+ });
223
+ }
224
+ };
225
+
226
+ // Initialize performance monitoring
227
+ logPerformance();
228
+
229
+ // Add keyboard navigation support
230
+ document.addEventListener('keydown', (e) => {
231
+ // Tab navigation for architecture tabs
232
+ if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') {
233
+ const activeTab = document.querySelector('.tab.active');
234
+ if (activeTab) {
235
+ const tabs = Array.from(document.querySelectorAll('.tab'));
236
+ const currentIndex = tabs.indexOf(activeTab);
237
+ let nextIndex;
238
+
239
+ if (e.key === 'ArrowLeft') {
240
+ nextIndex = currentIndex > 0 ? currentIndex - 1 : tabs.length - 1;
241
+ } else {
242
+ nextIndex = currentIndex < tabs.length - 1 ? currentIndex + 1 : 0;
243
+ }
244
+
245
+ tabs[nextIndex].click();
246
+ tabs[nextIndex].focus();
247
+ }
248
+ }
249
+ });
250
+
251
+ // Add loading states for dynamic content
252
+ const addLoadingState = (element, duration = 1000) => {
253
+ element.classList.add('loading');
254
+ setTimeout(() => {
255
+ element.classList.remove('loading');
256
+ }, duration);
257
+ };
258
+
259
+ // Initialize all interactive elements
260
+ document.addEventListener('DOMContentLoaded', () => {
261
+ // Add loading states to cards that might load content dynamically
262
+ document.querySelectorAll('.card').forEach(card => {
263
+ card.addEventListener('mouseenter', () => {
264
+ addLoadingState(card, 500);
265
+ });
266
+ });
267
+
268
+ // Initialize tooltips for chips
269
+ document.querySelectorAll('.chip').forEach(chip => {
270
+ chip.setAttribute('title', chip.textContent);
271
+ });
272
+ });
assets/logo.svg CHANGED
assets/styles.css CHANGED
@@ -1,7 +1,18 @@
1
  /* Custom styles and utilities beyond Tailwind */
2
- :root { --ring: 0 0% 100%; }
3
- .link { color: rgb(165 180 252); }
4
- .link:hover { text-decoration: underline; }
 
 
 
 
 
 
 
 
 
 
 
5
  .btn-primary {
6
  @apply inline-flex items-center gap-2 px-3 py-2 rounded-xl bg-indigo-500 hover:bg-indigo-400 text-white font-semibold shadow-glow transition;
7
  }
@@ -47,9 +58,474 @@ pre.mermaid { @apply rounded-2xl bg-slate-900/70 border border-white/10 p-4 over
47
  .metric-label { @apply text-slate-400 mt-1; }
48
  .list { @apply list-disc pl-5 space-y-1 text-slate-300; }
49
  .avatar { @apply rounded-2xl bg-slate-800 border border-white/10 p-6 text-center font-semibold; }
50
- .shadow-glow { box-shadow: 0 10px 40px rgba(99,102,241,0.2); }
51
- .animate-pulse-slow { animation: pulse-slow 8s ease-in-out infinite; }
 
 
 
 
 
 
 
 
 
 
 
52
  @keyframes pulse-slow {
53
  0%, 100% { transform: scale(1); opacity: .7; }
54
  50% { transform: scale(1.05); opacity: 1; }
55
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  /* Custom styles and utilities beyond Tailwind */
2
+ :root {
3
+ --ring: 0 0% 100%;
4
+ --shadow-glow: 0 0 20px rgba(99,102,241,0.3), 0 0 40px rgba(99,102,241,0.2);
5
+ }
6
+
7
+ /* Base link styles */
8
+ .link {
9
+ color: rgb(165 180 252);
10
+ transition: all 0.2s ease;
11
+ }
12
+ .link:hover {
13
+ text-decoration: underline;
14
+ color: rgb(129 140 248);
15
+ }
16
  .btn-primary {
17
  @apply inline-flex items-center gap-2 px-3 py-2 rounded-xl bg-indigo-500 hover:bg-indigo-400 text-white font-semibold shadow-glow transition;
18
  }
 
58
  .metric-label { @apply text-slate-400 mt-1; }
59
  .list { @apply list-disc pl-5 space-y-1 text-slate-300; }
60
  .avatar { @apply rounded-2xl bg-slate-800 border border-white/10 p-6 text-center font-semibold; }
61
+ /* Enhanced shadow and animation utilities */
62
+ .shadow-glow {
63
+ box-shadow: var(--shadow-glow);
64
+ transition: box-shadow 0.3s ease;
65
+ }
66
+ .shadow-glow:hover {
67
+ box-shadow: 0 0 30px rgba(99,102,241,0.4), 0 0 60px rgba(99,102,241,0.3);
68
+ }
69
+
70
+ .animate-pulse-slow {
71
+ animation: pulse-slow 8s ease-in-out infinite;
72
+ }
73
+
74
  @keyframes pulse-slow {
75
  0%, 100% { transform: scale(1); opacity: .7; }
76
  50% { transform: scale(1.05); opacity: 1; }
77
  }
78
+
79
+ /* Additional animations */
80
+ @keyframes fadeInUp {
81
+ from {
82
+ opacity: 0;
83
+ transform: translateY(30px);
84
+ }
85
+ to {
86
+ opacity: 1;
87
+ transform: translateY(0);
88
+ }
89
+ }
90
+
91
+ @keyframes slideInRight {
92
+ from {
93
+ opacity: 0;
94
+ transform: translateX(30px);
95
+ }
96
+ to {
97
+ opacity: 1;
98
+ transform: translateX(0);
99
+ }
100
+ }
101
+
102
+ .animate-fade-in-up {
103
+ animation: fadeInUp 0.6s ease-out;
104
+ }
105
+
106
+ .animate-slide-in-right {
107
+ animation: slideInRight 0.6s ease-out;
108
+ }
109
+
110
+ /* Enhanced button states */
111
+ .btn-primary:active {
112
+ transform: translateY(1px);
113
+ box-shadow: 0 5px 15px rgba(99,102,241,0.3);
114
+ }
115
+
116
+ .btn-secondary:active {
117
+ transform: translateY(1px);
118
+ }
119
+
120
+ .btn-ghost:active {
121
+ transform: translateY(1px);
122
+ }
123
+
124
+ /* Enhanced card interactions */
125
+ .card {
126
+ transition: all 0.3s ease;
127
+ }
128
+
129
+ .card:hover {
130
+ transform: translateY(-2px);
131
+ box-shadow: 0 20px 40px rgba(0,0,0,0.3);
132
+ }
133
+
134
+ .card.glass:hover {
135
+ background: rgba(15, 23, 42, 0.7);
136
+ backdrop-filter: blur(20px);
137
+ }
138
+
139
+ /* Enhanced feature cards */
140
+ .feature {
141
+ transition: all 0.3s ease;
142
+ }
143
+
144
+ .feature:hover {
145
+ transform: translateY(-4px);
146
+ background: rgba(15, 23, 42, 0.8);
147
+ border-color: rgba(99, 102, 241, 0.3);
148
+ }
149
+
150
+ .feature-icon {
151
+ transition: all 0.3s ease;
152
+ }
153
+
154
+ .feature:hover .feature-icon {
155
+ color: rgb(99, 102, 241);
156
+ transform: scale(1.1);
157
+ }
158
+
159
+ /* Enhanced chip interactions */
160
+ .chip {
161
+ transition: all 0.2s ease;
162
+ }
163
+
164
+ .chip:hover {
165
+ background: rgba(99, 102, 241, 0.1);
166
+ border-color: rgba(99, 102, 241, 0.3);
167
+ color: rgb(129, 140, 248);
168
+ transform: translateY(-1px);
169
+ }
170
+
171
+ /* Enhanced tab interactions */
172
+ .tab {
173
+ transition: all 0.2s ease;
174
+ position: relative;
175
+ overflow: hidden;
176
+ }
177
+
178
+ .tab::before {
179
+ content: '';
180
+ position: absolute;
181
+ top: 0;
182
+ left: -100%;
183
+ width: 100%;
184
+ height: 100%;
185
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
186
+ transition: left 0.5s ease;
187
+ }
188
+
189
+ .tab:hover::before {
190
+ left: 100%;
191
+ }
192
+
193
+ .tab:hover {
194
+ transform: translateY(-1px);
195
+ }
196
+
197
+ .tab.active {
198
+ background: linear-gradient(135deg, rgba(99, 102, 241, 0.2), rgba(79, 70, 229, 0.1));
199
+ box-shadow: 0 4px 15px rgba(99, 102, 241, 0.2);
200
+ }
201
+
202
+ /* Enhanced diagram styling */
203
+ pre.mermaid {
204
+ transition: all 0.3s ease;
205
+ position: relative;
206
+ }
207
+
208
+ pre.mermaid:hover {
209
+ border-color: rgba(99, 102, 241, 0.3);
210
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
211
+ }
212
+
213
+ /* Enhanced metric cards */
214
+ .metric {
215
+ transition: all 0.3s ease;
216
+ position: relative;
217
+ overflow: hidden;
218
+ }
219
+
220
+ .metric::before {
221
+ content: '';
222
+ position: absolute;
223
+ top: 0;
224
+ left: 0;
225
+ right: 0;
226
+ height: 2px;
227
+ background: linear-gradient(90deg, transparent, rgb(99, 102, 241), transparent);
228
+ transform: translateX(-100%);
229
+ transition: transform 0.6s ease;
230
+ }
231
+
232
+ .metric:hover::before {
233
+ transform: translateX(100%);
234
+ }
235
+
236
+ .metric:hover {
237
+ transform: translateY(-2px);
238
+ border-color: rgba(99, 102, 241, 0.3);
239
+ }
240
+
241
+ .metric-value {
242
+ transition: all 0.3s ease;
243
+ }
244
+
245
+ .metric:hover .metric-value {
246
+ color: rgb(129, 140, 248);
247
+ transform: scale(1.05);
248
+ }
249
+
250
+ /* Enhanced avatar styling */
251
+ .avatar {
252
+ transition: all 0.3s ease;
253
+ position: relative;
254
+ overflow: hidden;
255
+ }
256
+
257
+ .avatar::before {
258
+ content: '';
259
+ position: absolute;
260
+ top: 0;
261
+ left: 0;
262
+ right: 0;
263
+ bottom: 0;
264
+ background: linear-gradient(45deg, transparent, rgba(99, 102, 241, 0.1), transparent);
265
+ transform: translateX(-100%) translateY(-100%);
266
+ transition: transform 0.6s ease;
267
+ }
268
+
269
+ .avatar:hover::before {
270
+ transform: translateX(100%) translateY(100%);
271
+ }
272
+
273
+ .avatar:hover {
274
+ transform: translateY(-2px);
275
+ border-color: rgba(99, 102, 241, 0.3);
276
+ background: rgba(99, 102, 241, 0.05);
277
+ }
278
+
279
+ /* Enhanced responsive improvements */
280
+ @media (max-width: 1024px) {
281
+ .container {
282
+ padding-left: 1rem;
283
+ padding-right: 1rem;
284
+ }
285
+
286
+ .section {
287
+ padding-top: 3rem;
288
+ padding-bottom: 3rem;
289
+ }
290
+
291
+ .section-header h2 {
292
+ font-size: 2.5rem;
293
+ }
294
+ }
295
+
296
+ @media (max-width: 768px) {
297
+ .section-header h2 {
298
+ font-size: 2rem;
299
+ line-height: 1.2;
300
+ }
301
+
302
+ .section-header p {
303
+ font-size: 0.9rem;
304
+ }
305
+
306
+ .card-body {
307
+ padding: 1rem;
308
+ }
309
+
310
+ .feature {
311
+ padding: 1rem;
312
+ }
313
+
314
+ .metric-value {
315
+ font-size: 2rem;
316
+ }
317
+
318
+ .tabs {
319
+ flex-direction: column;
320
+ gap: 0.5rem;
321
+ }
322
+
323
+ .tab {
324
+ text-align: center;
325
+ padding: 0.75rem 1rem;
326
+ }
327
+
328
+ /* Hero section mobile improvements */
329
+ .hero h1 {
330
+ font-size: 2.5rem;
331
+ line-height: 1.1;
332
+ }
333
+
334
+ .hero p {
335
+ font-size: 1rem;
336
+ }
337
+
338
+ /* Button improvements for mobile */
339
+ .btn-primary,
340
+ .btn-secondary,
341
+ .btn-ghost {
342
+ padding: 0.75rem 1.5rem;
343
+ font-size: 0.9rem;
344
+ }
345
+
346
+ /* Chip improvements for mobile */
347
+ .chip {
348
+ padding: 0.5rem 1rem;
349
+ font-size: 0.8rem;
350
+ }
351
+
352
+ /* Diagram improvements for mobile */
353
+ pre.mermaid {
354
+ font-size: 0.8rem;
355
+ padding: 1rem;
356
+ overflow-x: auto;
357
+ }
358
+
359
+ /* Metric cards mobile layout */
360
+ .metric {
361
+ padding: 1rem;
362
+ }
363
+
364
+ .metric-value {
365
+ font-size: 1.75rem;
366
+ }
367
+
368
+ /* Avatar improvements for mobile */
369
+ .avatar {
370
+ padding: 1rem;
371
+ font-size: 0.9rem;
372
+ }
373
+
374
+ /* Navigation improvements */
375
+ nav {
376
+ display: none;
377
+ }
378
+
379
+ /* Header mobile layout */
380
+ header .flex {
381
+ gap: 1rem;
382
+ }
383
+
384
+ /* Feature grid mobile */
385
+ .grid.md\\:grid-cols-3 {
386
+ grid-template-columns: 1fr;
387
+ gap: 1rem;
388
+ }
389
+
390
+ .grid.md\\:grid-cols-2 {
391
+ grid-template-columns: 1fr;
392
+ gap: 1rem;
393
+ }
394
+
395
+ .grid.lg\\:grid-cols-2 {
396
+ grid-template-columns: 1fr;
397
+ gap: 1rem;
398
+ }
399
+
400
+ .grid.lg\\:grid-cols-3 {
401
+ grid-template-columns: 1fr;
402
+ gap: 1rem;
403
+ }
404
+
405
+ .grid.md\\:grid-cols-5 {
406
+ grid-template-columns: repeat(2, 1fr);
407
+ gap: 0.75rem;
408
+ }
409
+ }
410
+
411
+ @media (max-width: 480px) {
412
+ .container {
413
+ padding-left: 0.75rem;
414
+ padding-right: 0.75rem;
415
+ }
416
+
417
+ .section {
418
+ padding-top: 2rem;
419
+ padding-bottom: 2rem;
420
+ }
421
+
422
+ .section-header h2 {
423
+ font-size: 1.75rem;
424
+ }
425
+
426
+ .hero h1 {
427
+ font-size: 2rem;
428
+ }
429
+
430
+ .hero p {
431
+ font-size: 0.9rem;
432
+ }
433
+
434
+ .btn-primary,
435
+ .btn-secondary,
436
+ .btn-ghost {
437
+ padding: 0.625rem 1.25rem;
438
+ font-size: 0.85rem;
439
+ }
440
+
441
+ .chip {
442
+ padding: 0.375rem 0.75rem;
443
+ font-size: 0.75rem;
444
+ }
445
+
446
+ .metric-value {
447
+ font-size: 1.5rem;
448
+ }
449
+
450
+ .avatar {
451
+ padding: 0.75rem;
452
+ font-size: 0.8rem;
453
+ }
454
+
455
+ .grid.md\\:grid-cols-5 {
456
+ grid-template-columns: 1fr;
457
+ gap: 0.5rem;
458
+ }
459
+
460
+ /* Stack hero content vertically on very small screens */
461
+ .hero .grid {
462
+ grid-template-columns: 1fr;
463
+ gap: 2rem;
464
+ }
465
+
466
+ .hero .grid > div:first-child {
467
+ order: 2;
468
+ }
469
+
470
+ .hero .grid > div:last-child {
471
+ order: 1;
472
+ }
473
+ }
474
+
475
+ /* Print styles */
476
+ @media print {
477
+ .btn-primary,
478
+ .btn-secondary,
479
+ .btn-ghost,
480
+ .btn-icon {
481
+ background: none !important;
482
+ color: black !important;
483
+ border: 1px solid black !important;
484
+ }
485
+
486
+ .card,
487
+ .feature {
488
+ border: 1px solid #ccc !important;
489
+ background: white !important;
490
+ }
491
+
492
+ .shadow-glow {
493
+ box-shadow: none !important;
494
+ }
495
+
496
+ .animate-pulse-slow {
497
+ animation: none !important;
498
+ }
499
+ }
500
+
501
+ /* Focus states for accessibility */
502
+ .btn-primary:focus,
503
+ .btn-secondary:focus,
504
+ .btn-ghost:focus,
505
+ .tab:focus,
506
+ .chip:focus {
507
+ outline: 2px solid rgb(99, 102, 241);
508
+ outline-offset: 2px;
509
+ }
510
+
511
+ /* Loading states */
512
+ .loading {
513
+ position: relative;
514
+ overflow: hidden;
515
+ }
516
+
517
+ .loading::after {
518
+ content: '';
519
+ position: absolute;
520
+ top: 0;
521
+ left: -100%;
522
+ width: 100%;
523
+ height: 100%;
524
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent);
525
+ animation: loading 1.5s infinite;
526
+ }
527
+
528
+ @keyframes loading {
529
+ 0% { left: -100%; }
530
+ 100% { left: 100%; }
531
+ }
index.html CHANGED
@@ -47,67 +47,67 @@
47
  </div>
48
 
49
  <!-- Navbar -->
50
- <header class="sticky top-0 z-50 backdrop-blur supports-[backdrop-filter]:bg-slate-900/60 border-b border-white/10">
51
  <div class="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
52
  <div class="flex items-center gap-3">
53
- <img src="assets/logo.svg" class="w-9 h-9" alt="MedAI logo" />
54
  <span class="font-extrabold tracking-tight text-xl">MedAI</span>
55
- <span class="text-slate-400 hidden sm:inline">• COS30018</span>
56
  </div>
57
- <nav class="hidden md:flex items-center gap-6 text-sm">
58
- <a href="#overview" class="link">Overview</a>
59
- <a href="#capabilities" class="link">Capabilities</a>
60
- <a href="#architecture" class="link">Architecture</a>
61
- <a href="#training" class="link">Training</a>
62
- <a href="#evaluation" class="link">Evaluation</a>
63
- <a href="#team" class="link">Team</a>
64
  </nav>
65
  <div class="flex items-center gap-3">
66
- <a href="https://huggingface.co/spaces/MedAI-COS30018/MedicalDiagnosisSystem" target="_blank" class="btn-primary">Live App</a>
67
- <button id="themeToggle" aria-label="Toggle theme" class="btn-icon">
68
- <i data-lucide="sun-medium" class="icon"></i>
69
  </button>
70
  </div>
71
  </div>
72
  </header>
73
 
74
  <!-- Hero -->
75
- <section class="relative">
76
  <div class="max-w-7xl mx-auto px-4 py-16 lg:py-24">
77
  <div class="grid lg:grid-cols-2 gap-12 items-center">
78
  <div data-aos="fade-right" data-aos-duration="700">
79
- <h1 class="text-4xl lg:text-6xl font-extrabold leading-tight">
80
  Multi‑Agent <span class="text-indigo-400">Clinical Reasoning</span> System
81
  </h1>
82
  <p class="mt-5 text-slate-300 text-lg max-w-2xl">
83
  Safety‑first medical assistant coordinating Diagnostics, Pharmacology, and Triage agents via an MCP orchestrator,
84
  grounded by Agentic RAG over EMR/EHR and PubMed, and rigorously evaluated on MedMCQA and PubMedQA.
85
  </p>
86
- <div class="mt-8 flex flex-wrap gap-3">
87
- <a class="btn-secondary" href="https://huggingface.co/spaces/MedAI-COS30018/MedAI_Processing" target="_blank">Data Processing</a>
88
- <a class="btn-ghost" href="#architecture">See Architecture</a>
89
  </div>
90
- <div class="mt-6 flex items-center gap-6 text-sm text-slate-400">
91
  <span>Unit: COS30018 (Intelligent Systems)</span>
92
- <span>•</span>
93
  <span>School: Swinburne University of Technology</span>
94
  </div>
95
  </div>
96
  <div class="relative" data-aos="fade-left" data-aos-duration="700">
97
- <div class="card glass shadow-glow">
98
  <div class="p-6">
99
- <h3 class="font-semibold text-slate-200 mb-3">At a Glance</h3>
100
- <ul class="space-y-2 text-slate-300">
101
  <li>• 500k+ curated & synthetic cases</li>
102
  <li>• LoRA/QLoRA, Knowledge Distillation, GRPO</li>
103
  <li>• Agentic RAG (Node & Graph) with citations</li>
104
  <li>• Safety rails, HIL, uncertainty flags</li>
105
  <li>• HPC‑reproducible training</li>
106
  </ul>
107
- <div class="mt-6 flex gap-3">
108
- <a class="chip" href="https://huggingface.co/MedAI-COS30018" target="_blank">HF Org</a>
109
- <a class="chip" href="https://huggingface.co/collections/MedAI-COS30018/finetuning-68bbe0b045db4ed5aac5e9a2" target="_blank">Finetune Datasets</a>
110
- <a class="chip" href="https://huggingface.co/collections/MedAI-COS30018/rag-68bbe244bfa8ce6132fd5242" target="_blank">RAG Datasets</a>
111
  </div>
112
  </div>
113
  <div class="border-t border-white/10 p-4 text-xs text-slate-400">
@@ -195,79 +195,79 @@
195
  </section>
196
 
197
  <!-- Architecture -->
198
- <section id="architecture" class="section">
199
  <div class="container">
200
  <div class="section-header" data-aos="fade-up">
201
- <h2>Architecture</h2>
202
  <p>Click tabs to switch between layers.</p>
203
  </div>
204
  <div class="tabs mb-6" role="tablist" aria-label="Architecture layers">
205
- <button class="tab active" data-target="#arch-l1">L1 System</button>
206
- <button class="tab" data-target="#arch-l2">L2 RAG Internals</button>
207
- <button class="tab" data-target="#arch-l3">L3 Single Turn</button>
208
  </div>
209
 
210
- <div id="arch-l1" class="diagram visible">
211
- <pre class="mermaid">
212
- flowchart LR
213
- U([Clinician UI / EMR]) -->|symptoms, meds, files| MCP[MCP Orchestrator<br/>FastAPI routing, planning, safety, tracing]
214
- MCP --> DX[Diagnostics Agent]
215
- MCP --> RX[Pharmacology Agent]
216
- MCP --> TR[Triage Agent]
217
- subgraph RAG[Agentic RAG]
218
- QR[Query Router] --> RET[Retriever]
219
- RET --> SR[Safety Rails]
220
- end
221
- DX --> RAG
222
- RX --> RAG
223
- TR --> RAG
224
- SR --> KB[(Med KB / PubMed)]
225
- SR --> EMR[(EMR/EHR summaries)]
226
- DX --> FUSE[Evidence Fusion + Self-Consistency]
227
- RX --> FUSE
228
- TR --> FUSE
229
- FUSE --> OUT{{Final Report<br/>summary, plan, citations, cautions}}
230
- OUT --> QA[Evaluation & QA<br/>MedMCQA, PubMedQA, similarity audits]
231
- </pre>
232
  </div>
233
 
234
- <div id="arch-l2" class="diagram">
235
- <pre class="mermaid">
236
- flowchart LR
237
- subgraph AGENTIC_RAG[Agentic RAG]
238
- IN[Inputs<br/>symptoms, vitals, meds, files, LTM] --> QR[Query Router]
239
- QR --> RW[Rewriter / Disambiguator]
240
- RW --> RET[Retriever (vector + keyword)]
241
- RET -->|top-k| EVID[Evidence Bundle]
242
- RET -->|filter τ| SR[Safety Rails]
243
- SR --> EVID
244
- EVID -->|write traces| LTM[(VectorDB / LTM)]
245
- LTM --> RET
246
- EVID --> KB[(Med KB: PubMed / Guidelines)]
247
- EVID --> EMR[(EMR Summaries)]
248
- end
249
- </pre>
250
  </div>
251
 
252
- <div id="arch-l3" class="diagram">
253
- <pre class="mermaid">
254
- sequenceDiagram
255
- participant C as Clinician
256
- participant MCP as Orchestrator (MCP)
257
- participant RAG as RAG (Router→Retriever→Safety)
258
- participant AG as Agents (Dx/Rx/Triage)
259
- participant DB as VectorDB/LTM
260
- participant OUT as Evidence Fusion
261
- C->>MCP: case description
262
- MCP->>RAG: retrieve evidence
263
- RAG->>DB: similarity search
264
- DB-->>RAG: top-k docs
265
- RAG-->>MCP: evidence bundle + citations
266
- MCP->>AG: delegated tasks
267
- AG-->>OUT: candidate answers + rationales
268
- OUT-->>C: final report + citations + cautions
269
- OUT->>DB: write traces/embeddings
270
- </pre>
271
  </div>
272
  </div>
273
  </section>
 
47
  </div>
48
 
49
  <!-- Navbar -->
50
+ <header class="sticky top-0 z-50 backdrop-blur supports-[backdrop-filter]:bg-slate-900/60 border-b border-white/10" role="banner">
51
  <div class="max-w-7xl mx-auto px-4 py-3 flex items-center justify-between">
52
  <div class="flex items-center gap-3">
53
+ <img src="assets/logo.svg" class="w-9 h-9" alt="MedAI logo" width="36" height="36" />
54
  <span class="font-extrabold tracking-tight text-xl">MedAI</span>
55
+ <span class="text-slate-400 hidden sm:inline" aria-label="Course code">• COS30018</span>
56
  </div>
57
+ <nav class="hidden md:flex items-center gap-6 text-sm" role="navigation" aria-label="Main navigation">
58
+ <a href="#overview" class="link" aria-label="Go to Overview section">Overview</a>
59
+ <a href="#capabilities" class="link" aria-label="Go to Capabilities section">Capabilities</a>
60
+ <a href="#architecture" class="link" aria-label="Go to Architecture section">Architecture</a>
61
+ <a href="#training" class="link" aria-label="Go to Training section">Training</a>
62
+ <a href="#evaluation" class="link" aria-label="Go to Evaluation section">Evaluation</a>
63
+ <a href="#team" class="link" aria-label="Go to Team section">Team</a>
64
  </nav>
65
  <div class="flex items-center gap-3">
66
+ <a href="https://huggingface.co/spaces/MedAI-COS30018/MedicalDiagnosisSystem" target="_blank" rel="noopener noreferrer" class="btn-primary" aria-label="Open live application in new tab">Live App</a>
67
+ <button id="themeToggle" aria-label="Toggle between light and dark theme" class="btn-icon" type="button">
68
+ <i data-lucide="sun-medium" class="icon" aria-hidden="true"></i>
69
  </button>
70
  </div>
71
  </div>
72
  </header>
73
 
74
  <!-- Hero -->
75
+ <section class="relative" role="banner" aria-labelledby="hero-title">
76
  <div class="max-w-7xl mx-auto px-4 py-16 lg:py-24">
77
  <div class="grid lg:grid-cols-2 gap-12 items-center">
78
  <div data-aos="fade-right" data-aos-duration="700">
79
+ <h1 id="hero-title" class="text-4xl lg:text-6xl font-extrabold leading-tight">
80
  Multi‑Agent <span class="text-indigo-400">Clinical Reasoning</span> System
81
  </h1>
82
  <p class="mt-5 text-slate-300 text-lg max-w-2xl">
83
  Safety‑first medical assistant coordinating Diagnostics, Pharmacology, and Triage agents via an MCP orchestrator,
84
  grounded by Agentic RAG over EMR/EHR and PubMed, and rigorously evaluated on MedMCQA and PubMedQA.
85
  </p>
86
+ <div class="mt-8 flex flex-wrap gap-3" role="group" aria-label="Action buttons">
87
+ <a class="btn-secondary" href="https://huggingface.co/spaces/MedAI-COS30018/MedAI_Processing" target="_blank" rel="noopener noreferrer" aria-label="Open data processing application">Data Processing</a>
88
+ <a class="btn-ghost" href="#architecture" aria-label="Scroll to architecture section">See Architecture</a>
89
  </div>
90
+ <div class="mt-6 flex items-center gap-6 text-sm text-slate-400" role="contentinfo">
91
  <span>Unit: COS30018 (Intelligent Systems)</span>
92
+ <span aria-hidden="true">•</span>
93
  <span>School: Swinburne University of Technology</span>
94
  </div>
95
  </div>
96
  <div class="relative" data-aos="fade-left" data-aos-duration="700">
97
+ <div class="card glass shadow-glow" role="complementary" aria-labelledby="glance-title">
98
  <div class="p-6">
99
+ <h3 id="glance-title" class="font-semibold text-slate-200 mb-3">At a Glance</h3>
100
+ <ul class="space-y-2 text-slate-300" role="list">
101
  <li>• 500k+ curated & synthetic cases</li>
102
  <li>• LoRA/QLoRA, Knowledge Distillation, GRPO</li>
103
  <li>• Agentic RAG (Node & Graph) with citations</li>
104
  <li>• Safety rails, HIL, uncertainty flags</li>
105
  <li>• HPC‑reproducible training</li>
106
  </ul>
107
+ <div class="mt-6 flex gap-3" role="group" aria-label="External resources">
108
+ <a class="chip" href="https://huggingface.co/MedAI-COS30018" target="_blank" rel="noopener noreferrer" aria-label="Visit Hugging Face organization">HF Org</a>
109
+ <a class="chip" href="https://huggingface.co/collections/MedAI-COS30018/finetuning-68bbe0b045db4ed5aac5e9a2" target="_blank" rel="noopener noreferrer" aria-label="View fine-tuning datasets">Finetune Datasets</a>
110
+ <a class="chip" href="https://huggingface.co/collections/MedAI-COS30018/rag-68bbe244bfa8ce6132fd5242" target="_blank" rel="noopener noreferrer" aria-label="View RAG datasets">RAG Datasets</a>
111
  </div>
112
  </div>
113
  <div class="border-t border-white/10 p-4 text-xs text-slate-400">
 
195
  </section>
196
 
197
  <!-- Architecture -->
198
+ <section id="architecture" class="section" aria-labelledby="architecture-title">
199
  <div class="container">
200
  <div class="section-header" data-aos="fade-up">
201
+ <h2 id="architecture-title">Architecture</h2>
202
  <p>Click tabs to switch between layers.</p>
203
  </div>
204
  <div class="tabs mb-6" role="tablist" aria-label="Architecture layers">
205
+ <button class="tab active" data-target="#arch-l1" role="tab" aria-selected="true" aria-controls="arch-l1" id="tab-l1">L1 System</button>
206
+ <button class="tab" data-target="#arch-l2" role="tab" aria-selected="false" aria-controls="arch-l2" id="tab-l2">L2 RAG Internals</button>
207
+ <button class="tab" data-target="#arch-l3" role="tab" aria-selected="false" aria-controls="arch-l3" id="tab-l3">L3 Single Turn</button>
208
  </div>
209
 
210
+ <div id="arch-l1" class="diagram visible" role="tabpanel" aria-labelledby="tab-l1" aria-hidden="false">
211
+ <pre class="mermaid" aria-label="L1 System Architecture Diagram">
212
+ flowchart LR
213
+ U([Clinician UI / EMR]) -->|symptoms, meds, files| MCP[MCP Orchestrator<br/>FastAPI routing, planning, safety, tracing]
214
+ MCP --> DX[Diagnostics Agent]
215
+ MCP --> RX[Pharmacology Agent]
216
+ MCP --> TR[Triage Agent]
217
+ subgraph RAG[Agentic RAG]
218
+ QR[Query Router] --> RET[Retriever]
219
+ RET --> SR[Safety Rails]
220
+ end
221
+ DX --> RAG
222
+ RX --> RAG
223
+ TR --> RAG
224
+ SR --> KB[(Med KB / PubMed)]
225
+ SR --> EMR[(EMR/EHR summaries)]
226
+ DX --> FUSE[Evidence Fusion + Self-Consistency]
227
+ RX --> FUSE
228
+ TR --> FUSE
229
+ FUSE --> OUT{{Final Report<br/>summary, plan, citations, cautions}}
230
+ OUT --> QA[Evaluation & QA<br/>MedMCQA, PubMedQA, similarity audits]
231
+ </pre>
232
  </div>
233
 
234
+ <div id="arch-l2" class="diagram" role="tabpanel" aria-labelledby="tab-l2" aria-hidden="true">
235
+ <pre class="mermaid" aria-label="L2 RAG Internals Architecture Diagram">
236
+ flowchart LR
237
+ subgraph AGENTIC_RAG["Agentic RAG"]
238
+ IN["Inputs<br/>symptoms, vitals, meds, files, LTM"] --> QR["Query Router"]
239
+ QR --> RW["Rewriter / Disambiguator"]
240
+ RW --> RET["Retriever (vector + keyword)"]
241
+ RET -->|"top-k"| EVID["Evidence Bundle"]
242
+ RET -->|"filter τ"| SR["Safety Rails"]
243
+ SR --> EVID
244
+ EVID -->|"write traces"| LTM[("VectorDB / LTM")]
245
+ LTM --> RET
246
+ EVID --> KB[("Med KB: PubMed / Guidelines")]
247
+ EVID --> EMR[("EMR Summaries")]
248
+ end
249
+ </pre>
250
  </div>
251
 
252
+ <div id="arch-l3" class="diagram" role="tabpanel" aria-labelledby="tab-l3" aria-hidden="true">
253
+ <pre class="mermaid" aria-label="L3 Single Turn Sequence Diagram">
254
+ sequenceDiagram
255
+ participant C as Clinician
256
+ participant MCP as Orchestrator (MCP)
257
+ participant RAG as RAG (Router→Retriever→Safety)
258
+ participant AG as Agents (Dx/Rx/Triage)
259
+ participant DB as VectorDB/LTM
260
+ participant OUT as Evidence Fusion
261
+ C->>MCP: case description
262
+ MCP->>RAG: retrieve evidence
263
+ RAG->>DB: similarity search
264
+ DB-->>RAG: top-k docs
265
+ RAG-->>MCP: evidence bundle + citations
266
+ MCP->>AG: delegated tasks
267
+ AG-->>OUT: candidate answers + rationales
268
+ OUT-->>C: final report + citations + cautions
269
+ OUT->>DB: write traces/embeddings
270
+ </pre>
271
  </div>
272
  </div>
273
  </section>