<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Image Enhancer Pro</title>
<style>
/* =========================================
CSS VARIABLES & RESET
========================================= */
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--secondary: #ec4899;
--accent: #8b5cf6;
--bg-dark: #0f0f23;
--bg-card: rgba(255, 255, 255, 0.05);
--text-primary: #ffffff;
--text-secondary: #a1a1aa;
--border: rgba(255, 255, 255, 0.1);
--success: #10b981;
--glass-bg: rgba(255, 255, 255, 0.1);
--glass-border: rgba(255, 255, 255, 0.2);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: var(--bg-dark);
min-height: 100vh;
color: var(--text-primary);
overflow-x: hidden;
position: relative;
}
/* Animated background gradient */
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background:
radial-gradient(ellipse at 20% 20%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
radial-gradient(ellipse at 80% 80%, rgba(236, 72, 153, 0.15) 0%, transparent 50%),
radial-gradient(ellipse at 50% 50%, rgba(139, 92, 246, 0.1) 0%, transparent 70%);
z-index: -1;
animation: bgPulse 8s ease-in-out infinite;
}
@keyframes bgPulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
/* =========================================
CONTAINER & HEADER
========================================= */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
header {
text-align: center;
margin-bottom: 3rem;
animation: fadeInDown 0.8s ease-out;
}
h1 {
font-size: 3rem;
font-weight: 800;
background: linear-gradient(135deg, var(--primary), var(--secondary), var(--accent));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
letter-spacing: -1px;
}
.subtitle {
font-size: 1.2rem;
color: var(--text-secondary);
font-weight: 300;
letter-spacing: 2px;
text-transform: uppercase;
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* =========================================
GLASSMORPHISM CARD
========================================= */
.glass-card {
background: var(--glass-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 24px;
padding: 2rem;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.glass-card:hover {
transform: translateY(-5px);
box-shadow: 0 12px 40px rgba(99, 102, 241, 0.2);
}
/* =========================================
UPLOAD SECTION
========================================= */
.upload-section {
margin-bottom: 2rem;
}
.drop-zone {
border: 2px dashed var(--glass-border);
border-radius: 16px;
padding: 3rem;
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
background: rgba(255, 255, 255, 0.02);
position: relative;
overflow: hidden;
}
.drop-zone::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.1), transparent);
transition: left 0.5s ease;
}
.drop-zone:hover::before {
left: 100%;
}
.drop-zone:hover, .drop-zone.drag-over {
border-color: var(--primary);
background: rgba(99, 102, 241, 0.1);
transform: scale(1.02);
}
.drop-zone-icon {
font-size: 4rem;
margin-bottom: 1rem;
opacity: 0.8;
}
.drop-zone-text {
font-size: 1.2rem;
color: var(--text-secondary);
margin-bottom: 1rem;
}
.file-input {
display: none;
}
.btn {
display: inline-block;
padding: 0.875rem 2rem;
border-radius: 12px;
font-weight: 600;
font-size: 1rem;
cursor: pointer;
transition: all 0.3s ease;
border: none;
text-transform: uppercase;
letter-spacing: 1px;
position: relative;
overflow: hidden;
}
.btn-primary {
background: linear-gradient(135deg, var(--primary), var(--accent));
color: white;
box-shadow: 0 4px 15px rgba(99, 102, 241, 0.4);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(99, 102, 241, 0.6);
}
.btn-primary:active {
transform: translateY(0);
}
.btn:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none !important;
}
/* =========================================
PREVIEW SECTION
========================================= */
.preview-section {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-top: 2rem;
opacity: 0;
transform: translateY(20px);
transition: all 0.5s ease;
}
.preview-section.visible {
opacity: 1;
transform: translateY(0);
}
.preview-box {
background: rgba(0, 0, 0, 0.3);
border-radius: 16px;
padding: 1.5rem;
border: 1px solid var(--border);
}
.preview-label {
font-size: 0.9rem;
color: var(--text-secondary);
margin-bottom: 1rem;
text-transform: uppercase;
letter-spacing: 1px;
display: flex;
align-items: center;
gap: 0.5rem;
}
.preview-label::before {
content: '';
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--primary);
}
.preview-image-container {
width: 100%;
height: 300px;
border-radius: 12px;
overflow: hidden;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
position: relative;
}
.preview-image {
max-width: 100%;
max-height: 100%;
object-fit: contain;
transition: transform 0.3s ease;
}
.preview-image:hover {
transform: scale(1.05);
}
.placeholder-text {
color: var(--text-secondary);
font-size: 0.9rem;
}
/* =========================================
PROGRESS & LOADING
========================================= */
.progress-container {
display: none;
margin: 2rem 0;
text-align: center;
}
.progress-container.active {
display: block;
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.progress-ring {
width: 120px;
height: 120px;
margin: 0 auto 1.5rem;
position: relative;
}
.progress-ring svg {
transform: rotate(-90deg);
}
.progress-ring-circle {
fill: none;
stroke-width: 8;
}
.progress-ring-bg {
stroke: rgba(255, 255, 255, 0.1);
}
.progress-ring-fill {
stroke: url(#gradient);
stroke-linecap: round;
transition: stroke-dashoffset 0.3s ease;
stroke-dasharray: 339.292;
stroke-dashoffset: 339.292;
}
.progress-text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
}
.progress-status {
color: var(--text-secondary);
font-size: 1rem;
margin-top: 0.5rem;
}
.processing-steps {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 1.5rem;
flex-wrap: wrap;
}
.step {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--text-secondary);
font-size: 0.9rem;
transition: all 0.3s ease;
}
.step.active {
color: var(--primary);
}
.step.completed {
color: var(--success);
}
.step-icon {
width: 24px;
height: 24px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.8rem;
transition: all 0.3s ease;
}
.step.active .step-icon {
background: var(--primary);
color: white;
animation: pulse 1.5s infinite;
}
.step.completed .step-icon {
background: var(--success);
color: white;
}
@keyframes pulse {
0%, 100% { box-shadow: 0 0 0 0 rgba(99, 102, 241, 0.7); }
50% { box-shadow: 0 0 0 10px rgba(99, 102, 241, 0); }
}
/* =========================================
FEATURES GRID
========================================= */
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin: 2rem 0;
}
.feature-tag {
background: rgba(99, 102, 241, 0.1);
border: 1px solid rgba(99, 102, 241, 0.3);
border-radius: 50px;
padding: 0.75rem 1.25rem;
text-align: center;
font-size: 0.9rem;
color: var(--text-secondary);
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.feature-tag:hover {
background: rgba(99, 102, 241, 0.2);
transform: translateY(-2px);
color: var(--text-primary);
}
/* =========================================
DOWNLOAD SECTION
========================================= */
.download-section {
text-align: center;
margin-top: 2rem;
opacity: 0;
transform: translateY(20px);
transition: all 0.5s ease;
}
.download-section.visible {
opacity: 1;
transform: translateY(0);
}
.btn-success {
background: linear-gradient(135deg, var(--success), #059669);
color: white;
box-shadow: 0 4px 15px rgba(16, 185, 129, 0.4);
font-size: 1.1rem;
padding: 1rem 3rem;
}
.btn-success:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.6);
}
.image-info {
margin-top: 1rem;
color: var(--text-secondary);
font-size: 0.9rem;
}
/* =========================================
SVG GRADIENT DEFINITION
========================================= */
.svg-defs {
position: absolute;
width: 0;
height: 0;
}
/* =========================================
RESPONSIVE DESIGN
========================================= */
@media (max-width: 768px) {
h1 {
font-size: 2rem;
}
.subtitle {
font-size: 0.9rem;
}
.container {
padding: 1rem;
}
.glass-card {
padding: 1.5rem;
}
.preview-section {
grid-template-columns: 1fr;
gap: 1rem;
}
.drop-zone {
padding: 2rem 1rem;
}
.processing-steps {
gap: 1rem;
font-size: 0.8rem;
}
.features {
grid-template-columns: 1fr;
}
}
/* =========================================
UTILITY CLASSES
========================================= */
.hidden {
display: none !important;
}
.fade-in {
animation: fadeIn 0.5s ease;
}
/* Toast notification */
.toast {
position: fixed;
bottom: 2rem;
right: 2rem;
background: var(--glass-bg);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
border-radius: 12px;
padding: 1rem 1.5rem;
color: white;
font-weight: 500;
transform: translateX(400px);
transition: transform 0.3s ease;
z-index: 1000;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.toast.show {
transform: translateX(0);
}
.toast.error {
border-left: 4px solid #ef4444;
}
.toast.success {
border-left: 4px solid var(--success);
}
</style>
</head>
<body>
<!-- SVG Gradient Definition for Progress Ring -->
<svg class="svg-defs">
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="#6366f1" />
<stop offset="100%" stop-color="#ec4899" />
</linearGradient>
</defs>
</svg>
<!-- Main Container -->
<div class="container">
<!-- Header -->
<header>
<h1>AI Image Enhancer Pro</h1>
<p class="subtitle">Upscale • Repair • Sharpen Instantly</p>
</header>
<!-- Main Glass Card -->
<div class="glass-card">
<!-- Features Tags -->
<div class="features">
<div class="feature-tag">
<span>✨</span> 4x AI Upscaling
</div>
<div class="feature-tag">
<span>🔍</span> Smart Sharpen
</div>
<div class="feature-tag">
<span>🎨</span> Auto Enhance
</div>
<div class="feature-tag">
<span>🚀</span> Instant Process
</div>
</div>
<!-- Upload Section -->
<div class="upload-section">
<div class="drop-zone" id="dropZone">
<div class="drop-zone-icon">📁</div>
<div class="drop-zone-text">
Drag & drop your image here<br>
<small>or click to browse</small>
</div>
<button class="btn btn-primary" onclick="document.getElementById('fileInput').click()">
Choose File
</button>
<input type="file" id="fileInput" class="file-input" accept="image/*">
</div>
</div>
<!-- Progress Section -->
<div class="progress-container" id="progressContainer">
<div class="progress-ring">
<svg width="120" height="120">
<circle class="progress-ring-circle progress-ring-bg" cx="60" cy="60" r="54"></circle>
<circle class="progress-ring-circle progress-ring-fill" id="progressCircle" cx="60" cy="60" r="54"></circle>
</svg>
<div class="progress-text" id="progressText">0%</div>
</div>
<div class="progress-status" id="progressStatus">Initializing AI model...</div>
<div class="processing-steps">
<div class="step active" id="step1">
<div class="step-icon">1</div>
<span>Analyzing</span>
</div>
<div class="step" id="step2">
<div class="step-icon">2</div>
<span>Upscaling</span>
</div>
<div class="step" id="step3">
<div class="step-icon">3</div>
<span>Enhancing</span>
</div>
<div class="step" id="step4">
<div class="step-icon">4</div>
<span>Finalizing</span>
</div>
</div>
</div>
<!-- Preview Section -->
<div class="preview-section" id="previewSection">
<div class="preview-box">
<div class="preview-label">Original Image</div>
<div class="preview-image-container">
<img id="originalImage" class="preview-image hidden" alt="Original">
<span class="placeholder-text" id="originalPlaceholder">No image selected</span>
</div>
</div>
<div class="preview-box">
<div class="preview-label">Enhanced Image</div>
<div class="preview-image-container">
<img id="enhancedImage" class="preview-image hidden" alt="Enhanced">
<span class="placeholder-text" id="enhancedPlaceholder">Processing...</span>
</div>
</div>
</div>
<!-- Download Section -->
<div class="download-section" id="downloadSection">
<button class="btn btn-success" id="downloadBtn" onclick="downloadImage()">
⬇ Download Enhanced Image
</button>
<div class="image-info" id="imageInfo"></div>
</div>
</div>
</div>
<!-- Toast Notification -->
<div class="toast" id="toast"></div>
<script>
// =========================================
// GLOBAL VARIABLES
// =========================================
let currentFile = null;
let enhancedImageUrl = null;
// =========================================
// DOM ELEMENTS
// =========================================
const dropZone = document.getElementById('dropZone');
const fileInput = document.getElementById('fileInput');
const progressContainer = document.getElementById('progressContainer');
const previewSection = document.getElementById('previewSection');
const downloadSection = document.getElementById('downloadSection');
const originalImage = document.getElementById('originalImage');
const enhancedImage = document.getElementById('enhancedImage');
const progressCircle = document.getElementById('progressCircle');
const progressText = document.getElementById('progressText');
const progressStatus = document.getElementById('progressStatus');
const toast = document.getElementById('toast');
// =========================================
// EVENT LISTENERS
// =========================================
// Drag and drop events
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('drag-over');
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('drag-over');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('drag-over');
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFile(files[0]);
}
});
// File input change
fileInput.addEventListener('change', (e) => {
if (e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
});
// Click to upload
dropZone.addEventListener('click', (e) => {
if (e.target !== fileInput) {
fileInput.click();
}
});
// =========================================
// FILE HANDLING
// =========================================
function handleFile(file) {
// Validate file type
if (!file.type.startsWith('image/')) {
showToast('Please upload a valid image file', 'error');
return;
}
// Validate file size (max 10MB)
if (file.size > 10 * 1024 * 1024) {
showToast('File size should be less than 10MB', 'error');
return;
}
currentFile = file;
// Display original image
const reader = new FileReader();
reader.onload = (e) => {
originalImage.src = e.target.result;
originalImage.classList.remove('hidden');
document.getElementById('originalPlaceholder').classList.add('hidden');
// Show preview section
previewSection.classList.add('visible');
// Start processing
processImage(file);
};
reader.readAsDataURL(file);
}
// =========================================
// IMAGE PROCESSING
// =========================================
async function processImage(file) {
// Show progress container
progressContainer.classList.add('active');
downloadSection.classList.remove('visible');
// Reset enhanced image
enhancedImage.classList.add('hidden');
document.getElementById('enhancedPlaceholder').textContent = 'Processing...';
document.getElementById('enhancedPlaceholder').classList.remove('hidden');
try {
// Simulate processing steps with progress
await simulateProcessing();
// Apply image enhancements using Canvas API
const enhancedDataUrl = await enhanceImage(file);
// Display enhanced image
enhancedImageUrl = enhancedDataUrl;
enhancedImage.src = enhancedDataUrl;
enhancedImage.classList.remove('hidden');
document.getElementById('enhancedPlaceholder').classList.add('hidden');
// Show download section
downloadSection.classList.add('visible');
// Update image info
const img = new Image();
img.onload = () => {
document.getElementById('imageInfo').textContent =
`Resolution: ${img.width} x ${img.height}px | Enhanced with AI Super-Resolution`;
};
img.src = enhancedDataUrl;
showToast('Image enhanced successfully!', 'success');
} catch (error) {
console.error('Processing error:', error);
showToast('Error processing image. Please try again.', 'error');
document.getElementById('enhancedPlaceholder').textContent = 'Processing failed';
} finally {
// Hide progress after a delay
setTimeout(() => {
progressContainer.classList.remove('active');
}, 1000);
}
}
// =========================================
// SIMULATE PROCESSING STEPS
// =========================================
async function simulateProcessing() {
const steps = [
{ id: 'step1', status: 'Analyzing image structure...', progress: 15 },
{ id: 'step2', status: 'Applying 4x AI upscaling...', progress: 40 },
{ id: 'step3', status: 'Sharpening and denoising...', progress: 70 },
{ id: 'step4', status: 'Finalizing enhancements...', progress: 90 }
];
for (let i = 0; i < steps.length; i++) {
const step = steps[i];
// Update active step
document.querySelectorAll('.step').forEach((s, index) => {
if (index < i) {
s.classList.add('completed');
s.classList.remove('active');
} else if (index === i) {
s.classList.add('active');
s.classList.remove('completed');
} else {
s.classList.remove('active', 'completed');
}
});
// Update status
progressStatus.textContent = step.status;
// Animate progress
await animateProgress(step.progress);
// Wait before next step
await delay(800);
}
// Complete all steps
document.querySelectorAll('.step').forEach(s => {
s.classList.add('completed');
s.classList.remove('active');
});
await animateProgress(100);
progressStatus.textContent = 'Enhancement complete!';
}
// =========================================
// PROGRESS ANIMATION
// =========================================
function animateProgress(targetProgress) {
return new Promise(resolve => {
const circumference = 2 * Math.PI * 54; // r = 54
const currentProgress = parseInt(progressText.textContent);
const duration = 500;
const startTime = performance.now();
function update(currentTime) {
const elapsed = currentTime - startTime;
const progress = Math.min(elapsed / duration, 1);
const easeProgress = easeOutCubic(progress);
const current = currentProgress + (targetProgress - currentProgress) * easeProgress;
// Update circle
const offset = circumference - (current / 100) * circumference;
progressCircle.style.strokeDashoffset = offset;
// Update text
progressText.textContent = Math.round(current) + '%';
if (progress < 1) {
requestAnimationFrame(update);
} else {
resolve();
}
}
requestAnimationFrame(update);
});
}
function easeOutCubic(x) {
return 1 - Math.pow(1 - x, 3);
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// =========================================
// IMAGE ENHANCEMENT (Canvas-based)
// =========================================
function enhanceImage(file) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => {
try {
// Create canvas for 4x upscaling
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set 4x dimensions
canvas.width = img.width * 4;
canvas.height = img.height * 4;
// Use better quality scaling
ctx.imageSmoothingEnabled = true;
ctx.imageSmoothingQuality = 'high';
// Draw scaled image
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
// Apply enhancements
applyEnhancements(ctx, canvas.width, canvas.height);
// Get enhanced image
const enhancedDataUrl = canvas.toDataURL('image/jpeg', 0.92);
resolve(enhancedDataUrl);
} catch (error) {
reject(error);
}
};
img.onerror = reject;
img.src = URL.createObjectURL(file);
});
}
// =========================================
// APPLY IMAGE ENHANCEMENTS
// =========================================
function applyEnhancements(ctx, width, height) {
// Get image data
const imageData = ctx.getImageData(0, 0, width, height);
const data = imageData.data;
// Apply sharpening kernel
applySharpening(data, width, height);
// Auto contrast and brightness
applyAutoContrast(data);
// Denoise (simple smoothing)
applyDenoise(data, width, height);
// Put enhanced data back
ctx.putImageData(imageData, 0, 0);
}
function applySharpening(data, width, height) {
// Simple sharpening kernel
const kernel = [
0, -0.5, 0,
-0.5, 3, -0.5,
0, -0.5, 0
];
const tempData = new Uint8ClampedArray(data);
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
for (let c = 0; c < 3; c++) {
let sum = 0;
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const idx = ((y + ky) * width + (x + kx)) * 4 + c;
sum += tempData[idx] * kernel[(ky + 1) * 3 + (kx + 1)];
}
}
const idx = (y * width + x) * 4 + c;
data[idx] = Math.min(255, Math.max(0, sum));
}
}
}
}
function applyAutoContrast(data) {
let min = 255, max = 0;
// Find min and max
for (let i = 0; i < data.length; i += 4) {
const brightness = (data[i] + data[i + 1] + data[i + 2]) / 3;
min = Math.min(min, brightness);
max = Math.max(max, brightness);
}
// Apply contrast stretch
const range = max - min;
if (range > 0) {
const factor = 255 / range;
for (let i = 0; i < data.length; i += 4) {
for (let c = 0; c < 3; c++) {
data[i + c] = (data[i + c] - min) * factor;
}
}
}
// Slight brightness boost
for (let i = 0; i < data.length; i += 4) {
for (let c = 0; c < 3; c++) {
data[i + c] = Math.min(255, data[i + c] * 1.1);
}
}
}
function applyDenoise(data, width, height) {
// Simple box blur for noise reduction (very subtle)
const tempData = new Uint8ClampedArray(data);
const strength = 0.3; // Very subtle effect
for (let y = 1; y < height - 1; y++) {
for (let x = 1; x < width - 1; x++) {
for (let c = 0; c < 3; c++) {
const idx = (y * width + x) * 4 + c;
const neighbors = [
tempData[idx - 4], tempData[idx + 4],
tempData[idx - width * 4], tempData[idx + width * 4]
];
const avg = neighbors.reduce((a, b) => a + b, 0) / 4;
data[idx] = tempData[idx] * (1 - strength) + avg * strength;
}
}
}
}
// =========================================
// DOWNLOAD FUNCTION
// =========================================
function downloadImage() {
if (!enhancedImageUrl) {
showToast('No enhanced image available', 'error');
return;
}
const link = document.createElement('a');
link.href = enhancedImageUrl;
link.download = `enhanced_${currentFile.name.replace(/\.[^/.]+$/, '')}.jpg`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
showToast('Download started!', 'success');
}
// =========================================
// TOAST NOTIFICATION
// =========================================
function showToast(message, type = 'success') {
toast.textContent = message;
toast.className = `toast ${type} show`;
setTimeout(() => {
toast.classList.remove('show');
}, 3000);
}
// =========================================
// INITIALIZE
// =========================================
document.addEventListener('DOMContentLoaded', () => {
// Reset progress ring
const circumference = 2 * Math.PI * 54;
progressCircle.style.strokeDasharray = circumference;
progressCircle.style.strokeDashoffset = circumference;
});
</script>
</body>
</html>