• dfgdfgfdg

  • fsdfsdfsdf

  • dfsdfsdf

  • sdfsdfsdf

  • sdfsdfsdf

CHRONOS // TEMPORAL CARTOGRAPHY INTERFACE @import url(‘https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=VT323&display=swap’); :root{ –bg:#040604; –bg-2:#081008; –phosphor:#3CFF7A; –phosphor-dim:#12622e; –grid:#0e2b18; –amber:#FFB627; –red:#FF3B3B; –cyan:#39e6c8; –panel:#050b06; –text-dim:#5f9a72; } *{box-sizing:border-box; margin:0; padding:0;} html,body{ width:100%; height:100%; background:var(–bg); overflow:hidden; font-family:’Share Tech Mono’, monospace; color:var(–phosphor); cursor:crosshair; user-select:none; } /* ———- CRT / SCANLINE / VIGNETTE LAYERS ———- */ #crt-overlay{ position:fixed; inset:0; pointer-events:none; z-index:900; background: repeating-linear-gradient( to bottom, rgba(0,0,0,0) 0px, rgba(0,0,0,0) 1px, rgba(0,0,0,0.18) 2px, rgba(0,0,0,0.18) 3px ); mix-blend-mode:multiply; animation:flicker 6s infinite; } #vignette{ position:fixed; inset:0; pointer-events:none; z-index:899; background:radial-gradient(ellipse at center, rgba(0,0,0,0) 55%, rgba(0,0,0,0.75) 100%); } @keyframes flicker{ 0%,100%{opacity:1;} 92%{opacity:1;} 93%{opacity:0.82;} 94%{opacity:1;} 96%{opacity:0.9;} 97%{opacity:1;} } .glitch-active{ animation: glitchjump 0.15s steps(2) 2; } @keyframes glitchjump{ 0%{ transform:translate(0,0); filter:none; } 25%{ transform:translate(-2px,1px); filter:hue-rotate(20deg); } 50%{ transform:translate(2px,-1px); filter:hue-rotate(-15deg); } 75%{ transform:translate(-1px,0); } 100%{ transform:translate(0,0); filter:none; } } /* ———- BOOT SEQUENCE ———- */ #boot{ position:fixed; inset:0; z-index:1000; background:#000; color:var(–phosphor); font-size:16px; padding:40px; line-height:1.6; white-space:pre-wrap; } #boot .prompt-line{opacity:0;} #boot-cursor{display:inline-block; width:9px; height:16px; background:var(–phosphor); animation:blink 1s steps(1) infinite; vertical-align:middle;} @keyframes blink{ 50%{opacity:0;} } /* ———- HUD ———- */ #hud-top{ position:fixed; top:0; left:0; right:0; z-index:50; display:flex; justify-content:space-between; align-items:center; padding:10px 22px; border-bottom:1px solid var(–phosphor-dim); background:linear-gradient(to bottom, rgba(4,10,4,0.95), rgba(4,10,4,0.6)); font-size:12px; letter-spacing:2px; } #hud-top .brand{ color:var(–phosphor); font-weight:bold; text-shadow:0 0 6px var(–phosphor); } #hud-top .sub{ color:var(–text-dim); } #hud-clock{ color:var(–amber); text-shadow:0 0 6px var(–amber); } #hud-readout{ position:fixed; top:44px; left:22px; z-index:50; font-family:’VT323′, monospace; font-size:34px; color:var(–phosphor); text-shadow:0 0 10px rgba(60,255,122,0.6); letter-spacing:2px; } #hud-readout .range-label{ font-family:’Share Tech Mono’, monospace; font-size:11px; color:var(–text-dim); letter-spacing:2px; display:block; margin-top:-6px; } /* ———- VIEWPORT ———- */ #viewport{ position:absolute; top:0; left:0; width:100%; height:100%; overflow:hidden; } #stage{ position:absolute; top:0; left:0; width:100%; height:100%; transform-origin: 0 0; } svg#timeline-svg{ position:absolute; top:0; left:0; overflow:visible; } .grid-line{ stroke:var(–grid); stroke-width:1; } .main-axis{ stroke:var(–phosphor-dim); stroke-width:2; } .now-line{ stroke:var(–amber); stroke-width:1.5; stroke-dasharray:4 4; opacity:0.8;} .tick-label{ font-family:’Share Tech Mono’, monospace; fill:var(–text-dim); font-size:11px; letter-spacing:1px; } .tick-label.major{ fill:var(–phosphor); font-size:12px; } .branch-line{ stroke-width:1.5; opacity:0.75; } .connector-line{ stroke-width:1; stroke-dasharray:2 3; opacity:0.6; } .node-circle{ cursor:pointer; transition:r 0.15s ease; } .node-circle:hover{ } .node-label{ font-family:’Share Tech Mono’, monospace; font-size:11px; pointer-events:none; letter-spacing:0.5px; } .branch-tag{ font-family:’Share Tech Mono’, monospace; font-size:10px; letter-spacing:2px; opacity:0.85; } /* ———- BOTTOM DOCK ———- */ #dock{ position:fixed; bottom:0; left:0; right:0; z-index:60; border-top:1px solid var(–phosphor-dim); background:linear-gradient(to top, rgba(4,10,4,0.97), rgba(4,10,4,0.75)); padding:12px 18px; display:flex; align-items:center; gap:18px; flex-wrap:wrap; } .btn{ background:transparent; border:1px solid var(–phosphor-dim); color:var(–phosphor); font-family:’Share Tech Mono’, monospace; font-size:11px; letter-spacing:2px; padding:8px 14px; cursor:pointer; text-transform:uppercase; transition:all .12s ease; } .btn:hover{ background:var(–phosphor); color:#000; box-shadow:0 0 12px var(–phosphor); } .btn.amber{ border-color:#7a5a10; color:var(–amber); } .btn.amber:hover{ background:var(–amber); color:#000; box-shadow:0 0 12px var(–amber); } .btn.red{ border-color:#7a1a1a; color:var(–red); } .btn.red:hover{ background:var(–red); color:#000; box-shadow:0 0 12px var(–red); } #zoom-group{ display:flex; align-items:center; gap:6px; } #zoom-readout{ font-size:11px; color:var(–text-dim); width:90px; text-align:center; } #scrubber-wrap{ flex:1; min-width:220px; height:26px; position:relative; border:1px solid var(–phosphor-dim); background:var(–panel); } #scrubber-fill{ position:absolute; top:0; bottom:0; left:0; background:rgba(60,255,122,0.18); border-right:1px solid var(–phosphor); cursor:grab; } /* ———- DETAIL PANEL ———- */ #detail-panel{ position:fixed; top:0; right:-380px; width:360px; height:100%; z-index:80; background:rgba(3,8,4,0.97); border-left:1px solid var(–phosphor-dim); padding:70px 24px 24px 24px; transition:right 0.25s ease; overflow-y:auto; } #detail-panel.open{ right:0; } #detail-panel h2{ font-family:’VT323′, monospace; font-size:28px; color:var(–phosphor); text-shadow:0 0 8px rgba(60,255,122,0.5); letter-spacing:1px; margin-bottom:4px; } #detail-panel .yr{ color:var(–amber); font-size:13px; letter-spacing:2px; margin-bottom:14px; display:block;} #detail-panel .branch-name{ color:var(–cyan); font-size:11px; letter-spacing:2px; display:block; margin-bottom:18px;} #detail-panel p{ font-size:13px; line-height:1.7; color:#cfe9d8; } #detail-panel .close-btn{ position:absolute; top:22px; right:22px; cursor:pointer; color:var(–red); font-size:18px; border:1px solid #7a1a1a; width:28px; height:28px; text-align:center; line-height:26px; } #detail-panel .del-btn{ margin-top:24px; } /* ———- ADD NODE MODAL ———- */ #modal-overlay{ position:fixed; inset:0; z-index:200; background:rgba(0,0,0,0.75); display:none; align-items:center; justify-content:center; } #modal-overlay.open{ display:flex; } #modal{ width:420px; max-width:92vw; background:var(–panel); border:1px solid var(–phosphor); box-shadow:0 0 30px rgba(60,255,122,0.25); padding:26px; cursor:default; } #modal h3{ font-family:’VT323′, monospace; font-size:24px; letter-spacing:2px; color:var(–phosphor); margin-bottom:18px; text-shadow:0 0 6px rgba(60,255,122,0.5); } .field{ margin-bottom:14px; } .field label{ display:block; font-size:10px; letter-spacing:2px; color:var(–text-dim); margin-bottom:5px; } .field input, .field textarea, .field select{ width:100%; background:#000; border:1px solid var(–phosphor-dim); color:var(–phosphor); font-family:’Share Tech Mono’, monospace; font-size:13px; padding:8px 10px; outline:none; } .field input:focus, .field textarea:focus, .field select:focus{ border-color:var(–phosphor); box-shadow:0 0 8px rgba(60,255,122,0.3); } .field textarea{ resize:vertical; min-height:60px; } .color-swatches{ display:flex; gap:8px; margin-top:4px; } .swatch{ width:24px; height:24px; border:2px solid transparent; cursor:pointer; } .swatch.selected{ border-color:#fff; } .modal-actions{ display:flex; justify-content:flex-end; gap:10px; margin-top:20px; } /* ———- HELP / LEGEND ———- */ #legend{ position:fixed; top:44px; right:22px; z-index:50; font-size:10px; color:var(–text-dim); text-align:right; letter-spacing:1px; line-height:1.8; } #legend b{ color:var(–phosphor); } ::-webkit-scrollbar{ width:6px; } ::-webkit-scrollbar-thumb{ background:var(–phosphor-dim); } @media (max-width:720px){ #hud-readout{ font-size:24px; } #detail-panel{ width:100%; right:-100%; } #legend{ display:none; } .btn{ font-size:9px; padding:6px 8px; } }
CHRONOS  // TEMPORAL CARTOGRAPHY INTERFACE  REV 3.3.0
SYSTEM NOMINAL
-6000 VIEWPORT ANCHOR // SCROLL+DRAG TO NAVIGATE
LMB DRAG — pan timeline
SCROLL — zoom in / out
CLICK NODE — open record
X
MAIN SEQUENCE

EVENT

0 AD

OVERVIEW
(function(){ /* ======================= CONFIG ======================= */ const YEAR_MIN = -6000; const YEAR_MAX = 3033; const NOW_YEAR = 2026; const COLORS = [‘#3CFF7A’, ‘#FFB627’, ‘#39e6c8’, ‘#FF3B3B’, ‘#c9c9ff’]; const STORAGE_KEY = ‘chronos_timeline_data_v1’; const SVG_NS = ‘http://www.w3.org/2000/svg&#8217;; const LANE_GAP = 70; // vertical distance between lanes const NODE_R = 6; const AXIS_Y = 0; // main axis local y (stage coords), viewport centers it /* ======================= SEED DATA ======================= */ const seedEvents = [ {id:’e1′, year:-6000, branch:’MAIN’, title:’Neolithic Settlements’, desc:’Early agricultural communities take root across the Fertile Crescent, marking humanity\’s shift from foraging to farming.’, color:’#3CFF7A’}, {id:’e2′, year:-3100, branch:’MAIN’, title:’Unification of Egypt’, desc:’Upper and Lower Egypt are unified under a single ruler, beginning the era of the Pharaohs.’, color:’#3CFF7A’}, {id:’e3′, year:-2560, branch:’MAIN’, title:’Great Pyramid of Giza’, desc:’Construction of the Great Pyramid concludes, the last surviving Ancient Wonder of the World.’, color:’#3CFF7A’}, {id:’e4′, year:-776, branch:’MAIN’, title:’First Olympic Games’, desc:’Athletic competition begins at Olympia, Greece, inaugurating a tradition that spans millennia.’, color:’#3CFF7A’}, {id:’e5′, year:-509, branch:’MAIN’, title:’Roman Republic Founded’, desc:’Rome overthrows its monarchy, establishing a republic that will grow into an empire.’, color:’#3CFF7A’}, {id:’e6′, year:-221, branch:’MAIN’, title:’Unification of China’, desc:’Qin Shi Huang unifies warring states, becoming the first emperor of a unified China.’, color:’#3CFF7A’}, {id:’e7′, year:0, branch:’MAIN’, title:’Common Era Begins’, desc:’The reference point of the modern calendar system.’, color:’#FFB627′}, {id:’e8′, year:476, branch:’MAIN’, title:’Fall of Western Rome’, desc:’The Western Roman Empire collapses, reshaping the political map of Europe.’, color:’#3CFF7A’}, {id:’e9′, year:1453, branch:’MAIN’, title:’Fall of Constantinople’, desc:’The Ottoman conquest ends the Byzantine Empire and closes the medieval era.’, color:’#3CFF7A’}, {id:’e10′, year:1969, branch:’MAIN’, title:’Apollo 11 Moon Landing’, desc:’Humanity sets foot on another world for the first time.’, color:’#39e6c8′}, {id:’e11′, year:NOW_YEAR, branch:’MAIN’, title:’Present Signal’, desc:’Current temporal anchor point of this interface.’, color:’#FFB627′}, {id:’b1′, year:2032, branch:’OFF-WORLD COLONIES’, title:’Weyland-Yutani Lunar Charter’, desc:’Fictional projection: a commercial charter establishes permanent lunar extraction operations.’, color:’#c9c9ff’}, {id:’b2′, year:2079, branch:’OFF-WORLD COLONIES’, title:’Deep-Space Commercial Towing Est.’, desc:’Fictional projection: long-haul freight vessels begin routine deep-space refinery towing routes.’, color:’#c9c9ff’}, {id:’b3′, year:2122, branch:’OFF-WORLD COLONIES’, title:’Nostromo-Class Vessels Commissioned’, desc:’Fictional projection: a new class of commercial towing vessel enters service.’, color:’#c9c9ff’}, {id:’c1′, year:1859, branch:’SCIENTIFIC REVOLUTIONS’, title:’Origin of Species Published’, desc:’Darwin\’s theory of evolution by natural selection reframes the life sciences.’, color:’#FF3B3B’}, {id:’c2′, year:1905, branch:’SCIENTIFIC REVOLUTIONS’, title:’Special Relativity’, desc:’Einstein publishes the theory that redefines space, time, and energy.’, color:’#FF3B3B’}, {id:’c3′, year:1953, branch:’SCIENTIFIC REVOLUTIONS’, title:’DNA Structure Described’, desc:’Watson and Crick describe the double-helix structure of DNA.’, color:’#FF3B3B’}, ]; /* ======================= STATE ======================= */ let events = []; let scale = 0; // px per year let minScale = 0; let maxScale = 0; let camYear = 0; // year at left edge of viewport let vw = 0, vh = 0; let dragging = false, dragStartX = 0, dragStartCam = 0; let laneOrder = []; // branch names in order of first appearance (excluding MAIN) const svg = document.getElementById(‘timeline-svg’); const stage = document.getElementById(‘stage’); const viewport = document.getElementById(‘viewport’); /* ======================= PERSISTENCE ======================= */ function loadData(){ try{ const raw = localStorage.getItem(STORAGE_KEY); if(raw){ events = JSON.parse(raw); return; } }catch(e){} events = JSON.parse(JSON.stringify(seedEvents)); saveData(); } function saveData(){ try{ localStorage.setItem(STORAGE_KEY, JSON.stringify(events)); }catch(e){} } /* ======================= SCALE HELPERS ======================= */ function computeLaneOrder(){ laneOrder = []; events.forEach(ev=>{ if(ev.branch && ev.branch !== ‘MAIN’ && !laneOrder.includes(ev.branch)){ laneOrder.push(ev.branch); } }); } function laneY(branch){ if(!branch || branch === ‘MAIN’) return AXIS_Y; const idx = laneOrder.indexOf(branch); const side = idx % 2 === 0 ? 1 : -1; const tier = Math.floor(idx/2) + 1; return AXIS_Y + side * tier * LANE_GAP; } function yearToX(year){ return (year – YEAR_MIN) * scale; } function xToYear(x){ return x / scale + YEAR_MIN; } function formatYear(y){ y = Math.round(y); if(y = rawYears) return s; } return 5000; } function render(){ clearSvg(); computeLaneOrder(); const totalWidth = (YEAR_MAX – YEAR_MIN) * scale; const centerY = vh/2; svg.setAttribute(‘width’, totalWidth); svg.setAttribute(‘height’, vh); stage.style.height = vh + ‘px’; const g = svgEl(‘g’, {transform:`translate(0, ${centerY})`}); svg.appendChild(g); /* grid + ticks */ const step = niceStep(scale); const minorStep = step / 5; let startTick = Math.floor(YEAR_MIN/minorStep)*minorStep; for(let y = startTick; y <= YEAR_MAX; y += minorStep){ const x = yearToX(y); const isMajor = Math.abs(Math.round(y/step)*step – y) { const evs = events.filter(e=>e.branch===branch).sort((a,b)=>a.year-b.year); if(!evs.length) return; const y = laneY(branch); const x1 = yearToX(evs[0].year); const x2 = yearToX(evs[evs.length-1].year); const col = evs[0].color || ‘#39e6c8’; g.appendChild(svgEl(‘line’, { x1: Math.min(x1,x2), y1:y, x2:Math.max(x1,x2)+40, y2:y, class:’branch-line’, stroke:col })); const tag = svgEl(‘text’, {x:Math.min(x1,x2), y:y-10, class:’branch-tag’, fill:col}); tag.textContent = ‘// ‘ + branch; g.appendChild(tag); // connector from main axis at first branch event’s year g.appendChild(svgEl(‘line’, { x1:x1, y1:AXIS_Y, x2:x1, y2:y, class:’connector-line’, stroke:col })); }); /* nodes */ events.forEach(ev=>{ const x = yearToX(ev.year); const y = laneY(ev.branch); const col = ev.color || ‘#3CFF7A’; if(ev.branch && ev.branch !== ‘MAIN’){ g.appendChild(svgEl(‘line’, {x1:x, y1:AXIS_Y, x2:x, y2:y, class:’connector-line’, stroke:col})); } const node = svgEl(‘circle’, { cx:x, cy:y, r:NODE_R, fill:’#000′, stroke:col, ‘stroke-width’:2, class:’node-circle’, ‘data-id’:ev.id }); node.style.filter = `drop-shadow(0 0 4px ${col})`; node.addEventListener(‘click’, (e)=>{ e.stopPropagation(); openDetail(ev.id); }); node.addEventListener(‘mouseenter’, ()=> node.setAttribute(‘r’, NODE_R+3)); node.addEventListener(‘mouseleave’, ()=> node.setAttribute(‘r’, NODE_R)); g.appendChild(node); const lbl = svgEl(‘text’, { x:x+10, y:y – (ev.branch===’MAIN’||!ev.branch ? -22 : (laneY(ev.branch)>0? 18:-14)), class:’node-label’, fill:col }); lbl.textContent = ev.title; g.appendChild(lbl); }); updateReadouts(); updateScrubber(); } /* ======================= CAMERA / ZOOM / PAN ======================= */ function applyCamera(){ stage.style.transform = `translateX(${ -camYear * scale }px)`; } function clampCamera(){ const viewYears = vw / scale; const maxCam = YEAR_MAX – viewYears; const minCam = YEAR_MIN; if(camYear maxCam) camYear = Math.max(minCam, maxCam); } function setScale(newScale, anchorClientX){ newScale = Math.max(minScale, Math.min(maxScale, newScale)); const anchorYear = anchorClientX!==undefined ? xToYear((anchorClientX) / 1 + camYear*scale) : camYear + (vw/2)/scale; // recompute anchor in world using OLD scale const worldXBefore = anchorClientX!==undefined ? (anchorClientX + camYear*scale) : (vw/2 + camYear*scale); const yearAtAnchor = worldXBefore/scale; scale = newScale; const worldXAfterTarget = yearAtAnchor*scale; camYear = (worldXAfterTarget – (anchorClientX!==undefined?anchorClientX:vw/2)) / scale; clampCamera(); applyCamera(); render(); updateZoomReadout(); } function updateZoomReadout(){ const ratio = scale/minScale; const el = document.getElementById(‘zoom-readout’); if(ratio < 1.5) el.textContent = 'OVERVIEW'; else if(ratio < 8) el.textContent = 'REGIONAL x' + ratio.toFixed(1); else if(ratio e.id===id); if(!ev) return; selectedId = id; document.getElementById(‘detail-branch’).textContent = (ev.branch && ev.branch!==’MAIN’) ? ev.branch : ‘MAIN SEQUENCE’; document.getElementById(‘detail-title’).textContent = ev.title; document.getElementById(‘detail-year’).textContent = formatYear(ev.year); document.getElementById(‘detail-desc’).textContent = ev.desc || ‘[NO LOG DATA]’; document.getElementById(‘detail-panel’).classList.add(‘open’); } function closeDetail(){ document.getElementById(‘detail-panel’).classList.remove(‘open’); selectedId = null; } document.getElementById(‘detail-close’).addEventListener(‘click’, closeDetail); document.getElementById(‘detail-delete’).addEventListener(‘click’, ()=>{ if(!selectedId) return; events = events.filter(e=>e.id!==selectedId); saveData(); closeDetail(); render(); }); /* ======================= MODAL ======================= */ const modalOverlay = document.getElementById(‘modal-overlay’); let chosenColor = COLORS[0]; function buildSwatches(){ const wrap = document.getElementById(‘color-swatches’); wrap.innerHTML = ”; COLORS.forEach((c,i)=>{ const sw = document.createElement(‘div’); sw.className = ‘swatch’ + (i===0?’ selected’:”); sw.style.background = c; sw.addEventListener(‘click’, ()=>{ chosenColor = c; […wrap.children].forEach(ch=>ch.classList.remove(‘selected’)); sw.classList.add(‘selected’); }); wrap.appendChild(sw); }); } buildSwatches(); document.getElementById(‘btn-add’).addEventListener(‘click’, ()=>{ document.getElementById(‘f-title’).value=”; document.getElementById(‘f-year’).value=”; document.getElementById(‘f-branch’).value=”; document.getElementById(‘f-desc’).value=”; chosenColor = COLORS[0]; buildSwatches(); modalOverlay.classList.add(‘open’); }); document.getElementById(‘modal-cancel’).addEventListener(‘click’, ()=>modalOverlay.classList.remove(‘open’)); modalOverlay.addEventListener(‘click’, (e)=>{ if(e.target===modalOverlay) modalOverlay.classList.remove(‘open’); }); document.getElementById(‘modal-save’).addEventListener(‘click’, ()=>{ const title = document.getElementById(‘f-title’).value.trim(); const yearRaw = document.getElementById(‘f-year’).value.trim(); const branch = document.getElementById(‘f-branch’).value.trim().toUpperCase() || ‘MAIN’; const desc = document.getElementById(‘f-desc’).value.trim(); if(!title || yearRaw===”){ flashInvalid(); return; } const year = Math.max(YEAR_MIN, Math.min(YEAR_MAX, parseInt(yearRaw,10))); const id = ‘u’ + Date.now(); events.push({id, year, branch, title, desc, color:chosenColor}); saveData(); modalOverlay.classList.remove(‘open’); render(); focusOnYear(year); }); function flashInvalid(){ const modal = document.getElementById(‘modal’); modal.classList.add(‘glitch-active’); setTimeout(()=>modal.classList.remove(‘glitch-active’), 300); } /* ======================= RESET ======================= */ document.getElementById(‘btn-reset’).addEventListener(‘click’, ()=>{ if(!confirm(‘RESET ALL TEMPORAL RECORDS TO FACTORY SEED DATA?’)) return; events = JSON.parse(JSON.stringify(seedEvents)); saveData(); render(); }); /* ======================= NAV CONTROLS ======================= */ function focusOnYear(year){ camYear = year – (vw/2)/scale; clampCamera(); applyCamera(); updateReadouts(); updateScrubber(); } document.getElementById(‘btn-jump-origin’).addEventListener(‘click’, ()=>focusOnYear(YEAR_MIN+ (vw/2)/scale)); document.getElementById(‘btn-jump-zero’).addEventListener(‘click’, ()=>focusOnYear(0)); document.getElementById(‘btn-jump-now’).addEventListener(‘click’, ()=>focusOnYear(NOW_YEAR)); document.getElementById(‘btn-jump-end’).addEventListener(‘click’, ()=>focusOnYear(YEAR_MAX – (vw/2)/scale)); document.getElementById(‘btn-zoom-in’).addEventListener(‘click’, ()=>setScale(scale*1.6, vw/2)); document.getElementById(‘btn-zoom-out’).addEventListener(‘click’, ()=>setScale(scale/1.6, vw/2)); /* wheel zoom */ viewport.addEventListener(‘wheel’, (e)=>{ e.preventDefault(); const factor = e.deltaY { if(e.target.closest(‘#detail-panel’)) return; dragging = true; dragStartX = e.clientX; dragStartCam = camYear; viewport.style.cursor = ‘grabbing’; }); window.addEventListener(‘mousemove’, (e)=>{ if(!dragging) return; const dx = e.clientX – dragStartX; camYear = dragStartCam – dx/scale; clampCamera(); applyCamera(); updateReadouts(); updateScrubber(); }); window.addEventListener(‘mouseup’, ()=>{ dragging = false; viewport.style.cursor=’crosshair’; }); /* touch support (basic pan) */ let touchStartX=0, touchStartCam=0; viewport.addEventListener(‘touchstart’, (e)=>{ if(e.touches.length!==1) return; touchStartX = e.touches[0].clientX; touchStartCam = camYear; }, {passive:true}); viewport.addEventListener(‘touchmove’, (e)=>{ if(e.touches.length!==1) return; const dx = e.touches[0].clientX – touchStartX; camYear = touchStartCam – dx/scale; clampCamera(); applyCamera(); updateReadouts(); updateScrubber(); }, {passive:true}); /* scrubber drag */ const scrubberWrap = document.getElementById(‘scrubber-wrap’); let scrubDragging=false; scrubberWrap.addEventListener(‘mousedown’, (e)=>{ scrubDragging = true; moveScrubTo(e); }); window.addEventListener(‘mousemove’, (e)=>{ if(scrubDragging) moveScrubTo(e); }); window.addEventListener(‘mouseup’, ()=> scrubDragging=false); function moveScrubTo(e){ const rect = scrubberWrap.getBoundingClientRect(); const pct = Math.max(0, Math.min(1, (e.clientX-rect.left)/rect.width)); const total = YEAR_MAX-YEAR_MIN; camYear = YEAR_MIN + pct*total – (vw/2)/scale; clampCamera(); applyCamera(); updateReadouts(); updateScrubber(); render(); } /* ======================= RESIZE ======================= */ function handleResize(){ vw = viewport.clientWidth; vh = viewport.clientHeight; const totalYears = YEAR_MAX – YEAR_MIN; minScale = vw / totalYears; maxScale = minScale * 300; if(!scale) scale = minScale; clampCamera(); applyCamera(); render(); } window.addEventListener(‘resize’, handleResize); /* ======================= CLOCK ======================= */ function tickClock(){ const d = new Date(); document.getElementById(‘hud-clock’).textContent = ‘SYS TIME ‘ + d.toTimeString().slice(0,8) + ‘ // RECORDS: ‘ + events.length; } setInterval(tickClock, 1000); /* ======================= BOOT SEQUENCE ======================= */ function boot(){ const bootEl = document.getElementById(‘boot’); const textEl = document.getElementById(‘boot-text’); const lines = [ ‘CHRONOS TEMPORAL CARTOGRAPHY INTERFACE’, ‘REV 3.3.0 — COPYRIGHT UNKNOWN’, ”, ‘INITIALIZING CORE………..OK’, ‘LOADING TEMPORAL INDEX…….OK’, ‘CALIBRATING AXIS -6000 > 3033…….OK’, ‘MOUNTING BRANCH REGISTRY…..OK’, ”, ‘INTERFACE READY.’ ]; let full = lines.join(‘\n’); let i = 0; textEl.style.opacity = 1; const iv = setInterval(()=>{ textEl.textContent = full.slice(0, i); i += 3; if(i > full.length){ clearInterval(iv); setTimeout(()=>{ bootEl.classList.add(‘glitch-active’); setTimeout(()=>{ bootEl.style.display=’none’; init(); }, 180); }, 400); } }, 12); } /* ======================= INIT ======================= */ function init(){ loadData(); handleResize(); focusOnYear(YEAR_MIN + (vw/2)/scale); } document.addEventListener(‘DOMContentLoaded’, boot); })();