- {/* Filter tabs */}
-
-
-
+
+
+
+
+
-
+
+ search
+ setQuery(e.target.value)}
+ />
+ {query && (
+
+ )}
+
- {/* URL import */}
{getUrlType(importUrl) === 'youtube' ? 'smart_display'
@@ -1065,119 +982,112 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: So
-
- {/* 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}
- />
- {Math.round(volume * 100)}%
-
+
- {/* 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
-
- )}
-
- )}
-
+
+ {
+ const newVol = volume > 0 ? 0 : 0.5;
+ setVolume(newVol);
+ if (guildId) apiSetVolumeLive(guildId, newVol).catch(() => {});
+ }}
+ >
+ {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(() => {});
+ }, 50);
+ }
+ }}
+ style={{ '--vol': `${Math.round(volume * 100)}%` } as React.CSSProperties}
+ />
+ {Math.round(volume * 100)}%
+
- {/* Card size slider */}
-
-
grid_view
-
setCardSize(parseInt(e.target.value))}
+
+
+
+
+
+
+
+ grid_view
+ setCardSize(parseInt(e.target.value))}
+ />
+
+
+
+ {THEMES.map(t => (
+
setTheme(t.id)}
/>
-
+ ))}
- {/* ═══ 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}
-
- ))}
+
+
+
library_music
+
+ Sounds gesamt
+ {totalSoundsDisplay}
- )}
+
+
+
leaderboard
+
+
Most Played
+
+ {analyticsTop.length === 0 ? (
+ Noch keine Plays
+ ) : (
+ analyticsTop.map((item, idx) => (
+
+ {idx + 1}. {item.name} ({item.count})
+
+ ))
+ )}
+
+
+
+
{/* ═══ FOLDER CHIPS ═══ */}
{activeTab === 'all' && visibleFolders.length > 0 && (
@@ -1201,8 +1111,8 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: So
)}
- {/* ═══ SOUND GRID ═══ */}
-
+ {/* ═══ MAIN ═══ */}
+
{displaySounds.length === 0 ? (
{activeTab === 'favorites' ? '\u2B50' : '\uD83D\uDD07'}
@@ -1219,88 +1129,66 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: So
: 'Hier gibt\'s noch nichts zu hoeren.'}
- ) : (() => {
- // 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 });
- });
+ ) : (
+
+ {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)';
- 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}}
-
-
- );
- })}
-
-
- ));
- })()}
-
+ 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 && (
@@ -1327,7 +1215,14 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: So
{
const path = ctxMenu.sound.relativePath ?? ctxMenu.sound.fileName;
- await deleteAdminPaths([path]);
+ 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');
+ }
setCtxMenu(null);
}}>
delete
@@ -1408,142 +1303,6 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: So
)}
- {/* ═══ 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 && (
@@ -1667,7 +1426,7 @@ export default function SoundboardTab({ data, isAdmin: isAdminProp = false }: So
{dropPhase === 'naming' && (
)}
- {isAdmin && (
-
- )}
{streams.length === 0 && !isBroadcasting ? (
@@ -881,80 +801,6 @@ export default function StreamingTab({ data, isAdmin: isAdminProp = false }: { d
)}
- {/* ── 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 bb90670..abdf17a 100644
--- a/web/src/plugins/streaming/streaming.css
+++ b/web/src/plugins/streaming/streaming.css
@@ -373,25 +373,23 @@
/* ── Empty state ── */
.stream-empty {
- flex: 1; display: flex; flex-direction: column;
- align-items: center; justify-content: center; gap: 16px;
- padding: 40px; height: 100%;
+ text-align: center;
+ padding: 60px 20px;
+ color: var(--text-muted);
}
.stream-empty-icon {
- 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); }
+ font-size: 48px;
+ margin-bottom: 12px;
+ opacity: 0.4;
}
.stream-empty h3 {
- font-size: 26px; font-weight: 700; color: #f2f3f5;
- letter-spacing: -0.5px; margin: 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--text-normal);
+ margin-bottom: 6px;
}
.stream-empty p {
- font-size: 15px; color: #80848e;
- text-align: center; max-width: 360px; line-height: 1.5; margin: 0;
+ font-size: 14px;
}
/* ── Error ── */
diff --git a/web/src/plugins/watch-together/watch-together.css b/web/src/plugins/watch-together/watch-together.css
index 95111c5..32684b4 100644
--- a/web/src/plugins/watch-together/watch-together.css
+++ b/web/src/plugins/watch-together/watch-together.css
@@ -161,25 +161,23 @@
/* ── Empty state ── */
.wt-empty {
- flex: 1; display: flex; flex-direction: column;
- align-items: center; justify-content: center; gap: 16px;
- padding: 40px; height: 100%;
+ text-align: center;
+ padding: 60px 20px;
+ color: var(--text-muted);
}
.wt-empty-icon {
- 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); }
+ font-size: 48px;
+ margin-bottom: 12px;
+ opacity: 0.4;
}
.wt-empty h3 {
- font-size: 26px; font-weight: 700; color: #f2f3f5;
- letter-spacing: -0.5px; margin: 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: var(--text-normal);
+ margin-bottom: 6px;
}
.wt-empty p {
- font-size: 15px; color: #80848e;
- text-align: center; max-width: 360px; line-height: 1.5; margin: 0;
+ font-size: 14px;
}
/* ── Error ── */
@@ -558,7 +556,7 @@
.wt-quality-select {
background: var(--bg-secondary, #2a2620);
color: var(--text-primary, #e0e0e0);
- border: 1px solid var(--border-color, #3a352d);
+ border: 1px solid var(--border-color, #322d26);
border-radius: 6px;
padding: 2px 6px;
font-size: 12px;
diff --git a/web/src/styles.css b/web/src/styles.css
index 6319c92..4d5c046 100644
--- a/web/src/styles.css
+++ b/web/src/styles.css
@@ -1,169 +1,38 @@
-/* ============================================================
- GAMING HUB -- Global Styles
- Design System v3.0 -- CI Redesign (warm brown, DM Sans)
- ============================================================ */
-
-/* -- Google Fonts ------------------------------------------- */
+/* ── 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');
-/* ============================================================
- A) CSS CUSTOM PROPERTIES
- ============================================================ */
+/* ── CSS Variables ── */
:root {
- /* -- Surface Palette (warm brown) ------------------------- */
- --bg-deepest: #141209;
- --bg-deep: #1a1810;
+ --bg-deep: #1a1810;
--bg-primary: #211e17;
--bg-secondary: #2a2620;
--bg-tertiary: #322d26;
- --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;
- --warning: #fee75c;
- --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;
-}
-
-
-/* ============================================================
- B) ACCENT THEME SYSTEM
- ============================================================ */
-
-/* Default: Ember (orange) */
-:root,
-[data-accent="ember"] {
- --accent: #e67e22;
+ --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-soft: rgba(230, 126, 34, 0.15);
- --accent-text: #f0a050;
+ --accent-dim: rgba(230, 126, 34, 0.15);
+ --accent-border: rgba(230, 126, 34, 0.35);
+ --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;
}
-[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
- ============================================================ */
+/* ── Reset & Base ── */
*,
*::before,
*::after {
@@ -172,828 +41,506 @@
box-sizing: border-box;
}
-html {
- scroll-behavior: smooth;
-}
-
html, body {
height: 100%;
- overflow: hidden;
- font-family: var(--font-body);
- font-size: var(--text-base);
- color: var(--text-primary);
- background: var(--bg-primary);
+ font-family: var(--font);
+ font-size: 13px;
+ color: var(--text-normal);
+ background: var(--bg-deep);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
+ overflow: hidden;
}
#root {
height: 100%;
}
-/* -- 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 {
+/* ── App Shell ── */
+.hub-app {
display: flex;
+ flex-direction: column;
height: 100vh;
- width: 100vw;
overflow: hidden;
}
-/* -- Sidebar ------------------------------------------------ */
-.app-sidebar {
- width: var(--sidebar-nav-w);
- min-width: var(--sidebar-nav-w);
- background: var(--bg-deep);
- display: flex;
- flex-direction: column;
- border-right: 1px solid var(--border-subtle);
- z-index: 15;
-}
-
-.sidebar-header {
- height: var(--header-h);
+/* ── Header ── */
+.hub-header {
+ position: sticky;
+ top: 0;
+ z-index: 100;
display: flex;
align-items: center;
- padding: 0 var(--space-3);
- border-bottom: 1px solid var(--border-subtle);
- gap: var(--space-2);
- flex-shrink: 0;
-}
-
-.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;
-}
-
-.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;
-}
-
-.sidebar-nav {
- flex: 1;
- overflow-y: auto;
- padding: var(--space-2);
-}
-
-.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;
+ height: var(--header-height);
+ min-height: var(--header-height);
+ padding: 0 16px;
background: var(--bg-primary);
- position: relative;
+ border-bottom: 1px solid var(--border);
+ gap: 16px;
}
-/* -- Content Header ----------------------------------------- */
-.content-header {
- height: var(--header-h);
- min-height: var(--header-h);
+.hub-header-left {
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;
+ gap: 10px;
flex-shrink: 0;
}
-.content-header__title {
- font-family: var(--font-display);
- font-size: var(--text-lg);
- font-weight: var(--weight-semibold);
+.hub-logo {
+ font-size: 24px;
+ line-height: 1;
+}
+
+.hub-title {
+ font-size: 18px;
+ font-weight: 700;
+ color: var(--text-normal);
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;
+/* ── Connection Status Dot ── */
+.hub-conn-dot {
+ width: 10px;
+ height: 10px;
border-radius: 50%;
- cursor: pointer;
- border: 2px solid transparent;
- transition: all var(--duration-fast);
+ background: var(--danger);
+ flex-shrink: 0;
+ transition: background var(--transition);
+ box-shadow: 0 0 0 2px rgba(237, 66, 69, 0.25);
}
-.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%;
+.hub-conn-dot.online {
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% { 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); }
+ 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);
+ }
}
-/* -- Volume Control ----------------------------------------- */
-.volume-control {
+/* ── Tab Navigation ── */
+.hub-tabs {
display: flex;
align-items: center;
- gap: var(--space-2);
- color: var(--text-tertiary);
+ 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 {
font-size: 16px;
+ line-height: 1;
}
-.volume-slider {
- -webkit-appearance: none;
- appearance: none;
- width: 80px;
- height: 4px;
- border-radius: 2px;
- background: var(--bg-elevated);
- outline: none;
+.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);
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;
}
-.volume-slider::-webkit-slider-thumb {
- -webkit-appearance: none;
- appearance: none;
- width: 14px;
- height: 14px;
- border-radius: 50%;
- background: var(--accent);
+.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;
cursor: pointer;
- border: none;
- /* no glow */
+ 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;
}
-.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,
+/* ── Update Modal ── */
.hub-update-overlay {
position: fixed;
inset: 0;
- background: rgba(0, 0, 0, .7);
- /* no blur */
- z-index: 1000;
+ z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
- animation: modal-fade-in var(--duration-normal) ease;
+ 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;
}
-@keyframes modal-fade-in {
- from { opacity: 0; }
- to { opacity: 1; }
+/* ── 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-slide-in {
- from { opacity: 0; transform: scale(0.95) translateY(8px); }
- to { opacity: 1; transform: scale(1) translateY(0); }
+/* ── 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;
}
-/* -- Version Info Modal ------------------------------------- */
+/* ── 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 ── */
.hub-version-clickable {
cursor: pointer;
- transition: all var(--duration-fast);
+ transition: all var(--transition);
padding: 2px 8px;
- border-radius: var(--radius-sm);
+ border-radius: var(--radius);
}
-
.hub-version-clickable:hover {
color: var(--accent);
- background: var(--accent-soft);
+ 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);
}
-
.hub-version-modal {
- background: var(--bg-secondary);
- border: 1px solid var(--border-default);
- border-radius: var(--radius-lg);
+ background: var(--bg-primary);
+ border: 1px solid var(--border);
+ border-radius: 4px;
width: 340px;
- box-shadow: var(--shadow-xl);
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
overflow: hidden;
- animation: modal-slide-in var(--duration-normal) ease;
+ 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); }
}
-
.hub-version-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
- padding: var(--space-3) var(--space-4);
- border-bottom: 1px solid var(--border-subtle);
- font-weight: var(--weight-bold);
- font-size: var(--text-base);
+ padding: 14px 16px;
+ border-bottom: 1px solid var(--border);
+ font-weight: 700;
+ font-size: 14px;
}
-
.hub-version-modal-close {
background: none;
border: none;
- color: var(--text-tertiary);
+ color: var(--text-muted);
cursor: pointer;
- padding: var(--space-1) var(--space-2);
- border-radius: var(--radius-sm);
- font-size: var(--text-base);
- transition: all var(--duration-fast);
+ padding: 4px 8px;
+ border-radius: 6px;
+ font-size: 14px;
+ transition: all var(--transition);
}
-
.hub-version-modal-close:hover {
- background: var(--surface-glass-hover);
- color: var(--text-primary);
+ background: rgba(255, 255, 255, 0.08);
+ color: var(--text-normal);
}
-
.hub-version-modal-body {
- padding: var(--space-4);
+ padding: 16px;
display: flex;
flex-direction: column;
- gap: var(--space-3);
+ gap: 12px;
}
-
.hub-version-modal-row {
display: flex;
justify-content: space-between;
align-items: center;
}
-
.hub-version-modal-label {
- color: var(--text-secondary);
- font-size: var(--text-sm);
+ color: var(--text-muted);
+ font-size: 13px;
}
-
.hub-version-modal-value {
- font-weight: var(--weight-semibold);
- font-size: var(--text-sm);
+ font-weight: 600;
+ font-size: 13px;
display: flex;
align-items: center;
- gap: var(--space-2);
+ gap: 6px;
}
-
.hub-version-modal-dot {
width: 8px;
height: 8px;
@@ -1001,629 +548,131 @@ 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: var(--weight-medium);
- font-size: var(--text-sm);
+ font-weight: 500;
+ font-size: 13px;
}
-
.hub-version-modal-link:hover {
text-decoration: underline;
}
-
.hub-version-modal-hint {
- font-size: var(--text-xs);
+ font-size: 11px;
color: var(--accent);
- padding: var(--space-2) var(--space-3);
- background: var(--accent-soft);
- border-radius: var(--radius-sm);
+ padding: 6px 10px;
+ background: rgba(230, 126, 34, 0.1);
+ border-radius: var(--radius);
text-align: center;
}
-/* -- Update Section in Version Modal ------------------------ */
+/* ── Update Section in Version Modal ── */
.hub-version-modal-update {
- margin-top: var(--space-1);
- padding-top: var(--space-3);
- border-top: 1px solid var(--border-subtle);
+ margin-top: 4px;
+ padding-top: 12px;
+ border-top: 1px solid var(--border);
}
-
.hub-version-modal-update-btn {
width: 100%;
- padding: var(--space-3) var(--space-4);
+ padding: 10px 16px;
border: none;
- border-radius: var(--radius-sm);
+ border-radius: var(--radius);
background: var(--bg-tertiary);
- color: var(--text-primary);
- font-size: var(--text-sm);
- font-weight: var(--weight-semibold);
+ color: var(--text-normal);
+ font-size: 13px;
+ font-weight: 600;
cursor: pointer;
- transition: all var(--duration-fast);
+ transition: all var(--transition);
display: flex;
align-items: center;
justify-content: center;
- gap: var(--space-2);
- font-family: var(--font-body);
+ gap: 8px;
}
-
.hub-version-modal-update-btn:hover {
background: var(--bg-hover);
color: var(--accent);
}
-
.hub-version-modal-update-btn.ready {
- background: rgba(87, 210, 143, .15);
- color: var(--success);
+ background: rgba(46, 204, 113, 0.15);
+ color: #2ecc71;
}
-
.hub-version-modal-update-btn.ready:hover {
- background: rgba(87, 210, 143, .25);
+ background: rgba(46, 204, 113, 0.25);
}
-
.hub-version-modal-update-status {
display: flex;
align-items: center;
justify-content: center;
- gap: var(--space-2);
- font-size: var(--text-sm);
- color: var(--text-secondary);
- padding: var(--space-2) 0;
+ gap: 8px;
+ font-size: 13px;
+ color: var(--text-muted);
+ padding: 8px 0;
flex-wrap: wrap;
}
-
.hub-version-modal-update-status.success {
- color: var(--success);
+ color: #2ecc71;
}
-
.hub-version-modal-update-status.error {
- color: var(--danger);
+ color: #e74c3c;
}
-
.hub-version-modal-update-retry {
background: none;
border: none;
- color: var(--text-secondary);
- font-size: var(--text-xs);
+ color: var(--text-muted);
+ font-size: 11px;
cursor: pointer;
text-decoration: underline;
padding: 2px 4px;
width: 100%;
- margin-top: var(--space-1);
- font-family: var(--font-body);
+ margin-top: 4px;
}
-
.hub-version-modal-update-retry:hover {
- color: var(--text-primary);
+ color: var(--text-normal);
}
-
-/* -- 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 {
+@keyframes hub-spin {
to { transform: rotate(360deg); }
}
-
.hub-update-spinner {
width: 14px;
height: 14px;
- border: 2px solid var(--border-default);
+ border: 2px solid var(--border);
border-top-color: var(--accent);
border-radius: 50%;
- animation: spin 0.8s linear infinite;
+ animation: hub-spin 0.8s linear infinite;
flex-shrink: 0;
}
-/* -- 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);
- 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-admin-modal-input:focus {
- border-color: var(--accent);
- box-shadow: 0 0 0 3px var(--accent-soft);
-}
-
-.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);
-}
-
-.btn--ghost:hover {
- background: var(--surface-glass);
- color: var(--text-primary);
-}
-
-.btn--danger {
- background: var(--danger);
- color: #fff;
-}
-
-.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 {
+/* ── Main Content Area ── */
+.hub-content {
flex: 1;
+ overflow-y: auto;
+ overflow-x: hidden;
+ background: var(--bg-deep);
+ scrollbar-width: thin;
+ scrollbar-color: var(--bg-tertiary) transparent;
}
-.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);
+.hub-content::-webkit-scrollbar {
+ width: 6px;
}
-.toast__close:hover {
- color: var(--text-primary);
+.hub-content::-webkit-scrollbar-track {
+ background: transparent;
}
-@keyframes toast-in {
- from { opacity: 0; transform: translateX(20px); }
- to { opacity: 1; transform: translateX(0); }
+.hub-content::-webkit-scrollbar-thumb {
+ background: var(--bg-tertiary);
+ border-radius: 3px;
}
-@keyframes toast-out {
- from { opacity: 1; transform: translateX(0); }
- to { opacity: 0; transform: translateX(20px); }
+.hub-content::-webkit-scrollbar-thumb:hover {
+ background: var(--text-faint);
}
-
-/* Noise texture removed per CI v2 */
-
-
-/* ============================================================
- PRESERVED: Empty State
- ============================================================ */
+/* ── Empty State ── */
.hub-empty {
display: flex;
flex-direction: column;
@@ -1632,84 +681,124 @@ html, body {
height: 100%;
min-height: 300px;
text-align: center;
- padding: var(--space-7);
+ padding: 32px;
animation: fade-in 300ms ease;
}
.hub-empty-icon {
font-size: 64px;
line-height: 1;
- margin-bottom: var(--space-5);
+ margin-bottom: 20px;
opacity: 0.6;
filter: grayscale(30%);
}
.hub-empty h2 {
- font-size: var(--text-xl);
- font-weight: var(--weight-bold);
- color: var(--text-primary);
- margin-bottom: var(--space-2);
+ font-size: 22px;
+ font-weight: 700;
+ color: var(--text-normal);
+ margin-bottom: 8px;
}
.hub-empty p {
- font-size: var(--text-md);
- color: var(--text-secondary);
+ font-size: 15px;
+ color: var(--text-muted);
max-width: 360px;
line-height: 1.5;
}
-/* -- 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);
+/* ── Animations ── */
+@keyframes fade-in {
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
}
-/* -- 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;
+/* ── Selection ── */
+::selection {
+ background: rgba(var(--accent-rgb), 0.3);
+ color: var(--text-normal);
}
-.hub-download-btn:hover {
- color: var(--accent);
- background: var(--accent-soft);
+/* ── Focus Styles ── */
+:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: 2px;
}
-.hub-download-icon {
- font-size: var(--text-base);
- line-height: 1;
+/* ── 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-label {
- line-height: 1;
+@media (max-width: 480px) {
+ .hub-header-right {
+ display: none;
+ }
+
+ .hub-header {
+ padding: 0 8px;
+ gap: 6px;
+ }
+
+ .hub-title {
+ font-size: 14px;
+ }
}
-
-/* ============================================================
- PRESERVED: Radio Plugin Styles
- ============================================================ */
+/* ══════════════════════════════════════════════
+ RADIO PLUGIN – World Radio Globe
+ ══════════════════════════════════════════════ */
.radio-container {
display: flex;
@@ -1718,62 +807,80 @@ 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: #16131c;
- --bg-primary: #1d1926;
- --bg-secondary: #272235;
- --bg-tertiary: #312b42;
- --accent: #9b59b6;
+ --bg-deep: #13111c;
+ --bg-primary: #1a1726;
+ --bg-secondary: #241f35;
+ --bg-tertiary: #2e2845;
+ --accent: #9b59b6;
+ --accent-rgb: 155, 89, 182;
--accent-hover: #8e44ad;
}
.radio-container[data-theme="forest"] {
- --bg-deep: #121a14;
- --bg-primary: #172119;
- --bg-secondary: #1f2e22;
- --bg-tertiary: #283a2c;
- --accent: #2ecc71;
+ --bg-deep: #0f1a14;
+ --bg-primary: #142119;
+ --bg-secondary: #1c2e22;
+ --bg-tertiary: #253a2c;
+ --accent: #2ecc71;
+ --accent-rgb: 46, 204, 113;
--accent-hover: #27ae60;
}
.radio-container[data-theme="ocean"] {
- --bg-deep: #101620;
- --bg-primary: #151e2c;
- --bg-secondary: #1c2a38;
- --bg-tertiary: #243646;
- --accent: #3498db;
+ --bg-deep: #0a1628;
+ --bg-primary: #0f1e33;
+ --bg-secondary: #162a42;
+ --bg-tertiary: #1e3652;
+ --accent: #3498db;
+ --accent-rgb: 52, 152, 219;
--accent-hover: #2980b9;
}
.radio-container[data-theme="cherry"] {
- --bg-deep: #1a1014;
- --bg-primary: #22151a;
- --bg-secondary: #301e25;
- --bg-tertiary: #3e2830;
- --accent: #e74c6f;
+ --bg-deep: #1a0f14;
+ --bg-primary: #22141a;
+ --bg-secondary: #301c25;
+ --bg-tertiary: #3e2530;
+ --accent: #e74c6f;
+ --accent-rgb: 231, 76, 111;
--accent-hover: #c0392b;
}
-/* -- Radio Topbar ------------------------------------------- */
+/* ── Globe ── */
+/* ── Radio Topbar ── */
.radio-topbar {
display: flex;
align-items: center;
- padding: 0 var(--space-4);
- height: var(--header-h);
- background: var(--bg-secondary);
+ padding: 0 16px;
+ height: 52px;
+ background: var(--bg-secondary, #2a2620);
border-bottom: 1px solid rgba(0, 0, 0, .24);
z-index: 10;
flex-shrink: 0;
- gap: var(--space-4);
+ gap: 16px;
}
.radio-topbar-left {
display: flex;
align-items: center;
- gap: var(--space-3);
+ gap: 10px;
flex-shrink: 0;
}
@@ -1782,17 +889,17 @@ html, body {
}
.radio-topbar-title {
- font-size: var(--text-lg);
- font-weight: var(--weight-bold);
- color: var(--text-primary);
- letter-spacing: -0.02em;
+ font-size: 16px;
+ font-weight: 700;
+ color: var(--text-normal);
+ letter-spacing: -.02em;
}
.radio-topbar-np {
flex: 1;
display: flex;
align-items: center;
- gap: var(--space-3);
+ gap: 10px;
min-width: 0;
justify-content: center;
}
@@ -1800,7 +907,7 @@ html, body {
.radio-topbar-right {
display: flex;
align-items: center;
- gap: var(--space-2);
+ gap: 6px;
flex-shrink: 0;
margin-left: auto;
}
@@ -1808,17 +915,17 @@ html, body {
.radio-topbar-stop {
display: flex;
align-items: center;
- gap: var(--space-1);
+ gap: 4px;
background: var(--danger);
color: #fff;
border: none;
- 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);
+ border-radius: var(--radius);
+ padding: 6px 14px;
+ font-size: 13px;
+ font-family: var(--font);
+ font-weight: 600;
cursor: pointer;
- transition: all var(--duration-fast);
+ transition: all var(--transition);
flex-shrink: 0;
}
@@ -1829,11 +936,11 @@ html, body {
.radio-theme-inline {
display: flex;
align-items: center;
- gap: var(--space-1);
- margin-left: var(--space-1);
+ gap: 4px;
+ margin-left: 4px;
}
-/* -- Globe -------------------------------------------------- */
+/* ── Globe Wrapper ── */
.radio-globe-wrap {
position: relative;
flex: 1;
@@ -1849,10 +956,10 @@ html, body {
outline: none !important;
}
-/* -- Radio Search ------------------------------------------- */
+/* ── Search Overlay ── */
.radio-search {
position: absolute;
- top: var(--space-4);
+ top: 16px;
left: 50%;
transform: translateX(-50%);
z-index: 20;
@@ -1863,12 +970,11 @@ html, body {
display: flex;
align-items: center;
background: rgba(33, 30, 23, 0.92);
- /* no blur */
- border: 1px solid var(--border-default);
+ border: 1px solid var(--border);
border-radius: var(--radius-lg);
- padding: 0 var(--space-3);
- gap: var(--space-2);
- box-shadow: var(--shadow-lg);
+ padding: 0 14px;
+ gap: 8px;
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
}
.radio-search-icon {
@@ -1881,59 +987,60 @@ html, body {
flex: 1;
background: transparent;
border: none;
- color: var(--text-primary);
- font-family: var(--font-body);
- font-size: var(--text-base);
- padding: var(--space-3) 0;
+ color: var(--text-normal);
+ font-family: var(--font);
+ font-size: 14px;
+ padding: 12px 0;
outline: none;
}
.radio-search-input::placeholder {
- color: var(--text-tertiary);
+ color: var(--text-faint);
}
.radio-search-clear {
background: none;
border: none;
- color: var(--text-secondary);
- font-size: var(--text-base);
+ color: var(--text-muted);
+ font-size: 14px;
cursor: pointer;
- padding: var(--space-1);
- border-radius: var(--radius-xs);
- transition: color var(--duration-fast);
+ padding: 4px;
+ border-radius: 4px;
+ transition: color var(--transition);
}
.radio-search-clear:hover {
- color: var(--text-primary);
+ color: var(--text-normal);
}
-/* -- Radio Search Results ----------------------------------- */
+/* ── Search Results ── */
.radio-search-results {
- margin-top: var(--space-2);
+ margin-top: 6px;
background: rgba(33, 30, 23, 0.95);
- /* no blur */
- border: 1px solid var(--border-default);
+ border: 1px solid var(--border);
border-radius: var(--radius-lg);
max-height: 360px;
overflow-y: auto;
- box-shadow: var(--shadow-xl);
+ box-shadow: 0 12px 40px rgba(0,0,0,0.5);
+ scrollbar-width: thin;
+ scrollbar-color: var(--bg-tertiary) transparent;
}
.radio-search-result {
display: flex;
align-items: center;
- gap: var(--space-3);
+ gap: 10px;
width: 100%;
- padding: var(--space-3) var(--space-3);
+ padding: 10px 14px;
background: none;
border: none;
- border-bottom: 1px solid var(--border-subtle);
- color: var(--text-primary);
- font-family: var(--font-body);
- font-size: var(--text-base);
+ border-bottom: 1px solid var(--border);
+ color: var(--text-normal);
+ font-family: var(--font);
+ font-size: 14px;
cursor: pointer;
text-align: left;
- transition: background var(--duration-fast);
+ transition: background var(--transition);
}
.radio-search-result:last-child {
@@ -1941,11 +1048,11 @@ html, body {
}
.radio-search-result:hover {
- background: var(--accent-soft);
+ background: rgba(var(--accent-rgb), 0.08);
}
.radio-search-result-icon {
- font-size: var(--text-lg);
+ font-size: 18px;
flex-shrink: 0;
}
@@ -1957,59 +1064,58 @@ html, body {
}
.radio-search-result-title {
- font-weight: var(--weight-semibold);
+ font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.radio-search-result-sub {
- font-size: var(--text-sm);
- color: var(--text-secondary);
+ font-size: 12px;
+ color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
-/* -- Favorites FAB ------------------------------------------ */
+/* ── Favorites FAB ── */
.radio-fab {
position: absolute;
- top: var(--space-4);
- right: var(--space-4);
+ top: 16px;
+ right: 16px;
z-index: 20;
display: flex;
align-items: center;
- gap: var(--space-1);
- padding: var(--space-3) var(--space-3);
+ gap: 4px;
+ padding: 10px 14px;
background: rgba(33, 30, 23, 0.92);
- /* no blur */
- border: 1px solid var(--border-default);
+ border: 1px solid var(--border);
border-radius: var(--radius-lg);
- color: var(--text-primary);
+ color: var(--text-normal);
font-size: 16px;
cursor: pointer;
- box-shadow: var(--shadow-lg);
- transition: all var(--duration-fast);
+ box-shadow: 0 8px 32px rgba(0,0,0,0.4);
+ transition: all var(--transition);
}
.radio-fab:hover,
.radio-fab.active {
- background: var(--accent-soft);
- border-color: var(--accent);
+ background: rgba(var(--accent-rgb), 0.15);
+ border-color: rgba(var(--accent-rgb), 0.3);
}
.radio-fab-badge {
- font-size: var(--text-xs);
- font-weight: var(--weight-bold);
+ font-size: 11px;
+ font-weight: 700;
background: var(--accent);
color: #fff;
padding: 1px 6px;
- border-radius: var(--radius-full);
+ border-radius: 6px;
min-width: 18px;
text-align: center;
}
-/* -- Side Panel --------------------------------------------- */
+/* ── Side Panel ── */
.radio-panel {
position: absolute;
top: 0;
@@ -2018,12 +1124,11 @@ html, body {
height: 100%;
z-index: 15;
background: rgba(33, 30, 23, 0.95);
- /* no blur */
- border-left: 1px solid var(--border-default);
+ border-left: 1px solid var(--border);
display: flex;
flex-direction: column;
- animation: slide-in-right var(--duration-normal) ease;
- box-shadow: -8px 0 32px rgba(0, 0, 0, .3);
+ animation: slide-in-right 200ms ease;
+ box-shadow: -8px 0 32px rgba(0,0,0,0.3);
}
@keyframes slide-in-right {
@@ -2035,20 +1140,20 @@ html, body {
display: flex;
align-items: center;
justify-content: space-between;
- padding: var(--space-4);
- border-bottom: 1px solid var(--border-subtle);
+ padding: 16px;
+ border-bottom: 1px solid var(--border);
flex-shrink: 0;
}
.radio-panel-header h3 {
- font-size: var(--text-lg);
- font-weight: var(--weight-bold);
- color: var(--text-primary);
+ font-size: 16px;
+ font-weight: 700;
+ color: var(--text-normal);
}
.radio-panel-sub {
- font-size: var(--text-sm);
- color: var(--text-secondary);
+ font-size: 12px;
+ color: var(--text-muted);
display: block;
margin-top: 2px;
}
@@ -2056,60 +1161,62 @@ html, body {
.radio-panel-close {
background: none;
border: none;
- color: var(--text-secondary);
- font-size: var(--text-lg);
+ color: var(--text-muted);
+ font-size: 18px;
cursor: pointer;
- padding: var(--space-1) var(--space-2);
- border-radius: var(--radius-xs);
- transition: all var(--duration-fast);
+ padding: 4px 8px;
+ border-radius: 4px;
+ transition: all var(--transition);
}
.radio-panel-close:hover {
- color: var(--text-primary);
- background: var(--surface-glass-hover);
+ color: var(--text-normal);
+ background: var(--bg-secondary);
}
.radio-panel-body {
flex: 1;
overflow-y: auto;
- padding: var(--space-2);
+ padding: 8px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--bg-tertiary) transparent;
}
.radio-panel-empty {
text-align: center;
- color: var(--text-secondary);
- padding: var(--space-8) var(--space-4);
- font-size: var(--text-base);
+ color: var(--text-muted);
+ padding: 40px 16px;
+ font-size: 14px;
}
.radio-panel-loading {
display: flex;
flex-direction: column;
align-items: center;
- gap: var(--space-3);
- padding: var(--space-8) var(--space-4);
- color: var(--text-secondary);
- font-size: var(--text-base);
+ gap: 12px;
+ padding: 40px 16px;
+ color: var(--text-muted);
+ font-size: 14px;
}
-/* -- Station Card ------------------------------------------- */
+/* ── Station Card ── */
.radio-station {
display: flex;
align-items: center;
justify-content: space-between;
- padding: var(--space-3) var(--space-3);
- border-radius: var(--radius-sm);
- transition: background var(--duration-fast);
- gap: var(--space-3);
+ padding: 10px 12px;
+ border-radius: var(--radius);
+ transition: background var(--transition);
+ gap: 10px;
}
.radio-station:hover {
- background: var(--surface-glass-hover);
+ background: var(--bg-secondary);
}
.radio-station.playing {
- background: var(--accent-soft);
- border: 1px solid var(--accent);
+ background: rgba(var(--accent-rgb), 0.1);
+ border: 1px solid rgba(var(--accent-rgb), 0.2);
}
.radio-station-info {
@@ -2121,17 +1228,17 @@ html, body {
}
.radio-station-name {
- font-size: var(--text-base);
- font-weight: var(--weight-semibold);
- color: var(--text-primary);
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--text-normal);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.radio-station-loc {
- font-size: var(--text-xs);
- color: var(--text-tertiary);
+ font-size: 11px;
+ color: var(--text-faint);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -2140,31 +1247,31 @@ html, body {
.radio-station-live {
display: flex;
align-items: center;
- gap: var(--space-2);
- font-size: var(--text-xs);
+ gap: 6px;
+ font-size: 11px;
color: var(--accent);
- font-weight: var(--weight-semibold);
+ font-weight: 600;
}
.radio-station-btns {
display: flex;
- gap: var(--space-1);
+ gap: 4px;
flex-shrink: 0;
}
-/* -- Radio Buttons ------------------------------------------ */
+/* ── Buttons ── */
.radio-btn-play,
.radio-btn-stop {
width: 34px;
height: 34px;
border: none;
border-radius: 50%;
- font-size: var(--text-base);
+ font-size: 14px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
- transition: all var(--duration-fast);
+ transition: all var(--transition);
}
.radio-btn-play {
@@ -2199,23 +1306,23 @@ html, body {
font-size: 16px;
cursor: pointer;
background: transparent;
- color: var(--text-tertiary);
+ color: var(--text-faint);
display: flex;
align-items: center;
justify-content: center;
- transition: all var(--duration-fast);
+ transition: all var(--transition);
}
.radio-btn-fav:hover {
color: var(--warning);
- background: rgba(250, 166, 26, .1);
+ background: rgba(254, 231, 92, 0.1);
}
.radio-btn-fav.active {
color: var(--warning);
}
-/* -- Equalizer Animation ------------------------------------ */
+/* ── Equalizer Animation ── */
.radio-eq {
display: flex;
align-items: flex-end;
@@ -2230,23 +1337,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-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);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ color: var(--text-normal);
+ font-family: var(--font);
+ font-size: 13px;
+ padding: 6px 10px;
cursor: pointer;
outline: none;
max-width: 180px;
@@ -2269,27 +1376,28 @@ html, body {
}
.radio-np-name {
- font-size: var(--text-base);
- font-weight: var(--weight-semibold);
- color: var(--text-primary);
+ font-size: 14px;
+ font-weight: 600;
+ color: var(--text-normal);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.radio-np-loc {
- font-size: var(--text-xs);
- color: var(--text-secondary);
+ font-size: 11px;
+ color: var(--text-muted);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
-/* -- Radio Volume ------------------------------------------- */
+
+/* ── Volume Slider ── */
.radio-volume {
display: flex;
align-items: center;
- gap: var(--space-2);
+ gap: 6px;
flex-shrink: 0;
}
@@ -2306,7 +1414,7 @@ html, body {
width: 100px;
height: 4px;
border-radius: 2px;
- background: var(--bg-tertiary);
+ background: var(--bg-tertiary, #3a352d);
outline: none;
cursor: pointer;
}
@@ -2317,25 +1425,25 @@ html, body {
width: 14px;
height: 14px;
border-radius: 50%;
- background: var(--accent);
+ background: var(--accent, #e67e22);
cursor: pointer;
border: none;
- box-shadow: 0 0 4px rgba(0, 0, 0, .3);
+ box-shadow: 0 0 4px rgba(0,0,0,0.3);
}
.radio-volume-slider::-moz-range-thumb {
width: 14px;
height: 14px;
border-radius: 50%;
- background: var(--accent);
+ background: var(--accent, #e67e22);
cursor: pointer;
border: none;
- box-shadow: 0 0 4px rgba(0, 0, 0, .3);
+ box-shadow: 0 0 4px rgba(0,0,0,0.3);
}
.radio-volume-val {
- font-size: var(--text-xs);
- color: var(--text-secondary);
+ font-size: 11px;
+ color: var(--text-muted);
min-width: 32px;
text-align: right;
}
@@ -2355,43 +1463,42 @@ html, body {
.radio-theme-dot.active {
border-color: #fff;
- box-shadow: 0 0 6px rgba(255, 255, 255, .3);
}
-/* -- Radio Overlays ----------------------------------------- */
+/* ── Station count ── */
.radio-counter {
position: absolute;
- bottom: var(--space-4);
- left: var(--space-4);
+ bottom: 16px;
+ left: 16px;
z-index: 10;
- font-size: var(--text-sm);
- color: var(--text-tertiary);
+ font-size: 12px;
+ color: var(--text-faint);
background: rgba(33, 30, 23, 0.8);
- padding: var(--space-1) var(--space-3);
- border-radius: var(--radius-full);
+ padding: 4px 10px;
+ border-radius: 20px;
pointer-events: none;
}
.radio-attribution {
position: absolute;
- right: var(--space-4);
- bottom: var(--space-4);
+ right: 16px;
+ bottom: 16px;
z-index: 10;
- font-size: var(--text-sm);
- color: var(--text-tertiary);
+ font-size: 12px;
+ color: var(--text-faint);
background: rgba(33, 30, 23, 0.8);
- padding: var(--space-1) var(--space-3);
- border-radius: var(--radius-full);
+ padding: 4px 10px;
+ border-radius: 20px;
text-decoration: none;
- transition: color var(--duration-fast), background var(--duration-fast);
+ transition: color var(--transition), background var(--transition);
}
.radio-attribution:hover {
- color: var(--text-primary);
+ color: var(--text-normal);
background: rgba(33, 30, 23, 0.92);
}
-/* -- Radio Spinner ------------------------------------------ */
+/* ── Spinner ── */
.radio-spinner {
width: 24px;
height: 24px;
@@ -2401,223 +1508,32 @@ html, body {
animation: spin 0.7s linear infinite;
}
-/* -- 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;
+@keyframes spin {
+ to { transform: rotate(360deg); }
}
-.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 -------------------- */
+/* ── Radio Responsive ── */
@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: var(--space-3);
- right: var(--space-3);
- padding: var(--space-2) var(--space-3);
- font-size: var(--text-base);
+ top: 12px;
+ right: 12px;
+ padding: 8px 10px;
+ font-size: 14px;
}
.radio-search {
- top: var(--space-3);
+ top: 12px;
width: calc(100% - 80px);
left: calc(50% - 24px);
}
.radio-topbar {
- padding: 0 var(--space-3);
- gap: var(--space-2);
+ padding: 0 12px;
+ gap: 8px;
}
.radio-topbar-title {
@@ -2626,45 +1542,11 @@ html, body {
.radio-sel {
max-width: 140px;
- 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);
+ font-size: 12px;
}
}
-/* -- 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;
}
@@ -2677,3 +1559,1356 @@ 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;
+ }
+}