anycoder-0a24c96f / index.html
GenXDad's picture
Upload folder using huggingface_hub
1c75af6 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animai | AI Image to Video Generator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
<script src="https://unpkg.com/@phosphor-icons/web"></script>
<script>
tailwind.config = {
theme: {
extend: {
fontFamily: {
sans: ['Inter', 'sans-serif'],
mono: ['JetBrains Mono', 'monospace'],
},
colors: {
surface: {
900: '#0a0a0f',
800: '#13131f',
700: '#1c1c2e',
600: '#252540',
500: '#353560',
},
accent: {
400: '#a78bfa',
500: '#8b5cf6',
600: '#7c3aed',
},
glow: {
purple: 'rgba(139, 92, 246, 0.5)',
cyan: 'rgba(34, 211, 238, 0.5)',
}
}
}
}
}
</script>
<style>
:root {
--glass-bg: rgba(19, 19, 31, 0.7);
--glass-border: rgba(255, 255, 255, 0.08);
--glass-highlight: rgba(255, 255, 255, 0.03);
}
* {
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
background-color: #0a0a0f;
color: #e2e8f0;
font-family: 'Inter', sans-serif;
overflow-x: hidden;
min-height: 100vh;
}
/* Background ambient mesh */
.ambient-bg {
position: fixed;
inset: 0;
z-index: -1;
background:
radial-gradient(ellipse 80% 50% at 20% 40%, rgba(139, 92, 246, 0.12), transparent),
radial-gradient(ellipse 60% 40% at 80% 60%, rgba(34, 211, 238, 0.08), transparent),
radial-gradient(ellipse 50% 50% at 50% 50%, rgba(139, 92, 246, 0.05), transparent);
filter: blur(60px);
}
.ambient-bg::after {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.02'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
opacity: 0.4;
}
/* Glass morphism */
.glass-panel {
background: var(--glass-bg);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
inset 0 1px 0 var(--glass-highlight);
}
.glass-card {
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.06);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.glass-card:hover {
background: rgba(255, 255, 255, 0.06);
border-color: rgba(139, 92, 246, 0.3);
transform: translateY(-2px);
box-shadow: 0 12px 40px rgba(139, 92, 246, 0.15);
}
/* Upload zone */
.upload-zone {
border: 2px dashed rgba(255, 255, 255, 0.15);
background: rgba(255, 255, 255, 0.02);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.upload-zone:hover, .upload-zone.drag-over {
border-color: rgba(139, 92, 246, 0.6);
background: rgba(139, 92, 246, 0.08);
transform: scale(1.01);
}
.upload-zone.drag-over {
box-shadow: 0 0 40px rgba(139, 92, 246, 0.2);
}
/* Inputs */
.input-field {
background: rgba(0, 0, 0, 0.3);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.input-field:focus {
outline: none;
border-color: rgba(139, 92, 246, 0.5);
box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.1), 0 0 20px rgba(139, 92, 246, 0.1);
}
/* Range slider */
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 6px;
background: rgba(255, 255, 255, 0.1);
border-radius: 3px;
outline: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
background: #8b5cf6;
border-radius: 50%;
cursor: pointer;
box-shadow: 0 0 10px rgba(139, 92, 246, 0.5);
transition: all 0.2s ease;
}
input[type="range"]::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 0 20px rgba(139, 92, 246, 0.7);
}
input[type="range"]::-moz-range-thumb {
width: 18px;
height: 18px;
background: #8b5cf6;
border-radius: 50%;
cursor: pointer;
border: none;
box-shadow: 0 0 10px rgba(139, 92, 246, 0.5);
}
/* Buttons */
.btn-primary {
background: linear-gradient(135deg, #8b5cf6, #7c3aed);
position: relative;
overflow: hidden;
transition: all 0.3s ease;
}
.btn-primary::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(135deg, rgba(255,255,255,0.2), transparent);
opacity: 0;
transition: opacity 0.3s ease;
}
.btn-primary:hover::before {
opacity: 1;
}
.btn-primary:hover {
transform: translateY(-1px);
box-shadow: 0 8px 30px rgba(139, 92, 246, 0.4);
}
.btn-primary:active {
transform: translateY(0);
}
.btn-primary:disabled {
opacity: 0.6;
cursor: not-allowed;
transform: none;
}
/* Tag pills */
.tag-pill {
transition: all 0.2s ease;
cursor: pointer;
user-select: none;
}
.tag-pill:hover {
background: rgba(139, 92, 246, 0.2);
border-color: rgba(139, 92, 246, 0.4);
transform: translateY(-1px);
}
.tag-pill.active {
background: rgba(139, 92, 246, 0.25);
border-color: rgba(139, 92, 246, 0.6);
color: #c4b5fd;
}
/* Animations */
@keyframes pulse-glow {
0%, 100% { box-shadow: 0 0 20px rgba(139, 92, 246, 0.3); }
50% { box-shadow: 0 0 40px rgba(139, 92, 246, 0.5); }
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-6px); }
}
@keyframes shimmer {
0% { background-position: -200% 0; }
100% { background-position: 200% 0; }
}
.animate-pulse-glow {
animation: pulse-glow 2s ease-in-out infinite;
}
.animate-float {
animation: float 3s ease-in-out infinite;
}
.shimmer-bg {
background: linear-gradient(90deg, transparent 0%, rgba(139, 92, 246, 0.1) 50%, transparent 100%);
background-size: 200% 100%;
animation: shimmer 2s infinite;
}
/* Progress bar */
.progress-track {
background: rgba(255, 255, 255, 0.05);
}
.progress-fill {
background: linear-gradient(90deg, #8b5cf6, #a78bfa);
transition: width 0.3s ease;
box-shadow: 0 0 10px rgba(139, 92, 246, 0.4);
}
/* Video container */
.video-container {
position: relative;
overflow: hidden;
}
.video-container::before {
content: '';
position: absolute;
inset: 0;
border: 1px solid rgba(139, 92, 246, 0.2);
border-radius: inherit;
pointer-events: none;
z-index: 10;
}
/* Scrollbar */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
}
::-webkit-scrollbar-thumb {
background: rgba(139, 92, 246, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(139, 92, 246, 0.5);
}
/* Loading dots */
@keyframes loading-dot {
0%, 80%, 100% { transform: scale(0); opacity: 0.5; }
40% { transform: scale(1); opacity: 1; }
}
.loading-dot {
animation: loading-dot 1.4s infinite ease-in-out both;
}
.loading-dot:nth-child(1) { animation-delay: -0.32s; }
.loading-dot:nth-child(2) { animation-delay: -0.16s; }
/* Particle canvas */
#particleCanvas {
position: fixed;
inset: 0;
z-index: -1;
pointer-events: none;
}
/* Result video styling */
video {
max-width: 100%;
border-radius: 12px;
}
/* Modal */
.modal-overlay {
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
.modal-overlay.active {
opacity: 1;
visibility: visible;
}
.modal-content {
transform: scale(0.95) translateY(10px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.modal-overlay.active .modal-content {
transform: scale(1) translateY(0);
}
/* Toast */
.toast {
transform: translateX(120%);
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.toast.show {
transform: translateX(0);
}
</style>
</head>
<body class="antialiased">
<!-- Ambient Background -->
<div class="ambient-bg"></div>
<canvas id="particleCanvas"></canvas>
<!-- Toast Container -->
<div id="toastContainer" class="fixed top-24 right-6 z-[100] flex flex-col gap-3 pointer-events-none"></div>
<!-- Header -->
<header class="sticky top-0 z-50 glass-panel border-b border-white/5">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between">
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-xl bg-gradient-to-br from-accent-500 to-accent-600 flex items-center justify-center shadow-lg shadow-accent-600/20">
<i class="ph ph-film-strip text-white text-lg"></i>
</div>
<span class="text-xl font-bold tracking-tight bg-gradient-to-r from-white to-white/60 bg-clip-text text-transparent">Animai</span>
</div>
<nav class="hidden md:flex items-center gap-1">
<a href="#" class="px-3 py-1.5 rounded-lg text-sm text-white/60 hover:text-white hover:bg-white/5 transition-all">Gallery</a>
<a href="#" class="px-3 py-1.5 rounded-lg text-sm text-white/60 hover:text-white hover:bg-white/5 transition-all">API</a>
<a href="#" class="px-3 py-1.5 rounded-lg text-sm text-white/60 hover:text-white hover:bg-white/5 transition-all">Docs</a>
<div class="w-px h-5 bg-white/10 mx-2"></div>
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="px-3 py-1.5 rounded-lg text-sm font-medium text-accent-400 hover:text-accent-300 hover:bg-accent-500/10 transition-all flex items-center gap-1.5">
<i class="ph ph-code text-xs"></i>
Built with anycoder
</a>
</nav>
<div class="flex items-center gap-3">
<button id="historyBtn" class="p-2 rounded-lg hover:bg-white/5 transition-colors relative">
<i class="ph ph-clock-counter-clockwise text-white/60 text-lg"></i>
<span id="historyBadge" class="absolute -top-0.5 -right-0.5 w-4 h-4 bg-accent-500 rounded-full text-[10px] font-bold flex items-center justify-center text-white opacity-0 transition-opacity">0</span>
</button>
<button class="hidden sm:flex items-center gap-2 px-4 py-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 transition-all text-sm font-medium">
<i class="ph ph-user-circle text-lg"></i>
Sign In
</button>
<button class="md:hidden p-2 rounded-lg hover:bg-white/5 transition-colors">
<i class="ph ph-list text-white/60 text-xl"></i>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8 lg:py-12">
<!-- Hero -->
<div class="text-center mb-10 lg:mb-14">
<div class="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-accent-500/10 border border-accent-500/20 text-accent-400 text-xs font-medium mb-4">
<span class="w-1.5 h-1.5 rounded-full bg-accent-400 animate-pulse"></span>
AI-Powered Image to Video
</div>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight mb-4 leading-tight">
Bring your images <br class="hidden sm:block">
<span class="bg-gradient-to-r from-accent-400 via-purple-300 to-cyan-300 bg-clip-text text-transparent">to life with motion</span>
</h1>
<p class="text-white/50 text-lg max-w-2xl mx-auto leading-relaxed">
Transform static images into stunning cinematic videos using state-of-the-art diffusion models. Describe the motion, adjust the mood, and watch the magic happen.
</p>
</div>
<div class="grid lg:grid-cols-5 gap-6 lg:gap-8">
<!-- Left Panel - Controls -->
<aside class="lg:col-span-2 space-y-5">
<!-- Upload -->
<div class="glass-panel rounded-2xl p-5">
<label class="block text-sm font-semibold text-white/90 mb-3 flex items-center gap-2">
<i class="ph ph-image text-accent-400"></i>
Source Image
</label>
<div id="uploadZone" class="upload-zone rounded-xl p-8 text-center cursor-pointer relative group">
<input type="file" id="imageInput" accept="image/*" class="absolute inset-0 w-full h-full opacity-0 cursor-pointer z-10">
<div id="uploadPlaceholder" class="transition-all duration-300">
<div class="w-14 h-14 rounded-2xl bg-accent-500/10 flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform">
<i class="ph ph-upload-simple text-accent-400 text-2xl"></i>
</div>
<p class="text-white/80 font-medium text-sm mb-1">Drop an image or click to browse</p>
<p class="text-white/30 text-xs">JPG, PNG, WebP up to 10MB</p>
</div>
<div id="uploadPreview" class="hidden relative">
<img id="previewImg" src="" alt="Preview" class="w-full max-h-48 object-contain rounded-lg">
<button id="removeImage" class="absolute -top-2 -right-2 w-7 h-7 bg-red-500/90 hover:bg-red-500 rounded-full flex items-center justify-center transition-colors z-20 shadow-lg">
<i class="ph ph-x text-white text-xs"></i>
</button>
</div>
</div>
</div>
<!-- Prompt -->
<div class="glass-panel rounded-2xl p-5">
<label class="block text-sm font-semibold text-white/90 mb-3 flex items-center gap-2">
<i class="ph ph-text-t text-accent-400"></i>
Motion Prompt
</label>
<textarea id="promptInput" rows="3" class="input-field w-full rounded-xl px-4 py-3 text-sm text-white/90 placeholder-white/25 resize-none" placeholder="Describe the movement and atmosphere... e.g., 'Slow cinematic pan to the right with gentle snowfall and warm golden hour lighting'"></textarea>
<div class="mt-3 flex flex-wrap gap-2">
<button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Slow cinematic zoom in with dramatic lighting">Cinematic Zoom</button>
<button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Gentle parallax pan from left to right with depth of field">Parallax Pan</button>
<button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Dreamy floating particles with soft bokeh and ethereal glow">Dreamy Bokeh</button>
<button class="tag-pill px-3 py-1 rounded-full border border-white/10 bg-white/5 text-xs text-white/60" data-prompt="Fast motion timelapse with clouds rushing overhead">Timelapse</button>
</div>
</div>
<!-- Settings -->
<div class="glass-panel rounded-2xl p-5">
<label class="block text-sm font-semibold text-white/90 mb-4 flex items-center gap-2">
<i class="ph ph-sliders-horizontal text-accent-400"></i>
Motion Settings
</label>
<div class="space-y-5">
<div>
<div class="flex justify-between mb-2">
<span class="text-xs text-white/50">Motion Intensity</span>
<span id="intensityVal" class="text-xs text-accent-400 font-mono">65%</span>
</div>
<input type="range" id="intensitySlider" min="0" max="100" value="65">
</div>
<div>
<div class="flex justify-between mb-2">
<span class="text-xs text-white/50">Duration</span>
<span id="durationVal" class="text-xs text-accent-400 font-mono">4s</span>
</div>
<input type="range" id="durationSlider" min="2" max="10" value="4" step="0.5">
</div>
<div>
<div class="flex justify-between mb-2">
<span class="text-xs text-white/50">Frame Rate</span>
<span id="fpsVal" class="text-xs text-accent-400 font-mono">30fps</span>
</div>
<input type="range" id="fpsSlider" min="15" max="60" value="30" step="15">
</div>
</div>
<div class="mt-5 pt-4 border-t border-white/5 grid grid-cols-2 gap-3">
<label class="flex items-center gap-2 cursor-pointer group">
<div class="relative">
<input type="checkbox" id="enhanceToggle" class="peer sr-only" checked>
<div class="w-9 h-5 rounded-full bg-white/10 peer-checked:bg-accent-600 transition-colors"></div>
<div class="absolute left-0.5 top-0.5 w-4 h-4 rounded-full bg-white transition-transform peer-checked:translate-x-4"></div>
</div>
<span class="text-xs text-white/50 group-hover:text-white/70 transition-colors">AI Enhance</span>
</label>
<label class="flex items-center gap-2 cursor-pointer group">
<div class="relative">
<input type="checkbox" id="loopToggle" class="peer sr-only">
<div class="w-9 h-5 rounded-full bg-white/10 peer-checked:bg-accent-600 transition-colors"></div>
<div class="absolute left-0.5 top-0.5 w-4 h-4 rounded-full bg-white transition-transform peer-checked:translate-x-4"></div>
</div>
<span class="text-xs text-white/50 group-hover:text-white/70 transition-colors">Seamless Loop</span>
</label>
</div>
</div>
<!-- Generate Button -->
<button id="generateBtn" class="btn-primary w-full py-4 rounded-xl font-semibold text-white shadow-lg shadow-accent-600/20 flex items-center justify-center gap-2 text-sm tracking-wide disabled:shadow-none">
<i class="ph ph-magic-wand text-lg"></i>
<span id="generateText">Generate Video</span>
</button>
</aside>
<!-- Right Panel - Preview -->
<section class="lg:col-span-3 space-y-5">
<!-- Preview Area -->
<div class="glass-panel rounded-2xl p-1 overflow-hidden">
<div class="bg-surface-900/80 rounded-xl overflow-hidden min-h-[400px] lg:min-h-[500px] flex flex-col relative">
<!-- Empty State -->
<div id="emptyState" class="flex-1 flex flex-col items-center justify-center p-10 text-center">
<div class="w-24 h-24 rounded-3xl bg-white/5 border border-white/5 flex items-center justify-center mb-5 animate-float">
<i class="ph ph-video-camera text-white/20 text-4xl"></i>
</div>
<h3 class="text-white/40 font-semibold text-lg mb-1">Ready to create</h3>
<p class="text-white/25 text-sm max-w-xs">Upload an image and describe the motion to generate your first AI video</p>
</div>
<!-- Processing State -->
<div id="processingState" class="hidden flex-1 flex flex-col items-center justify-center p-10">
<div class="relative w-28 h-28 mb-6">
<svg class="w-full h-full -rotate-90" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="44" fill="none" stroke="rgba(255,255,255,0.05)" stroke-width="6"/>
<circle id="progressCircle" cx="50" cy="50" r="44" fill="none" stroke="url(#grad1)" stroke-width="6" stroke-linecap="round" stroke-dasharray="276.46" stroke-dashoffset="276.46" class="transition-all duration-300"/>
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#8b5cf6;stop-opacity:1" />
<stop offset="100%" style="stop-color:#a78bfa;stop-opacity:1" />
</linearGradient>
</defs>
</svg>
<div class="absolute inset-0 flex items-center justify-center">
<span id="progressPercent" class="text-2xl font-bold text-white/80">0%</span>
</div>
</div>
<h3 id="processStatus" class="text-white/60 font-medium mb-2">Analyzing image...</h3>
<p id="processSubstatus" class="text-white/30 text-sm">Preparing diffusion model</p>
<!-- Progress log -->
<div id="progressLog" class="mt-6 w-full max-w-xs space-y-1.5">
<div class="progress-track rounded-full h-1.5 overflow-hidden">
<div id="progressBar" class="progress-fill h-full rounded-full" style="width: 0%"></div>
</div>
</div>
</div>
<!-- Result State -->
<div id="resultState" class="hidden flex-1 flex flex-col p-4">
<div class="flex items-center justify-between mb-3 px-1">
<div class="flex items-center gap-2">
<div class="w-2 h-2 rounded-full bg-green-400 animate-pulse"></div>
<span class="text-xs text-white/50 font-mono">Generation Complete</span>
</div>
<div class="flex items-center gap-2">
<button id="downloadBtn" class="p-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 transition-all" title="Download Video">
<i class="ph ph-download-simple text-white/60"></i>
</button>
<button id="shareBtn" class="p-2 rounded-lg bg-white/5 hover:bg-white/10 border border-white/10 transition-all" title="Share">
<i class="ph ph-share-network text-white/60"></i>
</button>
</div>
</div>
<div class="flex-1 video-container rounded-xl bg-black flex items-center justify-center overflow-hidden">
<video id="resultVideo" controls class="max-w-full max-h-full" poster=""></video>
</div>
<div class="mt-3 flex items-center gap-3 px-1">
<div class="flex-1">
<p id="resultPrompt" class="text-xs text-white/40 truncate"></p>
</div>
<div class="flex items-center gap-1.5 text-xs text-white/30">
<i class="ph ph-clock"></i>
<span id="resultDuration">4s</span>
</div>
</div>
</div>
</div>
</div>
<!-- History Grid -->
<div id="historySection" class="hidden">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-semibold text-white/70 flex items-center gap-2">
<i class="ph ph-clock-counter-clockwise"></i>
Recent Generations
</h3>
<button id="clearHistory" class="text-xs text-white/30 hover:text-white/60 transition-colors">Clear All</button>
</div>
<div id="historyGrid" class="grid grid-cols-2 sm:grid-cols-3 gap-3">
<!-- History items injected here -->
</div>
</div>
</section>
</div>
</main>
<!-- Footer -->
<footer class="border-t border-white/5 mt-16 py-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 flex flex-col sm:flex-row items-center justify-between gap-4">
<p class="text-white/25 text-xs">Animai is a demonstration of image-to-video synthesis interfaces.</p>
<div class="flex items-center gap-4">
<a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="text-xs text-accent-400/60 hover:text-accent-400 transition-colors">Built with anycoder</a>
<a href="#" class="text-xs text-white/25 hover:text-white/50 transition-colors">Privacy</a>
<a href="#" class="text-xs text-white/25 hover:text-white/50 transition-colors">Terms</a>
</div>
</div>
</footer>
<!-- Share Modal -->
<div id="shareModal" class="modal-overlay fixed inset-0 z-[200] flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm">
<div class="modal-content glass-panel rounded-2xl p-6 max-w-sm w-full">
<div class="flex items-center justify-between mb-4">
<h3 class="font-semibold text-white/90">Share Video</h3>
<button id="closeModal" class="p-1 rounded-lg hover:bg-white/5 transition-colors">
<i class="ph ph-x text-white/50"></i>
</button>
</div>
<div class="space-y-3">
<button class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 transition-all text-left">
<div class="w-9 h-9 rounded-lg bg-[#1DA1F2]/20 flex items-center justify-center">
<i class="ph ph-twitter-logo text-[#1DA1F2]"></i>
</div>
<div>
<p class="text-sm text-white/80 font-medium">Twitter</p>
<p class="text-xs text-white/30">Share to your timeline</p>
</div>
</button>
<button class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 transition-all text-left">
<div class="w-9 h-9 rounded-lg bg-[#5865F2]/20 flex items-center justify-center">
<i class="ph ph-discord-logo text-[#5865F2]"></i>
</div>
<div>
<p class="text-sm text-white/80 font-medium">Discord</p>
<p class="text-xs text-white/30">Share in a channel</p>
</div>
</button>
<button id="copyLinkBtn" class="w-full flex items-center gap-3 p-3 rounded-xl bg-white/5 hover:bg-white/10 border border-white/5 transition-all text-left">
<div class="w-9 h-9 rounded-lg bg-accent-500/20 flex items-center justify-center">
<i class="ph ph-link text-accent-400"></i>
</div>
<div>
<p class="text-sm text-white/80 font-medium">Copy Link</p>
<p class="text-xs text-white/30">Copy to clipboard</p>
</div>
</button>
</div>
</div>
</div>
<script>
// ==================== STATE ====================
const state = {
image: null,
prompt: '',
intensity: 65,
duration: 4,
fps: 30,
enhance: true,
loop: false,
isGenerating: false,
history: []
};
// ==================== DOM ====================
const els = {
imageInput: document.getElementById('imageInput'),
uploadZone: document.getElementById('uploadZone'),
uploadPlaceholder: document.getElementById('uploadPlaceholder'),
uploadPreview: document.getElementById('uploadPreview'),
previewImg: document.getElementById('previewImg'),
removeImage: document.getElementById('removeImage'),
promptInput: document.getElementById('promptInput'),
intensitySlider: document.getElementById('intensitySlider'),
intensityVal: document.getElementById('intensityVal'),
durationSlider: document.getElementById('durationSlider'),
durationVal: document.getElementById('durationVal'),
fpsSlider: document.getElementById('fpsSlider'),
fpsVal: document.getElementById('fpsVal'),
enhanceToggle: document.getElementById('enhanceToggle'),
loopToggle: document.getElementById('loopToggle'),
generateBtn: document.getElementById('generateBtn'),
generateText: document.getElementById('generateText'),
emptyState: document.getElementById('emptyState'),
processingState: document.getElementById('processingState'),
resultState: document.getElementById('resultState'),
progressCircle: document.getElementById('progressCircle'),
progressPercent: document.getElementById('progressPercent'),
progressBar: document.getElementById('progressBar'),
processStatus: document.getElementById('processStatus'),
processSubstatus: document.getElementById('processSubstatus'),
resultVideo: document.getElementById('resultVideo'),
resultPrompt: document.getElementById('resultPrompt'),
resultDuration: document.getElementById('resultDuration'),
downloadBtn: document.getElementById('downloadBtn'),
shareBtn: document.getElementById('shareBtn'),
historySection: document.getElementById('historySection'),
historyGrid: document.getElementById('historyGrid'),
historyBadge: document.getElementById('historyBadge'),
clearHistory: document.getElementById('clearHistory'),
shareModal: document.getElementById('shareModal'),
closeModal: document.getElementById('closeModal'),
copyLinkBtn: document.getElementById('copyLinkBtn'),
toastContainer: document.getElementById('toastContainer'),
tagPills: document.querySelectorAll('.tag-pill')
};
// ==================== UTILS ====================
function showToast(message, type = 'info') {
const toast = document.createElement('div');
const colors = {
info: 'border-accent-500/30 bg-s