Spaces:
Running
Running
| <html lang="vi"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>Sài Gòn Sync - The World's Most Accurate Clock</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@700;800&family=Inter:wght@400;600;800&display=swap'); | |
| body { margin: 0; padding: 0; width: 100vw; height: 100vh; overflow: hidden; background-color: #050508; color: #fff; font-family: 'Inter', sans-serif; display: flex; align-items: center; justify-content: center; } | |
| .bg-glow { position: fixed; width: 60vw; height: 60vw; background: radial-gradient(circle, rgba(99, 102, 241, 0.15) 0%, rgba(0,0,0,0) 70%); top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 0; } | |
| .main-container { position: relative; z-index: 10; width: 100%; max-width: 700px; display: flex; flex-direction: column; align-items: center; padding: 20px; } | |
| .brand-title { font-family: 'JetBrains Mono', monospace; font-size: 10px; letter-spacing: 5px; font-weight: 800; background: linear-gradient(to right, #6366f1, #a5b4fc, #6366f1); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-transform: uppercase; margin-bottom: 30px; text-align: center; } | |
| .tz-select { background: rgba(255, 255, 255, 0.05); border: 1px solid rgba(255, 255, 255, 0.1); color: #a5b4fc; padding: 6px 18px; border-radius: 12px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 25px; outline: none; cursor: pointer; transition: 0.3s; } | |
| .clock-wrapper { display: flex; align-items: center; justify-content: center; font-family: 'JetBrains Mono', monospace; font-size: clamp(3.5rem, 18vw, 7.5rem); font-weight: 800; line-height: 1; gap: 4px; margin-bottom: 15px; } | |
| .digit-column { height: 1.1em; overflow: hidden; position: relative; width: 0.65em; text-align: center; } | |
| .digit-strip { transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1); } | |
| .digit-val { height: 1.1em; display: flex; align-items: center; justify-content: center; color: #fff; text-shadow: 0 0 30px rgba(99, 102, 241, 0.4); } | |
| .sep { color: #4f46e5; opacity: 0.8; margin: 0 4px; padding-bottom: 15px; } | |
| .stats-bar { background: rgba(255, 255, 255, 0.02); backdrop-filter: blur(25px); border: 1px solid rgba(255, 255, 255, 0.05); border-radius: 24px; padding: 15px 40px; display: flex; gap: 40px; margin-top: 40px; } | |
| .stat-box { display: flex; flex-direction: column; align-items: center; } | |
| .stat-label { font-size: 7px; text-transform: uppercase; letter-spacing: 2px; color: #4f46e5; margin-bottom: 4px; font-weight: 600; } | |
| .stat-data { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700; } | |
| #countdown-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #000; display: none; z-index: 100; flex-direction: column; align-items: center; justify-content: center; } | |
| #countdown-num { font-size: 18rem; font-weight: 900; color: #f87171; text-shadow: 0 0 60px rgba(248, 113, 113, 0.6); } | |
| </style> | |
| </head> | |
| <body onclick="lockScreen()"> | |
| <div class="bg-glow"></div> | |
| <div id="countdown-overlay"><div class="text-white text-2xl tracking-[15px] mb-10 font-black">HAPPY NEW YEAR</div><div id="countdown-num">10</div></div> | |
| <div class="main-container"> | |
| <div class="brand-title">Đồng hồ chính xác nhất thế giới</div> | |
| <select id="timezone-selector" class="tz-select" onchange="changeTimezone()"> | |
| <option value="Asia/Ho_Chi_Minh">🇻🇳 Sài Gòn (GMT+7)</option> | |
| <option value="Asia/Singapore">🇸🇬 Singapore (GMT+8)</option> | |
| <option value="Asia/Tokyo">🇯🇵 Tokyo (GMT+9)</option> | |
| <option value="Europe/London">🇬🇧 London (GMT+0)</option> | |
| <option value="America/New_York">🇺🇸 New York (GMT-5)</option> | |
| </select> | |
| <div class="clock-wrapper"> | |
| <div class="digit-column"><div class="digit-strip" id="h1"></div></div> | |
| <div class="digit-column"><div class="digit-strip" id="h2"></div></div> | |
| <div class="sep">:</div> | |
| <div class="digit-column"><div class="digit-strip" id="m1"></div></div> | |
| <div class="digit-column"><div class="digit-strip" id="m2"></div></div> | |
| <div class="sep">:</div> | |
| <div class="digit-column"><div class="digit-strip" id="s1"></div></div> | |
| <div class="digit-column"><div class="digit-strip" id="s2"></div></div> | |
| </div> | |
| <div id="full-date" class="text-gray-500 text-[11px] tracking-[5px] uppercase font-bold">---</div> | |
| <div class="stats-bar"> | |
| <div class="stat-box"><span class="stat-label">Network Ping</span><span class="stat-data" id="v-ping">--</span></div> | |
| <div class="stat-box"><span class="stat-label">Time Offset</span><span class="stat-data" id="v-offset">--</span></div> | |
| <div class="stat-box"><span class="stat-label">Server IP</span><span class="stat-data" id="v-ip">--</span></div> | |
| </div> | |
| </div> | |
| <script> | |
| let serverOffset = 0, selectedTimezone = 'Asia/Ho_Chi_Minh', wakeLock = null; | |
| const API_URL = 'https://tuhbooh-networttools.hf.space'; | |
| function createStrips() { | |
| ['h1','h2','m1','m2','s1','s2'].forEach(id => { | |
| const s = document.getElementById(id); s.innerHTML = ''; | |
| for(let i=0; i<=9; i++) { const d = document.createElement('div'); d.className = 'digit-val'; d.innerText = i; s.appendChild(d); } | |
| }); | |
| } | |
| function setDigit(id, val) { document.getElementById(id).style.transform = `translateY(-${val * 1.1}em)`; } | |
| function changeTimezone() { selectedTimezone = document.getElementById('timezone-selector').value; } | |
| async function lockScreen() { if(navigator.wakeLock) wakeLock = await navigator.wakeLock.request('screen'); } | |
| async function sync() { | |
| const start = performance.now(); | |
| try { | |
| const r = await fetch(`${API_URL}?t=${Date.now()}`); | |
| const d = await r.json(); | |
| const ping = performance.now() - start; | |
| serverOffset = (d.ts * 1000 + ping/2) - Date.now(); | |
| document.getElementById('v-ping').innerText = Math.round(ping) + "ms"; | |
| document.getElementById('v-offset').innerText = Math.round(Math.abs(serverOffset)) + "ms"; | |
| document.getElementById('v-ip').innerText = d.ip; | |
| } catch(e) {} | |
| } | |
| function update() { | |
| const now = new Date(Date.now() + serverOffset); | |
| const timeStr = now.toLocaleTimeString('en-GB', {timeZone: selectedTimezone, hour12:false, hour:'2-digit', minute:'2-digit', second:'2-digit'}); | |
| const [h, m, s] = timeStr.split(':'); | |
| setDigit('h1', h[0]); setDigit('h2', h[1]); setDigit('m1', m[0]); setDigit('m2', m[1]); setDigit('s1', s[0]); setDigit('s2', s[1]); | |
| document.getElementById('full-date').innerText = new Intl.DateTimeFormat('vi-VN', {timeZone: selectedTimezone, weekday:'long', day:'numeric', month:'long', year:'numeric'}).format(now); | |
| requestAnimationFrame(update); | |
| } | |
| createStrips(); sync(); | |
| setInterval(sync, 15000); // Sync mỗi 15s để ổn định | |
| requestAnimationFrame(update); | |
| </script> | |
| </body> | |
| </html> |