Testing347 commited on
Commit
12a1330
·
verified ·
1 Parent(s): 54481fe

Update capabilities.html

Browse files
Files changed (1) hide show
  1. capabilities.html +156 -28
capabilities.html CHANGED
@@ -78,6 +78,13 @@
78
  background-size: 40px 40px;
79
  background-position: center;
80
  }
 
 
 
 
 
 
 
81
  </style>
82
  </head>
83
 
@@ -97,12 +104,13 @@
97
  <div class="flex items-center space-x-3">
98
  <button id="lab-nav-btn"
99
  class="w-10 h-10 rounded-full border border-indigo-500/40 bg-gray-900/20 hover:bg-gray-900/40 backdrop-blur-sm transition flex items-center justify-center"
100
- aria-label="Open Lab Navigator" title="Lab Navigator">
101
  <i class="fas fa-asterisk text-indigo-300 text-sm"></i>
102
  </button>
103
 
104
  <button id="access-btn"
105
- class="px-6 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full hover:opacity-90 transition">
 
106
  Access
107
  </button>
108
  </div>
@@ -157,7 +165,7 @@
157
  <!-- MCAP -->
158
  <button type="button"
159
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
160
- data-dossier="mcap">
161
  <div class="flex items-start justify-between gap-4">
162
  <div>
163
  <div class="text-lg font-semibold text-gray-100">MCAP</div>
@@ -177,7 +185,7 @@
177
  <!-- CHAI -->
178
  <button type="button"
179
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
180
- data-dossier="chai">
181
  <div class="flex items-start justify-between gap-4">
182
  <div>
183
  <div class="text-lg font-semibold text-gray-100">CHAI</div>
@@ -197,7 +205,7 @@
197
  <!-- Quantum Lambda -->
198
  <button type="button"
199
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
200
- data-dossier="quantum_lambda">
201
  <div class="flex items-start justify-between gap-4">
202
  <div>
203
  <div class="text-lg font-semibold text-gray-100">Quantum Lambda</div>
@@ -217,7 +225,7 @@
217
  <!-- AI Scientist -->
218
  <button type="button"
219
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
220
- data-dossier="ai_scientist">
221
  <div class="flex items-start justify-between gap-4">
222
  <div>
223
  <div class="text-lg font-semibold text-gray-100">AI Scientist</div>
@@ -237,7 +245,7 @@
237
  <!-- Agentic Workforce -->
238
  <button type="button"
239
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element md:col-span-2"
240
- data-dossier="agentic_workforce">
241
  <div class="flex items-start justify-between gap-4">
242
  <div>
243
  <div class="text-lg font-semibold text-gray-100">Agentic Workforce</div>
@@ -396,18 +404,23 @@
396
  </button>
397
  </div>
398
 
399
- <form id="access-form" class="space-y-4">
 
 
 
 
 
400
  <div>
401
  <label for="name" class="block text-sm font-medium mb-1">Full Name</label>
402
- <input type="text" id="name" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
403
  </div>
404
  <div>
405
  <label for="email" class="block text-sm font-medium mb-1">Email</label>
406
- <input type="email" id="email" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
407
  </div>
408
  <div>
409
  <label for="institution" class="block text-sm font-medium mb-1">Institution/Organization</label>
410
- <input type="text" id="institution" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
411
  </div>
412
  <div>
413
  <label for="purpose" class="block text-sm font-medium mb-1">Purpose</label>
@@ -516,7 +529,9 @@
516
  </div>
517
 
518
  <script>
519
- /* VANTA BACKGROUND */
 
 
520
  const vantaEffect = VANTA.NET({
521
  el: "#vanta-bg",
522
  mouseControls: true,
@@ -534,7 +549,9 @@
534
  });
535
  window.addEventListener('resize', () => vantaEffect.resize());
536
 
537
- /* MODAL HELPERS */
 
 
538
  function trapFocus(modal) {
539
  const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
540
  if (!focusable.length) return;
@@ -555,12 +572,14 @@
555
  modal.addEventListener('keydown', handler);
556
  modal._focusHandler = handler;
557
  }
 
558
  function untrapFocus(modal) {
559
  if (modal._focusHandler) {
560
  modal.removeEventListener('keydown', modal._focusHandler);
561
  delete modal._focusHandler;
562
  }
563
  }
 
564
  const toggleModal = (modal, show) => {
565
  if (show) {
566
  modal.classList.remove('modal-hidden');
@@ -575,34 +594,82 @@
575
  }
576
  };
577
 
578
- /* ACCESS MODAL */
 
 
579
  const accessModal = document.getElementById('access-modal');
580
  const accessBtn = document.getElementById('access-btn');
581
  const closeAccessModal = document.getElementById('close-access-modal');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
 
583
  accessBtn.addEventListener('click', () => {
 
584
  toggleModal(accessModal, true);
585
  setTimeout(() => document.getElementById('name').focus(), 50);
586
  });
 
587
  closeAccessModal.addEventListener('click', () => toggleModal(accessModal, false));
588
  accessModal.addEventListener('click', (e) => { if (e.target === accessModal) toggleModal(accessModal, false); });
589
 
590
- document.getElementById('access-form').addEventListener('submit', (e) => {
 
 
 
 
 
591
  e.preventDefault();
 
592
  const name = document.getElementById('name').value.trim();
593
  const email = document.getElementById('email').value.trim();
594
  const institution = document.getElementById('institution').value.trim();
595
  const purpose = document.getElementById('purpose').value;
 
596
  if (!name || !email || !institution || !purpose) {
597
- alert('Please fill in all fields.');
 
 
 
 
598
  return;
599
  }
600
- alert('Request received. You will be contacted after review.');
601
- e.target.reset();
602
- toggleModal(accessModal, false);
 
 
 
 
603
  });
604
 
605
- /* LAB NAVIGATOR */
 
 
606
  const labNav = document.getElementById('lab-navigator');
607
  const labNavBtn = document.getElementById('lab-nav-btn');
608
  const labNavClose = document.getElementById('lab-nav-close');
@@ -625,7 +692,11 @@
625
  });
626
  });
627
 
628
- /* DOSSIERS */
 
 
 
 
629
  const DOSSIERS = {
630
  mcap: {
631
  title: "MCAP",
@@ -735,7 +806,31 @@
735
  const dossierSecondary = document.getElementById('dossier-secondary');
736
  const dossierMeta = document.getElementById('dossier-meta');
737
 
738
- function renderDossier(key) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
739
  const d = DOSSIERS[key];
740
  if (!d) return;
741
 
@@ -762,22 +857,55 @@
762
  dossierSecondary.onclick = d.secondary.action;
763
 
764
  dossierMeta.innerHTML = `Last updated: <span class="text-gray-300">${d.updated}</span>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
765
  }
766
 
767
- document.querySelectorAll('.program-card').forEach(btn => {
768
- btn.addEventListener('click', () => renderDossier(btn.getAttribute('data-dossier')));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
769
  });
770
 
771
- /* ESC behavior */
 
 
772
  document.addEventListener('keydown', (e) => {
773
  if (e.key === 'Escape') {
774
  if (labNav && !labNav.classList.contains('modal-hidden')) closeLabNav();
775
  if (accessModal && !accessModal.classList.contains('modal-hidden')) toggleModal(accessModal, false);
776
  }
777
  });
778
-
779
- /* Default dossier */
780
- renderDossier('mcap');
781
  </script>
782
  </body>
783
  </html>
 
78
  background-size: 40px 40px;
79
  background-position: center;
80
  }
81
+
82
+ /* Active card state (minimal, consistent with design) */
83
+ .program-card.is-active {
84
+ border-color: rgba(99,102,241,0.55) !important;
85
+ box-shadow: 0 0 0 1px rgba(99,102,241,0.22), 0 0 28px rgba(99,102,241,0.08);
86
+ transform: translateY(-1px);
87
+ }
88
  </style>
89
  </head>
90
 
 
104
  <div class="flex items-center space-x-3">
105
  <button id="lab-nav-btn"
106
  class="w-10 h-10 rounded-full border border-indigo-500/40 bg-gray-900/20 hover:bg-gray-900/40 backdrop-blur-sm transition flex items-center justify-center"
107
+ aria-label="Open Lab Navigator" title="Lab Navigator" aria-controls="lab-navigator" aria-haspopup="dialog">
108
  <i class="fas fa-asterisk text-indigo-300 text-sm"></i>
109
  </button>
110
 
111
  <button id="access-btn"
112
+ class="px-6 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full hover:opacity-90 transition"
113
+ aria-controls="access-modal" aria-haspopup="dialog">
114
  Access
115
  </button>
116
  </div>
 
165
  <!-- MCAP -->
166
  <button type="button"
167
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
168
+ data-dossier="mcap" aria-label="Open dossier: MCAP">
169
  <div class="flex items-start justify-between gap-4">
170
  <div>
171
  <div class="text-lg font-semibold text-gray-100">MCAP</div>
 
185
  <!-- CHAI -->
186
  <button type="button"
187
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
188
+ data-dossier="chai" aria-label="Open dossier: CHAI">
189
  <div class="flex items-start justify-between gap-4">
190
  <div>
191
  <div class="text-lg font-semibold text-gray-100">CHAI</div>
 
205
  <!-- Quantum Lambda -->
206
  <button type="button"
207
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
208
+ data-dossier="quantum_lambda" aria-label="Open dossier: Quantum Lambda">
209
  <div class="flex items-start justify-between gap-4">
210
  <div>
211
  <div class="text-lg font-semibold text-gray-100">Quantum Lambda</div>
 
225
  <!-- AI Scientist -->
226
  <button type="button"
227
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element"
228
+ data-dossier="ai_scientist" aria-label="Open dossier: AI Scientist">
229
  <div class="flex items-start justify-between gap-4">
230
  <div>
231
  <div class="text-lg font-semibold text-gray-100">AI Scientist</div>
 
245
  <!-- Agentic Workforce -->
246
  <button type="button"
247
  class="program-card text-left rounded-2xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/55 hover:bg-gray-900/45 transition p-5 conscious-element md:col-span-2"
248
+ data-dossier="agentic_workforce" aria-label="Open dossier: Agentic Workforce">
249
  <div class="flex items-start justify-between gap-4">
250
  <div>
251
  <div class="text-lg font-semibold text-gray-100">Agentic Workforce</div>
 
404
  </button>
405
  </div>
406
 
407
+ <!-- Inline feedback (replaces alert) -->
408
+ <div id="access-feedback"
409
+ class="hidden mb-4 rounded-lg border border-gray-800 bg-black/25 px-4 py-3 text-sm"
410
+ role="status" aria-live="polite"></div>
411
+
412
+ <form id="access-form" class="space-y-4" novalidate>
413
  <div>
414
  <label for="name" class="block text-sm font-medium mb-1">Full Name</label>
415
+ <input type="text" id="name" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring" autocomplete="name">
416
  </div>
417
  <div>
418
  <label for="email" class="block text-sm font-medium mb-1">Email</label>
419
+ <input type="email" id="email" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring" autocomplete="email">
420
  </div>
421
  <div>
422
  <label for="institution" class="block text-sm font-medium mb-1">Institution/Organization</label>
423
+ <input type="text" id="institution" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring" autocomplete="organization">
424
  </div>
425
  <div>
426
  <label for="purpose" class="block text-sm font-medium mb-1">Purpose</label>
 
529
  </div>
530
 
531
  <script>
532
+ /* -------------------------------------------------------------
533
+ VANTA BACKGROUND
534
+ ------------------------------------------------------------- */
535
  const vantaEffect = VANTA.NET({
536
  el: "#vanta-bg",
537
  mouseControls: true,
 
549
  });
550
  window.addEventListener('resize', () => vantaEffect.resize());
551
 
552
+ /* -------------------------------------------------------------
553
+ MODAL HELPERS
554
+ ------------------------------------------------------------- */
555
  function trapFocus(modal) {
556
  const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
557
  if (!focusable.length) return;
 
572
  modal.addEventListener('keydown', handler);
573
  modal._focusHandler = handler;
574
  }
575
+
576
  function untrapFocus(modal) {
577
  if (modal._focusHandler) {
578
  modal.removeEventListener('keydown', modal._focusHandler);
579
  delete modal._focusHandler;
580
  }
581
  }
582
+
583
  const toggleModal = (modal, show) => {
584
  if (show) {
585
  modal.classList.remove('modal-hidden');
 
594
  }
595
  };
596
 
597
+ /* -------------------------------------------------------------
598
+ ACCESS MODAL + INLINE FEEDBACK (replaces alerts)
599
+ ------------------------------------------------------------- */
600
  const accessModal = document.getElementById('access-modal');
601
  const accessBtn = document.getElementById('access-btn');
602
  const closeAccessModal = document.getElementById('close-access-modal');
603
+ const accessForm = document.getElementById('access-form');
604
+ const accessFeedback = document.getElementById('access-feedback');
605
+
606
+ function setAccessFeedback(kind, text) {
607
+ // kind: 'error' | 'success' | 'info'
608
+ accessFeedback.classList.remove('hidden');
609
+ accessFeedback.classList.remove('border-red-500/30', 'bg-red-900/10', 'text-red-200');
610
+ accessFeedback.classList.remove('border-emerald-500/30', 'bg-emerald-900/10', 'text-emerald-200');
611
+ accessFeedback.classList.remove('border-indigo-500/30', 'bg-indigo-900/10', 'text-indigo-200');
612
+
613
+ if (kind === 'error') {
614
+ accessFeedback.classList.add('border-red-500/30', 'bg-red-900/10', 'text-red-200');
615
+ } else if (kind === 'success') {
616
+ accessFeedback.classList.add('border-emerald-500/30', 'bg-emerald-900/10', 'text-emerald-200');
617
+ } else {
618
+ accessFeedback.classList.add('border-indigo-500/30', 'bg-indigo-900/10', 'text-indigo-200');
619
+ }
620
+
621
+ accessFeedback.textContent = text;
622
+ }
623
+
624
+ function clearAccessFeedback() {
625
+ accessFeedback.classList.add('hidden');
626
+ accessFeedback.textContent = '';
627
+ accessFeedback.className = 'hidden mb-4 rounded-lg border border-gray-800 bg-black/25 px-4 py-3 text-sm';
628
+ // Restore base classes in case they were overwritten by className (above line sets baseline)
629
+ }
630
 
631
  accessBtn.addEventListener('click', () => {
632
+ clearAccessFeedback();
633
  toggleModal(accessModal, true);
634
  setTimeout(() => document.getElementById('name').focus(), 50);
635
  });
636
+
637
  closeAccessModal.addEventListener('click', () => toggleModal(accessModal, false));
638
  accessModal.addEventListener('click', (e) => { if (e.target === accessModal) toggleModal(accessModal, false); });
639
 
640
+ function isValidEmail(email) {
641
+ // Lightweight sanity check (not exhaustive)
642
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
643
+ }
644
+
645
+ accessForm.addEventListener('submit', (e) => {
646
  e.preventDefault();
647
+
648
  const name = document.getElementById('name').value.trim();
649
  const email = document.getElementById('email').value.trim();
650
  const institution = document.getElementById('institution').value.trim();
651
  const purpose = document.getElementById('purpose').value;
652
+
653
  if (!name || !email || !institution || !purpose) {
654
+ setAccessFeedback('error', 'Please fill in all fields.');
655
+ return;
656
+ }
657
+ if (!isValidEmail(email)) {
658
+ setAccessFeedback('error', 'Please enter a valid email address.');
659
  return;
660
  }
661
+
662
+ // Static-only behavior: confirm receipt, keep it calm and “lab-like”.
663
+ setAccessFeedback('success', 'Request received. You will be contacted after review.');
664
+ accessForm.reset();
665
+
666
+ // Optional auto-close after a brief pause (keeps the flow, but not abrupt)
667
+ setTimeout(() => toggleModal(accessModal, false), 900);
668
  });
669
 
670
+ /* -------------------------------------------------------------
671
+ LAB NAVIGATOR
672
+ ------------------------------------------------------------- */
673
  const labNav = document.getElementById('lab-navigator');
674
  const labNavBtn = document.getElementById('lab-nav-btn');
675
  const labNavClose = document.getElementById('lab-nav-close');
 
692
  });
693
  });
694
 
695
+ /* -------------------------------------------------------------
696
+ DOSSIERS
697
+ + Hash deep-linking (#mcap, #chai, etc.)
698
+ + Active-card state
699
+ ------------------------------------------------------------- */
700
  const DOSSIERS = {
701
  mcap: {
702
  title: "MCAP",
 
806
  const dossierSecondary = document.getElementById('dossier-secondary');
807
  const dossierMeta = document.getElementById('dossier-meta');
808
 
809
+ const programCards = Array.from(document.querySelectorAll('.program-card'));
810
+
811
+ function setActiveCard(key) {
812
+ programCards.forEach(btn => {
813
+ const isMatch = btn.getAttribute('data-dossier') === key;
814
+ btn.classList.toggle('is-active', isMatch);
815
+ btn.setAttribute('aria-current', isMatch ? 'true' : 'false');
816
+ });
817
+ }
818
+
819
+ function updateHash(key, replace = false) {
820
+ const nextHash = `#${key}`;
821
+ if (replace) {
822
+ history.replaceState(null, '', nextHash);
823
+ } else {
824
+ history.pushState(null, '', nextHash);
825
+ }
826
+ }
827
+
828
+ function normalizeHashToKey() {
829
+ const raw = (window.location.hash || '').replace('#', '').trim().toLowerCase();
830
+ return raw && DOSSIERS[raw] ? raw : null;
831
+ }
832
+
833
+ function renderDossier(key, opts = { setHash: true, replaceHash: false, setActive: true }) {
834
  const d = DOSSIERS[key];
835
  if (!d) return;
836
 
 
857
  dossierSecondary.onclick = d.secondary.action;
858
 
859
  dossierMeta.innerHTML = `Last updated: <span class="text-gray-300">${d.updated}</span>`;
860
+
861
+ if (opts.setActive) setActiveCard(key);
862
+ if (opts.setHash) updateHash(key, opts.replaceHash);
863
+
864
+ // Keep the dossier panel in view on small screens without changing layout
865
+ // (only when user selects a card)
866
+ if (!opts.replaceHash) {
867
+ const isSmall = window.matchMedia && window.matchMedia('(max-width: 1023px)').matches;
868
+ if (isSmall) {
869
+ // subtle nudge: bring dossier header into view
870
+ dossierTitle.scrollIntoView({ behavior: 'smooth', block: 'start' });
871
+ }
872
+ }
873
  }
874
 
875
+ // Card click: render dossier + update hash + active state
876
+ programCards.forEach(btn => {
877
+ btn.addEventListener('click', () => {
878
+ const key = btn.getAttribute('data-dossier');
879
+ if (!key) return;
880
+ renderDossier(key, { setHash: true, replaceHash: false, setActive: true });
881
+ });
882
+ });
883
+
884
+ // Hash deep-link: initial load
885
+ (function initFromHashOrDefault() {
886
+ const keyFromHash = normalizeHashToKey();
887
+ const key = keyFromHash || 'mcap';
888
+ // replaceHash avoids adding an extra history entry on first load
889
+ renderDossier(key, { setHash: true, replaceHash: true, setActive: true });
890
+ })();
891
+
892
+ // Respond to manual hash changes / back-forward navigation
893
+ window.addEventListener('hashchange', () => {
894
+ const keyFromHash = normalizeHashToKey();
895
+ if (keyFromHash) {
896
+ renderDossier(keyFromHash, { setHash: false, replaceHash: false, setActive: true });
897
+ }
898
  });
899
 
900
+ /* -------------------------------------------------------------
901
+ GLOBAL ESC BEHAVIOR
902
+ ------------------------------------------------------------- */
903
  document.addEventListener('keydown', (e) => {
904
  if (e.key === 'Escape') {
905
  if (labNav && !labNav.classList.contains('modal-hidden')) closeLabNav();
906
  if (accessModal && !accessModal.classList.contains('modal-hidden')) toggleModal(accessModal, false);
907
  }
908
  });
 
 
 
909
  </script>
910
  </body>
911
  </html>