- {THEMES.map(t => (
-
setTheme(t.id)}
+
+ {/* Volume */}
+
+ {
+ const newVol = volume > 0 ? 0 : 0.5;
+ setVolume(newVol);
+ if (guildId) apiSetVolumeLive(guildId, newVol).catch(() => {});
+ }}
+ style={{ cursor: 'pointer' }}
+ >
+ {volume === 0 ? 'volume_off' : volume < 0.5 ? 'volume_down' : 'volume_up'}
+
+ {
+ const v = parseFloat(e.target.value);
+ setVolume(v);
+ if (guildId) {
+ if (volDebounceRef.current) clearTimeout(volDebounceRef.current);
+ volDebounceRef.current = setTimeout(() => {
+ apiSetVolumeLive(guildId, v).catch(() => {});
+ }, 120);
+ }
+ }}
+ style={{ '--vol': `${Math.round(volume * 100)}%` } as React.CSSProperties}
/>
- ))}
-
-
-
-
-
-
library_music
-
- Sounds gesamt
- {totalSoundsDisplay}
+ {Math.round(volume * 100)}%
-
-
-
leaderboard
-
-
Most Played
-
- {analyticsTop.length === 0 ? (
- Noch keine Plays
- ) : (
- analyticsTop.map((item, idx) => (
-
- {idx + 1}. {item.name} ({item.count})
-
- ))
- )}
-
+ {/* Channel selector */}
+
e.stopPropagation()}>
+
+ {channelOpen && (
+
+ {Object.entries(channelsByGuild).map(([guild, chs]) => (
+
+ {guild}
+ {chs.map(ch => (
+ handleChannelSelect(ch)}
+ >
+ volume_up
+ {ch.channelName}{ch.members ? ` (${ch.members})` : ''}
+
+ ))}
+
+ ))}
+ {channels.length === 0 && (
+
+ Keine Channels verfuegbar
+
+ )}
+
+ )}
+
+
+ {/* Card size slider */}
+
+ grid_view
+ setCardSize(parseInt(e.target.value))}
+ />
+ {/* ═══ MOST PLAYED / ANALYTICS ═══ */}
+ {analyticsTop.length > 0 && (
+
+
+ leaderboard
+ Most Played
+
+
+ {analyticsTop.map((item, idx) => (
+
{
+ const found = sounds.find(s => (s.relativePath ?? s.fileName) === item.relativePath);
+ if (found) handlePlay(found);
+ }}
+ >
+ {idx + 1}
+ {item.name}
+ {item.count}
+
+ ))}
+
+
+ )}
+
{/* ═══ FOLDER CHIPS ═══ */}
{activeTab === 'all' && visibleFolders.length > 0 && (
@@ -1111,8 +1201,8 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp }: Soundboard
)}
- {/* ═══ MAIN ═══ */}
-
+ {/* ═══ SOUND GRID ═══ */}
+
{displaySounds.length === 0 ? (
{activeTab === 'favorites' ? '\u2B50' : '\uD83D\uDD07'}
@@ -1129,66 +1219,88 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp }: Soundboard
: 'Hier gibt\'s noch nichts zu hoeren.'}
- ) : (
-
- {displaySounds.map((s, idx) => {
- const key = s.relativePath ?? s.fileName;
- const isFav = !!favs[key];
- const isPlaying = lastPlayed === s.name;
- const isNew = s.isRecent || s.badges?.includes('new');
- const initial = s.name.charAt(0).toUpperCase();
- const showInitial = firstOfInitial.has(idx);
- const folderColor = s.folder ? (folderColorMap[s.folder] || 'var(--accent)') : 'var(--accent)';
+ ) : (() => {
+ // Group sounds by initial letter for category headers
+ const groups: { letter: string; sounds: { sound: Sound; globalIdx: number }[] }[] = [];
+ let currentLetter = '';
+ displaySounds.forEach((s, idx) => {
+ const ch = s.name.charAt(0).toUpperCase();
+ const letter = /[A-Z]/.test(ch) ? ch : '#';
+ if (letter !== currentLetter) {
+ currentLetter = letter;
+ groups.push({ letter, sounds: [] });
+ }
+ groups[groups.length - 1].sounds.push({ sound: s, globalIdx: idx });
+ });
- return (
-
{
- const card = e.currentTarget;
- const rect = card.getBoundingClientRect();
- const ripple = document.createElement('div');
- ripple.className = 'ripple';
- const sz = Math.max(rect.width, rect.height);
- ripple.style.width = ripple.style.height = sz + 'px';
- ripple.style.left = (e.clientX - rect.left - sz / 2) + 'px';
- ripple.style.top = (e.clientY - rect.top - sz / 2) + 'px';
- card.appendChild(ripple);
- setTimeout(() => ripple.remove(), 500);
- handlePlay(s);
- }}
- onContextMenu={e => {
- e.preventDefault();
- e.stopPropagation();
- setCtxMenu({
- x: Math.min(e.clientX, window.innerWidth - 170),
- y: Math.min(e.clientY, window.innerHeight - 140),
- sound: s,
- });
- }}
- title={`${s.name}${s.folder ? ` (${s.folder})` : ''}`}
- >
- {isNew &&
NEU}
-
{ e.stopPropagation(); toggleFav(key); }}
- >
- {isFav ? 'star' : 'star_border'}
-
- {showInitial &&
{initial}}
-
{s.name}
- {s.folder &&
{s.folder}}
-
-
- );
- })}
-
- )}
-
+ return groups.map(group => (
+
+
+ {group.letter}
+ {group.sounds.length} Sound{group.sounds.length !== 1 ? 's' : ''}
+
+
+
+ {group.sounds.map(({ sound: s, globalIdx: idx }) => {
+ const key = s.relativePath ?? s.fileName;
+ const isFav = !!favs[key];
+ const isPlaying = lastPlayed === s.name;
+ const isNew = s.isRecent || s.badges?.includes('new');
+ const initial = s.name.charAt(0).toUpperCase();
+ const showInitial = firstOfInitial.has(idx);
+ const folderColor = s.folder ? (folderColorMap[s.folder] || 'var(--accent)') : 'var(--accent)';
+
+ return (
+
{
+ const card = e.currentTarget;
+ const rect = card.getBoundingClientRect();
+ const ripple = document.createElement('div');
+ ripple.className = 'ripple';
+ const sz = Math.max(rect.width, rect.height);
+ ripple.style.width = ripple.style.height = sz + 'px';
+ ripple.style.left = (e.clientX - rect.left - sz / 2) + 'px';
+ ripple.style.top = (e.clientY - rect.top - sz / 2) + 'px';
+ card.appendChild(ripple);
+ setTimeout(() => ripple.remove(), 500);
+ handlePlay(s);
+ }}
+ onContextMenu={e => {
+ e.preventDefault();
+ e.stopPropagation();
+ setCtxMenu({
+ x: Math.min(e.clientX, window.innerWidth - 170),
+ y: Math.min(e.clientY, window.innerHeight - 140),
+ sound: s,
+ });
+ }}
+ title={`${s.name}${s.folder ? ` (${s.folder})` : ''}`}
+ >
+ {isNew &&
NEU}
+
{ e.stopPropagation(); toggleFav(key); }}
+ >
+ {isFav ? 'star' : 'star_border'}
+
+ {showInitial &&
{initial}}
+
{s.name}
+ {s.folder &&
{s.folder}}
+
+
+ );
+ })}
+
+
+ ));
+ })()}
+
{/* ═══ CONTEXT MENU ═══ */}
{ctxMenu && (
@@ -1215,14 +1327,7 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp }: Soundboard
{
const path = ctxMenu.sound.relativePath ?? ctxMenu.sound.fileName;
- if (!window.confirm(`Sound "${ctxMenu.sound.name}" loeschen?`)) { setCtxMenu(null); return; }
- try {
- await apiAdminDelete([path]);
- notify('Sound geloescht');
- setRefreshKey(k => k + 1);
- } catch (e: any) {
- notify(e?.message || 'Loeschen fehlgeschlagen', 'error');
- }
+ await deleteAdminPaths([path]);
setCtxMenu(null);
}}>
delete
@@ -1303,6 +1408,142 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp }: Soundboard
)}
+ {/* ═══ ADMIN PANEL ═══ */}
+ {showAdmin && (
+
{ if (e.target === e.currentTarget) setShowAdmin(false); }}>
+
+
+ Admin
+
+
+
+
+
Eingeloggt als Admin
+
+
+
+
+
+
+
+ setAdminQuery(e.target.value)}
+ placeholder="Nach Name, Ordner oder Pfad filtern..."
+ />
+
+
+
+
+
+
+
+
+
+ {adminLoading ? (
+
Lade Sounds...
+ ) : adminFilteredSounds.length === 0 ? (
+
Keine Sounds gefunden.
+ ) : (
+
+ {adminFilteredSounds.map(sound => {
+ const key = soundKey(sound);
+ const editing = renameTarget === key;
+ return (
+
+
+
+
+
{sound.name}
+
+ {sound.folder ? `Ordner: ${sound.folder}` : 'Root'}
+ {' \u00B7 '}
+ {key}
+
+ {editing && (
+
+ setRenameValue(e.target.value)}
+ onKeyDown={e => {
+ if (e.key === 'Enter') void submitRename();
+ if (e.key === 'Escape') cancelRename();
+ }}
+ placeholder="Neuer Name..."
+ />
+
+
+
+ )}
+
+
+ {!editing && (
+
+
+
+
+ )}
+
+ );
+ })}
+
+ )}
+
+
+
+
+ )}
+
{/* ── Drag & Drop Overlay ── */}
{isDragging && (
@@ -1426,7 +1667,7 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp }: Soundboard
{dropPhase === 'naming' && (
)}
+ {isAdmin && (
+
+ )}
{streams.length === 0 && !isBroadcasting ? (
@@ -801,6 +881,80 @@ export default function StreamingTab({ data, isAdmin: isAdminProp }: { data: any
)}
+ {/* ── Notification Admin Modal ── */}
+ {showAdmin && (
+
setShowAdmin(false)}>
+
e.stopPropagation()}>
+
+
{'\uD83D\uDD14'} Benachrichtigungen
+
+
+
+
+
+
+ {notifyStatus.online
+ ? <>{'\u2705'} Bot online: {notifyStatus.botTag}>
+ : <>{'\u26A0\uFE0F'} Bot offline — DISCORD_TOKEN_NOTIFICATIONS setzen>}
+
+
+
+ {configLoading ? (
+
Lade Kan{'\u00E4'}le...
+ ) : availableChannels.length === 0 ? (
+
+ {notifyStatus.online
+ ? 'Keine Text-Kan\u00E4le gefunden. Bot hat m\u00F6glicherweise keinen Zugriff.'
+ : 'Bot ist nicht verbunden. Bitte DISCORD_TOKEN_NOTIFICATIONS konfigurieren.'}
+
+ ) : (
+ <>
+
+ W{'\u00E4'}hle die Kan{'\u00E4'}le, in die Benachrichtigungen gesendet werden sollen:
+
+
+ {availableChannels.map(ch => (
+
+ ))}
+
+
+
+
+ >
+ )}
+
+
+
+ )}
);
}
diff --git a/web/src/plugins/streaming/streaming.css b/web/src/plugins/streaming/streaming.css
index abdf17a..bb90670 100644
--- a/web/src/plugins/streaming/streaming.css
+++ b/web/src/plugins/streaming/streaming.css
@@ -373,23 +373,25 @@
/* ── Empty state ── */
.stream-empty {
- text-align: center;
- padding: 60px 20px;
- color: var(--text-muted);
+ flex: 1; display: flex; flex-direction: column;
+ align-items: center; justify-content: center; gap: 16px;
+ padding: 40px; height: 100%;
}
.stream-empty-icon {
- font-size: 48px;
- margin-bottom: 12px;
- opacity: 0.4;
+ font-size: 64px; line-height: 1;
+ animation: stream-float 3s ease-in-out infinite;
+}
+@keyframes stream-float {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-8px); }
}
.stream-empty h3 {
- font-size: 18px;
- font-weight: 600;
- color: var(--text-normal);
- margin-bottom: 6px;
+ font-size: 26px; font-weight: 700; color: #f2f3f5;
+ letter-spacing: -0.5px; margin: 0;
}
.stream-empty p {
- font-size: 14px;
+ font-size: 15px; color: #80848e;
+ text-align: center; max-width: 360px; line-height: 1.5; margin: 0;
}
/* ── Error ── */
diff --git a/web/src/plugins/watch-together/watch-together.css b/web/src/plugins/watch-together/watch-together.css
index 32684b4..95111c5 100644
--- a/web/src/plugins/watch-together/watch-together.css
+++ b/web/src/plugins/watch-together/watch-together.css
@@ -161,23 +161,25 @@
/* ── Empty state ── */
.wt-empty {
- text-align: center;
- padding: 60px 20px;
- color: var(--text-muted);
+ flex: 1; display: flex; flex-direction: column;
+ align-items: center; justify-content: center; gap: 16px;
+ padding: 40px; height: 100%;
}
.wt-empty-icon {
- font-size: 48px;
- margin-bottom: 12px;
- opacity: 0.4;
+ font-size: 64px; line-height: 1;
+ animation: wt-float 3s ease-in-out infinite;
+}
+@keyframes wt-float {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-8px); }
}
.wt-empty h3 {
- font-size: 18px;
- font-weight: 600;
- color: var(--text-normal);
- margin-bottom: 6px;
+ font-size: 26px; font-weight: 700; color: #f2f3f5;
+ letter-spacing: -0.5px; margin: 0;
}
.wt-empty p {
- font-size: 14px;
+ font-size: 15px; color: #80848e;
+ text-align: center; max-width: 360px; line-height: 1.5; margin: 0;
}
/* ── Error ── */
@@ -556,7 +558,7 @@
.wt-quality-select {
background: var(--bg-secondary, #2a2620);
color: var(--text-primary, #e0e0e0);
- border: 1px solid var(--border-color, #322d26);
+ border: 1px solid var(--border-color, #3a352d);
border-radius: 6px;
padding: 2px 6px;
font-size: 12px;
diff --git a/web/src/styles.css b/web/src/styles.css
index 4d5c046..6319c92 100644
--- a/web/src/styles.css
+++ b/web/src/styles.css
@@ -1,38 +1,169 @@
-/* ── Google Fonts ── */
+/* ============================================================
+ GAMING HUB -- Global Styles
+ Design System v3.0 -- CI Redesign (warm brown, DM Sans)
+ ============================================================ */
+
+/* -- Google Fonts ------------------------------------------- */
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500;9..40,600;9..40,700&family=DM+Mono:wght@400;500&display=swap');
-/* ── CSS Variables ── */
+/* ============================================================
+ A) CSS CUSTOM PROPERTIES
+ ============================================================ */
:root {
- --bg-deep: #1a1810;
+ /* -- Surface Palette (warm brown) ------------------------- */
+ --bg-deepest: #141209;
+ --bg-deep: #1a1810;
--bg-primary: #211e17;
--bg-secondary: #2a2620;
--bg-tertiary: #322d26;
- --bg-card: #2a2620;
- --bg-card-hover: #322d26;
- --bg-input: #1e1b15;
- --bg-header: #1e1b14;
- --text-normal: #dbdee1;
- --text-muted: #949ba4;
- --text-faint: #6d6f78;
- --accent: #e67e22;
- --accent-rgb: 230, 126, 34;
- --accent-hover: #d35400;
- --accent-dim: rgba(230, 126, 34, 0.15);
- --accent-border: rgba(230, 126, 34, 0.35);
+ --bg-elevated: #3a352d;
+ --bg-hover: #3e3830;
+ --bg-active: #453f36;
+ --bg-input: #1e1b15;
+ --bg-header: #1e1b14;
+
+ /* -- Text Colors ------------------------------------------ */
+ --text-primary: #dbdee1;
+ --text-secondary: #949ba4;
+ --text-tertiary: #6d6f78;
+ --text-disabled: #4a4940;
+
+ /* -- Semantic Colors -------------------------------------- */
--success: #57d28f;
- --danger: #ed4245;
--warning: #fee75c;
- --border: rgba(255, 255, 255, 0.05);
- --border-strong: rgba(255, 255, 255, 0.08);
- --radius: 4px;
- --radius-lg: 6px;
- --transition: 150ms ease;
- --font: 'DM Sans', system-ui, -apple-system, sans-serif;
- --mono: 'DM Mono', monospace;
- --header-height: 44px;
+ --danger: #ed4245;
+ --info: #5865f2;
+
+ /* -- Surface Tokens (solid, no glass) --------------------- */
+ --surface-glass: rgba(255, 255, 255, .04);
+ --surface-glass-hover: rgba(255, 255, 255, .07);
+ --surface-glass-active: rgba(255, 255, 255, .10);
+ --surface-glass-border: rgba(255, 255, 255, .05);
+ --surface-glass-border-hover: rgba(255, 255, 255, .10);
+
+ /* -- Border Tokens ---------------------------------------- */
+ --border-subtle: rgba(255, 255, 255, .05);
+ --border-default: rgba(255, 255, 255, .08);
+ --border-strong: rgba(255, 255, 255, .12);
+
+ /* -- Accent Helpers --------------------------------------- */
+ --accent-dim: rgba(230, 126, 34, 0.15);
+ --accent-border: rgba(230, 126, 34, 0.35);
+
+ /* -- Font Stacks ------------------------------------------ */
+ --font-display: 'DM Sans', system-ui, sans-serif;
+ --font-body: 'DM Sans', system-ui, -apple-system, sans-serif;
+ --font-mono: 'DM Mono', monospace;
+
+ /* -- Type Scale ------------------------------------------- */
+ --text-xs: 11px;
+ --text-sm: 12px;
+ --text-base: 13px;
+ --text-md: 14px;
+ --text-lg: 16px;
+ --text-xl: 20px;
+ --text-2xl: 24px;
+ --text-3xl: 32px;
+
+ /* -- Font Weights ----------------------------------------- */
+ --weight-regular: 400;
+ --weight-medium: 500;
+ --weight-semibold: 600;
+ --weight-bold: 700;
+
+ /* -- Spacing (4px base grid) ------------------------------ */
+ --space-1: 4px;
+ --space-2: 8px;
+ --space-3: 12px;
+ --space-4: 16px;
+ --space-5: 20px;
+ --space-6: 24px;
+ --space-7: 32px;
+ --space-8: 40px;
+ --space-9: 48px;
+ --space-10: 64px;
+
+ /* -- Border Radii ----------------------------------------- */
+ --radius-xs: 3px;
+ --radius-sm: 4px;
+ --radius-md: 6px;
+ --radius-lg: 8px;
+ --radius-xl: 8px;
+ --radius-full: 9999px;
+
+ /* -- Shadows / Elevation ---------------------------------- */
+ --shadow-xs: 0 1px 2px rgba(0, 0, 0, .25);
+ --shadow-sm: 0 2px 6px rgba(0, 0, 0, .3);
+ --shadow-md: 0 2px 8px rgba(0, 0, 0, .35);
+ --shadow-lg: 0 4px 16px rgba(0, 0, 0, .4);
+ --shadow-xl: 0 8px 24px rgba(0, 0, 0, .45);
+
+ /* -- Motion ----------------------------------------------- */
+ --duration-fast: 100ms;
+ --duration-normal: 150ms;
+ --duration-slow: 200ms;
+ --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
+ --ease-in-out: cubic-bezier(0.76, 0, 0.24, 1);
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
+
+ /* -- Layout ----------------------------------------------- */
+ --sidebar-nav-w: 200px;
+ --header-h: 44px;
}
-/* ── Reset & Base ── */
+
+/* ============================================================
+ B) ACCENT THEME SYSTEM
+ ============================================================ */
+
+/* Default: Ember (orange) */
+:root,
+[data-accent="ember"] {
+ --accent: #e67e22;
+ --accent-hover: #d35400;
+ --accent-soft: rgba(230, 126, 34, 0.15);
+ --accent-text: #f0a050;
+}
+
+[data-accent="amethyst"] {
+ --accent: #9b59b6;
+ --accent-hover: #8e44ad;
+ --accent-soft: rgba(155, 89, 182, 0.15);
+ --accent-text: #b580d0;
+}
+
+[data-accent="ocean"] {
+ --accent: #2e86c1;
+ --accent-hover: #2471a3;
+ --accent-soft: rgba(46, 134, 193, 0.15);
+ --accent-text: #5da8d8;
+}
+
+[data-accent="jade"] {
+ --accent: #27ae60;
+ --accent-hover: #1e8449;
+ --accent-soft: rgba(39, 174, 96, 0.15);
+ --accent-text: #52c47a;
+}
+
+[data-accent="rose"] {
+ --accent: #e74c8b;
+ --accent-hover: #c0397a;
+ --accent-soft: rgba(231, 76, 139, 0.15);
+ --accent-text: #f07aa8;
+}
+
+[data-accent="crimson"] {
+ --accent: #d63031;
+ --accent-hover: #b52728;
+ --accent-soft: rgba(214, 48, 49, 0.15);
+ --accent-text: #e25b5c;
+}
+
+
+/* ============================================================
+ C) GLOBAL RESET & BASE
+ ============================================================ */
*,
*::before,
*::after {
@@ -41,506 +172,828 @@
box-sizing: border-box;
}
+html {
+ scroll-behavior: smooth;
+}
+
html, body {
height: 100%;
- font-family: var(--font);
- font-size: 13px;
- color: var(--text-normal);
- background: var(--bg-deep);
+ overflow: hidden;
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ color: var(--text-primary);
+ background: var(--bg-primary);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
- overflow: hidden;
}
#root {
height: 100%;
}
-/* ── App Shell ── */
-.hub-app {
+/* -- Scrollbar ---------------------------------------------- */
+::-webkit-scrollbar {
+ width: 6px;
+ height: 6px;
+}
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, .12);
+ border-radius: var(--radius-full);
+}
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, .2);
+}
+
+/* Firefox */
+* {
+ scrollbar-width: thin;
+ scrollbar-color: rgba(255, 255, 255, .12) transparent;
+}
+
+/* -- Selection ---------------------------------------------- */
+::selection {
+ background: var(--accent-soft);
+ color: var(--accent-text);
+}
+
+/* -- Focus -------------------------------------------------- */
+:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
+}
+
+
+/* ============================================================
+ D) APP LAYOUT (Sidebar)
+ ============================================================ */
+.app-shell {
display: flex;
- flex-direction: column;
height: 100vh;
+ width: 100vw;
overflow: hidden;
}
-/* ── Header ── */
-.hub-header {
- position: sticky;
- top: 0;
- z-index: 100;
+/* -- Sidebar ------------------------------------------------ */
+.app-sidebar {
+ width: var(--sidebar-nav-w);
+ min-width: var(--sidebar-nav-w);
+ background: var(--bg-deep);
display: flex;
- align-items: center;
- height: var(--header-height);
- min-height: var(--header-height);
- padding: 0 16px;
- background: var(--bg-primary);
- border-bottom: 1px solid var(--border);
- gap: 16px;
+ flex-direction: column;
+ border-right: 1px solid var(--border-subtle);
+ z-index: 15;
}
-.hub-header-left {
+.sidebar-header {
+ height: var(--header-h);
display: flex;
align-items: center;
- gap: 10px;
+ padding: 0 var(--space-3);
+ border-bottom: 1px solid var(--border-subtle);
+ gap: var(--space-2);
flex-shrink: 0;
}
-.hub-logo {
- font-size: 24px;
- line-height: 1;
+.sidebar-logo {
+ width: 32px;
+ height: 32px;
+ min-width: 32px;
+ border-radius: var(--radius-md);
+ background: var(--accent);
+ display: grid;
+ place-items: center;
+ font-family: var(--font-display);
+ font-weight: var(--weight-bold);
+ font-size: var(--text-base);
+ color: #fff;
+ /* no glow */
+ flex-shrink: 0;
}
-.hub-title {
- font-size: 18px;
- font-weight: 700;
- color: var(--text-normal);
+.sidebar-brand {
+ font-family: var(--font-display);
+ font-weight: var(--weight-bold);
+ font-size: var(--text-md);
letter-spacing: -0.02em;
+ background: var(--accent);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
white-space: nowrap;
}
-/* ── Connection Status Dot ── */
-.hub-conn-dot {
- width: 10px;
- height: 10px;
- border-radius: 50%;
- background: var(--danger);
- flex-shrink: 0;
- transition: background var(--transition);
- box-shadow: 0 0 0 2px rgba(237, 66, 69, 0.25);
+.sidebar-nav {
+ flex: 1;
+ overflow-y: auto;
+ padding: var(--space-2);
}
-.hub-conn-dot.online {
+.sidebar-section-label {
+ padding: var(--space-5) var(--space-4) var(--space-2);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-semibold);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-tertiary);
+}
+
+/* -- Main --------------------------------------------------- */
+.app-main {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ background: var(--bg-primary);
+ position: relative;
+}
+
+/* -- Content Header ----------------------------------------- */
+.content-header {
+ height: var(--header-h);
+ min-height: var(--header-h);
+ display: flex;
+ align-items: center;
+ padding: 0 var(--space-6);
+ border-bottom: 1px solid var(--border-subtle);
+ gap: var(--space-4);
+ position: relative;
+ z-index: 5;
+ flex-shrink: 0;
+}
+
+.content-header__title {
+ font-family: var(--font-display);
+ font-size: var(--text-lg);
+ font-weight: var(--weight-semibold);
+ letter-spacing: -0.02em;
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ flex-shrink: 0;
+}
+
+.content-header__title .sound-count {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--accent);
+ background: var(--accent-soft);
+ padding: 2px 8px;
+ border-radius: var(--radius-full);
+ font-weight: var(--weight-medium);
+}
+
+.content-header__search {
+ flex: 1;
+ max-width: 320px;
+ height: 34px;
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: 0 var(--space-3);
+ border-radius: var(--radius-sm);
+ background: var(--surface-glass);
+ border: 1px solid var(--surface-glass-border);
+ color: var(--text-secondary);
+ font-size: var(--text-sm);
+ transition: all var(--duration-fast);
+}
+
+.content-header__search:focus-within {
+ border-color: var(--accent);
+ background: var(--surface-glass-hover);
+ box-shadow: 0 0 0 3px var(--accent-soft);
+}
+
+.content-header__search input {
+ flex: 1;
+ background: none;
+ border: none;
+ outline: none;
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-sm);
+}
+
+.content-header__search input::placeholder {
+ color: var(--text-tertiary);
+}
+
+.content-header__actions {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ margin-left: auto;
+}
+
+/* -- Content Area ------------------------------------------- */
+.content-area {
+ flex: 1;
+ overflow-y: auto;
+ background: var(--bg-primary);
+ position: relative;
+}
+
+
+/* ============================================================
+ E) SIDEBAR NAVIGATION ITEMS
+ ============================================================ */
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-sm);
+ color: var(--text-secondary);
+ cursor: pointer;
+ transition: all var(--duration-fast) ease;
+ position: relative;
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ margin-bottom: 1px;
+ text-decoration: none;
+ border: none;
+ background: none;
+ width: 100%;
+ text-align: left;
+}
+
+.nav-item:hover {
+ background: var(--bg-hover);
+ color: var(--text-primary);
+}
+
+.nav-item.active {
+ background: var(--accent-soft);
+ color: var(--accent-text);
+}
+
+.nav-item.active .nav-icon {
+ color: var(--accent);
+}
+
+.nav-icon {
+ width: 20px;
+ height: 20px;
+ display: grid;
+ place-items: center;
+ font-size: 16px;
+ flex-shrink: 0;
+}
+
+.nav-label {
+ flex: 1;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+/* -- Notification Badge on Nav Items ------------------------ */
+.nav-badge {
+ margin-left: auto;
+ background: var(--danger);
+ color: #fff;
+ font-size: 10px;
+ font-weight: var(--weight-bold);
+ min-width: 18px;
+ height: 18px;
+ border-radius: var(--radius-full);
+ display: grid;
+ place-items: center;
+ padding: 0 5px;
+}
+
+/* -- Now-Playing Indicator in Nav --------------------------- */
+.nav-now-playing {
+ margin-left: auto;
+ width: 14px;
+ display: flex;
+ align-items: flex-end;
+ gap: 2px;
+ height: 14px;
+}
+
+.nav-now-playing span {
+ display: block;
+ width: 2px;
+ border-radius: 1px;
+ background: var(--accent);
+ animation: eq-bar 1.2s ease-in-out infinite;
+}
+
+.nav-now-playing span:nth-child(1) { height: 40%; animation-delay: 0s; }
+.nav-now-playing span:nth-child(2) { height: 70%; animation-delay: 0.2s; }
+.nav-now-playing span:nth-child(3) { height: 50%; animation-delay: 0.4s; }
+
+@keyframes eq-bar {
+ 0%, 100% { transform: scaleY(0.3); }
+ 50% { transform: scaleY(1); }
+}
+
+
+/* ============================================================
+ F) CHANNEL DROPDOWN (in Sidebar)
+ ============================================================ */
+.channel-dropdown {
+ position: relative;
+ margin: 0 var(--space-2);
+ padding: var(--space-2) 0;
+}
+
+.channel-dropdown__trigger {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-sm);
+ cursor: pointer;
+ transition: all var(--duration-fast);
+ background: var(--surface-glass);
+ border: 1px solid var(--surface-glass-border);
+ width: 100%;
+ font-family: var(--font-body);
+ color: var(--text-primary);
+}
+
+.channel-dropdown__trigger:hover {
+ background: var(--surface-glass-hover);
+ border-color: var(--surface-glass-border-hover);
+}
+
+.channel-dropdown__trigger .channel-icon {
+ color: var(--text-tertiary);
+ flex-shrink: 0;
+}
+
+.channel-dropdown__trigger .channel-name {
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ color: var(--text-primary);
+ flex: 1;
+ text-align: left;
+}
+
+.channel-dropdown__trigger .channel-arrow {
+ color: var(--text-tertiary);
+ transition: transform var(--duration-fast);
+ font-size: var(--text-sm);
+}
+
+.channel-dropdown.open .channel-arrow {
+ transform: rotate(180deg);
+}
+
+.channel-dropdown__menu {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ right: 0;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-default);
+ border-radius: var(--radius-md);
+ padding: var(--space-1);
+ z-index: 50;
+ box-shadow: var(--shadow-lg);
+ display: none;
+ animation: dropdown-in var(--duration-normal) var(--ease-out);
+}
+
+.channel-dropdown.open .channel-dropdown__menu {
+ display: block;
+}
+
+@keyframes dropdown-in {
+ from { opacity: 0; transform: translateY(-4px); }
+}
+
+.channel-dropdown__item {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ border-radius: var(--radius-xs);
+ cursor: pointer;
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
+ transition: all var(--duration-fast);
+ border: none;
+ background: none;
+ width: 100%;
+ font-family: var(--font-body);
+ text-align: left;
+}
+
+.channel-dropdown__item:hover {
+ background: var(--surface-glass-hover);
+ color: var(--text-primary);
+}
+
+.channel-dropdown__item.selected {
+ background: var(--accent-soft);
+ color: var(--accent-text);
+}
+
+
+/* ============================================================
+ G) SIDEBAR FOOTER (User)
+ ============================================================ */
+.sidebar-footer {
+ padding: var(--space-2) var(--space-3);
+ border-top: 1px solid var(--border-subtle);
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ flex-shrink: 0;
+}
+
+.sidebar-avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: var(--radius-full);
+ background: var(--accent);
+ display: grid;
+ place-items: center;
+ font-size: var(--text-base);
+ font-weight: var(--weight-bold);
+ color: #fff;
+ position: relative;
+ flex-shrink: 0;
+}
+
+.sidebar-avatar .status-dot {
+ position: absolute;
+ bottom: -1px;
+ right: -1px;
+ width: 12px;
+ height: 12px;
+ border-radius: var(--radius-full);
+ background: var(--success);
+ border: 2.5px solid var(--bg-deep);
+}
+
+.sidebar-avatar .status-dot.warning {
+ background: var(--warning);
+}
+
+.sidebar-avatar .status-dot.offline {
+ background: var(--danger);
+}
+
+@keyframes pulse-status {
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(87, 210, 143, .4); }
+ 50% { box-shadow: 0 0 0 5px rgba(87, 210, 143, 0); }
+}
+
+.sidebar-avatar .status-dot.online {
+ animation: pulse-status 2s ease-in-out infinite;
+}
+
+.sidebar-user-info {
+ flex: 1;
+ min-width: 0;
+}
+
+.sidebar-username {
+ font-size: var(--text-sm);
+ font-weight: var(--weight-semibold);
+ color: var(--text-primary);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.sidebar-user-tag {
+ font-size: var(--text-xs);
+ color: var(--text-tertiary);
+}
+
+.sidebar-settings {
+ width: 28px;
+ height: 28px;
+ display: grid;
+ place-items: center;
+ border-radius: var(--radius-sm);
+ cursor: pointer;
+ color: var(--text-tertiary);
+ transition: all var(--duration-fast);
+ background: none;
+ border: none;
+}
+
+.sidebar-settings:hover {
+ background: var(--surface-glass-hover);
+ color: var(--text-primary);
+}
+
+.sidebar-settings.admin-active {
+ color: var(--accent);
+}
+
+/* -- Sidebar Accent Picker ---------------------------------- */
+.sidebar-accent-picker {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ border-top: 1px solid var(--border-subtle);
+}
+
+.accent-swatch {
+ width: 18px;
+ height: 18px;
+ border-radius: var(--radius-full);
+ border: 2px solid transparent;
+ cursor: pointer;
+ transition: all var(--duration-fast) ease;
+ flex-shrink: 0;
+ padding: 0;
+}
+
+.accent-swatch:hover {
+ transform: scale(1.2);
+ border-color: rgba(255, 255, 255, .2);
+}
+
+.accent-swatch.active {
+ border-color: #fff;
+ transform: scale(1.15);
+}
+
+
+/* ============================================================
+ H) CONTENT HEADER EXTRAS
+ ============================================================ */
+
+/* -- Playback Controls -------------------------------------- */
+.playback-controls {
+ display: flex;
+ align-items: center;
+ gap: var(--space-1);
+ padding: 0 var(--space-2);
+ border-left: 1px solid var(--border-subtle);
+ margin-left: var(--space-2);
+}
+
+.playback-btn {
+ height: 32px;
+ padding: 0 var(--space-3);
+ border-radius: var(--radius-sm);
+ display: flex;
+ align-items: center;
+ gap: var(--space-1);
+ font-family: var(--font-body);
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ cursor: pointer;
+ border: 1px solid transparent;
+ transition: all var(--duration-fast);
+ background: var(--surface-glass);
+ color: var(--text-secondary);
+}
+
+.playback-btn:hover {
+ background: var(--surface-glass-hover);
+ color: var(--text-primary);
+}
+
+.playback-btn--stop:hover {
+ background: rgba(237, 66, 69, .12);
+ color: var(--danger);
+ border-color: rgba(237, 66, 69, .2);
+}
+
+.playback-btn--party {
+ background: var(--accent);
+ color: #fff;
+ font-weight: var(--weight-semibold);
+}
+
+.playback-btn--party:hover {
+ opacity: 0.85;
+}
+
+/* -- Theme Picker ------------------------------------------- */
+.theme-picker {
+ display: flex;
+ gap: var(--space-1);
+ align-items: center;
+ padding: var(--space-1);
+ border-radius: var(--radius-full);
+ background: var(--surface-glass);
+ border: 1px solid var(--surface-glass-border);
+}
+
+.theme-swatch {
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ cursor: pointer;
+ border: 2px solid transparent;
+ transition: all var(--duration-fast);
+}
+
+.theme-swatch:hover {
+ transform: scale(1.2);
+}
+
+.theme-swatch.active {
+ border-color: #fff;
+ /* no glow */
+}
+
+.theme-swatch[data-t="ember"] { background: #e67e22; }
+.theme-swatch[data-t="amethyst"] { background: #9b59b6; }
+.theme-swatch[data-t="ocean"] { background: #2e86c1; }
+.theme-swatch[data-t="jade"] { background: #27ae60; }
+.theme-swatch[data-t="rose"] { background: #e74c8b; }
+.theme-swatch[data-t="crimson"] { background: #d63031; }
+
+/* -- Connection Badge --------------------------------------- */
+.connection-badge {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-1) var(--space-3);
+ border-radius: var(--radius-full);
+ font-size: var(--text-xs);
+ font-weight: var(--weight-medium);
+}
+
+.connection-badge.connected {
+ color: var(--success);
+ background: rgba(87, 210, 143, .1);
+}
+
+.connection-badge .dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
background: var(--success);
- box-shadow: 0 0 0 2px rgba(87, 210, 143, 0.25);
animation: pulse-dot 2s ease-in-out infinite;
}
@keyframes pulse-dot {
- 0%, 100% {
- box-shadow: 0 0 0 2px rgba(87, 210, 143, 0.25);
- }
- 50% {
- box-shadow: 0 0 0 6px rgba(87, 210, 143, 0.1);
- }
+ 0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(87, 210, 143, .4); }
+ 50% { opacity: .8; box-shadow: 0 0 0 6px rgba(87, 210, 143, 0); }
}
-/* ── Tab Navigation ── */
-.hub-tabs {
+/* -- Volume Control ----------------------------------------- */
+.volume-control {
display: flex;
align-items: center;
- gap: 4px;
- flex: 1;
- overflow-x: auto;
- scrollbar-width: none;
- -ms-overflow-style: none;
- padding: 0 4px;
-}
-
-.hub-tabs::-webkit-scrollbar {
- display: none;
-}
-
-.hub-tab {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 8px 14px;
- border: none;
- background: transparent;
- color: var(--text-muted);
- font-family: var(--font);
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- border-radius: var(--radius);
- white-space: nowrap;
- transition: all var(--transition);
- position: relative;
- user-select: none;
-}
-
-.hub-tab:hover {
- color: var(--text-normal);
- background: var(--bg-secondary);
-}
-
-.hub-tab.active {
- color: var(--accent);
- background: rgba(var(--accent-rgb), 0.1);
-}
-
-.hub-tab.active::after {
- content: '';
- position: absolute;
- bottom: -1px;
- left: 50%;
- transform: translateX(-50%);
- width: calc(100% - 16px);
- height: 2px;
- background: var(--accent);
- border-radius: 1px;
-}
-
-.hub-tab-icon {
+ gap: var(--space-2);
+ color: var(--text-tertiary);
font-size: 16px;
- line-height: 1;
}
-.hub-tab-label {
- text-transform: capitalize;
-}
-
-/* ── Header Right ── */
-.hub-header-right {
- display: flex;
- align-items: center;
- gap: 12px;
- flex-shrink: 0;
-}
-
-.hub-download-btn {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 10px;
- font-size: 12px;
- font-weight: 500;
- font-family: var(--font);
- text-decoration: none;
- color: var(--text-muted);
- background: var(--bg-secondary);
- border-radius: var(--radius);
+.volume-slider {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 80px;
+ height: 4px;
+ border-radius: 2px;
+ background: var(--bg-elevated);
+ outline: none;
cursor: pointer;
- transition: all var(--transition);
- white-space: nowrap;
-}
-.hub-download-btn:hover {
- color: var(--accent);
- background: rgba(var(--accent-rgb), 0.1);
-}
-.hub-download-icon {
- font-size: 14px;
- line-height: 1;
-}
-.hub-download-label {
- line-height: 1;
}
-.hub-version {
- font-size: 12px;
- color: var(--text-faint);
- font-weight: 500;
- font-variant-numeric: tabular-nums;
- background: var(--bg-secondary);
- padding: 4px 8px;
- border-radius: 4px;
-}
-
-/* ── Check for Updates Button ── */
-.hub-check-update-btn {
- background: none;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- color: var(--text-secondary);
- font-size: 14px;
- padding: 2px 8px;
+.volume-slider::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ background: var(--accent);
cursor: pointer;
- transition: all var(--transition);
- line-height: 1;
-}
-.hub-check-update-btn:hover:not(:disabled) {
- color: var(--accent);
- border-color: var(--accent);
-}
-.hub-check-update-btn:disabled {
- opacity: 0.4;
- cursor: default;
+ border: none;
+ /* no glow */
}
-/* ── Update Modal ── */
+.volume-slider::-moz-range-thumb {
+ width: 14px;
+ height: 14px;
+ border-radius: 50%;
+ background: var(--accent);
+ cursor: pointer;
+ border: none;
+ /* no glow */
+}
+
+.volume-label {
+ font-family: var(--font-mono);
+ font-size: var(--text-xs);
+ color: var(--text-tertiary);
+ min-width: 28px;
+}
+
+
+/* ============================================================
+ I) MODAL STYLES
+ ============================================================ */
+
+/* -- Modal Overlay (shared) --------------------------------- */
+.hub-admin-overlay,
+.hub-version-overlay,
.hub-update-overlay {
position: fixed;
inset: 0;
- z-index: 9999;
+ background: rgba(0, 0, 0, .7);
+ /* no blur */
+ z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
- background: rgba(0, 0, 0, 0.7);
- backdrop-filter: blur(4px);
-}
-.hub-update-modal {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: 6px;
- padding: 32px 40px;
- text-align: center;
- min-width: 320px;
- max-width: 400px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
-}
-.hub-update-icon {
- font-size: 40px;
- margin-bottom: 12px;
-}
-.hub-update-modal h2 {
- margin: 0 0 8px;
- font-size: 18px;
- color: var(--text-primary);
-}
-.hub-update-modal p {
- margin: 0 0 20px;
- font-size: 14px;
- color: var(--text-secondary);
-}
-.hub-update-progress {
- height: 4px;
- border-radius: 2px;
- background: var(--bg-deep);
- overflow: hidden;
-}
-.hub-update-progress-bar {
- height: 100%;
- width: 40%;
- border-radius: 2px;
- background: var(--accent);
- animation: hub-update-slide 1.5s ease-in-out infinite;
-}
-@keyframes hub-update-slide {
- 0% { transform: translateX(-100%); }
- 100% { transform: translateX(350%); }
-}
-.hub-update-btn {
- padding: 8px 32px;
- font-size: 14px;
- font-weight: 600;
- border: none;
- border-radius: var(--radius);
- background: var(--accent);
- color: #fff;
- cursor: pointer;
- transition: opacity var(--transition);
-}
-.hub-update-btn:hover {
- opacity: 0.85;
-}
-.hub-update-btn-secondary {
- background: var(--bg-tertiary);
- color: var(--text-secondary);
- margin-top: 4px;
-}
-.hub-update-versions {
- display: flex;
- flex-direction: column;
- gap: 2px;
- margin: 8px 0;
- font-size: 12px;
- color: var(--text-muted);
-}
-.hub-update-error-detail {
- font-size: 11px;
- color: #ef4444;
- background: rgba(239, 68, 68, 0.1);
- border-radius: var(--radius);
- padding: 6px 10px;
- word-break: break-word;
- max-width: 300px;
+ animation: modal-fade-in var(--duration-normal) ease;
}
-/* ── Refresh Button ── */
-.hub-refresh-btn {
- background: none;
- border: none;
- color: var(--text-muted);
- font-size: 1rem;
- cursor: pointer;
- padding: 4px 6px;
- border-radius: var(--radius);
- transition: all var(--transition);
- line-height: 1;
-}
-.hub-refresh-btn:hover {
- color: var(--accent);
- background: rgba(230, 126, 34, 0.1);
+@keyframes modal-fade-in {
+ from { opacity: 0; }
+ to { opacity: 1; }
}
-/* ── Admin Button (header) ── */
-.hub-admin-btn {
- background: none;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- color: var(--text-muted);
- font-size: 16px;
- padding: 4px 8px;
- cursor: pointer;
- transition: all var(--transition);
- line-height: 1;
-}
-.hub-admin-btn:hover {
- color: var(--accent);
- border-color: var(--accent);
-}
-.hub-admin-btn.active {
- color: #4ade80;
- border-color: #4ade80;
+@keyframes modal-slide-in {
+ from { opacity: 0; transform: scale(0.95) translateY(8px); }
+ to { opacity: 1; transform: scale(1) translateY(0); }
}
-/* ── Admin Login Modal ── */
-.hub-admin-overlay {
- position: fixed;
- inset: 0;
- z-index: 9999;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(0, 0, 0, 0.7);
- backdrop-filter: blur(4px);
-}
-.hub-admin-modal {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: 6px;
- width: 340px;
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
-}
-.hub-admin-modal-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 16px 20px;
- border-bottom: 1px solid var(--border);
- font-weight: 600;
-}
-.hub-admin-modal-close {
- background: none;
- border: none;
- color: var(--text-muted);
- cursor: pointer;
- font-size: 16px;
-}
-.hub-admin-modal-close:hover {
- color: var(--text);
-}
-.hub-admin-modal-body {
- padding: 20px;
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-.hub-admin-input {
- width: 100%;
- padding: 8px 12px;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- background: var(--bg-secondary);
- color: var(--text);
- font-size: 14px;
- font-family: var(--font);
- box-sizing: border-box;
-}
-.hub-admin-input:focus {
- outline: none;
- border-color: var(--accent);
-}
-.hub-admin-error {
- color: #ef4444;
- font-size: 13px;
- margin: 0;
-}
-.hub-admin-submit {
- padding: 8px 16px;
- background: var(--accent);
- color: #fff;
- border: none;
- border-radius: var(--radius);
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: opacity var(--transition);
-}
-.hub-admin-submit:hover {
- opacity: 0.9;
-}
-
-/* ── Version Info Modal ── */
+/* -- Version Info Modal ------------------------------------- */
.hub-version-clickable {
cursor: pointer;
- transition: all var(--transition);
+ transition: all var(--duration-fast);
padding: 2px 8px;
- border-radius: var(--radius);
+ border-radius: var(--radius-sm);
}
+
.hub-version-clickable:hover {
color: var(--accent);
- background: rgba(230, 126, 34, 0.1);
-}
-.hub-version-overlay {
- position: fixed;
- inset: 0;
- z-index: 9999;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(0, 0, 0, 0.7);
- backdrop-filter: blur(4px);
+ background: var(--accent-soft);
}
+
.hub-version-modal {
- background: var(--bg-primary);
- border: 1px solid var(--border);
- border-radius: 4px;
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-default);
+ border-radius: var(--radius-lg);
width: 340px;
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
+ box-shadow: var(--shadow-xl);
overflow: hidden;
- animation: hub-modal-in 200ms ease;
-}
-@keyframes hub-modal-in {
- from { opacity: 0; transform: scale(0.95) translateY(8px); }
- to { opacity: 1; transform: scale(1) translateY(0); }
+ animation: modal-slide-in var(--duration-normal) ease;
}
+
.hub-version-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 14px 16px;
- border-bottom: 1px solid var(--border);
- font-weight: 700;
- font-size: 14px;
+ padding: var(--space-3) var(--space-4);
+ border-bottom: 1px solid var(--border-subtle);
+ font-weight: var(--weight-bold);
+ font-size: var(--text-base);
}
+
.hub-version-modal-close {
background: none;
border: none;
- color: var(--text-muted);
+ color: var(--text-tertiary);
cursor: pointer;
- padding: 4px 8px;
- border-radius: 6px;
- font-size: 14px;
- transition: all var(--transition);
+ padding: var(--space-1) var(--space-2);
+ border-radius: var(--radius-sm);
+ font-size: var(--text-base);
+ transition: all var(--duration-fast);
}
+
.hub-version-modal-close:hover {
- background: rgba(255, 255, 255, 0.08);
- color: var(--text-normal);
+ background: var(--surface-glass-hover);
+ color: var(--text-primary);
}
+
.hub-version-modal-body {
- padding: 16px;
+ padding: var(--space-4);
display: flex;
flex-direction: column;
- gap: 12px;
+ gap: var(--space-3);
}
+
.hub-version-modal-row {
display: flex;
justify-content: space-between;
align-items: center;
}
+
.hub-version-modal-label {
- color: var(--text-muted);
- font-size: 13px;
+ color: var(--text-secondary);
+ font-size: var(--text-sm);
}
+
.hub-version-modal-value {
- font-weight: 600;
- font-size: 13px;
+ font-weight: var(--weight-semibold);
+ font-size: var(--text-sm);
display: flex;
align-items: center;
- gap: 6px;
+ gap: var(--space-2);
}
+
.hub-version-modal-dot {
width: 8px;
height: 8px;
@@ -548,131 +1001,629 @@ html, body {
background: var(--danger);
flex-shrink: 0;
}
+
.hub-version-modal-dot.online {
background: var(--success);
}
+
.hub-version-modal-link {
color: var(--accent);
text-decoration: none;
- font-weight: 500;
- font-size: 13px;
+ font-weight: var(--weight-medium);
+ font-size: var(--text-sm);
}
+
.hub-version-modal-link:hover {
text-decoration: underline;
}
+
.hub-version-modal-hint {
- font-size: 11px;
+ font-size: var(--text-xs);
color: var(--accent);
- padding: 6px 10px;
- background: rgba(230, 126, 34, 0.1);
- border-radius: var(--radius);
+ padding: var(--space-2) var(--space-3);
+ background: var(--accent-soft);
+ border-radius: var(--radius-sm);
text-align: center;
}
-/* ── Update Section in Version Modal ── */
+/* -- Update Section in Version Modal ------------------------ */
.hub-version-modal-update {
- margin-top: 4px;
- padding-top: 12px;
- border-top: 1px solid var(--border);
+ margin-top: var(--space-1);
+ padding-top: var(--space-3);
+ border-top: 1px solid var(--border-subtle);
}
+
.hub-version-modal-update-btn {
width: 100%;
- padding: 10px 16px;
+ padding: var(--space-3) var(--space-4);
border: none;
- border-radius: var(--radius);
+ border-radius: var(--radius-sm);
background: var(--bg-tertiary);
- color: var(--text-normal);
- font-size: 13px;
- font-weight: 600;
+ color: var(--text-primary);
+ font-size: var(--text-sm);
+ font-weight: var(--weight-semibold);
cursor: pointer;
- transition: all var(--transition);
+ transition: all var(--duration-fast);
display: flex;
align-items: center;
justify-content: center;
- gap: 8px;
+ gap: var(--space-2);
+ font-family: var(--font-body);
}
+
.hub-version-modal-update-btn:hover {
background: var(--bg-hover);
color: var(--accent);
}
+
.hub-version-modal-update-btn.ready {
- background: rgba(46, 204, 113, 0.15);
- color: #2ecc71;
+ background: rgba(87, 210, 143, .15);
+ color: var(--success);
}
+
.hub-version-modal-update-btn.ready:hover {
- background: rgba(46, 204, 113, 0.25);
+ background: rgba(87, 210, 143, .25);
}
+
.hub-version-modal-update-status {
display: flex;
align-items: center;
justify-content: center;
- gap: 8px;
- font-size: 13px;
- color: var(--text-muted);
- padding: 8px 0;
+ gap: var(--space-2);
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
+ padding: var(--space-2) 0;
flex-wrap: wrap;
}
+
.hub-version-modal-update-status.success {
- color: #2ecc71;
+ color: var(--success);
}
+
.hub-version-modal-update-status.error {
- color: #e74c3c;
+ color: var(--danger);
}
+
.hub-version-modal-update-retry {
background: none;
border: none;
- color: var(--text-muted);
- font-size: 11px;
+ color: var(--text-secondary);
+ font-size: var(--text-xs);
cursor: pointer;
text-decoration: underline;
padding: 2px 4px;
width: 100%;
- margin-top: 4px;
+ margin-top: var(--space-1);
+ font-family: var(--font-body);
}
+
.hub-version-modal-update-retry:hover {
- color: var(--text-normal);
+ color: var(--text-primary);
}
-@keyframes hub-spin {
+
+/* -- Update Modal (standalone) ------------------------------ */
+.hub-update-modal {
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-default);
+ border-radius: var(--radius-lg);
+ padding: var(--space-7) var(--space-8);
+ text-align: center;
+ min-width: 320px;
+ max-width: 400px;
+ box-shadow: var(--shadow-xl);
+}
+
+.hub-update-icon {
+ font-size: 40px;
+ margin-bottom: var(--space-3);
+}
+
+.hub-update-modal h2 {
+ margin: 0 0 var(--space-2);
+ font-size: var(--text-lg);
+ color: var(--text-primary);
+}
+
+.hub-update-modal p {
+ margin: 0 0 var(--space-5);
+ font-size: var(--text-base);
+ color: var(--text-secondary);
+}
+
+.hub-update-progress {
+ height: 4px;
+ border-radius: 2px;
+ background: var(--bg-deep);
+ overflow: hidden;
+}
+
+.hub-update-progress-bar {
+ height: 100%;
+ width: 40%;
+ border-radius: 2px;
+ background: var(--accent);
+ animation: update-slide 1.5s ease-in-out infinite;
+}
+
+@keyframes update-slide {
+ 0% { transform: translateX(-100%); }
+ 100% { transform: translateX(350%); }
+}
+
+.hub-update-btn {
+ padding: var(--space-2) var(--space-7);
+ font-size: var(--text-base);
+ font-weight: var(--weight-semibold);
+ border: none;
+ border-radius: var(--radius-sm);
+ background: var(--accent);
+ color: #fff;
+ cursor: pointer;
+ transition: opacity var(--duration-fast);
+ font-family: var(--font-body);
+}
+
+.hub-update-btn:hover {
+ opacity: 0.85;
+}
+
+.hub-update-btn-secondary {
+ background: var(--bg-tertiary);
+ color: var(--text-secondary);
+ margin-top: var(--space-1);
+}
+
+.hub-update-versions {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ margin: var(--space-2) 0;
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
+}
+
+.hub-update-error-detail {
+ font-size: var(--text-xs);
+ color: var(--danger);
+ background: rgba(237, 66, 69, .1);
+ border-radius: var(--radius-sm);
+ padding: var(--space-2) var(--space-3);
+ word-break: break-word;
+ max-width: 300px;
+}
+
+/* -- Spinner ------------------------------------------------ */
+@keyframes spin {
to { transform: rotate(360deg); }
}
+
.hub-update-spinner {
width: 14px;
height: 14px;
- border: 2px solid var(--border);
+ border: 2px solid var(--border-default);
border-top-color: var(--accent);
border-radius: 50%;
- animation: hub-spin 0.8s linear infinite;
+ animation: spin 0.8s linear infinite;
flex-shrink: 0;
}
-/* ── Main Content Area ── */
-.hub-content {
- flex: 1;
- overflow-y: auto;
- overflow-x: hidden;
+/* -- Admin Modal -------------------------------------------- */
+.hub-admin-modal {
+ background: var(--bg-secondary);
+ border: 1px solid var(--surface-glass-border);
+ border-radius: var(--radius-lg);
+ padding: var(--space-7);
+ width: 360px;
+ max-width: 90vw;
+ box-shadow: var(--shadow-xl);
+ animation: modal-slide-in 0.25s var(--ease-spring);
+}
+
+.hub-admin-modal-title {
+ font-size: var(--text-xl);
+ font-weight: var(--weight-bold);
+ margin-bottom: var(--space-2);
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+}
+
+.hub-admin-modal-subtitle {
+ font-size: var(--text-sm);
+ color: var(--text-tertiary);
+ margin-bottom: var(--space-6);
+}
+
+.hub-admin-modal-error {
+ font-size: var(--text-sm);
+ color: var(--danger);
+ margin-bottom: var(--space-3);
+ padding: var(--space-2) var(--space-3);
+ background: rgba(237, 66, 69, .1);
+ border-radius: var(--radius-sm);
+}
+
+.hub-admin-modal-input {
+ width: 100%;
background: var(--bg-deep);
- scrollbar-width: thin;
- scrollbar-color: var(--bg-tertiary) transparent;
+ border: 1px solid var(--surface-glass-border);
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ padding: var(--space-3) var(--space-3);
+ outline: none;
+ transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
+ margin-bottom: var(--space-4);
}
-.hub-content::-webkit-scrollbar {
- width: 6px;
+.hub-admin-modal-input:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px var(--accent-soft);
}
-.hub-content::-webkit-scrollbar-track {
+.hub-admin-modal-login {
+ width: 100%;
+ background: var(--accent);
+ border: none;
+ border-radius: var(--radius-sm);
+ color: #fff;
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ font-weight: var(--weight-semibold);
+ padding: var(--space-3);
+ cursor: pointer;
+ transition: all var(--duration-fast);
+ box-shadow: 0 2px 6px rgba(0, 0, 0, .3);
+}
+
+.hub-admin-modal-login:hover {
+ background: var(--accent-hover);
+ box-shadow: 0 2px 8px rgba(0, 0, 0, .35);
+}
+
+.hub-admin-modal-info {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ margin-bottom: var(--space-5);
+}
+
+.hub-admin-modal-avatar {
+ width: 44px;
+ height: 44px;
+ border-radius: 50%;
+ background: var(--accent);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 20px;
+ font-weight: var(--weight-bold);
+ color: #fff;
+ /* no glow */
+}
+
+.hub-admin-modal-text {
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+}
+
+.hub-admin-modal-name {
+ font-weight: var(--weight-semibold);
+ font-size: var(--text-md);
+}
+
+.hub-admin-modal-role {
+ font-size: var(--text-sm);
+ color: var(--success);
+ font-weight: var(--weight-medium);
+}
+
+.hub-admin-modal-logout {
+ width: 100%;
+ background: rgba(237, 66, 69, .12);
+ border: 1px solid rgba(237, 66, 69, .25);
+ border-radius: var(--radius-sm);
+ color: var(--danger);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ font-weight: var(--weight-medium);
+ padding: var(--space-3);
+ cursor: pointer;
+ transition: all var(--duration-fast);
+}
+
+.hub-admin-modal-logout:hover {
+ background: rgba(237, 66, 69, .2);
+}
+
+/* -- Admin Button ------------------------------------------- */
+.hub-admin-btn {
+ background: var(--surface-glass);
+ border: 1px solid var(--surface-glass-border);
+ color: var(--text-secondary);
+ font-size: 16px;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ cursor: pointer;
+ transition: all var(--duration-fast);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+}
+
+.hub-admin-btn:hover {
+ background: var(--surface-glass-hover);
+ border-color: var(--accent);
+ box-shadow: 0 0 12px var(--accent-soft);
+}
+
+.hub-admin-btn.logged-in {
+ border-color: var(--success);
+}
+
+.hub-admin-green-dot {
+ position: absolute;
+ top: 1px;
+ right: 1px;
+ width: 8px;
+ height: 8px;
+ background: var(--success);
+ border-radius: 50%;
+ border: 2px solid var(--bg-deep);
+}
+
+/* -- Check for Updates Button ------------------------------- */
+.hub-check-update-btn {
+ background: none;
+ border: 1px solid var(--border-default);
+ border-radius: var(--radius-sm);
+ color: var(--text-secondary);
+ font-size: var(--text-base);
+ padding: 2px var(--space-2);
+ cursor: pointer;
+ transition: all var(--duration-fast);
+ line-height: 1;
+ font-family: var(--font-body);
+}
+
+.hub-check-update-btn:hover:not(:disabled) {
+ color: var(--accent);
+ border-color: var(--accent);
+}
+
+.hub-check-update-btn:disabled {
+ opacity: 0.4;
+ cursor: default;
+}
+
+/* -- Version Badge ------------------------------------------ */
+.hub-version {
+ font-size: var(--text-sm);
+ color: var(--text-tertiary);
+ font-weight: var(--weight-medium);
+ font-variant-numeric: tabular-nums;
+ background: var(--bg-secondary);
+ padding: var(--space-1) var(--space-2);
+ border-radius: var(--radius-xs);
+}
+
+/* -- Refresh Button ----------------------------------------- */
+.hub-refresh-btn {
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ font-size: 1rem;
+ cursor: pointer;
+ padding: var(--space-1) var(--space-2);
+ border-radius: var(--radius-sm);
+ transition: all var(--duration-fast);
+ line-height: 1;
+}
+
+.hub-refresh-btn:hover {
+ color: var(--accent);
+ background: var(--accent-soft);
+}
+
+
+/* ============================================================
+ J) UTILITY CLASSES
+ ============================================================ */
+
+/* -- Badge -------------------------------------------------- */
+.badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ font-size: var(--text-xs);
+ font-weight: var(--weight-semibold);
+ padding: 2px var(--space-2);
+ border-radius: var(--radius-xs);
+ line-height: 1.5;
+ white-space: nowrap;
+}
+
+.badge--accent {
+ background: var(--accent-soft);
+ color: var(--accent-text);
+}
+
+.badge--success {
+ background: rgba(87, 210, 143, .15);
+ color: var(--success);
+}
+
+.badge--danger {
+ background: rgba(237, 66, 69, .15);
+ color: var(--danger);
+}
+
+.badge--warning {
+ background: rgba(250, 166, 26, .15);
+ color: var(--warning);
+}
+
+.badge--info {
+ background: rgba(88, 101, 242, .15);
+ color: var(--info);
+}
+
+/* -- Button ------------------------------------------------- */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: var(--space-2);
+ font-family: var(--font-body);
+ font-weight: var(--weight-medium);
+ border: none;
+ cursor: pointer;
+ transition: all var(--duration-fast);
+ border-radius: var(--radius-sm);
+ white-space: nowrap;
+}
+
+.btn--sm { height: 24px; padding: 0 var(--space-2); font-size: var(--text-xs); }
+.btn--md { height: 26px; padding: 0 var(--space-3); font-size: var(--text-sm); }
+.btn--lg { height: 32px; padding: 0 var(--space-4); font-size: var(--text-base); }
+
+.btn--primary {
+ background: var(--accent);
+ color: #fff;
+}
+
+.btn--primary:hover {
+ background: var(--accent-hover);
+}
+
+.btn--secondary {
+ background: var(--surface-glass);
+ color: var(--text-primary);
+ border: 1px solid var(--surface-glass-border);
+}
+
+.btn--secondary:hover {
+ background: var(--surface-glass-hover);
+}
+
+.btn--ghost {
background: transparent;
+ color: var(--text-secondary);
}
-.hub-content::-webkit-scrollbar-thumb {
- background: var(--bg-tertiary);
- border-radius: 3px;
+.btn--ghost:hover {
+ background: var(--surface-glass);
+ color: var(--text-primary);
}
-.hub-content::-webkit-scrollbar-thumb:hover {
- background: var(--text-faint);
+.btn--danger {
+ background: var(--danger);
+ color: #fff;
}
-/* ── Empty State ── */
+.btn--danger:hover {
+ background: #d63638;
+}
+
+/* Glass classes kept as simple surface */
+.glass--subtle {
+ background: var(--surface-glass);
+ border: 1px solid var(--surface-glass-border);
+}
+
+.glass--medium {
+ background: rgba(255, 255, 255, .06);
+ border: 1px solid rgba(255, 255, 255, .10);
+}
+
+.glass--strong {
+ background: rgba(255, 255, 255, .09);
+ border: 1px solid rgba(255, 255, 255, .14);
+}
+
+/* -- Toast Notifications ------------------------------------ */
+.toast-container {
+ position: fixed;
+ bottom: var(--space-4);
+ right: var(--space-4);
+ z-index: 2000;
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+ pointer-events: none;
+}
+
+.toast {
+ display: flex;
+ align-items: center;
+ gap: var(--space-3);
+ padding: var(--space-3) var(--space-4);
+ border-radius: var(--radius-md);
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-default);
+ box-shadow: var(--shadow-lg);
+ color: var(--text-primary);
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ pointer-events: auto;
+ animation: toast-in var(--duration-normal) var(--ease-out);
+ max-width: 380px;
+}
+
+.toast.toast-exit {
+ animation: toast-out var(--duration-fast) ease forwards;
+}
+
+.toast--success { border-left: 3px solid var(--success); }
+.toast--danger { border-left: 3px solid var(--danger); }
+.toast--warning { border-left: 3px solid var(--warning); }
+.toast--info { border-left: 3px solid var(--info); }
+
+.toast__icon {
+ font-size: var(--text-lg);
+ flex-shrink: 0;
+}
+
+.toast__message {
+ flex: 1;
+}
+
+.toast__close {
+ background: none;
+ border: none;
+ color: var(--text-tertiary);
+ cursor: pointer;
+ padding: var(--space-1);
+ border-radius: var(--radius-xs);
+ font-size: var(--text-sm);
+ transition: color var(--duration-fast);
+}
+
+.toast__close:hover {
+ color: var(--text-primary);
+}
+
+@keyframes toast-in {
+ from { opacity: 0; transform: translateX(20px); }
+ to { opacity: 1; transform: translateX(0); }
+}
+
+@keyframes toast-out {
+ from { opacity: 1; transform: translateX(0); }
+ to { opacity: 0; transform: translateX(20px); }
+}
+
+
+/* Noise texture removed per CI v2 */
+
+
+/* ============================================================
+ PRESERVED: Empty State
+ ============================================================ */
.hub-empty {
display: flex;
flex-direction: column;
@@ -681,124 +1632,84 @@ html, body {
height: 100%;
min-height: 300px;
text-align: center;
- padding: 32px;
+ padding: var(--space-7);
animation: fade-in 300ms ease;
}
.hub-empty-icon {
font-size: 64px;
line-height: 1;
- margin-bottom: 20px;
+ margin-bottom: var(--space-5);
opacity: 0.6;
filter: grayscale(30%);
}
.hub-empty h2 {
- font-size: 22px;
- font-weight: 700;
- color: var(--text-normal);
- margin-bottom: 8px;
+ font-size: var(--text-xl);
+ font-weight: var(--weight-bold);
+ color: var(--text-primary);
+ margin-bottom: var(--space-2);
}
.hub-empty p {
- font-size: 15px;
- color: var(--text-muted);
+ font-size: var(--text-md);
+ color: var(--text-secondary);
max-width: 360px;
line-height: 1.5;
}
-/* ── Animations ── */
-@keyframes fade-in {
- from {
- opacity: 0;
- transform: translateY(8px);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
+/* -- Avatar (general) --------------------------------------- */
+.hub-avatar {
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ background: var(--accent);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: var(--text-base);
+ font-weight: var(--weight-bold);
+ color: #fff;
+ box-shadow: 0 0 0 2px var(--bg-deep);
}
-/* ── Selection ── */
-::selection {
- background: rgba(var(--accent-rgb), 0.3);
- color: var(--text-normal);
+/* -- Download Button ---------------------------------------- */
+.hub-download-btn {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-1) var(--space-3);
+ font-size: var(--text-sm);
+ font-weight: var(--weight-medium);
+ font-family: var(--font-body);
+ text-decoration: none;
+ color: var(--text-secondary);
+ background: var(--bg-secondary);
+ border-radius: var(--radius-sm);
+ cursor: pointer;
+ transition: all var(--duration-fast);
+ white-space: nowrap;
+ border: none;
}
-/* ── Focus Styles ── */
-:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 2px;
+.hub-download-btn:hover {
+ color: var(--accent);
+ background: var(--accent-soft);
}
-/* ── Responsive ── */
-@media (max-width: 768px) {
- :root {
- --header-height: 48px;
- }
-
- .hub-header {
- padding: 0 10px;
- gap: 8px;
- }
-
- .hub-title {
- font-size: 15px;
- }
-
- .hub-logo {
- font-size: 20px;
- }
-
- .hub-tab {
- padding: 6px 10px;
- font-size: 13px;
- gap: 4px;
- }
-
- .hub-tab-label {
- display: none;
- }
-
- .hub-tab-icon {
- font-size: 18px;
- }
-
- .hub-version {
- font-size: 11px;
- }
-
- .hub-empty-icon {
- font-size: 48px;
- }
-
- .hub-empty h2 {
- font-size: 18px;
- }
-
- .hub-empty p {
- font-size: 14px;
- }
+.hub-download-icon {
+ font-size: var(--text-base);
+ line-height: 1;
}
-@media (max-width: 480px) {
- .hub-header-right {
- display: none;
- }
-
- .hub-header {
- padding: 0 8px;
- gap: 6px;
- }
-
- .hub-title {
- font-size: 14px;
- }
+.hub-download-label {
+ line-height: 1;
}
-/* ══════════════════════════════════════════════
- RADIO PLUGIN – World Radio Globe
- ══════════════════════════════════════════════ */
+
+/* ============================================================
+ PRESERVED: Radio Plugin Styles
+ ============================================================ */
.radio-container {
display: flex;
@@ -807,80 +1718,62 @@ html, body {
height: 100%;
overflow: hidden;
background: var(--bg-deep);
-
- /* Default-Theme Vars (scoped, damit data-theme sie überschreiben kann) */
- --bg-deep: #1a1810;
- --bg-primary: #211e17;
- --bg-secondary: #2a2620;
- --bg-tertiary: #322d26;
- --text-normal: #dbdee1;
- --text-muted: #949ba4;
- --text-faint: #6d6f78;
- --accent: #e67e22;
- --accent-rgb: 230, 126, 34;
- --accent-hover: #d35400;
- --border: rgba(255, 255, 255, 0.06);
}
-/* ── Radio Themes ── */
+/* -- Radio Themes ------------------------------------------- */
.radio-container[data-theme="purple"] {
- --bg-deep: #13111c;
- --bg-primary: #1a1726;
- --bg-secondary: #241f35;
- --bg-tertiary: #2e2845;
- --accent: #9b59b6;
- --accent-rgb: 155, 89, 182;
+ --bg-deep: #16131c;
+ --bg-primary: #1d1926;
+ --bg-secondary: #272235;
+ --bg-tertiary: #312b42;
+ --accent: #9b59b6;
--accent-hover: #8e44ad;
}
.radio-container[data-theme="forest"] {
- --bg-deep: #0f1a14;
- --bg-primary: #142119;
- --bg-secondary: #1c2e22;
- --bg-tertiary: #253a2c;
- --accent: #2ecc71;
- --accent-rgb: 46, 204, 113;
+ --bg-deep: #121a14;
+ --bg-primary: #172119;
+ --bg-secondary: #1f2e22;
+ --bg-tertiary: #283a2c;
+ --accent: #2ecc71;
--accent-hover: #27ae60;
}
.radio-container[data-theme="ocean"] {
- --bg-deep: #0a1628;
- --bg-primary: #0f1e33;
- --bg-secondary: #162a42;
- --bg-tertiary: #1e3652;
- --accent: #3498db;
- --accent-rgb: 52, 152, 219;
+ --bg-deep: #101620;
+ --bg-primary: #151e2c;
+ --bg-secondary: #1c2a38;
+ --bg-tertiary: #243646;
+ --accent: #3498db;
--accent-hover: #2980b9;
}
.radio-container[data-theme="cherry"] {
- --bg-deep: #1a0f14;
- --bg-primary: #22141a;
- --bg-secondary: #301c25;
- --bg-tertiary: #3e2530;
- --accent: #e74c6f;
- --accent-rgb: 231, 76, 111;
+ --bg-deep: #1a1014;
+ --bg-primary: #22151a;
+ --bg-secondary: #301e25;
+ --bg-tertiary: #3e2830;
+ --accent: #e74c6f;
--accent-hover: #c0392b;
}
-/* ── Globe ── */
-/* ── Radio Topbar ── */
+/* -- Radio Topbar ------------------------------------------- */
.radio-topbar {
display: flex;
align-items: center;
- padding: 0 16px;
- height: 52px;
- background: var(--bg-secondary, #2a2620);
+ padding: 0 var(--space-4);
+ height: var(--header-h);
+ background: var(--bg-secondary);
border-bottom: 1px solid rgba(0, 0, 0, .24);
z-index: 10;
flex-shrink: 0;
- gap: 16px;
+ gap: var(--space-4);
}
.radio-topbar-left {
display: flex;
align-items: center;
- gap: 10px;
+ gap: var(--space-3);
flex-shrink: 0;
}
@@ -889,17 +1782,17 @@ html, body {
}
.radio-topbar-title {
- font-size: 16px;
- font-weight: 700;
- color: var(--text-normal);
- letter-spacing: -.02em;
+ font-size: var(--text-lg);
+ font-weight: var(--weight-bold);
+ color: var(--text-primary);
+ letter-spacing: -0.02em;
}
.radio-topbar-np {
flex: 1;
display: flex;
align-items: center;
- gap: 10px;
+ gap: var(--space-3);
min-width: 0;
justify-content: center;
}
@@ -907,7 +1800,7 @@ html, body {
.radio-topbar-right {
display: flex;
align-items: center;
- gap: 6px;
+ gap: var(--space-2);
flex-shrink: 0;
margin-left: auto;
}
@@ -915,17 +1808,17 @@ html, body {
.radio-topbar-stop {
display: flex;
align-items: center;
- gap: 4px;
+ gap: var(--space-1);
background: var(--danger);
color: #fff;
border: none;
- border-radius: var(--radius);
- padding: 6px 14px;
- font-size: 13px;
- font-family: var(--font);
- font-weight: 600;
+ border-radius: var(--radius-sm);
+ padding: var(--space-2) var(--space-3);
+ font-size: var(--text-sm);
+ font-family: var(--font-body);
+ font-weight: var(--weight-semibold);
cursor: pointer;
- transition: all var(--transition);
+ transition: all var(--duration-fast);
flex-shrink: 0;
}
@@ -936,11 +1829,11 @@ html, body {
.radio-theme-inline {
display: flex;
align-items: center;
- gap: 4px;
- margin-left: 4px;
+ gap: var(--space-1);
+ margin-left: var(--space-1);
}
-/* ── Globe Wrapper ── */
+/* -- Globe -------------------------------------------------- */
.radio-globe-wrap {
position: relative;
flex: 1;
@@ -956,10 +1849,10 @@ html, body {
outline: none !important;
}
-/* ── Search Overlay ── */
+/* -- Radio Search ------------------------------------------- */
.radio-search {
position: absolute;
- top: 16px;
+ top: var(--space-4);
left: 50%;
transform: translateX(-50%);
z-index: 20;
@@ -970,11 +1863,12 @@ html, body {
display: flex;
align-items: center;
background: rgba(33, 30, 23, 0.92);
- border: 1px solid var(--border);
+ /* no blur */
+ border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
- padding: 0 14px;
- gap: 8px;
- box-shadow: 0 8px 32px rgba(0,0,0,0.4);
+ padding: 0 var(--space-3);
+ gap: var(--space-2);
+ box-shadow: var(--shadow-lg);
}
.radio-search-icon {
@@ -987,60 +1881,59 @@ html, body {
flex: 1;
background: transparent;
border: none;
- color: var(--text-normal);
- font-family: var(--font);
- font-size: 14px;
- padding: 12px 0;
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
+ padding: var(--space-3) 0;
outline: none;
}
.radio-search-input::placeholder {
- color: var(--text-faint);
+ color: var(--text-tertiary);
}
.radio-search-clear {
background: none;
border: none;
- color: var(--text-muted);
- font-size: 14px;
+ color: var(--text-secondary);
+ font-size: var(--text-base);
cursor: pointer;
- padding: 4px;
- border-radius: 4px;
- transition: color var(--transition);
+ padding: var(--space-1);
+ border-radius: var(--radius-xs);
+ transition: color var(--duration-fast);
}
.radio-search-clear:hover {
- color: var(--text-normal);
+ color: var(--text-primary);
}
-/* ── Search Results ── */
+/* -- Radio Search Results ----------------------------------- */
.radio-search-results {
- margin-top: 6px;
+ margin-top: var(--space-2);
background: rgba(33, 30, 23, 0.95);
- border: 1px solid var(--border);
+ /* no blur */
+ border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
max-height: 360px;
overflow-y: auto;
- box-shadow: 0 12px 40px rgba(0,0,0,0.5);
- scrollbar-width: thin;
- scrollbar-color: var(--bg-tertiary) transparent;
+ box-shadow: var(--shadow-xl);
}
.radio-search-result {
display: flex;
align-items: center;
- gap: 10px;
+ gap: var(--space-3);
width: 100%;
- padding: 10px 14px;
+ padding: var(--space-3) var(--space-3);
background: none;
border: none;
- border-bottom: 1px solid var(--border);
- color: var(--text-normal);
- font-family: var(--font);
- font-size: 14px;
+ border-bottom: 1px solid var(--border-subtle);
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-base);
cursor: pointer;
text-align: left;
- transition: background var(--transition);
+ transition: background var(--duration-fast);
}
.radio-search-result:last-child {
@@ -1048,11 +1941,11 @@ html, body {
}
.radio-search-result:hover {
- background: rgba(var(--accent-rgb), 0.08);
+ background: var(--accent-soft);
}
.radio-search-result-icon {
- font-size: 18px;
+ font-size: var(--text-lg);
flex-shrink: 0;
}
@@ -1064,58 +1957,59 @@ html, body {
}
.radio-search-result-title {
- font-weight: 600;
+ font-weight: var(--weight-semibold);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.radio-search-result-sub {
- font-size: 12px;
- color: var(--text-muted);
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
-/* ── Favorites FAB ── */
+/* -- Favorites FAB ------------------------------------------ */
.radio-fab {
position: absolute;
- top: 16px;
- right: 16px;
+ top: var(--space-4);
+ right: var(--space-4);
z-index: 20;
display: flex;
align-items: center;
- gap: 4px;
- padding: 10px 14px;
+ gap: var(--space-1);
+ padding: var(--space-3) var(--space-3);
background: rgba(33, 30, 23, 0.92);
- border: 1px solid var(--border);
+ /* no blur */
+ border: 1px solid var(--border-default);
border-radius: var(--radius-lg);
- color: var(--text-normal);
+ color: var(--text-primary);
font-size: 16px;
cursor: pointer;
- box-shadow: 0 8px 32px rgba(0,0,0,0.4);
- transition: all var(--transition);
+ box-shadow: var(--shadow-lg);
+ transition: all var(--duration-fast);
}
.radio-fab:hover,
.radio-fab.active {
- background: rgba(var(--accent-rgb), 0.15);
- border-color: rgba(var(--accent-rgb), 0.3);
+ background: var(--accent-soft);
+ border-color: var(--accent);
}
.radio-fab-badge {
- font-size: 11px;
- font-weight: 700;
+ font-size: var(--text-xs);
+ font-weight: var(--weight-bold);
background: var(--accent);
color: #fff;
padding: 1px 6px;
- border-radius: 6px;
+ border-radius: var(--radius-full);
min-width: 18px;
text-align: center;
}
-/* ── Side Panel ── */
+/* -- Side Panel --------------------------------------------- */
.radio-panel {
position: absolute;
top: 0;
@@ -1124,11 +2018,12 @@ html, body {
height: 100%;
z-index: 15;
background: rgba(33, 30, 23, 0.95);
- border-left: 1px solid var(--border);
+ /* no blur */
+ border-left: 1px solid var(--border-default);
display: flex;
flex-direction: column;
- animation: slide-in-right 200ms ease;
- box-shadow: -8px 0 32px rgba(0,0,0,0.3);
+ animation: slide-in-right var(--duration-normal) ease;
+ box-shadow: -8px 0 32px rgba(0, 0, 0, .3);
}
@keyframes slide-in-right {
@@ -1140,20 +2035,20 @@ html, body {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 16px;
- border-bottom: 1px solid var(--border);
+ padding: var(--space-4);
+ border-bottom: 1px solid var(--border-subtle);
flex-shrink: 0;
}
.radio-panel-header h3 {
- font-size: 16px;
- font-weight: 700;
- color: var(--text-normal);
+ font-size: var(--text-lg);
+ font-weight: var(--weight-bold);
+ color: var(--text-primary);
}
.radio-panel-sub {
- font-size: 12px;
- color: var(--text-muted);
+ font-size: var(--text-sm);
+ color: var(--text-secondary);
display: block;
margin-top: 2px;
}
@@ -1161,62 +2056,60 @@ html, body {
.radio-panel-close {
background: none;
border: none;
- color: var(--text-muted);
- font-size: 18px;
+ color: var(--text-secondary);
+ font-size: var(--text-lg);
cursor: pointer;
- padding: 4px 8px;
- border-radius: 4px;
- transition: all var(--transition);
+ padding: var(--space-1) var(--space-2);
+ border-radius: var(--radius-xs);
+ transition: all var(--duration-fast);
}
.radio-panel-close:hover {
- color: var(--text-normal);
- background: var(--bg-secondary);
+ color: var(--text-primary);
+ background: var(--surface-glass-hover);
}
.radio-panel-body {
flex: 1;
overflow-y: auto;
- padding: 8px;
- scrollbar-width: thin;
- scrollbar-color: var(--bg-tertiary) transparent;
+ padding: var(--space-2);
}
.radio-panel-empty {
text-align: center;
- color: var(--text-muted);
- padding: 40px 16px;
- font-size: 14px;
+ color: var(--text-secondary);
+ padding: var(--space-8) var(--space-4);
+ font-size: var(--text-base);
}
.radio-panel-loading {
display: flex;
flex-direction: column;
align-items: center;
- gap: 12px;
- padding: 40px 16px;
- color: var(--text-muted);
- font-size: 14px;
+ gap: var(--space-3);
+ padding: var(--space-8) var(--space-4);
+ color: var(--text-secondary);
+ font-size: var(--text-base);
}
-/* ── Station Card ── */
+/* -- Station Card ------------------------------------------- */
.radio-station {
display: flex;
align-items: center;
justify-content: space-between;
- padding: 10px 12px;
- border-radius: var(--radius);
- transition: background var(--transition);
- gap: 10px;
+ padding: var(--space-3) var(--space-3);
+ border-radius: var(--radius-sm);
+ transition: background var(--duration-fast);
+ gap: var(--space-3);
}
.radio-station:hover {
- background: var(--bg-secondary);
+ background: var(--surface-glass-hover);
}
.radio-station.playing {
- background: rgba(var(--accent-rgb), 0.1);
- border: 1px solid rgba(var(--accent-rgb), 0.2);
+ background: var(--accent-soft);
+ border: 1px solid var(--accent);
}
.radio-station-info {
@@ -1228,17 +2121,17 @@ html, body {
}
.radio-station-name {
- font-size: 14px;
- font-weight: 600;
- color: var(--text-normal);
+ font-size: var(--text-base);
+ font-weight: var(--weight-semibold);
+ color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.radio-station-loc {
- font-size: 11px;
- color: var(--text-faint);
+ font-size: var(--text-xs);
+ color: var(--text-tertiary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -1247,31 +2140,31 @@ html, body {
.radio-station-live {
display: flex;
align-items: center;
- gap: 6px;
- font-size: 11px;
+ gap: var(--space-2);
+ font-size: var(--text-xs);
color: var(--accent);
- font-weight: 600;
+ font-weight: var(--weight-semibold);
}
.radio-station-btns {
display: flex;
- gap: 4px;
+ gap: var(--space-1);
flex-shrink: 0;
}
-/* ── Buttons ── */
+/* -- Radio Buttons ------------------------------------------ */
.radio-btn-play,
.radio-btn-stop {
width: 34px;
height: 34px;
border: none;
border-radius: 50%;
- font-size: 14px;
+ font-size: var(--text-base);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
- transition: all var(--transition);
+ transition: all var(--duration-fast);
}
.radio-btn-play {
@@ -1306,23 +2199,23 @@ html, body {
font-size: 16px;
cursor: pointer;
background: transparent;
- color: var(--text-faint);
+ color: var(--text-tertiary);
display: flex;
align-items: center;
justify-content: center;
- transition: all var(--transition);
+ transition: all var(--duration-fast);
}
.radio-btn-fav:hover {
color: var(--warning);
- background: rgba(254, 231, 92, 0.1);
+ background: rgba(250, 166, 26, .1);
}
.radio-btn-fav.active {
color: var(--warning);
}
-/* ── Equalizer Animation ── */
+/* -- Equalizer Animation ------------------------------------ */
.radio-eq {
display: flex;
align-items: flex-end;
@@ -1337,23 +2230,23 @@ html, body {
animation: eq-bounce 0.8s ease-in-out infinite;
}
-.radio-eq span:nth-child(1) { height: 8px; animation-delay: 0s; }
+.radio-eq span:nth-child(1) { height: 8px; animation-delay: 0s; }
.radio-eq span:nth-child(2) { height: 14px; animation-delay: 0.15s; }
.radio-eq span:nth-child(3) { height: 10px; animation-delay: 0.3s; }
@keyframes eq-bounce {
0%, 100% { transform: scaleY(0.4); }
- 50% { transform: scaleY(1); }
+ 50% { transform: scaleY(1); }
}
.radio-sel {
background: var(--bg-secondary);
- border: 1px solid var(--border);
- border-radius: var(--radius);
- color: var(--text-normal);
- font-family: var(--font);
- font-size: 13px;
- padding: 6px 10px;
+ border: 1px solid var(--border-default);
+ border-radius: var(--radius-sm);
+ color: var(--text-primary);
+ font-family: var(--font-body);
+ font-size: var(--text-sm);
+ padding: var(--space-2) var(--space-3);
cursor: pointer;
outline: none;
max-width: 180px;
@@ -1376,28 +2269,27 @@ html, body {
}
.radio-np-name {
- font-size: 14px;
- font-weight: 600;
- color: var(--text-normal);
+ font-size: var(--text-base);
+ font-weight: var(--weight-semibold);
+ color: var(--text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.radio-np-loc {
- font-size: 11px;
- color: var(--text-muted);
+ font-size: var(--text-xs);
+ color: var(--text-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
-
-/* ── Volume Slider ── */
+/* -- Radio Volume ------------------------------------------- */
.radio-volume {
display: flex;
align-items: center;
- gap: 6px;
+ gap: var(--space-2);
flex-shrink: 0;
}
@@ -1414,7 +2306,7 @@ html, body {
width: 100px;
height: 4px;
border-radius: 2px;
- background: var(--bg-tertiary, #3a352d);
+ background: var(--bg-tertiary);
outline: none;
cursor: pointer;
}
@@ -1425,25 +2317,25 @@ html, body {
width: 14px;
height: 14px;
border-radius: 50%;
- background: var(--accent, #e67e22);
+ background: var(--accent);
cursor: pointer;
border: none;
- box-shadow: 0 0 4px rgba(0,0,0,0.3);
+ box-shadow: 0 0 4px rgba(0, 0, 0, .3);
}
.radio-volume-slider::-moz-range-thumb {
width: 14px;
height: 14px;
border-radius: 50%;
- background: var(--accent, #e67e22);
+ background: var(--accent);
cursor: pointer;
border: none;
- box-shadow: 0 0 4px rgba(0,0,0,0.3);
+ box-shadow: 0 0 4px rgba(0, 0, 0, .3);
}
.radio-volume-val {
- font-size: 11px;
- color: var(--text-muted);
+ font-size: var(--text-xs);
+ color: var(--text-secondary);
min-width: 32px;
text-align: right;
}
@@ -1463,42 +2355,43 @@ html, body {
.radio-theme-dot.active {
border-color: #fff;
+ box-shadow: 0 0 6px rgba(255, 255, 255, .3);
}
-/* ── Station count ── */
+/* -- Radio Overlays ----------------------------------------- */
.radio-counter {
position: absolute;
- bottom: 16px;
- left: 16px;
+ bottom: var(--space-4);
+ left: var(--space-4);
z-index: 10;
- font-size: 12px;
- color: var(--text-faint);
+ font-size: var(--text-sm);
+ color: var(--text-tertiary);
background: rgba(33, 30, 23, 0.8);
- padding: 4px 10px;
- border-radius: 20px;
+ padding: var(--space-1) var(--space-3);
+ border-radius: var(--radius-full);
pointer-events: none;
}
.radio-attribution {
position: absolute;
- right: 16px;
- bottom: 16px;
+ right: var(--space-4);
+ bottom: var(--space-4);
z-index: 10;
- font-size: 12px;
- color: var(--text-faint);
+ font-size: var(--text-sm);
+ color: var(--text-tertiary);
background: rgba(33, 30, 23, 0.8);
- padding: 4px 10px;
- border-radius: 20px;
+ padding: var(--space-1) var(--space-3);
+ border-radius: var(--radius-full);
text-decoration: none;
- transition: color var(--transition), background var(--transition);
+ transition: color var(--duration-fast), background var(--duration-fast);
}
.radio-attribution:hover {
- color: var(--text-normal);
+ color: var(--text-primary);
background: rgba(33, 30, 23, 0.92);
}
-/* ── Spinner ── */
+/* -- Radio Spinner ------------------------------------------ */
.radio-spinner {
width: 24px;
height: 24px;
@@ -1508,32 +2401,223 @@ html, body {
animation: spin 0.7s linear infinite;
}
-@keyframes spin {
- to { transform: rotate(360deg); }
+/* -- Radio Connection --------------------------------------- */
+.radio-conn {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ font-size: var(--text-sm);
+ color: var(--success);
+ cursor: pointer;
+ padding: var(--space-1) var(--space-3);
+ border-radius: var(--radius-full);
+ background: rgba(87, 210, 143, .08);
+ transition: all var(--duration-fast);
+ flex-shrink: 0;
+ user-select: none;
}
-/* ── Radio Responsive ── */
+.radio-conn:hover {
+ background: rgba(87, 210, 143, .15);
+}
+
+.radio-conn-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background: var(--success);
+ animation: pulse-dot 2s ease-in-out infinite;
+}
+
+.radio-conn-ping {
+ font-size: var(--text-xs);
+ color: var(--text-secondary);
+ font-weight: var(--weight-semibold);
+ font-variant-numeric: tabular-nums;
+}
+
+/* -- Radio Connection Modal --------------------------------- */
+.radio-modal-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(0, 0, 0, .55);
+ z-index: 9000;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ /* no blur */
+ animation: fade-in .15s ease;
+}
+
+.radio-modal {
+ background: var(--bg-secondary);
+ border: 1px solid var(--border-default);
+ border-radius: var(--radius-lg);
+ width: 340px;
+ box-shadow: var(--shadow-xl);
+ overflow: hidden;
+ animation: radio-modal-in var(--duration-normal) ease;
+}
+
+@keyframes radio-modal-in {
+ from { transform: translateY(20px); opacity: 0; }
+ to { transform: translateY(0); opacity: 1; }
+}
+
+.radio-modal-header {
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-3) var(--space-4);
+ border-bottom: 1px solid var(--border-subtle);
+ font-weight: var(--weight-bold);
+ font-size: var(--text-base);
+}
+
+.radio-modal-close {
+ margin-left: auto;
+ background: none;
+ border: none;
+ color: var(--text-secondary);
+ cursor: pointer;
+ padding: var(--space-1) var(--space-2);
+ border-radius: var(--radius-sm);
+ font-size: var(--text-base);
+ transition: all var(--duration-fast);
+}
+
+.radio-modal-close:hover {
+ background: var(--surface-glass-hover);
+ color: var(--text-primary);
+}
+
+.radio-modal-body {
+ padding: var(--space-4);
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+}
+
+.radio-modal-stat {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.radio-modal-label {
+ color: var(--text-secondary);
+ font-size: var(--text-sm);
+}
+
+.radio-modal-value {
+ font-weight: var(--weight-semibold);
+ font-size: var(--text-sm);
+ display: flex;
+ align-items: center;
+ gap: var(--space-2);
+}
+
+.radio-modal-dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ flex-shrink: 0;
+}
+
+
+/* ============================================================
+ ANIMATIONS (Shared)
+ ============================================================ */
+@keyframes fade-in {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+
+/* ============================================================
+ L) RESPONSIVE
+ ============================================================ */
+
+/* -- Tablet (< 768px): icon-only sidebar -------------------- */
@media (max-width: 768px) {
+ :root {
+ --sidebar-nav-w: 48px;
+ }
+
+ .sidebar-brand,
+ .sidebar-section-label,
+ .nav-label,
+ .sidebar-user-info,
+ .channel-dropdown {
+ display: none;
+ }
+
+ .sidebar-header {
+ justify-content: center;
+ padding: 0;
+ }
+
+ .sidebar-footer {
+ justify-content: center;
+ padding: var(--space-2);
+ }
+
+ .sidebar-settings {
+ display: none;
+ }
+
+ .nav-item {
+ justify-content: center;
+ padding: var(--space-2);
+ }
+
+ .nav-badge {
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ min-width: 14px;
+ height: 14px;
+ font-size: 9px;
+ padding: 0 3px;
+ }
+
+ .nav-now-playing {
+ position: absolute;
+ bottom: 2px;
+ right: 4px;
+ margin-left: 0;
+ }
+
+ .content-header {
+ padding: 0 var(--space-3);
+ gap: var(--space-2);
+ }
+
+ .content-header__search {
+ max-width: 200px;
+ }
+
+ /* Radio responsive */
.radio-panel {
width: 100%;
}
.radio-fab {
- top: 12px;
- right: 12px;
- padding: 8px 10px;
- font-size: 14px;
+ top: var(--space-3);
+ right: var(--space-3);
+ padding: var(--space-2) var(--space-3);
+ font-size: var(--text-base);
}
.radio-search {
- top: 12px;
+ top: var(--space-3);
width: calc(100% - 80px);
left: calc(50% - 24px);
}
.radio-topbar {
- padding: 0 12px;
- gap: 8px;
+ padding: 0 var(--space-3);
+ gap: var(--space-2);
}
.radio-topbar-title {
@@ -1542,11 +2626,45 @@ html, body {
.radio-sel {
max-width: 140px;
- font-size: 12px;
+ font-size: var(--text-sm);
+ }
+
+ .hub-empty-icon {
+ font-size: 48px;
+ }
+
+ .hub-empty h2 {
+ font-size: var(--text-lg);
+ }
+
+ .hub-empty p {
+ font-size: var(--text-base);
}
}
+/* -- Mobile (< 480px): hide less important controls --------- */
@media (max-width: 480px) {
+ .content-header__actions {
+ display: none;
+ }
+
+ .content-header__search {
+ max-width: none;
+ flex: 1;
+ }
+
+ .playback-controls {
+ display: none;
+ }
+
+ .theme-picker {
+ display: none;
+ }
+
+ .volume-control {
+ display: none;
+ }
+
.radio-topbar-np {
display: none;
}
@@ -1559,1356 +2677,3 @@ html, body {
max-width: 120px;
}
}
-
-/* ── Radio Connection Indicator ── */
-.radio-conn {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 12px;
- color: var(--success);
- cursor: pointer;
- padding: 4px 10px;
- border-radius: 20px;
- background: rgba(87, 210, 143, 0.08);
- transition: all var(--transition);
- flex-shrink: 0;
- user-select: none;
-}
-
-.radio-conn:hover {
- background: rgba(87, 210, 143, 0.15);
-}
-
-.radio-conn-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: var(--success);
- animation: pulse-dot 2s ease-in-out infinite;
-}
-
-.radio-conn-ping {
- font-size: 11px;
- color: var(--text-muted);
- font-weight: 600;
- font-variant-numeric: tabular-nums;
-}
-
-/* ── Radio Connection Modal ── */
-.radio-modal-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, .55);
- z-index: 9000;
- display: flex;
- align-items: center;
- justify-content: center;
- backdrop-filter: blur(4px);
- animation: fade-in .15s ease;
-}
-
-.radio-modal {
- background: var(--bg-primary);
- border: 1px solid var(--border);
- border-radius: 4px;
- width: 340px;
- box-shadow: 0 20px 60px rgba(0, 0, 0, .4);
- overflow: hidden;
- animation: radio-modal-in .2s ease;
-}
-
-@keyframes radio-modal-in {
- from { transform: translateY(20px); opacity: 0; }
- to { transform: translateY(0); opacity: 1; }
-}
-
-.radio-modal-header {
- display: flex;
- align-items: center;
- gap: 8px;
- padding: 14px 16px;
- border-bottom: 1px solid var(--border);
- font-weight: 700;
- font-size: 14px;
-}
-
-.radio-modal-close {
- margin-left: auto;
- background: none;
- border: none;
- color: var(--text-muted);
- cursor: pointer;
- padding: 4px 8px;
- border-radius: 6px;
- font-size: 14px;
- transition: all var(--transition);
-}
-
-.radio-modal-close:hover {
- background: rgba(255, 255, 255, .08);
- color: var(--text-normal);
-}
-
-.radio-modal-body {
- padding: 16px;
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-
-.radio-modal-stat {
- display: flex;
- justify-content: space-between;
- align-items: center;
-}
-
-.radio-modal-label {
- color: var(--text-muted);
- font-size: 13px;
-}
-
-.radio-modal-value {
- font-weight: 600;
- font-size: 13px;
- display: flex;
- align-items: center;
- gap: 6px;
-}
-
-.radio-modal-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- flex-shrink: 0;
-}
-
-/* ══════════════════════════════════════════════
- UNIFIED ADMIN PANEL (ap-*)
- ══════════════════════════════════════════════ */
-
-.ap-overlay {
- position: fixed;
- inset: 0;
- z-index: 9998;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(0, 0, 0, 0.7);
- backdrop-filter: blur(6px);
- animation: fade-in 150ms ease;
-}
-
-.ap-modal {
- display: flex;
- width: min(940px, calc(100vw - 40px));
- height: min(620px, calc(100vh - 60px));
- background: var(--bg-primary);
- border: 1px solid var(--border);
- border-radius: 4px;
- overflow: hidden;
- box-shadow: 0 24px 80px rgba(0, 0, 0, 0.5);
- animation: hub-modal-in 200ms ease;
- position: relative;
-}
-
-/* ── Sidebar ── */
-.ap-sidebar {
- width: 210px;
- min-width: 210px;
- background: var(--bg-deep);
- border-right: 1px solid var(--border);
- display: flex;
- flex-direction: column;
- padding: 0;
-}
-
-.ap-sidebar-title {
- padding: 18px 20px 14px;
- font-size: 15px;
- font-weight: 700;
- color: var(--text-normal);
- letter-spacing: -0.01em;
- border-bottom: 1px solid var(--border);
-}
-
-.ap-nav {
- display: flex;
- flex-direction: column;
- padding: 8px;
- gap: 2px;
-}
-
-.ap-nav-item {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 10px 14px;
- border: none;
- border-left: 3px solid transparent;
- background: transparent;
- color: var(--text-muted);
- font-family: var(--font);
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- border-radius: 0 var(--radius) var(--radius) 0;
- transition: all var(--transition);
- text-align: left;
-}
-
-.ap-nav-item:hover {
- color: var(--text-normal);
- background: var(--bg-secondary);
-}
-
-.ap-nav-item.active {
- color: var(--accent);
- background: rgba(var(--accent-rgb), 0.1);
- border-left-color: var(--accent);
- font-weight: 600;
-}
-
-.ap-nav-icon {
- font-size: 16px;
- line-height: 1;
-}
-
-.ap-nav-label {
- line-height: 1;
-}
-
-.ap-logout-btn {
- margin-top: auto;
- padding: 10px 16px;
- background: transparent;
- border: none;
- border-top: 1px solid var(--border);
- color: #e74c3c;
- font-size: 0.85rem;
- cursor: pointer;
- text-align: left;
- transition: background 0.15s;
-}
-.ap-logout-btn:hover {
- background: rgba(231, 76, 60, 0.1);
-}
-
-/* ── Content Area ── */
-.ap-content {
- flex: 1;
- display: flex;
- flex-direction: column;
- overflow: hidden;
-}
-
-.ap-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16px 20px;
- border-bottom: 1px solid var(--border);
- flex-shrink: 0;
-}
-
-.ap-title {
- font-size: 16px;
- font-weight: 700;
- color: var(--text-normal);
- margin: 0;
-}
-
-.ap-close {
- background: none;
- border: none;
- color: var(--text-muted);
- font-size: 16px;
- cursor: pointer;
- padding: 4px 8px;
- border-radius: 6px;
- transition: all var(--transition);
-}
-
-.ap-close:hover {
- color: var(--text-normal);
- background: rgba(255, 255, 255, 0.08);
-}
-
-.ap-body {
- flex: 1;
- overflow-y: auto;
- padding: 16px 20px;
- scrollbar-width: thin;
- scrollbar-color: var(--bg-tertiary) transparent;
-}
-
-.ap-body::-webkit-scrollbar {
- width: 6px;
-}
-.ap-body::-webkit-scrollbar-thumb {
- background: var(--bg-tertiary);
- border-radius: 3px;
-}
-
-.ap-tab-content {
- display: flex;
- flex-direction: column;
- gap: 12px;
- animation: fade-in 150ms ease;
-}
-
-/* ── Toolbar ── */
-.ap-toolbar {
- display: flex;
- align-items: center;
- gap: 10px;
-}
-
-.ap-search {
- flex: 1;
- padding: 8px 12px;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- background: var(--bg-secondary);
- color: var(--text-normal);
- font-size: 13px;
- font-family: var(--font);
-}
-
-.ap-search:focus {
- outline: none;
- border-color: var(--accent);
-}
-
-.ap-search::placeholder {
- color: var(--text-faint);
-}
-
-/* ── Buttons ── */
-.ap-btn {
- display: inline-flex;
- align-items: center;
- gap: 4px;
- padding: 8px 14px;
- border: none;
- border-radius: var(--radius);
- font-family: var(--font);
- font-size: 13px;
- font-weight: 500;
- cursor: pointer;
- transition: all var(--transition);
- white-space: nowrap;
-}
-
-.ap-btn:disabled {
- opacity: 0.5;
- cursor: default;
-}
-
-.ap-btn-primary {
- background: var(--accent);
- color: #fff;
-}
-.ap-btn-primary:hover:not(:disabled) {
- background: var(--accent-hover);
-}
-
-.ap-btn-danger {
- background: rgba(237, 66, 69, 0.15);
- color: var(--danger);
- border: 1px solid rgba(237, 66, 69, 0.3);
-}
-.ap-btn-danger:hover:not(:disabled) {
- background: rgba(237, 66, 69, 0.25);
-}
-
-.ap-btn-outline {
- background: var(--bg-secondary);
- color: var(--text-muted);
- border: 1px solid var(--border);
-}
-.ap-btn-outline:hover:not(:disabled) {
- color: var(--text-normal);
- border-color: var(--text-faint);
-}
-
-.ap-btn-sm {
- padding: 5px 10px;
- font-size: 12px;
-}
-
-/* ── Upload Zone ── */
-.ap-upload-zone {
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 14px;
- border: 2px dashed var(--border);
- border-radius: var(--radius);
- background: var(--bg-secondary);
- cursor: pointer;
- transition: all var(--transition);
- color: var(--text-muted);
- font-size: 13px;
-}
-
-.ap-upload-zone:hover {
- border-color: var(--accent);
- color: var(--accent);
- background: rgba(var(--accent-rgb), 0.05);
-}
-
-.ap-upload-progress {
- color: var(--accent);
- font-weight: 600;
-}
-
-/* ── Bulk Row ── */
-.ap-bulk-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 6px 0;
-}
-
-.ap-select-all {
- display: flex;
- align-items: center;
- gap: 8px;
- font-size: 12px;
- color: var(--text-muted);
- cursor: pointer;
-}
-
-.ap-select-all input[type="checkbox"] {
- accent-color: var(--accent);
-}
-
-/* ── Sound List ── */
-.ap-list-wrap {
- flex: 1;
- min-height: 0;
-}
-
-.ap-list {
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.ap-item {
- display: flex;
- align-items: center;
- gap: 10px;
- padding: 8px 10px;
- border-radius: var(--radius);
- background: var(--bg-secondary);
- transition: background var(--transition);
-}
-
-.ap-item:hover {
- background: var(--bg-tertiary);
-}
-
-.ap-item-check {
- flex-shrink: 0;
- cursor: pointer;
-}
-
-.ap-item-check input[type="checkbox"] {
- accent-color: var(--accent);
-}
-
-.ap-item-main {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.ap-item-name {
- font-size: 13px;
- font-weight: 600;
- color: var(--text-normal);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.ap-item-meta {
- font-size: 11px;
- color: var(--text-faint);
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.ap-item-actions {
- display: flex;
- gap: 4px;
- flex-shrink: 0;
-}
-
-.ap-rename-row {
- display: flex;
- align-items: center;
- gap: 6px;
- margin-top: 4px;
-}
-
-.ap-rename-input {
- flex: 1;
- padding: 5px 8px;
- border: 1px solid var(--accent);
- border-radius: var(--radius);
- background: var(--bg-deep);
- color: var(--text-normal);
- font-size: 12px;
- font-family: var(--font);
-}
-
-.ap-rename-input:focus {
- outline: none;
-}
-
-/* ── Empty ── */
-.ap-empty {
- text-align: center;
- color: var(--text-muted);
- padding: 40px 16px;
- font-size: 13px;
-}
-
-/* ── Hint ── */
-.ap-hint {
- font-size: 13px;
- color: var(--text-muted);
- margin: 0;
-}
-
-/* ── Status Badge ── */
-.ap-status-badge {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 13px;
- color: var(--text-muted);
-}
-
-.ap-status-dot {
- width: 8px;
- height: 8px;
- border-radius: 50%;
- background: var(--danger);
- flex-shrink: 0;
-}
-
-.ap-status-dot.online {
- background: var(--success);
- animation: pulse-dot 2s ease-in-out infinite;
-}
-
-/* ── Channel List (Streaming) ── */
-.ap-channel-list {
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.ap-channel-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 12px;
- border-radius: var(--radius);
- background: var(--bg-secondary);
- transition: background var(--transition);
-}
-
-.ap-channel-row:hover {
- background: var(--bg-tertiary);
-}
-
-.ap-channel-info {
- display: flex;
- flex-direction: column;
- gap: 2px;
- min-width: 0;
-}
-
-.ap-channel-name {
- font-size: 13px;
- font-weight: 600;
- color: var(--text-normal);
-}
-
-.ap-channel-guild {
- font-size: 11px;
- color: var(--text-faint);
-}
-
-.ap-channel-toggles {
- display: flex;
- gap: 8px;
- flex-shrink: 0;
-}
-
-.ap-toggle {
- display: flex;
- align-items: center;
- gap: 4px;
- font-size: 12px;
- color: var(--text-muted);
- cursor: pointer;
- padding: 4px 8px;
- border-radius: var(--radius);
- transition: all var(--transition);
-}
-
-.ap-toggle input[type="checkbox"] {
- accent-color: var(--accent);
-}
-
-.ap-toggle.active {
- color: var(--accent);
- background: rgba(var(--accent-rgb), 0.08);
-}
-
-/* ── Save Row ── */
-.ap-save-row {
- display: flex;
- justify-content: flex-end;
- padding-top: 8px;
-}
-
-/* ── Profile List (Game Library) ── */
-.ap-profile-list {
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.ap-profile-row {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 10px 12px;
- border-radius: var(--radius);
- background: var(--bg-secondary);
- transition: background var(--transition);
-}
-
-.ap-profile-row:hover {
- background: var(--bg-tertiary);
-}
-
-.ap-profile-avatar {
- width: 36px;
- height: 36px;
- border-radius: 50%;
- flex-shrink: 0;
-}
-
-.ap-profile-info {
- flex: 1;
- min-width: 0;
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-
-.ap-profile-name {
- font-size: 14px;
- font-weight: 600;
- color: var(--text-normal);
-}
-
-.ap-profile-details {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 11px;
-}
-
-.ap-platform-badge {
- padding: 1px 6px;
- border-radius: 3px;
- font-weight: 600;
- font-size: 10px;
-}
-
-.ap-platform-badge.steam {
- background: rgba(66, 133, 244, 0.15);
- color: #64b5f6;
-}
-
-.ap-platform-badge.gog {
- background: rgba(171, 71, 188, 0.15);
- color: #ce93d8;
-}
-
-.ap-profile-total {
- color: var(--text-faint);
-}
-
-/* ── Toast ── */
-.ap-toast {
- position: absolute;
- bottom: 16px;
- left: 50%;
- transform: translateX(-50%);
- padding: 8px 16px;
- border-radius: var(--radius);
- font-size: 13px;
- font-weight: 500;
- background: var(--bg-tertiary);
- color: var(--text-normal);
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
- animation: fade-in 150ms ease;
- z-index: 10;
-}
-
-.ap-toast.error {
- background: rgba(237, 66, 69, 0.2);
- color: #f87171;
-}
-
-/* ── Admin Panel Responsive ── */
-@media (max-width: 768px) {
- .ap-modal {
- flex-direction: column;
- width: calc(100vw - 16px);
- height: calc(100vh - 32px);
- }
-
- .ap-sidebar {
- width: 100%;
- min-width: 100%;
- flex-direction: row;
- border-right: none;
- border-bottom: 1px solid var(--border);
- overflow-x: auto;
- }
-
- .ap-sidebar-title {
- display: none;
- }
-
- .ap-nav {
- flex-direction: row;
- padding: 4px 8px;
- gap: 4px;
- }
-
- .ap-nav-item {
- border-left: none;
- border-bottom: 3px solid transparent;
- border-radius: var(--radius) var(--radius) 0 0;
- padding: 8px 12px;
- white-space: nowrap;
- }
-
- .ap-nav-item.active {
- border-left-color: transparent;
- border-bottom-color: var(--accent);
- }
-
- .ap-channel-toggles {
- flex-direction: column;
- gap: 4px;
- }
-}
-
-/* ════════════════════════════════════════════════════════════════════════════
- Unified Login Button (Header)
- ════════════════════════════════════════════════════════════════════════════ */
-.hub-user-btn {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 4px 12px;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- background: transparent;
- color: var(--text-muted);
- font-family: var(--font);
- font-size: 13px;
- font-weight: 500;
- cursor: pointer;
- transition: all var(--transition);
- line-height: 1;
- white-space: nowrap;
-}
-.hub-user-btn:hover {
- color: var(--accent);
- border-color: var(--accent);
- background: rgba(var(--accent-rgb), 0.06);
-}
-.hub-user-btn.logged-in {
- border-color: rgba(var(--accent-rgb), 0.3);
- color: var(--text-normal);
-}
-.hub-user-btn.admin {
- border-color: #4ade80;
- color: #4ade80;
-}
-.hub-user-avatar {
- width: 24px;
- height: 24px;
- border-radius: 50%;
- object-fit: cover;
-}
-.hub-user-icon {
- font-size: 16px;
- line-height: 1;
-}
-.hub-user-label {
- line-height: 1;
-}
-
-/* ════════════════════════════════════════════════════════════════════════════
- Login Modal
- ════════════════════════════════════════════════════════════════════════════ */
-.hub-login-overlay {
- position: fixed;
- inset: 0;
- z-index: 9999;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(0, 0, 0, 0.7);
- backdrop-filter: blur(4px);
-}
-.hub-login-modal {
- background: var(--bg-primary);
- border: 1px solid var(--border);
- border-radius: 4px;
- width: 380px;
- max-width: 92vw;
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
- overflow: hidden;
- animation: hub-modal-in 200ms ease;
-}
-.hub-login-modal-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16px 20px;
- border-bottom: 1px solid var(--border);
- font-weight: 700;
- font-size: 15px;
-}
-.hub-login-modal-close {
- background: none;
- border: none;
- color: var(--text-muted);
- cursor: pointer;
- font-size: 16px;
- padding: 4px;
- border-radius: 4px;
- transition: all var(--transition);
-}
-.hub-login-modal-close:hover {
- color: var(--text-normal);
- background: var(--bg-tertiary);
-}
-.hub-login-modal-body {
- padding: 20px;
-}
-.hub-login-subtitle {
- color: var(--text-muted);
- font-size: 13px;
- margin: 0 0 16px;
- line-height: 1.4;
-}
-
-/* Provider Buttons */
-.hub-login-providers {
- display: flex;
- flex-direction: column;
- gap: 10px;
-}
-.hub-login-provider-btn {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px 16px;
- border: 1px solid var(--border);
- border-radius: var(--radius-lg);
- background: var(--bg-secondary);
- color: var(--text-normal);
- font-family: var(--font);
- font-size: 14px;
- font-weight: 500;
- cursor: pointer;
- transition: all var(--transition);
- text-decoration: none;
- position: relative;
-}
-.hub-login-provider-btn:hover:not(:disabled) {
- border-color: var(--accent);
- background: rgba(var(--accent-rgb), 0.06);
- transform: translateY(-1px);
-}
-.hub-login-provider-btn:active:not(:disabled) {
- transform: translateY(0);
-}
-.hub-login-provider-btn:disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-.hub-login-provider-btn.discord:hover:not(:disabled) {
- border-color: #5865F2;
- background: rgba(88, 101, 242, 0.08);
-}
-.hub-login-provider-btn.steam:hover {
- border-color: #66c0f4;
- background: rgba(102, 192, 244, 0.08);
-}
-.hub-login-provider-btn.admin:hover {
- border-color: var(--accent);
-}
-.hub-login-provider-icon {
- flex-shrink: 0;
- width: 22px;
- height: 22px;
-}
-.hub-login-provider-icon-emoji {
- font-size: 20px;
- line-height: 1;
- flex-shrink: 0;
-}
-.hub-login-soon {
- position: absolute;
- right: 12px;
- font-size: 10px;
- font-weight: 600;
- text-transform: uppercase;
- color: var(--text-faint);
- background: var(--bg-tertiary);
- padding: 2px 6px;
- border-radius: 4px;
- letter-spacing: 0.5px;
-}
-.hub-login-hint {
- margin: 16px 0 0;
- font-size: 12px;
- color: var(--text-faint);
- line-height: 1.4;
-}
-.hub-login-back {
- background: none;
- border: none;
- color: var(--text-muted);
- font-family: var(--font);
- font-size: 13px;
- cursor: pointer;
- padding: 0;
- margin-bottom: 16px;
- transition: color var(--transition);
-}
-.hub-login-back:hover {
- color: var(--accent);
-}
-
-/* Admin form inside login modal */
-.hub-login-admin-form {
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-.hub-login-admin-label {
- font-size: 14px;
- font-weight: 600;
- color: var(--text-normal);
-}
-.hub-login-admin-input {
- width: 100%;
- padding: 10px 14px;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- background: var(--bg-secondary);
- color: var(--text-normal);
- font-size: 14px;
- font-family: var(--font);
- box-sizing: border-box;
- transition: border-color var(--transition);
-}
-.hub-login-admin-input:focus {
- outline: none;
- border-color: var(--accent);
-}
-.hub-login-admin-error {
- color: var(--danger);
- font-size: 13px;
- margin: 0;
-}
-.hub-login-admin-submit {
- padding: 10px 16px;
- background: var(--accent);
- color: #fff;
- border: none;
- border-radius: var(--radius);
- font-size: 14px;
- font-weight: 600;
- font-family: var(--font);
- cursor: pointer;
- transition: opacity var(--transition);
-}
-.hub-login-admin-submit:hover:not(:disabled) {
- opacity: 0.9;
-}
-.hub-login-admin-submit:disabled {
- opacity: 0.5;
- cursor: not-allowed;
-}
-
-/* ════════════════════════════════════════════════════════════════════════════
- User Settings Panel
- ════════════════════════════════════════════════════════════════════════════ */
-.hub-usettings-overlay {
- position: fixed;
- inset: 0;
- z-index: 9999;
- display: flex;
- align-items: center;
- justify-content: center;
- background: rgba(0, 0, 0, 0.7);
- backdrop-filter: blur(4px);
-}
-.hub-usettings-panel {
- background: var(--bg-primary);
- border: 1px solid var(--border);
- border-radius: 4px;
- width: 520px;
- max-width: 95vw;
- max-height: 85vh;
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
- overflow: hidden;
- display: flex;
- flex-direction: column;
- animation: hub-modal-in 200ms ease;
-}
-
-/* Header */
-.hub-usettings-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16px 20px;
- border-bottom: 1px solid var(--border);
- flex-shrink: 0;
-}
-.hub-usettings-user {
- display: flex;
- align-items: center;
- gap: 12px;
-}
-.hub-usettings-avatar {
- width: 40px;
- height: 40px;
- border-radius: 50%;
- object-fit: cover;
- border: 2px solid rgba(var(--accent-rgb), 0.3);
-}
-.hub-usettings-avatar-placeholder {
- width: 40px;
- height: 40px;
- border-radius: 50%;
- background: var(--accent);
- color: #fff;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 18px;
- font-weight: 700;
-}
-.hub-usettings-user-info {
- display: flex;
- flex-direction: column;
- gap: 2px;
-}
-.hub-usettings-username {
- font-weight: 600;
- font-size: 15px;
- color: var(--text-normal);
-}
-.hub-usettings-discriminator {
- font-size: 12px;
- color: var(--text-muted);
-}
-.hub-usettings-header-actions {
- display: flex;
- align-items: center;
- gap: 8px;
-}
-.hub-usettings-logout {
- background: none;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- color: var(--text-muted);
- font-size: 16px;
- padding: 6px 8px;
- cursor: pointer;
- transition: all var(--transition);
- line-height: 1;
-}
-.hub-usettings-logout:hover {
- color: var(--danger);
- border-color: var(--danger);
-}
-.hub-usettings-close {
- background: none;
- border: none;
- color: var(--text-muted);
- font-size: 16px;
- cursor: pointer;
- padding: 6px;
- border-radius: 4px;
- transition: all var(--transition);
-}
-.hub-usettings-close:hover {
- color: var(--text-normal);
- background: var(--bg-tertiary);
-}
-
-/* Toast */
-.hub-usettings-toast {
- padding: 8px 16px;
- font-size: 13px;
- text-align: center;
- animation: hub-toast-in 300ms ease;
-}
-.hub-usettings-toast.success {
- background: rgba(87, 210, 143, 0.1);
- color: var(--success);
-}
-.hub-usettings-toast.error {
- background: rgba(237, 66, 69, 0.1);
- color: var(--danger);
-}
-@keyframes hub-toast-in {
- from { opacity: 0; transform: translateY(-4px); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-/* Loading */
-.hub-usettings-loading {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 10px;
- padding: 40px;
- color: var(--text-muted);
- font-size: 14px;
-}
-
-/* Content */
-.hub-usettings-content {
- display: flex;
- flex-direction: column;
- overflow: hidden;
- flex: 1;
-}
-
-/* Section tabs */
-.hub-usettings-tabs {
- display: flex;
- gap: 4px;
- padding: 12px 20px 0;
- flex-shrink: 0;
-}
-.hub-usettings-tab {
- flex: 1;
- padding: 10px 12px;
- border: none;
- background: var(--bg-secondary);
- color: var(--text-muted);
- font-family: var(--font);
- font-size: 13px;
- font-weight: 500;
- cursor: pointer;
- border-radius: var(--radius) var(--radius) 0 0;
- transition: all var(--transition);
-}
-.hub-usettings-tab:hover {
- color: var(--text-normal);
- background: var(--bg-tertiary);
-}
-.hub-usettings-tab.active {
- color: var(--accent);
- background: rgba(var(--accent-rgb), 0.1);
- border-bottom: 2px solid var(--accent);
-}
-
-/* Current sound */
-.hub-usettings-current {
- padding: 12px 20px;
- display: flex;
- align-items: center;
- gap: 8px;
- flex-shrink: 0;
- background: var(--bg-secondary);
- margin: 0 20px;
- border-radius: 0 0 var(--radius) var(--radius);
- margin-bottom: 12px;
-}
-.hub-usettings-current-label {
- font-size: 13px;
- color: var(--text-muted);
- flex-shrink: 0;
-}
-.hub-usettings-current-value {
- display: flex;
- align-items: center;
- gap: 6px;
- font-size: 13px;
- font-weight: 500;
- color: var(--accent);
-}
-.hub-usettings-current-none {
- font-size: 13px;
- color: var(--text-faint);
- font-style: italic;
-}
-.hub-usettings-remove-btn {
- background: none;
- border: none;
- color: var(--text-faint);
- cursor: pointer;
- font-size: 11px;
- padding: 2px 4px;
- border-radius: 4px;
- transition: all var(--transition);
-}
-.hub-usettings-remove-btn:hover:not(:disabled) {
- color: var(--danger);
- background: rgba(237, 66, 69, 0.1);
-}
-
-/* Search */
-.hub-usettings-search-wrap {
- position: relative;
- padding: 0 20px;
- margin-bottom: 12px;
- flex-shrink: 0;
-}
-.hub-usettings-search {
- width: 100%;
- padding: 8px 32px 8px 12px;
- border: 1px solid var(--border);
- border-radius: var(--radius);
- background: var(--bg-secondary);
- color: var(--text-normal);
- font-size: 13px;
- font-family: var(--font);
- box-sizing: border-box;
- transition: border-color var(--transition);
-}
-.hub-usettings-search:focus {
- outline: none;
- border-color: var(--accent);
-}
-.hub-usettings-search-clear {
- position: absolute;
- right: 28px;
- top: 50%;
- transform: translateY(-50%);
- background: none;
- border: none;
- color: var(--text-faint);
- cursor: pointer;
- font-size: 12px;
- padding: 4px;
-}
-.hub-usettings-search-clear:hover {
- color: var(--text-normal);
-}
-
-/* Sound list */
-.hub-usettings-sounds {
- flex: 1;
- overflow-y: auto;
- padding: 0 20px 16px;
- scrollbar-width: thin;
- scrollbar-color: var(--bg-tertiary) transparent;
-}
-.hub-usettings-empty {
- text-align: center;
- padding: 32px;
- color: var(--text-faint);
- font-size: 14px;
-}
-
-/* Folder */
-.hub-usettings-folder {
- margin-bottom: 12px;
-}
-.hub-usettings-folder-name {
- font-size: 12px;
- font-weight: 600;
- color: var(--text-muted);
- text-transform: uppercase;
- letter-spacing: 0.5px;
- padding: 4px 0;
- margin-bottom: 6px;
- border-bottom: 1px solid var(--border);
-}
-.hub-usettings-folder-sounds {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
-}
-
-/* Sound button */
-.hub-usettings-sound-btn {
- display: flex;
- align-items: center;
- gap: 4px;
- padding: 5px 10px;
- border: 1px solid var(--border);
- border-radius: 6px;
- background: var(--bg-secondary);
- color: var(--text-normal);
- font-family: var(--font);
- font-size: 12px;
- cursor: pointer;
- transition: all var(--transition);
- white-space: nowrap;
-}
-.hub-usettings-sound-btn:hover:not(:disabled) {
- border-color: var(--accent);
- background: rgba(var(--accent-rgb), 0.06);
-}
-.hub-usettings-sound-btn.selected {
- border-color: var(--accent);
- background: rgba(var(--accent-rgb), 0.12);
- color: var(--accent);
-}
-.hub-usettings-sound-btn:disabled {
- opacity: 0.5;
- cursor: wait;
-}
-.hub-usettings-sound-icon {
- font-size: 12px;
- line-height: 1;
-}
-.hub-usettings-sound-name {
- max-width: 180px;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-/* ── Mobile responsive for user settings ── */
-@media (max-width: 600px) {
- .hub-usettings-panel {
- width: 100%;
- max-width: 100vw;
- max-height: 100vh;
- border-radius: 0;
- }
- .hub-user-label {
- display: none;
- }
-}