// Minimal CoBiE analog clock logic wrapped in its own scope to // avoid clashes with variables from other scripts on the page. (function () { const { COBIE_EPOCH, COBIE_UNITS, floorDiv, getTAIOffsetAt, toCobiets } = window.Cobie; function getMarkerOffset(width) { const points = [ { width: 1024, value: 2 }, { width: 450, value: 1.3 }, { width: 200, value: 0.8 } ]; // Sort points by width descending for easier handling points.sort((a, b) => b.width - a.width); for (let i = 0; i < points.length - 1; i++) { const p1 = points[i]; const p2 = points[i + 1]; if (width <= p1.width && width >= p2.width) { // Linear interpolation const t = (width - p2.width) / (p1.width - p2.width); return p2.value + t * (p1.value - p2.value); } } // Extrapolation for width > max known if (width > points[0].width) { const p1 = points[0]; const p2 = points[1]; const slope = (p1.value - p2.value) / (p1.width - p2.width); return p1.value + slope * (width - p1.width); } // Extrapolation for width < min known const p1 = points[points.length - 2]; const p2 = points[points.length - 1]; const slope = (p2.value - p1.value) / (p2.width - p1.width); return p2.value + slope * (width - p2.width); } function placeMarkers() { const clock = document.getElementById('clock'); let markers = clock.querySelectorAll('.marker'); let ticks = clock.querySelectorAll('.tick'); // Create markers if they don't exist yet if (markers.length === 0) { for (let i = 0; i < 16; i++) { const m = document.createElement('div'); m.className = 'marker'; m.textContent = i.toString(16).toUpperCase(); clock.appendChild(m); } markers = clock.querySelectorAll('.marker'); } // Create tick marks once if (ticks.length === 0) { for (let i = 0; i < 256; i++) { const t = document.createElement('div'); t.classList.add('tick'); if (i % 16 === 0) t.classList.add('big'); else if (i % 8 === 0) t.classList.add('mid'); // insert before markers so digits sit on top clock.insertBefore(t, clock.firstChild); } ticks = clock.querySelectorAll('.tick'); } // Unified radius based on the actual clock size const baseRadius = clock.offsetWidth / 2; // Tick lengths relative to the clock radius const lenBig = baseRadius * 0.12; const lenMid = baseRadius * 0.08; const lenSmall = baseRadius * 0.05; const outerR = baseRadius - 2; // just inside the border // Distance from center so marker's outer edge sits just inside the big tick const markerSize = markers[0] ? markers[0].offsetWidth : 0; const inset = baseRadius * 0.001; // 0.1% of the radius const markerRadius = outerR - lenBig - inset + markerSize / 2; markers.forEach((m, i) => { const angle = (i / 16) * 2 * Math.PI; m.style.left = '50%'; m.style.top = '50%'; m.style.transform = `translate(-50%, -50%) rotate(${angle}rad) translate(0, -${markerRadius}px) rotate(${-angle}rad)`; }); ticks.forEach((t, i) => { let len = lenSmall; if (t.classList.contains('big')) len = lenBig; else if (t.classList.contains('mid')) len = lenMid; const innerR = outerR - len; const angle = ((i + 1) / 256) * 2 * Math.PI; t.style.height = `${len}px`; t.style.left = '50%'; t.style.top = '50%'; t.style.transform = `translate(-50%, 0) rotate(${angle}rad) translate(0, -${innerR}px)`; if (clock.offsetWidth < 200 && !t.classList.contains('big') && !t.classList.contains('mid')) { t.style.display = 'none'; } else { t.style.display = ''; } }); } function renderClock(cob) { // Use fractional progress within each unit so angles stay small const xf = (cob % COBIE_UNITS.quantic) / COBIE_UNITS.quantic; const qf = (cob % COBIE_UNITS.chronon) / COBIE_UNITS.chronon; const cf = (cob % COBIE_UNITS.eonstrip) / COBIE_UNITS.eonstrip; const ef = (cob % COBIE_UNITS.megasequence) / COBIE_UNITS.megasequence; const mf = (cob % COBIE_UNITS.cosmocycle) / COBIE_UNITS.cosmocycle; Animate.rotateHand('handXeno', xf * 360); Animate.rotateHand('handQuantic', qf * 360); Animate.rotateHand('handChronon', cf * 360); Animate.rotateHand('handEonstrip', ef * 360); Animate.rotateHand('handMegasequence', mf * 360); } function updateClock() { renderClock(toCobiets(new Date())); } let intervalId = null; function startClock() { clearInterval(intervalId); updateClock(); intervalId = setInterval(updateClock, 1000); } function showTime(cob) { clearInterval(intervalId); renderClock(cob); } function initClock() { placeMarkers(); startClock(); const clk = document.getElementById('clock'); if (clk) { clk.addEventListener('click', () => { document.body.classList.toggle('fullscreen-clock'); // Re-position markers after toggling fullscreen requestAnimationFrame(placeMarkers); }); } window.addEventListener('resize', placeMarkers); window.CobieClock = { start: startClock, showTime }; } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initClock); } else { initClock(); } })();