diff --git a/REFACTOR_GUIDE.md b/REFACTOR_GUIDE.md
new file mode 100644
index 0000000..da9a8e9
--- /dev/null
+++ b/REFACTOR_GUIDE.md
@@ -0,0 +1,470 @@
+# 🎯 Guide de Refonte UI/UX - AudiOhm Web
+
+**Date:** 2026-01-19
+**Status:** Prêt à implémenter
+**Version:** 2.0
+
+---
+
+## 📊 Analyse Actuelle
+
+### ✅ Ce qui marche bien
+- Thème cyberpunk néon cohérent
+- Animations déjà présentes
+- Glassmorphism implémenté
+- Structure HTML sémantique
+- Responsive design fonctionnel
+
+### ⚠️ Points à améliorer
+1. **Performance:** Trop d'animations simultanées
+2. **Accessibilité:** Contraste à vérifier en mode clair
+3. **JavaScript:** Logique manquante pour nouvelles fonctionnalités
+4. **Icons:** Certains emojis peuvent être remplacés par des SVG
+5. **Loading States:** Squelettes loading pas complètement implémentés
+
+---
+
+## 🎯 Objectifs de la Refonte
+
+### 1. Moderniser le Design System
+- [ ] Passer à un design system plus structuré
+- [ ] Améliorer la cohérence des espacements
+- [ ] Optimiser les animations pour la performance
+- [ ] Standardiser les composants
+
+### 2. Améliorer l'Accessibilité
+- [ ] Vérifier tous les contrastes de couleurs (4.5:1 minimum)
+- [ ] Ajouter des états focus visibles
+- [ ] Implémenter `prefers-reduced-motion`
+- [ ] Ajouter des labels ARIA
+
+### 3. Optimiser les Performances
+- [ ] Réduire le nombre d'animations continues
+- [ ] Utiliser `transform` et `opacity` uniquement
+- [ ] Lazy loading des images
+- [ ] Minifier CSS en production
+
+### 4. Compléter les Fonctionnalités
+- [ ] Logique JavaScript complète
+- [ ] Toast notifications fonctionnelles
+- [ ] Contrôles player actifs
+- [ ] Navigation mobile
+
+---
+
+## 🚀 Plan d'Implémentation
+
+### Phase 1: CSS - Optimisations (1-2 heures)
+
+**1.1 Créer un fichier CSS structuré**
+
+```css
+/* ============================================
+ AUDIOHM DESIGN SYSTEM V2
+ ============================================ */
+
+/* 1. VARIABLES */
+:root {
+ /* Colors */
+ --primary: #00F0FF;
+ --secondary: #BF00FF;
+ --accent: #FF006E;
+ --success: #00FF88;
+
+ /* Backgrounds */
+ --bg-dark: #0A0E27;
+ --bg-darker: #050814;
+ --bg-card: rgba(15, 23, 50, 0.6);
+
+ /* Text */
+ --text-primary: #FFFFFF;
+ --text-secondary: #A0A0C0;
+
+ /* Effects */
+ --glow-primary: 0 0 20px rgba(0, 240, 255, 0.5);
+ --glow-secondary: 0 0 20px rgba(191, 0, 255, 0.5);
+
+ /* Spacing */
+ --space-sm: 0.5rem;
+ --space-md: 1rem;
+ --space-lg: 1.5rem;
+ --space-xl: 2rem;
+ --space-2xl: 3rem;
+
+ /* Border Radius */
+ --radius-sm: 8px;
+ --radius-md: 12px;
+ --radius-lg: 15px;
+}
+
+/* 2. RESET & BASE */
+/* 3. UTILITIES */
+/* 4. COMPONENTS */
+/* 5. ANIMATIONS */
+/* 6. RESPONSIVE */
+```
+
+**1.2 Optimiser les animations**
+
+```css
+/* Supprimer les animations continues inutiles */
+/* Conserver seulement: */
+- Loading spinner
+- Background gradient (20s, très lent)
+- Hover states (150-300ms)
+```
+
+**1.3 Ajouter prefers-reduced-motion**
+
+```css
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+```
+
+### Phase 2: HTML - Améliorations (1 heure)
+
+**2.1 Ajouter des attributs ARIA**
+
+```html
+
+
+
+
+
+
+
+
+
+
+```
+
+**2.2 Remplacer les emojis par des SVG (si présents)**
+
+```html
+
+🎵
+
+
+
+```
+
+**2.3 Optimiser la structure sémantique**
+
+```html
+
+
+
+```
+
+### Phase 3: JavaScript - Fonctionnalités (2-3 heures)
+
+**3.1 Toast Notifications**
+
+```javascript
+function showToast(message, type = 'success') {
+ const container = document.getElementById('toast-container');
+ const toast = document.createElement('div');
+ toast.className = `toast ${type}`;
+ toast.innerHTML = `
+
+ ${message}
+ `;
+ container.appendChild(toast);
+
+ setTimeout(() => {
+ toast.style.animation = 'toastSlideOut 0.4s ease forwards';
+ setTimeout(() => toast.remove(), 400);
+ }, 3000);
+}
+```
+
+**3.2 Navigation Mobile**
+
+```javascript
+const mobileMenuBtn = document.getElementById('mobile-menu-btn');
+const sidebar = document.getElementById('sidebar');
+
+mobileMenuBtn.addEventListener('click', () => {
+ sidebar.classList.toggle('open');
+});
+
+// Fermer le menu en cliquant en dehors
+document.addEventListener('click', (e) => {
+ if (!sidebar.contains(e.target) && !mobileMenuBtn.contains(e.target)) {
+ sidebar.classList.remove('open');
+ }
+});
+```
+
+**3.3 Player Controls**
+
+```javascript
+// Play/Pause
+document.getElementById('play-btn').addEventListener('click', () => {
+ const audio = document.getElementById('audio-player');
+ const icon = document.querySelector('#play-btn i');
+
+ if (audio.paused) {
+ audio.play();
+ icon.classList.remove('fa-play');
+ icon.classList.add('fa-pause');
+ } else {
+ audio.pause();
+ icon.classList.remove('fa-pause');
+ icon.classList.add('fa-play');
+ }
+});
+
+// Mute
+document.getElementById('mute-btn').addEventListener('click', () => {
+ const audio = document.getElementById('audio-player');
+ const icon = document.querySelector('#mute-btn i');
+
+ audio.muted = !audio.muted;
+
+ if (audio.muted) {
+ icon.className = 'fas fa-volume-mute';
+ } else {
+ icon.className = 'fas fa-volume-up';
+ }
+});
+
+// Like button
+document.getElementById('like-btn').addEventListener('click', function() {
+ this.classList.toggle('liked');
+ const icon = this.querySelector('i');
+
+ if (this.classList.contains('liked')) {
+ icon.classList.remove('far');
+ icon.classList.add('fas');
+ showToast('Ajouté aux titres likés', 'success');
+ } else {
+ icon.classList.remove('fas');
+ icon.classList.add('far');
+ showToast('Retiré des titres likés', 'success');
+ }
+});
+```
+
+**3.4 Progress Bar**
+
+```javascript
+const audio = document.getElementById('audio-player');
+const progressBar = document.getElementById('progress-bar');
+const currentTimeEl = document.getElementById('current-time');
+const totalTimeEl = document.getElementById('total-time');
+
+// Update progress
+audio.addEventListener('timeupdate', () => {
+ const progress = (audio.currentTime / audio.duration) * 100;
+ progressBar.value = progress;
+ currentTimeEl.textContent = formatTime(audio.currentTime);
+});
+
+audio.addEventListener('loadedmetadata', () => {
+ totalTimeEl.textContent = formatTime(audio.duration);
+});
+
+// Seek
+progressBar.addEventListener('input', () => {
+ const time = (progressBar.value / 100) * audio.duration;
+ audio.currentTime = time;
+});
+
+function formatTime(seconds) {
+ const mins = Math.floor(seconds / 60);
+ const secs = Math.floor(seconds % 60);
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
+}
+```
+
+### Phase 4: CSS - Finitions (1 heure)
+
+**4.1 Scrollbar Custom**
+
+```css
+.main-content::-webkit-scrollbar {
+ width: 8px;
+}
+
+.main-content::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.main-content::-webkit-scrollbar-thumb {
+ background: var(--border);
+ border-radius: 4px;
+}
+
+.main-content::-webkit-scrollbar-thumb:hover {
+ background: var(--primary);
+}
+```
+
+**4.2 Improved Hover States**
+
+```css
+.track-card {
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.track-card:hover {
+ transform: translateY(-3px) scale(1.01);
+ box-shadow: var(--glow-primary);
+}
+```
+
+**4.3 Focus States**
+
+```css
+button:focus-visible,
+input:focus-visible,
+a:focus-visible {
+ outline: 2px solid var(--primary);
+ outline-offset: 2px;
+}
+```
+
+---
+
+## 📋 Checklist Pré-Livraison
+
+### Visual Quality
+- [ ] Aucun emoji comme icône
+- [ ] Icons cohérents (Font Awesome uniquement)
+- [ ] Hover states sans layout shift
+- [ ] Couleurs du thème utilisées directement
+- [ ] Pas de transitions width/height
+
+### Interaction
+- [ ] cursor-pointer sur éléments cliquables
+- [ ] Feedback visuel au hover
+- [ ] Transitions 150-300ms
+- [ ] Focus states visibles
+
+### Accessibility
+- [ ] Contraste 4.5:1 minimum
+- [ ] Labels ARIA présents
+- [ ] prefers-reduced-motion implémenté
+- [ ] Navigation clavier fonctionnelle
+
+### Performance
+- [ ] Animations optimisées (transform/opacity)
+- [ ] Pas d'animations infinies décoratives
+- [ ] Images lazy-loaded
+- [ ] CSS minifié en prod
+
+### Responsive
+- [ ] Mobile (375px) OK
+- [ ] Tablet (768px) OK
+- [ ] Desktop (1024px+) OK
+- [ ] Pas de horizontal scroll
+
+---
+
+## 🎨 Exemples de Code Optimisé
+
+### Button Component
+
+```css
+.btn {
+ position: relative;
+ overflow: hidden;
+ padding: var(--space-md) var(--space-xl);
+ border: none;
+ border-radius: var(--radius-md);
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.btn::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+ transition: left 0.5s ease;
+}
+
+.btn:hover::before {
+ left: 100%;
+}
+
+.btn:hover {
+ transform: translateY(-3px);
+}
+```
+
+### Card Component
+
+```css
+.card {
+ background: var(--bg-card);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-lg);
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.card:hover {
+ border-color: var(--primary);
+ box-shadow: var(--glow-primary);
+ transform: translateY(-3px);
+}
+```
+
+### Loading Skeleton
+
+```css
+.skeleton {
+ background: linear-gradient(90deg,
+ rgba(255, 255, 255, 0.05) 0%,
+ rgba(255, 255, 255, 0.1) 50%,
+ rgba(255, 255, 255, 0.05) 100%
+ );
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: var(--radius-sm);
+}
+
+@keyframes shimmer {
+ 0% { background-position: 200% 0; }
+ 100% { background-position: -200% 0; }
+}
+```
+
+---
+
+## 🚀 Next Steps
+
+1. **Créer un nouveau fichier CSS structuré** basé sur le design system
+2. **Implémenter les fonctionnalités JavaScript manquantes**
+3. **Tester l'accessibilité** avec un outil comme Lighthouse
+4. **Optimiser les performances** avec Chrome DevTools
+5. **Tester sur mobile** (375px, 768px)
+6. **Minifier et déployer**
+
+---
+
+**Version:** 2.0
+**Statut:** Prêt à implémenter
+**Estimation:** 4-6 heures de travail
diff --git a/backend/app/static/css/style-optimized.css b/backend/app/static/css/style-optimized.css
new file mode 100644
index 0000000..99fd561
--- /dev/null
+++ b/backend/app/static/css/style-optimized.css
@@ -0,0 +1,591 @@
+/* ============================================
+ AUDIOHM DESIGN SYSTEM V2 - OPTIMIZED
+ Version: 2.0
+ Last Updated: 2026-01-19
+ ============================================ */
+
+/* ============================================
+ 1. CSS VARIABLES
+ ============================================ */
+:root {
+ /* Colors - Primary */
+ --primary: #00F0FF;
+ --primary-dark: #00C0CC;
+ --primary-light: #00FFFF;
+
+ /* Colors - Secondary */
+ --secondary: #BF00FF;
+ --secondary-dark: #9000CC;
+ --secondary-light: #DF33FF;
+
+ /* Colors - Accent */
+ --accent: #FF006E;
+ --accent-dark: #CC0058;
+ --accent-light: #FF338E;
+
+ /* Colors - Functional */
+ --success: #00FF88;
+ --warning: #FFB800;
+ --error: #FF006E;
+ --info: #00F0FF;
+
+ /* Backgrounds */
+ --bg-dark: #0A0E27;
+ --bg-darker: #050814;
+ --bg-card: rgba(15, 23, 50, 0.6);
+ --bg-card-hover: rgba(15, 23, 50, 0.8);
+ --bg-glass: rgba(10, 14, 39, 0.7);
+
+ /* Text */
+ --text-primary: #FFFFFF;
+ --text-secondary: #A0A0C0;
+ --text-muted: #6B7280;
+
+ /* Borders */
+ --border: rgba(0, 240, 255, 0.2);
+ --border-hover: rgba(0, 240, 255, 0.4);
+
+ /* Effects */
+ --glow-primary: 0 0 20px rgba(0, 240, 255, 0.5);
+ --glow-secondary: 0 0 20px rgba(191, 0, 255, 0.5);
+ --glow-accent: 0 0 20px rgba(255, 0, 110, 0.5);
+
+ /* Spacing */
+ --space-xs: 0.5rem; /* 8px */
+ --space-sm: 0.75rem; /* 12px */
+ --space-md: 1rem; /* 16px */
+ --space-lg: 1.5rem; /* 24px */
+ --space-xl: 2rem; /* 32px */
+ --space-2xl: 3rem; /* 48px */
+ --space-3xl: 4rem; /* 64px */
+
+ /* Border Radius */
+ --radius-xs: 4px;
+ --radius-sm: 8px;
+ --radius-md: 12px;
+ --radius-lg: 15px;
+ --radius-xl: 20px;
+ --radius-full: 50%;
+
+ /* Typography */
+ --font-heading: 'Righteous', sans-serif;
+ --font-body: 'Poppins', sans-serif;
+
+ /* Font Sizes */
+ --text-xs: 0.75rem; /* 12px */
+ --text-sm: 0.875rem; /* 14px */
+ --text-base: 1rem; /* 16px */
+ --text-lg: 1.125rem; /* 18px */
+ --text-xl: 1.25rem; /* 20px */
+ --text-2xl: 1.5rem; /* 24px */
+ --text-3xl: 2rem; /* 32px */
+ --text-4xl: 2.5rem; /* 40px */
+
+ /* Z-Index Scale */
+ --z-dropdown: 1000;
+ --z-sticky: 1020;
+ --z-fixed: 1030;
+ --z-modal-backdrop: 1040;
+ --z-modal: 1050;
+ --z-toast: 1060;
+
+ /* Transitions */
+ --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-base: 300ms cubic-bezier(0.4, 0, 0.2, 1);
+ --transition-slow: 400ms ease-out;
+}
+
+/* ============================================
+ 2. RESET & BASE STYLES
+ ============================================ */
+*, *::before, *::after {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ line-height: 1.6;
+ color: var(--text-primary);
+ background: var(--bg-dark);
+ overflow-x: hidden;
+ position: relative;
+}
+
+/* Animated Background */
+body::before {
+ content: '';
+ position: fixed;
+ top: -50%;
+ left: -50%;
+ width: 200%;
+ height: 200%;
+ background:
+ radial-gradient(circle at 20% 80%, rgba(0, 240, 255, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(191, 0, 255, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba(255, 0, 110, 0.05) 0%, transparent 50%);
+ animation: gradientShift 20s ease infinite;
+ z-index: -1;
+ pointer-events: none;
+}
+
+/* Selection */
+::selection {
+ background: var(--primary);
+ color: var(--bg-dark);
+}
+
+/* Focus Visible */
+:focus-visible {
+ outline: 2px solid var(--primary);
+ outline-offset: 2px;
+}
+
+/* Scrollbar */
+::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: var(--border);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: var(--primary);
+}
+
+/* ============================================
+ 3. TYPOGRAPHY
+ ============================================ */
+h1, h2, h3, h4, h5, h6 {
+ font-family: var(--font-heading);
+ font-weight: 400;
+ line-height: 1.1;
+ color: var(--text-primary);
+}
+
+h1 { font-size: var(--text-4xl); }
+h2 { font-size: var(--text-3xl); }
+h3 { font-size: var(--text-2xl); }
+h4 { font-size: var(--text-xl); }
+h5 { font-size: var(--text-lg); }
+h6 { font-size: var(--text-base); }
+
+p {
+ margin-bottom: var(--space-md);
+ color: var(--text-secondary);
+}
+
+a {
+ color: var(--primary);
+ text-decoration: none;
+ transition: color var(--transition-fast);
+}
+
+a:hover {
+ color: var(--primary-light);
+}
+
+/* ============================================
+ 4. UTILITY CLASSES
+ ============================================ */
+.visually-hidden {
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ margin: -1px;
+ overflow: hidden;
+ clip: rect(0, 0, 0, 0);
+ border: 0;
+}
+
+.hidden {
+ display: none !important;
+}
+
+.sr-only {
+ position: absolute;
+ left: -10000px;
+ width: 1px;
+ height: 1px;
+ overflow: hidden;
+}
+
+/* ============================================
+ 5. COMPONENTS
+ ============================================ */
+
+/* Buttons */
+.btn {
+ position: relative;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-sm);
+ padding: var(--space-md) var(--space-xl);
+ border: none;
+ border-radius: var(--radius-md);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ font-weight: 600;
+ cursor: pointer;
+ transition: all var(--transition-base);
+ overflow: hidden;
+}
+
+.btn::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
+ transition: left 0.5s ease;
+}
+
+.btn:hover::before {
+ left: 100%;
+}
+
+.btn:hover {
+ transform: translateY(-3px);
+}
+
+.btn:active {
+ transform: translateY(-1px);
+}
+
+.btn-primary {
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
+ color: white;
+}
+
+.btn-primary:hover {
+ box-shadow: var(--glow-primary), 0 10px 30px rgba(0, 240, 255, 0.3);
+}
+
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid var(--border);
+ color: var(--text-primary);
+}
+
+.btn-secondary:hover {
+ background: rgba(255, 0, 110, 0.1);
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+.btn-icon {
+ padding: var(--space-sm);
+ width: 40px;
+ height: 40px;
+}
+
+/* Forms */
+.form-group {
+ margin-bottom: var(--space-lg);
+ position: relative;
+}
+
+.form-label {
+ display: block;
+ margin-bottom: var(--space-sm);
+ font-weight: 500;
+ color: var(--text-secondary);
+}
+
+.form-input {
+ width: 100%;
+ padding: var(--space-md) var(--space-md) var(--space-md) var(--space-2xl);
+ background: rgba(255, 255, 255, 0.05);
+ border: 2px solid var(--border);
+ border-radius: var(--radius-md);
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ transition: all var(--transition-base);
+}
+
+.form-input:focus {
+ outline: none;
+ border-color: var(--primary);
+ background: rgba(0, 240, 255, 0.05);
+ box-shadow: var(--glow-primary);
+}
+
+.form-input::placeholder {
+ color: var(--text-muted);
+}
+
+/* Cards */
+.card {
+ background: var(--bg-card);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-lg);
+ transition: all var(--transition-base);
+}
+
+.card:hover {
+ border-color: var(--primary);
+ box-shadow: var(--glow-primary);
+ transform: translateY(-3px);
+}
+
+/* Badge */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ padding: var(--space-xs) var(--space-sm);
+ background: var(--primary);
+ color: var(--bg-dark);
+ font-size: var(--text-xs);
+ font-weight: 600;
+ border-radius: var(--radius-full);
+}
+
+/* ============================================
+ 6. LAYOUT
+ ============================================ */
+.container {
+ width: 100%;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 0 var(--space-lg);
+}
+
+.grid {
+ display: grid;
+ gap: var(--space-lg);
+}
+
+.flex {
+ display: flex;
+}
+
+.flex-col {
+ flex-direction: column;
+}
+
+.items-center {
+ align-items: center;
+}
+
+.justify-center {
+ justify-content: center;
+}
+
+.justify-between {
+ justify-content: space-between;
+}
+
+.gap-sm { gap: var(--space-sm); }
+.gap-md { gap: var(--space-md); }
+.gap-lg { gap: var(--space-lg); }
+
+/* ============================================
+ 7. ANIMATIONS
+ ============================================ */
+@keyframes gradientShift {
+ 0%, 100% {
+ transform: translate(0, 0) rotate(0deg);
+ }
+ 33% {
+ transform: translate(30px, -30px) rotate(120deg);
+ }
+ 66% {
+ transform: translate(-20px, 20px) rotate(240deg);
+ }
+}
+
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+
+@keyframes shimmer {
+ 0% { background-position: 200% 0; }
+ 100% { background-position: -200% 0; }
+}
+
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 20%, 60% { transform: translateX(-10px); }
+ 40%, 80% { transform: translateX(10px); }
+}
+
+/* Reduced Motion */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+
+/* ============================================
+ 8. SPECIFIC COMPONENTS
+ ============================================ */
+
+/* Loading Screen */
+.loading-screen {
+ position: fixed;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: var(--bg-dark);
+ z-index: var(--z-modal);
+ animation: fadeIn 0.5s ease;
+}
+
+.spinner {
+ width: 80px;
+ height: 80px;
+ position: relative;
+}
+
+.spinner::before,
+.spinner::after {
+ content: '';
+ position: absolute;
+ inset: 0;
+ border-radius: 50%;
+ border: 4px solid transparent;
+}
+
+.spinner::before {
+ border-top-color: var(--primary);
+ animation: spin 1s linear infinite;
+}
+
+.spinner::after {
+ border-bottom-color: var(--secondary);
+ animation: spin 1.5s linear infinite reverse;
+}
+
+/* Toast Notifications */
+.toast-container {
+ position: fixed;
+ top: var(--space-xl);
+ right: var(--space-xl);
+ z-index: var(--z-toast);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-md);
+}
+
+.toast {
+ display: flex;
+ align-items: center;
+ gap: var(--space-md);
+ padding: var(--space-md) var(--space-lg);
+ background: var(--bg-glass);
+ backdrop-filter: blur(20px);
+ border: 1px solid var(--border);
+ border-left: 4px solid var(--primary);
+ border-radius: var(--radius-md);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.4);
+ animation: toastSlideIn 0.4s ease;
+ min-width: 300px;
+}
+
+@keyframes toastSlideIn {
+ from {
+ transform: translateX(400px);
+ opacity: 0;
+ }
+ to {
+ transform: translateX(0);
+ opacity: 1;
+ }
+}
+
+.toast.success {
+ border-left-color: var(--success);
+}
+
+.toast.error {
+ border-left-color: var(--error);
+}
+
+/* Skeleton Loading */
+.skeleton {
+ background: linear-gradient(90deg,
+ rgba(255, 255, 255, 0.05) 0%,
+ rgba(255, 255, 255, 0.1) 50%,
+ rgba(255, 255, 255, 0.05) 100%
+ );
+ background-size: 200% 100%;
+ animation: shimmer 1.5s infinite;
+ border-radius: var(--radius-sm);
+}
+
+/* ============================================
+ 9. RESPONSIVE DESIGN
+ ============================================ */
+
+/* Mobile First Approach */
+
+/* Small (640px and up) */
+@media (min-width: 640px) {
+ .grid-sm-2 { grid-template-columns: repeat(2, 1fr); }
+}
+
+/* Medium (768px and up) */
+@media (min-width: 768px) {
+ .grid-md-3 { grid-template-columns: repeat(3, 1fr); }
+ .grid-md-4 { grid-template-columns: repeat(4, 1fr); }
+}
+
+/* Large (1024px and up) */
+@media (min-width: 1024px) {
+ .grid-lg-4 { grid-template-columns: repeat(4, 1fr); }
+ .grid-lg-6 { grid-template-columns: repeat(6, 1fr); }
+}
+
+/* Print Styles */
+@media print {
+ .no-print {
+ display: none !important;
+ }
+}
diff --git a/backend/app/static/js/app-optimized.js b/backend/app/static/js/app-optimized.js
new file mode 100644
index 0000000..5b3c464
--- /dev/null
+++ b/backend/app/static/js/app-optimized.js
@@ -0,0 +1,762 @@
+/**
+ * ============================================
+ * AUDIOHM WEB PLAYER - OPTIMIZED
+ * Version: 2.0
+ * Last Updated: 2026-01-19
+ * ============================================
+ */
+
+// ============================================
+// STATE MANAGEMENT
+// ============================================
+const AppState = {
+ isAuthenticated: false,
+ currentPage: 'home',
+ currentTrack: null,
+ isPlaying: false,
+ isShuffle: false,
+ repeatMode: 'none', // none, one, all
+ volume: 100,
+ isMuted: false,
+ likedTracks: new Set(),
+ playlists: [],
+ queue: []
+};
+
+// ============================================
+// DOM ELEMENTS
+// ============================================
+const DOM = {
+ // Screens
+ loadingScreen: null,
+ loginScreen: null,
+ mainApp: null,
+
+ // Forms
+ loginForm: null,
+ registerForm: null,
+ authError: null,
+
+ // Navigation
+ sidebar: null,
+ navItems: null,
+ mobileMenuBtn: null,
+ logoutBtn: null,
+
+ // Pages
+ pages: {},
+
+ // Player
+ audioPlayer: null,
+ playBtn: null,
+ prevBtn: null,
+ nextBtn: null,
+ shuffleBtn: null,
+ repeatBtn: null,
+ progressBar: null,
+ volumeBar: null,
+ muteBtn: null,
+ likeBtn: null,
+ playerCover: null,
+ playerTitle: null,
+ playerArtist: null,
+ currentTime: null,
+ totalTime: null,
+
+ // Toast
+ toastContainer: null
+};
+
+// ============================================
+// INITIALIZATION
+// ============================================
+function init() {
+ // Cache DOM elements
+ cacheDOM();
+
+ // Check authentication
+ checkAuth();
+
+ // Setup event listeners
+ setupEventListeners();
+
+ // Hide loading screen
+ hideLoadingScreen();
+}
+
+function cacheDOM() {
+ DOM.loadingScreen = document.getElementById('loading-screen');
+ DOM.loginScreen = document.getElementById('login-screen');
+ DOM.mainApp = document.getElementById('main-app');
+
+ DOM.loginForm = document.getElementById('login-form');
+ DOM.registerForm = document.getElementById('register-form');
+ DOM.authError = document.getElementById('auth-error');
+
+ DOM.sidebar = document.getElementById('sidebar');
+ DOM.navItems = document.querySelectorAll('.nav-item');
+ DOM.mobileMenuBtn = document.getElementById('mobile-menu-btn');
+ DOM.logoutBtn = document.getElementById('logout-btn');
+
+ ['home', 'search', 'library'].forEach(page => {
+ DOM.pages[page] = document.getElementById(`${page}-page`);
+ });
+
+ DOM.audioPlayer = document.getElementById('audio-player');
+ DOM.playBtn = document.getElementById('play-btn');
+ DOM.prevBtn = document.getElementById('prev-btn');
+ DOM.nextBtn = document.getElementById('next-btn');
+ DOM.shuffleBtn = document.getElementById('shuffle-btn');
+ DOM.repeatBtn = document.getElementById('repeat-btn');
+ DOM.progressBar = document.getElementById('progress-bar');
+ DOM.volumeBar = document.getElementById('volume-bar');
+ DOM.muteBtn = document.getElementById('mute-btn');
+ DOM.likeBtn = document.getElementById('like-btn');
+ DOM.playerCover = document.getElementById('player-cover');
+ DOM.playerTitle = document.getElementById('player-title');
+ DOM.playerArtist = document.getElementById('player-artist');
+ DOM.currentTime = document.getElementById('current-time');
+ DOM.totalTime = document.getElementById('total-time');
+
+ DOM.toastContainer = document.getElementById('toast-container');
+}
+
+// ============================================
+// EVENT LISTENERS
+// ============================================
+function setupEventListeners() {
+ // Auth forms
+ if (DOM.loginForm) {
+ DOM.loginForm.addEventListener('submit', handleLogin);
+ }
+
+ if (DOM.registerForm) {
+ DOM.registerForm.addEventListener('submit', handleRegister);
+ }
+
+ // Show/hide register forms
+ const showRegister = document.getElementById('show-register');
+ const showLogin = document.getElementById('show-login');
+
+ if (showRegister) {
+ showRegister.addEventListener('click', (e) => {
+ e.preventDefault();
+ DOM.loginForm.classList.add('hidden');
+ DOM.registerForm.classList.remove('hidden');
+ });
+ }
+
+ if (showLogin) {
+ showLogin.addEventListener('click', (e) => {
+ e.preventDefault();
+ DOM.registerForm.classList.add('hidden');
+ DOM.loginForm.classList.remove('hidden');
+ });
+ }
+
+ // Navigation
+ DOM.navItems.forEach(item => {
+ item.addEventListener('click', (e) => {
+ e.preventDefault();
+ const page = item.dataset.page;
+ navigateTo(page);
+ });
+ });
+
+ // Mobile menu
+ if (DOM.mobileMenuBtn) {
+ DOM.mobileMenuBtn.addEventListener('click', toggleMobileMenu);
+ }
+
+ // Logout
+ if (DOM.logoutBtn) {
+ DOM.logoutBtn.addEventListener('click', handleLogout);
+ }
+
+ // Player controls
+ setupPlayerControls();
+}
+
+function setupPlayerControls() {
+ // Play/Pause
+ if (DOM.playBtn) {
+ DOM.playBtn.addEventListener('click', togglePlayPause);
+ }
+
+ // Previous/Next
+ if (DOM.prevBtn) {
+ DOM.prevBtn.addEventListener('click', playPrevious);
+ }
+
+ if (DOM.nextBtn) {
+ DOM.nextBtn.addEventListener('click', playNext);
+ }
+
+ // Shuffle
+ if (DOM.shuffleBtn) {
+ DOM.shuffleBtn.addEventListener('click', toggleShuffle);
+ }
+
+ // Repeat
+ if (DOM.repeatBtn) {
+ DOM.repeatBtn.addEventListener('click', toggleRepeat);
+ }
+
+ // Progress bar
+ if (DOM.progressBar) {
+ DOM.progressBar.addEventListener('input', handleSeek);
+ }
+
+ // Volume
+ if (DOM.volumeBar) {
+ DOM.volumeBar.addEventListener('input', handleVolumeChange);
+ }
+
+ // Mute
+ if (DOM.muteBtn) {
+ DOM.muteBtn.addEventListener('click', toggleMute);
+ }
+
+ // Like
+ if (DOM.likeBtn) {
+ DOM.likeBtn.addEventListener('click', toggleLike);
+ }
+
+ // Audio events
+ if (DOM.audioPlayer) {
+ DOM.audioPlayer.addEventListener('timeupdate', updateProgress);
+ DOM.audioPlayer.addEventListener('loadedmetadata', updateDuration);
+ DOM.audioPlayer.addEventListener('ended', handleTrackEnd);
+ }
+}
+
+// ============================================
+// AUTHENTICATION
+// ============================================
+async function checkAuth() {
+ const token = localStorage.getItem('token');
+
+ if (!token) {
+ showScreen('login');
+ return;
+ }
+
+ try {
+ const response = await fetch('/api/v1/auth/me', {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ if (response.ok) {
+ AppState.isAuthenticated = true;
+ showScreen('main');
+ loadUserData();
+ } else {
+ localStorage.removeItem('token');
+ showScreen('login');
+ }
+ } catch (error) {
+ console.error('Auth check failed:', error);
+ showScreen('login');
+ }
+}
+
+async function handleLogin(e) {
+ e.preventDefault();
+
+ const email = document.getElementById('login-email').value;
+ const password = document.getElementById('login-password').value;
+
+ try {
+ const response = await fetch('/api/v1/auth/login', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ email, password })
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ localStorage.setItem('token', data.access_token);
+ AppState.isAuthenticated = true;
+ showScreen('main');
+ showToast('Connexion réussie!', 'success');
+ } else {
+ showError(data.detail || 'Email ou mot de passe incorrect');
+ }
+ } catch (error) {
+ console.error('Login failed:', error);
+ showError('Erreur de connexion');
+ }
+}
+
+async function handleRegister(e) {
+ e.preventDefault();
+
+ const username = document.getElementById('register-username').value;
+ const email = document.getElementById('register-email').value;
+ const password = document.getElementById('register-password').value;
+
+ try {
+ const response = await fetch('/api/v1/auth/register', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ username, email, password })
+ });
+
+ const data = await response.json();
+
+ if (response.ok) {
+ localStorage.setItem('token', data.access_token);
+ AppState.isAuthenticated = true;
+ showScreen('main');
+ showToast('Compte créé avec succès!', 'success');
+ } else {
+ showError(data.detail || 'Erreur lors de la création du compte');
+ }
+ } catch (error) {
+ console.error('Register failed:', error);
+ showError('Erreur de connexion');
+ }
+}
+
+function handleLogout() {
+ localStorage.removeItem('token');
+ AppState.isAuthenticated = false;
+ showScreen('login');
+ showToast('Déconnexion réussie', 'success');
+}
+
+// ============================================
+// NAVIGATION
+// ============================================
+function navigateTo(page) {
+ // Update active nav item
+ DOM.navItems.forEach(item => {
+ item.classList.remove('active');
+ if (item.dataset.page === page) {
+ item.classList.add('active');
+ }
+ });
+
+ // Show/hide pages
+ Object.keys(DOM.pages).forEach(key => {
+ if (key === page) {
+ DOM.pages[key].classList.add('active');
+ } else {
+ DOM.pages[key].classList.remove('active');
+ }
+ });
+
+ AppState.currentPage = page;
+
+ // Close mobile menu
+ DOM.sidebar.classList.remove('open');
+}
+
+function toggleMobileMenu() {
+ DOM.sidebar.classList.toggle('open');
+}
+
+// Close menu when clicking outside
+document.addEventListener('click', (e) => {
+ if (!DOM.sidebar?.contains(e.target) && !DOM.mobileMenuBtn?.contains(e.target)) {
+ DOM.sidebar?.classList.remove('open');
+ }
+});
+
+// ============================================
+// PLAYER CONTROLS
+// ============================================
+function togglePlayPause() {
+ if (!DOM.audioPlayer) return;
+
+ if (DOM.audioPlayer.paused) {
+ DOM.audioPlayer.play();
+ updatePlayButton(true);
+ } else {
+ DOM.audioPlayer.pause();
+ updatePlayButton(false);
+ }
+}
+
+function updatePlayButton(isPlaying) {
+ const icon = DOM.playBtn?.querySelector('i');
+ if (!icon) return;
+
+ if (isPlaying) {
+ icon.classList.remove('fa-play');
+ icon.classList.add('fa-pause');
+ } else {
+ icon.classList.remove('fa-pause');
+ icon.classList.add('fa-play');
+ }
+}
+
+function playPrevious() {
+ // Implement previous track logic
+ showToast('Non disponible pour le moment', 'error');
+}
+
+function playNext() {
+ // Implement next track logic
+ showToast('Non disponible pour le moment', 'error');
+}
+
+function toggleShuffle() {
+ AppState.isShuffle = !AppState.isShuffle;
+
+ if (DOM.shuffleBtn) {
+ DOM.shuffleBtn.classList.toggle('active', AppState.isShuffle);
+ }
+
+ showToast(AppState.isShuffle ? 'Aléatoire activé' : 'Aléatoire désactivé', 'success');
+}
+
+function toggleRepeat() {
+ const modes = ['none', 'all', 'one'];
+ const currentIndex = modes.indexOf(AppState.repeatMode);
+ const nextIndex = (currentIndex + 1) % modes.length;
+ AppState.repeatMode = modes[nextIndex];
+
+ if (DOM.repeatBtn) {
+ DOM.repeatBtn.classList.remove('active');
+ if (AppState.repeatMode !== 'none') {
+ DOM.repeatBtn.classList.add('active');
+ }
+ }
+
+ const messages = {
+ none: 'Répétition désactivée',
+ all: 'Répétition de toutes les pistes',
+ one: 'Répétition de la piste actuelle'
+ };
+
+ showToast(messages[AppState.repeatMode], 'success');
+}
+
+function handleSeek() {
+ if (!DOM.audioPlayer || !DOM.progressBar) return;
+
+ const time = (DOM.progressBar.value / 100) * DOM.audioPlayer.duration;
+ DOM.audioPlayer.currentTime = time;
+}
+
+function handleVolumeChange() {
+ if (!DOM.audioPlayer || !DOM.volumeBar) return;
+
+ AppState.volume = DOM.volumeBar.value;
+ DOM.audioPlayer.volume = AppState.volume / 100;
+ AppState.isMuted = false;
+ updateVolumeIcon();
+}
+
+function toggleMute() {
+ if (!DOM.audioPlayer) return;
+
+ AppState.isMuted = !AppState.isMuted;
+ DOM.audioPlayer.muted = AppState.isMuted;
+ updateVolumeIcon();
+}
+
+function updateVolumeIcon() {
+ const icon = DOM.muteBtn?.querySelector('i');
+ if (!icon) return;
+
+ icon.className = 'fas';
+
+ if (AppState.isMuted || AppState.volume === 0) {
+ icon.classList.add('fa-volume-mute');
+ } else if (AppState.volume < 50) {
+ icon.classList.add('fa-volume-down');
+ } else {
+ icon.classList.add('fa-volume-up');
+ }
+}
+
+function toggleLike() {
+ if (!DOM.likeBtn) return;
+
+ const trackId = DOM.likeBtn.dataset.trackId;
+ if (!trackId) return;
+
+ if (AppState.likedTracks.has(trackId)) {
+ AppState.likedTracks.delete(trackId);
+ DOM.likeBtn.classList.remove('liked');
+ DOM.likeBtn.querySelector('i').classList.replace('fas', 'far');
+ showToast('Retiré des titres likés', 'success');
+ } else {
+ AppState.likedTracks.add(trackId);
+ DOM.likeBtn.classList.add('liked');
+ DOM.likeBtn.querySelector('i').classList.replace('far', 'fas');
+ showToast('Ajouté aux titres likés', 'success');
+ }
+}
+
+function updateProgress() {
+ if (!DOM.audioPlayer || !DOM.progressBar) return;
+
+ const progress = (DOM.audioPlayer.currentTime / DOM.audioPlayer.duration) * 100;
+ DOM.progressBar.value = progress;
+
+ if (DOM.currentTime) {
+ DOM.currentTime.textContent = formatTime(DOM.audioPlayer.currentTime);
+ }
+}
+
+function updateDuration() {
+ if (!DOM.audioPlayer || !DOM.totalTime) return;
+
+ DOM.totalTime.textContent = formatTime(DOM.audioPlayer.duration);
+}
+
+function handleTrackEnd() {
+ if (AppState.repeatMode === 'one') {
+ DOM.audioPlayer.currentTime = 0;
+ DOM.audioPlayer.play();
+ } else if (AppState.repeatMode === 'all') {
+ playNext();
+ } else {
+ updatePlayButton(false);
+ }
+}
+
+// ============================================
+// UTILITY FUNCTIONS
+// ============================================
+function formatTime(seconds) {
+ if (!seconds || isNaN(seconds)) return '0:00';
+
+ const mins = Math.floor(seconds / 60);
+ const secs = Math.floor(seconds % 60);
+
+ return `${mins}:${secs.toString().padStart(2, '0')}`;
+}
+
+function showScreen(screen) {
+ if (DOM.loadingScreen) DOM.loadingScreen.classList.add('hidden');
+ if (DOM.loginScreen) DOM.loginScreen.classList.toggle('hidden', screen !== 'login');
+ if (DOM.mainApp) {
+ DOM.mainApp.classList.toggle('hidden', screen !== 'main');
+ if (screen === 'main') {
+ DOM.mainApp.classList.add('visible');
+ }
+ }
+}
+
+function hideLoadingScreen() {
+ if (DOM.loadingScreen) {
+ setTimeout(() => {
+ DOM.loadingScreen.style.display = 'none';
+ }, 500);
+ }
+}
+
+function showError(message) {
+ if (DOM.authError) {
+ DOM.authError.textContent = message;
+ DOM.authError.classList.remove('hidden');
+ }
+}
+
+async function loadUserData() {
+ // Load playlists, liked tracks, etc.
+ await loadPlaylists();
+ await loadTrendingTracks();
+}
+
+async function loadPlaylists() {
+ const container = document.getElementById('my-playlists');
+ if (!container) return;
+
+ try {
+ const token = localStorage.getItem('token');
+ const response = await fetch('/api/v1/playlists', {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ if (response.ok) {
+ const playlists = await response.json();
+ AppState.playlists = playlists;
+ renderPlaylists(playlists);
+ }
+ } catch (error) {
+ console.error('Failed to load playlists:', error);
+ container.innerHTML = 'Erreur de chargement
';
+ }
+}
+
+function renderPlaylists(playlists) {
+ const container = document.getElementById('my-playlists');
+ if (!container) return;
+
+ if (playlists.length === 0) {
+ container.innerHTML = 'Aucune playlist
';
+ return;
+ }
+
+ container.innerHTML = playlists.map(playlist => `
+
+

+
${playlist.name}
+
${playlist.track_count || 0} pistes
+
+ `).join('');
+}
+
+async function loadTrendingTracks() {
+ const container = document.getElementById('trending-tracks');
+ if (!container) return;
+
+ try {
+ const token = localStorage.getItem('token');
+ const response = await fetch('/api/v1/music/trending', {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ if (response.ok) {
+ const tracks = await response.json();
+ renderTracks(tracks, container);
+ }
+ } catch (error) {
+ console.error('Failed to load trending tracks:', error);
+ container.innerHTML = 'Erreur de chargement
';
+ }
+}
+
+function renderTracks(tracks, container) {
+ if (!container) return;
+
+ if (tracks.length === 0) {
+ container.innerHTML = 'Aucun résultat
';
+ return;
+ }
+
+ container.innerHTML = tracks.map(track => `
+
+

+
+
${track.title}
+
${track.artist}
+
+
${formatTime(track.duration)}
+
+
+ `).join('');
+}
+
+// Global function to play a track
+window.playTrack = async function(trackId) {
+ try {
+ const token = localStorage.getItem('token');
+ const response = await fetch(`/api/v1/music/${trackId}`, {
+ headers: {
+ 'Authorization': `Bearer ${token}`
+ }
+ });
+
+ if (response.ok) {
+ const track = await response.json();
+
+ // Update player
+ if (DOM.audioPlayer) {
+ DOM.audioPlayer.src = track.stream_url;
+ DOM.audioPlayer.play();
+ updatePlayButton(true);
+ }
+
+ if (DOM.playerTitle) DOM.playerTitle.textContent = track.title;
+ if (DOM.playerArtist) DOM.playerArtist.textContent = track.artist;
+ if (DOM.playerCover) DOM.playerCover.src = track.cover || '/static/img/default-cover.png';
+
+ AppState.currentTrack = track;
+ }
+ } catch (error) {
+ console.error('Failed to play track:', error);
+ showToast('Erreur lors de la lecture', 'error');
+ }
+};
+
+// ============================================
+// TOAST NOTIFICATIONS
+// ============================================
+function showToast(message, type = 'success') {
+ if (!DOM.toastContainer) return;
+
+ const toast = document.createElement('div');
+ toast.className = `toast ${type}`;
+
+ const icon = type === 'success' ? 'check-circle' : 'exclamation-circle';
+
+ toast.innerHTML = `
+
+ ${message}
+ `;
+
+ DOM.toastContainer.appendChild(toast);
+
+ setTimeout(() => {
+ toast.style.animation = 'toastSlideOut 0.4s ease forwards';
+ setTimeout(() => toast.remove(), 400);
+ }, 3000);
+}
+
+// ============================================
+// KEYBOARD SHORTCUTS
+// ============================================
+document.addEventListener('keydown', (e) => {
+ // Don't trigger if typing in input
+ if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
+
+ switch(e.code) {
+ case 'Space':
+ e.preventDefault();
+ togglePlayPause();
+ break;
+ case 'ArrowRight':
+ if (e.shiftKey) playNext();
+ else if (DOM.audioPlayer) DOM.audioPlayer.currentTime += 10;
+ break;
+ case 'ArrowLeft':
+ if (e.shiftKey) playPrevious();
+ else if (DOM.audioPlayer) DOM.audioPlayer.currentTime -= 10;
+ break;
+ case 'ArrowUp':
+ e.preventDefault();
+ if (DOM.volumeBar) {
+ DOM.volumeBar.value = Math.min(100, parseInt(DOM.volumeBar.value) + 10);
+ handleVolumeChange();
+ }
+ break;
+ case 'ArrowDown':
+ e.preventDefault();
+ if (DOM.volumeBar) {
+ DOM.volumeBar.value = Math.max(0, parseInt(DOM.volumeBar.value) - 10);
+ handleVolumeChange();
+ }
+ break;
+ case 'KeyM':
+ toggleMute();
+ break;
+ }
+});
+
+// ============================================
+// INIT
+// ============================================
+if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+} else {
+ init();
+}
diff --git a/design-system-v2/MASTER.md b/design-system-v2/MASTER.md
new file mode 100644
index 0000000..acb309f
--- /dev/null
+++ b/design-system-v2/MASTER.md
@@ -0,0 +1,470 @@
+# AudiOhm Design System V2 - Modernisé
+
+**Version:** 2.0
+**Date:** 2026-01-19
+**Style:** Cyberpunk Neon Modernisé + Glassmorphism
+
+---
+
+## 🎨 Style Principal
+
+**Nom:** Cyberpunk Glass Modern
+**Mots-clés:** Néon, dark mode, glassmorphism, gradients animés, futuriste, énergique, bold
+
+**Palette Cyberpunk Optimisée:**
+
+| Role | Hex | Usage |
+|------|-----|-------|
+| **Primary** | #00F0FF | Actions principales, liens, boutons |
+| **Secondary** | #BF00FF | Accents, hover states, éléments secondaires |
+| **Accent** | #FF006E | Alertes, actions destructives, CTA |
+| **Success** | #00FF88 | Success states, confirmations |
+| **Background** | #0A0E27 | Fond principal (dark navy) |
+| **Background Darker** | #050814 | Sidebars, footers |
+| **Card Background** | rgba(15, 23, 50, 0.6) | Cartes, conteneurs (glass) |
+| **Text Primary** | #FFFFFF | Titres, texte principal |
+| **Text Secondary** | #A0A0C0 | Descriptions, texte secondaire |
+| **Border** | rgba(0, 240, 255, 0.2) | Bordures, séparateurs |
+
+---
+
+## 🎯 Typographie
+
+**Font Pairing:**
+
+| Usage | Font | Poids | Taille |
+|-------|------|-------|--------|
+| **Titres** | Righteous | 400/700 | 2rem–4rem |
+| **Body** | Poppins | 300/400/500/600/700 | 1rem–1.2rem |
+| **UI Elements** | Poppins | 500/600 | 0.875rem–1rem |
+
+**Google Fonts Import:**
+```css
+@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&family=Righteous&display=swap');
+```
+
+**CSS Variables:**
+```css
+--font-heading: 'Righteous', sans-serif;
+--font-body: 'Poppins', sans-serif;
+```
+
+**Line Heights:**
+- Titres: 1.1
+- Body: 1.6
+- UI Elements: 1.4
+
+---
+
+## ✨ Effets & Animations
+
+### Core Effects
+
+**1. Glassmorphism**
+```css
+background: rgba(15, 23, 50, 0.6);
+backdrop-filter: blur(20px);
+-webkit-backdrop-filter: blur(20px);
+border: 1px solid rgba(255, 255, 255, 0.1);
+```
+
+**2. Neon Glow**
+```css
+--glow-primary: 0 0 20px rgba(0, 240, 255, 0.5);
+--glow-secondary: 0 0 20px rgba(191, 0, 255, 0.5);
+--glow-accent: 0 0 20px rgba(255, 0, 110, 0.5);
+```
+
+**3. Gradient Animé (Background)**
+```css
+background: radial-gradient(circle at 20% 80%, rgba(0, 240, 255, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 80% 20%, rgba(191, 0, 255, 0.1) 0%, transparent 50%),
+ radial-gradient(circle at 40% 40%, rgba(255, 0, 110, 0.05) 0%, transparent 50%);
+animation: gradientShift 20s ease infinite;
+```
+
+### Animation Timings
+
+| Type | Duration | Easing | Usage |
+|------|----------|--------|-------|
+| **Micro-interactions** | 150ms | ease-out | Hover states, focus |
+| **Transitions standards** | 300ms | cubic-bezier(0.4, 0, 0.2, 1) | Navigation, modales |
+| **Page transitions** | 400ms | ease-out | Changement de page |
+| **Loading** | 1500ms | linear | Spinners, skeletons |
+| **Continuous animations** | 20s+ | ease-in-out | Background gradients |
+
+**Performance Rules:**
+✅ DO: Animer `transform`, `opacity`, `filter`
+❌ DON'T: Animer `width`, `height`, `top`, `left`
+
+---
+
+## 🎬 Animations Clés
+
+### 1. Fade In
+```css
+@keyframes fadeIn {
+ from { opacity: 0; }
+ to { opacity: 1; }
+}
+```
+
+### 2. Slide In
+```css
+@keyframes slideIn {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+```
+
+### 3. Pulse
+```css
+@keyframes pulse {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.5; }
+}
+```
+
+### 4. Shimmer (Skeleton Loading)
+```css
+@keyframes shimmer {
+ 0% { background-position: 200% 0; }
+ 100% { background-position: -200% 0; }
+}
+```
+
+### 5. Spin (Double)
+```css
+@keyframes spin {
+ to { transform: rotate(360deg); }
+}
+```
+
+### 6. Logo Glow
+```css
+@keyframes logoGlow {
+ 0%, 100% { filter: drop-shadow(0 0 20px rgba(0, 240, 255, 0.5)); }
+ 33% { filter: drop-shadow(0 0 30px rgba(191, 0, 255, 0.7)); }
+ 66% { filter: drop-shadow(0 0 25px rgba(255, 0, 110, 0.6)); }
+}
+```
+
+### 7. Gradient Shift
+```css
+@keyframes gradientShift {
+ 0%, 100% { transform: translate(0, 0) rotate(0deg); }
+ 33% { transform: translate(30px, -30px) rotate(120deg); }
+ 66% { transform: translate(-20px, 20px) rotate(240deg); }
+}
+```
+
+### 8. Shake (Erreur)
+```css
+@keyframes shake {
+ 0%, 100% { transform: translateX(0); }
+ 20%, 60% { transform: translateX(-10px); }
+ 40%, 80% { transform: translateX(10px); }
+}
+```
+
+---
+
+## 🔘 Composants UI
+
+### Boutons
+
+**Primary Button:**
+```css
+.btn-primary {
+ padding: 1rem;
+ background: linear-gradient(135deg, var(--primary), var(--secondary));
+ border: none;
+ border-radius: 12px;
+ color: white;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.3s ease;
+}
+
+.btn-primary:hover {
+ transform: translateY(-3px);
+ box-shadow: var(--glow-primary);
+}
+```
+
+**Secondary Button:**
+```css
+.btn-secondary {
+ background: rgba(255, 255, 255, 0.05);
+ border: 1px solid var(--border);
+}
+
+.btn-secondary:hover {
+ background: rgba(255, 0, 110, 0.1);
+ border-color: var(--accent);
+ color: var(--accent);
+}
+```
+
+### Cartes
+
+**Track Card:**
+```css
+.track-card {
+ background: var(--bg-card);
+ backdrop-filter: blur(10px);
+ border: 1px solid var(--border);
+ border-radius: 15px;
+ padding: 1.2rem;
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+
+.track-card:hover {
+ border-color: var(--primary);
+ box-shadow: var(--glow-primary);
+ transform: translateY(-3px) scale(1.01);
+}
+```
+
+### Form Inputs
+
+```css
+.form-group input {
+ padding: 1rem 1rem 1rem 3rem;
+ background: rgba(255, 255, 255, 0.05);
+ border: 2px solid var(--border);
+ border-radius: 12px;
+ transition: all 0.3s ease;
+}
+
+.form-group input:focus {
+ border-color: var(--primary);
+ background: rgba(0, 240, 255, 0.05);
+ box-shadow: var(--glow-primary);
+}
+```
+
+---
+
+## 📐 Spacing & Layout
+
+### Spacing Scale
+```css
+--spacing-xs: 0.5rem; /* 8px */
+--spacing-sm: 0.75rem; /* 12px */
+--spacing-md: 1rem; /* 16px */
+--spacing-lg: 1.5rem; /* 24px */
+--spacing-xl: 2rem; /* 32px */
+--spacing-2xl: 3rem; /* 48px */
+--spacing-3xl: 4rem; /* 64px */
+```
+
+### Container Widths
+```css
+--container-sm: 640px;
+--container-md: 768px;
+--container-lg: 1024px;
+--container-xl: 1280px;
+--container-2xl: 1536px;
+```
+
+### Border Radius
+```css
+--radius-sm: 8px;
+--radius-md: 12px;
+--radius-lg: 15px;
+--radius-xl: 20px;
+--radius-full: 50%;
+```
+
+---
+
+## 🎭 Z-Index Scale
+
+```css
+--z-dropdown: 1000;
+--z-sticky: 1020;
+--z-fixed: 1030;
+--z-modal-backdrop: 1040;
+--z-modal: 1050;
+--z-popover: 1060;
+--z-tooltip: 1070;
+```
+
+---
+
+## ♿ Accessibility
+
+### Color Contrast
+- **Text on background:** Minimum 4.5:1 (WCAG AA)
+- **Large text:** Minimum 3:1 (WCAG AA)
+- **UI components:** Minimum 3:1
+
+### Focus States
+```css
+:focus-visible {
+ outline: 2px solid var(--primary);
+ outline-offset: 2px;
+}
+```
+
+### Reduced Motion
+```css
+@media (prefers-reduced-motion: reduce) {
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+```
+
+### Touch Targets
+- Minimum size: **44x44px** (mobile)
+- Spacing between targets: **8px minimum**
+
+---
+
+## 📱 Responsive Breakpoints
+
+```css
+--breakpoint-sm: 640px; /* Mobile */
+--breakpoint-md: 768px; /* Tablet */
+--breakpoint-lg: 1024px; /* Desktop */
+--breakpoint-xl: 1280px; /* Wide */
+--breakpoint-2xl: 1536px; /* Extra wide */
+```
+
+---
+
+## 🚀 Anti-Patterns à Éviter
+
+❌ **Ne pas faire:**
+1. Utiliser des emojis comme icônes (utiliser SVG)
+2. Animer `width`, `height`, `top`, `left`
+3. Animations infinies sur les éléments décoratifs
+4. Surcharger d'animations trop simultanées
+5. Néon trop brillant qui fatigue les yeux
+6. Texte sans contraste suffisant en mode clair
+7. Layout qui change au hover (scale uniquement)
+
+✅ **Faire:**
+1. Utiliser des SVG icon (Heroicons, Lucide)
+2. Animer `transform`, `opacity`, `filter`
+3. Animations continues seulement pour les loaders
+4. Limiter à 1-2 animations par élément
+5. Utiliser des ombres néon subtiles
+6. Contraste 4.5:1 minimum
+7. Transitions fluides sans layout shift
+
+---
+
+## 🎨 Composants Spécifiques AudiOhm
+
+### Player Bar
+- Position: Fixed bottom
+- Height: 90px
+- Background: Glassmorphism
+- Border top: Gradient rainbow (2px)
+- Controls: Shuffle, Prev, Play/Pause, Next, Repeat
+- Progress bar: Custom range input avec thumb néon
+- Volume: Slider + bouton mute
+- Actions: Like, Add to playlist
+
+### Sidebar
+- Width: 280px (desktop)
+- Background: Dark avec ligne supérieure gradient
+- Navigation: 3 items (Accueil, Rechercher, Bibliothèque)
+- Active state: Background cyan/10 + barre latérale
+- Footer: Bouton déconnexion
+
+### Cards
+- Track card: Image + Titre + Artiste + Durée + Bouton play
+- Playlist card: Image carrée + Nom + Info tracks
+- Hover: Border cyan + Glow + Scale 1.01
+
+### Loading States
+- Spinner: Double rotation (cyan + violet)
+- Skeleton: Shimmer animation
+- Text: "Chargement..." animé
+
+---
+
+## 📦 Éléments de Marque
+
+### Logo
+- Icon: Casque audio (FontAwesome)
+- Text: "AudiOhm"
+- Font: Righteous
+- Animation: Glow cyclique (3 couleurs)
+
+### Couleurs de Marque
+- Primary: Cyan (#00F0FF)
+- Secondary: Violet (#BF00FF)
+- Accent: Rose (#FF006E)
+
+### Tone of Voice
+- Moderne
+- Énergique
+- Futuriste
+- Accessible
+- Fun
+
+---
+
+## 🎯 Priorités d'Implémentation
+
+### Phase 1 - Fondations (CRITICAL)
+1. ✅ Variables CSS définies
+2. ✅ Typographie importée
+3. ✅ Couleurs configurées
+4. ✅ Reset CSS
+
+### Phase 2 - Composants Base (HIGH)
+1. Boutons (primary, secondary)
+2. Form inputs
+3. Cards (track, playlist)
+4. Navigation
+
+### Phase 3 - Animations (MEDIUM)
+1. Hover states
+2. Transitions
+3. Loading states
+4. Micro-interactions
+
+### Phase 4 - Polish (LOW)
+1. Scroll animations
+2. Parallax effects
+3. Advanced hover effects
+4. Particle effects
+
+---
+
+## 📚 Ressources
+
+### Google Fonts
+https://fonts.google.com/share?selection?family=Poppins:wght@300;400;500;600;700|Righteous
+
+### Icon Libraries
+- Font Awesome 6.5: https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.0/css/all.min.css
+- Heroicons: https://heroicons.com/
+- Lucide: https://lucide.dev/
+
+### Documentation
+- CSS Tricks: https://css-tricks.com/
+- MDN Web Docs: https://developer.mozilla.org/
+- Web.dev: https://web.dev/
+
+---
+
+**Version:** 2.0
+**Last Updated:** 2026-01-19
+**Maintained by:** AudiOhm Design Team