Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Sobriety Tracker</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| primary: '#00ff9d', | |
| secondary: '#00b4ff', | |
| cyan: '#00f7ff', | |
| purple: '#a855f7', | |
| magenta: '#ec4899', | |
| dark: '#0a0a0a', | |
| darker: '#050505', | |
| }, | |
| fontFamily: { | |
| sans: ['Inter', 'sans-serif'], | |
| }, | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Inter', sans-serif; | |
| background-color: #050505; | |
| } | |
| .glow-text { | |
| text-shadow: 0 0 8px rgba(0, 255, 157, 0.6); | |
| } | |
| .card-glow { | |
| box-shadow: 0 0 15px rgba(0, 180, 255, 0.3); | |
| } | |
| .progress-ring__circle { | |
| transition: stroke-dashoffset 0.35s; | |
| transform: rotate(-90deg); | |
| transform-origin: 50% 50%; | |
| } | |
| .progress-ring__gradient { | |
| stroke: url(#progressGradient); | |
| filter: drop-shadow(0 0 6px rgba(168, 85, 247, 0.7)); | |
| } | |
| .floating { | |
| animation: floating 3s ease-in-out infinite; | |
| } | |
| @keyframes floating { | |
| 0% { transform: translateY(0px); } | |
| 50% { transform: translateY(-10px); } | |
| 100% { transform: translateY(0px); } | |
| } | |
| .outer-glow { | |
| filter: drop-shadow(0 0 8px rgba(0, 247, 255, 0.4)); | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen bg-dark text-white flex flex-col items-center justify-center p-4"> | |
| <div class="max-w-3xl w-full space-y-8"> | |
| <!-- Header --> | |
| <div class="text-center"> | |
| <h1 class="text-4xl font-bold mb-2 glow-text">SOBRIETY JOURNEY</h1> | |
| <p class="text-gray-400">Every second counts towards a healthier you</p> | |
| </div> | |
| <!-- Timer --> | |
| <div class="bg-darker rounded-2xl p-8 card-glow relative overflow-hidden"> | |
| <div class="absolute inset-0 bg-gradient-to-br from-primary/10 to-secondary/10 opacity-30"></div> | |
| <div class="relative z-10"> | |
| <div class="flex flex-col items-center"> | |
| <div class="relative w-64 h-64 mb-6 outer-glow"> | |
| <svg class="w-full h-full" viewBox="0 0 100 100"> | |
| <defs> | |
| <linearGradient id="progressGradient" x1="0%" y1="0%" x2="100%" y2="100%"> | |
| <stop offset="0%" stop-color="#00f7ff" /> | |
| <stop offset="50%" stop-color="#a855f7" /> | |
| <stop offset="100%" stop-color="#ec4899" /> | |
| </linearGradient> | |
| </defs> | |
| <circle class="text-gray-800" stroke-width="8" stroke="currentColor" fill="transparent" r="45" cx="50" cy="50" /> | |
| <circle class="progress-ring__circle progress-ring__gradient" stroke-width="8" stroke-dasharray="283" stroke-dashoffset="0" stroke-linecap="round" fill="transparent" r="45" cx="50" cy="50" /> | |
| </svg> | |
| <div class="absolute inset-0 flex flex-col items-center justify-center"> | |
| <div class="text-5xl font-bold tracking-tighter glow-text" id="days">00</div> | |
| <div class="text-gray-400 text-sm uppercase tracking-wider">Days</div> | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-3 gap-4 text-center"> | |
| <div> | |
| <div class="text-3xl font-bold glow-text" id="hours">00</div> | |
| <div class="text-gray-400 text-sm uppercase tracking-wider">Hours</div> | |
| </div> | |
| <div> | |
| <div class="text-3xl font-bold glow-text" id="minutes">00</div> | |
| <div class="text-gray-400 text-sm uppercase tracking-wider">Minutes</div> | |
| </div> | |
| <div> | |
| <div class="text-3xl font-bold glow-text" id="seconds">00</div> | |
| <div class="text-gray-400 text-sm uppercase tracking-wider">Seconds</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Money Saved Card --> | |
| <div class="bg-darker rounded-2xl p-6 card-glow relative overflow-hidden"> | |
| <div class="absolute inset-0 bg-gradient-to-br from-secondary/10 to-primary/10 opacity-30"></div> | |
| <div class="relative z-10"> | |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-coins text-secondary mr-2"></i> | |
| <span>Money Saved</span> | |
| </h2> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center space-x-4"> | |
| <div class="bg-gradient-to-br from-primary to-secondary p-3 rounded-xl floating"> | |
| <i class="fas fa-wallet text-white text-2xl"></i> | |
| </div> | |
| <div> | |
| <div class="text-gray-400 text-sm">Total Savings</div> | |
| <div class="text-3xl font-bold glow-text" id="money-saved">$0.00</div> | |
| </div> | |
| </div> | |
| <div class="bg-gray-800/50 p-3 rounded-lg"> | |
| <div class="text-gray-400 text-sm mb-1">Daily Average</div> | |
| <div class="text-lg font-medium" id="daily-average">$0.00/day</div> | |
| </div> | |
| </div> | |
| <div class="mt-6"> | |
| <div class="flex justify-between text-sm text-gray-400 mb-1"> | |
| <span>Savings Progress</span> | |
| <span id="savings-percent">0%</span> | |
| </div> | |
| <div class="w-full bg-gray-800 rounded-full h-2.5"> | |
| <div class="bg-gradient-to-r from-cyan via-purple to-magenta h-2.5 rounded-full" id="savings-progress" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Settings Panel --> | |
| <div class="bg-darker rounded-2xl p-6 card-glow"> | |
| <h2 class="text-xl font-semibold mb-4 flex items-center"> | |
| <i class="fas fa-cog text-gray-400 mr-2"></i> | |
| <span>Settings</span> | |
| </h2> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-gray-400 text-sm mb-1">Start Date</label> | |
| <input type="datetime-local" id="start-date" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-400 text-sm mb-1">Daily Spending Before ($)</label> | |
| <input type="number" id="daily-spend" value="15" min="0" step="0.01" class="w-full bg-gray-800 border border-gray-700 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-primary"> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex justify-end space-x-3"> | |
| <button id="reset-btn" class="px-4 py-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition"> | |
| <i class="fas fa-redo mr-2"></i> Reset | |
| </button> | |
| <button id="save-btn" class="px-4 py-2 bg-gradient-to-r from-primary to-secondary hover:opacity-90 rounded-lg transition"> | |
| <i class="fas fa-save mr-2"></i> Save | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Elements | |
| const daysEl = document.getElementById('days'); | |
| const hoursEl = document.getElementById('hours'); | |
| const minutesEl = document.getElementById('minutes'); | |
| const secondsEl = document.getElementById('seconds'); | |
| const moneySavedEl = document.getElementById('money-saved'); | |
| const dailyAverageEl = document.getElementById('daily-average'); | |
| const savingsPercentEl = document.getElementById('savings-percent'); | |
| const savingsProgressEl = document.getElementById('savings-progress'); | |
| const startDateInput = document.getElementById('start-date'); | |
| const dailySpendInput = document.getElementById('daily-spend'); | |
| const saveBtn = document.getElementById('save-btn'); | |
| const resetBtn = document.getElementById('reset-btn'); | |
| const progressCircle = document.querySelector('.progress-ring__circle'); | |
| // Default values | |
| let startDate = new Date(); | |
| let dailySpend = 15; | |
| let targetSavings = 1000; // Example target | |
| // Load saved data from localStorage | |
| function loadData() { | |
| const savedStartDate = localStorage.getItem('sobrietyStartDate'); | |
| const savedDailySpend = localStorage.getItem('dailySpend'); | |
| if (savedStartDate) { | |
| startDate = new Date(savedStartDate); | |
| startDateInput.value = formatDateTimeLocal(startDate); | |
| } else { | |
| // Set default to now | |
| startDate = new Date(); | |
| startDateInput.value = formatDateTimeLocal(startDate); | |
| } | |
| if (savedDailySpend) { | |
| dailySpend = parseFloat(savedDailySpend); | |
| dailySpendInput.value = dailySpend; | |
| } | |
| updateTimer(); | |
| updateSavings(); | |
| } | |
| // Format date for datetime-local input | |
| function formatDateTimeLocal(date) { | |
| const year = date.getFullYear(); | |
| const month = String(date.getMonth() + 1).padStart(2, '0'); | |
| const day = String(date.getDate()).padStart(2, '0'); | |
| const hours = String(date.getHours()).padStart(2, '0'); | |
| const minutes = String(date.getMinutes()).padStart(2, '0'); | |
| return `${year}-${month}-${day}T${hours}:${minutes}`; | |
| } | |
| // Update timer | |
| function updateTimer() { | |
| const now = new Date(); | |
| const diff = now - startDate; | |
| const days = Math.floor(diff / (1000 * 60 * 60 * 24)); | |
| const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); | |
| const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60)); | |
| const seconds = Math.floor((diff % (1000 * 60)) / 1000); | |
| daysEl.textContent = String(days).padStart(2, '0'); | |
| hoursEl.textContent = String(hours).padStart(2, '0'); | |
| minutesEl.textContent = String(minutes).padStart(2, '0'); | |
| secondsEl.textContent = String(seconds).padStart(2, '0'); | |
| // Update progress circle (for days - max 365 days) | |
| const dayProgress = Math.min(days / 365, 1); | |
| const circumference = 2 * Math.PI * 45; | |
| const offset = circumference - dayProgress * circumference; | |
| progressCircle.style.strokeDashoffset = offset; | |
| } | |
| // Update savings | |
| function updateSavings() { | |
| const now = new Date(); | |
| const diff = now - startDate; | |
| const days = diff / (1000 * 60 * 60 * 24); | |
| const totalSaved = days * dailySpend; | |
| const avgPerDay = days > 0 ? totalSaved / days : 0; | |
| moneySavedEl.textContent = `$${totalSaved.toFixed(2)}`; | |
| dailyAverageEl.textContent = `$${avgPerDay.toFixed(2)}/day`; | |
| // Update progress bar | |
| const savingsPercent = Math.min((totalSaved / targetSavings) * 100, 100); | |
| savingsPercentEl.textContent = `${Math.floor(savingsPercent)}%`; | |
| savingsProgressEl.style.width = `${savingsPercent}%`; | |
| } | |
| // Save settings | |
| saveBtn.addEventListener('click', () => { | |
| const newStartDate = new Date(startDateInput.value); | |
| const newDailySpend = parseFloat(dailySpendInput.value); | |
| if (isNaN(newStartDate.getTime())) { | |
| alert('Please enter a valid start date'); | |
| return; | |
| } | |
| if (isNaN(newDailySpend) || newDailySpend < 0) { | |
| alert('Please enter a valid daily spending amount'); | |
| return; | |
| } | |
| startDate = newStartDate; | |
| dailySpend = newDailySpend; | |
| localStorage.setItem('sobrietyStartDate', startDate.toISOString()); | |
| localStorage.setItem('dailySpend', dailySpend.toString()); | |
| updateTimer(); | |
| updateSavings(); | |
| // Show confirmation | |
| const originalText = saveBtn.innerHTML; | |
| saveBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Saved!'; | |
| setTimeout(() => { | |
| saveBtn.innerHTML = originalText; | |
| }, 2000); | |
| }); | |
| // Reset everything | |
| resetBtn.addEventListener('click', () => { | |
| if (confirm('Are you sure you want to reset all data? This cannot be undone.')) { | |
| localStorage.removeItem('sobrietyStartDate'); | |
| localStorage.removeItem('dailySpend'); | |
| startDate = new Date(); | |
| startDateInput.value = formatDateTimeLocal(startDate); | |
| dailySpendInput.value = '15'; | |
| loadData(); | |
| } | |
| }); | |
| // Initialize | |
| loadData(); | |
| setInterval(updateTimer, 1000); | |
| setInterval(updateSavings, 60000); // Update savings every minute | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=AlexYehorov/tim" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |