// Scrollytelling functionality class ScrollytellingEngine { constructor() { this.scenes = document.querySelectorAll('.scene'); this.progressBar = document.querySelector('.progress-bar'); this.currentScene = 0; this.init(); } init() { // Set up intersection observer for scene detection this.setupIntersectionObserver(); // Set up scroll progress tracking this.setupScrollProgress(); // Initialize first scene this.activateScene(0); } setupIntersectionObserver() { const options = { root: null, rootMargin: '0px', threshold: this.buildThresholdList() }; this.observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const sceneIndex = parseInt(entry.target.dataset.scene); this.activateScene(sceneIndex); } }); }, options); this.scenes.forEach(scene => { this.observer.observe(scene); }); } buildThresholdList() { const thresholds = []; const numSteps = 20; for (let i = 1.0; i <= numSteps; i++) { const ratio = i / numSteps; thresholds.push(ratio); } thresholds.push(0); return thresholds; } setupScrollProgress() { window.addEventListener('scroll', () => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight - window.innerHeight; const scrollProgress = (scrollTop / scrollHeight) * 100; if (this.progressBar) { this.progressBar.style.width = `${scrollProgress}%`; } }); } activateScene(index) { // Update all scenes this.scenes.forEach((scene, i) => { if (i === index) { scene.classList.add('active'); } else { scene.classList.remove('active'); } }); this.currentScene = index; this.updateVisualization(index); } updateVisualization(sceneIndex) { const visualizationContainer = document.querySelector('.sticky > div'); if (!visualizationContainer) return; // Update visualization based on current scene switch(sceneIndex) { case 0: visualizationContainer.innerHTML = this.createScene0Visualization(); break; case 1: visualizationContainer.innerHTML = this.createScene1Visualization(); break; case 2: visualizationContainer.innerHTML = this.createScene2Visualization(); break; case 3: visualizationContainer.innerHTML = this.createScene3Visualization(); break; case 4: visualizationContainer.innerHTML = this.createScene4Visualization(); break; case 5: visualizationContainer.innerHTML = this.createScene5Visualization(); break; case 6: visualizationContainer.innerHTML = this.createScene6Visualization(); break; case 7: visualizationContainer.innerHTML = this.createScene7Visualization(); break; case 8: visualizationContainer.innerHTML = this.createScene8Visualization(); break; default: visualizationContainer.innerHTML = this.createDefaultVisualization(); } } createScene0Visualization() { return `

Cheater Dynamics

Good Cell Cheater Type-2 Cheaters: Transcribe but fail to translate
`; } createScene1Visualization() { return `

Constraints & Requirements

No Tags Allowed His-tag Intein Pristine Product, Universal QC
`; } createScene2Visualization() { return `

Dual Stress Sensors

σ32 Sensor σE/Cpx Sensor Native Stress Pathways
`; } createScene3Visualization() { return `

Production Gate Logic

(Scyt ∨ Senv) ∧ PM Stress OR + ProductionMode AND
`; } createScene4Visualization() { return `

Actuator & Latch

Flp-ssrA + FRT-Toxin
`; } createScene5Visualization() { return `

Translation Certificate

Ribosomal Re-initiation
`; } createScene6Visualization() { return `

Dual-Plug Antidote

Repressor sRNA Transcriptional + Translational Suppression
`; } createScene7Visualization() { return `

System Partitioning

Genome Core Plasmid App Stable Decision Logic + Swappable Components
`; } createScene8Visualization() { return `

Performance Metrics

≥10× Enrichment Target Enrichment False Commit Risk ≤0.1%
`; } createDefaultVisualization() { return `

System Overview

Synthetic Biology QC System
`; } } // Initialize when DOM is loaded document.addEventListener('DOMContentLoaded', () => { new ScrollytellingEngine(); // Set up reduced motion support if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { document.body.classList.add('reduced-motion'); } });