diff --git a/media_server/static/css/styles.css b/media_server/static/css/styles.css index 4f472a6..aa9fb3d 100644 --- a/media_server/static/css/styles.css +++ b/media_server/static/css/styles.css @@ -1,1627 +1,1742 @@ - :root { - --bg-primary: #121212; - --bg-secondary: #1e1e1e; - --bg-tertiary: #282828; - --text-primary: #ffffff; - --text-secondary: #b3b3b3; - --text-muted: #6a6a6a; - --accent: #1db954; - --accent-hover: #1ed760; - --border: #404040; - --error: #e74c3c; - } - - :root[data-theme="light"] { - --bg-primary: #ffffff; - --bg-secondary: #f5f5f5; - --bg-tertiary: #e8e8e8; - --text-primary: #1a1a1a; - --text-secondary: #4a4a4a; - --text-muted: #888888; - --accent: #1db954; - --accent-hover: #1ed760; - --border: #d0d0d0; - --error: #e74c3c; - } - - * { - margin: 0; - padding: 0; - box-sizing: border-box; - } - - html { - overflow-y: scroll; - } - - body { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Helvetica', 'Arial', sans-serif; - background: var(--bg-primary); - color: var(--text-primary); - line-height: 1.6; - } - - /* Prevent flash of untranslated content */ - body.loading-translations { - opacity: 0; - transition: opacity 0.1s ease-in; - } - - body.translations-loaded { - opacity: 1; - } - - /* Custom Scrollbars */ - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-track { - background: var(--bg-primary); - } - - ::-webkit-scrollbar-thumb { - background: var(--border); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb:hover { - background: var(--text-muted); - } - - * { - scrollbar-width: thin; - scrollbar-color: var(--border) var(--bg-primary); - } - - /* Focus-visible states for keyboard accessibility */ - :focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - } - - button:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; - box-shadow: 0 0 0 4px rgba(29, 185, 84, 0.2); - } - - input:focus-visible, - select:focus-visible, - textarea:focus-visible { - outline: none; - border-color: var(--accent); - box-shadow: 0 0 0 3px rgba(29, 185, 84, 0.15); - } - - .tab-btn:focus-visible { - outline: 2px solid var(--accent); - outline-offset: -2px; - } - - /* Prevent scrolling when dialog is open */ - body.dialog-open { - overflow: hidden; - } - - .container { - max-width: 800px; - margin: 0 auto; - padding: 2rem 2rem 0.5rem; - } - - header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.75rem; - padding-bottom: 0.5rem; - } - - h1 { - font-size: 1.5rem; - font-weight: 600; - } - - .version-label { - font-size: 0.7rem; - color: var(--text-muted); - background: var(--bg-tertiary); - padding: 0.15rem 0.5rem; - border-radius: 1rem; - font-weight: 500; - align-self: center; - } - - .status-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--error); - transition: background 0.3s; - } - - .status-dot.connected { - background: var(--accent); - } - - .theme-toggle { - background: transparent; - border: none; - padding: 0.3rem; - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: opacity 0.3s; - width: 32px; - height: 32px; - opacity: 0.7; - } - - .theme-toggle:hover { - opacity: 1; - } - - .theme-toggle svg { - width: 16px; - height: 16px; - fill: currentColor; - } - - /* Accent Color Picker */ - .accent-picker { - position: relative; - } - - .accent-picker-btn { - background: transparent; - border: none; - cursor: pointer; - padding: 4px; - display: flex; - align-items: center; - opacity: 0.8; - transition: opacity 0.2s; - } - - .accent-picker-btn:hover { - opacity: 1; - transform: none; - } - - .accent-dot { - width: 16px; - height: 16px; - border-radius: 50%; - background: var(--accent); - border: 2px solid var(--border); - display: block; - } - - .accent-picker-dropdown { - display: none; - position: absolute; - right: 0; - top: calc(100% + 4px); - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 8px; - padding: 8px; - gap: 6px; - z-index: 100; - box-shadow: 0 4px 12px rgba(0,0,0,0.3); - display: none; - grid-template-columns: repeat(3, 24px); - } - - .accent-picker-dropdown.open { - display: grid; - } - - .accent-swatch { - width: 24px; - height: 24px; - border-radius: 50%; - border: 2px solid transparent; - cursor: pointer; - transition: transform 0.15s, border-color 0.15s; - } - - .accent-swatch:hover { - transform: scale(1.2); - } - - .accent-swatch.active { - border-color: var(--text-primary); - } - - #locale-select { - background: var(--bg-tertiary); - border: 1px solid var(--border); - color: var(--text-primary); - border-radius: 6px; - padding: 4px 8px; - cursor: pointer; - font-size: 12px; - font-weight: 500; - transition: all 0.3s ease; - } - - #locale-select:hover { - border-color: var(--accent); - } - - #locale-select:focus { - outline: none; - border-color: var(--accent); - } - - #locale-select option { - background: var(--bg-secondary); - color: var(--text-primary); - } - - /* Tab Bar */ - .tab-bar { - display: flex; - gap: 0.25rem; - margin-bottom: 1.5rem; - padding: 0.25rem; - background: var(--bg-secondary); - border-radius: 10px; - border: 1px solid var(--border); - position: relative; - } - - .tab-indicator { - position: absolute; - top: 0.25rem; - bottom: 0.25rem; - left: 0; - background: var(--bg-tertiary); - border-radius: 8px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s cubic-bezier(0.4, 0, 0.2, 1); - z-index: 0; - pointer-events: none; - } - - .tab-btn { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - gap: 0.4rem; - padding: 0.6rem 0.5rem; - background: transparent; - border: none; - border-radius: 8px; - color: var(--text-muted); - font-size: 0.8rem; - font-weight: 500; - cursor: pointer; - transition: color 0.2s; - width: auto; - height: auto; - position: relative; - z-index: 1; - } - - .tab-btn:hover { - color: var(--text-primary); - transform: none !important; - } - - .tab-btn.active { - color: var(--accent); - background: transparent; - } - - .tab-btn svg { - width: 16px; - height: 16px; - flex-shrink: 0; - } - - [data-tab-content] { - display: none; - } - - [data-tab-content].active { - display: block; - } - - @media (max-width: 600px) { - .tab-btn span { - display: none; - } - - .tab-btn { - padding: 0.6rem; - } - } - - .player-container { - background: var(--bg-secondary); - border-radius: 12px; - padding: 2rem; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); - } - - .player-layout { - display: flex; - flex-direction: column; - } - - .player-details { - flex: 1; - min-width: 0; - } - - .album-art-container { - display: flex; - justify-content: center; - margin-bottom: 2rem; - position: relative; - } - - .album-art-glow { - position: absolute; - width: 300px; - height: 300px; - object-fit: cover; - border-radius: 8px; - filter: blur(40px) saturate(1.5); - opacity: 0.5; - top: 50%; - left: 50%; - transform: translate(-50%, -50%) scale(1.1); - z-index: 0; - pointer-events: none; - transition: opacity 0.5s ease, border-radius 0.6s ease; - } - - #album-art { - width: 300px; - height: 300px; - object-fit: cover; - border-radius: 8px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); - background: var(--bg-tertiary); - position: relative; - z-index: 1; - margin: 0; - transition: border-radius 0.6s ease, width 0.6s ease, height 0.6s ease, box-shadow 0.6s ease, margin 0.6s ease, filter 0.6s ease; - } - - :root[data-theme="light"] .album-art-glow { - opacity: 0.35; - filter: blur(50px) saturate(1.8); - } - - /* Vinyl Record Mode */ - .album-art-container.vinyl #album-art { - border-radius: 50%; - width: 210px; - height: 210px; - margin: 45px; - filter: saturate(0.8) brightness(0.92) contrast(1.05); - box-shadow: - 0 0 0 3px #2a2a2a, - 0 0 0 5px #1a1a1a, - 0 0 0 6px rgba(255,255,255,0.05), - 0 0 0 12px #1a1a1a, - 0 0 0 13px rgba(255,255,255,0.03), - 0 0 0 20px #1a1a1a, - 0 0 0 21px rgba(255,255,255,0.05), - 0 0 0 28px #1a1a1a, - 0 0 0 29px rgba(255,255,255,0.03), - 0 0 0 36px #1a1a1a, - 0 0 0 37px rgba(255,255,255,0.05), - 0 0 0 42px #1a1a1a, - 0 0 0 43px #2a2a2a, - 0 0 0 45px #111, - 0 4px 15px 45px rgba(0,0,0,0.4); - } - - /* Vinyl label vignette overlay */ - .album-art-container.vinyl::before { - content: ''; - position: absolute; - width: 210px; - height: 210px; - border-radius: 50%; - background: radial-gradient( - circle, - transparent 50%, - rgba(0,0,0,0.25) 100% - ); - z-index: 2; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - pointer-events: none; - opacity: 0; - transition: opacity 0.6s ease; - } - - .album-art-container.vinyl.spinning::before, - .album-art-container.vinyl.paused::before { - opacity: 1; - } - - .album-art-container.vinyl .album-art-glow { - border-radius: 50%; - } - - /* Center spindle hole */ - .album-art-container::after { - content: ''; - position: absolute; - width: 14px; - height: 14px; - border-radius: 50%; - background: #0a0a0a; - border: 2px solid #444; - box-shadow: inset 0 1px 3px rgba(0,0,0,0.8); - z-index: 3; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - pointer-events: none; - opacity: 0; - transition: opacity 0.4s ease 0.3s; - } - - .album-art-container.vinyl::after { - opacity: 1; - } - - .album-art-container.vinyl.spinning #album-art { - animation: vinylSpin 12s linear infinite; - } - - .album-art-container.vinyl.paused #album-art { - animation: vinylSpin 12s linear infinite; - animation-play-state: paused; - } - - @keyframes vinylSpin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } - } - - .track-info { - text-align: center; - margin-bottom: 2rem; - } - - #track-title { - font-size: 1.75rem; - font-weight: 600; - margin-bottom: 0.5rem; - color: var(--text-primary); - } - - #artist { - font-size: 1.125rem; - color: var(--text-secondary); - margin-bottom: 0.25rem; - } - - #album { - font-size: 0.875rem; - color: var(--text-muted); - } - - .playback-state { - display: flex; - justify-content: center; - align-items: center; - gap: 0.5rem; - font-size: 0.875rem; - color: var(--text-secondary); - margin-top: 0.5rem; - } - - .state-icon { - width: 16px; - height: 16px; - } - - .progress-container { - margin-bottom: 2rem; - } - - .time-display { - display: flex; - justify-content: space-between; - font-size: 0.75rem; - color: var(--text-secondary); - margin-bottom: 0.5rem; - } - - .progress-bar { - width: 100%; - height: 6px; - background: var(--bg-tertiary); - border-radius: 3px; - cursor: pointer; - position: relative; - transition: transform 0.15s ease; - } - - .progress-bar:hover { - transform: scaleY(1.4); - } - - .progress-fill { - height: 100%; - background: var(--accent); - border-radius: 3px; - width: 0; - transition: width 0.1s linear; - position: relative; - } - - .progress-fill::after { - content: ''; - position: absolute; - right: -6px; - top: 50%; - transform: translateY(-50%) scale(0); - width: 12px; - height: 12px; - background: var(--accent); - border-radius: 50%; - box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); - transition: transform 0.15s ease; - } - - .progress-bar:hover .progress-fill::after { - transform: translateY(-50%) scale(1); - } - - .controls { - display: flex; - gap: 1rem; - justify-content: center; - align-items: center; - margin-bottom: 2rem; - } - - button { - background: var(--bg-tertiary); - border: none; - color: var(--text-primary); - cursor: pointer; - border-radius: 50%; - width: 48px; - height: 48px; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; - } - - button:hover:not(:disabled) { - background: var(--accent); - color: #fff; - transform: scale(1.05); - } - - button:disabled { - opacity: 0.3; - cursor: not-allowed; - } - - button.primary { - width: 56px; - height: 56px; - background: var(--accent); - } - - button.primary:hover:not(:disabled) { - background: var(--accent-hover); - color: #fff; - transform: scale(1.1); - } - - .volume-container { - display: flex; - align-items: center; - gap: 1rem; - padding: 1rem; - background: var(--bg-tertiary); - border-radius: 8px; - margin-bottom: 1rem; - } - - #volume-slider { - flex: 1; - height: 6px; - -webkit-appearance: none; - appearance: none; - background: var(--bg-primary); - border-radius: 3px; - outline: none; - } - - #volume-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 16px; - height: 16px; - background: var(--accent); - border-radius: 50%; - cursor: pointer; - transition: transform 0.15s ease, box-shadow 0.15s ease; - } - - #volume-slider:hover::-webkit-slider-thumb { - transform: scale(1.3); - box-shadow: 0 0 6px rgba(29, 185, 84, 0.4); - } - - #volume-slider::-moz-range-thumb { - width: 16px; - height: 16px; - background: var(--accent); - border-radius: 50%; - cursor: pointer; - border: none; - transition: transform 0.15s ease, box-shadow 0.15s ease; - } - - #volume-slider:hover::-moz-range-thumb { - transform: scale(1.3); - box-shadow: 0 0 6px rgba(29, 185, 84, 0.4); - } - - .volume-display { - font-size: 0.875rem; - color: var(--text-secondary); - min-width: 40px; - text-align: right; - } - - .mute-btn { - width: 40px; - height: 40px; - } - - .source-info { - text-align: center; - font-size: 0.75rem; - color: var(--text-muted); - padding-top: 1rem; - border-top: 1px solid var(--border); - display: flex; - align-items: center; - justify-content: center; - gap: 0.75rem; - } - - .vinyl-toggle-btn { - width: 28px; - height: 28px; - padding: 0; - background: transparent; - border: 1px solid var(--border); - border-radius: 50%; - color: var(--text-muted); - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; - margin-left: auto; - } - - .vinyl-toggle-btn:hover { - color: var(--accent); - border-color: var(--accent); - background: transparent; - transform: none; - } - - .vinyl-toggle-btn.active { - color: var(--accent); - border-color: var(--accent); - background: rgba(29, 185, 84, 0.1); - } - - .vinyl-toggle-btn svg { - width: 16px; - height: 16px; - } - - /* Scripts Section */ - .scripts-container { - background: var(--bg-secondary); - border-radius: 12px; - padding: 2rem; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); - } - - .scripts-container h2 { - font-size: 1.25rem; - margin-bottom: 1rem; - color: var(--text-primary); - } - - .scripts-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); - gap: 1rem; - } - - .script-btn { - width: 100%; - height: auto; - min-height: 80px; - padding: 1rem; - border-radius: 8px; - background: var(--bg-tertiary); - border: 1px solid var(--border); - color: var(--text-primary); - cursor: pointer; - transition: all 0.2s; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 0.5rem; - } - - .script-btn:hover:not(:disabled) { - background: var(--accent); - border-color: var(--accent); - color: #fff; - transform: translateY(-2px); - } - - .script-btn:disabled { - opacity: 0.3; - cursor: not-allowed; - } - - .script-btn .script-label { - font-weight: 600; - font-size: 0.875rem; - } - - .script-btn .script-description { - font-size: 0.75rem; - color: var(--text-secondary); - text-align: center; - transition: color 0.2s; - } - - .script-btn:hover:not(:disabled) .script-description { - color: rgba(255, 255, 255, 0.85); - } - - .script-btn.executing { - opacity: 0.6; - pointer-events: none; - } - - /* Script Management Styles */ - .script-management { - background: var(--bg-secondary); - border-radius: 12px; - padding: 2rem; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); - overflow-x: auto; - } - - .script-management h2 { - font-size: 1.25rem; - margin-bottom: 1rem; - color: var(--text-primary); - } - - .add-card { - display: flex; - align-items: center; - justify-content: center; - border: 2px dashed var(--border); - border-radius: 8px; - padding: 1.5rem; - margin-top: 1rem; - cursor: pointer; - transition: border-color 0.2s, background 0.2s; - } - - .add-card:hover { - border-color: var(--text-muted); - background: var(--bg-tertiary); - } - - .add-card-icon { - font-size: 1.5rem; - color: var(--text-muted); - font-weight: 300; - line-height: 1; - } - - .add-card-grid { - border: 2px dashed var(--border); - background: transparent; - } - - .add-card-grid:hover:not(:disabled) { - border-color: var(--text-muted); - background: var(--bg-tertiary); - } - - .scripts-table { - width: 100%; - min-width: 500px; - border-collapse: collapse; - font-size: 0.875rem; - } - - .scripts-table th { - text-align: left; - padding: 0.75rem; - border-bottom: 2px solid var(--border); - color: var(--text-secondary); - font-weight: 600; - font-size: 0.75rem; - text-transform: uppercase; - } - - .scripts-table td { - padding: 0.75rem; - word-break: break-word; - border-bottom: 1px solid var(--border); - } - - .scripts-table tr:hover { - background: var(--bg-tertiary); - } - - .scripts-table code { - background: var(--bg-tertiary); - padding: 0.25rem 0.5rem; - border-radius: 4px; - font-size: 0.75rem; - color: var(--accent); - } - - .action-btn { - padding: 0.5rem; - border-radius: 6px; - border: 1px solid var(--border); - background: var(--bg-tertiary); - color: var(--text-primary); - cursor: pointer; - transition: all 0.2s; - width: 32px; - height: 32px; - display: inline-flex; - align-items: center; - justify-content: center; - } - - .action-btn svg { - width: 16px; - height: 16px; - fill: currentColor; - } - - .action-btn:hover { - background: var(--accent); - border-color: var(--accent); - color: #fff; - transform: translateY(-1px); - } - - .action-btn.delete:hover { - background: var(--error); - border-color: var(--error); - color: #fff; - } - - .action-buttons { - display: flex; - gap: 0.5rem; - align-items: center; - } - - .action-btn.execute:hover { - background: #3b82f6; - border-color: #3b82f6; - color: #fff; - } - - /* Execution Result Dialog */ - .execution-result { - font-family: 'Consolas', 'Monaco', 'Courier New', monospace; - background: var(--bg-primary); - border-radius: 6px; - padding: 1rem; - margin: 0.5rem 0; - max-height: 400px; - overflow-y: auto; - } - - .execution-result pre { - margin: 0; - white-space: pre-wrap; - word-wrap: break-word; - font-size: 0.813rem; - line-height: 1.5; - } - - .execution-status { - display: flex; - gap: 1rem; - margin-bottom: 1rem; - flex-wrap: wrap; - } - - .status-item { - display: flex; - flex-direction: column; - gap: 0.25rem; - } - - .status-item label { - font-size: 0.75rem; - color: var(--text-muted); - text-transform: uppercase; - font-weight: 600; - } - - .status-item value { - font-size: 0.875rem; - color: var(--text-primary); - font-weight: 500; - } - - .status-item.success value { - color: var(--accent); - } - - .status-item.error value { - color: var(--error); - } - - .result-section { - margin-bottom: 1rem; - } - - .result-section h4 { - font-size: 0.875rem; - color: var(--text-secondary); - margin-bottom: 0.5rem; - font-weight: 600; - } - - .loading-spinner { - display: inline-block; - width: 20px; - height: 20px; - border: 3px solid var(--bg-tertiary); - border-top-color: var(--accent); - border-radius: 50%; - animation: spin 0.8s linear infinite; - } - - @keyframes spin { - to { transform: rotate(360deg); } - } - - /* Dialog Styles */ - dialog { - background: var(--bg-secondary); - color: var(--text-primary); - border: 1px solid var(--border); - border-radius: 12px; - padding: 0; - max-width: 500px; - width: 90%; - margin: auto; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8); - } - - /* Ensure dialogs are hidden until explicitly opened */ - dialog:not([open]) { - display: none; - } - - dialog::backdrop { - background: rgba(0, 0, 0, 0.8); - } - - .dialog-header { - padding: 1.5rem; - background: var(--bg-secondary); - border-bottom: 1px solid var(--border); - } - - .dialog-header h3 { - margin: 0; - font-size: 1.25rem; - } - - .dialog-body { - padding: 1.5rem; - } - - .dialog-body label { - display: block; - margin-bottom: 1rem; - color: var(--text-secondary); - font-size: 0.875rem; - } - - .dialog-body input, - .dialog-body textarea, - .dialog-body select { - display: block; - width: 100%; - padding: 0.5rem; - margin-top: 0.25rem; - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: 6px; - color: var(--text-primary); - font-family: inherit; - font-size: 0.875rem; - } - - .dialog-body textarea { - min-height: 80px; - resize: vertical; - } - - .dialog-body input:focus, - .dialog-body textarea:focus, - .dialog-body select:focus { - outline: none; - border-color: var(--accent); - } - - .dialog-footer { - padding: 1.5rem; - border-top: 1px solid var(--border); - display: flex; - justify-content: flex-end; - gap: 0.5rem; - } - - .dialog-footer button { - padding: 0.625rem 1.5rem; - border-radius: 6px; - border: none; - font-size: 0.875rem; - font-weight: 600; - cursor: pointer; - transition: background 0.2s; - min-width: 100px; - white-space: nowrap; - } - - .dialog-footer .btn-primary { - background: var(--accent); - color: var(--text-primary); - } - - .dialog-footer .btn-primary:hover { - background: var(--accent-hover); - } - - .dialog-footer .btn-secondary { - background: var(--bg-tertiary); - color: var(--text-primary); - border: 1px solid var(--border); - } - - .dialog-footer .btn-secondary:hover { - background: var(--border); - } - - .empty-state { - text-align: center; - padding: 2rem; - color: var(--text-muted); - } - - .scripts-empty { - text-align: center; - color: var(--text-muted); - padding: 2rem; - font-size: 0.875rem; - } - - .empty-state-illustration { - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; - padding: 3rem 2rem; - } - - .empty-state-illustration svg { - width: 64px; - height: 64px; - fill: none; - stroke: var(--text-muted); - stroke-width: 1.5; - stroke-linecap: round; - stroke-linejoin: round; - opacity: 0.5; - } - - .empty-state-illustration p { - color: var(--text-muted); - font-size: 0.875rem; - margin: 0; - } - - .toast { - position: fixed; - bottom: 2rem; - right: 2rem; - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: 8px; - padding: 1rem 1.5rem; - padding-left: 1.25rem; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); - opacity: 0; - transform: translateY(20px); - transition: all 0.3s; - pointer-events: none; - z-index: 1000; - border-left: 4px solid var(--border); - } - - .toast.show { - opacity: 1; - transform: translateY(0); - } - - .toast.success { - border-color: var(--accent); - border-left-color: var(--accent); - } - - .toast.error { - border-color: var(--error); - border-left-color: var(--error); - } - - /* Auth Modal */ - #auth-overlay { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: rgba(0, 0, 0, 0.9); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - } - - #auth-overlay.hidden { - display: none; - } - - .auth-modal { - background: var(--bg-secondary); - padding: 2rem; - border-radius: 12px; - max-width: 400px; - width: 90%; - } - - .auth-modal h2 { - margin-bottom: 1rem; - font-size: 1.5rem; - } - - .auth-modal p { - margin-bottom: 1rem; - color: var(--text-secondary); - font-size: 0.875rem; - } - - #token-input { - width: 100%; - padding: 0.75rem; - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: 6px; - color: var(--text-primary); - font-size: 0.875rem; - margin-bottom: 1rem; - } - - #token-input:focus { - outline: none; - border-color: var(--accent); - } - - .btn-connect { - width: 100%; - height: auto; - padding: 0.75rem; - border-radius: 6px; - background: var(--accent); - font-weight: 600; - } - - .btn-connect:hover { - background: var(--accent-hover); - transform: none; - } - - .help-text { - background: var(--bg-tertiary); - padding: 0.75rem; - border-radius: 6px; - margin-top: 1rem; - } - - .help-text code { - background: var(--bg-primary); - padding: 0.25rem 0.5rem; - border-radius: 3px; - font-family: monospace; - font-size: 0.875rem; - } - - .error-message { - color: var(--error); - font-size: 0.875rem; - margin-top: 0.5rem; - display: none; - } - - .error-message.visible { - display: block; - } - - .clear-token-btn { - width: auto; - height: auto; - padding: 4px 10px; - border-radius: 6px; - font-size: 12px; - font-weight: 500; - background: var(--bg-tertiary); - color: var(--text-secondary); - border: 1px solid var(--border); - cursor: pointer; - opacity: 0.7; - } - - .clear-token-btn:hover { - opacity: 1; - background: var(--error); - color: var(--text-primary); - border-color: var(--error); - } - - /* Mini Player (Sticky) */ - .mini-player { - position: fixed; - bottom: 0; - left: 0; - right: 0; - background: rgba(30, 30, 30, 0.8); - -webkit-backdrop-filter: blur(20px) saturate(1.5); - backdrop-filter: blur(20px) saturate(1.5); - border-top: 1px solid rgba(255, 255, 255, 0.08); - padding: 0.75rem 1rem; - padding-top: calc(0.75rem + 2px); - display: flex; - align-items: center; - gap: 1.5rem; - z-index: 1000; - transform: translateY(0); - transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; - box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.3); - } - - :root[data-theme="light"] .mini-player { - background: rgba(245, 245, 245, 0.75); - border-top: 1px solid rgba(0, 0, 0, 0.08); - box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.1); - } - - .mini-player::before { - content: ''; - position: absolute; - top: 0; - left: 0; - height: 2px; - width: var(--mini-progress, 0%); - background: var(--accent); - transition: width 0.1s linear; - } - - .mini-player.hidden { - transform: translateY(100%); - opacity: 0; - pointer-events: none; - } - - .mini-player-info { - display: flex; - align-items: center; - gap: 0.75rem; - min-width: 200px; - flex-shrink: 0; - } - - .mini-album-art { - width: 40px; - height: 40px; - border-radius: 4px; - object-fit: cover; - flex-shrink: 0; - } - - .mini-track-details { - display: flex; - flex-direction: column; - min-width: 0; - } - - .mini-track-title { - font-size: 0.875rem; - font-weight: 600; - color: var(--text-primary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .mini-artist { - font-size: 0.75rem; - color: var(--text-secondary); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .mini-progress-container { - flex: 1; - display: flex; - align-items: center; - gap: 1rem; - min-width: 0; - } - - .mini-time-display { - display: flex; - gap: 0.5rem; - font-size: 0.75rem; - color: var(--text-secondary); - flex-shrink: 0; - } - - .mini-progress-bar { - flex: 1; - height: 4px; - background: var(--bg-tertiary); - border-radius: 2px; - cursor: pointer; - position: relative; - min-width: 100px; - } - - .mini-progress-bar:hover { - height: 6px; - } - - .mini-progress-fill { - height: 100%; - background: var(--accent); - border-radius: 2px; - width: 0%; - transition: width 0.1s linear; - position: relative; - } - - .mini-progress-fill::after { - content: ''; - position: absolute; - right: -5px; - top: 50%; - transform: translateY(-50%) scale(0); - width: 10px; - height: 10px; - background: var(--accent); - border-radius: 50%; - box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); - transition: transform 0.15s ease; - } - - .mini-progress-bar:hover .mini-progress-fill::after { - transform: translateY(-50%) scale(1); - } - - .mini-controls { - display: flex; - align-items: center; - gap: 0.5rem; - flex-shrink: 0; - } - - .mini-control-btn { - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: 50%; - width: 36px; - height: 36px; - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.2s; - padding: 0; - } - - .mini-control-btn:hover { - background: var(--accent); - border-color: var(--accent); - color: #fff; - transform: scale(1.05); - } - - .mini-control-btn:disabled { - opacity: 0.5; - cursor: not-allowed; - } - - .mini-control-btn svg { - width: 20px; - height: 20px; - fill: currentColor; - } - - .mini-volume-container { - display: flex; - align-items: center; - gap: 0.75rem; - flex-shrink: 0; - min-width: 180px; - } - - .mini-volume-slider { - flex: 1; - height: 4px; - -webkit-appearance: none; - appearance: none; - background: var(--bg-tertiary); - border-radius: 2px; - outline: none; - cursor: pointer; - min-width: 80px; - } - - .mini-volume-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 12px; - height: 12px; - background: var(--accent); - border-radius: 50%; - cursor: pointer; - } - - .mini-volume-slider::-moz-range-thumb { - width: 12px; - height: 12px; - background: var(--accent); - border-radius: 50%; - cursor: pointer; - border: none; - } - - .mini-volume-slider:hover::-webkit-slider-thumb { - transform: scale(1.2); - } - - .mini-volume-slider:hover::-moz-range-thumb { - transform: scale(1.2); - } - - .mini-volume-display { - font-size: 0.75rem; - color: var(--text-secondary); - min-width: 36px; - text-align: right; - } - - /* SVG Icons */ - svg { - width: 24px; - height: 24px; - fill: currentColor; - } - - button.primary svg { - width: 28px; - height: 28px; - } - - @media (max-width: 600px) { - .container { - padding: 1rem; - } - - #album-art { - width: 250px; - height: 250px; - } - - .album-art-container.vinyl #album-art { - width: 170px; - height: 170px; - margin: 40px; - box-shadow: - 0 0 0 3px #2a2a2a, - 0 0 0 5px #1a1a1a, - 0 0 0 6px rgba(255,255,255,0.05), - 0 0 0 12px #1a1a1a, - 0 0 0 13px rgba(255,255,255,0.03), - 0 0 0 20px #1a1a1a, - 0 0 0 21px rgba(255,255,255,0.05), - 0 0 0 28px #1a1a1a, - 0 0 0 29px rgba(255,255,255,0.03), - 0 0 0 36px #1a1a1a, - 0 0 0 37px rgba(255,255,255,0.04), - 0 0 0 38px #2a2a2a, - 0 0 0 40px #111, - 0 4px 12px 40px rgba(0,0,0,0.4); - } - - #track-title { - font-size: 1.5rem; - } - } - - /* Footer */ - footer { - text-align: center; - padding: 0.75rem 1rem; - margin-top: 0.5rem; - color: var(--text-muted); - font-size: 0.75rem; - transition: padding-bottom 0.3s ease-in-out; - } - - body.mini-player-visible footer { - padding-bottom: 70px; - } - - footer a { - color: var(--accent); - text-decoration: none; - transition: color 0.2s; - } - - footer a:hover { - color: var(--accent-hover); - text-decoration: underline; - } - - footer .separator { - margin: 0 0.5rem; - color: var(--text-muted); - } +:root { + --bg-primary: #121212; + --bg-secondary: #1e1e1e; + --bg-tertiary: #282828; + --text-primary: #ffffff; + --text-secondary: #b3b3b3; + --text-muted: #8a8a8a; + --accent: #1db954; + --accent-hover: #1ed760; + --border: #404040; + --error: #e74c3c; + --vinyl-ring: #1a1a1a; + --vinyl-groove: #2a2a2a; + --vinyl-highlight: rgba(255,255,255,0.05); + --vinyl-highlight-dim: rgba(255,255,255,0.03); + --vinyl-edge: #111; + --vinyl-spindle: #0a0a0a; + --shadow-elevation: rgba(0, 0, 0, 0.5); +} + +:root[data-theme="light"] { + --bg-primary: #ffffff; + --bg-secondary: #f5f5f5; + --bg-tertiary: #e8e8e8; + --text-primary: #1a1a1a; + --text-secondary: #4a4a4a; + --text-muted: #888888; + --accent: #1db954; + --accent-hover: #1ed760; + --border: #d0d0d0; + --error: #e74c3c; + --vinyl-ring: #c0c0c0; + --vinyl-groove: #b0b0b0; + --vinyl-highlight: rgba(255,255,255,0.3); + --vinyl-highlight-dim: rgba(255,255,255,0.15); + --vinyl-edge: #999; + --vinyl-spindle: #888; + --shadow-elevation: rgba(0, 0, 0, 0.15); +} + +:root[data-theme="light"] .player-container, +:root[data-theme="light"] .browser-container, +:root[data-theme="light"] .scripts-container, +:root[data-theme="light"] .script-management { + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); +} + +:root[data-theme="light"] .toast { + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); +} + +:root[data-theme="light"] dialog { + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15); +} + +:root[data-theme="light"] dialog::backdrop { + background: rgba(0, 0, 0, 0.4); +} + +:root[data-theme="light"] .mini-player { + box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.08); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; + scrollbar-width: thin; + scrollbar-color: var(--border) var(--bg-primary); +} + +html { + overflow-y: scroll; +} + +body { + font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', 'Arial', sans-serif; + background: var(--bg-primary); + color: var(--text-primary); + line-height: 1.6; +} + +/* Prevent flash of untranslated content */ +body.loading-translations { + opacity: 0; + transition: opacity 0.1s ease-in; +} + +body.translations-loaded { + opacity: 1; +} + +/* Custom Scrollbars */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--bg-primary); +} + +::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* Focus-visible states for keyboard accessibility */ +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +button:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + box-shadow: 0 0 0 4px rgba(29, 185, 84, 0.2); +} + +input:focus-visible, +select:focus-visible, +textarea:focus-visible { + outline: none; + border-color: var(--accent); + box-shadow: 0 0 0 3px rgba(29, 185, 84, 0.15); +} + +.tab-btn:focus-visible { + outline: 2px solid var(--accent); + outline-offset: -2px; +} + +/* Prevent scrolling when dialog is open */ +body.dialog-open { + overflow: hidden; +} + +.container { + max-width: 800px; + margin: 0 auto; + padding: 2rem 2rem 0.5rem; +} + +header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; +} + +h1 { + font-size: 1.5rem; + font-weight: 600; +} + +.version-label { + font-size: 0.7rem; + color: var(--text-muted); + background: var(--bg-tertiary); + padding: 0.15rem 0.5rem; + border-radius: 1rem; + font-weight: 500; + align-self: center; +} + +.status-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--error); + transition: background 0.3s; +} + +.status-dot.connected { + background: var(--accent); +} + +.theme-toggle { + background: transparent; + border: none; + padding: 0.3rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: opacity 0.3s; + width: 32px; + height: 32px; + opacity: 0.7; +} + +.theme-toggle:hover { + opacity: 1; +} + +.theme-toggle svg { + width: 16px; + height: 16px; + fill: currentColor; +} + +/* Accent Color Picker */ +.accent-picker { + position: relative; +} + +.accent-picker-btn { + background: transparent; + border: none; + cursor: pointer; + padding: 4px; + display: flex; + align-items: center; + opacity: 0.8; + transition: opacity 0.2s; +} + +.accent-picker-btn:hover { + opacity: 1; + transform: none; +} + +.accent-dot { + width: 16px; + height: 16px; + border-radius: 50%; + background: var(--accent); + border: 2px solid var(--border); + display: block; +} + +.accent-picker-dropdown { + display: none; + position: absolute; + right: 0; + top: calc(100% + 4px); + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 8px; + padding: 8px; + gap: 6px; + z-index: 100; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + grid-template-columns: repeat(3, 24px); +} + +.accent-picker-dropdown.open { + display: grid; +} + +.accent-swatch { + width: 24px; + height: 24px; + border-radius: 50%; + border: 2px solid transparent; + cursor: pointer; + transition: transform 0.15s, border-color 0.15s; +} + +.accent-swatch:hover { + transform: scale(1.2); +} + +.accent-swatch.active { + border-color: var(--text-primary); +} + +#locale-select { + background: var(--bg-tertiary); + border: 1px solid var(--border); + color: var(--text-primary); + border-radius: 6px; + padding: 4px 8px; + cursor: pointer; + font-size: 12px; + font-weight: 500; + transition: all 0.3s ease; +} + +#locale-select:hover { + border-color: var(--accent); +} + +#locale-select:focus { + outline: none; + border-color: var(--accent); +} + +#locale-select option { + background: var(--bg-secondary); + color: var(--text-primary); +} + +/* Tab Bar */ +.tab-bar { + display: flex; + gap: 0.25rem; + margin-bottom: 1.5rem; + padding: 0.25rem; + background: var(--bg-secondary); + border-radius: 10px; + border: 1px solid var(--border); + position: relative; +} + +.tab-indicator { + position: absolute; + top: 0.25rem; + bottom: 0.25rem; + left: 0; + background: var(--bg-tertiary); + border-radius: 8px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1), width 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 0; + pointer-events: none; +} + +.tab-btn { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 0.4rem; + padding: 0.6rem 0.5rem; + background: transparent; + border: none; + border-radius: 8px; + color: var(--text-muted); + font-size: 0.8rem; + font-weight: 500; + cursor: pointer; + transition: color 0.2s; + width: auto; + height: auto; + position: relative; + z-index: 1; +} + +.tab-btn:hover { + color: var(--text-primary); + transform: none !important; +} + +.tab-btn.active { + color: var(--accent); + background: transparent; +} + +.tab-btn svg { + width: 16px; + height: 16px; + flex-shrink: 0; +} + +[data-tab-content] { + display: none; +} + +[data-tab-content].active { + display: block; +} + +@media (max-width: 600px) { + .tab-btn span { + display: none; + } + + .tab-btn { + padding: 0.6rem; + } +} + +.player-container { + background: var(--bg-secondary); + border-radius: 12px; + padding: 2rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); +} + +.player-layout { + display: flex; + flex-direction: column; +} + +.player-details { + flex: 1; + min-width: 0; +} + +.album-art-container { + display: flex; + justify-content: center; + margin-bottom: 2rem; + position: relative; +} + +.album-art-glow { + position: absolute; + width: 300px; + height: 300px; + object-fit: cover; + border-radius: 8px; + filter: blur(40px) saturate(1.5); + opacity: 0.5; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(1.1); + z-index: 0; + pointer-events: none; + transition: opacity 0.5s ease, border-radius 0.6s ease; +} + +#album-art { + width: 300px; + height: 300px; + object-fit: cover; + border-radius: 8px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); + background: var(--bg-tertiary); + position: relative; + z-index: 1; + margin: 0; + transition: border-radius 0.6s ease, width 0.6s ease, height 0.6s ease, box-shadow 0.6s ease, margin 0.6s ease, filter 0.6s ease; +} + +:root[data-theme="light"] .album-art-glow { + opacity: 0.35; + filter: blur(50px) saturate(1.8); +} + +/* Vinyl Record Mode */ +.album-art-container.vinyl #album-art { + border-radius: 50%; + width: 210px; + height: 210px; + margin: 45px; + filter: saturate(0.8) brightness(0.92) contrast(1.05); + box-shadow: + 0 0 0 3px var(--vinyl-groove), + 0 0 0 5px var(--vinyl-ring), + 0 0 0 6px var(--vinyl-highlight), + 0 0 0 12px var(--vinyl-ring), + 0 0 0 13px var(--vinyl-highlight-dim), + 0 0 0 20px var(--vinyl-ring), + 0 0 0 21px var(--vinyl-highlight), + 0 0 0 28px var(--vinyl-ring), + 0 0 0 29px var(--vinyl-highlight-dim), + 0 0 0 36px var(--vinyl-ring), + 0 0 0 37px var(--vinyl-highlight), + 0 0 0 42px var(--vinyl-ring), + 0 0 0 43px var(--vinyl-groove), + 0 0 0 45px var(--vinyl-edge), + 0 4px 15px 45px var(--shadow-elevation); +} + +/* Vinyl label vignette overlay */ +.album-art-container.vinyl::before { + content: ''; + position: absolute; + width: 210px; + height: 210px; + border-radius: 50%; + background: radial-gradient( + circle, + transparent 50%, + rgba(0,0,0,0.25) 100% + ); + z-index: 2; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + pointer-events: none; + opacity: 0; + transition: opacity 0.6s ease; +} + +.album-art-container.vinyl.spinning::before, +.album-art-container.vinyl.paused::before { + opacity: 1; +} + +.album-art-container.vinyl .album-art-glow { + border-radius: 50%; +} + +/* Center spindle hole */ +.album-art-container::after { + content: ''; + position: absolute; + width: 14px; + height: 14px; + border-radius: 50%; + background: var(--vinyl-spindle); + border: 2px solid var(--border); + box-shadow: inset 0 1px 3px rgba(0,0,0,0.5); + z-index: 3; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + pointer-events: none; + opacity: 0; + transition: opacity 0.4s ease 0.3s; +} + +.album-art-container.vinyl::after { + opacity: 1; +} + +.album-art-container.vinyl.spinning #album-art { + animation: vinylSpin 12s linear infinite; +} + +.album-art-container.vinyl.paused #album-art { + animation: vinylSpin 12s linear infinite; + animation-play-state: paused; +} + +@keyframes vinylSpin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +.track-info { + text-align: center; + margin-bottom: 2rem; +} + +#track-title { + font-size: 1.75rem; + font-weight: 600; + margin-bottom: 0.5rem; + color: var(--text-primary); +} + +#artist { + font-size: 1.125rem; + color: var(--text-secondary); + margin-bottom: 0.25rem; +} + +#album { + font-size: 0.875rem; + color: var(--text-muted); +} + +.playback-state { + display: flex; + justify-content: center; + align-items: center; + gap: 0.5rem; + font-size: 0.875rem; + color: var(--text-secondary); + margin-top: 0.5rem; +} + +.state-icon { + width: 16px; + height: 16px; +} + +.progress-container { + margin-bottom: 2rem; +} + +.time-display { + display: flex; + justify-content: space-between; + font-size: 0.75rem; + color: var(--text-secondary); + margin-bottom: 0.5rem; +} + +.progress-bar { + width: 100%; + height: 6px; + background: var(--bg-tertiary); + border-radius: 3px; + cursor: pointer; + position: relative; + transition: transform 0.15s ease; +} + +.progress-bar:hover { + transform: scaleY(1.4); +} + +.progress-fill { + height: 100%; + background: var(--accent); + border-radius: 3px; + width: 0; + transition: width 0.1s linear; + position: relative; +} + +.progress-fill::after { + content: ''; + position: absolute; + right: -6px; + top: 50%; + transform: translateY(-50%) scale(0); + width: 12px; + height: 12px; + background: var(--accent); + border-radius: 50%; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); + transition: transform 0.15s ease; +} + +.progress-bar:hover .progress-fill::after, +.progress-bar.dragging .progress-fill::after { + transform: translateY(-50%) scale(1); +} + +.progress-bar.dragging { + transform: scaleY(1.4); +} + +.progress-bar.dragging .progress-fill { + transition: none; +} + +.controls { + display: flex; + gap: 1rem; + justify-content: center; + align-items: center; + margin-bottom: 2rem; +} + +button { + border: none; + background: none; + color: inherit; + font: inherit; + cursor: pointer; + padding: 0; +} + +button:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.controls button { + background: var(--bg-tertiary); + color: var(--text-primary); + border-radius: 50%; + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.controls button:hover:not(:disabled) { + background: var(--accent); + color: #fff; + transform: scale(1.05); +} + +.controls button.primary { + width: 56px; + height: 56px; + background: var(--accent); +} + +.controls button.primary:hover:not(:disabled) { + background: var(--accent-hover); + color: #fff; + transform: scale(1.1); +} + +.volume-container { + display: flex; + align-items: center; + gap: 1rem; + padding: 1rem; + background: var(--bg-tertiary); + border-radius: 8px; + margin-bottom: 1rem; +} + +#volume-slider { + flex: 1; + height: 6px; + -webkit-appearance: none; + appearance: none; + background: var(--bg-primary); + border-radius: 3px; + outline: none; +} + +#volume-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + background: var(--accent); + border-radius: 50%; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.15s ease; +} + +#volume-slider:hover::-webkit-slider-thumb { + transform: scale(1.3); + box-shadow: 0 0 6px rgba(29, 185, 84, 0.4); +} + +#volume-slider::-moz-range-thumb { + width: 16px; + height: 16px; + background: var(--accent); + border-radius: 50%; + cursor: pointer; + border: none; + transition: transform 0.15s ease, box-shadow 0.15s ease; +} + +#volume-slider:hover::-moz-range-thumb { + transform: scale(1.3); + box-shadow: 0 0 6px rgba(29, 185, 84, 0.4); +} + +.volume-display { + font-size: 0.875rem; + color: var(--text-secondary); + min-width: 40px; + text-align: right; +} + +.mute-btn { + width: 40px; + height: 40px; + background: var(--bg-tertiary); + border-radius: 50%; + color: var(--text-primary); + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; +} + +.mute-btn:hover:not(:disabled) { + background: var(--accent); + color: #fff; + transform: scale(1.05); +} + +.source-info { + text-align: center; + font-size: 0.75rem; + color: var(--text-muted); + padding-top: 1rem; + border-top: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; +} + +.vinyl-toggle-btn { + width: 28px; + height: 28px; + padding: 0; + background: transparent; + border: 1px solid var(--border); + border-radius: 50%; + color: var(--text-muted); + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + margin-left: auto; +} + +.vinyl-toggle-btn:hover { + color: var(--accent); + border-color: var(--accent); + background: transparent; + transform: none; +} + +.vinyl-toggle-btn.active { + color: var(--accent); + border-color: var(--accent); + background: rgba(29, 185, 84, 0.1); +} + +.vinyl-toggle-btn svg { + width: 16px; + height: 16px; +} + +/* Scripts Section */ +.scripts-container { + background: var(--bg-secondary); + border-radius: 12px; + padding: 2rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); +} + +.scripts-container h2 { + font-size: 1.25rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.scripts-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); + gap: 1rem; +} + +.script-btn { + width: 100%; + height: auto; + min-height: 80px; + padding: 1rem; + border-radius: 8px; + background: var(--bg-tertiary); + border: 1px solid var(--border); + color: var(--text-primary); + cursor: pointer; + transition: all 0.2s; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.script-btn:hover:not(:disabled) { + background: var(--accent); + border-color: var(--accent); + color: #fff; + transform: translateY(-2px); +} + +.script-btn:disabled { + opacity: 0.3; + cursor: not-allowed; +} + +.script-btn .script-label { + font-weight: 600; + font-size: 0.875rem; +} + +.script-btn .script-description { + font-size: 0.75rem; + color: var(--text-secondary); + text-align: center; + transition: color 0.2s; +} + +.script-btn:hover:not(:disabled) .script-description { + color: rgba(255, 255, 255, 0.85); +} + +.script-btn.executing { + opacity: 0.6; + pointer-events: none; +} + +/* Script Management Styles */ +.script-management { + background: var(--bg-secondary); + border-radius: 12px; + padding: 2rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); + overflow-x: auto; +} + +.script-management h2 { + font-size: 1.25rem; + margin-bottom: 1rem; + color: var(--text-primary); +} + +.add-card { + display: flex; + align-items: center; + justify-content: center; + border: 2px dashed var(--border); + border-radius: 8px; + padding: 1.5rem; + margin-top: 1rem; + cursor: pointer; + transition: border-color 0.2s, background 0.2s; +} + +.add-card:hover { + border-color: var(--text-muted); + background: var(--bg-tertiary); +} + +.add-card-icon { + font-size: 1.5rem; + color: var(--text-muted); + font-weight: 300; + line-height: 1; +} + +.add-card-grid { + border: 2px dashed var(--border); + background: transparent; +} + +.add-card-grid:hover:not(:disabled) { + border-color: var(--text-muted); + background: var(--bg-tertiary); +} + +.scripts-table { + width: 100%; + min-width: 500px; + border-collapse: collapse; + font-size: 0.875rem; +} + +.scripts-table th { + text-align: left; + padding: 0.75rem; + border-bottom: 2px solid var(--border); + color: var(--text-secondary); + font-weight: 600; + font-size: 0.75rem; + text-transform: uppercase; +} + +.scripts-table td { + padding: 0.75rem; + word-break: break-word; + border-bottom: 1px solid var(--border); +} + +.scripts-table tr:hover { + background: var(--bg-tertiary); +} + +.scripts-table code { + background: var(--bg-tertiary); + padding: 0.25rem 0.5rem; + border-radius: 4px; + font-size: 0.75rem; + color: var(--accent); +} + +.action-btn { + padding: 0.5rem; + border-radius: 6px; + border: 1px solid var(--border); + background: var(--bg-tertiary); + color: var(--text-primary); + cursor: pointer; + transition: all 0.2s; + width: 32px; + height: 32px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.action-btn svg { + width: 16px; + height: 16px; + fill: currentColor; +} + +.action-btn:hover { + background: var(--accent); + border-color: var(--accent); + color: #fff; + transform: translateY(-1px); +} + +.action-btn.delete:hover { + background: var(--error); + border-color: var(--error); + color: #fff; +} + +.action-buttons { + display: flex; + gap: 0.5rem; + align-items: center; +} + +.action-btn.execute:hover { + background: #3b82f6; + border-color: #3b82f6; + color: #fff; +} + +/* Execution Result Dialog */ +.execution-result { + font-family: 'Consolas', 'Monaco', 'Courier New', monospace; + background: var(--bg-primary); + border-radius: 6px; + padding: 1rem; + margin: 0.5rem 0; + max-height: 400px; + overflow-y: auto; +} + +.execution-result pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; + font-size: 0.813rem; + line-height: 1.5; +} + +.execution-status { + display: flex; + gap: 1rem; + margin-bottom: 1rem; + flex-wrap: wrap; +} + +.status-item { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.status-item label { + font-size: 0.75rem; + color: var(--text-muted); + text-transform: uppercase; + font-weight: 600; +} + +.status-item value { + font-size: 0.875rem; + color: var(--text-primary); + font-weight: 500; +} + +.status-item.success value { + color: var(--accent); +} + +.status-item.error value { + color: var(--error); +} + +.result-section { + margin-bottom: 1rem; +} + +.result-section h4 { + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 0.5rem; + font-weight: 600; +} + +.loading-spinner { + display: inline-block; + width: 20px; + height: 20px; + border: 3px solid var(--bg-tertiary); + border-top-color: var(--accent); + border-radius: 50%; + animation: spin 0.8s linear infinite; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Dialog Styles */ +dialog { + background: var(--bg-secondary); + color: var(--text-primary); + border: 1px solid var(--border); + border-radius: 12px; + padding: 0; + max-width: 500px; + width: 90%; + margin: auto; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.8); +} + +/* Ensure dialogs are hidden until explicitly opened */ +dialog:not([open]) { + display: none; +} + +dialog::backdrop { + background: rgba(0, 0, 0, 0.8); +} + +.confirm-dialog { + max-width: 400px; + padding: 1.5rem; +} + +.confirm-dialog p { + margin-bottom: 1.25rem; + font-size: 0.95rem; + line-height: 1.5; + color: var(--text-primary); +} + +.confirm-dialog-actions { + display: flex; + justify-content: flex-end; + gap: 8px; +} + +.confirm-dialog-actions button { + padding: 8px 20px; + border-radius: 6px; + font-size: 0.85rem; + font-weight: 500; + transition: all 0.2s; +} + +.btn-cancel { + background: var(--bg-tertiary); + color: var(--text-primary); +} + +.btn-cancel:hover { + background: var(--border); +} + +.btn-danger { + background: var(--error); + color: #fff; +} + +.btn-danger:hover { + filter: brightness(1.15); +} + +.dialog-header { + padding: 1.5rem; + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); +} + +.dialog-header h3 { + margin: 0; + font-size: 1.25rem; +} + +.dialog-body { + padding: 1.5rem; +} + +.dialog-body label { + display: block; + margin-bottom: 1rem; + color: var(--text-secondary); + font-size: 0.875rem; +} + +.dialog-body input, +.dialog-body textarea, +.dialog-body select { + display: block; + width: 100%; + padding: 0.5rem; + margin-top: 0.25rem; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 6px; + color: var(--text-primary); + font-family: inherit; + font-size: 0.875rem; +} + +.dialog-body textarea { + min-height: 80px; + resize: vertical; +} + +.dialog-body input:focus, +.dialog-body textarea:focus, +.dialog-body select:focus { + outline: none; + border-color: var(--accent); +} + +.dialog-footer { + padding: 1.5rem; + border-top: 1px solid var(--border); + display: flex; + justify-content: flex-end; + gap: 0.5rem; +} + +.dialog-footer button { + padding: 0.625rem 1.5rem; + border-radius: 6px; + border: none; + font-size: 0.875rem; + font-weight: 600; + cursor: pointer; + transition: background 0.2s; + min-width: 100px; + white-space: nowrap; +} + +.dialog-footer .btn-primary { + background: var(--accent); + color: var(--text-primary); +} + +.dialog-footer .btn-primary:hover { + background: var(--accent-hover); +} + +.dialog-footer .btn-secondary { + background: var(--bg-tertiary); + color: var(--text-primary); + border: 1px solid var(--border); +} + +.dialog-footer .btn-secondary:hover { + background: var(--border); +} + +.empty-state { + text-align: center; + padding: 2rem; + color: var(--text-muted); +} + +.scripts-empty { + text-align: center; + color: var(--text-muted); + padding: 2rem; + font-size: 0.875rem; +} + +.empty-state-illustration { + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; + padding: 3rem 2rem; +} + +.empty-state-illustration svg { + width: 64px; + height: 64px; + fill: none; + stroke: var(--text-muted); + stroke-width: 1.5; + stroke-linecap: round; + stroke-linejoin: round; + opacity: 0.5; +} + +.empty-state-illustration p { + color: var(--text-muted); + font-size: 0.875rem; + margin: 0; +} + +.toast-container { + position: fixed; + bottom: 2rem; + right: 2rem; + display: flex; + flex-direction: column-reverse; + gap: 8px; + z-index: 1000; + pointer-events: none; +} + +.toast { + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: 8px; + padding: 1rem 1.5rem; + padding-left: 1.25rem; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5); + opacity: 0; + transform: translateY(20px); + transition: all 0.3s; + pointer-events: auto; + border-left: 4px solid var(--border); +} + +.toast.show { + opacity: 1; + transform: translateY(0); +} + +.toast.success { + border-color: var(--accent); + border-left-color: var(--accent); +} + +.toast.error { + border-color: var(--error); + border-left-color: var(--error); +} + +/* Auth Modal */ +#auth-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.9); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +#auth-overlay.hidden { + display: none; +} + +.auth-modal { + background: var(--bg-secondary); + padding: 2rem; + border-radius: 12px; + max-width: 400px; + width: 90%; +} + +.auth-modal h2 { + margin-bottom: 1rem; + font-size: 1.5rem; +} + +.auth-modal p { + margin-bottom: 1rem; + color: var(--text-secondary); + font-size: 0.875rem; +} + +#token-input { + width: 100%; + padding: 0.75rem; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 6px; + color: var(--text-primary); + font-size: 0.875rem; + margin-bottom: 1rem; +} + +#token-input:focus { + outline: none; + border-color: var(--accent); +} + +.btn-connect { + width: 100%; + height: auto; + padding: 0.75rem; + border-radius: 6px; + background: var(--accent); + color: var(--text-primary); + font-weight: 600; + transition: background 0.2s; +} + +.btn-connect:hover { + background: var(--accent-hover); + transform: none; +} + +.help-text { + background: var(--bg-tertiary); + padding: 0.75rem; + border-radius: 6px; + margin-top: 1rem; +} + +.help-text code { + background: var(--bg-primary); + padding: 0.25rem 0.5rem; + border-radius: 3px; + font-family: monospace; + font-size: 0.875rem; +} + +.error-message { + color: var(--error); + font-size: 0.875rem; + margin-top: 0.5rem; + display: none; +} + +.error-message.visible { + display: block; +} + +.clear-token-btn { + width: auto; + height: auto; + padding: 4px 10px; + border-radius: 6px; + font-size: 12px; + font-weight: 500; + background: var(--bg-tertiary); + color: var(--text-secondary); + border: 1px solid var(--border); + cursor: pointer; + opacity: 0.7; +} + +.clear-token-btn:hover { + opacity: 1; + background: var(--error); + color: var(--text-primary); + border-color: var(--error); +} + +/* Mini Player (Sticky) */ +.mini-player { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: rgba(30, 30, 30, 0.8); + -webkit-backdrop-filter: blur(20px) saturate(1.5); + backdrop-filter: blur(20px) saturate(1.5); + border-top: 1px solid rgba(255, 255, 255, 0.08); + padding: 0.75rem 1rem; + padding-top: calc(0.75rem + 2px); + display: flex; + align-items: center; + gap: 1.5rem; + z-index: 1000; + transform: translateY(0); + transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; + box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.3); +} + +:root[data-theme="light"] .mini-player { + background: rgba(245, 245, 245, 0.75); + border-top: 1px solid rgba(0, 0, 0, 0.08); + box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.1); +} + +.mini-player::before { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 2px; + width: var(--mini-progress, 0%); + background: var(--accent); + transition: width 0.1s linear; +} + +.mini-player.hidden { + transform: translateY(100%); + opacity: 0; + pointer-events: none; +} + +.mini-player-info { + display: flex; + align-items: center; + gap: 0.75rem; + min-width: 200px; + flex-shrink: 0; +} + +.mini-album-art { + width: 40px; + height: 40px; + border-radius: 4px; + object-fit: cover; + flex-shrink: 0; +} + +.mini-track-details { + display: flex; + flex-direction: column; + min-width: 0; +} + +.mini-track-title { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.mini-artist { + font-size: 0.75rem; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.mini-progress-container { + flex: 1; + display: flex; + align-items: center; + gap: 1rem; + min-width: 0; +} + +.mini-time-display { + display: flex; + gap: 0.5rem; + font-size: 0.75rem; + color: var(--text-secondary); + flex-shrink: 0; +} + +.mini-progress-bar { + flex: 1; + height: 4px; + background: var(--bg-tertiary); + border-radius: 2px; + cursor: pointer; + position: relative; + min-width: 100px; +} + +.mini-progress-bar:hover { + height: 6px; +} + +.mini-progress-fill { + height: 100%; + background: var(--accent); + border-radius: 2px; + width: 0%; + transition: width 0.1s linear; + position: relative; +} + +.mini-progress-fill::after { + content: ''; + position: absolute; + right: -5px; + top: 50%; + transform: translateY(-50%) scale(0); + width: 10px; + height: 10px; + background: var(--accent); + border-radius: 50%; + box-shadow: 0 0 4px rgba(0, 0, 0, 0.3); + transition: transform 0.15s ease; +} + +.mini-progress-bar:hover .mini-progress-fill::after { + transform: translateY(-50%) scale(1); +} + +.mini-controls { + display: flex; + align-items: center; + gap: 0.5rem; + flex-shrink: 0; +} + +.mini-control-btn { + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: 50%; + width: 36px; + height: 36px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; + padding: 0; +} + +.mini-control-btn:hover { + background: var(--accent); + border-color: var(--accent); + color: #fff; + transform: scale(1.05); +} + +.mini-control-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.mini-control-btn svg { + width: 20px; + height: 20px; + fill: currentColor; +} + +.mini-volume-container { + display: flex; + align-items: center; + gap: 0.75rem; + flex-shrink: 0; + min-width: 180px; +} + +.mini-volume-slider { + flex: 1; + height: 4px; + -webkit-appearance: none; + appearance: none; + background: var(--bg-tertiary); + border-radius: 2px; + outline: none; + cursor: pointer; + min-width: 80px; +} + +.mini-volume-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 12px; + height: 12px; + background: var(--accent); + border-radius: 50%; + cursor: pointer; +} + +.mini-volume-slider::-moz-range-thumb { + width: 12px; + height: 12px; + background: var(--accent); + border-radius: 50%; + cursor: pointer; + border: none; +} + +.mini-volume-slider:hover::-webkit-slider-thumb { + transform: scale(1.2); +} + +.mini-volume-slider:hover::-moz-range-thumb { + transform: scale(1.2); +} + +.mini-volume-display { + font-size: 0.75rem; + color: var(--text-secondary); + min-width: 36px; + text-align: right; +} + +/* SVG Icons */ +svg { + width: 24px; + height: 24px; + fill: currentColor; +} + +button.primary svg { + width: 28px; + height: 28px; +} + +@media (max-width: 600px) { + .container { + padding: 1rem; + } + + #album-art { + width: 250px; + height: 250px; + } + + .album-art-container.vinyl #album-art { + width: 170px; + height: 170px; + margin: 40px; + box-shadow: + 0 0 0 3px #2a2a2a, + 0 0 0 5px #1a1a1a, + 0 0 0 6px rgba(255,255,255,0.05), + 0 0 0 12px #1a1a1a, + 0 0 0 13px rgba(255,255,255,0.03), + 0 0 0 20px #1a1a1a, + 0 0 0 21px rgba(255,255,255,0.05), + 0 0 0 28px #1a1a1a, + 0 0 0 29px rgba(255,255,255,0.03), + 0 0 0 36px #1a1a1a, + 0 0 0 37px rgba(255,255,255,0.04), + 0 0 0 38px #2a2a2a, + 0 0 0 40px #111, + 0 4px 12px 40px rgba(0,0,0,0.4); + } + + #track-title { + font-size: 1.5rem; + } +} + +/* Footer */ +footer { + text-align: center; + padding: 0.75rem 1rem; + margin-top: 0.5rem; + color: var(--text-muted); + font-size: 0.75rem; + transition: padding-bottom 0.3s ease-in-out; +} + +body.mini-player-visible footer { + padding-bottom: 70px; +} + +footer a { + color: var(--accent); + text-decoration: none; + transition: color 0.2s; +} + +footer a:hover { + color: var(--accent-hover); + text-decoration: underline; +} + +footer .separator { + margin: 0 0.5rem; + color: var(--text-muted); +} /* ======================================== Media Browser Styles @@ -2412,6 +2527,45 @@ } } +/* Connection Banner */ +.connection-banner { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + display: flex; + align-items: center; + justify-content: center; + gap: 12px; + padding: 10px 16px; + background: var(--error); + color: #fff; + font-size: 0.85rem; + font-weight: 500; + text-align: center; + transition: transform 0.3s ease; +} + +.connection-banner.hidden { + transform: translateY(-100%); + pointer-events: none; +} + +.connection-banner-btn { + padding: 4px 14px; + background: rgba(255, 255, 255, 0.2); + color: #fff; + border-radius: 4px; + font-size: 0.8rem; + font-weight: 600; + transition: background 0.2s; +} + +.connection-banner-btn:hover { + background: rgba(255, 255, 255, 0.35); +} + /* Wide screens - horizontal player layout */ @media (min-width: 900px) { .container { diff --git a/media_server/static/index.html b/media_server/static/index.html index 6a6e2b5..c6703d3 100644 --- a/media_server/static/index.html +++ b/media_server/static/index.html @@ -29,7 +29,7 @@ 0:00 0:00 -
@@ -39,7 +39,7 @@