Spaces:
Running
Running
Update capabilities.html
Browse files- capabilities.html +24 -28
capabilities.html
CHANGED
|
@@ -8,9 +8,9 @@
|
|
| 8 |
<!-- Tailwind -->
|
| 9 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
|
| 11 |
-
<!-- Three.js + Vanta -->
|
| 12 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 13 |
-
<script src="https://cdn.jsdelivr.net/npm/vanta@
|
| 14 |
|
| 15 |
<!-- Icons + Font -->
|
| 16 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
@@ -165,7 +165,7 @@
|
|
| 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,7 +185,7 @@
|
|
| 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,7 +205,7 @@
|
|
| 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,7 +225,7 @@
|
|
| 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,7 +245,7 @@
|
|
| 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>
|
|
@@ -550,7 +550,7 @@
|
|
| 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"])');
|
|
@@ -565,8 +565,6 @@
|
|
| 565 |
} else {
|
| 566 |
if (document.activeElement === last) { e.preventDefault(); first.focus(); }
|
| 567 |
}
|
| 568 |
-
} else if (e.key === 'Escape') {
|
| 569 |
-
toggleModal(modal, false);
|
| 570 |
}
|
| 571 |
}
|
| 572 |
modal.addEventListener('keydown', handler);
|
|
@@ -582,6 +580,7 @@
|
|
| 582 |
|
| 583 |
const toggleModal = (modal, show) => {
|
| 584 |
if (show) {
|
|
|
|
| 585 |
modal.classList.remove('modal-hidden');
|
| 586 |
modal.classList.add('modal-visible');
|
| 587 |
document.body.style.overflow = 'hidden';
|
|
@@ -591,12 +590,16 @@
|
|
| 591 |
modal.classList.add('modal-hidden');
|
| 592 |
document.body.style.overflow = '';
|
| 593 |
untrapFocus(modal);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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');
|
|
@@ -622,10 +625,13 @@
|
|
| 622 |
}
|
| 623 |
|
| 624 |
function clearAccessFeedback() {
|
| 625 |
-
accessFeedback.classList.add('hidden');
|
| 626 |
accessFeedback.textContent = '';
|
| 627 |
-
accessFeedback.
|
| 628 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 629 |
}
|
| 630 |
|
| 631 |
accessBtn.addEventListener('click', () => {
|
|
@@ -638,7 +644,6 @@
|
|
| 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 |
|
|
@@ -659,11 +664,8 @@
|
|
| 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 |
|
|
@@ -695,7 +697,7 @@
|
|
| 695 |
/* -------------------------------------------------------------
|
| 696 |
DOSSIERS
|
| 697 |
+ Hash deep-linking (#mcap, #chai, etc.)
|
| 698 |
-
+ Active-card state
|
| 699 |
------------------------------------------------------------- */
|
| 700 |
const DOSSIERS = {
|
| 701 |
mcap: {
|
|
@@ -812,7 +814,8 @@
|
|
| 812 |
programCards.forEach(btn => {
|
| 813 |
const isMatch = btn.getAttribute('data-dossier') === key;
|
| 814 |
btn.classList.toggle('is-active', isMatch);
|
| 815 |
-
btn.setAttribute('aria-
|
|
|
|
| 816 |
});
|
| 817 |
}
|
| 818 |
|
|
@@ -861,18 +864,14 @@
|
|
| 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');
|
|
@@ -881,15 +880,12 @@
|
|
| 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) {
|
|
@@ -898,7 +894,7 @@
|
|
| 898 |
});
|
| 899 |
|
| 900 |
/* -------------------------------------------------------------
|
| 901 |
-
GLOBAL ESC BEHAVIOR
|
| 902 |
------------------------------------------------------------- */
|
| 903 |
document.addEventListener('keydown', (e) => {
|
| 904 |
if (e.key === 'Escape') {
|
|
|
|
| 8 |
<!-- Tailwind -->
|
| 9 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
|
| 11 |
+
<!-- Three.js + Vanta (pinned) -->
|
| 12 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
|
| 13 |
+
<script src="https://cdn.jsdelivr.net/npm/vanta@0.5.24/dist/vanta.net.min.js"></script>
|
| 14 |
|
| 15 |
<!-- Icons + Font -->
|
| 16 |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
|
|
| 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" aria-pressed="false">
|
| 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" aria-pressed="false">
|
| 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" aria-pressed="false">
|
| 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" aria-pressed="false">
|
| 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" aria-pressed="false">
|
| 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>
|
|
|
|
| 550 |
window.addEventListener('resize', () => vantaEffect.resize());
|
| 551 |
|
| 552 |
/* -------------------------------------------------------------
|
| 553 |
+
MODAL HELPERS (focus trap + restore opener focus)
|
| 554 |
------------------------------------------------------------- */
|
| 555 |
function trapFocus(modal) {
|
| 556 |
const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
|
|
| 565 |
} else {
|
| 566 |
if (document.activeElement === last) { e.preventDefault(); first.focus(); }
|
| 567 |
}
|
|
|
|
|
|
|
| 568 |
}
|
| 569 |
}
|
| 570 |
modal.addEventListener('keydown', handler);
|
|
|
|
| 580 |
|
| 581 |
const toggleModal = (modal, show) => {
|
| 582 |
if (show) {
|
| 583 |
+
modal._opener = document.activeElement; // restore target on close
|
| 584 |
modal.classList.remove('modal-hidden');
|
| 585 |
modal.classList.add('modal-visible');
|
| 586 |
document.body.style.overflow = 'hidden';
|
|
|
|
| 590 |
modal.classList.add('modal-hidden');
|
| 591 |
document.body.style.overflow = '';
|
| 592 |
untrapFocus(modal);
|
| 593 |
+
if (modal._opener && typeof modal._opener.focus === 'function') {
|
| 594 |
+
modal._opener.focus();
|
| 595 |
+
}
|
| 596 |
+
modal._opener = null;
|
| 597 |
}
|
| 598 |
};
|
| 599 |
|
| 600 |
/* -------------------------------------------------------------
|
| 601 |
ACCESS MODAL + INLINE FEEDBACK (replaces alerts)
|
| 602 |
+
------------------------------------------------------------- */
|
| 603 |
const accessModal = document.getElementById('access-modal');
|
| 604 |
const accessBtn = document.getElementById('access-btn');
|
| 605 |
const closeAccessModal = document.getElementById('close-access-modal');
|
|
|
|
| 625 |
}
|
| 626 |
|
| 627 |
function clearAccessFeedback() {
|
|
|
|
| 628 |
accessFeedback.textContent = '';
|
| 629 |
+
accessFeedback.classList.add('hidden');
|
| 630 |
+
accessFeedback.classList.remove(
|
| 631 |
+
'border-red-500/30','bg-red-900/10','text-red-200',
|
| 632 |
+
'border-emerald-500/30','bg-emerald-900/10','text-emerald-200',
|
| 633 |
+
'border-indigo-500/30','bg-indigo-900/10','text-indigo-200'
|
| 634 |
+
);
|
| 635 |
}
|
| 636 |
|
| 637 |
accessBtn.addEventListener('click', () => {
|
|
|
|
| 644 |
accessModal.addEventListener('click', (e) => { if (e.target === accessModal) toggleModal(accessModal, false); });
|
| 645 |
|
| 646 |
function isValidEmail(email) {
|
|
|
|
| 647 |
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
| 648 |
}
|
| 649 |
|
|
|
|
| 664 |
return;
|
| 665 |
}
|
| 666 |
|
|
|
|
| 667 |
setAccessFeedback('success', 'Request received. You will be contacted after review.');
|
| 668 |
accessForm.reset();
|
|
|
|
|
|
|
| 669 |
setTimeout(() => toggleModal(accessModal, false), 900);
|
| 670 |
});
|
| 671 |
|
|
|
|
| 697 |
/* -------------------------------------------------------------
|
| 698 |
DOSSIERS
|
| 699 |
+ Hash deep-linking (#mcap, #chai, etc.)
|
| 700 |
+
+ Active-card state (aria-pressed)
|
| 701 |
------------------------------------------------------------- */
|
| 702 |
const DOSSIERS = {
|
| 703 |
mcap: {
|
|
|
|
| 814 |
programCards.forEach(btn => {
|
| 815 |
const isMatch = btn.getAttribute('data-dossier') === key;
|
| 816 |
btn.classList.toggle('is-active', isMatch);
|
| 817 |
+
btn.setAttribute('aria-pressed', String(isMatch));
|
| 818 |
+
btn.removeAttribute('aria-current');
|
| 819 |
});
|
| 820 |
}
|
| 821 |
|
|
|
|
| 864 |
if (opts.setActive) setActiveCard(key);
|
| 865 |
if (opts.setHash) updateHash(key, opts.replaceHash);
|
| 866 |
|
|
|
|
|
|
|
| 867 |
if (!opts.replaceHash) {
|
| 868 |
const isSmall = window.matchMedia && window.matchMedia('(max-width: 1023px)').matches;
|
| 869 |
if (isSmall) {
|
|
|
|
| 870 |
dossierTitle.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
| 871 |
}
|
| 872 |
}
|
| 873 |
}
|
| 874 |
|
|
|
|
| 875 |
programCards.forEach(btn => {
|
| 876 |
btn.addEventListener('click', () => {
|
| 877 |
const key = btn.getAttribute('data-dossier');
|
|
|
|
| 880 |
});
|
| 881 |
});
|
| 882 |
|
|
|
|
| 883 |
(function initFromHashOrDefault() {
|
| 884 |
const keyFromHash = normalizeHashToKey();
|
| 885 |
const key = keyFromHash || 'mcap';
|
|
|
|
| 886 |
renderDossier(key, { setHash: true, replaceHash: true, setActive: true });
|
| 887 |
})();
|
| 888 |
|
|
|
|
| 889 |
window.addEventListener('hashchange', () => {
|
| 890 |
const keyFromHash = normalizeHashToKey();
|
| 891 |
if (keyFromHash) {
|
|
|
|
| 894 |
});
|
| 895 |
|
| 896 |
/* -------------------------------------------------------------
|
| 897 |
+
GLOBAL ESC BEHAVIOR (single source of truth)
|
| 898 |
------------------------------------------------------------- */
|
| 899 |
document.addEventListener('keydown', (e) => {
|
| 900 |
if (e.key === 'Escape') {
|