diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7911b3b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Build artifacts +CoBiEClock.wdgt +build-widget/ diff --git a/README.md b/README.md index 73cf8fe..c3a733f 100644 --- a/README.md +++ b/README.md @@ -66,10 +66,12 @@ An interactive web app that visualizes the **CosmoChron Binary Epoch (CoBiE)** t ## macOS Dashboard Widget The repository includes a minimal Dashboard widget under -`macos-widget/`. To install it on macOS: +`macos-widget/`. A helper script `build-widget.sh` is provided to +package it automatically: -1. Compress the `macos-widget` folder into an archive. -2. Rename the archive to `CoBiEClock.wdgt` and double-click it. +1. Run `./build-widget.sh` from the repository root. +2. The script creates `CoBiEClock.wdgt` which you can double-click to + install. The widget runs offline and shows the analog CoBiE clock on your desktop. diff --git a/build-widget.sh b/build-widget.sh new file mode 100755 index 0000000..78c244c --- /dev/null +++ b/build-widget.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# Build the macOS Dashboard widget without duplicating code +set -euo pipefail + +WIDGET_NAME="CoBiEClock" +SRC_DIR="macos-widget" +BUILD_DIR="build-widget" + +# Clean build directory +rm -rf "$BUILD_DIR" +mkdir "$BUILD_DIR" + +# Copy unique widget files +cp "$SRC_DIR/Info.plist" "$SRC_DIR/index.html" "$BUILD_DIR/" + +# Copy shared files from repository root +cp clock.js cobie.js style.css logo.svg "$BUILD_DIR/" + +# Create archive and rename +zip -r "${WIDGET_NAME}.zip" "$BUILD_DIR" > /dev/null +mv "${WIDGET_NAME}.zip" "${WIDGET_NAME}.wdgt" + +echo "Widget created: ${WIDGET_NAME}.wdgt" diff --git a/macos-widget/clock.js b/macos-widget/clock.js deleted file mode 100644 index 0ad372e..0000000 --- a/macos-widget/clock.js +++ /dev/null @@ -1,204 +0,0 @@ -// 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 for the marker digits so they sit just inside big ticks - const markerSize = markers[0] ? markers[0].offsetWidth : 0; - const markerRadius = outerR - lenBig - markerSize * getMarkerOffset(clock.offsetWidth); - - 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 = ''; - } - }); - } - - const lastAngles = { - handXeno: 0, - handQuantic: 0, - handChronon: 0, - handEonstrip: 0, - handMegasequence: 0 - }; - - function rotateHand(id, angle) { - const el = document.getElementById(id); - if (!el) return; - const prev = lastAngles[id]; - - if (angle < prev) { - // When wrapping around (e.g. 15 → 0), animate to one full turn - // and then snap back to the new angle to avoid a jump. - const target = angle + 360; - const handle = () => { - el.removeEventListener('transitionend', handle); - // Snap back without animation - el.style.transition = 'none'; - el.style.transform = `translateX(-50%) translateZ(0) rotate(${angle}deg)`; - void el.offsetWidth; - el.style.transition = ''; - }; - el.addEventListener('transitionend', handle, { once: true }); - el.style.transform = `translateX(-50%) translateZ(0) rotate(${target}deg)`; - } else { - el.style.transform = `translateX(-50%) translateZ(0) rotate(${angle}deg)`; - } - - lastAngles[id] = angle; - } - - 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; - rotateHand('handXeno', xf * 360); - rotateHand('handQuantic', qf * 360); - rotateHand('handChronon', cf * 360); - rotateHand('handEonstrip', ef * 360); - 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(); - } -})(); diff --git a/macos-widget/cobie.js b/macos-widget/cobie.js deleted file mode 100644 index 37fceef..0000000 --- a/macos-widget/cobie.js +++ /dev/null @@ -1,185 +0,0 @@ -const COBIE_EPOCH = 0; - -const COBIE_UNITS = { - second: 1, - xenocycle: 0x10, - quantic: 0x100, - chronon: 0x1000, - eonstrip: 0x10000, - megasequence: 0x100000, - cosmocycle: 0x1000000, - galactic_year: 0x10000000, - universal_eon: 0x100000000, - celestial_era: 0x1000000000, - epoch_of_cosmos: 0x10000000000, - cosmic_aeon: 0x100000000000, - metaepoch: 0x1000000000000, - eternum: 0x10000000000000, - infinitum: 0x100000000000000, - astralmillennia: 0x1000000000000000 -}; - -function floorDiv(a, b) { - return Math.trunc(a / b); -} - -function parseCobiets(str) { - const m = /^([+-]?)([0-9A-Fa-f]+)\.([0-9A-Fa-f]{1,})$/.exec(str.trim()); - if (!m) return null; - const sign = m[1] === '-' ? -1 : 1; - - const allDateKeys = [ - 'astralmillennia','infinitum','eternum','metaepoch','cosmic_aeon', - 'epoch_of_cosmos','celestial_era','universal_eon','galactic_year', - 'cosmocycle','megasequence','eonstrip' - ]; - - let rawDateHex = m[2]; - if (rawDateHex.length < allDateKeys.length) { - rawDateHex = rawDateHex.padStart(allDateKeys.length, '0'); - } - - let dateKeys = [...allDateKeys]; - if (rawDateHex.length > allDateKeys.length) { - const extraCount = rawDateHex.length - allDateKeys.length; - for (let i = 0; i < extraCount; i++) { - dateKeys.unshift(null); - } - } - - const timeHexRaw = m[3]; - const timeHex = timeHexRaw.padStart(4, '0'); - const timeKeys = ['chronon', 'quantic', 'xenocycle', 'second']; - - let total = 0; - for (let i = 0; i < rawDateHex.length; i++) { - const digit = parseInt(rawDateHex[i], 16); - const key = dateKeys[i]; - if (key === null) { - const power = rawDateHex.length - 1 - i; - total += digit * Math.pow(16, power) * COBIE_UNITS['eonstrip']; - } else { - total += digit * COBIE_UNITS[key]; - } - } - - timeHex.split('').forEach((h, i) => { - total += parseInt(h, 16) * COBIE_UNITS[timeKeys[i]]; - }); - - return sign * total; -} - -function getTAIOffsetAt(date) { - const taiEpoch = new Date('1958-01-01T00:00:00Z'); - if (date < taiEpoch) { return 0; } - const leapSeconds = [ - { date: '1972-01-01T00:00:00Z', offset: 10 }, - { date: '1972-07-01T00:00:00Z', offset: 11 }, - { date: '1973-01-01T00:00:00Z', offset: 12 }, - { date: '1974-01-01T00:00:00Z', offset: 13 }, - { date: '1975-01-01T00:00:00Z', offset: 14 }, - { date: '1976-01-01T00:00:00Z', offset: 15 }, - { date: '1977-01-01T00:00:00Z', offset: 16 }, - { date: '1978-01-01T00:00:00Z', offset: 17 }, - { date: '1979-01-01T00:00:00Z', offset: 18 }, - { date: '1980-01-01T00:00:00Z', offset: 19 }, - { date: '1981-07-01T00:00:00Z', offset: 20 }, - { date: '1982-07-01T00:00:00Z', offset: 21 }, - { date: '1983-07-01T00:00:00Z', offset: 22 }, - { date: '1985-07-01T00:00:00Z', offset: 23 }, - { date: '1988-01-01T00:00:00Z', offset: 24 }, - { date: '1990-01-01T00:00:00Z', offset: 25 }, - { date: '1991-01-01T00:00:00Z', offset: 26 }, - { date: '1992-07-01T00:00:00Z', offset: 27 }, - { date: '1993-07-01T00:00:00Z', offset: 28 }, - { date: '1994-07-01T00:00:00Z', offset: 29 }, - { date: '1996-01-01T00:00:00Z', offset: 30 }, - { date: '1997-07-01T00:00:00Z', offset: 31 }, - { date: '1999-01-01T00:00:00Z', offset: 32 }, - { date: '2006-01-01T00:00:00Z', offset: 33 }, - { date: '2009-01-01T00:00:00Z', offset: 34 }, - { date: '2012-07-01T00:00:00Z', offset: 35 }, - { date: '2015-07-01T00:00:00Z', offset: 36 }, - { date: '2017-01-01T00:00:00Z', offset: 37 } - ]; - - for (let i = 0; i < leapSeconds.length; i++) { - const leapDate = new Date(leapSeconds[i].date); - if (date < leapDate) { - return i === 0 ? 10 : leapSeconds[i - 1].offset; - } - } - return 37; -} - -function toCobiets(date) { - const utcSec = floorDiv(date.getTime(), 1000); - const taiSec = utcSec + getTAIOffsetAt(date); - return taiSec - COBIE_EPOCH; -} - -function fromCobiets(cobiets) { - const taiSeconds = cobiets + COBIE_EPOCH; - const taiMs = taiSeconds * 1000; - let utcMs = taiMs; - for (let i = 0; i < 3; i++) { - const off = getTAIOffsetAt(new Date(utcMs)); - utcMs = taiMs - off * 1000; - } - return new Date(utcMs); -} - -const UNIT_KEYS = [ - 'astralmillennia','infinitum','eternum','metaepoch','cosmic_aeon','epoch_of_cosmos','celestial_era','universal_eon','galactic_year','cosmocycle','megasequence','eonstrip','chronon','quantic','xenocycle','second' -]; - -function breakdownNonNeg(cob) { - let rem = cob, bd = {}; - for (let key of UNIT_KEYS) { - bd[key] = floorDiv(rem, COBIE_UNITS[key]); - rem %= COBIE_UNITS[key]; - } - return bd; -} - -function formatCobieTimestamp(cobiets) { - const sign = cobiets < 0 ? '-' : '+'; - const absCob = Math.abs(cobiets); - const bd = breakdownNonNeg(absCob); - - const dateUnits = [ - 'astralmillennia','infinitum','eternum','metaepoch','cosmic_aeon','epoch_of_cosmos','celestial_era','universal_eon','galactic_year','cosmocycle','megasequence','eonstrip' - ]; - - let rawDateHex = dateUnits.map(key => bd[key].toString(16)).join(''); - rawDateHex = rawDateHex.replace(/^0+/, ''); - if (rawDateHex === '') rawDateHex = '0'; - - const timeHex = [bd.chronon, bd.quantic, bd.xenocycle, bd.second] - .map(n => n.toString(16)).join(''); - - const paddedTimeHex = timeHex.padStart(4, '0'); - return sign + rawDateHex + '.' + paddedTimeHex; -} - -const Cobie = { - COBIE_EPOCH, - COBIE_UNITS, - floorDiv, - parseCobiets, - getTAIOffsetAt, - toCobiets, - fromCobiets, - formatCobieTimestamp, - breakdownNonNeg -}; - -if (typeof module !== 'undefined' && module.exports) { - module.exports = Cobie; -} - -// Expose globally when loaded in a browser environment -if (typeof window !== 'undefined') { - window.Cobie = Cobie; -} diff --git a/macos-widget/logo.svg b/macos-widget/logo.svg deleted file mode 100644 index ed5d652..0000000 --- a/macos-widget/logo.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/macos-widget/style.css b/macos-widget/style.css deleted file mode 100644 index 33fd056..0000000 --- a/macos-widget/style.css +++ /dev/null @@ -1,561 +0,0 @@ - * { - margin: 0; - padding: 0; - box-sizing: border-box; - } - - body { - font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; - background: linear-gradient(135deg, #0a0e27 0%, #1a1f3a 100%); - color: #e0e0e0; - min-height: 100vh; - overflow-x: hidden; - } - - .container { - max-width: 1200px; - margin: 0 auto; - padding: 20px; - } - - .header { - text-align: center; - margin-bottom: 30px; - padding: 20px; - background: rgba(255, 255, 255, 0.05); - border-radius: 15px; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - } - - .tooltip { - position: absolute; - top: 100%; - left: 50%; - transform: translateX(-50%); - background: rgba(0, 0, 0, 0.8); - color: #fff; - padding: 8px 12px; - border-radius: 4px; - white-space: nowrap; - pointer-events: none; - opacity: 0; - transition: opacity 0.2s; - z-index: 9999; - } - - h1 { - font-size: 2.5em; - background: linear-gradient(45deg, #00ffff, #ff00ff); - background-clip: text; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - margin-bottom: 10px; - animation: glow 3s ease-in-out infinite; - } - - @keyframes glow { - 0%, 100% { opacity: 0.8; } - 50% { opacity: 1; } - } - - .current-time { - background: rgba(0, 255, 255, 0.1); - border: 2px solid rgba(0, 255, 255, 0.3); - border-radius: 10px; - padding: 20px; - margin-bottom: 20px; - text-align: center; - position: relative; - overflow: hidden; - flex: 1; - display: flex; - flex-direction: column; - justify-content: center; - height: var(--clock-size); - } - - .current-time.manual::before { - animation-play-state: paused; - } - - .current-time::before { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: linear-gradient(45deg, transparent, rgba(0, 255, 255, 0.1), transparent); - transform: rotate(45deg); - animation: sweep 3s linear infinite; - } - - @keyframes sweep { - 0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); } - 100% { transform: translateX(100%) translateY(100%) rotate(45deg); } - } - - .cobie-time { - font-size: 2.5em; - font-family: 'Courier New', monospace; - letter-spacing: 2px; - margin: 10px 0; - text-shadow: 0 0 20px rgba(0, 255, 255, 0.5); - } - - .regular-time { - font-size: 1.2em; - color: #aaa; - } - - .timezone-selector { - margin: 20px 0; - text-align: center; - } - - select { - padding: 10px 20px; - font-size: 16px; - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.3); - border-radius: 5px; - color: #fff; - cursor: pointer; - transition: all 0.3s ease; - } - - select:hover { - background: rgba(255, 255, 255, 0.2); - transform: translateY(-2px); - } - - .calendar-controls { - display: flex; - justify-content: center; - align-items: center; - gap: 20px; - margin: 20px 0; - } - - button { - padding: 10px 20px; - font-size: 16px; - background: linear-gradient(45deg, #00ffff, #0080ff); - border: none; - border-radius: 5px; - color: #fff; - cursor: pointer; - transition: all 0.3s ease; - position: relative; - overflow: hidden; - } - - button::before { - content: ''; - position: absolute; - top: 50%; - left: 50%; - width: 0; - height: 0; - background: rgba(255, 255, 255, 0.3); - border-radius: 50%; - transform: translate(-50%, -50%); - transition: width 0.6s, height 0.6s; - } - - button:hover::before { - width: 300px; - height: 300px; - } - - button:hover { - transform: translateY(-2px); - box-shadow: 0 5px 15px rgba(0, 255, 255, 0.3); - } - - .calendar-view { - background: rgba(255, 255, 255, 0.05); - border-radius: 15px; - padding: 20px; - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.1); - overflow: hidden; - } - - .calendar-header { - text-align: center; - font-size: 1.5em; - margin-bottom: 20px; - color: #00ffff; - } - - .eonstrip-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: 15px; - margin-bottom: 20px; - transform: translateX(0); - transition: transform 0.3s ease; - will-change: transform; - } - - .eonstrip-card { - background: rgba(255, 255, 255, 0.08); - border: 1px solid rgba(255, 255, 255, 0.2); - border-radius: 10px; - padding: 15px; - text-align: center; - transition: all 0.3s ease; - cursor: pointer; - position: relative; - overflow: visible; - white-space: normal; - } - - .eonstrip-card::before { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: radial-gradient(circle, rgba(255, 255, 255, 0.1) 0%, transparent 70%); - opacity: 0; - transition: opacity 0.3s ease; - } - - .eonstrip-card:hover::before { - opacity: 1; - } - - .eonstrip-card:hover { - transform: translateY(-5px); - box-shadow: 0 10px 20px rgba(0, 255, 255, 0.2); - border-color: rgba(0, 255, 255, 0.5); - z-index: 1000; - } - - .eonstrip-card:hover .tooltip { - opacity: 1; - } - - .eonstrip-card.current { - background: rgba(0, 255, 255, 0.2); - border-color: rgba(0, 255, 255, 0.5); - } - - .eonstrip-name { - font-size: 1em; - font-weight: bold; - color: #00ffff; - margin-bottom: 5px; - } - - .eonstrip-hex { - font-size: 0.85em; /* was default monospace size */ - font-family: 'Courier New', monospace; - color: #ffaaff; - margin-bottom: 10px; - } - - .eonstrip-dates { - font-size: 0.7em; - color: #aaa; - line-height: 1.4; - } - - .event-tag { - display: inline-block; - margin-top: 4px; - padding: 2px 6px; - font-size: 0.75em; - font-weight: 600; - color: #fff; - background: linear-gradient(135deg, rgba(0,255,255,0.25), rgba(255,0,255,0.25)); - border: 1px solid rgba(255,255,255,0.2); - border-radius: 4px; - text-shadow: 0 0 6px rgba(0,255,255,0.7); - } - - .time-details { - background: rgba(255, 255, 255, 0.05); - border-radius: 10px; - padding: 20px; - margin-top: 20px; - } - - .time-unit { - display: flex; - justify-content: space-between; - padding: 10px 0; - border-bottom: 1px solid rgba(255, 255, 255, 0.1); - } - - .time-unit:last-child { - border-bottom: none; - } - - .unit-name { - color: #00ffff; - font-weight: bold; - } - - .unit-value { - font-family: 'Courier New', monospace; - color: #ffaaff; - } - - .toggle-btn { - background: linear-gradient(45deg, #00ffff, #0080ff); - border: none; - border-radius: 8px; - color: #fff; - font-size: 0.9em; - padding: 8px 16px; - cursor: pointer; - position: relative; - overflow: hidden; - text-transform: uppercase; - letter-spacing: 1px; - box-shadow: 0 0 10px rgba(0,255,255,0.4); - transition: all 0.3s ease; - } - - .toggle-btn .arrow-icon { - display: inline-block; - margin-right: 6px; - transition: transform 0.3s ease; - } - - .toggle-btn:hover { - transform: translateY(-2px); - box-shadow: 0 0 20px rgba(0,255,255,0.6); - } - - .extended-section { - display: none; /* hidden by default */ - margin-top: 10px; - animation: fadeIn 0.4s ease; - } - - /* Simple fade‐in for when extended units show */ - @keyframes fadeIn { - from { opacity: 0; } - to { opacity: 1; } - } - - @media only screen - and (max-width: 812px) /* iPhone portrait widths go up to ~812px */ - and (orientation: portrait) { - - /* scale down your main text by 30% */ - html { - font-size: 70%; - } - - /* if you prefer targeting only the big “cobie-time” element: */ - .cobie-time { - font-size: 1.75em; /* was 2.5em, which is 70% of 2.5em */ - } - } - - -.eonstrip-grid { - grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); - transform: translateX(0); - transition: transform 0.3s ease; - will-change: transform; -} - -/* Layout combining current time and analog clock */ -.time-display { - --clock-size: 40vmin; - display: flex; - flex-wrap: wrap; - justify-content: flex-start; - align-items: stretch; - gap: 20px; -} - - -.analog-clock-container { - flex: 0 0 auto; - width: var(--clock-size); - margin-left: auto; - display: flex; - justify-content: center; - align-items: center; -} - -#clock { - position: relative; - width: var(--clock-size); - height: var(--clock-size); - border: 2px solid rgba(255, 255, 255, 0.2); - border-radius: 50%; - background: radial-gradient(circle at center, #0a0e27 0%, #1a1f3a 100%); - box-shadow: 0 0 25px rgba(0, 255, 255, 0.2), inset 0 0 40px rgba(255, 0, 255, 0.2); -} - -.clock-center { - position: absolute; - top: 50%; - left: 50%; - width: calc(var(--clock-size) * 0.13); - height: calc(var(--clock-size) * 0.13); - transform: translate(-50%, -50%) translateZ(0); - background: url('logo.svg') center/contain no-repeat; - background-color: transparent; - border-radius: 50%; - /* box-shadow: 0 0 8px rgba(0, 255, 255, 0.8); */ - z-index: 2; - pointer-events: none; -} - -@media screen and (min-width: 1200px) { - .clock-center { - width: calc(var(--clock-size) * 0.085); - height: calc(var(--clock-size) * 0.085); - } -} - -.clock-label { - position: absolute; - bottom: 30%; - left: 50%; - transform: translateX(-50%); - font-family: 'Great Vibes', cursive; - font-size: calc(var(--clock-size) * 0.06); - color: #ffaaff; - text-shadow: 0 0 6px rgba(255, 0, 255, 0.6); - pointer-events: none; - z-index: 0; -} - -body.fullscreen-clock .clock-label { - font-size: calc(var(--clock-size) * 0.08); -} - -.marker { - position: absolute; - width: 2em; - height: 2em; - text-align: center; - line-height: 2em; - /* Use a futuristic font for the clock markers */ - font-family: 'Orbitron', 'Trebuchet MS', 'Lucida Sans', Arial, sans-serif; - font-size: 1.2em; - font-weight: 600; - color: #00ffff; - background: none; - border: none; - border-radius: 0; - text-shadow: 0 0 6px rgba(0, 255, 255, 0.9), 0 0 12px rgba(0, 255, 255, 0.7); - box-shadow: none; - transform-origin: center; - font-variant-numeric: tabular-nums; - user-select: none; - pointer-events: none; - will-change: transform; - z-index: 1; -} - -.tick { - position: absolute; - width: 2px; - background: #00ffff; - transform-origin: center top; - z-index: 0; -} - -.tick.mid { - background: #66ffff; -} - -.tick.big { - background: #ffffff; -} - -.hand { - position: absolute; - bottom: 50%; - left: 50%; - transform-origin: bottom center; - transform: translateX(-50%); - transition: transform 0.5s ease-in-out; - border-radius: 2px; - z-index: 1; -} - - -.hand.xeno { - width: 2px; - height: 44%; - background: linear-gradient(to top, #66ccff, #0044ff); - box-shadow: 0 0 8px #66ccff; -} - -.hand.quantic { - width: 3px; - height: 40%; - background: linear-gradient(to top, #ff66ff, #9900ff); - box-shadow: 0 0 8px #ff66ff; -} - -.hand.chronon { - width: 4px; - height: 34%; - background: linear-gradient(to top, #ff4444, #880000); - box-shadow: 0 0 8px #ff4444; -} - -.hand.eonstrip { - width: 5px; - height: 30%; - background: linear-gradient(to top, #33ff99, #006633); - box-shadow: 0 0 8px #33ff99; -} - - -.hand.megasequence { - width: 6px; - height: 26%; - background: linear-gradient(to top, #ffbb33, #aa5500); - box-shadow: 0 0 8px #ffbb33; -} - -@media only screen and (max-height: 430px) and (orientation: landscape) { - .time-display { - --clock-size: 70vmin; - } -} - -body.fullscreen-clock .header, -body.fullscreen-clock .current-time, -body.fullscreen-clock .timezone-selector, -body.fullscreen-clock .calendar-controls, -body.fullscreen-clock .calendar-view, -body.fullscreen-clock .time-details, -body.fullscreen-clock .explanations { - display: none; -} - -body.fullscreen-clock .time-display { - position: fixed; - inset: 0; - justify-content: center; - align-items: center; - z-index: 1000; -} - -body.fullscreen-clock .analog-clock-container { - --clock-size: 80vmin; - width: var(--clock-size); - margin-left: 0; -} - -body.fullscreen-clock #clock { - width: var(--clock-size); - height: var(--clock-size); -}