Files
Viz/public/index.html
T

988 lines
47 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Orfel Viz</title>
<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=Cinzel:wght@700&family=Inter:wght@300;400;500;600;700;800&family=Montserrat:wght@400;600;800&family=Orbitron:wght@600;800;900&family=Outfit:wght@400;600;700;800&family=Syncopate:wght@700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="app-layout">
<!-- Header -->
<header class="app-header">
<div class="brand">
<div class="brand-logo">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
<polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
<polyline points="2 17 12 22 22 17"></polyline>
<polyline points="2 12 12 17 22 12"></polyline>
</svg>
</div>
<div class="brand-text">
<h1>Orfel Viz</h1>
</div>
</div>
<div class="header-actions">
<!-- Quick Presets -->
<div class="quick-preset-picker">
<label for="quickPresetSelect">Preset:</label>
<select id="quickPresetSelect">
<option value="trap_nation">🟣 Trap Nation</option>
<option value="ncs_electro">🟡 NCS Electro</option>
<option value="synthwave_horizon">🌅 Synthwave Horizon</option>
<option value="laser_oscilloscope">〰️ Laser Oscilloscope</option>
<option value="diamond_bass">💎 Diamond Bass</option>
<option value="cosmic_wormhole">🌀 Cosmic Wormhole</option>
<option value="studio_rack_eq">📊 Studio Graphic EQ</option>
<option value="minimal_ice_wave">❄️ Minimal Ice Wave</option>
<option value="toxic_emerald">🔺 Toxic Emerald</option>
<option value="solar_flare">🛑 Solar Flare</option>
<option value="cyberpunk_city">🏙️ Cyberpunk City</option>
<option value="monochrome_cinema">🎬 Monochrome Cinema</option>
<option value="retro_cassette">📼 Retro Cassette</option>
<option value="deep_space_nebula">🌌 Deep Space Nebula</option>
<option value="glitch_overdrive">⚡ Glitch Overdrive</option>
<option value="liquid_ambient">💧 Liquid Ambient</option>
</select>
</div>
<button id="headerExportBtn" class="btn btn-primary btn-glow">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
Exportieren
</button>
</div>
</header>
<!-- Main Workspace -->
<main class="workspace">
<!-- Left: Stage & Player -->
<section class="stage-section">
<!-- Aspect & Tools Bar -->
<div class="stage-toolbar">
<div class="aspect-switchers">
<button class="aspect-btn active" data-aspect="16:9" title="16:9 YouTube Standard (1920x1080 / 4K)">
<span class="icon">📺</span> 16:9
</button>
<button class="aspect-btn" data-aspect="9:16" title="9:16 Shorts, TikTok & Reels (1080x1920)">
<span class="icon">📱</span> 9:16
</button>
<button class="aspect-btn" data-aspect="1:1" title="1:1 Quadratisch (Instagram 1080x1080)">
<span class="icon">🔲</span> 1:1
</button>
</div>
<div class="stage-toggles">
<label class="toggle-chip" title="Zeigt TikTok & Shorts UI-Überlagerung für exaktes Platzieren">
<input type="checkbox" id="toggleSafeZone">
<span>Safe-Zone</span>
</label>
<label class="toggle-chip" title="Transparenz-Gitter im Hintergrund ein-/ausblenden">
<input type="checkbox" id="toggleAlphaGrid" checked>
<span>Alpha-Gitter</span>
</label>
</div>
</div>
<!-- Canvas Viewport -->
<div class="canvas-viewport" id="canvasViewport">
<div class="canvas-wrapper aspect-16-9" id="canvasWrapper">
<canvas id="vizCanvas" width="1920" height="1080"></canvas>
<!-- Safe Zone Overlay (Visual helper for Shorts UI) -->
<div class="safe-zone-overlay hidden" id="safeZoneOverlay">
<div class="safe-zone-box">
<div class="safe-zone-title">Shorts / TikTok Safe Area</div>
<div class="mock-right-buttons">
<div class="mock-btn">❤️</div>
<div class="mock-btn">💬</div>
<div class="mock-btn">↗️</div>
<div class="mock-disc">💿</div>
</div>
<div class="mock-bottom-bar">
<div class="mock-channel">@DeinChannel</div>
<div class="mock-song">🎵 Song Title • Original Audio</div>
</div>
</div>
</div>
</div>
</div>
<!-- Audio Player Controls -->
<div class="player-bar">
<div class="player-main-controls">
<button id="btnPlayPause" class="player-btn-play" title="Play / Pause (Leertaste)">
<svg id="iconPlay" viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>
<svg id="iconPause" class="hidden" viewBox="0 0 24 24" width="22" height="22" fill="currentColor"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>
</button>
<button id="btnRewind" class="player-btn-sub" title="-5 Sekunden">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polyline points="11 17 6 12 11 7"></polyline><polyline points="18 17 13 12 18 7"></polyline></svg>
</button>
<button id="btnForward" class="player-btn-sub" title="+5 Sekunden">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polyline points="13 17 18 12 13 7"></polyline><polyline points="6 17 11 12 6 7"></polyline></svg>
</button>
<div class="time-display">
<span id="currentTime">00:00</span> / <span id="durationTime">00:00</span>
</div>
</div>
<!-- Waveform / Timeline Scrubber -->
<div class="timeline-container">
<input type="range" id="seekScrubber" min="0" max="100" value="0" step="0.1" class="timeline-slider">
<div class="timeline-progress-bar" id="timelineProgressBar"></div>
</div>
<div class="player-extra-controls">
<button id="btnLoop" class="player-btn-sub active" title="Audio-Schleife an/aus">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polyline points="17 1 21 5 17 9"></polyline><path d="M3 11V9a4 4 0 0 1 4-4h14"></path><polyline points="7 23 3 19 7 15"></polyline><path d="M21 13v2a4 4 0 0 1-4 4H3"></path></svg>
</button>
<div class="volume-control">
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg>
<input type="range" id="volumeSlider" min="0" max="1" step="0.05" value="0.9" class="volume-slider">
</div>
</div>
</div>
</section>
<!-- Right: Inspector & Parameter Controls -->
<aside class="controls-sidebar">
<!-- Navigation Tabs -->
<nav class="sidebar-tabs">
<button class="tab-btn active" data-tab="tab-media">📁 Media</button>
<button class="tab-btn" data-tab="tab-shape">📐 Form</button>
<button class="tab-btn" data-tab="tab-spectrum">📊 Spektrum</button>
<button class="tab-btn" data-tab="tab-fx">✨ FX & 3D</button>
<button class="tab-btn" data-tab="tab-colors">🎨 Farben</button>
<button class="tab-btn" data-tab="tab-text">📝 Text</button>
<button class="tab-btn" data-tab="tab-presets">⚡ Presets</button>
<button class="tab-btn" data-tab="tab-export">🎬 Export</button>
</nav>
<div class="sidebar-content">
<!-- TAB 1: MEDIA -->
<div class="tab-panel active" id="tab-media">
<div class="panel-section">
<h3 class="section-title">1. Audio-Datei (Song)</h3>
<div class="dropzone" id="audioDropzone">
<input type="file" id="audioFileInput" accept="audio/*,.wav,.mp3,.flac,.m4a,.ogg" class="file-input-hidden">
<div class="dropzone-content">
<svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>
<p class="dropzone-text"><strong>WAV / MP3 / FLAC</strong> hierher ziehen</p>
<span class="dropzone-sub">oder klicken zum Auswählen (bis 200 MB)</span>
<div class="file-badge hidden" id="audioFileBadge">Kein Song geladen</div>
</div>
</div>
</div>
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">2. Cover-Art / Logo</h3>
<label class="switch-toggle" title="Cover-Artwork komplett deaktivieren (für reine Wellenformen/No-Cover Visuals)">
<input type="checkbox" id="showCover" checked>
<span class="switch-slider"></span>
</label>
</div>
<div id="coverUploadContainer">
<div class="dropzone" id="imageDropzone">
<input type="file" id="imageFileInput" accept="image/*,.png,.jpg,.jpeg,.webp" class="file-input-hidden">
<div class="dropzone-content">
<svg viewBox="0 0 24 24" width="32" height="32" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg>
<p class="dropzone-text"><strong>Cover-Bild / Logo</strong> hierher ziehen</p>
<span class="dropzone-sub">PNG / JPG (wird zentriert maskiert)</span>
<div class="file-badge hidden" id="imageFileBadge">Standard-Cover</div>
</div>
</div>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">3. Track-Metadaten</h3>
<div class="form-group">
<label for="trackTitle">Song-Titel</label>
<input type="text" id="trackTitle" placeholder="z. B. Du bist mein Käse" value="Dein Songtitel">
</div>
<div class="form-group">
<label for="trackArtist">Interpret / Artist</label>
<input type="text" id="trackArtist" placeholder="z. B. Jannik" value="Artist Name">
</div>
</div>
</div>
<!-- TAB 2: FORM & TOPOLOGIE -->
<div class="tab-panel" id="tab-shape">
<div class="panel-section">
<h3 class="section-title">Visualizer Form & Topologie</h3>
<div class="shape-grid-picker">
<label class="shape-card">
<input type="radio" name="shapeType" value="circle" checked>
<div class="shape-card-inner">
<span class="shape-icon">⭕</span>
<span class="shape-label">Kreis</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="linear">
<div class="shape-card-inner">
<span class="shape-icon">➖</span>
<span class="shape-label">Linear</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="hexagon">
<div class="shape-card-inner">
<span class="shape-icon">⬡</span>
<span class="shape-label">Hexagon</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="diamond">
<div class="shape-card-inner">
<span class="shape-icon">💎</span>
<span class="shape-label">Diamond</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="triangle">
<div class="shape-card-inner">
<span class="shape-icon">🔺</span>
<span class="shape-label">Dreieck</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="octagon">
<div class="shape-card-inner">
<span class="shape-icon">🛑</span>
<span class="shape-label">Achteck</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="oscilloscope">
<div class="shape-card-inner">
<span class="shape-icon">〰️</span>
<span class="shape-label">Oszilloskop</span>
</div>
</label>
<label class="shape-card">
<input type="radio" name="shapeType" value="tunnel">
<div class="shape-card-inner">
<span class="shape-icon">🌀</span>
<span class="shape-label">3D Tunnel</span>
</div>
</label>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Größe & Bass-Pulse</h3>
<div class="control-row">
<label for="circleRadius">Größe / Radius</label>
<div class="range-group">
<input type="range" id="circleRadius" min="40" max="350" value="150" step="1">
<span class="range-value" id="valCircleRadius">150px</span>
</div>
</div>
<div class="control-row">
<label for="pulseIntensity">Bass-Pulse Stärke</label>
<div class="range-group">
<input type="range" id="pulseIntensity" min="0" max="1" value="0.45" step="0.01">
<span class="range-value" id="valPulseIntensity">45%</span>
</div>
</div>
<div class="control-row">
<label for="pulseDecay">Pulse Glättung (Decay)</label>
<div class="range-group">
<input type="range" id="pulseDecay" min="0.1" max="0.95" value="0.8" step="0.01">
<span class="range-value" id="valPulseDecay">80%</span>
</div>
</div>
</div>
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">Rand & Rahmen</h3>
<label class="switch-toggle">
<input type="checkbox" id="showBorder" checked>
<span class="switch-slider"></span>
</label>
</div>
<div id="borderOptionsContainer">
<div class="control-row">
<label for="borderWidth">Rand-Dicke</label>
<div class="range-group">
<input type="range" id="borderWidth" min="1" max="30" value="6" step="1">
<span class="range-value" id="valBorderWidth">6px</span>
</div>
</div>
<div class="control-row">
<label for="borderColor">Rand-Farbe</label>
<input type="color" id="borderColor" value="#ffffff" class="color-picker-input">
</div>
</div>
</div>
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">Fortschritts-Ring (0-360°)</h3>
<label class="switch-toggle">
<input type="checkbox" id="showProgressRing" checked>
<span class="switch-slider"></span>
</label>
</div>
<div id="progressRingOptionsContainer">
<div class="control-row">
<label for="progressRingColor">Ring-Farbe</label>
<input type="color" id="progressRingColor" value="#00f0ff" class="color-picker-input">
</div>
<div class="control-row">
<label for="progressRingWidth">Ring-Dicke</label>
<div class="range-group">
<input type="range" id="progressRingWidth" min="2" max="20" value="6" step="1">
<span class="range-value" id="valProgressRingWidth">6px</span>
</div>
</div>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Rotation & Drehung</h3>
<div class="control-row">
<label for="rotationSpeed">Vinyl-Drehung (Speed)</label>
<div class="range-group">
<input type="range" id="rotationSpeed" min="0" max="20" value="3" step="0.5">
<span class="range-value" id="valRotationSpeed">3 RPM</span>
</div>
</div>
<div class="toggle-row">
<label for="beatSpinImpulse">Beat-Drehimpuls bei Drops</label>
<input type="checkbox" id="beatSpinImpulse" checked>
</div>
</div>
</div>
<!-- TAB 3: SPECTRUM -->
<div class="tab-panel" id="tab-spectrum">
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">Audio-Spektrum</h3>
<label class="switch-toggle" title="Spektrum ein-/ausschalten">
<input type="checkbox" id="showSpectrum" checked>
<span class="switch-slider"></span>
</label>
</div>
<div class="radio-pill-group">
<label class="radio-pill">
<input type="radio" name="spectrumMode" value="radial_bars" checked>
<span>📊 Balken</span>
</label>
<label class="radio-pill">
<input type="radio" name="spectrumMode" value="smooth_wave">
<span>〰️ Welle</span>
</label>
<label class="radio-pill">
<input type="radio" name="spectrumMode" value="floating_dots">
<span>✨ Punkte</span>
</label>
<label class="radio-pill">
<input type="radio" name="spectrumMode" value="dual_ring">
<span>⭕ Dual Ring</span>
</label>
<label class="radio-pill">
<input type="radio" name="spectrumMode" value="block_matrix">
<span>🧱 Block EQ</span>
</label>
</div>
</div>
<!-- Linear-spezifische Ausrichtung (sichtbar wenn Linear gewählt) -->
<div class="panel-section" id="linearAlignmentSection">
<h3 class="section-title">Lineare Horizont-Ausrichtung</h3>
<select id="linearAlignment" class="select-input">
<option value="center_mirrored">Symmetrischer Horizont (Oben & Unten)</option>
<option value="bottom_up">Boden-Equalizer (Von Unten Aufsteigend)</option>
<option value="top_down">Decken-Equalizer (Von Oben Herabfallend)</option>
<option value="floating_wave">Frei schwebende Center-Welle</option>
</select>
</div>
<div class="panel-section">
<h3 class="section-title">Geometrie & Dichte</h3>
<div class="control-row">
<label for="barCount">Anzahl Balken / Punkte</label>
<div class="range-group">
<input type="range" id="barCount" min="24" max="256" value="96" step="8">
<span class="range-value" id="valBarCount">96</span>
</div>
</div>
<div class="control-row">
<label for="spectrumMaxHeight">Maximale Höhe</label>
<div class="range-group">
<input type="range" id="spectrumMaxHeight" min="20" max="400" value="130" step="5">
<span class="range-value" id="valSpectrumMaxHeight">130px</span>
</div>
</div>
<div class="control-row">
<label for="barWidth">Balkenbreite</label>
<div class="range-group">
<input type="range" id="barWidth" min="1" max="25" value="4" step="0.5">
<span class="range-value" id="valBarWidth">4px</span>
</div>
</div>
<div class="control-row">
<label for="barSpacing">Abstand vom Zentrum</label>
<div class="range-group">
<input type="range" id="barSpacing" min="0" max="100" value="12" step="1">
<span class="range-value" id="valBarSpacing">12px</span>
</div>
</div>
<div class="control-row">
<label for="barCornerRadius">Ecken-Abrundung</label>
<div class="range-group">
<input type="range" id="barCornerRadius" min="0" max="10" value="2" step="0.5">
<span class="range-value" id="valBarCornerRadius">2px</span>
</div>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Frequenz-Analyse</h3>
<div class="control-row">
<label for="freqSensitivity">Empfindlichkeit</label>
<div class="range-group">
<input type="range" id="freqSensitivity" min="0.5" max="3.5" value="1.3" step="0.1">
<span class="range-value" id="valFreqSensitivity">1.3x</span>
</div>
</div>
<div class="control-row">
<label for="freqFocus">Frequenz-Fokus</label>
<select id="freqFocus" class="select-input">
<option value="balanced">Ausgewogen (Sub bis Höhen)</option>
<option value="bass_heavy">Basslastig (EDM / Trap)</option>
<option value="vocal_mid">Mitten & Gesang (Pop / Rock)</option>
</select>
</div>
<div class="toggle-row">
<label for="mirrorSpectrum">Spektrum spiegeln (Symmetrisch)</label>
<input type="checkbox" id="mirrorSpectrum" checked>
</div>
</div>
</div>
<!-- TAB 4: FX & 3D -->
<div class="tab-panel" id="tab-fx">
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">Partikel-System</h3>
<label class="switch-toggle">
<input type="checkbox" id="enableParticles" checked>
<span class="switch-slider"></span>
</label>
</div>
<div id="particleOptionsContainer">
<div class="control-row">
<label for="particleType">Partikel-Typ</label>
<select id="particleType" class="select-input">
<option value="starfield">3D Warp Starfield (Kameraflug)</option>
<option value="dust">Schwebender Staub (Floating Dust)</option>
<option value="sparks">Audio-Funken (Reactive Sparks)</option>
</select>
</div>
<div class="control-row">
<label for="particleCount">Partikel-Anzahl</label>
<div class="range-group">
<input type="range" id="particleCount" min="20" max="300" value="100" step="10">
<span class="range-value" id="valParticleCount">100</span>
</div>
</div>
<div class="control-row">
<label for="particleSpeed">Geschwindigkeit</label>
<div class="range-group">
<input type="range" id="particleSpeed" min="0.2" max="4.0" value="1.0" step="0.1">
<span class="range-value" id="valParticleSpeed">1.0x</span>
</div>
</div>
<div class="control-row">
<label for="particleWarp">Warp-Explosion bei Drops</label>
<div class="range-group">
<input type="range" id="particleWarp" min="0" max="6.0" value="2.5" step="0.2">
<span class="range-value" id="valParticleWarp">2.5x</span>
</div>
</div>
</div>
</div>
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">3D Synthwave Grid Floor</h3>
<label class="switch-toggle" title="Retro-Perspektiven-Gitterboden aktivieren">
<input type="checkbox" id="enableGrid">
<span class="switch-slider"></span>
</label>
</div>
<div id="gridOptionsContainer">
<div class="control-row">
<label for="colorGrid">Grid-Farbe</label>
<input type="color" id="colorGrid" value="#ff007f" class="color-picker-input">
</div>
<div class="control-row">
<label for="gridPerspective">Gitter-Perspektive (Horizont)</label>
<div class="range-group">
<input type="range" id="gridPerspective" min="0.3" max="0.9" value="0.65" step="0.05">
<span class="range-value" id="valGridPerspective">65%</span>
</div>
</div>
<div class="control-row">
<label for="gridAudioReactive">Audio-Reaktivität</label>
<div class="range-group">
<input type="range" id="gridAudioReactive" min="0" max="1.0" value="0.5" step="0.05">
<span class="range-value" id="valGridAudioReactive">50%</span>
</div>
</div>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Kamera & Beat-Effekte</h3>
<div class="toggle-row">
<label for="enableShake">Bass-Shake (Kamera-Erschütterung)</label>
<input type="checkbox" id="enableShake" checked>
</div>
<div class="control-row">
<label for="camShakeIntensity">Shake-Intensität</label>
<div class="range-group">
<input type="range" id="camShakeIntensity" min="0" max="1.0" value="0.35" step="0.05">
<span class="range-value" id="valCamShakeIntensity">35%</span>
</div>
</div>
<div class="toggle-row">
<label for="enableGlitch">RGB-Split / Glitch bei Drops</label>
<input type="checkbox" id="enableGlitch" checked>
</div>
<div class="control-row">
<label for="rgbSplitIntensity">Glitch-Stärke</label>
<div class="range-group">
<input type="range" id="rgbSplitIntensity" min="0" max="1.0" value="0.25" step="0.05">
<span class="range-value" id="valRgbSplitIntensity">25%</span>
</div>
</div>
<div class="toggle-row">
<label for="beatFlash">Beat-Flash (Strobe-Effekt bei Kicks)</label>
<input type="checkbox" id="beatFlash">
</div>
<div class="toggle-row">
<label for="enableBassZoom">Kamera Bass-Zoom (Pulsierender Viewport)</label>
<input type="checkbox" id="enableBassZoom" checked>
</div>
</div>
</div>
<!-- TAB 5: COLORS & NEON GLOW -->
<div class="tab-panel" id="tab-colors">
<div class="panel-section">
<h3 class="section-title">Kuratierte Farbpaletten</h3>
<div class="palette-quick-grid">
<button class="palette-chip" data-p1="#00f0ff" data-p2="#ff007f" data-p3="#7000ff" title="Cyberpunk Neon">
<span class="palette-preview" style="background: linear-gradient(135deg, #00f0ff, #ff007f, #7000ff);"></span>
<span>Cyberpunk</span>
</button>
<button class="palette-chip" data-p1="#ff7700" data-p2="#ff0055" data-p3="#7900ff" title="Synthwave Sunset">
<span class="palette-preview" style="background: linear-gradient(135deg, #ff7700, #ff0055, #7900ff);"></span>
<span>Synthwave</span>
</button>
<button class="palette-chip" data-p1="#00ff88" data-p2="#00e5ff" data-p3="#0055ff" title="Toxic Emerald">
<span class="palette-preview" style="background: linear-gradient(135deg, #00ff88, #00e5ff, #0055ff);"></span>
<span>Emerald</span>
</button>
<button class="palette-chip" data-p1="#00e1ff" data-p2="#ffffff" data-p3="#0051ff" title="Ice Blade">
<span class="palette-preview" style="background: linear-gradient(135deg, #00e1ff, #ffffff, #0051ff);"></span>
<span>Ice Blade</span>
</button>
<button class="palette-chip" data-p1="#ffcc00" data-p2="#ff3300" data-p3="#880000" title="Fire Amber">
<span class="palette-preview" style="background: linear-gradient(135deg, #ffcc00, #ff3300, #880000);"></span>
<span>Fire Amber</span>
</button>
<button class="palette-chip" data-p1="#bf00ff" data-p2="#e066ff" data-p3="#3b0066" title="Royal Violet">
<span class="palette-preview" style="background: linear-gradient(135deg, #bf00ff, #e066ff, #3b0066);"></span>
<span>Royal Violet</span>
</button>
<button class="palette-chip" data-p1="#ffffff" data-p2="#aaaaaa" data-p3="#555555" title="Monochrome Clean">
<span class="palette-preview" style="background: linear-gradient(135deg, #ffffff, #aaaaaa, #555555);"></span>
<span>Monochrome</span>
</button>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Farbverlaufs-Editor</h3>
<div class="control-row">
<label for="colorMode">Farb-Modus</label>
<select id="colorMode" class="select-input">
<option value="gradient_2">2-Farben Verlauf</option>
<option value="gradient_3">3-Farben Verlauf</option>
<option value="rainbow">Regenbogen / Chroma Beat-Loop</option>
<option value="solid">Einfarbig (Solid)</option>
</select>
</div>
<div class="color-picker-row">
<div class="color-picker-item">
<label for="colorGrad1">Farbe 1</label>
<input type="color" id="colorGrad1" value="#00f0ff" class="color-picker-input">
</div>
<div class="color-picker-item" id="colorGrad2Item">
<label for="colorGrad2">Farbe 2</label>
<input type="color" id="colorGrad2" value="#ff007f" class="color-picker-input">
</div>
<div class="color-picker-item" id="colorGrad3Item">
<label for="colorGrad3">Farbe 3</label>
<input type="color" id="colorGrad3" value="#7000ff" class="color-picker-input">
</div>
<div class="color-picker-item">
<label for="colorParticle">Partikel</label>
<input type="color" id="colorParticle" value="#ffffff" class="color-picker-input">
</div>
</div>
<div class="control-row">
<label for="gradientStyle">Verlauf-Geometrie</label>
<select id="gradientStyle" class="select-input">
<option value="angular">360° Winkelverlauf (Radial Conic)</option>
<option value="radial">Zentrum zu Rand (Linear Radial)</option>
<option value="linear_h">Horizontal (Links nach Rechts)</option>
<option value="linear_v">Vertikal (Oben nach Unten)</option>
</select>
</div>
</div>
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">Neon Bloom & Glow Engine</h3>
<label class="switch-toggle" title="Neon-Glow aktivieren">
<input type="checkbox" id="enableGlow" checked>
<span class="switch-slider"></span>
</label>
</div>
<div id="glowOptionsContainer">
<div class="control-row">
<label for="glowIntensity">Glow-Leuchtkraft</label>
<div class="range-group">
<input type="range" id="glowIntensity" min="0" max="1.0" value="0.75" step="0.05">
<span class="range-value" id="valGlowIntensity">75%</span>
</div>
</div>
<div class="control-row">
<label for="glowBlur">Glow-Radius (Bloom Spread)</label>
<div class="range-group">
<input type="range" id="glowBlur" min="0" max="60" value="25" step="2">
<span class="range-value" id="valGlowBlur">25px</span>
</div>
</div>
<div class="toggle-row">
<label for="laserCore">Doppelter Laser-Kern (Laser Core Line)</label>
<input type="checkbox" id="laserCore" checked>
</div>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Hintergrund-Modus</h3>
<select id="backgroundMode" class="select-input">
<option value="transparent">100% Transparent (Alpha-Kanal für DaVinci Resolve)</option>
<option value="blurred_cover">Unscharfes Cover-Artwork im Hintergrund</option>
<option value="dark_vignette">Dunkle Obsidian-Vignette</option>
<option value="solid_black">Reines Schwarz (#000000)</option>
</select>
</div>
</div>
<!-- TAB 6: TYPOGRAPHY -->
<div class="tab-panel" id="tab-text">
<div class="panel-section">
<div class="section-header-with-toggle">
<h3 class="section-title">Song-Info Overlay</h3>
<label class="switch-toggle" title="Text-Overlay aktivieren">
<input type="checkbox" id="showTextOverlay" checked>
<span class="switch-slider"></span>
</label>
</div>
<div id="textOptionsContainer">
<div class="control-row">
<label for="fontFamily">Schriftart</label>
<select id="fontFamily" class="select-input">
<option value="'Outfit', sans-serif">Outfit (Modern Clean)</option>
<option value="'Orbitron', sans-serif">Orbitron (Cyber / EDM)</option>
<option value="'Syncopate', sans-serif">Syncopate (Futuristic Wide)</option>
<option value="'Montserrat', sans-serif">Montserrat (Bold Studio)</option>
<option value="'Inter', sans-serif">Inter (Minimalist)</option>
<option value="'Cinzel', serif">Cinzel (Cinematic Serif)</option>
</select>
</div>
<div class="control-row">
<label for="textPosition">Position</label>
<select id="textPosition" class="select-input">
<option value="below_circle">Direkt unter der Form</option>
<option value="bottom_center">Unten Zentriert</option>
<option value="top_center">Oben Zentriert</option>
</select>
</div>
<div class="control-row">
<label for="titleFontSize">Titel Schriftgröße</label>
<div class="range-group">
<input type="range" id="titleFontSize" min="16" max="72" value="38" step="2">
<span class="range-value" id="valTitleFontSize">38px</span>
</div>
</div>
<div class="control-row">
<label for="artistFontSize">Artist Schriftgröße</label>
<div class="range-group">
<input type="range" id="artistFontSize" min="12" max="48" value="22" step="2">
<span class="range-value" id="valArtistFontSize">22px</span>
</div>
</div>
<div class="toggle-row">
<label for="textPulseWithBeat">Text pulsiert im Beat</label>
<input type="checkbox" id="textPulseWithBeat" checked>
</div>
<div class="toggle-row">
<label for="showTimecode">Live-Timecode einblenden</label>
<input type="checkbox" id="showTimecode" checked>
</div>
<div class="toggle-row">
<label for="showProgressBar">Linearer Fortschrittsbalken</label>
<input type="checkbox" id="showProgressBar">
</div>
</div>
</div>
</div>
<!-- TAB 7: PRESETS -->
<div class="tab-panel" id="tab-presets">
<div class="panel-section">
<h3 class="section-title">Presets</h3>
<div class="preset-cards-grid">
<button class="preset-select-btn" data-preset="trap_nation">
<span class="preset-icon">🟣</span>
<div class="preset-btn-info">
<strong>Trap Nation</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="ncs_electro">
<span class="preset-icon">🟡</span>
<div class="preset-btn-info">
<strong>NCS Electro</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="synthwave_horizon">
<span class="preset-icon">🌅</span>
<div class="preset-btn-info">
<strong>Synthwave Horizon</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="laser_oscilloscope">
<span class="preset-icon">〰️</span>
<div class="preset-btn-info">
<strong>Laser Oscilloscope</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="diamond_bass">
<span class="preset-icon">💎</span>
<div class="preset-btn-info">
<strong>Diamond Bass</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="cosmic_wormhole">
<span class="preset-icon">🌀</span>
<div class="preset-btn-info">
<strong>Cosmic Wormhole</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="studio_rack_eq">
<span class="preset-icon">📊</span>
<div class="preset-btn-info">
<strong>Studio Graphic EQ</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="minimal_ice_wave">
<span class="preset-icon">❄️</span>
<div class="preset-btn-info">
<strong>Minimal Ice Wave</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="toxic_emerald">
<span class="preset-icon">🔺</span>
<div class="preset-btn-info">
<strong>Toxic Emerald</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="solar_flare">
<span class="preset-icon">🛑</span>
<div class="preset-btn-info">
<strong>Solar Flare</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="cyberpunk_city">
<span class="preset-icon">🏙️</span>
<div class="preset-btn-info">
<strong>Cyberpunk City</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="monochrome_cinema">
<span class="preset-icon">🎬</span>
<div class="preset-btn-info">
<strong>Monochrome Cinema</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="retro_cassette">
<span class="preset-icon">📼</span>
<div class="preset-btn-info">
<strong>Retro Cassette</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="deep_space_nebula">
<span class="preset-icon">🌌</span>
<div class="preset-btn-info">
<strong>Deep Space Nebula</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="glitch_overdrive">
<span class="preset-icon">⚡</span>
<div class="preset-btn-info">
<strong>Glitch Overdrive</strong>
</div>
</button>
<button class="preset-select-btn" data-preset="liquid_ambient">
<span class="preset-icon">💧</span>
<div class="preset-btn-info">
<strong>Liquid Ambient</strong>
</div>
</button>
</div>
</div>
<div class="panel-section">
<h3 class="section-title">Eigene Presets</h3>
<div class="preset-manager-controls">
<button id="btnSavePreset" class="btn btn-secondary btn-sm">Speichern</button>
<button id="btnExportPresetJson" class="btn btn-secondary btn-sm">Exportieren (.json)</button>
<label class="btn btn-secondary btn-sm">
Importieren (.json)
<input type="file" id="importPresetInput" accept=".json" class="file-input-hidden">
</label>
</div>
</div>
</div>
<!-- TAB 8: VIDEO EXPORT -->
<div class="tab-panel" id="tab-export">
<div class="panel-section">
<h3 class="section-title">Video Export</h3>
<div class="form-group">
<label for="exportFormat">Format</label>
<select id="exportFormat" class="select-input">
<option value="webm_alpha" selected>WebM (Transparent / Alpha)</option>
<option value="mp4">MP4 Video</option>
</select>
</div>
<div class="form-row-2">
<div class="form-group">
<label for="exportResolution">Auflösung</label>
<select id="exportResolution" class="select-input">
<option value="1080p" selected>1080p Full HD</option>
<option value="4k">4K Ultra HD (2160p)</option>
<option value="720p">720p HD</option>
</select>
</div>
<div class="form-group">
<label for="exportFps">Bildrate</label>
<select id="exportFps" class="select-input">
<option value="60" selected>60 FPS</option>
<option value="30">30 FPS</option>
</select>
</div>
</div>
<!-- Video Render Button -->
<button id="btnStartExport" class="btn btn-primary btn-large btn-glow">
<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>
Video rendern & downloaden
</button>
<!-- Progress Card -->
<div class="export-progress-card hidden" id="exportProgressCard">
<div class="progress-header">
<span id="exportStatusText">Rendert Video...</span>
<span id="exportPercent">0%</span>
</div>
<div class="progress-bar-track">
<div class="progress-bar-fill" id="exportProgressBarFill"></div>
</div>
<div class="progress-meta">
<span id="exportFpsLive">Status: Rendert...</span>
<span id="exportEta">Verbleibend: --</span>
</div>
<button id="btnCancelExport" class="btn btn-secondary btn-sm" style="margin-top: 8px;">Abbrechen</button>
</div>
</div>
</div>
</div>
</aside>
</main>
</div>
<script src="presets.js"></script>
<script src="app.js"></script>
</body>
</html>