Testing347 commited on
Commit
37e6fe7
·
verified ·
1 Parent(s): f124ceb

Update consciousness.html

Browse files
Files changed (1) hide show
  1. consciousness.html +628 -148
consciousness.html CHANGED
@@ -1,3 +1,4 @@
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
@@ -65,6 +66,13 @@
65
  }
66
  .dot { width: 8px; height: 8px; border-radius: 999px; }
67
  .dot-concept { background: rgba(99,102,241,0.9); }
 
 
 
 
 
 
 
68
  </style>
69
  </head>
70
 
@@ -83,13 +91,13 @@
83
 
84
  <div class="flex items-center space-x-3">
85
  <button id="lab-nav-btn"
86
- 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"
87
  aria-label="Open Lab Navigator" title="Lab Navigator">
88
  <i class="fas fa-asterisk text-indigo-300 text-sm"></i>
89
  </button>
90
 
91
  <button id="access-btn"
92
- class="px-6 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full hover:opacity-90 transition">
93
  Access
94
  </button>
95
  </div>
@@ -136,7 +144,7 @@
136
  <div class="text-xs text-gray-400 uppercase tracking-wider mb-2">What we mean (operationally)</div>
137
  <div class="text-sm text-gray-300 leading-relaxed space-y-3">
138
  <p>
139
- Consciousness is a contested concept. In this lab, we treat it as a research target defined by operational criteria:
140
  measurable behaviors, internal signals, and controlled experiments—not metaphysical declarations.
141
  </p>
142
  <p class="text-gray-400">
@@ -156,7 +164,7 @@
156
  <div class="rounded-2xl border border-gray-800 bg-gray-900/30 p-5 conscious-element">
157
  <div class="text-sm font-semibold text-gray-100">Agency under constraints</div>
158
  <div class="text-sm text-gray-300 mt-2">
159
- Goal pursuit with explicit governance: permissions, logs, and fail-closed execution semantics.
160
  </div>
161
  </div>
162
 
@@ -178,18 +186,18 @@
178
  <div class="rounded-2xl border border-gray-800 bg-black/20 p-5">
179
  <div class="text-xs text-gray-400 uppercase tracking-wider mb-2">Public posture</div>
180
  <div class="text-sm text-gray-300 leading-relaxed">
181
- Language is restrained by design. We do not claim true consciousness on a web page. We claim a research agenda with measurable tests.
182
  Any future claims must be backed by transparent protocols, baselines, and replicable evidence.
183
  </div>
184
  </div>
185
 
186
  <div class="flex flex-col sm:flex-row gap-3">
187
  <button id="open-programs"
188
- class="px-5 py-3 rounded-xl bg-gradient-to-r from-indigo-600 to-purple-600 hover:opacity-90 transition">
189
  Open Programs
190
  </button>
191
  <button id="open-console"
192
- class="px-5 py-3 rounded-xl border border-gray-700 bg-gray-900/20 hover:bg-gray-900/35 transition">
193
  Open Console
194
  </button>
195
  </div>
@@ -205,7 +213,7 @@
205
  <div class="px-6 py-5 border-b border-gray-800/60 flex items-center justify-between">
206
  <div>
207
  <div class="text-lg font-semibold text-gray-100">Visualization</div>
208
- <div class="text-xs text-gray-500 mt-1">Aesthetic signal of integration,” not proof.</div>
209
  </div>
210
  <div class="text-xs px-2.5 py-1 rounded-full border border-indigo-500/30 text-indigo-200 bg-indigo-900/15">
211
  LIVE
@@ -229,7 +237,7 @@
229
  </div>
230
 
231
  <button id="request-access"
232
- class="w-full px-5 py-3 rounded-xl border border-gray-700 bg-gray-900/20 hover:bg-gray-900/35 transition">
233
  Request Access
234
  </button>
235
  </div>
@@ -264,23 +272,31 @@
264
  <h3 class="text-xl font-bold" id="access-modal-title">Request Access</h3>
265
  <p class="text-gray-400 mt-1">Curated access for research and evaluation</p>
266
  </div>
267
- <button id="close-access-modal" class="text-gray-400 hover:text-white" aria-label="Close">
268
  <i class="fas fa-times"></i>
269
  </button>
270
  </div>
271
 
272
- <form id="access-form" class="space-y-4">
 
 
 
 
 
273
  <div>
274
  <label for="name" class="block text-sm font-medium mb-1">Full Name</label>
275
- <input type="text" id="name" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
 
276
  </div>
277
  <div>
278
  <label for="email" class="block text-sm font-medium mb-1">Email</label>
279
- <input type="email" id="email" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
 
280
  </div>
281
  <div>
282
  <label for="institution" class="block text-sm font-medium mb-1">Institution/Organization</label>
283
- <input type="text" id="institution" class="w-full bg-gray-800/50 border border-gray-700 rounded-lg px-4 py-2 focus-ring">
 
284
  </div>
285
  <div>
286
  <label for="purpose" class="block text-sm font-medium mb-1">Purpose</label>
@@ -291,9 +307,10 @@
291
  <option value="partnership">Partnership</option>
292
  <option value="other">Other</option>
293
  </select>
 
294
  </div>
295
  <div class="pt-2">
296
- <button type="submit" class="w-full py-3 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg hover:opacity-90 transition">
297
  Submit Request
298
  </button>
299
  </div>
@@ -324,7 +341,7 @@
324
  </div>
325
 
326
  <button id="lab-nav-close"
327
- class="w-9 h-9 rounded-full border border-gray-800 bg-gray-900/30 hover:bg-gray-900/50 transition flex items-center justify-center"
328
  aria-label="Close Lab Navigator">
329
  <i class="fas fa-times text-gray-300 text-sm"></i>
330
  </button>
@@ -336,34 +353,34 @@
336
  radial-gradient(circle at 70% 70%, rgba(236,72,153,0.10), transparent 50%);"></div>
337
 
338
  <div class="relative grid grid-cols-1 sm:grid-cols-2 gap-3">
339
- <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4"
340
- data-nav="index.html">
341
  <div class="text-sm text-gray-200 font-medium">Start Here</div>
342
  <div class="text-xs text-gray-500 mt-1">Entry interface</div>
343
  </button>
344
 
345
- <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4"
346
- data-nav="capabilities.html">
347
  <div class="text-sm text-gray-200 font-medium">Programs</div>
348
  <div class="text-xs text-gray-500 mt-1">Program Bay</div>
349
  </button>
350
 
351
- <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4"
352
- data-nav="chat.html">
353
  <div class="text-sm text-gray-200 font-medium">Console</div>
354
  <div class="text-xs text-gray-500 mt-1">Controlled chat</div>
355
  </button>
356
 
357
- <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4"
358
- data-nav="research.html">
359
  <div class="text-sm text-gray-200 font-medium">Research</div>
360
  <div class="text-xs text-gray-500 mt-1">Notes and briefs</div>
361
  </button>
362
 
363
- <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4 sm:col-span-2"
364
- data-nav="contact.html">
365
- <div class="text-sm text-gray-200 font-medium">Contact</div>
366
- <div class="text-xs text-gray-500 mt-1">Direct channel</div>
367
  </button>
368
  </div>
369
 
@@ -375,11 +392,44 @@
375
 
376
  <div class="relative rounded-2xl border border-gray-800 bg-gray-900/30 overflow-hidden">
377
  <div class="px-6 py-5 border-b border-gray-800/60">
378
- <div class="text-lg font-semibold text-gray-100">Navigation</div>
379
- <div class="text-xs text-gray-500 mt-1">Reduced surface, consistent interface.</div>
 
 
 
 
 
 
 
 
380
  </div>
381
- <div class="p-6 text-sm text-gray-300 leading-relaxed">
382
- This page avoids strong consciousness claims. It communicates seriousness by showing test discipline and evidence gates, while keeping the aesthetic.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
383
  </div>
384
  </div>
385
 
@@ -388,25 +438,53 @@
388
  </div>
389
 
390
  <script>
391
- /* VANTA BACKGROUND */
392
- const vantaEffect = VANTA.NET({
393
- el: "#vanta-bg",
394
- mouseControls: true,
395
- touchControls: true,
396
- gyroControls: false,
397
- minHeight: 200.00,
398
- minWidth: 200.00,
399
- scale: 1.00,
400
- scaleMobile: 1.00,
401
- color: 0x4f46e5,
402
- backgroundColor: 0x020617,
403
- points: 12.00,
404
- maxDistance: 20.00,
405
- spacing: 15.00
406
- });
407
- window.addEventListener('resize', () => vantaEffect.resize());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
408
 
409
- /* MODAL HELPERS */
 
 
 
 
 
 
 
 
 
 
410
  function trapFocus(modal) {
411
  const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
412
  if (!focusable.length) return;
@@ -420,19 +498,19 @@
420
  } else {
421
  if (document.activeElement === last) { e.preventDefault(); first.focus(); }
422
  }
423
- } else if (e.key === 'Escape') {
424
- toggleModal(modal, false);
425
  }
426
  }
427
  modal.addEventListener('keydown', handler);
428
  modal._focusHandler = handler;
429
  }
 
430
  function untrapFocus(modal) {
431
  if (modal._focusHandler) {
432
  modal.removeEventListener('keydown', modal._focusHandler);
433
  delete modal._focusHandler;
434
  }
435
  }
 
436
  const toggleModal = (modal, show) => {
437
  if (show) {
438
  modal.classList.remove('modal-hidden');
@@ -447,137 +525,539 @@
447
  }
448
  };
449
 
450
- /* ACCESS MODAL */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
451
  const accessModal = document.getElementById('access-modal');
452
  const accessBtn = document.getElementById('access-btn');
453
  const closeAccessModal = document.getElementById('close-access-modal');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454
 
455
- accessBtn.addEventListener('click', () => {
456
- toggleModal(accessModal, true);
457
- setTimeout(() => document.getElementById('name').focus(), 50);
458
- });
459
- closeAccessModal.addEventListener('click', () => toggleModal(accessModal, false));
460
- accessModal.addEventListener('click', (e) => { if (e.target === accessModal) toggleModal(accessModal, false); });
461
-
462
- document.getElementById('access-form').addEventListener('submit', (e) => {
463
- e.preventDefault();
464
- const name = document.getElementById('name').value.trim();
465
- const email = document.getElementById('email').value.trim();
466
- const institution = document.getElementById('institution').value.trim();
467
- const purpose = document.getElementById('purpose').value;
468
- if (!name || !email || !institution || !purpose) {
469
- alert('Please fill in all fields.');
470
- return;
 
 
 
 
 
471
  }
472
- alert('Request received. You will be contacted after review.');
473
- e.target.reset();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
474
  toggleModal(accessModal, false);
475
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
476
 
477
- /* LAB NAVIGATOR */
 
 
478
  const labNav = document.getElementById('lab-navigator');
479
  const labNavBtn = document.getElementById('lab-nav-btn');
480
  const labNavClose = document.getElementById('lab-nav-close');
481
 
482
- function openLabNav() { toggleModal(labNav, true); setTimeout(() => labNav.focus(), 0); }
483
- function closeLabNav() { toggleModal(labNav, false); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
484
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
485
  labNavBtn.addEventListener('click', openLabNav);
486
- labNavClose.addEventListener('click', closeLabNav);
487
 
 
488
  labNav.addEventListener('click', (e) => {
489
- const shouldClose = e.target && e.target.getAttribute('data-lab-close') === 'true';
490
- if (shouldClose) closeLabNav();
 
491
  });
492
 
493
- document.querySelectorAll('.lab-node').forEach(btn => {
494
- btn.addEventListener('click', () => {
495
- const href = btn.getAttribute('data-nav');
496
- if (href) window.location.href = href;
 
 
 
497
  });
498
  });
499
 
500
- document.getElementById('open-programs').addEventListener('click', () => { window.location.href = "capabilities.html"; });
501
- document.getElementById('open-console').addEventListener('click', () => { window.location.href = "chat.html"; });
502
- document.getElementById('request-access').addEventListener('click', () => { accessBtn.click(); });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
 
504
- /* ESC behavior */
505
  document.addEventListener('keydown', (e) => {
506
  if (e.key === 'Escape') {
507
- if (labNav && !labNav.classList.contains('modal-hidden')) closeLabNav();
508
- if (accessModal && !accessModal.classList.contains('modal-hidden')) toggleModal(accessModal, false);
 
 
 
509
  }
510
  });
511
 
512
- /* Visualization (same as your original, with resize safety) */
513
- const container = document.getElementById('consciousness-visualization');
514
- if (container) {
515
- const canvas = document.createElement('canvas');
516
- container.appendChild(canvas);
517
- const ctx = canvas.getContext('2d');
518
-
519
- function resizeCanvas() {
520
- canvas.width = container.clientWidth;
521
- canvas.height = container.clientHeight;
522
- }
523
- resizeCanvas();
524
- window.addEventListener('resize', resizeCanvas);
525
-
526
- const particles = [];
527
- const particleCount = 150;
528
-
529
- function initParticles() {
530
- particles.length = 0;
531
- for (let i = 0; i < particleCount; i++) {
532
- particles.push({
533
- x: Math.random() * canvas.width,
534
- y: Math.random() * canvas.height,
535
- size: Math.random() * 3 + 1,
536
- speedX: Math.random() * 2 - 1,
537
- speedY: Math.random() * 2 - 1,
538
- alpha: Math.random() * 0.5 + 0.1
539
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  }
541
- }
542
- initParticles();
543
-
544
- function animate() {
545
- ctx.clearRect(0, 0, canvas.width, canvas.height);
546
-
547
- for (let i = 0; i < particles.length; i++) {
548
- for (let j = i + 1; j < particles.length; j++) {
549
- const dx = particles[i].x - particles[j].x;
550
- const dy = particles[i].y - particles[j].y;
551
- const distance = Math.sqrt(dx * dx + dy * dy);
552
- if (distance < 100) {
553
- ctx.beginPath();
554
- ctx.strokeStyle = `rgba(99, 102, 241, ${1 - distance / 100})`;
555
- ctx.lineWidth = 0.2;
556
- ctx.moveTo(particles[i].x, particles[i].y);
557
- ctx.lineTo(particles[j].x, particles[j].y);
558
- ctx.stroke();
559
- }
560
  }
561
  }
562
 
563
- for (let i = 0; i < particles.length; i++) {
564
- const p = particles[i];
565
- p.x += p.speedX;
566
- p.y += p.speedY;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
567
 
568
- if (p.x < 0 || p.x > canvas.width) p.speedX *= -1;
569
- if (p.y < 0 || p.y > canvas.height) p.speedY *= -1;
570
 
571
- ctx.beginPath();
572
- ctx.fillStyle = `rgba(99, 102, 241, ${p.alpha})`;
573
- ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
574
- ctx.fill();
 
575
  }
576
 
577
- requestAnimationFrame(animate);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578
  }
579
- animate();
580
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
581
  </script>
582
  </body>
583
- </html>
 
1
+ <!-- SILENTPATTERN FINAL BUILD: 2025-12-15 | pages: 10 | features: hash-deep-linking, lab-navigator, access-modal, form-validation, consciousness-visualization -->
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
 
66
  }
67
  .dot { width: 8px; height: 8px; border-radius: 999px; }
68
  .dot-concept { background: rgba(99,102,241,0.9); }
69
+
70
+ /* Active states */
71
+ .lab-node.active {
72
+ border-color: rgba(99,102,241,0.55) !important;
73
+ box-shadow: 0 0 0 1px rgba(99,102,241,0.22), 0 0 28px rgba(99,102,241,0.08);
74
+ transform: translateY(-1px);
75
+ }
76
  </style>
77
  </head>
78
 
 
91
 
92
  <div class="flex items-center space-x-3">
93
  <button id="lab-nav-btn"
94
+ 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 focus-ring"
95
  aria-label="Open Lab Navigator" title="Lab Navigator">
96
  <i class="fas fa-asterisk text-indigo-300 text-sm"></i>
97
  </button>
98
 
99
  <button id="access-btn"
100
+ class="px-6 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-full hover:opacity-90 transition focus-ring">
101
  Access
102
  </button>
103
  </div>
 
144
  <div class="text-xs text-gray-400 uppercase tracking-wider mb-2">What we mean (operationally)</div>
145
  <div class="text-sm text-gray-300 leading-relaxed space-y-3">
146
  <p>
147
+ "Consciousness" is a contested concept. In this lab, we treat it as a research target defined by operational criteria:
148
  measurable behaviors, internal signals, and controlled experiments—not metaphysical declarations.
149
  </p>
150
  <p class="text-gray-400">
 
164
  <div class="rounded-2xl border border-gray-800 bg-gray-900/30 p-5 conscious-element">
165
  <div class="text-sm font-semibold text-gray-100">Agency under constraints</div>
166
  <div class="text-sm text-gray-300 mt-2">
167
+ Goal pursuit with explicit governance: permissions, logs, and "fail-closed" execution semantics.
168
  </div>
169
  </div>
170
 
 
186
  <div class="rounded-2xl border border-gray-800 bg-black/20 p-5">
187
  <div class="text-xs text-gray-400 uppercase tracking-wider mb-2">Public posture</div>
188
  <div class="text-sm text-gray-300 leading-relaxed">
189
+ Language is restrained by design. We do not claim "true consciousness" on a web page. We claim a research agenda with measurable tests.
190
  Any future claims must be backed by transparent protocols, baselines, and replicable evidence.
191
  </div>
192
  </div>
193
 
194
  <div class="flex flex-col sm:flex-row gap-3">
195
  <button id="open-programs"
196
+ class="px-5 py-3 rounded-xl bg-gradient-to-r from-indigo-600 to-purple-600 hover:opacity-90 transition focus-ring">
197
  Open Programs
198
  </button>
199
  <button id="open-console"
200
+ class="px-5 py-3 rounded-xl border border-gray-700 bg-gray-900/20 hover:bg-gray-900/35 transition focus-ring">
201
  Open Console
202
  </button>
203
  </div>
 
213
  <div class="px-6 py-5 border-b border-gray-800/60 flex items-center justify-between">
214
  <div>
215
  <div class="text-lg font-semibold text-gray-100">Visualization</div>
216
+ <div class="text-xs text-gray-500 mt-1">Aesthetic signal of "integration," not proof.</div>
217
  </div>
218
  <div class="text-xs px-2.5 py-1 rounded-full border border-indigo-500/30 text-indigo-200 bg-indigo-900/15">
219
  LIVE
 
237
  </div>
238
 
239
  <button id="request-access"
240
+ class="w-full px-5 py-3 rounded-xl border border-gray-700 bg-gray-900/20 hover:bg-gray-900/35 transition focus-ring">
241
  Request Access
242
  </button>
243
  </div>
 
272
  <h3 class="text-xl font-bold" id="access-modal-title">Request Access</h3>
273
  <p class="text-gray-400 mt-1">Curated access for research and evaluation</p>
274
  </div>
275
+ <button id="close-access-modal" class="text-gray-400 hover:text-white focus-ring" aria-label="Close">
276
  <i class="fas fa-times"></i>
277
  </button>
278
  </div>
279
 
280
+ <!-- Inline feedback -->
281
+ <div id="access-feedback"
282
+ class="hidden mb-4 rounded-lg border border-gray-800 bg-black/25 px-4 py-3 text-sm"
283
+ role="status" aria-live="polite"></div>
284
+
285
+ <form id="access-form" class="space-y-4" novalidate>
286
  <div>
287
  <label for="name" class="block text-sm font-medium mb-1">Full Name</label>
288
+ <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">
289
+ <p id="name-error" class="hidden mt-1 text-xs text-red-300">Please enter your full name.</p>
290
  </div>
291
  <div>
292
  <label for="email" class="block text-sm font-medium mb-1">Email</label>
293
+ <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">
294
+ <p id="email-error" class="hidden mt-1 text-xs text-red-300">Please enter a valid email address.</p>
295
  </div>
296
  <div>
297
  <label for="institution" class="block text-sm font-medium mb-1">Institution/Organization</label>
298
+ <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">
299
+ <p id="institution-error" class="hidden mt-1 text-xs text-red-300">Please enter your institution/organization.</p>
300
  </div>
301
  <div>
302
  <label for="purpose" class="block text-sm font-medium mb-1">Purpose</label>
 
307
  <option value="partnership">Partnership</option>
308
  <option value="other">Other</option>
309
  </select>
310
+ <p id="purpose-error" class="hidden mt-1 text-xs text-red-300">Please select a purpose.</p>
311
  </div>
312
  <div class="pt-2">
313
+ <button type="submit" class="w-full py-3 bg-gradient-to-r from-indigo-600 to-purple-600 rounded-lg hover:opacity-90 transition focus-ring">
314
  Submit Request
315
  </button>
316
  </div>
 
341
  </div>
342
 
343
  <button id="lab-nav-close"
344
+ class="w-9 h-9 rounded-full border border-gray-800 bg-gray-900/30 hover:bg-gray-900/50 transition flex items-center justify-center focus-ring"
345
  aria-label="Close Lab Navigator">
346
  <i class="fas fa-times text-gray-300 text-sm"></i>
347
  </button>
 
353
  radial-gradient(circle at 70% 70%, rgba(236,72,153,0.10), transparent 50%);"></div>
354
 
355
  <div class="relative grid grid-cols-1 sm:grid-cols-2 gap-3">
356
+ <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4 focus-ring"
357
+ data-dossier="start" aria-current="false">
358
  <div class="text-sm text-gray-200 font-medium">Start Here</div>
359
  <div class="text-xs text-gray-500 mt-1">Entry interface</div>
360
  </button>
361
 
362
+ <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4 focus-ring"
363
+ data-dossier="programs" aria-current="false">
364
  <div class="text-sm text-gray-200 font-medium">Programs</div>
365
  <div class="text-xs text-gray-500 mt-1">Program Bay</div>
366
  </button>
367
 
368
+ <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4 focus-ring"
369
+ data-dossier="console" aria-current="false">
370
  <div class="text-sm text-gray-200 font-medium">Console</div>
371
  <div class="text-xs text-gray-500 mt-1">Controlled chat</div>
372
  </button>
373
 
374
+ <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4 focus-ring"
375
+ data-dossier="research" aria-current="false">
376
  <div class="text-sm text-gray-200 font-medium">Research</div>
377
  <div class="text-xs text-gray-500 mt-1">Notes and briefs</div>
378
  </button>
379
 
380
+ <button class="lab-node text-left rounded-xl border border-gray-800 bg-gray-900/30 hover:border-indigo-500/50 hover:bg-gray-900/45 transition p-4 focus-ring sm:col-span-2"
381
+ data-dossier="access" aria-current="false">
382
+ <div class="text-sm text-gray-200 font-medium">Access</div>
383
+ <div class="text-xs text-gray-500 mt-1">Curated entry</div>
384
  </button>
385
  </div>
386
 
 
392
 
393
  <div class="relative rounded-2xl border border-gray-800 bg-gray-900/30 overflow-hidden">
394
  <div class="px-6 py-5 border-b border-gray-800/60">
395
+ <div class="flex items-start justify-between gap-4">
396
+ <div>
397
+ <div id="dossier-title" class="text-lg font-semibold text-gray-100">Lab Dossier</div>
398
+ <div id="dossier-subtitle" class="text-xs text-gray-500 mt-1">Select a node to load details.</div>
399
+ </div>
400
+ <div id="dossier-status"
401
+ class="text-xs px-2.5 py-1 rounded-full border border-indigo-500/30 text-indigo-200 bg-indigo-900/15">
402
+ READY
403
+ </div>
404
+ </div>
405
  </div>
406
+
407
+ <div class="p-6 space-y-5 max-h-[560px] overflow-auto thin-scroll">
408
+ <div id="dossier-body" class="text-sm text-gray-300 leading-relaxed">
409
+ This page avoids strong consciousness claims. It communicates seriousness by showing test discipline and evidence gates, while keeping the aesthetic.
410
+ </div>
411
+
412
+ <div class="rounded-xl border border-gray-800 bg-black/20 p-4">
413
+ <div class="text-xs text-gray-400 uppercase tracking-wider mb-2">Evidence Capsule</div>
414
+ <ul id="dossier-evidence" class="text-sm text-gray-300 space-y-1">
415
+ <li class="text-gray-500">No dossier selected.</li>
416
+ </ul>
417
+ </div>
418
+
419
+ <div class="flex flex-col sm:flex-row gap-3">
420
+ <button id="dossier-primary"
421
+ class="flex-1 px-5 py-3 rounded-xl bg-gradient-to-r from-indigo-600 to-purple-600 hover:opacity-90 transition focus-ring">
422
+ Open
423
+ </button>
424
+ <button id="dossier-secondary"
425
+ class="flex-1 px-5 py-3 rounded-xl border border-gray-700 bg-gray-900/20 hover:bg-gray-900/35 transition focus-ring">
426
+ View Note
427
+ </button>
428
+ </div>
429
+
430
+ <div id="dossier-meta" class="text-xs text-gray-500">
431
+ Last updated: <span class="text-gray-300">—</span>
432
+ </div>
433
  </div>
434
  </div>
435
 
 
438
  </div>
439
 
440
  <script>
441
+ // Site-wide configuration
442
+ const CONFIG = {
443
+ MODAL_HASHES: new Set(['lab', 'access'])
444
+ };
445
+
446
+ /* -------------------------------------------------------------
447
+ UTILITY FUNCTIONS (consistent with other pages)
448
+ ------------------------------------------------------------- */
449
+ function escapeHtml(str) {
450
+ return String(str)
451
+ .replaceAll('&', '&amp;')
452
+ .replaceAll('<', '&lt;')
453
+ .replaceAll('>', '&gt;')
454
+ .replaceAll('"', '&quot;')
455
+ .replaceAll("'", '&#039;');
456
+ }
457
+
458
+ function isValidEmail(email) {
459
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
460
+ }
461
+
462
+ function currentHashKey() {
463
+ const h = (window.location.hash || '').replace('#', '').trim();
464
+ return h;
465
+ }
466
+
467
+ function setHash(key, replace = false) {
468
+ if (!key) return;
469
+ if (window.location.hash.replace('#', '') === key) return;
470
+ if (replace) {
471
+ history.replaceState(null, '', '#' + key);
472
+ } else {
473
+ history.pushState(null, '', '#' + key);
474
+ }
475
+ }
476
 
477
+ function clearHashIf(key) {
478
+ const h = currentHashKey();
479
+ if (!h) return;
480
+ if (!key || h === key) {
481
+ history.replaceState(null, '', window.location.pathname + window.location.search);
482
+ }
483
+ }
484
+
485
+ /* -------------------------------------------------------------
486
+ MODAL HELPERS (consistent with other pages)
487
+ ------------------------------------------------------------- */
488
  function trapFocus(modal) {
489
  const focusable = modal.querySelectorAll('a, button, input, select, textarea, [tabindex]:not([tabindex="-1"])');
490
  if (!focusable.length) return;
 
498
  } else {
499
  if (document.activeElement === last) { e.preventDefault(); first.focus(); }
500
  }
 
 
501
  }
502
  }
503
  modal.addEventListener('keydown', handler);
504
  modal._focusHandler = handler;
505
  }
506
+
507
  function untrapFocus(modal) {
508
  if (modal._focusHandler) {
509
  modal.removeEventListener('keydown', modal._focusHandler);
510
  delete modal._focusHandler;
511
  }
512
  }
513
+
514
  const toggleModal = (modal, show) => {
515
  if (show) {
516
  modal.classList.remove('modal-hidden');
 
525
  }
526
  };
527
 
528
+ /* -------------------------------------------------------------
529
+ VANTA BACKGROUND
530
+ ------------------------------------------------------------- */
531
+ let vantaEffect = null;
532
+ try {
533
+ if (window.VANTA && typeof VANTA.NET === 'function') {
534
+ vantaEffect = VANTA.NET({
535
+ el: "#vanta-bg",
536
+ mouseControls: true,
537
+ touchControls: true,
538
+ gyroControls: false,
539
+ minHeight: 200.00,
540
+ minWidth: 200.00,
541
+ scale: 1.00,
542
+ scaleMobile: 1.00,
543
+ color: 0x4f46e5,
544
+ backgroundColor: 0x020617,
545
+ points: 12.00,
546
+ maxDistance: 20.00,
547
+ spacing: 15.00
548
+ });
549
+ }
550
+ } catch (e) {
551
+ console.warn('Vanta background failed to initialize:', e);
552
+ vantaEffect = null;
553
+ }
554
+ window.addEventListener('resize', () => {
555
+ if (vantaEffect && typeof vantaEffect.resize === 'function') {
556
+ vantaEffect.resize();
557
+ }
558
+ });
559
+
560
+ /* -------------------------------------------------------------
561
+ ACCESS MODAL
562
+ ------------------------------------------------------------- */
563
  const accessModal = document.getElementById('access-modal');
564
  const accessBtn = document.getElementById('access-btn');
565
  const closeAccessModal = document.getElementById('close-access-modal');
566
+ const accessForm = document.getElementById('access-form');
567
+ const accessFeedback = document.getElementById('access-feedback');
568
+
569
+ const nameEl = document.getElementById('name');
570
+ const emailEl = document.getElementById('email');
571
+ const institutionEl = document.getElementById('institution');
572
+ const purposeEl = document.getElementById('purpose');
573
+
574
+ const nameErr = document.getElementById('name-error');
575
+ const emailErr = document.getElementById('email-error');
576
+ const institutionErr = document.getElementById('institution-error');
577
+ const purposeErr = document.getElementById('purpose-error');
578
+
579
+ function setAccessFeedback(kind, text) {
580
+ if (!accessFeedback) return;
581
+ accessFeedback.classList.remove('hidden');
582
+ accessFeedback.classList.remove('border-red-500/30', 'bg-red-900/10', 'text-red-200');
583
+ accessFeedback.classList.remove('border-emerald-500/30', 'bg-emerald-900/10', 'text-emerald-200');
584
+ accessFeedback.classList.remove('border-indigo-500/30', 'bg-indigo-900/10', 'text-indigo-200');
585
+
586
+ if (kind === 'error') {
587
+ accessFeedback.classList.add('border-red-500/30', 'bg-red-900/10', 'text-red-200');
588
+ } else if (kind === 'success') {
589
+ accessFeedback.classList.add('border-emerald-500/30', 'bg-emerald-900/10', 'text-emerald-200');
590
+ } else {
591
+ accessFeedback.classList.add('border-indigo-500/30', 'bg-indigo-900/10', 'text-indigo-200');
592
+ }
593
+ accessFeedback.textContent = text;
594
+ }
595
 
596
+ function hideAccessFeedback() {
597
+ if (!accessFeedback) return;
598
+ accessFeedback.textContent = '';
599
+ accessFeedback.classList.add('hidden');
600
+ accessFeedback.classList.remove(
601
+ 'border-red-500/30','bg-red-900/10','text-red-200',
602
+ 'border-emerald-500/30','bg-emerald-900/10','text-emerald-200',
603
+ 'border-indigo-500/30','bg-indigo-900/10','text-indigo-200'
604
+ );
605
+ }
606
+
607
+ function setFieldError(inputEl, errorEl, isError) {
608
+ if (!inputEl || !errorEl) return;
609
+ if (isError) {
610
+ errorEl.classList.remove('hidden');
611
+ inputEl.setAttribute('aria-invalid', 'true');
612
+ inputEl.classList.add('border-red-500/60');
613
+ } else {
614
+ errorEl.classList.add('hidden');
615
+ inputEl.removeAttribute('aria-invalid');
616
+ inputEl.classList.remove('border-red-500/60');
617
  }
618
+ }
619
+
620
+ function resetAccessErrors() {
621
+ hideAccessFeedback();
622
+ setFieldError(nameEl, nameErr, false);
623
+ setFieldError(emailEl, emailErr, false);
624
+ setFieldError(institutionEl, institutionErr, false);
625
+ setFieldError(purposeEl, purposeErr, false);
626
+ }
627
+
628
+ function openAccessModal(setHashFlag = true) {
629
+ resetAccessErrors();
630
+ toggleModal(accessModal, true);
631
+ if (setHashFlag) setHash('access');
632
+ setTimeout(() => nameEl && nameEl.focus(), 80);
633
+ }
634
+
635
+ function closeAccessModal(clearHashFlag = true) {
636
  toggleModal(accessModal, false);
637
+ if (clearHashFlag) clearHashIf('access');
638
+ }
639
+
640
+ accessBtn.addEventListener('click', () => openAccessModal(true));
641
+
642
+ if (closeAccessModal) closeAccessModal.addEventListener('click', () => closeAccessModal(true));
643
+
644
+ if (accessModal) {
645
+ accessModal.addEventListener('click', (e) => {
646
+ if (e.target === accessModal) closeAccessModal(true);
647
+ });
648
+ }
649
+
650
+ if (accessForm) {
651
+ // Clear per-field errors on input
652
+ [nameEl, emailEl, institutionEl, purposeEl].forEach(el => {
653
+ if (!el) return;
654
+ el.addEventListener('input', () => {
655
+ if (el === nameEl) setFieldError(nameEl, nameErr, !nameEl.value.trim());
656
+ if (el === emailEl) setFieldError(emailEl, emailErr, !isValidEmail(emailEl.value.trim()));
657
+ if (el === institutionEl) setFieldError(institutionEl, institutionErr, !institutionEl.value.trim());
658
+ if (el === purposeEl) setFieldError(purposeEl, purposeErr, !purposeEl.value);
659
+ });
660
+ el.addEventListener('change', () => el.dispatchEvent(new Event('input')));
661
+ });
662
+
663
+ accessForm.addEventListener('submit', (e) => {
664
+ e.preventDefault();
665
+ resetAccessErrors();
666
+
667
+ const name = (nameEl?.value || '').trim();
668
+ const email = (emailEl?.value || '').trim();
669
+ const institution = (institutionEl?.value || '').trim();
670
+ const purpose = (purposeEl?.value || '').trim();
671
+
672
+ let ok = true;
673
+
674
+ if (!name) { setFieldError(nameEl, nameErr, true); ok = false; }
675
+ if (!email || !isValidEmail(email)) { setFieldError(emailEl, emailErr, true); ok = false; }
676
+ if (!institution) { setFieldError(institutionEl, institutionErr, true); ok = false; }
677
+ if (!purpose) { setFieldError(purposeEl, purposeErr, true); ok = false; }
678
+
679
+ if (!ok) {
680
+ setAccessFeedback('error', 'Please correct the highlighted fields and resubmit.');
681
+ return;
682
+ }
683
+
684
+ setAccessFeedback('success', 'Request received. You will be contacted after review.');
685
+ accessForm.reset();
686
+ });
687
+ }
688
 
689
+ /* -------------------------------------------------------------
690
+ LAB NAVIGATOR
691
+ ------------------------------------------------------------- */
692
  const labNav = document.getElementById('lab-navigator');
693
  const labNavBtn = document.getElementById('lab-nav-btn');
694
  const labNavClose = document.getElementById('lab-nav-close');
695
 
696
+ const DOSSIERS = {
697
+ start: {
698
+ title: "Start Here",
699
+ subtitle: "Entry interface",
700
+ status: "ACTIVE",
701
+ body: "Return to the main interface.",
702
+ evidence: ["Public entry layer", "Programs as dossiers", "Console for controlled interaction"],
703
+ primary: { label: "Go to Index", action: () => { window.location.href = "index.html"; } },
704
+ secondary: { label: "Programs", action: () => { window.location.href = "capabilities.html"; } },
705
+ updated: "—"
706
+ },
707
+ programs: {
708
+ title: "Programs",
709
+ subtitle: "Program Bay",
710
+ status: "ACTIVE",
711
+ body: "Program Bay: MCAP, CHAI, Quantum Lambda, AI Scientist, Agentic Workforce.",
712
+ evidence: ["Dossiers with maturity levels", "Evidence capsules", "Access gates"],
713
+ primary: { label: "Open Programs", action: () => { window.location.href = "capabilities.html"; } },
714
+ secondary: { label: "Console", action: () => { window.location.href = "chat.html"; } },
715
+ updated: "—"
716
+ },
717
+ console: {
718
+ title: "Console",
719
+ subtitle: "Controlled chat",
720
+ status: "ACTIVE",
721
+ body: "A chat interface with constrained protocols, not a fully open-ended conversation. All interactions are logged and monitored for safety.",
722
+ evidence: ["Constrained topics", "Real-time monitoring", "Safety protocols enabled"],
723
+ primary: { label: "Open Console", action: () => { window.location.href = "chat.html"; } },
724
+ secondary: { label: "Research Notes", action: () => { window.location.href = "research.html"; } },
725
+ updated: "—"
726
+ },
727
+ research: {
728
+ title: "Research",
729
+ subtitle: "Notes and briefs",
730
+ status: "ACTIVE",
731
+ body: "Research dossiers, briefs, and technical notes. All content is evidence-gated with explicit uncertainty reporting.",
732
+ evidence: ["Evidence-gated content", "Uncertainty reporting", "Falsifiable hypotheses"],
733
+ primary: { label: "Open Research", action: () => { window.location.href = "research.html"; } },
734
+ secondary: { label: "View Notes", action: () => { /* Could open a modal or navigate */ } },
735
+ updated: "—"
736
+ },
737
+ access: {
738
+ title: "Access",
739
+ subtitle: "Curated entry",
740
+ status: "ACTIVE",
741
+ body: "Controlled access for qualified research and evaluation partners. All access is logged and requires explicit permission.",
742
+ evidence: ["Permission-based entry", "Audit trails", "Time-limited sessions"],
743
+ primary: { label: "Request Access", action: () => { openAccessModal(true); closeLabNav(); } },
744
+ secondary: { label: "Contact", action: () => { window.location.href = "contact.html"; } },
745
+ updated: "—"
746
+ }
747
+ };
748
+
749
+ function loadDossier(key) {
750
+ const dossier = DOSSIERS[key];
751
+ if (!dossier) return;
752
+
753
+ // Update UI elements
754
+ document.getElementById('dossier-title').textContent = dossier.title;
755
+ document.getElementById('dossier-subtitle').textContent = dossier.subtitle;
756
+ document.getElementById('dossier-status').textContent = dossier.status;
757
+ document.getElementById('dossier-body').textContent = dossier.body;
758
+
759
+ // Update evidence list
760
+ const evidenceList = document.getElementById('dossier-evidence');
761
+ evidenceList.innerHTML = '';
762
+ dossier.evidence.forEach(item => {
763
+ const li = document.createElement('li');
764
+ li.textContent = `• ${item}`;
765
+ evidenceList.appendChild(li);
766
+ });
767
 
768
+ // Update buttons
769
+ const primaryBtn = document.getElementById('dossier-primary');
770
+ const secondaryBtn = document.getElementById('dossier-secondary');
771
+
772
+ primaryBtn.textContent = dossier.primary.label;
773
+ primaryBtn.onclick = dossier.primary.action;
774
+
775
+ secondaryBtn.textContent = dossier.secondary.label;
776
+ secondaryBtn.onclick = dossier.secondary.action;
777
+
778
+ // Update metadata
779
+ document.getElementById('dossier-meta').innerHTML =
780
+ `Last updated: <span class="text-gray-300">${dossier.updated}</span>`;
781
+
782
+ // Update active state for lab nodes
783
+ document.querySelectorAll('.lab-node').forEach(node => {
784
+ node.classList.remove('active');
785
+ node.setAttribute('aria-current', 'false');
786
+ });
787
+
788
+ const activeNode = document.querySelector(`.lab-node[data-dossier="${key}"]`);
789
+ if (activeNode) {
790
+ activeNode.classList.add('active');
791
+ activeNode.setAttribute('aria-current', 'true');
792
+ }
793
+ }
794
+
795
+ function openLabNav(setHashFlag = true) {
796
+ toggleModal(labNav, true);
797
+ if (setHashFlag) setHash('lab');
798
+ // Load default dossier
799
+ loadDossier('start');
800
+ }
801
+
802
+ function closeLabNav(clearHashFlag = true) {
803
+ toggleModal(labNav, false);
804
+ if (clearHashFlag) clearHashIf('lab');
805
+ }
806
+
807
+ // Event listeners for lab navigator
808
  labNavBtn.addEventListener('click', openLabNav);
809
+ labNavClose.addEventListener('click', () => closeLabNav(true));
810
 
811
+ // Close on background click
812
  labNav.addEventListener('click', (e) => {
813
+ if (e.target.hasAttribute('data-lab-close')) {
814
+ closeLabNav(true);
815
+ }
816
  });
817
 
818
+ // Load dossier on node click
819
+ document.querySelectorAll('.lab-node').forEach(node => {
820
+ node.addEventListener('click', () => {
821
+ const dossierKey = node.getAttribute('data-dossier');
822
+ if (dossierKey && DOSSIERS[dossierKey]) {
823
+ loadDossier(dossierKey);
824
+ }
825
  });
826
  });
827
 
828
+ /* -------------------------------------------------------------
829
+ HASH-BASED NAVIGATION (Deep linking)
830
+ ------------------------------------------------------------- */
831
+ function handleHashChange() {
832
+ const hash = currentHashKey();
833
+
834
+ if (CONFIG.MODAL_HASHES.has(hash)) {
835
+ if (hash === 'lab' && labNav.classList.contains('modal-hidden')) {
836
+ openLabNav(false);
837
+ } else if (hash === 'access' && accessModal.classList.contains('modal-hidden')) {
838
+ openAccessModal(false);
839
+ }
840
+ } else {
841
+ // Close modals if hash doesn't match
842
+ if (!labNav.classList.contains('modal-hidden')) closeLabNav(false);
843
+ if (!accessModal.classList.contains('modal-hidden')) closeAccessModal(false);
844
+ }
845
+ }
846
+
847
+ window.addEventListener('hashchange', handleHashChange);
848
+ window.addEventListener('load', handleHashChange);
849
 
850
+ // Close modals on Escape key
851
  document.addEventListener('keydown', (e) => {
852
  if (e.key === 'Escape') {
853
+ if (!labNav.classList.contains('modal-hidden')) {
854
+ closeLabNav(true);
855
+ } else if (!accessModal.classList.contains('modal-hidden')) {
856
+ closeAccessModal(true);
857
+ }
858
  }
859
  });
860
 
861
+ /* -------------------------------------------------------------
862
+ CONSCIOUSNESS VISUALIZATION (Three.js)
863
+ ------------------------------------------------------------- */
864
+ function initConsciousnessVisualization() {
865
+ const container = document.getElementById('consciousness-visualization');
866
+ if (!container) return null;
867
+
868
+ try {
869
+ // Setup basic Three.js scene
870
+ const scene = new THREE.Scene();
871
+ const camera = new THREE.PerspectiveCamera(75, container.clientWidth / container.clientHeight, 0.1, 1000);
872
+ const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
873
+
874
+ renderer.setSize(container.clientWidth, container.clientHeight);
875
+ renderer.setClearColor(0x000000, 0);
876
+ container.appendChild(renderer.domElement);
877
+
878
+ // Create floating nodes representing "conscious elements"
879
+ const nodes = [];
880
+ const nodeCount = 8;
881
+ const nodeGeometry = new THREE.SphereGeometry(0.5, 16, 16);
882
+ const nodeMaterial = new THREE.MeshBasicMaterial({
883
+ color: 0x6366f1,
884
+ transparent: true,
885
+ opacity: 0.7
886
+ });
887
+
888
+ for (let i = 0; i < nodeCount; i++) {
889
+ const node = new THREE.Mesh(nodeGeometry, nodeMaterial.clone());
890
+
891
+ // Position in a sphere
892
+ const radius = 3;
893
+ const theta = Math.random() * Math.PI * 2;
894
+ const phi = Math.acos(2 * Math.random() - 1);
895
+
896
+ node.position.x = radius * Math.sin(phi) * Math.cos(theta);
897
+ node.position.y = radius * Math.sin(phi) * Math.sin(theta);
898
+ node.position.z = radius * Math.cos(phi);
899
+
900
+ // Random color variation
901
+ const hue = 0.6 + Math.random() * 0.2; // Purple to pink
902
+ node.material.color.setHSL(hue, 0.8, 0.6);
903
+
904
+ node.userData = {
905
+ originalPosition: node.position.clone(),
906
+ speed: 0.002 + Math.random() * 0.003,
907
+ phase: Math.random() * Math.PI * 2
908
+ };
909
+
910
+ scene.add(node);
911
+ nodes.push(node);
912
  }
913
+
914
+ // Create connecting lines
915
+ const lines = [];
916
+ const lineMaterial = new THREE.LineBasicMaterial({
917
+ color: 0x8b5cf6,
918
+ transparent: true,
919
+ opacity: 0.2,
920
+ linewidth: 1
921
+ });
922
+
923
+ for (let i = 0; i < nodes.length; i++) {
924
+ for (let j = i + 1; j < nodes.length; j++) {
925
+ const geometry = new THREE.BufferGeometry().setFromPoints([
926
+ nodes[i].position,
927
+ nodes[j].position
928
+ ]);
929
+ const line = new THREE.Line(geometry, lineMaterial);
930
+ scene.add(line);
931
+ lines.push({ line, start: nodes[i], end: nodes[j] });
932
  }
933
  }
934
 
935
+ camera.position.z = 8;
936
+
937
+ // Animation loop
938
+ let animationId;
939
+ let time = 0;
940
+
941
+ function animate() {
942
+ animationId = requestAnimationFrame(animate);
943
+ time += 0.01;
944
+
945
+ // Animate nodes
946
+ nodes.forEach((node, i) => {
947
+ const data = node.userData;
948
+ const t = time * data.speed + data.phase;
949
+
950
+ node.position.x = data.originalPosition.x + Math.sin(t) * 0.3;
951
+ node.position.y = data.originalPosition.y + Math.cos(t * 0.7) * 0.3;
952
+ node.position.z = data.originalPosition.z + Math.sin(t * 1.3) * 0.3;
953
+
954
+ // Pulsing opacity
955
+ node.material.opacity = 0.5 + Math.sin(t * 2) * 0.2;
956
+ });
957
+
958
+ // Update lines
959
+ lines.forEach(lineObj => {
960
+ const positions = [
961
+ lineObj.start.position.x, lineObj.start.position.y, lineObj.start.position.z,
962
+ lineObj.end.position.x, lineObj.end.position.y, lineObj.end.position.z
963
+ ];
964
+ lineObj.line.geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3));
965
+
966
+ // Update line opacity based on distance
967
+ const distance = lineObj.start.position.distanceTo(lineObj.end.position);
968
+ lineObj.line.material.opacity = Math.max(0.1, 0.4 - distance * 0.05);
969
+ });
970
 
971
+ renderer.render(scene, camera);
972
+ }
973
 
974
+ // Handle resize
975
+ function handleResize() {
976
+ camera.aspect = container.clientWidth / container.clientHeight;
977
+ camera.updateProjectionMatrix();
978
+ renderer.setSize(container.clientWidth, container.clientHeight);
979
  }
980
 
981
+ window.addEventListener('resize', handleResize);
982
+
983
+ // Cleanup function
984
+ function cleanup() {
985
+ cancelAnimationFrame(animationId);
986
+ window.removeEventListener('resize', handleResize);
987
+ if (container.contains(renderer.domElement)) {
988
+ container.removeChild(renderer.domElement);
989
+ }
990
+
991
+ // Dispose of Three.js resources
992
+ [nodeGeometry, lineMaterial].forEach(resource => {
993
+ if (resource.dispose) resource.dispose();
994
+ });
995
+
996
+ nodes.forEach(node => {
997
+ if (node.material.dispose) node.material.dispose();
998
+ if (node.geometry.dispose) node.geometry.dispose();
999
+ });
1000
+
1001
+ lines.forEach(lineObj => {
1002
+ if (lineObj.line.geometry.dispose) lineObj.line.geometry.dispose();
1003
+ if (lineObj.line.material.dispose) lineObj.line.material.dispose();
1004
+ });
1005
+ }
1006
+
1007
+ // Start animation
1008
+ animate();
1009
+
1010
+ return cleanup;
1011
+ } catch (error) {
1012
+ console.warn('Consciousness visualization failed:', error);
1013
+ container.innerHTML = `
1014
+ <div class="w-full h-full flex items-center justify-center text-gray-500">
1015
+ <div class="text-center">
1016
+ <div class="text-sm mb-2">Visualization unavailable</div>
1017
+ <div class="text-xs">Illustrative only—not evidence</div>
1018
+ </div>
1019
+ </div>
1020
+ `;
1021
+ return null;
1022
  }
 
1023
  }
1024
+
1025
+ // Initialize visualization when page loads
1026
+ let cleanupVisualization = null;
1027
+ window.addEventListener('DOMContentLoaded', () => {
1028
+ cleanupVisualization = initConsciousnessVisualization();
1029
+ });
1030
+
1031
+ /* -------------------------------------------------------------
1032
+ ADDITIONAL BUTTON HANDLERS
1033
+ ------------------------------------------------------------- */
1034
+ // Open Programs button
1035
+ document.getElementById('open-programs')?.addEventListener('click', () => {
1036
+ window.location.href = 'capabilities.html';
1037
+ });
1038
+
1039
+ // Open Console button
1040
+ document.getElementById('open-console')?.addEventListener('click', () => {
1041
+ window.location.href = 'chat.html';
1042
+ });
1043
+
1044
+ // Request Access button
1045
+ document.getElementById('request-access')?.addEventListener('click', () => {
1046
+ openAccessModal(true);
1047
+ });
1048
+
1049
+ /* -------------------------------------------------------------
1050
+ CLEANUP ON PAGE UNLOAD
1051
+ ------------------------------------------------------------- */
1052
+ window.addEventListener('beforeunload', () => {
1053
+ if (typeof cleanupVisualization === 'function') {
1054
+ cleanupVisualization();
1055
+ }
1056
+
1057
+ if (vantaEffect && typeof vantaEffect.destroy === 'function') {
1058
+ vantaEffect.destroy();
1059
+ }
1060
+ });
1061
  </script>
1062
  </body>
1063
+ </html>