From 0f14b798eabc6049dca1d8902cb51e261014f7c6 Mon Sep 17 00:00:00 2001 From: avi Date: Thu, 14 May 2026 12:40:42 -0500 Subject: [PATCH] Save point: Desktop OS security comparison (scene7) + exfiltration scenes with real-world counter values, SOURCES references page, counter completion detection - Scene 7: interactive canvas comparing Linux/macOS/Windows security with lock icons, animated packets - Scene 7b: Desktop Data Exfiltration Simulator with server nodes per OS, live counter - Real-world counter values: Android 00/yr, iOS 50/yr, macOS 00/yr, Windows 50/yr - SOURCES button + references page (sceneRefs) with 10 citations with clickable links - Both SOURCES and RETURN buttons appear on counter completion or via Escape skip - Fixed JS-breaking bug: refsBackBtn element after broke all event listeners - GrapheneOS/Linux tabs show buttons immediately (no data = no wait) --- index.html | 766 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 740 insertions(+), 26 deletions(-) diff --git a/index.html b/index.html index 873720a..aebd4a1 100644 --- a/index.html +++ b/index.html @@ -302,6 +302,22 @@ .tl-year.dim::after { background: #007700; } /* NEXT button */ + .btn-row { + position: fixed; + bottom: 5%; + left: 50%; + transform: translateX(-50%); + z-index: 5; + display: flex; + gap: 1rem; + justify-content: center; + align-items: center; + } + .btn-row .btnNext { + position: static; + transform: none; + margin: 0; + } .btnNext { position: fixed; bottom: 5%; @@ -724,6 +740,69 @@ } .exfil-legend-item { display: flex; align-items: center; gap: 0.3rem; } .exfil-legend-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; } + + /* Scene 7 — Desktop OS interactive canvas */ + #s7BtnRow, #s8BtnRow { position: fixed; bottom: 5%; left: 50%; transform: translateX(-50%); z-index: 5; display: flex; gap: 1.5rem; justify-content: center; visibility: hidden; opacity: 0; transition: opacity 0.5s ease; } + #s7BtnRow .btnNext, #s8BtnRow .btnNext { position: static; transform: none; margin: 0; visibility: visible; opacity: 1; pointer-events: auto; } + .s7canvas { + width: 100%; + max-width: 55rem; + margin-top: 0.8rem; + opacity: 0; + transition: opacity 4s ease; + } + .s7canvas.visible { opacity: 1; } + #deskCanvas { + width: 100%; + height: 320px; + border: 1px solid #003300; + border-radius: 4px; + cursor: pointer; + display: block; + } + /* Scene 7b — Desktop Exfiltration */ + .s7bcanvas { + width: 100%; + max-width: 55rem; + margin-top: 0.8rem; + opacity: 0; + transition: opacity 4s ease; + } + .s7bcanvas.visible { opacity: 1; } + #exfilDeskCanvas { + width: 100%; + height: 360px; + border: 1px solid #003300; + border-radius: 4px; + cursor: pointer; + display: block; + } + /* Scene Refs — References / Citations */ + #sceneRefs { + position: fixed; top: 0; left: 0; width: 100%; height: 100%; + background: #000; color: #00ff00; z-index: 100; + display: flex; flex-direction: column; align-items: center; justify-content: center; + opacity: 0; pointer-events: none; transition: opacity 0.8s ease; + font-family: 'Courier New', monospace; + } + #sceneRefs.visible { opacity: 1; pointer-events: auto; } + #sceneRefs .refs-content { + width: 80%; max-width: 50rem; max-height: 80vh; overflow-y: auto; + padding: 1rem; border: 1px solid #003300; border-radius: 4px; + background: #001000; + } + #sceneRefs h2 { text-align: center; margin-bottom: 1.5rem; color: #00ff00; font-size: 1.4rem; } + #sceneRefs ol { padding-left: 1.5rem; margin: 0; } + #sceneRefs li { margin-bottom: 0.8rem; line-height: 1.5; font-size: 0.85rem; } + #sceneRefs a { color: #00cc00; text-decoration: underline; } + #sceneRefs a:hover { color: #ffff00; } + #sceneRefs .refs-back { text-align: center; margin-top: 1.5rem; } + #sceneRefs .refs-back button { + background: #003300; color: #00ff00; border: 1px solid #00ff00; + padding: 0.6rem 2rem; font-family: 'Courier New', monospace; font-size: 1rem; + cursor: pointer; border-radius: 4px; + } + #sceneRefs .refs-back button:hover { background: #005500; } @@ -817,10 +896,30 @@ -
+
-
- +
+ +
CLICK A TAB TO COMPARE OS SECURITY
+
+
+ + +
+
+ +
+
+
+ +
DATA VALUE EXPOSED: $0/yr
+
CLICK A TAB TO SEE WHERE YOUR DATA GOES
+
+
+
+ + +
@@ -843,7 +942,10 @@
CLICK A TAB TO SEE WHERE YOUR DATA GOES
- +
+ + +
@@ -1660,30 +1762,247 @@ },30); } + // Desktop security interactive visualization state + let deskAnimId = null; + let deskMode = 'linux'; + let deskPackets = []; + let deskClicked = false; + + const deskModeData = { + linux: { + label: 'LINUX', color: '#00ff00', + locks: { kernel: true, telemetry: true, firewall: true, sandbox: false, antivirus: true, datacollection: true, updates: true }, + packetType: 'stay', + status: 'LINUX GIVES YOU FULL CONTROL — NO TELEMETRY, NO RESTRICTIONS, COMPLETE TRANSPARENCY.' + }, + macos: { + label: 'macOS', color: '#ffaa00', + locks: { kernel: false, telemetry: false, firewall: true, sandbox: true, antivirus: true, datacollection: false, updates: false }, + packetType: 'mixed', + status: 'APPLE CONTROLS THE ECOSYSTEM — SOME PRIVACY FEATURES, BUT YOU MUST TRUST APPLE.' + }, + windows: { + label: 'WINDOWS', color: '#ff4444', + locks: { kernel: false, telemetry: false, firewall: true, sandbox: false, antivirus: false, datacollection: false, updates: false }, + packetType: 'leak', + status: 'MICROSOFT COLLECTS EXTENSIVE TELEMETRY — YOUR DEVICE IS NOT FULLY UNDER YOUR CONTROL.' + } + }; + + const deskTabs = [ + { id: 'linux', x: 170, w: 130, label: 'LINUX' }, + { id: 'macos', x: 335, w: 130, label: 'macOS' }, + { id: 'windows', x: 500, w: 130, label: 'WINDOWS' }, + ]; + + const deskFeatList = [ + { id: 'kernel', label: 'KERNEL', x: 215, y: 98, lx: 365 }, + { id: 'telemetry', label: 'TELEMETRY', x: 215, y: 131, lx: 365 }, + { id: 'firewall', label: 'FIREWALL', x: 215, y: 164, lx: 365 }, + { id: 'sandbox', label: 'SANDBOX', x: 215, y: 197, lx: 365 }, + { id: 'antivirus', label: 'ANTIVIRUS', x: 440, y: 98, lx: 570 }, + { id: 'datacollection', label: 'DATA COLL.', x: 440, y: 131, lx: 570 }, + { id: 'updates', label: 'UPDATES', x: 440, y: 164, lx: 570 }, + ]; + + function initDeskScene() { + deskClicked = false; + deskMode = 'linux'; + initDeskPackets(); + } + + function initDeskPackets() { + const mode = deskModeData[deskMode]; + deskPackets = []; + for (let i = 0; i < 4; i++) { + const orbit = mode.packetType === 'stay' || (mode.packetType === 'mixed' && i < 2); + deskPackets.push({ + angle: (i / 4) * Math.PI * 2, + speed: 0.012 + Math.random() * 0.008, + radius: 30 + i * 15, + orbit: orbit, + leakX: 400 + (Math.random() - 0.5) * 200, + leakY: 30 + Math.random() * 40, + progress: Math.random(), + }); + } + } + + function drawDeskScene() { + const canvas = document.getElementById('deskCanvas'); + if (!canvas || canvas.offsetParent === null) { deskAnimId = requestAnimationFrame(drawDeskScene); return; } + const ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + const mode = deskModeData[deskMode]; + + for (const tab of deskTabs) { + const active = tab.id === deskMode; + ctx.fillStyle = active ? mode.color : '#001100'; + ctx.strokeStyle = active ? mode.color : '#003300'; + ctx.lineWidth = active ? 2 : 1; + const r = 4; + ctx.beginPath(); + ctx.moveTo(tab.x + r, 8); + ctx.lineTo(tab.x + tab.w - r, 8); + ctx.quadraticCurveTo(tab.x + tab.w, 8, tab.x + tab.w, 8 + r); + ctx.lineTo(tab.x + tab.w, 30 - r); + ctx.quadraticCurveTo(tab.x + tab.w, 30, tab.x + tab.w - r, 30); + ctx.lineTo(tab.x + r, 30); + ctx.quadraticCurveTo(tab.x, 30, tab.x, 30 - r); + ctx.lineTo(tab.x, 8 + r); + ctx.quadraticCurveTo(tab.x, 8, tab.x + r, 8); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + ctx.fillStyle = active ? '#000' : (active ? '#000' : mode.color); + ctx.font = 'bold 11px Courier New, monospace'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(tab.label, tab.x + tab.w / 2, 19); + } + + // Monitor bezel + ctx.strokeStyle = '#003300'; + ctx.lineWidth = 2; + ctx.fillStyle = '#000400'; + ctx.beginPath(); + ctx.roundRect(170, 60, 460, 200, 12); + ctx.fill(); + ctx.stroke(); + + // Screen inner + ctx.fillStyle = '#001100'; + ctx.beginPath(); + ctx.roundRect(178, 68, 444, 180, 6); + ctx.fill(); + + // Stand + ctx.fillStyle = '#000400'; + ctx.beginPath(); + ctx.moveTo(370, 260); + ctx.lineTo(430, 260); + ctx.lineTo(415, 272); + ctx.lineTo(385, 272); + ctx.closePath(); + ctx.fill(); + + // Base + ctx.fillStyle = '#000400'; + ctx.fillRect(355, 272, 90, 6); + + for (const feat of deskFeatList) { + ctx.fillStyle = '#00ff00'; + ctx.font = '11px Courier New, monospace'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + ctx.fillText(feat.label, feat.x, feat.y); + const locked = mode.locks[feat.id]; + ctx.fillStyle = locked ? '#00ff00' : '#ff4444'; + ctx.font = '14px sans-serif'; + ctx.textAlign = 'center'; + ctx.fillText(locked ? '\u{1F512}' : '\u{1F513}', feat.lx, feat.y); + } + + for (const pkt of deskPackets) { + let x, y; + if (pkt.orbit) { + const cx = 400, cy = 160; + x = cx + Math.cos(pkt.angle + pkt.progress * Math.PI * 2) * pkt.radius; + y = cy + Math.sin(pkt.angle + pkt.progress * Math.PI * 2) * pkt.radius * 0.6; + x = Math.max(185, Math.min(615, x)); + y = Math.max(80, Math.min(250, y)); + } else { + const t = pkt.progress; + if (t < 0.5) { + const p = t * 2; + x = 400 + (pkt.leakX - 400) * p; + y = 160 + (pkt.leakY - 160) * p; + } else { + const p = (t - 0.5) * 2; + x = pkt.leakX + (pkt.leakX - 400) * p; + y = pkt.leakY - 80 * p; + } + } + ctx.shadowColor = mode.color; + ctx.shadowBlur = 8; + ctx.fillStyle = mode.color; + ctx.beginPath(); + ctx.arc(x, y, 4, 0, Math.PI * 2); + ctx.fill(); + ctx.shadowBlur = 0; + pkt.progress += pkt.speed; + if (pkt.progress >= 1) { + pkt.progress = 0; + if (!pkt.orbit) { + pkt.leakX = 400 + (Math.random() - 0.5) * 200; + pkt.leakY = 30 + Math.random() * 40; + } + } + } + + deskAnimId = requestAnimationFrame(drawDeskScene); + } + + function handleDeskCanvasClick(mx, my) { + const canvas = document.getElementById('deskCanvas'); + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const cx = (mx - rect.left) * (canvas.width / rect.width); + const cy = (my - rect.top) * (canvas.height / rect.height); + for (const tab of deskTabs) { + if (cx >= tab.x && cx <= tab.x + tab.w && cy >= 8 && cy <= 30) { + if (tab.id !== deskMode) { + deskMode = tab.id; + initDeskPackets(); + const status = document.getElementById('deskStatus'); + if (status) status.textContent = deskModeData[deskMode].status; + deskClicked = true; + const s7Row = document.getElementById('s7BtnRow'); + if (s7Row.style.visibility !== 'visible') { + s7Row.style.cssText = ''; + s7Row.style.visibility = 'visible'; + let ro = 0; + const rfi = setInterval(() => { + ro += 0.05; if (ro >= 1) { ro = 1; clearInterval(rfi); s7Row.style.pointerEvents = 'auto'; } + s7Row.style.opacity = ro; + }, 30); + } + } + break; + } + } + } + function loadScene7(sceneElem) { s7c=[]; sceneElem.style.display='flex'; const txt=document.getElementById('s7Text'); - const vis=document.getElementById('s7Visual'); + const canvasDiv=document.getElementById('s7Canvas'); txt.innerHTML=''; - vis.className='s7visual'; + canvasDiv.classList.remove('visible'); + if (deskAnimId) { cancelAnimationFrame(deskAnimId); deskAnimId = null; } + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } + const s7Row = document.getElementById('s7BtnRow'); + s7Row.style.visibility = 'hidden'; + s7Row.style.opacity = '0'; + s7Row.style.pointerEvents = 'none'; let o=0; const fi=setInterval(()=>{ - if (sceneElem.style.display !== 'flex' || document.getElementById('returnFromScene7').style.visibility === 'visible') { clearInterval(fi); return; } + if (sceneElem.style.display !== 'flex' || document.getElementById('s7BtnRow').style.visibility === 'visible') { clearInterval(fi); return; } o+=0.05;if(o>=1){o=1;clearInterval(fi); - typeCalmly(txt,"DESKTOP COMPUTING IS DOMINATED BY CORPORATIONS THAT MONETIZE YOUR DATA AND RESTRICT YOUR FREEDOM.",()=>{ + typeCalmly(txt,"YOUR DESKTOP OS DETERMINES WHO CONTROLS YOUR COMPUTER — YOU OR A CORPORATION.",()=>{ const t1=setTimeout(()=>{ - txt.innerHTML+="\n\n"; - vis.classList.add('visible'); - vis.innerHTML=buildS6DesktopTable(); - typeCalmly(txt,"LINUX IS THE ONLY OS THAT RESPECTS YOUR FREEDOM — BECAUSE IT'S BUILT BY THE COMMUNITY, NOT A CORPORATION.",()=>{ - const t3=setTimeout(()=>{ - document.getElementById('returnFromScene7').style.cssText=''; - showNextBtn('returnFromScene7'); - },400); - s7c.push(t3); + typeCalmly(txt,"\n\nCLICK A TAB TO SEE HOW EACH OS HANDLES SECURITY AND PRIVACY.",()=>{ + const t2=setTimeout(()=>{ + canvasDiv.classList.add('visible'); + initDeskScene(); + drawDeskScene(); + const status=document.getElementById('deskStatus'); + if(status) status.textContent=deskModeData[deskMode].status; + },300); + s7c.push(t2); },8,20,s7c); - },300); + },600); s7c.push(t1); },8,20,s7c); } @@ -2131,6 +2450,7 @@ let s8c = []; let s9c = []; let s8bc = []; + let s7bc = []; // Scene 8b — Data Exfiltration Simulator let exfilAnimId = null; @@ -2141,8 +2461,8 @@ const exfilModeData = { grapheneos: { label: 'GRAPHENEOS', counterMax: 0, color: '#00ff00', status: 'YOUR DATA STAYS ON YOUR DEVICE. NO TRACKING. NO PROFILING.' }, - android: { label: 'ANDROID', counterMax: 1200, color: '#ff4444', status: 'GOOGLE AND THIRD-PARTY APPS COLLECT YOUR DATA BY DEFAULT. YOU ARE THE PRODUCT.' }, - ios: { label: 'iOS', counterMax: 600, color: '#ffff00', status: 'APPLE COLLECTS DATA THROUGH ITS SERVICES. SOME THIRD-PARTY TRACKING IS BLOCKED.' }, + android: { label: 'ANDROID', counterMax: 200, color: '#ff4444', status: 'GOOGLE AND THIRD-PARTY APPS COLLECT YOUR DATA BY DEFAULT. YOU ARE THE PRODUCT.' }, + ios: { label: 'iOS', counterMax: 150, color: '#ffff00', status: 'APPLE COLLECTS DATA THROUGH ITS SERVICES. SOME THIRD-PARTY TRACKING IS BLOCKED.' }, }; const exfilTabs = [ @@ -2357,6 +2677,14 @@ const st = document.getElementById('exfilStatus'); if (st) st.textContent = mode.status; + if (mode.counterMax > 0 && exfilCounter >= mode.counterMax && !window._exfilDone) { + window._exfilDone = true; + const rb = document.getElementById('returnFromScene8b'); + if (rb) { rb.style.cssText = ''; showNextBtn('returnFromScene8b'); } + const sb = document.getElementById('sourcesFromScene8b'); + if (sb) { sb.style.cssText = ''; showNextBtn('sourcesFromScene8b'); } + } + exfilAnimId = requestAnimationFrame(drawExfilScene); } @@ -2372,6 +2700,13 @@ exfilMode = tab.id; exfilPackets = []; exfilCounterTarget = 0; + window._exfilDone = false; + if (exfilModeData[exfilMode].counterMax === 0) { + const rb = document.getElementById('returnFromScene8b'); + if (rb) { rb.style.cssText = ''; showNextBtn('returnFromScene8b'); } + const sb = document.getElementById('sourcesFromScene8b'); + if (sb) { sb.style.cssText = ''; showNextBtn('sourcesFromScene8b'); } + } } break; } @@ -2408,6 +2743,289 @@ }, 30); } + // Scene Refs tracking + window.refsCaller = null; + + // Scene 7b — Desktop Exfiltration Simulator + let exfilDeskAnimId = null; + let exfilDeskMode = 'linux'; + let exfilDeskPackets = []; + let exfilDeskCounter = 0; + let exfilDeskCounterTarget = 0; + + const exfilDeskModeData = { + linux: { label: 'LINUX', counterMax: 0, color: '#00ff00', status: 'LINUX SENDS NO TELEMETRY. YOUR DATA STAYS ON YOUR MACHINE.' }, + macos: { label: 'macOS', counterMax: 100, color: '#ffaa00', status: 'APPLE COLLECTS USAGE DATA AND CAN REMOTELY CONTROL YOUR DEVICE.' }, + windows: { label: 'WINDOWS', counterMax: 150, color: '#ff4444', status: 'MICROSOFT COLLECTS EXTENSIVE TELEMETRY AND SHARES DATA WITH PARTNERS.' }, + }; + + const exfilDeskTabs = [ + { id: 'linux', x: 170, w: 130, label: 'LINUX' }, + { id: 'macos', x: 335, w: 130, label: 'macOS' }, + { id: 'windows', x: 500, w: 130, label: 'WINDOWS' }, + ]; + + const exfilDeskServers = { + linux: [], + macos: [ + { id: 'apple', label: 'APPLE', x: 575, y: 65, w: 100, h: 34, color: '#aaaaaa' }, + { id: 'ads', label: 'AD NETWORKS', x: 555, y: 185, w: 115, h: 34, color: '#ff44ff' }, + ], + windows: [ + { id: 'ms', label: 'MICROSOFT', x: 535, y: 55, w: 110, h: 34, color: '#00a4ef' }, + { id: 'broker', label: 'DATA BROKER', x: 645, y: 115, w: 115, h: 34, color: '#ff6644' }, + { id: 'ads', label: 'AD NETWORKS', x: 560, y: 195, w: 115, h: 34, color: '#ff44ff' }, + { id: 'telemetry', label: 'TELEMETRY', x: 635, y: 285, w: 110, h: 34, color: '#ff0000' }, + ], + }; + + const exfilDeskDataTypes = [ + { id: 'telemetry', label: 'TELEMETRY', color: '#ff6644' }, + { id: 'browsing', label: 'BROWSING', color: '#44aaff' }, + { id: 'files', label: 'FILES', color: '#ff44ff' }, + { id: 'keystrokes', label: 'KEYSTROKES', color: '#ffff44' }, + { id: 'mic', label: 'MIC', color: '#44ffaa' }, + { id: 'location', label: 'LOCATION', color: '#ff8844' }, + ]; + + function initExfilDeskScene() { + exfilDeskMode = 'linux'; + exfilDeskPackets = []; + exfilDeskCounter = 0; + exfilDeskCounterTarget = 0; + const legend = document.getElementById('exfilDeskLegend'); + if (legend) { + legend.innerHTML = ''; + for (const dt of exfilDeskDataTypes) { + const item = document.createElement('span'); + item.className = 'exfil-legend-item'; + item.innerHTML = '' + dt.label; + legend.appendChild(item); + } + } + } + + function spawnExfilDeskPacket() { + const servers = exfilDeskServers[exfilDeskMode]; + if (servers.length === 0) return; + const server = servers[Math.floor(Math.random() * servers.length)]; + const dataType = exfilDeskDataTypes[Math.floor(Math.random() * exfilDeskDataTypes.length)]; + const px = 80, py = 65, pw = 140, ph = 245; + const fromX = px + 10 + Math.random() * (pw - 20); + const fromY = py + 40 + Math.random() * (ph - 60); + const toX = server.x + Math.random() * server.w; + const toY = server.y + Math.random() * server.h; + exfilDeskPackets.push({ + fromX, fromY, toX, toY, + dataType, + progress: 0, + speed: 0.008 + Math.random() * 0.006, + }); + } + + function drawExfilDeskScene() { + const canvas = document.getElementById('exfilDeskCanvas'); + if (!canvas || canvas.offsetParent === null) { exfilDeskAnimId = requestAnimationFrame(drawExfilDeskScene); return; } + const ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + const mode = exfilDeskModeData[exfilDeskMode]; + const servers = exfilDeskServers[exfilDeskMode]; + + // Tabs + for (const tab of exfilDeskTabs) { + const active = tab.id === exfilDeskMode; + ctx.fillStyle = active ? mode.color : '#001100'; + ctx.strokeStyle = active ? mode.color : '#003300'; + ctx.lineWidth = active ? 2 : 1; + const r = 4; + ctx.beginPath(); + ctx.moveTo(tab.x + r, 8); + ctx.lineTo(tab.x + tab.w - r, 8); + ctx.quadraticCurveTo(tab.x + tab.w, 8, tab.x + tab.w, 8 + r); + ctx.lineTo(tab.x + tab.w, 30 - r); + ctx.quadraticCurveTo(tab.x + tab.w, 30, tab.x + tab.w - r, 30); + ctx.lineTo(tab.x + r, 30); + ctx.quadraticCurveTo(tab.x, 30, tab.x, 30 - r); + ctx.lineTo(tab.x, 8 + r); + ctx.quadraticCurveTo(tab.x, 8, tab.x + r, 8); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); + ctx.fillStyle = active ? '#000' : mode.color; + ctx.font = 'bold 11px Courier New, monospace'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(tab.label, tab.x + tab.w / 2, 19); + } + + // Monitor silhouette + const px = 70, py = 55, pw = 140, ph = 245; + ctx.shadowColor = mode.color; + ctx.shadowBlur = 15; + ctx.strokeStyle = mode.color; + ctx.lineWidth = 2; + ctx.fillStyle = '#000800'; + ctx.beginPath(); + ctx.roundRect(px, py, pw, ph, 18); + ctx.fill(); + ctx.stroke(); + ctx.shadowBlur = 0; + + ctx.fillStyle = '#001100'; + ctx.beginPath(); + ctx.roundRect(px + 8, py + 35, pw - 16, ph - 55, 6); + ctx.fill(); + + ctx.fillStyle = mode.color; + ctx.font = 'bold 12px Courier New, monospace'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(exfilDeskMode === 'linux' ? 'SECURE' : 'LEAKING', px + pw / 2, py + ph / 2); + + // Servers + connection lines + for (const srv of servers) { + ctx.strokeStyle = srv.color + '22'; + ctx.lineWidth = 1; + ctx.setLineDash([4, 6]); + ctx.beginPath(); + ctx.moveTo(px + pw / 2, py + ph / 2); + ctx.lineTo(srv.x + srv.w / 2, srv.y + srv.h / 2); + ctx.stroke(); + ctx.setLineDash([]); + + ctx.fillStyle = srv.color + '33'; + ctx.strokeStyle = srv.color; + ctx.lineWidth = 1.5; + ctx.beginPath(); + ctx.roundRect(srv.x, srv.y, srv.w, srv.h, 4); + ctx.fill(); + ctx.stroke(); + ctx.fillStyle = srv.color; + ctx.font = 'bold 10px Courier New, monospace'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText(srv.label, srv.x + srv.w / 2, srv.y + srv.h / 2); + } + + if (servers.length > 0 && Math.random() < 0.06) spawnExfilDeskPacket(); + + for (let i = exfilDeskPackets.length - 1; i >= 0; i--) { + const pkt = exfilDeskPackets[i]; + const x = pkt.fromX + (pkt.toX - pkt.fromX) * pkt.progress; + const y = pkt.fromY + (pkt.toY - pkt.fromY) * pkt.progress; + + ctx.strokeStyle = pkt.dataType.color + '44'; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(pkt.fromX, pkt.fromY); + ctx.lineTo(x, y); + ctx.stroke(); + + ctx.shadowColor = pkt.dataType.color; + ctx.shadowBlur = 10; + ctx.fillStyle = pkt.dataType.color; + ctx.beginPath(); + ctx.arc(x, y, 5, 0, Math.PI * 2); + ctx.fill(); + ctx.shadowBlur = 0; + + ctx.fillStyle = pkt.dataType.color; + ctx.font = '8px Courier New, monospace'; + ctx.textAlign = 'center'; + ctx.textBaseline = 'bottom'; + ctx.fillText(pkt.dataType.label, x, y - 6); + + pkt.progress += pkt.speed; + if (pkt.progress >= 1) { + exfilDeskPackets.splice(i, 1); + if (exfilDeskCounterTarget < mode.counterMax) { + exfilDeskCounterTarget = Math.min(mode.counterMax, exfilDeskCounterTarget + 18); + } + } + } + + while (exfilDeskPackets.length > 30) exfilDeskPackets.shift(); + + if (exfilDeskCounter < exfilDeskCounterTarget) { + exfilDeskCounter = Math.min(exfilDeskCounterTarget, exfilDeskCounter + 5); + } else if (exfilDeskCounter > exfilDeskCounterTarget) { + exfilDeskCounter = Math.max(exfilDeskCounterTarget, exfilDeskCounter - 5); + } + const ce = document.getElementById('exfilDeskCounter'); + if (ce) { + ce.textContent = 'DATA VALUE EXPOSED: $' + exfilDeskCounter.toFixed(0) + '/yr'; + ce.style.color = exfilDeskCounter > 0 ? '#ff4444' : '#00ff00'; + } + + const st = document.getElementById('exfilDeskStatus'); + if (st) st.textContent = mode.status; + + if (mode.counterMax > 0 && exfilDeskCounter >= mode.counterMax && !window._exfilDeskDone) { + window._exfilDeskDone = true; + const rb = document.getElementById('returnFromScene7b'); + if (rb) { rb.style.cssText = ''; showNextBtn('returnFromScene7b'); } + const sb = document.getElementById('sourcesFromScene7b'); + if (sb) { sb.style.cssText = ''; showNextBtn('sourcesFromScene7b'); } + } + + exfilDeskAnimId = requestAnimationFrame(drawExfilDeskScene); + } + + function handleExfilDeskCanvasClick(mx, my) { + const canvas = document.getElementById('exfilDeskCanvas'); + if (!canvas) return; + const rect = canvas.getBoundingClientRect(); + const cx = (mx - rect.left) * (canvas.width / rect.width); + const cy = (my - rect.top) * (canvas.height / rect.height); + for (const tab of exfilDeskTabs) { + if (cx >= tab.x && cx <= tab.x + tab.w && cy >= 8 && cy <= 30) { + if (tab.id !== exfilDeskMode) { + exfilDeskMode = tab.id; + exfilDeskPackets = []; + exfilDeskCounterTarget = 0; + window._exfilDeskDone = false; + if (exfilDeskModeData[exfilDeskMode].counterMax === 0) { + const rb = document.getElementById('returnFromScene7b'); + if (rb) { rb.style.cssText = ''; showNextBtn('returnFromScene7b'); } + const sb = document.getElementById('sourcesFromScene7b'); + if (sb) { sb.style.cssText = ''; showNextBtn('sourcesFromScene7b'); } + } + } + break; + } + } + } + + function loadScene7b(sceneElem) { + s7bc = []; + sceneElem.style.display = 'flex'; + const txt = document.getElementById('s7bText'); + const canvasDiv = document.getElementById('s7bCanvas'); + txt.innerHTML = ''; + canvasDiv.classList.remove('visible'); + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } + let o = 0; + const fi = setInterval(() => { + if (sceneElem.style.display !== 'flex' || document.getElementById('returnFromScene7b').style.visibility === 'visible') { clearInterval(fi); return; } + o += 0.05; if (o >= 1) { o = 1; clearInterval(fi); + typeCalmly(txt, "EVERY CLICK, EVERY KEYSTROKE, EVERY FILE YOU OPEN — YOUR OS MAY BE REPORTING IT ALL TO CORPORATE SERVERS.", () => { + const t1 = setTimeout(() => { + typeCalmly(txt, "\n\nCLICK THE TABS TO SEE WHERE YOUR DATA ACTUALLY GOES.", () => { + const t2 = setTimeout(() => { + canvasDiv.classList.add('visible'); + initExfilDeskScene(); + drawExfilDeskScene(); + }, 300); + s7bc.push(t2); + }, 8, 20, s7bc); + }, 600); + s7bc.push(t1); + }, 8, 20, s7bc); + } + sceneElem.style.opacity = o; + }, 30); + } + // Initialize on load window.addEventListener('load', () => { setTimeout(crtFlicker, 1500); @@ -2508,6 +3126,8 @@ if (meshAnimId) { cancelAnimationFrame(meshAnimId); meshAnimId = null; } if (phoneAnimId) { cancelAnimationFrame(phoneAnimId); phoneAnimId = null; } if (exfilAnimId) { cancelAnimationFrame(exfilAnimId); exfilAnimId = null; } + if (deskAnimId) { cancelAnimationFrame(deskAnimId); deskAnimId = null; } + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } const s6 = document.getElementById('scene6'); s6.style.display = 'flex'; s6.style.opacity = '1'; @@ -2542,10 +3162,37 @@ handlePhoneCanvasClick(e.clientX, e.clientY); }); + document.getElementById('deskCanvas').addEventListener('click', (e) => { + handleDeskCanvasClick(e.clientX, e.clientY); + }); + document.getElementById('exfilCanvas').addEventListener('click', (e) => { handleExfilCanvasClick(e.clientX, e.clientY); }); + document.getElementById('exfilDeskCanvas').addEventListener('click', (e) => { + handleExfilDeskCanvasClick(e.clientX, e.clientY); + }); + + document.getElementById('nextFromScene7').addEventListener('click', () => { + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } + if (deskAnimId) { cancelAnimationFrame(deskAnimId); deskAnimId = null; } + document.getElementById('scene7').style.display = 'none'; + loadScene7b(document.getElementById('scene7b')); + }); + + document.getElementById('sourcesFromScene7b').addEventListener('click', () => { + refsCaller = 'scene7b'; + document.getElementById('scene7b').style.display = 'none'; + document.getElementById('sceneRefs').classList.add('visible'); + }); + + document.getElementById('returnFromScene7b').addEventListener('click', () => { + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } + document.getElementById('scene7b').style.display = 'none'; + showTechHub(); + }); + document.getElementById('nextFromScene8').addEventListener('click', () => { if (exfilAnimId) { cancelAnimationFrame(exfilAnimId); exfilAnimId = null; } if (phoneAnimId) { cancelAnimationFrame(phoneAnimId); phoneAnimId = null; } @@ -2553,6 +3200,12 @@ loadScene8b(document.getElementById('scene8b')); }); + document.getElementById('sourcesFromScene8b').addEventListener('click', () => { + refsCaller = 'scene8b'; + document.getElementById('scene8b').style.display = 'none'; + document.getElementById('sceneRefs').classList.add('visible'); + }); + document.getElementById('returnFromScene8b').addEventListener('click', () => { if (exfilAnimId) { cancelAnimationFrame(exfilAnimId); exfilAnimId = null; } document.getElementById('scene8b').style.display = 'none'; @@ -2562,6 +3215,19 @@ // Keyboard shortcuts for testing document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { + // SceneRefs: return to caller + const refsDiv = document.getElementById('sceneRefs'); + if (refsDiv.classList.contains('visible')) { + refsDiv.classList.remove('visible'); + if (refsCaller === 'scene7b') { + document.getElementById('scene7b').style.display = 'flex'; + } else if (refsCaller === 'scene8b') { + document.getElementById('scene8b').style.display = 'flex'; + } + refsCaller = null; + return; + } + // Escape alone: Skip current animation to text // Scene 1 skip (rain or typewriter) @@ -2698,12 +3364,18 @@ if (s7.style.display === 'flex') { s7c.forEach(t => clearTimeout(t)); s7c = []; const txt = document.getElementById('s7Text'); - const vis = document.getElementById('s7Visual'); - txt.innerHTML = "DESKTOP COMPUTING IS DOMINATED BY CORPORATIONS THAT MONETIZE YOUR DATA AND RESTRICT YOUR FREEDOM.\n\nLINUX IS THE ONLY OS THAT RESPECTS YOUR FREEDOM — BECAUSE IT'S BUILT BY THE COMMUNITY, NOT A CORPORATION."; - vis.innerHTML=buildS6DesktopTable(); - vis.classList.add('visible'); - document.getElementById('returnFromScene7').style.cssText=''; - showNextBtn('returnFromScene7'); + txt.innerHTML = "YOUR DESKTOP OS DETERMINES WHO CONTROLS YOUR COMPUTER — YOU OR A CORPORATION.\n\nCLICK A TAB TO SEE HOW EACH OS HANDLES SECURITY AND PRIVACY."; + const canvasDiv = document.getElementById('s7Canvas'); + canvasDiv.classList.add('visible'); + if (deskAnimId) { cancelAnimationFrame(deskAnimId); deskAnimId = null; } + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } + initDeskScene(); + drawDeskScene(); + const s7Row = document.getElementById('s7BtnRow'); + s7Row.style.cssText = ''; + s7Row.style.visibility = 'visible'; + s7Row.style.opacity = '1'; + s7Row.style.pointerEvents = 'auto'; } // Scene 8 skip @@ -2751,8 +3423,29 @@ if (exfilAnimId) { cancelAnimationFrame(exfilAnimId); exfilAnimId = null; } initExfilScene(); drawExfilScene(); + window._exfilDone = true; document.getElementById('returnFromScene8b').style.cssText=''; showNextBtn('returnFromScene8b'); + document.getElementById('sourcesFromScene8b').style.cssText=''; + showNextBtn('sourcesFromScene8b'); + } + + // Scene 7b skip + const s7b = document.getElementById('scene7b'); + if (s7b.style.display === 'flex') { + s7bc.forEach(t => clearTimeout(t)); s7bc = []; + const txt = document.getElementById('s7bText'); + txt.innerHTML = "EVERY CLICK, EVERY KEYSTROKE, EVERY FILE YOU OPEN — YOUR OS MAY BE REPORTING IT ALL TO CORPORATE SERVERS.\n\nCLICK THE TABS TO SEE WHERE YOUR DATA ACTUALLY GOES."; + const canvasDiv = document.getElementById('s7bCanvas'); + canvasDiv.classList.add('visible'); + if (exfilDeskAnimId) { cancelAnimationFrame(exfilDeskAnimId); exfilDeskAnimId = null; } + initExfilDeskScene(); + drawExfilDeskScene(); + window._exfilDeskDone = true; + document.getElementById('returnFromScene7b').style.cssText=''; + showNextBtn('returnFromScene7b'); + document.getElementById('sourcesFromScene7b').style.cssText=''; + showNextBtn('sourcesFromScene7b'); } } @@ -2767,5 +3460,26 @@ } }); + + + +